diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/mozpkix/exports.gyp | 3 | ||||
-rw-r--r-- | lib/mozpkix/include/pkix/pkixc.h | 47 | ||||
-rw-r--r-- | lib/mozpkix/lib/pkixc.cpp | 218 | ||||
-rw-r--r-- | lib/mozpkix/mozpkix.gyp | 1 |
4 files changed, 268 insertions, 1 deletions
diff --git a/lib/mozpkix/exports.gyp b/lib/mozpkix/exports.gyp index 248efc910..d592bb70f 100644 --- a/lib/mozpkix/exports.gyp +++ b/lib/mozpkix/exports.gyp @@ -17,6 +17,7 @@ 'include/pkix/Time.h', 'include/pkix/Result.h', 'include/pkix/pkix.h', + 'include/pkix/pkixc.h', 'include/pkix/pkixnss.h', 'include/pkix/pkixtypes.h', 'include/pkix/pkixutil.h', @@ -44,4 +45,4 @@ 'variables': { 'module': 'nss' } -}
\ No newline at end of file +} diff --git a/lib/mozpkix/include/pkix/pkixc.h b/lib/mozpkix/include/pkix/pkixc.h new file mode 100644 index 000000000..a1cc7bf2e --- /dev/null +++ b/lib/mozpkix/include/pkix/pkixc.h @@ -0,0 +1,47 @@ +/* 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/. + */ + +#ifndef mozilla_pkix_pkixc_h +#define mozilla_pkix_pkixc_h + +#include "prerror.h" +#include "stdint.h" + +// VerifyCertificateChain will attempt to build a verified certificate chain +// starting from the 0th certificate in the given array to the indicated trust +// anchor. It returns true on success and false otherwise. No particular key +// usage is required, and no particular policy is required. The code signing +// extended key usage is required. No revocation checking is performed. RSA +// keys must be at least 2048 bits long, and EC keys must be from one of the +// curves secp256r1, secp384r1, or secp521r1. Only SHA256, SHA384, and SHA512 +// are acceptable digest algorithms. When doing name checking, the subject +// common name field is ignored. +// certificate is an array of pointers to certificates. +// certificateLengths is an array of the lengths of each certificate. +// numCertificates indicates how many certificates are in certificates. +// secondsSinceEpoch indicates the time at which the certificate chain must be +// valid, in seconds since the epoch. +// rootSHA256Hash identifies a trust anchor by the SHA256 hash of its contents. +// It must be an array of 32 bytes. +// hostname is a doman name for which the end-entity certificate must be valid. +// error will be set if and only if the return value is false. Its value may +// indicate why verification failed. + +#ifdef __cplusplus +extern "C" { +#endif +bool VerifyCodeSigningCertificateChain(const uint8_t** certificates, + const uint16_t* certificateLengths, + size_t numCertificates, + uint64_t secondsSinceEpoch, + const uint8_t* rootSHA256Hash, + const uint8_t* hostname, + size_t hostnameLength, + /* out */ PRErrorCode* error); +#ifdef __cplusplus +} +#endif + +#endif // mozilla_pkix_pkixc_h diff --git a/lib/mozpkix/lib/pkixc.cpp b/lib/mozpkix/lib/pkixc.cpp new file mode 100644 index 000000000..ec8cc120a --- /dev/null +++ b/lib/mozpkix/lib/pkixc.cpp @@ -0,0 +1,218 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "mozpkix/pkixc.h" + +#include "mozpkix/pkix.h" +#include "mozpkix/pkixnss.h" +#include "mozpkix/pkixtypes.h" +#include "secerr.h" + +using namespace mozilla::pkix; + +const size_t SHA256_DIGEST_LENGTH = 256 / 8; + +class CodeSigningTrustDomain final : public TrustDomain { + public: + explicit CodeSigningTrustDomain(const uint8_t** certificates, + const uint16_t* certificateLengths, + size_t numCertificates, + const uint8_t* rootSHA256Digest) + : mCertificates(certificates), + mCertificateLengths(certificateLengths), + mNumCertificates(numCertificates), + mRootSHA256Digest(rootSHA256Digest) {} + + virtual Result GetCertTrust(EndEntityOrCA endEntityOrCA, + const CertPolicyId& policy, + Input candidateCertDER, + /*out*/ TrustLevel& trustLevel) override { + uint8_t digestBuf[SHA256_DIGEST_LENGTH] = {0}; + Result rv = DigestBufNSS(candidateCertDER, DigestAlgorithm::sha256, + digestBuf, SHA256_DIGEST_LENGTH); + if (rv != Success) { + return rv; + } + Input candidateDigestInput; + rv = candidateDigestInput.Init(digestBuf, SHA256_DIGEST_LENGTH); + if (rv != Success) { + return rv; + } + Input rootDigestInput; + rv = rootDigestInput.Init(mRootSHA256Digest, SHA256_DIGEST_LENGTH); + if (rv != Success) { + return rv; + } + if (InputsAreEqual(candidateDigestInput, rootDigestInput)) { + trustLevel = TrustLevel::TrustAnchor; + } else { + trustLevel = TrustLevel::InheritsTrust; + } + return Success; + } + + virtual Result FindIssuer(Input encodedIssuerName, IssuerChecker& checker, + Time time) override { + for (size_t i = 0; i < mNumCertificates; i++) { + Input certInput; + Result rv = certInput.Init(mCertificates[i], mCertificateLengths[i]); + if (rv != Success) { + return rv; + } + bool keepGoing; + rv = checker.Check(certInput, nullptr /*additionalNameConstraints*/, + keepGoing); + if (rv != Success) { + return rv; + } + if (!keepGoing) { + break; + } + } + + return Success; + } + + virtual Result CheckRevocation( + EndEntityOrCA endEntityOrCA, const CertID& certID, Time time, + Duration validityDuration, + /*optional*/ const Input* stapledOCSPresponse, + /*optional*/ const Input* aiaExtension, + /*optional*/ const Input* sctExtension) override { + return Success; + } + + virtual Result IsChainValid(const DERArray& certChain, Time time, + const CertPolicyId& requiredPolicy) override { + return Success; + } + + virtual Result CheckSignatureDigestAlgorithm(DigestAlgorithm digestAlg, + EndEntityOrCA endEntityOrCA, + Time notBefore) override { + switch (digestAlg) { + case DigestAlgorithm::sha256: // fall through + case DigestAlgorithm::sha384: // fall through + case DigestAlgorithm::sha512: + return Success; + default: + return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED; + } + } + + virtual Result CheckRSAPublicKeyModulusSizeInBits( + EndEntityOrCA endEntityOrCA, unsigned int modulusSizeInBits) override { + if (modulusSizeInBits < 2048) { + return Result::ERROR_INADEQUATE_KEY_SIZE; + } + return Success; + } + + virtual Result VerifyRSAPKCS1SignedDigest( + const SignedDigest& signedDigest, Input subjectPublicKeyInfo) override { + return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo, + nullptr); + } + + virtual Result CheckECDSACurveIsAcceptable(EndEntityOrCA endEntityOrCA, + NamedCurve curve) override { + switch (curve) { + case NamedCurve::secp256r1: // fall through + case NamedCurve::secp384r1: // fall through + case NamedCurve::secp521r1: + return Success; + } + + return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE; + } + + virtual Result VerifyECDSASignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) override { + return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo, + nullptr); + } + + virtual Result CheckValidityIsAcceptable(Time notBefore, Time notAfter, + EndEntityOrCA endEntityOrCA, + KeyPurposeId keyPurpose) override { + return Success; + } + + virtual Result NetscapeStepUpMatchesServerAuth( + Time notBefore, /*out*/ bool& matches) override { + matches = false; + return Success; + } + + virtual void NoteAuxiliaryExtension(AuxiliaryExtension extension, + Input extensionData) override {} + + virtual Result DigestBuf(Input item, DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, + size_t digestBufLen) override { + return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen); + } + + private: + const uint8_t** mCertificates; + const uint16_t* mCertificateLengths; + size_t mNumCertificates; + const uint8_t* mRootSHA256Digest; +}; + +class CodeSigningNameMatchingPolicy : public NameMatchingPolicy { + public: + virtual Result FallBackToCommonName( + Time notBefore, + /*out*/ FallBackToSearchWithinSubject& fallBackToCommonName) override { + fallBackToCommonName = FallBackToSearchWithinSubject::No; + return Success; + } +}; + +bool VerifyCodeSigningCertificateChain( + const uint8_t** certificates, const uint16_t* certificateLengths, + size_t numCertificates, uint64_t secondsSinceEpoch, + const uint8_t* rootSHA256Digest, const uint8_t* hostname, + size_t hostnameLength, PRErrorCode* error) { + if (!error) { + return false; + } + if (!certificates || !certificateLengths || !rootSHA256Digest) { + *error = SEC_ERROR_INVALID_ARGS; + return false; + } + + CodeSigningTrustDomain trustDomain(certificates, certificateLengths, + numCertificates, rootSHA256Digest); + Input certificate; + Result rv = certificate.Init(certificates[0], certificateLengths[0]); + if (rv != Success) { + *error = MapResultToPRErrorCode(rv); + return false; + } + Time time = TimeFromEpochInSeconds(secondsSinceEpoch); + rv = BuildCertChain( + trustDomain, certificate, time, EndEntityOrCA::MustBeEndEntity, + KeyUsage::noParticularKeyUsageRequired, KeyPurposeId::id_kp_codeSigning, + CertPolicyId::anyPolicy, nullptr); + if (rv != Success) { + *error = MapResultToPRErrorCode(rv); + return false; + } + Input hostnameInput; + rv = hostnameInput.Init(hostname, hostnameLength); + if (rv != Success) { + *error = MapResultToPRErrorCode(rv); + return false; + } + CodeSigningNameMatchingPolicy nameMatchingPolicy; + rv = CheckCertHostname(certificate, hostnameInput, nameMatchingPolicy); + if (rv != Success) { + *error = MapResultToPRErrorCode(rv); + return false; + } + return true; +} diff --git a/lib/mozpkix/mozpkix.gyp b/lib/mozpkix/mozpkix.gyp index 1c552ba5f..abc3d149f 100644 --- a/lib/mozpkix/mozpkix.gyp +++ b/lib/mozpkix/mozpkix.gyp @@ -12,6 +12,7 @@ 'standalone_static_library': 1, 'sources': [ 'lib/pkixbuild.cpp', + 'lib/pkixc.cpp', 'lib/pkixcert.cpp', 'lib/pkixcheck.cpp', 'lib/pkixder.cpp', |