nquic_hp (nquic v1.0.0)

View Source

QUIC 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_mask/3

-spec generate_mask(aes_128_gcm | aes_256_gcm | chacha20_poly1305, binary(), binary()) -> binary().

Generate the 5-byte HP mask from the cipher, HP key, and 16-byte sample.

generate_mask_ctx(HpCtx, Sample)

-spec generate_mask_ctx(crypto:crypto_state(), binary()) -> binary().

Generate the 5-byte HP mask using a cached cipher context.

generate_mask_from_keys/3

-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.

init_hp_ctx/2

-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.

mask(Cipher, HPKey, Sample, Packet, PnOffset)

-spec mask(aes_128_gcm | aes_256_gcm | chacha20_poly1305,
           binary(),
           binary(),
           binary(),
           non_neg_integer()) ->
              binary().

Apply header protection to a packet.

mask_header/4

-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.

unmask(Cipher, HPKey, Sample, Packet, PnOffset)

-spec unmask(aes_128_gcm | aes_256_gcm | chacha20_poly1305,
             binary(),
             binary(),
             binary(),
             non_neg_integer()) ->
                binary().

Remove header protection from a packet.

unmask_header(Cipher, HPKey, Sample, PnOffset, 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.

unmask_header_mask/3

-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.