summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/mozpkix/exports.gyp3
-rw-r--r--lib/mozpkix/include/pkix/pkixc.h47
-rw-r--r--lib/mozpkix/lib/pkixc.cpp218
-rw-r--r--lib/mozpkix/mozpkix.gyp1
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',