authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-12-16 13:16:53-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-02 16:57:15-07:00
log462b3ed69c20ea5dcae1660761012b3d5fa91367
tree4df5933b4b4d6613ad2221d5ceadb0f003bb42f2
parentb97fc43baac9498799f3520ee860e5026e8dcb53

std.crypto.Tls: handshake fixes

* Handle multiple handshakes in one encrypted record * Fix incorrect handshake length sent to server

1 files changed, 114 insertions(+), 99 deletions(-)

lib/std/crypto/Tls.zig+114-99
...@@ -17,6 +17,10 @@ eof: bool,...@@ -17,6 +17,10 @@ eof: bool,
17pub const ciphertext_record_header_len = 5;17pub const ciphertext_record_header_len = 5;
18pub const max_ciphertext_len = (1 << 14) + 256;18pub const max_ciphertext_len = (1 << 14) + 256;
19pub const max_ciphertext_record_len = max_ciphertext_len + ciphertext_record_header_len;19pub const max_ciphertext_record_len = max_ciphertext_len + ciphertext_record_header_len;
20pub const hello_retry_request_sequence = [32]u8{
21 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91,
22 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C,
23};
2024
21pub const ProtocolVersion = enum(u16) {25pub const ProtocolVersion = enum(u16) {
22 tls_1_2 = 0x0303,26 tls_1_2 = 0x0303,
...@@ -450,7 +454,9 @@ pub fn init(stream: net.Stream, host: []const u8) !Tls {...@@ -450,7 +454,9 @@ pub fn init(stream: net.Stream, host: []const u8) !Tls {
450 const hello = frag[4..];454 const hello = frag[4..];
451 const legacy_version = mem.readIntBig(u16, hello[0..2]);455 const legacy_version = mem.readIntBig(u16, hello[0..2]);
452 const random = hello[2..34].*;456 const random = hello[2..34].*;
453 _ = random;457 if (mem.eql(u8, &random, &hello_retry_request_sequence)) {
458 @panic("TODO handle HelloRetryRequest");
459 }
454 const legacy_session_id_echo_len = hello[34];460 const legacy_session_id_echo_len = hello[34];
455 if (legacy_session_id_echo_len != 0) return error.TlsIllegalParameter;461 if (legacy_session_id_echo_len != 0) return error.TlsIllegalParameter;
456 const cipher_suite_int = mem.readIntBig(u16, hello[35..37]);462 const cipher_suite_int = mem.readIntBig(u16, hello[35..37]);
...@@ -551,7 +557,7 @@ pub fn init(stream: net.Stream, host: []const u8) !Tls {...@@ -551,7 +557,7 @@ pub fn init(stream: net.Stream, host: []const u8) !Tls {
551 p.server_handshake_key = hkdfExpandLabel(P.Hkdf, server_secret, "key", "", P.AEAD.key_length);557 p.server_handshake_key = hkdfExpandLabel(P.Hkdf, server_secret, "key", "", P.AEAD.key_length);
552 p.client_handshake_iv = hkdfExpandLabel(P.Hkdf, client_secret, "iv", "", P.AEAD.nonce_length);558 p.client_handshake_iv = hkdfExpandLabel(P.Hkdf, client_secret, "iv", "", P.AEAD.nonce_length);
553 p.server_handshake_iv = hkdfExpandLabel(P.Hkdf, server_secret, "iv", "", P.AEAD.nonce_length);559 p.server_handshake_iv = hkdfExpandLabel(P.Hkdf, server_secret, "iv", "", P.AEAD.nonce_length);
554 //std.debug.print("shared_key: {}\nhello_hash: {}\nearly_secret: {}\nempty_hash: {}\nderived_secret: {}\nhandshake_secret: {}\n client_secret: {}\n server_secret: {}\n", .{560 //std.debug.print("shared_key: {}\nhello_hash: {}\nearly_secret: {}\nempty_hash: {}\nderived_secret: {}\nhandshake_secret: {}\n client_secret: {}\n server_secret: {}\nclient_handshake_iv: {}\nserver_handshake_iv: {}\n", .{
555 // std.fmt.fmtSliceHexLower(&shared_key),561 // std.fmt.fmtSliceHexLower(&shared_key),
556 // std.fmt.fmtSliceHexLower(&hello_hash),562 // std.fmt.fmtSliceHexLower(&hello_hash),
557 // std.fmt.fmtSliceHexLower(&early_secret),563 // std.fmt.fmtSliceHexLower(&early_secret),
...@@ -560,6 +566,8 @@ pub fn init(stream: net.Stream, host: []const u8) !Tls {...@@ -560,6 +566,8 @@ pub fn init(stream: net.Stream, host: []const u8) !Tls {
560 // std.fmt.fmtSliceHexLower(&p.handshake_secret),566 // std.fmt.fmtSliceHexLower(&p.handshake_secret),
561 // std.fmt.fmtSliceHexLower(&client_secret),567 // std.fmt.fmtSliceHexLower(&client_secret),
562 // std.fmt.fmtSliceHexLower(&server_secret),568 // std.fmt.fmtSliceHexLower(&server_secret),
569 // std.fmt.fmtSliceHexLower(&p.client_handshake_iv),
570 // std.fmt.fmtSliceHexLower(&p.server_handshake_iv),
563 //});571 //});
564 },572 },
565 .TLS_CHACHA20_POLY1305_SHA256 => {573 .TLS_CHACHA20_POLY1305_SHA256 => {
...@@ -643,106 +651,113 @@ pub fn init(stream: net.Stream, host: []const u8) !Tls {...@@ -643,106 +651,113 @@ pub fn init(stream: net.Stream, host: []const u8) !Tls {
643 },651 },
644 };652 };
645653
646 const inner_ct = cleartext[cleartext.len - 1];654 const inner_ct = @intToEnum(ContentType, cleartext[cleartext.len - 1]);
647 std.debug.print("inner_ct={any}\n", .{@intToEnum(ContentType, inner_ct)});
648 switch (inner_ct) {655 switch (inner_ct) {
649 @enumToInt(ContentType.handshake) => {656 .handshake => {
650 const handshake_len = mem.readIntBig(u24, cleartext[1..4]);657 var ct_i: usize = 0;
651 if (4 + handshake_len > cleartext.len - 1) return error.TlsBadLength;658 while (true) {
652 std.debug.print("handshake type: {any} size: {d}\n", .{ @intToEnum(HandshakeType, cleartext[0]), handshake_len });659 const handshake_type = cleartext[ct_i];
653 switch (cleartext[0]) {660 ct_i += 1;
654 @enumToInt(HandshakeType.encrypted_extensions) => {661 const handshake_len = mem.readIntBig(u24, cleartext[ct_i..][0..3]);
655 const ext_size = mem.readIntBig(u16, cleartext[4..6]);662 ct_i += 3;
656 std.debug.print("{d} bytes of encrypted extensions\n", .{663 const next_handshake_i = ct_i + handshake_len;
657 ext_size,664 if (next_handshake_i > cleartext.len - 1)
658 });665 return error.TlsBadLength;
659 },666 switch (handshake_type) {
660 @enumToInt(HandshakeType.certificate) => {667 @enumToInt(HandshakeType.encrypted_extensions) => {
661 std.debug.print("cool certificate bro\n", .{});668 const ext_size = mem.readIntBig(u16, cleartext[ct_i..][0..2]);
662 },669 ct_i += 2;
663 @enumToInt(HandshakeType.certificate_verify) => {670 std.debug.print("{d} bytes of encrypted extensions\n", .{
664 std.debug.print("the certificate came with a fancy signature\n", .{});671 ext_size,
665 },672 });
666 @enumToInt(HandshakeType.finished) => {673 },
667 // This message is to trick buggy proxies into behaving correctly.674 @enumToInt(HandshakeType.certificate) => {
668 const client_change_cipher_spec_msg = [_]u8{675 std.debug.print("cool certificate bro\n", .{});
669 @enumToInt(ContentType.change_cipher_spec),676 },
670 0x03, 0x03, // legacy protocol version677 @enumToInt(HandshakeType.certificate_verify) => {
671 0x00, 0x01, // length678 std.debug.print("the certificate came with a fancy signature\n", .{});
672 0x01,679 },
673 };680 @enumToInt(HandshakeType.finished) => {
674 const app_cipher = switch (cipher_params) {681 // This message is to trick buggy proxies into behaving correctly.
675 inline .TLS_AES_128_GCM_SHA256, .TLS_AES_256_GCM_SHA384 => |*p, tag| c: {682 const client_change_cipher_spec_msg = [_]u8{
676 const P = @TypeOf(p.*);683 @enumToInt(ContentType.change_cipher_spec),
677 // TODO verify the server's data684 0x03, 0x03, // legacy protocol version
678 const handshake_hash = p.transcript_hash.finalResult();685 0x00, 0x01, // length
679 const verify_data = hmac(P.Hmac, &handshake_hash, p.client_finished_key);686 0x01,
680 const out_cleartext = [_]u8{687 };
681 @enumToInt(HandshakeType.finished),688 const app_cipher = switch (cipher_params) {
682 0, 0, verify_data.len + 1 + P.AEAD.tag_length, // length689 inline .TLS_AES_128_GCM_SHA256, .TLS_AES_256_GCM_SHA384 => |*p, tag| c: {
683 } ++ verify_data ++ [1]u8{@enumToInt(ContentType.handshake)};690 const P = @TypeOf(p.*);
684691 // TODO verify the server's data
685 const wrapped_len = out_cleartext.len + P.AEAD.tag_length;692 const handshake_hash = p.transcript_hash.finalResult();
686693 const verify_data = hmac(P.Hmac, &handshake_hash, p.client_finished_key);
687 var finished_msg = [_]u8{694 const out_cleartext = [_]u8{
688 @enumToInt(ContentType.application_data),695 @enumToInt(HandshakeType.finished),
689 0x03, 0x03, // legacy protocol version696 0, 0, verify_data.len, // length
690 0, wrapped_len, // byte length of encrypted record697 } ++ verify_data ++ [1]u8{@enumToInt(ContentType.handshake)};
691 } ++ ([1]u8{undefined} ** wrapped_len);698
692699 const wrapped_len = out_cleartext.len + P.AEAD.tag_length;
693 const ad = finished_msg[0..5];700
694 const ciphertext = finished_msg[5..][0..out_cleartext.len];701 var finished_msg = [_]u8{
695 const auth_tag = finished_msg[finished_msg.len - P.AEAD.tag_length ..];702 @enumToInt(ContentType.application_data),
696 const nonce = p.client_handshake_iv;703 0x03, 0x03, // legacy protocol version
697 P.AEAD.encrypt(ciphertext, auth_tag, &out_cleartext, ad, nonce, p.client_handshake_key);704 0, wrapped_len, // byte length of encrypted record
698705 } ++ ([1]u8{undefined} ** wrapped_len);
699 //const both_msgs = client_change_cipher_spec_msg ++ finished_msg;706
700 _ = client_change_cipher_spec_msg;707 const ad = finished_msg[0..5];
701 const both_msgs = finished_msg;708 const ciphertext = finished_msg[5..][0..out_cleartext.len];
702 try stream.writeAll(&both_msgs);709 const auth_tag = finished_msg[finished_msg.len - P.AEAD.tag_length ..];
703710 const nonce = p.client_handshake_iv;
704 const client_secret = hkdfExpandLabel(P.Hkdf, p.master_secret, "c ap traffic", &handshake_hash, P.Hash.digest_length);711 P.AEAD.encrypt(ciphertext, auth_tag, &out_cleartext, ad, nonce, p.client_handshake_key);
705 const server_secret = hkdfExpandLabel(P.Hkdf, p.master_secret, "s ap traffic", &handshake_hash, P.Hash.digest_length);712
706 //std.debug.print("master_secret={}\nclient_secret={}\nserver_secret={}\n", .{713 const both_msgs = client_change_cipher_spec_msg ++ finished_msg;
707 // std.fmt.fmtSliceHexLower(&p.master_secret),714 try stream.writeAll(&both_msgs);
708 // std.fmt.fmtSliceHexLower(&client_secret),715
709 // std.fmt.fmtSliceHexLower(&server_secret),716 const client_secret = hkdfExpandLabel(P.Hkdf, p.master_secret, "c ap traffic", &handshake_hash, P.Hash.digest_length);
710 //});717 const server_secret = hkdfExpandLabel(P.Hkdf, p.master_secret, "s ap traffic", &handshake_hash, P.Hash.digest_length);
711 break :c @unionInit(ApplicationCipher, @tagName(tag), .{718 //std.debug.print("master_secret={}\nclient_secret={}\nserver_secret={}\n", .{
712 .client_key = hkdfExpandLabel(P.Hkdf, client_secret, "key", "", P.AEAD.key_length),719 // std.fmt.fmtSliceHexLower(&p.master_secret),
713 .server_key = hkdfExpandLabel(P.Hkdf, server_secret, "key", "", P.AEAD.key_length),720 // std.fmt.fmtSliceHexLower(&client_secret),
714 .client_iv = hkdfExpandLabel(P.Hkdf, client_secret, "iv", "", P.AEAD.nonce_length),721 // std.fmt.fmtSliceHexLower(&server_secret),
715 .server_iv = hkdfExpandLabel(P.Hkdf, server_secret, "iv", "", P.AEAD.nonce_length),722 //});
716 });723 break :c @unionInit(ApplicationCipher, @tagName(tag), .{
717 },724 .client_key = hkdfExpandLabel(P.Hkdf, client_secret, "key", "", P.AEAD.key_length),
718 .TLS_CHACHA20_POLY1305_SHA256 => {725 .server_key = hkdfExpandLabel(P.Hkdf, server_secret, "key", "", P.AEAD.key_length),
719 @panic("TODO");726 .client_iv = hkdfExpandLabel(P.Hkdf, client_secret, "iv", "", P.AEAD.nonce_length),
720 },727 .server_iv = hkdfExpandLabel(P.Hkdf, server_secret, "iv", "", P.AEAD.nonce_length),
721 .TLS_AES_128_CCM_SHA256 => {728 });
722 @panic("TODO");729 },
723 },730 .TLS_CHACHA20_POLY1305_SHA256 => {
724 .TLS_AES_128_CCM_8_SHA256 => {731 @panic("TODO");
725 @panic("TODO");732 },
726 },733 .TLS_AES_128_CCM_SHA256 => {
727 };734 @panic("TODO");
728 std.debug.print("remaining bytes: {d}\n", .{len - end});735 },
729 return .{736 .TLS_AES_128_CCM_8_SHA256 => {
730 .application_cipher = app_cipher,737 @panic("TODO");
731 .read_seq = 0,738 },
732 .write_seq = 0,739 };
733 .partially_read_buffer = undefined,740 std.debug.print("remaining bytes: {d}\n", .{len - end});
734 .partially_read_len = 0,741 return .{
735 .eof = false,742 .application_cipher = app_cipher,
736 };743 .read_seq = 0,
737 },744 .write_seq = 0,
738 else => {745 .partially_read_buffer = undefined,
739 std.debug.print("handshake type: {d}\n", .{cleartext[0]});746 .partially_read_len = 0,
740 return error.TlsUnexpectedMessage;747 .eof = false,
741 },748 };
749 },
750 else => {
751 std.debug.print("handshake type: {d}\n", .{cleartext[0]});
752 return error.TlsUnexpectedMessage;
753 },
754 }
755 ct_i = next_handshake_i;
756 if (ct_i >= cleartext.len - 1) break;
742 }757 }
743 },758 },
744 else => {759 else => {
745 std.debug.print("inner content type: {d}\n", .{inner_ct});760 std.debug.print("inner content type: {any}\n", .{inner_ct});
746 return error.TlsUnexpectedMessage;761 return error.TlsUnexpectedMessage;
747 },762 },
748 }763 }
...@@ -803,7 +818,7 @@ pub fn write(tls: *Tls, stream: net.Stream, bytes: []const u8) !usize {...@@ -803,7 +818,7 @@ pub fn write(tls: *Tls, stream: net.Stream, bytes: []const u8) !usize {
803 tls.write_seq += 1;818 tls.write_seq += 1;
804 const nonce: [P.AEAD.nonce_length]u8 = @as(V, p.client_iv) ^ operand;819 const nonce: [P.AEAD.nonce_length]u8 = @as(V, p.client_iv) ^ operand;
805 P.AEAD.encrypt(ciphertext, auth_tag, cleartext, ad, nonce, p.client_key);820 P.AEAD.encrypt(ciphertext, auth_tag, cleartext, ad, nonce, p.client_key);
806 //std.debug.print("seq: {d} nonce: {} client_key: {} client_iv: {} ad: {} auth_tag: {}\nserver_key: {} server_iv: {}", .{821 //std.debug.print("seq: {d} nonce: {} client_key: {} client_iv: {} ad: {} auth_tag: {}\nserver_key: {} server_iv: {}\n", .{
807 // tls.write_seq - 1,822 // tls.write_seq - 1,
808 // std.fmt.fmtSliceHexLower(&nonce),823 // std.fmt.fmtSliceHexLower(&nonce),
809 // std.fmt.fmtSliceHexLower(&p.client_key),824 // std.fmt.fmtSliceHexLower(&p.client_key),