| ... | ... | @@ -23,27 +23,27 @@ partially_read_buffer: [tls.max_ciphertext_record_len]u8, |
| 23 | 23 | partially_read_len: u15, |
| 24 | 24 | eof: bool, |
| 25 | 25 | |
| 26 | | const cipher_suites = blk: { |
| 27 | | const fields = @typeInfo(CipherSuite).Enum.fields; |
| 28 | | var result: [(fields.len + 1) * 2]u8 = undefined; |
| 29 | | mem.writeIntBig(u16, result[0..2], result.len - 2); |
| 30 | | for (fields) |field, i| { |
| 31 | | const int = @enumToInt(@field(CipherSuite, field.name)); |
| 32 | | result[(i + 1) * 2] = @truncate(u8, int >> 8); |
| 33 | | result[(i + 1) * 2 + 1] = @truncate(u8, int); |
| 34 | | } |
| 35 | | break :blk result; |
| 36 | | }; |
| 26 | // Measurement taken with 0.11.0-dev.810+c2f5848fe |
| 27 | // on x86_64-linux Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz: |
| 28 | // zig run .lib/std/crypto/benchmark.zig -OReleaseFast |
| 29 | // aegis-128l: 15382 MiB/s |
| 30 | // aegis-256: 9553 MiB/s |
| 31 | // aes128-gcm: 3721 MiB/s |
| 32 | // aes256-gcm: 3010 MiB/s |
| 33 | // chacha20Poly1305: 597 MiB/s |
| 34 | |
| 35 | const cipher_suites = |
| 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 | 42 | /// `host` is only borrowed during this function call. |
| 39 | 43 | pub fn init(stream: net.Stream, host: []const u8) !Client { |
| 40 | | var x25519_priv_key: [32]u8 = undefined; |
| 41 | | crypto.random.bytes(&x25519_priv_key); |
| 42 | | const x25519_pub_key = crypto.dh.X25519.recoverPublicKey(x25519_priv_key) catch |err| { |
| 43 | | switch (err) { |
| 44 | | // Only possible to happen if the private key is all zeroes. |
| 45 | | error.IdentityElement => return error.InsufficientEntropy, |
| 46 | | } |
| 44 | const kp = crypto.dh.X25519.KeyPair.create(null) catch |err| switch (err) { |
| 45 | // Only possible to happen if the private key is all zeroes. |
| 46 | error.IdentityElement => return error.InsufficientEntropy, |
| 47 | 47 | }; |
| 48 | 48 | |
| 49 | 49 | // random (u32) |
| ... | ... | @@ -98,7 +98,7 @@ pub fn init(stream: net.Stream, host: []const u8) !Client { |
| 98 | 98 | 0, 36, // byte length of client_shares |
| 99 | 99 | 0x00, 0x1D, // NamedGroup.x25519 |
| 100 | 100 | 0, 32, // byte length of key_exchange |
| 101 | | } ++ x25519_pub_key ++ [_]u8{ |
| 101 | } ++ kp.public_key ++ [_]u8{ |
| 102 | 102 | |
| 103 | 103 | // Extension: server_name |
| 104 | 104 | 0, 0, // ExtensionType.server_name |
| ... | ... | @@ -120,7 +120,9 @@ pub fn init(stream: net.Stream, host: []const u8) !Client { |
| 120 | 120 | |
| 121 | 121 | // ClientHello |
| 122 | 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 | 126 | 0x01, 0x00, // legacy_compression_methods |
| 125 | 127 | } ++ extensions_header; |
| 126 | 128 | |
| ... | ... | @@ -191,9 +193,8 @@ pub fn init(stream: net.Stream, host: []const u8) !Client { |
| 191 | 193 | const legacy_session_id_echo_len = hello[34]; |
| 192 | 194 | if (legacy_session_id_echo_len != 0) return error.TlsIllegalParameter; |
| 193 | 195 | const cipher_suite_int = mem.readIntBig(u16, hello[35..37]); |
| 194 | | const cipher_suite_tag = std.meta.intToEnum(CipherSuite, cipher_suite_int) catch |
| 195 | | return error.TlsIllegalParameter; |
| 196 | | std.debug.print("server wants cipher suite {s}\n", .{@tagName(cipher_suite_tag)}); |
| 196 | const cipher_suite_tag = @intToEnum(CipherSuite, cipher_suite_int); |
| 197 | std.debug.print("server wants cipher suite {any}\n", .{cipher_suite_tag}); |
| 197 | 198 | const legacy_compression_method = hello[37]; |
| 198 | 199 | _ = legacy_compression_method; |
| 199 | 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 | 251 | } |
| 251 | 252 | |
| 252 | 253 | const shared_key = crypto.dh.X25519.scalarmult( |
| 253 | | x25519_priv_key, |
| 254 | kp.secret_key, |
| 254 | 255 | x25519_server_pub_key.*, |
| 255 | 256 | ) catch return error.TlsDecryptFailure; |
| 256 | 257 | |
| 257 | 258 | switch (cipher_suite_tag) { |
| 258 | | inline .TLS_AES_128_GCM_SHA256, .TLS_AES_256_GCM_SHA384 => |tag| { |
| 259 | | const P = std.meta.TagPayload(CipherParams, tag); |
| 259 | inline .AES_128_GCM_SHA256, |
| 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 | 266 | cipher_params = @unionInit(CipherParams, @tagName(tag), .{ |
| 261 | 267 | .handshake_secret = undefined, |
| 262 | 268 | .master_secret = undefined, |
| ... | ... | @@ -301,14 +307,8 @@ pub fn init(stream: net.Stream, host: []const u8) !Client { |
| 301 | 307 | // std.fmt.fmtSliceHexLower(&p.server_handshake_iv), |
| 302 | 308 | //}); |
| 303 | 309 | }, |
| 304 | | .TLS_CHACHA20_POLY1305_SHA256 => { |
| 305 | | @panic("TODO"); |
| 306 | | }, |
| 307 | | .TLS_AES_128_CCM_SHA256 => { |
| 308 | | @panic("TODO"); |
| 309 | | }, |
| 310 | | .TLS_AES_128_CCM_8_SHA256 => { |
| 311 | | @panic("TODO"); |
| 310 | else => { |
| 311 | return error.TlsIllegalParameter; |
| 312 | 312 | }, |
| 313 | 313 | } |
| 314 | 314 | }, |
| ... | ... | @@ -347,7 +347,7 @@ pub fn init(stream: net.Stream, host: []const u8) !Client { |
| 347 | 347 | .application_data => { |
| 348 | 348 | var cleartext_buf: [8000]u8 = undefined; |
| 349 | 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 | 351 | const P = @TypeOf(p.*); |
| 352 | 352 | const ciphertext_len = record_size - P.AEAD.tag_length; |
| 353 | 353 | const ciphertext = handshake_buf[i..][0..ciphertext_len]; |
| ... | ... | @@ -366,15 +366,6 @@ pub fn init(stream: net.Stream, host: []const u8) !Client { |
| 366 | 366 | p.transcript_hash.update(cleartext[0 .. cleartext.len - 1]); |
| 367 | 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 | 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 | 417 | 0x01, |
| 427 | 418 | }; |
| 428 | 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 | 421 | const P = @TypeOf(p.*); |
| 431 | 422 | // TODO verify the server's data |
| 432 | 423 | const handshake_hash = p.transcript_hash.finalResult(); |
| ... | ... | @@ -467,15 +458,6 @@ pub fn init(stream: net.Stream, host: []const u8) !Client { |
| 467 | 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 | 462 | std.debug.print("remaining bytes: {d}\n", .{len - end}); |
| 481 | 463 | return .{ |
| ... | ... | @@ -524,7 +506,7 @@ pub fn write(c: *Client, stream: net.Stream, bytes: []const u8) !usize { |
| 524 | 506 | var bytes_i: usize = 0; |
| 525 | 507 | // How many bytes are taken up by overhead per record. |
| 526 | 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 | 510 | const P = @TypeOf(p.*); |
| 529 | 511 | const V = @Vector(P.AEAD.nonce_length, u8); |
| 530 | 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 | 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 | 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 | 632 | }, |
| 660 | 633 | .application_data => { |
| 661 | 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 | 636 | const P = @TypeOf(p.*); |
| 664 | 637 | const V = @Vector(P.AEAD.nonce_length, u8); |
| 665 | 638 | const ad = frag[in - 5 ..][0..5]; |
| ... | ... | @@ -682,15 +655,6 @@ pub fn read(c: *Client, stream: net.Stream, buffer: []u8) !usize { |
| 682 | 655 | return error.TlsBadRecordMac; |
| 683 | 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 | 660 | const inner_ct = @intToEnum(ContentType, buffer[out + cleartext_len - 1]); |