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