Skip to documentation
SLOP

tiny.quic.connection.assemble

Reference tiny.quic connection assemble

Defined in connection.

API (6)

Actions

Public operations.

Types and contracts

Public types and contracts.

Values and defaults

Public values and defaults.

No direct callersNo direct callsconnectionassemble
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Called byCallstest sourcelib.quic.src.connection.assemble.test_RFC_900...2 assembled Initial layout decodes th...test sourcelib.quic.src.connection.assemble.test_RFC_900...2 one-byte PING pads to a full header...private sourcelib.quic.src.connection.assemblepacketLengthprivate sourcelib.quic.src.connection.assemblewriteHeaderprivate sourcelib.quic.src.connection.assemblewritePacketNumberconnection.assemblepacket
Static calls · unresolved targets: 1 · external targets: 3.

Source: lib/quic/src/connection/assemble.zig

zig
const std = @import("std");const quic = @import("../root.zig");const KeyStorage = [quic.crypto.Keys.storage_bytes_max]u8;pub const PacketType = enum { initial, handshake, one_rtt };pub const Spec = struct {    packet_type: PacketType,    destination: quic.packet.ConnectionId,    source: quic.packet.ConnectionId,    number: quic.connection.NumberEncoding,    key_phase: bool = false,    minimum_bytes: u16 = 0,};pub const Built = struct {    length: u16,    packet_number_offset: usize,    packet_number_length: u3,    payload_length: u16,};pub const Error = quic.packet.EncodeError || quic.crypto.packet.SealError ||    error{ NoSpace, PayloadTooLarge };/// Four bytes is the floor that a packet number and the payload behind it have to cover between/// them, so that header protection finds a whole sample to read. `packet` grows a short payload/// until the two together reach that floor, so a caller never has to pad by hand. One PING byte/// carried under a one-byte packet number therefore reaches the wire with three payload bytes.pub const sampled_bytes_min: usize = 4;pub fn packet(    keys: *quic.crypto.Keys,    spec: Spec,    payload: []const u8,    out: []u8,) Error!Built {    const pn_len: u3 = @intCast(spec.number.bits / 8);    std.debug.assert(pn_len >= 1);    std.debug.assert(pn_len <= sampled_bytes_min);    const payload_floor = @max(payload.len, sampled_bytes_min - pn_len);    var payload_len = payload_floor;    for (0..4) |_| {        const total = try packetLength(spec, pn_len, payload_len);        if (total == spec.minimum_bytes or spec.minimum_bytes == 0) break;        if (total < spec.minimum_bytes) {            payload_len = std.math.add(usize, payload_len, spec.minimum_bytes - total) catch                return error.PayloadTooLarge;        } else {            const excess = total - spec.minimum_bytes;            if (excess > payload_len - payload_floor) break;            payload_len -= excess;        }    }    const total = try packetLength(spec, pn_len, payload_len);    if (total > out.len) return error.NoSpace;    if (payload_len > std.math.maxInt(u16)) return error.PayloadTooLarge;    var output = quic.cursor.Write.init(out[0..total]);    const pn_offset = try writeHeader(spec, pn_len, payload_len, &output);    try writePacketNumber(spec.number.packet_number, pn_len, &output);    try output.put(payload);    for (payload.len..payload_len) |_| try output.byte(0);    const tag_end = std.math.add(usize, output.index, quic.crypto.packet.tag_bytes) catch        return error.PayloadTooLarge;    if (tag_end != total) return error.PayloadTooLarge;    @memset(out[output.index..tag_end], 0);    try quic.crypto.packet.seal(        keys,        spec.number.packet_number,        out[0..total],        pn_offset,        pn_len,        @intCast(payload_len),    );    return .{        .length = @intCast(total),        .packet_number_offset = pn_offset,        .packet_number_length = pn_len,        .payload_length = @intCast(payload_len),    };}fn packetLength(spec: Spec, pn_len: u3, payload_len: usize) Error!usize {    const protected_len = std.math.add(        usize,        payload_len,        quic.crypto.packet.tag_bytes,    ) catch return error.PayloadTooLarge;    const body_len = std.math.add(usize, pn_len, protected_len) catch        return error.PayloadTooLarge;    const header_len = switch (spec.packet_type) {        .initial, .handshake => try longHeaderLength(spec, body_len),        .one_rtt => 1 + spec.destination.length,    };    return std.math.add(usize, header_len, body_len) catch error.PayloadTooLarge;}fn longHeaderLength(spec: Spec, body_len: usize) Error!usize {    if (body_len > std.math.maxInt(u62)) return error.PayloadTooLarge;    const destination: usize = spec.destination.length;    const source: usize = spec.source.length;    const common = 1 + 4 + 1 + destination + 1 + source;    const token: usize = if (spec.packet_type == .initial) 1 else 0;    return common + token + quic.varint.encodedLength(@intCast(body_len));}fn writeHeader(    spec: Spec,    pn_len: u3,    payload_len: usize,    output: *quic.cursor.Write,) Error!usize {    if (spec.packet_type == .one_rtt) {        const first: u8 = 0x40 | (@as(u8, @intFromBool(spec.key_phase)) << 2) | (pn_len - 1);        try quic.packet.encodeShort(.{            .first = first,            .destination = spec.destination,            .packet_number_offset = 0,        }, output);        return output.index;    }    const protected_len = payload_len + quic.crypto.packet.tag_bytes;    const length: u62 = @intCast(@as(usize, pn_len) + protected_len);    const first: u8 = switch (spec.packet_type) {        .initial => 0xc0 | @as(u8, pn_len - 1),        .handshake => 0xe0 | @as(u8, pn_len - 1),        .one_rtt => unreachable,    };    const common = quic.packet.Common{        .first = first,        .version = 1,        .destination = spec.destination,        .source = spec.source,    };    const value: quic.packet.Long = switch (spec.packet_type) {        .initial => .{ .initial = .{            .common = common,            .token = &.{},            .length = length,            .packet_number_offset = 0,        } },        .handshake => .{ .handshake = .{            .common = common,            .length = length,            .packet_number_offset = 0,        } },        .one_rtt => unreachable,    };    try quic.packet.encodeLong(value, output);    return output.index;}fn writePacketNumber(value: u62, length: u3, output: *quic.cursor.Write) !void {    var bytes: [8]u8 = undefined;    std.mem.writeInt(u64, &bytes, value, .big);    try output.put(bytes[bytes.len - length ..]);}test "RFC 9000 section 12.2 assembled Initial layout decodes through packet codec" {    const secret: quic.crypto.Secret = @splat(0x42);    var key_bytes: KeyStorage align(quic.crypto.Keys.storage_alignment) = undefined;    var keys = try quic.crypto.Keys.derive(&key_bytes, .aes_128_gcm_sha256, secret);    defer _ = keys.deinit();    const cid = try quic.packet.ConnectionId.init("12345678");    var out: [1200]u8 = undefined;    const built = try packet(&keys, .{        .packet_type = .initial,        .destination = cid,        .source = cid,        .number = .{ .packet_number = 0, .bits = 32, .value = 0 },        .minimum_bytes = 1200,    }, &.{0x01}, &out);    try std.testing.expectEqual(@as(u16, 1200), built.length);    const decoded = try quic.packet.decodeLong(out[0..built.length]);    try std.testing.expectEqual(built.packet_number_offset, decoded.initial.packet_number_offset);    try std.testing.expectEqual(        @as(u62, @intCast(1200 - built.packet_number_offset)),        decoded.initial.length,    );}test "RFC 9001 section 5.4.2 one-byte PING pads to a full header protection sample" {    const secret: quic.crypto.Secret = @splat(0x42);    var key_bytes: KeyStorage align(quic.crypto.Keys.storage_alignment) = undefined;    var keys = try quic.crypto.Keys.derive(&key_bytes, .aes_128_gcm_sha256, secret);    defer _ = keys.deinit();    const cid = try quic.packet.ConnectionId.init("12345678");    var out: [64]u8 = undefined;    const built = try packet(&keys, .{        .packet_type = .one_rtt,        .destination = cid,        .source = cid,        .number = .{ .packet_number = 7, .bits = 8, .value = 7 },    }, &.{0x01}, &out);    try std.testing.expectEqual(@as(u16, sampled_bytes_min - 1), built.payload_length);    try std.testing.expectEqual(@as(u16, 1 + 8 + sampled_bytes_min + 16), built.length);}

Source: lib/quic/src/connection/root.zig:11

zig
pub const assemble = @import("assemble.zig");

Audit

Definitions7
Public names7
Members13
Version26.7.0
Revisiondaab053ee433