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;
3939
4040pub const Client = @import("tls/Client.zig");
4141
42pub const ciphertext_record_header_len = 5;
42pub const record_header_len = 5;
4343pub 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;
4545pub const hello_retry_request_sequence = [32]u8{
4646 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91,
4747 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 {
360360 @truncate(u8, x),
361361 };
362362}
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)
126126 const client_hello_bytes1 = plaintext_header[5..];
127127
128128 var handshake_cipher: tls.HandshakeCipher = undefined;
129
130 var handshake_buf: [8000]u8 = undefined;
131 var len: usize = 0;
132 var i: usize = i: {
133 const plaintext = handshake_buf[0..5];
134 len = try stream.readAtLeast(&handshake_buf, plaintext.len);
135 if (len < plaintext.len) return error.EndOfStream;
136 const ct = @intToEnum(tls.ContentType, plaintext[0]);
137 const frag_len = mem.readIntBig(u16, plaintext[3..][0..2]);
138 const end = plaintext.len + frag_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
129 var handshake_buffer: [8000]u8 = undefined;
130 var d: tls.Decoder = .{ .buf = &handshake_buffer };
131 {
132 try d.readAtLeastOurAmt(stream, tls.record_header_len);
133 const ct = d.decode(tls.ContentType);
134 d.skip(2); // legacy_record_version
135 const record_len = d.decode(u16);
136 try d.readAtLeast(stream, record_len);
137 const server_hello_fragment = d.buf[d.idx..][0..record_len];
138 var ptd = try d.sub(record_len);
146139 switch (ct) {
147140 .alert => {
148 const level = @intToEnum(tls.AlertLevel, frag[0]);
149 const desc = @intToEnum(tls.AlertDescription, frag[1]);
141 try ptd.ensure(2);
142 const level = ptd.decode(tls.AlertLevel);
143 const desc = ptd.decode(tls.AlertDescription);
150144 _ = level;
151145 _ = desc;
152146 return error.TlsAlert;
153147 },
154148 .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.
156160 return error.TlsUnexpectedMessage;
157161 }
158 const length = mem.readIntBig(u24, frag[1..4]);
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;
162 const legacy_session_id_echo_len = hsd.decode(u8);
170163 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);
172165 if (!mem.eql(u8, legacy_session_id_echo, &legacy_session_id))
173166 return error.TlsIllegalParameter;
174 i += 32;
175 const cipher_suite_int = mem.readIntBig(u16, frag[i..][0..2]);
176 i += 2;
177 const cipher_suite_tag = @intToEnum(tls.CipherSuite, cipher_suite_int);
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;
167 const cipher_suite_tag = hsd.decode(tls.CipherSuite);
168 hsd.skip(1); // legacy_compression_method
169 const extensions_size = hsd.decode(u16);
170 var all_extd = try hsd.sub(extensions_size);
184171 var supported_version: u16 = 0;
185172 var shared_key: [32]u8 = undefined;
186173 var have_shared_key = false;
187 while (i < frag.len) {
188 const et = @intToEnum(tls.ExtensionType, mem.readIntBig(u16, frag[i..][0..2]));
189 i += 2;
190 const ext_size = mem.readIntBig(u16, frag[i..][0..2]);
191 i += 2;
192 const next_i = i + ext_size;
193 if (next_i > frag.len) return error.TlsBadLength;
174 while (!all_extd.eof()) {
175 try all_extd.ensure(2 + 2);
176 const et = all_extd.decode(tls.ExtensionType);
177 const ext_size = all_extd.decode(u16);
178 var extd = try all_extd.sub(ext_size);
194179 switch (et) {
195180 .supported_versions => {
196181 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);
198184 },
199185 .key_share => {
200186 if (have_shared_key) return error.TlsIllegalParameter;
201187 have_shared_key = true;
202 const named_group = @intToEnum(tls.NamedGroup, mem.readIntBig(u16, frag[i..][0..2]));
203 i += 2;
204 const key_size = mem.readIntBig(u16, frag[i..][0..2]);
205 i += 2;
206
188 try extd.ensure(4);
189 const named_group = extd.decode(tls.NamedGroup);
190 const key_size = extd.decode(u16);
191 try extd.ensure(key_size);
207192 switch (named_group) {
208193 .x25519 => {
209 if (key_size != 32) return error.TlsBadLength;
210 const server_pub_key = frag[i..][0..32];
194 if (key_size != 32) return error.TlsIllegalParameter;
195 const server_pub_key = extd.array(32);
211196
212197 shared_key = crypto.dh.X25519.scalarmult(
213198 x25519_kp.secret_key,
......@@ -215,7 +200,7 @@ pub fn init(stream: net.Stream, ca_bundle: Certificate.Bundle, host: []const u8)
215200 ) catch return error.TlsDecryptFailure;
216201 },
217202 .secp256r1 => {
218 const server_pub_key = frag[i..][0..key_size];
203 const server_pub_key = extd.slice(key_size);
219204
220205 const PublicKey = crypto.sign.ecdsa.EcdsaP256Sha256.PublicKey;
221206 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)
233218 },
234219 else => {},
235220 }
236 i = next_i;
237221 }
238222 if (!have_shared_key) return error.TlsIllegalParameter;
223
239224 const tls_version = if (supported_version == 0) legacy_version else supported_version;
240 switch (tls_version) {
241 @enumToInt(tls.ProtocolVersion.tls_1_3) => {},
242 else => return error.TlsIllegalParameter,
243 }
225 if (tls_version != @enumToInt(tls.ProtocolVersion.tls_1_3))
226 return error.TlsIllegalParameter;
244227
245228 switch (cipher_suite_tag) {
246229 inline .AES_128_GCM_SHA256,
......@@ -264,7 +247,7 @@ pub fn init(stream: net.Stream, ca_bundle: Certificate.Bundle, host: []const u8)
264247 const p = &@field(handshake_cipher, @tagName(tag));
265248 p.transcript_hash.update(client_hello_bytes1); // Client Hello part 1
266249 p.transcript_hash.update(host); // Client Hello part 2
267 p.transcript_hash.update(frag); // Server Hello
250 p.transcript_hash.update(server_hello_fragment);
268251 const hello_hash = p.transcript_hash.peek();
269252 const zeroes = [1]u8{0} ** P.Hash.digest_length;
270253 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)
289272 },
290273 else => return error.TlsUnexpectedMessage,
291274 }
292 break :i end;
293 };
275 }
294276
295277 // This is used for two purposes:
296278 // * 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)
322304 var main_cert_pub_key_len: u16 = undefined;
323305
324306 while (true) {
325 const end_hdr = i + 5;
326 if (end_hdr > handshake_buf.len) return error.TlsRecordOverflow;
327 if (end_hdr > len) {
328 len += try stream.readAtLeast(handshake_buf[len..], end_hdr - len);
329 if (end_hdr > len) return error.EndOfStream;
330 }
331 const ct = @intToEnum(tls.ContentType, handshake_buf[i]);
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 }
307 try d.readAtLeastOurAmt(stream, tls.record_header_len);
308 const record_header = d.buf[d.idx..][0..5];
309 const ct = d.decode(tls.ContentType);
310 d.skip(2); // legacy_version
311 const record_len = d.decode(u16);
312 try d.readAtLeast(stream, record_len);
313 var record_decoder = try d.sub(record_len);
344314 switch (ct) {
345315 .change_cipher_spec => {
346 if (record_size != 1) return error.TlsUnexpectedMessage;
347 if (handshake_buf[i] != 0x01) return error.TlsUnexpectedMessage;
316 try record_decoder.ensure(1);
317 if (record_decoder.decode(u8) != 0x01) return error.TlsIllegalParameter;
348318 },
349319 .application_data => {
350320 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)
352322 const cleartext = switch (handshake_cipher) {
353323 inline else => |*p| c: {
354324 const P = @TypeOf(p.*);
355 const ciphertext_len = record_size - P.AEAD.tag_length;
356 const ciphertext = handshake_buf[i..][0..ciphertext_len];
357 i += ciphertext.len;
325 const ciphertext_len = record_len - P.AEAD.tag_length;
326 try record_decoder.ensure(ciphertext_len + P.AEAD.tag_length);
327 const ciphertext = record_decoder.slice(ciphertext_len);
358328 if (ciphertext.len > cleartext_buf.len) return error.TlsRecordOverflow;
359329 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).*;
361331 const V = @Vector(P.AEAD.nonce_length, u8);
362332 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
363333 const operand: V = pad ++ @bitCast([8]u8, big(read_seq));
364334 read_seq += 1;
365335 const nonce = @as(V, p.server_handshake_iv) ^ operand;
366 const ad = handshake_buf[end_hdr - 5 ..][0..5];
367 P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, p.server_handshake_key) catch
336 P.AEAD.decrypt(cleartext, ciphertext, auth_tag, record_header, nonce, p.server_handshake_key) catch
368337 return error.TlsBadRecordMac;
369338 break :c cleartext;
370339 },
371340 };
372341
373342 const inner_ct = @intToEnum(tls.ContentType, cleartext[cleartext.len - 1]);
374 switch (inner_ct) {
375 .handshake => {
376 var ct_i: usize = 0;
377 while (true) {
378 const handshake_type = @intToEnum(tls.HandshakeType, cleartext[ct_i]);
379 ct_i += 1;
380 const handshake_len = mem.readIntBig(u24, cleartext[ct_i..][0..3]);
381 ct_i += 3;
382 const next_handshake_i = ct_i + handshake_len;
383 if (next_handshake_i > cleartext.len - 1)
384 return error.TlsBadLength;
385 const wrapped_handshake = cleartext[ct_i - 4 .. next_handshake_i];
386 const handshake = cleartext[ct_i..next_handshake_i];
387 switch (handshake_type) {
388 .encrypted_extensions => {
389 if (handshake_state != .encrypted_extensions) return error.TlsUnexpectedMessage;
390 handshake_state = .certificate;
391 switch (handshake_cipher) {
392 inline else => |*p| p.transcript_hash.update(wrapped_handshake),
393 }
394 const total_ext_size = mem.readIntBig(u16, handshake[0..2]);
395 var hs_i: usize = 2;
396 const end_ext_i = 2 + total_ext_size;
397 while (hs_i < end_ext_i) {
398 const et = @intToEnum(tls.ExtensionType, mem.readIntBig(u16, handshake[hs_i..][0..2]));
399 hs_i += 2;
400 const ext_size = mem.readIntBig(u16, handshake[hs_i..][0..2]);
401 hs_i += 2;
402 const next_ext_i = hs_i + ext_size;
403 switch (et) {
404 .server_name => {},
405 else => {},
406 }
407 hs_i = next_ext_i;
408 }
409 },
410 .certificate => cert: {
411 switch (handshake_cipher) {
412 inline else => |*p| p.transcript_hash.update(wrapped_handshake),
413 }
414 switch (handshake_state) {
415 .certificate => {},
416 .trust_chain_established => break :cert,
417 else => return error.TlsUnexpectedMessage,
343 if (inner_ct != .handshake) return error.TlsUnexpectedMessage;
344
345 var ctd = tls.Decoder.fromTheirSlice(cleartext[0 .. cleartext.len - 1]);
346 while (true) {
347 try ctd.ensure(4);
348 const handshake_type = ctd.decode(tls.HandshakeType);
349 const handshake_len = ctd.decode(u24);
350 var hsd = try ctd.sub(handshake_len);
351 const wrapped_handshake = ctd.buf[ctd.idx - handshake_len - 4 .. ctd.idx];
352 const handshake = ctd.buf[ctd.idx - handshake_len .. ctd.idx];
353 switch (handshake_type) {
354 .encrypted_extensions => {
355 if (handshake_state != .encrypted_extensions) return error.TlsUnexpectedMessage;
356 handshake_state = .certificate;
357 switch (handshake_cipher) {
358 inline else => |*p| p.transcript_hash.update(wrapped_handshake),
359 }
360 try hsd.ensure(2);
361 const total_ext_size = hsd.decode(u16);
362 var all_extd = try hsd.sub(total_ext_size);
363 while (!all_extd.eof()) {
364 try all_extd.ensure(4);
365 const et = all_extd.decode(tls.ExtensionType);
366 const ext_size = all_extd.decode(u16);
367 var extd = try all_extd.sub(ext_size);
368 _ = extd;
369 switch (et) {
370 .server_name => {},
371 else => {},
372 }
373 }
374 },
375 .certificate => cert: {
376 switch (handshake_cipher) {
377 inline else => |*p| p.transcript_hash.update(wrapped_handshake),
378 }
379 switch (handshake_state) {
380 .certificate => {},
381 .trust_chain_established => break :cert,
382 else => return error.TlsUnexpectedMessage,
383 }
384 try hsd.ensure(1 + 4);
385 const cert_req_ctx_len = hsd.decode(u8);
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;
418403 }
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 for
443 // the certificate_verify message
444 // later.
445 main_cert_pub_key_algo = subject.pub_key_algo;
446 const pub_key = subject.pubKey();
447 if (pub_key.len > main_cert_pub_key_buf.len)
448 return error.CertificatePublicKeyInvalid;
449 @memcpy(&main_cert_pub_key_buf, pub_key.ptr, pub_key.len);
450 main_cert_pub_key_len = @intCast(@TypeOf(main_cert_pub_key_len), pub_key.len);
451 } else {
452 try prev_cert.verify(subject);
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 }
405 // Keep track of the public key for the
406 // certificate_verify message later.
407 main_cert_pub_key_algo = subject.pub_key_algo;
408 const pub_key = subject.pubKey();
409 if (pub_key.len > main_cert_pub_key_buf.len)
410 return error.CertificatePublicKeyInvalid;
411 @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);
413 } else {
414 try prev_cert.verify(subject);
415 }
478416
479 const scheme = @intToEnum(tls.SignatureScheme, mem.readIntBig(u16, handshake[0..2]));
480 const sig_len = mem.readIntBig(u16, handshake[2..4]);
481 if (4 + sig_len > handshake.len) return error.TlsBadLength;
482 const encoded_sig = handshake[4..][0..sig_len];
483 const max_digest_len = 64;
484 var verify_buffer =
485 ([1]u8{0x20} ** 64) ++
486 "TLS 1.3, server CertificateVerify\x00".* ++
487 @as([max_digest_len]u8, undefined);
488
489 const verify_bytes = switch (handshake_cipher) {
490 inline else => |*p| v: {
491 const transcript_digest = p.transcript_hash.peek();
492 verify_buffer[verify_buffer.len - max_digest_len ..][0..transcript_digest.len].* = transcript_digest;
493 p.transcript_hash.update(wrapped_handshake);
494 break :v verify_buffer[0 .. verify_buffer.len - max_digest_len + transcript_digest.len];
495 },
496 };
497 const main_cert_pub_key = main_cert_pub_key_buf[0..main_cert_pub_key_len];
498
499 switch (scheme) {
500 inline .ecdsa_secp256r1_sha256,
501 .ecdsa_secp384r1_sha384,
502 => |comptime_scheme| {
503 if (main_cert_pub_key_algo != .X9_62_id_ecPublicKey)
504 return error.TlsBadSignatureScheme;
505 const Ecdsa = SchemeEcdsa(comptime_scheme);
506 const sig = try Ecdsa.Signature.fromDer(encoded_sig);
507 const key = try Ecdsa.PublicKey.fromSec1(main_cert_pub_key);
508 try sig.verify(verify_bytes, key);
509 },
510 .rsa_pss_rsae_sha256 => {
511 if (main_cert_pub_key_algo != .rsaEncryption)
512 return error.TlsBadSignatureScheme;
513
514 const Hash = crypto.hash.sha2.Sha256;
515 const rsa = Certificate.rsa;
516 const components = try rsa.PublicKey.parseDer(main_cert_pub_key);
517 const exponent = components.exponent;
518 const modulus = components.modulus;
519 var rsa_mem_buf: [512 * 32]u8 = undefined;
520 var fba = std.heap.FixedBufferAllocator.init(&rsa_mem_buf);
521 const ally = fba.allocator();
522 switch (modulus.len) {
523 inline 128, 256, 512 => |modulus_len| {
524 const key = try rsa.PublicKey.fromBytes(exponent, modulus, ally);
525 const sig = rsa.PSSSignature.fromBytes(modulus_len, encoded_sig);
526 try rsa.PSSSignature.verify(modulus_len, sig, verify_bytes, key, Hash, ally);
527 },
528 else => {
529 return error.TlsBadRsaSignatureBitCount;
530 },
531 }
417 if (ca_bundle.verify(subject)) |_| {
418 handshake_state = .trust_chain_established;
419 break :cert;
420 } else |err| switch (err) {
421 error.CertificateIssuerNotFound => {},
422 else => |e| return e,
423 }
424
425 prev_cert = subject;
426 cert_index += 1;
427
428 try certs_decoder.ensure(2);
429 const total_ext_size = certs_decoder.decode(u16);
430 var all_extd = try certs_decoder.sub(total_ext_size);
431 _ = all_extd;
432 }
433 },
434 .certificate_verify => {
435 switch (handshake_state) {
436 .trust_chain_established => handshake_state = .finished,
437 .certificate => return error.TlsCertificateNotVerified,
438 else => return error.TlsUnexpectedMessage,
439 }
440
441 try hsd.ensure(4);
442 const scheme = hsd.decode(tls.SignatureScheme);
443 const sig_len = hsd.decode(u16);
444 try hsd.ensure(sig_len);
445 const encoded_sig = hsd.slice(sig_len);
446 const max_digest_len = 64;
447 var verify_buffer =
448 ([1]u8{0x20} ** 64) ++
449 "TLS 1.3, server CertificateVerify\x00".* ++
450 @as([max_digest_len]u8, undefined);
451
452 const verify_bytes = switch (handshake_cipher) {
453 inline else => |*p| v: {
454 const transcript_digest = p.transcript_hash.peek();
455 verify_buffer[verify_buffer.len - max_digest_len ..][0..transcript_digest.len].* = transcript_digest;
456 p.transcript_hash.update(wrapped_handshake);
457 break :v verify_buffer[0 .. verify_buffer.len - max_digest_len + transcript_digest.len];
458 },
459 };
460 const main_cert_pub_key = main_cert_pub_key_buf[0..main_cert_pub_key_len];
461
462 switch (scheme) {
463 inline .ecdsa_secp256r1_sha256,
464 .ecdsa_secp384r1_sha384,
465 => |comptime_scheme| {
466 if (main_cert_pub_key_algo != .X9_62_id_ecPublicKey)
467 return error.TlsBadSignatureScheme;
468 const Ecdsa = SchemeEcdsa(comptime_scheme);
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);
532490 },
533491 else => {
534 return error.TlsBadSignatureScheme;
492 return error.TlsBadRsaSignatureBitCount;
535493 },
536494 }
537495 },
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 },
604496 else => {
605 return error.TlsUnexpectedMessage;
497 return error.TlsBadSignatureScheme;
606498 },
607499 }
608 ct_i = next_handshake_i;
609 if (ct_i >= cleartext.len - 1) break;
610 }
611 },
612 else => {
613 return error.TlsUnexpectedMessage;
614 },
500 },
501 .finished => {
502 if (handshake_state != .finished) return error.TlsUnexpectedMessage;
503 // This message is to trick buggy proxies into behaving correctly.
504 const client_change_cipher_spec_msg = [_]u8{
505 @enumToInt(tls.ContentType.change_cipher_spec),
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;
615573 }
616574 },
617575 else => {
618576 return error.TlsUnexpectedMessage;
619577 },
620578 }
621 i = end;
622579 }
623
624 return error.TlsHandshakeFailure;
625580}
626581
627582pub 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 {
638593 inline else => |*p| l: {
639594 const P = @TypeOf(p.*);
640595 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;
642597 while (true) {
643598 const encrypted_content_len = @intCast(u16, @min(
644599 @min(bytes.len - bytes_i, max_ciphertext_len - 1),
645600 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,
647602 ));
648603 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
829784
830785 // Cleartext capacity of output buffer, in records, rounded up.
831786 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);
833788 const ask_len = @max(wanted_read_len, cleartext_stack_buffer.len);
834789 const ask_iovecs = limitVecs(&ask_iovecs_buf, ask_len);
835790 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
860815 continue;
861816 }
862817
863 if (in + tls.ciphertext_record_header_len > frag.len) {
818 if (in + tls.record_header_len > frag.len) {
864819 if (frag.ptr == frag1.ptr)
865820 return finishRead(c, frag, in, vp.total);
866821
867822 const first = frag[in..];
868823
869 if (frag1.len < tls.ciphertext_record_header_len)
824 if (frag1.len < tls.record_header_len)
870825 return finishRead2(c, first, frag1, vp.total);
871826
872827 // 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
875830 const record_len = (record_len_byte_0 << 8) | record_len_byte_1;
876831 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;
879834 const second_len = full_record_len - first.len;
880835 if (frag1.len < second_len)
881836 return finishRead2(c, first, frag1, vp.total);
......@@ -898,14 +853,14 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove
898853 const end = in + record_len;
899854 if (end > frag.len) {
900855 // 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
903858 if (frag.ptr == frag1.ptr)
904859 return finishRead(c, frag, in, vp.total);
905860
906861 // A record straddles the two fragments. Copy into the now-empty first fragment.
907862 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;
909864 const second_len = full_record_len - first.len;
910865 if (frag1.len < second_len)
911866 return finishRead2(c, first, frag1, vp.total);
......@@ -919,7 +874,12 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove
919874 }
920875 switch (ct) {
921876 .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;
923883 },
924884 .application_data => {
925885 const cleartext = switch (c.application_cipher) {