authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-15 14:59:49-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-15 14:59:49-07:00
log9a0e1704aee1db4a7ca29706e11dd5ec6739c094
treeea342add4bca3d29583f960970fb162791992882
parentbb15e4057c9c8bb22084990de475ab10a44592c0

std.crypto.Certificate: support v1

closes #14304

1 files changed, 30 insertions(+), 8 deletions(-)

lib/std/crypto/Certificate.zig+30-8
......@@ -3,6 +3,8 @@ index: u32,
33
44pub const Bundle = @import("Certificate/Bundle.zig");
55
6pub const Version = enum { v1, v2, v3 };
7
68pub const Algorithm = enum {
79 sha1WithRSAEncryption,
810 sha224WithRSAEncryption,
......@@ -130,6 +132,7 @@ pub const Parsed = struct {
130132 message_slice: Slice,
131133 subject_alt_name_slice: Slice,
132134 validity: Validity,
135 version: Version,
133136
134137 pub const PubKeyAlgo = union(AlgorithmCategory) {
135138 rsaEncryption: void,
......@@ -299,9 +302,12 @@ pub fn parse(cert: Certificate) !Parsed {
299302 const cert_bytes = cert.buffer;
300303 const certificate = try der.Element.parse(cert_bytes, cert.index);
301304 const tbs_certificate = try der.Element.parse(cert_bytes, certificate.slice.start);
302 const version = try der.Element.parse(cert_bytes, tbs_certificate.slice.start);
303 try checkVersion(cert_bytes, version);
304 const serial_number = try der.Element.parse(cert_bytes, version.slice.end);
305 const version_elem = try der.Element.parse(cert_bytes, tbs_certificate.slice.start);
306 const version = try parseVersion(cert_bytes, version_elem);
307 const serial_number = if (@bitCast(u8, version_elem.identifier) == 0xa0)
308 try der.Element.parse(cert_bytes, version_elem.slice.end)
309 else
310 version_elem;
305311 // RFC 5280, section 4.1.2.3:
306312 // "This field MUST contain the same algorithm identifier as
307313 // the signatureAlgorithm field in the sequence Certificate."
......@@ -370,6 +376,9 @@ pub fn parse(cert: Certificate) !Parsed {
370376 // Extensions
371377 var subject_alt_name_slice = der.Element.Slice.empty;
372378 ext: {
379 if (version == .v1)
380 break :ext;
381
373382 if (pub_key_info.slice.end >= tbs_certificate.slice.end)
374383 break :ext;
375384
......@@ -415,6 +424,7 @@ pub fn parse(cert: Certificate) !Parsed {
415424 .not_after = not_after_utc,
416425 },
417426 .subject_alt_name_slice = subject_alt_name_slice,
427 .version = version,
418428 };
419429}
420430
......@@ -588,12 +598,24 @@ fn parseEnum(comptime E: type, bytes: []const u8, element: der.Element) !E {
588598 return E.map.get(oid_bytes) orelse return error.CertificateHasUnrecognizedObjectId;
589599}
590600
591pub fn checkVersion(bytes: []const u8, version: der.Element) !void {
592 if (@bitCast(u8, version.identifier) != 0xa0 or
593 !mem.eql(u8, bytes[version.slice.start..version.slice.end], "\x02\x01\x02"))
594 {
595 return error.UnsupportedCertificateVersion;
601pub fn parseVersion(bytes: []const u8, version_elem: der.Element) !Version {
602 if (@bitCast(u8, version_elem.identifier) != 0xa0)
603 return .v1;
604
605 if (version_elem.slice.end - version_elem.slice.start != 3)
606 return error.CertificateFieldHasInvalidLength;
607
608 const encoded_version = bytes[version_elem.slice.start..version_elem.slice.end];
609
610 if (mem.eql(u8, encoded_version, "\x02\x01\x02")) {
611 return .v3;
612 } else if (mem.eql(u8, encoded_version, "\x02\x01\x01")) {
613 return .v2;
614 } else if (mem.eql(u8, encoded_version, "\x02\x01\x00")) {
615 return .v1;
596616 }
617
618 return error.UnsupportedCertificateVersion;
597619}
598620
599621fn verifyRsa(