structstd.crypto.Certificate.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

Error Sets

Error SetVerifyError[src]

Errors

anyerror means the error set is known only at runtime.

CertificateExpired
CertificateFieldHasInvalidLength
CertificateFieldHasWrongDataType
CertificateIssuerMismatch
CertificateNotYetValid
CertificatePublicKeyInvalid
CertificateSignatureAlgorithmMismatch
CertificateSignatureAlgorithmUnsupported
CertificateSignatureInvalid
CertificateSignatureInvalidLength
CertificateSignatureNamedCurveUnsupported
CertificateSignatureUnsupportedBitCount

Source Code

Source code
pub const VerifyError = error{
    CertificateIssuerMismatch,
    CertificateNotYetValid,
    CertificateExpired,
    CertificateSignatureAlgorithmUnsupported,
    CertificateSignatureAlgorithmMismatch,
    CertificateFieldHasInvalidLength,
    CertificateFieldHasWrongDataType,
    CertificatePublicKeyInvalid,
    CertificateSignatureInvalidLength,
    CertificateSignatureInvalid,
    CertificateSignatureUnsupportedBitCount,
    CertificateSignatureNamedCurveUnsupported,
}

Error SetVerifyHostNameError[src]

Errors

anyerror means the error set is known only at runtime.

CertificateFieldHasInvalidLength
CertificateHostMismatch

Source Code

Source code
pub const VerifyHostNameError = error{
    CertificateHostMismatch,
    CertificateFieldHasInvalidLength,
}

Functions

Functionslice[src]

pub fn slice(p: Parsed, s: Slice) []const u8

Parameters

Source Code

Source code
pub fn slice(p: Parsed, s: Slice) []const u8 {
    return p.certificate.buffer[s.start..s.end];
}

Functionissuer[src]

pub fn issuer(p: Parsed) []const u8

Parameters

Source Code

Source code
pub fn issuer(p: Parsed) []const u8 {
    return p.slice(p.issuer_slice);
}

Functionsubject[src]

pub fn subject(p: Parsed) []const u8

Parameters

Source Code

Source code
pub fn subject(p: Parsed) []const u8 {
    return p.slice(p.subject_slice);
}

FunctioncommonName[src]

pub fn commonName(p: Parsed) []const u8

Parameters

Source Code

Source code
pub fn commonName(p: Parsed) []const u8 {
    return p.slice(p.common_name_slice);
}

Functionsignature[src]

pub fn signature(p: Parsed) []const u8

Parameters

Source Code

Source code
pub fn signature(p: Parsed) []const u8 {
    return p.slice(p.signature_slice);
}

FunctionpubKey[src]

pub fn pubKey(p: Parsed) []const u8

Parameters

Source Code

Source code
pub fn pubKey(p: Parsed) []const u8 {
    return p.slice(p.pub_key_slice);
}

FunctionpubKeySigAlgo[src]

pub fn pubKeySigAlgo(p: Parsed) []const u8

Parameters

Source Code

Source code
pub fn pubKeySigAlgo(p: Parsed) []const u8 {
    return p.slice(p.pub_key_signature_algorithm_slice);
}

Functionmessage[src]

pub fn message(p: Parsed) []const u8

Parameters

Source Code

Source code
pub fn message(p: Parsed) []const u8 {
    return p.slice(p.message_slice);
}

FunctionsubjectAltName[src]

pub fn subjectAltName(p: Parsed) []const u8

Parameters

Source Code

Source code
pub fn subjectAltName(p: Parsed) []const u8 {
    return p.slice(p.subject_alt_name_slice);
}

Functionverify[src]

pub fn verify(parsed_subject: Parsed, parsed_issuer: Parsed, now_sec: i64) VerifyError!void

This function verifies:

  • That the subject's issuer is indeed the provided issuer.
  • The time validity of the subject.
  • The signature.

Parameters

parsed_subject: Parsed
parsed_issuer: Parsed
now_sec: i64

Source Code

Source code
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(),
        ),
    }
}

FunctionverifyHostName[src]

pub fn verifyHostName(parsed_subject: Parsed, host_name: []const u8) VerifyHostNameError!void

Parameters

parsed_subject: Parsed
host_name: []const u8

Source Code

Source code
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;
}

Source Code

Source code
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;
    }
}