authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-12-30 17:57:31-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-02 16:57:16-07:00
log0fb78b15aad74498d7f36785d5618edca7e83508
treeb337ad0fdfbe975ff59d44beda2eac02a26e40a4
parent341e68ff8fb83a146f6000b2214c1eae668e9667

std.crypto.tls: use a Decoder abstraction

This commit introduces tls.Decoder and then uses it in tls.Client. The purpose is to make it difficult to introduce vulnerabilities in the parsing code. With this abstraction in place, bugs in the TLS implementation will trip checks in the decoder, regardless of the actual length of packets sent by the other party, so that we can have confidence when using ReleaseFast builds.

2 files changed, 425 insertions(+), 338 deletions(-)

lib/std/crypto/tls.zig+129-2
...@@ -39,9 +39,9 @@ const assert = std.debug.assert;...@@ -39,9 +39,9 @@ const assert = std.debug.assert;
3939
40pub const Client = @import("tls/Client.zig");40pub const Client = @import("tls/Client.zig");
4141
42pub const ciphertext_record_header_len = 5;42pub const record_header_len = 5;
43pub const max_ciphertext_len = (1 << 14) + 256;43pub const max_ciphertext_len = (1 << 14) + 256;
44pub const max_ciphertext_record_len = max_ciphertext_len + ciphertext_record_header_len;44pub const max_ciphertext_record_len = max_ciphertext_len + record_header_len;
45pub const hello_retry_request_sequence = [32]u8{45pub const hello_retry_request_sequence = [32]u8{
46 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91,46 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91,
47 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C,47 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C,
...@@ -360,3 +360,130 @@ pub inline fn int3(x: u24) [3]u8 {...@@ -360,3 +360,130 @@ pub inline fn int3(x: u24) [3]u8 {
360 @truncate(u8, x),360 @truncate(u8, x),
361 };361 };
362}362}
363
364/// An abstraction to ensure that protocol-parsing code does not perform an
365/// out-of-bounds read.
366pub const Decoder = struct {
367 buf: []u8,
368 /// Points to the next byte in buffer that will be decoded.
369 idx: usize = 0,
370 /// Up to this point in `buf` we have already checked that `cap` is greater than it.
371 our_end: usize = 0,
372 /// Beyond this point in `buf` is extra tag-along bytes beyond the amount we
373 /// requested with `readAtLeast`.
374 their_end: usize = 0,
375 /// Points to the end within buffer that has been filled. Beyond this point
376 /// in buf is undefined bytes.
377 cap: usize = 0,
378 /// Debug helper to prevent illegal calls to read functions.
379 disable_reads: bool = false,
380
381 pub fn fromTheirSlice(buf: []u8) Decoder {
382 return .{
383 .buf = buf,
384 .their_end = buf.len,
385 .cap = buf.len,
386 .disable_reads = true,
387 };
388 }
389
390 /// Use this function to increase `their_end`.
391 pub fn readAtLeast(d: *Decoder, stream: anytype, their_amt: usize) !void {
392 assert(!d.disable_reads);
393 const existing_amt = d.cap - d.idx;
394 d.their_end = d.idx + their_amt;
395 if (their_amt <= existing_amt) return;
396 const request_amt = their_amt - existing_amt;
397 const dest = d.buf[d.cap..];
398 if (request_amt > dest.len) return error.TlsRecordOverflow;
399 const actual_amt = try stream.readAtLeast(dest, request_amt);
400 if (actual_amt < request_amt) return error.TlsConnectionTruncated;
401 d.cap += actual_amt;
402 }
403
404 /// Same as `readAtLeast` but also increases `our_end` by exactly `our_amt`.
405 /// Use when `our_amt` is calculated by us, not by them.
406 pub fn readAtLeastOurAmt(d: *Decoder, stream: anytype, our_amt: usize) !void {
407 assert(!d.disable_reads);
408 try readAtLeast(d, stream, our_amt);
409 d.our_end = d.idx + our_amt;
410 }
411
412 /// Use this function to increase `our_end`.
413 /// This should always be called with an amount provided by us, not them.
414 pub fn ensure(d: *Decoder, amt: usize) !void {
415 d.our_end = @max(d.idx + amt, d.our_end);
416 if (d.our_end > d.their_end) return error.TlsDecodeError;
417 }
418
419 /// Use this function to increase `idx`.
420 pub fn decode(d: *Decoder, comptime T: type) T {
421 switch (@typeInfo(T)) {
422 .Int => |info| switch (info.bits) {
423 8 => {
424 skip(d, 1);
425 return d.buf[d.idx - 1];
426 },
427 16 => {
428 skip(d, 2);
429 const b0: u16 = d.buf[d.idx - 2];
430 const b1: u16 = d.buf[d.idx - 1];
431 return (b0 << 8) | b1;
432 },
433 24 => {
434 skip(d, 3);
435 const b0: u24 = d.buf[d.idx - 3];
436 const b1: u24 = d.buf[d.idx - 2];
437 const b2: u24 = d.buf[d.idx - 1];
438 return (b0 << 16) | (b1 << 8) | b2;
439 },
440 else => @compileError("unsupported int type: " ++ @typeName(T)),
441 },
442 .Enum => |info| {
443 const int = d.decode(info.tag_type);
444 if (info.is_exhaustive) @compileError("exhaustive enum cannot be used");
445 return @intToEnum(T, int);
446 },
447 else => @compileError("unsupported type: " ++ @typeName(T)),
448 }
449 }
450
451 /// Use this function to increase `idx`.
452 pub fn array(d: *Decoder, comptime len: usize) *[len]u8 {
453 skip(d, len);
454 return d.buf[d.idx - len ..][0..len];
455 }
456
457 /// Use this function to increase `idx`.
458 pub fn slice(d: *Decoder, len: usize) []u8 {
459 skip(d, len);
460 return d.buf[d.idx - len ..][0..len];
461 }
462
463 /// Use this function to increase `idx`.
464 pub fn skip(d: *Decoder, amt: usize) void {
465 d.idx += amt;
466 assert(d.idx <= d.our_end); // insufficient ensured bytes
467 }
468
469 pub fn eof(d: Decoder) bool {
470 assert(d.our_end <= d.their_end);
471 assert(d.idx <= d.our_end);
472 return d.idx == d.their_end;
473 }
474
475 /// Provide the length they claim, and receive a sub-decoder specific to that slice.
476 /// The parent decoder is advanced to the end.
477 pub fn sub(d: *Decoder, their_len: usize) !Decoder {
478 const end = d.idx + their_len;
479 if (end > d.their_end) return error.TlsDecodeError;
480 const sub_buf = d.buf[d.idx..end];
481 d.idx = end;
482 d.our_end = end;
483 return fromTheirSlice(sub_buf);
484 }
485
486 pub fn rest(d: Decoder) []u8 {
487 return d.buf[d.idx..d.cap];
488 }
489};
lib/std/crypto/tls/Client.zig+296-336
...@@ -126,88 +126,73 @@ pub fn init(stream: net.Stream, ca_bundle: Certificate.Bundle, host: []const u8)...@@ -126,88 +126,73 @@ pub fn init(stream: net.Stream, ca_bundle: Certificate.Bundle, host: []const u8)
126 const client_hello_bytes1 = plaintext_header[5..];126 const client_hello_bytes1 = plaintext_header[5..];
127127
128 var handshake_cipher: tls.HandshakeCipher = undefined;128 var handshake_cipher: tls.HandshakeCipher = undefined;
129129 var handshake_buffer: [8000]u8 = undefined;
130 var handshake_buf: [8000]u8 = undefined;130 var d: tls.Decoder = .{ .buf = &handshake_buffer };
131 var len: usize = 0;131 {
132 var i: usize = i: {132 try d.readAtLeastOurAmt(stream, tls.record_header_len);
133 const plaintext = handshake_buf[0..5];133 const ct = d.decode(tls.ContentType);
134 len = try stream.readAtLeast(&handshake_buf, plaintext.len);134 d.skip(2); // legacy_record_version
135 if (len < plaintext.len) return error.EndOfStream;135 const record_len = d.decode(u16);
136 const ct = @intToEnum(tls.ContentType, plaintext[0]);136 try d.readAtLeast(stream, record_len);
137 const frag_len = mem.readIntBig(u16, plaintext[3..][0..2]);137 const server_hello_fragment = d.buf[d.idx..][0..record_len];
138 const end = plaintext.len + frag_len;138 var ptd = try d.sub(record_len);
139 if (end > handshake_buf.len) return error.TlsRecordOverflow;
140 if (end > len) {
141 len += try stream.readAtLeast(handshake_buf[len..], end - len);
142 if (end > len) return error.EndOfStream;
143 }
144 const frag = handshake_buf[plaintext.len..end];
145
146 switch (ct) {139 switch (ct) {
147 .alert => {140 .alert => {
148 const level = @intToEnum(tls.AlertLevel, frag[0]);141 try ptd.ensure(2);
149 const desc = @intToEnum(tls.AlertDescription, frag[1]);142 const level = ptd.decode(tls.AlertLevel);
143 const desc = ptd.decode(tls.AlertDescription);
150 _ = level;144 _ = level;
151 _ = desc;145 _ = desc;
152 return error.TlsAlert;146 return error.TlsAlert;
153 },147 },
154 .handshake => {148 .handshake => {
155 if (frag[0] != @enumToInt(tls.HandshakeType.server_hello)) {149 try ptd.ensure(4);
150 const handshake_type = ptd.decode(tls.HandshakeType);
151 if (handshake_type != .server_hello) return error.TlsUnexpectedMessage;
152 const length = ptd.decode(u24);
153 var hsd = try ptd.sub(length);
154 try hsd.ensure(2 + 32 + 1 + 32 + 2 + 1 + 2);
155 const legacy_version = hsd.decode(u16);
156 const random = hsd.array(32);
157 if (mem.eql(u8, random, &tls.hello_retry_request_sequence)) {
158 // This is a HelloRetryRequest message. This client implementation
159 // does not expect to get one.
156 return error.TlsUnexpectedMessage;160 return error.TlsUnexpectedMessage;
157 }161 }
158 const length = mem.readIntBig(u24, frag[1..4]);162 const legacy_session_id_echo_len = hsd.decode(u8);
159 if (4 + length != frag.len) return error.TlsBadLength;
160 var i: usize = 4;
161 const legacy_version = mem.readIntBig(u16, frag[i..][0..2]);
162 i += 2;
163 const random = frag[i..][0..32].*;
164 i += 32;
165 if (mem.eql(u8, &random, &tls.hello_retry_request_sequence)) {
166 @panic("TODO handle HelloRetryRequest");
167 }
168 const legacy_session_id_echo_len = frag[i];
169 i += 1;
170 if (legacy_session_id_echo_len != 32) return error.TlsIllegalParameter;163 if (legacy_session_id_echo_len != 32) return error.TlsIllegalParameter;
171 const legacy_session_id_echo = frag[i..][0..32];164 const legacy_session_id_echo = hsd.array(32);
172 if (!mem.eql(u8, legacy_session_id_echo, &legacy_session_id))165 if (!mem.eql(u8, legacy_session_id_echo, &legacy_session_id))
173 return error.TlsIllegalParameter;166 return error.TlsIllegalParameter;
174 i += 32;167 const cipher_suite_tag = hsd.decode(tls.CipherSuite);
175 const cipher_suite_int = mem.readIntBig(u16, frag[i..][0..2]);168 hsd.skip(1); // legacy_compression_method
176 i += 2;169 const extensions_size = hsd.decode(u16);
177 const cipher_suite_tag = @intToEnum(tls.CipherSuite, cipher_suite_int);170 var all_extd = try hsd.sub(extensions_size);
178 const legacy_compression_method = frag[i];
179 i += 1;
180 _ = legacy_compression_method;
181 const extensions_size = mem.readIntBig(u16, frag[i..][0..2]);
182 i += 2;
183 if (i + extensions_size != frag.len) return error.TlsBadLength;
184 var supported_version: u16 = 0;171 var supported_version: u16 = 0;
185 var shared_key: [32]u8 = undefined;172 var shared_key: [32]u8 = undefined;
186 var have_shared_key = false;173 var have_shared_key = false;
187 while (i < frag.len) {174 while (!all_extd.eof()) {
188 const et = @intToEnum(tls.ExtensionType, mem.readIntBig(u16, frag[i..][0..2]));175 try all_extd.ensure(2 + 2);
189 i += 2;176 const et = all_extd.decode(tls.ExtensionType);
190 const ext_size = mem.readIntBig(u16, frag[i..][0..2]);177 const ext_size = all_extd.decode(u16);
191 i += 2;178 var extd = try all_extd.sub(ext_size);
192 const next_i = i + ext_size;
193 if (next_i > frag.len) return error.TlsBadLength;
194 switch (et) {179 switch (et) {
195 .supported_versions => {180 .supported_versions => {
196 if (supported_version != 0) return error.TlsIllegalParameter;181 if (supported_version != 0) return error.TlsIllegalParameter;
197 supported_version = mem.readIntBig(u16, frag[i..][0..2]);182 try extd.ensure(2);
183 supported_version = extd.decode(u16);
198 },184 },
199 .key_share => {185 .key_share => {
200 if (have_shared_key) return error.TlsIllegalParameter;186 if (have_shared_key) return error.TlsIllegalParameter;
201 have_shared_key = true;187 have_shared_key = true;
202 const named_group = @intToEnum(tls.NamedGroup, mem.readIntBig(u16, frag[i..][0..2]));188 try extd.ensure(4);
203 i += 2;189 const named_group = extd.decode(tls.NamedGroup);
204 const key_size = mem.readIntBig(u16, frag[i..][0..2]);190 const key_size = extd.decode(u16);
205 i += 2;191 try extd.ensure(key_size);
206
207 switch (named_group) {192 switch (named_group) {
208 .x25519 => {193 .x25519 => {
209 if (key_size != 32) return error.TlsBadLength;194 if (key_size != 32) return error.TlsIllegalParameter;
210 const server_pub_key = frag[i..][0..32];195 const server_pub_key = extd.array(32);
211196
212 shared_key = crypto.dh.X25519.scalarmult(197 shared_key = crypto.dh.X25519.scalarmult(
213 x25519_kp.secret_key,198 x25519_kp.secret_key,
...@@ -215,7 +200,7 @@ pub fn init(stream: net.Stream, ca_bundle: Certificate.Bundle, host: []const u8)...@@ -215,7 +200,7 @@ pub fn init(stream: net.Stream, ca_bundle: Certificate.Bundle, host: []const u8)
215 ) catch return error.TlsDecryptFailure;200 ) catch return error.TlsDecryptFailure;
216 },201 },
217 .secp256r1 => {202 .secp256r1 => {
218 const server_pub_key = frag[i..][0..key_size];203 const server_pub_key = extd.slice(key_size);
219204
220 const PublicKey = crypto.sign.ecdsa.EcdsaP256Sha256.PublicKey;205 const PublicKey = crypto.sign.ecdsa.EcdsaP256Sha256.PublicKey;
221 const pk = PublicKey.fromSec1(server_pub_key) catch {206 const pk = PublicKey.fromSec1(server_pub_key) catch {
...@@ -233,14 +218,12 @@ pub fn init(stream: net.Stream, ca_bundle: Certificate.Bundle, host: []const u8)...@@ -233,14 +218,12 @@ pub fn init(stream: net.Stream, ca_bundle: Certificate.Bundle, host: []const u8)
233 },218 },
234 else => {},219 else => {},
235 }220 }
236 i = next_i;
237 }221 }
238 if (!have_shared_key) return error.TlsIllegalParameter;222 if (!have_shared_key) return error.TlsIllegalParameter;
223
239 const tls_version = if (supported_version == 0) legacy_version else supported_version;224 const tls_version = if (supported_version == 0) legacy_version else supported_version;
240 switch (tls_version) {225 if (tls_version != @enumToInt(tls.ProtocolVersion.tls_1_3))
241 @enumToInt(tls.ProtocolVersion.tls_1_3) => {},226 return error.TlsIllegalParameter;
242 else => return error.TlsIllegalParameter,
243 }
244227
245 switch (cipher_suite_tag) {228 switch (cipher_suite_tag) {
246 inline .AES_128_GCM_SHA256,229 inline .AES_128_GCM_SHA256,
...@@ -264,7 +247,7 @@ pub fn init(stream: net.Stream, ca_bundle: Certificate.Bundle, host: []const u8)...@@ -264,7 +247,7 @@ pub fn init(stream: net.Stream, ca_bundle: Certificate.Bundle, host: []const u8)
264 const p = &@field(handshake_cipher, @tagName(tag));247 const p = &@field(handshake_cipher, @tagName(tag));
265 p.transcript_hash.update(client_hello_bytes1); // Client Hello part 1248 p.transcript_hash.update(client_hello_bytes1); // Client Hello part 1
266 p.transcript_hash.update(host); // Client Hello part 2249 p.transcript_hash.update(host); // Client Hello part 2
267 p.transcript_hash.update(frag); // Server Hello250 p.transcript_hash.update(server_hello_fragment);
268 const hello_hash = p.transcript_hash.peek();251 const hello_hash = p.transcript_hash.peek();
269 const zeroes = [1]u8{0} ** P.Hash.digest_length;252 const zeroes = [1]u8{0} ** P.Hash.digest_length;
270 const early_secret = P.Hkdf.extract(&[1]u8{0}, &zeroes);253 const early_secret = P.Hkdf.extract(&[1]u8{0}, &zeroes);
...@@ -289,8 +272,7 @@ pub fn init(stream: net.Stream, ca_bundle: Certificate.Bundle, host: []const u8)...@@ -289,8 +272,7 @@ pub fn init(stream: net.Stream, ca_bundle: Certificate.Bundle, host: []const u8)
289 },272 },
290 else => return error.TlsUnexpectedMessage,273 else => return error.TlsUnexpectedMessage,
291 }274 }
292 break :i end;275 }
293 };
294276
295 // This is used for two purposes:277 // This is used for two purposes:
296 // * Detect whether a certificate is the first one presented, in which case278 // * Detect whether a certificate is the first one presented, in which case
...@@ -322,29 +304,17 @@ pub fn init(stream: net.Stream, ca_bundle: Certificate.Bundle, host: []const u8)...@@ -322,29 +304,17 @@ pub fn init(stream: net.Stream, ca_bundle: Certificate.Bundle, host: []const u8)
322 var main_cert_pub_key_len: u16 = undefined;304 var main_cert_pub_key_len: u16 = undefined;
323305
324 while (true) {306 while (true) {
325 const end_hdr = i + 5;307 try d.readAtLeastOurAmt(stream, tls.record_header_len);
326 if (end_hdr > handshake_buf.len) return error.TlsRecordOverflow;308 const record_header = d.buf[d.idx..][0..5];
327 if (end_hdr > len) {309 const ct = d.decode(tls.ContentType);
328 len += try stream.readAtLeast(handshake_buf[len..], end_hdr - len);310 d.skip(2); // legacy_version
329 if (end_hdr > len) return error.EndOfStream;311 const record_len = d.decode(u16);
330 }312 try d.readAtLeast(stream, record_len);
331 const ct = @intToEnum(tls.ContentType, handshake_buf[i]);313 var record_decoder = try d.sub(record_len);
332 i += 1;
333 const legacy_version = mem.readIntBig(u16, handshake_buf[i..][0..2]);
334 i += 2;
335 _ = legacy_version;
336 const record_size = mem.readIntBig(u16, handshake_buf[i..][0..2]);
337 i += 2;
338 const end = i + record_size;
339 if (end > handshake_buf.len) return error.TlsRecordOverflow;
340 if (end > len) {
341 len += try stream.readAtLeast(handshake_buf[len..], end - len);
342 if (end > len) return error.EndOfStream;
343 }
344 switch (ct) {314 switch (ct) {
345 .change_cipher_spec => {315 .change_cipher_spec => {
346 if (record_size != 1) return error.TlsUnexpectedMessage;316 try record_decoder.ensure(1);
347 if (handshake_buf[i] != 0x01) return error.TlsUnexpectedMessage;317 if (record_decoder.decode(u8) != 0x01) return error.TlsIllegalParameter;
348 },318 },
349 .application_data => {319 .application_data => {
350 const cleartext_buf = &cleartext_bufs[cert_index % 2];320 const cleartext_buf = &cleartext_bufs[cert_index % 2];
...@@ -352,276 +322,261 @@ pub fn init(stream: net.Stream, ca_bundle: Certificate.Bundle, host: []const u8)...@@ -352,276 +322,261 @@ pub fn init(stream: net.Stream, ca_bundle: Certificate.Bundle, host: []const u8)
352 const cleartext = switch (handshake_cipher) {322 const cleartext = switch (handshake_cipher) {
353 inline else => |*p| c: {323 inline else => |*p| c: {
354 const P = @TypeOf(p.*);324 const P = @TypeOf(p.*);
355 const ciphertext_len = record_size - P.AEAD.tag_length;325 const ciphertext_len = record_len - P.AEAD.tag_length;
356 const ciphertext = handshake_buf[i..][0..ciphertext_len];326 try record_decoder.ensure(ciphertext_len + P.AEAD.tag_length);
357 i += ciphertext.len;327 const ciphertext = record_decoder.slice(ciphertext_len);
358 if (ciphertext.len > cleartext_buf.len) return error.TlsRecordOverflow;328 if (ciphertext.len > cleartext_buf.len) return error.TlsRecordOverflow;
359 const cleartext = cleartext_buf[0..ciphertext.len];329 const cleartext = cleartext_buf[0..ciphertext.len];
360 const auth_tag = handshake_buf[i..][0..P.AEAD.tag_length].*;330 const auth_tag = record_decoder.array(P.AEAD.tag_length).*;
361 const V = @Vector(P.AEAD.nonce_length, u8);331 const V = @Vector(P.AEAD.nonce_length, u8);
362 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);332 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
363 const operand: V = pad ++ @bitCast([8]u8, big(read_seq));333 const operand: V = pad ++ @bitCast([8]u8, big(read_seq));
364 read_seq += 1;334 read_seq += 1;
365 const nonce = @as(V, p.server_handshake_iv) ^ operand;335 const nonce = @as(V, p.server_handshake_iv) ^ operand;
366 const ad = handshake_buf[end_hdr - 5 ..][0..5];336 P.AEAD.decrypt(cleartext, ciphertext, auth_tag, record_header, nonce, p.server_handshake_key) catch
367 P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, p.server_handshake_key) catch
368 return error.TlsBadRecordMac;337 return error.TlsBadRecordMac;
369 break :c cleartext;338 break :c cleartext;
370 },339 },
371 };340 };
372341
373 const inner_ct = @intToEnum(tls.ContentType, cleartext[cleartext.len - 1]);342 const inner_ct = @intToEnum(tls.ContentType, cleartext[cleartext.len - 1]);
374 switch (inner_ct) {343 if (inner_ct != .handshake) return error.TlsUnexpectedMessage;
375 .handshake => {344
376 var ct_i: usize = 0;345 var ctd = tls.Decoder.fromTheirSlice(cleartext[0 .. cleartext.len - 1]);
377 while (true) {346 while (true) {
378 const handshake_type = @intToEnum(tls.HandshakeType, cleartext[ct_i]);347 try ctd.ensure(4);
379 ct_i += 1;348 const handshake_type = ctd.decode(tls.HandshakeType);
380 const handshake_len = mem.readIntBig(u24, cleartext[ct_i..][0..3]);349 const handshake_len = ctd.decode(u24);
381 ct_i += 3;350 var hsd = try ctd.sub(handshake_len);
382 const next_handshake_i = ct_i + handshake_len;351 const wrapped_handshake = ctd.buf[ctd.idx - handshake_len - 4 .. ctd.idx];
383 if (next_handshake_i > cleartext.len - 1)352 const handshake = ctd.buf[ctd.idx - handshake_len .. ctd.idx];
384 return error.TlsBadLength;353 switch (handshake_type) {
385 const wrapped_handshake = cleartext[ct_i - 4 .. next_handshake_i];354 .encrypted_extensions => {
386 const handshake = cleartext[ct_i..next_handshake_i];355 if (handshake_state != .encrypted_extensions) return error.TlsUnexpectedMessage;
387 switch (handshake_type) {356 handshake_state = .certificate;
388 .encrypted_extensions => {357 switch (handshake_cipher) {
389 if (handshake_state != .encrypted_extensions) return error.TlsUnexpectedMessage;358 inline else => |*p| p.transcript_hash.update(wrapped_handshake),
390 handshake_state = .certificate;359 }
391 switch (handshake_cipher) {360 try hsd.ensure(2);
392 inline else => |*p| p.transcript_hash.update(wrapped_handshake),361 const total_ext_size = hsd.decode(u16);
393 }362 var all_extd = try hsd.sub(total_ext_size);
394 const total_ext_size = mem.readIntBig(u16, handshake[0..2]);363 while (!all_extd.eof()) {
395 var hs_i: usize = 2;364 try all_extd.ensure(4);
396 const end_ext_i = 2 + total_ext_size;365 const et = all_extd.decode(tls.ExtensionType);
397 while (hs_i < end_ext_i) {366 const ext_size = all_extd.decode(u16);
398 const et = @intToEnum(tls.ExtensionType, mem.readIntBig(u16, handshake[hs_i..][0..2]));367 var extd = try all_extd.sub(ext_size);
399 hs_i += 2;368 _ = extd;
400 const ext_size = mem.readIntBig(u16, handshake[hs_i..][0..2]);369 switch (et) {
401 hs_i += 2;370 .server_name => {},
402 const next_ext_i = hs_i + ext_size;371 else => {},
403 switch (et) {372 }
404 .server_name => {},373 }
405 else => {},374 },
406 }375 .certificate => cert: {
407 hs_i = next_ext_i;376 switch (handshake_cipher) {
408 }377 inline else => |*p| p.transcript_hash.update(wrapped_handshake),
409 },378 }
410 .certificate => cert: {379 switch (handshake_state) {
411 switch (handshake_cipher) {380 .certificate => {},
412 inline else => |*p| p.transcript_hash.update(wrapped_handshake),381 .trust_chain_established => break :cert,
413 }382 else => return error.TlsUnexpectedMessage,
414 switch (handshake_state) {383 }
415 .certificate => {},384 try hsd.ensure(1 + 4);
416 .trust_chain_established => break :cert,385 const cert_req_ctx_len = hsd.decode(u8);
417 else => return error.TlsUnexpectedMessage,386 if (cert_req_ctx_len != 0) return error.TlsIllegalParameter;
387 const certs_size = hsd.decode(u24);
388 var certs_decoder = try hsd.sub(certs_size);
389 while (!certs_decoder.eof()) {
390 try certs_decoder.ensure(3);
391 const cert_size = certs_decoder.decode(u24);
392 var certd = try certs_decoder.sub(cert_size);
393
394 const subject_cert: Certificate = .{
395 .buffer = certd.buf,
396 .index = @intCast(u32, certd.idx),
397 };
398 const subject = try subject_cert.parse();
399 if (cert_index == 0) {
400 // Verify the host on the first certificate.
401 if (!hostMatchesCommonName(host, subject.commonName())) {
402 return error.TlsCertificateHostMismatch;
418 }403 }
419 var hs_i: u32 = 0;
420 const cert_req_ctx_len = handshake[hs_i];
421 hs_i += 1;
422 if (cert_req_ctx_len != 0) return error.TlsIllegalParameter;
423 const certs_size = mem.readIntBig(u24, handshake[hs_i..][0..3]);
424 hs_i += 3;
425 const end_certs = hs_i + certs_size;
426 while (hs_i < end_certs) {
427 const cert_size = mem.readIntBig(u24, handshake[hs_i..][0..3]);
428 hs_i += 3;
429 const end_cert = hs_i + cert_size;
430
431 const subject_cert: Certificate = .{
432 .buffer = handshake,
433 .index = hs_i,
434 };
435 const subject = try subject_cert.parse();
436 if (cert_index == 0) {
437 // Verify the host on the first certificate.
438 if (!hostMatchesCommonName(host, subject.commonName())) {
439 return error.TlsCertificateHostMismatch;
440 }
441404
442 // Keep track of the public key for405 // Keep track of the public key for the
443 // the certificate_verify message406 // certificate_verify message later.
444 // later.407 main_cert_pub_key_algo = subject.pub_key_algo;
445 main_cert_pub_key_algo = subject.pub_key_algo;408 const pub_key = subject.pubKey();
446 const pub_key = subject.pubKey();409 if (pub_key.len > main_cert_pub_key_buf.len)
447 if (pub_key.len > main_cert_pub_key_buf.len)410 return error.CertificatePublicKeyInvalid;
448 return error.CertificatePublicKeyInvalid;411 @memcpy(&main_cert_pub_key_buf, pub_key.ptr, pub_key.len);
449 @memcpy(&main_cert_pub_key_buf, pub_key.ptr, pub_key.len);412 main_cert_pub_key_len = @intCast(@TypeOf(main_cert_pub_key_len), pub_key.len);
450 main_cert_pub_key_len = @intCast(@TypeOf(main_cert_pub_key_len), pub_key.len);413 } else {
451 } else {414 try prev_cert.verify(subject);
452 try prev_cert.verify(subject);415 }
453 }
454
455 if (ca_bundle.verify(subject)) |_| {
456 handshake_state = .trust_chain_established;
457 break :cert;
458 } else |err| switch (err) {
459 error.CertificateIssuerNotFound => {},
460 else => |e| return e,
461 }
462
463 prev_cert = subject;
464 cert_index += 1;
465
466 hs_i = end_cert;
467 const total_ext_size = mem.readIntBig(u16, handshake[hs_i..][0..2]);
468 hs_i += 2;
469 hs_i += total_ext_size;
470 }
471 },
472 .certificate_verify => {
473 switch (handshake_state) {
474 .trust_chain_established => handshake_state = .finished,
475 .certificate => return error.TlsCertificateNotVerified,
476 else => return error.TlsUnexpectedMessage,
477 }
478416
479 const scheme = @intToEnum(tls.SignatureScheme, mem.readIntBig(u16, handshake[0..2]));417 if (ca_bundle.verify(subject)) |_| {
480 const sig_len = mem.readIntBig(u16, handshake[2..4]);418 handshake_state = .trust_chain_established;
481 if (4 + sig_len > handshake.len) return error.TlsBadLength;419 break :cert;
482 const encoded_sig = handshake[4..][0..sig_len];420 } else |err| switch (err) {
483 const max_digest_len = 64;421 error.CertificateIssuerNotFound => {},
484 var verify_buffer =422 else => |e| return e,
485 ([1]u8{0x20} ** 64) ++423 }
486 "TLS 1.3, server CertificateVerify\x00".* ++424
487 @as([max_digest_len]u8, undefined);425 prev_cert = subject;
488426 cert_index += 1;
489 const verify_bytes = switch (handshake_cipher) {427
490 inline else => |*p| v: {428 try certs_decoder.ensure(2);
491 const transcript_digest = p.transcript_hash.peek();429 const total_ext_size = certs_decoder.decode(u16);
492 verify_buffer[verify_buffer.len - max_digest_len ..][0..transcript_digest.len].* = transcript_digest;430 var all_extd = try certs_decoder.sub(total_ext_size);
493 p.transcript_hash.update(wrapped_handshake);431 _ = all_extd;
494 break :v verify_buffer[0 .. verify_buffer.len - max_digest_len + transcript_digest.len];432 }
495 },433 },
496 };434 .certificate_verify => {
497 const main_cert_pub_key = main_cert_pub_key_buf[0..main_cert_pub_key_len];435 switch (handshake_state) {
498436 .trust_chain_established => handshake_state = .finished,
499 switch (scheme) {437 .certificate => return error.TlsCertificateNotVerified,
500 inline .ecdsa_secp256r1_sha256,438 else => return error.TlsUnexpectedMessage,
501 .ecdsa_secp384r1_sha384,439 }
502 => |comptime_scheme| {440
503 if (main_cert_pub_key_algo != .X9_62_id_ecPublicKey)441 try hsd.ensure(4);
504 return error.TlsBadSignatureScheme;442 const scheme = hsd.decode(tls.SignatureScheme);
505 const Ecdsa = SchemeEcdsa(comptime_scheme);443 const sig_len = hsd.decode(u16);
506 const sig = try Ecdsa.Signature.fromDer(encoded_sig);444 try hsd.ensure(sig_len);
507 const key = try Ecdsa.PublicKey.fromSec1(main_cert_pub_key);445 const encoded_sig = hsd.slice(sig_len);
508 try sig.verify(verify_bytes, key);446 const max_digest_len = 64;
509 },447 var verify_buffer =
510 .rsa_pss_rsae_sha256 => {448 ([1]u8{0x20} ** 64) ++
511 if (main_cert_pub_key_algo != .rsaEncryption)449 "TLS 1.3, server CertificateVerify\x00".* ++
512 return error.TlsBadSignatureScheme;450 @as([max_digest_len]u8, undefined);
513451
514 const Hash = crypto.hash.sha2.Sha256;452 const verify_bytes = switch (handshake_cipher) {
515 const rsa = Certificate.rsa;453 inline else => |*p| v: {
516 const components = try rsa.PublicKey.parseDer(main_cert_pub_key);454 const transcript_digest = p.transcript_hash.peek();
517 const exponent = components.exponent;455 verify_buffer[verify_buffer.len - max_digest_len ..][0..transcript_digest.len].* = transcript_digest;
518 const modulus = components.modulus;456 p.transcript_hash.update(wrapped_handshake);
519 var rsa_mem_buf: [512 * 32]u8 = undefined;457 break :v verify_buffer[0 .. verify_buffer.len - max_digest_len + transcript_digest.len];
520 var fba = std.heap.FixedBufferAllocator.init(&rsa_mem_buf);458 },
521 const ally = fba.allocator();459 };
522 switch (modulus.len) {460 const main_cert_pub_key = main_cert_pub_key_buf[0..main_cert_pub_key_len];
523 inline 128, 256, 512 => |modulus_len| {461
524 const key = try rsa.PublicKey.fromBytes(exponent, modulus, ally);462 switch (scheme) {
525 const sig = rsa.PSSSignature.fromBytes(modulus_len, encoded_sig);463 inline .ecdsa_secp256r1_sha256,
526 try rsa.PSSSignature.verify(modulus_len, sig, verify_bytes, key, Hash, ally);464 .ecdsa_secp384r1_sha384,
527 },465 => |comptime_scheme| {
528 else => {466 if (main_cert_pub_key_algo != .X9_62_id_ecPublicKey)
529 return error.TlsBadRsaSignatureBitCount;467 return error.TlsBadSignatureScheme;
530 },468 const Ecdsa = SchemeEcdsa(comptime_scheme);
531 }469 const sig = try Ecdsa.Signature.fromDer(encoded_sig);
470 const key = try Ecdsa.PublicKey.fromSec1(main_cert_pub_key);
471 try sig.verify(verify_bytes, key);
472 },
473 .rsa_pss_rsae_sha256 => {
474 if (main_cert_pub_key_algo != .rsaEncryption)
475 return error.TlsBadSignatureScheme;
476
477 const Hash = crypto.hash.sha2.Sha256;
478 const rsa = Certificate.rsa;
479 const components = try rsa.PublicKey.parseDer(main_cert_pub_key);
480 const exponent = components.exponent;
481 const modulus = components.modulus;
482 var rsa_mem_buf: [512 * 32]u8 = undefined;
483 var fba = std.heap.FixedBufferAllocator.init(&rsa_mem_buf);
484 const ally = fba.allocator();
485 switch (modulus.len) {
486 inline 128, 256, 512 => |modulus_len| {
487 const key = try rsa.PublicKey.fromBytes(exponent, modulus, ally);
488 const sig = rsa.PSSSignature.fromBytes(modulus_len, encoded_sig);
489 try rsa.PSSSignature.verify(modulus_len, sig, verify_bytes, key, Hash, ally);
532 },490 },
533 else => {491 else => {
534 return error.TlsBadSignatureScheme;492 return error.TlsBadRsaSignatureBitCount;
535 },493 },
536 }494 }
537 },495 },
538 .finished => {
539 if (handshake_state != .finished) return error.TlsUnexpectedMessage;
540 // This message is to trick buggy proxies into behaving correctly.
541 const client_change_cipher_spec_msg = [_]u8{
542 @enumToInt(tls.ContentType.change_cipher_spec),
543 0x03, 0x03, // legacy protocol version
544 0x00, 0x01, // length
545 0x01,
546 };
547 const app_cipher = switch (handshake_cipher) {
548 inline else => |*p, tag| c: {
549 const P = @TypeOf(p.*);
550 const finished_digest = p.transcript_hash.peek();
551 p.transcript_hash.update(wrapped_handshake);
552 const expected_server_verify_data = tls.hmac(P.Hmac, &finished_digest, p.server_finished_key);
553 if (!mem.eql(u8, &expected_server_verify_data, handshake))
554 return error.TlsDecryptError;
555 const handshake_hash = p.transcript_hash.finalResult();
556 const verify_data = tls.hmac(P.Hmac, &handshake_hash, p.client_finished_key);
557 const out_cleartext = [_]u8{
558 @enumToInt(tls.HandshakeType.finished),
559 0, 0, verify_data.len, // length
560 } ++ verify_data ++ [1]u8{@enumToInt(tls.ContentType.handshake)};
561
562 const wrapped_len = out_cleartext.len + P.AEAD.tag_length;
563
564 var finished_msg = [_]u8{
565 @enumToInt(tls.ContentType.application_data),
566 0x03, 0x03, // legacy protocol version
567 0, wrapped_len, // byte length of encrypted record
568 } ++ @as([wrapped_len]u8, undefined);
569
570 const ad = finished_msg[0..5];
571 const ciphertext = finished_msg[5..][0..out_cleartext.len];
572 const auth_tag = finished_msg[finished_msg.len - P.AEAD.tag_length ..];
573 const nonce = p.client_handshake_iv;
574 P.AEAD.encrypt(ciphertext, auth_tag, &out_cleartext, ad, nonce, p.client_handshake_key);
575
576 const both_msgs = client_change_cipher_spec_msg ++ finished_msg;
577 try stream.writeAll(&both_msgs);
578
579 const client_secret = hkdfExpandLabel(P.Hkdf, p.master_secret, "c ap traffic", &handshake_hash, P.Hash.digest_length);
580 const server_secret = hkdfExpandLabel(P.Hkdf, p.master_secret, "s ap traffic", &handshake_hash, P.Hash.digest_length);
581 break :c @unionInit(tls.ApplicationCipher, @tagName(tag), .{
582 .client_secret = client_secret,
583 .server_secret = server_secret,
584 .client_key = hkdfExpandLabel(P.Hkdf, client_secret, "key", "", P.AEAD.key_length),
585 .server_key = hkdfExpandLabel(P.Hkdf, server_secret, "key", "", P.AEAD.key_length),
586 .client_iv = hkdfExpandLabel(P.Hkdf, client_secret, "iv", "", P.AEAD.nonce_length),
587 .server_iv = hkdfExpandLabel(P.Hkdf, server_secret, "iv", "", P.AEAD.nonce_length),
588 });
589 },
590 };
591 var client: Client = .{
592 .read_seq = 0,
593 .write_seq = 0,
594 .partial_cleartext_idx = 0,
595 .partial_ciphertext_idx = 0,
596 .partial_ciphertext_end = @intCast(u15, len - end),
597 .received_close_notify = false,
598 .application_cipher = app_cipher,
599 .partially_read_buffer = undefined,
600 };
601 mem.copy(u8, &client.partially_read_buffer, handshake_buf[len..end]);
602 return client;
603 },
604 else => {496 else => {
605 return error.TlsUnexpectedMessage;497 return error.TlsBadSignatureScheme;
606 },498 },
607 }499 }
608 ct_i = next_handshake_i;500 },
609 if (ct_i >= cleartext.len - 1) break;501 .finished => {
610 }502 if (handshake_state != .finished) return error.TlsUnexpectedMessage;
611 },503 // This message is to trick buggy proxies into behaving correctly.
612 else => {504 const client_change_cipher_spec_msg = [_]u8{
613 return error.TlsUnexpectedMessage;505 @enumToInt(tls.ContentType.change_cipher_spec),
614 },506 0x03, 0x03, // legacy protocol version
507 0x00, 0x01, // length
508 0x01,
509 };
510 const app_cipher = switch (handshake_cipher) {
511 inline else => |*p, tag| c: {
512 const P = @TypeOf(p.*);
513 const finished_digest = p.transcript_hash.peek();
514 p.transcript_hash.update(wrapped_handshake);
515 const expected_server_verify_data = tls.hmac(P.Hmac, &finished_digest, p.server_finished_key);
516 if (!mem.eql(u8, &expected_server_verify_data, handshake))
517 return error.TlsDecryptError;
518 const handshake_hash = p.transcript_hash.finalResult();
519 const verify_data = tls.hmac(P.Hmac, &handshake_hash, p.client_finished_key);
520 const out_cleartext = [_]u8{
521 @enumToInt(tls.HandshakeType.finished),
522 0, 0, verify_data.len, // length
523 } ++ verify_data ++ [1]u8{@enumToInt(tls.ContentType.handshake)};
524
525 const wrapped_len = out_cleartext.len + P.AEAD.tag_length;
526
527 var finished_msg = [_]u8{
528 @enumToInt(tls.ContentType.application_data),
529 0x03, 0x03, // legacy protocol version
530 0, wrapped_len, // byte length of encrypted record
531 } ++ @as([wrapped_len]u8, undefined);
532
533 const ad = finished_msg[0..5];
534 const ciphertext = finished_msg[5..][0..out_cleartext.len];
535 const auth_tag = finished_msg[finished_msg.len - P.AEAD.tag_length ..];
536 const nonce = p.client_handshake_iv;
537 P.AEAD.encrypt(ciphertext, auth_tag, &out_cleartext, ad, nonce, p.client_handshake_key);
538
539 const both_msgs = client_change_cipher_spec_msg ++ finished_msg;
540 try stream.writeAll(&both_msgs);
541
542 const client_secret = hkdfExpandLabel(P.Hkdf, p.master_secret, "c ap traffic", &handshake_hash, P.Hash.digest_length);
543 const server_secret = hkdfExpandLabel(P.Hkdf, p.master_secret, "s ap traffic", &handshake_hash, P.Hash.digest_length);
544 break :c @unionInit(tls.ApplicationCipher, @tagName(tag), .{
545 .client_secret = client_secret,
546 .server_secret = server_secret,
547 .client_key = hkdfExpandLabel(P.Hkdf, client_secret, "key", "", P.AEAD.key_length),
548 .server_key = hkdfExpandLabel(P.Hkdf, server_secret, "key", "", P.AEAD.key_length),
549 .client_iv = hkdfExpandLabel(P.Hkdf, client_secret, "iv", "", P.AEAD.nonce_length),
550 .server_iv = hkdfExpandLabel(P.Hkdf, server_secret, "iv", "", P.AEAD.nonce_length),
551 });
552 },
553 };
554 const leftover = d.rest();
555 var client: Client = .{
556 .read_seq = 0,
557 .write_seq = 0,
558 .partial_cleartext_idx = 0,
559 .partial_ciphertext_idx = 0,
560 .partial_ciphertext_end = @intCast(u15, leftover.len),
561 .received_close_notify = false,
562 .application_cipher = app_cipher,
563 .partially_read_buffer = undefined,
564 };
565 mem.copy(u8, &client.partially_read_buffer, leftover);
566 return client;
567 },
568 else => {
569 return error.TlsUnexpectedMessage;
570 },
571 }
572 if (ctd.eof()) break;
615 }573 }
616 },574 },
617 else => {575 else => {
618 return error.TlsUnexpectedMessage;576 return error.TlsUnexpectedMessage;
619 },577 },
620 }578 }
621 i = end;
622 }579 }
623
624 return error.TlsHandshakeFailure;
625}580}
626581
627pub fn write(c: *Client, stream: net.Stream, bytes: []const u8) !usize {582pub fn write(c: *Client, stream: net.Stream, bytes: []const u8) !usize {
...@@ -638,12 +593,12 @@ pub fn write(c: *Client, stream: net.Stream, bytes: []const u8) !usize {...@@ -638,12 +593,12 @@ pub fn write(c: *Client, stream: net.Stream, bytes: []const u8) !usize {
638 inline else => |*p| l: {593 inline else => |*p| l: {
639 const P = @TypeOf(p.*);594 const P = @TypeOf(p.*);
640 const V = @Vector(P.AEAD.nonce_length, u8);595 const V = @Vector(P.AEAD.nonce_length, u8);
641 const overhead_len = tls.ciphertext_record_header_len + P.AEAD.tag_length + 1;596 const overhead_len = tls.record_header_len + P.AEAD.tag_length + 1;
642 while (true) {597 while (true) {
643 const encrypted_content_len = @intCast(u16, @min(598 const encrypted_content_len = @intCast(u16, @min(
644 @min(bytes.len - bytes_i, max_ciphertext_len - 1),599 @min(bytes.len - bytes_i, max_ciphertext_len - 1),
645 ciphertext_buf.len -600 ciphertext_buf.len -
646 tls.ciphertext_record_header_len - P.AEAD.tag_length - ciphertext_end - 1,601 tls.record_header_len - P.AEAD.tag_length - ciphertext_end - 1,
647 ));602 ));
648 if (encrypted_content_len == 0) break :l overhead_len;603 if (encrypted_content_len == 0) break :l overhead_len;
649604
...@@ -829,7 +784,7 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove...@@ -829,7 +784,7 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove
829784
830 // Cleartext capacity of output buffer, in records, rounded up.785 // Cleartext capacity of output buffer, in records, rounded up.
831 const buf_cap = (cleartext_buf_len +| (max_ciphertext_len - 1)) / max_ciphertext_len;786 const buf_cap = (cleartext_buf_len +| (max_ciphertext_len - 1)) / max_ciphertext_len;
832 const wanted_read_len = buf_cap * (max_ciphertext_len + tls.ciphertext_record_header_len);787 const wanted_read_len = buf_cap * (max_ciphertext_len + tls.record_header_len);
833 const ask_len = @max(wanted_read_len, cleartext_stack_buffer.len);788 const ask_len = @max(wanted_read_len, cleartext_stack_buffer.len);
834 const ask_iovecs = limitVecs(&ask_iovecs_buf, ask_len);789 const ask_iovecs = limitVecs(&ask_iovecs_buf, ask_len);
835 const actual_read_len = try stream.readv(ask_iovecs);790 const actual_read_len = try stream.readv(ask_iovecs);
...@@ -860,13 +815,13 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove...@@ -860,13 +815,13 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove
860 continue;815 continue;
861 }816 }
862817
863 if (in + tls.ciphertext_record_header_len > frag.len) {818 if (in + tls.record_header_len > frag.len) {
864 if (frag.ptr == frag1.ptr)819 if (frag.ptr == frag1.ptr)
865 return finishRead(c, frag, in, vp.total);820 return finishRead(c, frag, in, vp.total);
866821
867 const first = frag[in..];822 const first = frag[in..];
868823
869 if (frag1.len < tls.ciphertext_record_header_len)824 if (frag1.len < tls.record_header_len)
870 return finishRead2(c, first, frag1, vp.total);825 return finishRead2(c, first, frag1, vp.total);
871826
872 // A record straddles the two fragments. Copy into the now-empty first fragment.827 // A record straddles the two fragments. Copy into the now-empty first fragment.
...@@ -875,7 +830,7 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove...@@ -875,7 +830,7 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove
875 const record_len = (record_len_byte_0 << 8) | record_len_byte_1;830 const record_len = (record_len_byte_0 << 8) | record_len_byte_1;
876 if (record_len > max_ciphertext_len) return error.TlsRecordOverflow;831 if (record_len > max_ciphertext_len) return error.TlsRecordOverflow;
877832
878 const full_record_len = record_len + tls.ciphertext_record_header_len;833 const full_record_len = record_len + tls.record_header_len;
879 const second_len = full_record_len - first.len;834 const second_len = full_record_len - first.len;
880 if (frag1.len < second_len)835 if (frag1.len < second_len)
881 return finishRead2(c, first, frag1, vp.total);836 return finishRead2(c, first, frag1, vp.total);
...@@ -898,14 +853,14 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove...@@ -898,14 +853,14 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove
898 const end = in + record_len;853 const end = in + record_len;
899 if (end > frag.len) {854 if (end > frag.len) {
900 // We need the record header on the next iteration of the loop.855 // We need the record header on the next iteration of the loop.
901 in -= tls.ciphertext_record_header_len;856 in -= tls.record_header_len;
902857
903 if (frag.ptr == frag1.ptr)858 if (frag.ptr == frag1.ptr)
904 return finishRead(c, frag, in, vp.total);859 return finishRead(c, frag, in, vp.total);
905860
906 // A record straddles the two fragments. Copy into the now-empty first fragment.861 // A record straddles the two fragments. Copy into the now-empty first fragment.
907 const first = frag[in..];862 const first = frag[in..];
908 const full_record_len = record_len + tls.ciphertext_record_header_len;863 const full_record_len = record_len + tls.record_header_len;
909 const second_len = full_record_len - first.len;864 const second_len = full_record_len - first.len;
910 if (frag1.len < second_len)865 if (frag1.len < second_len)
911 return finishRead2(c, first, frag1, vp.total);866 return finishRead2(c, first, frag1, vp.total);
...@@ -919,7 +874,12 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove...@@ -919,7 +874,12 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove
919 }874 }
920 switch (ct) {875 switch (ct) {
921 .alert => {876 .alert => {
922 @panic("TODO handle an alert here");877 if (in + 2 > frag.len) return error.TlsDecodeError;
878 const level = @intToEnum(tls.AlertLevel, frag[in]);
879 const desc = @intToEnum(tls.AlertDescription, frag[in + 1]);
880 _ = level;
881 _ = desc;
882 return error.TlsAlert;
923 },883 },
924 .application_data => {884 .application_data => {
925 const cleartext = switch (c.application_cipher) {885 const cleartext = switch (c.application_cipher) {