Consistent capitalization in comments
This commit is contained in:
1 parent
f035859fd2
commit
10b64cbf52
1 file changed
+32
-32
+32
-32
@@ -23,11 +23,11 @@ from .chab3 import decrypt, encrypt, random
|
||||
from .exceptions import _Burn
|
||||
from .kdf import kdf
|
||||
|
||||
# -- fixed-size record layouts (struct handles packing + length checks) -----
|
||||
# hybrid scheme = ML-KEM-768 (pk 1184 / sk-seed 64 / ct 1088) || X25519 (32)
|
||||
_TAG = struct.Struct("32s") # bare kdf tag
|
||||
# -- Fixed-size record layouts (struct handles packing + length checks) -----
|
||||
# Hybrid scheme = ML-KEM-768 (pk 1184 / sk-seed 64 / ct 1088) || X25519 (32)
|
||||
_TAG = struct.Struct("32s") # Bare kdf tag
|
||||
|
||||
# handshake message bodies; box(N) is a chab3 box of N plaintext bytes (N+28)
|
||||
# Handshake message bodies; box(N) is a chab3 box of N plaintext bytes (N+28)
|
||||
_MSG1_REGISTER = struct.Struct("1184s32s32s") # e_alice_m | e_alice_x | commitment
|
||||
_MSG2_REGISTER = struct.Struct(
|
||||
"32s1088s1244s"
|
||||
@@ -47,7 +47,7 @@ _MSG2_RESUME = struct.Struct("32s1088s1088s32s") # e_bob | sa_ct | ee_ct | tb
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# hybrid KEM: ML-KEM-768 || X25519
|
||||
# Hybrid KEM: ML-KEM-768 || X25519
|
||||
#
|
||||
# A static private key is a single 32-byte idroot secret: each scheme's
|
||||
# key is derived from it with one domain-separated kdf call, so the two
|
||||
@@ -55,8 +55,8 @@ _MSG2_RESUME = struct.Struct("32s1088s1088s32s") # e_bob | sa_ct | ee_ct | tb
|
||||
# held (and the only thing the credential stores). Ephemeral keys are
|
||||
# generated directly -- nothing of them persists.
|
||||
#
|
||||
# Each party ships one X25519 ephemeral public per connection (alice's
|
||||
# inside e_alice in msg1, bob's as e_bob in msg2); every X25519 leg is a
|
||||
# Each party ships one X25519 ephemeral public per connection (Alice's
|
||||
# inside e_alice in msg1, Bob's as e_bob in msg2); every X25519 leg is a
|
||||
# plain DH against those shipped keys. ML-KEM legs ship as bare ML-KEM
|
||||
# ciphertexts.
|
||||
|
||||
@@ -102,13 +102,13 @@ def _check(ok: bool, why: str) -> None:
|
||||
|
||||
|
||||
def open_hello(msg1: bytes, *, shared: bytes, ad: bytes, flow: str) -> bytes:
|
||||
# open a msg1 box under a candidate credential's shared key (the
|
||||
# Open a msg1 box under a candidate credential's shared key (the
|
||||
# responder's caller scan); ValueError = not this credential
|
||||
return decrypt(msg1, key=kdf(flow + "/msg1", shared), ad=ad)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# ad-chained boxes: each box's associated data is the previous box's tag
|
||||
# AD-chained boxes: each box's associated data is the previous box's tag
|
||||
|
||||
|
||||
class _Chain:
|
||||
@@ -126,14 +126,14 @@ class _Chain:
|
||||
return plain
|
||||
|
||||
def advance(self, msg: bytes) -> None:
|
||||
# a box decrypted outside the chain (the responder's caller scan)
|
||||
# A box decrypted outside the chain (the responder's caller scan)
|
||||
self._ad = msg[-16:]
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# the two exchanges. Stage names carry the acting party:
|
||||
# alice drives with startA/respondA (+ confirmA when registering),
|
||||
# bob with respondB (+ confirmB/finishB when registering, completeB
|
||||
# The two exchanges. Stage names carry the acting party:
|
||||
# Alice drives with startA/respondA (+ confirmA when registering),
|
||||
# Bob with respondB (+ confirmB/finishB when registering, completeB
|
||||
# when resuming)
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ class Registration:
|
||||
def __init__(self, shared: bytes, idroot: bytes, ad: bytes):
|
||||
self._shared = shared
|
||||
self._idroot = idroot
|
||||
# static key objects, derived once from the persisted idroot
|
||||
# Static key objects, derived once from the persisted idroot
|
||||
self._mlkem = _mlkem_private_key(idroot)
|
||||
self._x25519 = _x25519_private_key(idroot)
|
||||
self._static_m, self._static_x = _public_from_idroot(idroot)
|
||||
@@ -168,7 +168,7 @@ class Registration:
|
||||
return self._chain.encrypt(body, key=kdf("reg/msg1", self._shared))
|
||||
|
||||
def respondA(self, msg2: bytes) -> bytes:
|
||||
# open bob's reply -- an outer AEAD failure burns nothing, it
|
||||
# Open Bob's reply -- an outer AEAD failure burns nothing, it
|
||||
# proves no key knowledge -- then challenge his static
|
||||
plain = self._chain.decrypt(msg2, key=kdf("reg/msg2", self._shared))
|
||||
try:
|
||||
@@ -176,11 +176,11 @@ class Registration:
|
||||
# ee is forward secrecy, made to our connection key only
|
||||
ee = self._eph_m.decapsulate(ee_ct)
|
||||
ee += self._ephemeral.exchange(X25519PublicKey.from_public_bytes(e_bob))
|
||||
# solving ee lets us read bob's static from the inner box
|
||||
# Solving ee lets us read Bob's static from the inner box
|
||||
s_bob_m, s_bob_x = _MSG2_REGISTER_INNER.unpack(
|
||||
decrypt(inner, key=kdf("reg/msg2/inner", ee))
|
||||
)
|
||||
# we challenge his static: only the real bob can solve this
|
||||
# We challenge his static: only the real Bob can solve this
|
||||
es, es_ct = MLKEM768PublicKey.from_public_bytes(s_bob_m).encapsulate()
|
||||
es += self._ephemeral.exchange(X25519PublicKey.from_public_bytes(s_bob_x))
|
||||
except (struct.error, ValueError) as e:
|
||||
@@ -199,12 +199,12 @@ class Registration:
|
||||
)
|
||||
|
||||
def confirmA(self, msg4: bytes) -> tuple[bytes, bytes]:
|
||||
# bob's challenge to our static plus his root confirmation. The
|
||||
# Bob's challenge to our static plus his root confirmation. The
|
||||
# caller gates the answer (msg5) on the out-of-band comparison.
|
||||
plain = self._chain.decrypt(msg4, key=kdf("reg/msg4", self._shared, self._ee))
|
||||
try:
|
||||
se_ct, tb = _MSG4_REGISTER.unpack(plain)
|
||||
# solving se proves to bob that we hold alice's static
|
||||
# Solving se proves to Bob that we hold Alice's static
|
||||
se = self._mlkem.decapsulate(se_ct)
|
||||
se += self._x25519.exchange(X25519PublicKey.from_public_bytes(self._e_bob))
|
||||
except (struct.error, ValueError) as e:
|
||||
@@ -225,13 +225,13 @@ class Registration:
|
||||
|
||||
def respondB(self, msg1: bytes, opened: bytes) -> bytes:
|
||||
# msg2 from an opened msg1 (open_hello did the caller scan); our
|
||||
# connection key ships here once, alice answers every leg with it
|
||||
# connection key ships here once, Alice answers every leg with it
|
||||
self._chain.advance(msg1)
|
||||
try:
|
||||
e_alice_m, e_alice_x, commitment = _MSG1_REGISTER.unpack(opened)
|
||||
self._ephemeral = X25519PrivateKey.generate()
|
||||
e_bob = self._ephemeral.public_key().public_bytes_raw()
|
||||
# forward secrecy, made to alice's connection key only
|
||||
# Forward secrecy, made to Alice's connection key only
|
||||
ee, ee_ct = MLKEM768PublicKey.from_public_bytes(e_alice_m).encapsulate()
|
||||
ee += self._ephemeral.exchange(X25519PublicKey.from_public_bytes(e_alice_x))
|
||||
except (struct.error, ValueError) as e:
|
||||
@@ -249,7 +249,7 @@ class Registration:
|
||||
return self._chain.encrypt(body, key=kdf("reg/msg2", self._shared))
|
||||
|
||||
def confirmB(self, msg3: bytes) -> tuple[bytes, bytes]:
|
||||
# alice's challenge to our static, her static and her reveal. The
|
||||
# Alice's challenge to our static, her static and her reveal. The
|
||||
# caller gates the answer (msg4) on the out-of-band comparison.
|
||||
plain = self._chain.decrypt(msg3, key=kdf("reg/msg3", self._shared, self._ee))
|
||||
try:
|
||||
@@ -285,7 +285,7 @@ class Registration:
|
||||
return msg4, sas
|
||||
|
||||
def finishB(self, msg5: bytes) -> None:
|
||||
# alice's root confirmation: proves she solved our se challenge
|
||||
# Alice's root confirmation: proves she solved our se challenge
|
||||
plain = self._chain.decrypt(
|
||||
msg5, key=kdf("reg/msg5", self._shared, self._ee, self._es)
|
||||
)
|
||||
@@ -311,7 +311,7 @@ class Registration:
|
||||
|
||||
class Resumption:
|
||||
"""Resumption exchange: statics proven by decapsulation, never on
|
||||
the wire; all legs hybrid. Alice: startA/respondA; bob:
|
||||
the wire; all legs hybrid. Alice: startA/respondA; Bob:
|
||||
respondB/completeB; finish() -> (send, recv)."""
|
||||
|
||||
def __init__(self, shared: bytes, idroot: bytes, peer_pub: tuple, ad: bytes):
|
||||
@@ -325,7 +325,7 @@ class Resumption:
|
||||
# -- alice ----------------------------------------------------------------
|
||||
|
||||
def startA(self) -> bytes:
|
||||
# msg1: our connection keys plus a challenge to bob's static
|
||||
# msg1: our connection keys plus a challenge to Bob's static
|
||||
self._eph_m, self._ephemeral, e_alice_m, e_alice_x = _fresh_ephemeral()
|
||||
peer_m, peer_x = self._peer_pub
|
||||
sb, sb_ct = MLKEM768PublicKey.from_public_bytes(peer_m).encapsulate()
|
||||
@@ -335,11 +335,11 @@ class Resumption:
|
||||
return self._chain.encrypt(body, key=kdf("res/msg1", self._shared))
|
||||
|
||||
def respondA(self, msg2: bytes) -> bytes:
|
||||
# open bob's reply and solve his three challenges
|
||||
# Open Bob's reply and solve his three challenges
|
||||
plain = self._chain.decrypt(msg2, key=kdf("res/msg2", self._shared, self._sb))
|
||||
try:
|
||||
e_bob, sa_ct, ee_ct, tb = _MSG2_RESUME.unpack(plain)
|
||||
# solving sa proves to bob that we hold alice's static
|
||||
# Solving sa proves to Bob that we hold Alice's static
|
||||
sa = self._mlkem.decapsulate(sa_ct)
|
||||
sa += self._x25519.exchange(X25519PublicKey.from_public_bytes(e_bob))
|
||||
# ee is forward secrecy, made to our connection key
|
||||
@@ -361,20 +361,20 @@ class Resumption:
|
||||
# -- bob ------------------------------------------------------------------
|
||||
|
||||
def respondB(self, msg1: bytes, opened: bytes) -> bytes:
|
||||
# prove our static, challenge alice's, forward secrecy
|
||||
# Prove our static, challenge Alice's, forward secrecy
|
||||
self._chain.advance(msg1)
|
||||
try:
|
||||
e_alice_m, e_alice_x, sb_ct = _MSG1_RESUME.unpack(opened)
|
||||
# solving sb proves to alice that we hold bob's static
|
||||
# Solving sb proves to Alice that we hold Bob's static
|
||||
sb = self._mlkem.decapsulate(sb_ct)
|
||||
sb += self._x25519.exchange(X25519PublicKey.from_public_bytes(e_alice_x))
|
||||
self._ephemeral = X25519PrivateKey.generate()
|
||||
e_bob = self._ephemeral.public_key().public_bytes_raw()
|
||||
peer_m, peer_x = self._peer_pub
|
||||
# we challenge alice's static the same way
|
||||
# We challenge Alice's static the same way
|
||||
sa, sa_ct = MLKEM768PublicKey.from_public_bytes(peer_m).encapsulate()
|
||||
sa += self._ephemeral.exchange(X25519PublicKey.from_public_bytes(peer_x))
|
||||
# and make forward secrecy to her connection key
|
||||
# And make forward secrecy to her connection key
|
||||
ee, ee_ct = MLKEM768PublicKey.from_public_bytes(e_alice_m).encapsulate()
|
||||
ee += self._ephemeral.exchange(X25519PublicKey.from_public_bytes(e_alice_x))
|
||||
except (struct.error, ValueError) as e:
|
||||
@@ -386,7 +386,7 @@ class Resumption:
|
||||
return self._chain.encrypt(body, key=kdf("res/msg2", self._shared, sb))
|
||||
|
||||
def completeB(self, msg3: bytes) -> None:
|
||||
# alice answers with just her tag
|
||||
# Alice answers with just her tag
|
||||
plain = self._chain.decrypt(
|
||||
msg3, key=kdf("res/msg3", self._shared, self._sb, self._sa, self._ee)
|
||||
)
|
||||
|
||||
Reference in new issue
Block a user