Skip to content

Key ID and Key Derivation

krisjanisp edited this page Oct 9, 2026 · 9 revisions

Key ID and key derivation

This page describes encrypted key IDs, format revision 7. The repository format specification is maintained alongside the implementation.

Both stubs need the same 32-byte PSK and SAE numeric assignments. The public ID is a single AES-256 ciphertext block in lowercase, hyphenated UUID notation. Uppercase input is accepted. Its version/variant fields match UUIDv4, but its collision budget is smaller than an ordinary UUIDv4 generator's 122 random bits.

For the computational key-secrecy claim, assumptions and conditional proof sketch, see Computational security without QKD. SHA preimage resistance alone is not the security argument for this HKDF construction.

Plaintext layout

Current v7 plaintext and encrypted public ID layout

Download the editable three-page draw.io source.

Two-byte integers are unsigned big-endian; offsets are zero-based.

Bytes Contents
0–1 Key length in bytes, 1–8192
2–3 Master SAE code
4–5 Slave SAE code
6–14 72 OS-random bits, with no reserved version/variant bits
15 One-byte keyed checksum

The server binary always issues with the nonzero codes of the authenticated SAE pair and checks the decrypted codes on retrieval against the certificate's slave SAE and the URL's master SAE. Only the library's registry-less mode (used by Rust API tests, not reachable from the CLI) issues zero for both SAE codes.

Exact derivation

Current v7 AES and HKDF key derivation

Concatenation is denoted by $\parallel$; slices exclude the upper bound. Labels are ASCII, with a trailing zero byte ($\texttt{0x00}$). All cryptographic inputs are raw bytes, not UUID text. Let $P$ be the 16-byte plaintext, $C$ the ciphertext, and $L$ the key length in bytes encoded in $P[0..2]$.

For readability, $\mathrm{Extract}$ denotes HKDF-Extract-SHA512 (salt, IKM) and $\mathrm{Expand}$ denotes HKDF-Expand-SHA512 (PRK, info, output length in bytes).

$$ \begin{aligned} \mathrm{PRK} &= \mathrm{Extract}(\texttt{qkd-stub:psk} \parallel \texttt{0x00},, \mathrm{PSK}), \\ K_{\mathrm{AES}} &= \mathrm{Expand}(\mathrm{PRK},, \texttt{qkd-stub:id-encryption:v7} \parallel \texttt{0x00},, 32), \\ P[15] &= \mathrm{Expand}(\mathrm{PRK},, \texttt{qkd-stub:id-check:v7} \parallel \texttt{0x00} \parallel P[0..15],, 1)[0], \\ C &= \mathrm{AES\text{-}256\text{-}Encrypt\text{-}Block}(K_{\mathrm{AES}},, P), \\ K(C,L) &= \mathrm{Expand}(\mathrm{PRK},, \texttt{qkd-stub:key:v7} \parallel \texttt{0x00} \parallel C,, L). \end{aligned} $$

Accept $C$ only if (C[6] >> 4) == 4 and (C[8] >> 6) == 2. Otherwise replace all nine random bytes, recompute the checksum, and encrypt again. Never overwrite ciphertext bits. Acceptance probability is approximately $1/64$, with 64 attempts on average. After 4096 unsuccessful attempts issuance fails (HTTP 503); the idealized probability is about $10^{-28}$ per ID.

Retrieval validates the external representation and version/variant, decrypts once, validates length and checksum, then performs SAE authorization. Invalid IDs return HTTP 400; valid IDs for another pair return HTTP 401. Batches remain atomic. Keys are derived from the accepted ciphertext, including all 128 bits.

Limits and migration

Filtering leaves approximately $2^{66}$ accepted IDs per fixed pair and length, assuming AES behaves as a pseudorandom permutation. Thus collisions reach birthday scale around $2^{33}$ issuances for that pair/length; this does not provide the collision budget of ordinary UUIDv4. Reclaiming the six internal UUID bits compensates for the six bits consumed by filtering. Repeated plaintext produces repeated ciphertext and the same key.

The checksum is not authentication or proof of issuance. Conditional on a plausible decrypted length, a wrong PSK passes this 8-bit check with probability about $1/256$; length validation rejects additional wrong-key decryptions.

Authorized repeat retrieval of the same ID returns the same key while the PSK remains unchanged, including after a restart. This supports retries; retrieval does not consume the ID, and IDs do not expire. The stub keeps no issuance history.

PSK-derived keys do not have forward secrecy. PSK holders can recover metadata and keys; observers may still learn communication partners through endpoints, request paths or traffic patterns.

Previous plaintext UUIDv8 IDs and older formats are rejected. Upgrade both servers and obtain new IDs. Revision v7 in labels is unrelated to UUIDv7. The older UUID layout and key-derivation PNGs are historical, not this format.

Independent test vectors

PSK: 32 bytes of 07. Each vector has a 32-byte output key. The Python HTTPS test reproduces these using OpenSSL AES and Python HMAC.

Plaintext (hex) Public ID Key (Base64)
0020000000000000000000000000070e ab531e5f-28fd-4f93-b7cf-f49a723babac 29dcgpL9WDwAf/0CfUUAm5JptfBv9w7mr7mvu0ZYfo8=
00200001000200000000000000002807 0a142bd6-b238-400c-9bf3-3dc2cdef20fd GuyWf9LhOQDfzpjOFM9DVYYa2UWRk3b5RtZchFiK1jE=

Historical packet-layout diagram (v6)

The original diagram is preserved below for reference. It describes the previous plaintext UUIDv8 format, not the current encrypted v7 format described above.

Historical v6 key-ID packet layout

The original editable draw.io poster contains the diagram source embedded in the PNG. Download that file and open it in draw.io to edit it.

Clone this wiki locally