| ... | @@ -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..]; |
| 127 | | 127 | |
| 128 | var handshake_cipher: tls.HandshakeCipher = undefined; | 128 | var handshake_cipher: tls.HandshakeCipher = undefined; |
| 129 | | 129 | 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); |
| 211 | | 196 | |
| 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); |
| 219 | | 204 | |
| 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 | } | | |
| 244 | | 227 | |
| 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 1 | 248 | p.transcript_hash.update(client_hello_bytes1); // Client Hello part 1 |
| 266 | p.transcript_hash.update(host); // Client Hello part 2 | 249 | 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); |
| 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 | }; | | |
| 294 | | 276 | |
| 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 case | 278 | // * 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; |
| 323 | | 305 | |
| 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 | }; |
| 372 | | 341 | |
| 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 | } | | |
| 441 | | 404 | |
| 442 | // Keep track of the public key for | 405 | // Keep track of the public key for the |
| 443 | // the certificate_verify message | 406 | // 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 | } | | |
| 478 | | 416 | |
| 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; |
| 488 | | 426 | 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) { |
| 498 | | 436 | .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); |
| 513 | | 451 | |
| 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 | } |
| 626 | | 581 | |
| 627 | pub fn write(c: *Client, stream: net.Stream, bytes: []const u8) !usize { | 582 | 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,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; |
| 649 | | 604 | |
| ... | @@ -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 |
| 829 | | 784 | |
| 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 | } |
| 862 | | 817 | |
| 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); |
| 866 | | 821 | |
| 867 | const first = frag[in..]; | 822 | const first = frag[in..]; |
| 868 | | 823 | |
| 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); |
| 871 | | 826 | |
| 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; |
| 877 | | 832 | |
| 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; |
| 902 | | 857 | |
| 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); |
| 905 | | 860 | |
| 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) { |