| ... | @@ -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; |
| 100 | | 94 | |
| 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; |
| 105 | | 99 | |
| 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_version | 102 | 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; | | |
| 113 | | 104 | |
| 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 | } |
| 127 | | 118 | |
| 128 | const client_hello_bytes1 = hello_header[5..]; | 119 | const client_hello_bytes1 = plaintext_header[5..]; |
| 129 | | 120 | |
| 130 | var cipher_params: CipherParams = undefined; | 121 | var cipher_params: CipherParams = undefined; |
| 131 | | 122 | |
| ... | @@ -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 | } |
| 254 | | 239 | |
| ... | @@ -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 1 | 260 | 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) catch | 346 | 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); |