mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-08 00:07:30 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
83
security/pkix/lib/ScopedPtr.h
Normal file
83
security/pkix/lib/ScopedPtr.h
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/* -*- 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 2013 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.
|
||||
*/
|
||||
|
||||
#ifndef mozilla_pkix_ScopedPtr_h
|
||||
#define mozilla_pkix_ScopedPtr_h
|
||||
|
||||
namespace mozilla { namespace pkix {
|
||||
|
||||
// A subset polyfill of std::unique_ptr that does not support move construction
|
||||
// or move assignment. This is used instead of std::unique_ptr because some
|
||||
// important toolchains still don't provide std::unique_ptr, including in
|
||||
// particular Android NDK projects with APP_STL=stlport_static or
|
||||
// ALL_STL=stlport_shared.
|
||||
template <typename T, void (&Destroyer)(T*)>
|
||||
class ScopedPtr final
|
||||
{
|
||||
public:
|
||||
explicit ScopedPtr(T* value = nullptr) : mValue(value) { }
|
||||
|
||||
ScopedPtr(const ScopedPtr&) = delete;
|
||||
|
||||
~ScopedPtr()
|
||||
{
|
||||
if (mValue) {
|
||||
Destroyer(mValue);
|
||||
}
|
||||
}
|
||||
|
||||
void operator=(const ScopedPtr&) = delete;
|
||||
|
||||
T& operator*() const { return *mValue; }
|
||||
T* operator->() const { return mValue; }
|
||||
|
||||
explicit operator bool() const { return mValue; }
|
||||
|
||||
T* get() const { return mValue; }
|
||||
|
||||
T* release()
|
||||
{
|
||||
T* result = mValue;
|
||||
mValue = nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
void reset(T* newValue = nullptr)
|
||||
{
|
||||
// The C++ standard requires std::unique_ptr to destroy the old value
|
||||
// pointed to by mValue, if any, *after* assigning the new value to mValue.
|
||||
T* oldValue = mValue;
|
||||
mValue = newValue;
|
||||
if (oldValue) {
|
||||
Destroyer(oldValue);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
T* mValue;
|
||||
};
|
||||
|
||||
} } // namespace mozilla::pkix
|
||||
|
||||
#endif // mozilla_pkix_ScopedPtr_h
|
||||
382
security/pkix/lib/pkixbuild.cpp
Normal file
382
security/pkix/lib/pkixbuild.cpp
Normal file
|
|
@ -0,0 +1,382 @@
|
|||
/* -*- 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 2013 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 "pkix/pkix.h"
|
||||
|
||||
#include "pkixcheck.h"
|
||||
#include "pkixutil.h"
|
||||
|
||||
namespace mozilla { namespace pkix {
|
||||
|
||||
static Result BuildForward(TrustDomain& trustDomain,
|
||||
const BackCert& subject,
|
||||
Time time,
|
||||
KeyUsage requiredKeyUsageIfPresent,
|
||||
KeyPurposeId requiredEKUIfPresent,
|
||||
const CertPolicyId& requiredPolicy,
|
||||
/*optional*/ const Input* stapledOCSPResponse,
|
||||
unsigned int subCACount);
|
||||
|
||||
TrustDomain::IssuerChecker::IssuerChecker() { }
|
||||
TrustDomain::IssuerChecker::~IssuerChecker() { }
|
||||
|
||||
// The implementation of TrustDomain::IssuerTracker is in a subclass only to
|
||||
// hide the implementation from external users.
|
||||
class PathBuildingStep final : public TrustDomain::IssuerChecker
|
||||
{
|
||||
public:
|
||||
PathBuildingStep(TrustDomain& trustDomain, const BackCert& subject,
|
||||
Time time, KeyPurposeId requiredEKUIfPresent,
|
||||
const CertPolicyId& requiredPolicy,
|
||||
/*optional*/ const Input* stapledOCSPResponse,
|
||||
unsigned int subCACount, Result deferredSubjectError)
|
||||
: trustDomain(trustDomain)
|
||||
, subject(subject)
|
||||
, time(time)
|
||||
, requiredEKUIfPresent(requiredEKUIfPresent)
|
||||
, requiredPolicy(requiredPolicy)
|
||||
, stapledOCSPResponse(stapledOCSPResponse)
|
||||
, subCACount(subCACount)
|
||||
, deferredSubjectError(deferredSubjectError)
|
||||
, result(Result::FATAL_ERROR_LIBRARY_FAILURE)
|
||||
, resultWasSet(false)
|
||||
{
|
||||
}
|
||||
|
||||
Result Check(Input potentialIssuerDER,
|
||||
/*optional*/ const Input* additionalNameConstraints,
|
||||
/*out*/ bool& keepGoing) override;
|
||||
|
||||
Result CheckResult() const;
|
||||
|
||||
private:
|
||||
TrustDomain& trustDomain;
|
||||
const BackCert& subject;
|
||||
const Time time;
|
||||
const KeyPurposeId requiredEKUIfPresent;
|
||||
const CertPolicyId& requiredPolicy;
|
||||
/*optional*/ Input const* const stapledOCSPResponse;
|
||||
const unsigned int subCACount;
|
||||
const Result deferredSubjectError;
|
||||
|
||||
// Initialized lazily.
|
||||
uint8_t subjectSignatureDigestBuf[MAX_DIGEST_SIZE_IN_BYTES];
|
||||
der::PublicKeyAlgorithm subjectSignaturePublicKeyAlg;
|
||||
SignedDigest subjectSignature;
|
||||
|
||||
Result RecordResult(Result currentResult, /*out*/ bool& keepGoing);
|
||||
Result result;
|
||||
bool resultWasSet;
|
||||
|
||||
PathBuildingStep(const PathBuildingStep&) = delete;
|
||||
void operator=(const PathBuildingStep&) = delete;
|
||||
};
|
||||
|
||||
Result
|
||||
PathBuildingStep::RecordResult(Result newResult, /*out*/ bool& keepGoing)
|
||||
{
|
||||
if (newResult == Result::ERROR_UNTRUSTED_CERT) {
|
||||
newResult = Result::ERROR_UNTRUSTED_ISSUER;
|
||||
} else if (newResult == Result::ERROR_EXPIRED_CERTIFICATE) {
|
||||
newResult = Result::ERROR_EXPIRED_ISSUER_CERTIFICATE;
|
||||
} else if (newResult == Result::ERROR_NOT_YET_VALID_CERTIFICATE) {
|
||||
newResult = Result::ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE;
|
||||
}
|
||||
|
||||
if (resultWasSet) {
|
||||
if (result == Success) {
|
||||
return NotReached("RecordResult called after finding a chain",
|
||||
Result::FATAL_ERROR_INVALID_STATE);
|
||||
}
|
||||
// If every potential issuer has the same problem (e.g. expired) and/or if
|
||||
// there is only one bad potential issuer, then return a more specific
|
||||
// error. Otherwise, punt on trying to decide which error should be
|
||||
// returned by returning the generic Result::ERROR_UNKNOWN_ISSUER error.
|
||||
if (newResult != Success && newResult != result) {
|
||||
newResult = Result::ERROR_UNKNOWN_ISSUER;
|
||||
}
|
||||
}
|
||||
|
||||
result = newResult;
|
||||
resultWasSet = true;
|
||||
keepGoing = result != Success;
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result
|
||||
PathBuildingStep::CheckResult() const
|
||||
{
|
||||
if (!resultWasSet) {
|
||||
return Result::ERROR_UNKNOWN_ISSUER;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// The code that executes in the inner loop of BuildForward
|
||||
Result
|
||||
PathBuildingStep::Check(Input potentialIssuerDER,
|
||||
/*optional*/ const Input* additionalNameConstraints,
|
||||
/*out*/ bool& keepGoing)
|
||||
{
|
||||
BackCert potentialIssuer(potentialIssuerDER, EndEntityOrCA::MustBeCA,
|
||||
&subject);
|
||||
Result rv = potentialIssuer.Init();
|
||||
if (rv != Success) {
|
||||
return RecordResult(rv, keepGoing);
|
||||
}
|
||||
|
||||
// Simple TrustDomain::FindIssuers implementations may pass in all possible
|
||||
// CA certificates without any filtering. Because of this, we don't consider
|
||||
// a mismatched name to be an error. Instead, we just pretend that any
|
||||
// certificate without a matching name was never passed to us. In particular,
|
||||
// we treat the case where the TrustDomain only asks us to check CA
|
||||
// certificates with mismatched names as equivalent to the case where the
|
||||
// TrustDomain never called Check() at all.
|
||||
if (!InputsAreEqual(potentialIssuer.GetSubject(), subject.GetIssuer())) {
|
||||
keepGoing = true;
|
||||
return Success;
|
||||
}
|
||||
|
||||
// Loop prevention, done as recommended by RFC4158 Section 5.2
|
||||
// TODO: this doesn't account for subjectAltNames!
|
||||
// TODO(perf): This probably can and should be optimized in some way.
|
||||
bool loopDetected = false;
|
||||
for (const BackCert* prev = potentialIssuer.childCert;
|
||||
!loopDetected && prev != nullptr; prev = prev->childCert) {
|
||||
if (InputsAreEqual(potentialIssuer.GetSubjectPublicKeyInfo(),
|
||||
prev->GetSubjectPublicKeyInfo()) &&
|
||||
InputsAreEqual(potentialIssuer.GetSubject(), prev->GetSubject())) {
|
||||
// XXX: error code
|
||||
return RecordResult(Result::ERROR_UNKNOWN_ISSUER, keepGoing);
|
||||
}
|
||||
}
|
||||
|
||||
if (potentialIssuer.GetNameConstraints()) {
|
||||
rv = CheckNameConstraints(*potentialIssuer.GetNameConstraints(),
|
||||
subject, requiredEKUIfPresent);
|
||||
if (rv != Success) {
|
||||
return RecordResult(rv, keepGoing);
|
||||
}
|
||||
}
|
||||
|
||||
if (additionalNameConstraints) {
|
||||
rv = CheckNameConstraints(*additionalNameConstraints, subject,
|
||||
requiredEKUIfPresent);
|
||||
if (rv != Success) {
|
||||
return RecordResult(rv, keepGoing);
|
||||
}
|
||||
}
|
||||
|
||||
rv = CheckTLSFeatures(subject, potentialIssuer);
|
||||
if (rv != Success) {
|
||||
return RecordResult(rv, keepGoing);
|
||||
}
|
||||
|
||||
// RFC 5280, Section 4.2.1.3: "If the keyUsage extension is present, then the
|
||||
// subject public key MUST NOT be used to verify signatures on certificates
|
||||
// or CRLs unless the corresponding keyCertSign or cRLSign bit is set."
|
||||
rv = BuildForward(trustDomain, potentialIssuer, time, KeyUsage::keyCertSign,
|
||||
requiredEKUIfPresent, requiredPolicy, nullptr, subCACount);
|
||||
if (rv != Success) {
|
||||
return RecordResult(rv, keepGoing);
|
||||
}
|
||||
|
||||
// Calculate the digest of the subject's signed data if we haven't already
|
||||
// done so. We do this lazily to avoid doing it at all if we backtrack before
|
||||
// getting to this point. We cache the result to avoid recalculating it if we
|
||||
// backtrack after getting to this point.
|
||||
if (subjectSignature.digest.GetLength() == 0) {
|
||||
rv = DigestSignedData(trustDomain, subject.GetSignedData(),
|
||||
subjectSignatureDigestBuf,
|
||||
subjectSignaturePublicKeyAlg, subjectSignature);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
rv = VerifySignedDigest(trustDomain, subjectSignaturePublicKeyAlg,
|
||||
subjectSignature,
|
||||
potentialIssuer.GetSubjectPublicKeyInfo());
|
||||
if (rv != Success) {
|
||||
return RecordResult(rv, keepGoing);
|
||||
}
|
||||
|
||||
// We avoid doing revocation checking for expired certificates because OCSP
|
||||
// responders are allowed to forget about expired certificates, and many OCSP
|
||||
// responders return an error when asked for the status of an expired
|
||||
// certificate.
|
||||
if (deferredSubjectError != Result::ERROR_EXPIRED_CERTIFICATE) {
|
||||
CertID certID(subject.GetIssuer(), potentialIssuer.GetSubjectPublicKeyInfo(),
|
||||
subject.GetSerialNumber());
|
||||
Time notBefore(Time::uninitialized);
|
||||
Time notAfter(Time::uninitialized);
|
||||
// This should never fail. If we're here, we've already parsed the validity
|
||||
// and checked that the given time is in the certificate's validity period.
|
||||
rv = ParseValidity(subject.GetValidity(), ¬Before, ¬After);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
Duration validityDuration(notAfter, notBefore);
|
||||
rv = trustDomain.CheckRevocation(subject.endEntityOrCA, certID, time,
|
||||
validityDuration, stapledOCSPResponse,
|
||||
subject.GetAuthorityInfoAccess());
|
||||
if (rv != Success) {
|
||||
return RecordResult(rv, keepGoing);
|
||||
}
|
||||
|
||||
if (subject.endEntityOrCA == EndEntityOrCA::MustBeEndEntity) {
|
||||
const Input* sctExtension = subject.GetSignedCertificateTimestamps();
|
||||
if (sctExtension) {
|
||||
Input sctList;
|
||||
rv = ExtractSignedCertificateTimestampListFromExtension(*sctExtension,
|
||||
sctList);
|
||||
if (rv != Success) {
|
||||
return RecordResult(rv, keepGoing);
|
||||
}
|
||||
trustDomain.NoteAuxiliaryExtension(AuxiliaryExtension::EmbeddedSCTList,
|
||||
sctList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RecordResult(Success, keepGoing);
|
||||
}
|
||||
|
||||
// Recursively build the path from the given subject certificate to the root.
|
||||
//
|
||||
// Be very careful about changing the order of checks. The order is significant
|
||||
// because it affects which error we return when a certificate or certificate
|
||||
// chain has multiple problems. See the error ranking documentation in
|
||||
// pkix/pkix.h.
|
||||
static Result
|
||||
BuildForward(TrustDomain& trustDomain,
|
||||
const BackCert& subject,
|
||||
Time time,
|
||||
KeyUsage requiredKeyUsageIfPresent,
|
||||
KeyPurposeId requiredEKUIfPresent,
|
||||
const CertPolicyId& requiredPolicy,
|
||||
/*optional*/ const Input* stapledOCSPResponse,
|
||||
unsigned int subCACount)
|
||||
{
|
||||
Result rv;
|
||||
|
||||
TrustLevel trustLevel;
|
||||
// If this is an end-entity and not a trust anchor, we defer reporting
|
||||
// any error found here until after attempting to find a valid chain.
|
||||
// See the explanation of error prioritization in pkix.h.
|
||||
rv = CheckIssuerIndependentProperties(trustDomain, subject, time,
|
||||
requiredKeyUsageIfPresent,
|
||||
requiredEKUIfPresent, requiredPolicy,
|
||||
subCACount, trustLevel);
|
||||
Result deferredEndEntityError = Success;
|
||||
if (rv != Success) {
|
||||
if (subject.endEntityOrCA == EndEntityOrCA::MustBeEndEntity &&
|
||||
trustLevel != TrustLevel::TrustAnchor) {
|
||||
deferredEndEntityError = rv;
|
||||
} else {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
if (trustLevel == TrustLevel::TrustAnchor) {
|
||||
// End of the recursion.
|
||||
|
||||
NonOwningDERArray chain;
|
||||
for (const BackCert* cert = &subject; cert; cert = cert->childCert) {
|
||||
rv = chain.Append(cert->GetDER());
|
||||
if (rv != Success) {
|
||||
return NotReached("NonOwningDERArray::SetItem failed.", rv);
|
||||
}
|
||||
}
|
||||
|
||||
// This must be done here, after the chain is built but before any
|
||||
// revocation checks have been done.
|
||||
return trustDomain.IsChainValid(chain, time);
|
||||
}
|
||||
|
||||
if (subject.endEntityOrCA == EndEntityOrCA::MustBeCA) {
|
||||
// Avoid stack overflows and poor performance by limiting cert chain
|
||||
// length.
|
||||
static const unsigned int MAX_SUBCA_COUNT = 6;
|
||||
static_assert(1/*end-entity*/ + MAX_SUBCA_COUNT + 1/*root*/ ==
|
||||
NonOwningDERArray::MAX_LENGTH,
|
||||
"MAX_SUBCA_COUNT and NonOwningDERArray::MAX_LENGTH mismatch.");
|
||||
if (subCACount >= MAX_SUBCA_COUNT) {
|
||||
return Result::ERROR_UNKNOWN_ISSUER;
|
||||
}
|
||||
++subCACount;
|
||||
} else {
|
||||
assert(subCACount == 0);
|
||||
}
|
||||
|
||||
// Find a trusted issuer.
|
||||
|
||||
PathBuildingStep pathBuilder(trustDomain, subject, time,
|
||||
requiredEKUIfPresent, requiredPolicy,
|
||||
stapledOCSPResponse, subCACount,
|
||||
deferredEndEntityError);
|
||||
|
||||
// TODO(bug 965136): Add SKI/AKI matching optimizations
|
||||
rv = trustDomain.FindIssuer(subject.GetIssuer(), pathBuilder, time);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = pathBuilder.CheckResult();
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// If we found a valid chain but deferred reporting an error with the
|
||||
// end-entity certificate, report it now.
|
||||
if (deferredEndEntityError != Success) {
|
||||
return deferredEndEntityError;
|
||||
}
|
||||
|
||||
// We've built a valid chain from the subject cert up to a trusted root.
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result
|
||||
BuildCertChain(TrustDomain& trustDomain, Input certDER,
|
||||
Time time, EndEntityOrCA endEntityOrCA,
|
||||
KeyUsage requiredKeyUsageIfPresent,
|
||||
KeyPurposeId requiredEKUIfPresent,
|
||||
const CertPolicyId& requiredPolicy,
|
||||
/*optional*/ const Input* stapledOCSPResponse)
|
||||
{
|
||||
// XXX: Support the legacy use of the subject CN field for indicating the
|
||||
// domain name the certificate is valid for.
|
||||
BackCert cert(certDER, endEntityOrCA, nullptr);
|
||||
Result rv = cert.Init();
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return BuildForward(trustDomain, cert, time, requiredKeyUsageIfPresent,
|
||||
requiredEKUIfPresent, requiredPolicy, stapledOCSPResponse,
|
||||
0/*subCACount*/);
|
||||
}
|
||||
|
||||
} } // namespace mozilla::pkix
|
||||
323
security/pkix/lib/pkixcert.cpp
Normal file
323
security/pkix/lib/pkixcert.cpp
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
/* -*- 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;
|
||||
}
|
||||
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
|
||||
};
|
||||
// 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
|
||||
1088
security/pkix/lib/pkixcheck.cpp
Normal file
1088
security/pkix/lib/pkixcheck.cpp
Normal file
File diff suppressed because it is too large
Load diff
66
security/pkix/lib/pkixcheck.h
Normal file
66
security/pkix/lib/pkixcheck.h
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/* -*- 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 2013 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.
|
||||
*/
|
||||
|
||||
#ifndef mozilla_pkix_pkixcheck_h
|
||||
#define mozilla_pkix_pkixcheck_h
|
||||
|
||||
#include "pkix/pkixtypes.h"
|
||||
|
||||
namespace mozilla { namespace pkix {
|
||||
|
||||
class BackCert;
|
||||
|
||||
Result CheckIssuerIndependentProperties(
|
||||
TrustDomain& trustDomain,
|
||||
const BackCert& cert,
|
||||
Time time,
|
||||
KeyUsage requiredKeyUsageIfPresent,
|
||||
KeyPurposeId requiredEKUIfPresent,
|
||||
const CertPolicyId& requiredPolicy,
|
||||
unsigned int subCACount,
|
||||
/*out*/ TrustLevel& trustLevel);
|
||||
|
||||
Result CheckNameConstraints(Input encodedNameConstraints,
|
||||
const BackCert& firstChild,
|
||||
KeyPurposeId requiredEKUIfPresent);
|
||||
|
||||
Result CheckIssuer(Input encodedIssuer);
|
||||
|
||||
// ParseValidity and CheckValidity are usually used together. First you parse
|
||||
// the dates from the DER Validity sequence, then you compare them to the time
|
||||
// at which you are validating. They are separate so that the notBefore and
|
||||
// notAfter times can be used for other things before they are checked against
|
||||
// the time of validation.
|
||||
Result ParseValidity(Input encodedValidity,
|
||||
/*optional out*/ Time* notBeforeOut = nullptr,
|
||||
/*optional out*/ Time* notAfterOut = nullptr);
|
||||
Result CheckValidity(Time time, Time notBefore, Time notAfter);
|
||||
|
||||
// Check that a subject has TLS Feature (rfc7633) requirements that match its
|
||||
// potential issuer
|
||||
Result CheckTLSFeatures(const BackCert& subject, BackCert& potentialIssuer);
|
||||
|
||||
} } // namespace mozilla::pkix
|
||||
|
||||
#endif // mozilla_pkix_pkixcheck_h
|
||||
612
security/pkix/lib/pkixder.cpp
Normal file
612
security/pkix/lib/pkixder.cpp
Normal file
|
|
@ -0,0 +1,612 @@
|
|||
/* -*- 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 2013 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 "pkixder.h"
|
||||
|
||||
#include "pkixutil.h"
|
||||
|
||||
namespace mozilla { namespace pkix { namespace der {
|
||||
|
||||
// Too complicated to be inline
|
||||
Result
|
||||
ReadTagAndGetValue(Reader& input, /*out*/ uint8_t& tag, /*out*/ Input& value)
|
||||
{
|
||||
Result rv;
|
||||
|
||||
rv = input.Read(tag);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
if ((tag & 0x1F) == 0x1F) {
|
||||
return Result::ERROR_BAD_DER; // high tag number form not allowed
|
||||
}
|
||||
|
||||
uint16_t length;
|
||||
|
||||
// The short form of length is a single byte with the high order bit set
|
||||
// to zero. The long form of length is one byte with the high order bit
|
||||
// set, followed by N bytes, where N is encoded in the lowest 7 bits of
|
||||
// the first byte.
|
||||
uint8_t length1;
|
||||
rv = input.Read(length1);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
if (!(length1 & 0x80)) {
|
||||
length = length1;
|
||||
} else if (length1 == 0x81) {
|
||||
uint8_t length2;
|
||||
rv = input.Read(length2);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
if (length2 < 128) {
|
||||
// Not shortest possible encoding
|
||||
return Result::ERROR_BAD_DER;
|
||||
}
|
||||
length = length2;
|
||||
} else if (length1 == 0x82) {
|
||||
rv = input.Read(length);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
if (length < 256) {
|
||||
// Not shortest possible encoding
|
||||
return Result::ERROR_BAD_DER;
|
||||
}
|
||||
} else {
|
||||
// We don't support lengths larger than 2^16 - 1.
|
||||
return Result::ERROR_BAD_DER;
|
||||
}
|
||||
|
||||
return input.Skip(length, value);
|
||||
}
|
||||
|
||||
static Result
|
||||
OptionalNull(Reader& input)
|
||||
{
|
||||
if (input.Peek(NULLTag)) {
|
||||
return Null(input);
|
||||
}
|
||||
return Success;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
Result
|
||||
AlgorithmIdentifierValue(Reader& input, /*out*/ Reader& algorithmOIDValue)
|
||||
{
|
||||
Result rv = ExpectTagAndGetValue(input, der::OIDTag, algorithmOIDValue);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
return OptionalNull(input);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Result
|
||||
SignatureAlgorithmIdentifierValue(Reader& input,
|
||||
/*out*/ PublicKeyAlgorithm& publicKeyAlgorithm,
|
||||
/*out*/ DigestAlgorithm& digestAlgorithm)
|
||||
{
|
||||
// RFC 5758 Section 3.2 (ECDSA with SHA-2), and RFC 3279 Section 2.2.3
|
||||
// (ECDSA with SHA-1) say that parameters must be omitted.
|
||||
//
|
||||
// RFC 4055 Section 5 and RFC 3279 Section 2.2.1 both say that parameters for
|
||||
// RSA must be encoded as NULL; we relax that requirement by allowing the
|
||||
// NULL to be omitted, to match all the other signature algorithms we support
|
||||
// and for compatibility.
|
||||
Reader algorithmID;
|
||||
Result rv = AlgorithmIdentifierValue(input, algorithmID);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// RFC 5758 Section 3.2 (ecdsa-with-SHA224 is intentionally excluded)
|
||||
// python DottedOIDToCode.py ecdsa-with-SHA256 1.2.840.10045.4.3.2
|
||||
static const uint8_t ecdsa_with_SHA256[] = {
|
||||
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02
|
||||
};
|
||||
// python DottedOIDToCode.py ecdsa-with-SHA384 1.2.840.10045.4.3.3
|
||||
static const uint8_t ecdsa_with_SHA384[] = {
|
||||
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03
|
||||
};
|
||||
// python DottedOIDToCode.py ecdsa-with-SHA512 1.2.840.10045.4.3.4
|
||||
static const uint8_t ecdsa_with_SHA512[] = {
|
||||
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04
|
||||
};
|
||||
|
||||
// RFC 4055 Section 5 (sha224WithRSAEncryption is intentionally excluded)
|
||||
// python DottedOIDToCode.py sha256WithRSAEncryption 1.2.840.113549.1.1.11
|
||||
static const uint8_t sha256WithRSAEncryption[] = {
|
||||
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b
|
||||
};
|
||||
// python DottedOIDToCode.py sha384WithRSAEncryption 1.2.840.113549.1.1.12
|
||||
static const uint8_t sha384WithRSAEncryption[] = {
|
||||
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c
|
||||
};
|
||||
// python DottedOIDToCode.py sha512WithRSAEncryption 1.2.840.113549.1.1.13
|
||||
static const uint8_t sha512WithRSAEncryption[] = {
|
||||
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d
|
||||
};
|
||||
|
||||
// RFC 3279 Section 2.2.1
|
||||
// python DottedOIDToCode.py sha-1WithRSAEncryption 1.2.840.113549.1.1.5
|
||||
static const uint8_t sha_1WithRSAEncryption[] = {
|
||||
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05
|
||||
};
|
||||
|
||||
// NIST Open Systems Environment (OSE) Implementor's Workshop (OIW)
|
||||
// http://www.oiw.org/agreements/stable/12s-9412.txt (no longer works).
|
||||
// http://www.imc.org/ietf-pkix/old-archive-97/msg01166.html
|
||||
// We need to support this this non-PKIX OID for compatibility.
|
||||
// python DottedOIDToCode.py sha1WithRSASignature 1.3.14.3.2.29
|
||||
static const uint8_t sha1WithRSASignature[] = {
|
||||
0x2b, 0x0e, 0x03, 0x02, 0x1d
|
||||
};
|
||||
|
||||
// RFC 3279 Section 2.2.3
|
||||
// python DottedOIDToCode.py ecdsa-with-SHA1 1.2.840.10045.4.1
|
||||
static const uint8_t ecdsa_with_SHA1[] = {
|
||||
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01
|
||||
};
|
||||
|
||||
// Matching is attempted based on a rough estimate of the commonality of the
|
||||
// algorithm, to minimize the number of MatchRest calls.
|
||||
if (algorithmID.MatchRest(sha256WithRSAEncryption)) {
|
||||
publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1;
|
||||
digestAlgorithm = DigestAlgorithm::sha256;
|
||||
} else if (algorithmID.MatchRest(ecdsa_with_SHA256)) {
|
||||
publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA;
|
||||
digestAlgorithm = DigestAlgorithm::sha256;
|
||||
} else if (algorithmID.MatchRest(sha_1WithRSAEncryption)) {
|
||||
publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1;
|
||||
digestAlgorithm = DigestAlgorithm::sha1;
|
||||
} else if (algorithmID.MatchRest(ecdsa_with_SHA1)) {
|
||||
publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA;
|
||||
digestAlgorithm = DigestAlgorithm::sha1;
|
||||
} else if (algorithmID.MatchRest(ecdsa_with_SHA384)) {
|
||||
publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA;
|
||||
digestAlgorithm = DigestAlgorithm::sha384;
|
||||
} else if (algorithmID.MatchRest(ecdsa_with_SHA512)) {
|
||||
publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA;
|
||||
digestAlgorithm = DigestAlgorithm::sha512;
|
||||
} else if (algorithmID.MatchRest(sha384WithRSAEncryption)) {
|
||||
publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1;
|
||||
digestAlgorithm = DigestAlgorithm::sha384;
|
||||
} else if (algorithmID.MatchRest(sha512WithRSAEncryption)) {
|
||||
publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1;
|
||||
digestAlgorithm = DigestAlgorithm::sha512;
|
||||
} else if (algorithmID.MatchRest(sha1WithRSASignature)) {
|
||||
// XXX(bug 1042479): recognize this old OID for compatibility.
|
||||
publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1;
|
||||
digestAlgorithm = DigestAlgorithm::sha1;
|
||||
} else {
|
||||
return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result
|
||||
DigestAlgorithmIdentifier(Reader& input, /*out*/ DigestAlgorithm& algorithm)
|
||||
{
|
||||
Reader r;
|
||||
return der::Nested(input, SEQUENCE, [&algorithm](Reader& r) -> Result {
|
||||
Reader algorithmID;
|
||||
Result rv = AlgorithmIdentifierValue(r, algorithmID);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// RFC 4055 Section 2.1
|
||||
// python DottedOIDToCode.py id-sha1 1.3.14.3.2.26
|
||||
static const uint8_t id_sha1[] = {
|
||||
0x2b, 0x0e, 0x03, 0x02, 0x1a
|
||||
};
|
||||
// python DottedOIDToCode.py id-sha256 2.16.840.1.101.3.4.2.1
|
||||
static const uint8_t id_sha256[] = {
|
||||
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01
|
||||
};
|
||||
// python DottedOIDToCode.py id-sha384 2.16.840.1.101.3.4.2.2
|
||||
static const uint8_t id_sha384[] = {
|
||||
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02
|
||||
};
|
||||
// python DottedOIDToCode.py id-sha512 2.16.840.1.101.3.4.2.3
|
||||
static const uint8_t id_sha512[] = {
|
||||
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03
|
||||
};
|
||||
|
||||
// Matching is attempted based on a rough estimate of the commonality of the
|
||||
// algorithm, to minimize the number of MatchRest calls.
|
||||
if (algorithmID.MatchRest(id_sha1)) {
|
||||
algorithm = DigestAlgorithm::sha1;
|
||||
} else if (algorithmID.MatchRest(id_sha256)) {
|
||||
algorithm = DigestAlgorithm::sha256;
|
||||
} else if (algorithmID.MatchRest(id_sha384)) {
|
||||
algorithm = DigestAlgorithm::sha384;
|
||||
} else if (algorithmID.MatchRest(id_sha512)) {
|
||||
algorithm = DigestAlgorithm::sha512;
|
||||
} else {
|
||||
return Result::ERROR_INVALID_ALGORITHM;
|
||||
}
|
||||
|
||||
return Success;
|
||||
});
|
||||
}
|
||||
|
||||
Result
|
||||
SignedData(Reader& input, /*out*/ Reader& tbs,
|
||||
/*out*/ SignedDataWithSignature& signedData)
|
||||
{
|
||||
Reader::Mark mark(input.GetMark());
|
||||
|
||||
Result rv;
|
||||
rv = ExpectTagAndGetValue(input, SEQUENCE, tbs);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = input.GetInput(mark, signedData.data);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = ExpectTagAndGetValue(input, der::SEQUENCE, signedData.algorithm);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = BitStringWithNoUnusedBits(input, signedData.signature);
|
||||
if (rv == Result::ERROR_BAD_DER) {
|
||||
rv = Result::ERROR_BAD_SIGNATURE;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
Result
|
||||
BitStringWithNoUnusedBits(Reader& input, /*out*/ Input& value)
|
||||
{
|
||||
Reader valueWithUnusedBits;
|
||||
Result rv = ExpectTagAndGetValue(input, BIT_STRING, valueWithUnusedBits);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
uint8_t unusedBitsAtEnd;
|
||||
if (valueWithUnusedBits.Read(unusedBitsAtEnd) != Success) {
|
||||
return Result::ERROR_BAD_DER;
|
||||
}
|
||||
// XXX: Really the constraint should be that unusedBitsAtEnd must be less
|
||||
// than 7. But, we suspect there are no real-world values in OCSP responses
|
||||
// or certificates with non-zero unused bits. It seems like NSS assumes this
|
||||
// in various places, so we enforce it too in order to simplify this code. If
|
||||
// we find compatibility issues, we'll know we're wrong and we'll have to
|
||||
// figure out how to shift the bits around.
|
||||
if (unusedBitsAtEnd != 0) {
|
||||
return Result::ERROR_BAD_DER;
|
||||
}
|
||||
return valueWithUnusedBits.SkipToEnd(value);
|
||||
}
|
||||
|
||||
static inline Result
|
||||
ReadDigit(Reader& input, /*out*/ unsigned int& value)
|
||||
{
|
||||
uint8_t b;
|
||||
if (input.Read(b) != Success) {
|
||||
return Result::ERROR_INVALID_DER_TIME;
|
||||
}
|
||||
if (b < '0' || b > '9') {
|
||||
return Result::ERROR_INVALID_DER_TIME;
|
||||
}
|
||||
value = static_cast<unsigned int>(b - static_cast<uint8_t>('0'));
|
||||
return Success;
|
||||
}
|
||||
|
||||
static inline Result
|
||||
ReadTwoDigits(Reader& input, unsigned int minValue, unsigned int maxValue,
|
||||
/*out*/ unsigned int& value)
|
||||
{
|
||||
unsigned int hi;
|
||||
Result rv = ReadDigit(input, hi);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
unsigned int lo;
|
||||
rv = ReadDigit(input, lo);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
value = (hi * 10) + lo;
|
||||
if (value < minValue || value > maxValue) {
|
||||
return Result::ERROR_INVALID_DER_TIME;
|
||||
}
|
||||
return Success;
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
// We parse GeneralizedTime and UTCTime according to RFC 5280 and we do not
|
||||
// accept all time formats allowed in the ASN.1 spec. That is,
|
||||
// GeneralizedTime must always be in the format YYYYMMDDHHMMSSZ and UTCTime
|
||||
// must always be in the format YYMMDDHHMMSSZ. Timezone formats of the form
|
||||
// +HH:MM or -HH:MM or NOT accepted.
|
||||
Result
|
||||
TimeChoice(Reader& tagged, uint8_t expectedTag, /*out*/ Time& time)
|
||||
{
|
||||
unsigned int days;
|
||||
|
||||
Reader input;
|
||||
Result rv = ExpectTagAndGetValue(tagged, expectedTag, input);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
unsigned int yearHi;
|
||||
unsigned int yearLo;
|
||||
if (expectedTag == GENERALIZED_TIME) {
|
||||
rv = ReadTwoDigits(input, 0, 99, yearHi);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
rv = ReadTwoDigits(input, 0, 99, yearLo);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
} else if (expectedTag == UTCTime) {
|
||||
rv = ReadTwoDigits(input, 0, 99, yearLo);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
yearHi = yearLo >= 50u ? 19u : 20u;
|
||||
} else {
|
||||
return NotReached("invalid tag given to TimeChoice",
|
||||
Result::ERROR_INVALID_DER_TIME);
|
||||
}
|
||||
unsigned int year = (yearHi * 100u) + yearLo;
|
||||
if (year < 1970u) {
|
||||
// We don't support dates before January 1, 1970 because that is the epoch.
|
||||
return Result::ERROR_INVALID_DER_TIME;
|
||||
}
|
||||
days = DaysBeforeYear(year);
|
||||
|
||||
unsigned int month;
|
||||
rv = ReadTwoDigits(input, 1u, 12u, month);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
unsigned int daysInMonth;
|
||||
static const unsigned int jan = 31u;
|
||||
const unsigned int feb = ((year % 4u == 0u) &&
|
||||
((year % 100u != 0u) || (year % 400u == 0u)))
|
||||
? 29u
|
||||
: 28u;
|
||||
static const unsigned int mar = 31u;
|
||||
static const unsigned int apr = 30u;
|
||||
static const unsigned int may = 31u;
|
||||
static const unsigned int jun = 30u;
|
||||
static const unsigned int jul = 31u;
|
||||
static const unsigned int aug = 31u;
|
||||
static const unsigned int sep = 30u;
|
||||
static const unsigned int oct = 31u;
|
||||
static const unsigned int nov = 30u;
|
||||
static const unsigned int dec = 31u;
|
||||
switch (month) {
|
||||
case 1: daysInMonth = jan; break;
|
||||
case 2: daysInMonth = feb; days += jan; break;
|
||||
case 3: daysInMonth = mar; days += jan + feb; break;
|
||||
case 4: daysInMonth = apr; days += jan + feb + mar; break;
|
||||
case 5: daysInMonth = may; days += jan + feb + mar + apr; break;
|
||||
case 6: daysInMonth = jun; days += jan + feb + mar + apr + may; break;
|
||||
case 7: daysInMonth = jul; days += jan + feb + mar + apr + may + jun;
|
||||
break;
|
||||
case 8: daysInMonth = aug; days += jan + feb + mar + apr + may + jun +
|
||||
jul;
|
||||
break;
|
||||
case 9: daysInMonth = sep; days += jan + feb + mar + apr + may + jun +
|
||||
jul + aug;
|
||||
break;
|
||||
case 10: daysInMonth = oct; days += jan + feb + mar + apr + may + jun +
|
||||
jul + aug + sep;
|
||||
break;
|
||||
case 11: daysInMonth = nov; days += jan + feb + mar + apr + may + jun +
|
||||
jul + aug + sep + oct;
|
||||
break;
|
||||
case 12: daysInMonth = dec; days += jan + feb + mar + apr + may + jun +
|
||||
jul + aug + sep + oct + nov;
|
||||
break;
|
||||
default:
|
||||
return NotReached("month already bounds-checked by ReadTwoDigits",
|
||||
Result::FATAL_ERROR_INVALID_STATE);
|
||||
}
|
||||
|
||||
unsigned int dayOfMonth;
|
||||
rv = ReadTwoDigits(input, 1u, daysInMonth, dayOfMonth);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
days += dayOfMonth - 1;
|
||||
|
||||
unsigned int hours;
|
||||
rv = ReadTwoDigits(input, 0u, 23u, hours);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
unsigned int minutes;
|
||||
rv = ReadTwoDigits(input, 0u, 59u, minutes);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
unsigned int seconds;
|
||||
rv = ReadTwoDigits(input, 0u, 59u, seconds);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
uint8_t b;
|
||||
if (input.Read(b) != Success) {
|
||||
return Result::ERROR_INVALID_DER_TIME;
|
||||
}
|
||||
if (b != 'Z') {
|
||||
return Result::ERROR_INVALID_DER_TIME;
|
||||
}
|
||||
if (End(input) != Success) {
|
||||
return Result::ERROR_INVALID_DER_TIME;
|
||||
}
|
||||
|
||||
uint64_t totalSeconds = (static_cast<uint64_t>(days) * 24u * 60u * 60u) +
|
||||
(static_cast<uint64_t>(hours) * 60u * 60u) +
|
||||
(static_cast<uint64_t>(minutes) * 60u) +
|
||||
seconds;
|
||||
|
||||
time = TimeFromElapsedSecondsAD(totalSeconds);
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result
|
||||
IntegralBytes(Reader& input, uint8_t tag,
|
||||
IntegralValueRestriction valueRestriction,
|
||||
/*out*/ Input& value,
|
||||
/*optional out*/ Input::size_type* significantBytes)
|
||||
{
|
||||
Result rv = ExpectTagAndGetValue(input, tag, value);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
Reader reader(value);
|
||||
|
||||
// There must be at least one byte in the value. (Zero is encoded with a
|
||||
// single 0x00 value byte.)
|
||||
uint8_t firstByte;
|
||||
rv = reader.Read(firstByte);
|
||||
if (rv != Success) {
|
||||
if (rv == Result::ERROR_BAD_DER) {
|
||||
return Result::ERROR_INVALID_INTEGER_ENCODING;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
// If there is a byte after an initial 0x00/0xFF, then the initial byte
|
||||
// indicates a positive/negative integer value with its high bit set/unset.
|
||||
bool prefixed = !reader.AtEnd() && (firstByte == 0 || firstByte == 0xff);
|
||||
|
||||
if (prefixed) {
|
||||
uint8_t nextByte;
|
||||
if (reader.Read(nextByte) != Success) {
|
||||
return NotReached("Read of one byte failed but not at end.",
|
||||
Result::FATAL_ERROR_LIBRARY_FAILURE);
|
||||
}
|
||||
if ((firstByte & 0x80) == (nextByte & 0x80)) {
|
||||
return Result::ERROR_INVALID_INTEGER_ENCODING;
|
||||
}
|
||||
}
|
||||
|
||||
switch (valueRestriction) {
|
||||
case IntegralValueRestriction::MustBe0To127:
|
||||
if (value.GetLength() != 1 || (firstByte & 0x80) != 0) {
|
||||
return Result::ERROR_INVALID_INTEGER_ENCODING;
|
||||
}
|
||||
break;
|
||||
|
||||
case IntegralValueRestriction::MustBePositive:
|
||||
if ((value.GetLength() == 1 && firstByte == 0) ||
|
||||
(firstByte & 0x80) != 0) {
|
||||
return Result::ERROR_INVALID_INTEGER_ENCODING;
|
||||
}
|
||||
break;
|
||||
|
||||
case IntegralValueRestriction::NoRestriction:
|
||||
break;
|
||||
}
|
||||
|
||||
if (significantBytes) {
|
||||
*significantBytes = value.GetLength();
|
||||
if (prefixed) {
|
||||
assert(*significantBytes > 1);
|
||||
--*significantBytes;
|
||||
}
|
||||
|
||||
assert(*significantBytes > 0);
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
// This parser will only parse values between 0..127. If this range is
|
||||
// increased then callers will need to be changed.
|
||||
Result
|
||||
IntegralValue(Reader& input, uint8_t tag, /*out*/ uint8_t& value)
|
||||
{
|
||||
// Conveniently, all the Integers that we actually have to be able to parse
|
||||
// are positive and very small. Consequently, this parser is *much* simpler
|
||||
// than a general Integer parser would need to be.
|
||||
Input valueBytes;
|
||||
Result rv = IntegralBytes(input, tag, IntegralValueRestriction::MustBe0To127,
|
||||
valueBytes, nullptr);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
Reader valueReader(valueBytes);
|
||||
rv = valueReader.Read(value);
|
||||
if (rv != Success) {
|
||||
return NotReached("IntegralBytes already validated the value.", rv);
|
||||
}
|
||||
rv = End(valueReader);
|
||||
assert(rv == Success); // guaranteed by IntegralBytes's range checks.
|
||||
return rv;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
Result
|
||||
OptionalVersion(Reader& input, /*out*/ Version& version)
|
||||
{
|
||||
static const uint8_t TAG = CONTEXT_SPECIFIC | CONSTRUCTED | 0;
|
||||
if (!input.Peek(TAG)) {
|
||||
version = Version::v1;
|
||||
return Success;
|
||||
}
|
||||
return Nested(input, TAG, [&version](Reader& value) -> Result {
|
||||
uint8_t integerValue;
|
||||
Result rv = Integer(value, integerValue);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
// XXX(bug 1031093): We shouldn't accept an explicit encoding of v1,
|
||||
// but we do here for compatibility reasons.
|
||||
switch (integerValue) {
|
||||
case static_cast<uint8_t>(Version::v3): version = Version::v3; break;
|
||||
case static_cast<uint8_t>(Version::v2): version = Version::v2; break;
|
||||
case static_cast<uint8_t>(Version::v1): version = Version::v1; break;
|
||||
case static_cast<uint8_t>(Version::v4): version = Version::v4; break;
|
||||
default:
|
||||
return Result::ERROR_BAD_DER;
|
||||
}
|
||||
return Success;
|
||||
});
|
||||
}
|
||||
|
||||
} } } // namespace mozilla::pkix::der
|
||||
565
security/pkix/lib/pkixder.h
Normal file
565
security/pkix/lib/pkixder.h
Normal file
|
|
@ -0,0 +1,565 @@
|
|||
/* -*- 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 2013 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.
|
||||
*/
|
||||
|
||||
#ifndef mozilla_pkix_pkixder_h
|
||||
#define mozilla_pkix_pkixder_h
|
||||
|
||||
// Expect* functions advance the input mark and return Success if the input
|
||||
// matches the given criteria; they fail with the input mark in an undefined
|
||||
// state if the input does not match the criteria.
|
||||
//
|
||||
// Match* functions advance the input mark and return true if the input matches
|
||||
// the given criteria; they return false without changing the input mark if the
|
||||
// input does not match the criteria.
|
||||
//
|
||||
// Skip* functions unconditionally advance the input mark and return Success if
|
||||
// they are able to do so; otherwise they fail with the input mark in an
|
||||
// undefined state.
|
||||
|
||||
#include "pkix/Input.h"
|
||||
#include "pkix/pkixtypes.h"
|
||||
|
||||
namespace mozilla { namespace pkix { namespace der {
|
||||
|
||||
enum Class : uint8_t
|
||||
{
|
||||
UNIVERSAL = 0 << 6,
|
||||
// APPLICATION = 1 << 6, // unused
|
||||
CONTEXT_SPECIFIC = 2 << 6,
|
||||
// PRIVATE = 3 << 6 // unused
|
||||
};
|
||||
|
||||
enum Constructed
|
||||
{
|
||||
CONSTRUCTED = 1 << 5
|
||||
};
|
||||
|
||||
enum Tag : uint8_t
|
||||
{
|
||||
BOOLEAN = UNIVERSAL | 0x01,
|
||||
INTEGER = UNIVERSAL | 0x02,
|
||||
BIT_STRING = UNIVERSAL | 0x03,
|
||||
OCTET_STRING = UNIVERSAL | 0x04,
|
||||
NULLTag = UNIVERSAL | 0x05,
|
||||
OIDTag = UNIVERSAL | 0x06,
|
||||
ENUMERATED = UNIVERSAL | 0x0a,
|
||||
UTF8String = UNIVERSAL | 0x0c,
|
||||
SEQUENCE = UNIVERSAL | CONSTRUCTED | 0x10, // 0x30
|
||||
SET = UNIVERSAL | CONSTRUCTED | 0x11, // 0x31
|
||||
PrintableString = UNIVERSAL | 0x13,
|
||||
TeletexString = UNIVERSAL | 0x14,
|
||||
IA5String = UNIVERSAL | 0x16,
|
||||
UTCTime = UNIVERSAL | 0x17,
|
||||
GENERALIZED_TIME = UNIVERSAL | 0x18,
|
||||
};
|
||||
|
||||
enum class EmptyAllowed { No = 0, Yes = 1 };
|
||||
|
||||
Result ReadTagAndGetValue(Reader& input, /*out*/ uint8_t& tag,
|
||||
/*out*/ Input& value);
|
||||
Result End(Reader& input);
|
||||
|
||||
inline Result
|
||||
ExpectTagAndGetValue(Reader& input, uint8_t tag, /*out*/ Input& value)
|
||||
{
|
||||
uint8_t actualTag;
|
||||
Result rv = ReadTagAndGetValue(input, actualTag, value);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
if (tag != actualTag) {
|
||||
return Result::ERROR_BAD_DER;
|
||||
}
|
||||
return Success;
|
||||
}
|
||||
|
||||
inline Result
|
||||
ExpectTagAndGetValue(Reader& input, uint8_t tag, /*out*/ Reader& value)
|
||||
{
|
||||
Input valueInput;
|
||||
Result rv = ExpectTagAndGetValue(input, tag, valueInput);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
return value.Init(valueInput);
|
||||
}
|
||||
|
||||
inline Result
|
||||
ExpectTagAndEmptyValue(Reader& input, uint8_t tag)
|
||||
{
|
||||
Reader value;
|
||||
Result rv = ExpectTagAndGetValue(input, tag, value);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
return End(value);
|
||||
}
|
||||
|
||||
inline Result
|
||||
ExpectTagAndSkipValue(Reader& input, uint8_t tag)
|
||||
{
|
||||
Input ignoredValue;
|
||||
return ExpectTagAndGetValue(input, tag, ignoredValue);
|
||||
}
|
||||
|
||||
// Like ExpectTagAndGetValue, except the output Input will contain the
|
||||
// encoded tag and length along with the value.
|
||||
inline Result
|
||||
ExpectTagAndGetTLV(Reader& input, uint8_t tag, /*out*/ Input& tlv)
|
||||
{
|
||||
Reader::Mark mark(input.GetMark());
|
||||
Result rv = ExpectTagAndSkipValue(input, tag);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
return input.GetInput(mark, tlv);
|
||||
}
|
||||
|
||||
inline Result
|
||||
End(Reader& input)
|
||||
{
|
||||
if (!input.AtEnd()) {
|
||||
return Result::ERROR_BAD_DER;
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
template <typename Decoder>
|
||||
inline Result
|
||||
Nested(Reader& input, uint8_t tag, Decoder decoder)
|
||||
{
|
||||
Reader nested;
|
||||
Result rv = ExpectTagAndGetValue(input, tag, nested);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
rv = decoder(nested);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
return End(nested);
|
||||
}
|
||||
|
||||
template <typename Decoder>
|
||||
inline Result
|
||||
Nested(Reader& input, uint8_t outerTag, uint8_t innerTag, Decoder decoder)
|
||||
{
|
||||
Reader nestedInput;
|
||||
Result rv = ExpectTagAndGetValue(input, outerTag, nestedInput);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
rv = Nested(nestedInput, innerTag, decoder);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
return End(nestedInput);
|
||||
}
|
||||
|
||||
// This can be used to decode constructs like this:
|
||||
//
|
||||
// ...
|
||||
// foos SEQUENCE OF Foo,
|
||||
// ...
|
||||
// Foo ::= SEQUENCE {
|
||||
// }
|
||||
//
|
||||
// using code like this:
|
||||
//
|
||||
// Result Foo(Reader& r) { /*...*/ }
|
||||
//
|
||||
// rv = der::NestedOf(input, der::SEQEUENCE, der::SEQUENCE, Foo);
|
||||
//
|
||||
// or:
|
||||
//
|
||||
// Result Bar(Reader& r, int value) { /*...*/ }
|
||||
//
|
||||
// int value = /*...*/;
|
||||
//
|
||||
// rv = der::NestedOf(input, der::SEQUENCE, [value](Reader& r) {
|
||||
// return Bar(r, value);
|
||||
// });
|
||||
//
|
||||
// In these examples the function will get called once for each element of
|
||||
// foos.
|
||||
//
|
||||
template <typename Decoder>
|
||||
inline Result
|
||||
NestedOf(Reader& input, uint8_t outerTag, uint8_t innerTag,
|
||||
EmptyAllowed mayBeEmpty, Decoder decoder)
|
||||
{
|
||||
Reader inner;
|
||||
Result rv = ExpectTagAndGetValue(input, outerTag, inner);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (inner.AtEnd()) {
|
||||
if (mayBeEmpty != EmptyAllowed::Yes) {
|
||||
return Result::ERROR_BAD_DER;
|
||||
}
|
||||
return Success;
|
||||
}
|
||||
|
||||
do {
|
||||
rv = Nested(inner, innerTag, decoder);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
} while (!inner.AtEnd());
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
// Often, a function will need to decode an Input or Reader that contains
|
||||
// DER-encoded data wrapped in a SEQUENCE (or similar) with nothing after it.
|
||||
// This function reduces the boilerplate necessary for stripping the outermost
|
||||
// SEQUENCE (or similar) and ensuring that nothing follows it.
|
||||
inline Result
|
||||
ExpectTagAndGetValueAtEnd(Reader& outer, uint8_t expectedTag,
|
||||
/*out*/ Reader& inner)
|
||||
{
|
||||
Result rv = der::ExpectTagAndGetValue(outer, expectedTag, inner);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
return der::End(outer);
|
||||
}
|
||||
|
||||
// Similar to the above, but takes an Input instead of a Reader&.
|
||||
inline Result
|
||||
ExpectTagAndGetValueAtEnd(Input outer, uint8_t expectedTag,
|
||||
/*out*/ Reader& inner)
|
||||
{
|
||||
Reader outerReader(outer);
|
||||
return ExpectTagAndGetValueAtEnd(outerReader, expectedTag, inner);
|
||||
}
|
||||
|
||||
// Universal types
|
||||
|
||||
namespace internal {
|
||||
|
||||
enum class IntegralValueRestriction
|
||||
{
|
||||
NoRestriction,
|
||||
MustBePositive,
|
||||
MustBe0To127,
|
||||
};
|
||||
|
||||
Result IntegralBytes(Reader& input, uint8_t tag,
|
||||
IntegralValueRestriction valueRestriction,
|
||||
/*out*/ Input& value,
|
||||
/*optional out*/ Input::size_type* significantBytes = nullptr);
|
||||
|
||||
// This parser will only parse values between 0..127. If this range is
|
||||
// increased then callers will need to be changed.
|
||||
Result IntegralValue(Reader& input, uint8_t tag, /*out*/ uint8_t& value);
|
||||
|
||||
} // namespace internal
|
||||
|
||||
Result
|
||||
BitStringWithNoUnusedBits(Reader& input, /*out*/ Input& value);
|
||||
|
||||
inline Result
|
||||
Boolean(Reader& input, /*out*/ bool& value)
|
||||
{
|
||||
Reader valueReader;
|
||||
Result rv = ExpectTagAndGetValue(input, BOOLEAN, valueReader);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
uint8_t intValue;
|
||||
rv = valueReader.Read(intValue);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
rv = End(valueReader);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
switch (intValue) {
|
||||
case 0: value = false; return Success;
|
||||
case 0xFF: value = true; return Success;
|
||||
default:
|
||||
return Result::ERROR_BAD_DER;
|
||||
}
|
||||
}
|
||||
|
||||
// This is for BOOLEAN DEFAULT FALSE.
|
||||
// The standard stipulates that "The encoding of a set value or sequence value
|
||||
// shall not include an encoding for any component value which is equal to its
|
||||
// default value." However, it appears to be common that other libraries
|
||||
// incorrectly include the value of a BOOLEAN even when it's equal to the
|
||||
// default value, so we allow invalid explicit encodings here.
|
||||
inline Result
|
||||
OptionalBoolean(Reader& input, /*out*/ bool& value)
|
||||
{
|
||||
value = false;
|
||||
if (input.Peek(BOOLEAN)) {
|
||||
Result rv = Boolean(input, value);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
return Success;
|
||||
}
|
||||
|
||||
// This parser will only parse values between 0..127. If this range is
|
||||
// increased then callers will need to be changed.
|
||||
inline Result
|
||||
Enumerated(Reader& input, uint8_t& value)
|
||||
{
|
||||
return internal::IntegralValue(input, ENUMERATED | 0, value);
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
// internal::TimeChoice implements the shared functionality of GeneralizedTime
|
||||
// and TimeChoice. tag must be either UTCTime or GENERALIZED_TIME.
|
||||
//
|
||||
// Only times from 1970-01-01-00:00:00 onward are accepted, in order to
|
||||
// eliminate the chance for complications in converting times to traditional
|
||||
// time formats that start at 1970.
|
||||
Result TimeChoice(Reader& input, uint8_t tag, /*out*/ Time& time);
|
||||
|
||||
} // namespace internal
|
||||
|
||||
// Only times from 1970-01-01-00:00:00 onward are accepted, in order to
|
||||
// eliminate the chance for complications in converting times to traditional
|
||||
// time formats that start at 1970.
|
||||
inline Result
|
||||
GeneralizedTime(Reader& input, /*out*/ Time& time)
|
||||
{
|
||||
return internal::TimeChoice(input, GENERALIZED_TIME, time);
|
||||
}
|
||||
|
||||
// Only times from 1970-01-01-00:00:00 onward are accepted, in order to
|
||||
// eliminate the chance for complications in converting times to traditional
|
||||
// time formats that start at 1970.
|
||||
inline Result
|
||||
TimeChoice(Reader& input, /*out*/ Time& time)
|
||||
{
|
||||
uint8_t expectedTag = input.Peek(UTCTime) ? UTCTime : GENERALIZED_TIME;
|
||||
return internal::TimeChoice(input, expectedTag, time);
|
||||
}
|
||||
|
||||
// Parse a DER integer value into value. Empty values, negative values, and
|
||||
// zero are rejected. If significantBytes is not null, then it will be set to
|
||||
// the number of significant bytes in the value (the length of the value, less
|
||||
// the length of any leading padding), which is useful for key size checks.
|
||||
inline Result
|
||||
PositiveInteger(Reader& input, /*out*/ Input& value,
|
||||
/*optional out*/ Input::size_type* significantBytes = nullptr)
|
||||
{
|
||||
return internal::IntegralBytes(
|
||||
input, INTEGER, internal::IntegralValueRestriction::MustBePositive,
|
||||
value, significantBytes);
|
||||
}
|
||||
|
||||
// This parser will only parse values between 0..127. If this range is
|
||||
// increased then callers will need to be changed.
|
||||
inline Result
|
||||
Integer(Reader& input, /*out*/ uint8_t& value)
|
||||
{
|
||||
return internal::IntegralValue(input, INTEGER, value);
|
||||
}
|
||||
|
||||
// This parser will only parse values between 0..127. If this range is
|
||||
// increased then callers will need to be changed. The default value must be
|
||||
// -1; defaultValue is only a parameter to make it clear in the calling code
|
||||
// what the default value is.
|
||||
inline Result
|
||||
OptionalInteger(Reader& input, long defaultValue, /*out*/ long& value)
|
||||
{
|
||||
// If we need to support a different default value in the future, we need to
|
||||
// test that parsedValue != defaultValue.
|
||||
if (defaultValue != -1) {
|
||||
return Result::FATAL_ERROR_INVALID_ARGS;
|
||||
}
|
||||
|
||||
if (!input.Peek(INTEGER)) {
|
||||
value = defaultValue;
|
||||
return Success;
|
||||
}
|
||||
|
||||
uint8_t parsedValue;
|
||||
Result rv = Integer(input, parsedValue);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
value = parsedValue;
|
||||
return Success;
|
||||
}
|
||||
|
||||
inline Result
|
||||
Null(Reader& input)
|
||||
{
|
||||
return ExpectTagAndEmptyValue(input, NULLTag);
|
||||
}
|
||||
|
||||
template <uint8_t Len>
|
||||
Result
|
||||
OID(Reader& input, const uint8_t (&expectedOid)[Len])
|
||||
{
|
||||
Reader value;
|
||||
Result rv = ExpectTagAndGetValue(input, OIDTag, value);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
if (!value.MatchRest(expectedOid)) {
|
||||
return Result::ERROR_BAD_DER;
|
||||
}
|
||||
return Success;
|
||||
}
|
||||
|
||||
// PKI-specific types
|
||||
|
||||
inline Result
|
||||
CertificateSerialNumber(Reader& input, /*out*/ Input& value)
|
||||
{
|
||||
// http://tools.ietf.org/html/rfc5280#section-4.1.2.2:
|
||||
//
|
||||
// * "The serial number MUST be a positive integer assigned by the CA to
|
||||
// each certificate."
|
||||
// * "Certificate users MUST be able to handle serialNumber values up to 20
|
||||
// octets. Conforming CAs MUST NOT use serialNumber values longer than 20
|
||||
// octets."
|
||||
// * "Note: Non-conforming CAs may issue certificates with serial numbers
|
||||
// that are negative or zero. Certificate users SHOULD be prepared to
|
||||
// gracefully handle such certificates."
|
||||
return internal::IntegralBytes(
|
||||
input, INTEGER, internal::IntegralValueRestriction::NoRestriction,
|
||||
value);
|
||||
}
|
||||
|
||||
// x.509 and OCSP both use this same version numbering scheme, though OCSP
|
||||
// only supports v1.
|
||||
enum class Version { v1 = 0, v2 = 1, v3 = 2, v4 = 3 };
|
||||
|
||||
// X.509 Certificate and OCSP ResponseData both use
|
||||
// "[0] EXPLICIT Version DEFAULT v1". Although an explicit encoding of v1 is
|
||||
// illegal, we support it because some real-world OCSP responses explicitly
|
||||
// encode it.
|
||||
Result OptionalVersion(Reader& input, /*out*/ Version& version);
|
||||
|
||||
template <typename ExtensionHandler>
|
||||
inline Result
|
||||
OptionalExtensions(Reader& input, uint8_t tag,
|
||||
ExtensionHandler extensionHandler)
|
||||
{
|
||||
if (!input.Peek(tag)) {
|
||||
return Success;
|
||||
}
|
||||
|
||||
return Nested(input, tag, [extensionHandler](Reader& tagged) {
|
||||
// Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
|
||||
//
|
||||
// TODO(bug 997994): According to the specification, there should never be
|
||||
// an empty sequence of extensions but we've found OCSP responses that have
|
||||
// that (see bug 991898).
|
||||
return NestedOf(tagged, SEQUENCE, SEQUENCE, EmptyAllowed::Yes,
|
||||
[extensionHandler](Reader& extension) -> Result {
|
||||
// Extension ::= SEQUENCE {
|
||||
// extnID OBJECT IDENTIFIER,
|
||||
// critical BOOLEAN DEFAULT FALSE,
|
||||
// extnValue OCTET STRING
|
||||
// }
|
||||
Reader extnID;
|
||||
Result rv = ExpectTagAndGetValue(extension, OIDTag, extnID);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
bool critical;
|
||||
rv = OptionalBoolean(extension, critical);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
Input extnValue;
|
||||
rv = ExpectTagAndGetValue(extension, OCTET_STRING, extnValue);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
bool understood = false;
|
||||
rv = extensionHandler(extnID, extnValue, critical, understood);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
if (critical && !understood) {
|
||||
return Result::ERROR_UNKNOWN_CRITICAL_EXTENSION;
|
||||
}
|
||||
return Success;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Result DigestAlgorithmIdentifier(Reader& input,
|
||||
/*out*/ DigestAlgorithm& algorithm);
|
||||
|
||||
enum class PublicKeyAlgorithm
|
||||
{
|
||||
RSA_PKCS1,
|
||||
ECDSA,
|
||||
};
|
||||
|
||||
Result SignatureAlgorithmIdentifierValue(
|
||||
Reader& input,
|
||||
/*out*/ PublicKeyAlgorithm& publicKeyAlgorithm,
|
||||
/*out*/ DigestAlgorithm& digestAlgorithm);
|
||||
|
||||
struct SignedDataWithSignature final
|
||||
{
|
||||
public:
|
||||
Input data;
|
||||
Input algorithm;
|
||||
Input signature;
|
||||
|
||||
void operator=(const SignedDataWithSignature&) = delete;
|
||||
};
|
||||
|
||||
// Parses a SEQUENCE into tbs and then parses an AlgorithmIdentifier followed
|
||||
// by a BIT STRING into signedData. This handles the commonality between
|
||||
// parsing the signed/signature fields of certificates and OCSP responses. In
|
||||
// the case of an OCSP response, the caller needs to parse the certs
|
||||
// separately.
|
||||
//
|
||||
// Note that signatureAlgorithm is NOT parsed or validated.
|
||||
//
|
||||
// Certificate ::= SEQUENCE {
|
||||
// tbsCertificate TBSCertificate,
|
||||
// signatureAlgorithm AlgorithmIdentifier,
|
||||
// signatureValue BIT STRING }
|
||||
//
|
||||
// BasicOCSPResponse ::= SEQUENCE {
|
||||
// tbsResponseData ResponseData,
|
||||
// signatureAlgorithm AlgorithmIdentifier,
|
||||
// signature BIT STRING,
|
||||
// certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
|
||||
Result SignedData(Reader& input, /*out*/ Reader& tbs,
|
||||
/*out*/ SignedDataWithSignature& signedDataWithSignature);
|
||||
|
||||
} } } // namespace mozilla::pkix::der
|
||||
|
||||
#endif // mozilla_pkix_pkixder_h
|
||||
2050
security/pkix/lib/pkixnames.cpp
Normal file
2050
security/pkix/lib/pkixnames.cpp
Normal file
File diff suppressed because it is too large
Load diff
228
security/pkix/lib/pkixnss.cpp
Normal file
228
security/pkix/lib/pkixnss.cpp
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
/*- *- 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 2013 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 "pkix/pkixnss.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "cryptohi.h"
|
||||
#include "keyhi.h"
|
||||
#include "pk11pub.h"
|
||||
#include "pkix/pkix.h"
|
||||
#include "pkixutil.h"
|
||||
#include "ScopedPtr.h"
|
||||
#include "secerr.h"
|
||||
#include "sslerr.h"
|
||||
|
||||
namespace mozilla { namespace pkix {
|
||||
|
||||
namespace {
|
||||
|
||||
Result
|
||||
VerifySignedDigest(const SignedDigest& sd,
|
||||
Input subjectPublicKeyInfo,
|
||||
SECOidTag pubKeyAlg,
|
||||
void* pkcs11PinArg)
|
||||
{
|
||||
SECOidTag digestAlg;
|
||||
switch (sd.digestAlgorithm) {
|
||||
case DigestAlgorithm::sha512: digestAlg = SEC_OID_SHA512; break;
|
||||
case DigestAlgorithm::sha384: digestAlg = SEC_OID_SHA384; break;
|
||||
case DigestAlgorithm::sha256: digestAlg = SEC_OID_SHA256; break;
|
||||
case DigestAlgorithm::sha1: digestAlg = SEC_OID_SHA1; break;
|
||||
MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
|
||||
}
|
||||
|
||||
SECItem subjectPublicKeyInfoSECItem =
|
||||
UnsafeMapInputToSECItem(subjectPublicKeyInfo);
|
||||
ScopedPtr<CERTSubjectPublicKeyInfo, SECKEY_DestroySubjectPublicKeyInfo>
|
||||
spki(SECKEY_DecodeDERSubjectPublicKeyInfo(&subjectPublicKeyInfoSECItem));
|
||||
if (!spki) {
|
||||
return MapPRErrorCodeToResult(PR_GetError());
|
||||
}
|
||||
ScopedPtr<SECKEYPublicKey, SECKEY_DestroyPublicKey>
|
||||
pubKey(SECKEY_ExtractPublicKey(spki.get()));
|
||||
if (!pubKey) {
|
||||
return MapPRErrorCodeToResult(PR_GetError());
|
||||
}
|
||||
|
||||
SECItem digestSECItem(UnsafeMapInputToSECItem(sd.digest));
|
||||
SECItem signatureSECItem(UnsafeMapInputToSECItem(sd.signature));
|
||||
SECStatus srv = VFY_VerifyDigestDirect(&digestSECItem, pubKey.get(),
|
||||
&signatureSECItem, pubKeyAlg,
|
||||
digestAlg, pkcs11PinArg);
|
||||
if (srv != SECSuccess) {
|
||||
return MapPRErrorCodeToResult(PR_GetError());
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Result
|
||||
VerifyRSAPKCS1SignedDigestNSS(const SignedDigest& sd,
|
||||
Input subjectPublicKeyInfo,
|
||||
void* pkcs11PinArg)
|
||||
{
|
||||
return VerifySignedDigest(sd, subjectPublicKeyInfo,
|
||||
SEC_OID_PKCS1_RSA_ENCRYPTION, pkcs11PinArg);
|
||||
}
|
||||
|
||||
Result
|
||||
VerifyECDSASignedDigestNSS(const SignedDigest& sd,
|
||||
Input subjectPublicKeyInfo,
|
||||
void* pkcs11PinArg)
|
||||
{
|
||||
return VerifySignedDigest(sd, subjectPublicKeyInfo,
|
||||
SEC_OID_ANSIX962_EC_PUBLIC_KEY, pkcs11PinArg);
|
||||
}
|
||||
|
||||
Result
|
||||
DigestBufNSS(Input item,
|
||||
DigestAlgorithm digestAlg,
|
||||
/*out*/ uint8_t* digestBuf,
|
||||
size_t digestBufLen)
|
||||
{
|
||||
SECOidTag oid;
|
||||
size_t bits;
|
||||
switch (digestAlg) {
|
||||
case DigestAlgorithm::sha512: oid = SEC_OID_SHA512; bits = 512; break;
|
||||
case DigestAlgorithm::sha384: oid = SEC_OID_SHA384; bits = 384; break;
|
||||
case DigestAlgorithm::sha256: oid = SEC_OID_SHA256; bits = 256; break;
|
||||
case DigestAlgorithm::sha1: oid = SEC_OID_SHA1; bits = 160; break;
|
||||
MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
|
||||
}
|
||||
if (digestBufLen != bits / 8) {
|
||||
return Result::FATAL_ERROR_INVALID_ARGS;
|
||||
}
|
||||
|
||||
SECItem itemSECItem = UnsafeMapInputToSECItem(item);
|
||||
if (itemSECItem.len >
|
||||
static_cast<decltype(itemSECItem.len)>(
|
||||
std::numeric_limits<int32_t>::max())) {
|
||||
PR_NOT_REACHED("large items should not be possible here");
|
||||
return Result::FATAL_ERROR_INVALID_ARGS;
|
||||
}
|
||||
SECStatus srv = PK11_HashBuf(oid, digestBuf, itemSECItem.data,
|
||||
static_cast<int32_t>(itemSECItem.len));
|
||||
if (srv != SECSuccess) {
|
||||
return MapPRErrorCodeToResult(PR_GetError());
|
||||
}
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result
|
||||
MapPRErrorCodeToResult(PRErrorCode error)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
#define MOZILLA_PKIX_MAP(mozilla_pkix_result, value, nss_result) \
|
||||
case nss_result: return Result::mozilla_pkix_result;
|
||||
|
||||
MOZILLA_PKIX_MAP_LIST
|
||||
|
||||
#undef MOZILLA_PKIX_MAP
|
||||
|
||||
default:
|
||||
return Result::ERROR_UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
PRErrorCode
|
||||
MapResultToPRErrorCode(Result result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
#define MOZILLA_PKIX_MAP(mozilla_pkix_result, value, nss_result) \
|
||||
case Result::mozilla_pkix_result: return nss_result;
|
||||
|
||||
MOZILLA_PKIX_MAP_LIST
|
||||
|
||||
#undef MOZILLA_PKIX_MAP
|
||||
|
||||
MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RegisterErrorTable()
|
||||
{
|
||||
// Note that these error strings are not localizable.
|
||||
// When these strings change, update the localization information too.
|
||||
static const PRErrorMessage ErrorTableText[] = {
|
||||
{ "MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE",
|
||||
"The server uses key pinning (HPKP) but no trusted certificate chain "
|
||||
"could be constructed that matches the pinset. Key pinning violations "
|
||||
"cannot be overridden." },
|
||||
{ "MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY",
|
||||
"The server uses a certificate with a basic constraints extension "
|
||||
"identifying it as a certificate authority. For a properly-issued "
|
||||
"certificate, this should not be the case." },
|
||||
{ "MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE",
|
||||
"The server presented a certificate with a key size that is too small "
|
||||
"to establish a secure connection." },
|
||||
{ "MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA",
|
||||
"An X.509 version 1 certificate that is not a trust anchor was used to "
|
||||
"issue the server's certificate. X.509 version 1 certificates are "
|
||||
"deprecated and should not be used to sign other certificates." },
|
||||
{ "MOZILLA_PKIX_ERROR_NO_RFC822NAME_MATCH",
|
||||
"The certificate is not valid for the given email address." },
|
||||
{ "MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE",
|
||||
"The server presented a certificate that is not yet valid." },
|
||||
{ "MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE",
|
||||
"A certificate that is not yet valid was used to issue the server's "
|
||||
"certificate." },
|
||||
{ "MOZILLA_PKIX_ERROR_SIGNATURE_ALGORITHM_MISMATCH",
|
||||
"The signature algorithm in the signature field of the certificate does "
|
||||
"not match the algorithm in its signatureAlgorithm field." },
|
||||
{ "MOZILLA_PKIX_ERROR_OCSP_RESPONSE_FOR_CERT_MISSING",
|
||||
"The OCSP response does not include a status for the certificate being "
|
||||
"verified." },
|
||||
{ "MOZILLA_PKIX_ERROR_VALIDITY_TOO_LONG",
|
||||
"The server presented a certificate that is valid for too long." },
|
||||
{ "MOZILLA_PKIX_ERROR_REQUIRED_TLS_FEATURE_MISSING",
|
||||
"A required TLS feature is missing." },
|
||||
{ "MOZILLA_PKIX_ERROR_INVALID_INTEGER_ENCODING",
|
||||
"The server presented a certificate that contains an invalid encoding of "
|
||||
"an integer. Common causes include negative serial numbers, negative RSA "
|
||||
"moduli, and encodings that are longer than necessary." },
|
||||
{ "MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME",
|
||||
"The server presented a certificate with an empty issuer distinguished "
|
||||
"name." },
|
||||
};
|
||||
// Note that these error strings are not localizable.
|
||||
// When these strings change, update the localization information too.
|
||||
|
||||
static const PRErrorTable ErrorTable = {
|
||||
ErrorTableText,
|
||||
"pkixerrors",
|
||||
ERROR_BASE,
|
||||
PR_ARRAY_SIZE(ErrorTableText)
|
||||
};
|
||||
|
||||
(void) PR_ErrorInstallTable(&ErrorTable);
|
||||
}
|
||||
|
||||
} } // namespace mozilla::pkix
|
||||
1007
security/pkix/lib/pkixocsp.cpp
Normal file
1007
security/pkix/lib/pkixocsp.cpp
Normal file
File diff suppressed because it is too large
Load diff
46
security/pkix/lib/pkixresult.cpp
Normal file
46
security/pkix/lib/pkixresult.cpp
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*- *- 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 2013 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 "pkix/Result.h"
|
||||
#include "pkixutil.h"
|
||||
|
||||
namespace mozilla { namespace pkix {
|
||||
|
||||
const char*
|
||||
MapResultToName(Result result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
#define MOZILLA_PKIX_MAP(mozilla_pkix_result, value, nss_result) \
|
||||
case Result::mozilla_pkix_result: return "Result::" #mozilla_pkix_result;
|
||||
|
||||
MOZILLA_PKIX_MAP_LIST
|
||||
|
||||
#undef MOZILLA_PKIX_MAP
|
||||
|
||||
MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
|
||||
}
|
||||
}
|
||||
|
||||
} } // namespace mozilla::pkix
|
||||
78
security/pkix/lib/pkixtime.cpp
Normal file
78
security/pkix/lib/pkixtime.cpp
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/* -*- 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 "pkix/Time.h"
|
||||
#include "pkixutil.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 3)
|
||||
#endif
|
||||
#include "windows.h"
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
#else
|
||||
#include "sys/time.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla { namespace pkix {
|
||||
|
||||
Time
|
||||
Now()
|
||||
{
|
||||
uint64_t seconds;
|
||||
|
||||
#ifdef WIN32
|
||||
// "Contains a 64-bit value representing the number of 100-nanosecond
|
||||
// intervals since January 1, 1601 (UTC)."
|
||||
// - http://msdn.microsoft.com/en-us/library/windows/desktop/ms724284(v=vs.85).aspx
|
||||
FILETIME ft;
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
uint64_t ft64 = (static_cast<uint64_t>(ft.dwHighDateTime) << 32) |
|
||||
ft.dwLowDateTime;
|
||||
seconds = (DaysBeforeYear(1601) * Time::ONE_DAY_IN_SECONDS) +
|
||||
ft64 / (1000u * 1000u * 1000u / 100u);
|
||||
#else
|
||||
// "The gettimeofday() function shall obtain the current time, expressed as
|
||||
// seconds and microseconds since the Epoch."
|
||||
// - http://pubs.opengroup.org/onlinepubs/009695399/functions/gettimeofday.html
|
||||
timeval tv;
|
||||
(void) gettimeofday(&tv, nullptr);
|
||||
seconds = (DaysBeforeYear(1970) * Time::ONE_DAY_IN_SECONDS) +
|
||||
static_cast<uint64_t>(tv.tv_sec);
|
||||
#endif
|
||||
|
||||
return TimeFromElapsedSecondsAD(seconds);
|
||||
}
|
||||
|
||||
Time
|
||||
TimeFromEpochInSeconds(uint64_t secondsSinceEpoch)
|
||||
{
|
||||
uint64_t seconds = (DaysBeforeYear(1970) * Time::ONE_DAY_IN_SECONDS) +
|
||||
secondsSinceEpoch;
|
||||
return TimeFromElapsedSecondsAD(seconds);
|
||||
}
|
||||
|
||||
} } // namespace mozilla::pkix
|
||||
289
security/pkix/lib/pkixutil.h
Normal file
289
security/pkix/lib/pkixutil.h
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
/* -*- 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 2013 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.
|
||||
*/
|
||||
|
||||
#ifndef mozilla_pkix_pkixutil_h
|
||||
#define mozilla_pkix_pkixutil_h
|
||||
|
||||
#include "pkixder.h"
|
||||
|
||||
namespace mozilla { namespace pkix {
|
||||
|
||||
// During path building and verification, we build a linked list of BackCerts
|
||||
// from the current cert toward the end-entity certificate. The linked list
|
||||
// is used to verify properties that aren't local to the current certificate
|
||||
// and/or the direct link between the current certificate and its issuer,
|
||||
// such as name constraints.
|
||||
//
|
||||
// Each BackCert contains pointers to all the given certificate's extensions
|
||||
// so that we can parse the extension block once and then process the
|
||||
// extensions in an order that may be different than they appear in the cert.
|
||||
class BackCert final
|
||||
{
|
||||
public:
|
||||
// certDER and childCert must be valid for the lifetime of BackCert.
|
||||
BackCert(Input certDER, EndEntityOrCA endEntityOrCA,
|
||||
const BackCert* childCert)
|
||||
: der(certDER)
|
||||
, endEntityOrCA(endEntityOrCA)
|
||||
, childCert(childCert)
|
||||
{
|
||||
}
|
||||
|
||||
Result Init();
|
||||
|
||||
const Input GetDER() const { return der; }
|
||||
const der::SignedDataWithSignature& GetSignedData() const {
|
||||
return signedData;
|
||||
}
|
||||
|
||||
der::Version GetVersion() const { return version; }
|
||||
const Input GetSerialNumber() const { return serialNumber; }
|
||||
const Input GetSignature() const { return signature; }
|
||||
const Input GetIssuer() const { return issuer; }
|
||||
// XXX: "validity" is a horrible name for the structure that holds
|
||||
// notBefore & notAfter, but that is the name used in RFC 5280 and we use the
|
||||
// RFC 5280 names for everything.
|
||||
const Input GetValidity() const { return validity; }
|
||||
const Input GetSubject() const { return subject; }
|
||||
const Input GetSubjectPublicKeyInfo() const
|
||||
{
|
||||
return subjectPublicKeyInfo;
|
||||
}
|
||||
const Input* GetAuthorityInfoAccess() const
|
||||
{
|
||||
return MaybeInput(authorityInfoAccess);
|
||||
}
|
||||
const Input* GetBasicConstraints() const
|
||||
{
|
||||
return MaybeInput(basicConstraints);
|
||||
}
|
||||
const Input* GetCertificatePolicies() const
|
||||
{
|
||||
return MaybeInput(certificatePolicies);
|
||||
}
|
||||
const Input* GetExtKeyUsage() const
|
||||
{
|
||||
return MaybeInput(extKeyUsage);
|
||||
}
|
||||
const Input* GetKeyUsage() const
|
||||
{
|
||||
return MaybeInput(keyUsage);
|
||||
}
|
||||
const Input* GetInhibitAnyPolicy() const
|
||||
{
|
||||
return MaybeInput(inhibitAnyPolicy);
|
||||
}
|
||||
const Input* GetNameConstraints() const
|
||||
{
|
||||
return MaybeInput(nameConstraints);
|
||||
}
|
||||
const Input* GetSubjectAltName() const
|
||||
{
|
||||
return MaybeInput(subjectAltName);
|
||||
}
|
||||
const Input* GetRequiredTLSFeatures() const
|
||||
{
|
||||
return MaybeInput(requiredTLSFeatures);
|
||||
}
|
||||
const Input* GetSignedCertificateTimestamps() const
|
||||
{
|
||||
return MaybeInput(signedCertificateTimestamps);
|
||||
}
|
||||
|
||||
private:
|
||||
const Input der;
|
||||
|
||||
public:
|
||||
const EndEntityOrCA endEntityOrCA;
|
||||
BackCert const* const childCert;
|
||||
|
||||
private:
|
||||
// When parsing certificates in BackCert::Init, we don't accept empty
|
||||
// extensions. Consequently, we don't have to store a distinction between
|
||||
// empty extensions and extensions that weren't included. However, when
|
||||
// *processing* extensions, we distinguish between whether an extension was
|
||||
// included or not based on whetehr the GetXXX function for the extension
|
||||
// returns nullptr.
|
||||
static inline const Input* MaybeInput(const Input& item)
|
||||
{
|
||||
return item.GetLength() > 0 ? &item : nullptr;
|
||||
}
|
||||
|
||||
der::SignedDataWithSignature signedData;
|
||||
|
||||
der::Version version;
|
||||
Input serialNumber;
|
||||
Input signature;
|
||||
Input issuer;
|
||||
// XXX: "validity" is a horrible name for the structure that holds
|
||||
// notBefore & notAfter, but that is the name used in RFC 5280 and we use the
|
||||
// RFC 5280 names for everything.
|
||||
Input validity;
|
||||
Input subject;
|
||||
Input subjectPublicKeyInfo;
|
||||
|
||||
Input authorityInfoAccess;
|
||||
Input basicConstraints;
|
||||
Input certificatePolicies;
|
||||
Input extKeyUsage;
|
||||
Input inhibitAnyPolicy;
|
||||
Input keyUsage;
|
||||
Input nameConstraints;
|
||||
Input subjectAltName;
|
||||
Input criticalNetscapeCertificateType;
|
||||
Input requiredTLSFeatures;
|
||||
Input signedCertificateTimestamps; // RFC 6962 (Certificate Transparency)
|
||||
|
||||
Result RememberExtension(Reader& extnID, Input extnValue, bool critical,
|
||||
/*out*/ bool& understood);
|
||||
|
||||
BackCert(const BackCert&) = delete;
|
||||
void operator=(const BackCert&) = delete;
|
||||
};
|
||||
|
||||
class NonOwningDERArray final : public DERArray
|
||||
{
|
||||
public:
|
||||
NonOwningDERArray()
|
||||
: numItems(0)
|
||||
{
|
||||
// we don't need to initialize the items array because we always check
|
||||
// numItems before accessing i.
|
||||
}
|
||||
|
||||
size_t GetLength() const override { return numItems; }
|
||||
|
||||
const Input* GetDER(size_t i) const override
|
||||
{
|
||||
return i < numItems ? &items[i] : nullptr;
|
||||
}
|
||||
|
||||
Result Append(Input der)
|
||||
{
|
||||
if (numItems >= MAX_LENGTH) {
|
||||
return Result::FATAL_ERROR_INVALID_ARGS;
|
||||
}
|
||||
Result rv = items[numItems].Init(der); // structure assignment
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
++numItems;
|
||||
return Success;
|
||||
}
|
||||
|
||||
// Public so we can static_assert on this. Keep in sync with MAX_SUBCA_COUNT.
|
||||
static const size_t MAX_LENGTH = 8;
|
||||
private:
|
||||
Input items[MAX_LENGTH]; // avoids any heap allocations
|
||||
size_t numItems;
|
||||
|
||||
NonOwningDERArray(const NonOwningDERArray&) = delete;
|
||||
void operator=(const NonOwningDERArray&) = delete;
|
||||
};
|
||||
|
||||
// Extracts the SignedCertificateTimestampList structure which is encoded as an
|
||||
// OCTET STRING within the X.509v3 / OCSP extensions (see RFC 6962 section 3.3).
|
||||
Result
|
||||
ExtractSignedCertificateTimestampListFromExtension(Input extnValue,
|
||||
Input& sctList);
|
||||
|
||||
inline unsigned int
|
||||
DaysBeforeYear(unsigned int year)
|
||||
{
|
||||
assert(year <= 9999);
|
||||
return ((year - 1u) * 365u)
|
||||
+ ((year - 1u) / 4u) // leap years are every 4 years,
|
||||
- ((year - 1u) / 100u) // except years divisible by 100,
|
||||
+ ((year - 1u) / 400u); // except years divisible by 400.
|
||||
}
|
||||
|
||||
static const size_t MAX_DIGEST_SIZE_IN_BYTES = 512 / 8; // sha-512
|
||||
|
||||
Result DigestSignedData(TrustDomain& trustDomain,
|
||||
const der::SignedDataWithSignature& signedData,
|
||||
/*out*/ uint8_t(&digestBuf)[MAX_DIGEST_SIZE_IN_BYTES],
|
||||
/*out*/ der::PublicKeyAlgorithm& publicKeyAlg,
|
||||
/*out*/ SignedDigest& signedDigest);
|
||||
|
||||
Result VerifySignedDigest(TrustDomain& trustDomain,
|
||||
der::PublicKeyAlgorithm publicKeyAlg,
|
||||
const SignedDigest& signedDigest,
|
||||
Input signerSubjectPublicKeyInfo);
|
||||
|
||||
// Combines DigestSignedData and VerifySignedDigest
|
||||
Result VerifySignedData(TrustDomain& trustDomain,
|
||||
const der::SignedDataWithSignature& signedData,
|
||||
Input signerSubjectPublicKeyInfo);
|
||||
|
||||
// Extracts the key parameters from |subjectPublicKeyInfo|, invoking
|
||||
// the relevant methods of |trustDomain|.
|
||||
Result
|
||||
CheckSubjectPublicKeyInfo(Input subjectPublicKeyInfo, TrustDomain& trustDomain,
|
||||
EndEntityOrCA endEntityOrCA);
|
||||
|
||||
// In a switch over an enum, sometimes some compilers are not satisfied that
|
||||
// all control flow paths have been considered unless there is a default case.
|
||||
// However, in our code, such a default case is almost always unreachable dead
|
||||
// code. That can be particularly problematic when the compiler wants the code
|
||||
// to choose a value, such as a return value, for the default case, but there's
|
||||
// no appropriate "impossible case" value to choose.
|
||||
//
|
||||
// MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM accounts for this. Example:
|
||||
//
|
||||
// // In xy.cpp
|
||||
// #include "xt.h"
|
||||
//
|
||||
// enum class XY { X, Y };
|
||||
//
|
||||
// int func(XY xy) {
|
||||
// switch (xy) {
|
||||
// case XY::X: return 1;
|
||||
// case XY::Y; return 2;
|
||||
// MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
|
||||
// }
|
||||
// }
|
||||
#if defined(__clang__)
|
||||
// Clang will warn if not all cases are covered (-Wswitch-enum) AND it will
|
||||
// warn if a switch statement that covers every enum label has a default case
|
||||
// (-W-covered-switch-default). Versions prior to 3.5 warned about unreachable
|
||||
// code in such default cases (-Wunreachable-code) even when
|
||||
// -W-covered-switch-default was disabled, but that changed in Clang 3.5.
|
||||
#define MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM // empty
|
||||
#elif defined(__GNUC__)
|
||||
// GCC will warn if not all cases are covered (-Wswitch-enum). It does not
|
||||
// assume that the default case is unreachable.
|
||||
#define MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM \
|
||||
default: assert(false); __builtin_unreachable();
|
||||
#elif defined(_MSC_VER)
|
||||
// MSVC will warn if not all cases are covered (C4061, level 4). It does not
|
||||
// assume that the default case is unreachable.
|
||||
#define MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM \
|
||||
default: assert(false); __assume(0);
|
||||
#else
|
||||
#error Unsupported compiler for MOZILLA_PKIX_UNREACHABLE_DEFAULT.
|
||||
#endif
|
||||
|
||||
} } // namespace mozilla::pkix
|
||||
|
||||
#endif // mozilla_pkix_pkixutil_h
|
||||
103
security/pkix/lib/pkixverify.cpp
Normal file
103
security/pkix/lib/pkixverify.cpp
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/* -*- 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 2015 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
|
||||
DigestSignedData(TrustDomain& trustDomain,
|
||||
const der::SignedDataWithSignature& signedData,
|
||||
/*out*/ uint8_t(&digestBuf)[MAX_DIGEST_SIZE_IN_BYTES],
|
||||
/*out*/ der::PublicKeyAlgorithm& publicKeyAlg,
|
||||
/*out*/ SignedDigest& signedDigest)
|
||||
{
|
||||
Reader signatureAlg(signedData.algorithm);
|
||||
Result rv = der::SignatureAlgorithmIdentifierValue(
|
||||
signatureAlg, publicKeyAlg, signedDigest.digestAlgorithm);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
if (!signatureAlg.AtEnd()) {
|
||||
return Result::ERROR_BAD_DER;
|
||||
}
|
||||
|
||||
size_t digestLen;
|
||||
switch (signedDigest.digestAlgorithm) {
|
||||
case DigestAlgorithm::sha512: digestLen = 512 / 8; break;
|
||||
case DigestAlgorithm::sha384: digestLen = 384 / 8; break;
|
||||
case DigestAlgorithm::sha256: digestLen = 256 / 8; break;
|
||||
case DigestAlgorithm::sha1: digestLen = 160 / 8; break;
|
||||
MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
|
||||
}
|
||||
assert(digestLen <= sizeof(digestBuf));
|
||||
|
||||
rv = trustDomain.DigestBuf(signedData.data, signedDigest.digestAlgorithm,
|
||||
digestBuf, digestLen);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
rv = signedDigest.digest.Init(digestBuf, digestLen);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return signedDigest.signature.Init(signedData.signature);
|
||||
}
|
||||
|
||||
Result
|
||||
VerifySignedDigest(TrustDomain& trustDomain,
|
||||
der::PublicKeyAlgorithm publicKeyAlg,
|
||||
const SignedDigest& signedDigest,
|
||||
Input signerSubjectPublicKeyInfo)
|
||||
{
|
||||
switch (publicKeyAlg) {
|
||||
case der::PublicKeyAlgorithm::ECDSA:
|
||||
return trustDomain.VerifyECDSASignedDigest(signedDigest,
|
||||
signerSubjectPublicKeyInfo);
|
||||
case der::PublicKeyAlgorithm::RSA_PKCS1:
|
||||
return trustDomain.VerifyRSAPKCS1SignedDigest(signedDigest,
|
||||
signerSubjectPublicKeyInfo);
|
||||
MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
|
||||
}
|
||||
}
|
||||
|
||||
Result
|
||||
VerifySignedData(TrustDomain& trustDomain,
|
||||
const der::SignedDataWithSignature& signedData,
|
||||
Input signerSubjectPublicKeyInfo)
|
||||
{
|
||||
uint8_t digestBuf[MAX_DIGEST_SIZE_IN_BYTES];
|
||||
der::PublicKeyAlgorithm publicKeyAlg;
|
||||
SignedDigest signedDigest;
|
||||
Result rv = DigestSignedData(trustDomain, signedData, digestBuf,
|
||||
publicKeyAlg, signedDigest);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
return VerifySignedDigest(trustDomain, publicKeyAlg, signedDigest,
|
||||
signerSubjectPublicKeyInfo);
|
||||
}
|
||||
|
||||
} } // namespace mozilla::pkix
|
||||
Loading…
Add table
Add a link
Reference in a new issue