authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-12-18 18:01:13-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-02 16:57:15-07:00
loge2efba76aa0e1566da65721db64537d94fea69df
treec5ae77a293ff58ee6969c8201bb3487d826fabf3
parent41f4461cdabb50e45c0f956d4a1380d2008cd127

std.crypto.tls: refactor to remove mutations

build up the hello message with array concatenation and helper functions rather than hard-coded offsets and lengths.

2 files changed, 101 insertions(+), 89 deletions(-)

lib/std/crypto/tls.zig+34
...@@ -310,3 +310,37 @@ pub fn hmac(comptime Hmac: type, message: []const u8, key: [Hmac.key_length]u8)...@@ -310,3 +310,37 @@ pub fn hmac(comptime Hmac: type, message: []const u8, key: [Hmac.key_length]u8)
310 Hmac.create(&result, message, &key);310 Hmac.create(&result, message, &key);
311 return result;311 return result;
312}312}
313
314pub inline fn extension(comptime et: ExtensionType, bytes: anytype) [2 + 2 + bytes.len]u8 {
315 return int2(@enumToInt(et)) ++ array(1, bytes);
316}
317
318pub inline fn array(comptime elem_size: comptime_int, bytes: anytype) [2 + bytes.len]u8 {
319 comptime assert(bytes.len % elem_size == 0);
320 return int2(bytes.len) ++ bytes;
321}
322
323pub inline fn enum_array(comptime E: type, comptime tags: []const E) [2 + @sizeOf(E) * tags.len]u8 {
324 assert(@sizeOf(E) == 2);
325 var result: [tags.len * 2]u8 = undefined;
326 for (tags) |elem, i| {
327 result[i * 2] = @truncate(u8, @enumToInt(elem) >> 8);
328 result[i * 2 + 1] = @truncate(u8, @enumToInt(elem));
329 }
330 return array(2, result);
331}
332
333pub inline fn int2(x: u16) [2]u8 {
334 return .{
335 @truncate(u8, x >> 8),
336 @truncate(u8, x),
337 };
338}
339
340pub inline fn int3(x: u24) [3]u8 {
341 return .{
342 @truncate(u8, x >> 16),
343 @truncate(u8, x >> 8),
344 @truncate(u8, x),
345 };
346}
lib/std/crypto/tls/Client.zig+67-89
...@@ -13,6 +13,10 @@ const HandshakeType = tls.HandshakeType;...@@ -13,6 +13,10 @@ const HandshakeType = tls.HandshakeType;
13const CipherParams = tls.CipherParams;13const CipherParams = tls.CipherParams;
14const max_ciphertext_len = tls.max_ciphertext_len;14const max_ciphertext_len = tls.max_ciphertext_len;
15const hkdfExpandLabel = tls.hkdfExpandLabel;15const hkdfExpandLabel = tls.hkdfExpandLabel;
16const int2 = tls.int2;
17const int3 = tls.int3;
18const array = tls.array;
19const enum_array = tls.enum_array;
1620
17application_cipher: ApplicationCipher,21application_cipher: ApplicationCipher,
18read_seq: u64,22read_seq: u64,
...@@ -25,6 +29,8 @@ eof: bool,...@@ -25,6 +29,8 @@ eof: bool,
2529
26/// `host` is only borrowed during this function call.30/// `host` is only borrowed during this function call.
27pub fn init(stream: net.Stream, host: []const u8) !Client {31pub fn init(stream: net.Stream, host: []const u8) !Client {
32 const host_len = @intCast(u16, host.len);
33
28 const kp = crypto.dh.X25519.KeyPair.create(null) catch |err| switch (err) {34 const kp = crypto.dh.X25519.KeyPair.create(null) catch |err| switch (err) {
29 // Only possible to happen if the private key is all zeroes.35 // Only possible to happen if the private key is all zeroes.
30 error.IdentityElement => return error.InsufficientEntropy,36 error.IdentityElement => return error.InsufficientEntropy,
...@@ -34,92 +40,70 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {...@@ -34,92 +40,70 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {
34 var rand_buf: [32]u8 = undefined;40 var rand_buf: [32]u8 = undefined;
35 crypto.random.bytes(&rand_buf);41 crypto.random.bytes(&rand_buf);
3642
37 const extensions_header = [_]u8{43 const extensions_payload =
38 // Extensions byte length44 tls.extension(.supported_versions, [_]u8{
39 undefined, undefined,45 0x02, // byte length of supported versions
40
41 // Extension: supported_versions (only TLS 1.3)
42 0, 43, // ExtensionType.supported_versions
43 0x00, 0x05, // byte length of this extension payload
44 0x04, // byte length of supported versions
45 0x03, 0x04, // TLS 1.346 0x03, 0x04, // TLS 1.3
46 0x03, 0x03, // TLS 1.247 }) ++ tls.extension(.signature_algorithms, enum_array(tls.SignatureScheme, &.{
4748 .rsa_pkcs1_sha256,
48 // Extension: signature_algorithms49 .rsa_pkcs1_sha384,
49 0, 13, // ExtensionType.signature_algorithms50 .rsa_pkcs1_sha512,
50 0x00, 0x22, // byte length of this extension payload51 .ecdsa_secp256r1_sha256,
51 0x00, 0x20, // byte length of signature algorithms list52 .ecdsa_secp384r1_sha384,
52 0x04, 0x01, // rsa_pkcs1_sha25653 .ecdsa_secp521r1_sha512,
53 0x05, 0x01, // rsa_pkcs1_sha38454 .rsa_pss_rsae_sha256,
54 0x06, 0x01, // rsa_pkcs1_sha51255 .rsa_pss_rsae_sha384,
55 0x04, 0x03, // ecdsa_secp256r1_sha25656 .rsa_pss_rsae_sha512,
56 0x05, 0x03, // ecdsa_secp384r1_sha38457 .ed25519,
57 0x06, 0x03, // ecdsa_secp521r1_sha51258 .ed448,
58 0x08, 0x04, // rsa_pss_rsae_sha25659 .rsa_pss_pss_sha256,
59 0x08, 0x05, // rsa_pss_rsae_sha38460 .rsa_pss_pss_sha384,
60 0x08, 0x06, // rsa_pss_rsae_sha51261 .rsa_pss_pss_sha512,
61 0x08, 0x07, // ed2551962 .rsa_pkcs1_sha1,
62 0x08, 0x08, // ed44863 .ecdsa_sha1,
63 0x08, 0x09, // rsa_pss_pss_sha25664 })) ++ tls.extension(.supported_groups, enum_array(tls.NamedGroup, &.{
64 0x08, 0x0a, // rsa_pss_pss_sha38465 //.secp256r1,
65 0x08, 0x0b, // rsa_pss_pss_sha51266 .x25519,
66 0x02, 0x01, // rsa_pkcs1_sha167 })) ++ [_]u8{
67 0x02, 0x03, // ecdsa_sha1
68
69 // Extension: supported_groups
70 0, 10, // ExtensionType.supported_groups
71 0x00, 0x0c, // byte length of this extension payload
72 0x00, 0x0a, // byte length of supported groups list
73 0x00, 0x17, // secp256r1
74 0x00, 0x18, // secp384r1
75 0x00, 0x19, // secp521r1
76 0x00, 0x1D, // x25519
77 0x00, 0x1E, // x448
78
79 // Extension: key_share68 // Extension: key_share
80 0, 51, // ExtensionType.key_share69 0, 51, // ExtensionType.key_share
81 0, 38, // byte length of this extension payload70 0, 38, // byte length of this extension payload
82 0, 36, // byte length of client_shares71 0, 36, // byte length of client_shares
83 0x00, 0x1D, // NamedGroup.x2551972 0x00, 0x1D, // NamedGroup.x25519
84 0, 32, // byte length of key_exchange73 0, 32, // byte length of key_exchange
85 } ++ kp.public_key ++ [_]u8{74 } ++ kp.public_key ++
8675 int2(@enumToInt(tls.ExtensionType.server_name)) ++
87 // Extension: server_name76 int2(host_len + 5) ++ // byte length of this extension payload
88 0, 0, // ExtensionType.server_name77 int2(host_len + 3) ++ // server_name_list byte count
89 undefined, undefined, // byte length of this extension payload78 [1]u8{0x00} ++ // name_type
90 undefined, undefined, // server_name_list byte count79 int2(host_len);
91 0x00, // name_type80
92 undefined, undefined, // host name len81 const extensions_header =
93 };82 int2(@intCast(u16, extensions_payload.len + host_len)) ++
9483 extensions_payload;
95 var hello_header = [_]u8{84
85 const legacy_compression_methods = 0x0100;
86
87 const client_hello =
88 int2(@enumToInt(tls.ProtocolVersion.tls_1_2)) ++
89 rand_buf ++
90 [1]u8{0} ++
91 cipher_suites ++
92 int2(legacy_compression_methods) ++
93 extensions_header;
94
95 const handshake =
96 [_]u8{@enumToInt(HandshakeType.client_hello)} ++
97 int3(@intCast(u24, client_hello.len + host_len)) ++
98 client_hello;
99
100 const hello_header = [_]u8{
96 // Plaintext header101 // Plaintext header
97 @enumToInt(ContentType.handshake),102 @enumToInt(ContentType.handshake),
98 0x03, 0x01, // legacy_record_version103 0x03, 0x01, // legacy_record_version
99 undefined, undefined, // Plaintext fragment length (u16)104 } ++
100105 int2(@intCast(u16, handshake.len + host_len)) ++
101 // Handshake header106 handshake;
102 @enumToInt(HandshakeType.client_hello),
103 undefined, undefined, undefined, // handshake length (u24)
104
105 // ClientHello
106 0x03, 0x03, // legacy_version
107 } ++ rand_buf ++ [1]u8{0} ++
108 int2(cipher_suites.len) ++ cipher_suites ++
109 [_]u8{
110 0x01, 0x00, // legacy_compression_methods
111 } ++ extensions_header;
112
113 mem.writeIntBig(u16, hello_header[3..][0..2], @intCast(u16, hello_header.len - 5 + host.len));
114 mem.writeIntBig(u24, hello_header[6..][0..3], @intCast(u24, hello_header.len - 9 + host.len));
115 mem.writeIntBig(
116 u16,
117 hello_header[hello_header.len - extensions_header.len ..][0..2],
118 @intCast(u16, extensions_header.len - 2 + host.len),
119 );
120 mem.writeIntBig(u16, hello_header[hello_header.len - 7 ..][0..2], @intCast(u16, 5 + host.len));
121 mem.writeIntBig(u16, hello_header[hello_header.len - 5 ..][0..2], @intCast(u16, 3 + host.len));
122 mem.writeIntBig(u16, hello_header[hello_header.len - 2 ..][0..2], @intCast(u16, 0 + host.len));
123107
124 {108 {
125 var iovecs = [_]std.os.iovec_const{109 var iovecs = [_]std.os.iovec_const{
...@@ -699,13 +683,6 @@ inline fn big(x: anytype) @TypeOf(x) {...@@ -699,13 +683,6 @@ inline fn big(x: anytype) @TypeOf(x) {
699 };683 };
700}684}
701685
702inline fn int2(x: u16) [2]u8 {
703 return .{
704 @truncate(u8, x >> 8),
705 @truncate(u8, x),
706 };
707}
708
709/// The priority order here is chosen based on what crypto algorithms Zig has686/// The priority order here is chosen based on what crypto algorithms Zig has
710/// available in the standard library as well as what is faster. Following are687/// available in the standard library as well as what is faster. Following are
711/// a few data points on the relative performance of these algorithms.688/// a few data points on the relative performance of these algorithms.
...@@ -727,9 +704,10 @@ inline fn int2(x: u16) [2]u8 {...@@ -727,9 +704,10 @@ inline fn int2(x: u16) [2]u8 {
727/// aegis-256: 461 MiB/s704/// aegis-256: 461 MiB/s
728/// aes128-gcm: 138 MiB/s705/// aes128-gcm: 138 MiB/s
729/// aes256-gcm: 120 MiB/s706/// aes256-gcm: 120 MiB/s
730const cipher_suites =707const cipher_suites = enum_array(tls.CipherSuite, &.{
731 int2(@enumToInt(tls.CipherSuite.AEGIS_128L_SHA256)) ++708 .AEGIS_128L_SHA256,
732 int2(@enumToInt(tls.CipherSuite.AEGIS_256_SHA384)) ++709 .AEGIS_256_SHA384,
733 int2(@enumToInt(tls.CipherSuite.AES_128_GCM_SHA256)) ++710 .AES_128_GCM_SHA256,
734 int2(@enumToInt(tls.CipherSuite.AES_256_GCM_SHA384)) ++711 .AES_256_GCM_SHA384,
735 int2(@enumToInt(tls.CipherSuite.CHACHA20_POLY1305_SHA256));712 .CHACHA20_POLY1305_SHA256,
713});