Esempio n. 1
0
 def validate_compressed_public_key(cls, public_key: bytes) -> None:
     try:
         PublicKey.from_compressed_bytes(public_key)
     except (EthKeysValidationError, ValueError) as error:
         raise ValidationError(
             f"Public key {encode_hex(public_key)} is invalid compressed public key: {error}"
         ) from error
Esempio n. 2
0
    def complete_handshake(self, response_packet: Packet) -> HandshakeResult:
        if not self.is_response_packet(response_packet):
            raise ValueError(
                f"Packet {response_packet} is not the expected response packet"
            )
        if not isinstance(response_packet, WhoAreYouPacket):
            raise TypeError("Invariant: Only WhoAreYou packets are valid responses")
        who_are_you_packet = response_packet

        # compute session keys
        (
            ephemeral_private_key,
            ephemeral_public_key,
        ) = self.handshake_scheme.create_handshake_key_pair()

        remote_public_key_object = PublicKey.from_compressed_bytes(
            self.remote_enr.public_key
        )
        remote_public_key_uncompressed = remote_public_key_object.to_bytes()
        session_keys = self.handshake_scheme.compute_session_keys(
            local_private_key=ephemeral_private_key,
            remote_public_key=remote_public_key_uncompressed,
            local_node_id=self.local_enr.node_id,
            remote_node_id=self.remote_node_id,
            id_nonce=who_are_you_packet.id_nonce,
            is_locally_initiated=True,
        )

        # prepare response packet
        id_nonce_signature = self.handshake_scheme.create_id_nonce_signature(
            id_nonce=who_are_you_packet.id_nonce,
            ephemeral_public_key=ephemeral_public_key,
            private_key=self.local_private_key,
        )

        enr: Optional[ENRAPI]

        if who_are_you_packet.enr_sequence_number < self.local_enr.sequence_number:
            enr = self.local_enr
        else:
            enr = None

        auth_header_packet = AuthHeaderPacket.prepare(
            tag=self.tag,
            auth_tag=get_random_auth_tag(),
            id_nonce=who_are_you_packet.id_nonce,
            message=self.initial_message,
            initiator_key=session_keys.encryption_key,
            id_nonce_signature=id_nonce_signature,
            auth_response_key=session_keys.auth_response_key,
            enr=enr,
            ephemeral_public_key=ephemeral_public_key,
        )

        return HandshakeResult(
            session_keys=session_keys,
            enr=None,
            message=None,
            auth_header_packet=auth_header_packet,
        )
Esempio n. 3
0
    def validate_signature(cls, enr: "ENR") -> None:
        public_key = PublicKey.from_compressed_bytes(
            enr[cls.public_key_enr_key])
        message = enr.get_signing_message()

        try:
            signature = NonRecoverableSignature(enr.signature)
        except BadSignature:
            is_valid = False
        else:
            is_valid = signature.verify_msg(message, public_key)

        if not is_valid:
            raise ValidationError("Invalid signature")
Esempio n. 4
0
    def validate_signature(cls, *, message: bytes, signature: bytes,
                           public_key: bytes) -> None:
        public_key_object = PublicKey.from_compressed_bytes(public_key)

        try:
            signature_object = NonRecoverableSignature(signature)
        except BadSignature:
            is_valid = False
        else:
            is_valid = signature_object.verify_msg(message, public_key_object)

        if not is_valid:
            raise ValidationError(
                f"Signature {encode_hex(signature)} is not valid for message {encode_hex(message)} "
                f"and public key {encode_hex(public_key)}")
Esempio n. 5
0
    def compute_session_keys(cls, *, local_private_key: bytes,
                             remote_public_key: bytes, local_node_id: NodeID,
                             remote_node_id: NodeID, id_nonce: IDNonce,
                             is_locally_initiated: bool) -> SessionKeys:
        local_private_key_object = PrivateKey(local_private_key)
        remote_public_key_object = PublicKey.from_compressed_bytes(
            remote_public_key)
        secret = ecdh_agree(local_private_key_object, remote_public_key_object)

        if is_locally_initiated:
            initiator_node_id, recipient_node_id = local_node_id, remote_node_id
        else:
            initiator_node_id, recipient_node_id = remote_node_id, local_node_id

        info = b"".join((
            HKDF_INFO,
            initiator_node_id,
            recipient_node_id,
        ))

        hkdf = HKDF(
            algorithm=SHA256(),
            length=3 * AES128_KEY_SIZE,
            salt=id_nonce,
            info=info,
            backend=cls.cryptography_backend,
        )
        expanded_key = hkdf.derive(secret)

        if len(expanded_key) != 3 * AES128_KEY_SIZE:
            raise Exception(
                "Invariant: Secret is expanded to three AES128 keys")

        initiator_key = expanded_key[:AES128_KEY_SIZE]
        recipient_key = expanded_key[AES128_KEY_SIZE:2 * AES128_KEY_SIZE]
        auth_response_key = expanded_key[2 * AES128_KEY_SIZE:3 *
                                         AES128_KEY_SIZE]

        if is_locally_initiated:
            encryption_key, decryption_key = initiator_key, recipient_key
        else:
            encryption_key, decryption_key = recipient_key, initiator_key

        return SessionKeys(
            encryption_key=AES128Key(encryption_key),
            decryption_key=AES128Key(decryption_key),
            auth_response_key=AES128Key(auth_response_key),
        )
Esempio n. 6
0
def ecdh_agree(private_key: bytes, public_key: bytes) -> bytes:
    """
    Perform the ECDH key agreement.

    The public key is expected in uncompressed format and the resulting secret point will be
    formatted as a 0x02 or 0x03 prefix (depending on the sign of the secret's y component)
    followed by 32 bytes of the x component.
    """
    # We cannot use `cryptography.hazmat.primitives.asymmetric.ec.ECDH only gives us the x
    # component of the shared secret point, but we need both x and y.
    if len(public_key) == 33:
        public_key_eth_keys = PublicKey.from_compressed_bytes(public_key)
    else:
        public_key_eth_keys = PublicKey(public_key)
    public_key_compressed = public_key_eth_keys.to_compressed_bytes()
    public_key_coincurve = coincurve.keys.PublicKey(public_key_compressed)
    secret_coincurve = public_key_coincurve.multiply(private_key)
    return secret_coincurve.format()  # type: ignore
Esempio n. 7
0
 def extract_node_id(cls, enr: CommonENRAPI) -> NodeID:
     public_key_object = PublicKey.from_compressed_bytes(enr.public_key)
     uncompressed_bytes = public_key_object.to_bytes()
     return NodeID(keccak(uncompressed_bytes))
Esempio n. 8
0
 def extract_node_address(cls, enr: "ENR") -> bytes:
     public_key = PublicKey.from_compressed_bytes(
         enr[cls.public_key_enr_key])
     return keccak(public_key.to_bytes())