authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-12-15 20:35:41-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-02 16:57:15-07:00
log40a85506b2e6a97af9c06bdcd001b6fd84cc549a
tree8a7bffbaf33a5638cd42a8d8e3a2ace188697240
parent595fff7cb664b5dc517a682b3daec5ee2767fe0d

std.crypto.Tls: add read/write methods


3 files changed, 459 insertions(+), 137 deletions(-)

lib/std/crypto/Tls.zig+427-135
...@@ -5,24 +5,24 @@ const mem = std.mem;...@@ -5,24 +5,24 @@ const mem = std.mem;
5const crypto = std.crypto;5const crypto = std.crypto;
6const assert = std.debug.assert;6const assert = std.debug.assert;
77
8state: State = .start,8application_cipher: ApplicationCipher,
9x25519_priv_key: [32]u8 = undefined,9read_seq: u64,
10x25519_pub_key: [32]u8 = undefined,10write_seq: u64,
11x25519_server_pub_key: [32]u8 = undefined,11/// The size is enough to contain exactly one TLSCiphertext record.
1212partially_read_buffer: [max_ciphertext_len + ciphertext_record_header_len]u8,
13const ProtocolVersion = enum(u16) {13/// The number of partially read bytes inside `partiall_read_buffer`.
14partially_read_len: u15,
15
16pub const ciphertext_record_header_len = 5;
17pub const max_ciphertext_len = (1 << 14) + 256;
18
19pub const ProtocolVersion = enum(u16) {
14 tls_1_2 = 0x0303,20 tls_1_2 = 0x0303,
15 tls_1_3 = 0x0304,21 tls_1_3 = 0x0304,
16 _,22 _,
17};23};
1824
19const State = enum {25pub const ContentType = enum(u8) {
20 /// In this state, all fields are undefined except state.
21 start,
22 sent_hello,
23};
24
25const ContentType = enum(u8) {
26 invalid = 0,26 invalid = 0,
27 change_cipher_spec = 20,27 change_cipher_spec = 20,
28 alert = 21,28 alert = 21,
...@@ -31,7 +31,7 @@ const ContentType = enum(u8) {...@@ -31,7 +31,7 @@ const ContentType = enum(u8) {
31 _,31 _,
32};32};
3333
34const HandshakeType = enum(u8) {34pub const HandshakeType = enum(u8) {
35 client_hello = 1,35 client_hello = 1,
36 server_hello = 2,36 server_hello = 2,
37 new_session_ticket = 4,37 new_session_ticket = 4,
...@@ -45,7 +45,7 @@ const HandshakeType = enum(u8) {...@@ -45,7 +45,7 @@ const HandshakeType = enum(u8) {
45 message_hash = 254,45 message_hash = 254,
46};46};
4747
48const ExtensionType = enum(u16) {48pub const ExtensionType = enum(u16) {
49 /// RFC 606649 /// RFC 6066
50 server_name = 0,50 server_name = 0,
51 /// RFC 606651 /// RFC 6066
...@@ -92,13 +92,13 @@ const ExtensionType = enum(u16) {...@@ -92,13 +92,13 @@ const ExtensionType = enum(u16) {
92 key_share = 51,92 key_share = 51,
93};93};
9494
95const AlertLevel = enum(u8) {95pub const AlertLevel = enum(u8) {
96 warning = 1,96 warning = 1,
97 fatal = 2,97 fatal = 2,
98 _,98 _,
99};99};
100100
101const AlertDescription = enum(u8) {101pub const AlertDescription = enum(u8) {
102 close_notify = 0,102 close_notify = 0,
103 unexpected_message = 10,103 unexpected_message = 10,
104 bad_record_mac = 20,104 bad_record_mac = 20,
...@@ -129,7 +129,7 @@ const AlertDescription = enum(u8) {...@@ -129,7 +129,7 @@ const AlertDescription = enum(u8) {
129 _,129 _,
130};130};
131131
132const SignatureScheme = enum(u16) {132pub const SignatureScheme = enum(u16) {
133 // RSASSA-PKCS1-v1_5 algorithms133 // RSASSA-PKCS1-v1_5 algorithms
134 rsa_pkcs1_sha256 = 0x0401,134 rsa_pkcs1_sha256 = 0x0401,
135 rsa_pkcs1_sha384 = 0x0501,135 rsa_pkcs1_sha384 = 0x0501,
...@@ -161,7 +161,7 @@ const SignatureScheme = enum(u16) {...@@ -161,7 +161,7 @@ const SignatureScheme = enum(u16) {
161 _,161 _,
162};162};
163163
164const NamedGroup = enum(u16) {164pub const NamedGroup = enum(u16) {
165 // Elliptic Curve Groups (ECDHE)165 // Elliptic Curve Groups (ECDHE)
166 secp256r1 = 0x0017,166 secp256r1 = 0x0017,
167 secp384r1 = 0x0018,167 secp384r1 = 0x0018,
...@@ -211,7 +211,7 @@ const NamedGroup = enum(u16) {...@@ -211,7 +211,7 @@ const NamedGroup = enum(u16) {
211// * ExtensionType extension_type;211// * ExtensionType extension_type;
212// * opaque extension_data<0..2^16-1>;212// * opaque extension_data<0..2^16-1>;
213213
214const CipherSuite = enum(u16) {214pub const CipherSuite = enum(u16) {
215 TLS_AES_128_GCM_SHA256 = 0x1301,215 TLS_AES_128_GCM_SHA256 = 0x1301,
216 TLS_AES_256_GCM_SHA384 = 0x1302,216 TLS_AES_256_GCM_SHA384 = 0x1302,
217 TLS_CHACHA20_POLY1305_SHA256 = 0x1303,217 TLS_CHACHA20_POLY1305_SHA256 = 0x1303,
...@@ -219,6 +219,73 @@ const CipherSuite = enum(u16) {...@@ -219,6 +219,73 @@ const CipherSuite = enum(u16) {
219 TLS_AES_128_CCM_8_SHA256 = 0x1305,219 TLS_AES_128_CCM_8_SHA256 = 0x1305,
220};220};
221221
222pub const CipherParams = union(CipherSuite) {
223 TLS_AES_128_GCM_SHA256: struct {
224 const AEAD = crypto.aead.aes_gcm.Aes128Gcm;
225 const Hash = crypto.hash.sha2.Sha256;
226 const Hmac = crypto.auth.hmac.Hmac(Hash);
227 const Hkdf = crypto.kdf.hkdf.Hkdf(Hmac);
228
229 handshake_secret: [Hkdf.key_len]u8,
230 master_secret: [Hkdf.key_len]u8,
231 client_handshake_key: [AEAD.key_length]u8,
232 server_handshake_key: [AEAD.key_length]u8,
233 client_finished_key: [Hmac.key_length]u8,
234 server_finished_key: [Hmac.key_length]u8,
235 client_handshake_iv: [AEAD.nonce_length]u8,
236 server_handshake_iv: [AEAD.nonce_length]u8,
237 transcript_hash: Hash,
238 },
239 TLS_AES_256_GCM_SHA384: struct {
240 const AEAD = crypto.aead.aes_gcm.Aes256Gcm;
241 const Hash = crypto.hash.sha2.Sha384;
242 const Hmac = crypto.auth.hmac.Hmac(Hash);
243 const Hkdf = crypto.kdf.hkdf.Hkdf(Hmac);
244
245 handshake_secret: [Hkdf.key_len]u8,
246 master_secret: [Hkdf.key_len]u8,
247 client_handshake_key: [AEAD.key_length]u8,
248 server_handshake_key: [AEAD.key_length]u8,
249 client_finished_key: [Hmac.key_length]u8,
250 server_finished_key: [Hmac.key_length]u8,
251 client_handshake_iv: [AEAD.nonce_length]u8,
252 server_handshake_iv: [AEAD.nonce_length]u8,
253 transcript_hash: Hash,
254 },
255 TLS_CHACHA20_POLY1305_SHA256: void,
256 TLS_AES_128_CCM_SHA256: void,
257 TLS_AES_128_CCM_8_SHA256: void,
258};
259
260/// Encryption parameters for application traffic.
261pub const ApplicationCipher = union(CipherSuite) {
262 TLS_AES_128_GCM_SHA256: struct {
263 const AEAD = crypto.aead.aes_gcm.Aes128Gcm;
264 const Hash = crypto.hash.sha2.Sha256;
265 const Hmac = crypto.auth.hmac.Hmac(Hash);
266 const Hkdf = crypto.kdf.hkdf.Hkdf(Hmac);
267
268 client_key: [AEAD.key_length]u8,
269 server_key: [AEAD.key_length]u8,
270 client_iv: [AEAD.nonce_length]u8,
271 server_iv: [AEAD.nonce_length]u8,
272 },
273 TLS_AES_256_GCM_SHA384: struct {
274 const AEAD = crypto.aead.aes_gcm.Aes256Gcm;
275 const Hash = crypto.hash.sha2.Sha384;
276 const Hmac = crypto.auth.hmac.Hmac(Hash);
277 const Hkdf = crypto.kdf.hkdf.Hkdf(Hmac);
278
279 client_key: [AEAD.key_length]u8,
280 server_key: [AEAD.key_length]u8,
281 client_iv: [AEAD.nonce_length]u8,
282 server_iv: [AEAD.nonce_length]u8,
283 },
284 TLS_CHACHA20_POLY1305_SHA256: void,
285 TLS_AES_128_CCM_SHA256: void,
286 TLS_AES_128_CCM_8_SHA256: void,
287};
288
222const cipher_suites = blk: {289const cipher_suites = blk: {
223 const fields = @typeInfo(CipherSuite).Enum.fields;290 const fields = @typeInfo(CipherSuite).Enum.fields;
224 var result: [(fields.len + 1) * 2]u8 = undefined;291 var result: [(fields.len + 1) * 2]u8 = undefined;
...@@ -231,10 +298,11 @@ const cipher_suites = blk: {...@@ -231,10 +298,11 @@ const cipher_suites = blk: {
231 break :blk result;298 break :blk result;
232};299};
233300
234pub fn init(tls: *Tls, stream: net.Stream, host: []const u8) !void {301/// `host` is only borrowed during this function call.
235 assert(tls.state == .start);302pub fn init(stream: net.Stream, host: []const u8) !Tls {
236 crypto.random.bytes(&tls.x25519_priv_key);303 var x25519_priv_key: [32]u8 = undefined;
237 tls.x25519_pub_key = crypto.dh.X25519.recoverPublicKey(tls.x25519_priv_key) catch |err| {304 crypto.random.bytes(&x25519_priv_key);
305 const x25519_pub_key = crypto.dh.X25519.recoverPublicKey(x25519_priv_key) catch |err| {
238 switch (err) {306 switch (err) {
239 // Only possible to happen if the private key is all zeroes.307 // Only possible to happen if the private key is all zeroes.
240 error.IdentityElement => return error.InsufficientEntropy,308 error.IdentityElement => return error.InsufficientEntropy,
...@@ -293,7 +361,7 @@ pub fn init(tls: *Tls, stream: net.Stream, host: []const u8) !void {...@@ -293,7 +361,7 @@ pub fn init(tls: *Tls, stream: net.Stream, host: []const u8) !void {
293 0, 36, // byte length of client_shares361 0, 36, // byte length of client_shares
294 0x00, 0x1D, // NamedGroup.x25519362 0x00, 0x1D, // NamedGroup.x25519
295 0, 32, // byte length of key_exchange363 0, 32, // byte length of key_exchange
296 } ++ tls.x25519_pub_key ++ [_]u8{364 } ++ x25519_pub_key ++ [_]u8{
297365
298 // Extension: server_name366 // Extension: server_name
299 0, 0, // ExtensionType.server_name367 0, 0, // ExtensionType.server_name
...@@ -330,25 +398,23 @@ pub fn init(tls: *Tls, stream: net.Stream, host: []const u8) !void {...@@ -330,25 +398,23 @@ pub fn init(tls: *Tls, stream: net.Stream, host: []const u8) !void {
330 mem.writeIntBig(u16, hello_header[hello_header.len - 5 ..][0..2], @intCast(u16, 3 + host.len));398 mem.writeIntBig(u16, hello_header[hello_header.len - 5 ..][0..2], @intCast(u16, 3 + host.len));
331 mem.writeIntBig(u16, hello_header[hello_header.len - 2 ..][0..2], @intCast(u16, 0 + host.len));399 mem.writeIntBig(u16, hello_header[hello_header.len - 2 ..][0..2], @intCast(u16, 0 + host.len));
332400
333 var iovecs = [_]std.os.iovec_const{401 {
334 .{402 var iovecs = [_]std.os.iovec_const{
335 .iov_base = &hello_header,403 .{
336 .iov_len = hello_header.len,404 .iov_base = &hello_header,
337 },405 .iov_len = hello_header.len,
338 .{406 },
339 .iov_base = host.ptr,407 .{
340 .iov_len = host.len,408 .iov_base = host.ptr,
341 },409 .iov_len = host.len,
342 };410 },
343 try stream.writevAll(&iovecs);411 };
412 try stream.writevAll(&iovecs);
413 }
344414
345 const client_hello_bytes1 = hello_header[5..];415 const client_hello_bytes1 = hello_header[5..];
346416
347 var client_handshake_key: [32]u8 = undefined;417 var cipher_params: CipherParams = undefined;
348 var server_handshake_key: [32]u8 = undefined;
349 var client_handshake_iv: [12]u8 = undefined;
350 var server_handshake_iv: [12]u8 = undefined;
351 var cipher_suite: CipherSuite = undefined;
352418
353 var handshake_buf: [4000]u8 = undefined;419 var handshake_buf: [4000]u8 = undefined;
354 var len: usize = 0;420 var len: usize = 0;
...@@ -386,16 +452,16 @@ pub fn init(tls: *Tls, stream: net.Stream, host: []const u8) !void {...@@ -386,16 +452,16 @@ pub fn init(tls: *Tls, stream: net.Stream, host: []const u8) !void {
386 const legacy_session_id_echo_len = hello[34];452 const legacy_session_id_echo_len = hello[34];
387 if (legacy_session_id_echo_len != 0) return error.TlsIllegalParameter;453 if (legacy_session_id_echo_len != 0) return error.TlsIllegalParameter;
388 const cipher_suite_int = mem.readIntBig(u16, hello[35..37]);454 const cipher_suite_int = mem.readIntBig(u16, hello[35..37]);
389 cipher_suite = std.meta.intToEnum(CipherSuite, cipher_suite_int) catch455 const cipher_suite_tag = std.meta.intToEnum(CipherSuite, cipher_suite_int) catch
390 return error.TlsIllegalParameter;456 return error.TlsIllegalParameter;
391 std.debug.print("server wants cipher suite {s}\n", .{@tagName(cipher_suite)});457 std.debug.print("server wants cipher suite {s}\n", .{@tagName(cipher_suite_tag)});
392 const legacy_compression_method = hello[37];458 const legacy_compression_method = hello[37];
393 _ = legacy_compression_method;459 _ = legacy_compression_method;
394 const extensions_size = mem.readIntBig(u16, hello[38..40]);460 const extensions_size = mem.readIntBig(u16, hello[38..40]);
395 if (40 + extensions_size != hello.len) return error.TlsBadLength;461 if (40 + extensions_size != hello.len) return error.TlsBadLength;
396 var i: usize = 40;462 var i: usize = 40;
397 var supported_version: u16 = 0;463 var supported_version: u16 = 0;
398 var have_server_pub_key = false;464 var opt_x25519_server_pub_key: ?*[32]u8 = null;
399 while (i < hello.len) {465 while (i < hello.len) {
400 const et = mem.readIntBig(u16, hello[i..][0..2]);466 const et = mem.readIntBig(u16, hello[i..][0..2]);
401 i += 2;467 i += 2;
...@@ -409,7 +475,7 @@ pub fn init(tls: *Tls, stream: net.Stream, host: []const u8) !void {...@@ -409,7 +475,7 @@ pub fn init(tls: *Tls, stream: net.Stream, host: []const u8) !void {
409 supported_version = mem.readIntBig(u16, hello[i..][0..2]);475 supported_version = mem.readIntBig(u16, hello[i..][0..2]);
410 },476 },
411 @enumToInt(ExtensionType.key_share) => {477 @enumToInt(ExtensionType.key_share) => {
412 if (have_server_pub_key) return error.TlsIllegalParameter;478 if (opt_x25519_server_pub_key != null) return error.TlsIllegalParameter;
413 const named_group = mem.readIntBig(u16, hello[i..][0..2]);479 const named_group = mem.readIntBig(u16, hello[i..][0..2]);
414 i += 2;480 i += 2;
415 switch (named_group) {481 switch (named_group) {
...@@ -417,8 +483,7 @@ pub fn init(tls: *Tls, stream: net.Stream, host: []const u8) !void {...@@ -417,8 +483,7 @@ pub fn init(tls: *Tls, stream: net.Stream, host: []const u8) !void {
417 const key_size = mem.readIntBig(u16, hello[i..][0..2]);483 const key_size = mem.readIntBig(u16, hello[i..][0..2]);
418 i += 2;484 i += 2;
419 if (key_size != 32) return error.TlsBadLength;485 if (key_size != 32) return error.TlsBadLength;
420 tls.x25519_server_pub_key = hello[i..][0..32].*;486 opt_x25519_server_pub_key = hello[i..][0..32];
421 have_server_pub_key = true;
422 },487 },
423 else => {488 else => {
424 std.debug.print("named group: {x}\n", .{named_group});489 std.debug.print("named group: {x}\n", .{named_group});
...@@ -432,7 +497,8 @@ pub fn init(tls: *Tls, stream: net.Stream, host: []const u8) !void {...@@ -432,7 +497,8 @@ pub fn init(tls: *Tls, stream: net.Stream, host: []const u8) !void {
432 }497 }
433 i = next_i;498 i = next_i;
434 }499 }
435 if (!have_server_pub_key) return error.TlsIllegalParameter;500 const x25519_server_pub_key = opt_x25519_server_pub_key orelse
501 return error.TlsIllegalParameter;
436 const tls_version = if (supported_version == 0) legacy_version else supported_version;502 const tls_version = if (supported_version == 0) legacy_version else supported_version;
437 switch (tls_version) {503 switch (tls_version) {
438 @enumToInt(ProtocolVersion.tls_1_2) => {504 @enumToInt(ProtocolVersion.tls_1_2) => {
...@@ -445,28 +511,44 @@ pub fn init(tls: *Tls, stream: net.Stream, host: []const u8) !void {...@@ -445,28 +511,44 @@ pub fn init(tls: *Tls, stream: net.Stream, host: []const u8) !void {
445 }511 }
446512
447 const shared_key = crypto.dh.X25519.scalarmult(513 const shared_key = crypto.dh.X25519.scalarmult(
448 tls.x25519_priv_key,514 x25519_priv_key,
449 tls.x25519_server_pub_key,515 x25519_server_pub_key.*,
450 ) catch return error.TlsDecryptFailure;516 ) catch return error.TlsDecryptFailure;
451517
452 switch (cipher_suite) {518 switch (cipher_suite_tag) {
453 .TLS_AES_128_GCM_SHA256 => {519 inline .TLS_AES_128_GCM_SHA256, .TLS_AES_256_GCM_SHA384 => |tag| {
454 const AEAD = crypto.aead.aes_gcm.Aes128Gcm;520 const P = std.meta.TagPayload(CipherParams, tag);
455 const Hash = crypto.hash.sha2.Sha256;521 cipher_params = @unionInit(CipherParams, @tagName(tag), .{
456 const Hmac = crypto.auth.hmac.Hmac(Hash);522 .handshake_secret = undefined,
457 const Hkdf = crypto.kdf.hkdf.Hkdf(Hmac);523 .master_secret = undefined,
458524 .client_handshake_key = undefined,
459 const hello_hash = helloHash(client_hello_bytes1, host, frag, Hash);525 .server_handshake_key = undefined,
460 const early_secret = Hkdf.extract(&[1]u8{0}, &([1]u8{0} ** Hash.digest_length));526 .client_finished_key = undefined,
461 const empty_hash = emptyHash(Hash);527 .server_finished_key = undefined,
462 const derived_secret = hkdfExpandLabel(Hkdf, early_secret, "derived", &empty_hash, Hash.digest_length);528 .client_handshake_iv = undefined,
463 const handshake_secret = Hkdf.extract(&derived_secret, &shared_key);529 .server_handshake_iv = undefined,
464 const client_secret = hkdfExpandLabel(Hkdf, handshake_secret, "c hs traffic", &hello_hash, Hash.digest_length);530 .transcript_hash = P.Hash.init(.{}),
465 const server_secret = hkdfExpandLabel(Hkdf, handshake_secret, "s hs traffic", &hello_hash, Hash.digest_length);531 });
466 client_handshake_key[0..AEAD.key_length].* = hkdfExpandLabel(Hkdf, client_secret, "key", "", AEAD.key_length);532 const p = &@field(cipher_params, @tagName(tag));
467 server_handshake_key[0..AEAD.key_length].* = hkdfExpandLabel(Hkdf, server_secret, "key", "", AEAD.key_length);533 p.transcript_hash.update(client_hello_bytes1); // Client Hello part 1
468 client_handshake_iv = hkdfExpandLabel(Hkdf, client_secret, "iv", "", AEAD.nonce_length);534 p.transcript_hash.update(host); // Client Hello part 2
469 server_handshake_iv = hkdfExpandLabel(Hkdf, server_secret, "iv", "", AEAD.nonce_length);535 p.transcript_hash.update(frag); // Server Hello
536 const hello_hash = p.transcript_hash.peek();
537 const zeroes = [1]u8{0} ** P.Hash.digest_length;
538 const early_secret = P.Hkdf.extract(&[1]u8{0}, &zeroes);
539 const empty_hash = emptyHash(P.Hash);
540 const hs_derived_secret = hkdfExpandLabel(P.Hkdf, early_secret, "derived", &empty_hash, P.Hash.digest_length);
541 p.handshake_secret = P.Hkdf.extract(&hs_derived_secret, &shared_key);
542 const ap_derived_secret = hkdfExpandLabel(P.Hkdf, p.handshake_secret, "derived", &empty_hash, P.Hash.digest_length);
543 p.master_secret = P.Hkdf.extract(&ap_derived_secret, &zeroes);
544 const client_secret = hkdfExpandLabel(P.Hkdf, p.handshake_secret, "c hs traffic", &hello_hash, P.Hash.digest_length);
545 const server_secret = hkdfExpandLabel(P.Hkdf, p.handshake_secret, "s hs traffic", &hello_hash, P.Hash.digest_length);
546 p.client_finished_key = hkdfExpandLabel(P.Hkdf, client_secret, "finished", "", P.Hmac.key_length);
547 p.server_finished_key = hkdfExpandLabel(P.Hkdf, server_secret, "finished", "", P.Hmac.key_length);
548 p.client_handshake_key = hkdfExpandLabel(P.Hkdf, client_secret, "key", "", P.AEAD.key_length);
549 p.server_handshake_key = hkdfExpandLabel(P.Hkdf, server_secret, "key", "", P.AEAD.key_length);
550 p.client_handshake_iv = hkdfExpandLabel(P.Hkdf, client_secret, "iv", "", P.AEAD.nonce_length);
551 p.server_handshake_iv = hkdfExpandLabel(P.Hkdf, server_secret, "iv", "", P.AEAD.nonce_length);
470 //std.debug.print("shared_key: {}\nhello_hash: {}\nearly_secret: {}\nempty_hash: {}\nderived_secret: {}\nhandshake_secret: {}\n client_secret: {}\n server_secret: {}\n", .{552 //std.debug.print("shared_key: {}\nhello_hash: {}\nearly_secret: {}\nempty_hash: {}\nderived_secret: {}\nhandshake_secret: {}\n client_secret: {}\n server_secret: {}\n", .{
471 // std.fmt.fmtSliceHexLower(&shared_key),553 // std.fmt.fmtSliceHexLower(&shared_key),
472 // std.fmt.fmtSliceHexLower(&hello_hash),554 // std.fmt.fmtSliceHexLower(&hello_hash),
...@@ -478,24 +560,6 @@ pub fn init(tls: *Tls, stream: net.Stream, host: []const u8) !void {...@@ -478,24 +560,6 @@ pub fn init(tls: *Tls, stream: net.Stream, host: []const u8) !void {
478 // std.fmt.fmtSliceHexLower(&server_secret),560 // std.fmt.fmtSliceHexLower(&server_secret),
479 //});561 //});
480 },562 },
481 .TLS_AES_256_GCM_SHA384 => {
482 const AEAD = crypto.aead.aes_gcm.Aes256Gcm;
483 const Hash = crypto.hash.sha2.Sha384;
484 const Hmac = crypto.auth.hmac.Hmac(Hash);
485 const Hkdf = crypto.kdf.hkdf.Hkdf(Hmac);
486
487 const hello_hash = helloHash(client_hello_bytes1, host, frag, Hash);
488 const early_secret = Hkdf.extract(&[1]u8{0}, &([1]u8{0} ** Hash.digest_length));
489 const empty_hash = emptyHash(Hash);
490 const derived_secret = hkdfExpandLabel(Hkdf, early_secret, "derived", &empty_hash, Hash.digest_length);
491 const handshake_secret = Hkdf.extract(&derived_secret, &shared_key);
492 const client_secret = hkdfExpandLabel(Hkdf, handshake_secret, "c hs traffic", &hello_hash, Hash.digest_length);
493 const server_secret = hkdfExpandLabel(Hkdf, handshake_secret, "s hs traffic", &hello_hash, Hash.digest_length);
494 client_handshake_key = hkdfExpandLabel(Hkdf, client_secret, "key", "", AEAD.key_length);
495 server_handshake_key = hkdfExpandLabel(Hkdf, server_secret, "key", "", AEAD.key_length);
496 client_handshake_iv = hkdfExpandLabel(Hkdf, client_secret, "iv", "", AEAD.nonce_length);
497 server_handshake_iv = hkdfExpandLabel(Hkdf, server_secret, "iv", "", AEAD.nonce_length);
498 },
499 .TLS_CHACHA20_POLY1305_SHA256 => {563 .TLS_CHACHA20_POLY1305_SHA256 => {
500 @panic("TODO");564 @panic("TODO");
501 },565 },
...@@ -541,50 +605,24 @@ pub fn init(tls: *Tls, stream: net.Stream, host: []const u8) !void {...@@ -541,50 +605,24 @@ pub fn init(tls: *Tls, stream: net.Stream, host: []const u8) !void {
541 },605 },
542 .application_data => {606 .application_data => {
543 var cleartext_buf: [1000]u8 = undefined;607 var cleartext_buf: [1000]u8 = undefined;
544 const cleartext = switch (cipher_suite) {608 const cleartext = switch (cipher_params) {
545 .TLS_AES_128_GCM_SHA256 => c: {609 inline .TLS_AES_128_GCM_SHA256, .TLS_AES_256_GCM_SHA384 => |*p| c: {
546 const AEAD = crypto.aead.aes_gcm.Aes128Gcm;610 const P = @TypeOf(p.*);
547 const ciphertext_len = record_size - AEAD.tag_length;611 const ciphertext_len = record_size - P.AEAD.tag_length;
548 const ciphertext = handshake_buf[i..][0..ciphertext_len];612 const ciphertext = handshake_buf[i..][0..ciphertext_len];
549 i += ciphertext.len;613 i += ciphertext.len;
550 if (ciphertext.len > cleartext_buf.len) return error.TlsRecordOverflow;614 if (ciphertext.len > cleartext_buf.len) return error.TlsRecordOverflow;
551 const cleartext = cleartext_buf[0..ciphertext.len];615 const cleartext = cleartext_buf[0..ciphertext.len];
552 const auth_tag = handshake_buf[i..][0..AEAD.tag_length].*;616 const auth_tag = handshake_buf[i..][0..P.AEAD.tag_length].*;
553 const V = @Vector(AEAD.nonce_length, u8);617 const V = @Vector(P.AEAD.nonce_length, u8);
554 const pad = [1]u8{0} ** (AEAD.nonce_length - 8);618 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
555 const operand: V = pad ++ @bitCast([8]u8, big(read_seq));619 const operand: V = pad ++ @bitCast([8]u8, big(read_seq));
556 read_seq += 1;620 read_seq += 1;
557 const nonce: [AEAD.nonce_length]u8 = @as(V, server_handshake_iv) ^ operand;621 const nonce: [P.AEAD.nonce_length]u8 = @as(V, p.server_handshake_iv) ^ operand;
558 //std.debug.print("seq: {d} nonce: {} operand: {}\n", .{
559 // read_seq - 1,
560 // std.fmt.fmtSliceHexLower(&nonce),
561 // std.fmt.fmtSliceHexLower(&@as([12]u8, operand)),
562 //});
563 const ad = handshake_buf[end_hdr - 5 ..][0..5];622 const ad = handshake_buf[end_hdr - 5 ..][0..5];
564 const key = server_handshake_key[0..AEAD.key_length].*;623 P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, p.server_handshake_key) catch
565 AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, key) catch
566 return error.TlsBadRecordMac;624 return error.TlsBadRecordMac;
567625 p.transcript_hash.update(cleartext[0 .. cleartext.len - 1]);
568 break :c cleartext;
569 },
570 .TLS_AES_256_GCM_SHA384 => c: {
571 const AEAD = crypto.aead.aes_gcm.Aes256Gcm;
572 const ciphertext_len = record_size - AEAD.tag_length;
573 const ciphertext = handshake_buf[i..][0..ciphertext_len];
574 i += ciphertext.len;
575 if (ciphertext.len > cleartext_buf.len) return error.TlsRecordOverflow;
576 const cleartext = cleartext_buf[0..ciphertext.len];
577 const auth_tag = handshake_buf[i..][0..AEAD.tag_length].*;
578 const V = @Vector(AEAD.nonce_length, u8);
579 const pad = [1]u8{0} ** (AEAD.nonce_length - 8);
580 const operand: V = pad ++ @bitCast([8]u8, big(read_seq));
581 read_seq += 1;
582 const nonce: [AEAD.nonce_length]u8 = @as(V, server_handshake_iv) ^ operand;
583 const ad = handshake_buf[end_hdr - 5 ..][0..5];
584 const key = server_handshake_key[0..AEAD.key_length].*;
585 AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, key) catch
586 return error.TlsBadRecordMac;
587
588 break :c cleartext;626 break :c cleartext;
589 },627 },
590 .TLS_CHACHA20_POLY1305_SHA256 => {628 .TLS_CHACHA20_POLY1305_SHA256 => {
...@@ -611,6 +649,86 @@ pub fn init(tls: *Tls, stream: net.Stream, host: []const u8) !void {...@@ -611,6 +649,86 @@ pub fn init(tls: *Tls, stream: net.Stream, host: []const u8) !void {
611 }649 }
612 std.debug.print("empty encrypted extensions\n", .{});650 std.debug.print("empty encrypted extensions\n", .{});
613 },651 },
652 @enumToInt(HandshakeType.certificate) => {
653 std.debug.print("cool certificate bro\n", .{});
654 },
655 @enumToInt(HandshakeType.certificate_verify) => {
656 std.debug.print("the certificate came with a fancy signature\n", .{});
657 },
658 @enumToInt(HandshakeType.finished) => {
659 // This message is to trick buggy proxies into behaving correctly.
660 const client_change_cipher_spec_msg = [_]u8{
661 @enumToInt(ContentType.change_cipher_spec),
662 0x03, 0x03, // legacy protocol version
663 0x00, 0x01, // length
664 0x01,
665 };
666 const app_cipher = switch (cipher_params) {
667 inline .TLS_AES_128_GCM_SHA256, .TLS_AES_256_GCM_SHA384 => |*p, tag| c: {
668 const P = @TypeOf(p.*);
669 // TODO verify the server's data
670 const handshake_hash = p.transcript_hash.finalResult();
671 const verify_data = hmac(P.Hmac, &handshake_hash, p.client_finished_key);
672 const out_cleartext = [_]u8{
673 @enumToInt(HandshakeType.finished),
674 0, 0, verify_data.len + 1 + P.AEAD.tag_length, // length
675 } ++ verify_data ++ [1]u8{@enumToInt(ContentType.handshake)};
676
677 const wrapped_len = out_cleartext.len + P.AEAD.tag_length;
678
679 var finished_msg = [_]u8{
680 @enumToInt(ContentType.application_data),
681 0x03, 0x03, // legacy protocol version
682 0, wrapped_len, // byte length of encrypted record
683 } ++ ([1]u8{undefined} ** wrapped_len);
684
685 const ad = finished_msg[0..5];
686 const ciphertext = finished_msg[5..][0..out_cleartext.len];
687 const auth_tag = finished_msg[finished_msg.len - P.AEAD.tag_length ..];
688 const nonce = p.client_handshake_iv;
689 P.AEAD.encrypt(ciphertext, auth_tag, &out_cleartext, ad, nonce, p.client_handshake_key);
690
691 {
692 var iovecs = [_]std.os.iovec_const{
693 .{
694 .iov_base = &client_change_cipher_spec_msg,
695 .iov_len = client_change_cipher_spec_msg.len,
696 },
697 .{
698 .iov_base = &finished_msg,
699 .iov_len = finished_msg.len,
700 },
701 };
702 try stream.writevAll(&iovecs);
703 }
704
705 const client_secret = hkdfExpandLabel(P.Hkdf, p.master_secret, "c ap traffic", &handshake_hash, P.Hash.digest_length);
706 const server_secret = hkdfExpandLabel(P.Hkdf, p.master_secret, "s ap traffic", &handshake_hash, P.Hash.digest_length);
707 break :c @unionInit(ApplicationCipher, @tagName(tag), .{
708 .client_key = hkdfExpandLabel(P.Hkdf, client_secret, "key", "", P.AEAD.key_length),
709 .server_key = hkdfExpandLabel(P.Hkdf, server_secret, "key", "", P.AEAD.key_length),
710 .client_iv = hkdfExpandLabel(P.Hkdf, client_secret, "iv", "", P.AEAD.nonce_length),
711 .server_iv = hkdfExpandLabel(P.Hkdf, server_secret, "iv", "", P.AEAD.nonce_length),
712 });
713 },
714 .TLS_CHACHA20_POLY1305_SHA256 => {
715 @panic("TODO");
716 },
717 .TLS_AES_128_CCM_SHA256 => {
718 @panic("TODO");
719 },
720 .TLS_AES_128_CCM_8_SHA256 => {
721 @panic("TODO");
722 },
723 };
724 return .{
725 .application_cipher = app_cipher,
726 .read_seq = read_seq,
727 .write_seq = 1,
728 .partially_read_buffer = undefined,
729 .partially_read_len = 0,
730 };
731 },
614 else => {732 else => {
615 std.debug.print("handshake type: {d}\n", .{cleartext[0]});733 std.debug.print("handshake type: {d}\n", .{cleartext[0]});
616 return error.TlsUnexpectedMessage;734 return error.TlsUnexpectedMessage;
...@@ -631,14 +749,185 @@ pub fn init(tls: *Tls, stream: net.Stream, host: []const u8) !void {...@@ -631,14 +749,185 @@ pub fn init(tls: *Tls, stream: net.Stream, host: []const u8) !void {
631 i = end;749 i = end;
632 }750 }
633751
634 tls.state = .sent_hello;752 return error.TlsHandshakeFailure;
753}
754
755pub fn write(tls: *Tls, stream: net.Stream, bytes: []const u8) !usize {
756 var ciphertext_buf: [max_ciphertext_len * 4]u8 = undefined;
757 var iovecs_buf: [5]std.os.iovec_const = undefined;
758 var ciphertext_end: usize = 0;
759 var iovec_end: usize = 0;
760 var bytes_i: usize = 0;
761 switch (tls.application_cipher) {
762 inline .TLS_AES_128_GCM_SHA256, .TLS_AES_256_GCM_SHA384 => |*p| {
763 const P = @TypeOf(p.*);
764 const V = @Vector(P.AEAD.nonce_length, u8);
765 while (true) {
766 const ciphertext_len = @intCast(u16, @min(
767 @min(bytes.len - bytes_i, max_ciphertext_len),
768 ciphertext_buf.len - 5 - P.AEAD.tag_length - ciphertext_end,
769 ));
770 if (ciphertext_len == 0) return bytes_i;
771
772 const wrapped_len = ciphertext_len + P.AEAD.tag_length;
773 const record = ciphertext_buf[ciphertext_end..][0 .. 5 + wrapped_len];
774
775 const ad = record[0..5];
776 ciphertext_end += 5;
777 const ciphertext = ciphertext_buf[ciphertext_end..][0..ciphertext_len];
778 ciphertext_end += ciphertext_len;
779 const auth_tag = ciphertext_buf[ciphertext_end..][0..P.AEAD.tag_length];
780 ciphertext_end += P.AEAD.tag_length;
781 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
782 const operand: V = pad ++ @bitCast([8]u8, big(tls.write_seq));
783 tls.write_seq += 1;
784 const nonce: [P.AEAD.nonce_length]u8 = @as(V, p.client_iv) ^ operand;
785 ad.* =
786 [_]u8{@enumToInt(ContentType.application_data)} ++
787 int2(@enumToInt(ProtocolVersion.tls_1_2)) ++
788 int2(wrapped_len);
789 const cleartext = bytes[bytes_i..ciphertext.len];
790 P.AEAD.encrypt(ciphertext, auth_tag, cleartext, ad, nonce, p.client_key);
791
792 iovecs_buf[iovec_end] = .{
793 .iov_base = record.ptr,
794 .iov_len = record.len,
795 };
796 iovec_end += 1;
797
798 bytes_i += ciphertext_len;
799 }
800 },
801 .TLS_CHACHA20_POLY1305_SHA256 => {
802 @panic("TODO");
803 },
804 .TLS_AES_128_CCM_SHA256 => {
805 @panic("TODO");
806 },
807 .TLS_AES_128_CCM_8_SHA256 => {
808 @panic("TODO");
809 },
810 }
811
812 // Ideally we would call writev exactly once here, however, we must ensure
813 // that we don't return with a record partially written.
814 var i: usize = 0;
815 var total_amt: usize = 0;
816 while (true) {
817 var amt = try stream.writev(iovecs_buf[i..iovec_end]);
818 total_amt += amt;
819 while (amt >= iovecs_buf[i].iov_len) {
820 amt -= iovecs_buf[i].iov_len;
821 i += 1;
822 // Rely on the property that iovecs delineate records, meaning that
823 // if amt equals zero here, we have fortunately found ourselves
824 // with a short read that aligns at the record boundary.
825 if (i >= iovec_end or amt == 0) return total_amt;
826 }
827 iovecs_buf[i].iov_base += amt;
828 iovecs_buf[i].iov_len -= amt;
829 }
635}830}
636831
637pub fn writeAll(tls: *Tls, stream: net.Stream, buffer: []const u8) !void {832pub fn writeAll(tls: *Tls, stream: net.Stream, bytes: []const u8) !void {
638 _ = tls;833 var index: usize = 0;
639 _ = stream;834 while (index < bytes.len) {
640 _ = buffer;835 index += try tls.write(stream, bytes[index..]);
641 @panic("hold on a minute, we didn't finish implementing the handshake yet");836 }
837}
838
839/// Returns number of bytes that have been read, which are now populated inside
840/// `buffer`. A return value of zero bytes does not necessarily mean end of
841/// stream.
842pub fn read(tls: *Tls, stream: net.Stream, buffer: []u8) !usize {
843 const prev_len = tls.partially_read_len;
844 var in_buf: [max_ciphertext_len * 4]u8 = undefined;
845 mem.copy(u8, &in_buf, tls.partially_read_buffer[0..prev_len]);
846
847 // Capacity of output buffer, in records, rounded up.
848 const buf_cap = (buffer.len +| (max_ciphertext_len - 1)) / max_ciphertext_len;
849 const wanted_read_len = buf_cap * (max_ciphertext_len + ciphertext_record_header_len);
850 const actual_read_len = try stream.read(in_buf[prev_len..@min(wanted_read_len, in_buf.len)]);
851 const frag = in_buf[0 .. prev_len + actual_read_len];
852 var in: usize = 0;
853 var out: usize = 0;
854
855 while (true) {
856 if (in + ciphertext_record_header_len > frag.len) {
857 return finishRead(tls, frag, in, out);
858 }
859 const ct = @intToEnum(ContentType, frag[in]);
860 in += 1;
861 const legacy_version = mem.readIntBig(u16, frag[in..][0..2]);
862 in += 2;
863 _ = legacy_version;
864 const record_size = mem.readIntBig(u16, frag[in..][0..2]);
865 in += 2;
866 const end = in + record_size;
867 if (end > frag.len) {
868 if (record_size > max_ciphertext_len) return error.TlsRecordOverflow;
869 return finishRead(tls, frag, in, out);
870 }
871 switch (ct) {
872 .alert => {
873 @panic("TODO handle an alert here");
874 },
875 .application_data => {
876 const cleartext_len = switch (tls.application_cipher) {
877 inline .TLS_AES_128_GCM_SHA256, .TLS_AES_256_GCM_SHA384 => |*p| c: {
878 const P = @TypeOf(p.*);
879 const V = @Vector(P.AEAD.nonce_length, u8);
880 const ciphertext_len = record_size - P.AEAD.tag_length;
881 const ciphertext = frag[in..][0..ciphertext_len];
882 in += ciphertext_len;
883 const auth_tag = frag[in..][0..P.AEAD.tag_length].*;
884 const cleartext = buffer[out..][0..ciphertext_len];
885 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
886 const operand: V = pad ++ @bitCast([8]u8, big(tls.read_seq));
887 tls.read_seq += 1;
888 const nonce: [P.AEAD.nonce_length]u8 = @as(V, p.server_iv) ^ operand;
889 const ad = frag[0..ciphertext_record_header_len];
890 P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, p.server_key) catch
891 return error.TlsBadRecordMac;
892 break :c cleartext.len;
893 },
894 .TLS_CHACHA20_POLY1305_SHA256 => {
895 @panic("TODO");
896 },
897 .TLS_AES_128_CCM_SHA256 => {
898 @panic("TODO");
899 },
900 .TLS_AES_128_CCM_8_SHA256 => {
901 @panic("TODO");
902 },
903 };
904
905 const inner_ct = buffer[out + cleartext_len - 1];
906 switch (inner_ct) {
907 @enumToInt(ContentType.handshake) => {
908 std.debug.print("the server wants to keep shaking hands\n", .{});
909 },
910 @enumToInt(ContentType.application_data) => {
911 out += cleartext_len - 1;
912 },
913 else => {
914 return error.TlsUnexpectedMessage;
915 },
916 }
917 },
918 else => {
919 return error.TlsUnexpectedMessage;
920 },
921 }
922 in = end;
923 }
924}
925
926fn finishRead(tls: *Tls, frag: []const u8, in: usize, out: usize) usize {
927 const saved_buf = frag[in..];
928 mem.copy(u8, &tls.partially_read_buffer, saved_buf);
929 tls.partially_read_len = @intCast(u15, saved_buf.len);
930 return out;
642}931}
643932
644fn hkdfExpandLabel(933fn hkdfExpandLabel(
...@@ -674,13 +963,9 @@ fn emptyHash(comptime Hash: type) [Hash.digest_length]u8 {...@@ -674,13 +963,9 @@ fn emptyHash(comptime Hash: type) [Hash.digest_length]u8 {
674 return result;963 return result;
675}964}
676965
677fn helloHash(s0: []const u8, s1: []const u8, s2: []const u8, comptime Hash: type) [Hash.digest_length]u8 {966fn hmac(comptime Hmac: type, message: []const u8, key: [Hmac.key_length]u8) [Hmac.mac_length]u8 {
678 var h = Hash.init(.{});967 var result: [Hmac.mac_length]u8 = undefined;
679 h.update(s0);968 Hmac.create(&result, message, &key);
680 h.update(s1);
681 h.update(s2);
682 var result: [Hash.digest_length]u8 = undefined;
683 h.final(&result);
684 return result;969 return result;
685}970}
686971
...@@ -693,3 +978,10 @@ inline fn big(x: anytype) @TypeOf(x) {...@@ -693,3 +978,10 @@ inline fn big(x: anytype) @TypeOf(x) {
693 .Little => @byteSwap(x),978 .Little => @byteSwap(x),
694 };979 };
695}980}
981
982inline fn int2(x: u16) [2]u8 {
983 return .{
984 @truncate(u8, x >> 8),
985 @truncate(u8, x),
986 };
987}
lib/std/crypto/sha2.zig+22
...@@ -142,6 +142,11 @@ fn Sha2x32(comptime params: Sha2Params32) type {...@@ -142,6 +142,11 @@ fn Sha2x32(comptime params: Sha2Params32) type {
142 d.total_len += b.len;142 d.total_len += b.len;
143 }143 }
144144
145 pub fn peek(d: Self) [digest_length]u8 {
146 var copy = d;
147 return copy.finalResult();
148 }
149
145 pub fn final(d: *Self, out: *[digest_length]u8) void {150 pub fn final(d: *Self, out: *[digest_length]u8) void {
146 // The buffer here will never be completely full.151 // The buffer here will never be completely full.
147 mem.set(u8, d.buf[d.buf_len..], 0);152 mem.set(u8, d.buf[d.buf_len..], 0);
...@@ -175,6 +180,12 @@ fn Sha2x32(comptime params: Sha2Params32) type {...@@ -175,6 +180,12 @@ fn Sha2x32(comptime params: Sha2Params32) type {
175 }180 }
176 }181 }
177182
183 pub fn finalResult(d: *Self) [digest_length]u8 {
184 var result: [digest_length]u8 = undefined;
185 d.final(&result);
186 return result;
187 }
188
178 const W = [64]u32{189 const W = [64]u32{
179 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,190 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,
180 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,191 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,
...@@ -621,6 +632,11 @@ fn Sha2x64(comptime params: Sha2Params64) type {...@@ -621,6 +632,11 @@ fn Sha2x64(comptime params: Sha2Params64) type {
621 d.total_len += b.len;632 d.total_len += b.len;
622 }633 }
623634
635 pub fn peek(d: Self) [digest_length]u8 {
636 var copy = d;
637 return copy.finalResult();
638 }
639
624 pub fn final(d: *Self, out: *[digest_length]u8) void {640 pub fn final(d: *Self, out: *[digest_length]u8) void {
625 // The buffer here will never be completely full.641 // The buffer here will never be completely full.
626 mem.set(u8, d.buf[d.buf_len..], 0);642 mem.set(u8, d.buf[d.buf_len..], 0);
...@@ -654,6 +670,12 @@ fn Sha2x64(comptime params: Sha2Params64) type {...@@ -654,6 +670,12 @@ fn Sha2x64(comptime params: Sha2Params64) type {
654 }670 }
655 }671 }
656672
673 pub fn finalResult(d: *Self) [digest_length]u8 {
674 var result: [digest_length]u8 = undefined;
675 d.final(&result);
676 return result;
677 }
678
657 fn round(d: *Self, b: *const [128]u8) void {679 fn round(d: *Self, b: *const [128]u8) void {
658 var s: [80]u64 = undefined;680 var s: [80]u64 = undefined;
659681
lib/std/http/Client.zig+10-2
...@@ -12,7 +12,7 @@ pub const Request = struct {...@@ -12,7 +12,7 @@ pub const Request = struct {
12 client: *Client,12 client: *Client,
13 stream: net.Stream,13 stream: net.Stream,
14 headers: std.ArrayListUnmanaged(u8) = .{},14 headers: std.ArrayListUnmanaged(u8) = .{},
15 tls: std.crypto.Tls = .{},15 tls: std.crypto.Tls,
16 protocol: Protocol,16 protocol: Protocol,
1717
18 pub const Protocol = enum { http, https };18 pub const Protocol = enum { http, https };
...@@ -55,6 +55,13 @@ pub const Request = struct {...@@ -55,6 +55,13 @@ pub const Request = struct {
55 },55 },
56 }56 }
57 }57 }
58
59 pub fn read(req: *Request, buffer: []u8) !usize {
60 switch (req.protocol) {
61 .http => return req.stream.read(buffer),
62 .https => return req.tls.read(req.stream, buffer),
63 }
64 }
58};65};
5966
60pub fn deinit(client: *Client) void {67pub fn deinit(client: *Client) void {
...@@ -68,6 +75,7 @@ pub fn request(client: *Client, options: Request.Options) !Request {...@@ -68,6 +75,7 @@ pub fn request(client: *Client, options: Request.Options) !Request {
68 .client = client,75 .client = client,
69 .stream = try net.tcpConnectToHost(client.allocator, options.host, options.port),76 .stream = try net.tcpConnectToHost(client.allocator, options.host, options.port),
70 .protocol = options.protocol,77 .protocol = options.protocol,
78 .tls = undefined,
71 };79 };
72 client.active_requests += 1;80 client.active_requests += 1;
73 errdefer req.deinit();81 errdefer req.deinit();
...@@ -75,7 +83,7 @@ pub fn request(client: *Client, options: Request.Options) !Request {...@@ -75,7 +83,7 @@ pub fn request(client: *Client, options: Request.Options) !Request {
75 switch (options.protocol) {83 switch (options.protocol) {
76 .http => {},84 .http => {},
77 .https => {85 .https => {
78 try req.tls.init(req.stream, options.host);86 req.tls = try std.crypto.Tls.init(req.stream, options.host);
79 },87 },
80 }88 }
8189