authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-11-02 02:45:12-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-11-07 20:25:26-05:00
log7afb2777250a251a065b8a970eb7f14e2d5b5ce2
tree90a778fca9e850330aa238748b7d830ee36a9127
parent7f20c78c95d54e7fb0958693a9df2dee40dd99d6

std.crypto.tls: fix x25519_ml_kem768 key share

This is mostly nfc cleanup as I was bisecting the client hello to find the problematic part, and the only bug fix ended up being key_share.x25519_kp.public_key ++ key_share.ml_kem768_kp.public_key.toBytes() to key_share.ml_kem768_kp.public_key.toBytes() ++ key_share.x25519_kp.public_key) and the same swap in `KeyShare.exchange` as per some random blog that says "a hybrid keyshare, constructed by concatenating the public KEM key with the public X25519 key". I also note that based on the same blog post, there was a draft version of this method that indeed had these values swapped, and that used to be supported by this code, but it was not properly fixed up when this code was updated from the draft spec. Closes #21747

2 files changed, 135 insertions(+), 138 deletions(-)

lib/std/crypto/tls.zig+43-24
...@@ -291,6 +291,12 @@ pub const NamedGroup = enum(u16) {...@@ -291,6 +291,12 @@ pub const NamedGroup = enum(u16) {
291 _,291 _,
292};292};
293293
294pub const PskKeyExchangeMode = enum(u8) {
295 psk_ke = 0,
296 psk_dhe_ke = 1,
297 _,
298};
299
294pub const CipherSuite = enum(u16) {300pub const CipherSuite = enum(u16) {
295 RSA_WITH_AES_128_CBC_SHA = 0x002F,301 RSA_WITH_AES_128_CBC_SHA = 0x002F,
296 DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033,302 DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033,
...@@ -407,6 +413,11 @@ pub const CipherSuite = enum(u16) {...@@ -407,6 +413,11 @@ pub const CipherSuite = enum(u16) {
407 }413 }
408};414};
409415
416pub const CompressionMethod = enum(u8) {
417 null = 0,
418 _,
419};
420
410pub const CertificateType = enum(u8) {421pub const CertificateType = enum(u8) {
411 X509 = 0,422 X509 = 0,
412 RawPublicKey = 2,423 RawPublicKey = 2,
...@@ -419,6 +430,11 @@ pub const KeyUpdateRequest = enum(u8) {...@@ -419,6 +430,11 @@ pub const KeyUpdateRequest = enum(u8) {
419 _,430 _,
420};431};
421432
433pub const ChangeCipherSpecType = enum(u8) {
434 change_cipher_spec = 1,
435 _,
436};
437
422pub fn HandshakeCipherT(comptime AeadType: type, comptime HashType: type, comptime explicit_iv_length: comptime_int) type {438pub fn HandshakeCipherT(comptime AeadType: type, comptime HashType: type, comptime explicit_iv_length: comptime_int) type {
423 return struct {439 return struct {
424 pub const A = ApplicationCipherT(AeadType, HashType, explicit_iv_length);440 pub const A = ApplicationCipherT(AeadType, HashType, explicit_iv_length);
...@@ -560,34 +576,38 @@ pub fn hmac(comptime Hmac: type, message: []const u8, key: [Hmac.key_length]u8)...@@ -560,34 +576,38 @@ pub fn hmac(comptime Hmac: type, message: []const u8, key: [Hmac.key_length]u8)
560 return result;576 return result;
561}577}
562578
563pub inline fn extension(comptime et: ExtensionType, bytes: anytype) [2 + 2 + bytes.len]u8 {579pub inline fn extension(et: ExtensionType, bytes: anytype) [2 + 2 + bytes.len]u8 {
564 return int2(@intFromEnum(et)) ++ array(1, bytes);580 return int(u16, @intFromEnum(et)) ++ array(u16, u8, bytes);
565}
566
567pub inline fn array(comptime elem_size: comptime_int, bytes: anytype) [2 + bytes.len]u8 {
568 comptime assert(bytes.len % elem_size == 0);
569 return int2(bytes.len) ++ bytes;
570}581}
571582
572pub inline fn enum_array(comptime E: type, comptime tags: []const E) [2 + @sizeOf(E) * tags.len]u8 {583pub inline fn array(
573 assert(@sizeOf(E) == 2);584 comptime Len: type,
574 var result: [tags.len * 2]u8 = undefined;585 comptime Elem: type,
575 for (tags, 0..) |elem, i| {586 elems: anytype,
576 result[i * 2] = @as(u8, @truncate(@intFromEnum(elem) >> 8));587) [@divExact(@bitSizeOf(Len), 8) + @divExact(@bitSizeOf(Elem), 8) * elems.len]u8 {
577 result[i * 2 + 1] = @as(u8, @truncate(@intFromEnum(elem)));588 const len_size = @divExact(@bitSizeOf(Len), 8);
589 const elem_size = @divExact(@bitSizeOf(Elem), 8);
590 var arr: [len_size + elem_size * elems.len]u8 = undefined;
591 std.mem.writeInt(Len, arr[0..len_size], @intCast(elem_size * elems.len), .big);
592 const ElemInt = @Type(.{ .int = .{ .signedness = .unsigned, .bits = @bitSizeOf(Elem) } });
593 for (0.., @as([elems.len]Elem, elems)) |index, elem| {
594 std.mem.writeInt(
595 ElemInt,
596 arr[len_size + elem_size * index ..][0..elem_size],
597 switch (@typeInfo(Elem)) {
598 .int => @as(Elem, elem),
599 .@"enum" => @intFromEnum(@as(Elem, elem)),
600 else => @bitCast(@as(Elem, elem)),
601 },
602 .big,
603 );
578 }604 }
579 return array(2, result);
580}
581
582pub inline fn int2(int: u16) [2]u8 {
583 var arr: [2]u8 = undefined;
584 std.mem.writeInt(u16, &arr, int, .big);
585 return arr;605 return arr;
586}606}
587607
588pub inline fn int3(int: u24) [3]u8 {608pub inline fn int(comptime Int: type, val: Int) [@divExact(@bitSizeOf(Int), 8)]u8 {
589 var arr: [3]u8 = undefined;609 var arr: [@divExact(@bitSizeOf(Int), 8)]u8 = undefined;
590 std.mem.writeInt(u24, &arr, int, .big);610 std.mem.writeInt(Int, &arr, val, .big);
591 return arr;611 return arr;
592}612}
593613
...@@ -670,9 +690,8 @@ pub const Decoder = struct {...@@ -670,9 +690,8 @@ pub const Decoder = struct {
670 else => @compileError("unsupported int type: " ++ @typeName(T)),690 else => @compileError("unsupported int type: " ++ @typeName(T)),
671 },691 },
672 .@"enum" => |info| {692 .@"enum" => |info| {
673 const int = d.decode(info.tag_type);
674 if (info.is_exhaustive) @compileError("exhaustive enum cannot be used");693 if (info.is_exhaustive) @compileError("exhaustive enum cannot be used");
675 return @as(T, @enumFromInt(int));694 return @enumFromInt(d.decode(info.tag_type));
676 },695 },
677 else => @compileError("unsupported type: " ++ @typeName(T)),696 else => @compileError("unsupported type: " ++ @typeName(T)),
678 }697 }
lib/std/crypto/tls/Client.zig+92-114
...@@ -10,10 +10,8 @@ const Certificate = std.crypto.Certificate;...@@ -10,10 +10,8 @@ const Certificate = std.crypto.Certificate;
10const max_ciphertext_len = tls.max_ciphertext_len;10const max_ciphertext_len = tls.max_ciphertext_len;
11const hmacExpandLabel = tls.hmacExpandLabel;11const hmacExpandLabel = tls.hmacExpandLabel;
12const hkdfExpandLabel = tls.hkdfExpandLabel;12const hkdfExpandLabel = tls.hkdfExpandLabel;
13const int2 = tls.int2;13const int = tls.int;
14const int3 = tls.int3;
15const array = tls.array;14const array = tls.array;
16const enum_array = tls.enum_array;
1715
18tls_version: tls.ProtocolVersion,16tls_version: tls.ProtocolVersion,
19read_seq: u64,17read_seq: u64,
...@@ -156,70 +154,62 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -156,70 +154,62 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
156 error.IdentityElement => return error.InsufficientEntropy,154 error.IdentityElement => return error.InsufficientEntropy,
157 };155 };
158156
159 const extensions_payload =157 const extensions_payload = tls.extension(.supported_versions, array(u8, tls.ProtocolVersion, .{
160 tls.extension(.supported_versions, [_]u8{2 + 2} ++ // byte length of supported versions158 .tls_1_3,
161 int2(@intFromEnum(tls.ProtocolVersion.tls_1_3)) ++159 .tls_1_2,
162 int2(@intFromEnum(tls.ProtocolVersion.tls_1_2))) ++160 })) ++ tls.extension(.signature_algorithms, array(u16, tls.SignatureScheme, .{
163 tls.extension(.signature_algorithms, enum_array(tls.SignatureScheme, &.{
164 .ecdsa_secp256r1_sha256,161 .ecdsa_secp256r1_sha256,
165 .ecdsa_secp384r1_sha384,162 .ecdsa_secp384r1_sha384,
166 .rsa_pss_rsae_sha256,163 .rsa_pss_rsae_sha256,
167 .rsa_pss_rsae_sha384,164 .rsa_pss_rsae_sha384,
168 .rsa_pss_rsae_sha512,165 .rsa_pss_rsae_sha512,
169 .ed25519,166 .ed25519,
170 })) ++ tls.extension(.supported_groups, enum_array(tls.NamedGroup, &.{167 })) ++ tls.extension(.supported_groups, array(u16, tls.NamedGroup, .{
171 .x25519_ml_kem768,168 .x25519_ml_kem768,
172 .secp256r1,169 .secp256r1,
173 .x25519,170 .x25519,
174 })) ++ tls.extension(171 })) ++ tls.extension(.psk_key_exchange_modes, array(u8, tls.PskKeyExchangeMode, .{
175 .key_share,172 .psk_dhe_ke,
176 array(1, int2(@intFromEnum(tls.NamedGroup.x25519)) ++173 })) ++ tls.extension(.key_share, array(
177 array(1, key_share.x25519_kp.public_key) ++174 u16,
178 int2(@intFromEnum(tls.NamedGroup.secp256r1)) ++175 u8,
179 array(1, key_share.secp256r1_kp.public_key.toUncompressedSec1()) ++176 int(u16, @intFromEnum(tls.NamedGroup.x25519_ml_kem768)) ++
180 int2(@intFromEnum(tls.NamedGroup.x25519_ml_kem768)) ++177 array(u16, u8, key_share.ml_kem768_kp.public_key.toBytes() ++ key_share.x25519_kp.public_key) ++
181 array(1, key_share.x25519_kp.public_key ++ key_share.ml_kem768_kp.public_key.toBytes())),178 int(u16, @intFromEnum(tls.NamedGroup.secp256r1)) ++
182 ) ++179 array(u16, u8, key_share.secp256r1_kp.public_key.toUncompressedSec1()) ++
183 int2(@intFromEnum(tls.ExtensionType.server_name)) ++180 int(u16, @intFromEnum(tls.NamedGroup.x25519)) ++
184 int2(host_len + 5) ++ // byte length of this extension payload181 array(u16, u8, key_share.x25519_kp.public_key),
185 int2(host_len + 3) ++ // server_name_list byte count182 )) ++ int(u16, @intFromEnum(tls.ExtensionType.server_name)) ++
186 [1]u8{0x00} ++ // name_type183 int(u16, 2 + 1 + 2 + host_len) ++ // byte length of this extension payload
187 int2(host_len);184 int(u16, 1 + 2 + host_len) ++ // server_name_list byte count
185 .{0x00} ++ // name_type
186 int(u16, host_len);
188187
189 const extensions_header =188 const extensions_header =
190 int2(@intCast(extensions_payload.len + host_len)) ++189 int(u16, @intCast(extensions_payload.len + host_len)) ++
191 extensions_payload;190 extensions_payload;
192191
193 const legacy_compression_methods = 0x0100;
194
195 const client_hello =192 const client_hello =
196 int2(@intFromEnum(tls.ProtocolVersion.tls_1_2)) ++193 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
197 client_hello_rand ++194 client_hello_rand ++
198 [1]u8{32} ++ legacy_session_id ++195 [1]u8{32} ++ legacy_session_id ++
199 cipher_suites ++196 cipher_suites ++
200 int2(legacy_compression_methods) ++197 array(u8, tls.CompressionMethod, .{.null}) ++
201 extensions_header;198 extensions_header;
202199
203 const out_handshake =200 const out_handshake = .{@intFromEnum(tls.HandshakeType.client_hello)} ++
204 [_]u8{@intFromEnum(tls.HandshakeType.client_hello)} ++201 int(u24, @intCast(client_hello.len + host_len)) ++
205 int3(@intCast(client_hello.len + host_len)) ++
206 client_hello;202 client_hello;
207203
208 const cleartext_header = [_]u8{@intFromEnum(tls.ContentType.handshake)} ++204 const cleartext_header = .{@intFromEnum(tls.ContentType.handshake)} ++
209 int2(@intFromEnum(tls.ProtocolVersion.tls_1_0)) ++ // legacy_record_version205 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_0)) ++
210 int2(@intCast(out_handshake.len + host_len)) ++206 int(u16, @intCast(out_handshake.len + host_len)) ++
211 out_handshake;207 out_handshake;
212208
213 {209 {
214 var iovecs = [_]std.posix.iovec_const{210 var iovecs = [_]std.posix.iovec_const{
215 .{211 .{ .base = &cleartext_header, .len = cleartext_header.len },
216 .base = &cleartext_header,212 .{ .base = host.ptr, .len = host.len },
217 .len = cleartext_header.len,
218 },
219 .{
220 .base = host.ptr,
221 .len = host.len,
222 },
223 };213 };
224 try stream.writevAll(&iovecs);214 try stream.writevAll(&iovecs);
225 }215 }
...@@ -526,7 +516,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -526,7 +516,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
526 },516 },
527 .change_cipher_spec => {517 .change_cipher_spec => {
528 try ctd.ensure(1);518 try ctd.ensure(1);
529 if (ctd.decode(u8) != 0x01) return error.TlsIllegalParameter;519 if (ctd.decode(tls.ChangeCipherSpecType) != .change_cipher_spec) return error.TlsIllegalParameter;
530 cipher_state = pending_cipher_state;520 cipher_state = pending_cipher_state;
531 },521 },
532 .handshake => while (true) {522 .handshake => while (true) {
...@@ -648,20 +638,13 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -648,20 +638,13 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
648 if (handshake_state != .server_hello_done) return error.TlsUnexpectedMessage;638 if (handshake_state != .server_hello_done) return error.TlsUnexpectedMessage;
649 handshake_state = .finished;639 handshake_state = .finished;
650640
651 const client_key_exchange_msg =641 const client_key_exchange_msg = .{@intFromEnum(tls.ContentType.handshake)} ++
652 [_]u8{@intFromEnum(tls.ContentType.handshake)} ++ // record content type642 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
653 int2(@intFromEnum(tls.ProtocolVersion.tls_1_2)) ++ // legacy protocol version643 array(u16, u8, .{@intFromEnum(tls.HandshakeType.client_key_exchange)} ++
654 int2(0x46) ++ // record length644 array(u24, u8, array(u8, u8, key_share.secp256r1_kp.public_key.toUncompressedSec1())));
655 .{@intFromEnum(tls.HandshakeType.client_key_exchange)} ++ // handshake type645 const client_change_cipher_spec_msg = .{@intFromEnum(tls.ContentType.change_cipher_spec)} ++
656 int3(0x42) ++ // params length646 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
657 .{0x41} ++ // pubkey length647 array(u16, tls.ChangeCipherSpecType, .{.change_cipher_spec});
658 key_share.secp256r1_kp.public_key.toUncompressedSec1();
659 // This message is to trick buggy proxies into behaving correctly.
660 const client_change_cipher_spec_msg =
661 [_]u8{@intFromEnum(tls.ContentType.change_cipher_spec)} ++ // record content type
662 int2(@intFromEnum(tls.ProtocolVersion.tls_1_2)) ++ // legacy protocol version
663 int2(1) ++ // record length
664 .{0x01};
665 const pre_master_secret = key_share.getSharedSecret().?;648 const pre_master_secret = key_share.getSharedSecret().?;
666 switch (handshake_cipher) {649 switch (handshake_cipher) {
667 inline else => |*p| {650 inline else => |*p| {
...@@ -680,10 +663,13 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -680,10 +663,13 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
680 @sizeOf(P.Tls_1_2),663 @sizeOf(P.Tls_1_2),
681 );664 );
682 const verify_data_len = 12;665 const verify_data_len = 12;
683 const client_verify_cleartext =666 const client_verify_cleartext = .{@intFromEnum(tls.HandshakeType.finished)} ++
684 [_]u8{@intFromEnum(tls.HandshakeType.finished)} ++ // handshake type667 array(u24, u8, hmacExpandLabel(
685 int3(verify_data_len) ++ // verify data length668 P.Hmac,
686 hmacExpandLabel(P.Hmac, &master_secret, &.{ "client finished", &p.transcript_hash.peek() }, verify_data_len);669 &master_secret,
670 &.{ "client finished", &p.transcript_hash.peek() },
671 verify_data_len,
672 ));
687 p.transcript_hash.update(&client_verify_cleartext);673 p.transcript_hash.update(&client_verify_cleartext);
688 p.version = .{ .tls_1_2 = .{674 p.version = .{ .tls_1_2 = .{
689 .server_verify_data = hmacExpandLabel(675 .server_verify_data = hmacExpandLabel(
...@@ -709,25 +695,23 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -709,25 +695,23 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
709 const operand: V = pad ++ @as([8]u8, @bitCast(big(write_seq)));695 const operand: V = pad ++ @as([8]u8, @bitCast(big(write_seq)));
710 break :nonce @as(V, pv.app_cipher.client_write_IV ++ pv.app_cipher.client_salt) ^ operand;696 break :nonce @as(V, pv.app_cipher.client_write_IV ++ pv.app_cipher.client_salt) ^ operand;
711 };697 };
712 var client_verify_msg = [_]u8{@intFromEnum(tls.ContentType.handshake)} ++ // record content type698 var client_verify_msg = .{@intFromEnum(tls.ContentType.handshake)} ++
713 int2(@intFromEnum(tls.ProtocolVersion.tls_1_2)) ++ // legacy protocol version699 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
714 int2(P.record_iv_length + client_verify_cleartext.len + P.mac_length) ++ // record length700 array(u16, u8, nonce[P.fixed_iv_length..].* ++
715 nonce[P.fixed_iv_length..].* ++701 @as([client_verify_cleartext.len + P.mac_length]u8, undefined));
716 @as([client_verify_cleartext.len + P.mac_length]u8, undefined);
717 P.AEAD.encrypt(702 P.AEAD.encrypt(
718 client_verify_msg[client_verify_msg.len - P.mac_length -703 client_verify_msg[client_verify_msg.len - P.mac_length -
719 client_verify_cleartext.len ..][0..client_verify_cleartext.len],704 client_verify_cleartext.len ..][0..client_verify_cleartext.len],
720 client_verify_msg[client_verify_msg.len - P.mac_length ..][0..P.mac_length],705 client_verify_msg[client_verify_msg.len - P.mac_length ..][0..P.mac_length],
721 &client_verify_cleartext,706 &client_verify_cleartext,
722 std.mem.toBytes(big(write_seq)) ++ client_verify_msg[0 .. 1 + 2] ++ int2(client_verify_cleartext.len),707 std.mem.toBytes(big(write_seq)) ++ client_verify_msg[0 .. 1 + 2] ++ int(u16, client_verify_cleartext.len),
723 nonce,708 nonce,
724 pv.app_cipher.client_write_key,709 pv.app_cipher.client_write_key,
725 );710 );
726 const all_msgs = client_key_exchange_msg ++ client_change_cipher_spec_msg ++ client_verify_msg;711 const all_msgs = client_key_exchange_msg ++ client_change_cipher_spec_msg ++ client_verify_msg;
727 var all_msgs_vec = [_]std.posix.iovec_const{.{712 var all_msgs_vec = [_]std.posix.iovec_const{
728 .base = &all_msgs,713 .{ .base = &all_msgs, .len = all_msgs.len },
729 .len = all_msgs.len,714 };
730 }};
731 try stream.writevAll(&all_msgs_vec);715 try stream.writevAll(&all_msgs_vec);
732 },716 },
733 }717 }
...@@ -755,11 +739,9 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -755,11 +739,9 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
755 if (cipher_state == .cleartext) return error.TlsUnexpectedMessage;739 if (cipher_state == .cleartext) return error.TlsUnexpectedMessage;
756 if (handshake_state != .finished) return error.TlsUnexpectedMessage;740 if (handshake_state != .finished) return error.TlsUnexpectedMessage;
757 // This message is to trick buggy proxies into behaving correctly.741 // This message is to trick buggy proxies into behaving correctly.
758 const client_change_cipher_spec_msg =742 const client_change_cipher_spec_msg = .{@intFromEnum(tls.ContentType.change_cipher_spec)} ++
759 [_]u8{@intFromEnum(tls.ContentType.change_cipher_spec)} ++743 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
760 int2(@intFromEnum(tls.ProtocolVersion.tls_1_2)) ++ // legacy protocol version744 array(u16, tls.ChangeCipherSpecType, .{.change_cipher_spec});
761 int2(1) ++ // length
762 .{0x01};
763 const app_cipher = app_cipher: switch (handshake_cipher) {745 const app_cipher = app_cipher: switch (handshake_cipher) {
764 inline else => |*p, tag| switch (tls_version) {746 inline else => |*p, tag| switch (tls_version) {
765 .tls_1_3 => {747 .tls_1_3 => {
...@@ -771,17 +753,15 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -771,17 +753,15 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
771 if (!mem.eql(u8, &expected_server_verify_data, hsd.buf)) return error.TlsDecryptError;753 if (!mem.eql(u8, &expected_server_verify_data, hsd.buf)) return error.TlsDecryptError;
772 const handshake_hash = p.transcript_hash.finalResult();754 const handshake_hash = p.transcript_hash.finalResult();
773 const verify_data = tls.hmac(P.Hmac, &handshake_hash, pv.client_finished_key);755 const verify_data = tls.hmac(P.Hmac, &handshake_hash, pv.client_finished_key);
774 const out_cleartext = [_]u8{756 const out_cleartext = .{@intFromEnum(tls.HandshakeType.finished)} ++
775 @intFromEnum(tls.HandshakeType.finished),757 array(u24, u8, verify_data) ++
776 0, 0, verify_data.len, // length758 .{@intFromEnum(tls.ContentType.handshake)};
777 } ++ verify_data ++ [1]u8{@intFromEnum(tls.ContentType.handshake)};
778759
779 const wrapped_len = out_cleartext.len + P.AEAD.tag_length;760 const wrapped_len = out_cleartext.len + P.AEAD.tag_length;
780761
781 var finished_msg = [_]u8{@intFromEnum(tls.ContentType.application_data)} ++762 var finished_msg = .{@intFromEnum(tls.ContentType.application_data)} ++
782 int2(@intFromEnum(tls.ProtocolVersion.tls_1_2)) ++ // legacy protocol version763 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
783 int2(wrapped_len) ++ // byte length of encrypted record764 array(u16, u8, @as([wrapped_len]u8, undefined));
784 @as([wrapped_len]u8, undefined);
785765
786 const ad = finished_msg[0..tls.record_header_len];766 const ad = finished_msg[0..tls.record_header_len];
787 const ciphertext = finished_msg[tls.record_header_len..][0..out_cleartext.len];767 const ciphertext = finished_msg[tls.record_header_len..][0..out_cleartext.len];
...@@ -790,10 +770,9 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -790,10 +770,9 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
790 P.AEAD.encrypt(ciphertext, auth_tag, &out_cleartext, ad, nonce, pv.client_handshake_key);770 P.AEAD.encrypt(ciphertext, auth_tag, &out_cleartext, ad, nonce, pv.client_handshake_key);
791771
792 const all_msgs = client_change_cipher_spec_msg ++ finished_msg;772 const all_msgs = client_change_cipher_spec_msg ++ finished_msg;
793 var all_msgs_vec = [_]std.posix.iovec_const{.{773 var all_msgs_vec = [_]std.posix.iovec_const{
794 .base = &all_msgs,774 .{ .base = &all_msgs, .len = all_msgs.len },
795 .len = all_msgs.len,775 };
796 }};
797 try stream.writevAll(&all_msgs_vec);776 try stream.writevAll(&all_msgs_vec);
798777
799 const client_secret = hkdfExpandLabel(P.Hkdf, pv.master_secret, "c ap traffic", &handshake_hash, P.Hash.digest_length);778 const client_secret = hkdfExpandLabel(P.Hkdf, pv.master_secret, "c ap traffic", &handshake_hash, P.Hash.digest_length);
...@@ -965,10 +944,9 @@ fn prepareCiphertextRecord(...@@ -965,10 +944,9 @@ fn prepareCiphertextRecord(
965944
966 const record_start = ciphertext_end;945 const record_start = ciphertext_end;
967 const ad = ciphertext_buf[ciphertext_end..][0..tls.record_header_len];946 const ad = ciphertext_buf[ciphertext_end..][0..tls.record_header_len];
968 ad.* =947 ad.* = .{@intFromEnum(tls.ContentType.application_data)} ++
969 [_]u8{@intFromEnum(tls.ContentType.application_data)} ++948 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
970 int2(@intFromEnum(tls.ProtocolVersion.tls_1_2)) ++949 int(u16, ciphertext_len + P.AEAD.tag_length);
971 int2(ciphertext_len + P.AEAD.tag_length);
972 ciphertext_end += ad.len;950 ciphertext_end += ad.len;
973 const ciphertext = ciphertext_buf[ciphertext_end..][0..ciphertext_len];951 const ciphertext = ciphertext_buf[ciphertext_end..][0..ciphertext_len];
974 ciphertext_end += ciphertext_len;952 ciphertext_end += ciphertext_len;
...@@ -1023,10 +1001,10 @@ fn prepareCiphertextRecord(...@@ -1023,10 +1001,10 @@ fn prepareCiphertextRecord(
1023 const record_start = ciphertext_end;1001 const record_start = ciphertext_end;
1024 const record_header = ciphertext_buf[ciphertext_end..][0..tls.record_header_len];1002 const record_header = ciphertext_buf[ciphertext_end..][0..tls.record_header_len];
1025 ciphertext_end += tls.record_header_len;1003 ciphertext_end += tls.record_header_len;
1026 record_header.* = [_]u8{@intFromEnum(inner_content_type)} ++1004 record_header.* = .{@intFromEnum(inner_content_type)} ++
1027 int2(@intFromEnum(tls.ProtocolVersion.tls_1_2)) ++1005 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
1028 int2(P.record_iv_length + message_len + P.mac_length);1006 int(u16, P.record_iv_length + message_len + P.mac_length);
1029 const ad = std.mem.toBytes(big(c.write_seq)) ++ record_header[0 .. 1 + 2] ++ int2(message_len);1007 const ad = std.mem.toBytes(big(c.write_seq)) ++ record_header[0 .. 1 + 2] ++ int(u16, message_len);
1030 const record_iv = ciphertext_buf[ciphertext_end..][0..P.record_iv_length];1008 const record_iv = ciphertext_buf[ciphertext_end..][0..P.record_iv_length];
1031 ciphertext_end += P.record_iv_length;1009 ciphertext_end += P.record_iv_length;
1032 const nonce: [P.AEAD.nonce_length]u8 = if (builtin.zig_backend == .stage2_x86_64 and1010 const nonce: [P.AEAD.nonce_length]u8 = if (builtin.zig_backend == .stage2_x86_64 and
...@@ -1569,25 +1547,17 @@ const KeyShare = struct {...@@ -1569,25 +1547,17 @@ const KeyShare = struct {
1569 ) error{ TlsIllegalParameter, TlsDecryptFailure }!void {1547 ) error{ TlsIllegalParameter, TlsDecryptFailure }!void {
1570 switch (named_group) {1548 switch (named_group) {
1571 .x25519_ml_kem768 => {1549 .x25519_ml_kem768 => {
1572 const xksl = crypto.dh.X25519.public_length;1550 const hksl = crypto.kem.ml_kem.MLKem768.ciphertext_length;
1573 const hksl = xksl + crypto.kem.ml_kem.MLKem768.ciphertext_length;1551 const xksl = hksl + crypto.dh.X25519.public_length;
1574 if (server_pub_key.len != hksl) return error.TlsIllegalParameter;1552 if (server_pub_key.len != xksl) return error.TlsIllegalParameter;
15751553
1576 const xsk = crypto.dh.X25519.scalarmult(ks.x25519_kp.secret_key, server_pub_key[0..xksl].*) catch1554 const hsk = ks.ml_kem768_kp.secret_key.decaps(server_pub_key[0..hksl]) catch
1577 return error.TlsDecryptFailure;1555 return error.TlsDecryptFailure;
1578 const hsk = ks.ml_kem768_kp.secret_key.decaps(server_pub_key[xksl..hksl]) catch1556 const xsk = crypto.dh.X25519.scalarmult(ks.x25519_kp.secret_key, server_pub_key[hksl..xksl].*) catch
1579 return error.TlsDecryptFailure;1557 return error.TlsDecryptFailure;
1580 @memcpy(ks.sk_buf[0..xsk.len], &xsk);1558 @memcpy(ks.sk_buf[0..hsk.len], &hsk);
1581 @memcpy(ks.sk_buf[xsk.len..][0..hsk.len], &hsk);1559 @memcpy(ks.sk_buf[hsk.len..][0..xsk.len], &xsk);
1582 ks.sk_len = xsk.len + hsk.len;1560 ks.sk_len = hsk.len + xsk.len;
1583 },
1584 .x25519 => {
1585 const ksl = crypto.dh.X25519.public_length;
1586 if (server_pub_key.len != ksl) return error.TlsIllegalParameter;
1587 const sk = crypto.dh.X25519.scalarmult(ks.x25519_kp.secret_key, server_pub_key[0..ksl].*) catch
1588 return error.TlsDecryptFailure;
1589 @memcpy(ks.sk_buf[0..sk.len], &sk);
1590 ks.sk_len = sk.len;
1591 },1561 },
1592 .secp256r1 => {1562 .secp256r1 => {
1593 const PublicKey = crypto.sign.ecdsa.EcdsaP256Sha256.PublicKey;1563 const PublicKey = crypto.sign.ecdsa.EcdsaP256Sha256.PublicKey;
...@@ -1598,6 +1568,14 @@ const KeyShare = struct {...@@ -1598,6 +1568,14 @@ const KeyShare = struct {
1598 @memcpy(ks.sk_buf[0..sk.len], &sk);1568 @memcpy(ks.sk_buf[0..sk.len], &sk);
1599 ks.sk_len = sk.len;1569 ks.sk_len = sk.len;
1600 },1570 },
1571 .x25519 => {
1572 const ksl = crypto.dh.X25519.public_length;
1573 if (server_pub_key.len != ksl) return error.TlsIllegalParameter;
1574 const sk = crypto.dh.X25519.scalarmult(ks.x25519_kp.secret_key, server_pub_key[0..ksl].*) catch
1575 return error.TlsDecryptFailure;
1576 @memcpy(ks.sk_buf[0..sk.len], &sk);
1577 ks.sk_len = sk.len;
1578 },
1601 else => return error.TlsIllegalParameter,1579 else => return error.TlsIllegalParameter,
1602 }1580 }
1603 }1581 }
...@@ -1877,7 +1855,7 @@ fn limitVecs(iovecs: []std.posix.iovec, len: usize) []std.posix.iovec {...@@ -1877,7 +1855,7 @@ fn limitVecs(iovecs: []std.posix.iovec, len: usize) []std.posix.iovec {
1877/// aes128-gcm: 138 MiB/s1855/// aes128-gcm: 138 MiB/s
1878/// aes256-gcm: 120 MiB/s1856/// aes256-gcm: 120 MiB/s
1879const cipher_suites = if (crypto.core.aes.has_hardware_support)1857const cipher_suites = if (crypto.core.aes.has_hardware_support)
1880 enum_array(tls.CipherSuite, &.{1858 array(u16, tls.CipherSuite, .{
1881 .AEGIS_128L_SHA256,1859 .AEGIS_128L_SHA256,
1882 .AEGIS_256_SHA512,1860 .AEGIS_256_SHA512,
1883 .AES_128_GCM_SHA256,1861 .AES_128_GCM_SHA256,
...@@ -1888,7 +1866,7 @@ const cipher_suites = if (crypto.core.aes.has_hardware_support)...@@ -1888,7 +1866,7 @@ const cipher_suites = if (crypto.core.aes.has_hardware_support)
1888 .ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,1866 .ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
1889 })1867 })
1890else1868else
1891 enum_array(tls.CipherSuite, &.{1869 array(u16, tls.CipherSuite, .{
1892 .CHACHA20_POLY1305_SHA256,1870 .CHACHA20_POLY1305_SHA256,
1893 .ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,1871 .ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
1894 .AEGIS_128L_SHA256,1872 .AEGIS_128L_SHA256,