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 "MainThreadUtils.h"
10 #include "cert_storage/src/cert_storage.h"
11 // FIXME: these two must be included before certdb.h {
16 #include "mozilla/ArrayUtils.h"
17 #include "mozilla/Casting.h"
18 #include "mozilla/Logging.h"
19 #include "mozilla/Preferences.h"
20 #include "mozpkix/pkixnss.h"
21 #include "NSSCertDBTrustDomain.h"
22 #include "nsComponentManagerUtils.h"
23 #include "nsDirectoryServiceUtils.h"
24 #include "nsIContentSignatureVerifier.h"
25 #include "nsIX509CertDB.h"
26 #include "nsNSSCertificate.h"
27 #include "nsNetUtil.h"
30 // Generated by gen_cert_header.py, which gets called by the build system.
31 #include "xpcshell.inc"
32 // Add-on signing Certificates
33 #include "addons-public.inc"
34 #include "addons-public-intermediate.inc"
35 #include "addons-stage.inc"
36 #include "addons-stage-intermediate.inc"
37 // Content signature root certificates
38 #include "content-signature-dev.inc"
39 #include "content-signature-local.inc"
40 #include "content-signature-prod.inc"
41 #include "content-signature-stage.inc"
43 using namespace mozilla::pkix
;
45 extern mozilla::LazyLogModule gPIPNSSLog
;
50 AppTrustDomain::AppTrustDomain(nsTArray
<Span
<const uint8_t>>&& collectedCerts
)
51 : mIntermediates(std::move(collectedCerts
)),
52 mCertBlocklist(do_GetService(NS_CERT_STORAGE_CID
)) {}
54 nsresult
AppTrustDomain::SetTrustedRoot(AppTrustedRoot trustedRoot
) {
55 if (!mTrustedRoots
.IsEmpty()) {
56 return NS_ERROR_ALREADY_INITIALIZED
;
58 switch (trustedRoot
) {
59 case nsIX509CertDB::AppXPCShellRoot
:
60 mTrustedRoots
.AppendElements(xpcshellRoots
, std::size(xpcshellRoots
));
63 case nsIX509CertDB::AddonsPublicRoot
:
64 mTrustedRoots
.AppendElements(addonsPublicRoots
,
65 std::size(addonsPublicRoots
));
68 case nsIX509CertDB::AddonsStageRoot
:
69 mTrustedRoots
.AppendElements(addonsStageRoots
,
70 std::size(addonsStageRoots
));
73 case nsIContentSignatureVerifier::ContentSignatureLocalRoot
:
74 mTrustedRoots
.AppendElements(contentSignatureLocalRoots
,
75 std::size(contentSignatureLocalRoots
));
78 case nsIContentSignatureVerifier::ContentSignatureProdRoot
:
79 mTrustedRoots
.AppendElements(contentSignatureProdRoots
,
80 std::size(contentSignatureProdRoots
));
83 case nsIContentSignatureVerifier::ContentSignatureStageRoot
:
84 mTrustedRoots
.AppendElements(contentSignatureStageRoots
,
85 std::size(contentSignatureStageRoots
));
88 case nsIContentSignatureVerifier::ContentSignatureDevRoot
:
89 mTrustedRoots
.AppendElements(contentSignatureDevRoots
,
90 std::size(contentSignatureDevRoots
));
94 return NS_ERROR_INVALID_ARG
;
97 // If we're verifying add-ons signed by our production root, we want to make
98 // sure a valid intermediate certificate is available for path building.
99 // The intermediate bundled with signed XPI files may have expired and be
100 // considered invalid, which can result in bug 1548973.
101 if (trustedRoot
== nsIX509CertDB::AddonsPublicRoot
) {
102 mAddonsIntermediates
.AppendElements(addonsPublicIntermediates
,
103 std::size(addonsPublicIntermediates
));
105 // Similarly to the above logic for production, we hardcode the intermediate
106 // stage certificate here, so that stage is equivalent to production.
107 if (trustedRoot
== nsIX509CertDB::AddonsStageRoot
) {
108 mAddonsIntermediates
.AppendElements(addonsStageIntermediates
,
109 std::size(addonsStageIntermediates
));
115 pkix::Result
AppTrustDomain::FindIssuer(Input encodedIssuerName
,
116 IssuerChecker
& checker
, Time
) {
117 MOZ_ASSERT(!mTrustedRoots
.IsEmpty());
118 if (mTrustedRoots
.IsEmpty()) {
119 return pkix::Result::FATAL_ERROR_INVALID_STATE
;
122 nsTArray
<Input
> candidates
;
123 for (const auto& root
: mTrustedRoots
) {
125 pkix::Result rv
= rootInput
.Init(root
.Elements(), root
.Length());
126 // This should never fail, since the possible roots are all hard-coded and
127 // they should never be too long.
131 candidates
.AppendElement(std::move(rootInput
));
133 for (const auto& intermediate
: mAddonsIntermediates
) {
134 Input intermediateInput
;
136 intermediateInput
.Init(intermediate
.Elements(), intermediate
.Length());
137 // Again, this should never fail for the same reason as above.
141 candidates
.AppendElement(std::move(intermediateInput
));
143 for (const auto& intermediate
: mIntermediates
) {
144 Input intermediateInput
;
146 intermediateInput
.Init(intermediate
.Elements(), intermediate
.Length());
147 // This is untrusted input, so skip any intermediates that are too large.
151 candidates
.AppendElement(std::move(intermediateInput
));
154 for (const auto& candidate
: candidates
) {
156 pkix::Result rv
= checker
.Check(
157 candidate
, nullptr /*additionalNameConstraints*/, keepGoing
);
166 // If the above did not succeed in building a verified certificate chain,
167 // fall back to searching for candidates in NSS. This is important in case an
168 // intermediate involved in add-on signing expires before it is replaced. See
170 SECItem encodedIssuerNameSECItem
= UnsafeMapInputToSECItem(encodedIssuerName
);
171 UniqueCERTCertList
nssCandidates(CERT_CreateSubjectCertList(
172 nullptr, CERT_GetDefaultCertDB(), &encodedIssuerNameSECItem
, 0, false));
174 for (CERTCertListNode
* n
= CERT_LIST_HEAD(nssCandidates
);
175 !CERT_LIST_END(n
, nssCandidates
); n
= CERT_LIST_NEXT(n
)) {
178 certDER
.Init(n
->cert
->derCert
.data
, n
->cert
->derCert
.len
);
180 continue; // probably too big
184 rv
= checker
.Check(certDER
, nullptr /*additionalNameConstraints*/,
198 pkix::Result
AppTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA
,
199 const CertPolicyId
& policy
,
200 Input candidateCertDER
,
201 /*out*/ TrustLevel
& trustLevel
) {
202 MOZ_ASSERT(policy
.IsAnyPolicy());
203 MOZ_ASSERT(!mTrustedRoots
.IsEmpty());
204 if (!policy
.IsAnyPolicy()) {
205 return pkix::Result::FATAL_ERROR_INVALID_ARGS
;
207 if (mTrustedRoots
.IsEmpty()) {
208 return pkix::Result::FATAL_ERROR_INVALID_STATE
;
211 nsTArray
<uint8_t> issuerBytes
;
212 nsTArray
<uint8_t> serialBytes
;
213 nsTArray
<uint8_t> subjectBytes
;
214 nsTArray
<uint8_t> pubKeyBytes
;
216 pkix::Result result
=
217 BuildRevocationCheckArrays(candidateCertDER
, endEntityOrCA
, issuerBytes
,
218 serialBytes
, subjectBytes
, pubKeyBytes
);
219 if (result
!= Success
) {
223 int16_t revocationState
;
224 nsresult nsrv
= mCertBlocklist
->GetRevocationState(
225 issuerBytes
, serialBytes
, subjectBytes
, pubKeyBytes
, &revocationState
);
226 if (NS_FAILED(nsrv
)) {
227 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE
;
230 if (revocationState
== nsICertStorage::STATE_ENFORCE
) {
231 return pkix::Result::ERROR_REVOKED_CERTIFICATE
;
234 // mTrustedRoots are the only trust anchors for this validation.
235 Span
<const uint8_t> candidateCertDERSpan
= {candidateCertDER
.UnsafeGetData(),
236 candidateCertDER
.GetLength()};
237 for (const auto& trustedRoot
: mTrustedRoots
) {
238 if (trustedRoot
== candidateCertDERSpan
) {
239 trustLevel
= TrustLevel::TrustAnchor
;
244 trustLevel
= TrustLevel::InheritsTrust
;
248 pkix::Result
AppTrustDomain::DigestBuf(Input item
, DigestAlgorithm digestAlg
,
249 /*out*/ uint8_t* digestBuf
,
250 size_t digestBufLen
) {
251 return DigestBufNSS(item
, digestAlg
, digestBuf
, digestBufLen
);
254 pkix::Result
AppTrustDomain::CheckRevocation(EndEntityOrCA
, const CertID
&, Time
,
256 /*optional*/ const Input
*,
257 /*optional*/ const Input
*,
258 /*optional*/ const Input
*) {
259 // We don't currently do revocation checking. If we need to distrust an Apps
260 // certificate, we will use the active distrust mechanism.
264 pkix::Result
AppTrustDomain::IsChainValid(const DERArray
& certChain
, Time time
,
265 const CertPolicyId
& requiredPolicy
) {
266 MOZ_ASSERT(requiredPolicy
.IsAnyPolicy());
270 pkix::Result
AppTrustDomain::CheckSignatureDigestAlgorithm(
271 DigestAlgorithm digestAlg
, EndEntityOrCA
, Time
) {
273 case DigestAlgorithm::sha256
: // fall through
274 case DigestAlgorithm::sha384
: // fall through
275 case DigestAlgorithm::sha512
:
277 case DigestAlgorithm::sha1
:
278 return pkix::Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
;
280 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE
;
283 pkix::Result
AppTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
284 EndEntityOrCA
/*endEntityOrCA*/, unsigned int modulusSizeInBits
) {
285 if (modulusSizeInBits
< 2048u) {
286 return pkix::Result::ERROR_INADEQUATE_KEY_SIZE
;
291 pkix::Result
AppTrustDomain::VerifyRSAPKCS1SignedData(
292 Input data
, DigestAlgorithm digestAlgorithm
, Input signature
,
293 Input subjectPublicKeyInfo
) {
294 // TODO: We should restrict signatures to SHA-256 or better.
295 return VerifyRSAPKCS1SignedDataNSS(data
, digestAlgorithm
, signature
,
296 subjectPublicKeyInfo
, nullptr);
299 pkix::Result
AppTrustDomain::VerifyRSAPSSSignedData(
300 Input data
, DigestAlgorithm digestAlgorithm
, Input signature
,
301 Input subjectPublicKeyInfo
) {
302 return VerifyRSAPSSSignedDataNSS(data
, digestAlgorithm
, signature
,
303 subjectPublicKeyInfo
, nullptr);
306 pkix::Result
AppTrustDomain::CheckECDSACurveIsAcceptable(
307 EndEntityOrCA
/*endEntityOrCA*/, NamedCurve curve
) {
309 case NamedCurve::secp256r1
: // fall through
310 case NamedCurve::secp384r1
: // fall through
311 case NamedCurve::secp521r1
:
315 return pkix::Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE
;
318 pkix::Result
AppTrustDomain::VerifyECDSASignedData(
319 Input data
, DigestAlgorithm digestAlgorithm
, Input signature
,
320 Input subjectPublicKeyInfo
) {
321 return VerifyECDSASignedDataNSS(data
, digestAlgorithm
, signature
,
322 subjectPublicKeyInfo
, nullptr);
325 pkix::Result
AppTrustDomain::CheckValidityIsAcceptable(
326 Time
/*notBefore*/, Time
/*notAfter*/, EndEntityOrCA
/*endEntityOrCA*/,
327 KeyPurposeId
/*keyPurpose*/) {
331 pkix::Result
AppTrustDomain::NetscapeStepUpMatchesServerAuth(
333 /*out*/ bool& matches
) {
338 void AppTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension
/*extension*/,
339 Input
/*extensionData*/) {}
342 } // namespace mozilla