Ejemplo n.º 1
0
def decrypt(
    sender_pubkey: str,
    receiver_privkey: Union[str, bytes],
    msg: bytes,
    nonce: bytes,
) -> bytes:
    """
    Decrypt with receiver's secp256k1 private key

    Parameters
    ----------
    sender_pubkey: bytes
        The sender's public key
    receiver_privkey: Union[str, bytes]
        Receiver's private key (hex str or bytes)
    msg: bytes
        Data to decrypt
    nonce: bytes
        The nonce(16 bytes) used in the aes encryption

    Returns
    -------
    bytes
        Plain text
    """
    sender_pubkey = hex2pub(sender_pubkey)
    if isinstance(receiver_privkey, str):
        private_key = hex2prv(receiver_privkey)
    elif isinstance(receiver_privkey, bytes):
        private_key = PrivateKey(receiver_privkey)
    else:
        raise TypeError("Invalid secret key type")

    shared_key = decapsulate(sender_pubkey, private_key)
    return aes_decrypt(shared_key, msg, nonce)
Ejemplo n.º 2
0
def generate_keys(data_dir):
    """
    Generates a key pair for the client.

    Args:
        data_dir (:obj:`str`): path to data directory where the keys will be stored.

    Returns:
        :obj:`tuple`: a tuple containing a ``PrivateKey`` and a ``str`` representing the client sk and compressed pk
        respectively.

    Raises:
        :obj:`FileExistsError`: if the key pair already exists in the given directory.
    """

    # Create the output folder it it does not exist (and all the parents if they don't either)
    Path(data_dir).mkdir(parents=True, exist_ok=True)
    sk_file_name = os.path.join(data_dir, "sk.der")

    if os.path.exists(sk_file_name):
        raise FileExistsError("The client key pair already exists")

    sk = PrivateKey()
    pk = sk.public_key
    save_key(sk, sk_file_name)

    return sk, Cryptographer.get_compressed_pk(pk)
Ejemplo n.º 3
0
    def parse_fromwallet(cls, kds, vds):
        """Class method to parse entry from wallet entry

        :param kds: BCDatastream object for keys
        :type kds: BCDataStream
        :param vds: BCDataStream object for values
        :type vds: BCDataStream
        :return: KeyPair
        :rtype: KeyPair
        """
        pubkeyraw = kds.read_bytes(kds.read_compact_size())
        privkeyraw = vds.read_bytes(vds.read_compact_size())
        if len(privkeyraw) == 279:
            sec = privkeyraw[9:41]
        else:
            sec = privkeyraw[8:40]
        privkey = PrivateKey(sec)
        pubkey = PublicKey(pubkeyraw)
        if len(pubkeyraw) == 33:
            compress = True
        else:
            compress = False
        if pubkey == privkey.public_key:
            pubkey = privkey.public_key.format(compressed=compress)
            return cls(
                rawkey=pubkeyraw,
                rawvalue=privkeyraw,
                pubkey=pubkey,
                privkey=privkey,
                sec=sec,
                compressed=compress,
            )
        else:
            raise KeypairError(message="Pubkey {} error".format(
                pubkey.format(compressed=compress).hex()))
Ejemplo n.º 4
0
def test_receive_mediatedtransfer_outoforder(raiden_network, private_keys):
    alice_app = raiden_network[0]
    messages = setup_messages_cb()

    graph = alice_app.raiden.channelgraphs.values()[0]
    token_address = graph.token_address

    mt_helper = MediatedTransferTestHelper(raiden_network, graph)
    initiator_address = alice_app.raiden.address
    path = mt_helper.get_paths_of_length(initiator_address, 2)

    # make sure we have no messages before the transfer
    assert len(messages) == 0

    alice_address, bob_address, charlie_address = path
    amount = 10
    result = alice_app.raiden.transfer_async(
        token_address,
        amount,
        charlie_address,
    )

    # check for invitation ping
    assert len(messages) == 2  # Ping, Ack
    ping_message = decode(messages[0])
    assert isinstance(ping_message, Ping)
    decoded = decode(messages[1])
    assert isinstance(decoded, Ack)
    assert decoded.echo == sha3(ping_message.encode() + charlie_address)

    assert result.wait(timeout=10)
    gevent.sleep(1.)

    # check that transfer messages were added
    assert len(messages) == 22  # Ping, Ack + tranfer messages
    # make sure that the mediated transfer is sent after the invitation ping
    assert isinstance(decode(messages[2]), MediatedTransfer)

    # and now send one more mediated transfer with the same nonce, simulating
    # an out-of-order/resent message that arrives late
    locksroot = HASH
    lock = Lock(amount, 1, locksroot)
    identifier = create_default_identifier(
        alice_app.raiden.address,
        graph.token_address,
        charlie_address,
    )
    mediated_transfer = MediatedTransfer(identifier=identifier,
                                         nonce=1,
                                         token=token_address,
                                         transferred_amount=amount,
                                         recipient=bob_address,
                                         locksroot=locksroot,
                                         lock=lock,
                                         target=charlie_address,
                                         initiator=initiator_address,
                                         fee=0)
    alice_key = PrivateKey(private_keys[0])
    bob_app = mt_helper.get_app_from_address(bob_address)
    sign_and_send(mediated_transfer, alice_key, alice_address, bob_app)
def test_decode_tampered_refund_transfer(tester_state, tester_nettingchannel_library_address):
    privatekey0 = tester.DEFAULT_KEY
    address0 = privatekey_to_address(privatekey0)

    decoder = deploy_decoder_tester(tester_state, tester_nettingchannel_library_address)

    refund_transfer = make_refund_transfer()
    refund_transfer.sign(PrivateKey(privatekey0), address0)

    message_encoded = refund_transfer.encode()
    transfer_raw, _ = decoder.getTransferRawAddress(message_encoded)

    names_slices = compute_slices(messages.RefundTransfer.fields_spec)
    for name, slice_ in names_slices.iteritems():
        if name == 'signature':
            continue

        tampered_transfer = bytearray(transfer_raw)
        tampered_transfer.pop(slice_.start)
        tampered_transfer = str(tampered_transfer)
        with pytest.raises(TransactionFailed):
            decoder.decodeTransfer(tampered_transfer)

        tampered_transfer = bytearray(transfer_raw)
        tampered_transfer.pop(slice_.stop - 1)
        tampered_transfer = str(tampered_transfer)
        with pytest.raises(TransactionFailed):
            decoder.decodeTransfer(tampered_transfer)
def test_decode_mediated_transfer(
        amount,
        identifier,
        nonce,
        transferred_amount,
        locksroot,
        fee,
        tester_state,
        tester_nettingchannel_library_address):

    privatekey0 = tester.DEFAULT_KEY
    address0 = privatekey_to_address(privatekey0)

    decoder = deploy_decoder_tester(tester_state, tester_nettingchannel_library_address)

    mediated_transfer = make_mediated_transfer(
        amount=amount,
        identifier=identifier,
        nonce=nonce,
        fee=fee,
        transferred_amount=transferred_amount,
        locksroot=locksroot,
    )

    mediated_transfer.sign(PrivateKey(privatekey0), address0)
    assert_decoder_results(mediated_transfer, decoder)
Ejemplo n.º 7
0
def make_direct_transfer_from_channel(block_number, from_channel,
                                      partner_channel, amount, pkey):
    """ Helper to create and register a direct transfer from `from_channel` to
    `partner_channel`.
    """
    identifier = from_channel.get_next_nonce()

    direct_transfer_msg = from_channel.create_directtransfer(
        amount,
        identifier=identifier,
    )

    address = privatekey_to_address(pkey)
    sign_key = PrivateKey(pkey)
    direct_transfer_msg.sign(sign_key, address)

    # if this fails it's not the right key for the current `from_channel`
    assert direct_transfer_msg.sender == from_channel.our_state.address

    from_channel.register_transfer(
        block_number,
        direct_transfer_msg,
    )
    partner_channel.register_transfer(
        block_number,
        direct_transfer_msg,
    )

    return direct_transfer_msg
def make_privkey_address():
    private_key_bin = sha3(''.join(
        random.choice(string.printable) for _ in range(20)))
    privkey = PrivateKey(private_key_bin)
    pubkey = privkey.public_key.format(compressed=False)
    address = publickey_to_address(pubkey)
    return privkey, address
Ejemplo n.º 9
0
 def signRecoverableMessage(pri_key, message):
     message = prepareMessage(message)
     privkey = PrivateKey(
         pri_key
     )  # we expect to have a private key as bytes. unhexlify it before passing.
     sig_check = privkey.sign_recoverable(message)
     return electrumSig(sig_check)
Ejemplo n.º 10
0
def test_close_wrong_channel(tester_channels):
    """ Close must not accept a transfer aimed at a different channel. """
    pkey0, pkey1, nettingchannel, channel0, _ = tester_channels[0]
    opened_block = nettingchannel.opened(sender=pkey0)
    wrong_address = make_address()

    # make a transfer where the recipient is totally wrong
    transfer_wrong_channel = DirectTransfer(
        identifier=1,
        nonce=1 + (opened_block * (2**32)),
        token=channel0.token_address,
        channel=wrong_address,
        transferred_amount=10,
        recipient=channel0.our_state.address,
        locksroot=EMPTY_MERKLE_ROOT,
    )

    transfer_wrong_channel.sign(PrivateKey(pkey1),
                                privatekey_to_address(pkey1))

    transfer_wrong_channel_hash = sha3(
        transfer_wrong_channel.packed().data[:-65])
    with pytest.raises(TransactionFailed):
        nettingchannel.close(
            transfer_wrong_channel.nonce,
            transfer_wrong_channel.transferred_amount,
            transfer_wrong_channel.locksroot,
            transfer_wrong_channel_hash,
            transfer_wrong_channel.signature,
            sender=pkey0,
        )
Ejemplo n.º 11
0
def test_update_must_fail_with_a_nonparticipant_transfer(
        tester_channels, private_keys):
    """ updateTransfer must not accept a transfer from a non participant. """
    pkey0, pkey1, nettingchannel, channel0, channel1 = tester_channels[0]
    nonparticipant_key = private_keys[2]

    # make a transfer where pkey1 is the target
    transfer_nonparticipant = DirectTransfer(
        identifier=1,
        nonce=1,
        token=channel0.token_address,
        transferred_amount=10,
        recipient=channel1.our_address,
        locksroot='',
    )

    nonparticipant_address = privatekey_to_address(nonparticipant_key)
    nonparticipant_sign_key = PrivateKey(nonparticipant_key)

    transfer_nonparticipant.sign(nonparticipant_sign_key,
                                 nonparticipant_address)
    transfer_nonparticipant_data = str(transfer_nonparticipant.packed().data)

    nettingchannel.close('', sender=pkey0)

    with pytest.raises(TransactionFailed):
        nettingchannel.updateTransfer(transfer_nonparticipant_data,
                                      sender=pkey1)
Ejemplo n.º 12
0
def test_update_must_fail_with_a_channel_address(tester_channels):
    """ updateTransfer must not accept a transfer signed with the wrong channel address. """
    pkey0, pkey1, nettingchannel, channel0, channel1 = tester_channels[0]
    opened_block = nettingchannel.opened(sender=pkey0)
    wrong_channel = factories.make_address()

    # make a transfer where pkey1 is the target
    transfer_wrong_recipient = DirectTransfer(
        identifier=1,
        nonce=1 + (opened_block * (2**32)),
        token=channel0.token_address,
        channel=wrong_channel,
        transferred_amount=10,
        recipient=channel1.our_state.address,
        locksroot=EMPTY_MERKLE_ROOT,
    )

    our_address = privatekey_to_address(pkey0)
    our_sign_key = PrivateKey(pkey0)

    transfer_wrong_recipient.sign(our_sign_key, our_address)

    nettingchannel.close(sender=pkey0)

    transfer_wrong_recipient_hash = sha3(
        transfer_wrong_recipient.packed().data[:-65])
    with pytest.raises(TransactionFailed):
        nettingchannel.updateTransfer(
            transfer_wrong_recipient.nonce,
            transfer_wrong_recipient.transferred_amount,
            transfer_wrong_recipient.locksroot,
            transfer_wrong_recipient_hash,
            transfer_wrong_recipient.signature,
            sender=pkey1,
        )
Ejemplo n.º 13
0
def test_close_valid_tranfer_different_token(tester_state,
                                             tester_nettingcontracts,
                                             token_amount, tester_events):
    """ Valid messages from a different channel must be rejected. """
    pkey0, pkey1, nettingchannel = tester_nettingcontracts[0]

    from raiden.tests.fixtures.tester import (
        tester_token,
        tester_token_address,
    )

    private_keys = [pkey0, pkey1]
    other_token = tester_token(
        token_amount,
        private_keys,
        tester_state,
        tester_token_address(private_keys, token_amount, tester_state),
        tester_events,
    )

    opened_block = nettingchannel.opened(sender=pkey0)
    nonce = 1 + (opened_block * (2**32))
    direct_transfer_other_token = make_direct_transfer(
        nonce=nonce,
        token=other_token.address,
    )

    address = privatekey_to_address(pkey0)
    sign_key = PrivateKey(pkey0)
    direct_transfer_other_token.sign(sign_key, address)

    direct_transfer_data = direct_transfer_other_token.encode()

    with pytest.raises(TransactionFailed):
        nettingchannel.close(direct_transfer_data, sender=pkey1)
Ejemplo n.º 14
0
 def convert(self, value, param, ctx) -> PrivateKey:
     val = value[2:] if value.startswith(('0x', '0X')) else value
     try:
         num = bytes.fromhex(val)
         return PrivateKey(num)
     except (ValueError, TypeError):
         self.fail('%s is not a valid private key of 64 hexadecimal digits')
Ejemplo n.º 15
0
 def __init__(self, private_key, env: Optional[BinanceEnvironment] = None):
     super().__init__(env)
     self._private_key = private_key
     self._pk = PrivateKey(bytes.fromhex(self._private_key))
     self._public_key = self._pk.public_key.format(compressed=True)
     self._address = address_from_public_key(self._public_key,
                                             self._env.hrp)
Ejemplo n.º 16
0
 def __init__(self, generate_keys=0):
     self.tester = EthereumTester(PyEVMBackend())
     self.web3 = Web3(EthereumTesterProvider(self.tester))
     if generate_keys > 0:
         generated_keys = [urandom(32) for _ in range(generate_keys)]
         self.private_keys = [PrivateKey(key) for key in generated_keys]
         self.accounts = [
             self.tester.add_account(f'{encode_hex(key)}')
             for key in generated_keys
         ]
         for account in self.accounts:
             self.tester.send_transaction({
                 'from':
                 self.tester.get_accounts()[0],
                 'to':
                 account,
                 'value':
                 10**21,
                 'gas':
                 21000,
             })
     else:
         self.accounts = self.tester.get_accounts()
     self.contract_manager = ContractManager(CONTRACTS_SOURCE_DIRS)
     self.name_to_creation_hash = dict()
     self.name_to_contract = dict()
Ejemplo n.º 17
0
    def load(file_path: str, password: str) -> 'KeyWallet':
        """Loads a wallet from a keystore file with your password and generates an instance of Wallet.

        :param file_path: File path of the keystore file. type(str)
        :param password:
            Password for the keystore file.
            It must include alphabet character, number, and special character.
        :return: An instance of Wallet class.
        """
        try:
            with open(file_path, 'rb') as file:
                private_key: bytes = extract_key_from_keyfile(
                    file, bytes(password, 'utf-8'))
                private_key_object = PrivateKey(private_key)
                wallet = KeyWallet(private_key_object)
                logger.info(
                    f"Loaded Wallet by the keystore file. Address: {wallet.get_address()}, File path: {file_path}"
                )
                return wallet
        except FileNotFoundError:
            logger.exception(
                f"Raised KeyStoreException while loading the wallet by the keystore file because the file is not found."
            )
            raise KeyStoreException("File is not found.")
        except ValueError:
            logger.exception(
                f"Raised KeyStoreException while loading the wallet by the keystore file because the password is wrong."
            )
            raise KeyStoreException("Password is wrong.")
        except Exception as e:
            logger.exception(
                f"Raised KeyStoreException while loading the wallet by the keystore file. Error message: {e}"
            )
            raise KeyStoreException(f'keystore file error.{e}')
Ejemplo n.º 18
0
def test_close_accepts_only_transfer_from_participants(tester_channels,
                                                       private_keys):
    """ Close must not accept a transfer signed by a non participant. """
    pkey0, _, nettingchannel, channel0, _ = tester_channels[0]
    nonparticipant_key = private_keys[2]
    opened_block = nettingchannel.opened(sender=pkey0)

    # make a transfer where pkey0 is the target
    transfer_nonparticipant = DirectTransfer(
        identifier=1,
        nonce=1 + (opened_block * (2**32)),
        token=channel0.token_address,
        channel=channel0.identifier,
        transferred_amount=10,
        recipient=channel0.our_state.address,
        locksroot=EMPTY_MERKLE_ROOT,
    )

    nonparticipant_address = privatekey_to_address(nonparticipant_key)
    nonparticipant_sign_key = PrivateKey(nonparticipant_key)

    transfer_nonparticipant.sign(nonparticipant_sign_key,
                                 nonparticipant_address)

    transfer_nonparticipant_hash = sha3(
        transfer_nonparticipant.packed().data[:-65])
    with pytest.raises(TransactionFailed):
        nettingchannel.close(
            transfer_nonparticipant.nonce,
            transfer_nonparticipant.transferred_amount,
            transfer_nonparticipant.locksroot,
            transfer_nonparticipant_hash,
            transfer_nonparticipant.signature,
            sender=pkey0,
        )
Ejemplo n.º 19
0
def test_receive_hashlocktransfer_unknown(raiden_network):
    app0 = raiden_network[0]  # pylint: disable=unbalanced-tuple-unpacking

    graph0 = app0.raiden.channelgraphs.values()[0]

    other_key = PrivateKey(HASH2)
    other_address = privatekey_to_address(HASH2)
    amount = 10
    refund_transfer = make_refund_transfer(
        identifier=1,
        nonce=1,
        token=graph0.token_address,
        transferred_amount=amount,
        recipient=app0.raiden.address,
        locksroot=HASH,
        amount=amount,
        hashlock=HASH,
    )
    sign_and_send(refund_transfer, other_key, other_address, app0)

    secret = Secret(1, HASH, graph0.token_address)
    sign_and_send(secret, other_key, other_address, app0)

    secret_request = SecretRequest(1, HASH, 1)
    sign_and_send(secret_request, other_key, other_address, app0)

    reveal_secret = RevealSecret(HASH)
    sign_and_send(reveal_secret, other_key, other_address, app0)
Ejemplo n.º 20
0
def test_close_accepts_only_transfer_from_participants(tester_channels,
                                                       private_keys):
    """ Close must not accept a transfer from a non participant. """
    pkey0, _, nettingchannel, channel0, _ = tester_channels[0]
    nonparticipant_key = private_keys[2]
    opened_block = nettingchannel.opened(sender=pkey0)

    # make a transfer where pkey0 is the target
    transfer_nonparticipant = DirectTransfer(
        identifier=1,
        nonce=1 + (opened_block * (2**32)),
        token=channel0.token_address,
        transferred_amount=10,
        recipient=channel0.our_address,
        locksroot='',
    )

    nonparticipant_address = privatekey_to_address(nonparticipant_key)
    nonparticipant_sign_key = PrivateKey(nonparticipant_key)

    transfer_nonparticipant.sign(nonparticipant_sign_key,
                                 nonparticipant_address)
    transfer_nonparticipant_data = str(transfer_nonparticipant.packed().data)

    with pytest.raises(TransactionFailed):
        nettingchannel.close(transfer_nonparticipant_data, sender=pkey0)
Ejemplo n.º 21
0
def test_withdraw_tampered_lock_amount(tree, tester_channels, tester_state,
                                       tester_token, settle_timeout):
    """ withdraw must fail if the lock amonut is tampered. """
    pkey0, pkey1, nettingchannel, channel0, _ = tester_channels[0]

    current_block = tester_state.block.number
    expiration = current_block + settle_timeout - 1
    locks = [
        make_lock(
            hashlock=hashlock,
            expiration=expiration,
        ) for hashlock in tree
    ]

    merkle_tree = Merkletree(sha3(lock.as_bytes) for lock in locks)

    opened_block = nettingchannel.opened(sender=pkey0)
    nonce = 1 + (opened_block * (2**32))
    direct_transfer = make_direct_transfer(
        nonce=nonce,
        channel=channel0.channel_address,
        locksroot=merkle_tree.merkleroot,
        token=tester_token.address,
        recipient=privatekey_to_address(pkey1))

    address = privatekey_to_address(pkey0)
    sign_key = PrivateKey(pkey0)
    direct_transfer.sign(sign_key, address)

    direct_transfer_hash = sha3(direct_transfer.packed().data[:-65])
    nettingchannel.close(
        direct_transfer.nonce,
        direct_transfer.transferred_amount,
        direct_transfer.locksroot,
        direct_transfer_hash,
        direct_transfer.signature,
        sender=pkey1,
    )

    for lock in locks:
        secret = HASHLOCKS_SECRESTS[lock.hashlock]

        lock_encoded = lock.as_bytes
        merkle_proof = merkle_tree.make_proof(sha3(lock_encoded))

        tampered_lock = make_lock(
            amount=lock.amount * 100,
            hashlock=lock.hashlock,
            expiration=lock.expiration,
        )
        tampered_lock_encoded = sha3(tampered_lock.as_bytes)

        with pytest.raises(TransactionFailed):
            nettingchannel.withdraw(
                tampered_lock_encoded,
                ''.join(merkle_proof),
                secret,
                sender=pkey1,
            )
Ejemplo n.º 22
0
def private_key_to_address(private_key: Union[str, bytes]) -> Address:
    """ Converts a private key to an Ethereum address. """
    if isinstance(private_key, str):
        private_key = decode_hex(private_key)

    assert isinstance(private_key, bytes)
    privkey = PrivateKey(private_key)
    return public_key_to_address(privkey.public_key)
Ejemplo n.º 23
0
    def test_encode_public(self):
        secretkey = PrivateKey(secret=b16decode(self.secret_2))
        publickey = PublicKey.from_secret(secretkey.secret)

        self.assertEqual(self.public_address,
                         integrity.compute_public_address(publickey))
        self.assertEqual(self.public_digest,
                         b16encode(integrity.public_digest(publickey)))
Ejemplo n.º 24
0
def gen_keys(mnemonic):

    w = Wallet(mnemonic)
    sk, opk = w.derive_account("eth", account=0)
    sk = PrivateKey(sk)
    pk = PublicKey(opk)

    return sk, pk, opk
Ejemplo n.º 25
0
 def setUp(self):
     private_key_string = 'b344570eb80043d7c5ae9800c813b8842660898bf03cbd41e583b4e54af4e7fa'
     self._private_key = PrivateKey(
         bytes(bytearray.fromhex(private_key_string)))
     self._public_key = self._private_key.public_key.format(
         compressed=False)
     self.chain = AElf(self._url)
     self.toolkit = AElfToolkit(self._url, self._private_key)
Ejemplo n.º 26
0
 def sign_data(cls, pri_key: bytes, digest_bytes: bytes):
     privkey = PrivateKey(pri_key)
     # we expect to have a private key as bytes. unhexlify it before passing.
     item = cls()
     item.pub_key = privkey.public_key.format()
     item.digest_bytes = digest_bytes
     item.sig_ser = privkey.sign(digest_bytes, hasher=None)
     return item
Ejemplo n.º 27
0
def private_key_to_address(private_key: Union[str, bytes]) -> ChecksumAddress:
    """ Converts a private key to an Ethereum address. """
    if isinstance(private_key, str):
        private_key_bytes = to_bytes(hexstr=private_key)
    else:
        private_key_bytes = private_key
    pk = PrivateKey(private_key_bytes)
    return public_key_to_address(pk.public_key)
Ejemplo n.º 28
0
def get_public_from_private(priv: Union[bytearray, bytes, str]):
    """Gets public key from private key."""

    if isinstance(priv, str):
        priv = bytes.fromhex(priv)

    privkey = PrivateKey(priv)
    return privkey.public_key.format(compressed=False)[1:]
Ejemplo n.º 29
0
def private_key_to_address(private_key: Union[str, bytes]) -> Address:
    """ Converts a private key to an Ethereum address. """
    if isinstance(private_key, str):
        private_key = to_bytes(hexstr=private_key)

    assert isinstance(private_key, bytes)
    pk = PrivateKey(private_key)
    return public_key_to_address(pk.public_key)
Ejemplo n.º 30
0
    def test_hdkf(self):
        derived = HKDF(b'secret', 32, b'', SHA256).hex()
        self.assertEqual(
            derived,
            "2f34e5ff91ec85d53ca9b543683174d0cf550b60d5f52b24c97b386cfcf6cbbf")

        k1 = PrivateKey(secret=bytes([2]))
        self.assertEqual(k1.to_int(), 2)

        k2 = PrivateKey(secret=bytes([3]))
        self.assertEqual(k2.to_int(), 3)

        self.assertEqual(encapsulate(k1, k2.public_key),
                         decapsulate(k1.public_key, k2))
        self.assertEqual(
            encapsulate(k1, k2.public_key).hex(),
            "6f982d63e8590c9d9b5b4c1959ff80315d772edd8f60287c9361d548d5200f82")