| ... | @@ -23,27 +23,27 @@ partially_read_buffer: [tls.max_ciphertext_record_len]u8, | ... | @@ -23,27 +23,27 @@ partially_read_buffer: [tls.max_ciphertext_record_len]u8, |
| 23 | partially_read_len: u15, | 23 | partially_read_len: u15, |
| 24 | eof: bool, | 24 | eof: bool, |
| 25 | | 25 | |
| 26 | const cipher_suites = blk: { | 26 | // Measurement taken with 0.11.0-dev.810+c2f5848fe |
| 27 | const fields = @typeInfo(CipherSuite).Enum.fields; | 27 | // on x86_64-linux Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz: |
| 28 | var result: [(fields.len + 1) * 2]u8 = undefined; | 28 | // zig run .lib/std/crypto/benchmark.zig -OReleaseFast |
| 29 | mem.writeIntBig(u16, result[0..2], result.len - 2); | 29 | // aegis-128l: 15382 MiB/s |
| 30 | for (fields) |field, i| { | 30 | // aegis-256: 9553 MiB/s |
| 31 | const int = @enumToInt(@field(CipherSuite, field.name)); | 31 | // aes128-gcm: 3721 MiB/s |
| 32 | result[(i + 1) * 2] = @truncate(u8, int >> 8); | 32 | // aes256-gcm: 3010 MiB/s |
| 33 | result[(i + 1) * 2 + 1] = @truncate(u8, int); | 33 | // chacha20Poly1305: 597 MiB/s |
| 34 | } | 34 | |
| 35 | break :blk result; | 35 | const cipher_suites = |
| 36 | }; | 36 | int2(@enumToInt(tls.CipherSuite.AEGIS_128L_SHA256)) ++ |
| | 37 | int2(@enumToInt(tls.CipherSuite.AEGIS_256_SHA384)) ++ |
| | 38 | int2(@enumToInt(tls.CipherSuite.AES_128_GCM_SHA256)) ++ |
| | 39 | int2(@enumToInt(tls.CipherSuite.AES_256_GCM_SHA384)) ++ |
| | 40 | int2(@enumToInt(tls.CipherSuite.CHACHA20_POLY1305_SHA256)); |
| 37 | | 41 | |
| 38 | /// `host` is only borrowed during this function call. | 42 | /// `host` is only borrowed during this function call. |
| 39 | pub fn init(stream: net.Stream, host: []const u8) !Client { | 43 | pub fn init(stream: net.Stream, host: []const u8) !Client { |
| 40 | var x25519_priv_key: [32]u8 = undefined; | 44 | const kp = crypto.dh.X25519.KeyPair.create(null) catch |err| switch (err) { |
| 41 | crypto.random.bytes(&x25519_priv_key); | 45 | // Only possible to happen if the private key is all zeroes. |
| 42 | const x25519_pub_key = crypto.dh.X25519.recoverPublicKey(x25519_priv_key) catch |err| { | 46 | error.IdentityElement => return error.InsufficientEntropy, |
| 43 | switch (err) { | | |
| 44 | // Only possible to happen if the private key is all zeroes. | | |
| 45 | error.IdentityElement => return error.InsufficientEntropy, | | |
| 46 | } | | |
| 47 | }; | 47 | }; |
| 48 | | 48 | |
| 49 | // random (u32) | 49 | // random (u32) |
| ... | @@ -98,7 +98,7 @@ pub fn init(stream: net.Stream, host: []const u8) !Client { | ... | @@ -98,7 +98,7 @@ pub fn init(stream: net.Stream, host: []const u8) !Client { |
| 98 | 0, 36, // byte length of client_shares | 98 | 0, 36, // byte length of client_shares |
| 99 | 0x00, 0x1D, // NamedGroup.x25519 | 99 | 0x00, 0x1D, // NamedGroup.x25519 |
| 100 | 0, 32, // byte length of key_exchange | 100 | 0, 32, // byte length of key_exchange |
| 101 | } ++ x25519_pub_key ++ [_]u8{ | 101 | } ++ kp.public_key ++ [_]u8{ |
| 102 | | 102 | |
| 103 | // Extension: server_name | 103 | // Extension: server_name |
| 104 | 0, 0, // ExtensionType.server_name | 104 | 0, 0, // ExtensionType.server_name |
| ... | @@ -120,7 +120,9 @@ pub fn init(stream: net.Stream, host: []const u8) !Client { | ... | @@ -120,7 +120,9 @@ pub fn init(stream: net.Stream, host: []const u8) !Client { |
| 120 | | 120 | |
| 121 | // ClientHello | 121 | // ClientHello |
| 122 | 0x03, 0x03, // legacy_version | 122 | 0x03, 0x03, // legacy_version |
| 123 | } ++ rand_buf ++ [1]u8{0} ++ cipher_suites ++ [_]u8{ | 123 | } ++ rand_buf ++ [1]u8{0} ++ |
| | 124 | int2(cipher_suites.len) ++ cipher_suites ++ |
| | 125 | [_]u8{ |
| 124 | 0x01, 0x00, // legacy_compression_methods | 126 | 0x01, 0x00, // legacy_compression_methods |
| 125 | } ++ extensions_header; | 127 | } ++ extensions_header; |
| 126 | | 128 | |
| ... | @@ -191,9 +193,8 @@ pub fn init(stream: net.Stream, host: []const u8) !Client { | ... | @@ -191,9 +193,8 @@ pub fn init(stream: net.Stream, host: []const u8) !Client { |
| 191 | const legacy_session_id_echo_len = hello[34]; | 193 | const legacy_session_id_echo_len = hello[34]; |
| 192 | if (legacy_session_id_echo_len != 0) return error.TlsIllegalParameter; | 194 | if (legacy_session_id_echo_len != 0) return error.TlsIllegalParameter; |
| 193 | const cipher_suite_int = mem.readIntBig(u16, hello[35..37]); | 195 | const cipher_suite_int = mem.readIntBig(u16, hello[35..37]); |
| 194 | const cipher_suite_tag = std.meta.intToEnum(CipherSuite, cipher_suite_int) catch | 196 | const cipher_suite_tag = @intToEnum(CipherSuite, cipher_suite_int); |
| 195 | return error.TlsIllegalParameter; | 197 | std.debug.print("server wants cipher suite {any}\n", .{cipher_suite_tag}); |
| 196 | std.debug.print("server wants cipher suite {s}\n", .{@tagName(cipher_suite_tag)}); | | |
| 197 | const legacy_compression_method = hello[37]; | 198 | const legacy_compression_method = hello[37]; |
| 198 | _ = legacy_compression_method; | 199 | _ = legacy_compression_method; |
| 199 | const extensions_size = mem.readIntBig(u16, hello[38..40]); | 200 | const extensions_size = mem.readIntBig(u16, hello[38..40]); |
| ... | @@ -250,13 +251,18 @@ pub fn init(stream: net.Stream, host: []const u8) !Client { | ... | @@ -250,13 +251,18 @@ pub fn init(stream: net.Stream, host: []const u8) !Client { |
| 250 | } | 251 | } |
| 251 | | 252 | |
| 252 | const shared_key = crypto.dh.X25519.scalarmult( | 253 | const shared_key = crypto.dh.X25519.scalarmult( |
| 253 | x25519_priv_key, | 254 | kp.secret_key, |
| 254 | x25519_server_pub_key.*, | 255 | x25519_server_pub_key.*, |
| 255 | ) catch return error.TlsDecryptFailure; | 256 | ) catch return error.TlsDecryptFailure; |
| 256 | | 257 | |
| 257 | switch (cipher_suite_tag) { | 258 | switch (cipher_suite_tag) { |
| 258 | inline .TLS_AES_128_GCM_SHA256, .TLS_AES_256_GCM_SHA384 => |tag| { | 259 | inline .AES_128_GCM_SHA256, |
| 259 | const P = std.meta.TagPayload(CipherParams, tag); | 260 | .AES_256_GCM_SHA384, |
| | 261 | .CHACHA20_POLY1305_SHA256, |
| | 262 | .AEGIS_256_SHA384, |
| | 263 | .AEGIS_128L_SHA256, |
| | 264 | => |tag| { |
| | 265 | const P = std.meta.TagPayloadByName(CipherParams, @tagName(tag)); |
| 260 | cipher_params = @unionInit(CipherParams, @tagName(tag), .{ | 266 | cipher_params = @unionInit(CipherParams, @tagName(tag), .{ |
| 261 | .handshake_secret = undefined, | 267 | .handshake_secret = undefined, |
| 262 | .master_secret = undefined, | 268 | .master_secret = undefined, |
| ... | @@ -301,14 +307,8 @@ pub fn init(stream: net.Stream, host: []const u8) !Client { | ... | @@ -301,14 +307,8 @@ pub fn init(stream: net.Stream, host: []const u8) !Client { |
| 301 | // std.fmt.fmtSliceHexLower(&p.server_handshake_iv), | 307 | // std.fmt.fmtSliceHexLower(&p.server_handshake_iv), |
| 302 | //}); | 308 | //}); |
| 303 | }, | 309 | }, |
| 304 | .TLS_CHACHA20_POLY1305_SHA256 => { | 310 | else => { |
| 305 | @panic("TODO"); | 311 | return error.TlsIllegalParameter; |
| 306 | }, | | |
| 307 | .TLS_AES_128_CCM_SHA256 => { | | |
| 308 | @panic("TODO"); | | |
| 309 | }, | | |
| 310 | .TLS_AES_128_CCM_8_SHA256 => { | | |
| 311 | @panic("TODO"); | | |
| 312 | }, | 312 | }, |
| 313 | } | 313 | } |
| 314 | }, | 314 | }, |
| ... | @@ -347,7 +347,7 @@ pub fn init(stream: net.Stream, host: []const u8) !Client { | ... | @@ -347,7 +347,7 @@ pub fn init(stream: net.Stream, host: []const u8) !Client { |
| 347 | .application_data => { | 347 | .application_data => { |
| 348 | var cleartext_buf: [8000]u8 = undefined; | 348 | var cleartext_buf: [8000]u8 = undefined; |
| 349 | const cleartext = switch (cipher_params) { | 349 | const cleartext = switch (cipher_params) { |
| 350 | inline .TLS_AES_128_GCM_SHA256, .TLS_AES_256_GCM_SHA384 => |*p| c: { | 350 | inline else => |*p| c: { |
| 351 | const P = @TypeOf(p.*); | 351 | const P = @TypeOf(p.*); |
| 352 | const ciphertext_len = record_size - P.AEAD.tag_length; | 352 | const ciphertext_len = record_size - P.AEAD.tag_length; |
| 353 | const ciphertext = handshake_buf[i..][0..ciphertext_len]; | 353 | const ciphertext = handshake_buf[i..][0..ciphertext_len]; |
| ... | @@ -366,15 +366,6 @@ pub fn init(stream: net.Stream, host: []const u8) !Client { | ... | @@ -366,15 +366,6 @@ pub fn init(stream: net.Stream, host: []const u8) !Client { |
| 366 | p.transcript_hash.update(cleartext[0 .. cleartext.len - 1]); | 366 | p.transcript_hash.update(cleartext[0 .. cleartext.len - 1]); |
| 367 | break :c cleartext; | 367 | break :c cleartext; |
| 368 | }, | 368 | }, |
| 369 | .TLS_CHACHA20_POLY1305_SHA256 => { | | |
| 370 | @panic("TODO"); | | |
| 371 | }, | | |
| 372 | .TLS_AES_128_CCM_SHA256 => { | | |
| 373 | @panic("TODO"); | | |
| 374 | }, | | |
| 375 | .TLS_AES_128_CCM_8_SHA256 => { | | |
| 376 | @panic("TODO"); | | |
| 377 | }, | | |
| 378 | }; | 369 | }; |
| 379 | | 370 | |
| 380 | const inner_ct = @intToEnum(ContentType, cleartext[cleartext.len - 1]); | 371 | const inner_ct = @intToEnum(ContentType, cleartext[cleartext.len - 1]); |
| ... | @@ -426,7 +417,7 @@ pub fn init(stream: net.Stream, host: []const u8) !Client { | ... | @@ -426,7 +417,7 @@ pub fn init(stream: net.Stream, host: []const u8) !Client { |
| 426 | 0x01, | 417 | 0x01, |
| 427 | }; | 418 | }; |
| 428 | const app_cipher = switch (cipher_params) { | 419 | const app_cipher = switch (cipher_params) { |
| 429 | inline .TLS_AES_128_GCM_SHA256, .TLS_AES_256_GCM_SHA384 => |*p, tag| c: { | 420 | inline else => |*p, tag| c: { |
| 430 | const P = @TypeOf(p.*); | 421 | const P = @TypeOf(p.*); |
| 431 | // TODO verify the server's data | 422 | // TODO verify the server's data |
| 432 | const handshake_hash = p.transcript_hash.finalResult(); | 423 | const handshake_hash = p.transcript_hash.finalResult(); |
| ... | @@ -467,15 +458,6 @@ pub fn init(stream: net.Stream, host: []const u8) !Client { | ... | @@ -467,15 +458,6 @@ pub fn init(stream: net.Stream, host: []const u8) !Client { |
| 467 | .server_iv = hkdfExpandLabel(P.Hkdf, server_secret, "iv", "", P.AEAD.nonce_length), | 458 | .server_iv = hkdfExpandLabel(P.Hkdf, server_secret, "iv", "", P.AEAD.nonce_length), |
| 468 | }); | 459 | }); |
| 469 | }, | 460 | }, |
| 470 | .TLS_CHACHA20_POLY1305_SHA256 => { | | |
| 471 | @panic("TODO"); | | |
| 472 | }, | | |
| 473 | .TLS_AES_128_CCM_SHA256 => { | | |
| 474 | @panic("TODO"); | | |
| 475 | }, | | |
| 476 | .TLS_AES_128_CCM_8_SHA256 => { | | |
| 477 | @panic("TODO"); | | |
| 478 | }, | | |
| 479 | }; | 461 | }; |
| 480 | std.debug.print("remaining bytes: {d}\n", .{len - end}); | 462 | std.debug.print("remaining bytes: {d}\n", .{len - end}); |
| 481 | return .{ | 463 | return .{ |
| ... | @@ -524,7 +506,7 @@ pub fn write(c: *Client, stream: net.Stream, bytes: []const u8) !usize { | ... | @@ -524,7 +506,7 @@ pub fn write(c: *Client, stream: net.Stream, bytes: []const u8) !usize { |
| 524 | var bytes_i: usize = 0; | 506 | var bytes_i: usize = 0; |
| 525 | // How many bytes are taken up by overhead per record. | 507 | // How many bytes are taken up by overhead per record. |
| 526 | const overhead_len: usize = switch (c.application_cipher) { | 508 | const overhead_len: usize = switch (c.application_cipher) { |
| 527 | inline .TLS_AES_128_GCM_SHA256, .TLS_AES_256_GCM_SHA384 => |*p| l: { | 509 | inline else => |*p| l: { |
| 528 | const P = @TypeOf(p.*); | 510 | const P = @TypeOf(p.*); |
| 529 | const V = @Vector(P.AEAD.nonce_length, u8); | 511 | const V = @Vector(P.AEAD.nonce_length, u8); |
| 530 | const overhead_len = tls.ciphertext_record_header_len + P.AEAD.tag_length + 1; | 512 | const overhead_len = tls.ciphertext_record_header_len + P.AEAD.tag_length + 1; |
| ... | @@ -577,15 +559,6 @@ pub fn write(c: *Client, stream: net.Stream, bytes: []const u8) !usize { | ... | @@ -577,15 +559,6 @@ pub fn write(c: *Client, stream: net.Stream, bytes: []const u8) !usize { |
| 577 | iovec_end += 1; | 559 | iovec_end += 1; |
| 578 | } | 560 | } |
| 579 | }, | 561 | }, |
| 580 | .TLS_CHACHA20_POLY1305_SHA256 => { | | |
| 581 | @panic("TODO"); | | |
| 582 | }, | | |
| 583 | .TLS_AES_128_CCM_SHA256 => { | | |
| 584 | @panic("TODO"); | | |
| 585 | }, | | |
| 586 | .TLS_AES_128_CCM_8_SHA256 => { | | |
| 587 | @panic("TODO"); | | |
| 588 | }, | | |
| 589 | }; | 562 | }; |
| 590 | | 563 | |
| 591 | // Ideally we would call writev exactly once here, however, we must ensure | 564 | // Ideally we would call writev exactly once here, however, we must ensure |
| ... | @@ -659,7 +632,7 @@ pub fn read(c: *Client, stream: net.Stream, buffer: []u8) !usize { | ... | @@ -659,7 +632,7 @@ pub fn read(c: *Client, stream: net.Stream, buffer: []u8) !usize { |
| 659 | }, | 632 | }, |
| 660 | .application_data => { | 633 | .application_data => { |
| 661 | const cleartext_len = switch (c.application_cipher) { | 634 | const cleartext_len = switch (c.application_cipher) { |
| 662 | inline .TLS_AES_128_GCM_SHA256, .TLS_AES_256_GCM_SHA384 => |*p| c: { | 635 | inline else => |*p| c: { |
| 663 | const P = @TypeOf(p.*); | 636 | const P = @TypeOf(p.*); |
| 664 | const V = @Vector(P.AEAD.nonce_length, u8); | 637 | const V = @Vector(P.AEAD.nonce_length, u8); |
| 665 | const ad = frag[in - 5 ..][0..5]; | 638 | const ad = frag[in - 5 ..][0..5]; |
| ... | @@ -682,15 +655,6 @@ pub fn read(c: *Client, stream: net.Stream, buffer: []u8) !usize { | ... | @@ -682,15 +655,6 @@ pub fn read(c: *Client, stream: net.Stream, buffer: []u8) !usize { |
| 682 | return error.TlsBadRecordMac; | 655 | return error.TlsBadRecordMac; |
| 683 | break :c cleartext.len; | 656 | break :c cleartext.len; |
| 684 | }, | 657 | }, |
| 685 | .TLS_CHACHA20_POLY1305_SHA256 => { | | |
| 686 | @panic("TODO"); | | |
| 687 | }, | | |
| 688 | .TLS_AES_128_CCM_SHA256 => { | | |
| 689 | @panic("TODO"); | | |
| 690 | }, | | |
| 691 | .TLS_AES_128_CCM_8_SHA256 => { | | |
| 692 | @panic("TODO"); | | |
| 693 | }, | | |
| 694 | }; | 658 | }; |
| 695 | | 659 | |
| 696 | const inner_ct = @intToEnum(ContentType, buffer[out + cleartext_len - 1]); | 660 | const inner_ct = @intToEnum(ContentType, buffer[out + cleartext_len - 1]); |