authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-12-19 01:07:52-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-02 16:57:15-07:00
log3237000d957617120e32e54498cac9afa23cbcd4
tree03fc00c8b7738b38db7264c27f3021e8fdedf0e7
parent5d7eca6669228cec762fc9063a7ea3cb52af357c

std.crypto.tls: rudimentary certificate parsing


2 files changed, 174 insertions(+), 1 deletions(-)

lib/std/crypto/tls.zig+109
......@@ -349,3 +349,112 @@ pub inline fn int3(x: u24) [3]u8 {
349349 @truncate(u8, x),
350350 };
351351}
352
353pub const Der = struct {
354 pub const Class = enum(u2) {
355 universal,
356 application,
357 context_specific,
358 private,
359 };
360
361 pub const PC = enum(u1) {
362 primitive,
363 constructed,
364 };
365
366 pub const Identifier = packed struct(u8) {
367 tag: Tag,
368 pc: PC,
369 class: Class,
370 };
371
372 pub const Tag = enum(u5) {
373 boolean = 1,
374 integer = 2,
375 bitstring = 3,
376 null = 5,
377 object_identifier = 6,
378 sequence = 16,
379 _,
380 };
381
382 pub const Oid = enum {
383 commonName,
384 countryName,
385 localityName,
386 stateOrProvinceName,
387 organizationName,
388 organizationalUnitName,
389 sha256WithRSAEncryption,
390 sha384WithRSAEncryption,
391 sha512WithRSAEncryption,
392 sha224WithRSAEncryption,
393
394 pub const map = std.ComptimeStringMap(Oid, .{
395 .{ &[_]u8{ 0x55, 0x04, 0x03 }, .commonName },
396 .{ &[_]u8{ 0x55, 0x04, 0x06 }, .countryName },
397 .{ &[_]u8{ 0x55, 0x04, 0x07 }, .localityName },
398 .{ &[_]u8{ 0x55, 0x04, 0x08 }, .stateOrProvinceName },
399 .{ &[_]u8{ 0x55, 0x04, 0x0A }, .organizationName },
400 .{ &[_]u8{ 0x55, 0x04, 0x0B }, .organizationalUnitName },
401 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B }, .sha256WithRSAEncryption },
402 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0C }, .sha384WithRSAEncryption },
403 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0D }, .sha512WithRSAEncryption },
404 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0E }, .sha224WithRSAEncryption },
405 });
406 };
407
408 pub const Element = struct {
409 identifier: Identifier,
410 contents: []const u8,
411 };
412
413 pub const ParseElementError = error{CertificateHasFieldWithInvalidLength};
414
415 pub fn parseElement(bytes: []const u8, index: *usize) ParseElementError!Der.Element {
416 var i = index.*;
417 const identifier = @bitCast(Identifier, bytes[i]);
418 i += 1;
419 const size_byte = bytes[i];
420 i += 1;
421 if ((size_byte >> 7) == 0) {
422 const contents = bytes[i..][0..size_byte];
423 index.* = i + contents.len;
424 return .{
425 .identifier = identifier,
426 .contents = contents,
427 };
428 }
429
430 const len_size = @truncate(u7, size_byte);
431 if (len_size > @sizeOf(usize)) {
432 return error.CertificateHasFieldWithInvalidLength;
433 }
434
435 const end = i + len_size;
436 var long_form_size: usize = 0;
437 while (i < end) : (i += 1) {
438 long_form_size = (long_form_size << 8) | bytes[i];
439 }
440
441 const contents = bytes[i..][0..long_form_size];
442 index.* = i + contents.len;
443
444 return .{
445 .identifier = identifier,
446 .contents = contents,
447 };
448 }
449
450 pub const ParseObjectIdError = error{
451 CertificateHasUnrecognizedObjectId,
452 CertificateFieldHasWrongDataType,
453 } || ParseElementError;
454
455 pub fn parseObjectId(bytes: []const u8, index: *usize) ParseObjectIdError!Oid {
456 const oid_element = try parseElement(bytes, index);
457 if (oid_element.identifier.tag != .object_identifier) return error.CertificateFieldHasWrongDataType;
458 return Oid.map.get(oid_element.contents) orelse return error.CertificateHasUnrecognizedObjectId;
459 }
460};
lib/std/crypto/tls/Client.zig+65-1
......@@ -402,7 +402,71 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {
402402 while (hs_i < end_certs) {
403403 const cert_size = mem.readIntBig(u24, handshake[hs_i..][0..3]);
404404 hs_i += 3;
405 hs_i += cert_size;
405 const end_cert = hs_i + cert_size;
406
407 const certificate = try tls.Der.parseElement(handshake, &hs_i);
408 {
409 var cert_i: usize = 0;
410 const tbs_certificate = try tls.Der.parseElement(certificate.contents, &cert_i);
411 {
412 var tbs_i: usize = 0;
413 const version = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
414 const serial_number = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
415 const signature = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
416 const issuer = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
417 const validity = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
418 const subject = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
419 const subject_pub_key = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
420 const extensions = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
421
422 // RFC 5280, section 4.1.2.3:
423 // "This field MUST contain the same algorithm identifier as
424 // the signatureAlgorithm field in the sequence Certificate."
425 _ = signature;
426
427 _ = issuer;
428 _ = validity;
429
430 std.debug.print("version: {any} '{}'\n", .{
431 version.identifier, std.fmt.fmtSliceHexLower(version.contents),
432 });
433
434 std.debug.print("serial_number: {any} {}\n", .{
435 serial_number.identifier,
436 std.fmt.fmtSliceHexLower(serial_number.contents),
437 });
438
439 std.debug.print("subject: {any} {}\n", .{
440 subject.identifier,
441 std.fmt.fmtSliceHexLower(subject.contents),
442 });
443
444 std.debug.print("subject pub key: {any} {}\n", .{
445 subject_pub_key.identifier,
446 std.fmt.fmtSliceHexLower(subject_pub_key.contents),
447 });
448
449 std.debug.print("extensions: {any} {}\n", .{
450 extensions.identifier,
451 std.fmt.fmtSliceHexLower(extensions.contents),
452 });
453 }
454 const signature_algorithm = try tls.Der.parseElement(certificate.contents, &cert_i);
455 const signature_value = try tls.Der.parseElement(certificate.contents, &cert_i);
456
457 {
458 var sa_i: usize = 0;
459 const algorithm = try tls.Der.parseObjectId(signature_algorithm.contents, &sa_i);
460 std.debug.print("cert has this signature algorithm: {any}\n", .{algorithm});
461 //const parameters = try tls.Der.parseElement(signature_algorithm.contents, &sa_i);
462 }
463
464 std.debug.print("signature_value: {any} {d} bytes\n", .{
465 signature_value.identifier, signature_value.contents.len,
466 });
467 }
468
469 hs_i = end_cert;
406470 const total_ext_size = mem.readIntBig(u16, handshake[hs_i..][0..2]);
407471 hs_i += 2;
408472 hs_i += total_ext_size;