1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "AppTrustDomain.h"
9 #include "pkix/pkixnss.h"
10 #include "mozilla/ArrayUtils.h"
11 #include "nsIX509CertDB.h"
12 #include "nsNSSCertificate.h"
16 // Generated in Makefile.in
17 #include "marketplace-prod-public.inc"
18 #include "marketplace-prod-reviewers.inc"
19 #include "marketplace-dev-public.inc"
20 #include "marketplace-dev-reviewers.inc"
21 #include "marketplace-stage.inc"
22 #include "xpcshell.inc"
23 // Trusted Hosted Apps Certificates
24 #include "manifest-signing-root.inc"
25 #include "manifest-signing-test-root.inc"
27 using namespace mozilla::pkix
;
30 extern PRLogModuleInfo
* gPIPNSSLog
;
33 static const unsigned int DEFAULT_MINIMUM_NON_ECC_BITS
= 2048;
35 namespace mozilla
{ namespace psm
{
37 AppTrustDomain::AppTrustDomain(ScopedCERTCertList
& certChain
, void* pinArg
)
38 : mCertChain(certChain
)
40 , mMinimumNonECCBits(DEFAULT_MINIMUM_NON_ECC_BITS
)
45 AppTrustDomain::SetTrustedRoot(AppTrustedRoot trustedRoot
)
49 // Load the trusted certificate into the in-memory NSS database so that
50 // CERT_CreateSubjectCertList can find it.
54 case nsIX509CertDB::AppMarketplaceProdPublicRoot
:
55 trustedDER
.data
= const_cast<uint8_t*>(marketplaceProdPublicRoot
);
56 trustedDER
.len
= mozilla::ArrayLength(marketplaceProdPublicRoot
);
59 case nsIX509CertDB::AppMarketplaceProdReviewersRoot
:
60 trustedDER
.data
= const_cast<uint8_t*>(marketplaceProdReviewersRoot
);
61 trustedDER
.len
= mozilla::ArrayLength(marketplaceProdReviewersRoot
);
64 case nsIX509CertDB::AppMarketplaceDevPublicRoot
:
65 trustedDER
.data
= const_cast<uint8_t*>(marketplaceDevPublicRoot
);
66 trustedDER
.len
= mozilla::ArrayLength(marketplaceDevPublicRoot
);
69 case nsIX509CertDB::AppMarketplaceDevReviewersRoot
:
70 trustedDER
.data
= const_cast<uint8_t*>(marketplaceDevReviewersRoot
);
71 trustedDER
.len
= mozilla::ArrayLength(marketplaceDevReviewersRoot
);
74 case nsIX509CertDB::AppMarketplaceStageRoot
:
75 trustedDER
.data
= const_cast<uint8_t*>(marketplaceStageRoot
);
76 trustedDER
.len
= mozilla::ArrayLength(marketplaceStageRoot
);
77 // The staging root was generated with a 1024-bit key.
78 mMinimumNonECCBits
= 1024u;
81 case nsIX509CertDB::AppXPCShellRoot
:
82 trustedDER
.data
= const_cast<uint8_t*>(xpcshellRoot
);
83 trustedDER
.len
= mozilla::ArrayLength(xpcshellRoot
);
86 case nsIX509CertDB::TrustedHostedAppPublicRoot
:
87 trustedDER
.data
= const_cast<uint8_t*>(trustedAppPublicRoot
);
88 trustedDER
.len
= mozilla::ArrayLength(trustedAppPublicRoot
);
91 case nsIX509CertDB::TrustedHostedAppTestRoot
:
92 trustedDER
.data
= const_cast<uint8_t*>(trustedAppTestRoot
);
93 trustedDER
.len
= mozilla::ArrayLength(trustedAppTestRoot
);
97 PR_SetError(SEC_ERROR_INVALID_ARGS
, 0);
101 mTrustedRoot
= CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
102 &trustedDER
, nullptr, false, true);
111 AppTrustDomain::FindIssuer(Input encodedIssuerName
, IssuerChecker
& checker
,
115 MOZ_ASSERT(mTrustedRoot
);
117 return Result::FATAL_ERROR_INVALID_STATE
;
120 // TODO(bug 1035418): If/when mozilla::pkix relaxes the restriction that
121 // FindIssuer must only pass certificates with a matching subject name to
122 // checker.Check, we can stop using CERT_CreateSubjectCertList and instead
123 // use logic like this:
125 // 1. First, try the trusted trust anchor.
126 // 2. Secondly, iterate through the certificates that were stored in the CMS
127 // message, passing each one to checker.Check.
128 SECItem encodedIssuerNameSECItem
=
129 UnsafeMapInputToSECItem(encodedIssuerName
);
131 candidates(CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
132 &encodedIssuerNameSECItem
, 0,
135 for (CERTCertListNode
* n
= CERT_LIST_HEAD(candidates
);
136 !CERT_LIST_END(n
, candidates
); n
= CERT_LIST_NEXT(n
)) {
138 Result rv
= certDER
.Init(n
->cert
->derCert
.data
, n
->cert
->derCert
.len
);
140 continue; // probably too big
144 rv
= checker
.Check(certDER
, nullptr/*additionalNameConstraints*/,
159 AppTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA
,
160 const CertPolicyId
& policy
,
161 Input candidateCertDER
,
162 /*out*/ TrustLevel
& trustLevel
)
164 MOZ_ASSERT(policy
.IsAnyPolicy());
165 MOZ_ASSERT(mTrustedRoot
);
166 if (!policy
.IsAnyPolicy()) {
167 return Result::FATAL_ERROR_INVALID_ARGS
;
170 return Result::FATAL_ERROR_INVALID_STATE
;
173 // Handle active distrust of the certificate.
175 // XXX: This would be cleaner and more efficient if we could get the trust
176 // information without constructing a CERTCertificate here, but NSS doesn't
177 // expose it in any other easy-to-use fashion.
178 SECItem candidateCertDERSECItem
=
179 UnsafeMapInputToSECItem(candidateCertDER
);
180 ScopedCERTCertificate
candidateCert(
181 CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &candidateCertDERSECItem
,
182 nullptr, false, true));
183 if (!candidateCert
) {
184 return MapPRErrorCodeToResult(PR_GetError());
188 if (CERT_GetCertTrust(candidateCert
.get(), &trust
) == SECSuccess
) {
189 uint32_t flags
= SEC_GET_TRUST_FLAGS(&trust
, trustObjectSigning
);
191 // For DISTRUST, we use the CERTDB_TRUSTED or CERTDB_TRUSTED_CA bit,
192 // because we can have active distrust for either type of cert. Note that
193 // CERTDB_TERMINAL_RECORD means "stop trying to inherit trust" so if the
194 // relevant trust bit isn't set then that means the cert must be considered
196 uint32_t relevantTrustBit
= endEntityOrCA
== EndEntityOrCA::MustBeCA
199 if (((flags
& (relevantTrustBit
| CERTDB_TERMINAL_RECORD
)))
200 == CERTDB_TERMINAL_RECORD
) {
201 trustLevel
= TrustLevel::ActivelyDistrusted
;
206 // mTrustedRoot is the only trust anchor for this validation.
207 if (CERT_CompareCerts(mTrustedRoot
.get(), candidateCert
.get())) {
208 trustLevel
= TrustLevel::TrustAnchor
;
212 trustLevel
= TrustLevel::InheritsTrust
;
217 AppTrustDomain::VerifySignedData(const SignedDataWithSignature
& signedData
,
218 Input subjectPublicKeyInfo
)
220 return ::mozilla::pkix::VerifySignedDataNSS(signedData
, subjectPublicKeyInfo
,
221 mMinimumNonECCBits
, mPinArg
);
225 AppTrustDomain::DigestBuf(Input item
, /*out*/ uint8_t* digestBuf
,
228 return ::mozilla::pkix::DigestBufNSS(item
, digestBuf
, digestBufLen
);
232 AppTrustDomain::CheckRevocation(EndEntityOrCA
, const CertID
&, Time
,
233 /*optional*/ const Input
*,
234 /*optional*/ const Input
*)
236 // We don't currently do revocation checking. If we need to distrust an Apps
237 // certificate, we will use the active distrust mechanism.
242 AppTrustDomain::IsChainValid(const DERArray
& certChain
, Time time
)
244 SECStatus srv
= ConstructCERTCertListFromReversedDERArray(certChain
,
246 if (srv
!= SECSuccess
) {
247 return MapPRErrorCodeToResult(PR_GetError());
253 AppTrustDomain::CheckPublicKey(Input subjectPublicKeyInfo
)
255 return ::mozilla::pkix::CheckPublicKeyNSS(subjectPublicKeyInfo
,
259 } } // namespace mozilla::psm