| ... | @@ -33,7 +33,7 @@ received_close_notify: bool, | ... | @@ -33,7 +33,7 @@ received_close_notify: bool, |
| 33 | /// This makes the application vulnerable to truncation attacks unless the | 33 | /// This makes the application vulnerable to truncation attacks unless the |
| 34 | /// application layer itself verifies that the amount of data received equals | 34 | /// application layer itself verifies that the amount of data received equals |
| 35 | /// the amount of data expected, such as HTTP with the Content-Length header. | 35 | /// the amount of data expected, such as HTTP with the Content-Length header. |
| 36 | allow_truncation_attacks: bool = false, | 36 | allow_truncation_attacks: bool, |
| 37 | application_cipher: tls.ApplicationCipher, | 37 | application_cipher: tls.ApplicationCipher, |
| 38 | /// The size is enough to contain exactly one TLSCiphertext record. | 38 | /// The size is enough to contain exactly one TLSCiphertext record. |
| 39 | /// This buffer is segmented into four parts: | 39 | /// This buffer is segmented into four parts: |
| ... | @@ -44,6 +44,24 @@ application_cipher: tls.ApplicationCipher, | ... | @@ -44,6 +44,24 @@ application_cipher: tls.ApplicationCipher, |
| 44 | /// The fields `partial_cleartext_idx`, `partial_ciphertext_idx`, and | 44 | /// The fields `partial_cleartext_idx`, `partial_ciphertext_idx`, and |
| 45 | /// `partial_ciphertext_end` describe the span of the segments. | 45 | /// `partial_ciphertext_end` describe the span of the segments. |
| 46 | partially_read_buffer: [tls.max_ciphertext_record_len]u8, | 46 | partially_read_buffer: [tls.max_ciphertext_record_len]u8, |
| | 47 | /// If non-null, ssl secrets are logged to a file. Creating such a log file allows other |
| | 48 | /// programs with access to that file to decrypt all traffic over this connection. |
| | 49 | ssl_key_log: ?struct { |
| | 50 | client_key_seq: u64, |
| | 51 | server_key_seq: u64, |
| | 52 | client_random: [32]u8, |
| | 53 | file: std.fs.File, |
| | 54 | |
| | 55 | fn clientCounter(key_log: *@This()) u64 { |
| | 56 | defer key_log.client_key_seq += 1; |
| | 57 | return key_log.client_key_seq; |
| | 58 | } |
| | 59 | |
| | 60 | fn serverCounter(key_log: *@This()) u64 { |
| | 61 | defer key_log.server_key_seq += 1; |
| | 62 | return key_log.server_key_seq; |
| | 63 | } |
| | 64 | }, |
| 47 | | 65 | |
| 48 | /// This is an example of the type that is needed by the read and write | 66 | /// This is an example of the type that is needed by the read and write |
| 49 | /// functions. It can have any fields but it must at least have these | 67 | /// functions. It can have any fields but it must at least have these |
| ... | @@ -88,6 +106,32 @@ pub const StreamInterface = struct { | ... | @@ -88,6 +106,32 @@ pub const StreamInterface = struct { |
| 88 | } | 106 | } |
| 89 | }; | 107 | }; |
| 90 | | 108 | |
| | 109 | pub const Options = struct { |
| | 110 | /// How to perform host verification of server certificates. |
| | 111 | host: union(enum) { |
| | 112 | /// No host verification is performed, which prevents a trusted connection from |
| | 113 | /// being established. |
| | 114 | no_verification, |
| | 115 | /// Verify that the server certificate was issues for a given host. |
| | 116 | explicit: []const u8, |
| | 117 | }, |
| | 118 | /// How to verify the authenticity of server certificates. |
| | 119 | ca: union(enum) { |
| | 120 | /// No ca verification is performed, which prevents a trusted connection from |
| | 121 | /// being established. |
| | 122 | no_verification, |
| | 123 | /// Verify that the server certificate is a valid self-signed certificate. |
| | 124 | /// This provides no authorization guarantees, as anyone can create a |
| | 125 | /// self-signed certificate. |
| | 126 | self_signed, |
| | 127 | /// Verify that the server certificate is authorized by a given ca bundle. |
| | 128 | bundle: Certificate.Bundle, |
| | 129 | }, |
| | 130 | /// If non-null, ssl secrets are logged to this file. Creating such a log file allows |
| | 131 | /// other programs with access to that file to decrypt all traffic over this connection. |
| | 132 | ssl_key_log_file: ?std.fs.File = null, |
| | 133 | }; |
| | 134 | |
| 91 | pub fn InitError(comptime Stream: type) type { | 135 | pub fn InitError(comptime Stream: type) type { |
| 92 | return std.mem.Allocator.Error || Stream.WriteError || Stream.ReadError || tls.AlertDescription.Error || error{ | 136 | return std.mem.Allocator.Error || Stream.WriteError || Stream.ReadError || tls.AlertDescription.Error || error{ |
| 93 | InsufficientEntropy, | 137 | InsufficientEntropy, |
| ... | @@ -140,12 +184,17 @@ pub fn InitError(comptime Stream: type) type { | ... | @@ -140,12 +184,17 @@ pub fn InitError(comptime Stream: type) type { |
| 140 | /// must conform to `StreamInterface`. | 184 | /// must conform to `StreamInterface`. |
| 141 | /// | 185 | /// |
| 142 | /// `host` is only borrowed during this function call. | 186 | /// `host` is only borrowed during this function call. |
| 143 | pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) InitError(@TypeOf(stream))!Client { | 187 | pub fn init(stream: anytype, options: Options) InitError(@TypeOf(stream))!Client { |
| | 188 | const host = switch (options.host) { |
| | 189 | .no_verification => "", |
| | 190 | .explicit => |host| host, |
| | 191 | }; |
| 144 | const host_len: u16 = @intCast(host.len); | 192 | const host_len: u16 = @intCast(host.len); |
| 145 | | 193 | |
| 146 | var random_buffer: [128]u8 = undefined; | 194 | var random_buffer: [128]u8 = undefined; |
| 147 | crypto.random.bytes(&random_buffer); | 195 | crypto.random.bytes(&random_buffer); |
| 148 | const client_hello_rand = random_buffer[0..32].*; | 196 | const client_hello_rand = random_buffer[0..32].*; |
| | 197 | var key_seq: u64 = 0; |
| 149 | var server_hello_rand: [32]u8 = undefined; | 198 | var server_hello_rand: [32]u8 = undefined; |
| 150 | const legacy_session_id = random_buffer[32..64].*; | 199 | const legacy_session_id = random_buffer[32..64].*; |
| 151 | | 200 | |
| ... | @@ -179,15 +228,21 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In | ... | @@ -179,15 +228,21 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In |
| 179 | array(u16, u8, key_share.secp256r1_kp.public_key.toUncompressedSec1()) ++ | 228 | array(u16, u8, key_share.secp256r1_kp.public_key.toUncompressedSec1()) ++ |
| 180 | int(u16, @intFromEnum(tls.NamedGroup.x25519)) ++ | 229 | int(u16, @intFromEnum(tls.NamedGroup.x25519)) ++ |
| 181 | array(u16, u8, key_share.x25519_kp.public_key), | 230 | array(u16, u8, key_share.x25519_kp.public_key), |
| 182 | )) ++ int(u16, @intFromEnum(tls.ExtensionType.server_name)) ++ | 231 | )); |
| | 232 | const server_name_extension = int(u16, @intFromEnum(tls.ExtensionType.server_name)) ++ |
| 183 | int(u16, 2 + 1 + 2 + host_len) ++ // byte length of this extension payload | 233 | int(u16, 2 + 1 + 2 + host_len) ++ // byte length of this extension payload |
| 184 | int(u16, 1 + 2 + host_len) ++ // server_name_list byte count | 234 | int(u16, 1 + 2 + host_len) ++ // server_name_list byte count |
| 185 | .{0x00} ++ // name_type | 235 | .{0x00} ++ // name_type |
| 186 | int(u16, host_len); | 236 | int(u16, host_len); |
| | 237 | const server_name_extension_len = switch (options.host) { |
| | 238 | .no_verification => 0, |
| | 239 | .explicit => server_name_extension.len + host_len, |
| | 240 | }; |
| 187 | | 241 | |
| 188 | const extensions_header = | 242 | const extensions_header = |
| 189 | int(u16, @intCast(extensions_payload.len + host_len)) ++ | 243 | int(u16, @intCast(extensions_payload.len + server_name_extension_len)) ++ |
| 190 | extensions_payload; | 244 | extensions_payload ++ |
| | 245 | server_name_extension; |
| 191 | | 246 | |
| 192 | const client_hello = | 247 | const client_hello = |
| 193 | int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++ | 248 | int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++ |
| ... | @@ -198,20 +253,24 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In | ... | @@ -198,20 +253,24 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In |
| 198 | extensions_header; | 253 | extensions_header; |
| 199 | | 254 | |
| 200 | const out_handshake = .{@intFromEnum(tls.HandshakeType.client_hello)} ++ | 255 | const out_handshake = .{@intFromEnum(tls.HandshakeType.client_hello)} ++ |
| 201 | int(u24, @intCast(client_hello.len + host_len)) ++ | 256 | int(u24, @intCast(client_hello.len - server_name_extension.len + server_name_extension_len)) ++ |
| 202 | client_hello; | 257 | client_hello; |
| 203 | | 258 | |
| 204 | const cleartext_header = .{@intFromEnum(tls.ContentType.handshake)} ++ | 259 | const cleartext_header_buf = .{@intFromEnum(tls.ContentType.handshake)} ++ |
| 205 | int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_0)) ++ | 260 | int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_0)) ++ |
| 206 | int(u16, @intCast(out_handshake.len + host_len)) ++ | 261 | int(u16, @intCast(out_handshake.len - server_name_extension.len + server_name_extension_len)) ++ |
| 207 | out_handshake; | 262 | out_handshake; |
| | 263 | const cleartext_header = switch (options.host) { |
| | 264 | .no_verification => cleartext_header_buf[0 .. cleartext_header_buf.len - server_name_extension.len], |
| | 265 | .explicit => &cleartext_header_buf, |
| | 266 | }; |
| 208 | | 267 | |
| 209 | { | 268 | { |
| 210 | var iovecs = [_]std.posix.iovec_const{ | 269 | var iovecs = [_]std.posix.iovec_const{ |
| 211 | .{ .base = &cleartext_header, .len = cleartext_header.len }, | 270 | .{ .base = cleartext_header.ptr, .len = cleartext_header.len }, |
| 212 | .{ .base = host.ptr, .len = host.len }, | 271 | .{ .base = host.ptr, .len = host.len }, |
| 213 | }; | 272 | }; |
| 214 | try stream.writevAll(&iovecs); | 273 | try stream.writevAll(iovecs[0..if (host.len == 0) 1 else 2]); |
| 215 | } | 274 | } |
| 216 | | 275 | |
| 217 | var tls_version: tls.ProtocolVersion = undefined; | 276 | var tls_version: tls.ProtocolVersion = undefined; |
| ... | @@ -472,6 +531,12 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In | ... | @@ -472,6 +531,12 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In |
| 472 | pv.master_secret = P.Hkdf.extract(&ap_derived_secret, &zeroes); | 531 | pv.master_secret = P.Hkdf.extract(&ap_derived_secret, &zeroes); |
| 473 | const client_secret = hkdfExpandLabel(P.Hkdf, pv.handshake_secret, "c hs traffic", &hello_hash, P.Hash.digest_length); | 532 | const client_secret = hkdfExpandLabel(P.Hkdf, pv.handshake_secret, "c hs traffic", &hello_hash, P.Hash.digest_length); |
| 474 | const server_secret = hkdfExpandLabel(P.Hkdf, pv.handshake_secret, "s hs traffic", &hello_hash, P.Hash.digest_length); | 533 | const server_secret = hkdfExpandLabel(P.Hkdf, pv.handshake_secret, "s hs traffic", &hello_hash, P.Hash.digest_length); |
| | 534 | if (options.ssl_key_log_file) |key_log_file| logSecrets(key_log_file, .{ |
| | 535 | .client_random = &client_hello_rand, |
| | 536 | }, .{ |
| | 537 | .SERVER_HANDSHAKE_TRAFFIC_SECRET = &server_secret, |
| | 538 | .CLIENT_HANDSHAKE_TRAFFIC_SECRET = &client_secret, |
| | 539 | }); |
| 475 | pv.client_finished_key = hkdfExpandLabel(P.Hkdf, client_secret, "finished", "", P.Hmac.key_length); | 540 | pv.client_finished_key = hkdfExpandLabel(P.Hkdf, client_secret, "finished", "", P.Hmac.key_length); |
| 476 | pv.server_finished_key = hkdfExpandLabel(P.Hkdf, server_secret, "finished", "", P.Hmac.key_length); | 541 | pv.server_finished_key = hkdfExpandLabel(P.Hkdf, server_secret, "finished", "", P.Hmac.key_length); |
| 477 | pv.client_handshake_key = hkdfExpandLabel(P.Hkdf, client_secret, "key", "", P.AEAD.key_length); | 542 | pv.client_handshake_key = hkdfExpandLabel(P.Hkdf, client_secret, "key", "", P.AEAD.key_length); |
| ... | @@ -544,6 +609,13 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In | ... | @@ -544,6 +609,13 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In |
| 544 | const cert_size = certs_decoder.decode(u24); | 609 | const cert_size = certs_decoder.decode(u24); |
| 545 | const certd = try certs_decoder.sub(cert_size); | 610 | const certd = try certs_decoder.sub(cert_size); |
| 546 | | 611 | |
| | 612 | if (tls_version == .tls_1_3) { |
| | 613 | try certs_decoder.ensure(2); |
| | 614 | const total_ext_size = certs_decoder.decode(u16); |
| | 615 | const all_extd = try certs_decoder.sub(total_ext_size); |
| | 616 | _ = all_extd; |
| | 617 | } |
| | 618 | |
| 547 | const subject_cert: Certificate = .{ | 619 | const subject_cert: Certificate = .{ |
| 548 | .buffer = certd.buf, | 620 | .buffer = certd.buf, |
| 549 | .index = @intCast(certd.idx), | 621 | .index = @intCast(certd.idx), |
| ... | @@ -551,7 +623,10 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In | ... | @@ -551,7 +623,10 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In |
| 551 | const subject = try subject_cert.parse(); | 623 | const subject = try subject_cert.parse(); |
| 552 | if (cert_index == 0) { | 624 | if (cert_index == 0) { |
| 553 | // Verify the host on the first certificate. | 625 | // Verify the host on the first certificate. |
| 554 | try subject.verifyHostName(host); | 626 | switch (options.host) { |
| | 627 | .no_verification => {}, |
| | 628 | .explicit => try subject.verifyHostName(host), |
| | 629 | } |
| 555 | | 630 | |
| 556 | // Keep track of the public key for the | 631 | // Keep track of the public key for the |
| 557 | // certificate_verify message later. | 632 | // certificate_verify message later. |
| ... | @@ -560,23 +635,27 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In | ... | @@ -560,23 +635,27 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In |
| 560 | try prev_cert.verify(subject, now_sec); | 635 | try prev_cert.verify(subject, now_sec); |
| 561 | } | 636 | } |
| 562 | | 637 | |
| 563 | if (ca_bundle.verify(subject, now_sec)) |_| { | 638 | switch (options.ca) { |
| 564 | handshake_state = .trust_chain_established; | 639 | .no_verification => { |
| 565 | break :cert; | 640 | handshake_state = .trust_chain_established; |
| 566 | } else |err| switch (err) { | 641 | break :cert; |
| 567 | error.CertificateIssuerNotFound => {}, | 642 | }, |
| 568 | else => |e| return e, | 643 | .self_signed => { |
| | 644 | try subject.verify(subject, now_sec); |
| | 645 | handshake_state = .trust_chain_established; |
| | 646 | break :cert; |
| | 647 | }, |
| | 648 | .bundle => |ca_bundle| if (ca_bundle.verify(subject, now_sec)) |_| { |
| | 649 | handshake_state = .trust_chain_established; |
| | 650 | break :cert; |
| | 651 | } else |err| switch (err) { |
| | 652 | error.CertificateIssuerNotFound => {}, |
| | 653 | else => |e| return e, |
| | 654 | }, |
| 569 | } | 655 | } |
| 570 | | 656 | |
| 571 | prev_cert = subject; | 657 | prev_cert = subject; |
| 572 | cert_index += 1; | 658 | cert_index += 1; |
| 573 | | | |
| 574 | if (tls_version == .tls_1_3) { | | |
| 575 | try certs_decoder.ensure(2); | | |
| 576 | const total_ext_size = certs_decoder.decode(u16); | | |
| 577 | const all_extd = try certs_decoder.sub(total_ext_size); | | |
| 578 | _ = all_extd; | | |
| 579 | } | | |
| 580 | } | 659 | } |
| 581 | }, | 660 | }, |
| 582 | .server_key_exchange => { | 661 | .server_key_exchange => { |
| ... | @@ -625,6 +704,11 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In | ... | @@ -625,6 +704,11 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In |
| 625 | &client_hello_rand, | 704 | &client_hello_rand, |
| 626 | &server_hello_rand, | 705 | &server_hello_rand, |
| 627 | }, 48); | 706 | }, 48); |
| | 707 | if (options.ssl_key_log_file) |key_log_file| logSecrets(key_log_file, .{ |
| | 708 | .client_random = &client_hello_rand, |
| | 709 | }, .{ |
| | 710 | .CLIENT_RANDOM = &master_secret, |
| | 711 | }); |
| 628 | const key_block = hmacExpandLabel( | 712 | const key_block = hmacExpandLabel( |
| 629 | P.Hmac, | 713 | P.Hmac, |
| 630 | &master_secret, | 714 | &master_secret, |
| ... | @@ -748,6 +832,14 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In | ... | @@ -748,6 +832,14 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In |
| 748 | | 832 | |
| 749 | const client_secret = hkdfExpandLabel(P.Hkdf, pv.master_secret, "c ap traffic", &handshake_hash, P.Hash.digest_length); | 833 | const client_secret = hkdfExpandLabel(P.Hkdf, pv.master_secret, "c ap traffic", &handshake_hash, P.Hash.digest_length); |
| 750 | const server_secret = hkdfExpandLabel(P.Hkdf, pv.master_secret, "s ap traffic", &handshake_hash, P.Hash.digest_length); | 834 | const server_secret = hkdfExpandLabel(P.Hkdf, pv.master_secret, "s ap traffic", &handshake_hash, P.Hash.digest_length); |
| | 835 | if (options.ssl_key_log_file) |key_log_file| logSecrets(key_log_file, .{ |
| | 836 | .counter = key_seq, |
| | 837 | .client_random = &client_hello_rand, |
| | 838 | }, .{ |
| | 839 | .SERVER_TRAFFIC_SECRET = &server_secret, |
| | 840 | .CLIENT_TRAFFIC_SECRET = &client_secret, |
| | 841 | }); |
| | 842 | key_seq += 1; |
| 751 | break :app_cipher @unionInit(tls.ApplicationCipher, @tagName(tag), .{ .tls_1_3 = .{ | 843 | break :app_cipher @unionInit(tls.ApplicationCipher, @tagName(tag), .{ .tls_1_3 = .{ |
| 752 | .client_secret = client_secret, | 844 | .client_secret = client_secret, |
| 753 | .server_secret = server_secret, | 845 | .server_secret = server_secret, |
| ... | @@ -784,8 +876,15 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In | ... | @@ -784,8 +876,15 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In |
| 784 | .partial_ciphertext_idx = 0, | 876 | .partial_ciphertext_idx = 0, |
| 785 | .partial_ciphertext_end = @intCast(leftover.len), | 877 | .partial_ciphertext_end = @intCast(leftover.len), |
| 786 | .received_close_notify = false, | 878 | .received_close_notify = false, |
| | 879 | .allow_truncation_attacks = false, |
| 787 | .application_cipher = app_cipher, | 880 | .application_cipher = app_cipher, |
| 788 | .partially_read_buffer = undefined, | 881 | .partially_read_buffer = undefined, |
| | 882 | .ssl_key_log = if (options.ssl_key_log_file) |key_log_file| .{ |
| | 883 | .client_key_seq = key_seq, |
| | 884 | .server_key_seq = key_seq, |
| | 885 | .client_random = client_hello_rand, |
| | 886 | .file = key_log_file, |
| | 887 | } else null, |
| 789 | }; | 888 | }; |
| 790 | @memcpy(client.partially_read_buffer[0..leftover.len], leftover); | 889 | @memcpy(client.partially_read_buffer[0..leftover.len], leftover); |
| 791 | return client; | 890 | return client; |
| ... | @@ -1358,6 +1457,12 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.posix.iove | ... | @@ -1358,6 +1457,12 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.posix.iove |
| 1358 | const pv = &p.tls_1_3; | 1457 | const pv = &p.tls_1_3; |
| 1359 | const P = @TypeOf(p.*); | 1458 | const P = @TypeOf(p.*); |
| 1360 | const server_secret = hkdfExpandLabel(P.Hkdf, pv.server_secret, "traffic upd", "", P.Hash.digest_length); | 1459 | const server_secret = hkdfExpandLabel(P.Hkdf, pv.server_secret, "traffic upd", "", P.Hash.digest_length); |
| | 1460 | if (c.ssl_key_log) |*key_log| logSecrets(key_log.file, .{ |
| | 1461 | .counter = key_log.serverCounter(), |
| | 1462 | .client_random = &key_log.client_random, |
| | 1463 | }, .{ |
| | 1464 | .SERVER_TRAFFIC_SECRET = &server_secret, |
| | 1465 | }); |
| 1361 | pv.server_secret = server_secret; | 1466 | pv.server_secret = server_secret; |
| 1362 | pv.server_key = hkdfExpandLabel(P.Hkdf, server_secret, "key", "", P.AEAD.key_length); | 1467 | pv.server_key = hkdfExpandLabel(P.Hkdf, server_secret, "key", "", P.AEAD.key_length); |
| 1363 | pv.server_iv = hkdfExpandLabel(P.Hkdf, server_secret, "iv", "", P.AEAD.nonce_length); | 1468 | pv.server_iv = hkdfExpandLabel(P.Hkdf, server_secret, "iv", "", P.AEAD.nonce_length); |
| ... | @@ -1372,6 +1477,12 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.posix.iove | ... | @@ -1372,6 +1477,12 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.posix.iove |
| 1372 | const pv = &p.tls_1_3; | 1477 | const pv = &p.tls_1_3; |
| 1373 | const P = @TypeOf(p.*); | 1478 | const P = @TypeOf(p.*); |
| 1374 | const client_secret = hkdfExpandLabel(P.Hkdf, pv.client_secret, "traffic upd", "", P.Hash.digest_length); | 1479 | const client_secret = hkdfExpandLabel(P.Hkdf, pv.client_secret, "traffic upd", "", P.Hash.digest_length); |
| | 1480 | if (c.ssl_key_log) |*key_log| logSecrets(key_log.file, .{ |
| | 1481 | .counter = key_log.clientCounter(), |
| | 1482 | .client_random = &key_log.client_random, |
| | 1483 | }, .{ |
| | 1484 | .CLIENT_TRAFFIC_SECRET = &client_secret, |
| | 1485 | }); |
| 1375 | pv.client_secret = client_secret; | 1486 | pv.client_secret = client_secret; |
| 1376 | pv.client_key = hkdfExpandLabel(P.Hkdf, client_secret, "key", "", P.AEAD.key_length); | 1487 | pv.client_key = hkdfExpandLabel(P.Hkdf, client_secret, "key", "", P.AEAD.key_length); |
| 1377 | pv.client_iv = hkdfExpandLabel(P.Hkdf, client_secret, "iv", "", P.AEAD.nonce_length); | 1488 | pv.client_iv = hkdfExpandLabel(P.Hkdf, client_secret, "iv", "", P.AEAD.nonce_length); |
| ... | @@ -1426,6 +1537,18 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.posix.iove | ... | @@ -1426,6 +1537,18 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.posix.iove |
| 1426 | } | 1537 | } |
| 1427 | } | 1538 | } |
| 1428 | | 1539 | |
| | 1540 | fn logSecrets(key_log_file: std.fs.File, context: anytype, secrets: anytype) void { |
| | 1541 | const locked = if (key_log_file.lock(.exclusive)) |_| true else |_| false; |
| | 1542 | defer if (locked) key_log_file.unlock(); |
| | 1543 | key_log_file.seekFromEnd(0) catch {}; |
| | 1544 | inline for (@typeInfo(@TypeOf(secrets)).@"struct".fields) |field| key_log_file.writer().print("{s}" ++ |
| | 1545 | (if (@hasField(@TypeOf(context), "counter")) "_{d}" else "") ++ " {} {}\n", .{field.name} ++ |
| | 1546 | (if (@hasField(@TypeOf(context), "counter")) .{context.counter} else .{}) ++ .{ |
| | 1547 | std.fmt.fmtSliceHexLower(context.client_random), |
| | 1548 | std.fmt.fmtSliceHexLower(@field(secrets, field.name)), |
| | 1549 | }) catch {}; |
| | 1550 | } |
| | 1551 | |
| 1429 | fn finishRead(c: *Client, frag: []const u8, in: usize, out: usize) usize { | 1552 | fn finishRead(c: *Client, frag: []const u8, in: usize, out: usize) usize { |
| 1430 | const saved_buf = frag[in..]; | 1553 | const saved_buf = frag[in..]; |
| 1431 | if (c.partial_ciphertext_idx > c.partial_cleartext_idx) { | 1554 | if (c.partial_ciphertext_idx > c.partial_cleartext_idx) { |