Source code for rfc9180.helpers
from .utils import I2OSP, concat
[docs]
def append_header(
enc: bytes, ct: bytes, kem_id: int, kdf_id: int, aead_id: int, mode: int
) -> bytes:
"""
Encode an HPKE message with a self-describing header.
Format: "HPKE" | kem_id(2) | kdf_id(2) | aead_id(2) | mode(1) | enc | ct
Parameters
----------
enc : bytes
Encapsulated public key.
ct : bytes
Ciphertext.
kem_id : int
KEM algorithm identifier (2 bytes).
kdf_id : int
KDF algorithm identifier (2 bytes).
aead_id : int
AEAD algorithm identifier (2 bytes).
mode : int
HPKE mode identifier (1 byte).
Returns
-------
bytes
Complete HPKE message with header.
"""
header = concat(
b"HPKE",
I2OSP(kem_id, 2),
I2OSP(kdf_id, 2),
I2OSP(aead_id, 2),
I2OSP(mode, 1),
)
return header + enc + ct
[docs]
def parse_header(msg: bytes, enc_len: int) -> tuple[int, int, int, int, bytes, bytes]:
"""
Parse a self-describing HPKE message.
Parameters
----------
msg : bytes
Complete HPKE message with header.
enc_len : int
Length of the encapsulated public key (Nenc) for the specific KEM.
Returns
-------
tuple
Tuple of (kem_id, kdf_id, aead_id, mode, enc, ct).
Raises
------
ValueError
If message is too short or has invalid magic bytes.
"""
if len(msg) < 4 + 2 + 2 + 2 + 1:
raise ValueError("Message too short for header")
magic = msg[:4]
if magic != b"HPKE":
raise ValueError("Invalid magic bytes")
offset = 4
kem_id = int.from_bytes(msg[offset : offset + 2], "big")
offset += 2
kdf_id = int.from_bytes(msg[offset : offset + 2], "big")
offset += 2
aead_id = int.from_bytes(msg[offset : offset + 2], "big")
offset += 2
mode = int.from_bytes(msg[offset : offset + 1], "big")
offset += 1
if len(msg) < offset + enc_len:
raise ValueError("Message too short for enc")
enc = msg[offset : offset + enc_len]
ct = msg[offset + enc_len :]
return kem_id, kdf_id, aead_id, mode, enc, ct