authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-11-05 02:24:14-05:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-11-07 20:25:26-05:00
loga6ede7ba86987b9ae2bb6b8aac60f66af56e7b08
treededfcb6fedf772ec5e8d78f10ca413ea2d68a88e
parentde53e6e4f2dc7a41dc50b309fee87e06475e4838

std.crypto.tls: support handshake fragments


1 files changed, 32 insertions(+), 22 deletions(-)

lib/std/crypto/tls/Client.zig+32-22
...@@ -274,13 +274,14 @@ pub fn init(stream: anytype, options: Options) InitError(@TypeOf(stream))!Client...@@ -274,13 +274,14 @@ pub fn init(stream: anytype, options: Options) InitError(@TypeOf(stream))!Client
274 }274 }
275275
276 var tls_version: tls.ProtocolVersion = undefined;276 var tls_version: tls.ProtocolVersion = undefined;
277 // This is used for two purposes:277 // These are used for two purposes:
278 // * Detect whether a certificate is the first one presented, in which case278 // * Detect whether a certificate is the first one presented, in which case
279 // we need to verify the host name.279 // we need to verify the host name.
280 var cert_index: usize = 0;
280 // * Flip back and forth between the two cleartext buffers in order to keep281 // * Flip back and forth between the two cleartext buffers in order to keep
281 // the previous certificate in memory so that it can be verified by the282 // the previous certificate in memory so that it can be verified by the
282 // next one.283 // next one.
283 var cert_index: usize = 0;284 var cert_buf_index: usize = 0;
284 var write_seq: u64 = 0;285 var write_seq: u64 = 0;
285 var read_seq: u64 = 0;286 var read_seq: u64 = 0;
286 var prev_cert: Certificate.Parsed = undefined;287 var prev_cert: Certificate.Parsed = undefined;
...@@ -315,10 +316,12 @@ pub fn init(stream: anytype, options: Options) InitError(@TypeOf(stream))!Client...@@ -315,10 +316,12 @@ pub fn init(stream: anytype, options: Options) InitError(@TypeOf(stream))!Client
315 var main_cert_pub_key: CertificatePublicKey = undefined;316 var main_cert_pub_key: CertificatePublicKey = undefined;
316 const now_sec = std.time.timestamp();317 const now_sec = std.time.timestamp();
317318
319 var cleartext_fragment_start: usize = 0;
320 var cleartext_fragment_end: usize = 0;
318 var cleartext_bufs: [2][tls.max_ciphertext_inner_record_len]u8 = undefined;321 var cleartext_bufs: [2][tls.max_ciphertext_inner_record_len]u8 = undefined;
319 var handshake_buffer: [tls.max_ciphertext_record_len]u8 = undefined;322 var handshake_buffer: [tls.max_ciphertext_record_len]u8 = undefined;
320 var d: tls.Decoder = .{ .buf = &handshake_buffer };323 var d: tls.Decoder = .{ .buf = &handshake_buffer };
321 while (true) {324 fragment: while (true) {
322 try d.readAtLeastOurAmt(stream, tls.record_header_len);325 try d.readAtLeastOurAmt(stream, tls.record_header_len);
323 const record_header = d.buf[d.idx..][0..tls.record_header_len];326 const record_header = d.buf[d.idx..][0..tls.record_header_len];
324 const record_ct = d.decode(tls.ContentType);327 const record_ct = d.decode(tls.ContentType);
...@@ -332,15 +335,16 @@ pub fn init(stream: anytype, options: Options) InitError(@TypeOf(stream))!Client...@@ -332,15 +335,16 @@ pub fn init(stream: anytype, options: Options) InitError(@TypeOf(stream))!Client
332 std.debug.assert(tls_version == .tls_1_3);335 std.debug.assert(tls_version == .tls_1_3);
333 if (record_ct != .application_data) return error.TlsUnexpectedMessage;336 if (record_ct != .application_data) return error.TlsUnexpectedMessage;
334 try record_decoder.ensure(record_len);337 try record_decoder.ensure(record_len);
335 const cleartext_buf = &cleartext_bufs[cert_index % 2];338 const cleartext_buf = &cleartext_bufs[cert_buf_index % 2];
336 const cleartext = cleartext: switch (handshake_cipher) {339 switch (handshake_cipher) {
337 inline else => |*p| {340 inline else => |*p| {
338 const pv = &p.version.tls_1_3;341 const pv = &p.version.tls_1_3;
339 const P = @TypeOf(p.*).A;342 const P = @TypeOf(p.*).A;
340 if (record_len < P.AEAD.tag_length) return error.TlsRecordOverflow;343 if (record_len < P.AEAD.tag_length) return error.TlsRecordOverflow;
341 const ciphertext = record_decoder.slice(record_len - P.AEAD.tag_length);344 const ciphertext = record_decoder.slice(record_len - P.AEAD.tag_length);
342 if (ciphertext.len > cleartext_buf.len) return error.TlsRecordOverflow;345 const cleartext_fragment_buf = cleartext_buf[cleartext_fragment_end..];
343 const cleartext = cleartext_buf[0..ciphertext.len];346 if (ciphertext.len > cleartext_fragment_buf.len) return error.TlsRecordOverflow;
347 const cleartext = cleartext_fragment_buf[0..ciphertext.len];
344 const auth_tag = record_decoder.array(P.AEAD.tag_length).*;348 const auth_tag = record_decoder.array(P.AEAD.tag_length).*;
345 const nonce = if (builtin.zig_backend == .stage2_x86_64 and349 const nonce = if (builtin.zig_backend == .stage2_x86_64 and
346 P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)350 P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)
...@@ -357,27 +361,29 @@ pub fn init(stream: anytype, options: Options) InitError(@TypeOf(stream))!Client...@@ -357,27 +361,29 @@ pub fn init(stream: anytype, options: Options) InitError(@TypeOf(stream))!Client
357 };361 };
358 P.AEAD.decrypt(cleartext, ciphertext, auth_tag, record_header, nonce, pv.server_handshake_key) catch362 P.AEAD.decrypt(cleartext, ciphertext, auth_tag, record_header, nonce, pv.server_handshake_key) catch
359 return error.TlsBadRecordMac;363 return error.TlsBadRecordMac;
360 break :cleartext mem.trimRight(u8, cleartext, "\x00");364 cleartext_fragment_end += std.mem.trimRight(u8, cleartext, "\x00").len;
361 },365 },
362 };366 }
363 read_seq += 1;367 read_seq += 1;
364 const ct: tls.ContentType = @enumFromInt(cleartext[cleartext.len - 1]);368 cleartext_fragment_end -= 1;
369 const ct: tls.ContentType = @enumFromInt(cleartext_buf[cleartext_fragment_end]);
365 if (ct != .handshake) return error.TlsUnexpectedMessage;370 if (ct != .handshake) return error.TlsUnexpectedMessage;
366 break :content .{ tls.Decoder.fromTheirSlice(@constCast(cleartext[0 .. cleartext.len - 1])), ct };371 break :content .{ tls.Decoder.fromTheirSlice(@constCast(cleartext_buf[cleartext_fragment_start..cleartext_fragment_end])), ct };
367 },372 },
368 .application => {373 .application => {
369 std.debug.assert(tls_version == .tls_1_2);374 std.debug.assert(tls_version == .tls_1_2);
370 if (record_ct != .handshake) return error.TlsUnexpectedMessage;375 if (record_ct != .handshake) return error.TlsUnexpectedMessage;
371 try record_decoder.ensure(record_len);376 try record_decoder.ensure(record_len);
372 const cleartext_buf = &cleartext_bufs[cert_index % 2];377 const cleartext_buf = &cleartext_bufs[cert_buf_index % 2];
373 const cleartext = cleartext: switch (handshake_cipher) {378 switch (handshake_cipher) {
374 inline else => |*p| {379 inline else => |*p| {
375 const pv = &p.version.tls_1_2;380 const pv = &p.version.tls_1_2;
376 const P = @TypeOf(p.*).A;381 const P = @TypeOf(p.*).A;
377 if (record_len < P.record_iv_length + P.mac_length) return error.TlsRecordOverflow;382 if (record_len < P.record_iv_length + P.mac_length) return error.TlsRecordOverflow;
378 const message_len: u16 = record_len - P.record_iv_length - P.mac_length;383 const message_len: u16 = record_len - P.record_iv_length - P.mac_length;
379 if (message_len > cleartext_buf.len) return error.TlsRecordOverflow;384 const cleartext_fragment_buf = cleartext_buf[cleartext_fragment_end..];
380 const cleartext = cleartext_buf[0..message_len];385 if (message_len > cleartext_fragment_buf.len) return error.TlsRecordOverflow;
386 const cleartext = cleartext_fragment_buf[0..message_len];
381 const ad = std.mem.toBytes(big(read_seq)) ++387 const ad = std.mem.toBytes(big(read_seq)) ++
382 record_header[0 .. 1 + 2] ++388 record_header[0 .. 1 + 2] ++
383 std.mem.toBytes(big(message_len));389 std.mem.toBytes(big(message_len));
...@@ -400,16 +406,16 @@ pub fn init(stream: anytype, options: Options) InitError(@TypeOf(stream))!Client...@@ -400,16 +406,16 @@ pub fn init(stream: anytype, options: Options) InitError(@TypeOf(stream))!Client
400 const ciphertext = record_decoder.slice(message_len);406 const ciphertext = record_decoder.slice(message_len);
401 const auth_tag = record_decoder.array(P.mac_length);407 const auth_tag = record_decoder.array(P.mac_length);
402 P.AEAD.decrypt(cleartext, ciphertext, auth_tag.*, ad, nonce, pv.app_cipher.server_write_key) catch return error.TlsBadRecordMac;408 P.AEAD.decrypt(cleartext, ciphertext, auth_tag.*, ad, nonce, pv.app_cipher.server_write_key) catch return error.TlsBadRecordMac;
403 break :cleartext cleartext;409 cleartext_fragment_end += message_len;
404 },410 },
405 };411 }
406 read_seq += 1;412 read_seq += 1;
407 break :content .{ tls.Decoder.fromTheirSlice(cleartext), record_ct };413 break :content .{ tls.Decoder.fromTheirSlice(cleartext_buf[cleartext_fragment_start..cleartext_fragment_end]), record_ct };
408 },414 },
409 };415 };
410 switch (ct) {416 switch (ct) {
411 .alert => {417 .alert => {
412 try ctd.ensure(2);418 ctd.ensure(2) catch continue :fragment;
413 const level = ctd.decode(tls.AlertLevel);419 const level = ctd.decode(tls.AlertLevel);
414 const desc = ctd.decode(tls.AlertDescription);420 const desc = ctd.decode(tls.AlertDescription);
415 _ = level;421 _ = level;
...@@ -420,15 +426,15 @@ pub fn init(stream: anytype, options: Options) InitError(@TypeOf(stream))!Client...@@ -420,15 +426,15 @@ pub fn init(stream: anytype, options: Options) InitError(@TypeOf(stream))!Client
420 return error.TlsUnexpectedMessage;426 return error.TlsUnexpectedMessage;
421 },427 },
422 .change_cipher_spec => {428 .change_cipher_spec => {
423 try ctd.ensure(1);429 ctd.ensure(1) catch continue :fragment;
424 if (ctd.decode(tls.ChangeCipherSpecType) != .change_cipher_spec) return error.TlsIllegalParameter;430 if (ctd.decode(tls.ChangeCipherSpecType) != .change_cipher_spec) return error.TlsIllegalParameter;
425 cipher_state = pending_cipher_state;431 cipher_state = pending_cipher_state;
426 },432 },
427 .handshake => while (true) {433 .handshake => while (true) {
428 try ctd.ensure(4);434 ctd.ensure(4) catch continue :fragment;
429 const handshake_type = ctd.decode(tls.HandshakeType);435 const handshake_type = ctd.decode(tls.HandshakeType);
430 const handshake_len = ctd.decode(u24);436 const handshake_len = ctd.decode(u24);
431 var hsd = try ctd.sub(handshake_len);437 var hsd = ctd.sub(handshake_len) catch continue :fragment;
432 const wrapped_handshake = ctd.buf[ctd.idx - handshake_len - 4 .. ctd.idx];438 const wrapped_handshake = ctd.buf[ctd.idx - handshake_len - 4 .. ctd.idx];
433 switch (handshake_type) {439 switch (handshake_type) {
434 .server_hello => {440 .server_hello => {
...@@ -657,6 +663,7 @@ pub fn init(stream: anytype, options: Options) InitError(@TypeOf(stream))!Client...@@ -657,6 +663,7 @@ pub fn init(stream: anytype, options: Options) InitError(@TypeOf(stream))!Client
657 prev_cert = subject;663 prev_cert = subject;
658 cert_index += 1;664 cert_index += 1;
659 }665 }
666 cert_buf_index += 1;
660 },667 },
661 .server_key_exchange => {668 .server_key_exchange => {
662 if (tls_version != .tls_1_2) return error.TlsUnexpectedMessage;669 if (tls_version != .tls_1_2) return error.TlsUnexpectedMessage;
...@@ -892,9 +899,12 @@ pub fn init(stream: anytype, options: Options) InitError(@TypeOf(stream))!Client...@@ -892,9 +899,12 @@ pub fn init(stream: anytype, options: Options) InitError(@TypeOf(stream))!Client
892 else => return error.TlsUnexpectedMessage,899 else => return error.TlsUnexpectedMessage,
893 }900 }
894 if (ctd.eof()) break;901 if (ctd.eof()) break;
902 cleartext_fragment_start = ctd.idx;
895 },903 },
896 else => return error.TlsUnexpectedMessage,904 else => return error.TlsUnexpectedMessage,
897 }905 }
906 cleartext_fragment_start = 0;
907 cleartext_fragment_end = 0;
898 }908 }
899}909}
900910