from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
import struct
class AuthenticatedECDHExchange:
def __init__(self, curve: ec.EllipticCurve, my_auth_private_key: ec.EllipticCurvePrivateKey, peers_auth_public_key: ec.EllipticCurvePublicKey):
self._curve = curve
# Generate an ephemeral private key for use in the exchange.
self._private_key = ec.generate_private_key(
curve, default_backend())
self.enc_key = None
self.mac_key = None
# long term keys used for authentication.
self._my_auth_private_key = my_auth_private_key
self._peers_auth_public_key = peers_auth_public_key
def get_signed_public_bytes(self) -> bytes:
= self._private_key.public_key()
public_key
= public_key.public_bytes(
raw_bytes =serialization.Encoding.PEM,
encodingformat=serialization.PublicFormat.SubjectPublicKeyInfo)
# This is a signature to prove who we are.
= self._my_auth_private_key.sign(
signature = raw_bytes,
data =ec.ECDSA(hashes.SHA256())
signature_algorithm
)
return struct.pack('I', len(signature)) + raw_bytes + signature
def generate_authenticated_session_key(self, signed_peer_bytes: bytes) -> None:
# let l be the length of I, unsigned int, probably equal to 4.
= struct.calcsize('I')
l
= struct.unpack('I', signed_peer_bytes[:l])
len_of_signature, = signed_peer_bytes[l:(-len_of_signature)], signed_peer_bytes[(-len_of_signature):]
peer_bytes, signature
self._peers_auth_public_key.verify(
=signature,
signature=peer_bytes,
data=ec.ECDSA(hashes.SHA256()),
signature_algorithm
)
= serialization.load_pem_public_key(
peer_public_key
peer_bytes,=default_backend())
backend= self._private_key.exchange(
shared_key
ec.ECDH(),
peer_public_key)
# derive 64 bytes of key material for 2 32-byte keys
= HKDF(
key_material =hashes.SHA256(),
algorithm=64,
length=None,
salt=None,
info=default_backend()).derive(shared_key)
backend
# get the encryption key
self.enc_key = key_material[:32]
# derive an MAC key
self.mac_key = key_material[32:64]
6.9 ECDH LEFT TO THE READER
EXERCISE 6.9 ECDH LEFT TO THE READER
We did not show code for verifying the public parameters received in the
AuthenticatedECDHExchange
class. Luckily for you, we’ve left it as an exercise to the reader! Update thegenerate_session_key
method to begenerate_authenticated_session_key
. This method should implement the algorithm previously described for getting the signature length, verifying the signature using a public key, and then deriving the session keys.
Let’s generate a private and public ECDSA keys for Alice.
= ec.generate_private_key(curve=ec.SECP384R1(), backend=default_backend())
alice_private_key = alice_private_key.public_key() alice_public_key
Let’s do the same thing for Bob.
= ec.generate_private_key(curve=ec.SECP384R1(), backend=default_backend())
bob_private_key = bob_private_key.public_key() bob_public_key
Assume they know each other’s public key. That is, Alice knows Bob’s public key and vice versa.
Now, let them exchange keys with ECDH:
= AuthenticatedECDHExchange(
alice_ecdh =ec.SECP384R1(),
curve=alice_private_key,
my_auth_private_key=bob_public_key,
peers_auth_public_key )
= AuthenticatedECDHExchange(
bob_ecdh =ec.SECP384R1(),
curve=bob_private_key,
my_auth_private_key=alice_public_key,
peers_auth_public_key )
alice_ecdh.generate_authenticated_session_key(bob_ecdh.get_signed_public_bytes()) bob_ecdh.generate_authenticated_session_key(alice_ecdh.get_signed_public_bytes())
if alice_ecdh.enc_key and alice_ecdh.mac_key and alice_ecdh.enc_key == bob_ecdh.enc_key and alice_ecdh.mac_key == bob_ecdh.mac_key:
print("[PASS]")
else:
print("[FAIL]")
[PASS]
print("SessionKeys")
print("-----------")
print(f"Encryption Key: {alice_ecdh.enc_key.hex(' ')}")
print(f"MAC Key: {alice_ecdh.mac_key.hex(' ')}")
SessionKeys
-----------
Encryption Key: 4b fe 71 65 d2 29 68 39 94 53 4d 9c 47 5b 62 24 ab 14 3e f5 d0 1a c0 22 fc cd f0 b0 08 9d ff 80
MAC Key: ff 91 1e 07 8b 10 bd 4a a3 de b5 a8 c6 ad 5e d9 e2 23 88 f1 13 65 37 6b e2 6a 73 2e 6a c1 8a 52