| ... | ... | @@ -61,9 +61,6 @@ pub const ReadError = error{ |
| 61 | 61 | TlsUnexpectedMessage, |
| 62 | 62 | TlsIllegalParameter, |
| 63 | 63 | TlsSequenceOverflow, |
| 64 | | /// The buffer provided to the read function was not at least |
| 65 | | /// `min_buffer_len`. |
| 66 | | OutputBufferUndersize, |
| 67 | 64 | }; |
| 68 | 65 | |
| 69 | 66 | pub const SslKeyLog = struct { |
| ... | ... | @@ -372,7 +369,8 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client |
| 372 | 369 | }; |
| 373 | 370 | P.AEAD.decrypt(cleartext, ciphertext, auth_tag, record_header, nonce, pv.server_handshake_key) catch |
| 374 | 371 | return error.TlsBadRecordMac; |
| 375 | | cleartext_fragment_end += std.mem.trimEnd(u8, cleartext, "\x00").len; |
| 372 | // TODO use scalar, non-slice version |
| 373 | cleartext_fragment_end += mem.trimEnd(u8, cleartext, "\x00").len; |
| 376 | 374 | }, |
| 377 | 375 | } |
| 378 | 376 | read_seq += 1; |
| ... | ... | @@ -395,9 +393,9 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client |
| 395 | 393 | const cleartext_fragment_buf = cleartext_buf[cleartext_fragment_end..]; |
| 396 | 394 | if (message_len > cleartext_fragment_buf.len) return error.TlsRecordOverflow; |
| 397 | 395 | const cleartext = cleartext_fragment_buf[0..message_len]; |
| 398 | | const ad = std.mem.toBytes(big(read_seq)) ++ |
| 396 | const ad = mem.toBytes(big(read_seq)) ++ |
| 399 | 397 | record_header[0 .. 1 + 2] ++ |
| 400 | | std.mem.toBytes(big(message_len)); |
| 398 | mem.toBytes(big(message_len)); |
| 401 | 399 | const record_iv = record_decoder.array(P.record_iv_length).*; |
| 402 | 400 | const masked_read_seq = read_seq & |
| 403 | 401 | comptime std.math.shl(u64, std.math.maxInt(u64), 8 * P.record_iv_length); |
| ... | ... | @@ -738,7 +736,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client |
| 738 | 736 | &.{ "server finished", &p.transcript_hash.finalResult() }, |
| 739 | 737 | P.verify_data_length, |
| 740 | 738 | ), |
| 741 | | .app_cipher = std.mem.bytesToValue(P.Tls_1_2, &key_block), |
| 739 | .app_cipher = mem.bytesToValue(P.Tls_1_2, &key_block), |
| 742 | 740 | } }; |
| 743 | 741 | const pv = &p.version.tls_1_2; |
| 744 | 742 | const nonce: [P.AEAD.nonce_length]u8 = nonce: { |
| ... | ... | @@ -756,7 +754,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client |
| 756 | 754 | client_verify_cleartext.len ..][0..client_verify_cleartext.len], |
| 757 | 755 | client_verify_msg[client_verify_msg.len - P.mac_length ..][0..P.mac_length], |
| 758 | 756 | &client_verify_cleartext, |
| 759 | | std.mem.toBytes(big(write_seq)) ++ client_verify_msg[0 .. 1 + 2] ++ int(u16, client_verify_cleartext.len), |
| 757 | mem.toBytes(big(write_seq)) ++ client_verify_msg[0 .. 1 + 2] ++ int(u16, client_verify_cleartext.len), |
| 760 | 758 | nonce, |
| 761 | 759 | pv.app_cipher.client_write_key, |
| 762 | 760 | ); |
| ... | ... | @@ -873,7 +871,10 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client |
| 873 | 871 | .input = input, |
| 874 | 872 | .reader = .{ |
| 875 | 873 | .buffer = options.read_buffer, |
| 876 | | .vtable = &.{ .stream = stream }, |
| 874 | .vtable = &.{ |
| 875 | .stream = stream, |
| 876 | .readVec = readVec, |
| 877 | }, |
| 877 | 878 | .seek = 0, |
| 878 | 879 | .end = 0, |
| 879 | 880 | }, |
| ... | ... | @@ -1017,7 +1018,7 @@ fn prepareCiphertextRecord( |
| 1017 | 1018 | const nonce = nonce: { |
| 1018 | 1019 | const V = @Vector(P.AEAD.nonce_length, u8); |
| 1019 | 1020 | const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8); |
| 1020 | | const operand: V = pad ++ std.mem.toBytes(big(c.write_seq)); |
| 1021 | const operand: V = pad ++ mem.toBytes(big(c.write_seq)); |
| 1021 | 1022 | break :nonce @as(V, pv.client_iv) ^ operand; |
| 1022 | 1023 | }; |
| 1023 | 1024 | P.AEAD.encrypt(ciphertext, auth_tag, cleartext, ad, nonce, pv.client_key); |
| ... | ... | @@ -1048,7 +1049,7 @@ fn prepareCiphertextRecord( |
| 1048 | 1049 | record_header.* = .{@intFromEnum(inner_content_type)} ++ |
| 1049 | 1050 | int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++ |
| 1050 | 1051 | int(u16, P.record_iv_length + message_len + P.mac_length); |
| 1051 | | const ad = std.mem.toBytes(big(c.write_seq)) ++ record_header[0 .. 1 + 2] ++ int(u16, message_len); |
| 1052 | const ad = mem.toBytes(big(c.write_seq)) ++ record_header[0 .. 1 + 2] ++ int(u16, message_len); |
| 1052 | 1053 | const record_iv = ciphertext_buf[ciphertext_end..][0..P.record_iv_length]; |
| 1053 | 1054 | ciphertext_end += P.record_iv_length; |
| 1054 | 1055 | const nonce: [P.AEAD.nonce_length]u8 = nonce: { |
| ... | ... | @@ -1076,7 +1077,22 @@ pub fn eof(c: Client) bool { |
| 1076 | 1077 | } |
| 1077 | 1078 | |
| 1078 | 1079 | fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize { |
| 1080 | // This function writes exclusively to the buffer. |
| 1081 | _ = w; |
| 1082 | _ = limit; |
| 1083 | const c: *Client = @alignCast(@fieldParentPtr("reader", r)); |
| 1084 | return readIndirect(c); |
| 1085 | } |
| 1086 | |
| 1087 | fn readVec(r: *Reader, data: [][]u8) Reader.Error!usize { |
| 1088 | // This function writes exclusively to the buffer. |
| 1089 | _ = data; |
| 1079 | 1090 | const c: *Client = @alignCast(@fieldParentPtr("reader", r)); |
| 1091 | return readIndirect(c); |
| 1092 | } |
| 1093 | |
| 1094 | fn readIndirect(c: *Client) Reader.Error!usize { |
| 1095 | const r = &c.reader; |
| 1080 | 1096 | if (c.eof()) return error.EndOfStream; |
| 1081 | 1097 | const input = c.input; |
| 1082 | 1098 | // If at least one full encrypted record is not buffered, read once. |
| ... | ... | @@ -1108,8 +1124,13 @@ fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize |
| 1108 | 1124 | if (record_end > input.buffered().len) return 0; |
| 1109 | 1125 | } |
| 1110 | 1126 | |
| 1111 | | var cleartext_stack_buffer: [max_ciphertext_len]u8 = undefined; |
| 1112 | | const cleartext, const inner_ct: tls.ContentType = cleartext: switch (c.application_cipher) { |
| 1127 | if (r.seek == r.end) { |
| 1128 | r.seek = 0; |
| 1129 | r.end = 0; |
| 1130 | } |
| 1131 | const cleartext_buffer = r.buffer[r.end..]; |
| 1132 | |
| 1133 | const cleartext_len, const inner_ct: tls.ContentType = cleartext: switch (c.application_cipher) { |
| 1113 | 1134 | inline else => |*p| switch (c.tls_version) { |
| 1114 | 1135 | .tls_1_3 => { |
| 1115 | 1136 | const pv = &p.tls_1_3; |
| ... | ... | @@ -1121,23 +1142,24 @@ fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize |
| 1121 | 1142 | const nonce = nonce: { |
| 1122 | 1143 | const V = @Vector(P.AEAD.nonce_length, u8); |
| 1123 | 1144 | const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8); |
| 1124 | | const operand: V = pad ++ std.mem.toBytes(big(c.read_seq)); |
| 1145 | const operand: V = pad ++ mem.toBytes(big(c.read_seq)); |
| 1125 | 1146 | break :nonce @as(V, pv.server_iv) ^ operand; |
| 1126 | 1147 | }; |
| 1127 | | const cleartext = cleartext_stack_buffer[0..ciphertext.len]; |
| 1148 | const cleartext = cleartext_buffer[0..ciphertext.len]; |
| 1128 | 1149 | P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, pv.server_key) catch |
| 1129 | 1150 | return failRead(c, error.TlsBadRecordMac); |
| 1151 | // TODO use scalar, non-slice version |
| 1130 | 1152 | const msg = mem.trimRight(u8, cleartext, "\x00"); |
| 1131 | | break :cleartext .{ msg[0 .. msg.len - 1], @enumFromInt(msg[msg.len - 1]) }; |
| 1153 | break :cleartext .{ msg.len - 1, @enumFromInt(msg[msg.len - 1]) }; |
| 1132 | 1154 | }, |
| 1133 | 1155 | .tls_1_2 => { |
| 1134 | 1156 | const pv = &p.tls_1_2; |
| 1135 | 1157 | const P = @TypeOf(p.*); |
| 1136 | 1158 | const message_len: u16 = record_len - P.record_iv_length - P.mac_length; |
| 1137 | 1159 | const ad_header = input.take(tls.record_header_len) catch unreachable; // already peeked |
| 1138 | | const ad = std.mem.toBytes(big(c.read_seq)) ++ |
| 1160 | const ad = mem.toBytes(big(c.read_seq)) ++ |
| 1139 | 1161 | ad_header[0 .. 1 + 2] ++ |
| 1140 | | std.mem.toBytes(big(message_len)); |
| 1162 | mem.toBytes(big(message_len)); |
| 1141 | 1163 | const record_iv = (input.takeArray(P.record_iv_length) catch unreachable).*; // already peeked |
| 1142 | 1164 | const masked_read_seq = c.read_seq & |
| 1143 | 1165 | comptime std.math.shl(u64, std.math.maxInt(u64), 8 * P.record_iv_length); |
| ... | ... | @@ -1149,14 +1171,15 @@ fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize |
| 1149 | 1171 | }; |
| 1150 | 1172 | const ciphertext = input.take(message_len) catch unreachable; // already peeked |
| 1151 | 1173 | const auth_tag = (input.takeArray(P.mac_length) catch unreachable).*; // already peeked |
| 1152 | | const cleartext = cleartext_stack_buffer[0..ciphertext.len]; |
| 1174 | const cleartext = cleartext_buffer[0..ciphertext.len]; |
| 1153 | 1175 | P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, pv.server_write_key) catch |
| 1154 | 1176 | return failRead(c, error.TlsBadRecordMac); |
| 1155 | | break :cleartext .{ cleartext, ct }; |
| 1177 | break :cleartext .{ cleartext.len, ct }; |
| 1156 | 1178 | }, |
| 1157 | 1179 | else => unreachable, |
| 1158 | 1180 | }, |
| 1159 | 1181 | }; |
| 1182 | const cleartext = cleartext_buffer[0..cleartext_len]; |
| 1160 | 1183 | c.read_seq = std.math.add(u64, c.read_seq, 1) catch return failRead(c, error.TlsSequenceOverflow); |
| 1161 | 1184 | switch (inner_ct) { |
| 1162 | 1185 | .alert => { |
| ... | ... | @@ -1245,9 +1268,8 @@ fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize |
| 1245 | 1268 | return 0; |
| 1246 | 1269 | }, |
| 1247 | 1270 | .application_data => { |
| 1248 | | if (@intFromEnum(limit) < cleartext.len) return failRead(c, error.OutputBufferUndersize); |
| 1249 | | try w.writeAll(cleartext); |
| 1250 | | return cleartext.len; |
| 1271 | r.end += cleartext.len; |
| 1272 | return 0; |
| 1251 | 1273 | }, |
| 1252 | 1274 | else => return failRead(c, error.TlsUnexpectedMessage), |
| 1253 | 1275 | } |