| ... | @@ -17,6 +17,10 @@ eof: bool, | ... | @@ -17,6 +17,10 @@ eof: bool, |
| 17 | pub const ciphertext_record_header_len = 5; | 17 | pub const ciphertext_record_header_len = 5; |
| 18 | pub const max_ciphertext_len = (1 << 14) + 256; | 18 | pub const max_ciphertext_len = (1 << 14) + 256; |
| 19 | pub const max_ciphertext_record_len = max_ciphertext_len + ciphertext_record_header_len; | 19 | pub const max_ciphertext_record_len = max_ciphertext_len + ciphertext_record_header_len; |
| | 20 | pub 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 | }; |
| 20 | | 24 | |
| 21 | pub const ProtocolVersion = enum(u16) { | 25 | pub 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 | }; |
| 645 | | 653 | |
| 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 version | 677 | @enumToInt(HandshakeType.certificate_verify) => { |
| 671 | 0x00, 0x01, // length | 678 | 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 data | 684 | 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, // length | 689 | 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.*); |
| 684 | | 691 | // 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(); |
| 686 | | 693 | 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 version | 696 | 0, 0, verify_data.len, // length |
| 690 | 0, wrapped_len, // byte length of encrypted record | 697 | } ++ verify_data ++ [1]u8{@enumToInt(ContentType.handshake)}; |
| 691 | } ++ ([1]u8{undefined} ** wrapped_len); | 698 | |
| 692 | | 699 | 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 |
| 698 | | 705 | } ++ ([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 ..]; |
| 703 | | 710 | 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), |