tiny.quic.crypto.retry
Defined in crypto.
API (8)
Actions
Public operations.
Types and contracts
Public types and contracts.
Values and defaults
Public values and defaults.
connection_id_bytes_maxpseudo_bytes_maxtag_bytesv1_key: The 16-byte AES-128 key RFC 9001 section 5.8 fixes for QUIC version 1 Retry integrity tags.v1_nonce: The 12-byte nonce RFC 9001 section 5.8 fixes for QUIC version 1 Retry integrity tags.
Source
Source: lib/quic/src/crypto/retry.zig
zig
const std = @import("std");const quic = @import("../root.zig");const Aes128 = std.crypto.core.aes.Aes128;const Ghash = std.crypto.onetimeauth.Ghash;pub const tag_bytes: usize = Ghash.mac_length;pub const connection_id_bytes_max: usize = 20;pub const pseudo_bytes_max: usize = quic.crypto.packet.packet_bytes_max - tag_bytes;/// The 16-byte AES-128 key RFC 9001 section 5.8 fixes for QUIC version 1 Retry integrity tags. A/// caller checking a Retry integrity tag against another implementation needs the key that/// implementation used. Every endpoint uses it, so the tag shows the sender saw the original/// destination connection ID. It carries no proof of the sender's identity.pub const v1_key = [_]u8{ 0xbe, 0x0c, 0x69, 0x0b, 0x9f, 0x66, 0x57, 0x5a, 0x1d, 0x76, 0x6b, 0x54, 0xe3, 0x68, 0xc8, 0x4e,};/// The 12-byte nonce RFC 9001 section 5.8 fixes for QUIC version 1 Retry integrity tags. It pairs/// with the key for the same check. The tag computation uses it with the fixed key over the/// pseudo-packet.pub const v1_nonce = [_]u8{ 0x46, 0x15, 0x99, 0xd3, 0x5d, 0x63, 0x2b, 0xf2, 0x23, 0x98, 0x25, 0xbb,};pub const TagError = error{ ConnectionIdTooLong, PacketTooLarge,};fn authenticate(prefix: []const u8, retry_pseudo: []const u8) [tag_bytes]u8 { const aad_len = prefix.len + retry_pseudo.len; const cipher = Aes128.initEnc(v1_key); var hash_key: [Ghash.key_length]u8 = undefined; cipher.encrypt(&hash_key, &@splat(0)); const block_count = @divCeil(aad_len, Ghash.block_length) + 1; var mac = Ghash.initForBlockCount(&hash_key, block_count); mac.update(prefix); mac.update(retry_pseudo); mac.pad(); var lengths: [Ghash.block_length]u8 = @splat(0); std.mem.writeInt(u64, lengths[0..8], @as(u64, aad_len) * 8, .big); mac.update(&lengths); var result: [tag_bytes]u8 = undefined; mac.final(&result); var counter: [Aes128.block.block_length]u8 = @splat(0); counter[0..v1_nonce.len].* = v1_nonce; std.mem.writeInt(u32, counter[v1_nonce.len..][0..4], 1, .big); var encrypted_counter: [Aes128.block.block_length]u8 = undefined; cipher.encrypt(&encrypted_counter, &counter); for (0..tag_bytes) |index| result[index] ^= encrypted_counter[index]; return result;}pub fn tag(odcid: []const u8, retry_pseudo: []const u8) TagError![tag_bytes]u8 { if (odcid.len > connection_id_bytes_max) return error.ConnectionIdTooLong; if (retry_pseudo.len > pseudo_bytes_max) return error.PacketTooLarge; var prefix: [1 + connection_id_bytes_max]u8 = undefined; prefix[0] = @intCast(odcid.len); @memcpy(prefix[1..][0..odcid.len], odcid); return authenticate(prefix[0 .. 1 + odcid.len], retry_pseudo);}pub fn verify(odcid: []const u8, retry_packet: []const u8) bool { if (odcid.len > connection_id_bytes_max) return false; if (retry_packet.len <= tag_bytes) return false; if (retry_packet.len > quic.crypto.packet.packet_bytes_max) return false; const tag_offset = retry_packet.len - tag_bytes; const expected = tag(odcid, retry_packet[0..tag_offset]) catch return false; const actual: [tag_bytes]u8 = retry_packet[tag_offset..][0..tag_bytes].*; return std.crypto.timing_safe.eql([tag_bytes]u8, expected, actual);}Source: lib/quic/src/crypto/root.zig:20
zig
pub const retry = @import("retry.zig");Audit
| Definitions | 9 |
|---|---|
| Public names | 9 |
| Members | 2 |
| Version | 26.7.0 |
| Revision | daab053ee433 |