struct Parsed [src]

Fields

certificate: Certificate
issuer_slice: Slice
subject_slice: Slice
common_name_slice: Slice
signature_slice: Slice
signature_algorithm: Algorithm
pub_key_algo: PubKeyAlgo
pub_key_slice: Slice
message_slice: Slice
subject_alt_name_slice: Slice
validity: Validity
version: Version

Members

Source

pub const Parsed = struct { certificate: Certificate, issuer_slice: Slice, subject_slice: Slice, common_name_slice: Slice, signature_slice: Slice, signature_algorithm: Algorithm, pub_key_algo: PubKeyAlgo, pub_key_slice: Slice, message_slice: Slice, subject_alt_name_slice: Slice, validity: Validity, version: Version, pub const PubKeyAlgo = union(AlgorithmCategory) { rsaEncryption: void, rsassa_pss: void, X9_62_id_ecPublicKey: NamedCurve, curveEd25519: void, }; pub const Validity = struct { not_before: u64, not_after: u64, }; pub const Slice = der.Element.Slice; pub fn slice(p: Parsed, s: Slice) []const u8 { return p.certificate.buffer[s.start..s.end]; } pub fn issuer(p: Parsed) []const u8 { return p.slice(p.issuer_slice); } pub fn subject(p: Parsed) []const u8 { return p.slice(p.subject_slice); } pub fn commonName(p: Parsed) []const u8 { return p.slice(p.common_name_slice); } pub fn signature(p: Parsed) []const u8 { return p.slice(p.signature_slice); } pub fn pubKey(p: Parsed) []const u8 { return p.slice(p.pub_key_slice); } pub fn pubKeySigAlgo(p: Parsed) []const u8 { return p.slice(p.pub_key_signature_algorithm_slice); } pub fn message(p: Parsed) []const u8 { return p.slice(p.message_slice); } pub fn subjectAltName(p: Parsed) []const u8 { return p.slice(p.subject_alt_name_slice); } pub const VerifyError = error{ CertificateIssuerMismatch, CertificateNotYetValid, CertificateExpired, CertificateSignatureAlgorithmUnsupported, CertificateSignatureAlgorithmMismatch, CertificateFieldHasInvalidLength, CertificateFieldHasWrongDataType, CertificatePublicKeyInvalid, CertificateSignatureInvalidLength, CertificateSignatureInvalid, CertificateSignatureUnsupportedBitCount, CertificateSignatureNamedCurveUnsupported, }; /// This function verifies: /// * That the subject's issuer is indeed the provided issuer. /// * The time validity of the subject. /// * The signature. pub fn verify(parsed_subject: Parsed, parsed_issuer: Parsed, now_sec: i64) VerifyError!void { // Check that the subject's issuer name matches the issuer's // subject name. if (!mem.eql(u8, parsed_subject.issuer(), parsed_issuer.subject())) { return error.CertificateIssuerMismatch; } if (now_sec < parsed_subject.validity.not_before) return error.CertificateNotYetValid; if (now_sec > parsed_subject.validity.not_after) return error.CertificateExpired; switch (parsed_subject.signature_algorithm) { inline .sha1WithRSAEncryption, .sha224WithRSAEncryption, .sha256WithRSAEncryption, .sha384WithRSAEncryption, .sha512WithRSAEncryption, => |algorithm| return verifyRsa( algorithm.Hash(), parsed_subject.message(), parsed_subject.signature(), parsed_issuer.pub_key_algo, parsed_issuer.pubKey(), ), inline .ecdsa_with_SHA224, .ecdsa_with_SHA256, .ecdsa_with_SHA384, .ecdsa_with_SHA512, => |algorithm| return verify_ecdsa( algorithm.Hash(), parsed_subject.message(), parsed_subject.signature(), parsed_issuer.pub_key_algo, parsed_issuer.pubKey(), ), .md2WithRSAEncryption, .md5WithRSAEncryption => { return error.CertificateSignatureAlgorithmUnsupported; }, .curveEd25519 => return verifyEd25519( parsed_subject.message(), parsed_subject.signature(), parsed_issuer.pub_key_algo, parsed_issuer.pubKey(), ), } } pub const VerifyHostNameError = error{ CertificateHostMismatch, CertificateFieldHasInvalidLength, }; pub fn verifyHostName(parsed_subject: Parsed, host_name: []const u8) VerifyHostNameError!void { // If the Subject Alternative Names extension is present, this is // what to check. Otherwise, only the common name is checked. const subject_alt_name = parsed_subject.subjectAltName(); if (subject_alt_name.len == 0) { if (checkHostName(host_name, parsed_subject.commonName())) { return; } else { return error.CertificateHostMismatch; } } const general_names = try der.Element.parse(subject_alt_name, 0); var name_i = general_names.slice.start; while (name_i < general_names.slice.end) { const general_name = try der.Element.parse(subject_alt_name, name_i); name_i = general_name.slice.end; switch (@as(GeneralNameTag, @enumFromInt(@intFromEnum(general_name.identifier.tag)))) { .dNSName => { const dns_name = subject_alt_name[general_name.slice.start..general_name.slice.end]; if (checkHostName(host_name, dns_name)) return; }, else => {}, } } return error.CertificateHostMismatch; } // Check hostname according to RFC2818 specification: // // If more than one identity of a given type is present in // the certificate (e.g., more than one DNSName name, a match in any one // of the set is considered acceptable.) Names may contain the wildcard // character * which is considered to match any single domain name // component or component fragment. E.g., *.a.com matches foo.a.com but // not bar.foo.a.com. f*.com matches foo.com but not bar.com. fn checkHostName(host_name: []const u8, dns_name: []const u8) bool { if (std.ascii.eqlIgnoreCase(dns_name, host_name)) { return true; // exact match } var it_host = std.mem.splitScalar(u8, host_name, '.'); var it_dns = std.mem.splitScalar(u8, dns_name, '.'); const len_match = while (true) { const host = it_host.next(); const dns = it_dns.next(); if (host == null or dns == null) { break host == null and dns == null; } // If not a wildcard and they dont // match then there is no match. if (mem.eql(u8, dns.?, "*") == false and std.ascii.eqlIgnoreCase(dns.?, host.?) == false) { return false; } }; // If the components are not the same // length then there is no match. return len_match; } }