| ... | @@ -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 | } |
| 275 | | 275 | |
| 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 case | 278 | // * 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 keep | 281 | // * 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 the | 282 | // 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(); |
| 317 | | 318 | |
| | 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 and | 349 | 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) catch | 362 | 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 | } |
| 900 | | 910 | |