зеркало из https://github.com/mozilla/gecko-dev.git
301 строка
12 KiB
C++
301 строка
12 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This code is made available to you under your choice of the following sets
|
|
* of licensing terms:
|
|
*/
|
|
/* 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/.
|
|
*/
|
|
/* Copyright 2014 Mozilla Contributors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "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;
|
|
}
|
|
// XXX: Ignored. What are we supposed to check? This seems totally redundant
|
|
// with Certificate.signatureAlgorithm. Is it important to check that they
|
|
// are consistent with each other? It doesn't seem to matter!
|
|
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;
|
|
}
|
|
|
|
static const uint8_t CSC = der::CONTEXT_SPECIFIC | der::CONSTRUCTED;
|
|
|
|
// 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.
|
|
if (tbsCertificate.Peek(CSC | 1)) {
|
|
rv = der::ExpectTagAndSkipValue(tbsCertificate, CSC | 1);
|
|
if (rv != Success) {
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
// Ignore subjectUniqueID if present.
|
|
if (tbsCertificate.Peek(CSC | 2)) {
|
|
rv = der::ExpectTagAndSkipValue(tbsCertificate, CSC | 2);
|
|
if (rv != Success) {
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
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_pk_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
|
|
};
|
|
|
|
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_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;
|
|
}
|
|
|
|
} } // namespace mozilla::pkix
|