ReviewOS

also looking at this

mail-os/mail

Harden encrypted mail storage, recovery, and forwarding

#8
Merged chris wants to merge codex/mail-storage-hardening into main
50 files +1701 -9001

Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.

packages/zig/build.zig.zonmodified+4-1
Changes to packages/zig/build.zig.zon
@@ -33,7 +33,10 @@
3333 // internet connectivity.
3434 .dependencies = .{
3535 .tls = .{
36 .path = "vendor/zig-tls",
36 // Keep TLS sourced from the canonical zig-utils/zig-tls repository
37 // instead of carrying a divergent snapshot under vendor/.
38 .url = "git+https://github.com/zig-utils/zig-tls.git#42f4ea2398868c6a1d45b99a2b1a518e13fb10f2",
39 .hash = "zig_tls-0.1.1-CwcE5Jb4JwAOOgutzm4ZFMftuFwc3Bjdfc6ceyQRSXuq",
3740 },
3841 .search_engine = .{
3942 .path = "vendor/zig-search-engine",
packages/zig/src/core/protocol.zigmodified+4-4
Changes to packages/zig/src/core/protocol.zig
@@ -176,13 +176,13 @@ fn hasReverseDns(allocator: std.mem.Allocator, ip: []const u8) bool { renamed throughout
176176 });
177177 const rc1 = std.c.getaddrinfo(ip_z.ptr, null, &hints, &res);
178178 defer if (res) |r| std.c.freeaddrinfo(r);
179 if (@intFromEnum(rc1) != 0) return true; // unparseable -> don't penalize
179 if (@backingInt(rc1) != 0) return true; // unparseable -> don't penalize
180180 const node = res orelse return true;
181181 const sa = node.addr orelse return true;
182182
183183 var host_buf: [256]u8 = undefined;
184184 const rc2 = std.c.getnameinfo(sa, node.addrlen, &host_buf, @intCast(host_buf.len), null, 0, .{});
185 if (@intFromEnum(rc2) != 0) return true; // lookup error -> don't penalize
185 if (@backingInt(rc2) != 0) return true; // lookup error -> don't penalize
186186 const host = std.mem.sliceTo(&host_buf, 0);
187187 return !std.mem.eql(u8, host, ip); // numeric echo means no PTR
188188}
@@ -680,7 +680,7 @@ pub const Session = struct {
680680 self.conn_wrapper.deinitTls();
681681 // Clean up TLS reader/writer - use stored alignment
682682 if (self.tls_reader_info) |info| {
683 const log2_align: std.mem.Alignment = @enumFromInt(std.math.log2(info.alignment));
683 const log2_align: std.mem.Alignment = @fromBackingInt(@intCast(std.math.log2(info.alignment)));
684684 const ptr_bytes: [*]u8 = @ptrCast(info.ptr);
685685 switch (info.alignment) {
686686 1 => self.allocator.rawFree(ptr_bytes[0..info.size], log2_align, @returnAddress()),
@@ -692,7 +692,7 @@ pub const Session = struct {
692692 }
693693 }
694694 if (self.tls_writer_info) |info| {
695 const log2_align: std.mem.Alignment = @enumFromInt(std.math.log2(info.alignment));
695 const log2_align: std.mem.Alignment = @fromBackingInt(@intCast(std.math.log2(info.alignment)));
696696 const ptr_bytes: [*]u8 = @ptrCast(info.ptr);
697697 switch (info.alignment) {
698698 1 => self.allocator.rawFree(ptr_bytes[0..info.size], log2_align, @returnAddress()),
packages/zig/src/features/sieve.zigmodified+2-2
Changes to packages/zig/src/features/sieve.zig
@@ -280,8 +280,8 @@ pub const SieveParser = struct {
280280 .pos = 0,
281281 .line = 1,
282282 .column = 1,
283 .errors = .{},
284 .requires = .{},
283 .errors = .empty,
284 .requires = .empty,
285285 };
286286 }
287287
packages/zig/vendor/zig-tls/LICENSEdeleted+0-22
Changes to packages/zig/vendor/zig-tls/LICENSE
@@ -1,22 +0,0 @@
1MIT License
2
3Copyright (c) 2024 zig-tls contributors
4Based on tls.zig by Igor Anić (https://github.com/ianic/tls.zig)
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
packages/zig/vendor/zig-tls/README.mddeleted+0-62
Changes to packages/zig/vendor/zig-tls/README.md
@@ -1,62 +0,0 @@
1# zig-tls
2
3Pure Zig TLS 1.3 implementation for SMTP and other protocols.
4
5## Features
6
7- TLS 1.3 client and server support
8- Pure Zig implementation (no C dependencies)
9- Non-blocking I/O
10- STARTTLS support for protocol upgrades
11- Certificate and private key management
12
13## Installation
14
15Add to your `build.zig.zon`:
16
17```zig
18.dependencies = .{
19 .tls = .{
20 .path = "../zig-tls",
21 },
22},
23```
24
25Add to your `build.zig`:
26
27```zig
28const tls = b.dependency("tls", .{
29 .target = target,
30 .optimize = optimize,
31});
32exe.root_module.addImport("tls", tls.module("tls"));
33```
34
35## Usage
36
37### Server Example
38
39```zig
40const tls = @import("tls");
41
42// Load certificate and key
43var cert_key = try tls.config.CertKeyPair.fromFilePathAbsolute(
44 allocator,
45 "/path/to/cert.pem",
46 "/path/to/key.pem",
47);
48defer cert_key.deinit(allocator);
49
50// Create TLS connection from stream
51const tls_conn = try tls.serverFromStream(stream, .{
52 .auth = &cert_key,
53});
54
55// Read/write through TLS
56const n = try tls_conn.read(buffer);
57try tls_conn.write(data);
58```
59
60## License
61
62MIT - Based on https://github.com/ianic/tls.zig
packages/zig/vendor/zig-tls/build.zigdeleted+0-27
Changes to packages/zig/vendor/zig-tls/build.zig
@@ -1,27 +0,0 @@
1const std = ;
2
3pub fn build(b: *std.Build) void {
4 const target = b.standardTargetOptions(.{});
5 const optimize = b.standardOptimizeOption(.{});
6
7 // Main TLS module
8 const tls_module = b.addModule("tls", .{
9 .root_source_file = b.path("src/root.zig"),
10 .target = target,
11 .optimize = optimize,
12 });
13 _ = tls_module;
14
15 // Unit tests
16 const lib_mod = b.createModule(.{
17 .root_source_file = b.path("src/root.zig"),
18 .target = target,
19 .optimize = optimize,
20 });
21 const unit_tests = b.addTest(.{
22 .root_module = lib_mod,
23 });
24 const run_unit_tests = b.addRunArtifact(unit_tests);
25 const test_step = b.step("test", "Run unit tests");
26 test_step.dependOn(&run_unit_tests.step);
27}
packages/zig/vendor/zig-tls/build.zig.zondeleted+0-14
Changes to packages/zig/vendor/zig-tls/build.zig.zon
@@ -1,14 +0,0 @@
1.{
2 .name = .zig_tls,
3 .version = "0.1.1",
4 .minimum_zig_version = "0.15.0",
5 .fingerprint = 0x114f7e81e404070b,
6 .dependencies = .{},
7 .paths = .{
8 "README.md",
9 "LICENSE",
10 "build.zig",
11 "build.zig.zon",
12 "src",
13 },
14}
packages/zig/vendor/zig-tls/src/Ktls.zigdeleted+0-141
Changes to packages/zig/vendor/zig-tls/src/Ktls.zig
@@ -1,141 +0,0 @@
1//! Kernel structs and constants from: /usr/include/linux/tls.h or
2//! https://github.com/torvalds/linux/blob/master/include/uapi/linux/tls.h
3
4const std = ;
5const mem = std.mem;
6const Cipher = .Cipher;
7
8const Ktls = @This();
9
10const U = union(enum) {
11 aes_gcm_128: AesGcm128,
12 aes_gcm_256: AesGcm256,
13 chacha20_poly1305: Chacha20Poly1305,
14};
15
16tx: U,
17rx: U,
18
19pub fn txBytes(k: *Ktls) []const u8 {
20 return switch (k.tx) {
21 inline else => |*v| mem.asBytes(v),
22 };
23}
24
25pub fn rxBytes(k: *Ktls) []const u8 {
26 return switch (k.rx) {
27 inline else => |*v| mem.asBytes(v),
28 };
29}
30
31pub const VERSION_1_2 = 0x0303;
32pub const VERSION_1_3 = 0x0304;
33pub const TX = 1;
34pub const RX = 2;
35
36pub const AES_GCM_128 = 51;
37pub const AES_GCM_256 = 52;
38pub const CHACHA20_POLY1305 = 54;
39
40pub const Info = extern struct {
41 version: u16 = VERSION_1_3,
42 cipher_type: u16 = mem.zeroes(u16),
43};
44pub const AesGcm128 = extern struct {
45 info: Info = mem.zeroes(Info),
46 iv: [8]u8 = mem.zeroes([8]u8),
47 key: [16]u8 = mem.zeroes([16]u8),
48 salt: [4]u8 = mem.zeroes([4]u8),
49 rec_seq: [8]u8 = mem.zeroes([8]u8),
50};
51pub const AesGcm256 = extern struct {
52 info: Info = mem.zeroes(Info),
53 iv: [8]u8 = mem.zeroes([8]u8),
54 key: [32]u8 = mem.zeroes([32]u8),
55 salt: [4]u8 = mem.zeroes([4]u8),
56 rec_seq: [8]u8 = mem.zeroes([8]u8),
57};
58pub const Chacha20Poly1305 = extern struct {
59 info: Info = mem.zeroes(Info),
60 iv: [12]u8 = mem.zeroes([12]u8),
61 key: [32]u8 = mem.zeroes([32]u8),
62 salt: [0]u8 = mem.zeroes([0]u8),
63 rec_seq: [8]u8 = mem.zeroes([8]u8),
64};
65
66pub fn init(cipher: Cipher) Ktls {
67 switch (cipher) {
68 // TLS 1.3 ciphers
69 .AES_128_GCM_SHA256 => |keys| {
70 const k = makeKeys(AesGcm128, .{ .cipher_type = AES_GCM_128 }, keys);
71 return .{
72 .tx = .{ .aes_gcm_128 = k.tx },
73 .rx = .{ .aes_gcm_128 = k.rx },
74 };
75 },
76 .AES_256_GCM_SHA384 => |keys| {
77 const k = makeKeys(AesGcm256, .{ .cipher_type = AES_GCM_256 }, keys);
78 return .{
79 .tx = .{ .aes_gcm_256 = k.tx },
80 .rx = .{ .aes_gcm_256 = k.rx },
81 };
82 },
83 .CHACHA20_POLY1305_SHA256 => |keys| {
84 const k = makeKeys(Chacha20Poly1305, .{ .cipher_type = CHACHA20_POLY1305 }, keys);
85 return .{
86 .tx = .{ .chacha20_poly1305 = k.tx },
87 .rx = .{ .chacha20_poly1305 = k.rx },
88 };
89 },
90
91 // TLS 1.2 ciphers
92 .ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
93 .ECDHE_RSA_WITH_AES_128_GCM_SHA256,
94 => |keys| {
95 const k = makeKeys(AesGcm128, .{ .cipher_type = AES_GCM_128, .version = VERSION_1_2 }, keys);
96 return .{
97 .tx = .{ .aes_gcm_128 = k.tx },
98 .rx = .{ .aes_gcm_128 = k.rx },
99 };
100 },
101 .ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
102 .ECDHE_RSA_WITH_AES_256_GCM_SHA384,
103 => |keys| {
104 const k = makeKeys(AesGcm256, .{ .cipher_type = AES_GCM_256, .version = VERSION_1_2 }, keys);
105 return .{
106 .tx = .{ .aes_gcm_256 = k.tx },
107 .rx = .{ .aes_gcm_256 = k.rx },
108 };
109 },
110 .ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
111 .ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
112 => |keys| {
113 const k = makeKeys(Chacha20Poly1305, .{ .cipher_type = CHACHA20_POLY1305, .version = VERSION_1_2 }, keys);
114 return .{
115 .tx = .{ .chacha20_poly1305 = k.tx },
116 .rx = .{ .chacha20_poly1305 = k.rx },
117 };
118 },
119
120 else => unreachable,
121 }
122}
123
124fn makeKeys(comptime T: type, info: Info, keys: anytype) struct { tx: T, rx: T } {
125 return .{
126 .tx = makeKey(T, info, keys.encrypt_iv, keys.encrypt_key, keys.encrypt_seq),
127 .rx = makeKey(T, info, keys.decrypt_iv, keys.decrypt_key, keys.decrypt_seq),
128 };
129}
130
131fn makeKey(comptime T: type, info: Info, iv: anytype, key: anytype, seq: u64) T {
132 const salt_size = @sizeOf(@FieldType(T, "salt"));
133 var t: T = .{
134 .info = info,
135 .salt = iv[0..salt_size].*,
136 .iv = if (iv.len > salt_size) iv[salt_size..].* else @splat(0),
137 .key = key,
138 };
139 mem.writeInt(u64, &t.rec_seq, seq, .big);
140 return t;
141}
packages/zig/vendor/zig-tls/src/PrivateKey.zigdeleted+0-267
Changes to packages/zig/vendor/zig-tls/src/PrivateKey.zig
@@ -1,267 +0,0 @@
1const std = ;
2const Allocator = std.mem.Allocator;
3const Certificate = std.crypto.Certificate;
4const der = Certificate.der;
5const rsa = ;
6const base64 = std.base64.standard.decoderWithIgnore(" \t\r\n");
7const proto = ;
8
9const max_ecdsa_key_len = 66;
10
11signature_scheme: proto.SignatureScheme,
12
13key: union {
14 rsa: rsa.KeyPair,
15 ecdsa: [max_ecdsa_key_len]u8,
16},
17
18const PrivateKey = @This();
19
20pub fn fromFile(_: Allocator, file: std.Io.File) !PrivateKey {
21 // Read file using posix since Zig 0.16 File.reader requires Io context
22 const fd = file.handle;
23 var buf: [8192]u8 = undefined;
24 var total: usize = 0;
25 while (total < buf.len) {
26 const n = std.posix.read(fd, buf[total..]) catch break;
27 if (n == 0) break;
28 total += n;
29 }
30 return try parsePem(buf[0..total]);
31}
32
33pub fn parsePem(buf: []const u8) !PrivateKey {
34 const key_start, const key_end, const marker_version = try findKey(buf);
35 const encoded = std.mem.trim(u8, buf[key_start..key_end], " \t\r\n");
36
37 // required bytes:
38 // 2412, 1821, 1236 for rsa 4096, 3072, 2048 bits size keys
39 var decoded: [4096]u8 = undefined;
40 const n = try base64.decode(&decoded, encoded);
41
42 if (marker_version == 2) {
43 return try parseEcDer(decoded[0..n]);
44 }
45 return try parseDer(decoded[0..n]);
46}
47
48fn findKey(buf: []const u8) !struct { usize, usize, usize } {
49 const markers = [_]struct {
50 begin: []const u8,
51 end: []const u8,
52 }{
53 .{ .begin = "-----BEGIN PRIVATE KEY-----", .end = "-----END PRIVATE KEY-----" },
54 .{ .begin = "-----BEGIN EC PRIVATE KEY-----", .end = "-----END EC PRIVATE KEY-----" },
55 };
56
57 for (markers, 1..) |marker, ver| {
58 const begin_marker_start = std.mem.indexOfPos(u8, buf, 0, marker.begin) orelse continue;
59 const key_start = begin_marker_start + marker.begin.len;
60 const key_end = std.mem.indexOfPos(u8, buf, key_start, marker.end) orelse continue;
61
62 return .{ key_start, key_end, ver };
63 }
64 return error.MissingEndMarker;
65}
66
67// ref: https://asn1js.eu/#MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBKFkVJCtU9FR6egz3yNxKBwXd86cFzMYqyGb8hRc1zVvLdw-So_2FBtITp6jzYmFShZANiAAQ-CH3a1R0V6dFlTK8Rs4M4egrpPtdta0osysO0Zl8mkBiDsTlvJNqeAp7L2ItHgFW8k_CfhgQT6iLDacNMhKC4XOV07r_ePD-mmkvqvRmzfOowHUoVRhCKrOTmF_J9Syc
68pub fn parseDer(buf: []const u8) !PrivateKey {
69 const info = try der.Element.parse(buf, 0);
70 const version = try der.Element.parse(buf, info.slice.start);
71
72 const algo_seq = try der.Element.parse(buf, version.slice.end);
73 const algo_cat = try der.Element.parse(buf, algo_seq.slice.start);
74
75 const key_str = try der.Element.parse(buf, algo_seq.slice.end);
76 const key_seq = try der.Element.parse(buf, key_str.slice.start);
77 const key_int = try der.Element.parse(buf, key_seq.slice.start);
78
79 const category = try Certificate.parseAlgorithmCategory(buf, algo_cat);
80 switch (category) {
81 .rsaEncryption => {
82 const modulus = try der.Element.parse(buf, key_int.slice.end);
83 const public_exponent = try der.Element.parse(buf, modulus.slice.end);
84 const private_exponent = try der.Element.parse(buf, public_exponent.slice.end);
85
86 const public_key = try rsa.PublicKey.fromBytes(content(buf, modulus), content(buf, public_exponent));
87 const secret_key = try rsa.SecretKey.fromBytes(public_key.modulus, content(buf, private_exponent));
88 const key_pair = rsa.KeyPair{ .public = public_key, .secret = secret_key };
89
90 return .{
91 .signature_scheme = switch (key_pair.public.modulus.bits()) {
92 4096 => .rsa_pss_rsae_sha512,
93 3072 => .rsa_pss_rsae_sha384,
94 else => .rsa_pss_rsae_sha256,
95 },
96 .key = .{ .rsa = key_pair },
97 };
98 },
99 .X9_62_id_ecPublicKey => {
100 const key = try der.Element.parse(buf, key_int.slice.end);
101 const algo_param = try der.Element.parse(buf, algo_cat.slice.end);
102 const named_curve = try Certificate.parseNamedCurve(buf, algo_param);
103 return .{
104 .signature_scheme = signatureScheme(named_curve),
105 .key = .{ .ecdsa = ecdsaKey(buf, key) },
106 };
107 },
108 else => unreachable,
109 }
110}
111
112// References:
113// https://asn1js.eu/#MHcCAQEEINJSRKv8kSKEzLHptfAlg-LGh4_pHHlq0XLf30Q9pcztoAoGCCqGSM49AwEHoUQDQgAEJpmLyp8aGCgyMcFIJaIq_-4V1K6nPpeoih3bT2npeplF9eyXj7rm8eW9Ua6VLhq71mqtMC-YLm-IkORBVq1cuA
114// https://www.rfc-editor.org/rfc/rfc5915
115pub fn parseEcDer(bytes: []const u8) !PrivateKey {
116 const pki_msg = try der.Element.parse(bytes, 0);
117 const version = try der.Element.parse(bytes, pki_msg.slice.start);
118 const key = try der.Element.parse(bytes, version.slice.end);
119 const parameters = try der.Element.parse(bytes, key.slice.end);
120 const curve = try der.Element.parse(bytes, parameters.slice.start);
121 const named_curve = try Certificate.parseNamedCurve(bytes, curve);
122 return .{
123 .signature_scheme = signatureScheme(named_curve),
124 .key = .{ .ecdsa = ecdsaKey(bytes, key) },
125 };
126}
127
128fn signatureScheme(named_curve: Certificate.NamedCurve) proto.SignatureScheme {
129 return switch (named_curve) {
130 .X9_62_prime256v1 => .ecdsa_secp256r1_sha256,
131 .secp384r1 => .ecdsa_secp384r1_sha384,
132 .secp521r1 => .ecdsa_secp521r1_sha512,
133 };
134}
135
136fn ecdsaKey(bytes: []const u8, e: der.Element) [max_ecdsa_key_len]u8 {
137 const data = content(bytes, e);
138 var ecdsa_key: [max_ecdsa_key_len]u8 = undefined;
139 @memcpy(ecdsa_key[0..data.len], data);
140 return ecdsa_key;
141}
142
143fn content(bytes: []const u8, e: der.Element) []const u8 {
144 return bytes[e.slice.start..e.slice.end];
145}
146
147const testing = std.testing;
148const testu = ;
149
150test "parse ec pem" {
151 const data = @embedFile("testdata/ec_private_key.pem");
152 var pk = try parsePem(data);
153 const priv_key = &testu.hexToBytes(
154 \\ 10 35 3d ca 1b 15 1d 06 aa 71 b8 ef f3 19 22
155 \\ 43 78 f3 20 98 1e b1 2f 2b 64 7e 71 d0 30 2a
156 \\ 90 aa e5 eb 99 c3 90 65 3d c1 26 19 be 3f 08
157 \\ 20 9b 01
158 );
159 try testing.expectEqualSlices(u8, priv_key, pk.key.ecdsa[0..priv_key.len]);
160 try testing.expectEqual(.ecdsa_secp384r1_sha384, pk.signature_scheme);
161}
162
163test "parse ec prime256v1" {
164 const data = @embedFile("testdata/ec_prime256v1_private_key.pem");
165 var pk = try parsePem(data);
166 const priv_key = &testu.hexToBytes(
167 \\ d2 52 44 ab fc 91 22 84 cc b1 e9 b5 f0 25 83
168 \\ e2 c6 87 8f e9 1c 79 6a d1 72 df df 44 3d a5
169 \\ cc ed
170 );
171 try testing.expectEqualSlices(u8, priv_key, pk.key.ecdsa[0..priv_key.len]);
172 try testing.expectEqual(.ecdsa_secp256r1_sha256, pk.signature_scheme);
173}
174
175test "parse ec secp384r1" {
176 const data = @embedFile("testdata/ec_secp384r1_private_key.pem");
177 var pk = try parsePem(data);
178 const priv_key = &testu.hexToBytes(
179 \\ ee 6d 8a 5e 0d d3 b0 c6 4b 32 40 80 e2 3a de
180 \\ 8b 1e dd e2 92 db 36 1c db 91 ea ba a1 06 0d
181 \\ 42 2d d9 a9 dc 05 43 29 f1 78 7c f9 08 af c5
182 \\ 03 1f 6d
183 );
184 try testing.expectEqualSlices(u8, priv_key, pk.key.ecdsa[0..priv_key.len]);
185 try testing.expectEqual(.ecdsa_secp384r1_sha384, pk.signature_scheme);
186}
187
188test "parse ec secp521r1" {
189 const data = @embedFile("testdata/ec_secp521r1_private_key.pem");
190 var pk = try parsePem(data);
191 const priv_key = &testu.hexToBytes(
192 \\ 01 f0 2f 5a c7 24 18 ea 68 23 8c 2e a1 b4 b8
193 \\ dc f2 11 b2 96 b0 ec 87 80 42 bf de ba f4 96
194 \\ 83 8f 9b db c6 60 a7 4c d9 60 3a e4 ba 0b df
195 \\ ae 24 d3 1b c2 6e 82 a0 88 c1 ed 17 20 0d 3a
196 \\ f1 c5 7e e8 0b 27
197 );
198 try testing.expectEqualSlices(u8, priv_key, pk.key.ecdsa[0..priv_key.len]);
199 try testing.expectEqual(.ecdsa_secp521r1_sha512, pk.signature_scheme);
200}
201
202test "parse rsa pem" {
203 const data = @embedFile("testdata/rsa_private_key.pem");
204 const pk = try parsePem(data);
205
206 // expected results from:
207 // $ openssl pkey -in testdata/rsa_private_key.pem -text -noout
208 const modulus = &testu.hexToBytes(
209 \\ 00 de f7 23 e6 75 cc 6f dd d5 6e 0f 8c 09 f8
210 \\ 62 e3 60 1b c0 7d 8c d5 04 50 2c 36 e2 3b f7
211 \\ 33 9f a1 14 af be cf 1a 0f 4c f5 cb 39 70 0e
212 \\ 3b 97 d6 21 f7 48 91 79 ca 7c 68 fc ea 62 a1
213 \\ 5a 72 4f 78 57 0e cc f2 a3 50 05 f1 4c ca 51
214 \\ 73 10 9a 18 8e 71 f5 b4 c7 3e be 4c ef 37 d4
215 \\ 84 4b 82 1c ec 08 a3 cc 07 3d 5c 0b e5 85 3f
216 \\ fe b6 44 77 8f 3c 6a 2f 33 c3 5d f6 f2 29 46
217 \\ 04 25 7e 05 d9 f8 3b 2d a4 40 66 9f 0d 6d 1a
218 \\ fa bc 0a c5 8b 86 43 30 ef 14 20 41 9d b5 cc
219 \\ 3e 63 b5 48 04 27 c9 5c d3 62 28 5f f5 b6 e4
220 \\ 77 49 99 ac 84 4a a6 67 a5 9a 1a 37 c7 60 4c
221 \\ ba c1 70 cf 57 64 4a 21 ea 05 53 10 ec 94 71
222 \\ 4a 43 04 83 00 aa 5a 28 bc f2 8c 58 14 92 d2
223 \\ 83 17 f4 7b 29 0f e7 87 a2 47 b2 53 19 12 23
224 \\ fb 4b ce 5a f8 a1 84 f9 b1 f3 bf e3 fa 10 f8
225 \\ ad af 87 ce 03 0e a0 2c 13 71 57 c4 55 44 48
226 \\ 44 cb
227 );
228 const public_exponent = &testu.hexToBytes("01 00 01");
229 const private_exponent = &testu.hexToBytes(
230 \\ 50 3b 80 98 aa a5 11 50 33 40 32 aa 02 e0 75
231 \\ bd 3a 55 62 34 0b 9c 8f bb c5 dd 4e 15 a4 03
232 \\ d8 9a 5f 56 4a 84 3d ed 69 95 3d 37 03 02 ac
233 \\ 21 1c 36 06 c4 ff 4c 63 37 d7 93 c3 48 10 a5
234 \\ fa 62 6c 7c 6f 60 02 a4 0f e4 c3 8b 0d 76 b7
235 \\ c0 2e a3 4d 86 e6 92 d1 eb db 10 d6 38 31 ea
236 \\ 15 3d d1 e8 81 c7 67 60 e7 8c 9a df 51 ce d0
237 \\ 7a 88 32 b9 c1 54 b8 7d 98 fc d4 23 1a 05 0e
238 \\ f2 ea e1 72 29 28 2a 68 b7 90 18 80 1c 21 d6
239 \\ 36 a8 6b 4a 9c dd 14 b8 9f 85 ee 95 0b f4 c6
240 \\ 17 02 aa 4d ea 4d f9 39 d7 dd 9d b4 1d d2 f8
241 \\ 92 46 0f 18 41 80 f4 ea 27 55 29 f8 37 59 bf
242 \\ 43 ec a3 eb 19 ba bc 13 06 95 3d 25 4b c9 72
243 \\ cf 41 0a 6f aa cb 79 d4 7b fa b1 09 7c e2 2f
244 \\ 85 51 44 8b c6 97 8e 46 f9 6b ac 08 87 92 ce
245 \\ af 0b bf 8c bd 27 51 8f 09 e4 d3 f9 04 ac fa
246 \\ f2 04 70 3e d9 a6 28 17 c2 2d 74 e9 25 40 02
247 \\ 49
248 );
249
250 try testing.expectEqual(.rsa_pss_rsae_sha256, pk.signature_scheme);
251 const kp = pk.key.rsa;
252 {
253 var bytes: [modulus.len]u8 = undefined;
254 try kp.public.modulus.toBytes(&bytes, .big);
255 try testing.expectEqualSlices(u8, modulus, &bytes);
256 }
257 {
258 var bytes: [private_exponent.len]u8 = undefined;
259 try kp.public.public_exponent.toBytes(&bytes, .big);
260 try testing.expectEqualSlices(u8, public_exponent, bytes[bytes.len - public_exponent.len .. bytes.len]);
261 }
262 {
263 var btytes: [private_exponent.len]u8 = undefined;
264 try kp.secret.private_exponent.toBytes(&btytes, .big);
265 try testing.expectEqualSlices(u8, private_exponent, &btytes);
266 }
267}
packages/zig/vendor/zig-tls/src/cbc/main.zigdeleted+0-148
Changes to packages/zig/vendor/zig-tls/src/cbc/main.zig
@@ -1,148 +0,0 @@
1// This file is originally copied from: https://github.com/jedisct1/zig-cbc.
2//
3// It is modified then to have TLS padding insead of PKCS#7 padding.
4// Reference:
5// https://datatracker.ietf.org/doc/html/rfc5246/#section-6.2.3.2
6// https://crypto.stackexchange.com/questions/98917/on-the-correctness-of-the-padding-example-of-rfc-5246
7//
8// If required padding i n bytes
9// PKCS#7 padding is (n...n)
10// TLS padding is (n-1...n-1) - n times of n-1 value
11//
12const std = ;
13const aes = std.crypto.core.aes;
14const mem = std.mem;
15const debug = std.debug;
16
17/// CBC mode with TLS 1.2 padding
18///
19/// Important: the counter mode doesn't provide authenticated encryption: the ciphertext can be trivially modified without this being detected.
20/// If you need authenticated encryption, use anything from `std.crypto.aead` instead.
21/// If you really need to use CBC mode, make sure to use a MAC to authenticate the ciphertext.
22pub fn CBC(comptime BlockCipher: anytype) type {
23 const EncryptCtx = aes.AesEncryptCtx(BlockCipher);
24 const DecryptCtx = aes.AesDecryptCtx(BlockCipher);
25
26 return struct {
27 const Self = @This();
28
29 enc_ctx: EncryptCtx,
30 dec_ctx: DecryptCtx,
31
32 /// Initialize the CBC context with the given key.
33 pub fn init(key: [BlockCipher.key_bits / 8]u8) Self {
34 const enc_ctx = BlockCipher.initEnc(key);
35 const dec_ctx = DecryptCtx.initFromEnc(enc_ctx);
36
37 return Self{ .enc_ctx = enc_ctx, .dec_ctx = dec_ctx };
38 }
39
40 /// Return the length of the ciphertext given the length of the plaintext.
41 pub fn paddedLength(length: usize) usize {
42 return (std.math.divCeil(usize, length + 1, EncryptCtx.block_length) catch unreachable) * EncryptCtx.block_length;
43 }
44
45 /// Encrypt the given plaintext for the given IV.
46 /// The destination buffer must be large enough to hold the padded plaintext.
47 /// Use the `paddedLength()` function to compute the ciphertext size.
48 /// IV must be secret and unpredictable.
49 pub fn encrypt(self: Self, dst: []u8, src: []const u8, iv: [EncryptCtx.block_length]u8) void {
50 // Note: encryption *could* be parallelized, see https://research.kudelskisecurity.com/2022/11/17/some-aes-cbc-encryption-myth-busting/
51 const block_length = EncryptCtx.block_length;
52 const padded_length = paddedLength(src.len);
53 debug.assert(dst.len == padded_length); // destination buffer must hold the padded plaintext
54 var cv = iv;
55 var i: usize = 0;
56 while (i + block_length <= src.len) : (i += block_length) {
57 const in = src[i..][0..block_length];
58 for (cv[0..], in) |*x, y| x.* ^= y;
59 self.enc_ctx.encrypt(&cv, &cv);
60 @memcpy(dst[i..][0..block_length], &cv);
61 }
62 // Last block
63 var in: [block_length]u8 = @splat(0);
64 const padding_length: u8 = @intCast(padded_length - src.len - 1);
65 @memset(&in, padding_length);
66 @memcpy(in[0 .. src.len - i], src[i..]);
67 for (cv[0..], in) |*x, y| x.* ^= y;
68 self.enc_ctx.encrypt(&cv, &cv);
69 @memcpy(dst[i..], cv[0 .. dst.len - i]);
70 }
71
72 /// Decrypt the given ciphertext for the given IV.
73 /// The destination buffer must be large enough to hold the plaintext.
74 /// IV must be secret, unpredictable and match the one used for encryption.
75 pub fn decrypt(self: Self, dst: []u8, src: []const u8, iv: [DecryptCtx.block_length]u8) !void {
76 const block_length = DecryptCtx.block_length;
77 if (src.len != dst.len) {
78 return error.EncodingError;
79 }
80 debug.assert(src.len % block_length == 0);
81 var i: usize = 0;
82 var cv = iv;
83 var out: [block_length]u8 = undefined;
84 // Decryption could be parallelized
85 while (i + block_length <= dst.len) : (i += block_length) {
86 const in = src[i..][0..block_length];
87 self.dec_ctx.decrypt(&out, in);
88 for (&out, cv) |*x, y| x.* ^= y;
89 cv = in.*;
90 @memcpy(dst[i..][0..block_length], &out);
91 }
92 // Last block - We intentionally don't check the padding to mitigate timing attacks
93 if (i < dst.len) {
94 const in = src[i..][0..block_length];
95 @memset(&out, 0);
96 self.dec_ctx.decrypt(&out, in);
97 for (&out, cv) |*x, y| x.* ^= y;
98 @memcpy(dst[i..], out[0 .. dst.len - i]);
99 }
100 }
101 };
102}
103
104test "CBC mode" {
105 const M = CBC(aes.Aes128);
106 const key = [_]u8{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c };
107 const iv = [_]u8{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
108 const src_ = "This is a test of AES-CBC that goes on longer than a couple blocks. It is a somewhat long test case to type out!";
109 const expected = "\xA0\x8C\x09\x7D\xFF\x42\xB6\x65\x4D\x4B\xC6\x90\x90\x39\xDE\x3D\xC7\xCA\xEB\xF6\x9A\x4F\x09\x97\xC9\x32\xAB\x75\x88\xB7\x57\x17";
110 var res: [32]u8 = undefined;
111
112 try comptime std.testing.expect(src_.len / M.paddedLength(1) >= 3); // Ensure that we have at least 3 blocks
113
114 const z = M.init(key);
115
116 // Test encryption and decryption with distinct buffers
117 var h = std.crypto.hash.sha2.Sha256.init(.{});
118 inline for (0..src_.len) |len| {
119 const src = src_[0..len];
120 var dst: [M.paddedLength(src.len)]u8 = @splat(0);
121
122 z.encrypt(&dst, src, iv);
123 h.update(&dst);
124
125 var decrypted: [dst.len]u8 = @splat(0);
126 try z.decrypt(&decrypted, &dst, iv);
127
128 const padding = decrypted[decrypted.len - 1] + 1;
129 try std.testing.expectEqualSlices(u8, src, decrypted[0 .. decrypted.len - padding]);
130 }
131 h.final(&res);
132 try std.testing.expectEqualSlices(u8, expected, &res);
133
134 // Test encryption and decryption with the same buffer
135 h = std.crypto.hash.sha2.Sha256.init(.{});
136 inline for (0..src_.len) |len| {
137 var buf: [M.paddedLength(len)]u8 = @splat(0);
138 @memcpy(buf[0..len], src_[0..len]);
139 z.encrypt(&buf, buf[0..len], iv);
140 h.update(&buf);
141
142 try z.decrypt(&buf, &buf, iv);
143
144 try std.testing.expectEqualSlices(u8, src_[0..len], buf[0..len]);
145 }
146 h.final(&res);
147 try std.testing.expectEqualSlices(u8, expected, &res);
148}
packages/zig/vendor/zig-tls/src/cipher.zigdeleted+0-1089
Changes to packages/zig/vendor/zig-tls/src/cipher.zig
@@ -1,1089 +0,0 @@
1const std = ;
2const crypto = std.crypto;
3const hkdfExpandLabel = crypto.tls.hkdfExpandLabel;
4
5const Sha1 = crypto.hash.Sha1;
6const Sha256 = crypto.hash.sha2.Sha256;
7const Sha384 = crypto.hash.sha2.Sha384;
8
9const record = ;
10const Record = record.Record;
11const Transcript = .Transcript;
12const proto = ;
13
14const csprng = .csprng;
15
16// tls 1.2 cbc cipher types
17const CbcAes128Sha1 = CbcType(crypto.core.aes.Aes128, Sha1);
18const CbcAes128Sha256 = CbcType(crypto.core.aes.Aes128, Sha256);
19const CbcAes256Sha384 = CbcType(crypto.core.aes.Aes256, Sha384);
20// tls 1.2 gcm cipher types
21const Aead12Aes128Gcm = Aead12Type(crypto.aead.aes_gcm.Aes128Gcm);
22const Aead12Aes256Gcm = Aead12Type(crypto.aead.aes_gcm.Aes256Gcm);
23// tls 1.2 chacha cipher type
24const Aead12ChaCha = Aead12ChaChaType(crypto.aead.chacha_poly.ChaCha20Poly1305);
25// tls 1.3 cipher types
26const Aes128GcmSha256 = Aead13Type(crypto.aead.aes_gcm.Aes128Gcm, Sha256);
27const Aes256GcmSha384 = Aead13Type(crypto.aead.aes_gcm.Aes256Gcm, Sha384);
28const ChaChaSha256 = Aead13Type(crypto.aead.chacha_poly.ChaCha20Poly1305, Sha256);
29const Aegis128Sha256 = Aead13Type(crypto.aead.aegis.Aegis128L, Sha256);
30
31pub const encrypt_overhead_tls_12: comptime_int = @max(
32 CbcAes128Sha1.encrypt_overhead,
33 CbcAes128Sha256.encrypt_overhead,
34 CbcAes256Sha384.encrypt_overhead,
35 Aead12Aes128Gcm.encrypt_overhead,
36 Aead12Aes256Gcm.encrypt_overhead,
37 Aead12ChaCha.encrypt_overhead,
38);
39pub const encrypt_overhead_tls_13: comptime_int = @max(
40 Aes128GcmSha256.encrypt_overhead,
41 Aes256GcmSha384.encrypt_overhead,
42 ChaChaSha256.encrypt_overhead,
43 Aegis128Sha256.encrypt_overhead,
44);
45
46// ref (length): https://www.rfc-editor.org/rfc/rfc8446#section-5.1
47pub const max_cleartext_len = 1 << 14;
48// ref (length): https://www.rfc-editor.org/rfc/rfc8446#section-5.2
49// The sum of the lengths of the content and the padding, plus one for the inner
50// content type, plus any expansion added by the AEAD algorithm.
51pub const max_ciphertext_len = max_cleartext_len + 256;
52pub const max_ciphertext_record_len = record.header_len + max_ciphertext_len;
53// max ciphertext produced with this implementation ()
54pub const max_encrypted_record_len = max_cleartext_len + @max(encrypt_overhead_tls_13, encrypt_overhead_tls_12);
55
56test {
57 try testing.expectEqual(16645, max_ciphertext_record_len);
58 try testing.expectEqual(85, encrypt_overhead_tls_12);
59 try testing.expectEqual(22, encrypt_overhead_tls_13);
60 try testing.expectEqual(16469, max_encrypted_record_len);
61}
62
63/// Returns type for cipher suite tag.
64fn CipherType(comptime tag: CipherSuite) type {
65 return switch (tag) {
66 // tls 1.2 cbc
67 .ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
68 .ECDHE_RSA_WITH_AES_128_CBC_SHA,
69 .RSA_WITH_AES_128_CBC_SHA,
70 => CbcAes128Sha1,
71 .ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
72 .ECDHE_RSA_WITH_AES_128_CBC_SHA256,
73 .RSA_WITH_AES_128_CBC_SHA256,
74 => CbcAes128Sha256,
75 .ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
76 .ECDHE_RSA_WITH_AES_256_CBC_SHA384,
77 => CbcAes256Sha384,
78
79 // tls 1.2 gcm
80 .ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
81 .ECDHE_RSA_WITH_AES_128_GCM_SHA256,
82 => Aead12Aes128Gcm,
83 .ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
84 .ECDHE_RSA_WITH_AES_256_GCM_SHA384,
85 => Aead12Aes256Gcm,
86
87 // tls 1.2 chacha
88 .ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
89 .ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
90 => Aead12ChaCha,
91
92 // tls 1.3
93 .AES_128_GCM_SHA256 => Aes128GcmSha256,
94 .AES_256_GCM_SHA384 => Aes256GcmSha384,
95 .CHACHA20_POLY1305_SHA256 => ChaChaSha256,
96 .AEGIS_128L_SHA256 => Aegis128Sha256,
97
98 else => unreachable,
99 };
100}
101
102/// Provides initialization and common encrypt/decrypt methods for all supported
103/// ciphers. Tls 1.2 has only application cipher, tls 1.3 has separate cipher
104/// for handshake and application.
105pub const Cipher = union(CipherSuite) {
106 // tls 1.2 cbc
107 ECDHE_ECDSA_WITH_AES_128_CBC_SHA: CipherType(.ECDHE_ECDSA_WITH_AES_128_CBC_SHA),
108 ECDHE_RSA_WITH_AES_128_CBC_SHA: CipherType(.ECDHE_RSA_WITH_AES_128_CBC_SHA),
109 RSA_WITH_AES_128_CBC_SHA: CipherType(.RSA_WITH_AES_128_CBC_SHA),
110
111 ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: CipherType(.ECDHE_ECDSA_WITH_AES_128_CBC_SHA256),
112 ECDHE_RSA_WITH_AES_128_CBC_SHA256: CipherType(.ECDHE_RSA_WITH_AES_128_CBC_SHA256),
113 RSA_WITH_AES_128_CBC_SHA256: CipherType(.RSA_WITH_AES_128_CBC_SHA256),
114
115 ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: CipherType(.ECDHE_ECDSA_WITH_AES_256_CBC_SHA384),
116 ECDHE_RSA_WITH_AES_256_CBC_SHA384: CipherType(.ECDHE_RSA_WITH_AES_256_CBC_SHA384),
117 // tls 1.2 gcm
118 ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: CipherType(.ECDHE_ECDSA_WITH_AES_128_GCM_SHA256),
119 ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: CipherType(.ECDHE_RSA_WITH_AES_256_GCM_SHA384),
120 ECDHE_RSA_WITH_AES_128_GCM_SHA256: CipherType(.ECDHE_RSA_WITH_AES_128_GCM_SHA256),
121 ECDHE_RSA_WITH_AES_256_GCM_SHA384: CipherType(.ECDHE_RSA_WITH_AES_256_GCM_SHA384),
122 // tls 1.2 chacha
123 ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: CipherType(.ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256),
124 ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: CipherType(.ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256),
125 // tls 1.3
126 AES_128_GCM_SHA256: CipherType(.AES_128_GCM_SHA256),
127 AES_256_GCM_SHA384: CipherType(.AES_256_GCM_SHA384),
128 CHACHA20_POLY1305_SHA256: CipherType(.CHACHA20_POLY1305_SHA256),
129 AEGIS_128L_SHA256: CipherType(.AEGIS_128L_SHA256),
130
131 // tls 1.2 application cipher
132 pub fn initTls12(tag: CipherSuite, key_material: []const u8, side: proto.Side) !Cipher {
133 switch (tag) {
134 inline .ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
135 .ECDHE_RSA_WITH_AES_128_CBC_SHA,
136 .RSA_WITH_AES_128_CBC_SHA,
137 .ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
138 .ECDHE_RSA_WITH_AES_128_CBC_SHA256,
139 .RSA_WITH_AES_128_CBC_SHA256,
140 .ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
141 .ECDHE_RSA_WITH_AES_256_CBC_SHA384,
142 .ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
143 .ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
144 .ECDHE_RSA_WITH_AES_128_GCM_SHA256,
145 .ECDHE_RSA_WITH_AES_256_GCM_SHA384,
146 .ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
147 .ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
148 => |comptime_tag| {
149 return @unionInit(Cipher, @tagName(comptime_tag), CipherType(comptime_tag).init(key_material, side));
150 },
151 else => return error.TlsIllegalParameter,
152 }
153 }
154
155 // tls 1.3 handshake or application cipher
156 pub fn initTls13(tag: CipherSuite, secret: Transcript.Secret, side: proto.Side) !Cipher {
157 return switch (tag) {
158 inline .AES_128_GCM_SHA256,
159 .AES_256_GCM_SHA384,
160 .CHACHA20_POLY1305_SHA256,
161 .AEGIS_128L_SHA256,
162 => |comptime_tag| {
163 return @unionInit(Cipher, @tagName(comptime_tag), CipherType(comptime_tag).init(secret, side));
164 },
165 else => return error.TlsIllegalParameter,
166 };
167 }
168
169 pub fn encrypt(
170 c: *Cipher,
171 buf: []u8,
172 content_type: proto.ContentType,
173 cleartext: []const u8,
174 ) ![]const u8 {
175 return switch (c.*) {
176 inline else => |*f| try f.encrypt(buf, content_type, cleartext),
177 };
178 }
179
180 pub fn decrypt(
181 c: *Cipher,
182 buf: []u8,
183 rec: Record,
184 ) !struct { proto.ContentType, []u8 } {
185 return switch (c.*) {
186 inline else => |*f| {
187 const content_type, const cleartext = try f.decrypt(buf, rec);
188 if (cleartext.len > max_cleartext_len) return error.TlsRecordOverflow;
189 return .{ content_type, cleartext };
190 },
191 };
192 }
193
194 pub fn recordLen(c: Cipher, cleartext_len: usize) usize {
195 return switch (c) {
196 inline else => |f| f.recordLen(cleartext_len),
197 };
198 }
199
200 pub fn encryptOverhead(c: Cipher) usize {
201 return switch (c) {
202 inline else => |f| return @TypeOf(f).encrypt_overhead,
203 };
204 }
205
206 pub fn encryptSeq(c: Cipher) u64 {
207 return switch (c) {
208 inline else => |f| f.encrypt_seq,
209 };
210 }
211
212 pub fn keyUpdateEncrypt(c: *Cipher) !void {
213 return switch (c.*) {
214 inline .AES_128_GCM_SHA256,
215 .AES_256_GCM_SHA384,
216 .CHACHA20_POLY1305_SHA256,
217 .AEGIS_128L_SHA256,
218 => |*f| f.keyUpdateEncrypt(),
219 // can't happen on tls 1.2
220 else => return error.TlsUnexpectedMessage,
221 };
222 }
223 pub fn keyUpdateDecrypt(c: *Cipher) !void {
224 return switch (c.*) {
225 inline .AES_128_GCM_SHA256,
226 .AES_256_GCM_SHA384,
227 .CHACHA20_POLY1305_SHA256,
228 .AEGIS_128L_SHA256,
229 => |*f| f.keyUpdateDecrypt(),
230 // can't happen on tls 1.2
231 else => return error.TlsUnexpectedMessage,
232 };
233 }
234};
235
236fn Aead12Type(comptime AeadType: type) type {
237 return struct {
238 const explicit_iv_len = 8;
239 const key_len = AeadType.key_length;
240 const auth_tag_len = AeadType.tag_length;
241 const nonce_len = AeadType.nonce_length;
242 const iv_len = AeadType.nonce_length - explicit_iv_len;
243 const encrypt_overhead = record.header_len + explicit_iv_len + auth_tag_len;
244
245 encrypt_key: [key_len]u8,
246 decrypt_key: [key_len]u8,
247 encrypt_iv: [iv_len]u8,
248 decrypt_iv: [iv_len]u8,
249 encrypt_seq: u64 = 0,
250 decrypt_seq: u64 = 0,
251 rnd: std.Random = csprng,
252
253 const Self = @This();
254
255 fn init(key_material: []const u8, side: proto.Side) Self {
256 const client_key = key_material[0..key_len].*;
257 const server_key = key_material[key_len..][0..key_len].*;
258 const client_iv = key_material[2 * key_len ..][0..iv_len].*;
259 const server_iv = key_material[2 * key_len + iv_len ..][0..iv_len].*;
260 return .{
261 .encrypt_key = if (side == .client) client_key else server_key,
262 .decrypt_key = if (side == .client) server_key else client_key,
263 .encrypt_iv = if (side == .client) client_iv else server_iv,
264 .decrypt_iv = if (side == .client) server_iv else client_iv,
265 };
266 }
267
268 /// Returns encrypted tls record in format:
269 /// ----------------- buf ----------------------
270 /// header | explicit_iv | ciphertext | auth_tag
271 ///
272 /// tls record header: 5 bytes
273 /// explicit_iv: 8 bytes
274 /// ciphertext: same length as cleartext
275 /// auth_tag: 16 bytes
276 pub fn encrypt(
277 self: *Self,
278 buf: []u8,
279 content_type: proto.ContentType,
280 cleartext: []const u8,
281 ) ![]const u8 {
282 const record_len = record.header_len + explicit_iv_len + cleartext.len + auth_tag_len;
283 if (buf.len < record_len) return error.TlsCipherNoSpaceLeft;
284
285 const header = buf[0..record.header_len];
286 const explicit_iv = buf[record.header_len..][0..explicit_iv_len];
287 const ciphertext = buf[record.header_len + explicit_iv_len ..][0..cleartext.len];
288 const auth_tag = buf[record.header_len + explicit_iv_len + cleartext.len ..][0..auth_tag_len];
289
290 header.* = record.header(content_type, explicit_iv_len + cleartext.len + auth_tag_len);
291 self.rnd.bytes(explicit_iv);
292 const iv = self.encrypt_iv ++ explicit_iv.*;
293 const ad = additionalData(self.encrypt_seq, content_type, cleartext.len);
294 AeadType.encrypt(ciphertext, auth_tag, cleartext, &ad, iv, self.encrypt_key);
295 self.encrypt_seq +%= 1;
296
297 return buf[0..record_len];
298 }
299
300 pub fn recordLen(_: Self, cleartext_len: usize) usize {
301 return record.header_len + explicit_iv_len + cleartext_len + auth_tag_len;
302 }
303
304 /// Decrypts payload into cleartext. Returns tls record content type and
305 /// cleartext.
306 /// Accepts tls record header and payload:
307 /// header | ----------- payload ---------------
308 /// header | explicit_iv | ciphertext | auth_tag
309 pub fn decrypt(
310 self: *Self,
311 buf: []u8,
312 rec: Record,
313 ) !struct { proto.ContentType, []u8 } {
314 const overhead = explicit_iv_len + auth_tag_len;
315 if (rec.payload.len < overhead) return error.TlsDecryptError;
316 const cleartext_len = rec.payload.len - overhead;
317 if (buf.len < cleartext_len) return error.TlsCipherNoSpaceLeft;
318
319 const explicit_iv = rec.payload[0..explicit_iv_len];
320 const ciphertext = rec.payload[explicit_iv_len..][0..cleartext_len];
321 const auth_tag = rec.payload[explicit_iv_len + cleartext_len ..][0..auth_tag_len];
322
323 const iv = self.decrypt_iv ++ explicit_iv.*;
324 const ad = additionalData(self.decrypt_seq, rec.content_type, cleartext_len);
325 const cleartext = buf[0..cleartext_len];
326 AeadType.decrypt(cleartext, ciphertext, auth_tag.*, &ad, iv, self.decrypt_key) catch return error.TlsDecryptError;
327 self.decrypt_seq +%= 1;
328 return .{ rec.content_type, cleartext };
329 }
330 };
331}
332
333fn Aead12ChaChaType(comptime AeadType: type) type {
334 return struct {
335 const key_len = AeadType.key_length;
336 const auth_tag_len = AeadType.tag_length;
337 const nonce_len = AeadType.nonce_length;
338 const encrypt_overhead = record.header_len + auth_tag_len;
339
340 encrypt_key: [key_len]u8,
341 decrypt_key: [key_len]u8,
342 encrypt_iv: [nonce_len]u8,
343 decrypt_iv: [nonce_len]u8,
344 encrypt_seq: u64 = 0,
345 decrypt_seq: u64 = 0,
346
347 const Self = @This();
348
349 fn init(key_material: []const u8, side: proto.Side) Self {
350 const client_key = key_material[0..key_len].*;
351 const server_key = key_material[key_len..][0..key_len].*;
352 const client_iv = key_material[2 * key_len ..][0..nonce_len].*;
353 const server_iv = key_material[2 * key_len + nonce_len ..][0..nonce_len].*;
354 return .{
355 .encrypt_key = if (side == .client) client_key else server_key,
356 .decrypt_key = if (side == .client) server_key else client_key,
357 .encrypt_iv = if (side == .client) client_iv else server_iv,
358 .decrypt_iv = if (side == .client) server_iv else client_iv,
359 };
360 }
361
362 /// Returns encrypted tls record in format:
363 /// ------------ buf -------------
364 /// header | ciphertext | auth_tag
365 ///
366 /// tls record header: 5 bytes
367 /// ciphertext: same length as cleartext
368 /// auth_tag: 16 bytes
369 pub fn encrypt(
370 self: *Self,
371 buf: []u8,
372 content_type: proto.ContentType,
373 cleartext: []const u8,
374 ) ![]const u8 {
375 const record_len = record.header_len + cleartext.len + auth_tag_len;
376 if (buf.len < record_len) return error.TlsCipherNoSpaceLeft;
377
378 const ciphertext = buf[record.header_len..][0..cleartext.len];
379 const auth_tag = buf[record.header_len + ciphertext.len ..][0..auth_tag_len];
380
381 const ad = additionalData(self.encrypt_seq, content_type, cleartext.len);
382 const iv = ivWithSeq(nonce_len, self.encrypt_iv, self.encrypt_seq);
383 AeadType.encrypt(ciphertext, auth_tag, cleartext, &ad, iv, self.encrypt_key);
384 self.encrypt_seq +%= 1;
385
386 buf[0..record.header_len].* = record.header(content_type, ciphertext.len + auth_tag.len);
387 return buf[0..record_len];
388 }
389
390 pub fn recordLen(_: Self, cleartext_len: usize) usize {
391 return record.header_len + cleartext_len + auth_tag_len;
392 }
393
394 /// Decrypts payload into cleartext. Returns tls record content type and
395 /// cleartext.
396 /// Accepts tls record header and payload:
397 /// header | ----- payload -------
398 /// header | ciphertext | auth_tag
399 pub fn decrypt(
400 self: *Self,
401 buf: []u8,
402 rec: Record,
403 ) !struct { proto.ContentType, []u8 } {
404 const overhead = auth_tag_len;
405 if (rec.payload.len < overhead) return error.TlsDecryptError;
406 const cleartext_len = rec.payload.len - overhead;
407 if (buf.len < cleartext_len) return error.TlsCipherNoSpaceLeft;
408
409 const ciphertext = rec.payload[0..cleartext_len];
410 const auth_tag = rec.payload[cleartext_len..][0..auth_tag_len];
411 const cleartext = buf[0..cleartext_len];
412
413 const ad = additionalData(self.decrypt_seq, rec.content_type, cleartext_len);
414 const iv = ivWithSeq(nonce_len, self.decrypt_iv, self.decrypt_seq);
415 AeadType.decrypt(cleartext, ciphertext, auth_tag.*, &ad, iv, self.decrypt_key) catch return error.TlsDecryptError;
416 self.decrypt_seq +%= 1;
417 return .{ rec.content_type, cleartext };
418 }
419 };
420}
421
422fn Aead13Type(comptime AeadType: type, comptime Hash: type) type {
423 return struct {
424 const Hmac = crypto.auth.hmac.Hmac(Hash);
425 const Hkdf = crypto.kdf.hkdf.Hkdf(Hmac);
426
427 const key_len = AeadType.key_length;
428 const auth_tag_len = AeadType.tag_length;
429 const nonce_len = AeadType.nonce_length;
430 const digest_len = Hash.digest_length;
431 const encrypt_overhead = record.header_len + 1 + auth_tag_len;
432
433 encrypt_secret: [digest_len]u8,
434 decrypt_secret: [digest_len]u8,
435 encrypt_key: [key_len]u8,
436 decrypt_key: [key_len]u8,
437 encrypt_iv: [nonce_len]u8,
438 decrypt_iv: [nonce_len]u8,
439 encrypt_seq: u64 = 0,
440 decrypt_seq: u64 = 0,
441
442 const Self = @This();
443
444 pub fn init(secret: Transcript.Secret, side: proto.Side) Self {
445 var self = Self{
446 .encrypt_secret = if (side == .client) secret.client[0..digest_len].* else secret.server[0..digest_len].*,
447 .decrypt_secret = if (side == .server) secret.client[0..digest_len].* else secret.server[0..digest_len].*,
448 .encrypt_key = undefined,
449 .decrypt_key = undefined,
450 .encrypt_iv = undefined,
451 .decrypt_iv = undefined,
452 };
453 self.keyGenerate();
454 return self;
455 }
456
457 fn keyGenerate(self: *Self) void {
458 self.encrypt_key = hkdfExpandLabel(Hkdf, self.encrypt_secret, "key", "", key_len);
459 self.decrypt_key = hkdfExpandLabel(Hkdf, self.decrypt_secret, "key", "", key_len);
460 self.encrypt_iv = hkdfExpandLabel(Hkdf, self.encrypt_secret, "iv", "", nonce_len);
461 self.decrypt_iv = hkdfExpandLabel(Hkdf, self.decrypt_secret, "iv", "", nonce_len);
462 }
463
464 pub fn keyUpdateEncrypt(self: *Self) void {
465 self.encrypt_secret = hkdfExpandLabel(Hkdf, self.encrypt_secret, "traffic upd", "", digest_len);
466 self.encrypt_seq = 0;
467 self.keyGenerate();
468 }
469
470 pub fn keyUpdateDecrypt(self: *Self) void {
471 self.decrypt_secret = hkdfExpandLabel(Hkdf, self.decrypt_secret, "traffic upd", "", digest_len);
472 self.decrypt_seq = 0;
473 self.keyGenerate();
474 }
475
476 /// Returns encrypted tls record in format:
477 /// ------------ buf -------------
478 /// header | ciphertext | auth_tag
479 ///
480 /// tls record header: 5 bytes
481 /// ciphertext: cleartext len + 1 byte content type
482 /// auth_tag: 16 bytes
483 pub fn encrypt(
484 self: *Self,
485 buf: []u8,
486 content_type: proto.ContentType,
487 cleartext: []const u8,
488 ) ![]const u8 {
489 const payload_len = cleartext.len + 1 + auth_tag_len;
490 const record_len = record.header_len + payload_len;
491 if (buf.len < record_len) return error.TlsCipherNoSpaceLeft;
492
493 const header = buf[0..record.header_len];
494 header.* = record.header(.application_data, payload_len);
495
496 // Skip @memcpy if cleartext is already part of the buf at right position
497 if (&cleartext[0] != &buf[record.header_len]) {
498 @memcpy(buf[record.header_len..][0..cleartext.len], cleartext);
499 }
500 buf[record.header_len + cleartext.len] = @backingInt(content_type);
501 const ciphertext = buf[record.header_len..][0 .. cleartext.len + 1];
502 const auth_tag = buf[record.header_len + ciphertext.len ..][0..auth_tag_len];
503
504 const iv = ivWithSeq(nonce_len, self.encrypt_iv, self.encrypt_seq);
505 AeadType.encrypt(ciphertext, auth_tag, ciphertext, header, iv, self.encrypt_key);
506 self.encrypt_seq +%= 1;
507 return buf[0..record_len];
508 }
509
510 pub fn recordLen(_: Self, cleartext_len: usize) usize {
511 const payload_len = cleartext_len + 1 + auth_tag_len;
512 return record.header_len + payload_len;
513 }
514
515 /// Decrypts payload into cleartext. Returns tls record content type and
516 /// cleartext.
517 /// Accepts tls record header and payload:
518 /// header | ------- payload ---------
519 /// header | ciphertext | auth_tag
520 /// header | cleartext + ct | auth_tag
521 /// Ciphertext after decryption contains cleartext and content type (1 byte).
522 pub fn decrypt(
523 self: *Self,
524 buf: []u8,
525 rec: Record,
526 ) !struct { proto.ContentType, []u8 } {
527 const overhead = auth_tag_len + 1;
528 if (rec.payload.len < overhead) return error.TlsDecryptError;
529 const ciphertext_len = rec.payload.len - auth_tag_len;
530 if (ciphertext_len > max_cleartext_len + 1) return error.TlsRecordOverflow;
531 if (buf.len < ciphertext_len) return error.TlsCipherNoSpaceLeft;
532
533 const ciphertext = rec.payload[0..ciphertext_len];
534 const auth_tag = rec.payload[ciphertext_len..][0..auth_tag_len];
535
536 const iv = ivWithSeq(nonce_len, self.decrypt_iv, self.decrypt_seq);
537 AeadType.decrypt(buf[0..ciphertext_len], ciphertext, auth_tag.*, rec.header, iv, self.decrypt_key) catch return error.TlsBadRecordMac;
538
539 // Remove zero bytes padding
540 var content_type_idx: usize = ciphertext_len - 1;
541 while (buf[content_type_idx] == 0 and content_type_idx > 0) : (content_type_idx -= 1) {}
542
543 const cleartext = buf[0..content_type_idx];
544 const content_type: proto.ContentType = @fromBackingInt(@intCast(buf[content_type_idx]));
545 self.decrypt_seq +%= 1;
546 return .{ content_type, cleartext };
547 }
548 };
549}
550
551fn CbcType(comptime BlockCipher: type, comptime HashType: type) type {
552 const CBC = .CBC(BlockCipher);
553 return struct {
554 const mac_len = HashType.digest_length; // 20, 32, 48 bytes for sha1, sha256, sha384
555 const key_len = BlockCipher.key_bits / 8; // 16, 32 for Aes128, Aes256
556 const iv_len = 16;
557 const encrypt_overhead = record.header_len + iv_len + mac_len + max_padding;
558
559 const Hmac = crypto.auth.hmac.Hmac(HashType);
560 const paddedLength = CBC.paddedLength;
561 const max_padding = 16;
562
563 encrypt_secret: [mac_len]u8,
564 decrypt_secret: [mac_len]u8,
565 encrypt_key: [key_len]u8,
566 decrypt_key: [key_len]u8,
567 encrypt_seq: u64 = 0,
568 decrypt_seq: u64 = 0,
569 rnd: std.Random = csprng,
570
571 const Self = @This();
572
573 fn init(key_material: []const u8, side: proto.Side) Self {
574 const client_secret = key_material[0..mac_len].*;
575 const server_secret = key_material[mac_len..][0..mac_len].*;
576 const client_key = key_material[2 * mac_len ..][0..key_len].*;
577 const server_key = key_material[2 * mac_len + key_len ..][0..key_len].*;
578 return .{
579 .encrypt_secret = if (side == .client) client_secret else server_secret,
580 .decrypt_secret = if (side == .client) server_secret else client_secret,
581 .encrypt_key = if (side == .client) client_key else server_key,
582 .decrypt_key = if (side == .client) server_key else client_key,
583 };
584 }
585
586 /// Returns encrypted tls record in format:
587 /// ----------------- buf -----------------
588 /// header | iv | ------ ciphertext -------
589 /// header | iv | cleartext | mac | padding
590 ///
591 /// tls record header: 5 bytes
592 /// iv: 16 bytes
593 /// ciphertext: cleartext length + mac + padding
594 /// mac: 20, 32 or 48 (sha1, sha256, sha384)
595 /// padding: 1-16 bytes
596 ///
597 /// Max encrypt buf overhead = iv + mac + padding (1-16)
598 /// aes_128_cbc_sha => 16 + 20 + 16 = 52
599 /// aes_128_cbc_sha256 => 16 + 32 + 16 = 64
600 /// aes_256_cbc_sha384 => 16 + 48 + 16 = 80
601 pub fn encrypt(
602 self: *Self,
603 buf: []u8,
604 content_type: proto.ContentType,
605 cleartext: []const u8,
606 ) ![]const u8 {
607 if (buf.len < self.recordLen(cleartext.len)) return error.TlsCipherNoSpaceLeft;
608 const cleartext_idx = record.header_len + iv_len; // position of cleartext in buf
609 @memcpy(buf[cleartext_idx..][0..cleartext.len], cleartext);
610
611 { // calculate mac from (ad + cleartext)
612 // ... | ad | cleartext | mac | ...
613 // | -- mac msg -- | mac |
614 const ad = additionalData(self.encrypt_seq, content_type, cleartext.len);
615 const mac_msg = buf[cleartext_idx - ad.len ..][0 .. ad.len + cleartext.len];
616 @memcpy(mac_msg[0..ad.len], &ad);
617 const mac = buf[cleartext_idx + cleartext.len ..][0..mac_len];
618 Hmac.create(mac, mac_msg, &self.encrypt_secret);
619 self.encrypt_seq +%= 1;
620 }
621
622 // ... | cleartext | mac | ...
623 // ... | -- plaintext --- ...
624 // ... | cleartext | mac | padding
625 // ... | ------ ciphertext -------
626 const unpadded_len = cleartext.len + mac_len;
627 const padded_len = paddedLength(unpadded_len);
628 const plaintext = buf[cleartext_idx..][0..unpadded_len];
629 const ciphertext = buf[cleartext_idx..][0..padded_len];
630
631 // Add header and iv at the buf start
632 // header | iv | ...
633 buf[0..record.header_len].* = record.header(content_type, iv_len + ciphertext.len);
634 const iv = buf[record.header_len..][0..iv_len];
635 self.rnd.bytes(iv);
636
637 // encrypt plaintext into ciphertext
638 CBC.init(self.encrypt_key).encrypt(ciphertext, plaintext, iv[0..iv_len].*);
639
640 // header | iv | ------ ciphertext -------
641 return buf[0 .. record.header_len + iv_len + ciphertext.len];
642 }
643
644 pub fn recordLen(_: Self, cleartext_len: usize) usize {
645 const unpadded_len = cleartext_len + mac_len;
646 const padded_len = paddedLength(unpadded_len);
647 return record.header_len + iv_len + padded_len;
648 }
649
650 /// Decrypts payload into cleartext. Returns tls record content type and
651 /// cleartext.
652 pub fn decrypt(
653 self: *Self,
654 buf: []u8,
655 rec: Record,
656 ) !struct { proto.ContentType, []u8 } {
657 if (rec.payload.len < iv_len + mac_len + 1) return error.TlsDecryptError;
658
659 // --------- payload ------------
660 // iv | ------ ciphertext -------
661 // iv | cleartext | mac | padding
662 const iv = rec.payload[0..iv_len];
663 const ciphertext = rec.payload[iv_len..];
664
665 if (buf.len < ciphertext.len + additional_data_len) return error.TlsCipherNoSpaceLeft;
666 // ---------- buf ---------------
667 // ad | ------ plaintext --------
668 // ad | cleartext | mac | padding
669 const plaintext = buf[additional_data_len..][0..ciphertext.len];
670 // decrypt ciphertext -> plaintext
671 CBC.init(self.decrypt_key).decrypt(plaintext, ciphertext, iv[0..iv_len].*) catch return error.TlsDecryptError;
672
673 // get padding len from last padding byte
674 const padding_len = plaintext[plaintext.len - 1] + 1;
675 if (plaintext.len < mac_len + padding_len) return error.TlsDecryptError;
676 // split plaintext into cleartext and mac
677 const cleartext_len = plaintext.len - mac_len - padding_len;
678 const cleartext = plaintext[0..cleartext_len];
679 const mac = plaintext[cleartext_len..][0..mac_len];
680
681 // write ad to the buf
682 var ad = additionalData(self.decrypt_seq, rec.content_type, cleartext_len);
683 @memcpy(buf[0..ad.len], &ad);
684 const mac_msg = buf[0 .. ad.len + cleartext_len];
685 self.decrypt_seq +%= 1;
686
687 // calculate expected mac and compare with received mac
688 var expected_mac: [mac_len]u8 = undefined;
689 Hmac.create(&expected_mac, mac_msg, &self.decrypt_secret);
690 if (!std.mem.eql(u8, &expected_mac, mac))
691 return error.TlsBadRecordMac;
692
693 return .{ rec.content_type, cleartext };
694 }
695 };
696}
697
698// xor lower 8 iv bytes with seq
699fn ivWithSeq(comptime nonce_len: usize, iv: [nonce_len]u8, seq: u64) [nonce_len]u8 {
700 var res = iv;
701 const buf = res[nonce_len - 8 ..];
702 const operand = std.mem.readInt(u64, buf, .big);
703 std.mem.writeInt(u64, buf, operand ^ seq, .big);
704 return res;
705}
706
707pub const additional_data_len = record.header_len + @sizeOf(u64);
708
709fn additionalData(seq: u64, content_type: proto.ContentType, payload_len: usize) [additional_data_len]u8 {
710 const header = record.header(content_type, payload_len);
711 var seq_buf: [8]u8 = undefined;
712 std.mem.writeInt(u64, &seq_buf, seq, .big);
713 return seq_buf ++ header;
714}
715
716// Cipher suites lists. In the order of preference.
717// For the preference using grades priority and rules from Go project.
718// https://ciphersuite.info/page/faq/
719// https://github.com/golang/go/blob/73186ba00251b3ed8baaab36e4f5278c7681155b/src/crypto/tls/cipher_suites.go#L226
720pub const cipher_suites = struct {
721 pub const tls12_secure = if (crypto.core.aes.has_hardware_support) [_]CipherSuite{
722 // recommended
723 .ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
724 .ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
725 .ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
726 // secure
727 .ECDHE_RSA_WITH_AES_128_GCM_SHA256,
728 .ECDHE_RSA_WITH_AES_256_GCM_SHA384,
729 .ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
730 } else [_]CipherSuite{
731 // recommended
732 .ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
733 .ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
734 .ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
735
736 // secure
737 .ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
738 .ECDHE_RSA_WITH_AES_128_GCM_SHA256,
739 .ECDHE_RSA_WITH_AES_256_GCM_SHA384,
740 };
741 pub const tls12_weak = [_]CipherSuite{
742 // weak
743 .ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
744 .ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
745 .ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
746 .ECDHE_RSA_WITH_AES_128_CBC_SHA256,
747 .ECDHE_RSA_WITH_AES_256_CBC_SHA384,
748 .ECDHE_RSA_WITH_AES_128_CBC_SHA,
749
750 .RSA_WITH_AES_128_CBC_SHA256,
751 .RSA_WITH_AES_128_CBC_SHA,
752 };
753 pub const tls13_ = if (crypto.core.aes.has_hardware_support) [_]CipherSuite{
754 .AES_128_GCM_SHA256,
755 .AES_256_GCM_SHA384,
756 .CHACHA20_POLY1305_SHA256,
757 // Excluded because didn't find server which supports it to test
758 // .AEGIS_128L_SHA256
759 } else [_]CipherSuite{
760 .CHACHA20_POLY1305_SHA256,
761 .AES_128_GCM_SHA256,
762 .AES_256_GCM_SHA384,
763 };
764
765 pub const tls13 = &tls13_;
766 pub const tls12 = &(tls12_secure ++ tls12_weak);
767 pub const secure = &(tls13_ ++ tls12_secure);
768 pub const all = &(tls13_ ++ tls12_secure ++ tls12_weak);
769
770 pub fn includes(list: []const CipherSuite, cs: CipherSuite) bool {
771 for (list) |s| {
772 if (cs == s) return true;
773 }
774 return false;
775 }
776};
777
778// Weak, secure, recommended grades are from https://ciphersuite.info/page/faq/
779pub const CipherSuite = enum(u16) {
780 // tls 1.2 cbc sha1
781 ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xc009, // weak
782 ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xc013, // weak
783 RSA_WITH_AES_128_CBC_SHA = 0x002F, // weak
784 // tls 1.2 cbc sha256
785 ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xc023, // weak
786 ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xc027, // weak
787 RSA_WITH_AES_128_CBC_SHA256 = 0x003c, // weak
788 // tls 1.2 cbc sha384
789 ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xc024, // weak
790 ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xc028, // weak
791 // tls 1.2 gcm
792 ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xc02b, // recommended
793 ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xc02c, // recommended
794 ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xc02f, // secure
795 ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xc030, // secure
796 // tls 1.2 chacha
797 ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xcca9, // recommended
798 ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xcca8, // secure
799 // tls 1.3 (all are recommended)
800 AES_128_GCM_SHA256 = 0x1301,
801 AES_256_GCM_SHA384 = 0x1302,
802 CHACHA20_POLY1305_SHA256 = 0x1303,
803 AEGIS_128L_SHA256 = 0x1307,
804 // AEGIS_256_SHA512 = 0x1306,
805 _,
806
807 pub fn validate(cs: CipherSuite) !void {
808 if (cipher_suites.includes(cipher_suites.tls12, cs)) return;
809 if (cipher_suites.includes(cipher_suites.tls13, cs)) return;
810 return error.TlsIllegalParameter;
811 }
812
813 pub const Versions = enum {
814 both,
815 tls_1_3,
816 tls_1_2,
817 };
818
819 // get tls versions from list of cipher suites
820 pub fn versions(list: []const CipherSuite) !Versions {
821 var has_12 = false;
822 var has_13 = false;
823 for (list) |cs| {
824 if (cipher_suites.includes(cipher_suites.tls12, cs)) {
825 has_12 = true;
826 } else {
827 if (cipher_suites.includes(cipher_suites.tls13, cs)) has_13 = true;
828 }
829 }
830 if (has_12 and has_13) return .both;
831 if (has_12) return .tls_1_2;
832 if (has_13) return .tls_1_3;
833 return error.TlsIllegalParameter;
834 }
835
836 pub const KeyExchangeAlgorithm = enum {
837 ecdhe,
838 rsa,
839 };
840
841 pub fn keyExchange(s: CipherSuite) KeyExchangeAlgorithm {
842 return switch (s) {
843 // Random premaster secret, encrypted with publich key from certificate.
844 // No server key exchange message.
845 .RSA_WITH_AES_128_CBC_SHA,
846 .RSA_WITH_AES_128_CBC_SHA256,
847 => .rsa,
848 else => .ecdhe,
849 };
850 }
851
852 pub const HashTag = enum {
853 sha256,
854 sha384,
855 sha512,
856 };
857
858 pub fn hash(cs: CipherSuite) HashTag {
859 return switch (cs) {
860 .ECDHE_RSA_WITH_AES_256_CBC_SHA384,
861 .ECDHE_RSA_WITH_AES_256_GCM_SHA384,
862 .ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
863 .ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
864 .AES_256_GCM_SHA384,
865 => .sha384,
866 else => .sha256,
867 };
868 }
869};
870
871const testing = std.testing;
872const testu = ;
873
874test "CipherSuite validate" {
875 {
876 const cs: CipherSuite = .AES_256_GCM_SHA384;
877 try cs.validate();
878 try testing.expectEqual(cs.hash(), .sha384);
879 try testing.expectEqual(cs.keyExchange(), .ecdhe);
880 }
881 {
882 const cs: CipherSuite = .AES_128_GCM_SHA256;
883 try cs.validate();
884 try testing.expectEqual(.sha256, cs.hash());
885 try testing.expectEqual(.ecdhe, cs.keyExchange());
886 }
887 for (cipher_suites.tls12) |cs| {
888 try cs.validate();
889 _ = cs.hash();
890 _ = cs.keyExchange();
891 }
892}
893
894test "CipherSuite versions" {
895 try testing.expectEqual(.tls_1_3, CipherSuite.versions(&[_]CipherSuite{.AES_128_GCM_SHA256}));
896 try testing.expectEqual(.both, CipherSuite.versions(&[_]CipherSuite{ .AES_128_GCM_SHA256, .ECDHE_ECDSA_WITH_AES_128_CBC_SHA }));
897 try testing.expectEqual(.tls_1_2, CipherSuite.versions(&[_]CipherSuite{.RSA_WITH_AES_128_CBC_SHA}));
898}
899
900test "gcm 1.2 encrypt overhead" {
901 inline for ([_]type{
902 Aead12Aes128Gcm,
903 Aead12Aes256Gcm,
904 }) |T| {
905 {
906 const expected_key_len = switch (T) {
907 Aead12Aes128Gcm => 16,
908 Aead12Aes256Gcm => 32,
909 else => unreachable,
910 };
911 try testing.expectEqual(expected_key_len, T.key_len);
912 try testing.expectEqual(16, T.auth_tag_len);
913 try testing.expectEqual(12, T.nonce_len);
914 try testing.expectEqual(4, T.iv_len);
915 try testing.expectEqual(29, T.encrypt_overhead);
916 }
917 }
918}
919
920test "cbc 1.2 encrypt overhead" {
921 try testing.expectEqual(85, encrypt_overhead_tls_12);
922
923 inline for ([_]type{
924 CbcAes128Sha1,
925 CbcAes128Sha256,
926 CbcAes256Sha384,
927 }) |T| {
928 switch (T) {
929 CbcAes128Sha1 => {
930 try testing.expectEqual(20, T.mac_len);
931 try testing.expectEqual(16, T.key_len);
932 try testing.expectEqual(57, T.encrypt_overhead);
933 },
934 CbcAes128Sha256 => {
935 try testing.expectEqual(32, T.mac_len);
936 try testing.expectEqual(16, T.key_len);
937 try testing.expectEqual(69, T.encrypt_overhead);
938 },
939 CbcAes256Sha384 => {
940 try testing.expectEqual(48, T.mac_len);
941 try testing.expectEqual(32, T.key_len);
942 try testing.expectEqual(85, T.encrypt_overhead);
943 },
944 else => unreachable,
945 }
946 try testing.expectEqual(16, T.paddedLength(1)); // cbc block padding
947 try testing.expectEqual(16, T.iv_len);
948 }
949}
950
951test "overhead tls 1.3" {
952 try testing.expectEqual(22, encrypt_overhead_tls_13);
953 try expectOverhead(Aes128GcmSha256, 16, 16, 12, 22);
954 try expectOverhead(Aes256GcmSha384, 32, 16, 12, 22);
955 try expectOverhead(ChaChaSha256, 32, 16, 12, 22);
956 try expectOverhead(Aegis128Sha256, 16, 16, 16, 22);
957 // and tls 1.2 chacha
958 try expectOverhead(Aead12ChaCha, 32, 16, 12, 21);
959}
960
961fn expectOverhead(T: type, key_len: usize, auth_tag_len: usize, nonce_len: usize, overhead: usize) !void {
962 try testing.expectEqual(key_len, T.key_len);
963 try testing.expectEqual(auth_tag_len, T.auth_tag_len);
964 try testing.expectEqual(nonce_len, T.nonce_len);
965 try testing.expectEqual(overhead, T.encrypt_overhead);
966}
967
968test "client/server encryption tls 1.3" {
969 inline for (cipher_suites.tls13) |cs| {
970 var buf: [256]u8 = undefined;
971 testu.fill(&buf);
972 const secret = Transcript.Secret{
973 .client = buf[0..128],
974 .server = buf[128..],
975 };
976 var client_cipher = try Cipher.initTls13(cs, secret, .client);
977 var server_cipher = try Cipher.initTls13(cs, secret, .server);
978 try encryptDecrypt(&client_cipher, &server_cipher);
979
980 try client_cipher.keyUpdateEncrypt();
981 try server_cipher.keyUpdateDecrypt();
982 try encryptDecrypt(&client_cipher, &server_cipher);
983
984 try client_cipher.keyUpdateDecrypt();
985 try server_cipher.keyUpdateEncrypt();
986 try encryptDecrypt(&client_cipher, &server_cipher);
987 }
988}
989
990test "client/server encryption tls 1.2" {
991 inline for (cipher_suites.tls12) |cs| {
992 var key_material: [256]u8 = undefined;
993 testu.fill(&key_material);
994 var client_cipher = try Cipher.initTls12(cs, &key_material, .client);
995 var server_cipher = try Cipher.initTls12(cs, &key_material, .server);
996 try encryptDecrypt(&client_cipher, &server_cipher);
997 }
998}
999
1000fn encryptDecrypt(client_cipher: *Cipher, server_cipher: *Cipher) !void {
1001 const cleartext =
1002 \\ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
1003 \\ eiusmod tempor incididunt ut labore et dolore magna aliqua.
1004 ;
1005 var buf: [256]u8 = undefined;
1006
1007 { // client to server
1008 // encrypt
1009 const encrypted = try client_cipher.encrypt(&buf, .application_data, cleartext);
1010 const expected_encrypted_len = switch (client_cipher.*) {
1011 inline else => |f| brk: {
1012 const T = @TypeOf(f);
1013 break :brk switch (T) {
1014 CbcAes128Sha1,
1015 CbcAes128Sha256,
1016 CbcAes256Sha384,
1017 => record.header_len + T.paddedLength(T.iv_len + cleartext.len + T.mac_len),
1018 Aead12Aes128Gcm,
1019 Aead12Aes256Gcm,
1020 Aead12ChaCha,
1021 Aes128GcmSha256,
1022 Aes256GcmSha384,
1023 ChaChaSha256,
1024 Aegis128Sha256,
1025 => cleartext.len + T.encrypt_overhead,
1026 else => unreachable,
1027 };
1028 },
1029 };
1030 try testing.expectEqual(client_cipher.recordLen(cleartext.len), encrypted.len);
1031 try testing.expectEqual(expected_encrypted_len, encrypted.len);
1032 // decrypt
1033 const content_type, const decrypted = try server_cipher.decrypt(&buf, Record.init(encrypted));
1034 try testing.expectEqualSlices(u8, cleartext, decrypted);
1035 try testing.expectEqual(.application_data, content_type);
1036 }
1037 // server to client
1038 {
1039 const encrypted = try server_cipher.encrypt(&buf, .application_data, cleartext);
1040 const content_type, const decrypted = try client_cipher.decrypt(&buf, Record.init(encrypted));
1041 try testing.expectEqualSlices(u8, cleartext, decrypted);
1042 try testing.expectEqual(.application_data, content_type);
1043 }
1044}
1045
1046const data13 = ;
1047
1048/// Pair of ciphers based on keys from well-known key/iv pairs
1049pub fn testCiphers() struct { Cipher, Cipher } {
1050 const client_cipher: Cipher = .{ .AES_256_GCM_SHA384 = .{
1051 .encrypt_secret = undefined,
1052 .decrypt_secret = undefined,
1053 .encrypt_key = data13.client_application_key,
1054 .decrypt_key = data13.server_application_key,
1055 .encrypt_iv = data13.client_application_iv,
1056 .decrypt_iv = data13.server_application_iv,
1057 } };
1058 const server_cipher: Cipher = .{ .AES_256_GCM_SHA384 = .{
1059 .encrypt_secret = undefined,
1060 .decrypt_secret = undefined,
1061 .encrypt_key = data13.server_application_key,
1062 .decrypt_key = data13.client_application_key,
1063 .encrypt_iv = data13.server_application_iv,
1064 .decrypt_iv = data13.client_application_iv,
1065 } };
1066 return .{ client_cipher, server_cipher };
1067}
1068
1069test testCiphers {
1070 var client_cipher, var server_cipher = testCiphers();
1071 var buffer: [1024]u8 = undefined;
1072 {
1073 const payload = "ping";
1074 const ciphertext = try client_cipher.encrypt(&buffer, .application_data, payload);
1075 try testing.expectEqualSlices(u8, &data13.client_ping_wrapped, ciphertext);
1076
1077 const content_type, const cleartext = try server_cipher.decrypt(&buffer, record.Record.init(ciphertext));
1078 try testing.expectEqualStrings(payload, cleartext);
1079 try testing.expectEqual(.application_data, content_type);
1080 }
1081 {
1082 const close_notify = &proto.Alert.closeNotify();
1083 const ciphertext = try client_cipher.encrypt(&buffer, .alert, close_notify);
1084
1085 const content_type, const cleartext = try server_cipher.decrypt(&buffer, record.Record.init(ciphertext));
1086 try testing.expectEqualStrings(close_notify, cleartext);
1087 try testing.expectEqual(.alert, content_type);
1088 }
1089}
packages/zig/vendor/zig-tls/src/connection.zigdeleted+0-663
Changes to packages/zig/vendor/zig-tls/src/connection.zig
@@ -1,663 +0,0 @@
1const std = ;
2const builtin = ;
3const mem = std.mem;
4const Io = std.Io;
5const assert = std.debug.assert;
6
7const proto = ;
8const record = ;
9const Record = record.Record;
10const cipher = ;
11const Cipher = cipher.Cipher;
12const SessionResumption = .Options.SessionResumption;
13
14const log = std.log.scoped(.tls);
15
16pub const Connection = struct {
17 /// Underlying network connection stream reader/writer pair.
18 input: *Io.Reader, // source of the encrypted (ciphertext) data
19 output: *Io.Writer, // sink to send encrypted (ciphertext) data
20 cipher: Cipher,
21
22 max_encrypt_seq: u64 = std.math.maxInt(u64) - 1,
23 key_update_requested: bool = false,
24 received_close_notify: bool = false,
25 /// Part of the cleartext record returned from next but not yet read by client.
26 cleartext_buf: []const u8 = &.{},
27
28 session_resumption: ?*SessionResumption = null,
29 session_resumption_secret_idx: ?usize = null,
30
31 const Self = @This();
32
33 /// Encrypts and writes single tls record to the stream.
34 fn writeRecord(c: *Self, content_type: proto.ContentType, bytes: []const u8) !void {
35 assert(bytes.len <= cipher.max_cleartext_len);
36 // If key update is requested send key update message and update
37 // my encryption keys.
38 if (c.cipher.encryptSeq() >= c.max_encrypt_seq or @atomicLoad(bool, &c.key_update_requested, .monotonic)) {
39 @atomicStore(bool, &c.key_update_requested, false, .monotonic);
40
41 // If the request_update field is set to "update_requested",
42 // then the receiver MUST send a KeyUpdate of its own with
43 // request_update set to "update_not_requested" prior to sending
44 // its next Application Data record. This mechanism allows
45 // either side to force an update to the entire connection, but
46 // causes an implementation which receives multiple KeyUpdates
47 // while it is silent to respond with a single update.
48 //
49 // rfc: https://datatracker.ietf.org/doc/html/rfc8446#autoid-57
50 const key_update = &record.handshakeHeader(.key_update, 1) ++ [_]u8{0};
51 try c.encryptWrite(.handshake, key_update);
52 try c.cipher.keyUpdateEncrypt();
53 }
54 try c.encryptWrite(content_type, bytes);
55 }
56
57 fn encryptWrite(c: *Self, content_type: proto.ContentType, bytes: []const u8) !void {
58 const encrypted_len = c.cipher.recordLen(bytes.len);
59 const writable = try c.output.writableSliceGreedy(encrypted_len);
60 const rec = try c.cipher.encrypt(writable, content_type, bytes);
61 c.output.advance(rec.len);
62 try c.output.flush();
63 }
64
65 /// Returns next record of cleartext data. Null on end of stream.
66 /// Can be used in iterator like loop without memcpy to another buffer:
67 /// while (try client.next()) |buf| { ... }
68 pub fn next(c: *Self) anyerror!?[]const u8 {
69 return c.nextRecord(&.{}) catch |err| {
70 if (err == error.EndOfStream) return null;
71 // Write alert on tls errors.
72 // Stream errors return to the caller.
73 if (mem.startsWith(u8, @errorName(err), "Tls"))
74 try c.encryptWrite(.alert, &proto.alertFromError(err));
75 return err;
76 };
77 }
78
79 /// Decrypt next tls record into buffer, if buffer is not big enough reuse
80 /// input ciphertext buffer for cleartext. Returns cleartext of the next tls
81 /// record.
82 fn nextRecord(c: *Self, buffer: []u8) ![]const u8 {
83 assert(c.cleartext_buf.len == 0);
84 if (c.received_close_notify) return error.EndOfStream;
85 while (true) {
86 const rec = try Record.read(c.input);
87 if (rec.protocol_version != .tls_1_2) return error.TlsBadVersion;
88
89 // If provided buffer is not big enought reuse input buffer for
90 // cleartext. `rec.header` and `rec.payload`(ciphertext) are
91 // pointing somewhere in this buffer. Decrypter is first reading
92 // then writing a block, cleartext has less length then ciphertext,
93 // cleartext starts from the beginning of the buffer, so ciphertext
94 // is always ahead of cleartext.
95 const cleartext_buf = if (buffer.len >= rec.payload.len) buffer else @constCast(rec.buffer);
96 const content_type, const cleartext = try c.cipher.decrypt(cleartext_buf, rec);
97
98 switch (content_type) {
99 .application_data => {},
100 .handshake => {
101 const handshake_type: proto.Handshake = @fromBackingInt(@intCast(cleartext[0]));
102 switch (handshake_type) {
103 .new_session_ticket => {
104 if (c.session_resumption) |r| {
105 r.pushTicket(cleartext, c.session_resumption_secret_idx.?) catch {};
106 }
107 continue;
108 },
109 .key_update => {
110 if (cleartext.len != 5) return error.TlsDecodeError;
111 // rfc: Upon receiving a KeyUpdate, the receiver MUST
112 // update its receiving keys.
113 try c.cipher.keyUpdateDecrypt();
114 const key: proto.KeyUpdateRequest = @fromBackingInt(@intCast(cleartext[4]));
115 switch (key) {
116 .update_requested => {
117 @atomicStore(bool, &c.key_update_requested, true, .monotonic);
118 },
119 .update_not_requested => {},
120 else => return error.TlsIllegalParameter,
121 }
122 // this record is handled read next
123 continue;
124 },
125 else => {},
126 }
127 },
128 .alert => {
129 if (cleartext.len < 2) return error.TlsUnexpectedMessage;
130 try proto.Alert.parse(cleartext[0..2].*).toError();
131 // server side clean shutdown
132 c.received_close_notify = true;
133 return error.EndOfStream;
134 },
135 else => return error.TlsUnexpectedMessage,
136 }
137 return cleartext;
138 }
139 }
140
141 pub fn eof(c: *Self) bool {
142 return c.received_close_notify and c.cleartext_buf.len == 0;
143 }
144
145 pub fn close(c: *Self) anyerror!void {
146 if (c.received_close_notify) return;
147 try c.writeRecord(.alert, &proto.Alert.closeNotify());
148 }
149
150 // write/read
151
152 /// Encrypts cleartext and writes it to the underlying stream as single
153 /// tls record. Max single tls record payload length is 1<<14 (16K)
154 /// bytes.
155 pub fn write(c: *Self, bytes: []const u8) !usize {
156 const encrypt_overhead = c.cipher.encryptOverhead();
157 assert(c.output.buffer.len > encrypt_overhead);
158 // Find maximum number of bytes which can fit into output buffer as encrypted ciphertext
159 const n = @min(bytes.len, cipher.max_cleartext_len, c.output.buffer.len - encrypt_overhead);
160 try c.writeRecord(.application_data, bytes[0..n]);
161 return n;
162 }
163
164 /// Encrypts cleartext and writes it to the underlying stream. If needed
165 /// splits cleartext into multiple tls record.
166 pub fn writeAll(c: *Self, bytes: []const u8) !void {
167 var index: usize = 0;
168 while (index < bytes.len) {
169 index += try c.write(bytes[index..]);
170 }
171 }
172
173 pub fn read(c: *Self, buffer: []u8) !usize {
174 if (c.cleartext_buf.len == 0) {
175 const cleartext = c.nextRecord(buffer) catch |err| {
176 if (err == error.EndOfStream) return 0;
177 if (mem.startsWith(u8, @errorName(err), "Tls"))
178 try c.encryptWrite(.alert, &proto.alertFromError(err));
179 return err;
180 };
181 if (cleartext.ptr == buffer.ptr) {
182 // provided buffer is used for cleartext
183 return cleartext.len;
184 }
185 // Buffer was too small input ciphertext buffer was used for cleartext
186 // Store referenct to the cleartext
187 c.cleartext_buf = cleartext;
188 }
189 // move part of the cleartext_buf into provided buffer
190 const n = @min(c.cleartext_buf.len, buffer.len);
191 @memcpy(buffer[0..n], c.cleartext_buf[0..n]);
192 c.cleartext_buf = c.cleartext_buf[n..];
193 return n;
194 }
195
196 /// Returns the number of bytes read. If the number read is smaller than
197 /// `buffer.len`, it means the stream reached the end.
198 pub fn readAll(c: *Self, buffer: []u8) !usize {
199 return c.readAtLeast(buffer, buffer.len);
200 }
201
202 /// Returns the number of bytes read, calling the underlying read function
203 /// the minimal number of times until the buffer has at least `len` bytes
204 /// filled. If the number read is less than `len` it means the stream
205 /// reached the end.
206 pub fn readAtLeast(c: *Self, buffer: []u8, len: usize) !usize {
207 assert(len <= buffer.len);
208 var index: usize = 0;
209 while (index < len) {
210 const amt = try c.read(buffer[index..]);
211 if (amt == 0) break;
212 index += amt;
213 }
214 return index;
215 }
216
217 /// Returns the number of bytes read. If the number read is less than
218 /// the space provided it means the stream reached the end.
219 pub fn readv(c: *Self, iovecs: []std.posix.iovec) !usize {
220 var vp: VecPut = .{ .iovecs = iovecs };
221 while (true) {
222 if (c.cleartext_buf.len == 0) {
223 c.cleartext_buf = try c.next() orelse break;
224 }
225 const n = vp.put(c.cleartext_buf);
226 const read_buf_len = c.cleartext_buf.len;
227 c.cleartext_buf = c.cleartext_buf[n..];
228 if (n < read_buf_len) break;
229 }
230 return vp.total;
231 }
232
233 // Io.Reader interface
234
235 pub const Reader = struct {
236 conn: *Connection,
237 interface: Io.Reader,
238 err: ?anyerror = null,
239
240 pub fn init(c: *Connection, buffer: []u8) Reader {
241 return .{
242 .conn = c,
243 .interface = .{
244 .vtable = &.{
245 .stream = stream,
246 },
247 .buffer = buffer,
248 .seek = 0,
249 .end = 0,
250 },
251 };
252 }
253
254 fn stream(r: *Io.Reader, w: *Io.Writer, limit: Io.Limit) Io.Reader.StreamError!usize {
255 const self: *Reader = @fieldParentPtr("interface", r);
256 const n = self.conn.read(limit.slice(w.unusedCapacitySlice())) catch |err| {
257 self.err = err;
258 if (err == error.EndOfStream) return error.EndOfStream;
259 return error.ReadFailed;
260 };
261 if (n == 0) return error.EndOfStream;
262 w.advance(n);
263 return n;
264 }
265 };
266
267 /// There is no strict requirement on buffer size.
268 /// If the buffer is big enough tls record will be decrypted directly into
269 /// provided buffer. If not input buffer will be used for record decryption
270 /// and than cleartext will be copied to the reader buffer when needed.
271 pub fn reader(c: *Self, buffer: []u8) Reader {
272 return .init(c, buffer);
273 }
274
275 // Io.Writer interface
276
277 pub const Writer = struct {
278 conn: *Connection,
279 interface: Io.Writer,
280 err: ?anyerror = null,
281
282 pub fn init(c: *Connection, buffer: []u8) Writer {
283 return .{
284 .conn = c,
285 .interface = .{
286 .vtable = &.{
287 .drain = drain,
288 },
289 .buffer = buffer,
290 .end = 0,
291 },
292 };
293 }
294
295 fn drain(w: *Io.Writer, data: []const []const u8, splat: usize) Io.Writer.Error!usize {
296 const self: *Writer = @fieldParentPtr("interface", w);
297 // w.buffer is consumed first
298 try self.writeAll(w.buffered());
299 w.end = 0;
300
301 if (data.len == 0) return 0;
302 // Followed by each slice of `data` in order
303 var n: usize = 0;
304 for (data[0 .. data.len - 1]) |bytes| {
305 try self.writeAll(bytes);
306 n += bytes.len;
307 }
308
309 // Last element of `data` is repeated as necessary so that it is
310 // written `splat` number of times, which may be zero.
311 const pattern = data[data.len - 1];
312 for (0..splat) |_| {
313 try self.writeAll(pattern);
314 n += pattern.len;
315 }
316
317 // Number of bytes consumed from `data` is returned, excluding bytes
318 // from w.buffer.
319 return n;
320 }
321
322 fn writeAll(self: *Writer, bytes: []const u8) Io.Writer.Error!void {
323 self.conn.writeAll(bytes) catch |err| {
324 self.err = err;
325 return error.WriteFailed;
326 };
327 }
328 };
329
330 pub fn writer(c: *Self, buffer: []u8) Writer {
331 return .init(c, buffer);
332 }
333};
334
335const testing = std.testing;
336const data12 = ;
337const testu = ;
338
339test "encrypt decrypt" {
340 var output_buf: [1024]u8 = undefined;
341 const server_pongs = data12.server_pong ++ data12.server_pong ++
342 data12.server_pong ++ data12.server_pong;
343 var stream_reader: Io.Reader = .fixed(&server_pongs);
344 var stream_writer: Io.Writer = .fixed(&output_buf);
345 var conn: Connection = .{
346 .input = &stream_reader,
347 .output = &stream_writer,
348 .cipher = try Cipher.initTls12(.ECDHE_RSA_WITH_AES_128_CBC_SHA, &data12.key_material, .client),
349 };
350 conn.cipher.ECDHE_RSA_WITH_AES_128_CBC_SHA.rnd = testu.random(0); // use fixed rng
351
352 { // encrypt verify data from example
353 _ = testu.random(0x40); // sets iv to 40, 41, ... 4f
354 try conn.writeRecord(.handshake, &data12.client_finished);
355 try testing.expectEqualSlices(u8, &data12.verify_data_encrypted_msg, conn.output.buffered());
356 }
357 _ = conn.output.consumeAll(); // reset writer buffer
358 { // encrypt ping
359 const cleartext = "ping";
360 _ = testu.random(0); // sets iv to 00, 01, ... 0f
361
362 try conn.writeAll(cleartext);
363 try testing.expectEqualSlices(u8, &data12.encrypted_ping_msg, conn.output.buffered());
364 }
365 _ = conn.output.consumeAll();
366 { // writer interface
367 const cleartext = "ping";
368 _ = testu.random(0); // sets iv to 00, 01, ... 0f
369 conn.cipher.ECDHE_RSA_WITH_AES_128_CBC_SHA.encrypt_seq = 1; // reset sequence
370
371 var writer = conn.writer(&.{});
372 var w = &writer.interface;
373 try w.writeAll(cleartext);
374 try testing.expectEqualSlices(u8, &data12.encrypted_ping_msg, conn.output.buffered());
375 }
376 { // decrypt server pong message
377 conn.cipher.ECDHE_RSA_WITH_AES_128_CBC_SHA.decrypt_seq = 1;
378 try testing.expectEqualStrings("pong", (try conn.next()).?);
379 }
380 { // test reader interface
381 conn.cipher.ECDHE_RSA_WITH_AES_128_CBC_SHA.decrypt_seq = 1;
382 var buffer: [2]u8 = undefined;
383 var reader = conn.reader(&buffer);
384 var rdr = &reader.interface;
385 try testing.expectEqualStrings("", conn.cleartext_buf);
386 try testing.expectEqualStrings("po", try rdr.take(rdr.buffer.len));
387 // cleartext record part which didn't fit into reader buffer
388 try testing.expectEqualStrings("ng", conn.cleartext_buf);
389 try testing.expectEqualStrings("n", try rdr.take(1));
390 try testing.expectEqualStrings("", conn.cleartext_buf);
391 try testing.expectEqualStrings("g", try rdr.take(1));
392 }
393 { // reader discard
394 conn.cipher.ECDHE_RSA_WITH_AES_128_CBC_SHA.decrypt_seq = 1;
395 var buffer: [5]u8 = undefined;
396 var reader = conn.reader(&buffer);
397 var rdr = &reader.interface;
398 try testing.expectEqual(1, try rdr.discard(.limited(1)));
399 try testing.expectEqualStrings("ong", conn.cleartext_buf);
400 try testing.expectEqual(3, try rdr.discard(.limited(3)));
401 }
402 { // test readv interface
403 conn.cipher.ECDHE_RSA_WITH_AES_128_CBC_SHA.decrypt_seq = 1;
404 var buffer: [9]u8 = undefined;
405 var iovecs = [_]std.posix.iovec{
406 .{ .base = &buffer, .len = 3 },
407 .{ .base = buffer[3..], .len = 3 },
408 .{ .base = buffer[6..], .len = 3 },
409 };
410 const n = try conn.readv(iovecs[0..]);
411 try testing.expectEqual(4, n);
412 try testing.expectEqualStrings("pong", buffer[0..n]);
413 }
414}
415
416// Copied from: https://github.com/ziglang/zig/blob/455899668b620dfda40252501c748c0a983555bd/lib/std/crypto/tls/Client.zig#L1354
417/// Abstraction for sending multiple byte buffers to a slice of iovecs.
418pub const VecPut = struct {
419 iovecs: []const std.posix.iovec,
420 idx: usize = 0,
421 off: usize = 0,
422 total: usize = 0,
423
424 /// Returns the amount actually put which is always equal to bytes.len
425 /// unless the vectors ran out of space.
426 pub fn put(vp: *VecPut, bytes: []const u8) usize {
427 if (vp.idx >= vp.iovecs.len) return 0;
428 var bytes_i: usize = 0;
429 while (true) {
430 const v = vp.iovecs[vp.idx];
431 const dest = v.base[vp.off..v.len];
432 const src = bytes[bytes_i..][0..@min(dest.len, bytes.len - bytes_i)];
433 @memcpy(dest[0..src.len], src);
434 bytes_i += src.len;
435 vp.off += src.len;
436 if (vp.off >= v.len) {
437 vp.off = 0;
438 vp.idx += 1;
439 if (vp.idx >= vp.iovecs.len) {
440 vp.total += bytes_i;
441 return bytes_i;
442 }
443 }
444 if (bytes_i >= bytes.len) {
445 vp.total += bytes_i;
446 return bytes_i;
447 }
448 }
449 }
450};
451
452test "client/server connection" {
453 // create ciphers pair
454 const cipher_client, const cipher_server = brk: {
455 const Transcript = .Transcript;
456 const CipherSuite = .CipherSuite;
457 const cipher_suite: CipherSuite = .AES_256_GCM_SHA384;
458
459 var rnd: [128]u8 = undefined;
460 .fill(&rnd);
461 const secret = Transcript.Secret{
462 .client = rnd[0..64],
463 .server = rnd[64..],
464 };
465
466 break :brk .{
467 try Cipher.initTls13(cipher_suite, secret, .client),
468 try Cipher.initTls13(cipher_suite, secret, .server),
469 };
470 };
471
472 var client_conn: Connection = .{
473 .input = undefined,
474 .output = undefined,
475 .cipher = cipher_client,
476 };
477 var server_conn: Connection = .{
478 .input = undefined,
479 .output = undefined,
480 .cipher = cipher_server,
481 };
482
483 // big enough cleartext to produce multiple tls records
484 var cleartext_buf: [cipher.max_cleartext_len * 5]u8 = undefined;
485 // fill cleartext_buf with random bytes
486 var prng = std.Random.DefaultPrng.init(0);
487 const random = prng.random();
488 random.bytes(&cleartext_buf);
489
490 // cleartext -> client_conn -> ciphertext -> server_conn -> cleartext
491 for (0..16) |_| {
492 // use random part of clartext_buf
493 const n = random.intRangeAtMost(usize, cipher.max_cleartext_len + 1, cleartext_buf.len);
494 const client_cleartext = cleartext_buf[0..n];
495
496 // prepare ciphertext buffer
497 var ciphertext_buf: [cleartext_buf.len]u8 = undefined;
498 var w: Io.Writer = .fixed(&ciphertext_buf);
499 client_conn.output = &w;
500
501 // write cleartext to the server side
502 try client_conn.writeAll(client_cleartext);
503 const ciphertext_len = NonBlock.init(cipher_client).encryptedLength(n);
504 try testing.expectEqual(ciphertext_len, w.buffered().len);
505
506 // feed ciphertext from client to the server
507 var r: Io.Reader = .fixed(w.buffered());
508 server_conn.input = &r;
509 var server_cleartext_buf: [cleartext_buf.len]u8 = undefined;
510 // read cleartext from the server connection
511 const nr = try server_conn.readAll(&server_cleartext_buf);
512 const server_cleartext = server_cleartext_buf[0..nr];
513
514 try testing.expectEqual(n, nr);
515 try testing.expectEqualSlices(u8, client_cleartext, server_cleartext);
516 }
517}
518
519pub const NonBlock = struct {
520 const Self = @This();
521
522 inner: Connection,
523
524 pub fn init(c: Cipher) Self {
525 return .{ .inner = .{ .cipher = c, .input = undefined, .output = undefined } };
526 }
527
528 /// Required ciphertext buffer length for the given cleartext length.
529 pub fn encryptedLength(self: Self, cleartext_len: usize) usize {
530 const c = self.inner.cipher;
531 const records_count = cleartext_len / cipher.max_cleartext_len;
532 if (records_count == 0) return c.recordLen(cleartext_len);
533 const last_chunk_len = cleartext_len - cipher.max_cleartext_len * records_count;
534 return c.recordLen(cipher.max_cleartext_len) * records_count +
535 if (last_chunk_len == 0) 0 else c.recordLen(last_chunk_len);
536 }
537
538 fn reset(self: *Self) void {
539 self.inner.input = undefined;
540 self.inner.output = undefined;
541 }
542
543 /// Encrypts cleartext into ciphertext.
544 /// If ciphertext.len is >= encryptedLength(cleartext.len) whole
545 /// cleartext will be consumed.
546 pub fn encrypt(
547 self: *Self,
548 /// Cleartext data to encrypt
549 cleartext: []const u8,
550 /// Write buffer for ciphertext; encrypted data
551 ciphertext: []u8,
552 ) !struct {
553 /// Number of bytes consumed from cleartext
554 cleartext_pos: usize = 0,
555 /// Unused part of the provided cleartext buffer
556 unused_cleartext: []const u8,
557 /// Encrypted ciphertext data
558 ciphertext: []u8,
559 } {
560 defer self.reset();
561 var output: Io.Writer = .fixed(ciphertext);
562 self.inner.output = &output;
563
564 var n: usize = 0;
565 while (n < cleartext.len and output.end < output.buffer.len) {
566 n += try self.inner.write(cleartext[n..]);
567 }
568 return .{
569 .cleartext_pos = n,
570 .unused_cleartext = cleartext[n..],
571 .ciphertext = output.buffered(),
572 };
573 }
574
575 /// Decrypts ciphertext into cleartext.
576 /// NOTE: It is safe to reuses ciphertext buffer for cleartext data.
577 pub fn decrypt(
578 self: *Self,
579 /// Ciphertext data recieved from the other side of the tls connection
580 ciphertext: []const u8,
581 /// Write buffer for cleartext; decrypted data
582 cleartext: []u8,
583 ) !struct {
584 /// Number of bytes consumed from provided ciphertext buffer
585 ciphertext_pos: usize,
586 /// Unconsumed part of the provided ciphertext buffer
587 unused_ciphertext: []const u8,
588 /// Decrypted cleartext data
589 cleartext: []u8,
590 /// Is clear notify alert received
591 closed: bool = false,
592 } {
593 defer self.reset();
594 var input: Io.Reader = .fixed(ciphertext);
595 self.inner.input = &input;
596
597 var n: usize = 0;
598 while (n < cleartext.len) {
599 const nn = self.inner.read(cleartext[n..]) catch |err| switch (err) {
600 error.InputBufferUndersize => break,
601 else => return err,
602 };
603 if (nn == 0) break;
604 n += nn;
605 }
606
607 return .{
608 .ciphertext_pos = input.seek,
609 .unused_ciphertext = input.buffered(),
610 .cleartext = cleartext[0..n],
611 .closed = self.inner.received_close_notify,
612 };
613 }
614
615 pub fn close(self: *Self, ciphertext: []u8) ![]const u8 {
616 return try self.inner.cipher.encrypt(ciphertext, .alert, &proto.Alert.closeNotify());
617 }
618};
619
620test "nonblock encrypt" {
621 const data13 = ;
622 const client_cipher, _ = cipher.testCiphers();
623 var conn = NonBlock.init(client_cipher);
624
625 const cleartext = "ping";
626 try testing.expectEqual(26, conn.encryptedLength(cleartext.len));
627 var ciphertext: [32]u8 = undefined;
628
629 // with enough big buffer
630 {
631 const res = try conn.encrypt(cleartext, &ciphertext);
632 try testing.expectEqual(cleartext.len, res.cleartext_pos);
633 try testing.expectEqual(26, res.ciphertext.len);
634 try testing.expectEqualSlices(u8, &data13.client_ping_wrapped, res.ciphertext);
635 }
636
637 // while ciphertext buffer is not enough for cleartext record
638 for (conn.inner.cipher.encryptOverhead() + 1..ciphertext.len) |i| {
639 const res = try conn.encrypt(cleartext, ciphertext[0..i]);
640 const expected_used_cleartext = @min(cleartext.len, i - conn.inner.cipher.encryptOverhead());
641 try testing.expectEqual(expected_used_cleartext, res.cleartext_pos);
642 try testing.expectEqual(expected_used_cleartext + conn.inner.cipher.encryptOverhead(), res.ciphertext.len);
643 }
644}
645
646test "nonblock decrypt" {
647 const data13 = ;
648 _, const server_cipher = cipher.testCiphers();
649 var conn = NonBlock.init(server_cipher);
650
651 const ciphertext = data13.client_ping_wrapped;
652 var cleartext_buf: [32]u8 = undefined;
653
654 for (1..ciphertext.len - 1) |i| {
655 const res = try conn.decrypt(ciphertext[0..i], &cleartext_buf);
656 try testing.expectEqual(0, res.ciphertext_pos);
657 try testing.expectEqual(0, res.cleartext.len);
658 try testing.expectEqual(i, res.unused_ciphertext.len);
659 }
660
661 const res = try conn.decrypt(&ciphertext, &cleartext_buf);
662 try testing.expectEqualSlices(u8, "ping", res.cleartext);
663}
packages/zig/vendor/zig-tls/src/handshake_client.zigdeleted+0-1396
Changes to packages/zig/vendor/zig-tls/src/handshake_client.zig
@@ -1,1396 +0,0 @@
1const std = ;
2const assert = std.debug.assert;
3const crypto = std.crypto;
4const mem = std.mem;
5const posix = std.posix;
6const Io = std.Io;
7const Certificate = crypto.Certificate;
8
9// Helper function for Zig 0.16 compatibility
10fn getMilliTimestamp() i64 {
11 var ts: std.c.timespec = undefined;
12 if (std.c.clock_gettime(.REALTIME, &ts) != 0) return 0;
13 return ts.sec * 1000 + @divFloor(ts.nsec, std.time.ns_per_ms);
14}
15
16const rng = ;
17const Cipher = .Cipher;
18const CipherSuite = .CipherSuite;
19const cipher_suites = .cipher_suites;
20const max_cleartext_len = .max_cleartext_len;
21
22const Transcript = .Transcript;
23const record = ;
24const Record = record.Record;
25const rsa = ;
26const key_log = ;
27const PrivateKey = ;
28const proto = ;
29
30const common = ;
31const CertificateBuilder = common.CertificateBuilder;
32const CertificateParser = common.CertificateParser;
33const DhKeyPair = common.DhKeyPair;
34const CertKeyPair = common.CertKeyPair;
35const cert = common.cert;
36
37const log = std.log.scoped(.tls);
38
39pub const Options = struct {
40 host: []const u8,
41 /// Set of root certificate authorities that clients use when verifying
42 /// server certificates.
43 root_ca: cert.Bundle,
44
45 /// Controls whether a client verifies the server's certificate chain and
46 /// host name.
47 insecure_skip_verify: bool = false,
48
49 /// List of cipher suites to use.
50 /// To use just tls 1.3 cipher suites:
51 /// .cipher_suites = &tls.CipherSuite.tls13,
52 /// To select particular cipher suite:
53 /// .cipher_suites = &[_]tls.CipherSuite{tls.CipherSuite.CHACHA20_POLY1305_SHA256},
54 cipher_suites: []const CipherSuite = cipher_suites.all,
55
56 /// List of named groups to use.
57 /// To use specific named group:
58 /// .named_groups = &[_]tls.NamedGroup{.secp384r1},
59 named_groups: []const proto.NamedGroup = &[_]proto.NamedGroup{
60 .x25519,
61 .secp256r1,
62 .secp384r1,
63 .x25519_ml_kem768,
64 },
65
66 /// Client authentication certificates and private key.
67 auth: ?*CertKeyPair = null,
68
69 /// If this structure is provided it will be filled with handshake attributes
70 /// at the end of the handshake process.
71 diagnostic: ?*Diagnostic = null,
72
73 /// For logging current connection tls keys, so we can share them with
74 /// Wireshark and analyze decrypted traffic there.
75 key_log_callback: ?key_log.Callback = null,
76
77 session_resumption: ?*SessionResumption = null,
78
79 pub const Diagnostic = struct {
80 tls_version: proto.Version = @fromBackingInt(@intCast(0)),
81 cipher_suite_tag: CipherSuite = @fromBackingInt(@intCast(0)),
82 named_group: proto.NamedGroup = @fromBackingInt(@intCast(0)),
83 signature_scheme: proto.SignatureScheme = @fromBackingInt(@intCast(0)),
84 client_signature_scheme: proto.SignatureScheme = @fromBackingInt(@intCast(0)),
85 is_session_resumption: bool = false,
86 max_server_record_len: usize = 0,
87 max_server_cleartext_len: usize = 0,
88 max_client_record_len: usize = 0,
89 };
90
91 /// Collects and stores session resumption tickets.
92 pub const SessionResumption = struct {
93 const Self = @This();
94
95 pub const Ticket = struct {
96 // in seconds
97 lifetime: u32,
98 // in milliseconds
99 created_at: i64,
100 // value to add to the milliseconds age
101 age_add: u32,
102 // nonce and identity are pointes to the part of the payload
103 nonce: []const u8, // <0..255>
104 identity: []const u8, // <1..2^16-1>
105 // payload is allocated and should be freed
106 payload: []const u8,
107 // pointer to the externaly allocated secret
108 secret: []const u8,
109
110 pub fn init(payload: []const u8, secret: []const u8) !Ticket {
111 var d = record.Decoder.init(.handshake, payload);
112 const handshake_type: proto.Handshake = @fromBackingInt(@intCast(try d.decode(u8)));
113 if (handshake_type != proto.Handshake.new_session_ticket) return error.InvalidType;
114 const length = try d.decode(u24);
115 if (d.rest().len != length) return error.InvalidLength;
116
117 return .{
118 .lifetime = try d.decode(u32),
119 .age_add = try d.decode(u32),
120 .nonce = try d.slice(try d.decode(u8)),
121 .identity = try d.slice(try d.decode(u16)),
122 .payload = payload,
123 .secret = secret,
124 .created_at = getMilliTimestamp(),
125 };
126 }
127
128 /// Obfuscated age for pre shared extension in client hello message when
129 /// using this ticket.
130 pub fn obfuscatedAge(t: Ticket) u32 {
131 const age: u32 = @intCast(getMilliTimestamp() - t.created_at);
132 return t.age_add +% age;
133 }
134 };
135
136 allocator: std.mem.Allocator,
137 hash_tag: CipherSuite.HashTag = .sha256,
138 tickets: std.ArrayListUnmanaged(Ticket) = .empty,
139 secrets: std.ArrayListUnmanaged([]const u8) = .empty,
140 used_tickets: usize = 0,
141
142 pub fn init(allocator: mem.Allocator) Self {
143 return .{ .allocator = allocator };
144 }
145
146 /// Set resumption master secret (known at then end of the handshake) to
147 /// be used by new session ticket's sent on that connection. Returns
148 /// index of the appended secret in the secrets list. Connection should
149 /// use that index in pushTicket to connect ticket and the secret.
150 pub fn appendSecret(self: *Self, tag: CipherSuite.HashTag, secret: []const u8) !usize {
151 if (self.secrets.items.len == 0) self.hash_tag = tag;
152 if (tag != self.hash_tag) return error.InvalidHash;
153 try self.secrets.ensureUnusedCapacity(self.allocator, 1);
154 self.secrets.appendAssumeCapacity(try self.allocator.dupe(u8, secret));
155 return self.secrets.items.len - 1;
156 }
157
158 /// When new session ticket message is recived connection should push ticket.
159 pub fn pushTicket(self: *Self, data: []const u8, secret_idx: usize) !void {
160 assert(self.secrets.items.len > secret_idx);
161 try self.tickets.ensureUnusedCapacity(self.allocator, 1);
162 const payload = try self.allocator.dupe(u8, data);
163 errdefer self.allocator.free(payload);
164 const ticket = try Ticket.init(payload, self.secrets.items[secret_idx]);
165 self.tickets.appendAssumeCapacity(ticket);
166 }
167
168 pub fn popTicket(self: *Self) ?Ticket {
169 if (self.used_tickets >= self.tickets.items.len) return null;
170 defer self.used_tickets += 1;
171 return self.tickets.items[self.used_tickets];
172 }
173
174 pub fn deinit(self: *Self) void {
175 for (self.tickets.items) |ticket|
176 self.allocator.free(ticket.payload);
177 self.tickets.deinit(self.allocator);
178 for (self.secrets.items) |secret|
179 self.allocator.free(secret);
180 self.secrets.deinit(self.allocator);
181 }
182
183 pub fn print(self: Self) !void {
184 std.debug.print(
185 "resumption tag: {}, tickets: {} used_tickets: {}\n",
186 .{ self.hash_tag, self.tickets.items.len, self.used_tickets },
187 );
188 for (self.tickets.items) |ticket| {
189 std.debug.print(
190 " lifetime: {}, ticket age add: {}, nonce: {x}\n ticket: {x}\n secret: {x}\n\n",
191 .{ ticket.lifetime, ticket.age_add, ticket.nonce, ticket.identity, ticket.secret },
192 );
193 }
194 }
195 };
196};
197
198const ResumptionTicket = Options.SessionResumption.Ticket;
199
200const supported_named_groups = &[_]proto.NamedGroup{
201 .x25519,
202 .secp256r1,
203 .secp384r1,
204 .x25519_ml_kem768,
205};
206
207/// Handshake parses tls server message and creates client messages. Collects
208/// tls attributes: server random, cipher suite and so on. Client messages are
209/// created using provided buffer. Provided record reader is used to get tls
210/// record when needed.
211pub const Handshake = struct {
212 /// Underlying network connection stream reader/writer pair.
213 input: *Io.Reader,
214 output: *Io.Writer,
215
216 client_random: [32]u8 = undefined,
217 server_random: [32]u8 = undefined,
218 master_secret: [48]u8 = undefined,
219 key_material: [48 * 4]u8 = undefined, // for sha256 32 * 4 is filled, for sha384 48 * 4
220
221 transcript: Transcript = .{},
222 cipher_suite: CipherSuite = @fromBackingInt(@intCast(0)),
223 named_group: ?proto.NamedGroup = null,
224 dh_kp: DhKeyPair = undefined,
225 rsa_secret: RsaSecret = undefined,
226 tls_version: proto.Version = .tls_1_2,
227 cipher: Cipher = undefined,
228 cert: CertificateParser = undefined,
229 client_certificate_requested: bool = false,
230 // public key len: x25519 = 32, secp256r1 = 65, secp384r1 = 97, x25519_ml_kem768 = 1120
231 server_pub_key_buf: [1120]u8 = undefined,
232 server_pub_key: []const u8 = undefined,
233 pre_shared_selected_identity: ?u16 = null,
234 // statistics
235 max_server_record_len: usize = 0,
236 max_server_cleartext_len: usize = 0,
237 max_client_record_len: usize = 0,
238
239 const Self = @This();
240
241 fn initKeys(h: *Self, opt: Options) !void {
242 const init_keys_buf_len = 32 + 46 + DhKeyPair.seed_len;
243 var buf: [init_keys_buf_len]u8 = undefined;
244 rng.fill(&buf);
245
246 h.client_random = buf[0..32].*;
247 h.rsa_secret = RsaSecret.init(buf[32..][0..46].*);
248 h.dh_kp = try DhKeyPair.init(buf[32 + 46 ..][0..DhKeyPair.seed_len].*, opt.named_groups);
249
250 h.cert = .{
251 .host = opt.host,
252 .root_ca = opt.root_ca,
253 .skip_verify = opt.insecure_skip_verify,
254 };
255 }
256
257 /// Handshake exchanges messages with server to get agreement about
258 /// cryptographic parameters. That upgrades existing client-server
259 /// connection to TLS connection. Returns cipher used in application for
260 /// encrypted message exchange.
261 ///
262 /// Handles TLS 1.2 and TLS 1.3 connections. After initial client hello
263 /// server chooses in its server hello which TLS version will be used.
264 ///
265 /// TLS 1.2 handshake messages exchange:
266 /// Client Server
267 /// --------------------------------------------------------------
268 /// ClientHello client flight 1 --->
269 /// ServerHello
270 /// Certificate
271 /// ServerKeyExchange
272 /// CertificateRequest*
273 /// <--- server flight 1 ServerHelloDone
274 /// Certificate*
275 /// ClientKeyExchange
276 /// CertificateVerify*
277 /// ChangeCipherSpec
278 /// Finished client flight 2 --->
279 /// ChangeCipherSpec
280 /// <--- server flight 2 Finished
281 ///
282 /// TLS 1.3 handshake messages exchange:
283 /// Client Server
284 /// --------------------------------------------------------------
285 /// ClientHello client flight 1 --->
286 /// ServerHello
287 /// {EncryptedExtensions}
288 /// {CertificateRequest*}
289 /// {Certificate}
290 /// {CertificateVerify}
291 /// <--- server flight 1 {Finished}
292 /// ChangeCipherSpec
293 /// {Certificate*}
294 /// {CertificateVerify*}
295 /// Finished client flight 2 --->
296 ///
297 /// * - optional
298 /// {} - encrypted
299 ///
300 /// References:
301 /// https://datatracker.ietf.org/doc/html/rfc5246#section-7.3
302 /// https://datatracker.ietf.org/doc/html/rfc8446#section-2
303 ///
304 pub fn handshake(h: *Self, opt: Options) !struct { Cipher, ?usize } {
305 var resumption_ticket: ?ResumptionTicket =
306 if (opt.session_resumption) |r|
307 r.popTicket()
308 else
309 null;
310
311 defer h.updateDiagnostic(opt, resumption_ticket != null);
312 try h.initKeys(opt);
313
314 if (resumption_ticket) |ticket| {
315 h.transcript.use(opt.session_resumption.?.hash_tag);
316 h.transcript.setPreSharedSecret(ticket.secret, ticket.nonce);
317 }
318
319 try h.makeClientHello(opt, resumption_ticket); // client flight 1
320 h.max_client_record_len = h.output.end;
321 try h.output.flush();
322
323 try h.readServerFlight1(); // server flight 1
324
325 if (resumption_ticket != null and h.pre_shared_selected_identity == null) {
326 // server didn't accept resuption
327 resumption_ticket = null;
328 h.transcript.clearPreSharedSecret();
329 }
330
331 if (resumption_ticket != null and h.cipher_suite.hash() != h.transcript.tag)
332 return error.TlsAlertIllegalParameter; // session resumption must use same hash as previous session
333 h.transcript.use(h.cipher_suite.hash());
334
335 // tls 1.3 specific handshake part
336 if (h.tls_version == .tls_1_3) {
337 try h.generateHandshakeCipher(opt.key_log_callback);
338 try h.readEncryptedServerFlight1(resumption_ticket != null); // server flight 1
339 const app_cipher = try h.generateApplicationCipher(opt.key_log_callback);
340 try h.makeClientFlight2Tls13(opt.auth); // client flight 2
341 h.max_client_record_len = @max(h.max_client_record_len, h.output.end);
342 try h.output.flush();
343
344 const secret_idx: ?usize = if (opt.session_resumption) |r|
345 try r.appendSecret(h.transcript.tag, h.transcript.resumptionSecret())
346 else
347 null;
348
349 return .{ app_cipher, secret_idx };
350 }
351
352 // tls 1.2 specific handshake part
353 try h.generateCipher(opt.key_log_callback);
354 try h.makeClientFlight2Tls12(opt.auth); // client flight 2
355 h.max_client_record_len = @max(h.max_client_record_len, h.output.end);
356 try h.output.flush();
357 try h.readServerFlight2(); // server flight 2
358 return .{ h.cipher, null };
359 }
360
361 fn clientFlight1(h: *Self, opt: Options) !void {
362 try h.makeClientHello(opt, null);
363 }
364
365 fn serverFlight1(h: *Self, opt: Options) !void {
366 try h.readServerFlight1();
367 h.transcript.use(h.cipher_suite.hash());
368 if (h.tls_version == .tls_1_3) {
369 try h.generateHandshakeCipher(opt.key_log_callback);
370 try h.readEncryptedServerFlight1(false);
371 }
372 }
373
374 fn clientFlight2(h: *Self, opt: Options) !void {
375 if (h.tls_version == .tls_1_3) {
376 const app_cipher = try h.generateApplicationCipher(opt.key_log_callback);
377 try h.makeClientFlight2Tls13(opt.auth);
378 h.cipher = app_cipher;
379 } else {
380 // tls 1.2 specific handshake part
381 try h.generateCipher(opt.key_log_callback);
382 try h.makeClientFlight2Tls12(opt.auth);
383 }
384 }
385
386 fn serverFlight2(h: *Self, _: Options) !void {
387 if (h.tls_version == .tls_1_3) return;
388 try h.readServerFlight2();
389 }
390
391 /// Prepare key material and generate cipher for TLS 1.2
392 fn generateCipher(h: *Self, key_log_callback: ?key_log.Callback) !void {
393 try h.verifyCertificateSignatureTls12();
394 try h.generateKeyMaterial(key_log_callback);
395 h.cipher = try Cipher.initTls12(h.cipher_suite, &h.key_material, .client);
396 }
397
398 /// Generate TLS 1.2 pre master secret, master secret and key material.
399 fn generateKeyMaterial(h: *Self, key_log_callback: ?key_log.Callback) !void {
400 const pre_master_secret = if (h.named_group) |named_group|
401 try h.dh_kp.sharedKey(named_group, h.server_pub_key)
402 else
403 &h.rsa_secret.secret;
404
405 h.transcript.masterSecret(&h.master_secret, pre_master_secret, h.client_random, h.server_random);
406 h.transcript.keyMaterial(&h.key_material, &h.master_secret, h.client_random, h.server_random);
407
408 if (key_log_callback) |cb| {
409 cb(key_log.label.client_random, &h.client_random, &h.master_secret);
410 }
411 }
412
413 /// TLS 1.3 cipher used during handshake
414 fn generateHandshakeCipher(h: *Self, key_log_callback: ?key_log.Callback) !void {
415 const shared_key = try h.dh_kp.sharedKey(h.named_group.?, h.server_pub_key);
416 const handshake_secret = h.transcript.handshakeSecret(shared_key);
417 if (key_log_callback) |cb| {
418 cb(key_log.label.server_handshake_traffic_secret, &h.client_random, handshake_secret.server);
419 cb(key_log.label.client_handshake_traffic_secret, &h.client_random, handshake_secret.client);
420 }
421 h.cipher = try Cipher.initTls13(h.cipher_suite, handshake_secret, .client);
422 }
423
424 /// TLS 1.3 application (client) cipher
425 fn generateApplicationCipher(h: *Self, key_log_callback: ?key_log.Callback) !Cipher {
426 const application_secret = h.transcript.applicationSecret();
427 if (key_log_callback) |cb| {
428 cb(key_log.label.server_traffic_secret_0, &h.client_random, application_secret.server);
429 cb(key_log.label.client_traffic_secret_0, &h.client_random, application_secret.client);
430 }
431 return try Cipher.initTls13(h.cipher_suite, application_secret, .client);
432 }
433
434 fn makeClientHello(h: *Self, opt: Options, resumption_ticket: ?ResumptionTicket) !void {
435 // Prepare data
436 const tls_versions = try CipherSuite.versions(opt.cipher_suites);
437 const shared_keys: []const []const u8 = if (tls_versions != .tls_1_2) brk: {
438 var keys: [supported_named_groups.len][]const u8 = undefined;
439 for (opt.named_groups, 0..) |ng, i| {
440 keys[i] = try h.dh_kp.publicKey(ng);
441 }
442 break :brk keys[0..opt.named_groups.len];
443 } else &.{};
444 const supported_versions = switch (tls_versions) {
445 .both => &[_]proto.Version{ .tls_1_3, .tls_1_2 },
446 .tls_1_3 => &[_]proto.Version{.tls_1_3},
447 .tls_1_2 => &[_]proto.Version{.tls_1_2},
448 };
449 const psk_binder_len = if (resumption_ticket == null) 0 else h.transcript.hashLength() + 3;
450
451 var w: record.Writer = .initFromIo(h.output);
452
453 // Placeholder for tls record header (5 bytes) and handshake header (4 bytes).
454 // We will write it when we know payload length.
455 const header_pos = try w.skip(9);
456
457 try w.enumValue(proto.Version.tls_1_2);
458 try w.slice(&h.client_random);
459 try w.byte(0); // no session id
460 try w.enumList(CipherSuite, opt.cipher_suites);
461 try w.slice(&[_]u8{ 0x01, 0x00 }); // no compression
462 // Remeber position to write extensions length
463 const ext_len_pos = try w.skip(2);
464 try w.extension(.supported_versions, supported_versions);
465 try w.extension(.signature_algorithms, common.supported_signature_algorithms);
466 try w.extension(.supported_groups, opt.named_groups);
467 try w.keyShare(opt.named_groups, shared_keys);
468 try w.serverName(opt.host);
469 // binder key placeholder
470 const binder_pos: ?usize = if (resumption_ticket) |ticket| brk: {
471 // Add pre shared key extension if this is session resumption. Must be last extension.
472 try w.extension(.psk_key_exchange_modes, &[_]proto.KeyExchangeModes{.psk_dhe_ke});
473 try w.preSharedKey(ticket.identity, ticket.obfuscatedAge(), psk_binder_len);
474 // Retruns placeholder for binder, we can write binder when we have
475 // hash up to thise point with valid extnsions length and
476 // record header written.
477 break :brk try w.skip(psk_binder_len);
478 } else null;
479
480 // Write extensions length to the placeholder
481 var ew = w.writerAt(ext_len_pos);
482 try ew.int(u16, w.pos() - ext_len_pos - 2);
483 // Writer header to the placeholder
484 var hw = w.writerAt(header_pos);
485 try hw.recordHeader(.handshake, w.pos() - 5);
486 try hw.handshakeRecordHeader(.client_hello, w.pos() - 9);
487
488 const pre_binder_hash_buf = w.buffered()[record.header_len .. w.pos() - psk_binder_len];
489 h.transcript.update(pre_binder_hash_buf);
490
491 if (binder_pos) |pos| {
492 // Write binder to the placeholder and update hash with binder extenstion
493 const binder = h.transcript.pskBinder();
494 var bw = w.writerAt(pos);
495 try bw.preSharedKeyBinder(binder);
496 h.transcript.update(w.buffered()[w.buffered().len - psk_binder_len ..]);
497 }
498
499 h.output.advance(w.buffered().len);
500 }
501
502 /// Process first flight of the messages from the server.
503 /// Read server hello message. If TLS 1.3 is chosen in server hello
504 /// return. For TLS 1.2 continue and read certificate, key_exchange
505 /// eventual certificate request and hello done messages.
506 fn readServerFlight1(h: *Self) !void {
507 var handshake_states: []const proto.Handshake = &.{.server_hello};
508
509 while (true) {
510 var d = try Record.decoder(h.input);
511 try d.expectContentType(.handshake);
512 h.transcript.update(d.payload);
513 h.max_server_record_len = @max(h.max_server_record_len, d.payload.len + record.header_len);
514
515 // Multiple handshake messages can be packed in single tls record.
516 while (!d.eof()) {
517 const handshake_type = try d.decode(proto.Handshake);
518
519 const length = try d.decode(u24);
520 if (length > max_cleartext_len)
521 return error.TlsUnsupportedFragmentedHandshakeMessage;
522
523 brk: {
524 for (handshake_states) |state|
525 if (state == handshake_type) break :brk;
526 return error.TlsUnexpectedMessage;
527 }
528 switch (handshake_type) {
529 .server_hello => { // server hello, ref: https://datatracker.ietf.org/doc/html/rfc5246#section-7.4.1.3
530 try h.parseServerHello(&d, length);
531 if (h.tls_version == .tls_1_3) {
532 if (!d.eof()) return error.TlsIllegalParameter;
533 return; // end of tls 1.3 server flight 1
534 }
535 handshake_states = if (h.cert.skip_verify)
536 &.{ .certificate, .server_key_exchange, .server_hello_done }
537 else
538 &.{.certificate};
539 },
540 .certificate => {
541 try h.cert.parseCertificate(&d, h.tls_version);
542 handshake_states = if (h.cipher_suite.keyExchange() == .rsa)
543 &.{.server_hello_done}
544 else
545 &.{.server_key_exchange};
546 },
547 .server_key_exchange => {
548 try h.parseServerKeyExchange(&d);
549 handshake_states = &.{ .certificate_request, .server_hello_done };
550 },
551 .certificate_request => {
552 h.client_certificate_requested = true;
553 try d.skip(length);
554 handshake_states = &.{.server_hello_done};
555 },
556 .server_hello_done => {
557 if (length != 0) return error.TlsIllegalParameter;
558 return;
559 },
560 else => return error.TlsUnexpectedMessage,
561 }
562 }
563 }
564 }
565
566 /// Parse server hello message.
567 fn parseServerHello(h: *Self, d: *record.Decoder, length: u24) !void {
568 if (try d.decode(proto.Version) != proto.Version.tls_1_2)
569 return error.TlsBadVersion;
570 h.server_random = try d.array(32);
571 if (isServerHelloRetryRequest(&h.server_random))
572 return error.TlsServerHelloRetryRequest;
573
574 const session_id_len = try d.decode(u8);
575 if (session_id_len > 32) return error.TlsIllegalParameter;
576 try d.skip(session_id_len);
577
578 h.cipher_suite = try d.decode(CipherSuite);
579 try h.cipher_suite.validate();
580 try d.skip(1); // skip compression method
581
582 const extensions_present = length > 2 + 32 + 1 + session_id_len + 2 + 1;
583 if (extensions_present) {
584 const exs_len = try d.decode(u16);
585 var l: usize = 0;
586 while (l < exs_len) {
587 const typ = try d.decode(proto.Extension);
588 const len = try d.decode(u16);
589 defer l += len + 4;
590
591 switch (typ) {
592 .supported_versions => {
593 switch (try d.decode(proto.Version)) {
594 .tls_1_2, .tls_1_3 => |v| h.tls_version = v,
595 else => return error.TlsIllegalParameter,
596 }
597 if (len != 2) return error.TlsIllegalParameter;
598 },
599 .key_share => {
600 h.named_group = try d.decode(proto.NamedGroup);
601 h.server_pub_key = try common.dupe(&h.server_pub_key_buf, try d.slice(try d.decode(u16)));
602 if (len != h.server_pub_key.len + 4) return error.TlsIllegalParameter;
603 },
604 .pre_shared_key => {
605 h.pre_shared_selected_identity = try d.decode(u16);
606 },
607 else => {
608 try d.skip(len);
609 },
610 }
611 }
612 }
613 }
614
615 fn isServerHelloRetryRequest(server_random: []const u8) bool {
616 // Ref: https://datatracker.ietf.org/doc/html/rfc8446#section-4.1.3
617 const hello_retry_request_magic = [32]u8{
618 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91,
619 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C,
620 };
621 return mem.eql(u8, server_random, &hello_retry_request_magic);
622 }
623
624 fn parseServerKeyExchange(h: *Self, d: *record.Decoder) !void {
625 const curve_type = try d.decode(proto.Curve);
626 h.named_group = try d.decode(proto.NamedGroup);
627 h.server_pub_key = try common.dupe(&h.server_pub_key_buf, try d.slice(try d.decode(u8)));
628 h.cert.signature_scheme = try d.decode(proto.SignatureScheme);
629 h.cert.signature = try common.dupe(&h.cert.signature_buf, try d.slice(try d.decode(u16)));
630 if (curve_type != .named_curve) return error.TlsIllegalParameter;
631 }
632
633 /// Read encrypted part (after server hello) of the server first flight
634 /// for TLS 1.3: change cipher spec, eventual certificate request,
635 /// certificate, certificate verify and handshake finished messages.
636 fn readEncryptedServerFlight1(h: *Self, is_session_resumption: bool) !void {
637 // buffer for decrypted handshake records
638 var cleartext_buffer: [max_cleartext_len]u8 = undefined;
639 // cleartext writer
640 var cw = Io.Writer.fixed(&cleartext_buffer);
641 // valid handshake types for the next record
642 var handshake_states: []const proto.Handshake = &.{.encrypted_extensions};
643
644 outer: while (true) {
645 // wrapped record decoder
646 const rec = try Record.read(h.input);
647 if (rec.protocol_version != .tls_1_2) return error.TlsBadVersion;
648 h.max_server_record_len = @max(h.max_server_record_len, rec.buffer.len);
649 switch (rec.content_type) {
650 .change_cipher_spec => {},
651 .application_data => {
652 // `decrypt` will return TlsCipherNoSpaceLeft
653 // unusedCapacitySlice is too small to hold cleartext
654 const content_type, const cleartext = try h.cipher.decrypt(cw.unusedCapacitySlice(), rec);
655 cw.advance(cleartext.len);
656 h.max_server_cleartext_len = @max(h.max_server_cleartext_len, cw.end);
657
658 var d = record.Decoder.init(content_type, cw.buffered());
659 try d.expectContentType(.handshake);
660 while (!d.eof()) {
661 const handshake_type = try d.decode(proto.Handshake);
662 const length = try d.decode(u24);
663
664 if (length > max_cleartext_len)
665 return error.TlsUnsupportedFragmentedHandshakeMessage;
666 if (length > d.rest().len)
667 continue :outer; // handshake fragmented into multiple records
668 // jump to outer must be before this defer to presrve buffers
669 defer {
670 // 'd.idx' data is consumed
671 h.transcript.update(d.payload[0..d.idx]);
672 _ = cw.consume(d.idx);
673 d = record.Decoder.init(content_type, cw.buffered());
674 }
675 // check valid handshake types in this state
676 brk: {
677 for (handshake_states) |state|
678 if (state == handshake_type) break :brk;
679 return error.TlsUnexpectedMessage;
680 }
681 switch (handshake_type) {
682 .encrypted_extensions => {
683 try d.skip(length);
684 handshake_states = if (is_session_resumption)
685 &.{ .certificate_request, .certificate, .finished }
686 else if (h.cert.skip_verify)
687 &.{ .certificate_request, .certificate, .finished }
688 else
689 &.{ .certificate_request, .certificate };
690 },
691 .certificate_request => {
692 h.client_certificate_requested = true;
693 try d.skip(length);
694 handshake_states = if (h.cert.skip_verify)
695 &.{ .certificate, .finished }
696 else
697 &.{.certificate};
698 },
699 .certificate => {
700 try h.cert.parseCertificate(&d, h.tls_version);
701 handshake_states = &.{.certificate_verify};
702 },
703 .certificate_verify => {
704 try h.cert.parseCertificateVerify(&d);
705 try h.cert.verifySignature(h.transcript.serverCertificateVerify());
706 handshake_states = &.{.finished};
707 },
708 .finished => {
709 const actual = try d.slice(length);
710 const expected = h.transcript.serverFinishedTls13();
711 if (!mem.eql(u8, expected, actual))
712 return error.TlsDecryptError;
713 return;
714 },
715 else => return error.TlsUnexpectedMessage,
716 }
717 }
718 },
719 else => return error.TlsUnexpectedMessage,
720 }
721 }
722 }
723
724 fn verifyCertificateSignatureTls12(h: *Self) !void {
725 if (h.cipher_suite.keyExchange() != .ecdhe) return;
726 const verify_bytes = brk: {
727 var buffer: [h.server_pub_key_buf.len + 128]u8 = undefined;
728 var w = Io.Writer.fixed(&buffer);
729 try w.writeAll(&h.client_random);
730 try w.writeAll(&h.server_random);
731 try w.writeByte(@backingInt(proto.Curve.named_curve));
732 try w.writeInt(u16, @backingInt(h.named_group.?), .big);
733 try w.writeByte(@intCast(h.server_pub_key.len));
734 try w.writeAll(h.server_pub_key);
735 break :brk w.buffered();
736 };
737 try h.cert.verifySignature(verify_bytes);
738 }
739
740 /// Create client key exchange, change cipher spec and handshake
741 /// finished messages for tls 1.2.
742 /// If client certificate is requested also adds client certificate and
743 /// certificate verify messages.
744 fn makeClientFlight2Tls12(h: *Self, auth: ?*CertKeyPair) !void {
745 var w: record.Writer = .initFromIo(h.output);
746 var cert_builder: ?CertificateBuilder = null;
747
748 // Client certificate
749 if (h.client_certificate_requested) {
750 if (auth) |a| {
751 const cb = h.certificateBuilder(a);
752 cert_builder = cb;
753 var hw = try w.writerAdvance(record.header_len);
754 try cb.makeCertificate(&hw);
755 h.transcript.update(hw.buffered());
756 try w.record(.handshake, hw.buffered());
757 } else {
758 var hw = try w.writerAdvance(record.header_len);
759 try hw.handshakeRecord(.certificate, &[_]u8{ 0, 0, 0 });
760 h.transcript.update(hw.buffered());
761 try w.record(.handshake, hw.buffered());
762 }
763 }
764
765 { // Client key exchange
766 var hw = try w.writerAdvance(record.header_len);
767 if (h.named_group) |named_group| {
768 const key = try h.dh_kp.publicKey(named_group);
769 try hw.handshakeRecordHeader(.client_key_exchange, 1 + key.len);
770 try hw.int(u8, key.len);
771 try hw.slice(key);
772 } else {
773 var key_buf: [512]u8 = undefined;
774 const key = try h.rsa_secret.encrypted(&key_buf, h.cert.pub_key_algo, h.cert.pub_key);
775 try hw.handshakeRecordHeader(.client_key_exchange, 2 + key.len);
776 try hw.int(u16, key.len);
777 try hw.slice(key);
778 }
779 h.transcript.update(hw.buffered());
780 try w.record(.handshake, hw.buffered());
781 }
782
783 // Client certificate verify
784 if (cert_builder) |cb| {
785 var hw = try w.writerAdvance(record.header_len);
786 try cb.makeCertificateVerify(&hw);
787 h.transcript.update(hw.buffered());
788 try w.record(.handshake, hw.buffered());
789 }
790
791 // Client change cipher spec
792 try w.record(.change_cipher_spec, &[_]u8{1});
793
794 { // Client handshake finished
795 const client_finished = &record.handshakeHeader(.finished, 12) ++
796 h.transcript.clientFinishedTls12(&h.master_secret);
797 h.transcript.update(client_finished);
798 try h.writeEncrypted(&w, client_finished);
799 }
800
801 h.output.advance(w.buffered().len);
802 }
803
804 /// Create client change cipher spec and handshake finished messages for
805 /// tls 1.3.
806 /// If the client certificate is requested by the server and client is
807 /// configured with certificates and private key then client certificate
808 /// and client certificate verify messages are also created. If the
809 /// server has requested certificate but the client is not configured
810 /// empty certificate message is sent, as is required by rfc.
811 fn makeClientFlight2Tls13(h: *Self, auth: ?*CertKeyPair) !void {
812 var w: record.Writer = .initFromIo(h.output);
813
814 // Client change cipher spec
815 try w.record(.change_cipher_spec, &[_]u8{1});
816
817 if (h.client_certificate_requested) {
818 if (auth) |a| {
819 const cb = h.certificateBuilder(a);
820 { // Certificate
821 var hw = try w.writerAdvance(record.header_len);
822 try cb.makeCertificate(&hw);
823 h.transcript.update(hw.buffered());
824 try h.writeEncrypted(&w, hw.buffered());
825 }
826 { // Client certificate
827 var hw = try w.writerAdvance(record.header_len);
828 try cb.makeCertificateVerify(&hw);
829 h.transcript.update(hw.buffered());
830 try h.writeEncrypted(&w, hw.buffered());
831 }
832 } else {
833 // Empty certificate message and no certificate verify message
834 var hw = try w.writerAdvance(record.header_len);
835 try hw.handshakeRecord(.certificate, &[_]u8{ 0, 0, 0, 0 });
836 h.transcript.update(hw.buffered());
837 try h.writeEncrypted(&w, hw.buffered());
838 }
839 }
840
841 { // Finished
842 var hw = try w.writerAdvance(record.header_len);
843 try hw.handshakeRecord(.finished, h.transcript.clientFinishedTls13());
844 h.transcript.update(hw.buffered());
845 try h.writeEncrypted(&w, hw.buffered());
846 }
847
848 h.output.advance(w.buffered().len);
849 }
850
851 fn certificateBuilder(h: *Self, auth: *CertKeyPair) CertificateBuilder {
852 return .{
853 .cert_key_pair = auth,
854 .transcript = &h.transcript,
855 .tls_version = h.tls_version,
856 .side = .client,
857 };
858 }
859
860 fn readServerFlight2(h: *Self) !void {
861 // Read server change cipher spec message.
862 {
863 var d = try Record.decoder(h.input);
864 try d.expectContentType(.change_cipher_spec);
865 h.max_server_record_len = @max(h.max_server_record_len, d.payload.len + record.header_len);
866 }
867 // Read encrypted server handshake finished message. Verify that
868 // content of the server finished message is based on transcript
869 // hash and master secret.
870 {
871 const rec = try Record.read(h.input);
872 h.max_server_record_len = @max(h.max_server_record_len, rec.buffer.len);
873 const content_type, const server_finished = try h.cipher.decrypt(@constCast(rec.buffer), rec);
874 if (content_type != .handshake)
875 return error.TlsUnexpectedMessage;
876 const expected = record.handshakeHeader(.finished, 12) ++ h.transcript.serverFinishedTls12(&h.master_secret);
877 if (!mem.eql(u8, server_finished, &expected))
878 return error.TlsBadRecordMac;
879 }
880 }
881
882 /// Write encrypted handshake message into `w`
883 fn writeEncrypted(h: *Self, w: *record.Writer, cleartext: []const u8) !void {
884 const ciphertext = try h.cipher.encrypt(w.unused(), .handshake, cleartext);
885 w.advance(ciphertext.len);
886 }
887
888 // Copy handshake parameters to opt.diagnostic
889 fn updateDiagnostic(h: *Self, opt: Options, is_session_resumption: bool) void {
890 if (opt.diagnostic) |d| {
891 d.tls_version = h.tls_version;
892 d.cipher_suite_tag = h.cipher_suite;
893 d.named_group = h.named_group orelse @as(proto.NamedGroup, @fromBackingInt(@intCast(0x0000)));
894 d.signature_scheme = h.cert.signature_scheme;
895 d.is_session_resumption = is_session_resumption;
896 d.max_server_record_len = h.max_server_record_len;
897 d.max_server_cleartext_len = h.max_server_cleartext_len;
898 d.max_client_record_len = h.max_client_record_len;
899 if (opt.auth) |a|
900 d.client_signature_scheme = a.key.signature_scheme;
901 }
902 }
903};
904
905const RsaSecret = struct {
906 secret: [48]u8,
907
908 fn init(rand: [46]u8) RsaSecret {
909 return .{ .secret = [_]u8{ 0x03, 0x03 } ++ rand };
910 }
911
912 // Pre master secret encrypted with certificate public key.
913 fn encrypted(
914 self: RsaSecret,
915 out: *[512]u8,
916 cert_pub_key_algo: Certificate.Parsed.PubKeyAlgo,
917 cert_pub_key: []const u8,
918 ) ![]const u8 {
919 if (cert_pub_key_algo != .rsaEncryption) return error.TlsBadSignatureScheme;
920 const pk = try rsa.PublicKey.fromDer(cert_pub_key);
921 return try pk.encryptPkcsv1_5(&self.secret, out);
922 }
923};
924
925const testing = std.testing;
926const data12 = ;
927const data13 = ;
928const testu = ;
929
930test "parse tls 1.2 server hello" {
931 var buffer: [1024]u8 = undefined;
932 var writer: Io.Writer = .fixed(&buffer);
933 var reader: Io.Reader = .fixed(&data12.server_hello_responses);
934 var h: Handshake = .{
935 .output = &writer,
936 .input = &reader,
937 .client_random = data12.client_random,
938 };
939 // Set to known instead of random
940 h.dh_kp.x25519_kp.secret_key = data12.client_secret;
941
942 // Parse server hello, certificate and key exchange messages.
943 // Read cipher suite, named group, signature scheme, server random certificate public key
944 // Verify host name, signature
945 // Calculate key material
946 h.cert = .{ .host = "example.ulfheim.net", .skip_verify = true, .root_ca = .empty };
947 try h.readServerFlight1();
948 try testing.expectEqual(.ECDHE_RSA_WITH_AES_128_CBC_SHA, h.cipher_suite);
949 try testing.expectEqual(.x25519, h.named_group.?);
950 try testing.expectEqual(.rsa_pkcs1_sha256, h.cert.signature_scheme);
951 try testing.expectEqualSlices(u8, &data12.server_random, &h.server_random);
952 try testing.expectEqualSlices(u8, &data12.server_pub_key, h.server_pub_key);
953 try testing.expectEqualSlices(u8, &data12.signature, h.cert.signature);
954 try testing.expectEqualSlices(u8, &data12.cert_pub_key, h.cert.pub_key);
955
956 try h.verifyCertificateSignatureTls12();
957 try h.generateKeyMaterial(null);
958
959 try testing.expectEqualSlices(u8, &data12.key_material, h.key_material[0..data12.key_material.len]);
960}
961
962test "verify google.com certificate" {
963 var buffer: [1024]u8 = undefined;
964 var writer: Io.Writer = .fixed(&buffer);
965 var reader: Io.Reader = .fixed(@embedFile("testdata/google.com/server_hello"));
966 var h: Handshake = .{
967 .output = &writer,
968 .input = &reader,
969 .client_random = @embedFile("testdata/google.com/client_random").*,
970 };
971
972 var ca_bundle: Certificate.Bundle = .empty;
973 try ca_bundle.rescan(testing.allocator);
974 defer ca_bundle.deinit(testing.allocator);
975
976 h.cert = .{ .host = "google.com", .skip_verify = true, .root_ca = .empty, .now_sec = 1714846451 };
977 try h.readServerFlight1();
978 try h.verifyCertificateSignatureTls12();
979}
980
981test "parse tls 1.3 server hello" {
982 var reader: Io.Reader = .fixed(&data13.server_hello);
983 var d = try Record.decoder(&reader);
984
985 const handshake_type = try d.decode(proto.Handshake);
986 const length = try d.decode(u24);
987 try testing.expectEqual(0x000076, length);
988 try testing.expectEqual(.server_hello, handshake_type);
989
990 var h: Handshake = undefined;
991 try h.parseServerHello(&d, length);
992
993 try testing.expectEqual(.AES_256_GCM_SHA384, h.cipher_suite);
994 try testing.expectEqualSlices(u8, &data13.server_random, &h.server_random);
995 try testing.expectEqual(.tls_1_3, h.tls_version);
996 try testing.expectEqual(.x25519, h.named_group);
997 try testing.expectEqualSlices(u8, &data13.server_pub_key, h.server_pub_key);
998}
999
1000test "init tls 1.3 handshake cipher" {
1001 const cipher_suite_tag: CipherSuite = .AES_256_GCM_SHA384;
1002
1003 var transcript = Transcript{};
1004 transcript.use(cipher_suite_tag.hash());
1005 transcript.update(data13.client_hello[record.header_len..]);
1006 transcript.update(data13.server_hello[record.header_len..]);
1007
1008 var dh_kp = DhKeyPair{
1009 .x25519_kp = .{
1010 .public_key = data13.client_public_key,
1011 .secret_key = data13.client_private_key,
1012 },
1013 };
1014 const shared_key = try dh_kp.sharedKey(.x25519, &data13.server_pub_key);
1015 try testing.expectEqualSlices(u8, &data13.shared_key, shared_key);
1016
1017 const cipher = try Cipher.initTls13(cipher_suite_tag, transcript.handshakeSecret(shared_key), .client);
1018
1019 const c = &cipher.AES_256_GCM_SHA384;
1020 try testing.expectEqualSlices(u8, &data13.server_handshake_key, &c.decrypt_key);
1021 try testing.expectEqualSlices(u8, &data13.client_handshake_key, &c.encrypt_key);
1022 try testing.expectEqualSlices(u8, &data13.server_handshake_iv, &c.decrypt_iv);
1023 try testing.expectEqualSlices(u8, &data13.client_handshake_iv, &c.encrypt_iv);
1024}
1025
1026fn initExampleHandshake(h: *Handshake) !void {
1027 h.cipher_suite = .AES_256_GCM_SHA384;
1028 h.transcript.use(h.cipher_suite.hash());
1029 h.transcript.update(data13.client_hello[record.header_len..]);
1030 h.transcript.update(data13.server_hello[record.header_len..]);
1031 h.cipher = try Cipher.initTls13(h.cipher_suite, h.transcript.handshakeSecret(&data13.shared_key), .client);
1032 h.tls_version = .tls_1_3;
1033 h.cert.now_sec = 1714846451;
1034 h.server_pub_key = &data13.server_pub_key;
1035}
1036
1037test "tls 1.3 decrypt wrapped record" {
1038 var cipher = brk: {
1039 var h: Handshake = .{ .input = undefined, .output = undefined };
1040 try initExampleHandshake(&h);
1041 break :brk h.cipher;
1042 };
1043
1044 var cleartext_buf: [1024]u8 = undefined;
1045 {
1046 const rec = record.Record.init(&data13.server_encrypted_extensions_wrapped);
1047
1048 const content_type, const cleartext = try cipher.decrypt(&cleartext_buf, rec);
1049 try testing.expectEqual(.handshake, content_type);
1050 try testing.expectEqualSlices(u8, &data13.server_encrypted_extensions, cleartext);
1051 }
1052 {
1053 const rec = record.Record.init(&data13.server_certificate_wrapped);
1054
1055 const content_type, const cleartext = try cipher.decrypt(&cleartext_buf, rec);
1056 try testing.expectEqual(.handshake, content_type);
1057 try testing.expectEqualSlices(u8, &data13.server_certificate, cleartext);
1058 }
1059}
1060
1061test "tls 1.3 process server flight" {
1062 var h: Handshake = brk: {
1063 var buffer: [1024]u8 = undefined;
1064 var writer: Io.Writer = .fixed(&buffer);
1065 var reader: Io.Reader = .fixed(&data13.server_flight);
1066 break :brk .{ .input = &reader, .output = &writer };
1067 };
1068 try initExampleHandshake(&h);
1069
1070 h.cert = .{ .host = "example.ulfheim.net", .skip_verify = true, .root_ca = .empty };
1071 try h.readEncryptedServerFlight1(false);
1072
1073 { // application cipher keys calculation
1074 try testing.expectEqualSlices(u8, &data13.handshake_hash, &h.transcript.sha384.hash.peek());
1075
1076 var cipher = try Cipher.initTls13(h.cipher_suite, h.transcript.applicationSecret(), .client);
1077 const c = &cipher.AES_256_GCM_SHA384;
1078 try testing.expectEqualSlices(u8, &data13.server_application_key, &c.decrypt_key);
1079 try testing.expectEqualSlices(u8, &data13.client_application_key, &c.encrypt_key);
1080 try testing.expectEqualSlices(u8, &data13.server_application_iv, &c.decrypt_iv);
1081 try testing.expectEqualSlices(u8, &data13.client_application_iv, &c.encrypt_iv);
1082
1083 var buffer: [128]u8 = undefined;
1084 const encrypted = try cipher.encrypt(&buffer, .application_data, "ping");
1085 try testing.expectEqualSlices(u8, &data13.client_ping_wrapped, encrypted);
1086 }
1087 { // client finished message
1088 const verify_data = h.transcript.clientFinishedTls13();
1089 try testing.expectEqualSlices(u8, &data13.client_finished_verify_data, verify_data);
1090
1091 var buffer: [128]u8 = undefined;
1092 var w: Io.Writer = .fixed(&buffer);
1093 h.output = &w;
1094 try h.makeClientFlight2Tls13(null);
1095 try testing.expectEqualSlices(u8, &testu.hexToBytes("140303000101"), w.buffered()[0..6]);
1096 try testing.expectEqualSlices(u8, &data13.client_finished_wrapped, w.buffered()[6..]);
1097 }
1098}
1099
1100test "create client hello" {
1101 const expected = testu.hexToBytes(
1102 "16 03 03 00 6d " ++ // record header
1103 "01 00 00 69 " ++ // handshake header
1104 "03 03 " ++ // protocol version
1105 "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f " ++ // client random
1106 "00 " ++ // no session id
1107 "00 02 c0 2b " ++ // cipher suites
1108 "01 00 " ++ // compression methods
1109 "00 3e " ++ // extensions length
1110 "00 2b 00 03 02 03 03 " ++ // supported versions extension
1111 "00 0d 00 14 00 12 04 03 05 03 08 04 08 05 08 06 08 07 02 01 04 01 05 01 " ++ // signature algorithms extension
1112 "00 0a 00 08 00 06 00 1d 00 17 00 18 " ++ // named groups extension
1113 "00 00 00 0f 00 0d 00 00 0a 67 6f 6f 67 6c 65 2e 63 6f 6d ", // server name extension
1114 );
1115
1116 var h = brk: {
1117 var buffer: [256 + 10]u8 = undefined; // reserved + host name len
1118 var stream_writer: Io.Writer = .fixed(&buffer);
1119 break :brk Handshake{
1120 .output = &stream_writer,
1121 .input = undefined,
1122 .client_random = testu.hexToBytes(
1123 \\ 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
1124 ),
1125 };
1126 };
1127
1128 try h.makeClientHello(.{
1129 .host = "google.com",
1130 .root_ca = .empty,
1131 .cipher_suites = &[_]CipherSuite{CipherSuite.ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
1132 .named_groups = &[_]proto.NamedGroup{ .x25519, .secp256r1, .secp384r1 },
1133 }, null);
1134
1135 const actual = h.output.buffered();
1136 try testing.expectEqualSlices(u8, &expected, actual);
1137 try testing.expectEqual(152, h.output.unusedCapacityLen());
1138 try testing.expectEqual(114, expected.len);
1139}
1140
1141test "client hello size" {
1142 const opt: Options = .{
1143 .host = "some-host-name.net",
1144 .root_ca = .empty,
1145 .cipher_suites = cipher_suites.all,
1146 //.named_groups = supported_named_groups,
1147 };
1148
1149 var buffer: [max_cleartext_len]u8 = undefined;
1150 var stream_writer: Io.Writer = .fixed(&buffer);
1151 var h = Handshake{
1152 .output = &stream_writer,
1153 .input = undefined,
1154 };
1155 try h.initKeys(opt);
1156 try h.makeClientHello(opt, null);
1157 try testing.expectEqual(1572 + opt.host.len, h.output.end);
1158 //try testing.expectEqual(2794 + opt.host.len, h.stream_writer.end);
1159}
1160
1161test "handshake verify server finished message" {
1162 var h: Handshake = brk: {
1163 var buffer: [1024]u8 = undefined;
1164 var writer: Io.Writer = .fixed(&buffer);
1165 var reader: Io.Reader = .fixed(&data12.server_handshake_finished_msgs);
1166 break :brk .{ .input = &reader, .output = &writer };
1167 };
1168
1169 h.cipher_suite = .ECDHE_ECDSA_WITH_AES_128_CBC_SHA;
1170 h.master_secret = data12.master_secret;
1171
1172 // add handshake messages to the transcript
1173 for (data12.handshake_messages) |msg| {
1174 h.transcript.update(msg[record.header_len..]);
1175 }
1176
1177 // expect verify data
1178 const client_finished = h.transcript.clientFinishedTls12(&h.master_secret);
1179 try testing.expectEqualSlices(u8, &data12.client_finished, &record.handshakeHeader(.finished, 12) ++ client_finished);
1180
1181 // init client with prepared key_material
1182 h.cipher = try Cipher.initTls12(.ECDHE_RSA_WITH_AES_128_CBC_SHA, &data12.key_material, .client);
1183
1184 // check that server verify data matches calculates from hashes of all handshake messages
1185 h.transcript.update(&data12.client_finished);
1186 try h.readServerFlight2();
1187}
1188
1189pub const NonBlock = struct {
1190 const Self = @This();
1191
1192 // inner sync handshake
1193 inner: Handshake = undefined,
1194 opt: Options = undefined,
1195 state: State = .none,
1196
1197 const State = enum {
1198 none,
1199 init,
1200 client_flight_1,
1201 server_flight_1,
1202 client_flight_2,
1203 server_flight_2,
1204
1205 fn next(self: *State) void {
1206 self.* = @fromBackingInt(@intCast(@backingInt(self.*) + 1));
1207 }
1208 };
1209
1210 pub fn init(opt: Options) Self {
1211 var inner = Handshake{
1212 .input = undefined,
1213 .output = undefined,
1214 };
1215 inner.initKeys(opt) catch unreachable;
1216 return .{
1217 .inner = inner,
1218 .opt = opt,
1219 .state = .init,
1220 };
1221 }
1222
1223 fn send(self: *Self) !void {
1224 switch (self.state) {
1225 .init => {
1226 try self.inner.clientFlight1(self.opt);
1227 self.state.next();
1228 },
1229 .server_flight_1 => {
1230 try self.inner.clientFlight2(self.opt);
1231 self.state.next();
1232 if (self.done()) self.inner.updateDiagnostic(self.opt, false);
1233 },
1234 else => return,
1235 }
1236 }
1237
1238 fn recv(self: *Self) !void {
1239 const prev: Transcript = self.inner.transcript;
1240 errdefer self.inner.transcript = prev;
1241
1242 switch (self.state) {
1243 .client_flight_1 => {
1244 try self.inner.serverFlight1(self.opt);
1245 self.state.next();
1246 },
1247 .client_flight_2 => {
1248 try self.inner.serverFlight2(self.opt);
1249 self.state.next();
1250 if (self.done()) self.inner.updateDiagnostic(self.opt, false);
1251 },
1252 else => return,
1253 }
1254 }
1255
1256 /// True when handshake is successfully finished
1257 pub fn done(self: Self) bool {
1258 return self.state == .server_flight_2 or
1259 (self.inner.tls_version == .tls_1_3 and self.state == .client_flight_2);
1260 }
1261
1262 /// Runs next handshake step.
1263 pub fn run(
1264 self: *Self,
1265 /// Data received from the peer
1266 recv_buf: []const u8,
1267 /// Scratch buffer where data to be sent to the peer will be prepared
1268 send_buf: []u8,
1269 ) !struct {
1270 /// Number of bytes consumed from recv_buf
1271 recv_pos: usize,
1272 /// Number of bytes prepared in send_buf
1273 send_pos: usize,
1274 /// Unused part of the recv_buf,
1275 unused_recv: []const u8,
1276 /// Part of the send_buf that should be sent to the peer
1277 send: []const u8,
1278 } {
1279 if (self.done()) return .{
1280 .recv_pos = 0,
1281 .send_pos = 0,
1282 .unused_recv = recv_buf,
1283 .send = &.{},
1284 };
1285
1286 var input: Io.Reader = .fixed(recv_buf);
1287 var output: Io.Writer = .fixed(send_buf);
1288 self.inner.input = &input;
1289 self.inner.output = &output;
1290
1291 const recv_pos: usize = if (recv_buf.len > 0) brk: {
1292 self.recv() catch |err| switch (err) {
1293 error.EndOfStream, error.InputBufferUndersize => break :brk 0,
1294 else => return err,
1295 };
1296 break :brk input.seek;
1297 } else 0;
1298
1299 try self.send();
1300
1301 return .{
1302 .recv_pos = recv_pos,
1303 .send_pos = output.end,
1304 .unused_recv = recv_buf[recv_pos..],
1305 .send = output.buffered(),
1306 };
1307 }
1308
1309 /// Cipher produced in handshake, null until successful handshake.
1310 pub fn cipher(self: Self) ?Cipher {
1311 return if (self.done()) self.inner.cipher else null;
1312 }
1313};
1314
1315test "nonblock handshake" {
1316 var buffer: [1024]u8 = undefined;
1317 var h = NonBlock.init(.{
1318 .host = "example.ulfheim.net",
1319 .insecure_skip_verify = true,
1320 .root_ca = .empty,
1321 .cipher_suites = &[_]CipherSuite{CipherSuite.AES_256_GCM_SHA384},
1322 .named_groups = &[_]proto.NamedGroup{.x25519},
1323 });
1324 { // update secrets to well known from example
1325 h.inner.client_random = data13.client_random;
1326 h.inner.dh_kp.x25519_kp = .{
1327 .public_key = data13.client_public_key,
1328 .secret_key = data13.client_private_key,
1329 };
1330 }
1331
1332 const expected_client_flight_1 = testu.hexToBytes(
1333 \\ 16 03 03 00 9c
1334 \\ 01 00 00 98
1335 \\ 03 03
1336 \\ 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
1337 \\ 00
1338 \\ 00 02 13 02
1339 \\ 01 00
1340 \\ 00 6d
1341 \\ 00 2b 00 03 02 03 04
1342 \\ 00 0d 00 14 00 12 04 03 05 03 08 04 08 05 08 06 08 07 02 01 04 01 05 01
1343 \\ 00 0a 00 04 00 02 00 1d 00 33 00 26 00 24 00 1d 00 20
1344 \\ 35 80 72 d6 36 58 80 d1 ae ea 32 9a df 91 21 38 38 51 ed 21 a2 8e 3b 75 e9 65 d0 d2 cd 16 62 54
1345 \\ 00 00 00 18 00 16 00 00 13 65 78 61 6d 70 6c 65 2e 75 6c 66 68 65 69 6d 2e 6e 65 74
1346 );
1347
1348 var res = try h.run(&.{}, &buffer);
1349 try testing.expectEqualSlices(u8, &expected_client_flight_1, res.send);
1350
1351 { // update transcript to well known from example
1352 h.inner.transcript = .{};
1353 h.inner.transcript.update(data13.client_hello[record.header_len..]);
1354 }
1355
1356 // Parsing partial server flight consumes nothing
1357 var i: usize = 1;
1358 while (i < data13.server_flight_1.len) : (i += 16) {
1359 //for (1..data13.server_flight_1.len - 1) |i| {
1360 res = try h.run(data13.server_flight_1[0..i], &.{});
1361 try testing.expectEqual(0, res.recv_pos);
1362 try testing.expectEqual(i, res.unused_recv.len);
1363 }
1364
1365 res = try h.run(&(data13.server_flight_1 ++ "dummy footer".*), &buffer);
1366 {
1367 try testing.expectEqual(data13.server_flight_1.len, res.recv_pos); // footer is not touched
1368 try testing.expectEqual(.tls_1_3, h.inner.tls_version);
1369 try testing.expectEqual(.x25519, h.inner.named_group);
1370 try testing.expectEqualSlices(u8, &data13.server_random, &h.inner.server_random);
1371 try testing.expectEqual(.AES_256_GCM_SHA384, h.inner.cipher_suite);
1372 try testing.expectEqualSlices(u8, &data13.server_pub_key, h.inner.server_pub_key);
1373 try testing.expect(h.done());
1374 try testing.expectEqualSlices(u8, &data13.client_flight_2, res.send);
1375 }
1376
1377 // When done pushing more data is noop
1378 res = try h.run(&("dummy footer".*), &buffer);
1379 try testing.expectEqual(0, res.recv_pos);
1380 try testing.expectEqual(0, res.send_pos);
1381 try testing.expect(h.done());
1382}
1383
1384test "note about sizes" {
1385 // values valid only for 64 bit platform, so skip in ci
1386 if (true) return error.SkipZigTest;
1387
1388 try testing.expectEqual(18928, @sizeOf(Handshake));
1389 try testing.expectEqual(19072, @sizeOf(NonBlock));
1390 try testing.expectEqual(14384, @sizeOf(DhKeyPair));
1391 try testing.expectEqual(136, @sizeOf(Options));
1392 try testing.expectEqual(2792, @sizeOf(CertKeyPair));
1393 try testing.expectEqual(1736, @sizeOf(CertificateParser));
1394 try testing.expectEqual(48, @sizeOf(cert.Bundle));
1395 try testing.expectEqual(208, @sizeOf(Cipher));
1396}
packages/zig/vendor/zig-tls/src/handshake_common.zigdeleted+0-594
Changes to packages/zig/vendor/zig-tls/src/handshake_common.zig
@@ -1,594 +0,0 @@
1const std = ;
2const assert = std.debug.assert;
3const mem = std.mem;
4const crypto = std.crypto;
5const Certificate = crypto.Certificate;
6const Io = std.Io;
7
8const Transcript = .Transcript;
9const PrivateKey = ;
10const record = ;
11const rsa = ;
12const proto = ;
13
14const X25519 = crypto.dh.X25519;
15const EcdsaP256Sha256 = crypto.sign.ecdsa.EcdsaP256Sha256;
16const EcdsaP384Sha384 = crypto.sign.ecdsa.EcdsaP384Sha384;
17const MLKem768 = crypto.kem.ml_kem.MLKem768;
18
19pub const supported_signature_algorithms = &[_]proto.SignatureScheme{
20 .ecdsa_secp256r1_sha256,
21 .ecdsa_secp384r1_sha384,
22 .rsa_pss_rsae_sha256,
23 .rsa_pss_rsae_sha384,
24 .rsa_pss_rsae_sha512,
25 .ed25519,
26 .rsa_pkcs1_sha1,
27 .rsa_pkcs1_sha256,
28 .rsa_pkcs1_sha384,
29};
30
31pub const CertKeyPair = struct {
32 /// A chain of one or more certificates, leaf first.
33 ///
34 /// Each X.509 certificate contains the public key of a key pair, extra
35 /// information (the name of the holder, the name of an issuer of the
36 /// certificate, validity time spans) and a signature generated using the
37 /// private key of the issuer of the certificate.
38 ///
39 /// All certificates from the bundle are sent to the other side when creating
40 /// Certificate tls message.
41 ///
42 /// Leaf certificate and private key are used to create signature for
43 /// CertifyVerify tls message.
44 bundle: Certificate.Bundle,
45
46 /// Private key corresponding to the public key in leaf certificate from the
47 /// bundle.
48 key: PrivateKey,
49
50 /// Ecdsa key pair derived from key. Computed on init and cached because it
51 /// is costly operation. Important for server which is creating many
52 /// signatures with the same key to not repeat that operation.
53 ecdsa_key_pair: ?EcdsaKeyPair = null,
54
55 pub fn fromFilePath(
56 allocator: mem.Allocator,
57 io: Io,
58 dir: std.Io.Dir,
59 cert_path: []const u8,
60 key_path: []const u8,
61 ) !CertKeyPair {
62 var bundle: cert.Bundle = .empty;
63 try bundle.addCertsFromFilePath(allocator, dir, cert_path);
64
65 const key_file = try dir.openFile(io, key_path, .{});
66 defer key_file.close(io);
67 const key = try PrivateKey.fromFile(allocator, key_file);
68
69 return .{ .bundle = bundle, .key = key, .ecdsa_key_pair = try EcdsaKeyPair.init(key) };
70 }
71
72 pub fn fromFilePathAbsolute(
73 allocator: mem.Allocator,
74 io: Io,
75 cert_path: []const u8,
76 key_path: []const u8,
77 ) !CertKeyPair {
78 var bundle: cert.Bundle = .empty;
79 const now = Io.Clock.real.now(io);
80 try bundle.addCertsFromFilePathAbsolute(allocator, io, now, cert_path);
81
82 const key_file = try std.Io.Dir.openFileAbsolute(io, key_path, .{});
83 defer key_file.close(io);
84 const key = try PrivateKey.fromFile(allocator, key_file);
85
86 return .{ .bundle = bundle, .key = key, .ecdsa_key_pair = try EcdsaKeyPair.init(key) };
87 }
88
89 /// Reads certs directly from PEM files (delegates to fromFilePathAbsolute)
90 pub fn fromFilePathAbsoluteSync(
91 allocator: mem.Allocator,
92 cert_path: []const u8,
93 key_path: []const u8,
94 ) !CertKeyPair {
95 // This function is called from contexts that have io_compat initialized.
96 // We use the same approach - read cert/key via Io.
97 // Callers should use fromFilePathAbsolute directly when they have Io.
98
99 // Read key file using posix fd operations (no Io needed)
100 // For cert, manually parse PEM
101 var cert_buf: [32768]u8 = undefined;
102 var cert_len: usize = 0;
103
104 // Use posix read since we may not have Io
105 const cert_path_z = @as([*:0]const u8, @ptrCast(cert_path.ptr));
106 const cert_fd = std.c.open(cert_path_z, .{}, @as(std.c.mode_t, 0));
107 if (cert_fd < 0) return error.FileNotFound;
108 defer _ = std.c.close(@intCast(cert_fd));
109
110 while (cert_len < cert_buf.len) {
111 const n = std.posix.read(@intCast(cert_fd), cert_buf[cert_len..]) catch break;
112 if (n == 0) break;
113 cert_len += n;
114 }
115
116 // Parse PEM certificates manually
117 var bundle: cert.Bundle = .empty;
118 const pem_data = cert_buf[0..cert_len];
119
120 // Find and decode PEM blocks - add DER bytes directly to bundle
121 const base64_decoder = std.base64.standard.decoderWithIgnore(" \t\r\n");
122 var start: usize = 0;
123 while (std.mem.indexOfPos(u8, pem_data, start, "-----BEGIN CERTIFICATE-----")) |begin| {
124 const content_start = begin + 27; // Length of "-----BEGIN CERTIFICATE-----"
125 if (std.mem.indexOfPos(u8, pem_data, content_start, "-----END CERTIFICATE-----")) |end| {
126 const base64_data = pem_data[content_start..end];
127 const decoded_start: u32 = @intCast(bundle.bytes.items.len);
128 const dest_buf = bundle.bytes.addManyAsSlice(allocator, base64_decoder.calcSizeUpperBound(base64_data.len)) catch {
129 start = end + 25;
130 continue;
131 };
132 const decoded_len = base64_decoder.decode(dest_buf, base64_data) catch {
133 bundle.bytes.items.len = decoded_start;
134 start = end + 25;
135 continue;
136 };
137 bundle.bytes.items.len = decoded_start + decoded_len;
138 // Parse and add to map
139 bundle.parseCert(allocator, decoded_start, 0) catch {
140 bundle.bytes.items.len = decoded_start;
141 };
142 start = end + 25;
143 } else break;
144 }
145
146 // Read key file using posix fd
147 const key_path_z = @as([*:0]const u8, @ptrCast(key_path.ptr));
148 const key_fd = std.c.open(key_path_z, .{}, @as(std.c.mode_t, 0));
149 if (key_fd < 0) return error.FileNotFound;
150 defer _ = std.c.close(@intCast(key_fd));
151
152 var key_buf: [8192]u8 = undefined;
153 var key_len: usize = 0;
154 while (key_len < key_buf.len) {
155 const n = std.posix.read(@intCast(key_fd), key_buf[key_len..]) catch break;
156 if (n == 0) break;
157 key_len += n;
158 }
159 const key = try PrivateKey.parsePem(key_buf[0..key_len]);
160
161 return .{ .bundle = bundle, .key = key, .ecdsa_key_pair = try EcdsaKeyPair.init(key) };
162 }
163
164 pub fn deinit(c: *CertKeyPair, allocator: mem.Allocator) void {
165 c.bundle.deinit(allocator);
166 }
167
168 const EcdsaKeyPair = union(enum) {
169 ecdsa_secp256r1_sha256: EcdsaP256Sha256.KeyPair,
170 ecdsa_secp384r1_sha384: EcdsaP384Sha384.KeyPair,
171
172 fn init(pk: PrivateKey) !?EcdsaKeyPair {
173 switch (pk.signature_scheme) {
174 inline .ecdsa_secp256r1_sha256,
175 .ecdsa_secp384r1_sha384,
176 => |comptime_scheme| {
177 const Ecdsa = SchemeEcdsa(comptime_scheme);
178 const key = pk.key.ecdsa;
179 const key_len = Ecdsa.SecretKey.encoded_length;
180 if (key.len < key_len) return error.InvalidEncoding;
181 const secret_key = try Ecdsa.SecretKey.fromBytes(key[0..key_len].*);
182 const key_pair = try Ecdsa.KeyPair.fromSecretKey(secret_key);
183 return switch (comptime_scheme) {
184 .ecdsa_secp256r1_sha256 => .{ .ecdsa_secp256r1_sha256 = key_pair },
185 .ecdsa_secp384r1_sha384 => .{ .ecdsa_secp384r1_sha384 = key_pair },
186 else => unreachable,
187 };
188 },
189 else => return null,
190 }
191 }
192 };
193};
194
195pub const cert = struct {
196 // A chain of one or more certificates.
197 //
198 // They are used to verify that certificate chain sent by the other side
199 // forms valid trust chain.
200 pub const Bundle = crypto.Certificate.Bundle;
201
202 pub fn fromFilePath(allocator: mem.Allocator, dir: std.fs.Dir, path: []const u8) !Bundle {
203 var bundle: Bundle = .empty;
204 try bundle.addCertsFromFilePath(allocator, dir, path);
205 return bundle;
206 }
207
208 pub fn fromFilePathAbsolute(allocator: mem.Allocator, path: []const u8) !Bundle {
209 var bundle: Bundle = .empty;
210 try bundle.addCertsFromFilePathAbsolute(allocator, path);
211 return bundle;
212 }
213
214 pub fn fromSystem(allocator: mem.Allocator) !Bundle {
215 var bundle: Bundle = .empty;
216 try bundle.rescan(allocator);
217 return bundle;
218 }
219};
220
221pub const CertificateBuilder = struct {
222 cert_key_pair: *CertKeyPair,
223 transcript: *Transcript,
224 tls_version: proto.Version = .tls_1_3,
225 side: proto.Side = .client,
226
227 pub fn makeCertificate(h: CertificateBuilder, w: *record.Writer) !void {
228 const certs = h.cert_key_pair.bundle.bytes.items;
229 const certs_count = h.cert_key_pair.bundle.map.size;
230
231 // Differences between tls 1.3 and 1.2
232 // TLS 1.3 has request context in header and extensions for each certificate.
233 // Here we use empty length for each field.
234 // TLS 1.2 don't have these two fields.
235 const request_context, const extensions = if (h.tls_version == .tls_1_3)
236 .{ &[_]u8{0}, &[_]u8{ 0, 0 } }
237 else
238 .{ &[_]u8{}, &[_]u8{} };
239 const certs_len = certs.len + (3 + extensions.len) * certs_count;
240
241 // Write handshake header
242 try w.handshakeRecordHeader(.certificate, certs_len + request_context.len + 3);
243 try w.slice(request_context);
244 try w.int(u24, certs_len);
245
246 // Write each certificate
247 var index: u32 = 0;
248 while (index < certs.len) {
249 const e = try Certificate.der.Element.parse(certs, index);
250 const crt = certs[index..e.slice.end];
251 try w.int(u24, crt.len); // certificate length
252 try w.slice(crt); // certificate
253 try w.slice(extensions); // certificate extensions
254 index = e.slice.end;
255 }
256 }
257
258 pub fn makeCertificateVerify(h: CertificateBuilder, w: *record.Writer) !void {
259 // Creates signature for client certificate signature message.
260 // Returns signature bytes and signature scheme.
261 const signature, const signature_scheme = switch (h.cert_key_pair.key.signature_scheme) {
262 inline .ecdsa_secp256r1_sha256,
263 .ecdsa_secp384r1_sha384,
264 => |comptime_scheme| brk: {
265 const Ecdsa = SchemeEcdsa(comptime_scheme);
266 const key_pair = switch (comptime_scheme) {
267 .ecdsa_secp256r1_sha256 => h.cert_key_pair.ecdsa_key_pair.?.ecdsa_secp256r1_sha256,
268 .ecdsa_secp384r1_sha384 => h.cert_key_pair.ecdsa_key_pair.?.ecdsa_secp384r1_sha384,
269 else => unreachable,
270 };
271 var signer = try key_pair.signer(null);
272 h.setSignatureVerifyBytes(&signer);
273 const signature = try signer.finalize();
274 var buf: [Ecdsa.Signature.der_encoded_length_max]u8 = undefined;
275 break :brk .{ signature.toDer(&buf), comptime_scheme };
276 },
277 inline .rsa_pss_rsae_sha256,
278 .rsa_pss_rsae_sha384,
279 .rsa_pss_rsae_sha512,
280 => |comptime_scheme| brk: {
281 const Hash = SchemeHash(comptime_scheme);
282 var signer = try h.cert_key_pair.key.key.rsa.signerOaep(Hash, null);
283 h.setSignatureVerifyBytes(&signer);
284 var buf: [512]u8 = undefined;
285 const signature = try signer.finalize(&buf);
286 break :brk .{ signature.bytes, comptime_scheme };
287 },
288 else => return error.TlsUnknownSignatureScheme,
289 };
290
291 try w.handshakeRecordHeader(.certificate_verify, signature.len + 4);
292 try w.enumValue(signature_scheme);
293 try w.int(u16, signature.len);
294 try w.slice(signature);
295 }
296
297 fn setSignatureVerifyBytes(h: CertificateBuilder, signer: anytype) void {
298 if (h.tls_version == .tls_1_2) {
299 // tls 1.2 signature uses current transcript hash value.
300 // ref: https://datatracker.ietf.org/doc/html/rfc5246.html#section-7.4.8
301 const Hash = @TypeOf(signer.h);
302 signer.h = h.transcript.hash(Hash);
303 } else {
304 // tls 1.3 signature is computed over concatenation of 64 spaces,
305 // context, separator and content.
306 // ref: https://datatracker.ietf.org/doc/html/rfc8446#section-4.4.3
307 if (h.side == .server) {
308 signer.update(h.transcript.serverCertificateVerify());
309 } else {
310 signer.update(h.transcript.clientCertificateVerify());
311 }
312 }
313 }
314};
315
316pub fn SchemeEcdsa(comptime scheme: proto.SignatureScheme) type {
317 return switch (scheme) {
318 .ecdsa_secp256r1_sha256 => EcdsaP256Sha256,
319 .ecdsa_secp384r1_sha384 => EcdsaP384Sha384,
320 else => unreachable,
321 };
322}
323
324pub const CertificateParser = struct {
325 pub_key_algo: Certificate.Parsed.PubKeyAlgo = undefined,
326 pub_key_buf: [1038]u8 = undefined,
327 pub_key: []const u8 = undefined,
328
329 signature_scheme: proto.SignatureScheme = @fromBackingInt(@intCast(0)),
330 signature_buf: [1024]u8 = undefined,
331 signature: []const u8 = undefined,
332
333 root_ca: Certificate.Bundle,
334 host: []const u8,
335 skip_verify: bool = false,
336 now_sec: i64 = 0,
337
338 pub fn parseCertificate(h: *CertificateParser, d: *record.Decoder, tls_version: proto.Version) !void {
339 if (h.now_sec == 0) {
340 var ts: std.c.timespec = undefined;
341 if (std.c.clock_gettime(.REALTIME, &ts) == 0) {
342 h.now_sec = ts.sec;
343 }
344 }
345 if (tls_version == .tls_1_3) {
346 const request_context = try d.decode(u8);
347 if (request_context != 0) return error.TlsIllegalParameter;
348 }
349
350 var trust_chain_established = false;
351 var last_cert: ?Certificate.Parsed = null;
352 const certs_len = try d.decode(u24);
353 const start_idx = d.idx;
354 while (d.idx - start_idx < certs_len) {
355 const crt_len = try d.decode(u24);
356 const crt = try d.slice(crt_len);
357 if (tls_version == .tls_1_3) {
358 // certificate extensions present in tls 1.3
359 try d.skip(try d.decode(u16));
360 }
361 if (trust_chain_established)
362 continue;
363
364 const subject = try (Certificate{ .buffer = crt, .index = 0 }).parse();
365 if (last_cert) |pc| {
366 if (pc.verify(subject, h.now_sec)) {
367 last_cert = subject;
368 } else |err| switch (err) {
369 error.CertificateIssuerMismatch => {
370 // skip certificate which is not part of the chain
371 continue;
372 },
373 else => return err,
374 }
375 } else { // first certificate
376 if (!h.skip_verify and h.host.len > 0) {
377 try subject.verifyHostName(h.host);
378 }
379 h.pub_key = try dupe(&h.pub_key_buf, subject.pubKey());
380 h.pub_key_algo = subject.pub_key_algo;
381 last_cert = subject;
382 }
383 if (!h.skip_verify) {
384 if (h.root_ca.verify(last_cert.?, h.now_sec)) |_| {
385 trust_chain_established = true;
386 } else |err| switch (err) {
387 error.CertificateIssuerNotFound => {},
388 else => return err,
389 }
390 }
391 }
392 if (!h.skip_verify and !trust_chain_established) {
393 return error.CertificateIssuerNotFound;
394 }
395 }
396
397 pub fn parseCertificateVerify(h: *CertificateParser, d: *record.Decoder) !void {
398 h.signature_scheme = try d.decode(proto.SignatureScheme);
399 h.signature = try dupe(&h.signature_buf, try d.slice(try d.decode(u16)));
400 }
401
402 pub fn verifySignature(h: *CertificateParser, verify_bytes: []const u8) !void {
403 switch (h.signature_scheme) {
404 inline .ecdsa_secp256r1_sha256,
405 .ecdsa_secp384r1_sha384,
406 => |comptime_scheme| {
407 if (h.pub_key_algo != .X9_62_id_ecPublicKey) return error.TlsBadSignatureScheme;
408 const cert_named_curve = h.pub_key_algo.X9_62_id_ecPublicKey;
409 switch (cert_named_curve) {
410 inline .secp384r1, .X9_62_prime256v1 => |comptime_cert_named_curve| {
411 const Ecdsa = SchemeEcdsaCert(comptime_scheme, comptime_cert_named_curve);
412 const key = try Ecdsa.PublicKey.fromSec1(h.pub_key);
413 const sig = try Ecdsa.Signature.fromDer(h.signature);
414 try sig.verify(verify_bytes, key);
415 },
416 else => return error.TlsUnknownSignatureScheme,
417 }
418 },
419 .ed25519 => {
420 if (h.pub_key_algo != .curveEd25519) return error.TlsBadSignatureScheme;
421 const Eddsa = crypto.sign.Ed25519;
422 if (h.signature.len != Eddsa.Signature.encoded_length) return error.InvalidEncoding;
423 const sig = Eddsa.Signature.fromBytes(h.signature[0..Eddsa.Signature.encoded_length].*);
424 if (h.pub_key.len != Eddsa.PublicKey.encoded_length) return error.InvalidEncoding;
425 const key = try Eddsa.PublicKey.fromBytes(h.pub_key[0..Eddsa.PublicKey.encoded_length].*);
426 try sig.verify(verify_bytes, key);
427 },
428 inline .rsa_pss_rsae_sha256,
429 .rsa_pss_rsae_sha384,
430 .rsa_pss_rsae_sha512,
431 => |comptime_scheme| {
432 if (h.pub_key_algo != .rsaEncryption) return error.TlsBadSignatureScheme;
433 const Hash = SchemeHash(comptime_scheme);
434 const pk = try rsa.PublicKey.fromDer(h.pub_key);
435 const sig = rsa.Pss(Hash).Signature{ .bytes = h.signature };
436 try sig.verify(verify_bytes, pk, null);
437 },
438 inline .rsa_pkcs1_sha1,
439 .rsa_pkcs1_sha256,
440 .rsa_pkcs1_sha384,
441 .rsa_pkcs1_sha512,
442 => |comptime_scheme| {
443 if (h.pub_key_algo != .rsaEncryption) return error.TlsBadSignatureScheme;
444 const Hash = SchemeHash(comptime_scheme);
445 const pk = try rsa.PublicKey.fromDer(h.pub_key);
446 const sig = rsa.PKCS1v1_5(Hash).Signature{ .bytes = h.signature };
447 try sig.verify(verify_bytes, pk);
448 },
449 else => return error.TlsUnknownSignatureScheme,
450 }
451 }
452
453 fn SchemeEcdsaCert(comptime scheme: proto.SignatureScheme, comptime cert_named_curve: Certificate.NamedCurve) type {
454 const Sha256 = crypto.hash.sha2.Sha256;
455 const Sha384 = crypto.hash.sha2.Sha384;
456 const Ecdsa = crypto.sign.ecdsa.Ecdsa;
457
458 return switch (scheme) {
459 .ecdsa_secp256r1_sha256 => Ecdsa(cert_named_curve.Curve(), Sha256),
460 .ecdsa_secp384r1_sha384 => Ecdsa(cert_named_curve.Curve(), Sha384),
461 else => @compileError("bad scheme"),
462 };
463 }
464};
465
466pub fn SchemeHash(comptime scheme: proto.SignatureScheme) type {
467 const Sha256 = crypto.hash.sha2.Sha256;
468 const Sha384 = crypto.hash.sha2.Sha384;
469 const Sha512 = crypto.hash.sha2.Sha512;
470
471 return switch (scheme) {
472 .rsa_pkcs1_sha1 => crypto.hash.Sha1,
473 .rsa_pss_rsae_sha256, .rsa_pkcs1_sha256 => Sha256,
474 .rsa_pss_rsae_sha384, .rsa_pkcs1_sha384 => Sha384,
475 .rsa_pss_rsae_sha512, .rsa_pkcs1_sha512 => Sha512,
476 else => @compileError("bad scheme"),
477 };
478}
479
480pub fn dupe(buf: []u8, data: []const u8) ![]u8 {
481 if (buf.len < data.len) {
482 return error.BufferUndersize;
483 }
484 @memcpy(buf[0..data.len], data);
485 return buf[0..data.len];
486}
487
488pub fn dupeMin(buf: []u8, data: []const u8) []u8 {
489 const n = @min(data.len, buf.len);
490 @memcpy(buf[0..n], data[0..n]);
491 return buf[0..n];
492}
493
494pub const DhKeyPair = struct {
495 x25519_kp: X25519.KeyPair = undefined,
496 secp256r1_kp: EcdsaP256Sha256.KeyPair = undefined,
497 secp384r1_kp: EcdsaP384Sha384.KeyPair = undefined,
498 ml_kem768: MLKem768.KeyPair = undefined,
499
500 secp256r1_pk_buf: [EcdsaP256Sha256.PublicKey.uncompressed_sec1_encoded_length]u8 = undefined, //65 bytes
501 secp384r1_pk_buf: [EcdsaP384Sha384.PublicKey.uncompressed_sec1_encoded_length]u8 = undefined, //97
502 ml_kem768_pk_buf: [MLKem768.PublicKey.encoded_length + X25519.public_length]u8 = undefined, // 1216
503 shared_key_buf: [64]u8 = undefined,
504
505 pub const seed_len = 32 + 32 + 48 + 64 + 64;
506
507 pub fn init(seed: [seed_len]u8, named_groups: []const proto.NamedGroup) !DhKeyPair {
508 var kp: DhKeyPair = .{};
509 for (named_groups) |ng|
510 switch (ng) {
511 .x25519 => kp.x25519_kp = try X25519.KeyPair.generateDeterministic(seed[0..][0..X25519.seed_length].*),
512 .secp256r1 => kp.secp256r1_kp = try EcdsaP256Sha256.KeyPair.generateDeterministic(seed[32..][0..EcdsaP256Sha256.KeyPair.seed_length].*),
513 .secp384r1 => kp.secp384r1_kp = try EcdsaP384Sha384.KeyPair.generateDeterministic(seed[32 + 32 ..][0..EcdsaP384Sha384.KeyPair.seed_length].*),
514 .x25519_ml_kem768 => kp.ml_kem768 = try MLKem768.KeyPair.generateDeterministic(seed[32 + 32 + 48 + 64 ..][0..MLKem768.seed_length].*),
515 else => return error.TlsIllegalParameter,
516 };
517 return kp;
518 }
519
520 // x25519: 32, secp256r1: 32, secp384r1: 48, x25519_ml_kem768: 64
521 pub fn sharedKey(self: *DhKeyPair, named_group: proto.NamedGroup, server_pub_key: []const u8) ![]const u8 {
522 return switch (named_group) {
523 .x25519 => {
524 if (server_pub_key.len != X25519.public_length)
525 return error.TlsIllegalParameter;
526 self.shared_key_buf[0..32].* = try X25519.scalarmult(
527 self.x25519_kp.secret_key,
528 server_pub_key[0..X25519.public_length].*,
529 );
530 return self.shared_key_buf[0..32];
531 },
532 .secp256r1 => {
533 const pk = try EcdsaP256Sha256.PublicKey.fromSec1(server_pub_key);
534 const mul = try pk.p.mulPublic(self.secp256r1_kp.secret_key.bytes, .big);
535 self.shared_key_buf[0..32].* = mul.affineCoordinates().x.toBytes(.big);
536 return self.shared_key_buf[0..32];
537 },
538 .secp384r1 => {
539 const pk = try EcdsaP384Sha384.PublicKey.fromSec1(server_pub_key);
540 const mul = try pk.p.mulPublic(self.secp384r1_kp.secret_key.bytes, .big);
541 self.shared_key_buf[0..48].* = mul.affineCoordinates().x.toBytes(.big);
542 return self.shared_key_buf[0..48];
543 },
544 .x25519_ml_kem768 => {
545 const hksl = crypto.kem.ml_kem.MLKem768.ciphertext_length;
546 const xksl = hksl + crypto.dh.X25519.public_length;
547 if (server_pub_key.len != xksl) return error.TlsIllegalParameter;
548
549 const hsk = self.ml_kem768.secret_key.decaps(server_pub_key[0..hksl]) catch
550 return error.TlsDecryptFailure;
551 const xsk = crypto.dh.X25519.scalarmult(self.x25519_kp.secret_key, server_pub_key[hksl..xksl].*) catch
552 return error.TlsDecryptFailure;
553 self.shared_key_buf = (hsk ++ xsk);
554 return &self.shared_key_buf;
555 },
556 else => return error.TlsIllegalParameter,
557 };
558 }
559
560 // Returns 32, 65, 97 or 1216 bytes ml_kem
561 pub fn publicKey(self: *DhKeyPair, named_group: proto.NamedGroup) ![]const u8 {
562 return switch (named_group) {
563 .x25519 => &self.x25519_kp.public_key,
564 .secp256r1 => {
565 self.secp256r1_pk_buf = self.secp256r1_kp.public_key.toUncompressedSec1();
566 return &self.secp256r1_pk_buf;
567 },
568 .secp384r1 => {
569 self.secp384r1_pk_buf = self.secp384r1_kp.public_key.toUncompressedSec1();
570 return &self.secp384r1_pk_buf;
571 },
572 .x25519_ml_kem768 => {
573 self.ml_kem768_pk_buf = self.ml_kem768.public_key.toBytes() ++ self.x25519_kp.public_key;
574 return &self.ml_kem768_pk_buf;
575 },
576 else => return error.TlsIllegalParameter,
577 };
578 }
579};
580
581const testing = std.testing;
582const testu = ;
583
584test "DhKeyPair.x25519" {
585 var seed: [DhKeyPair.seed_len]u8 = undefined;
586 testu.fill(&seed);
587 const server_pub_key = &testu.hexToBytes("3303486548531f08d91e675caf666c2dc924ac16f47a861a7f4d05919d143637");
588 const expected = &testu.hexToBytes(
589 \\ F1 67 FB 4A 49 B2 91 77 08 29 45 A1 F7 08 5A 21
590 \\ AF FE 9E 78 C2 03 9B 81 92 40 72 73 74 7A 46 1E
591 );
592 var kp = try DhKeyPair.init(seed, &.{.x25519});
593 try testing.expectEqualSlices(u8, expected, try kp.sharedKey(.x25519, server_pub_key));
594}
packages/zig/vendor/zig-tls/src/handshake_server.zigdeleted+0-1069
Changes to packages/zig/vendor/zig-tls/src/handshake_server.zig
@@ -1,1069 +0,0 @@
1const std = ;
2const assert = std.debug.assert;
3const crypto = std.crypto;
4const mem = std.mem;
5const Io = std.Io;
6const Certificate = crypto.Certificate;
7
8const Cipher = .Cipher;
9const CipherSuite = .CipherSuite;
10const cipher_suites = .cipher_suites;
11const max_cleartext_len = .max_cleartext_len;
12
13const Transcript = .Transcript;
14const record = ;
15const Record = record.Record;
16const PrivateKey = ;
17const proto = ;
18
19const common = ;
20const CertificateBuilder = common.CertificateBuilder;
21const CertificateParser = common.CertificateParser;
22const DhKeyPair = common.DhKeyPair;
23const CertKeyPair = common.CertKeyPair;
24const cert = common.cert;
25
26const rng = ;
27const log = std.log.scoped(.tls);
28
29pub const Options = struct {
30 /// Server authentication. If null server will not send Certificate and
31 /// CertificateVerify message.
32 auth: ?*CertKeyPair,
33
34 /// If not null server will request client certificate. If auth_type is
35 /// .request empty client certificate message will be accepted.
36 /// Client certificate will be verified with root_ca certificates.
37 client_auth: ?ClientAuth = null,
38
39 /// List of supported cipher suites (TLS 1.3 and/or TLS 1.2)
40 /// Default includes both TLS 1.3 and TLS 1.2 secure ciphers for maximum compatibility.
41 cipher_suites: []const CipherSuite = cipher_suites.all,
42
43 /// Named groups (elliptic curves) to support for key exchange
44 named_groups: []const proto.NamedGroup = &[_]proto.NamedGroup{ .x25519, .secp256r1, .secp384r1 },
45};
46
47pub const ClientAuth = struct {
48 /// Set of root certificate authorities that server use when verifying
49 /// client certificates.
50 root_ca: cert.Bundle,
51
52 auth_type: Type = .require,
53
54 pub const Type = enum {
55 /// Client certificate will be requested during the handshake, but does
56 /// not require that the client send any certificates.
57 request,
58 /// Client certificate will be requested during the handshake, and client
59 /// has to send valid certificate.
60 require,
61 };
62};
63
64pub const Handshake = struct {
65 // public key len: x25519 = 32, secp256r1 = 65, secp384r1 = 97
66 const max_pub_key_len = 98;
67 const supported_named_groups = &[_]proto.NamedGroup{ .x25519, .secp256r1, .secp384r1 };
68
69 /// Underlying network connection stream reader/writer pair.
70 input: *Io.Reader,
71 output: *Io.Writer,
72
73 server_random: [32]u8 = undefined,
74 client_random: [32]u8 = undefined,
75 legacy_session_id_buf: [32]u8 = undefined,
76 legacy_session_id: []u8 = &.{},
77 cipher_suite: CipherSuite = @fromBackingInt(@intCast(0)),
78 signature_scheme: proto.SignatureScheme = @fromBackingInt(@intCast(0)),
79 named_group: proto.NamedGroup = @fromBackingInt(@intCast(0)),
80 client_pub_key_buf: [max_pub_key_len]u8 = undefined,
81 client_pub_key: []u8 = &.{},
82 server_pub_key_buf: [max_pub_key_len]u8 = undefined,
83 server_pub_key: []u8 = &.{},
84
85 cipher: Cipher = undefined,
86 transcript: Transcript = .{},
87
88 // TLS 1.2 specific fields
89 tls_version: proto.Version = .tls_1_3,
90 master_secret: [48]u8 = undefined,
91 key_material: [48 * 4]u8 = undefined,
92 dh_kp: DhKeyPair = undefined,
93
94 const Self = @This();
95
96 fn writeAlert(h: *Self, cph: ?*Cipher, err: anyerror) !void {
97 if (cph) |c| {
98 const cleartext = proto.alertFromError(err);
99 const ciphertext = try c.encrypt(h.output.unusedCapacitySlice(), .alert, &cleartext);
100 h.output.advance(ciphertext.len);
101 } else {
102 const alert = record.header(.alert, 2) ++ proto.alertFromError(err);
103 try h.output.writeAll(&alert);
104 }
105 try h.output.flush();
106 }
107
108 pub fn handshake(h: *Self, opt: Options) !Cipher {
109 h.initKeys(opt);
110
111 h.readClientHello(opt.cipher_suites, opt.named_groups) catch |err| {
112 try h.writeAlert(null, err);
113 return err;
114 };
115 h.transcript.use(h.cipher_suite.hash());
116
117 if (h.tls_version == .tls_1_3) {
118 // TLS 1.3 handshake
119 h.serverFlight(opt) catch |err| {
120 try h.writeAlert(null, err);
121 return err;
122 };
123 try h.output.flush();
124
125 h.clientFlight2(opt) catch |err| {
126 // Alert received from client
127 if (!mem.startsWith(u8, @errorName(err), "TlsAlert")) {
128 try h.writeAlert(&h.cipher, err);
129 }
130 return err;
131 };
132 } else {
133 // TLS 1.2 handshake
134 h.serverFlightTls12(opt) catch |err| {
135 try h.writeAlert(null, err);
136 return err;
137 };
138 try h.output.flush();
139
140 h.clientFlight2Tls12(opt) catch |err| {
141 if (!mem.startsWith(u8, @errorName(err), "TlsAlert")) {
142 try h.writeAlert(if (h.cipher_suite.validate() == null) &h.cipher else null, err);
143 }
144 return err;
145 };
146 }
147 return h.cipher;
148 }
149
150 fn initKeys(h: *Self, opt: Options) void {
151 rng.fill(&h.server_random);
152 if (opt.auth) |a| {
153 // required signature scheme in client hello
154 h.signature_scheme = a.key.signature_scheme;
155 }
156 // Initialize DH key pair for key exchange (used in TLS 1.2 ECDHE)
157 var seed: [DhKeyPair.seed_len]u8 = undefined;
158 rng.fill(&seed);
159 h.dh_kp = DhKeyPair.init(seed, opt.named_groups) catch unreachable;
160 }
161
162 fn clientFlight1(h: *Self, opt: Options) !void {
163 try h.readClientHello(opt.cipher_suites, opt.named_groups);
164 h.transcript.use(h.cipher_suite.hash());
165 }
166
167 /// TLS 1.2 server flight: ServerHello, Certificate, ServerKeyExchange, ServerHelloDone
168 fn serverFlightTls12(h: *Self, opt: Options) !void {
169 var w: record.Writer = .initFromIo(h.output);
170
171 // Debug: log the selected named_group (use info level to show in production)
172 log.info("TLS 1.2 serverFlightTls12: named_group={x}, cipher_suite={x}", .{ @backingInt(h.named_group), @backingInt(h.cipher_suite) });
173
174 // Generate server's DH public key
175 log.info("TLS 1.2: generating DH public key", .{});
176 h.server_pub_key = try common.dupe(&h.server_pub_key_buf, try h.dh_kp.publicKey(h.named_group));
177 log.info("TLS 1.2: DH public key generated, len={d}", .{h.server_pub_key.len});
178
179 // ServerHello
180 {
181 log.info("TLS 1.2: making ServerHello", .{});
182 const hello = try h.makeServerHelloTls12(&w);
183 h.transcript.update(hello[record.header_len..]);
184 log.info("TLS 1.2: ServerHello done, pos={d}", .{w.pos()});
185 }
186
187 // Certificate (if auth is enabled)
188 if (opt.auth) |auth| {
189 log.info("TLS 1.2: making Certificate at pos={d}", .{w.pos()});
190 const cb = CertificateBuilder{
191 .cert_key_pair = auth,
192 .transcript = &h.transcript,
193 .tls_version = .tls_1_2,
194 .side = .server,
195 };
196 // Skip space for record header (5 bytes)
197 const header_pos = try w.skip(record.header_len);
198 const content_start = w.pos();
199 try cb.makeCertificate(&w);
200 const content_len = w.pos() - content_start;
201 h.transcript.update(w.buffered()[content_start..]);
202 // Go back and write the record header
203 var hw = w.writerAt(header_pos);
204 try hw.recordHeader(.handshake, content_len);
205 log.info("TLS 1.2: Certificate done, content_len={d}, pos={d}", .{ content_len, w.pos() });
206 }
207
208 // ServerKeyExchange (for ECDHE cipher suites)
209 if (h.cipher_suite.keyExchange() == .ecdhe) {
210 if (opt.auth) |auth| {
211 log.info("TLS 1.2: making ServerKeyExchange at pos={d}", .{w.pos()});
212 // Skip space for record header (5 bytes)
213 const header_pos = try w.skip(record.header_len);
214 const content_start = w.pos();
215 try h.makeServerKeyExchange(&w, auth);
216 const content_len = w.pos() - content_start;
217 h.transcript.update(w.buffered()[content_start..]);
218 // Go back and write the record header
219 var hw = w.writerAt(header_pos);
220 try hw.recordHeader(.handshake, content_len);
221 log.info("TLS 1.2: ServerKeyExchange done, content_len={d}, pos={d}", .{ content_len, w.pos() });
222 }
223 }
224
225 // ServerHelloDone
226 {
227 log.info("TLS 1.2: making ServerHelloDone at pos={d}", .{w.pos()});
228 // Skip space for record header (5 bytes)
229 const header_pos = try w.skip(record.header_len);
230 const content_start = w.pos();
231 // ServerHelloDone is just a handshake header with 0 length body
232 try w.handshakeRecordHeader(.server_hello_done, 0);
233 const content_len = w.pos() - content_start;
234 h.transcript.update(w.buffered()[content_start..]);
235 // Go back and write the record header
236 var hw = w.writerAt(header_pos);
237 try hw.recordHeader(.handshake, content_len);
238 log.info("TLS 1.2: ServerHelloDone done, content_len={d}, pos={d}", .{ content_len, w.pos() });
239 }
240
241 log.info("TLS 1.2: serverFlightTls12 complete, total bytes={d}", .{w.buffered().len});
242 log.info("TLS 1.2: h.output.end before advance={d}", .{h.output.end});
243 h.output.advance(w.buffered().len);
244 log.info("TLS 1.2: h.output.end after advance={d}", .{h.output.end});
245 }
246
247 fn makeServerHelloTls12(h: *Self, w: *record.Writer) ![]const u8 {
248 const header_pos = try w.skip(9);
249
250 try w.enumValue(proto.Version.tls_1_2);
251 try w.slice(&h.server_random);
252 {
253 try w.int(u8, h.legacy_session_id.len);
254 if (h.legacy_session_id.len > 0) try w.slice(h.legacy_session_id);
255 }
256 try w.enumValue(h.cipher_suite);
257 try w.slice(&[_]u8{0}); // compression method (null)
258
259 // Extensions for TLS 1.2 ServerHello
260 // RFC 5746: renegotiation_info extension is required by modern clients
261 // Extension structure: type (2) + length (2) + data
262 // renegotiation_info: type=0xff01, length=1, renegotiated_connection_length=0
263 try w.int(u16, 5); // Extensions total length
264 try w.int(u16, 0xff01); // Extension type: renegotiation_info
265 try w.int(u16, 1); // Extension data length
266 try w.int(u8, 0); // renegotiated_connection length (0 for initial handshake)
267
268 var hw = w.writerAt(header_pos);
269 try hw.recordHeader(.handshake, w.pos() - 5);
270 try hw.handshakeRecordHeader(.server_hello, w.pos() - 9);
271
272 return w.buffered();
273 }
274
275 fn makeServerKeyExchange(h: *Self, hw: *record.Writer, auth: *CertKeyPair) !void {
276 const content_start = hw.pos();
277 // Skip handshake header, write it at end
278 _ = try hw.skip(4);
279
280 // ECParameters - named_curve type + curve ID
281 try hw.int(u8, @backingInt(proto.Curve.named_curve));
282 try hw.enumValue(h.named_group);
283
284 // ECPoint - public key
285 const pub_key = h.server_pub_key;
286 try hw.int(u8, pub_key.len);
287 try hw.slice(pub_key);
288
289 // Signature - sign (client_random + server_random + ec_params + ec_point)
290 const params_end = hw.pos();
291 const params = hw.buffered()[content_start + 4 .. params_end];
292
293 // Build data to sign: client_random || server_random || ec_params
294 var sign_buf: [32 + 32 + 256]u8 = undefined;
295 @memcpy(sign_buf[0..32], &h.client_random);
296 @memcpy(sign_buf[32..64], &h.server_random);
297 @memcpy(sign_buf[64 .. 64 + params.len], params);
298 const sign_data = sign_buf[0 .. 64 + params.len];
299
300 // Sign the data using the appropriate signing method based on key type
301 const signature_scheme = auth.key.signature_scheme;
302 log.info("TLS 1.2 ServerKeyExchange: signature_scheme={x}, ecdsa_key_pair_present={}", .{ @backingInt(signature_scheme), auth.ecdsa_key_pair != null });
303 try hw.enumValue(signature_scheme);
304
305 const signature = switch (signature_scheme) {
306 inline .ecdsa_secp256r1_sha256,
307 .ecdsa_secp384r1_sha384,
308 => |comptime_scheme| brk: {
309 log.info("TLS 1.2 ServerKeyExchange: entering ECDSA branch", .{});
310 const Ecdsa = common.SchemeEcdsa(comptime_scheme);
311 const key_pair = switch (comptime_scheme) {
312 .ecdsa_secp256r1_sha256 => auth.ecdsa_key_pair.?.ecdsa_secp256r1_sha256,
313 .ecdsa_secp384r1_sha384 => auth.ecdsa_key_pair.?.ecdsa_secp384r1_sha384,
314 else => unreachable,
315 };
316 log.info("TLS 1.2 ServerKeyExchange: got key_pair, creating signer", .{});
317 var signer = try key_pair.signer(null);
318 log.info("TLS 1.2 ServerKeyExchange: signer created, updating", .{});
319 signer.update(sign_data);
320 log.info("TLS 1.2 ServerKeyExchange: finalizing signature", .{});
321 const sig = try signer.finalize();
322 log.info("TLS 1.2 ServerKeyExchange: converting to DER", .{});
323 var buf: [Ecdsa.Signature.der_encoded_length_max]u8 = undefined;
324 break :brk sig.toDer(&buf);
325 },
326 inline .rsa_pss_rsae_sha256,
327 .rsa_pss_rsae_sha384,
328 .rsa_pss_rsae_sha512,
329 => |comptime_scheme| brk: {
330 const Hash = common.SchemeHash(comptime_scheme);
331 var signer = try auth.key.key.rsa.signerOaep(Hash, null);
332 signer.update(sign_data);
333 var buf: [512]u8 = undefined;
334 const sig = try signer.finalize(&buf);
335 break :brk sig.bytes;
336 },
337 else => return error.TlsUnknownSignatureScheme,
338 };
339
340 try hw.int(u16, signature.len);
341 try hw.slice(signature);
342
343 // Write handshake header
344 var header_w = hw.writerAt(content_start);
345 try header_w.handshakeRecordHeader(.server_key_exchange, hw.pos() - content_start - 4);
346 }
347
348 /// TLS 1.2 client flight 2: ClientKeyExchange, ChangeCipherSpec, Finished
349 fn clientFlight2Tls12(h: *Self, _: Options) !void {
350 log.info("TLS 1.2 clientFlight2Tls12: starting", .{});
351 // Read ClientKeyExchange
352 {
353 var d = Record.decoder(h.input) catch |err| {
354 log.err("TLS 1.2 clientFlight2Tls12: Record.decoder failed: {}", .{err});
355 return err;
356 };
357 log.info("TLS 1.2 clientFlight2Tls12: got record, content_type={x}", .{@backingInt(d.content_type)});
358 d.expectContentType(.handshake) catch |err| {
359 log.err("TLS 1.2 clientFlight2Tls12: expected handshake but got content_type={x}, payload_len={d}", .{ @backingInt(d.content_type), d.payload.len });
360 if (d.content_type == .alert and d.payload.len >= 2) {
361 log.err("TLS 1.2 clientFlight2Tls12: client sent alert: level={d}, desc={d}", .{ d.payload[0], d.payload[1] });
362 }
363 return err;
364 };
365 h.transcript.update(d.payload);
366
367 const handshake_type = try d.decode(proto.Handshake);
368 log.info("TLS 1.2 clientFlight2Tls12: handshake_type={x} (decimal={d}), expected client_key_exchange={x}", .{ @backingInt(handshake_type), @backingInt(handshake_type), @backingInt(proto.Handshake.client_key_exchange) });
369 if (handshake_type != .client_key_exchange) {
370 log.err("TLS 1.2 clientFlight2Tls12: handshake type mismatch! got={s}, expected=client_key_exchange", .{@tagName(handshake_type)});
371 return error.TlsUnexpectedMessage;
372 }
373 log.info("TLS 1.2 clientFlight2Tls12: handshake type check passed", .{});
374 const length = try d.decode(u24);
375 log.info("TLS 1.2 clientFlight2Tls12: ClientKeyExchange length={d}", .{length});
376
377 // For ECDHE, client sends its public key
378 if (h.cipher_suite.keyExchange() == .ecdhe) {
379 const client_pub_key_len = try d.decode(u8);
380 log.info("TLS 1.2 clientFlight2Tls12: client pub key len={d}, expected={d}", .{ client_pub_key_len, length - 1 });
381 if (client_pub_key_len != length - 1) return error.TlsDecodeError;
382 h.client_pub_key = try common.dupe(&h.client_pub_key_buf, try d.slice(client_pub_key_len));
383 log.info("TLS 1.2 clientFlight2Tls12: got client pub key, len={d}", .{h.client_pub_key.len});
384 } else {
385 // RSA key exchange - skip for now
386 try d.skip(length);
387 }
388 }
389 log.info("TLS 1.2 clientFlight2Tls12: finished reading ClientKeyExchange", .{});
390
391 // Generate pre-master secret and derive keys
392 if (h.cipher_suite.keyExchange() == .ecdhe) {
393 log.info("TLS 1.2 clientFlight2Tls12: generating shared key, named_group={x}", .{@backingInt(h.named_group)});
394 const pre_master_secret = try h.dh_kp.sharedKey(h.named_group, h.client_pub_key);
395 log.info("TLS 1.2 clientFlight2Tls12: got pre_master_secret, len={d}", .{pre_master_secret.len});
396 h.transcript.masterSecret(&h.master_secret, pre_master_secret, h.client_random, h.server_random);
397 h.transcript.keyMaterial(&h.key_material, &h.master_secret, h.client_random, h.server_random);
398 log.info("TLS 1.2 clientFlight2Tls12: derived keys", .{});
399 }
400
401 // Initialize cipher for decryption
402 log.info("TLS 1.2 clientFlight2Tls12: initializing cipher", .{});
403 h.cipher = try Cipher.initTls12(h.cipher_suite, &h.key_material, .server);
404 log.info("TLS 1.2 clientFlight2Tls12: cipher initialized", .{});
405
406 // Read ChangeCipherSpec
407 log.info("TLS 1.2 clientFlight2Tls12: reading ChangeCipherSpec", .{});
408 {
409 var d = try Record.decoder(h.input);
410 log.info("TLS 1.2 clientFlight2Tls12: got record, content_type={x}", .{@backingInt(d.content_type)});
411 try d.expectContentType(.change_cipher_spec);
412 log.info("TLS 1.2 clientFlight2Tls12: ChangeCipherSpec payload_len={d}, payload[0]={d}", .{ d.payload.len, if (d.payload.len > 0) d.payload[0] else 255 });
413 if (d.payload.len != 1 or d.payload[0] != 1) return error.TlsUnexpectedMessage;
414 }
415 log.info("TLS 1.2 clientFlight2Tls12: ChangeCipherSpec OK", .{});
416
417 // Read encrypted Finished
418 // In TLS 1.2, the encrypted Finished message uses content_type=handshake (0x16)
419 // The encryption is applied to the payload, but the record header type stays as handshake
420 log.info("TLS 1.2 clientFlight2Tls12: reading encrypted Finished", .{});
421 {
422 const rec = try Record.read(h.input);
423 log.info("TLS 1.2 clientFlight2Tls12: got record, content_type={x}", .{@backingInt(rec.content_type)});
424 // TLS 1.2 encrypted Finished uses handshake (0x16) content type, NOT application_data
425 if (rec.content_type != .handshake) {
426 log.err("TLS 1.2 clientFlight2Tls12: expected handshake, got {x}", .{@backingInt(rec.content_type)});
427 return error.TlsUnexpectedMessage;
428 }
429
430 var cleartext_buf: [128]u8 = undefined;
431 // For TLS 1.2, decrypt returns the record's content_type (which is handshake)
432 const content_type, const cleartext = try h.cipher.decrypt(&cleartext_buf, rec);
433 log.info("TLS 1.2 clientFlight2Tls12: decrypted, content_type={x}, cleartext_len={d}", .{ @backingInt(content_type), cleartext.len });
434 if (content_type != .handshake) return error.TlsUnexpectedMessage;
435
436 // Parse Finished message
437 var d = record.Decoder.init(content_type, cleartext);
438 const handshake_type = try d.decode(proto.Handshake);
439 if (handshake_type != .finished) return error.TlsUnexpectedMessage;
440 const length = try d.decode(u24);
441 if (length != 12) return error.TlsDecodeError;
442
443 const client_verify_data = try d.slice(12);
444 const expected_verify_data = h.transcript.clientFinishedTls12(&h.master_secret);
445 if (!mem.eql(u8, client_verify_data, &expected_verify_data))
446 return error.TlsDecryptError;
447
448 // Update transcript with Finished message
449 h.transcript.update(cleartext);
450 }
451
452 // Send ChangeCipherSpec and Finished
453 {
454 var w: record.Writer = .initFromIo(h.output);
455
456 // ChangeCipherSpec
457 try w.record(.change_cipher_spec, &[_]u8{1});
458
459 // Finished (encrypted)
460 const server_finished = &record.handshakeHeader(.finished, 12) ++
461 h.transcript.serverFinishedTls12(&h.master_secret);
462 const ciphertext = try h.cipher.encrypt(w.unused(), .handshake, server_finished);
463 w.advance(ciphertext.len);
464
465 h.output.advance(w.buffered().len);
466 try h.output.flush();
467 }
468 }
469
470 fn clientFlight2(h: *Self, opt: Options) !void {
471 // calculate application cipher before updating transcript in readClientFlight2
472 const application_secret = h.transcript.applicationSecret();
473 const app_cipher = try Cipher.initTls13(h.cipher_suite, application_secret, .server);
474 // set application cipher instead of EndOfStream error
475 h.readClientFlight2(opt) catch |err| {
476 if (err != error.EndOfStream and err != error.InputBufferUndersize) {
477 // don't change on short reads: https://github.com/ianic/tls.zig/commit/2f3f23485e01e4be8219c4a1ceda01ed961da61d
478 h.cipher = app_cipher;
479 }
480 return err;
481 };
482 h.cipher = app_cipher;
483 }
484
485 fn serverFlight(h: *Self, opt: Options) !void {
486 var w: record.Writer = .initFromIo(h.output);
487
488 const shared_key = brk: {
489 var seed: [DhKeyPair.seed_len]u8 = undefined;
490 rng.fill(&seed);
491 var kp = try DhKeyPair.init(seed, &[_]proto.NamedGroup{h.named_group});
492 h.server_pub_key = try common.dupe(&h.server_pub_key_buf, try kp.publicKey(h.named_group));
493 break :brk try kp.sharedKey(h.named_group, h.client_pub_key);
494 };
495 {
496 const hello = try h.makeServerHello(&w);
497 h.transcript.update(hello[record.header_len..]);
498 }
499 {
500 const handshake_secret = h.transcript.handshakeSecret(shared_key);
501 h.cipher = try Cipher.initTls13(h.cipher_suite, handshake_secret, .server);
502 }
503 try w.record(.change_cipher_spec, &[_]u8{1});
504 {
505 var hw = try w.writerAdvance(record.header_len);
506 try hw.handshakeRecord(.encrypted_extensions, &[_]u8{ 0, 0 });
507 h.transcript.update(hw.buffered());
508 try h.writeEncrypted(&w, hw.buffered());
509 }
510 if (opt.client_auth) |_| { // Certificate request
511 var hw = try w.writerAdvance(record.header_len);
512 try makeCertificateRequest(&hw);
513 h.transcript.update(hw.buffered());
514 try h.writeEncrypted(&w, hw.buffered());
515 }
516 if (opt.auth) |auth| {
517 const cb = CertificateBuilder{
518 .cert_key_pair = auth,
519 .transcript = &h.transcript,
520 .side = .server,
521 };
522 { // Certificate
523 var hw = try w.writerAdvance(record.header_len);
524 try cb.makeCertificate(&hw);
525 h.transcript.update(hw.buffered());
526 try h.writeEncrypted(&w, hw.buffered());
527 }
528 { // Certificate verify
529 var hw = try w.writerAdvance(record.header_len);
530 try cb.makeCertificateVerify(&hw);
531 h.transcript.update(hw.buffered());
532 try h.writeEncrypted(&w, hw.buffered());
533 }
534 }
535 { // Finished
536 var hw = try w.writerAdvance(record.header_len);
537 try hw.handshakeRecord(.finished, h.transcript.serverFinishedTls13());
538 h.transcript.update(hw.buffered());
539 try h.writeEncrypted(&w, hw.buffered());
540 }
541
542 h.output.advance(w.buffered().len);
543 }
544
545 fn readClientFlight2(h: *Self, opt: Options) !void {
546 // buffer for decrypted handshake records
547 var cleartext_buffer: [max_cleartext_len]u8 = undefined;
548 // cleartext writer
549 var cw = Io.Writer.fixed(&cleartext_buffer);
550
551 var handshake_state: proto.Handshake = .finished;
552 var crt_parser: CertificateParser = undefined;
553 if (opt.client_auth) |client_auth| {
554 crt_parser = .{ .root_ca = client_auth.root_ca, .host = "" };
555 handshake_state = .certificate;
556 }
557
558 outer: while (true) {
559 const rec = try Record.read(h.input);
560 if (rec.protocol_version != .tls_1_2 and rec.content_type != .alert)
561 return error.TlsProtocolVersion;
562
563 switch (rec.content_type) {
564 .change_cipher_spec => {
565 if (rec.payload.len != 1) return error.TlsUnexpectedMessage;
566 },
567 .application_data => {
568 const content_type, const cleartext = try h.cipher.decrypt(cw.unusedCapacitySlice(), rec);
569 cw.advance(cleartext.len);
570
571 var d = record.Decoder.init(content_type, cw.buffered());
572 try d.expectContentType(.handshake);
573 while (!d.eof()) {
574 const handshake_type = try d.decode(proto.Handshake);
575 const length = try d.decode(u24);
576
577 if (length > max_cleartext_len)
578 return error.TlsRecordOverflow;
579 if (length > d.rest().len)
580 continue :outer; // fragmented handshake into multiple records
581
582 defer {
583 h.transcript.update(d.payload[0..d.idx]);
584 _ = cw.consume(d.idx);
585 d = record.Decoder.init(content_type, cw.buffered());
586 }
587
588 if (handshake_state != handshake_type)
589 return error.TlsUnexpectedMessage;
590
591 switch (handshake_type) {
592 .certificate => {
593 if (length == 4) {
594 // got empty certificate message
595 if (opt.client_auth.?.auth_type == .require)
596 return error.TlsCertificateRequired;
597 try d.skip(length);
598 handshake_state = .finished;
599 } else {
600 try crt_parser.parseCertificate(&d, .tls_1_3);
601 handshake_state = .certificate_verify;
602 }
603 },
604 .certificate_verify => {
605 try crt_parser.parseCertificateVerify(&d);
606 crt_parser.verifySignature(h.transcript.clientCertificateVerify()) catch |err| return switch (err) {
607 error.TlsUnknownSignatureScheme => error.TlsIllegalParameter,
608 else => error.TlsDecryptError,
609 };
610 handshake_state = .finished;
611 },
612 .finished => {
613 const actual = try d.slice(length);
614 const expected = h.transcript.clientFinishedTls13();
615 if (!mem.eql(u8, expected, actual))
616 return if (expected.len == actual.len)
617 error.TlsDecryptError
618 else
619 error.TlsDecodeError;
620 return;
621 },
622 else => return error.TlsUnexpectedMessage,
623 }
624 }
625 },
626 .alert => {
627 var d = record.Decoder.init(rec.content_type, rec.payload);
628 return d.raiseAlert();
629 },
630 else => return error.TlsUnexpectedMessage,
631 }
632 }
633 }
634
635 /// Write encrypted handshake message into `w` Cleartext and write buffer
636 /// `w.unused()` are reusing same buffer. Cleartext is written 5 bytes ahead
637 /// (record header len) from w.unused() position to avoid memcopy in the
638 /// encrypt. Encrypt will add tls record head in first 5 bytes, encrypt
639 /// cleartext and add hmac at end.
640 fn writeEncrypted(h: *Self, w: *record.Writer, cleartext: []const u8) !void {
641 const ciphertext = try h.cipher.encrypt(w.unused(), .handshake, cleartext);
642 w.advance(ciphertext.len);
643 }
644
645 fn makeServerHello(h: *Self, w: *record.Writer) ![]const u8 {
646 const header_pos = try w.skip(9);
647
648 try w.enumValue(proto.Version.tls_1_2);
649 try w.slice(&h.server_random);
650 {
651 try w.int(u8, h.legacy_session_id.len);
652 if (h.legacy_session_id.len > 0) try w.slice(h.legacy_session_id);
653 }
654 try w.enumValue(h.cipher_suite);
655 try w.slice(&[_]u8{0}); // compression method
656
657 const ext_len_pos = try w.skip(2); // extensions length placeholder writer
658 { // supported versions extension
659 try w.enumValue(proto.Extension.supported_versions);
660 try w.int(u16, 2);
661 try w.enumValue(proto.Version.tls_1_3);
662 }
663 { // key share extension
664 const key_len: u16 = @intCast(h.server_pub_key.len);
665 try w.enumValue(proto.Extension.key_share);
666 try w.int(u16, key_len + 4);
667 try w.enumValue(h.named_group);
668 try w.int(u16, key_len);
669 try w.slice(h.server_pub_key);
670 }
671 var ew = w.writerAt(ext_len_pos);
672 try ew.int(u16, w.pos() - ext_len_pos - 2);
673 var hw = w.writerAt(header_pos);
674 try hw.recordHeader(.handshake, w.pos() - 5);
675 try hw.handshakeRecordHeader(.server_hello, w.pos() - 9);
676
677 return w.buffered();
678 }
679
680 fn makeCertificateRequest(w: *record.Writer) !void {
681 const header_pos = try w.skip(4 + 1 + 2);
682 const ext_head = w.pos();
683 try w.extension(.signature_algorithms, common.supported_signature_algorithms);
684 const ext_len = w.pos() - ext_head;
685 var hw = w.writerAt(header_pos);
686 try hw.handshakeRecordHeader(.certificate_request, ext_len + 3);
687 try hw.int(u8, 0); // certificate request context length = 0
688 try hw.int(u16, ext_len); // extensions length
689 }
690
691 fn readClientHello(h: *Self, supported_cipher_suites: []const CipherSuite, server_named_groups: []const proto.NamedGroup) !void {
692 log.info("readClientHello: starting to parse ClientHello", .{});
693 var d = try Record.decoder(h.input);
694 if (d.payload.len > max_cleartext_len) return error.TlsRecordOverflow;
695 try d.expectContentType(.handshake);
696 h.transcript.update(d.payload);
697
698 const handshake_type = try d.decode(proto.Handshake);
699 if (handshake_type != .client_hello) return error.TlsUnexpectedMessage;
700 _ = try d.decode(u24); // handshake length
701 if (try d.decode(proto.Version) != .tls_1_2) return error.TlsProtocolVersion;
702
703 h.client_random = try d.array(32);
704 { // legacy session id
705 const len = try d.decode(u8);
706 h.legacy_session_id = try common.dupe(&h.legacy_session_id_buf, try d.slice(len));
707 }
708 // Track cipher suite candidates for both TLS versions separately
709 // This is needed because we don't know which TLS version will be used yet
710 var tls13_cipher: CipherSuite = @fromBackingInt(@intCast(0));
711 var tls12_cipher: CipherSuite = @fromBackingInt(@intCast(0));
712 { // cipher suites
713 const end_idx = try d.decode(u16) + d.idx;
714
715 while (d.idx < end_idx) {
716 const cipher_suite = try d.decode(CipherSuite);
717 if (cipher_suites.includes(supported_cipher_suites, cipher_suite)) {
718 // Track TLS 1.3 and TLS 1.2 ciphers separately
719 if (cipher_suites.includes(cipher_suites.tls13, cipher_suite)) {
720 if (@backingInt(tls13_cipher) == 0) {
721 tls13_cipher = cipher_suite;
722 }
723 } else if (cipher_suites.includes(cipher_suites.tls12, cipher_suite)) {
724 if (@backingInt(tls12_cipher) == 0) {
725 tls12_cipher = cipher_suite;
726 }
727 }
728 }
729 }
730 // Must have at least one supported cipher
731 if (@backingInt(tls13_cipher) == 0 and @backingInt(tls12_cipher) == 0)
732 return error.TlsNoSupportedCiphers;
733 }
734 log.info("readClientHello: tls13_cipher={x}, tls12_cipher={x}", .{ @backingInt(tls13_cipher), @backingInt(tls12_cipher) });
735 try d.skip(2); // compression methods
736
737 var key_share_received = false;
738 var tls_1_3_supported = false;
739 var supported_groups_buf: [16]proto.NamedGroup = undefined;
740 var supported_groups_len: usize = 0;
741
742 // extensions
743 const extensions_end_idx = try d.decode(u16) + d.idx;
744 while (d.idx < extensions_end_idx) {
745 const extension_type = try d.decode(proto.Extension);
746 const extension_len = try d.decode(u16);
747
748 switch (extension_type) {
749 .supported_versions => {
750 const end_idx = try d.decode(u8) + d.idx;
751 while (d.idx < end_idx) {
752 const version = try d.decode(proto.Version);
753 if (version == proto.Version.tls_1_3) {
754 tls_1_3_supported = true;
755 }
756 }
757 },
758 .key_share => {
759 if (extension_len == 0) return error.TlsDecodeError;
760 key_share_received = true;
761 var selected_named_group_idx = supported_named_groups.len;
762 const end_idx = try d.decode(u16) + d.idx;
763 while (d.idx < end_idx) {
764 const named_group = try d.decode(proto.NamedGroup);
765 switch (@backingInt(named_group)) {
766 0x0001...0x0016,
767 0x001a...0x001c,
768 0xff01...0xff02,
769 => return error.TlsIllegalParameter,
770 else => {},
771 }
772 const client_pub_key = try d.slice(try d.decode(u16));
773 for (supported_named_groups, 0..) |supported, idx| {
774 if (named_group == supported and idx < selected_named_group_idx) {
775 h.named_group = named_group;
776 h.client_pub_key = try common.dupe(&h.client_pub_key_buf, client_pub_key);
777 selected_named_group_idx = idx;
778 }
779 }
780 }
781 if (@backingInt(h.named_group) == 0)
782 return error.TlsIllegalParameter;
783 },
784 .supported_groups => {
785 const end_idx = try d.decode(u16) + d.idx;
786 while (d.idx < end_idx) {
787 const named_group = try d.decode(proto.NamedGroup);
788 switch (@backingInt(named_group)) {
789 // Deprecated/insecure curves - skip silently for TLS 1.2 compatibility
790 // Some clients (including Apple SecureTransport) may include these
791 0x0001...0x0016,
792 0x001a...0x001c,
793 0xff01...0xff02,
794 => {},
795 else => {
796 // Store supported groups for TLS 1.2 fallback
797 if (supported_groups_len < supported_groups_buf.len) {
798 supported_groups_buf[supported_groups_len] = named_group;
799 supported_groups_len += 1;
800 }
801 },
802 }
803 }
804 },
805 .signature_algorithms => {
806 if (@backingInt(h.signature_scheme) == 0) {
807 try d.skip(extension_len);
808 } else {
809 var found = false;
810 const list_len = try d.decode(u16);
811 if (list_len == 0) return error.TlsDecodeError;
812 const end_idx = list_len + d.idx;
813 while (d.idx < end_idx) {
814 const signature_scheme = try d.decode(proto.SignatureScheme);
815 if (signature_scheme == h.signature_scheme) found = true;
816 }
817 if (!found) return error.TlsHandshakeFailure;
818 }
819 },
820 else => {
821 try d.skip(extension_len);
822 },
823 }
824 }
825
826 // Determine TLS version based on client capabilities
827 log.info("readClientHello: tls_1_3_supported={}, key_share_received={}, supported_groups_len={}", .{ tls_1_3_supported, key_share_received, supported_groups_len });
828 if (tls_1_3_supported and key_share_received and @backingInt(tls13_cipher) != 0) {
829 // TLS 1.3 handshake - requires TLS 1.3 cipher
830 h.tls_version = .tls_1_3;
831 h.cipher_suite = tls13_cipher;
832 if (@backingInt(h.named_group) == 0) return error.TlsIllegalParameter;
833 } else if (@backingInt(tls12_cipher) != 0) {
834 // TLS 1.2 handshake - need to select named group from supported_groups
835 h.tls_version = .tls_1_2;
836 h.cipher_suite = tls12_cipher;
837 log.info("readClientHello: TLS 1.2 path, cipher keyExchange={}", .{h.cipher_suite.keyExchange()});
838
839 // For TLS 1.2 ECDHE, select a named group from supported_groups extension
840 if (h.cipher_suite.keyExchange() == .ecdhe) {
841 // If no supported_groups extension was received, default to common curves
842 // per RFC 8422 - most TLS 1.2 clients support secp256r1/secp384r1
843 const client_groups = if (supported_groups_len > 0)
844 supported_groups_buf[0..supported_groups_len]
845 else
846 &[_]proto.NamedGroup{ .secp256r1, .secp384r1 };
847
848 log.info("readClientHello: client_groups count={}, server_named_groups count={}", .{ client_groups.len, server_named_groups.len });
849
850 // Find first matching named group between server and client
851 for (server_named_groups) |server_ng| {
852 for (client_groups) |client_ng| {
853 if (server_ng == client_ng) {
854 h.named_group = server_ng;
855 break;
856 }
857 }
858 if (@backingInt(h.named_group) != 0) break;
859 }
860 if (@backingInt(h.named_group) == 0) return error.TlsIllegalParameter;
861 }
862 log.info("TLS 1.2 readClientHello: selected named_group={x}", .{@backingInt(h.named_group)});
863 } else {
864 // No compatible cipher suite found
865 return error.TlsNoSupportedCiphers;
866 }
867 log.info("readClientHello complete: tls_version={}, cipher_suite={x}, named_group={x}", .{ h.tls_version, @backingInt(h.cipher_suite), @backingInt(h.named_group) });
868 }
869};
870
871const testing = std.testing;
872const data13 = ;
873const testu = ;
874
875test "read client hello" {
876 var reader: Io.Reader = .fixed(&data13.client_hello);
877 var h: Handshake = .{
878 .input = &reader,
879 .output = undefined,
880 };
881 h.signature_scheme = .ecdsa_secp521r1_sha512; // this must be supported in signature_algorithms extension
882 try h.readClientHello(cipher_suites.tls13, &[_]proto.NamedGroup{ .x25519, .secp256r1, .secp384r1 });
883
884 try testing.expectEqual(CipherSuite.AES_256_GCM_SHA384, h.cipher_suite);
885 try testing.expectEqual(.x25519, h.named_group);
886 try testing.expectEqualSlices(u8, &data13.client_random, &h.client_random);
887 try testing.expectEqualSlices(u8, &data13.client_public_key, h.client_pub_key);
888}
889
890test "make server hello" {
891 var h: Handshake = .{ .input = undefined, .output = undefined };
892
893 h.cipher_suite = .AES_256_GCM_SHA384;
894 testu.fillFrom(&h.server_random, 0);
895 testu.fillFrom(&h.server_pub_key_buf, 0x20);
896 h.named_group = .x25519;
897 h.server_pub_key = h.server_pub_key_buf[0..32];
898
899 const expected = &testu.hexToBytes(
900 \\ 16 03 03 00 5a 02 00 00 56
901 \\ 03 03
902 \\ 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
903 \\ 00
904 \\ 13 02 00
905 \\ 00 2e 00 2b 00 02 03 04
906 \\ 00 33 00 24 00 1d 00 20
907 \\ 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f
908 );
909
910 var buffer: [128]u8 = undefined;
911 var w: record.Writer = .init(&buffer);
912 const actual = try h.makeServerHello(&w);
913 try testing.expectEqual(95, actual.len);
914 try testing.expectEqualSlices(u8, expected, actual);
915}
916
917test "make certificate request" {
918 var buffer: [32]u8 = undefined;
919
920 const expected = testu.hexToBytes("0d 00 00 1b" ++ // handshake header
921 "00 00 18" ++ // extension length
922 "00 0d" ++ // signature algorithms extension
923 "00 14" ++ // extension length
924 "00 12" ++ // list length 6 * 2 bytes
925 "04 03 05 03 08 04 08 05 08 06 08 07 02 01 04 01 05 01" // signature schemes
926 );
927
928 var w: record.Writer = .init(&buffer);
929 try Handshake.makeCertificateRequest(&w);
930 try testing.expectEqualSlices(u8, &expected, w.buffered());
931}
932
933pub const NonBlock = struct {
934 const Self = @This();
935
936 // inner sync handshake
937 inner: Handshake = undefined,
938 opt: Options = undefined,
939 state: State = undefined,
940
941 const State = enum {
942 init,
943 client_flight_1,
944 server_flight,
945 client_flight_2,
946
947 fn next(self: *State) void {
948 self.* = @fromBackingInt(@intCast(@backingInt(self.*) + 1));
949 }
950 };
951
952 pub fn init(opt: Options) Self {
953 var inner: Handshake = .{
954 .input = undefined,
955 .output = undefined,
956 };
957 inner.initKeys(opt);
958 return .{
959 .opt = opt,
960 .inner = inner,
961 .state = .init,
962 };
963 }
964
965 fn recv(self: *Self) !void {
966 const prev: Transcript = self.inner.transcript;
967 errdefer self.inner.transcript = prev;
968
969 switch (self.state) {
970 .init => {
971 try self.inner.clientFlight1(self.opt);
972 self.state.next();
973 },
974 .server_flight => {
975 // Use appropriate client flight based on TLS version
976 if (self.inner.tls_version == .tls_1_2) {
977 try self.inner.clientFlight2Tls12(self.opt);
978 } else {
979 try self.inner.clientFlight2(self.opt);
980 }
981 self.state.next();
982 },
983 else => return,
984 }
985 }
986
987 /// True when handshake is successfully finished
988 pub fn done(self: Self) bool {
989 return self.state == .client_flight_2;
990 }
991
992 /// Runs next handshake step.
993 pub fn run(
994 self: *Self,
995 /// Data received from the peer
996 recv_buf: []const u8,
997 /// Scratch buffer where data to be sent to the peer will be prepared
998 send_buf: []u8,
999 ) !struct {
1000 /// Number of bytes consumed from recv_buf
1001 recv_pos: usize,
1002 /// Number of bytes prepared in send_buf
1003 send_pos: usize,
1004 /// Unused part of the recv_buf,
1005 unused_recv: []const u8,
1006 /// Part of the send_buf that should be sent to the peer
1007 send: []const u8,
1008 } {
1009 if (self.done()) return .{
1010 .recv_pos = 0,
1011 .send_pos = 0,
1012 .unused_recv = &.{},
1013 .send = &.{},
1014 };
1015
1016 var reader: Io.Reader = .fixed(recv_buf);
1017 self.inner.input = &reader;
1018 var writer: Io.Writer = .fixed(send_buf);
1019 self.inner.output = &writer;
1020
1021 var recv_pos: usize = 0;
1022 out: switch (self.state) {
1023 .init, .server_flight => {
1024 self.recv() catch |err| switch (err) {
1025 error.EndOfStream, error.InputBufferUndersize => {
1026 return .{
1027 .recv_pos = 0,
1028 .send_pos = 0,
1029 .unused_recv = recv_buf,
1030 .send = &.{},
1031 };
1032 },
1033 else => return err,
1034 };
1035 recv_pos = reader.seek;
1036 continue :out self.state;
1037 },
1038 .client_flight_1 => {
1039 if (recv_buf.ptr == send_buf.ptr and recv_pos != recv_buf.len) {
1040 // recv buffer is fully consumed, same buffer can be used for write
1041 return error.TlsUnexpectedMessage;
1042 }
1043 // Use appropriate server flight based on TLS version
1044 if (self.inner.tls_version == .tls_1_2) {
1045 try self.inner.serverFlightTls12(self.opt);
1046 } else {
1047 try self.inner.serverFlight(self.opt);
1048 }
1049 self.state.next();
1050 },
1051 .client_flight_2 => {
1052 // done
1053 },
1054 }
1055
1056 log.info("NonBlock run() returning: recv_pos={d}, writer.end={d}, send.len={d}", .{ recv_pos, writer.end, writer.buffered().len });
1057 return .{
1058 .recv_pos = recv_pos,
1059 .send_pos = writer.end,
1060 .unused_recv = recv_buf[recv_pos..],
1061 .send = writer.buffered(),
1062 };
1063 }
1064
1065 /// Cipher produced in handshake, null until successful handshake.
1066 pub fn cipher(self: Self) ?Cipher {
1067 return if (self.done()) self.inner.cipher else null;
1068 }
1069};
packages/zig/vendor/zig-tls/src/key_log.zigdeleted+0-67
Changes to packages/zig/vendor/zig-tls/src/key_log.zig
@@ -1,67 +0,0 @@
1//! Exporting tls key so we can share them with Wireshark and analyze decrypted
2//! traffic in Wireshark.
3//! To configure Wireshark to use exprted keys see curl reference.
4//!
5//! References:
6//! curl: https://everything.curl.dev/usingcurl/tls/sslkeylogfile.html
7//! openssl: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_keylog_callback.html
8//! https://udn.realityripple.com/docs/Mozilla/Projects/NSS/Key_Log_Format
9
10const std = ;
11
12const key_log_file_env = "SSLKEYLOGFILE";
13
14pub const label = struct {
15 // tls 1.3
16 pub const client_handshake_traffic_secret: []const u8 = "CLIENT_HANDSHAKE_TRAFFIC_SECRET";
17 pub const server_handshake_traffic_secret: []const u8 = "SERVER_HANDSHAKE_TRAFFIC_SECRET";
18 pub const client_traffic_secret_0: []const u8 = "CLIENT_TRAFFIC_SECRET_0";
19 pub const server_traffic_secret_0: []const u8 = "SERVER_TRAFFIC_SECRET_0";
20 // tls 1.2
21 pub const client_random: []const u8 = "CLIENT_RANDOM";
22};
23
24pub const Callback = *const fn (label: []const u8, client_random: []const u8, secret: []const u8) void;
25
26/// Writes tls keys to the file pointed by SSLKEYLOGFILE environment variable.
27pub fn callback(label_: []const u8, client_random: []const u8, secret: []const u8) void {
28 const builtin = ;
29 const native_os = builtin.os.tag;
30 const file_name: []const u8 = if (native_os == .windows)
31 return
32 else if (native_os == .wasi and !builtin.link_libc)
33 return
34 else
35 std.posix.getenv(key_log_file_env) orelse return;
36 fileAppend(file_name, label_, client_random, secret) catch return;
37}
38
39pub fn fileAppend(file_name: []const u8, label_: []const u8, client_random: []const u8, secret: []const u8) !void {
40 var buf: [1024]u8 = undefined;
41 const line = try formatLine(&buf, label_, client_random, secret);
42 try fileWrite(file_name, line);
43}
44
45fn fileWrite(file_name: []const u8, line: []const u8) !void {
46 var path_buf: [std.posix.PATH_MAX]u8 = undefined;
47 if (file_name.len >= path_buf.len) return error.NameTooLong;
48 @memcpy(path_buf[0..file_name.len], file_name);
49 path_buf[file_name.len] = 0;
50 const fd = try std.posix.open(path_buf[0..file_name.len :0], .{ .ACCMODE = .WRONLY, .CREAT = true, .APPEND = true }, 0o600);
51 defer _ = std.c.close(fd);
52 _ = try std.posix.write(fd, line);
53}
54
55pub fn formatLine(buf: []u8, label_: []const u8, client_random: []const u8, secret: []const u8) ![]const u8 {
56 var w = std.Io.Writer.fixed(buf);
57 try w.print("{s} ", .{label_});
58 for (client_random) |b| {
59 try w.print("{x:0>2}", .{b});
60 }
61 try w.writeByte(' ');
62 for (secret) |b| {
63 try w.print("{x:0>2}", .{b});
64 }
65 try w.writeByte('\n');
66 return w.buffered();
67}
packages/zig/vendor/zig-tls/src/protocol.zigdeleted+0-309
Changes to packages/zig/vendor/zig-tls/src/protocol.zig
@@ -1,309 +0,0 @@
1pub const Version = enum(u16) {
2 tls_1_2 = 0x0303,
3 tls_1_3 = 0x0304,
4 _,
5};
6
7pub const ContentType = enum(u8) {
8 invalid = 0,
9 change_cipher_spec = 20,
10 alert = 21,
11 handshake = 22,
12 application_data = 23,
13 _,
14};
15
16pub const Handshake = enum(u8) {
17 client_hello = 1,
18 server_hello = 2,
19 new_session_ticket = 4,
20 end_of_early_data = 5,
21 encrypted_extensions = 8,
22 certificate = 11,
23 server_key_exchange = 12,
24 certificate_request = 13,
25 server_hello_done = 14,
26 certificate_verify = 15,
27 client_key_exchange = 16,
28 finished = 20,
29 key_update = 24,
30 message_hash = 254,
31 _,
32};
33
34pub const Curve = enum(u8) {
35 named_curve = 0x03,
36 _,
37};
38
39pub const KeyExchangeModes = enum(u8) {
40 psk_ke = 0,
41 psk_dhe_ke = 1,
42};
43
44pub const Extension = enum(u16) {
45 /// RFC 6066
46 server_name = 0,
47 /// RFC 6066
48 max_fragment_length = 1,
49 /// RFC 6066
50 status_request = 5,
51 /// RFC 8422, 7919
52 supported_groups = 10,
53 /// RFC 8446
54 signature_algorithms = 13,
55 /// RFC 5764
56 use_srtp = 14,
57 /// RFC 6520
58 heartbeat = 15,
59 /// RFC 7301
60 application_layer_protocol_negotiation = 16,
61 /// RFC 6962
62 signed_certificate_timestamp = 18,
63 /// RFC 7250
64 client_certificate_type = 19,
65 /// RFC 7250
66 server_certificate_type = 20,
67 /// RFC 7685
68 padding = 21,
69 /// RFC 8446
70 pre_shared_key = 41,
71 /// RFC 8446
72 early_data = 42,
73 /// RFC 8446
74 supported_versions = 43,
75 /// RFC 8446
76 cookie = 44,
77 /// RFC 8446
78 psk_key_exchange_modes = 45,
79 /// RFC 8446
80 certificate_authorities = 47,
81 /// RFC 8446
82 oid_filters = 48,
83 /// RFC 8446
84 post_handshake_auth = 49,
85 /// RFC 8446
86 signature_algorithms_cert = 50,
87 /// RFC 8446
88 key_share = 51,
89
90 _,
91};
92
93pub fn alertFromError(err: anyerror) [2]u8 {
94 return [2]u8{ @backingInt(Alert.Level.fatal), @backingInt(Alert.fromError(err)) };
95}
96
97pub const Alert = enum(u8) {
98 pub const Level = enum(u8) {
99 warning = 1,
100 fatal = 2,
101 _,
102 };
103
104 pub const Error = error{
105 TlsAlertUnexpectedMessage,
106 TlsAlertBadRecordMac,
107 TlsAlertRecordOverflow,
108 TlsAlertHandshakeFailure,
109 TlsAlertBadCertificate,
110 TlsAlertUnsupportedCertificate,
111 TlsAlertCertificateRevoked,
112 TlsAlertCertificateExpired,
113 TlsAlertCertificateUnknown,
114 TlsAlertIllegalParameter,
115 TlsAlertUnknownCa,
116 TlsAlertAccessDenied,
117 TlsAlertDecodeError,
118 TlsAlertDecryptError,
119 TlsAlertProtocolVersion,
120 TlsAlertInsufficientSecurity,
121 TlsAlertInternalError,
122 TlsAlertInappropriateFallback,
123 TlsAlertMissingExtension,
124 TlsAlertUnsupportedExtension,
125 TlsAlertUnrecognizedName,
126 TlsAlertBadCertificateStatusResponse,
127 TlsAlertUnknownPskIdentity,
128 TlsAlertCertificateRequired,
129 TlsAlertNoApplicationProtocol,
130 TlsAlertUnknown,
131 };
132
133 close_notify = 0,
134 unexpected_message = 10,
135 bad_record_mac = 20,
136 record_overflow = 22,
137 handshake_failure = 40,
138 bad_certificate = 42,
139 unsupported_certificate = 43,
140 certificate_revoked = 44,
141 certificate_expired = 45,
142 certificate_unknown = 46,
143 illegal_parameter = 47,
144 unknown_ca = 48,
145 access_denied = 49,
146 decode_error = 50,
147 decrypt_error = 51,
148 protocol_version = 70,
149 insufficient_security = 71,
150 internal_error = 80,
151 inappropriate_fallback = 86,
152 user_canceled = 90,
153 missing_extension = 109,
154 unsupported_extension = 110,
155 unrecognized_name = 112,
156 bad_certificate_status_response = 113,
157 unknown_psk_identity = 115,
158 certificate_required = 116,
159 no_application_protocol = 120,
160 _,
161
162 pub fn toError(alert: Alert) Error!void {
163 return switch (alert) {
164 .close_notify => {}, // not an error
165 .unexpected_message => error.TlsAlertUnexpectedMessage,
166 .bad_record_mac => error.TlsAlertBadRecordMac,
167 .record_overflow => error.TlsAlertRecordOverflow,
168 .handshake_failure => error.TlsAlertHandshakeFailure,
169 .bad_certificate => error.TlsAlertBadCertificate,
170 .unsupported_certificate => error.TlsAlertUnsupportedCertificate,
171 .certificate_revoked => error.TlsAlertCertificateRevoked,
172 .certificate_expired => error.TlsAlertCertificateExpired,
173 .certificate_unknown => error.TlsAlertCertificateUnknown,
174 .illegal_parameter => error.TlsAlertIllegalParameter,
175 .unknown_ca => error.TlsAlertUnknownCa,
176 .access_denied => error.TlsAlertAccessDenied,
177 .decode_error => error.TlsAlertDecodeError,
178 .decrypt_error => error.TlsAlertDecryptError,
179 .protocol_version => error.TlsAlertProtocolVersion,
180 .insufficient_security => error.TlsAlertInsufficientSecurity,
181 .internal_error => error.TlsAlertInternalError,
182 .inappropriate_fallback => error.TlsAlertInappropriateFallback,
183 .user_canceled => {}, // not an error
184 .missing_extension => error.TlsAlertMissingExtension,
185 .unsupported_extension => error.TlsAlertUnsupportedExtension,
186 .unrecognized_name => error.TlsAlertUnrecognizedName,
187 .bad_certificate_status_response => error.TlsAlertBadCertificateStatusResponse,
188 .unknown_psk_identity => error.TlsAlertUnknownPskIdentity,
189 .certificate_required => error.TlsAlertCertificateRequired,
190 .no_application_protocol => error.TlsAlertNoApplicationProtocol,
191 _ => error.TlsAlertUnknown,
192 };
193 }
194
195 pub fn fromError(err: anyerror) Alert {
196 return switch (err) {
197 error.TlsUnexpectedMessage => .unexpected_message,
198 error.TlsBadRecordMac => .bad_record_mac,
199 error.TlsRecordOverflow => .record_overflow,
200 error.TlsHandshakeFailure => .handshake_failure,
201 error.TlsBadCertificate => .bad_certificate,
202 error.TlsUnsupportedCertificate => .unsupported_certificate,
203 error.TlsCertificateRevoked => .certificate_revoked,
204 error.TlsCertificateExpired => .certificate_expired,
205 error.TlsCertificateUnknown => .certificate_unknown,
206 error.TlsIllegalParameter,
207 error.IdentityElement,
208 error.InvalidEncoding,
209 error.TlsNoSupportedCiphers,
210 => .illegal_parameter,
211 error.TlsUnknownCa => .unknown_ca,
212 error.TlsAccessDenied => .access_denied,
213 error.TlsDecodeError => .decode_error,
214 error.TlsDecryptError => .decrypt_error,
215 error.TlsProtocolVersion => .protocol_version,
216 error.TlsInsufficientSecurity => .insufficient_security,
217 error.TlsInternalError => .internal_error,
218 error.TlsInappropriateFallback => .inappropriate_fallback,
219 error.TlsMissingExtension => .missing_extension,
220 error.TlsUnsupportedExtension => .unsupported_extension,
221 error.TlsUnrecognizedName => .unrecognized_name,
222 error.TlsBadCertificateStatusResponse => .bad_certificate_status_response,
223 error.TlsUnknownPskIdentity => .unknown_psk_identity,
224 error.TlsCertificateRequired => .certificate_required,
225 error.TlsNoApplicationProtocol => .no_application_protocol,
226 else => .internal_error,
227 };
228 }
229
230 pub fn parse(buf: [2]u8) Alert {
231 const level: Alert.Level = @fromBackingInt(@intCast(buf[0]));
232 const alert: Alert = @fromBackingInt(@intCast(buf[1]));
233 _ = level;
234 return alert;
235 }
236
237 pub fn closeNotify() [2]u8 {
238 return [2]u8{
239 @backingInt(Alert.Level.warning),
240 @backingInt(Alert.close_notify),
241 };
242 }
243};
244
245pub const SignatureScheme = enum(u16) {
246 // RSASSA-PKCS1-v1_5 algorithms
247 rsa_pkcs1_sha256 = 0x0401,
248 rsa_pkcs1_sha384 = 0x0501,
249 rsa_pkcs1_sha512 = 0x0601,
250
251 // ECDSA algorithms
252 ecdsa_secp256r1_sha256 = 0x0403,
253 ecdsa_secp384r1_sha384 = 0x0503,
254 ecdsa_secp521r1_sha512 = 0x0603,
255
256 // RSASSA-PSS algorithms with public key OID rsaEncryption
257 rsa_pss_rsae_sha256 = 0x0804,
258 rsa_pss_rsae_sha384 = 0x0805,
259 rsa_pss_rsae_sha512 = 0x0806,
260
261 // EdDSA algorithms
262 ed25519 = 0x0807,
263 ed448 = 0x0808,
264
265 // RSASSA-PSS algorithms with public key OID RSASSA-PSS
266 rsa_pss_pss_sha256 = 0x0809,
267 rsa_pss_pss_sha384 = 0x080a,
268 rsa_pss_pss_sha512 = 0x080b,
269
270 // Legacy algorithms
271 rsa_pkcs1_sha1 = 0x0201,
272 ecdsa_sha1 = 0x0203,
273
274 _,
275};
276
277pub const NamedGroup = enum(u16) {
278 // Elliptic Curve Groups (ECDHE)
279 secp256r1 = 0x0017,
280 secp384r1 = 0x0018,
281 secp521r1 = 0x0019,
282 x25519 = 0x001D,
283 x448 = 0x001E,
284
285 // Finite Field Groups (DHE)
286 ffdhe2048 = 0x0100,
287 ffdhe3072 = 0x0101,
288 ffdhe4096 = 0x0102,
289 ffdhe6144 = 0x0103,
290 ffdhe8192 = 0x0104,
291
292 // Hybrid post-quantum key agreements
293 x25519_kyber512d00 = 0xFE30,
294 x25519_kyber768d00 = 0x6399,
295 x25519_ml_kem768 = 0x11EC,
296
297 _,
298};
299
300pub const KeyUpdateRequest = enum(u8) {
301 update_not_requested = 0,
302 update_requested = 1,
303 _,
304};
305
306pub const Side = enum {
307 client,
308 server,
309};
packages/zig/vendor/zig-tls/src/random.zigdeleted+0-47
Changes to packages/zig/vendor/zig-tls/src/random.zig
@@ -1,47 +0,0 @@
1/// Cross-platform CSPRNG that works on macOS, Linux, and other targets.
2const std = ;
3const builtin = ;
4
5/// Fill buffer with cryptographically secure random bytes.
6pub fn fill(buf: []u8) void {
7 switch (builtin.os.tag) {
8 .macos, .ios, .freebsd, .netbsd, .openbsd => {
9 std.c.arc4random_buf(buf.ptr, buf.len);
10 },
11 .linux => {
12 // Use getrandom syscall directly (always available via kernel >= 3.17)
13 const rc = std.os.linux.getrandom(buf.ptr, buf.len, 0);
14 // Check for error: on Linux syscalls, error is indicated by
15 // return value > max_usize - 4096 (i.e., negative errno)
16 if (rc > std.math.maxInt(usize) - 4096) {
17 // Fallback: read from /dev/urandom
18 urandomFill(buf);
19 }
20 },
21 else => {
22 // Fallback for other platforms
23 urandomFill(buf);
24 },
25 }
26}
27
28fn urandomFill(buf: []u8) void {
29 const fd = std.posix.openat(std.posix.AT.FDCWD, "/dev/urandom", .{ .ACCMODE = .RDONLY }, 0) catch return;
30 defer _ = std.c.close(fd);
31 var pos: usize = 0;
32 while (pos < buf.len) {
33 const n = std.posix.read(fd, buf[pos..]) catch return;
34 if (n == 0) return;
35 pos += n;
36 }
37}
38
39/// std.Random interface backed by this CSPRNG
40pub const csprng: std.Random = .{
41 .ptr = undefined,
42 .fillFn = struct {
43 fn f(_: *anyopaque, b: []u8) void {
44 fill(b);
45 }
46 }.f,
47};
packages/zig/vendor/zig-tls/src/record.zigdeleted+0-462
Changes to packages/zig/vendor/zig-tls/src/record.zig
@@ -1,462 +0,0 @@
1const std = ;
2const builtin = ;
3const assert = std.debug.assert;
4const mem = std.mem;
5const Io = std.Io;
6const proto = ;
7const max_ciphertext_len = .max_ciphertext_len;
8
9const log = std.log.scoped(.tls);
10
11pub const header_len = 5;
12
13pub const Record = struct {
14 content_type: proto.ContentType,
15 protocol_version: proto.Version = .tls_1_2,
16 buffer: []const u8,
17 header: []const u8,
18 payload: []const u8,
19
20 pub fn init(buffer: []const u8) Record {
21 return .{
22 .content_type = @fromBackingInt(@intCast(buffer[0])),
23 .protocol_version = @fromBackingInt(@intCast(mem.readInt(u16, buffer[1..3], .big))),
24 .buffer = buffer,
25 .header = buffer[0..header_len],
26 .payload = buffer[header_len..],
27 };
28 }
29
30 const Error = error{
31 EndOfStream, // clean close of the stream
32 ReadFailed, // all other stream close
33 InputBufferUndersize, // input can't fit tls record
34 TlsRecordOverflow, // incorrect tls record
35 };
36
37 pub fn read(rdr: *Io.Reader) Error!Record {
38 if (header_len > rdr.buffer.len) {
39 @branchHint(.cold);
40 return error.InputBufferUndersize;
41 }
42 const hdr = try rdr.peek(header_len);
43 const payload_len = mem.readInt(u16, hdr[3..5], .big);
44 if (payload_len > max_ciphertext_len) {
45 @branchHint(.cold);
46 return error.TlsRecordOverflow;
47 }
48 const record_len = header_len + payload_len;
49 if (record_len > rdr.buffer.len) {
50 @branchHint(.cold);
51 return error.InputBufferUndersize;
52 }
53 return .init(try rdr.take(record_len));
54 }
55
56 pub fn decoder(rdr: *Io.Reader) !Decoder {
57 const rec = try Record.read(rdr);
58 if (@backingInt(rec.protocol_version) != 0x0300 and
59 @backingInt(rec.protocol_version) != 0x0301 and
60 rec.protocol_version != .tls_1_2)
61 return error.TlsBadVersion;
62 return .{
63 .content_type = rec.content_type,
64 .payload = rec.payload,
65 };
66 }
67};
68
69pub const Decoder = struct {
70 content_type: proto.ContentType,
71 payload: []const u8,
72 idx: usize = 0,
73
74 pub fn init(content_type: proto.ContentType, payload: []const u8) Decoder {
75 return .{
76 .content_type = content_type,
77 .payload = payload,
78 };
79 }
80
81 pub fn decode(d: *Decoder, comptime T: type) !T {
82 switch (@typeInfo(T)) {
83 .int => |info| switch (info.bits) {
84 8 => {
85 try skip(d, 1);
86 return d.payload[d.idx - 1];
87 },
88 16 => {
89 try skip(d, 2);
90 const b0: u16 = d.payload[d.idx - 2];
91 const b1: u16 = d.payload[d.idx - 1];
92 return (b0 << 8) | b1;
93 },
94 24 => {
95 try skip(d, 3);
96 const b0: u24 = d.payload[d.idx - 3];
97 const b1: u24 = d.payload[d.idx - 2];
98 const b2: u24 = d.payload[d.idx - 1];
99 return (b0 << 16) | (b1 << 8) | b2;
100 },
101 32 => {
102 try skip(d, 4);
103 const b0: u32 = d.payload[d.idx - 4];
104 const b1: u32 = d.payload[d.idx - 3];
105 const b2: u32 = d.payload[d.idx - 2];
106 const b3: u32 = d.payload[d.idx - 1];
107 return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
108 },
109 else => @compileError("unsupported int type: " ++ @typeName(T)),
110 },
111 .@"enum" => |info| {
112 const int = try d.decode(info.tag_type);
113 if (info.mode == .exhaustive) @compileError("exhaustive enum cannot be used");
114 return @as(T, @fromBackingInt(@intCast(int)));
115 },
116 else => @compileError("unsupported type: " ++ @typeName(T)),
117 }
118 }
119
120 pub fn array(d: *Decoder, comptime len: usize) ![len]u8 {
121 try d.skip(len);
122 return d.payload[d.idx - len ..][0..len].*;
123 }
124
125 pub fn slice(d: *Decoder, len: usize) ![]const u8 {
126 try d.skip(len);
127 return d.payload[d.idx - len ..][0..len];
128 }
129
130 pub fn skip(d: *Decoder, amt: usize) !void {
131 if (d.idx + amt > d.payload.len) return error.TlsDecodeError;
132 d.idx += amt;
133 }
134
135 pub fn rest(d: Decoder) []const u8 {
136 return d.payload[d.idx..];
137 }
138
139 pub fn eof(d: Decoder) bool {
140 return d.idx == d.payload.len;
141 }
142
143 pub fn expectContentType(d: *Decoder, content_type: proto.ContentType) !void {
144 if (d.content_type == content_type) return;
145
146 switch (d.content_type) {
147 .alert => try d.raiseAlert(),
148 else => return error.TlsUnexpectedMessage,
149 }
150 }
151
152 pub fn raiseAlert(d: *Decoder) !void {
153 if (d.payload.len < 2) return error.TlsUnexpectedMessage;
154 try proto.Alert.parse(try d.array(2)).toError();
155 return error.TlsAlertCloseNotify;
156 }
157};
158
159const testing = std.testing;
160const data12 = ;
161const testu = ;
162const CipherSuite = .CipherSuite;
163
164test "record.read" {
165 // expected records in the data12.server_responses
166 const expected = [_]struct {
167 content_type: proto.ContentType,
168 payload_len: usize,
169 }{
170 .{ .content_type = .handshake, .payload_len = 49 },
171 .{ .content_type = .handshake, .payload_len = 815 },
172 .{ .content_type = .handshake, .payload_len = 300 },
173 .{ .content_type = .handshake, .payload_len = 4 },
174 .{ .content_type = .change_cipher_spec, .payload_len = 1 },
175 .{ .content_type = .handshake, .payload_len = 64 },
176 };
177 var reader: Io.Reader = .fixed(&data12.server_responses);
178
179 {
180 var n: usize = 0;
181 for (expected) |e| {
182 const rec = try Record.read(&reader);
183 try testing.expectEqual(e.content_type, rec.content_type);
184 try testing.expectEqual(e.payload_len, rec.payload.len);
185 try testing.expectEqual(.tls_1_2, rec.protocol_version);
186 try testing.expectEqual(e.payload_len + header_len, rec.buffer.len);
187
188 n += rec.payload.len + header_len;
189 try testing.expectEqual(n, reader.seek);
190 }
191 try testing.expectEqual(data12.server_responses.len, reader.seek);
192 try testing.expectError(error.EndOfStream, Record.read(&reader));
193 }
194
195 reader.seek = 0; // rewind
196 { // use smaller buffer to force fill, buffer must fit largest record 815 + 5 (header)
197 var buffer: [820]u8 = undefined;
198 var limited = reader.limited(.unlimited, &buffer);
199 for (expected) |e| {
200 const rec = try Record.read(&limited.interface);
201 try testing.expectEqual(e.content_type, rec.content_type);
202 try testing.expectEqual(e.payload_len, rec.payload.len);
203 try testing.expectEqual(.tls_1_2, rec.protocol_version);
204 }
205 try testing.expectError(error.EndOfStream, Record.read(&limited.interface));
206 }
207}
208
209test Decoder {
210 var reader: Io.Reader = .fixed(&data12.server_responses);
211 var d = (try Record.decoder(&reader));
212 try testing.expectEqual(.handshake, d.content_type);
213
214 try testing.expectEqual(.server_hello, try d.decode(proto.Handshake));
215 try testing.expectEqual(45, try d.decode(u24)); // length
216 try testing.expectEqual(.tls_1_2, try d.decode(proto.Version));
217 try testing.expectEqualStrings(
218 &testu.hexToBytes("707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f"),
219 try d.slice(32),
220 ); // server random
221 try testing.expectEqual(0, try d.decode(u8)); // session id len
222 try testing.expectEqual(.ECDHE_RSA_WITH_AES_128_CBC_SHA, try d.decode(CipherSuite));
223 try testing.expectEqual(0, try d.decode(u8)); // compression method
224 try testing.expectEqual(5, try d.decode(u16)); // extension length
225 try testing.expectEqual(5, d.rest().len);
226 try d.skip(5);
227 try testing.expect(d.eof());
228}
229
230pub const Writer = struct {
231 inner: Io.Writer,
232
233 pub fn initFromIo(io_w: *Io.Writer) Writer {
234 return .{ .inner = Io.Writer.fixed(io_w.unusedCapacitySlice()) };
235 }
236
237 pub fn init(buffer: []u8) Writer {
238 return .{ .inner = Io.Writer.fixed(buffer) };
239 }
240
241 fn ensureCapacity(w: Writer, n: usize) !void {
242 if (w.inner.unusedCapacityLen() < n)
243 return error.OutputBufferUndersize;
244 }
245
246 pub fn buffered(w: Writer) []const u8 {
247 return w.inner.buffered();
248 }
249
250 pub fn bytesWritten(w: Writer) usize {
251 return w.inner.end;
252 }
253
254 pub fn pos(w: Writer) usize {
255 return w.inner.end;
256 }
257
258 pub fn byte(w: *Writer, b: u8) !void {
259 try w.ensureCapacity(1);
260 try w.inner.writeByte(b);
261 }
262
263 pub fn slice(w: *Writer, bytes: []const u8) !void {
264 try w.ensureCapacity(bytes.len);
265 try w.inner.writeAll(bytes);
266 }
267
268 pub fn int(w: *Writer, comptime T: type, value: anytype) !void {
269 try w.ensureCapacity(@divExact(@typeInfo(T).int.bits, 8));
270 try w.inner.writeInt(T, @intCast(value), .big);
271 }
272
273 pub fn writableArray(w: *Writer, comptime len: usize) !*[len]u8 {
274 try w.ensureCapacity(len);
275 return try w.inner.writableArray(len);
276 }
277
278 pub fn enumValue(w: *Writer, value: anytype) !void {
279 const i = @backingInt(value);
280 try w.int(@TypeOf(i), i);
281 }
282
283 pub fn enumList(w: *Writer, comptime E: type, tags: []const E) !void {
284 assert(@sizeOf(E) == 2);
285 try w.int(u16, tags.len * 2);
286 for (tags) |t| {
287 try w.enumValue(t);
288 }
289 }
290
291 /// Default extension writer, writes extension type and list of tags
292 pub fn extension(w: *Writer, ex: proto.Extension, tags: anytype) !void {
293 try w.enumValue(ex);
294 if (ex == .supported_versions) {
295 try w.int(u16, tags.len * 2 + 1);
296 try w.int(u8, tags.len * 2);
297 } else if (ex == .psk_key_exchange_modes) {
298 try w.int(u16, tags.len + 1);
299 try w.int(u8, tags.len);
300 } else {
301 try w.int(u16, tags.len * 2 + 2);
302 try w.int(u16, tags.len * 2);
303 }
304 for (tags) |t| {
305 try w.enumValue(t);
306 }
307 }
308
309 /// Key share extension
310 pub fn keyShare(w: *Writer, named_groups: []const proto.NamedGroup, keys: []const []const u8) !void {
311 if (keys.len == 0) return;
312 assert(named_groups.len == keys.len);
313 try w.enumValue(proto.Extension.key_share);
314 var l: usize = 0;
315 for (keys) |key| {
316 l += key.len + 4;
317 }
318 try w.int(u16, l + 2);
319 try w.int(u16, l);
320 for (named_groups, 0..) |ng, i| {
321 const key = keys[i];
322 try w.enumValue(ng);
323 try w.int(u16, key.len);
324 try w.inner.writeAll(key);
325 }
326 }
327
328 /// Server name extension
329 pub fn serverName(w: *Writer, host: []const u8) !void {
330 const host_len: u16 = @intCast(host.len);
331 try w.enumValue(proto.Extension.server_name);
332 try w.int(u16, host_len + 5); // byte length of extension payload
333 try w.int(u16, host_len + 3); // server_name_list byte count
334 try w.inner.writeByte(0); // name type
335 try w.int(u16, host_len);
336 try w.inner.writeAll(host);
337 }
338
339 /// Writes header of the pre shared key extension, without binders
340 pub fn preSharedKey(
341 w: *Writer,
342 identity: []const u8,
343 obfuscated_age: u32,
344 binder_len: u8,
345 ) !void {
346 try w.enumValue(proto.Extension.pre_shared_key);
347 try w.int(u16, identity.len + binder_len + 4 + 2 + 2);
348 try w.int(u16, identity.len + 4 + 2);
349 try w.int(u16, identity.len);
350 try w.inner.writeAll(identity);
351 try w.int(u32, obfuscated_age);
352 }
353
354 /// Writes the rest of the pre shared keys extension
355 pub fn preSharedKeyBinder(w: *Writer, binder: []const u8) !void {
356 try w.int(u16, binder.len + 1);
357 try w.int(u8, binder.len);
358 try w.inner.writeAll(binder);
359 }
360
361 /// tls record
362 pub fn record(w: *Writer, content_type: proto.ContentType, payload: []const u8) !void {
363 try w.enumValue(content_type);
364 try w.enumValue(proto.Version.tls_1_2);
365 try w.int(u16, payload.len);
366 if (w.unused().ptr == payload.ptr) {
367 w.inner.advance(payload.len);
368 } else {
369 try w.slice(payload);
370 }
371 }
372
373 pub fn recordHeader(w: *Writer, content_type: proto.ContentType, payload_len: usize) !void {
374 try w.enumValue(content_type);
375 try w.enumValue(proto.Version.tls_1_2);
376 try w.int(u16, payload_len);
377 }
378
379 /// tls handshake record
380 pub fn handshakeRecord(w: *Writer, handshake_type: proto.Handshake, payload: []const u8) !void {
381 try w.enumValue(handshake_type);
382 try w.int(u24, payload.len);
383 if (w.unused().ptr == payload.ptr) {
384 w.inner.advance(payload.len);
385 } else {
386 try w.slice(payload);
387 }
388 }
389
390 pub fn handshakeRecordHeader(w: *Writer, handshake_type: proto.Handshake, payload_len: usize) !void {
391 try w.enumValue(handshake_type);
392 try w.int(u24, payload_len);
393 }
394
395 pub fn unused(w: *Writer) []u8 {
396 return w.inner.unusedCapacitySlice();
397 }
398
399 pub fn advance(w: *Writer, n: usize) void {
400 w.inner.advance(n);
401 }
402
403 /// Skip `n` bytes from current position and return position before skip.
404 /// Used with writerAt to later return to that point and write skipped
405 /// bytes.
406 pub fn skip(w: *Writer, n: usize) !usize {
407 try w.ensureCapacity(n);
408 const p = w.pos();
409 w.inner.advance(n);
410 return p;
411 }
412
413 /// Returns writer at some previous position
414 /// Parent buffer position is not changed.
415 pub fn writerAt(w: *Writer, p: usize) Writer {
416 assert(p < w.inner.end);
417 return Writer{ .inner = Io.Writer.fixed(w.inner.buffered()[p..]) };
418 }
419
420 /// Returns new writer in unused buffer part advancing `n` bytes.
421 /// Parent buffer position is not changed.
422 pub fn writerAdvance(w: *Writer, n: usize) !Writer {
423 try w.ensureCapacity(n);
424 return Writer{ .inner = Io.Writer.fixed(w.inner.unusedCapacitySlice()[n..]) };
425 }
426};
427
428test "Writer" {
429 var buf: [16]u8 = undefined;
430 var w = Writer{ .inner = Io.Writer.fixed(&buf) };
431
432 try w.slice("ab");
433 try w.enumValue(proto.Curve.named_curve);
434 try w.enumValue(proto.NamedGroup.x25519);
435 try w.int(u16, 0x1234);
436 try testing.expectEqualSlices(u8, &[_]u8{ 'a', 'b', 0x03, 0x00, 0x1d, 0x12, 0x34 }, w.buffered());
437}
438
439fn int2(int: u16) [2]u8 {
440 var arr: [2]u8 = undefined;
441 std.mem.writeInt(u16, &arr, int, .big);
442 return arr;
443}
444
445fn int3(int: u24) [3]u8 {
446 var arr: [3]u8 = undefined;
447 std.mem.writeInt(u24, &arr, int, .big);
448 return arr;
449}
450
451pub fn header(content_type: proto.ContentType, payload_len: usize) [header_len]u8 {
452 return [1]u8{@backingInt(content_type)} ++
453 int2(@backingInt(proto.Version.tls_1_2)) ++
454 int2(@intCast(payload_len));
455}
456
457pub fn handshakeHeader(handshake_type: proto.Handshake, payload_len: usize) [4]u8 {
458 var ret: [4]u8 = undefined;
459 ret[0] = @backingInt(handshake_type);
460 std.mem.writeInt(u24, ret[1..4], @intCast(payload_len), .big);
461 return ret;
462}
packages/zig/vendor/zig-tls/src/root.zigdeleted+0-208
Changes to packages/zig/vendor/zig-tls/src/root.zig
@@ -1,208 +0,0 @@
1const std = ;
2const assert = std.debug.assert;
3const Io = std.Io;
4
5pub const max_ciphertext_record_len = .max_ciphertext_record_len;
6
7/// Buffer of this size will fit any tls ciphertext record sent by other side.
8/// To decrytp we need full record, smalled buffer will not work in general
9/// case. Bigger can be used for performance reason.
10pub const input_buffer_len = max_ciphertext_record_len; // 16645 bytes
11
12/// Needed output buffer during handshake is the size of the tls hello message,
13/// which is (when client authentication is not used) ~1600 bytes. After
14/// handshake it limits how big tls record can be produced. This suggested value
15/// can hold max ciphertext record produced with this implementation.
16pub const output_buffer_len = .max_encrypted_record_len; // 16469 bytes
17
18pub const Connection = .Connection;
19pub const rsa = ;
20
21const handshake = struct {
22 const Client = .Handshake;
23 const Server = .Handshake;
24};
25
26/// Upgrades existing stream to the tls connection by the client tls handshake.
27pub inline fn clientFromStream(stream: anytype, opt: config.Client) !Connection {
28 const input, const output = streamToRaderWriter(stream);
29 return try client(input, output, opt);
30}
31
32pub fn client(input: *Io.Reader, output: *Io.Writer, opt: config.Client) !Connection {
33 assert(input.buffer.len >= input_buffer_len);
34 assert(output.buffer.len >= 2048); // client hello requires: 1572 + opt.host.len
35
36 var hc: handshake.Client = .{ .input = input, .output = output };
37 const cipher, const session_resumption_secret_idx = try hc.handshake(opt);
38 return .{
39 .cipher = cipher,
40 .input = input,
41 .output = output,
42 .session_resumption_secret_idx = session_resumption_secret_idx,
43 .session_resumption = opt.session_resumption,
44 };
45}
46
47/// Upgrades existing stream to the tls connection by the server side tls handshake.
48pub inline fn serverFromStream(stream: anytype, opt: config.Server) !Connection {
49 const input, const output = streamToRaderWriter(stream);
50 return try server(input, output, opt);
51}
52
53pub fn server(input: *Io.Reader, output: *Io.Writer, opt: config.Server) !Connection {
54 var hs: handshake.Server = .{ .input = input, .output = output };
55 const cipher = try hs.handshake(opt);
56 return .{
57 .cipher = cipher,
58 .input = input,
59 .output = output,
60 };
61}
62
63/// With default buffer sizes
64inline fn streamToRaderWriter(stream: anytype) struct { *Io.Reader, *Io.Writer } {
65 var input_buf: [input_buffer_len]u8 = undefined;
66 var output_buf: [output_buffer_len]u8 = undefined;
67 var reader = stream.reader(&input_buf);
68 var writer = stream.writer(&output_buf);
69 const input = if (@hasField(@TypeOf(reader), "interface")) &reader.interface else reader.interface();
70 const output = &writer.interface;
71 return .{ input, output };
72}
73
74pub const Cipher = .Cipher;
75pub const config = struct {
76 pub const proto = ;
77 const common = ;
78
79 pub const CipherSuite = .CipherSuite;
80 pub const PrivateKey = ;
81 pub const NamedGroup = proto.NamedGroup;
82 pub const Version = proto.Version;
83 pub const ContentType = proto.ContentType;
84 pub const Record = .Record;
85 pub const cert = common.cert;
86 pub const CertKeyPair = common.CertKeyPair;
87
88 pub const cipher_suites = .cipher_suites;
89 pub const key_log = ;
90
91 pub const Client = .Options;
92 pub const Server = .Options;
93};
94
95/// Non-blocking client/server handshake and connection. Handshake produces
96/// cipher used in connection to encrypt data for sending and decrypt received
97/// data.
98pub const nonblock = struct {
99 pub const Client = .NonBlock;
100 pub const Server = .NonBlock;
101 pub const Connection = .NonBlock;
102};
103
104pub const Ktls = ;
105
106test "nonblock handshake and connection" {
107 const testing = .testing;
108
109 // data from server to the client
110 var sc_buf: [max_ciphertext_record_len]u8 = undefined;
111 // data from client to the server
112 var cs_buf: [max_ciphertext_record_len]u8 = undefined;
113
114 // client/server handshake produces ciphers
115 const cli_cipher, const srv_cipher = brk: {
116 var cli = nonblock.Client.init(.{
117 .root_ca = .empty,
118 .host = &.{},
119 .insecure_skip_verify = true,
120 });
121 var srv = nonblock.Server.init(.{ .auth = null });
122
123 // client flight1; client hello is in buf1
124 var cr = try cli.run(&sc_buf, &cs_buf);
125 try testing.expectEqual(0, cr.recv_pos);
126 try testing.expect(cr.send.len > 0);
127 try testing.expect(!cli.done());
128
129 { // short read, partial buffer received
130 for (0..cr.send_pos) |i| {
131 const sr = try srv.run(cs_buf[0..i], &sc_buf);
132 try testing.expectEqual(0, sr.recv_pos);
133 try testing.expectEqual(0, sr.send_pos);
134 }
135 }
136
137 // server flight 1; server parses client hello from buf2 and writes server hello into buf1
138 var sr = try srv.run(&cs_buf, &sc_buf);
139 try testing.expectEqual(sr.recv_pos, cr.send_pos);
140 try testing.expect(sr.send.len > 0);
141 try testing.expect(!srv.done());
142
143 { // short read, partial buffer received
144 for (0..sr.send_pos) |i| {
145 cr = try cli.run(sc_buf[0..i], &cs_buf);
146 try testing.expectEqual(0, cr.recv_pos);
147 try testing.expectEqual(0, cr.send_pos);
148 }
149 }
150
151 // client flight 2; client parses server hello from buf1 and writes finished into buf2
152 cr = try cli.run(&sc_buf, &cs_buf);
153 try testing.expectEqual(sr.send_pos, cr.recv_pos);
154 try testing.expect(cr.send.len > 0);
155 try testing.expect(cli.done()); // client is done
156 try testing.expect(cli.cipher() != null);
157
158 // server parses client finished
159 sr = try srv.run(&cs_buf, &sc_buf);
160 try testing.expectEqual(sr.recv_pos, cr.send_pos);
161 try testing.expectEqual(0, sr.send.len);
162 try testing.expect(srv.done()); // server is done
163 try testing.expect(srv.cipher() != null);
164
165 break :brk .{ cli.cipher().?, srv.cipher().? };
166 };
167 { // use ciphers in connection
168 var cli = nonblock.Connection.init(cli_cipher);
169 var srv = nonblock.Connection.init(srv_cipher);
170
171 const cleartext = "Lorem ipsum dolor sit amet";
172 { // client to server
173 const e = try cli.encrypt(cleartext, &cs_buf);
174 try testing.expectEqual(cleartext.len, e.cleartext_pos);
175 try testing.expect(e.ciphertext.len > cleartext.len);
176 try testing.expect(e.unused_cleartext.len == 0);
177
178 const d = try srv.decrypt(e.ciphertext, &sc_buf);
179 try testing.expectEqualSlices(u8, cleartext, d.cleartext);
180 try testing.expectEqual(e.ciphertext.len, d.ciphertext_pos);
181 try testing.expectEqual(0, d.unused_ciphertext.len);
182 }
183 { // server to client
184 const e = try srv.encrypt(cleartext, &sc_buf);
185 const d = try cli.decrypt(e.ciphertext, &cs_buf);
186 try testing.expectEqualSlices(u8, cleartext, d.cleartext);
187 }
188 { // server sends close
189 const close_buf = try srv.close(&sc_buf);
190 const d = try cli.decrypt(close_buf, &cs_buf);
191 try testing.expectEqual(close_buf.len, d.ciphertext_pos);
192 try testing.expectEqual(0, d.unused_ciphertext.len);
193 try testing.expect(d.closed);
194 }
195 }
196}
197
198test {
199 _ = ;
200 _ = ;
201 _ = ;
202
203 _ = ;
204 _ = ;
205 _ = ;
206 _ = ;
207 _ = ;
208}
packages/zig/vendor/zig-tls/src/rsa/der.zigdeleted+0-467
Changes to packages/zig/vendor/zig-tls/src/rsa/der.zig
@@ -1,467 +0,0 @@
1//! An encoding of ASN.1.
2//!
3//! Distinguised Encoding Rules as defined in X.690 and X.691.
4//!
5//! A version of Basic Encoding Rules (BER) where there is exactly ONE way to
6//! represent non-constructed elements. This is useful for cryptographic signatures.
7//!
8//! Currently an implementation detail of the standard library not fit for public
9//! use since it's missing an encoder.
10
11const std = ;
12const builtin = ;
13
14pub const Index = usize;
15const log = std.log.scoped(.der);
16
17/// A secure DER parser that:
18/// - Does NOT read memory outside `bytes`.
19/// - Does NOT return elements with slices outside `bytes`.
20/// - Errors on values that do NOT follow DER rules.
21/// - Lengths that could be represented in a shorter form.
22/// - Booleans that are not 0xff or 0x00.
23pub const Parser = struct {
24 bytes: []const u8,
25 index: Index = 0,
26
27 pub const Error = Element.Error || error{
28 UnexpectedElement,
29 InvalidIntegerEncoding,
30 Overflow,
31 NonCanonical,
32 InvalidBitString,
33 };
34
35 pub fn expectBool(self: *Parser) Error!bool {
36 const ele = try self.expect(.universal, false, .boolean);
37 if (ele.slice.len() != 1) return error.InvalidBool;
38
39 return switch (self.view(ele)[0]) {
40 0x00 => false,
41 0xff => true,
42 else => error.InvalidBool,
43 };
44 }
45
46 pub fn expectBitstring(self: *Parser) Error!BitString {
47 const ele = try self.expect(.universal, false, .bitstring);
48 const bytes = self.view(ele);
49 const right_padding = bytes[0];
50 if (right_padding >= 8) return error.InvalidBitString;
51 return .{
52 .bytes = bytes[1..],
53 .right_padding = @intCast(right_padding),
54 };
55 }
56
57 // TODO: return high resolution date time type instead of epoch seconds
58 pub fn expectDateTime(self: *Parser) Error!i64 {
59 const ele = try self.expect(.universal, false, null);
60 const bytes = self.view(ele);
61 switch (ele.identifier.tag) {
62 .utc_time => {
63 // Example: "YYMMDD000000Z"
64 if (bytes.len != 13)
65 return error.InvalidDateTime;
66 if (bytes[12] != 'Z')
67 return error.InvalidDateTime;
68
69 var date: Date = undefined;
70 date.year = try parseTimeDigits(bytes[0..2], 0, 99);
71 date.year += if (date.year >= 50) 1900 else 2000;
72 date.month = try parseTimeDigits(bytes[2..4], 1, 12);
73 date.day = try parseTimeDigits(bytes[4..6], 1, 31);
74 const time = try parseTime(bytes[6..12]);
75
76 return date.toEpochSeconds() + time.toSec();
77 },
78 .generalized_time => {
79 // Examples:
80 // "19920622123421Z"
81 // "19920722132100.3Z"
82 if (bytes.len < 15)
83 return error.InvalidDateTime;
84
85 var date: Date = undefined;
86 date.year = try parseYear4(bytes[0..4]);
87 date.month = try parseTimeDigits(bytes[4..6], 1, 12);
88 date.day = try parseTimeDigits(bytes[6..8], 1, 31);
89 const time = try parseTime(bytes[8..14]);
90
91 return date.toEpochSeconds() + time.toSec();
92 },
93 else => return error.InvalidDateTime,
94 }
95 }
96
97 pub fn expectOid(self: *Parser) Error![]const u8 {
98 const oid = try self.expect(.universal, false, .object_identifier);
99 return self.view(oid);
100 }
101
102 pub fn expectEnum(self: *Parser, comptime Enum: type) Error!Enum {
103 const oid = try self.expectOid();
104 return Enum.oids.get(oid) orelse {
105 if (builtin.mode == .Debug) {
106 var buf: [256]u8 = undefined;
107 var stream = std.io.fixedBufferStream(&buf);
108 try .decode(oid, stream.writer());
109 log.warn("unknown oid {s} for enum {s}\n", .{ stream.getWritten(), @typeName(Enum) });
110 }
111 return error.UnknownObjectId;
112 };
113 }
114
115 pub fn expectInt(self: *Parser, comptime T: type) Error!T {
116 const ele = try self.expectPrimitive(.integer);
117 const bytes = self.view(ele);
118
119 const info = @typeInfo(T);
120 if (info != .int) @compileError(@typeName(T) ++ " is not an int type");
121 const Shift = std.math.Log2Int(u8);
122
123 var result: std.meta.Int(.unsigned, info.int.bits) = 0;
124 for (bytes, 0..) |b, index| {
125 const shifted = @shlWithOverflow(b, @as(Shift, @intCast(index * 8)));
126 if (shifted[1] == 1) return error.Overflow;
127
128 result |= shifted[0];
129 }
130
131 return @bitCast(result);
132 }
133
134 pub fn expectString(self: *Parser, allowed: std.EnumSet(String.Tag)) Error!String {
135 const ele = try self.expect(.universal, false, null);
136 switch (ele.identifier.tag) {
137 inline .string_utf8,
138 .string_numeric,
139 .string_printable,
140 .string_teletex,
141 .string_videotex,
142 .string_ia5,
143 .string_visible,
144 .string_universal,
145 .string_bmp,
146 => |t| {
147 const tagname = @tagName(t)["string_".len..];
148 const tag = std.meta.stringToEnum(String.Tag, tagname) orelse unreachable;
149 if (allowed.contains(tag)) {
150 return String{ .tag = tag, .data = self.view(ele) };
151 }
152 },
153 else => {},
154 }
155 return error.UnexpectedElement;
156 }
157
158 pub fn expectPrimitive(self: *Parser, tag: ?Identifier.Tag) Error!Element {
159 var elem = try self.expect(.universal, false, tag);
160 if (tag == .integer and elem.slice.len() > 0) {
161 if (self.view(elem)[0] == 0) elem.slice.start += 1;
162 if (elem.slice.len() > 0 and self.view(elem)[0] == 0) return error.InvalidIntegerEncoding;
163 }
164 return elem;
165 }
166
167 /// Remember to call `expectEnd`
168 pub fn expectSequence(self: *Parser) Error!Element {
169 return try self.expect(.universal, true, .sequence);
170 }
171
172 /// Remember to call `expectEnd`
173 pub fn expectSequenceOf(self: *Parser) Error!Element {
174 return try self.expect(.universal, true, .sequence_of);
175 }
176
177 pub fn expectEnd(self: *Parser, val: usize) Error!void {
178 if (self.index != val) return error.NonCanonical; // either forgot to parse end OR an attacker
179 }
180
181 pub fn expect(
182 self: *Parser,
183 class: ?Identifier.Class,
184 constructed: ?bool,
185 tag: ?Identifier.Tag,
186 ) Error!Element {
187 if (self.index >= self.bytes.len) return error.EndOfStream;
188
189 const res = try Element.init(self.bytes, self.index);
190 if (tag) |e| {
191 if (res.identifier.tag != e) return error.UnexpectedElement;
192 }
193 if (constructed) |e| {
194 if (res.identifier.constructed != e) return error.UnexpectedElement;
195 }
196 if (class) |e| {
197 if (res.identifier.class != e) return error.UnexpectedElement;
198 }
199 self.index = if (res.identifier.constructed) res.slice.start else res.slice.end;
200 return res;
201 }
202
203 pub fn view(self: Parser, elem: Element) []const u8 {
204 return elem.slice.view(self.bytes);
205 }
206
207 pub fn seek(self: *Parser, index: usize) void {
208 self.index = index;
209 }
210
211 pub fn eof(self: *Parser) bool {
212 return self.index == self.bytes.len;
213 }
214};
215
216pub const Element = struct {
217 identifier: Identifier,
218 slice: Slice,
219
220 pub const Slice = struct {
221 start: Index,
222 end: Index,
223
224 pub fn len(self: Slice) Index {
225 return self.end - self.start;
226 }
227
228 pub fn view(self: Slice, bytes: []const u8) []const u8 {
229 return bytes[self.start..self.end];
230 }
231 };
232
233 pub const Error = error{ InvalidLength, EndOfStream, ReadFailed };
234
235 pub fn init(bytes: []const u8, index: Index) Error!Element {
236 var reader = std.Io.Reader.fixed(bytes[index..]);
237
238 const identifier = @as(Identifier, @bitCast(try reader.takeByte()));
239 const size_or_len_size = try reader.takeByte();
240
241 var start = index + 2;
242 // short form between 0-127
243 if (size_or_len_size < 128) {
244 const end = start + size_or_len_size;
245 if (end > bytes.len) return error.InvalidLength;
246
247 return .{ .identifier = identifier, .slice = .{ .start = start, .end = end } };
248 }
249
250 // long form between 0 and std.math.maxInt(u1024)
251 const len_size: u7 = @truncate(size_or_len_size);
252 start += len_size;
253 if (len_size > @sizeOf(Index)) return error.InvalidLength;
254 const len = try reader.takeVarInt(Index, .big, len_size);
255 if (len < 128) return error.InvalidLength; // should have used short form
256
257 const end = std.math.add(Index, start, len) catch return error.InvalidLength;
258 if (end > bytes.len) return error.InvalidLength;
259
260 return .{ .identifier = identifier, .slice = .{ .start = start, .end = end } };
261 }
262};
263
264test Element {
265 const short_form = [_]u8{ 0x30, 0x03, 0x02, 0x01, 0x09 };
266 try std.testing.expectEqual(Element{
267 .identifier = Identifier{ .tag = .sequence, .constructed = true, .class = .universal },
268 .slice = .{ .start = 2, .end = short_form.len },
269 }, Element.init(&short_form, 0));
270
271 const long_form = [_]u8{ 0x30, 129, 129 } ++ @as([129]u8, @splat(0));
272 try std.testing.expectEqual(Element{
273 .identifier = Identifier{ .tag = .sequence, .constructed = true, .class = .universal },
274 .slice = .{ .start = 3, .end = long_form.len },
275 }, Element.init(&long_form, 0));
276}
277
278test "parser.expectInt" {
279 const one = [_]u8{ 2, 1, 1 };
280 var parser = Parser{ .bytes = &one };
281 try std.testing.expectEqual(@as(u8, 1), try parser.expectInt(u8));
282}
283
284pub const Identifier = packed struct(u8) {
285 tag: Tag,
286 constructed: bool,
287 class: Class,
288
289 pub const Class = enum(u2) {
290 universal,
291 application,
292 context_specific,
293 private,
294 };
295
296 // https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/asn1-tags.html
297 pub const Tag = enum(u5) {
298 boolean = 1,
299 integer = 2,
300 bitstring = 3,
301 octetstring = 4,
302 null = 5,
303 object_identifier = 6,
304 real = 9,
305 enumerated = 10,
306 string_utf8 = 12,
307 sequence = 16,
308 sequence_of = 17,
309 string_numeric = 18,
310 string_printable = 19,
311 string_teletex = 20,
312 string_videotex = 21,
313 string_ia5 = 22,
314 utc_time = 23,
315 generalized_time = 24,
316 string_visible = 26,
317 string_universal = 28,
318 string_bmp = 30,
319 _,
320 };
321};
322
323pub const BitString = struct {
324 bytes: []const u8,
325 right_padding: u3,
326
327 pub fn bitLen(self: BitString) usize {
328 return self.bytes.len * 8 + self.right_padding;
329 }
330};
331
332pub const String = struct {
333 tag: Tag,
334 data: []const u8,
335
336 pub const Tag = enum {
337 /// Blessed.
338 utf8,
339 /// us-ascii ([-][0-9][eE][.])*
340 numeric,
341 /// us-ascii ([A-Z][a-z][0-9][.?!,][ \t])*
342 printable,
343 /// iso-8859-1 with escaping into different character sets.
344 /// Cursed.
345 teletex,
346 /// iso-8859-1
347 videotex,
348 /// us-ascii first 128 characters.
349 ia5,
350 /// us-ascii without control characters.
351 visible,
352 /// utf-32-be
353 universal,
354 /// utf-16-be
355 bmp,
356 };
357
358 pub const all = [_]Tag{
359 .utf8,
360 .numeric,
361 .printable,
362 .teletex,
363 .videotex,
364 .ia5,
365 .visible,
366 .universal,
367 .bmp,
368 };
369};
370
371const Date = struct {
372 year: Year,
373 month: u8,
374 day: u8,
375
376 const Year = std.time.epoch.Year;
377
378 fn toEpochSeconds(date: Date) i64 {
379 // Euclidean Affine Transform by Cassio and Neri.
380 // Shift and correction constants for 1970-01-01.
381 const s = 82;
382 const K = 719468 + 146097 * s;
383 const L = 400 * s;
384
385 const Y_G: u32 = date.year;
386 const M_G: u32 = date.month;
387 const D_G: u32 = date.day;
388 // Map to computational calendar.
389 const J: u32 = if (M_G <= 2) 1 else 0;
390 const Y: u32 = Y_G + L - J;
391 const M: u32 = if (J != 0) M_G + 12 else M_G;
392 const D: u32 = D_G - 1;
393 const C: u32 = Y / 100;
394
395 // Rata die.
396 const y_star: u32 = 1461 * Y / 4 - C + C / 4;
397 const m_star: u32 = (979 * M - 2919) / 32;
398 const N: u32 = y_star + m_star + D;
399 const days: i32 = @intCast(N - K);
400
401 return @as(i64, days) * std.time.epoch.secs_per_day;
402 }
403};
404
405const Time = struct {
406 hour: std.math.IntFittingRange(0, 24),
407 minute: std.math.IntFittingRange(0, 60),
408 second: std.math.IntFittingRange(0, 60),
409
410 fn toSec(t: Time) i64 {
411 var sec: i64 = 0;
412 sec += @as(i64, t.hour) * 60 * 60;
413 sec += @as(i64, t.minute) * 60;
414 sec += t.second;
415 return sec;
416 }
417};
418
419fn parseTimeDigits(
420 text: *const [2]u8,
421 min: comptime_int,
422 max: comptime_int,
423) !std.math.IntFittingRange(min, max) {
424 const result = std.fmt.parseInt(std.math.IntFittingRange(min, max), text, 10) catch
425 return error.InvalidTime;
426 if (result < min) return error.InvalidTime;
427 if (result > max) return error.InvalidTime;
428 return result;
429}
430
431test parseTimeDigits {
432 const expectEqual = std.testing.expectEqual;
433 try expectEqual(@as(u8, 0), try parseTimeDigits("00", 0, 99));
434 try expectEqual(@as(u8, 99), try parseTimeDigits("99", 0, 99));
435 try expectEqual(@as(u8, 42), try parseTimeDigits("42", 0, 99));
436
437 const expectError = std.testing.expectError;
438 try expectError(error.InvalidTime, parseTimeDigits("13", 1, 12));
439 try expectError(error.InvalidTime, parseTimeDigits("00", 1, 12));
440 try expectError(error.InvalidTime, parseTimeDigits("Di", 0, 99));
441}
442
443fn parseYear4(text: *const [4]u8) !Date.Year {
444 const result = std.fmt.parseInt(Date.Year, text, 10) catch return error.InvalidYear;
445 if (result > 9999) return error.InvalidYear;
446 return result;
447}
448
449test parseYear4 {
450 const expectEqual = std.testing.expectEqual;
451 try expectEqual(@as(Date.Year, 0), try parseYear4("0000"));
452 try expectEqual(@as(Date.Year, 9999), try parseYear4("9999"));
453 try expectEqual(@as(Date.Year, 1988), try parseYear4("1988"));
454
455 const expectError = std.testing.expectError;
456 try expectError(error.InvalidYear, parseYear4("999b"));
457 try expectError(error.InvalidYear, parseYear4("crap"));
458 try expectError(error.InvalidYear, parseYear4("r:bQ"));
459}
460
461fn parseTime(bytes: *const [6]u8) !Time {
462 return .{
463 .hour = try parseTimeDigits(bytes[0..2], 0, 23),
464 .minute = try parseTimeDigits(bytes[2..4], 0, 59),
465 .second = try parseTimeDigits(bytes[4..6], 0, 59),
466 };
467}
packages/zig/vendor/zig-tls/src/rsa/oid.zigdeleted+0-132
Changes to packages/zig/vendor/zig-tls/src/rsa/oid.zig
@@ -1,132 +0,0 @@
1//! Developed by ITU-U and ISO/IEC for naming objects. Used in DER.
2//!
3//! This implementation supports any number of `u32` arcs.
4
5const Arc = u32;
6const encoding_base = 128;
7
8/// Returns encoded length.
9pub fn encodeLen(dot_notation: []const u8) !usize {
10 var split = std.mem.splitScalar(u8, dot_notation, '.');
11 if (split.next() == null) return 0;
12 if (split.next() == null) return 1;
13
14 var res: usize = 1;
15 while (split.next()) |s| {
16 const parsed = try std.fmt.parseUnsigned(Arc, s, 10);
17 const n_bytes = if (parsed == 0) 0 else std.math.log(Arc, encoding_base, parsed);
18
19 res += n_bytes;
20 res += 1;
21 }
22
23 return res;
24}
25
26pub const EncodeError = std.fmt.ParseIntError || error{
27 MissingPrefix,
28 BufferTooSmall,
29};
30
31pub fn encode(dot_notation: []const u8, buf: []u8) EncodeError![]const u8 {
32 if (buf.len < try encodeLen(dot_notation)) return error.BufferTooSmall;
33
34 var split = std.mem.splitScalar(u8, dot_notation, '.');
35 const first_str = split.next() orelse return error.MissingPrefix;
36 const second_str = split.next() orelse return error.MissingPrefix;
37
38 const first = try std.fmt.parseInt(u8, first_str, 10);
39 const second = try std.fmt.parseInt(u8, second_str, 10);
40
41 buf[0] = first * 40 + second;
42
43 var i: usize = 1;
44 while (split.next()) |s| {
45 var parsed = try std.fmt.parseUnsigned(Arc, s, 10);
46 const n_bytes = if (parsed == 0) 0 else std.math.log(Arc, encoding_base, parsed);
47
48 for (0..n_bytes) |j| {
49 const place = std.math.pow(Arc, encoding_base, n_bytes - @as(Arc, @intCast(j)));
50 const digit: u8 = @intCast(@divFloor(parsed, place));
51
52 buf[i] = digit | 0x80;
53 parsed -= digit * place;
54
55 i += 1;
56 }
57 buf[i] = @intCast(parsed);
58 i += 1;
59 }
60
61 return buf[0..i];
62}
63
64pub fn decode(encoded: []const u8, writer: anytype) @TypeOf(writer).Error!void {
65 const first = @divTrunc(encoded[0], 40);
66 const second = encoded[0] - first * 40;
67 try writer.print("{d}.{d}", .{ first, second });
68
69 var i: usize = 1;
70 while (i != encoded.len) {
71 const n_bytes: usize = brk: {
72 var res: usize = 1;
73 var j: usize = i;
74 while (encoded[j] & 0x80 != 0) {
75 res += 1;
76 j += 1;
77 }
78 break :brk res;
79 };
80
81 var n: usize = 0;
82 for (0..n_bytes) |j| {
83 const place = std.math.pow(usize, encoding_base, n_bytes - j - 1);
84 n += place * (encoded[i] & 0b01111111);
85 i += 1;
86 }
87 try writer.print(".{d}", .{n});
88 }
89}
90
91pub fn encodeComptime(comptime dot_notation: []const u8) [encodeLen(dot_notation) catch unreachable]u8 {
92 @setEvalBranchQuota(10_000);
93 var buf: [encodeLen(dot_notation) catch unreachable]u8 = undefined;
94 _ = encode(dot_notation, &buf) catch unreachable;
95 return buf;
96}
97
98const std = ;
99
100fn testOid(expected_encoded: []const u8, expected_dot_notation: []const u8) !void {
101 var buf: [256]u8 = undefined;
102 const encoded = try encode(expected_dot_notation, &buf);
103 try std.testing.expectEqualSlices(u8, expected_encoded, encoded);
104
105 var stream = std.io.fixedBufferStream(&buf);
106 try decode(expected_encoded, stream.writer());
107 try std.testing.expectEqualStrings(expected_dot_notation, stream.getWritten());
108}
109
110test "encode and decode" {
111 // https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-object-identifier
112 try testOid(
113 &[_]u8{ 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x14 },
114 "1.3.6.1.4.1.311.21.20",
115 );
116 // https://luca.ntop.org/Teaching/Appunti/asn1.html
117 try testOid(&[_]u8{ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d }, "1.2.840.113549");
118 // https://www.sysadmins.lv/blog-en/how-to-encode-object-identifier-to-an-asn1-der-encoded-string.aspx
119 try testOid(&[_]u8{ 0x2a, 0x86, 0x8d, 0x20 }, "1.2.100000");
120 try testOid(
121 &[_]u8{ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b },
122 "1.2.840.113549.1.1.11",
123 );
124 try testOid(&[_]u8{ 0x2b, 0x65, 0x70 }, "1.3.101.112");
125}
126
127test encodeComptime {
128 try std.testing.expectEqual(
129 [_]u8{ 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x14 },
130 encodeComptime("1.3.6.1.4.1.311.21.20"),
131 );
132}
packages/zig/vendor/zig-tls/src/rsa/rsa.zigdeleted+0-900
Changes to packages/zig/vendor/zig-tls/src/rsa/rsa.zig
@@ -1,900 +0,0 @@
1//! RFC8017: Public Key Cryptography Standards #1 v2.2 (PKCS1)
2const std = ;
3const der = ;
4const ff = std.crypto.ff;
5const rng = ;
6
7pub const max_modulus_bits = 4096;
8const max_modulus_len = max_modulus_bits / 8;
9
10const Modulus = std.crypto.ff.Modulus(max_modulus_bits);
11const Fe = Modulus.Fe;
12
13pub const ValueError = error{
14 Modulus,
15 Exponent,
16};
17
18pub const PublicKey = struct {
19 /// `n`
20 modulus: Modulus,
21 /// `e`
22 public_exponent: Fe,
23
24 pub const FromBytesError = ValueError || ff.OverflowError || ff.FieldElementError || ff.InvalidModulusError || error{InsecureBitCount};
25
26 pub fn fromBytes(mod: []const u8, exp: []const u8) FromBytesError!PublicKey {
27 const modulus = try Modulus.fromBytes(mod, .big);
28 if (modulus.bits() <= 512) return error.InsecureBitCount;
29 const public_exponent = try Fe.fromBytes(modulus, exp, .big);
30
31 if (std.debug.runtime_safety) {
32 // > the RSA public exponent e is an integer between 3 and n - 1 satisfying
33 // > GCD(e,\lambda(n)) = 1, where \lambda(n) = LCM(r_1 - 1, ..., r_u - 1)
34 const e_v = public_exponent.toPrimitive(u32) catch return error.Exponent;
35 if (!public_exponent.isOdd()) return error.Exponent;
36 if (e_v < 3) return error.Exponent;
37 if (modulus.v.compare(public_exponent.v) == .lt) return error.Exponent;
38 }
39
40 return .{ .modulus = modulus, .public_exponent = public_exponent };
41 }
42
43 pub fn fromDer(bytes: []const u8) (der.Parser.Error || FromBytesError)!PublicKey {
44 var parser = der.Parser{ .bytes = bytes };
45
46 const seq = try parser.expectSequence();
47 defer parser.seek(seq.slice.end);
48
49 const modulus = try parser.expectPrimitive(.integer);
50 const pub_exp = try parser.expectPrimitive(.integer);
51
52 try parser.expectEnd(seq.slice.end);
53 try parser.expectEnd(bytes.len);
54
55 return try fromBytes(parser.view(modulus), parser.view(pub_exp));
56 }
57
58 /// Parse an RSA public key from a DER-encoded `SubjectPublicKeyInfo`
59 /// (X.509 / OpenSSL `-pubout` / DKIM `p=` format): unwrap the
60 /// AlgorithmIdentifier + BIT STRING and parse the inner PKCS#1 RSAPublicKey.
61 pub fn fromSpki(bytes: []const u8) (der.Parser.Error || FromBytesError)!PublicKey {
62 var parser = der.Parser{ .bytes = bytes };
63 _ = try parser.expectSequence(); // SubjectPublicKeyInfo
64 const algorithm = try parser.expectSequence(); // AlgorithmIdentifier
65 parser.seek(algorithm.slice.end);
66 const subject_public_key = try parser.expectBitstring();
67 return try fromDer(subject_public_key.bytes);
68 }
69
70 /// Deprecated.
71 ///
72 /// Encrypt a short message using RSAES-PKCS1-v1_5.
73 /// The use of this scheme for encrypting an arbitrary message, as opposed to a
74 /// randomly generated key, is NOT RECOMMENDED.
75 pub fn encryptPkcsv1_5(pk: PublicKey, msg: []const u8, out: []u8) ![]const u8 {
76 // align variable names with spec
77 const k = byteLen(pk.modulus.bits());
78 if (out.len < k) return error.BufferTooSmall;
79 if (msg.len > k - 11) return error.MessageTooLong;
80
81 // EM = 0x00 || 0x02 || PS || 0x00 || M.
82 var em = out[0..k];
83 em[0] = 0;
84 em[1] = 2;
85
86 const ps = em[2..][0 .. k - msg.len - 3];
87 // Section: 7.2.1
88 // PS consists of pseudo-randomly generated nonzero octets.
89 for (ps) |*v| {
90 var rnd_byte: [1]u8 = undefined;
91 rng.fill(&rnd_byte);
92 v.* = if (rnd_byte[0] == 0) 1 else rnd_byte[0];
93 }
94
95 em[em.len - msg.len - 1] = 0;
96 @memcpy(em[em.len - msg.len ..][0..msg.len], msg);
97
98 const m = try Fe.fromBytes(pk.modulus, em, .big);
99 const e = try pk.modulus.powPublic(m, pk.public_exponent);
100 try e.toBytes(em, .big);
101 return em;
102 }
103
104 /// Encrypt a short message using Optimal Asymmetric Encryption Padding (RSAES-OAEP).
105 pub fn encryptOaep(
106 pk: PublicKey,
107 comptime Hash: type,
108 msg: []const u8,
109 label: []const u8,
110 out: []u8,
111 ) ![]const u8 {
112 // align variable names with spec
113 const k = byteLen(pk.modulus.bits());
114 if (out.len < k) return error.BufferTooSmall;
115
116 if (msg.len > k - 2 * Hash.digest_length - 2) return error.MessageTooLong;
117
118 // EM = 0x00 || maskedSeed || maskedDB.
119 var em = out[0..k];
120 em[0] = 0;
121 const seed = em[1..][0..Hash.digest_length];
122 rng.fill(seed);
123
124 // DB = lHash || PS || 0x01 || M.
125 var db = em[1 + seed.len ..];
126 const lHash = labelHash(Hash, label);
127 @memcpy(db[0..lHash.len], &lHash);
128 @memset(db[lHash.len .. db.len - msg.len - 2], 0);
129 db[db.len - msg.len - 1] = 1;
130 @memcpy(db[db.len - msg.len ..], msg);
131
132 var mgf_buf: [max_modulus_len]u8 = undefined;
133
134 const db_mask = mgf1(Hash, seed, mgf_buf[0..db.len]);
135 for (db, db_mask) |*v, m| v.* ^= m;
136
137 const seed_mask = mgf1(Hash, db, mgf_buf[0..seed.len]);
138 for (seed, seed_mask) |*v, m| v.* ^= m;
139
140 const m = try Fe.fromBytes(pk.modulus, em, .big);
141 const e = try pk.modulus.powPublic(m, pk.public_exponent);
142 try e.toBytes(em, .big);
143 return em;
144 }
145};
146
147pub fn byteLen(bits: usize) usize {
148 return std.math.divCeil(usize, bits, 8) catch unreachable;
149}
150
151pub const SecretKey = struct {
152 /// `d`
153 private_exponent: Fe,
154
155 pub const FromBytesError = ValueError || ff.OverflowError || ff.FieldElementError;
156
157 pub fn fromBytes(n: Modulus, exp: []const u8) FromBytesError!SecretKey {
158 const d = try Fe.fromBytes(n, exp, .big);
159 if (std.debug.runtime_safety) {
160 // > The RSA private exponent d is a positive integer less than n
161 // > satisfying e * d == 1 (mod \lambda(n)),
162 if (!d.isOdd()) return error.Exponent;
163 if (d.v.compare(n.v) != .lt) return error.Exponent;
164 }
165
166 return .{ .private_exponent = d };
167 }
168};
169
170pub const KeyPair = struct {
171 public: PublicKey,
172 secret: SecretKey,
173
174 pub const FromDerError = PublicKey.FromBytesError || SecretKey.FromBytesError || der.Parser.Error || error{ KeyMismatch, InvalidVersion };
175
176 pub fn fromDer(bytes: []const u8) FromDerError!KeyPair {
177 var parser = der.Parser{ .bytes = bytes };
178 const seq = try parser.expectSequence();
179 const version = try parser.expectInt(u8);
180
181 const mod = try parser.expectPrimitive(.integer);
182 const pub_exp = try parser.expectPrimitive(.integer);
183 const sec_exp = try parser.expectPrimitive(.integer);
184
185 const public = try PublicKey.fromBytes(parser.view(mod), parser.view(pub_exp));
186 const secret = try SecretKey.fromBytes(public.modulus, parser.view(sec_exp));
187
188 const prime1 = try parser.expectPrimitive(.integer);
189 const prime2 = try parser.expectPrimitive(.integer);
190 const exp1 = try parser.expectPrimitive(.integer);
191 const exp2 = try parser.expectPrimitive(.integer);
192 const coeff = try parser.expectPrimitive(.integer);
193 _ = .{ exp1, exp2, coeff };
194
195 switch (version) {
196 0 => {},
197 1 => {
198 _ = try parser.expectSequenceOf();
199 while (!parser.eof()) {
200 _ = try parser.expectSequence();
201 const ri = try parser.expectPrimitive(.integer);
202 const di = try parser.expectPrimitive(.integer);
203 const ti = try parser.expectPrimitive(.integer);
204 _ = .{ ri, di, ti };
205 }
206 },
207 else => return error.InvalidVersion,
208 }
209
210 try parser.expectEnd(seq.slice.end);
211 try parser.expectEnd(bytes.len);
212
213 if (std.debug.runtime_safety) {
214 const p = try Fe.fromBytes(public.modulus, parser.view(prime1), .big);
215 const q = try Fe.fromBytes(public.modulus, parser.view(prime2), .big);
216
217 // check that n = p * q
218 const expected_zero = public.modulus.mul(p, q);
219 if (!expected_zero.isZero()) return error.KeyMismatch;
220
221 // TODO: check that d * e is one mod p-1 and mod q-1. Note d and e were bound
222 // const de = secret.private_exponent.mul(public.public_exponent);
223 // const one = public.modulus.one();
224
225 // if (public.modulus.mul(de, p).compare(one) != .eq) return error.KeyMismatch;
226 // if (public.modulus.mul(de, q).compare(one) != .eq) return error.KeyMismatch;
227 }
228
229 return .{ .public = public, .secret = secret };
230 }
231
232 /// Deprecated.
233 pub fn signPkcsv1_5(kp: KeyPair, comptime Hash: type, msg: []const u8, out: []u8) !PKCS1v1_5(Hash).Signature {
234 var st = try signerPkcsv1_5(kp, Hash);
235 st.update(msg);
236 return try st.finalize(out);
237 }
238
239 /// Deprecated.
240 pub fn signerPkcsv1_5(kp: KeyPair, comptime Hash: type) !PKCS1v1_5(Hash).Signer {
241 return PKCS1v1_5(Hash).Signer.init(kp);
242 }
243
244 /// Deprecated.
245 pub fn decryptPkcsv1_5(kp: KeyPair, ciphertext: []const u8, out: []u8) ![]const u8 {
246 const k = byteLen(kp.public.modulus.bits());
247 if (out.len < k) return error.BufferTooSmall;
248
249 const em = out[0..k];
250
251 const m = try Fe.fromBytes(kp.public.modulus, ciphertext, .big);
252 const e = try kp.public.modulus.pow(m, kp.secret.private_exponent);
253 try e.toBytes(em, .big);
254
255 // Care shall be taken to ensure that an opponent cannot
256 // distinguish these error conditions, whether by error
257 // message or timing.
258 const msg_start = ct.lastIndexOfScalar(em, 0) orelse em.len;
259 const ps_len = em.len - msg_start;
260 if (ct.@"or"(em[0] != 0, ct.@"or"(em[1] != 2, ps_len < 8))) {
261 return error.Inconsistent;
262 }
263
264 return em[msg_start + 1 ..];
265 }
266
267 pub fn signOaep(
268 kp: KeyPair,
269 comptime Hash: type,
270 msg: []const u8,
271 salt: ?[]const u8,
272 out: []u8,
273 ) !Pss(Hash).Signature {
274 var st = try signerOaep(kp, Hash, salt);
275 st.update(msg);
276 return try st.finalize(out);
277 }
278
279 /// Salt must outlive returned `PSS.Signer`.
280 pub fn signerOaep(kp: KeyPair, comptime Hash: type, salt: ?[]const u8) !Pss(Hash).Signer {
281 return Pss(Hash).Signer.init(kp, salt);
282 }
283
284 pub fn decryptOaep(
285 kp: KeyPair,
286 comptime Hash: type,
287 ciphertext: []const u8,
288 label: []const u8,
289 out: []u8,
290 ) ![]u8 {
291 // align variable names with spec
292 const k = byteLen(kp.public.modulus.bits());
293 if (out.len < k) return error.BufferTooSmall;
294
295 const mod = try Fe.fromBytes(kp.public.modulus, ciphertext, .big);
296 const exp = kp.public.modulus.pow(mod, kp.secret.private_exponent) catch unreachable;
297 const em = out[0..k];
298 try exp.toBytes(em, .big);
299
300 const y = em[0];
301 const seed = em[1..][0..Hash.digest_length];
302 const db = em[1 + Hash.digest_length ..];
303
304 var mgf_buf: [max_modulus_len]u8 = undefined;
305
306 const seed_mask = mgf1(Hash, db, mgf_buf[0..seed.len]);
307 for (seed, seed_mask) |*v, m| v.* ^= m;
308
309 const db_mask = mgf1(Hash, seed, mgf_buf[0..db.len]);
310 for (db, db_mask) |*v, m| v.* ^= m;
311
312 const expected_hash = labelHash(Hash, label);
313 const actual_hash = db[0..expected_hash.len];
314
315 // Care shall be taken to ensure that an opponent cannot
316 // distinguish these error conditions, whether by error
317 // message or timing.
318 const msg_start = ct.indexOfScalarPos(em, expected_hash.len + 1, 1) orelse 0;
319 if (ct.@"or"(y != 0, ct.@"or"(msg_start == 0, !ct.memEql(&expected_hash, actual_hash)))) {
320 return error.Inconsistent;
321 }
322
323 return em[msg_start + 1 ..];
324 }
325
326 /// Encrypt short plaintext with secret key.
327 pub fn encrypt(kp: KeyPair, plaintext: []const u8, out: []u8) !void {
328 const n = kp.public.modulus;
329 const k = byteLen(n.bits());
330 if (plaintext.len > k) return error.MessageTooLong;
331
332 const msg_as_int = try Fe.fromBytes(n, plaintext, .big);
333 const enc_as_int = try n.pow(msg_as_int, kp.secret.private_exponent);
334 try enc_as_int.toBytes(out, .big);
335 }
336};
337
338/// Deprecated.
339///
340/// Signature Scheme with Appendix v1.5 (RSASSA-PKCS1-v1_5)
341///
342/// This standard has been superceded by PSS which is formally proven secure
343/// and has fewer footguns.
344pub fn PKCS1v1_5(comptime Hash: type) type {
345 return struct {
346 const PkcsT = @This();
347 pub const Signature = struct {
348 bytes: []const u8,
349
350 const Self = @This();
351
352 pub fn verifier(self: Self, public_key: PublicKey) !Verifier {
353 return Verifier.init(self, public_key);
354 }
355
356 pub fn verify(self: Self, msg: []const u8, public_key: PublicKey) !void {
357 var st = Verifier.init(self, public_key);
358 st.update(msg);
359 return st.verify();
360 }
361 };
362
363 pub const Signer = struct {
364 h: Hash,
365 key_pair: KeyPair,
366
367 fn init(key_pair: KeyPair) Signer {
368 return .{
369 .h = Hash.init(.{}),
370 .key_pair = key_pair,
371 };
372 }
373
374 pub fn update(self: *Signer, data: []const u8) void {
375 self.h.update(data);
376 }
377
378 pub fn finalize(self: *Signer, out: []u8) !PkcsT.Signature {
379 const k = byteLen(self.key_pair.public.modulus.bits());
380 if (out.len < k) return error.BufferTooSmall;
381
382 var hash: [Hash.digest_length]u8 = undefined;
383 self.h.final(&hash);
384
385 const em = try emsaEncode(hash, out[0..k]);
386 try self.key_pair.encrypt(em, em);
387 return .{ .bytes = em };
388 }
389 };
390
391 pub const Verifier = struct {
392 h: Hash,
393 sig: PkcsT.Signature,
394 public_key: PublicKey,
395
396 fn init(sig: PkcsT.Signature, public_key: PublicKey) Verifier {
397 return Verifier{
398 .h = Hash.init(.{}),
399 .sig = sig,
400 .public_key = public_key,
401 };
402 }
403
404 pub fn update(self: *Verifier, data: []const u8) void {
405 self.h.update(data);
406 }
407
408 pub fn verify(self: *Verifier) !void {
409 const pk = self.public_key;
410 const s = try Fe.fromBytes(pk.modulus, self.sig.bytes, .big);
411 const emm = try pk.modulus.powPublic(s, pk.public_exponent);
412
413 var em_buf: [max_modulus_len]u8 = undefined;
414 const em = em_buf[0..byteLen(pk.modulus.bits())];
415 try emm.toBytes(em, .big);
416
417 var hash: [Hash.digest_length]u8 = undefined;
418 self.h.final(&hash);
419
420 var em_buf2: [max_modulus_len]u8 = undefined;
421 const expected_em = try emsaEncode(hash, em_buf2[0..byteLen(pk.modulus.bits())]);
422 if (!std.mem.eql(u8, expected_em, em)) return error.Inconsistent;
423 }
424 };
425
426 /// PKCS Encrypted Message Signature Appendix
427 fn emsaEncode(hash: [Hash.digest_length]u8, out: []u8) ![]u8 {
428 const digest_header = comptime digestHeader();
429 const tLen = digest_header.len + Hash.digest_length;
430 const emLen = out.len;
431 if (emLen < tLen + 11) return error.ModulusTooShort;
432 if (out.len < emLen) return error.BufferTooSmall;
433
434 var res = out[0..emLen];
435 res[0] = 0;
436 res[1] = 1;
437 const padding_len = emLen - tLen - 3;
438 @memset(res[2..][0..padding_len], 0xff);
439 res[2 + padding_len] = 0;
440 @memcpy(res[2 + padding_len + 1 ..][0..digest_header.len], digest_header);
441 @memcpy(res[res.len - hash.len ..], &hash);
442
443 return res;
444 }
445
446 /// DER encoded header. Sequence of digest algo + digest.
447 /// TODO: use a DER encoder instead
448 fn digestHeader() []const u8 {
449 const sha2 = std.crypto.hash.sha2;
450 // Section 9.2 Notes 1.
451 return switch (Hash) {
452 std.crypto.hash.Sha1 => &hexToBytes(
453 \\30 21 30 09 06 05 2b 0e 03 02 1a 05 00 04 14
454 ),
455 sha2.Sha224 => &hexToBytes(
456 \\30 2d 30 0d 06 09 60 86 48 01 65 03 04 02 04
457 \\05 00 04 1c
458 ),
459 sha2.Sha256 => &hexToBytes(
460 \\30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00
461 \\04 20
462 ),
463 sha2.Sha384 => &hexToBytes(
464 \\30 41 30 0d 06 09 60 86 48 01 65 03 04 02 02 05 00
465 \\04 30
466 ),
467 sha2.Sha512 => &hexToBytes(
468 \\30 51 30 0d 06 09 60 86 48 01 65 03 04 02 03 05 00
469 \\04 40
470 ),
471 // sha2.Sha512224 => &hexToBytes(
472 // \\30 2d 30 0d 06 09 60 86 48 01 65 03 04 02 05
473 // \\05 00 04 1c
474 // ),
475 // sha2.Sha512256 => &hexToBytes(
476 // \\30 31 30 0d 06 09 60 86 48 01 65 03 04 02 06
477 // \\05 00 04 20
478 // ),
479 else => @compileError("unknown Hash " ++ @typeName(Hash)),
480 };
481 }
482 };
483}
484
485/// Probabilistic Signature Scheme (RSASSA-PSS)
486pub fn Pss(comptime Hash: type) type {
487 // RFC 4055 S3.1
488 const default_salt_len = Hash.digest_length;
489 return struct {
490 pub const Signature = struct {
491 bytes: []const u8,
492
493 const Self = @This();
494
495 pub fn verifier(self: Self, public_key: PublicKey) !Verifier {
496 return Verifier.init(self, public_key);
497 }
498
499 pub fn verify(self: Self, msg: []const u8, public_key: PublicKey, salt_len: ?usize) !void {
500 var st = Verifier.init(self, public_key, salt_len orelse default_salt_len);
501 st.update(msg);
502 return st.verify();
503 }
504 };
505
506 const PssT = @This();
507
508 pub const Signer = struct {
509 h: Hash,
510 key_pair: KeyPair,
511 salt: ?[]const u8,
512
513 fn init(key_pair: KeyPair, salt: ?[]const u8) Signer {
514 return .{
515 .h = Hash.init(.{}),
516 .key_pair = key_pair,
517 .salt = salt,
518 };
519 }
520
521 pub fn update(self: *Signer, data: []const u8) void {
522 self.h.update(data);
523 }
524
525 pub fn finalize(self: *Signer, out: []u8) !PssT.Signature {
526 var hashed: [Hash.digest_length]u8 = undefined;
527 self.h.final(&hashed);
528
529 const salt = if (self.salt) |s| s else brk: {
530 var res: [default_salt_len]u8 = undefined;
531 rng.fill(&res);
532 break :brk &res;
533 };
534
535 const em_bits = self.key_pair.public.modulus.bits() - 1;
536 const em = try emsaEncode(hashed, salt, em_bits, out);
537 try self.key_pair.encrypt(em, em);
538 return .{ .bytes = em };
539 }
540 };
541
542 pub const Verifier = struct {
543 h: Hash,
544 sig: PssT.Signature,
545 public_key: PublicKey,
546 salt_len: usize,
547
548 fn init(sig: PssT.Signature, public_key: PublicKey, salt_len: usize) Verifier {
549 return Verifier{
550 .h = Hash.init(.{}),
551 .sig = sig,
552 .public_key = public_key,
553 .salt_len = salt_len,
554 };
555 }
556
557 pub fn update(self: *Verifier, data: []const u8) void {
558 self.h.update(data);
559 }
560
561 pub fn verify(self: *Verifier) !void {
562 const pk = self.public_key;
563 const s = try Fe.fromBytes(pk.modulus, self.sig.bytes, .big);
564 const emm = try pk.modulus.powPublic(s, pk.public_exponent);
565
566 var em_buf: [max_modulus_len]u8 = undefined;
567 const em_bits = pk.modulus.bits() - 1;
568 const em_len = std.math.divCeil(usize, em_bits, 8) catch unreachable;
569 var em = em_buf[0..em_len];
570 try emm.toBytes(em, .big);
571
572 if (em.len < Hash.digest_length + self.salt_len + 2) return error.Inconsistent;
573 if (em[em.len - 1] != 0xbc) return error.Inconsistent;
574
575 const db = em[0 .. em.len - Hash.digest_length - 1];
576 if (@clz(db[0]) < em.len * 8 - em_bits) return error.Inconsistent;
577
578 const expected_hash = em[db.len..][0..Hash.digest_length];
579 var mgf_buf: [max_modulus_len]u8 = undefined;
580 const db_mask = mgf1(Hash, expected_hash, mgf_buf[0..db.len]);
581 for (db, db_mask) |*v, m| v.* ^= m;
582
583 for (1..db.len - self.salt_len - 1) |i| {
584 if (db[i] != 0) return error.Inconsistent;
585 }
586 if (db[db.len - self.salt_len - 1] != 1) return error.Inconsistent;
587 const salt = db[db.len - self.salt_len ..];
588 var mp_buf: [max_modulus_len]u8 = undefined;
589 var mp = mp_buf[0 .. 8 + Hash.digest_length + self.salt_len];
590 @memset(mp[0..8], 0);
591 self.h.final(mp[8..][0..Hash.digest_length]);
592 @memcpy(mp[8 + Hash.digest_length ..][0..salt.len], salt);
593
594 var actual_hash: [Hash.digest_length]u8 = undefined;
595 Hash.hash(mp, &actual_hash, .{});
596
597 if (!std.mem.eql(u8, expected_hash, &actual_hash)) return error.Inconsistent;
598 }
599 };
600
601 /// PSS Encrypted Message Signature Appendix
602 fn emsaEncode(msg_hash: [Hash.digest_length]u8, salt: []const u8, em_bits: usize, out: []u8) ![]u8 {
603 const em_len = std.math.divCeil(usize, em_bits, 8) catch unreachable;
604
605 if (em_len < Hash.digest_length + salt.len + 2) return error.Encoding;
606
607 // EM = maskedDB || H || 0xbc
608 var em = out[0..em_len];
609 em[em.len - 1] = 0xbc;
610
611 var mp_buf: [max_modulus_len]u8 = undefined;
612 // M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt;
613 const mp = mp_buf[0 .. 8 + Hash.digest_length + salt.len];
614 @memset(mp[0..8], 0);
615 @memcpy(mp[8..][0..Hash.digest_length], &msg_hash);
616 @memcpy(mp[8 + Hash.digest_length ..][0..salt.len], salt);
617
618 // H = Hash(M')
619 const hash = em[em.len - 1 - Hash.digest_length ..][0..Hash.digest_length];
620 Hash.hash(mp, hash, .{});
621
622 // DB = PS || 0x01 || salt
623 var db = em[0 .. em_len - Hash.digest_length - 1];
624 @memset(db[0 .. db.len - salt.len - 1], 0);
625 db[db.len - salt.len - 1] = 1;
626 @memcpy(db[db.len - salt.len ..], salt);
627
628 var mgf_buf: [max_modulus_len]u8 = undefined;
629 const db_mask = mgf1(Hash, hash, mgf_buf[0..db.len]);
630 for (db, db_mask) |*v, m| v.* ^= m;
631
632 // Set the leftmost 8emLen - emBits bits of the leftmost octet
633 // in maskedDB to zero.
634 const shift = std.math.comptimeMod(8 * em_len - em_bits, 8);
635 const mask = @as(u8, 0xff) >> shift;
636 db[0] &= mask;
637
638 return em;
639 }
640 };
641}
642
643/// Mask generation function. Currently the only one defined.
644fn mgf1(comptime Hash: type, seed: []const u8, out: []u8) []u8 {
645 var c: [@sizeOf(u32)]u8 = undefined;
646 var tmp: [Hash.digest_length]u8 = undefined;
647
648 var i: usize = 0;
649 var counter: u32 = 0;
650 while (i < out.len) : (counter += 1) {
651 var hasher = Hash.init(.{});
652 hasher.update(seed);
653 std.mem.writeInt(u32, &c, counter, .big);
654 hasher.update(&c);
655
656 const left = out.len - i;
657 if (left >= Hash.digest_length) {
658 // optimization: write straight to `out`
659 hasher.final(out[i..][0..Hash.digest_length]);
660 i += Hash.digest_length;
661 } else {
662 hasher.final(&tmp);
663 @memcpy(out[i..][0..left], tmp[0..left]);
664 i += left;
665 }
666 }
667
668 return out;
669}
670
671test mgf1 {
672 const Hash = std.crypto.hash.sha2.Sha256;
673 var out: [Hash.digest_length * 2 + 1]u8 = undefined;
674 try std.testing.expectEqualSlices(
675 u8,
676 &hexToBytes(
677 \\ed 1b 84 6b b9 26 39 00 c8 17 82 ad 08 eb 17 01
678 \\fa 8c 72 21 c6 57 63 77 31 7f 5c e8 09 89 9f
679 ),
680 mgf1(Hash, "asdf", out[0 .. Hash.digest_length - 1]),
681 );
682 try std.testing.expectEqualSlices(
683 u8,
684 &hexToBytes(
685 \\ed 1b 84 6b b9 26 39 00 c8 17 82 ad 08 eb 17 01
686 \\fa 8c 72 21 c6 57 63 77 31 7f 5c e8 09 89 9f 5a
687 \\22 F2 80 D5 28 08 F4 93 83 76 00 DE 09 E4 EC 92
688 \\4A 2C 7C EF 0D F7 7B BE 8F 7F 12 CB 8F 33 A6 65
689 \\AB
690 ),
691 mgf1(Hash, "asdf", &out),
692 );
693}
694
695/// For OAEP.
696fn labelHash(comptime Hash: type, label: []const u8) [Hash.digest_length]u8 {
697 if (label.len == 0) {
698 // magic constants from NIST
699 const sha2 = std.crypto.hash.sha2;
700 switch (Hash) {
701 std.crypto.hash.Sha1 => return hexToBytes(
702 \\da39a3ee 5e6b4b0d 3255bfef 95601890
703 \\afd80709
704 ),
705 sha2.Sha256 => return hexToBytes(
706 \\e3b0c442 98fc1c14 9afbf4c8 996fb924
707 \\27ae41e4 649b934c a495991b 7852b855
708 ),
709 sha2.Sha384 => return hexToBytes(
710 \\38b060a7 51ac9638 4cd9327e b1b1e36a
711 \\21fdb711 14be0743 4c0cc7bf 63f6e1da
712 \\274edebf e76f65fb d51ad2f1 4898b95b
713 ),
714 sha2.Sha512 => return hexToBytes(
715 \\cf83e135 7eefb8bd f1542850 d66d8007
716 \\d620e405 0b5715dc 83f4a921 d36ce9ce
717 \\47d0d13c 5d85f2b0 ff8318d2 877eec2f
718 \\63b931bd 47417a81 a538327a f927da3e
719 ),
720 // just use the empty hash...
721 else => {},
722 }
723 }
724 var res: [Hash.digest_length]u8 = undefined;
725 Hash.hash(label, &res, .{});
726 return res;
727}
728
729const ct = if (std.options.side_channels_mitigations == .none) ct_unprotected else ct_protected;
730
731const ct_unprotected = struct {
732 fn lastIndexOfScalar(slice: []const u8, value: u8) ?usize {
733 return std.mem.lastIndexOfScalar(u8, slice, value);
734 }
735
736 fn indexOfScalarPos(slice: []const u8, start_index: usize, value: u8) ?usize {
737 return std.mem.indexOfScalarPos(u8, slice, start_index, value);
738 }
739
740 fn memEql(a: []const u8, b: []const u8) bool {
741 return std.mem.eql(u8, a, b);
742 }
743
744 fn @"and"(a: bool, b: bool) bool {
745 return a and b;
746 }
747
748 fn @"or"(a: bool, b: bool) bool {
749 return a or b;
750 }
751};
752
753const ct_protected = struct {
754 fn lastIndexOfScalar(slice: []const u8, value: u8) ?usize {
755 var res: ?usize = null;
756 var i: usize = slice.len;
757 while (i != 0) {
758 i -= 1;
759 if (@intFromBool(res == null) & @intFromBool(slice[i] == value) == 1) res = i;
760 }
761 return res;
762 }
763
764 fn indexOfScalarPos(slice: []const u8, start_index: usize, value: u8) ?usize {
765 var res: ?usize = null;
766 for (slice[start_index..], start_index..) |c, j| {
767 if (c == value) res = j;
768 }
769 return res;
770 }
771
772 fn memEql(a: []const u8, b: []const u8) bool {
773 var res: u1 = 1;
774 for (a, b) |a_elem, b_elem| {
775 res &= @intFromBool(a_elem == b_elem);
776 }
777 return res == 1;
778 }
779
780 fn @"and"(a: bool, b: bool) bool {
781 return (@intFromBool(a) & @intFromBool(b)) == 1;
782 }
783
784 fn @"or"(a: bool, b: bool) bool {
785 return (@intFromBool(a) | @intFromBool(b)) == 1;
786 }
787};
788
789test ct {
790 const c = ct_unprotected;
791 try std.testing.expectEqual(true, c.@"or"(true, false));
792 try std.testing.expectEqual(true, c.@"and"(true, true));
793 try std.testing.expectEqual(true, c.memEql("Asdf", "Asdf"));
794 try std.testing.expectEqual(false, c.memEql("asdf", "Asdf"));
795 try std.testing.expectEqual(3, c.indexOfScalarPos("asdff", 1, 'f'));
796 try std.testing.expectEqual(4, c.lastIndexOfScalar("asdff", 'f'));
797}
798
799fn removeNonHex(comptime hex: []const u8) []const u8 {
800 var res: [hex.len]u8 = undefined;
801 var i: usize = 0;
802 for (hex) |c| {
803 if (std.ascii.isHex(c)) {
804 res[i] = c;
805 i += 1;
806 }
807 }
808 return res[0..i];
809}
810
811/// For readable copy/pasting from hex viewers.
812fn hexToBytes(comptime hex: []const u8) [removeNonHex(hex).len / 2]u8 {
813 const hex2 = comptime removeNonHex(hex);
814 comptime var res: [hex2.len / 2]u8 = undefined;
815 _ = comptime std.fmt.hexToBytes(&res, hex2) catch unreachable;
816 return res;
817}
818
819test hexToBytes {
820 const hex =
821 \\e3b0c442 98fc1c14 9afbf4c8 996fb924
822 \\27ae41e4 649b934c a495991b 7852b855
823 ;
824 try std.testing.expectEqual(
825 [_]u8{
826 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14,
827 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
828 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c,
829 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55,
830 },
831 hexToBytes(hex),
832 );
833}
834
835const TestHash = std.crypto.hash.sha2.Sha256;
836fn testKeypair() !KeyPair {
837 const keypair_bytes = @embedFile("testdata/id_rsa.der");
838 const kp = try KeyPair.fromDer(keypair_bytes);
839 try std.testing.expectEqual(2048, kp.public.modulus.bits());
840 return kp;
841}
842
843const skip_slow_tests = false;
844
845test "rsa PKCS1-v1_5 encrypt and decrypt" {
846 if (skip_slow_tests) return error.SkipZigTest;
847 const kp = try testKeypair();
848
849 const msg = "rsa PKCS1-v1_5 encrypt and decrypt";
850 var out: [max_modulus_len]u8 = undefined;
851 const enc = try kp.public.encryptPkcsv1_5(msg, &out);
852
853 var out2: [max_modulus_len]u8 = undefined;
854 const dec = try kp.decryptPkcsv1_5(enc, &out2);
855
856 try std.testing.expectEqualSlices(u8, msg, dec);
857}
858
859test "rsa OAEP encrypt and decrypt" {
860 if (skip_slow_tests) return error.SkipZigTest;
861 const kp = try testKeypair();
862
863 const msg = "rsa OAEP encrypt and decrypt";
864 const label = "";
865 var out: [max_modulus_len]u8 = undefined;
866 const enc = try kp.public.encryptOaep(TestHash, msg, label, &out);
867
868 var out2: [max_modulus_len]u8 = undefined;
869 const dec = try kp.decryptOaep(TestHash, enc, label, &out2);
870
871 try std.testing.expectEqualSlices(u8, msg, dec);
872}
873
874test "rsa PKCS1-v1_5 signature" {
875 if (skip_slow_tests) return error.SkipZigTest;
876 const kp = try testKeypair();
877
878 const msg = "rsa PKCS1-v1_5 signature";
879 var out: [max_modulus_len]u8 = undefined;
880
881 const signature = try kp.signPkcsv1_5(TestHash, msg, &out);
882 try signature.verify(msg, kp.public);
883}
884
885test "rsa PSS signature" {
886 if (skip_slow_tests) return error.SkipZigTest;
887 const kp = try testKeypair();
888
889 const msg = "rsa PSS signature";
890 var out: [max_modulus_len]u8 = undefined;
891
892 const salts = [_][]const u8{ "asdf", "" };
893 for (salts) |salt| {
894 const signature = try kp.signOaep(TestHash, msg, salt, &out);
895 try signature.verify(msg, kp.public, salt.len);
896 }
897
898 const signature = try kp.signOaep(TestHash, msg, null, &out); // random salt
899 try signature.verify(msg, kp.public, null);
900}
packages/zig/vendor/zig-tls/src/rsa/testdata/id_rsa.derdeleted+0-0

Binary file. Nothing to show line by line.

packages/zig/vendor/zig-tls/src/testdata/google.com/client_randomdeleted+0-1
Changes to packages/zig/vendor/zig-tls/src/testdata/google.com/client_random
@@ -1,1 +0,0 @@
1'���qp0x�0)쩖�~�+�`��tY4��D_
packages/zig/vendor/zig-tls/src/testdata/google.com/server_hellodeleted+0-0

Binary file. Nothing to show line by line.

packages/zig/vendor/zig-tls/src/testdata/tls12.zigdeleted+0-244
Changes to packages/zig/vendor/zig-tls/src/testdata/tls12.zig
@@ -1,244 +0,0 @@
1/// Messages from The Illustrated TLS 1.2 Connection
2/// https://tls12.xargs.org/
3const hexToBytes = .hexToBytes;
4
5pub const client_hello = hexToBytes(
6 \\ 16 03 01 00 a5 01 00 00 a1 03 03 00 01 02 03 04
7 \\ 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14
8 \\ 15 16 17 18 19 1a 1b 1c 1d 1e 1f 00 00 20 cc a8
9 \\ cc a9 c0 2f c0 30 c0 2b c0 2c c0 13 c0 09 c0 14
10 \\ c0 0a 00 9c 00 9d 00 2f 00 35 c0 12 00 0a 01 00
11 \\ 00 58 00 00 00 18 00 16 00 00 13 65 78 61 6d 70
12 \\ 6c 65 2e 75 6c 66 68 65 69 6d 2e 6e 65 74 00 05
13 \\ 00 05 01 00 00 00 00 00 0a 00 0a 00 08 00 1d 00
14 \\ 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 12 00
15 \\ 10 04 01 04 03 05 01 05 03 06 01 06 03 02 01 02
16 \\ 03 ff 01 00 01 00 00 12 00 00
17);
18pub const server_hello = hexToBytes(
19 \\ 16 03 03 00 31 02 00 00 2d 03 03 70 71 72 73 74
20 \\ 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84
21 \\ 85 86 87 88 89 8a 8b 8c 8d 8e 8f 00 c0 13 00 00
22 \\ 05 ff 01 00 01 00
23);
24pub const server_certificate = hexToBytes(
25 \\ 16 03 03 03 2f 0b 00 03 2b 00 03 28 00 03 25 30
26 \\ 82 03 21 30 82 02 09 a0 03 02 01 02 02 08 15 5a
27 \\ 92 ad c2 04 8f 90 30 0d 06 09 2a 86 48 86 f7 0d
28 \\ 01 01 0b 05 00 30 22 31 0b 30 09 06 03 55 04 06
29 \\ 13 02 55 53 31 13 30 11 06 03 55 04 0a 13 0a 45
30 \\ 78 61 6d 70 6c 65 20 43 41 30 1e 17 0d 31 38 31
31 \\ 30 30 35 30 31 33 38 31 37 5a 17 0d 31 39 31 30
32 \\ 30 35 30 31 33 38 31 37 5a 30 2b 31 0b 30 09 06
33 \\ 03 55 04 06 13 02 55 53 31 1c 30 1a 06 03 55 04
34 \\ 03 13 13 65 78 61 6d 70 6c 65 2e 75 6c 66 68 65
35 \\ 69 6d 2e 6e 65 74 30 82 01 22 30 0d 06 09 2a 86
36 \\ 48 86 f7 0d 01 01 01 05 00 03 82 01 0f 00 30 82
37 \\ 01 0a 02 82 01 01 00 c4 80 36 06 ba e7 47 6b 08
38 \\ 94 04 ec a7 b6 91 04 3f f7 92 bc 19 ee fb 7d 74
39 \\ d7 a8 0d 00 1e 7b 4b 3a 4a e6 0f e8 c0 71 fc 73
40 \\ e7 02 4c 0d bc f4 bd d1 1d 39 6b ba 70 46 4a 13
41 \\ e9 4a f8 3d f3 e1 09 59 54 7b c9 55 fb 41 2d a3
42 \\ 76 52 11 e1 f3 dc 77 6c aa 53 37 6e ca 3a ec be
43 \\ c3 aa b7 3b 31 d5 6c b6 52 9c 80 98 bc c9 e0 28
44 \\ 18 e2 0b f7 f8 a0 3a fd 17 04 50 9e ce 79 bd 9f
45 \\ 39 f1 ea 69 ec 47 97 2e 83 0f b5 ca 95 de 95 a1
46 \\ e6 04 22 d5 ee be 52 79 54 a1 e7 bf 8a 86 f6 46
47 \\ 6d 0d 9f 16 95 1a 4c f7 a0 46 92 59 5c 13 52 f2
48 \\ 54 9e 5a fb 4e bf d7 7a 37 95 01 44 e4 c0 26 87
49 \\ 4c 65 3e 40 7d 7d 23 07 44 01 f4 84 ff d0 8f 7a
50 \\ 1f a0 52 10 d1 f4 f0 d5 ce 79 70 29 32 e2 ca be
51 \\ 70 1f df ad 6b 4b b7 11 01 f4 4b ad 66 6a 11 13
52 \\ 0f e2 ee 82 9e 4d 02 9d c9 1c dd 67 16 db b9 06
53 \\ 18 86 ed c1 ba 94 21 02 03 01 00 01 a3 52 30 50
54 \\ 30 0e 06 03 55 1d 0f 01 01 ff 04 04 03 02 05 a0
55 \\ 30 1d 06 03 55 1d 25 04 16 30 14 06 08 2b 06 01
56 \\ 05 05 07 03 02 06 08 2b 06 01 05 05 07 03 01 30
57 \\ 1f 06 03 55 1d 23 04 18 30 16 80 14 89 4f de 5b
58 \\ cc 69 e2 52 cf 3e a3 00 df b1 97 b8 1d e1 c1 46
59 \\ 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 00 03
60 \\ 82 01 01 00 59 16 45 a6 9a 2e 37 79 e4 f6 dd 27
61 \\ 1a ba 1c 0b fd 6c d7 55 99 b5 e7 c3 6e 53 3e ff
62 \\ 36 59 08 43 24 c9 e7 a5 04 07 9d 39 e0 d4 29 87
63 \\ ff e3 eb dd 09 c1 cf 1d 91 44 55 87 0b 57 1d d1
64 \\ 9b df 1d 24 f8 bb 9a 11 fe 80 fd 59 2b a0 39 8c
65 \\ de 11 e2 65 1e 61 8c e5 98 fa 96 e5 37 2e ef 3d
66 \\ 24 8a fd e1 74 63 eb bf ab b8 e4 d1 ab 50 2a 54
67 \\ ec 00 64 e9 2f 78 19 66 0d 3f 27 cf 20 9e 66 7f
68 \\ ce 5a e2 e4 ac 99 c7 c9 38 18 f8 b2 51 07 22 df
69 \\ ed 97 f3 2e 3e 93 49 d4 c6 6c 9e a6 39 6d 74 44
70 \\ 62 a0 6b 42 c6 d5 ba 68 8e ac 3a 01 7b dd fc 8e
71 \\ 2c fc ad 27 cb 69 d3 cc dc a2 80 41 44 65 d3 ae
72 \\ 34 8c e0 f3 4a b2 fb 9c 61 83 71 31 2b 19 10 41
73 \\ 64 1c 23 7f 11 a5 d6 5c 84 4f 04 04 84 99 38 71
74 \\ 2b 95 9e d6 85 bc 5c 5d d6 45 ed 19 90 94 73 40
75 \\ 29 26 dc b4 0e 34 69 a1 59 41 e8 e2 cc a8 4b b6
76 \\ 08 46 36 a0
77);
78pub const server_key_exchange = hexToBytes(
79 \\ 16 03 03 01 2c 0c 00 01 28 03 00 1d 20 9f d7 ad
80 \\ 6d cf f4 29 8d d3 f9 6d 5b 1b 2a f9 10 a0 53 5b
81 \\ 14 88 d7 f8 fa bb 34 9a 98 28 80 b6 15 04 01 01
82 \\ 00 04 02 b6 61 f7 c1 91 ee 59 be 45 37 66 39 bd
83 \\ c3 d4 bb 81 e1 15 ca 73 c8 34 8b 52 5b 0d 23 38
84 \\ aa 14 46 67 ed 94 31 02 14 12 cd 9b 84 4c ba 29
85 \\ 93 4a aa cc e8 73 41 4e c1 1c b0 2e 27 2d 0a d8
86 \\ 1f 76 7d 33 07 67 21 f1 3b f3 60 20 cf 0b 1f d0
87 \\ ec b0 78 de 11 28 be ba 09 49 eb ec e1 a1 f9 6e
88 \\ 20 9d c3 6e 4f ff d3 6b 67 3a 7d dc 15 97 ad 44
89 \\ 08 e4 85 c4 ad b2 c8 73 84 12 49 37 25 23 80 9e
90 \\ 43 12 d0 c7 b3 52 2e f9 83 ca c1 e0 39 35 ff 13
91 \\ a8 e9 6b a6 81 a6 2e 40 d3 e7 0a 7f f3 58 66 d3
92 \\ d9 99 3f 9e 26 a6 34 c8 1b 4e 71 38 0f cd d6 f4
93 \\ e8 35 f7 5a 64 09 c7 dc 2c 07 41 0e 6f 87 85 8c
94 \\ 7b 94 c0 1c 2e 32 f2 91 76 9e ac ca 71 64 3b 8b
95 \\ 98 a9 63 df 0a 32 9b ea 4e d6 39 7e 8c d0 1a 11
96 \\ 0a b3 61 ac 5b ad 1c cd 84 0a 6c 8a 6e aa 00 1a
97 \\ 9d 7d 87 dc 33 18 64 35 71 22 6c 4d d2 c2 ac 41
98 \\ fb
99);
100pub const server_hello_done = hexToBytes("16 03 03 00 04 0e 00 00 00 ");
101pub const server_change_cipher_spec = hexToBytes("14 03 03 00 01 01 ");
102
103pub const server_handshake_finished = hexToBytes(
104 \\ 16 03 03 00 40 51 52 53 54 55 56 57 58 59 5a 5b
105 \\ 5c 5d 5e 5f 60 18 e0 75 31 7b 10 03 15 f6 08 1f
106 \\ cb f3 13 78 1a ac 73 ef e1 9f e2 5b a1 af 59 c2
107 \\ 0b e9 4f c0 1b da 2d 68 00 29 8b 73 a7 e8 49 d7
108 \\ 4b d4 94 cf 7d
109);
110pub const client_key_exchange_for_transcript = hexToBytes(
111 \\ 16 03 03 00 25 10 00 00 21 20 35 80 72 d6 36 58
112 \\ 80 d1 ae ea 32 9a df 91 21 38 38 51 ed 21 a2 8e
113 \\ 3b 75 e9 65 d0 d2 cd 16 62 54
114);
115
116pub const server_hello_responses = server_hello ++ server_certificate ++ server_key_exchange ++ server_hello_done;
117
118pub const server_responses = server_hello_responses ++ server_change_cipher_spec ++ server_handshake_finished;
119
120pub const server_handshake_finished_msgs = server_change_cipher_spec ++ server_handshake_finished;
121
122pub const master_secret = hexToBytes(
123 \\ 91 6a bf 9d a5 59 73 e1 36 14 ae 0a 3f 5d 3f 37
124 \\ b0 23 ba 12 9a ee 02 cc 91 34 33 81 27 cd 70 49
125 \\ 78 1c 8e 19 fc 1e b2 a7 38 7a c0 6a e2 37 34 4c
126);
127
128pub const client_key_exchange = hexToBytes(
129 \\ 16 03 03 00 25 10 00 00 21 20 00 01 02 03 04 05
130 \\ 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15
131 \\ 16 17 18 19 1a 1b 1c 1d 1e 1f
132);
133pub const client_change_cyper_spec = hexToBytes("14 03 03 00 01 01 ");
134
135pub const client_handshake_finished = hexToBytes(
136 \\ 16 03 03 00 40 20 21 22 23 24 25 26 27 28 29 2a
137 \\ 2b 2c 2d 2e 2f a9 ac f5 5a f3 7a 90 17 63 ff 91
138 \\ 68 9a b7 ee a0 d4 0c 1c ca 62 44 ef f3 0b a3 6d
139 \\ d0 df 86 3f 7d e3 98 d3 1a cc 37 6a e6 7a 00 6d
140 \\ 8c 08 bc 8a 5a
141);
142
143pub const handshake_messages = [_][]const u8{
144 &client_hello,
145 &server_hello,
146 &server_certificate,
147 &server_key_exchange,
148 &server_hello_done,
149 &client_key_exchange_for_transcript,
150};
151
152pub const client_finished = hexToBytes("14 00 00 0c cf 91 96 26 f1 36 0c 53 6a aa d7 3a ");
153
154// with iv 40 " ++ 41 ... 4f
155// client_sequence = 0
156pub const verify_data_encrypted_msg = hexToBytes(
157 \\ 16 03 03 00 40 40 41 42 43 44 45 46 47 48 49 4a
158 \\ 4b 4c 4d 4e 4f 22 7b c9 ba 81 ef 30 f2 a8 a7 8f
159 \\ f1 df 50 84 4d 58 04 b7 ee b2 e2 14 c3 2b 68 92
160 \\ ac a3 db 7b 78 07 7f dd 90 06 7c 51 6b ac b3 ba
161 \\ 90 de df 72 0f
162);
163
164// with iv 00 " ++ 01 ... 1f
165// client_sequence = 1
166pub const encrypted_ping_msg = hexToBytes(
167 \\ 17 03 03 00 30 00 01 02 03 04 05 06 07 08 09 0a
168 \\ 0b 0c 0d 0e 0f 6c 42 1c 71 c4 2b 18 3b fa 06 19
169 \\ 5d 13 3d 0a 09 d0 0f c7 cb 4e 0f 5d 1c da 59 d1
170 \\ 47 ec 79 0c 99
171);
172
173pub const key_material = hexToBytes(
174 \\ 1b 7d 11 7c 7d 5f 69 0b c2 63 ca e8 ef 60 af 0f
175 \\ 18 78 ac c2 2a d8 bd d8 c6 01 a6 17 12 6f 63 54
176 \\ 0e b2 09 06 f7 81 fa d2 f6 56 d0 37 b1 73 ef 3e
177 \\ 11 16 9f 27 23 1a 84 b6 75 2a 18 e7 a9 fc b7 cb
178 \\ cd d8 f9 8d d8 f7 69 eb a0 d2 55 0c 92 38 ee bf
179 \\ ef 5c 32 25 1a bb 67 d6 43 45 28 db 49 37 d5 40
180 \\ d3 93 13 5e 06 a1 1b b8 0e 45 ea eb e3 2c ac 72
181 \\ 75 74 38 fb b3 df 64 5c bd a4 06 7c df a0 f8 48
182);
183
184pub const server_pong = hexToBytes(
185 \\ 17 03 03 00 30 61 62 63 64 65 66 67 68 69 6a 6b
186 \\ 6c 6d 6e 6f 70 97 83 48 8a f5 fa 20 bf 7a 2e f6
187 \\ 9d eb b5 34 db 9f b0 7a 8c 27 21 de e5 40 9f 77
188 \\ af 0c 3d de 56
189);
190
191pub const client_random = hexToBytes(
192 \\ 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
193);
194
195pub const server_random = hexToBytes(
196 \\ 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f
197);
198
199pub const client_secret = hexToBytes(
200 \\ 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f
201);
202
203pub const server_pub_key = hexToBytes(
204 \\ 9f d7 ad 6d cf f4 29 8d d3 f9 6d 5b 1b 2a f9 10 a0 53 5b 14 88 d7 f8 fa bb 34 9a 98 28 80 b6 15
205);
206
207pub const signature = hexToBytes(
208 \\ 04 02 b6 61 f7 c1 91 ee 59 be 45 37 66 39 bd c3
209 \\ d4 bb 81 e1 15 ca 73 c8 34 8b 52 5b 0d 23 38 aa
210 \\ 14 46 67 ed 94 31 02 14 12 cd 9b 84 4c ba 29 93
211 \\ 4a aa cc e8 73 41 4e c1 1c b0 2e 27 2d 0a d8 1f
212 \\ 76 7d 33 07 67 21 f1 3b f3 60 20 cf 0b 1f d0 ec
213 \\ b0 78 de 11 28 be ba 09 49 eb ec e1 a1 f9 6e 20
214 \\ 9d c3 6e 4f ff d3 6b 67 3a 7d dc 15 97 ad 44 08
215 \\ e4 85 c4 ad b2 c8 73 84 12 49 37 25 23 80 9e 43
216 \\ 12 d0 c7 b3 52 2e f9 83 ca c1 e0 39 35 ff 13 a8
217 \\ e9 6b a6 81 a6 2e 40 d3 e7 0a 7f f3 58 66 d3 d9
218 \\ 99 3f 9e 26 a6 34 c8 1b 4e 71 38 0f cd d6 f4 e8
219 \\ 35 f7 5a 64 09 c7 dc 2c 07 41 0e 6f 87 85 8c 7b
220 \\ 94 c0 1c 2e 32 f2 91 76 9e ac ca 71 64 3b 8b 98
221 \\ a9 63 df 0a 32 9b ea 4e d6 39 7e 8c d0 1a 11 0a
222 \\ b3 61 ac 5b ad 1c cd 84 0a 6c 8a 6e aa 00 1a 9d
223 \\ 7d 87 dc 33 18 64 35 71 22 6c 4d d2 c2 ac 41 fb
224);
225
226pub const cert_pub_key = hexToBytes(
227 \\ 30 82 01 0a 02 82 01 01 00 c4 80 36 06 ba e7 47
228 \\ 6b 08 94 04 ec a7 b6 91 04 3f f7 92 bc 19 ee fb
229 \\ 7d 74 d7 a8 0d 00 1e 7b 4b 3a 4a e6 0f e8 c0 71
230 \\ fc 73 e7 02 4c 0d bc f4 bd d1 1d 39 6b ba 70 46
231 \\ 4a 13 e9 4a f8 3d f3 e1 09 59 54 7b c9 55 fb 41
232 \\ 2d a3 76 52 11 e1 f3 dc 77 6c aa 53 37 6e ca 3a
233 \\ ec be c3 aa b7 3b 31 d5 6c b6 52 9c 80 98 bc c9
234 \\ e0 28 18 e2 0b f7 f8 a0 3a fd 17 04 50 9e ce 79
235 \\ bd 9f 39 f1 ea 69 ec 47 97 2e 83 0f b5 ca 95 de
236 \\ 95 a1 e6 04 22 d5 ee be 52 79 54 a1 e7 bf 8a 86
237 \\ f6 46 6d 0d 9f 16 95 1a 4c f7 a0 46 92 59 5c 13
238 \\ 52 f2 54 9e 5a fb 4e bf d7 7a 37 95 01 44 e4 c0
239 \\ 26 87 4c 65 3e 40 7d 7d 23 07 44 01 f4 84 ff d0
240 \\ 8f 7a 1f a0 52 10 d1 f4 f0 d5 ce 79 70 29 32 e2
241 \\ ca be 70 1f df ad 6b 4b b7 11 01 f4 4b ad 66 6a
242 \\ 11 13 0f e2 ee 82 9e 4d 02 9d c9 1c dd 67 16 db
243 \\ b9 06 18 86 ed c1 ba 94 21 02 03 01 00 01
244);
packages/zig/vendor/zig-tls/src/testdata/tls13.zigdeleted+0-69
Changes to packages/zig/vendor/zig-tls/src/testdata/tls13.zig
@@ -1,69 +0,0 @@
1const hexToBytes = .hexToBytes;
2
3pub const client_hello =
4 hexToBytes("16030100f8010000f40303000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20e0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000813021303130100ff010000a30000001800160000136578616d706c652e756c666865696d2e6e6574000b000403000102000a00160014001d0017001e0019001801000101010201030104002300000016000000170000000d001e001c040305030603080708080809080a080b080408050806040105010601002b0003020304002d00020101003300260024001d0020358072d6365880d1aeea329adf9121383851ed21a28e3b75e965d0d2cd166254");
5
6pub const server_hello =
7 hexToBytes("160303007a") ++ // record header
8 hexToBytes("020000760303") ++ // handshake header, server version
9 hexToBytes("707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f") ++ // server_random
10 hexToBytes("20e0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff") ++ // session id
11 hexToBytes("130200") ++ // cipher suite, compression method
12 hexToBytes("002e002b00020304") ++ // extensions, supported version
13 hexToBytes("00330024001d00209fd7ad6dcff4298dd3f96d5b1b2af910a0535b1488d7f8fabb349a982880b615"); // extension key share
14
15pub const client_random = hexToBytes(
16 \\ 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
17);
18
19pub const server_random =
20 hexToBytes("707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f");
21pub const server_pub_key =
22 hexToBytes("9fd7ad6dcff4298dd3f96d5b1b2af910a0535b1488d7f8fabb349a982880b615");
23pub const client_private_key =
24 hexToBytes("202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f");
25pub const client_public_key =
26 hexToBytes("358072d6365880d1aeea329adf9121383851ed21a28e3b75e965d0d2cd166254");
27
28pub const shared_key = hexToBytes("df4a291baa1eb7cfa6934b29b474baad2697e29f1f920dcc77c8a0a088447624");
29pub const server_handshake_key = hexToBytes("9f13575ce3f8cfc1df64a77ceaffe89700b492ad31b4fab01c4792be1b266b7f");
30pub const server_handshake_iv = hexToBytes("9563bc8b590f671f488d2da3");
31pub const client_handshake_key = hexToBytes("1135b4826a9a70257e5a391ad93093dfd7c4214812f493b3e3daae1eb2b1ac69");
32pub const client_handshake_iv = hexToBytes("4256d2e0e88babdd05eb2f27");
33
34pub const server_application_key = hexToBytes("01f78623f17e3edcc09e944027ba3218d57c8e0db93cd3ac419309274700ac27");
35pub const server_application_iv = hexToBytes("196a750b0c5049c0cc51a541");
36pub const client_application_key = hexToBytes("de2f4c7672723a692319873e5c227606691a32d1c59d8b9f51dbb9352e9ca9cc");
37pub const client_application_iv = hexToBytes("bb007956f474b25de902432f");
38
39pub const server_encrypted_extensions_wrapped =
40 hexToBytes("17030300176be02f9da7c2dc9ddef56f2468b90adfa25101ab0344ae");
41pub const server_encrypted_extensions =
42 hexToBytes("080000020000");
43
44pub const server_certificate_wrapped =
45 hexToBytes("1703030343baf00a9be50f3f2307e726edcbdacbe4b18616449d46c6207af6e9953ee5d2411ba65d31feaf4f78764f2d693987186cc01329c187a5e4608e8d27b318e98dd94769f7739ce6768392caca8dcc597d77ec0d1272233785f6e69d6f43effa8e7905edfdc4037eee5933e990a7972f206913a31e8d04931366d3d8bcd6a4a4d647dd4bd80b0ff863ce3554833d744cf0e0b9c07cae726dd23f9953df1f1ce3aceb3b7230871e92310cfb2b098486f43538f8e82d8404e5c6c25f66a62ebe3c5f26232640e20a769175ef83483cd81e6cb16e78dfad4c1b714b04b45f6ac8d1065ad18c13451c9055c47da300f93536ea56f531986d6492775393c4ccb095467092a0ec0b43ed7a0687cb470ce350917b0ac30c6e5c24725a78c45f9f5f29b6626867f6f79ce054273547b36df030bd24af10d632dba54fc4e890bd0586928c0206ca2e28e44e227a2d5063195935df38da8936092eef01e84cad2e49d62e470a6c7745f625ec39e4fc23329c79d1172876807c36d736ba42bb69b004ff55f93850dc33c1f98abb92858324c76ff1eb085db3c1fc50f74ec04442e622973ea70743418794c388140bb492d6294a0540e5a59cfae60ba0f14899fca71333315ea083a68e1d7c1e4cdc2f56bcd6119681a4adbc1bbf42afd806c3cbd42a076f545dee4e118d0b396754be2b042a685dd4727e89c0386a94d3cd6ecb9820e9d49afeed66c47e6fc243eabebbcb0b02453877f5ac5dbfbdf8db1052a3c994b224cd9aaaf56b026bb9efa2e01302b36401ab6494e7018d6e5b573bd38bcef023b1fc92946bbca0209ca5fa926b4970b1009103645cb1fcfe552311ff730558984370038fd2cce2a91fc74d6f3e3ea9f843eed356f6f82d35d03bc24b81b58ceb1a43ec9437e6f1e50eb6f555e321fd67c8332eb1b832aa8d795a27d479c6e27d5a61034683891903f66421d094e1b00a9a138d861e6f78a20ad3e1580054d2e305253c713a02fe1e28deee7336246f6ae34331806b46b47b833c39b9d31cd300c2a6ed831399776d07f570eaf0059a2c68a5f3ae16b617404af7b7231a4d942758fc020b3f23ee8c15e36044cfd67cd640993b16207597fbf385ea7a4d99e8d456ff83d41f7b8b4f069b028a2a63a919a70e3a10e3084158faa5bafa30186c6b2f238eb530c73e");
46pub const server_certificate =
47 hexToBytes("0b00032e0000032a0003253082032130820209a0030201020208155a92adc2048f90300d06092a864886f70d01010b05003022310b300906035504061302555331133011060355040a130a4578616d706c65204341301e170d3138313030353031333831375a170d3139313030353031333831375a302b310b3009060355040613025553311c301a060355040313136578616d706c652e756c666865696d2e6e657430820122300d06092a864886f70d01010105000382010f003082010a0282010100c4803606bae7476b089404eca7b691043ff792bc19eefb7d74d7a80d001e7b4b3a4ae60fe8c071fc73e7024c0dbcf4bdd11d396bba70464a13e94af83df3e10959547bc955fb412da3765211e1f3dc776caa53376eca3aecbec3aab73b31d56cb6529c8098bcc9e02818e20bf7f8a03afd1704509ece79bd9f39f1ea69ec47972e830fb5ca95de95a1e60422d5eebe527954a1e7bf8a86f6466d0d9f16951a4cf7a04692595c1352f2549e5afb4ebfd77a37950144e4c026874c653e407d7d23074401f484ffd08f7a1fa05210d1f4f0d5ce79702932e2cabe701fdfad6b4bb71101f44bad666a11130fe2ee829e4d029dc91cdd6716dbb9061886edc1ba94210203010001a3523050300e0603551d0f0101ff0404030205a0301d0603551d250416301406082b0601050507030206082b06010505070301301f0603551d23041830168014894fde5bcc69e252cf3ea300dfb197b81de1c146300d06092a864886f70d01010b05000382010100591645a69a2e3779e4f6dd271aba1c0bfd6cd75599b5e7c36e533eff3659084324c9e7a504079d39e0d42987ffe3ebdd09c1cf1d914455870b571dd19bdf1d24f8bb9a11fe80fd592ba0398cde11e2651e618ce598fa96e5372eef3d248afde17463ebbfabb8e4d1ab502a54ec0064e92f7819660d3f27cf209e667fce5ae2e4ac99c7c93818f8b2510722dfed97f32e3e9349d4c66c9ea6396d744462a06b42c6d5ba688eac3a017bddfc8e2cfcad27cb69d3ccdca280414465d3ae348ce0f34ab2fb9c618371312b191041641c237f11a5d65c844f0404849938712b959ed685bc5c5dd645ed19909473402926dcb40e3469a15941e8e2cca84bb6084636a00000");
48
49pub const server_certificate_verify_wrapped = hexToBytes("170303011973719fce07ec2f6d3bba0292a0d40b2770c06a271799a53314f6f77fc95c5fe7b9a4329fd9548c670ebeea2f2d5c351dd9356ef2dcd52eb137bd3a676522f8cd0fb7560789ad7b0e3caba2e37e6b4199c6793b3346ed46cf740a9fa1fec414dc715c415c60e575703ce6a34b70b5191aa6a61a18faff216c687ad8d17e12a7e99915a611bfc1a2befc15e6e94d784642e682fd17382a348c301056b940c9847200408bec56c81ea3d7217ab8e85a88715395899c90587f72e8ddd74b26d8edc1c7c837d9f2ebbc260962219038b05654a63a0b12999b4a8306a3ddcc0e17c53ba8f9c80363f7841354d291b4ace0c0f330c0fcd5aa9deef969ae8ab2d98da88ebb6ea80a3a11f00ea296a3232367ff075e1c66dd9cbedc4713");
50pub const server_finished_wrapped = hexToBytes("17030300451061de27e51c2c9f342911806f282b710c10632ca5006755880dbf7006002d0e84fed9adf27a43b5192303e4df5c285d58e3c76224078440c0742374744aecf28cf3182fd0");
51
52pub const handshake_hash = hexToBytes("fa6800169a6baac19159524fa7b9721b41be3c9db6f3f93fa5ff7e3db3ece204d2b456c51046e40ec5312c55a86126f5");
53
54pub const client_finished_verify_data = hexToBytes("bff56a671b6c659d0a7c5dd18428f58bdd38b184a3ce342d9fde95cbd5056f7da7918ee320eab7a93abd8f1c02454d27");
55
56pub const client_finished_wrapped = hexToBytes("17030300459ff9b063175177322a46dd9896f3c3bb820ab51743ebc25fdadd53454b73deb54cc7248d411a18bccf657a960824e9a19364837c350a69a88d4bf635c85eb874aebc9dfde8");
57
58pub const client_ping_wrapped = hexToBytes("1703030015828139cb7b73aaabf5b82fbf9a2961bcde10038a32");
59pub const server_flight =
60 hexToBytes("140303000101") ++
61 server_encrypted_extensions_wrapped ++
62 server_certificate_wrapped ++
63 server_certificate_verify_wrapped ++
64 server_finished_wrapped;
65
66pub const server_flight_1 = server_hello ++ server_flight;
67
68pub const client_flight_2 =
69 hexToBytes("140303000101") ++ client_finished_wrapped;
packages/zig/vendor/zig-tls/src/testu.zigdeleted+0-117
Changes to packages/zig/vendor/zig-tls/src/testu.zig
@@ -1,117 +0,0 @@
1const std = ;
2
3pub fn bufPrint(var_name: []const u8, buf: []const u8) void {
4 // std.debug.print("\nconst {s} = [_]u8;\n", .{});
11
12 std.debug.print("const {s} = \"", .{var_name});
13 const charset = "0123456789abcdef";
14 for (buf) |b| {
15 const x = charset[b >> 4];
16 const y = charset[b & 15];
17 std.debug.print("{c}{c} ", .{ x, y });
18 }
19 std.debug.print("\"\n", .{});
20}
21
22const random_instance = std.Random{ .ptr = undefined, .fillFn = randomFillFn };
23var random_seed: u8 = 0;
24
25pub fn randomFillFn(_: *anyopaque, buf: []u8) void {
26 for (buf) |*v| {
27 v.* = random_seed;
28 random_seed +%= 1;
29 }
30}
31
32pub fn random(seed: u8) std.Random {
33 random_seed = seed;
34 return random_instance;
35}
36
37// Fill buf with 0,1,..ff,0,...
38pub fn fill(buf: []u8) void {
39 fillFrom(buf, 0);
40}
41
42pub fn fillFrom(buf: []u8, start: u8) void {
43 var i: u8 = start;
44 for (buf) |*v| {
45 v.* = i;
46 i +%= 1;
47 }
48}
49
50pub const Stream = struct {
51 output: std.io.FixedBufferStream([]u8) = undefined,
52 input: std.io.FixedBufferStream([]const u8) = undefined,
53
54 pub fn init(input: []const u8, output: []u8) Stream {
55 return .{
56 .input = std.io.fixedBufferStream(input),
57 .output = std.io.fixedBufferStream(output),
58 };
59 }
60
61 pub const ReadError = error{};
62 pub const WriteError = error{NoSpaceLeft};
63
64 pub fn write(self: *Stream, buf: []const u8) !usize {
65 return try self.output.writer().write(buf);
66 }
67
68 pub fn writeAll(self: *Stream, buffer: []const u8) !void {
69 var n: usize = 0;
70 while (n < buffer.len) {
71 n += try self.write(buffer[n..]);
72 }
73 }
74
75 pub fn read(self: *Stream, buffer: []u8) !usize {
76 return self.input.read(buffer);
77 }
78};
79
80// Copied from: https://github.com/clickingbuttons/zig/blob/f1cea91624fd2deae28bfb2414a4fd9c7e246883/lib/std/crypto/rsa.zig#L791
81/// For readable copy/pasting from hex viewers.
82pub fn hexToBytes(comptime hex: []const u8) [removeNonHex(hex).len / 2]u8 {
83 @setEvalBranchQuota(1000 * 100);
84 const hex2 = comptime removeNonHex(hex);
85 comptime var res: [hex2.len / 2]u8 = undefined;
86 _ = comptime std.fmt.hexToBytes(&res, hex2) catch unreachable;
87 return res;
88}
89
90fn removeNonHex(comptime hex: []const u8) []const u8 {
91 @setEvalBranchQuota(1000 * 100);
92 var res: [hex.len]u8 = undefined;
93 var i: usize = 0;
94 for (hex) |c| {
95 if (std.ascii.isHex(c)) {
96 res[i] = c;
97 i += 1;
98 }
99 }
100 return res[0..i];
101}
102
103test hexToBytes {
104 const hex =
105 \\e3b0c442 98fc1c14 9afbf4c8 996fb924
106 \\27ae41e4 649b934c a495991b 7852b855
107 ;
108 try std.testing.expectEqual(
109 [_]u8{
110 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14,
111 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
112 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c,
113 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55,
114 },
115 hexToBytes(hex),
116 );
117}
packages/zig/vendor/zig-tls/src/transcript.zigdeleted+0-454
Changes to packages/zig/vendor/zig-tls/src/transcript.zig
@@ -1,454 +0,0 @@
1const std = ;
2const crypto = std.crypto;
3const tls = crypto.tls;
4const hkdfExpandLabel = tls.hkdfExpandLabel;
5
6const Sha256 = crypto.hash.sha2.Sha256;
7const Sha384 = crypto.hash.sha2.Sha384;
8const Sha512 = crypto.hash.sha2.Sha512;
9
10const HashTag = .CipherSuite.HashTag;
11
12// Transcript holds hash of all handshake message.
13//
14// Until the server hello is parsed we don't know which hash (sha256, sha384,
15// sha512) will be used so we update all of them. Handshake process will set
16// `selected` field once cipher suite is known. Other function will use that
17// selected hash. We continue to calculate all hashes because client certificate
18// message could use different hash than the other part of the handshake.
19// Handshake hash is dictated by the server selected cipher. Client certificate
20// hash is dictated by the private key used.
21//
22// Most of the functions are inlined because they are returning pointers.
23//
24pub const Transcript = struct {
25 sha256: Type(.sha256) = .{ .hash = Sha256.init(.{}) },
26 sha384: Type(.sha384) = .{ .hash = Sha384.init(.{}) },
27 sha512: Type(.sha512) = .{ .hash = Sha512.init(.{}) },
28
29 tag: HashTag = .sha256,
30
31 // Transcript Type from hash tag
32 fn Type(h: HashTag) type {
33 return switch (h) {
34 .sha256 => TranscriptT(Sha256),
35 .sha384 => TranscriptT(Sha384),
36 .sha512 => TranscriptT(Sha512),
37 };
38 }
39
40 /// Set hash to use in all following function calls.
41 pub fn use(t: *Transcript, tag: HashTag) void {
42 t.tag = tag;
43 }
44
45 pub fn update(t: *Transcript, buf: []const u8) void {
46 t.sha256.hash.update(buf);
47 t.sha384.hash.update(buf);
48 t.sha512.hash.update(buf);
49 }
50
51 // tls 1.2 handshake specific
52
53 pub fn masterSecret(
54 t: *Transcript,
55 out: []u8,
56 pre_master_secret: []const u8,
57 client_random: [32]u8,
58 server_random: [32]u8,
59 ) void {
60 switch (t.tag) {
61 inline else => |h| @field(t, @tagName(h)).masterSecret(
62 out,
63 pre_master_secret,
64 client_random,
65 server_random,
66 ),
67 }
68 }
69
70 pub fn keyMaterial(
71 t: *Transcript,
72 out: []u8,
73 master_secret: []const u8,
74 client_random: [32]u8,
75 server_random: [32]u8,
76 ) void {
77 switch (t.tag) {
78 inline else => |h| @field(t, @tagName(h)).keyExpansion(
79 out,
80 master_secret,
81 client_random,
82 server_random,
83 ),
84 }
85 }
86
87 pub fn clientFinishedTls12(t: *Transcript, master_secret: []const u8) [12]u8 {
88 return switch (t.tag) {
89 inline else => |h| @field(t, @tagName(h)).clientFinishedTls12(master_secret),
90 };
91 }
92
93 pub fn serverFinishedTls12(t: *Transcript, master_secret: []const u8) [12]u8 {
94 return switch (t.tag) {
95 inline else => |h| @field(t, @tagName(h)).serverFinishedTls12(master_secret),
96 };
97 }
98
99 // tls 1.3 handshake specific
100
101 pub fn setPreSharedSecret(
102 t: *Transcript,
103 resumption_master_secret: []const u8,
104 ticket_nonce: []const u8,
105 ) void {
106 switch (t.tag) {
107 inline else => |h| @field(t, @tagName(h)).setPreSharedSecret(resumption_master_secret, ticket_nonce),
108 }
109 }
110
111 pub fn clearPreSharedSecret(t: *Transcript) void {
112 switch (t.tag) {
113 inline else => |h| @field(t, @tagName(h)).clearPreSharedSecret(),
114 }
115 }
116
117 pub fn serverCertificateVerify(t: *Transcript) []const u8 {
118 return switch (t.tag) {
119 inline else => |h| @field(t, @tagName(h)).serverCertificateVerify(),
120 };
121 }
122
123 pub fn clientCertificateVerify(t: *Transcript) []const u8 {
124 return switch (t.tag) {
125 inline else => |h| @field(t, @tagName(h)).clientCertificateVerify(),
126 };
127 }
128
129 pub fn serverFinishedTls13(t: *Transcript) []const u8 {
130 return switch (t.tag) {
131 inline else => |h| @field(t, @tagName(h)).serverFinishedTls13(),
132 };
133 }
134
135 pub fn clientFinishedTls13(t: *Transcript) []const u8 {
136 return switch (t.tag) {
137 inline else => |h| @field(t, @tagName(h)).clientFinishedTls13(),
138 };
139 }
140
141 pub const Secret = struct {
142 client: []const u8,
143 server: []const u8,
144 };
145
146 pub fn handshakeSecret(t: *Transcript, shared_key: []const u8) Secret {
147 return switch (t.tag) {
148 inline else => |h| @field(t, @tagName(h)).handshakeSecret(shared_key),
149 };
150 }
151
152 pub fn applicationSecret(t: *Transcript) Secret {
153 return switch (t.tag) {
154 inline else => |h| @field(t, @tagName(h)).applicationSecret(),
155 };
156 }
157
158 pub fn resumptionSecret(t: *Transcript) []const u8 {
159 return switch (t.tag) {
160 inline else => |h| @field(t, @tagName(h)).resumptionSecret(),
161 };
162 }
163
164 pub fn pskBinder(t: *Transcript) []const u8 {
165 return switch (t.tag) {
166 inline else => |h| @field(t, @tagName(h)).pskBinder(),
167 };
168 }
169
170 pub fn hashLength(t: *Transcript) u8 {
171 return switch (t.tag) {
172 inline else => |h| @TypeOf(@field(t, @tagName(h))).hash_length,
173 };
174 }
175
176 // other
177
178 pub fn Hkdf(h: HashTag) type {
179 return Type(h).Hkdf;
180 }
181
182 /// Copy of the current hash value
183 pub fn hash(t: *Transcript, comptime Hash: type) Hash {
184 return switch (Hash) {
185 Sha256 => t.sha256.hash,
186 Sha384 => t.sha384.hash,
187 Sha512 => t.sha512.hash,
188 else => @compileError("unimplemented"),
189 };
190 }
191};
192
193fn TranscriptT(comptime Hash: type) type {
194 return struct {
195 const Hmac = crypto.auth.hmac.Hmac(Hash);
196 const Hkdf = crypto.kdf.hkdf.Hkdf(Hmac);
197 const hash_length = Hash.digest_length; // Hmac.mac_length == Hmac.key_length == Hash.digest_length
198
199 hash: Hash,
200 handshake_secret: ?[hash_length]u8 = null,
201 server_finished_key: [hash_length]u8 = undefined,
202 client_finished_key: [hash_length]u8 = undefined,
203 buffer: [hash_length + 64 + 34]u8 = undefined,
204
205 const Self = @This();
206
207 fn init(transcript: Hash) Self {
208 return .{ .transcript = transcript };
209 }
210
211 fn serverCertificateVerify(self: *Self) []const u8 {
212 self.buffer = @as([64]u8, @splat(0x20)) ++
213 "TLS 1.3, server CertificateVerify\x00".* ++
214 self.hash.peek();
215 return &self.buffer;
216 }
217
218 // ref: https://www.rfc-editor.org/rfc/rfc8446#section-4.4.3
219 fn clientCertificateVerify(self: *Self) []u8 {
220 self.buffer = @as([64]u8, @splat(0x20)) ++
221 "TLS 1.3, client CertificateVerify\x00".* ++
222 self.hash.peek();
223 return &self.buffer;
224 }
225
226 fn masterSecret(
227 _: *Self,
228 out: []u8,
229 pre_master_secret: []const u8,
230 client_random: [32]u8,
231 server_random: [32]u8,
232 ) void {
233 const seed = "master secret" ++ client_random ++ server_random;
234
235 var a1: [hash_length]u8 = undefined;
236 var a2: [hash_length]u8 = undefined;
237 Hmac.create(&a1, seed, pre_master_secret);
238 Hmac.create(&a2, &a1, pre_master_secret);
239
240 var p1: [hash_length]u8 = undefined;
241 var p2: [hash_length]u8 = undefined;
242 Hmac.create(&p1, a1 ++ seed, pre_master_secret);
243 Hmac.create(&p2, a2 ++ seed, pre_master_secret);
244
245 const master_secret = p1 ++ p2;
246 const len = @min(out.len, master_secret.len);
247 @memcpy(out[0..len], master_secret[0..len]);
248 }
249
250 fn keyExpansion(
251 _: *Self,
252 out: []u8,
253 master_secret: []const u8,
254 client_random: [32]u8,
255 server_random: [32]u8,
256 ) void {
257 const seed = "key expansion" ++ server_random ++ client_random;
258
259 const a0 = seed;
260 var a1: [hash_length]u8 = undefined;
261 var a2: [hash_length]u8 = undefined;
262 var a3: [hash_length]u8 = undefined;
263 var a4: [hash_length]u8 = undefined;
264 Hmac.create(&a1, a0, master_secret);
265 Hmac.create(&a2, &a1, master_secret);
266 Hmac.create(&a3, &a2, master_secret);
267 Hmac.create(&a4, &a3, master_secret);
268
269 var key_material: [hash_length * 4]u8 = undefined;
270 Hmac.create(key_material[0..hash_length], a1 ++ seed, master_secret);
271 Hmac.create(key_material[hash_length .. hash_length * 2], a2 ++ seed, master_secret);
272 Hmac.create(key_material[hash_length * 2 .. hash_length * 3], a3 ++ seed, master_secret);
273 Hmac.create(key_material[hash_length * 3 ..], a4 ++ seed, master_secret);
274
275 const len = @min(out.len, key_material.len);
276 @memcpy(out[0..len], key_material[0..len]);
277 }
278
279 fn clientFinishedTls12(self: *Self, master_secret: []const u8) [12]u8 {
280 const seed = "client finished" ++ self.hash.peek();
281 var a1: [hash_length]u8 = undefined;
282 var p1: [hash_length]u8 = undefined;
283 Hmac.create(&a1, seed, master_secret);
284 Hmac.create(&p1, a1 ++ seed, master_secret);
285 return p1[0..12].*;
286 }
287
288 fn serverFinishedTls12(self: *Self, master_secret: []const u8) [12]u8 {
289 const seed = "server finished" ++ self.hash.peek();
290 var a1: [hash_length]u8 = undefined;
291 var p1: [hash_length]u8 = undefined;
292 Hmac.create(&a1, seed, master_secret);
293 Hmac.create(&p1, a1 ++ seed, master_secret);
294 return p1[0..12].*;
295 }
296
297 // tls 1.3
298
299 fn setPreSharedSecret(
300 self: *Self,
301 resumption_secret: []const u8,
302 ticket_nonce: []const u8,
303 ) void {
304 const ikm = hkdfExpandLabel(
305 Hkdf,
306 resumption_secret[0..hash_length].*,
307 "resumption",
308 ticket_nonce,
309 hash_length,
310 );
311 self.handshake_secret = Hkdf.extract(&[1]u8{0}, &ikm);
312 }
313
314 fn clearPreSharedSecret(self: *Self) void {
315 self.handshake_secret = null;
316 }
317
318 fn handshakeSecret(self: *Self, shared_key: []const u8) Transcript.Secret {
319 const hello_hash = self.hash.peek();
320
321 const empty_hash = tls.emptyHash(Hash);
322 const zeroes: [hash_length]u8 = @splat(0);
323 const early_secret = if (self.handshake_secret) |hs| hs else Hkdf.extract(&[1]u8{0}, &zeroes);
324 const hs_derived_secret = hkdfExpandLabel(Hkdf, early_secret, "derived", &empty_hash, hash_length);
325
326 const secret = Hkdf.extract(&hs_derived_secret, shared_key);
327 self.handshake_secret = secret;
328 const client_secret = hkdfExpandLabel(Hkdf, secret, "c hs traffic", &hello_hash, hash_length);
329 const server_secret = hkdfExpandLabel(Hkdf, secret, "s hs traffic", &hello_hash, hash_length);
330
331 self.server_finished_key = hkdfExpandLabel(Hkdf, server_secret, "finished", "", hash_length);
332 self.client_finished_key = hkdfExpandLabel(Hkdf, client_secret, "finished", "", hash_length);
333
334 self.buffer[0..hash_length].* = client_secret;
335 self.buffer[hash_length .. 2 * hash_length].* = server_secret;
336 return .{
337 .client = self.buffer[0..hash_length],
338 .server = self.buffer[hash_length .. 2 * hash_length],
339 };
340 }
341
342 fn applicationSecret(self: *Self) Transcript.Secret {
343 const handshake_hash = self.hash.peek();
344
345 const empty_hash = tls.emptyHash(Hash);
346 const zeroes: [hash_length]u8 = @splat(0);
347 const ap_derived_secret = hkdfExpandLabel(Hkdf, self.handshake_secret.?, "derived", &empty_hash, hash_length);
348 const master_secret = Hkdf.extract(&ap_derived_secret, &zeroes);
349
350 self.buffer[0..hash_length].* = hkdfExpandLabel(Hkdf, master_secret, "c ap traffic", &handshake_hash, hash_length);
351 self.buffer[hash_length .. 2 * hash_length].* = hkdfExpandLabel(Hkdf, master_secret, "s ap traffic", &handshake_hash, hash_length);
352 return .{
353 .client = self.buffer[0..hash_length],
354 .server = self.buffer[hash_length .. 2 * hash_length],
355 };
356 }
357
358 fn resumptionSecret(self: *Self) []const u8 {
359 const handshake_hash = self.hash.peek();
360 const empty_hash = tls.emptyHash(Hash);
361 const zeroes: [hash_length]u8 = @splat(0);
362 const ap_derived_secret = hkdfExpandLabel(Hkdf, self.handshake_secret.?, "derived", &empty_hash, hash_length);
363 const master_secret = Hkdf.extract(&ap_derived_secret, &zeroes);
364 self.buffer[0..hash_length].* = hkdfExpandLabel(Hkdf, master_secret, "res master", &handshake_hash, hash_length);
365 return self.buffer[0..hash_length];
366 }
367
368 fn pskBinder(self: *Self) []const u8 {
369 const secret = self.handshake_secret.?;
370 const prk = hkdfExpandLabel(Hkdf, secret, "res binder", &tls.emptyHash(Hash), hash_length);
371 const expanded = hkdfExpandLabel(Hkdf, prk, "finished", "", hash_length);
372 Hmac.create(self.buffer[0..hash_length], &self.hash.peek(), &expanded);
373 return self.buffer[0..hash_length];
374 }
375
376 fn serverFinishedTls13(self: *Self) []const u8 {
377 Hmac.create(self.buffer[0..hash_length], &self.hash.peek(), &self.server_finished_key);
378 return self.buffer[0..hash_length];
379 }
380
381 // client finished message with header
382 fn clientFinishedTls13(self: *Self) []const u8 {
383 Hmac.create(self.buffer[0..hash_length], &self.hash.peek(), &self.client_finished_key);
384 return self.buffer[0..hash_length];
385 }
386 };
387}
388
389const hexToBytes = .hexToBytes;
390const testing = std.testing;
391
392fn pskBinder_(
393 comptime Hash: type,
394 resumption_master_secret: [Hash.digest_length]u8,
395 binder: *[Hash.digest_length]u8,
396 binder_hash: [Hash.digest_length]u8,
397 ticket_nonce: []const u8,
398) void {
399 const Hmac = crypto.auth.hmac.Hmac(Hash);
400 const Hkdf = crypto.kdf.hkdf.Hkdf(Hmac);
401
402 const ikm = hkdfExpandLabel(Hkdf, resumption_master_secret, "resumption", ticket_nonce, Hash.digest_length);
403 const secret = Hkdf.extract(&[1]u8{0}, &ikm);
404 const prk = hkdfExpandLabel(Hkdf, secret, "res binder", &tls.emptyHash(Hash), Hash.digest_length);
405 const expanded = hkdfExpandLabel(Hkdf, prk, "finished", "", Hash.digest_length);
406 Hmac.create(binder, &binder_hash, &expanded);
407}
408
409// Example from: https://datatracker.ietf.org/doc/html/rfc8448#autoid-4
410test pskBinder_ {
411 // input values from example
412 const resumption_master_secret = hexToBytes("7d f2 35 f2 03 1d 2a 05 12 87 d0 2b 02 41 b0 bf da f8 6c c8 56 23 1f 2d 5a ba 46 c4 34 ec 19 6c");
413 const binder_hash = hexToBytes("63 22 4b 2e 45 73 f2 d3 45 4c a8 4b 9d 00 9a 04 f6 be 9e 05 71 1a 83 96 47 3a ef a0 1e 92 4a 14");
414 const ticket_nonce = hexToBytes("00 00");
415 // expected intermediate and resulting finished from example
416 const expected_ikm = hexToBytes("4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c a4 c5 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3");
417 const expected_secret = hexToBytes("9b 21 88 e9 b2 fc 6d 64 d7 1d c3 29 90 0e 20 bb 41 91 50 00 f6 78 aa 83 9c bb 79 7c b7 d8 33 2c");
418 const expected_prk = hexToBytes("69 fe 13 1a 3b ba d5 d6 3c 64 ee bc c3 0e 39 5b 9d 81 07 72 6a 13 d0 74 e3 89 db c8 a4 e4 72 56");
419 const expected_expanded = hexToBytes("55 88 67 3e 72 cb 59 c8 7d 22 0c af fe 94 f2 de a9 a3 b1 60 9f 7d 50 e9 0a 48 22 7d b9 ed 7e aa");
420 const expected_binder = hexToBytes("3a dd 4f b2 d8 fd f8 22 a0 ca 3c f7 67 8e f5 e8 8d ae 99 01 41 c5 92 4d 57 bb 6f a3 1b 9e 5f 9d");
421
422 const Hash = Sha256;
423 const Hmac = crypto.auth.hmac.Hmac(Hash);
424 const Hkdf = crypto.kdf.hkdf.Hkdf(Hmac);
425
426 const ikm = hkdfExpandLabel(Hkdf, resumption_master_secret, "resumption", &ticket_nonce, Hash.digest_length);
427 const secret = Hkdf.extract(&[1]u8{0}, &ikm);
428 const prk = hkdfExpandLabel(Hkdf, secret, "res binder", &tls.emptyHash(Hash), Hash.digest_length);
429 const expanded = hkdfExpandLabel(Hkdf, prk, "finished", "", Hash.digest_length);
430 var binder: [Hash.digest_length]u8 = undefined;
431 Hmac.create(&binder, &binder_hash, &expanded);
432
433 if (false) {
434 std.debug.print("ikm : {x}\n", .{ikm});
435 std.debug.print("secret : {x}\n", .{secret});
436 std.debug.print("prk : {x}\n", .{prk});
437 std.debug.print("expanded : {x}\n", .{expanded});
438 std.debug.print("binder : {x}\n", .{binder});
439 }
440
441 try testing.expectEqualSlices(u8, &expected_ikm, &ikm);
442 try testing.expectEqualSlices(u8, &expected_secret, &secret);
443 try testing.expectEqualSlices(u8, &expected_prk, &prk);
444 try testing.expectEqualSlices(u8, &expected_expanded, &expanded);
445 try testing.expectEqualSlices(u8, &expected_binder, &binder);
446
447 pskBinder_(Hash, resumption_master_secret, &binder, binder_hash, &ticket_nonce);
448 // test pskBinder function
449 try testing.expectEqualSlices(
450 u8,
451 &expected_binder,
452 &binder,
453 );
454}