crypto::prf
The IEEE 802.11 PRF, over HMAC-SHA1.
Where the pairwise transient key comes from. Once the PMK is known — from
wpa_psk — the 4-way handshake mixes it with both
MAC addresses and both nonces through this PRF to produce the PTK, from
which the key-encryption key, the key-confirmation key and the temporal
(data) key are sliced. sha1_prf in the adapter’s crypto table.
Byte layout, taken from the reference not from memory
Section titled “Byte layout, taken from the reference not from memory”The exact input to each HMAC is label ‖ NUL ‖ data ‖ counter, where the
label’s length includes its NUL terminator and the counter is a single
byte starting at zero and incrementing per 20-byte output block. That is
read directly from esp-idf’s sha1-prf.c (components/wpa_supplicant),
which is the implementation the blobs on the other side of this were built
to interoperate with — so matching it byte for byte is the point, and a
guess about the spec’s wording is exactly what would not do.
Functions
Section titled “Functions”fn ptk<const X: usize>(pmk: &[u8], aa: &[u8; 6], spa: &[u8; 6], anonce: &[u8; 32], snonce: &[u8; 32]) -> [u8; X]Derive the PTK for a CCMP association.
The handshake’s fixed label is "Pairwise key expansion", and the data is
the two MAC addresses then the two nonces, each pair in ascending byte
order — the ordering is what lets both ends derive the same key without
agreeing who is “first”. X is the PTK length in bytes: 48 for CCMP (16
KCK ‖ 16 KEK ‖ 16 TK), 64 for TKIP.
sha1_prf
Section titled “sha1_prf”fn sha1_prf(key: &[u8], label: &[u8], data: &[u8], out: &mut [u8])IEEE 802.11 PRF: fill out from key, a text label, and data.
Each 20-byte block of out is HMAC-SHA1(key, label ‖ NUL ‖ data ‖ i) for
i = 0, 1, 2, …; the final block is truncated to fit. This is the general
form; [ptk] wires in the handshake’s specific label and data.