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
- commonName (Function)
- issuer (Function)
- message (Function)
- pubKey (Function)
- PubKeyAlgo (union)
- pubKeySigAlgo (Function)
- signature (Function)
- slice (Function)
- Slice (struct)
- subject (Function)
- subjectAltName (Function)
- Validity (struct)
- verify (Function)
- VerifyError (Error Set)
- verifyHostName (Function)
- VerifyHostNameError (Error Set)
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;
}
}