Exemplo n.º 1
0
def test_encrypt_decrypt():
    msg = b'test yeah'
    privkey = ecies.generate_privkey()
    ciphertext = ecies.encrypt(msg, privkey.public_key)
    decrypted = ecies.decrypt(ciphertext, privkey)
    assert decrypted == msg

    privkey2 = ecies.generate_privkey()
    with pytest.raises(ecies.DecryptionError):
        decrypted = ecies.decrypt(ciphertext, privkey2)
Exemplo n.º 2
0
def test_decrypt_known_good_handshake():
    # Data taken from https://gist.github.com/fjl/3a78780d17c755d22df2
    privkey = keys.PrivateKey(
        decode_hex("c45f950382d542169ea207959ee0220ec1491755abe405cd7498d6b16adb6df8"))
    auth_ciphertext = decode_hex(
        "04a0274c5951e32132e7f088c9bdfdc76c9d91f0dc6078e848f8e3361193dbdc43b94351ea3d89e4ff33ddcefbc80070498824857f499656c4f79bbd97b6c51a514251d69fd1785ef8764bd1d262a883f780964cce6a14ff206daf1206aa073a2d35ce2697ebf3514225bef186631b2fd2316a4b7bcdefec8d75a1025ba2c5404a34e7795e1dd4bc01c6113ece07b0df13b69d3ba654a36e35e69ff9d482d88d2f0228e7d96fe11dccbb465a1831c7d4ad3a026924b182fc2bdfe016a6944312021da5cc459713b13b86a686cf34d6fe6615020e4acf26bf0d5b7579ba813e7723eb95b3cef9942f01a58bd61baee7c9bdd438956b426a4ffe238e61746a8c93d5e10680617c82e48d706ac4953f5e1c4c4f7d013c87d34a06626f498f34576dc017fdd3d581e83cfd26cf125b6d2bda1f1d56")  # noqa: E501
    auth_plaintext = decode_hex(
        "884c36f7ae6b406637c1f61b2f57e1d2cab813d24c6559aaf843c3f48962f32f46662c066d39669b7b2e3ba14781477417600e7728399278b1b5d801a519aa570034fdb5419558137e0d44cd13d319afe5629eeccb47fd9dfe55cc6089426e46cc762dd8a0636e07a54b31169eba0c7a20a1ac1ef68596f1f283b5c676bae4064abfcce24799d09f67e392632d3ffdc12e3d6430dcb0ea19c318343ffa7aae74d4cd26fecb93657d1cd9e9eaf4f8be720b56dd1d39f190c4e1c6b7ec66f077bb1100")  # noqa: E501
    decrypted = ecies.decrypt(auth_ciphertext, privkey)
    assert auth_plaintext == decrypted
Exemplo n.º 3
0
def decode_auth_plain(ciphertext: bytes, privkey: datatypes.PrivateKey) -> Tuple[
        datatypes.Signature, datatypes.PublicKey, bytes, int]:
    """Decode legacy pre-EIP-8 auth message format"""
    message = ecies.decrypt(ciphertext, privkey)
    if len(message) != AUTH_MSG_LEN:
        raise ValueError("Unexpected size for auth message: {}".format(len(message)))
    signature = keys.Signature(signature_bytes=message[:SIGNATURE_LEN])
    pubkey_start = SIGNATURE_LEN + HASH_LEN
    pubkey = keys.PublicKey(message[pubkey_start: pubkey_start + PUBKEY_LEN])
    nonce_start = pubkey_start + PUBKEY_LEN
    nonce = message[nonce_start: nonce_start + HASH_LEN]
    return signature, pubkey, nonce, SUPPORTED_RLPX_VERSION
Exemplo n.º 4
0
def decode_ack_plain(
        ciphertext: bytes, privkey: datatypes.PrivateKey) -> Tuple[datatypes.PublicKey, bytes, int]:
    """Decrypts and decodes a legacy pre-EIP-8 auth ack message.

    Returns the remote's ephemeral pubkey, nonce and protocol version.
    """
    message = ecies.decrypt(ciphertext, privkey)
    if len(message) != AUTH_ACK_LEN:
        raise ValueError("Unexpected size for ack message: {}".format(len(message)))
    eph_pubkey = keys.PublicKey(message[:PUBKEY_LEN])
    nonce = message[PUBKEY_LEN: PUBKEY_LEN + HASH_LEN]
    return eph_pubkey, nonce, SUPPORTED_RLPX_VERSION
Exemplo n.º 5
0
def decode_ack_eip8(
        ciphertext: bytes, privkey: datatypes.PrivateKey) -> Tuple[datatypes.PublicKey, bytes, int]:
    """Decrypts and decodes a EIP-8 auth ack message.

    Returns the remote's ephemeral pubkey, nonce and protocol version.
    """
    # The length of the actual msg is stored in plaintext on the first two bytes.
    encoded_size = ciphertext[:2]
    auth_ack = ciphertext[2:]
    message = ecies.decrypt(auth_ack, privkey, shared_mac_data=encoded_size)
    values = rlp.decode(message, sedes=eip8_ack_sedes, strict=False)
    pubkey_bytes, nonce, version = values[:3]
    return keys.PublicKey(pubkey_bytes), nonce, version
Exemplo n.º 6
0
def test_decrypt_known_good_handshake():
    # Data taken from https://gist.github.com/fjl/3a78780d17c755d22df2
    privkey = keys.PrivateKey(
        decode_hex(
            "c45f950382d542169ea207959ee0220ec1491755abe405cd7498d6b16adb6df8")
    )
    auth_ciphertext = decode_hex(
        "04a0274c5951e32132e7f088c9bdfdc76c9d91f0dc6078e848f8e3361193dbdc43b94351ea3d89e4ff33ddcefbc80070498824857f499656c4f79bbd97b6c51a514251d69fd1785ef8764bd1d262a883f780964cce6a14ff206daf1206aa073a2d35ce2697ebf3514225bef186631b2fd2316a4b7bcdefec8d75a1025ba2c5404a34e7795e1dd4bc01c6113ece07b0df13b69d3ba654a36e35e69ff9d482d88d2f0228e7d96fe11dccbb465a1831c7d4ad3a026924b182fc2bdfe016a6944312021da5cc459713b13b86a686cf34d6fe6615020e4acf26bf0d5b7579ba813e7723eb95b3cef9942f01a58bd61baee7c9bdd438956b426a4ffe238e61746a8c93d5e10680617c82e48d706ac4953f5e1c4c4f7d013c87d34a06626f498f34576dc017fdd3d581e83cfd26cf125b6d2bda1f1d56"
    )  # noqa: E501
    auth_plaintext = decode_hex(
        "884c36f7ae6b406637c1f61b2f57e1d2cab813d24c6559aaf843c3f48962f32f46662c066d39669b7b2e3ba14781477417600e7728399278b1b5d801a519aa570034fdb5419558137e0d44cd13d319afe5629eeccb47fd9dfe55cc6089426e46cc762dd8a0636e07a54b31169eba0c7a20a1ac1ef68596f1f283b5c676bae4064abfcce24799d09f67e392632d3ffdc12e3d6430dcb0ea19c318343ffa7aae74d4cd26fecb93657d1cd9e9eaf4f8be720b56dd1d39f190c4e1c6b7ec66f077bb1100"
    )  # noqa: E501
    decrypted = ecies.decrypt(auth_ciphertext, privkey)
    assert auth_plaintext == decrypted
Exemplo n.º 7
0
def decode_auth_eip8(ciphertext: bytes, privkey: datatypes.PrivateKey) -> Tuple[
        datatypes.Signature, datatypes.PublicKey, bytes, int]:
    """Decode EIP-8 auth message format"""
    # The length of the actual msg is stored in plaintext on the first two bytes.
    encoded_size = ciphertext[:2]
    auth_msg = ciphertext[2:]
    message = ecies.decrypt(auth_msg, privkey, shared_mac_data=encoded_size)
    values = rlp.decode(message, sedes=eip8_auth_sedes, strict=False)
    signature_bytes, pubkey_bytes, nonce, version = values[:4]
    return (
        keys.Signature(signature_bytes=signature_bytes),
        keys.PublicKey(pubkey_bytes),
        nonce,
        version
    )
Exemplo n.º 8
0
def decode_auth_eip8(ciphertext: bytes, privkey: datatypes.PrivateKey) -> Tuple[
        datatypes.Signature, datatypes.PublicKey, bytes, int]:
    """Decode EIP-8 auth message format"""
    # The length of the actual msg is stored in plaintext on the first two bytes.
    encoded_size = ciphertext[:2]
    auth_msg = ciphertext[2:]
    message = ecies.decrypt(auth_msg, privkey, shared_mac_data=encoded_size)
    values = rlp.decode(message, sedes=eip8_auth_sedes, strict=False)
    signature_bytes, pubkey_bytes, nonce, version = values[:4]
    return (
        keys.Signature(signature_bytes=signature_bytes),
        keys.PublicKey(pubkey_bytes),
        nonce,
        version
    )
Exemplo n.º 9
0
async def test_handshake():
    # TODO: this test should be re-written to not depend on functionality in the `ETHPeer` class.
    cancel_token = CancelToken("test_handshake")
    use_eip8 = False
    initiator_remote = kademlia.Node(
        keys.PrivateKey(test_values['receiver_private_key']).public_key,
        kademlia.Address('0.0.0.0', 0, 0))
    initiator = HandshakeInitiator(
        initiator_remote,
        keys.PrivateKey(test_values['initiator_private_key']), use_eip8,
        cancel_token)
    initiator.ephemeral_privkey = keys.PrivateKey(
        test_values['initiator_ephemeral_private_key'])

    responder_remote = kademlia.Node(
        keys.PrivateKey(test_values['initiator_private_key']).public_key,
        kademlia.Address('0.0.0.0', 0, 0))
    responder = HandshakeResponder(
        responder_remote, keys.PrivateKey(test_values['receiver_private_key']),
        use_eip8, cancel_token)
    responder.ephemeral_privkey = keys.PrivateKey(
        test_values['receiver_ephemeral_private_key'])

    # Check that the auth message generated by the initiator is what we expect. Notice that we
    # can't use the auth_init generated here because the non-deterministic prefix would cause the
    # derived secrets to not match the expected values.
    _auth_init = initiator.create_auth_message(test_values['initiator_nonce'])
    assert len(_auth_init) == len(test_values['auth_plaintext'])
    assert _auth_init[65:] == test_values['auth_plaintext'][
        65:]  # starts with non deterministic k

    # Check that encrypting and decrypting the auth_init gets us the orig msg.
    _auth_init_ciphertext = initiator.encrypt_auth_message(_auth_init)
    assert _auth_init == ecies.decrypt(_auth_init_ciphertext,
                                       responder.privkey)

    # Check that the responder correctly decodes the auth msg.
    auth_msg_ciphertext = test_values['auth_ciphertext']
    initiator_ephemeral_pubkey, initiator_nonce, _ = decode_authentication(
        auth_msg_ciphertext, responder.privkey)
    assert initiator_nonce == test_values['initiator_nonce']
    assert initiator_ephemeral_pubkey == (keys.PrivateKey(
        test_values['initiator_ephemeral_private_key']).public_key)

    # Check that the auth_ack msg generated by the responder is what we expect.
    auth_ack_msg = responder.create_auth_ack_message(
        test_values['receiver_nonce'])
    assert auth_ack_msg == test_values['authresp_plaintext']

    # Check that the secrets derived from ephemeral key agreements match the expected values.
    auth_ack_ciphertext = test_values['authresp_ciphertext']
    aes_secret, mac_secret, egress_mac, ingress_mac = responder.derive_secrets(
        initiator_nonce, test_values['receiver_nonce'],
        initiator_ephemeral_pubkey, auth_msg_ciphertext, auth_ack_ciphertext)
    assert aes_secret == test_values['aes_secret']
    assert mac_secret == test_values['mac_secret']
    # Test values are from initiator perspective, so they're reversed here.
    assert ingress_mac.digest() == test_values['initial_egress_MAC']
    assert egress_mac.digest() == test_values['initial_ingress_MAC']

    # Check that the initiator secrets match as well.
    responder_ephemeral_pubkey, responder_nonce = initiator.decode_auth_ack_message(
        test_values['authresp_ciphertext'])
    (initiator_aes_secret, initiator_mac_secret, initiator_egress_mac,
     initiator_ingress_mac) = initiator.derive_secrets(
         initiator_nonce, responder_nonce, responder_ephemeral_pubkey,
         auth_msg_ciphertext, auth_ack_ciphertext)
    assert initiator_aes_secret == aes_secret
    assert initiator_mac_secret == mac_secret
    assert initiator_ingress_mac.digest() == test_values['initial_ingress_MAC']
    assert initiator_egress_mac.digest() == test_values['initial_egress_MAC']

    # Finally, check that two Peers configured with the secrets generated above understand each
    # other.
    responder_reader = asyncio.StreamReader()
    initiator_reader = asyncio.StreamReader()
    # Link the initiator's writer to the responder's reader, and the responder's writer to the
    # initiator's reader.
    responder_writer = MockStreamWriter(initiator_reader.feed_data)
    initiator_writer = MockStreamWriter(responder_reader.feed_data)
    initiator_peer = DumbPeer(remote=initiator.remote,
                              privkey=initiator.privkey,
                              reader=initiator_reader,
                              writer=initiator_writer,
                              aes_secret=initiator_aes_secret,
                              mac_secret=initiator_mac_secret,
                              egress_mac=initiator_egress_mac,
                              ingress_mac=initiator_ingress_mac,
                              headerdb=None,
                              network_id=1)
    initiator_peer.base_protocol.send_handshake()
    responder_peer = DumbPeer(remote=responder.remote,
                              privkey=responder.privkey,
                              reader=responder_reader,
                              writer=responder_writer,
                              aes_secret=aes_secret,
                              mac_secret=mac_secret,
                              egress_mac=egress_mac,
                              ingress_mac=ingress_mac,
                              headerdb=None,
                              network_id=1)
    responder_peer.base_protocol.send_handshake()

    # The handshake msgs sent by each peer (above) are going to be fed directly into their remote's
    # reader, and thus the read_msg() calls will return immediately.
    responder_hello, _ = await responder_peer.read_msg()
    initiator_hello, _ = await initiator_peer.read_msg()

    assert isinstance(responder_hello, Hello)
    assert isinstance(initiator_hello, Hello)
Exemplo n.º 10
0
async def test_handshake():
    # TODO: this test should be re-written to not depend on functionality in the `ETHPeer` class.
    cancel_token = CancelToken("test_handshake")
    use_eip8 = False
    initiator_remote = kademlia.Node(
        keys.PrivateKey(test_values['receiver_private_key']).public_key,
        kademlia.Address('0.0.0.0', 0, 0))
    initiator = HandshakeInitiator(
        initiator_remote,
        keys.PrivateKey(test_values['initiator_private_key']), use_eip8,
        cancel_token)
    initiator.ephemeral_privkey = keys.PrivateKey(
        test_values['initiator_ephemeral_private_key'])

    responder_remote = kademlia.Node(
        keys.PrivateKey(test_values['initiator_private_key']).public_key,
        kademlia.Address('0.0.0.0', 0, 0))
    responder = HandshakeResponder(
        responder_remote, keys.PrivateKey(test_values['receiver_private_key']),
        use_eip8, cancel_token)
    responder.ephemeral_privkey = keys.PrivateKey(
        test_values['receiver_ephemeral_private_key'])

    # Check that the auth message generated by the initiator is what we expect. Notice that we
    # can't use the auth_init generated here because the non-deterministic prefix would cause the
    # derived secrets to not match the expected values.
    _auth_init = initiator.create_auth_message(test_values['initiator_nonce'])
    assert len(_auth_init) == len(test_values['auth_plaintext'])
    assert _auth_init[65:] == test_values['auth_plaintext'][
        65:]  # starts with non deterministic k

    # Check that encrypting and decrypting the auth_init gets us the orig msg.
    _auth_init_ciphertext = initiator.encrypt_auth_message(_auth_init)
    assert _auth_init == ecies.decrypt(_auth_init_ciphertext,
                                       responder.privkey)

    # Check that the responder correctly decodes the auth msg.
    auth_msg_ciphertext = test_values['auth_ciphertext']
    initiator_ephemeral_pubkey, initiator_nonce, _ = decode_authentication(
        auth_msg_ciphertext, responder.privkey)
    assert initiator_nonce == test_values['initiator_nonce']
    assert initiator_ephemeral_pubkey == (keys.PrivateKey(
        test_values['initiator_ephemeral_private_key']).public_key)

    # Check that the auth_ack msg generated by the responder is what we expect.
    auth_ack_msg = responder.create_auth_ack_message(
        test_values['receiver_nonce'])
    assert auth_ack_msg == test_values['authresp_plaintext']

    # Check that the secrets derived from ephemeral key agreements match the expected values.
    auth_ack_ciphertext = test_values['authresp_ciphertext']
    aes_secret, mac_secret, egress_mac, ingress_mac = responder.derive_secrets(
        initiator_nonce, test_values['receiver_nonce'],
        initiator_ephemeral_pubkey, auth_msg_ciphertext, auth_ack_ciphertext)
    assert aes_secret == test_values['aes_secret']
    assert mac_secret == test_values['mac_secret']
    # Test values are from initiator perspective, so they're reversed here.
    assert ingress_mac.digest() == test_values['initial_egress_MAC']
    assert egress_mac.digest() == test_values['initial_ingress_MAC']

    # Check that the initiator secrets match as well.
    responder_ephemeral_pubkey, responder_nonce = initiator.decode_auth_ack_message(
        test_values['authresp_ciphertext'])
    (initiator_aes_secret, initiator_mac_secret, initiator_egress_mac,
     initiator_ingress_mac) = initiator.derive_secrets(
         initiator_nonce, responder_nonce, responder_ephemeral_pubkey,
         auth_msg_ciphertext, auth_ack_ciphertext)
    assert initiator_aes_secret == aes_secret
    assert initiator_mac_secret == mac_secret
    assert initiator_ingress_mac.digest() == test_values['initial_ingress_MAC']
    assert initiator_egress_mac.digest() == test_values['initial_egress_MAC']

    # Finally, check that two Peers configured with the secrets generated above understand each
    # other.
    (
        (responder_reader, responder_writer),
        (initiator_reader, initiator_writer),
    ) = get_directly_connected_streams()

    capabilities = (('paragon', 1), )

    initiator_transport = Transport(remote=initiator_remote,
                                    private_key=initiator.privkey,
                                    reader=initiator_reader,
                                    writer=initiator_writer,
                                    aes_secret=initiator_aes_secret,
                                    mac_secret=initiator_mac_secret,
                                    egress_mac=initiator_egress_mac,
                                    ingress_mac=initiator_ingress_mac)
    initiator_p2p_protocol = P2PProtocolV5(initiator_transport, 0, False)
    initiator_multiplexer = Multiplexer(
        transport=initiator_transport,
        base_protocol=initiator_p2p_protocol,
        protocols=(),
    )
    initiator_multiplexer.get_base_protocol().send(
        Hello(
            HelloPayload(
                client_version_string='initiator',
                capabilities=capabilities,
                listen_port=30303,
                version=DEVP2P_V5,
                remote_public_key=initiator.privkey.public_key.to_bytes(),
            )))

    responder_transport = Transport(
        remote=responder_remote,
        private_key=responder.privkey,
        reader=responder_reader,
        writer=responder_writer,
        aes_secret=aes_secret,
        mac_secret=mac_secret,
        egress_mac=egress_mac,
        ingress_mac=ingress_mac,
    )
    responder_p2p_protocol = P2PProtocolV5(responder_transport, 0, False)
    responder_multiplexer = Multiplexer(
        transport=responder_transport,
        base_protocol=responder_p2p_protocol,
        protocols=(),
    )
    responder_multiplexer.get_base_protocol().send(
        Hello(
            HelloPayload(
                client_version_string='responder',
                capabilities=capabilities,
                listen_port=30303,
                version=DEVP2P_V5,
                remote_public_key=responder.privkey.public_key.to_bytes(),
            )))

    async with initiator_multiplexer.multiplex():
        async with responder_multiplexer.multiplex():
            initiator_stream = initiator_multiplexer.stream_protocol_messages(
                initiator_p2p_protocol, )
            responder_stream = responder_multiplexer.stream_protocol_messages(
                responder_p2p_protocol, )

            initiator_hello = await asyncio.wait_for(
                initiator_stream.asend(None), timeout=0.1)
            responder_hello = await asyncio.wait_for(
                responder_stream.asend(None), timeout=0.1)

            await initiator_stream.aclose()
            await responder_stream.aclose()

    assert isinstance(responder_hello, Hello)
    assert isinstance(initiator_hello, Hello)
Exemplo n.º 11
0
async def test_handshake():
    # This data comes from https://gist.github.com/fjl/3a78780d17c755d22df2
    test_values = {
        "initiator_private_key": "5e173f6ac3c669587538e7727cf19b782a4f2fda07c1eaa662c593e5e85e3051",
        "receiver_private_key": "c45f950382d542169ea207959ee0220ec1491755abe405cd7498d6b16adb6df8",
        "initiator_ephemeral_private_key": "19c2185f4f40634926ebed3af09070ca9e029f2edd5fae6253074896205f5f6c",  # noqa: E501
        "receiver_ephemeral_private_key": "d25688cf0ab10afa1a0e2dba7853ed5f1e5bf1c631757ed4e103b593ff3f5620",  # noqa: E501
        "auth_plaintext": "884c36f7ae6b406637c1f61b2f57e1d2cab813d24c6559aaf843c3f48962f32f46662c066d39669b7b2e3ba14781477417600e7728399278b1b5d801a519aa570034fdb5419558137e0d44cd13d319afe5629eeccb47fd9dfe55cc6089426e46cc762dd8a0636e07a54b31169eba0c7a20a1ac1ef68596f1f283b5c676bae4064abfcce24799d09f67e392632d3ffdc12e3d6430dcb0ea19c318343ffa7aae74d4cd26fecb93657d1cd9e9eaf4f8be720b56dd1d39f190c4e1c6b7ec66f077bb1100",  # noqa: E501
        "authresp_plaintext": "802b052f8b066640bba94a4fc39d63815c377fced6fcb84d27f791c9921ddf3e9bf0108e298f490812847109cbd778fae393e80323fd643209841a3b7f110397f37ec61d84cea03dcc5e8385db93248584e8af4b4d1c832d8c7453c0089687a700",  # noqa: E501
        "auth_ciphertext": "04a0274c5951e32132e7f088c9bdfdc76c9d91f0dc6078e848f8e3361193dbdc43b94351ea3d89e4ff33ddcefbc80070498824857f499656c4f79bbd97b6c51a514251d69fd1785ef8764bd1d262a883f780964cce6a14ff206daf1206aa073a2d35ce2697ebf3514225bef186631b2fd2316a4b7bcdefec8d75a1025ba2c5404a34e7795e1dd4bc01c6113ece07b0df13b69d3ba654a36e35e69ff9d482d88d2f0228e7d96fe11dccbb465a1831c7d4ad3a026924b182fc2bdfe016a6944312021da5cc459713b13b86a686cf34d6fe6615020e4acf26bf0d5b7579ba813e7723eb95b3cef9942f01a58bd61baee7c9bdd438956b426a4ffe238e61746a8c93d5e10680617c82e48d706ac4953f5e1c4c4f7d013c87d34a06626f498f34576dc017fdd3d581e83cfd26cf125b6d2bda1f1d56",  # noqa: E501
        "authresp_ciphertext": "049934a7b2d7f9af8fd9db941d9da281ac9381b5740e1f64f7092f3588d4f87f5ce55191a6653e5e80c1c5dd538169aa123e70dc6ffc5af1827e546c0e958e42dad355bcc1fcb9cdf2cf47ff524d2ad98cbf275e661bf4cf00960e74b5956b799771334f426df007350b46049adb21a6e78ab1408d5e6ccde6fb5e69f0f4c92bb9c725c02f99fa72b9cdc8dd53cff089e0e73317f61cc5abf6152513cb7d833f09d2851603919bf0fbe44d79a09245c6e8338eb502083dc84b846f2fee1cc310d2cc8b1b9334728f97220bb799376233e113",  # noqa: E501
        "ecdhe_shared_secret": "e3f407f83fc012470c26a93fdff534100f2c6f736439ce0ca90e9914f7d1c381",
        "initiator_nonce": "cd26fecb93657d1cd9e9eaf4f8be720b56dd1d39f190c4e1c6b7ec66f077bb11",
        "receiver_nonce": "f37ec61d84cea03dcc5e8385db93248584e8af4b4d1c832d8c7453c0089687a7",
        "aes_secret": "c0458fa97a5230830e05f4f20b7c755c1d4e54b1ce5cf43260bb191eef4e418d",
        "mac_secret": "48c938884d5067a1598272fcddaa4b833cd5e7d92e8228c0ecdfabbe68aef7f1",
        "token": "3f9ec2592d1554852b1f54d228f042ed0a9310ea86d038dc2b401ba8cd7fdac4",
        "initial_egress_MAC": "09771e93b1a6109e97074cbe2d2b0cf3d3878efafe68f53c41bb60c0ec49097e",
        "initial_ingress_MAC": "75823d96e23136c89666ee025fb21a432be906512b3dd4a3049e898adb433847",
        "initiator_hello_packet": "6ef23fcf1cec7312df623f9ae701e63b550cdb8517fefd8dd398fc2acd1d935e6e0434a2b96769078477637347b7b01924fff9ff1c06df2f804df3b0402bbb9f87365b3c6856b45e1e2b6470986813c3816a71bff9d69dd297a5dbd935ab578f6e5d7e93e4506a44f307c332d95e8a4b102585fd8ef9fc9e3e055537a5cec2e9",  # noqa: E501
        "receiver_hello_packet": "6ef23fcf1cec7312df623f9ae701e63be36a1cdd1b19179146019984f3625d4a6e0434a2b96769050577657247b7b02bc6c314470eca7e3ef650b98c83e9d7dd4830b3f718ff562349aead2530a8d28a8484604f92e5fced2c6183f304344ab0e7c301a0c05559f4c25db65e36820b4b909a226171a60ac6cb7beea09376d6d8"  # noqa: E501
    }
    for k, v in test_values.items():
        test_values[k] = decode_hex(v)

    initiator_remote = kademlia.Node(
        keys.PrivateKey(test_values['receiver_private_key']).public_key,
        kademlia.Address('0.0.0.0', 0, 0))
    initiator = HandshakeInitiator(
        initiator_remote,
        keys.PrivateKey(test_values['initiator_private_key']))
    initiator.ephemeral_privkey = keys.PrivateKey(test_values['initiator_ephemeral_private_key'])

    responder_remote = kademlia.Node(
        keys.PrivateKey(test_values['initiator_private_key']).public_key,
        kademlia.Address('0.0.0.0', 0, 0))
    responder = HandshakeResponder(
        responder_remote,
        keys.PrivateKey(test_values['receiver_private_key']))
    responder.ephemeral_privkey = keys.PrivateKey(test_values['receiver_ephemeral_private_key'])

    # Check that the auth message generated by the initiator is what we expect. Notice that we
    # can't use the auth_init generated here because the non-deterministic prefix would cause the
    # derived secrets to not match the expected values.
    _auth_init = initiator.create_auth_message(test_values['initiator_nonce'])
    assert len(_auth_init) == len(test_values['auth_plaintext'])
    assert _auth_init[65:] == test_values['auth_plaintext'][65:]  # starts with non deterministic k

    # Check that encrypting and decrypting the auth_init gets us the orig msg.
    _auth_init_ciphertext = initiator.encrypt_auth_message(_auth_init)
    assert _auth_init == ecies.decrypt(_auth_init_ciphertext, responder.privkey)

    # Check that the responder correctly decodes the auth msg.
    auth_msg_ciphertext = test_values['auth_ciphertext']
    initiator_ephemeral_pubkey, initiator_nonce = responder.decode_authentication(
        auth_msg_ciphertext)
    assert initiator_nonce == test_values['initiator_nonce']
    assert initiator_ephemeral_pubkey == (
        keys.PrivateKey(test_values['initiator_ephemeral_private_key']).public_key)

    # Check that the auth_ack msg generated by the responder is what we expect.
    auth_ack_msg = responder.create_auth_ack_message(test_values['receiver_nonce'])
    assert auth_ack_msg == test_values['authresp_plaintext']

    # Check that the secrets derived from ephemeral key agreements match the expected values.
    auth_ack_ciphertext = test_values['authresp_ciphertext']
    aes_secret, mac_secret, egress_mac, ingress_mac = responder.derive_secrets(
        initiator_nonce, test_values['receiver_nonce'],
        initiator_ephemeral_pubkey, auth_msg_ciphertext, auth_ack_ciphertext)
    assert aes_secret == test_values['aes_secret']
    assert mac_secret == test_values['mac_secret']
    # Test values are from initiator perspective, so they're reversed here.
    assert ingress_mac.digest() == test_values['initial_egress_MAC']
    assert egress_mac.digest() == test_values['initial_ingress_MAC']

    # Check that the initiator secrets match as well.
    responder_ephemeral_pubkey, responder_nonce = initiator.decode_auth_ack_message(
        test_values['authresp_ciphertext'])
    (initiator_aes_secret,
     initiator_mac_secret,
     initiator_egress_mac,
     initiator_ingress_mac) = initiator.derive_secrets(
         initiator_nonce, responder_nonce,
         responder_ephemeral_pubkey, auth_msg_ciphertext, auth_ack_ciphertext)
    assert initiator_aes_secret == aes_secret
    assert initiator_mac_secret == mac_secret
    assert initiator_ingress_mac.digest() == test_values['initial_ingress_MAC']
    assert initiator_egress_mac.digest() == test_values['initial_egress_MAC']

    # Finally, check that two Peers configured with the secrets generated above understand each
    # other.
    responder_reader = asyncio.StreamReader()
    initiator_reader = asyncio.StreamReader()
    # Link the initiator's writer to the responder's reader, and the responder's writer to the
    # initiator's reader.
    responder_writer = type(
        "mock-streamwriter",
        (object,),
        {"write": initiator_reader.feed_data}
    )
    initiator_writer = type(
        "mock-streamwriter",
        (object,),
        {"write": responder_reader.feed_data}
    )
    initiator_peer = BasePeer(
        remote=initiator.remote, privkey=initiator.privkey, reader=initiator_reader,
        writer=initiator_writer, aes_secret=initiator_aes_secret, mac_secret=initiator_mac_secret,
        egress_mac=initiator_egress_mac, ingress_mac=initiator_ingress_mac, chaindb=None,
        network_id=1)
    initiator_peer.base_protocol.send_handshake()
    responder_peer = BasePeer(
        remote=responder.remote, privkey=responder.privkey, reader=responder_reader,
        writer=responder_writer, aes_secret=aes_secret, mac_secret=mac_secret,
        egress_mac=egress_mac, ingress_mac=ingress_mac, chaindb=None, network_id=1)
    responder_peer.base_protocol.send_handshake()

    # The handshake msgs sent by each peer (above) are going to be fed directly into their remote's
    # reader, and thus the read_msg() calls will return immediately.
    responder_hello, _ = await responder_peer.read_msg()
    initiator_hello, _ = await initiator_peer.read_msg()

    assert isinstance(responder_hello, Hello)
    assert isinstance(initiator_hello, Hello)
Exemplo n.º 12
0
async def test_handshake():
    # This data comes from https://gist.github.com/fjl/3a78780d17c755d22df2
    test_values = {
        "initiator_private_key":
        "5e173f6ac3c669587538e7727cf19b782a4f2fda07c1eaa662c593e5e85e3051",
        "receiver_private_key":
        "c45f950382d542169ea207959ee0220ec1491755abe405cd7498d6b16adb6df8",
        "initiator_ephemeral_private_key":
        "19c2185f4f40634926ebed3af09070ca9e029f2edd5fae6253074896205f5f6c",  # noqa: E501
        "receiver_ephemeral_private_key":
        "d25688cf0ab10afa1a0e2dba7853ed5f1e5bf1c631757ed4e103b593ff3f5620",  # noqa: E501
        "auth_plaintext":
        "884c36f7ae6b406637c1f61b2f57e1d2cab813d24c6559aaf843c3f48962f32f46662c066d39669b7b2e3ba14781477417600e7728399278b1b5d801a519aa570034fdb5419558137e0d44cd13d319afe5629eeccb47fd9dfe55cc6089426e46cc762dd8a0636e07a54b31169eba0c7a20a1ac1ef68596f1f283b5c676bae4064abfcce24799d09f67e392632d3ffdc12e3d6430dcb0ea19c318343ffa7aae74d4cd26fecb93657d1cd9e9eaf4f8be720b56dd1d39f190c4e1c6b7ec66f077bb1100",  # noqa: E501
        "authresp_plaintext":
        "802b052f8b066640bba94a4fc39d63815c377fced6fcb84d27f791c9921ddf3e9bf0108e298f490812847109cbd778fae393e80323fd643209841a3b7f110397f37ec61d84cea03dcc5e8385db93248584e8af4b4d1c832d8c7453c0089687a700",  # noqa: E501
        "auth_ciphertext":
        "04a0274c5951e32132e7f088c9bdfdc76c9d91f0dc6078e848f8e3361193dbdc43b94351ea3d89e4ff33ddcefbc80070498824857f499656c4f79bbd97b6c51a514251d69fd1785ef8764bd1d262a883f780964cce6a14ff206daf1206aa073a2d35ce2697ebf3514225bef186631b2fd2316a4b7bcdefec8d75a1025ba2c5404a34e7795e1dd4bc01c6113ece07b0df13b69d3ba654a36e35e69ff9d482d88d2f0228e7d96fe11dccbb465a1831c7d4ad3a026924b182fc2bdfe016a6944312021da5cc459713b13b86a686cf34d6fe6615020e4acf26bf0d5b7579ba813e7723eb95b3cef9942f01a58bd61baee7c9bdd438956b426a4ffe238e61746a8c93d5e10680617c82e48d706ac4953f5e1c4c4f7d013c87d34a06626f498f34576dc017fdd3d581e83cfd26cf125b6d2bda1f1d56",  # noqa: E501
        "authresp_ciphertext":
        "049934a7b2d7f9af8fd9db941d9da281ac9381b5740e1f64f7092f3588d4f87f5ce55191a6653e5e80c1c5dd538169aa123e70dc6ffc5af1827e546c0e958e42dad355bcc1fcb9cdf2cf47ff524d2ad98cbf275e661bf4cf00960e74b5956b799771334f426df007350b46049adb21a6e78ab1408d5e6ccde6fb5e69f0f4c92bb9c725c02f99fa72b9cdc8dd53cff089e0e73317f61cc5abf6152513cb7d833f09d2851603919bf0fbe44d79a09245c6e8338eb502083dc84b846f2fee1cc310d2cc8b1b9334728f97220bb799376233e113",  # noqa: E501
        "ecdhe_shared_secret":
        "e3f407f83fc012470c26a93fdff534100f2c6f736439ce0ca90e9914f7d1c381",
        "initiator_nonce":
        "cd26fecb93657d1cd9e9eaf4f8be720b56dd1d39f190c4e1c6b7ec66f077bb11",
        "receiver_nonce":
        "f37ec61d84cea03dcc5e8385db93248584e8af4b4d1c832d8c7453c0089687a7",
        "aes_secret":
        "c0458fa97a5230830e05f4f20b7c755c1d4e54b1ce5cf43260bb191eef4e418d",
        "mac_secret":
        "48c938884d5067a1598272fcddaa4b833cd5e7d92e8228c0ecdfabbe68aef7f1",
        "token":
        "3f9ec2592d1554852b1f54d228f042ed0a9310ea86d038dc2b401ba8cd7fdac4",
        "initial_egress_MAC":
        "09771e93b1a6109e97074cbe2d2b0cf3d3878efafe68f53c41bb60c0ec49097e",
        "initial_ingress_MAC":
        "75823d96e23136c89666ee025fb21a432be906512b3dd4a3049e898adb433847",
        "initiator_hello_packet":
        "6ef23fcf1cec7312df623f9ae701e63b550cdb8517fefd8dd398fc2acd1d935e6e0434a2b96769078477637347b7b01924fff9ff1c06df2f804df3b0402bbb9f87365b3c6856b45e1e2b6470986813c3816a71bff9d69dd297a5dbd935ab578f6e5d7e93e4506a44f307c332d95e8a4b102585fd8ef9fc9e3e055537a5cec2e9",  # noqa: E501
        "receiver_hello_packet":
        "6ef23fcf1cec7312df623f9ae701e63be36a1cdd1b19179146019984f3625d4a6e0434a2b96769050577657247b7b02bc6c314470eca7e3ef650b98c83e9d7dd4830b3f718ff562349aead2530a8d28a8484604f92e5fced2c6183f304344ab0e7c301a0c05559f4c25db65e36820b4b909a226171a60ac6cb7beea09376d6d8"  # noqa: E501
    }
    for k, v in test_values.items():
        test_values[k] = decode_hex(v)

    initiator_remote = kademlia.Node(
        keys.PrivateKey(test_values['receiver_private_key']).public_key,
        kademlia.Address('0.0.0.0', 0, 0))
    initiator = HandshakeInitiator(
        initiator_remote,
        keys.PrivateKey(test_values['initiator_private_key']))
    initiator.ephemeral_privkey = keys.PrivateKey(
        test_values['initiator_ephemeral_private_key'])

    responder_remote = kademlia.Node(
        keys.PrivateKey(test_values['initiator_private_key']).public_key,
        kademlia.Address('0.0.0.0', 0, 0))
    responder = HandshakeResponder(
        responder_remote, keys.PrivateKey(test_values['receiver_private_key']))
    responder.ephemeral_privkey = keys.PrivateKey(
        test_values['receiver_ephemeral_private_key'])

    # Check that the auth message generated by the initiator is what we expect. Notice that we
    # can't use the auth_init generated here because the non-deterministic prefix would cause the
    # derived secrets to not match the expected values.
    _auth_init = initiator.create_auth_message(test_values['initiator_nonce'])
    assert len(_auth_init) == len(test_values['auth_plaintext'])
    assert _auth_init[65:] == test_values['auth_plaintext'][
        65:]  # starts with non deterministic k

    # Check that encrypting and decrypting the auth_init gets us the orig msg.
    _auth_init_ciphertext = initiator.encrypt_auth_message(_auth_init)
    assert _auth_init == ecies.decrypt(_auth_init_ciphertext,
                                       responder.privkey)

    # Check that the responder correctly decodes the auth msg.
    auth_msg_ciphertext = test_values['auth_ciphertext']
    initiator_ephemeral_pubkey, initiator_nonce = responder.decode_authentication(
        auth_msg_ciphertext)
    assert initiator_nonce == test_values['initiator_nonce']
    assert initiator_ephemeral_pubkey == (keys.PrivateKey(
        test_values['initiator_ephemeral_private_key']).public_key)

    # Check that the auth_ack msg generated by the responder is what we expect.
    auth_ack_msg = responder.create_auth_ack_message(
        test_values['receiver_nonce'])
    assert auth_ack_msg == test_values['authresp_plaintext']

    # Check that the secrets derived from ephemeral key agreements match the expected values.
    auth_ack_ciphertext = test_values['authresp_ciphertext']
    aes_secret, mac_secret, egress_mac, ingress_mac = responder.derive_secrets(
        initiator_nonce, test_values['receiver_nonce'],
        initiator_ephemeral_pubkey, auth_msg_ciphertext, auth_ack_ciphertext)
    assert aes_secret == test_values['aes_secret']
    assert mac_secret == test_values['mac_secret']
    # Test values are from initiator perspective, so they're reversed here.
    assert ingress_mac.digest() == test_values['initial_egress_MAC']
    assert egress_mac.digest() == test_values['initial_ingress_MAC']

    # Check that the initiator secrets match as well.
    responder_ephemeral_pubkey, responder_nonce = initiator.decode_auth_ack_message(
        test_values['authresp_ciphertext'])
    (initiator_aes_secret, initiator_mac_secret, initiator_egress_mac,
     initiator_ingress_mac) = initiator.derive_secrets(
         initiator_nonce, responder_nonce, responder_ephemeral_pubkey,
         auth_msg_ciphertext, auth_ack_ciphertext)
    assert initiator_aes_secret == aes_secret
    assert initiator_mac_secret == mac_secret
    assert initiator_ingress_mac.digest() == test_values['initial_ingress_MAC']
    assert initiator_egress_mac.digest() == test_values['initial_egress_MAC']

    # Finally, check that two Peers configured with the secrets generated above understand each
    # other.
    responder_reader = asyncio.StreamReader()
    initiator_reader = asyncio.StreamReader()
    # Link the initiator's writer to the responder's reader, and the responder's writer to the
    # initiator's reader.
    responder_writer = type("mock-streamwriter", (object, ),
                            {"write": initiator_reader.feed_data})
    initiator_writer = type("mock-streamwriter", (object, ),
                            {"write": responder_reader.feed_data})
    initiator_peer = DummyPeer(remote=initiator.remote,
                               privkey=initiator.privkey,
                               reader=initiator_reader,
                               writer=initiator_writer,
                               aes_secret=initiator_aes_secret,
                               mac_secret=initiator_mac_secret,
                               egress_mac=initiator_egress_mac,
                               ingress_mac=initiator_ingress_mac,
                               chaindb=None,
                               network_id=1)
    initiator_peer.base_protocol.send_handshake()
    responder_peer = DummyPeer(remote=responder.remote,
                               privkey=responder.privkey,
                               reader=responder_reader,
                               writer=responder_writer,
                               aes_secret=aes_secret,
                               mac_secret=mac_secret,
                               egress_mac=egress_mac,
                               ingress_mac=ingress_mac,
                               chaindb=None,
                               network_id=1)
    responder_peer.base_protocol.send_handshake()

    # The handshake msgs sent by each peer (above) are going to be fed directly into their remote's
    # reader, and thus the read_msg() calls will return immediately.
    responder_hello, _ = await responder_peer.read_msg()
    initiator_hello, _ = await initiator_peer.read_msg()

    assert isinstance(responder_hello, Hello)
    assert isinstance(initiator_hello, Hello)
Exemplo n.º 13
0
def _decrypt(private_key: bytes, msg: bytes):
    priv_key = keys.PrivateKey(codecs.decode(private_key, 'hex'))
    e = ecies.decrypt(msg, priv_key, shared_mac_data=SHARED_MAC_DATA)
    return e.decode(encoding='utf-8')