123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
- #include "pkix/pkixutil.h"
- namespace mozilla { namespace pkix {
- Result
- BackCert::Init()
- {
- Result rv;
- // Certificate ::= SEQUENCE {
- // tbsCertificate TBSCertificate,
- // signatureAlgorithm AlgorithmIdentifier,
- // signatureValue BIT STRING }
- Reader tbsCertificate;
- // The scope of |input| and |certificate| are limited to this block so we
- // don't accidentally confuse them for tbsCertificate later.
- {
- Reader certificate;
- rv = der::ExpectTagAndGetValueAtEnd(der, der::SEQUENCE, certificate);
- if (rv != Success) {
- return rv;
- }
- rv = der::SignedData(certificate, tbsCertificate, signedData);
- if (rv != Success) {
- return rv;
- }
- rv = der::End(certificate);
- if (rv != Success) {
- return rv;
- }
- }
- // TBSCertificate ::= SEQUENCE {
- // version [0] EXPLICIT Version DEFAULT v1,
- // serialNumber CertificateSerialNumber,
- // signature AlgorithmIdentifier,
- // issuer Name,
- // validity Validity,
- // subject Name,
- // subjectPublicKeyInfo SubjectPublicKeyInfo,
- // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
- // -- If present, version MUST be v2 or v3
- // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
- // -- If present, version MUST be v2 or v3
- // extensions [3] EXPLICIT Extensions OPTIONAL
- // -- If present, version MUST be v3
- // }
- rv = der::OptionalVersion(tbsCertificate, version);
- if (rv != Success) {
- return rv;
- }
- rv = der::CertificateSerialNumber(tbsCertificate, serialNumber);
- if (rv != Success) {
- return rv;
- }
- rv = der::ExpectTagAndGetValue(tbsCertificate, der::SEQUENCE, signature);
- if (rv != Success) {
- return rv;
- }
- rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, issuer);
- if (rv != Success) {
- return rv;
- }
- rv = der::ExpectTagAndGetValue(tbsCertificate, der::SEQUENCE, validity);
- if (rv != Success) {
- return rv;
- }
- // TODO(bug XXXXXXX): We rely on the the caller of mozilla::pkix to validate
- // that the name is syntactically valid, if they care. In Gecko we do this
- // implicitly by parsing the certificate into a CERTCertificate object.
- // Instead of relying on the caller to do this, we should do it ourselves.
- rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, subject);
- if (rv != Success) {
- return rv;
- }
- rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE,
- subjectPublicKeyInfo);
- if (rv != Success) {
- return rv;
- }
- // According to RFC 5280, all fields below this line are forbidden for
- // certificate versions less than v3. However, for compatibility reasons,
- // we parse v1/v2 certificates in the same way as v3 certificates. So if
- // these fields appear in a v1 certificate, they will be used.
- // Ignore issuerUniqueID if present.
- rv = der::SkipOptionalImplicitPrimitiveTag(tbsCertificate, 1);
- if (rv != Success) {
- return rv;
- }
- // Ignore subjectUniqueID if present.
- rv = der::SkipOptionalImplicitPrimitiveTag(tbsCertificate, 2);
- if (rv != Success) {
- return rv;
- }
- static const uint8_t CSC = der::CONTEXT_SPECIFIC | der::CONSTRUCTED;
- rv = der::OptionalExtensions(
- tbsCertificate, CSC | 3,
- [this](Reader& extnID, const Input& extnValue, bool critical,
- /*out*/ bool& understood) {
- return RememberExtension(extnID, extnValue, critical, understood);
- });
- if (rv != Success) {
- return rv;
- }
- // The Netscape Certificate Type extension is an obsolete
- // Netscape-proprietary mechanism that we ignore in favor of the standard
- // extensions. However, some CAs have issued certificates with the Netscape
- // Cert Type extension marked critical. Thus, for compatibility reasons, we
- // "understand" this extension by ignoring it when it is not critical, and
- // by ensuring that the equivalent standardized extensions are present when
- // it is marked critical, based on the assumption that the information in
- // the Netscape Cert Type extension is consistent with the information in
- // the standard extensions.
- //
- // Here is a mapping between the Netscape Cert Type extension and the
- // standard extensions:
- //
- // Netscape Cert Type | BasicConstraints.cA | Extended Key Usage
- // --------------------+-----------------------+----------------------
- // SSL Server | false | id_kp_serverAuth
- // SSL Client | false | id_kp_clientAuth
- // S/MIME Client | false | id_kp_emailProtection
- // Object Signing | false | id_kp_codeSigning
- // SSL Server CA | true | id_kp_serverAuth
- // SSL Client CA | true | id_kp_clientAuth
- // S/MIME CA | true | id_kp_emailProtection
- // Object Signing CA | true | id_kp_codeSigning
- if (criticalNetscapeCertificateType.GetLength() > 0 &&
- (basicConstraints.GetLength() == 0 || extKeyUsage.GetLength() == 0)) {
- return Result::ERROR_UNKNOWN_CRITICAL_EXTENSION;
- }
- return der::End(tbsCertificate);
- }
- Result
- BackCert::RememberExtension(Reader& extnID, Input extnValue,
- bool critical, /*out*/ bool& understood)
- {
- understood = false;
- // python DottedOIDToCode.py id-ce-keyUsage 2.5.29.15
- static const uint8_t id_ce_keyUsage[] = {
- 0x55, 0x1d, 0x0f
- };
- // python DottedOIDToCode.py id-ce-subjectAltName 2.5.29.17
- static const uint8_t id_ce_subjectAltName[] = {
- 0x55, 0x1d, 0x11
- };
- // python DottedOIDToCode.py id-ce-basicConstraints 2.5.29.19
- static const uint8_t id_ce_basicConstraints[] = {
- 0x55, 0x1d, 0x13
- };
- // python DottedOIDToCode.py id-ce-nameConstraints 2.5.29.30
- static const uint8_t id_ce_nameConstraints[] = {
- 0x55, 0x1d, 0x1e
- };
- // python DottedOIDToCode.py id-ce-certificatePolicies 2.5.29.32
- static const uint8_t id_ce_certificatePolicies[] = {
- 0x55, 0x1d, 0x20
- };
- // python DottedOIDToCode.py id-ce-policyConstraints 2.5.29.36
- static const uint8_t id_ce_policyConstraints[] = {
- 0x55, 0x1d, 0x24
- };
- // python DottedOIDToCode.py id-ce-extKeyUsage 2.5.29.37
- static const uint8_t id_ce_extKeyUsage[] = {
- 0x55, 0x1d, 0x25
- };
- // python DottedOIDToCode.py id-ce-inhibitAnyPolicy 2.5.29.54
- static const uint8_t id_ce_inhibitAnyPolicy[] = {
- 0x55, 0x1d, 0x36
- };
- // python DottedOIDToCode.py id-pe-authorityInfoAccess 1.3.6.1.5.5.7.1.1
- static const uint8_t id_pe_authorityInfoAccess[] = {
- 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01
- };
- // python DottedOIDToCode.py id-pkix-ocsp-nocheck 1.3.6.1.5.5.7.48.1.5
- static const uint8_t id_pkix_ocsp_nocheck[] = {
- 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x05
- };
- // python DottedOIDToCode.py Netscape-certificate-type 2.16.840.1.113730.1.1
- static const uint8_t Netscape_certificate_type[] = {
- 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01
- };
- // python DottedOIDToCode.py id-pe-tlsfeature 1.3.6.1.5.5.7.1.24
- static const uint8_t id_pe_tlsfeature[] = {
- 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x18
- };
- // python DottedOIDToCode.py id-embeddedSctList 1.3.6.1.4.1.11129.2.4.2
- // See Section 3.3 of RFC 6962.
- static const uint8_t id_embeddedSctList[] = {
- 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x02
- };
- Input* out = nullptr;
- // We already enforce the maximum possible constraints for policies so we
- // can safely ignore even critical policy constraint extensions.
- //
- // XXX: Doing it this way won't allow us to detect duplicate
- // policyConstraints extensions, but that's OK because (and only because) we
- // ignore the extension.
- Input dummyPolicyConstraints;
- // We don't need to save the contents of this extension if it is present. We
- // just need to handle its presence (it is essentially ignored right now).
- Input dummyOCSPNocheck;
- // For compatibility reasons, for some extensions we have to allow empty
- // extension values. This would normally interfere with our duplicate
- // extension checking code. However, as long as the extensions we allow to
- // have empty values are also the ones we implicitly allow duplicates of,
- // this will work fine.
- bool emptyValueAllowed = false;
- // RFC says "Conforming CAs MUST mark this extension as non-critical" for
- // both authorityKeyIdentifier and subjectKeyIdentifier, and we do not use
- // them for anything, so we totally ignore them here.
- if (extnID.MatchRest(id_ce_keyUsage)) {
- out = &keyUsage;
- } else if (extnID.MatchRest(id_ce_subjectAltName)) {
- out = &subjectAltName;
- } else if (extnID.MatchRest(id_ce_basicConstraints)) {
- out = &basicConstraints;
- } else if (extnID.MatchRest(id_ce_nameConstraints)) {
- out = &nameConstraints;
- } else if (extnID.MatchRest(id_ce_certificatePolicies)) {
- out = &certificatePolicies;
- } else if (extnID.MatchRest(id_ce_policyConstraints)) {
- out = &dummyPolicyConstraints;
- } else if (extnID.MatchRest(id_ce_extKeyUsage)) {
- out = &extKeyUsage;
- } else if (extnID.MatchRest(id_ce_inhibitAnyPolicy)) {
- out = &inhibitAnyPolicy;
- } else if (extnID.MatchRest(id_pe_authorityInfoAccess)) {
- out = &authorityInfoAccess;
- } else if (extnID.MatchRest(id_pe_tlsfeature)) {
- out = &requiredTLSFeatures;
- } else if (extnID.MatchRest(id_embeddedSctList)) {
- out = &signedCertificateTimestamps;
- } else if (extnID.MatchRest(id_pkix_ocsp_nocheck) && critical) {
- // We need to make sure we don't reject delegated OCSP response signing
- // certificates that contain the id-pkix-ocsp-nocheck extension marked as
- // critical when validating OCSP responses. Without this, an application
- // that implements soft-fail OCSP might ignore a valid Revoked or Unknown
- // response, and an application that implements hard-fail OCSP might fail
- // to connect to a server given a valid Good response.
- out = &dummyOCSPNocheck;
- // We allow this extension to have an empty value.
- // See http://comments.gmane.org/gmane.ietf.x509/30947
- emptyValueAllowed = true;
- } else if (extnID.MatchRest(Netscape_certificate_type) && critical) {
- out = &criticalNetscapeCertificateType;
- }
- if (out) {
- // Don't allow an empty value for any extension we understand. This way, we
- // can test out->GetLength() != 0 or out->Init() to check for duplicates.
- if (extnValue.GetLength() == 0 && !emptyValueAllowed) {
- return Result::ERROR_EXTENSION_VALUE_INVALID;
- }
- if (out->Init(extnValue) != Success) {
- // Duplicate extension
- return Result::ERROR_EXTENSION_VALUE_INVALID;
- }
- understood = true;
- }
- return Success;
- }
- Result
- ExtractSignedCertificateTimestampListFromExtension(Input extnValue,
- Input& sctList)
- {
- Reader decodedValue;
- Result rv = der::ExpectTagAndGetValueAtEnd(extnValue, der::OCTET_STRING,
- decodedValue);
- if (rv != Success) {
- return rv;
- }
- return decodedValue.SkipToEnd(sctList);
- }
- } } // namespace mozilla::pkix
|