Bug 1936278 - Prevent search mode chiclet from being dismissed when clicking in page...
[gecko.git] / security / ct / CTLogVerifier.cpp
blobe0897d38dc8491077afa10fee427ed2bf17a646f
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 "CTLogVerifier.h"
9 #include <stdint.h>
11 #include "CTSerialization.h"
12 #include "CertVerifier.h"
13 #include "hasht.h"
14 #include "mozpkix/Result.h"
15 #include "mozpkix/pkixnss.h"
16 #include "mozpkix/pkixutil.h"
18 using namespace mozilla::pkix;
20 namespace mozilla {
21 namespace ct {
23 // A TrustDomain used to extract the SCT log signature parameters
24 // given its subjectPublicKeyInfo.
25 // Only RSASSA-PKCS1v15 with SHA-256 and ECDSA (using the NIST P-256 curve)
26 // with SHA-256 are allowed.
27 // RSA keys must be at least 2048 bits.
28 // See See RFC 6962, Section 2.1.4.
29 class SignatureParamsTrustDomain final : public TrustDomain {
30 public:
31 SignatureParamsTrustDomain()
32 : mSignatureAlgorithm(DigitallySigned::SignatureAlgorithm::Anonymous) {}
34 pkix::Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input,
35 TrustLevel&) override {
36 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
39 pkix::Result FindIssuer(Input, IssuerChecker&, Time) override {
40 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
43 pkix::Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
44 const Input*, const Input*,
45 const Input*) override {
46 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
49 pkix::Result IsChainValid(const DERArray&, Time,
50 const CertPolicyId&) override {
51 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
54 pkix::Result DigestBuf(Input, DigestAlgorithm, uint8_t*, size_t) override {
55 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
58 pkix::Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA,
59 Time) override {
60 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
63 pkix::Result CheckECDSACurveIsAcceptable(EndEntityOrCA,
64 NamedCurve curve) override {
65 assert(mSignatureAlgorithm ==
66 DigitallySigned::SignatureAlgorithm::Anonymous);
67 if (curve != NamedCurve::secp256r1) {
68 return pkix::Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE;
70 mSignatureAlgorithm = DigitallySigned::SignatureAlgorithm::ECDSA;
71 return Success;
74 pkix::Result VerifyECDSASignedData(Input, DigestAlgorithm, Input,
75 Input) override {
76 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
79 pkix::Result CheckRSAPublicKeyModulusSizeInBits(
80 EndEntityOrCA, unsigned int modulusSizeInBits) override {
81 assert(mSignatureAlgorithm ==
82 DigitallySigned::SignatureAlgorithm::Anonymous);
83 // Require RSA keys of at least 2048 bits. See RFC 6962, Section 2.1.4.
84 if (modulusSizeInBits < 2048) {
85 return pkix::Result::ERROR_INADEQUATE_KEY_SIZE;
87 mSignatureAlgorithm = DigitallySigned::SignatureAlgorithm::RSA;
88 return Success;
91 pkix::Result VerifyRSAPKCS1SignedData(Input, DigestAlgorithm, Input,
92 Input) override {
93 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
96 pkix::Result VerifyRSAPSSSignedData(Input, DigestAlgorithm, Input,
97 Input) override {
98 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
101 pkix::Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA,
102 KeyPurposeId) override {
103 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
106 pkix::Result NetscapeStepUpMatchesServerAuth(Time, bool&) override {
107 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
110 void NoteAuxiliaryExtension(AuxiliaryExtension, Input) override {}
112 DigitallySigned::SignatureAlgorithm mSignatureAlgorithm;
115 CTLogVerifier::CTLogVerifier(CTLogOperatorId operatorId, CTLogState state,
116 uint64_t timestamp)
117 : mSignatureAlgorithm(DigitallySigned::SignatureAlgorithm::Anonymous),
118 mOperatorId(operatorId),
119 mState(state),
120 mTimestamp(timestamp) {}
122 pkix::Result CTLogVerifier::Init(Input subjectPublicKeyInfo) {
123 SignatureParamsTrustDomain trustDomain;
124 pkix::Result rv = CheckSubjectPublicKeyInfo(subjectPublicKeyInfo, trustDomain,
125 EndEntityOrCA::MustBeEndEntity);
126 if (rv != Success) {
127 return rv;
129 mSignatureAlgorithm = trustDomain.mSignatureAlgorithm;
131 InputToBuffer(subjectPublicKeyInfo, mSubjectPublicKeyInfo);
133 mKeyId.resize(SHA256_LENGTH);
134 rv = DigestBufNSS(subjectPublicKeyInfo, DigestAlgorithm::sha256,
135 mKeyId.data(), mKeyId.size());
136 if (rv != Success) {
137 return rv;
140 return Success;
143 pkix::Result CTLogVerifier::Verify(const LogEntry& entry,
144 const SignedCertificateTimestamp& sct,
145 SignatureCache* signatureCache) {
146 if (mKeyId.empty() || sct.logId != mKeyId || !signatureCache) {
147 return pkix::Result::FATAL_ERROR_INVALID_ARGS;
149 if (!SignatureParametersMatch(sct.signature)) {
150 return pkix::Result::FATAL_ERROR_INVALID_ARGS;
153 Buffer serializedLogEntry;
154 pkix::Result rv = EncodeLogEntry(entry, serializedLogEntry);
155 if (rv != Success) {
156 return rv;
159 Input logEntryInput;
160 rv = BufferToInput(serializedLogEntry, logEntryInput);
161 if (rv != Success) {
162 return rv;
165 // sct.extensions may be empty. If it is, sctExtensionsInput will remain in
166 // its default state, which is valid but of length 0.
167 Input sctExtensionsInput;
168 if (!sct.extensions.empty()) {
169 rv = sctExtensionsInput.Init(sct.extensions.data(), sct.extensions.size());
170 if (rv != Success) {
171 return rv;
175 Buffer serializedData;
176 rv = EncodeV1SCTSignedData(sct.timestamp, logEntryInput, sctExtensionsInput,
177 serializedData);
178 if (rv != Success) {
179 return rv;
181 return VerifySignature(serializedData, sct.signature.signatureData,
182 signatureCache);
185 bool CTLogVerifier::SignatureParametersMatch(const DigitallySigned& signature) {
186 return signature.SignatureParametersMatch(
187 DigitallySigned::HashAlgorithm::SHA256, mSignatureAlgorithm);
190 pkix::Result CTLogVerifier::VerifySignature(Input data, Input signature,
191 SignatureCache* signatureCache) {
192 Input spki;
193 pkix::Result rv = BufferToInput(mSubjectPublicKeyInfo, spki);
194 if (rv != Success) {
195 return rv;
198 switch (mSignatureAlgorithm) {
199 case DigitallySigned::SignatureAlgorithm::RSA:
200 rv = psm::VerifySignedDataWithCache(
201 der::PublicKeyAlgorithm::RSA_PKCS1,
202 mozilla::glean::sct_signature_cache::total,
203 mozilla::glean::sct_signature_cache::hits, data,
204 DigestAlgorithm::sha256, signature, spki, signatureCache, nullptr);
205 break;
206 case DigitallySigned::SignatureAlgorithm::ECDSA:
207 rv = psm::VerifySignedDataWithCache(
208 der::PublicKeyAlgorithm::ECDSA,
209 mozilla::glean::sct_signature_cache::total,
210 mozilla::glean::sct_signature_cache::hits, data,
211 DigestAlgorithm::sha256, signature, spki, signatureCache, nullptr);
212 break;
213 // We do not expect new values added to this enum any time soon,
214 // so just listing all the available ones seems to be the easiest way
215 // to suppress warning C4061 on MSVC (which expects all values of the
216 // enum to be explicitly handled).
217 case DigitallySigned::SignatureAlgorithm::Anonymous:
218 case DigitallySigned::SignatureAlgorithm::DSA:
219 default:
220 assert(false);
221 return pkix::Result::FATAL_ERROR_INVALID_ARGS;
223 if (rv != Success) {
224 if (IsFatalError(rv)) {
225 return rv;
227 // If the error is non-fatal, we assume the signature was invalid.
228 return pkix::Result::ERROR_BAD_SIGNATURE;
230 return Success;
233 pkix::Result CTLogVerifier::VerifySignature(const Buffer& data,
234 const Buffer& signature,
235 SignatureCache* signatureCache) {
236 Input dataInput;
237 pkix::Result rv = BufferToInput(data, dataInput);
238 if (rv != Success) {
239 return rv;
241 Input signatureInput;
242 rv = BufferToInput(signature, signatureInput);
243 if (rv != Success) {
244 return rv;
246 return VerifySignature(dataInput, signatureInput, signatureCache);
249 } // namespace ct
250 } // namespace mozilla