노트북 02 — 하이브리드 X3DH 키 합의#
새로 생성한 두 신원(identity) 사이에서 하이브리드 핸드셰이크를 실행하고, 양쪽이 동일한 96바이트의 키 자료를 유도하는지 확인합니다.
from pqmsg.identity import generate_identity
from pqmsg.session import initiate_session, accept_session, Handshake
단계 1 — 신원 생성#
alice = generate_identity("alice")
bob = generate_identity("bob")
print("alice X25519 pub (first 8B):", alice.x25519_public[:8].hex())
print("bob X25519 pub (first 8B):", bob.x25519_public[:8].hex())
print("alice ML-KEM pub size :", len(alice.ml_kem_public))
print("bob ML-KEM pub size :", len(bob.ml_kem_public))
alice X25519 pub (first 8B): 36f7fc3d734b1626
bob X25519 pub (first 8B): 90a17827bd896256
alice ML-KEM pub size : 1184
bob ML-KEM pub size : 1184
단계 2 — Alice가 세션 개시#
initiate_session은 X25519 DH, ML-KEM 캡슐화, 그리고 HKDF 유도를 수행합니다.
sess_a, handshake = initiate_session(
ours=alice, peer_name=bob.name,
peer_x25519_pub=bob.x25519_public,
peer_ml_kem_pub=bob.ml_kem_public,
)
print("root_key (first 8B): ", sess_a.root_key[:8].hex())
print("chain_key_send (first 8B): ", sess_a.chain_key_send[:8].hex())
print("chain_key_recv (first 8B): ", sess_a.chain_key_recv[:8].hex())
print("ephemeral_pk size: ", len(handshake.ephemeral_pk))
print("kem_ciphertext size: ", len(handshake.kem_ciphertext))
root_key (first 8B): 43d31a03c0a3d06a
chain_key_send (first 8B): 98f3dc07eb3f2e7a
chain_key_recv (first 8B): b154502f31803a3a
ephemeral_pk size: 32
kem_ciphertext size: 1088
단계 3 — Bob이 세션 수락#
accept_session은 Bob의 개인키와 Alice가 보낸 핸드셰이크 필드를 사용합니다.
sess_b = accept_session(
ours=bob, peer_name=alice.name,
peer_x25519_pub=alice.x25519_public,
handshake=handshake,
)
print("root_key match: ", sess_a.root_key == sess_b.root_key)
print("chain_a2b mirrored: ", sess_a.chain_key_send == sess_b.chain_key_recv)
print("chain_b2a mirrored: ", sess_a.chain_key_recv == sess_b.chain_key_send)
root_key match: True
chain_a2b mirrored: True
chain_b2a mirrored: True
두 줄 모두가 성공해야 하는 이유#
두 체인 키는 방향성을 가집니다: 하나는 Alice→Bob용, 다른 하나는 Bob→Alice용입니다. 이 핸드셰이크 이후, 양 당사자는 독립적으로 암호화하고 상대방은 복호화할 수 있습니다. 다음 노트북에서는 대칭 래칫을 10단계 굴려서 전방 비밀성을 보여주고 — 어디에서 멈추는지도 함께 살펴봅니다.