Line data Source code
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/. */
6 :
7 : #include "CTLogVerifier.h"
8 :
9 : #include <stdint.h>
10 :
11 : #include "CTSerialization.h"
12 : #include "hasht.h"
13 : #include "mozilla/ArrayUtils.h"
14 : #include "mozilla/Assertions.h"
15 : #include "pkix/pkixnss.h"
16 : #include "pkixutil.h"
17 :
18 : namespace mozilla { namespace ct {
19 :
20 : using namespace mozilla::pkix;
21 :
22 : // A TrustDomain used to extract the SCT log signature parameters
23 : // given its subjectPublicKeyInfo.
24 : // Only RSASSA-PKCS1v15 with SHA-256 and ECDSA (using the NIST P-256 curve)
25 : // with SHA-256 are allowed.
26 : // RSA keys must be at least 2048 bits.
27 : // See See RFC 6962, Section 2.1.4.
28 15 : class SignatureParamsTrustDomain final : public TrustDomain
29 : {
30 : public:
31 15 : SignatureParamsTrustDomain()
32 15 : : mSignatureAlgorithm(DigitallySigned::SignatureAlgorithm::Anonymous)
33 : {
34 15 : }
35 :
36 0 : Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input,
37 : TrustLevel&) override
38 : {
39 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
40 : }
41 :
42 0 : Result FindIssuer(Input, IssuerChecker&, Time) override
43 : {
44 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
45 : }
46 :
47 0 : Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
48 : const Input*, const Input*) override
49 : {
50 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
51 : }
52 :
53 0 : Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override
54 : {
55 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
56 : }
57 :
58 0 : Result DigestBuf(Input, DigestAlgorithm, uint8_t*, size_t) override
59 : {
60 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
61 : }
62 :
63 0 : Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA,
64 : Time) override
65 : {
66 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
67 : }
68 :
69 11 : Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve curve) override
70 : {
71 11 : MOZ_ASSERT(mSignatureAlgorithm ==
72 : DigitallySigned::SignatureAlgorithm::Anonymous);
73 11 : if (curve != NamedCurve::secp256r1) {
74 0 : return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE;
75 : }
76 11 : mSignatureAlgorithm = DigitallySigned::SignatureAlgorithm::ECDSA;
77 11 : return Success;
78 : }
79 :
80 0 : Result VerifyECDSASignedDigest(const SignedDigest&, Input) override
81 : {
82 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
83 : }
84 :
85 4 : Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA,
86 : unsigned int modulusSizeInBits)
87 : override
88 : {
89 4 : MOZ_ASSERT(mSignatureAlgorithm ==
90 : DigitallySigned::SignatureAlgorithm::Anonymous);
91 : // Require RSA keys of at least 2048 bits. See RFC 6962, Section 2.1.4.
92 4 : if (modulusSizeInBits < 2048) {
93 0 : return Result::ERROR_INADEQUATE_KEY_SIZE;
94 : }
95 4 : mSignatureAlgorithm = DigitallySigned::SignatureAlgorithm::RSA;
96 4 : return Success;
97 : }
98 :
99 0 : Result VerifyRSAPKCS1SignedDigest(const SignedDigest&, Input) override
100 : {
101 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
102 : }
103 :
104 0 : Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA,
105 : KeyPurposeId) override
106 : {
107 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
108 : }
109 :
110 0 : Result NetscapeStepUpMatchesServerAuth(Time, bool&) override
111 : {
112 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
113 : }
114 :
115 0 : void NoteAuxiliaryExtension(AuxiliaryExtension, Input) override
116 : {
117 0 : }
118 :
119 : DigitallySigned::SignatureAlgorithm mSignatureAlgorithm;
120 : };
121 :
122 :
123 15 : CTLogVerifier::CTLogVerifier()
124 : : mSignatureAlgorithm(DigitallySigned::SignatureAlgorithm::Anonymous)
125 : , mOperatorId(-1)
126 : , mDisqualified(false)
127 15 : , mDisqualificationTime(UINT64_MAX)
128 : {
129 15 : }
130 :
131 : Result
132 15 : CTLogVerifier::Init(Input subjectPublicKeyInfo,
133 : CTLogOperatorId operatorId,
134 : CTLogStatus logStatus,
135 : uint64_t disqualificationTime)
136 : {
137 15 : switch (logStatus) {
138 : case CTLogStatus::Included:
139 13 : mDisqualified = false;
140 13 : mDisqualificationTime = UINT64_MAX;
141 13 : break;
142 : case CTLogStatus::Disqualified:
143 2 : mDisqualified = true;
144 2 : mDisqualificationTime = disqualificationTime;
145 2 : break;
146 : case CTLogStatus::Unknown:
147 : default:
148 0 : MOZ_ASSERT_UNREACHABLE("Unsupported CTLogStatus");
149 : return Result::FATAL_ERROR_INVALID_ARGS;
150 : }
151 :
152 30 : SignatureParamsTrustDomain trustDomain;
153 : Result rv = CheckSubjectPublicKeyInfo(subjectPublicKeyInfo, trustDomain,
154 15 : EndEntityOrCA::MustBeEndEntity);
155 15 : if (rv != Success) {
156 0 : return rv;
157 : }
158 15 : mSignatureAlgorithm = trustDomain.mSignatureAlgorithm;
159 :
160 15 : rv = InputToBuffer(subjectPublicKeyInfo, mSubjectPublicKeyInfo);
161 15 : if (rv != Success) {
162 0 : return rv;
163 : }
164 :
165 15 : if (mSignatureAlgorithm == DigitallySigned::SignatureAlgorithm::ECDSA) {
166 : SECItem spkiSECItem = {
167 : siBuffer,
168 11 : mSubjectPublicKeyInfo.begin(),
169 11 : static_cast<unsigned int>(mSubjectPublicKeyInfo.length())
170 33 : };
171 : UniqueCERTSubjectPublicKeyInfo spki(
172 22 : SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiSECItem));
173 11 : if (!spki) {
174 0 : return MapPRErrorCodeToResult(PR_GetError());
175 : }
176 11 : mPublicECKey.reset(SECKEY_ExtractPublicKey(spki.get()));
177 11 : if (!mPublicECKey) {
178 0 : return MapPRErrorCodeToResult(PR_GetError());
179 : }
180 22 : UniquePK11SlotInfo slot(PK11_GetInternalSlot());
181 11 : if (!slot) {
182 0 : return MapPRErrorCodeToResult(PR_GetError());
183 : }
184 11 : CK_OBJECT_HANDLE handle = PK11_ImportPublicKey(slot.get(),
185 11 : mPublicECKey.get(), false);
186 11 : if (handle == CK_INVALID_HANDLE) {
187 0 : return MapPRErrorCodeToResult(PR_GetError());
188 : }
189 : } else {
190 4 : mPublicECKey.reset(nullptr);
191 : }
192 :
193 15 : if (!mKeyId.resizeUninitialized(SHA256_LENGTH)) {
194 0 : return Result::FATAL_ERROR_NO_MEMORY;
195 : }
196 15 : rv = DigestBufNSS(subjectPublicKeyInfo, DigestAlgorithm::sha256,
197 15 : mKeyId.begin(), mKeyId.length());
198 15 : if (rv != Success) {
199 0 : return rv;
200 : }
201 :
202 15 : mOperatorId = operatorId;
203 15 : return Success;
204 : }
205 :
206 : Result
207 0 : CTLogVerifier::Verify(const LogEntry& entry,
208 : const SignedCertificateTimestamp& sct)
209 : {
210 0 : if (mKeyId.empty() || sct.logId != mKeyId) {
211 0 : return Result::FATAL_ERROR_INVALID_ARGS;
212 : }
213 0 : if (!SignatureParametersMatch(sct.signature)) {
214 0 : return Result::FATAL_ERROR_INVALID_ARGS;
215 : }
216 :
217 0 : Buffer serializedLogEntry;
218 0 : Result rv = EncodeLogEntry(entry, serializedLogEntry);
219 0 : if (rv != Success) {
220 0 : return rv;
221 : }
222 :
223 0 : Input logEntryInput;
224 0 : rv = BufferToInput(serializedLogEntry, logEntryInput);
225 0 : if (rv != Success) {
226 0 : return rv;
227 : }
228 :
229 : // sct.extensions may be empty. If it is, sctExtensionsInput will remain in
230 : // its default state, which is valid but of length 0.
231 0 : Input sctExtensionsInput;
232 0 : if (sct.extensions.length() > 0) {
233 0 : rv = sctExtensionsInput.Init(sct.extensions.begin(),
234 0 : sct.extensions.length());
235 0 : if (rv != Success) {
236 0 : return rv;
237 : }
238 : }
239 :
240 0 : Buffer serializedData;
241 0 : rv = EncodeV1SCTSignedData(sct.timestamp, logEntryInput, sctExtensionsInput,
242 0 : serializedData);
243 0 : if (rv != Success) {
244 0 : return rv;
245 : }
246 0 : return VerifySignature(serializedData, sct.signature.signatureData);
247 : }
248 :
249 : Result
250 0 : CTLogVerifier::VerifySignedTreeHead(const SignedTreeHead& sth)
251 : {
252 0 : if (!SignatureParametersMatch(sth.signature)) {
253 0 : return Result::FATAL_ERROR_INVALID_ARGS;
254 : }
255 :
256 0 : Buffer serializedData;
257 0 : Result rv = EncodeTreeHeadSignature(sth, serializedData);
258 0 : if (rv != Success) {
259 0 : return rv;
260 : }
261 0 : return VerifySignature(serializedData, sth.signature.signatureData);
262 : }
263 :
264 : bool
265 0 : CTLogVerifier::SignatureParametersMatch(const DigitallySigned& signature)
266 : {
267 0 : return signature.SignatureParametersMatch(
268 0 : DigitallySigned::HashAlgorithm::SHA256, mSignatureAlgorithm);
269 : }
270 :
271 : static Result
272 0 : FasterVerifyECDSASignedDigestNSS(const SignedDigest& sd,
273 : UniqueSECKEYPublicKey& pubkey)
274 : {
275 0 : MOZ_ASSERT(pubkey);
276 0 : if (!pubkey) {
277 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
278 : }
279 : // The signature is encoded as a DER SEQUENCE of two INTEGERs. PK11_Verify
280 : // expects the signature as only the two integers r and s (so no encoding -
281 : // just two series of bytes each half as long as SECKEY_SignatureLen(pubkey)).
282 : // DSAU_DecodeDerSigToLen converts from the former format to the latter.
283 0 : SECItem derSignatureSECItem(UnsafeMapInputToSECItem(sd.signature));
284 0 : size_t signatureLen = SECKEY_SignatureLen(pubkey.get());
285 0 : if (signatureLen == 0) {
286 0 : return MapPRErrorCodeToResult(PR_GetError());
287 : }
288 : UniqueSECItem signatureSECItem(DSAU_DecodeDerSigToLen(&derSignatureSECItem,
289 0 : signatureLen));
290 0 : if (!signatureSECItem) {
291 0 : return MapPRErrorCodeToResult(PR_GetError());
292 : }
293 0 : SECItem digestSECItem(UnsafeMapInputToSECItem(sd.digest));
294 0 : SECStatus srv = PK11_Verify(pubkey.get(), signatureSECItem.get(),
295 0 : &digestSECItem, nullptr);
296 0 : if (srv != SECSuccess) {
297 0 : return MapPRErrorCodeToResult(PR_GetError());
298 : }
299 :
300 0 : return Success;
301 : }
302 :
303 : Result
304 0 : CTLogVerifier::VerifySignature(Input data, Input signature)
305 : {
306 : uint8_t digest[SHA256_LENGTH];
307 0 : Result rv = DigestBufNSS(data, DigestAlgorithm::sha256, digest,
308 0 : ArrayLength(digest));
309 0 : if (rv != Success) {
310 0 : return rv;
311 : }
312 :
313 0 : SignedDigest signedDigest;
314 0 : signedDigest.digestAlgorithm = DigestAlgorithm::sha256;
315 0 : rv = signedDigest.digest.Init(digest, ArrayLength(digest));
316 0 : if (rv != Success) {
317 0 : return rv;
318 : }
319 0 : rv = signedDigest.signature.Init(signature);
320 0 : if (rv != Success) {
321 0 : return rv;
322 : }
323 :
324 0 : Input spki;
325 0 : rv = BufferToInput(mSubjectPublicKeyInfo, spki);
326 0 : if (rv != Success) {
327 0 : return rv;
328 : }
329 :
330 0 : switch (mSignatureAlgorithm) {
331 : case DigitallySigned::SignatureAlgorithm::RSA:
332 0 : rv = VerifyRSAPKCS1SignedDigestNSS(signedDigest, spki, nullptr);
333 0 : break;
334 : case DigitallySigned::SignatureAlgorithm::ECDSA:
335 0 : rv = FasterVerifyECDSASignedDigestNSS(signedDigest, mPublicECKey);
336 0 : break;
337 : // We do not expect new values added to this enum any time soon,
338 : // so just listing all the available ones seems to be the easiest way
339 : // to suppress warning C4061 on MSVC (which expects all values of the
340 : // enum to be explicitly handled).
341 : case DigitallySigned::SignatureAlgorithm::Anonymous:
342 : case DigitallySigned::SignatureAlgorithm::DSA:
343 : default:
344 0 : MOZ_ASSERT_UNREACHABLE("RSA/ECDSA expected");
345 : return Result::FATAL_ERROR_INVALID_ARGS;
346 : }
347 0 : if (rv != Success) {
348 0 : if (IsFatalError(rv)) {
349 0 : return rv;
350 : }
351 : // If the error is non-fatal, we assume the signature was invalid.
352 0 : return Result::ERROR_BAD_SIGNATURE;
353 : }
354 0 : return Success;
355 : }
356 :
357 : Result
358 0 : CTLogVerifier::VerifySignature(const Buffer& data, const Buffer& signature)
359 : {
360 0 : Input dataInput;
361 0 : Result rv = BufferToInput(data, dataInput);
362 0 : if (rv != Success) {
363 0 : return rv;
364 : }
365 0 : Input signatureInput;
366 0 : rv = BufferToInput(signature, signatureInput);
367 0 : if (rv != Success) {
368 0 : return rv;
369 : }
370 0 : return VerifySignature(dataInput, signatureInput);
371 : }
372 :
373 : } } // namespace mozilla::ct
|