lib/quic/src/tls/message/profile.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 const quic = @import("../../root.zig");
  3 const message = @import("root.zig");
  4 
  5 const cursor = quic.cursor;
  6 const ExtensionType = std.crypto.tls.ExtensionType;
  7 
  8 pub const ClientHello = struct {
  9     random: [32]u8,
 10     key_share: [32]u8,
 11     alpn: []const u8,
 12     transport_parameters: []const u8,
 13     server_name: ?[]const u8,
 14     cipher_suite: std.crypto.tls.CipherSuite = .AES_128_GCM_SHA256,
 15 };
 16 
 17 pub const ServerHello = struct {
 18     random: [32]u8,
 19     key_share: [32]u8,
 20     cipher_suite: std.crypto.tls.CipherSuite,
 21 };
 22 
 23 pub const EncryptedExtensions = struct {
 24     alpn: []const u8,
 25     transport_parameters: []const u8,
 26     acknowledge_server_name: bool,
 27 };
 28 
 29 pub const Error = message.EncodeError;
 30 
 31 pub fn clientHelloBodyLength(value: ClientHello) Error!usize {
 32     try validateAlpn(value.alpn);
 33     var extensions: usize = 7 + 8 + 8 + 42;
 34     extensions = try add(extensions, 7 + value.alpn.len);
 35     extensions = try add(extensions, 4 + value.transport_parameters.len);
 36     extensions = try add(extensions, 6 + 6);
 37     if (value.server_name) |name| extensions = try add(extensions, 9 + name.len);
 38     return add(45, extensions);
 39 }
 40 
 41 pub fn serverHelloBodyLength() usize {
 42     return 86;
 43 }
 44 
 45 pub fn encryptedExtensionsBodyLength(value: EncryptedExtensions) Error!usize {
 46     try validateAlpn(value.alpn);
 47     var extensions = try add(7 + value.alpn.len, 4 + value.transport_parameters.len);
 48     extensions = try add(extensions, 5 + 5);
 49     if (value.acknowledge_server_name) extensions = try add(extensions, 4);
 50     return add(2, extensions);
 51 }
 52 
 53 pub fn encodeClientHello(value: ClientHello, output: *cursor.Write) Error!void {
 54     const body_length = try clientHelloBodyLength(value);
 55     try writeHandshakeHeader(.client_hello, body_length, output);
 56     try output.int(u16, 0x0303);
 57     try output.put(&value.random);
 58     try output.byte(0);
 59     try output.int(u16, 4);
 60     try output.int(u16, @backingInt(value.cipher_suite));
 61     const alternate: std.crypto.tls.CipherSuite = switch (value.cipher_suite) {
 62         .AES_128_GCM_SHA256 => .CHACHA20_POLY1305_SHA256,
 63         .CHACHA20_POLY1305_SHA256 => .AES_128_GCM_SHA256,
 64         else => return error.InvalidLength,
 65     };
 66     try output.int(u16, @backingInt(alternate));
 67     try output.put(&.{ 1, 0 });
 68     try output.int(u16, @intCast(body_length - 45));
 69     if (value.server_name) |name| try writeServerName(name, output);
 70     try writeSupportedVersions(true, output);
 71     try writeSupportedGroups(output);
 72     try writeSignatureAlgorithms(output);
 73     try writeKeyShare(value.key_share, true, output);
 74     try writeAlpn(value.alpn, output);
 75     try writeExtension(.quic_transport_parameters, value.transport_parameters, output);
 76     try writeCertificateType(.server_certificate_type, true, output);
 77     try writeCertificateType(.client_certificate_type, true, output);
 78 }
 79 
 80 pub fn encodeServerHello(value: ServerHello, output: *cursor.Write) Error!void {
 81     try writeHandshakeHeader(.server_hello, serverHelloBodyLength(), output);
 82     try output.int(u16, 0x0303);
 83     try output.put(&value.random);
 84     try output.byte(0);
 85     try output.int(u16, @backingInt(value.cipher_suite));
 86     try output.byte(0);
 87     try output.int(u16, 46);
 88     try writeSupportedVersions(false, output);
 89     try writeKeyShare(value.key_share, false, output);
 90 }
 91 
 92 pub fn encodeEncryptedExtensions(
 93     value: EncryptedExtensions,
 94     output: *cursor.Write,
 95 ) Error!void {
 96     const body_length = try encryptedExtensionsBodyLength(value);
 97     try writeHandshakeHeader(.encrypted_extensions, body_length, output);
 98     try output.int(u16, @intCast(body_length - 2));
 99     try writeAlpn(value.alpn, output);
100     try writeExtension(.quic_transport_parameters, value.transport_parameters, output);
101     try writeCertificateType(.server_certificate_type, false, output);
102     try writeCertificateType(.client_certificate_type, false, output);
103     if (value.acknowledge_server_name) try writeExtension(.server_name, &.{}, output);
104 }
105 
106 pub fn encodeCertificateRequest(output: *cursor.Write) Error!void {
107     try writeHandshakeHeader(.certificate_request, 11, output);
108     try output.byte(0);
109     try output.int(u16, 8);
110     try writeSignatureAlgorithms(output);
111 }
112 
113 pub fn encodeCertificate(public_key: [32]u8, output: *cursor.Write) Error!void {
114     const encoded = quic.tls.encodeSubjectPublicKeyInfo(public_key);
115     try message.encodeSingleCertificate(&.{}, &encoded, output);
116 }
117 
118 pub fn encodeCertificateVerify(signature: [64]u8, output: *cursor.Write) Error!void {
119     try message.encodeCertificateVerify(.{
120         .algorithm = .ed25519,
121         .signature = &signature,
122     }, output);
123 }
124 
125 pub fn encodeFinished(verify_data: [32]u8, output: *cursor.Write) Error!void {
126     try message.encodeFinished(.{ .verify_data = &verify_data }, output);
127 }
128 
129 fn writeSupportedVersions(client: bool, output: *cursor.Write) Error!void {
130     if (client) {
131         try writeExtensionHeader(.supported_versions, 3, output);
132         try output.byte(2);
133     } else {
134         try writeExtensionHeader(.supported_versions, 2, output);
135     }
136     try output.int(u16, 0x0304);
137 }
138 
139 fn writeSupportedGroups(output: *cursor.Write) Error!void {
140     try writeExtensionHeader(.supported_groups, 4, output);
141     try output.int(u16, 2);
142     try output.int(u16, @backingInt(std.crypto.tls.NamedGroup.x25519));
143 }
144 
145 fn writeSignatureAlgorithms(output: *cursor.Write) Error!void {
146     try writeExtensionHeader(.signature_algorithms, 4, output);
147     try output.int(u16, 2);
148     try output.int(u16, @backingInt(std.crypto.tls.SignatureScheme.ed25519));
149 }
150 
151 fn writeKeyShare(key: [32]u8, client: bool, output: *cursor.Write) Error!void {
152     try writeExtensionHeader(.key_share, if (client) 38 else 36, output);
153     if (client) try output.int(u16, 36);
154     try output.int(u16, @backingInt(std.crypto.tls.NamedGroup.x25519));
155     try output.int(u16, 32);
156     try output.put(&key);
157 }
158 
159 fn writeAlpn(alpn: []const u8, output: *cursor.Write) Error!void {
160     try validateAlpn(alpn);
161     try writeExtensionHeader(.application_layer_protocol_negotiation, 3 + alpn.len, output);
162     try output.int(u16, @intCast(1 + alpn.len));
163     try output.byte(@intCast(alpn.len));
164     try output.put(alpn);
165 }
166 
167 fn writeCertificateType(
168     kind: ExtensionType,
169     client: bool,
170     output: *cursor.Write,
171 ) Error!void {
172     try writeExtensionHeader(kind, if (client) 2 else 1, output);
173     if (client) try output.byte(1);
174     try output.byte(@backingInt(std.crypto.tls.CertificateType.RawPublicKey));
175 }
176 
177 fn writeServerName(name: []const u8, output: *cursor.Write) Error!void {
178     const list_length = try add(3, name.len);
179     const data_length = try add(2, list_length);
180     try writeExtensionHeader(.server_name, data_length, output);
181     try writeLength(u16, list_length, output);
182     try output.byte(0);
183     try writeLength(u16, name.len, output);
184     try output.put(name);
185 }
186 
187 fn writeExtension(
188     kind: ExtensionType,
189     data: []const u8,
190     output: *cursor.Write,
191 ) Error!void {
192     try writeExtensionHeader(kind, data.len, output);
193     try output.put(data);
194 }
195 
196 fn writeExtensionHeader(
197     kind: ExtensionType,
198     length: usize,
199     output: *cursor.Write,
200 ) Error!void {
201     try output.int(u16, @backingInt(kind));
202     try writeLength(u16, length, output);
203 }
204 
205 fn writeHandshakeHeader(
206     kind: std.crypto.tls.HandshakeType,
207     length: usize,
208     output: *cursor.Write,
209 ) Error!void {
210     try output.byte(@backingInt(kind));
211     try writeLength(u24, length, output);
212 }
213 
214 fn writeLength(comptime T: type, length: usize, output: *cursor.Write) Error!void {
215     if (length > std.math.maxInt(T)) return error.InvalidLength;
216     try output.int(T, @intCast(length));
217 }
218 
219 fn validateAlpn(alpn: []const u8) Error!void {
220     if (alpn.len == 0) return error.InvalidLength;
221     if (alpn.len > std.math.maxInt(u8)) return error.InvalidLength;
222 }
223 
224 fn add(left: usize, right: usize) Error!usize {
225     return std.math.add(usize, left, right) catch error.InvalidLength;
226 }