authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-12-18 21:32:15-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-02 16:57:15-07:00
log5d7eca6669228cec762fc9063a7ea3cb52af357c
tree1f4d637842106104cac9b7551ea6f4957653c0cc
parente2c16d03abae01538ec8ca65e6dc1ecc6f6ec420

std.crypto.tls.Client: fix verify_data for batched handshakes


2 files changed, 56 insertions(+), 43 deletions(-)

lib/std/crypto/tls.zig+6-1
...@@ -221,6 +221,12 @@ pub const CipherSuite = enum(u16) {...@@ -221,6 +221,12 @@ pub const CipherSuite = enum(u16) {
221 _,221 _,
222};222};
223223
224pub const CertificateType = enum(u8) {
225 X509 = 0,
226 RawPublicKey = 2,
227 _,
228};
229
224pub fn CipherParamsT(comptime AeadType: type, comptime HashType: type) type {230pub fn CipherParamsT(comptime AeadType: type, comptime HashType: type) type {
225 return struct {231 return struct {
226 pub const AEAD = AeadType;232 pub const AEAD = AeadType;
...@@ -237,7 +243,6 @@ pub fn CipherParamsT(comptime AeadType: type, comptime HashType: type) type {...@@ -237,7 +243,6 @@ pub fn CipherParamsT(comptime AeadType: type, comptime HashType: type) type {
237 client_handshake_iv: [AEAD.nonce_length]u8,243 client_handshake_iv: [AEAD.nonce_length]u8,
238 server_handshake_iv: [AEAD.nonce_length]u8,244 server_handshake_iv: [AEAD.nonce_length]u8,
239 transcript_hash: Hash,245 transcript_hash: Hash,
240 finished_digest: [Hash.digest_length]u8,
241 };246 };
242}247}
243248
lib/std/crypto/tls/Client.zig+50-42
...@@ -62,12 +62,6 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {...@@ -62,12 +62,6 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {
62 .rsa_pss_rsae_sha384,62 .rsa_pss_rsae_sha384,
63 .rsa_pss_rsae_sha512,63 .rsa_pss_rsae_sha512,
64 .ed25519,64 .ed25519,
65 .ed448,
66 .rsa_pss_pss_sha256,
67 .rsa_pss_pss_sha384,
68 .rsa_pss_pss_sha512,
69 .rsa_pkcs1_sha1,
70 .ecdsa_sha1,
71 })) ++ tls.extension(.supported_groups, enum_array(tls.NamedGroup, &.{65 })) ++ tls.extension(.supported_groups, enum_array(tls.NamedGroup, &.{
72 .secp256r1,66 .secp256r1,
73 .x25519,67 .x25519,
...@@ -98,24 +92,21 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {...@@ -98,24 +92,21 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {
98 int2(legacy_compression_methods) ++92 int2(legacy_compression_methods) ++
99 extensions_header;93 extensions_header;
10094
101 const handshake =95 const out_handshake =
102 [_]u8{@enumToInt(HandshakeType.client_hello)} ++96 [_]u8{@enumToInt(HandshakeType.client_hello)} ++
103 int3(@intCast(u24, client_hello.len + host_len)) ++97 int3(@intCast(u24, client_hello.len + host_len)) ++
104 client_hello;98 client_hello;
10599
106 const hello_header = [_]u8{100 const plaintext_header = [_]u8{
107 // Plaintext header
108 @enumToInt(ContentType.handshake),101 @enumToInt(ContentType.handshake),
109 0x03, 0x01, // legacy_record_version102 0x03, 0x01, // legacy_record_version
110 } ++103 } ++ int2(@intCast(u16, out_handshake.len + host_len)) ++ out_handshake;
111 int2(@intCast(u16, handshake.len + host_len)) ++
112 handshake;
113104
114 {105 {
115 var iovecs = [_]std.os.iovec_const{106 var iovecs = [_]std.os.iovec_const{
116 .{107 .{
117 .iov_base = &hello_header,108 .iov_base = &plaintext_header,
118 .iov_len = hello_header.len,109 .iov_len = plaintext_header.len,
119 },110 },
120 .{111 .{
121 .iov_base = host.ptr,112 .iov_base = host.ptr,
...@@ -125,7 +116,7 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {...@@ -125,7 +116,7 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {
125 try stream.writevAll(&iovecs);116 try stream.writevAll(&iovecs);
126 }117 }
127118
128 const client_hello_bytes1 = hello_header[5..];119 const client_hello_bytes1 = plaintext_header[5..];
129120
130 var cipher_params: CipherParams = undefined;121 var cipher_params: CipherParams = undefined;
131122
...@@ -176,7 +167,6 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {...@@ -176,7 +167,6 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {
176 const cipher_suite_int = mem.readIntBig(u16, frag[i..][0..2]);167 const cipher_suite_int = mem.readIntBig(u16, frag[i..][0..2]);
177 i += 2;168 i += 2;
178 const cipher_suite_tag = @intToEnum(CipherSuite, cipher_suite_int);169 const cipher_suite_tag = @intToEnum(CipherSuite, cipher_suite_int);
179 std.debug.print("server wants cipher suite {any}\n", .{cipher_suite_tag});
180 const legacy_compression_method = frag[i];170 const legacy_compression_method = frag[i];
181 i += 1;171 i += 1;
182 _ = legacy_compression_method;172 _ = legacy_compression_method;
...@@ -243,12 +233,7 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {...@@ -243,12 +233,7 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {
243 if (!have_shared_key) return error.TlsIllegalParameter;233 if (!have_shared_key) return error.TlsIllegalParameter;
244 const tls_version = if (supported_version == 0) legacy_version else supported_version;234 const tls_version = if (supported_version == 0) legacy_version else supported_version;
245 switch (tls_version) {235 switch (tls_version) {
246 @enumToInt(tls.ProtocolVersion.tls_1_2) => {236 @enumToInt(tls.ProtocolVersion.tls_1_3) => {},
247 std.debug.print("server wants TLS v1.2\n", .{});
248 },
249 @enumToInt(tls.ProtocolVersion.tls_1_3) => {
250 std.debug.print("server wants TLS v1.3\n", .{});
251 },
252 else => return error.TlsIllegalParameter,237 else => return error.TlsIllegalParameter,
253 }238 }
254239
...@@ -270,7 +255,6 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {...@@ -270,7 +255,6 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {
270 .client_handshake_iv = undefined,255 .client_handshake_iv = undefined,
271 .server_handshake_iv = undefined,256 .server_handshake_iv = undefined,
272 .transcript_hash = P.Hash.init(.{}),257 .transcript_hash = P.Hash.init(.{}),
273 .finished_digest = undefined,
274 });258 });
275 const p = &@field(cipher_params, @tagName(tag));259 const p = &@field(cipher_params, @tagName(tag));
276 p.transcript_hash.update(client_hello_bytes1); // Client Hello part 1260 p.transcript_hash.update(client_hello_bytes1); // Client Hello part 1
...@@ -361,7 +345,6 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {...@@ -361,7 +345,6 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {
361 const ad = handshake_buf[end_hdr - 5 ..][0..5];345 const ad = handshake_buf[end_hdr - 5 ..][0..5];
362 P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, p.server_handshake_key) catch346 P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, p.server_handshake_key) catch
363 return error.TlsBadRecordMac;347 return error.TlsBadRecordMac;
364 p.transcript_hash.update(cleartext[0 .. cleartext.len - 1]);
365 break :c cleartext;348 break :c cleartext;
366 },349 },
367 };350 };
...@@ -378,17 +361,22 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {...@@ -378,17 +361,22 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {
378 const next_handshake_i = ct_i + handshake_len;361 const next_handshake_i = ct_i + handshake_len;
379 if (next_handshake_i > cleartext.len - 1)362 if (next_handshake_i > cleartext.len - 1)
380 return error.TlsBadLength;363 return error.TlsBadLength;
364 const wrapped_handshake = cleartext[ct_i - 4 .. next_handshake_i];
365 const handshake = cleartext[ct_i..next_handshake_i];
381 switch (handshake_type) {366 switch (handshake_type) {
382 @enumToInt(HandshakeType.encrypted_extensions) => {367 @enumToInt(HandshakeType.encrypted_extensions) => {
383 const total_ext_size = mem.readIntBig(u16, cleartext[ct_i..][0..2]);368 switch (cipher_params) {
384 ct_i += 2;369 inline else => |*p| p.transcript_hash.update(wrapped_handshake),
385 const end_ext_i = ct_i + total_ext_size;370 }
386 while (ct_i < end_ext_i) {371 const total_ext_size = mem.readIntBig(u16, handshake[0..2]);
387 const et = mem.readIntBig(u16, cleartext[ct_i..][0..2]);372 var hs_i: usize = 2;
388 ct_i += 2;373 const end_ext_i = 2 + total_ext_size;
389 const ext_size = mem.readIntBig(u16, cleartext[ct_i..][0..2]);374 while (hs_i < end_ext_i) {
390 ct_i += 2;375 const et = mem.readIntBig(u16, handshake[hs_i..][0..2]);
391 const next_ext_i = ct_i + ext_size;376 hs_i += 2;
377 const ext_size = mem.readIntBig(u16, handshake[hs_i..][0..2]);
378 hs_i += 2;
379 const next_ext_i = hs_i + ext_size;
392 switch (et) {380 switch (et) {
393 @enumToInt(tls.ExtensionType.server_name) => {},381 @enumToInt(tls.ExtensionType.server_name) => {},
394 else => {382 else => {
...@@ -397,19 +385,38 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {...@@ -397,19 +385,38 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {
397 });385 });
398 },386 },
399 }387 }
400 ct_i = next_ext_i;388 hs_i = next_ext_i;
401 }389 }
402 },390 },
403 @enumToInt(HandshakeType.certificate) => {391 @enumToInt(HandshakeType.certificate) => {
404 std.debug.print("cool certificate bro\n", .{});392 switch (cipher_params) {
393 inline else => |*p| p.transcript_hash.update(wrapped_handshake),
394 }
395 var hs_i: usize = 0;
396 const cert_req_ctx_len = handshake[hs_i];
397 hs_i += 1;
398 if (cert_req_ctx_len != 0) return error.TlsIllegalParameter;
399 const certs_size = mem.readIntBig(u24, handshake[hs_i..][0..3]);
400 hs_i += 3;
401 const end_certs = hs_i + certs_size;
402 while (hs_i < end_certs) {
403 const cert_size = mem.readIntBig(u24, handshake[hs_i..][0..3]);
404 hs_i += 3;
405 hs_i += cert_size;
406 const total_ext_size = mem.readIntBig(u16, handshake[hs_i..][0..2]);
407 hs_i += 2;
408 hs_i += total_ext_size;
409
410 std.debug.print("received certificate of size {d} bytes with {d} bytes of extensions\n", .{
411 cert_size, total_ext_size,
412 });
413 }
405 },414 },
406 @enumToInt(HandshakeType.certificate_verify) => {415 @enumToInt(HandshakeType.certificate_verify) => {
407 std.debug.print("the certificate came with a fancy signature\n", .{});
408 switch (cipher_params) {416 switch (cipher_params) {
409 inline else => |*p| {417 inline else => |*p| p.transcript_hash.update(wrapped_handshake),
410 p.finished_digest = p.transcript_hash.peek();
411 },
412 }418 }
419 std.debug.print("ignoring certificate_verify\n", .{});
413 },420 },
414 @enumToInt(HandshakeType.finished) => {421 @enumToInt(HandshakeType.finished) => {
415 // This message is to trick buggy proxies into behaving correctly.422 // This message is to trick buggy proxies into behaving correctly.
...@@ -422,9 +429,10 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {...@@ -422,9 +429,10 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {
422 const app_cipher = switch (cipher_params) {429 const app_cipher = switch (cipher_params) {
423 inline else => |*p, tag| c: {430 inline else => |*p, tag| c: {
424 const P = @TypeOf(p.*);431 const P = @TypeOf(p.*);
425 const expected_server_verify_data = tls.hmac(P.Hmac, &p.finished_digest, p.server_finished_key);432 const finished_digest = p.transcript_hash.peek();
426 const actual_server_verify_data = cleartext[ct_i..][0..handshake_len];433 p.transcript_hash.update(wrapped_handshake);
427 if (!mem.eql(u8, &expected_server_verify_data, actual_server_verify_data))434 const expected_server_verify_data = tls.hmac(P.Hmac, &finished_digest, p.server_finished_key);
435 if (!mem.eql(u8, &expected_server_verify_data, handshake))
428 return error.TlsDecryptError;436 return error.TlsDecryptError;
429 const handshake_hash = p.transcript_hash.finalResult();437 const handshake_hash = p.transcript_hash.finalResult();
430 const verify_data = tls.hmac(P.Hmac, &handshake_hash, p.client_finished_key);438 const verify_data = tls.hmac(P.Hmac, &handshake_hash, p.client_finished_key);