blob: ec8cc120aee59a4f27a3e6ea54dcb9f34e726e3f [file] [log] [blame]
/* 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;
}