Example #1
0
 def _create_api_input(input_address: int,
                       input_note: api.ZethNote) -> api.JoinsplitInput:
     mk_path = compute_merkle_path(input_address, mk_tree)
     input_nullifier = compute_nullifier(input_note, sender_ask)
     return create_api_joinsplit_input(mk_path, input_address,
                                       input_note, sender_ask,
                                       input_nullifier)
Example #2
0
 def _check_path_for_num_entries(num_entries: int,
                                 address: int) -> None:
     mktree = MerkleTree.empty_with_size(tree_size, MERKLE_TREE_HASH)
     for val in TEST_VALUES[0:num_entries]:
         mktree.insert(val)
     _ = mktree.recompute_root()
     mkpath = compute_merkle_path(address, mktree)
     self._check_merkle_path(address, mkpath, mktree)
Example #3
0
def charlie_double_withdraw(
        zeth_client: MixerClient,
        mk_tree: MerkleTree,
        input1: Tuple[int, ZethNote],
        charlie_eth_address: str,
        keystore: mock.KeyStore) -> contracts.MixResult:
    """
    Charlie tries to carry out a double spending by modifying the value of the
    nullifier of the previous payment
    """
    print(
        f" === Charlie attempts to withdraw {CHARLIE_WITHDRAW_ETH}ETH once " +
        "more (double spend) one of his note on the Mixer ===")

    charlie_apk = keystore["Charlie"].addr_pk.a_pk
    charlie_ask = keystore["Charlie"].addr_sk.a_sk

    tree_depth = mk_tree.depth
    mk_path1 = compute_merkle_path(input1[0], mk_tree)
    mk_root = mk_tree.get_root()

    # Create the an additional dummy input for the MixerClient
    input2 = get_dummy_input_and_address(charlie_apk)
    dummy_mk_path = mock.get_dummy_merkle_path(tree_depth)

    note1_value = to_zeth_units(EtherValue(CHARLIE_WITHDRAW_CHANGE_ETH))
    v_out = EtherValue(CHARLIE_WITHDRAW_ETH)

    # ### ATTACK BLOCK
    # Add malicious nullifiers: we reuse old nullifiers to double spend by
    # adding $r$ to them so that they have the same value as before in Z_r,
    # and so the zksnark verification passes, but have different values in
    # {0;1}^256 so that they appear different to the contract.
    # See: https://github.com/clearmatics/zeth/issues/38

    attack_primary_input3: int = 0
    attack_primary_input4: int = 0

    def compute_h_sig_attack_nf(
            nf0: bytes,
            nf1: bytes,
            sign_vk: JoinsplitSigVerificationKey) -> bytes:
        # We disassemble the nfs to get the formatting of the primary inputs
        input_nullifier0 = nf0.hex()
        input_nullifier1 = nf1.hex()
        nf0_rev = "{0:0256b}".format(int(input_nullifier0, 16))
        primary_input3_bits = nf0_rev[:FIELD_CAPACITY]
        primary_input3_res_bits = nf0_rev[FIELD_CAPACITY:]
        nf1_rev = "{0:0256b}".format(int(input_nullifier1, 16))
        primary_input4_bits = nf1_rev[:FIELD_CAPACITY]
        primary_input4_res_bits = nf1_rev[FIELD_CAPACITY:]

        # We perform the attack, recoding the modified public input values
        nonlocal attack_primary_input3
        nonlocal attack_primary_input4
        attack_primary_input3 = int(primary_input3_bits, 2) + ZETH_PRIME
        attack_primary_input4 = int(primary_input4_bits, 2) + ZETH_PRIME

        # We reassemble the nfs
        attack_primary_input3_bits = "{0:0256b}".format(attack_primary_input3)
        attack_nf0_bits = attack_primary_input3_bits[
            len(attack_primary_input3_bits) - FIELD_CAPACITY:] +\
            primary_input3_res_bits
        attack_nf0 = "{0:064x}".format(int(attack_nf0_bits, 2))
        attack_primary_input4_bits = "{0:0256b}".format(attack_primary_input4)
        attack_nf1_bits = attack_primary_input4_bits[
            len(attack_primary_input4_bits) - FIELD_CAPACITY:] +\
            primary_input4_res_bits
        attack_nf1 = "{0:064x}".format(int(attack_nf1_bits, 2))
        return compute_h_sig(
            bytes.fromhex(attack_nf0), bytes.fromhex(attack_nf1), sign_vk)

    (output_note1, output_note2, proof_json, signing_keypair) = \
        zeth_client.get_proof_joinsplit_2_by_2(
            mk_root,
            input1,
            mk_path1,
            input2,
            dummy_mk_path,
            charlie_ask,  # sender
            (charlie_apk, note1_value),  # recipient1
            (charlie_apk, 0),  # recipient2
            to_zeth_units(EtherValue(0)),  # v_in
            to_zeth_units(v_out),  # v_out
            compute_h_sig_attack_nf)

    # Update the primary inputs to the modified nullifiers, since libsnark
    # overwrites them with values in Z_p

    assert attack_primary_input3 != 0
    assert attack_primary_input4 != 0

    print("proof_json => ", proof_json)
    print("proof_json[inputs][3] => ", proof_json["inputs"][3])
    print("proof_json[inputs][4] => ", proof_json["inputs"][4])
    proof_json["inputs"][3] = hex(attack_primary_input3)
    proof_json["inputs"][4] = hex(attack_primary_input4)
    # ### ATTACK BLOCK

    # construct pk object from bytes
    pk_charlie = keystore["Charlie"].addr_pk.k_pk

    # encrypt the coins
    ciphertexts = encrypt_notes([
        (output_note1, pk_charlie),
        (output_note2, pk_charlie)])

    # Compute the joinSplit signature
    joinsplit_sig_charlie = joinsplit_sign(
        signing_keypair,
        charlie_eth_address,
        ciphertexts,
        proof_json)

    mix_params = contracts.MixParameters(
        proof_json,
        signing_keypair.vk,
        joinsplit_sig_charlie,
        ciphertexts)

    tx_hash = zeth_client.mix(
        mix_params,
        charlie_eth_address,
        # Pay an arbitrary amount (1 wei here) that will be refunded since the
        # `mix` function is payable
        None,
        EtherValue(1, 'wei'))
    return wait_for_tx_update_mk_tree(zeth_client, mk_tree, tx_hash)
Example #4
0
    def create_prover_inputs(
        mix_call_desc: MixCallDescription
    ) -> Tuple[ProofInputs, signing.SigningKeyPair]:
        """
        Given the basic parameters for a mix call, compute the input to the prover
        server, and the signing key pair.
        """

        # Compute Merkle paths
        mk_tree = mix_call_desc.mk_tree
        mk_root = mk_tree.get_root()
        inputs = mix_call_desc.inputs
        mk_paths = [compute_merkle_path(addr, mk_tree) for addr, _ in inputs]

        # Extract (<ownership-address>, <value>) tuples
        outputs_with_a_pk = \
            [(zeth_addr.a_pk, to_zeth_units(value))
             for (zeth_addr, value) in mix_call_desc.outputs]
        output0 = outputs_with_a_pk[0]
        output1 = outputs_with_a_pk[1]

        # Public input and output values as Zeth units
        public_in_value_zeth_units = to_zeth_units(mix_call_desc.v_in)
        public_out_value_zeth_units = to_zeth_units(mix_call_desc.v_out)

        # Generate the signing key
        signing_keypair = signing.gen_signing_keypair()
        sender_ask = mix_call_desc.sender_ownership_keypair.a_sk

        # Compute the input note nullifiers
        (input_address0, input_note0) = mix_call_desc.inputs[0]
        (input_address1, input_note1) = mix_call_desc.inputs[1]
        input_nullifier0 = compute_nullifier(input_note0, sender_ask)
        input_nullifier1 = compute_nullifier(input_note1, sender_ask)

        # Convert to JoinsplitInput objects
        js_inputs: List[JoinsplitInput] = [
            create_joinsplit_input(mk_paths[0], input_address0, input_note0,
                                   sender_ask, input_nullifier0),
            create_joinsplit_input(mk_paths[1], input_address1, input_note1,
                                   sender_ask, input_nullifier1)
        ]

        # Use the specified or default h_sig computation
        compute_h_sig_cb = mix_call_desc.compute_h_sig_cb or compute_h_sig
        h_sig = compute_h_sig_cb(input_nullifier0, input_nullifier1,
                                 signing_keypair.vk)
        phi = _phi_randomness()

        # Joinsplit Output Notes
        output_note0, output_note1 = _create_zeth_notes(
            phi, h_sig, output0, output1)
        js_outputs = [output_note0, output_note1]

        proof_inputs = ProofInputs(
            mk_root=mk_root.hex(),
            js_inputs=js_inputs,
            js_outputs=js_outputs,
            pub_in_value=int64_to_hex(public_in_value_zeth_units),
            pub_out_value=int64_to_hex(public_out_value_zeth_units),
            h_sig=h_sig.hex(),
            phi=phi.hex())
        return (proof_inputs, signing_keypair)
Example #5
0
    def create_mix_parameters_keep_signing_key(
        self,
        mk_tree: MerkleTree,
        sender_ownership_keypair: OwnershipKeyPair,
        sender_eth_address: str,
        inputs: List[Tuple[int, ZethNote]],
        outputs: List[Tuple[ZethAddressPub, EtherValue]],
        v_in: EtherValue,
        v_out: EtherValue,
        compute_h_sig_cb: Optional[ComputeHSigCB] = None
    ) -> Tuple[contracts.MixParameters, JoinsplitSigKeyPair]:
        assert len(inputs) <= constants.JS_INPUTS
        assert len(outputs) <= constants.JS_OUTPUTS

        sender_a_sk = sender_ownership_keypair.a_sk
        sender_a_pk = sender_ownership_keypair.a_pk
        inputs = \
            inputs + \
            [get_dummy_input_and_address(sender_a_pk)
             for _ in range(constants.JS_INPUTS - len(inputs))]
        mk_root = mk_tree.get_root()
        mk_paths = [compute_merkle_path(addr, mk_tree) for addr, _ in inputs]

        # Generate output notes and proof.  Dummy outputs are constructed with
        # value 0 to an invalid ZethAddressPub, formed from the senders
        # a_pk, and an ephemeral k_pk.
        dummy_k_pk = generate_encryption_keypair().k_pk
        dummy_addr_pk = ZethAddressPub(sender_a_pk, dummy_k_pk)
        outputs = \
            outputs + \
            [(dummy_addr_pk, EtherValue(0))
             for _ in range(constants.JS_OUTPUTS - len(outputs))]
        outputs_with_a_pk = \
            [(zeth_addr.a_pk, to_zeth_units(value))
             for (zeth_addr, value) in outputs]

        # Timer used to time proof-generation round trip time.
        timer = Timer.started()

        (output_note1, output_note2, extproof, signing_keypair) = \
            self.get_proof_joinsplit_2_by_2(
                mk_root,
                inputs[0],
                mk_paths[0],
                inputs[1],
                mk_paths[1],
                sender_a_sk,
                outputs_with_a_pk[0],
                outputs_with_a_pk[1],
                to_zeth_units(v_in),
                to_zeth_units(v_out),
                compute_h_sig_cb)

        proof_gen_time_s = timer.elapsed_seconds()
        print(f"PROOF GEN ROUND TRIP: {proof_gen_time_s} seconds")

        # Encrypt the notes
        outputs_and_notes = zip(outputs, [output_note1, output_note2])
        output_notes_with_k_pk = \
            [(note, zeth_addr.k_pk)
             for ((zeth_addr, _), note) in outputs_and_notes]
        ciphertexts = encrypt_notes(output_notes_with_k_pk)

        # Sign
        signature = joinsplit_sign(signing_keypair, sender_eth_address,
                                   ciphertexts, extproof)

        mix_params = contracts.MixParameters(extproof, signing_keypair.vk,
                                             signature, ciphertexts)
        return mix_params, signing_keypair