Bug 1853320 [wpt PR 41940] - [FLEDGE] Split up runs of /trusted-scoring-signals.https...
[gecko.git] / security / ct / CTLogVerifier.cpp
blob67271926225f85ca83a217d96e6a2c4d3eabfa78
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 "hasht.h"
13 #include "mozpkix/pkixnss.h"
14 #include "mozpkix/pkixutil.h"
16 namespace mozilla {
17 namespace ct {
19 using namespace mozilla::pkix;
21 // A TrustDomain used to extract the SCT log signature parameters
22 // given its subjectPublicKeyInfo.
23 // Only RSASSA-PKCS1v15 with SHA-256 and ECDSA (using the NIST P-256 curve)
24 // with SHA-256 are allowed.
25 // RSA keys must be at least 2048 bits.
26 // See See RFC 6962, Section 2.1.4.
27 class SignatureParamsTrustDomain final : public TrustDomain {
28 public:
29 SignatureParamsTrustDomain()
30 : mSignatureAlgorithm(DigitallySigned::SignatureAlgorithm::Anonymous) {}
32 Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input,
33 TrustLevel&) override {
34 return Result::FATAL_ERROR_LIBRARY_FAILURE;
37 Result FindIssuer(Input, IssuerChecker&, Time) override {
38 return Result::FATAL_ERROR_LIBRARY_FAILURE;
41 Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
42 const Input*, const Input*, const Input*) override {
43 return Result::FATAL_ERROR_LIBRARY_FAILURE;
46 Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override {
47 return Result::FATAL_ERROR_LIBRARY_FAILURE;
50 Result DigestBuf(Input, DigestAlgorithm, uint8_t*, size_t) override {
51 return Result::FATAL_ERROR_LIBRARY_FAILURE;
54 Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA,
55 Time) override {
56 return Result::FATAL_ERROR_LIBRARY_FAILURE;
59 Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve curve) override {
60 assert(mSignatureAlgorithm ==
61 DigitallySigned::SignatureAlgorithm::Anonymous);
62 if (curve != NamedCurve::secp256r1) {
63 return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE;
65 mSignatureAlgorithm = DigitallySigned::SignatureAlgorithm::ECDSA;
66 return Success;
69 Result VerifyECDSASignedData(Input, DigestAlgorithm, Input, Input) override {
70 return Result::FATAL_ERROR_LIBRARY_FAILURE;
73 Result CheckRSAPublicKeyModulusSizeInBits(
74 EndEntityOrCA, unsigned int modulusSizeInBits) override {
75 assert(mSignatureAlgorithm ==
76 DigitallySigned::SignatureAlgorithm::Anonymous);
77 // Require RSA keys of at least 2048 bits. See RFC 6962, Section 2.1.4.
78 if (modulusSizeInBits < 2048) {
79 return Result::ERROR_INADEQUATE_KEY_SIZE;
81 mSignatureAlgorithm = DigitallySigned::SignatureAlgorithm::RSA;
82 return Success;
85 Result VerifyRSAPKCS1SignedData(Input, DigestAlgorithm, Input,
86 Input) override {
87 return Result::FATAL_ERROR_LIBRARY_FAILURE;
90 Result VerifyRSAPSSSignedData(Input, DigestAlgorithm, Input, Input) override {
91 return Result::FATAL_ERROR_LIBRARY_FAILURE;
94 Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA,
95 KeyPurposeId) override {
96 return Result::FATAL_ERROR_LIBRARY_FAILURE;
99 Result NetscapeStepUpMatchesServerAuth(Time, bool&) override {
100 return Result::FATAL_ERROR_LIBRARY_FAILURE;
103 void NoteAuxiliaryExtension(AuxiliaryExtension, Input) override {}
105 DigitallySigned::SignatureAlgorithm mSignatureAlgorithm;
108 CTLogVerifier::CTLogVerifier()
109 : mSignatureAlgorithm(DigitallySigned::SignatureAlgorithm::Anonymous),
110 mOperatorId(-1),
111 mDisqualified(false),
112 mDisqualificationTime(UINT64_MAX) {}
114 Result CTLogVerifier::Init(Input subjectPublicKeyInfo,
115 CTLogOperatorId operatorId, CTLogStatus logStatus,
116 uint64_t disqualificationTime) {
117 switch (logStatus) {
118 case CTLogStatus::Included:
119 mDisqualified = false;
120 mDisqualificationTime = UINT64_MAX;
121 break;
122 case CTLogStatus::Disqualified:
123 mDisqualified = true;
124 mDisqualificationTime = disqualificationTime;
125 break;
126 case CTLogStatus::Unknown:
127 default:
128 assert(false);
129 return Result::FATAL_ERROR_INVALID_ARGS;
132 SignatureParamsTrustDomain trustDomain;
133 Result rv = CheckSubjectPublicKeyInfo(subjectPublicKeyInfo, trustDomain,
134 EndEntityOrCA::MustBeEndEntity);
135 if (rv != Success) {
136 return rv;
138 mSignatureAlgorithm = trustDomain.mSignatureAlgorithm;
140 InputToBuffer(subjectPublicKeyInfo, mSubjectPublicKeyInfo);
142 if (mSignatureAlgorithm == DigitallySigned::SignatureAlgorithm::ECDSA) {
143 SECItem spkiSECItem = {
144 siBuffer, mSubjectPublicKeyInfo.data(),
145 static_cast<unsigned int>(mSubjectPublicKeyInfo.size())};
146 UniqueCERTSubjectPublicKeyInfo spki(
147 SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiSECItem));
148 if (!spki) {
149 return MapPRErrorCodeToResult(PR_GetError());
151 mPublicECKey.reset(SECKEY_ExtractPublicKey(spki.get()));
152 if (!mPublicECKey) {
153 return MapPRErrorCodeToResult(PR_GetError());
155 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
156 if (!slot) {
157 return MapPRErrorCodeToResult(PR_GetError());
159 CK_OBJECT_HANDLE handle =
160 PK11_ImportPublicKey(slot.get(), mPublicECKey.get(), false);
161 if (handle == CK_INVALID_HANDLE) {
162 return MapPRErrorCodeToResult(PR_GetError());
164 } else {
165 mPublicECKey.reset(nullptr);
168 mKeyId.resize(SHA256_LENGTH);
169 rv = DigestBufNSS(subjectPublicKeyInfo, DigestAlgorithm::sha256,
170 mKeyId.data(), mKeyId.size());
171 if (rv != Success) {
172 return rv;
175 mOperatorId = operatorId;
176 return Success;
179 Result CTLogVerifier::Verify(const LogEntry& entry,
180 const SignedCertificateTimestamp& sct) {
181 if (mKeyId.empty() || sct.logId != mKeyId) {
182 return Result::FATAL_ERROR_INVALID_ARGS;
184 if (!SignatureParametersMatch(sct.signature)) {
185 return Result::FATAL_ERROR_INVALID_ARGS;
188 Buffer serializedLogEntry;
189 Result rv = EncodeLogEntry(entry, serializedLogEntry);
190 if (rv != Success) {
191 return rv;
194 Input logEntryInput;
195 rv = BufferToInput(serializedLogEntry, logEntryInput);
196 if (rv != Success) {
197 return rv;
200 // sct.extensions may be empty. If it is, sctExtensionsInput will remain in
201 // its default state, which is valid but of length 0.
202 Input sctExtensionsInput;
203 if (!sct.extensions.empty()) {
204 rv = sctExtensionsInput.Init(sct.extensions.data(), sct.extensions.size());
205 if (rv != Success) {
206 return rv;
210 Buffer serializedData;
211 rv = EncodeV1SCTSignedData(sct.timestamp, logEntryInput, sctExtensionsInput,
212 serializedData);
213 if (rv != Success) {
214 return rv;
216 return VerifySignature(serializedData, sct.signature.signatureData);
219 bool CTLogVerifier::SignatureParametersMatch(const DigitallySigned& signature) {
220 return signature.SignatureParametersMatch(
221 DigitallySigned::HashAlgorithm::SHA256, mSignatureAlgorithm);
224 static Result FasterVerifyECDSASignedDataNSS(Input data, Input signature,
225 UniqueSECKEYPublicKey& pubkey) {
226 assert(pubkey);
227 if (!pubkey) {
228 return Result::FATAL_ERROR_LIBRARY_FAILURE;
230 // The signature is encoded as a DER SEQUENCE of two INTEGERs. PK11_Verify
231 // expects the signature as only the two integers r and s (so no encoding -
232 // just two series of bytes each half as long as SECKEY_SignatureLen(pubkey)).
233 // DSAU_DecodeDerSigToLen converts from the former format to the latter.
234 SECItem derSignatureSECItem(UnsafeMapInputToSECItem(signature));
235 size_t signatureLen = SECKEY_SignatureLen(pubkey.get());
236 if (signatureLen == 0) {
237 return MapPRErrorCodeToResult(PR_GetError());
239 UniqueSECItem signatureSECItem(
240 DSAU_DecodeDerSigToLen(&derSignatureSECItem, signatureLen));
241 if (!signatureSECItem) {
242 return MapPRErrorCodeToResult(PR_GetError());
244 SECItem dataSECItem(UnsafeMapInputToSECItem(data));
245 SECStatus srv =
246 PK11_VerifyWithMechanism(pubkey.get(), CKM_ECDSA_SHA256, nullptr,
247 signatureSECItem.get(), &dataSECItem, nullptr);
248 if (srv != SECSuccess) {
249 return MapPRErrorCodeToResult(PR_GetError());
251 return Success;
254 Result CTLogVerifier::VerifySignature(Input data, Input signature) {
255 Input spki;
256 Result rv = BufferToInput(mSubjectPublicKeyInfo, spki);
257 if (rv != Success) {
258 return rv;
261 switch (mSignatureAlgorithm) {
262 case DigitallySigned::SignatureAlgorithm::RSA:
263 rv = VerifyRSAPKCS1SignedDataNSS(data, DigestAlgorithm::sha256, signature,
264 spki, nullptr);
265 break;
266 case DigitallySigned::SignatureAlgorithm::ECDSA:
267 rv = FasterVerifyECDSASignedDataNSS(data, signature, mPublicECKey);
268 break;
269 // We do not expect new values added to this enum any time soon,
270 // so just listing all the available ones seems to be the easiest way
271 // to suppress warning C4061 on MSVC (which expects all values of the
272 // enum to be explicitly handled).
273 case DigitallySigned::SignatureAlgorithm::Anonymous:
274 case DigitallySigned::SignatureAlgorithm::DSA:
275 default:
276 assert(false);
277 return Result::FATAL_ERROR_INVALID_ARGS;
279 if (rv != Success) {
280 if (IsFatalError(rv)) {
281 return rv;
283 // If the error is non-fatal, we assume the signature was invalid.
284 return Result::ERROR_BAD_SIGNATURE;
286 return Success;
289 Result CTLogVerifier::VerifySignature(const Buffer& data,
290 const Buffer& signature) {
291 Input dataInput;
292 Result rv = BufferToInput(data, dataInput);
293 if (rv != Success) {
294 return rv;
296 Input signatureInput;
297 rv = BufferToInput(signature, signatureInput);
298 if (rv != Success) {
299 return rv;
301 return VerifySignature(dataInput, signatureInput);
304 } // namespace ct
305 } // namespace mozilla