nquic_hp (nquic v1.0.0)
View SourceQUIC header protection per RFC 9001 Section 5.4.
Masks and unmasks the first byte and packet number bytes of QUIC packets. Supports AES-128-ECB, AES-256-ECB, and ChaCha20 mask generation. The 5-byte mask protects 1 byte of flags and up to 4 bytes of packet number.
Summary
Functions
Generate the 5-byte HP mask from the cipher, HP key, and 16-byte sample.
Generate the 5-byte HP mask using a cached cipher context.
Generate the 5-byte HP mask from a role key map.
Uses cached cipher context (hp_ctx) when present, falls back to one-shot.
Create a cached AES-ECB cipher context for header protection. Avoids per-packet EVP_CIPHER_CTX alloc/destroy (~200-400ns savings per packet). Only works for AES ciphers; ChaCha20 uses a different IV per packet.
Apply header protection to a packet.
Apply header protection to just the header bytes (send path optimization).
Returns {MaskedHeader, PnLen}. The caller assembles
[MaskedHeader, Ciphertext, Tag] as an iolist, avoiding a full packet copy.
Inlines the PN XOR per PnLen so the masked header is built in a single
binary allocation (no xor_pn intermediate). Mirrors unmask_header_mask/3.
Remove header protection from a packet.
Unmask just the header of a protected packet (recv path optimization).
Returns {UnmaskedFirstByte, PnLen, TruncatedPN, UnmaskedHeader}
without rebuilding the full packet. CiphertextAndTag can be extracted
as a zero-copy sub-binary of the original packet.
Unmask header using a pre-computed 5-byte mask (recv path optimization).
Callers generate the mask via generate_mask_ctx/2 or generate_mask/3,
then pass it here. Avoids coupling mask generation to header unmasking.
Returns {UnmaskedFirstByte, PnLen, TruncatedPN, UnmaskedHeader}.
The truncated packet number is also returned as an integer so the
recv path can hand it to nquic_packet_number:decode/3 without a
second binary slice.
Functions
Generate the 5-byte HP mask from the cipher, HP key, and 16-byte sample.
-spec generate_mask_ctx(crypto:crypto_state(), binary()) -> binary().
Generate the 5-byte HP mask using a cached cipher context.
-spec generate_mask_from_keys(#{hp := term(), hp_ctx => crypto:crypto_state(), atom() => term()}, aes_128_gcm | aes_256_gcm | chacha20_poly1305, binary()) -> binary().
Generate the 5-byte HP mask from a role key map.
Uses cached cipher context (hp_ctx) when present, falls back to one-shot.
-spec init_hp_ctx(aes_128_gcm | aes_256_gcm, binary()) -> crypto:crypto_state().
Create a cached AES-ECB cipher context for header protection. Avoids per-packet EVP_CIPHER_CTX alloc/destroy (~200-400ns savings per packet). Only works for AES ciphers; ChaCha20 uses a different IV per packet.
-spec mask(aes_128_gcm | aes_256_gcm | chacha20_poly1305, binary(), binary(), binary(), non_neg_integer()) -> binary().
Apply header protection to a packet.
-spec mask_header(binary(), binary(), non_neg_integer(), boolean()) -> {binary(), 1..4}.
Apply header protection to just the header bytes (send path optimization).
Returns {MaskedHeader, PnLen}. The caller assembles
[MaskedHeader, Ciphertext, Tag] as an iolist, avoiding a full packet copy.
Inlines the PN XOR per PnLen so the masked header is built in a single
binary allocation (no xor_pn intermediate). Mirrors unmask_header_mask/3.
-spec unmask(aes_128_gcm | aes_256_gcm | chacha20_poly1305, binary(), binary(), binary(), non_neg_integer()) -> binary().
Remove header protection from a packet.
-spec unmask_header(aes_128_gcm | aes_256_gcm | chacha20_poly1305, binary(), binary(), non_neg_integer(), binary()) -> {non_neg_integer(), 1..4, non_neg_integer(), binary()}.
Unmask just the header of a protected packet (recv path optimization).
Returns {UnmaskedFirstByte, PnLen, TruncatedPN, UnmaskedHeader}
without rebuilding the full packet. CiphertextAndTag can be extracted
as a zero-copy sub-binary of the original packet.
-spec unmask_header_mask(binary(), non_neg_integer(), binary()) -> {non_neg_integer(), 1..4, non_neg_integer(), binary()}.
Unmask header using a pre-computed 5-byte mask (recv path optimization).
Callers generate the mask via generate_mask_ctx/2 or generate_mask/3,
then pass it here. Avoids coupling mask generation to header unmasking.
Returns {UnmaskedFirstByte, PnLen, TruncatedPN, UnmaskedHeader}.
The truncated packet number is also returned as an integer so the
recv path can hand it to nquic_packet_number:decode/3 without a
second binary slice.