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 "mozpkix/pkixnss.h"
14 #include "mozpkix/pkixutil.h"
15
16 namespace mozilla {
17 namespace ct {
18
19 using namespace mozilla::pkix;
20
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:
SignatureParamsTrustDomain()29 SignatureParamsTrustDomain()
30 : mSignatureAlgorithm(DigitallySigned::SignatureAlgorithm::Anonymous) {}
31
GetCertTrust(EndEntityOrCA,const CertPolicyId &,Input,TrustLevel &)32 Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input,
33 TrustLevel&) override {
34 return Result::FATAL_ERROR_LIBRARY_FAILURE;
35 }
36
FindIssuer(Input,IssuerChecker &,Time)37 Result FindIssuer(Input, IssuerChecker&, Time) override {
38 return Result::FATAL_ERROR_LIBRARY_FAILURE;
39 }
40
CheckRevocation(EndEntityOrCA,const CertID &,Time,Duration,const Input *,const Input *,const Input *)41 Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
42 const Input*, const Input*, const Input*) override {
43 return Result::FATAL_ERROR_LIBRARY_FAILURE;
44 }
45
IsChainValid(const DERArray &,Time,const CertPolicyId &)46 Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override {
47 return Result::FATAL_ERROR_LIBRARY_FAILURE;
48 }
49
DigestBuf(Input,DigestAlgorithm,uint8_t *,size_t)50 Result DigestBuf(Input, DigestAlgorithm, uint8_t*, size_t) override {
51 return Result::FATAL_ERROR_LIBRARY_FAILURE;
52 }
53
CheckSignatureDigestAlgorithm(DigestAlgorithm,EndEntityOrCA,Time)54 Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA,
55 Time) override {
56 return Result::FATAL_ERROR_LIBRARY_FAILURE;
57 }
58
CheckECDSACurveIsAcceptable(EndEntityOrCA,NamedCurve curve)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;
64 }
65 mSignatureAlgorithm = DigitallySigned::SignatureAlgorithm::ECDSA;
66 return Success;
67 }
68
VerifyECDSASignedDigest(const SignedDigest &,Input)69 Result VerifyECDSASignedDigest(const SignedDigest&, Input) override {
70 return Result::FATAL_ERROR_LIBRARY_FAILURE;
71 }
72
CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA,unsigned int modulusSizeInBits)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;
80 }
81 mSignatureAlgorithm = DigitallySigned::SignatureAlgorithm::RSA;
82 return Success;
83 }
84
VerifyRSAPKCS1SignedDigest(const SignedDigest &,Input)85 Result VerifyRSAPKCS1SignedDigest(const SignedDigest&, Input) override {
86 return Result::FATAL_ERROR_LIBRARY_FAILURE;
87 }
88
CheckValidityIsAcceptable(Time,Time,EndEntityOrCA,KeyPurposeId)89 Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA,
90 KeyPurposeId) override {
91 return Result::FATAL_ERROR_LIBRARY_FAILURE;
92 }
93
NetscapeStepUpMatchesServerAuth(Time,bool &)94 Result NetscapeStepUpMatchesServerAuth(Time, bool&) override {
95 return Result::FATAL_ERROR_LIBRARY_FAILURE;
96 }
97
NoteAuxiliaryExtension(AuxiliaryExtension,Input)98 void NoteAuxiliaryExtension(AuxiliaryExtension, Input) override {}
99
100 DigitallySigned::SignatureAlgorithm mSignatureAlgorithm;
101 };
102
CTLogVerifier()103 CTLogVerifier::CTLogVerifier()
104 : mSignatureAlgorithm(DigitallySigned::SignatureAlgorithm::Anonymous),
105 mOperatorId(-1),
106 mDisqualified(false),
107 mDisqualificationTime(UINT64_MAX) {}
108
Init(Input subjectPublicKeyInfo,CTLogOperatorId operatorId,CTLogStatus logStatus,uint64_t disqualificationTime)109 Result CTLogVerifier::Init(Input subjectPublicKeyInfo,
110 CTLogOperatorId operatorId, CTLogStatus logStatus,
111 uint64_t disqualificationTime) {
112 switch (logStatus) {
113 case CTLogStatus::Included:
114 mDisqualified = false;
115 mDisqualificationTime = UINT64_MAX;
116 break;
117 case CTLogStatus::Disqualified:
118 mDisqualified = true;
119 mDisqualificationTime = disqualificationTime;
120 break;
121 case CTLogStatus::Unknown:
122 default:
123 assert(false);
124 return Result::FATAL_ERROR_INVALID_ARGS;
125 }
126
127 SignatureParamsTrustDomain trustDomain;
128 Result rv = CheckSubjectPublicKeyInfo(subjectPublicKeyInfo, trustDomain,
129 EndEntityOrCA::MustBeEndEntity);
130 if (rv != Success) {
131 return rv;
132 }
133 mSignatureAlgorithm = trustDomain.mSignatureAlgorithm;
134
135 InputToBuffer(subjectPublicKeyInfo, mSubjectPublicKeyInfo);
136
137 if (mSignatureAlgorithm == DigitallySigned::SignatureAlgorithm::ECDSA) {
138 SECItem spkiSECItem = {
139 siBuffer, mSubjectPublicKeyInfo.data(),
140 static_cast<unsigned int>(mSubjectPublicKeyInfo.size())};
141 UniqueCERTSubjectPublicKeyInfo spki(
142 SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiSECItem));
143 if (!spki) {
144 return MapPRErrorCodeToResult(PR_GetError());
145 }
146 mPublicECKey.reset(SECKEY_ExtractPublicKey(spki.get()));
147 if (!mPublicECKey) {
148 return MapPRErrorCodeToResult(PR_GetError());
149 }
150 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
151 if (!slot) {
152 return MapPRErrorCodeToResult(PR_GetError());
153 }
154 CK_OBJECT_HANDLE handle =
155 PK11_ImportPublicKey(slot.get(), mPublicECKey.get(), false);
156 if (handle == CK_INVALID_HANDLE) {
157 return MapPRErrorCodeToResult(PR_GetError());
158 }
159 } else {
160 mPublicECKey.reset(nullptr);
161 }
162
163 mKeyId.resize(SHA256_LENGTH);
164 rv = DigestBufNSS(subjectPublicKeyInfo, DigestAlgorithm::sha256,
165 mKeyId.data(), mKeyId.size());
166 if (rv != Success) {
167 return rv;
168 }
169
170 mOperatorId = operatorId;
171 return Success;
172 }
173
Verify(const LogEntry & entry,const SignedCertificateTimestamp & sct)174 Result CTLogVerifier::Verify(const LogEntry& entry,
175 const SignedCertificateTimestamp& sct) {
176 if (mKeyId.empty() || sct.logId != mKeyId) {
177 return Result::FATAL_ERROR_INVALID_ARGS;
178 }
179 if (!SignatureParametersMatch(sct.signature)) {
180 return Result::FATAL_ERROR_INVALID_ARGS;
181 }
182
183 Buffer serializedLogEntry;
184 Result rv = EncodeLogEntry(entry, serializedLogEntry);
185 if (rv != Success) {
186 return rv;
187 }
188
189 Input logEntryInput;
190 rv = BufferToInput(serializedLogEntry, logEntryInput);
191 if (rv != Success) {
192 return rv;
193 }
194
195 // sct.extensions may be empty. If it is, sctExtensionsInput will remain in
196 // its default state, which is valid but of length 0.
197 Input sctExtensionsInput;
198 if (!sct.extensions.empty()) {
199 rv = sctExtensionsInput.Init(sct.extensions.data(), sct.extensions.size());
200 if (rv != Success) {
201 return rv;
202 }
203 }
204
205 Buffer serializedData;
206 rv = EncodeV1SCTSignedData(sct.timestamp, logEntryInput, sctExtensionsInput,
207 serializedData);
208 if (rv != Success) {
209 return rv;
210 }
211 return VerifySignature(serializedData, sct.signature.signatureData);
212 }
213
SignatureParametersMatch(const DigitallySigned & signature)214 bool CTLogVerifier::SignatureParametersMatch(const DigitallySigned& signature) {
215 return signature.SignatureParametersMatch(
216 DigitallySigned::HashAlgorithm::SHA256, mSignatureAlgorithm);
217 }
218
FasterVerifyECDSASignedDigestNSS(const SignedDigest & sd,UniqueSECKEYPublicKey & pubkey)219 static Result FasterVerifyECDSASignedDigestNSS(const SignedDigest& sd,
220 UniqueSECKEYPublicKey& pubkey) {
221 assert(pubkey);
222 if (!pubkey) {
223 return Result::FATAL_ERROR_LIBRARY_FAILURE;
224 }
225 // The signature is encoded as a DER SEQUENCE of two INTEGERs. PK11_Verify
226 // expects the signature as only the two integers r and s (so no encoding -
227 // just two series of bytes each half as long as SECKEY_SignatureLen(pubkey)).
228 // DSAU_DecodeDerSigToLen converts from the former format to the latter.
229 SECItem derSignatureSECItem(UnsafeMapInputToSECItem(sd.signature));
230 size_t signatureLen = SECKEY_SignatureLen(pubkey.get());
231 if (signatureLen == 0) {
232 return MapPRErrorCodeToResult(PR_GetError());
233 }
234 UniqueSECItem signatureSECItem(
235 DSAU_DecodeDerSigToLen(&derSignatureSECItem, signatureLen));
236 if (!signatureSECItem) {
237 return MapPRErrorCodeToResult(PR_GetError());
238 }
239 SECItem digestSECItem(UnsafeMapInputToSECItem(sd.digest));
240 SECStatus srv = PK11_Verify(pubkey.get(), signatureSECItem.get(),
241 &digestSECItem, nullptr);
242 if (srv != SECSuccess) {
243 return MapPRErrorCodeToResult(PR_GetError());
244 }
245
246 return Success;
247 }
248
VerifySignature(Input data,Input signature)249 Result CTLogVerifier::VerifySignature(Input data, Input signature) {
250 uint8_t digest[SHA256_LENGTH];
251 Result rv = DigestBufNSS(data, DigestAlgorithm::sha256, digest,
252 MOZILLA_CT_ARRAY_LENGTH(digest));
253 if (rv != Success) {
254 return rv;
255 }
256
257 SignedDigest signedDigest;
258 signedDigest.digestAlgorithm = DigestAlgorithm::sha256;
259 rv = signedDigest.digest.Init(digest, MOZILLA_CT_ARRAY_LENGTH(digest));
260 if (rv != Success) {
261 return rv;
262 }
263 rv = signedDigest.signature.Init(signature);
264 if (rv != Success) {
265 return rv;
266 }
267
268 Input spki;
269 rv = BufferToInput(mSubjectPublicKeyInfo, spki);
270 if (rv != Success) {
271 return rv;
272 }
273
274 switch (mSignatureAlgorithm) {
275 case DigitallySigned::SignatureAlgorithm::RSA:
276 rv = VerifyRSAPKCS1SignedDigestNSS(signedDigest, spki, nullptr);
277 break;
278 case DigitallySigned::SignatureAlgorithm::ECDSA:
279 rv = FasterVerifyECDSASignedDigestNSS(signedDigest, mPublicECKey);
280 break;
281 // We do not expect new values added to this enum any time soon,
282 // so just listing all the available ones seems to be the easiest way
283 // to suppress warning C4061 on MSVC (which expects all values of the
284 // enum to be explicitly handled).
285 case DigitallySigned::SignatureAlgorithm::Anonymous:
286 case DigitallySigned::SignatureAlgorithm::DSA:
287 default:
288 assert(false);
289 return Result::FATAL_ERROR_INVALID_ARGS;
290 }
291 if (rv != Success) {
292 if (IsFatalError(rv)) {
293 return rv;
294 }
295 // If the error is non-fatal, we assume the signature was invalid.
296 return Result::ERROR_BAD_SIGNATURE;
297 }
298 return Success;
299 }
300
VerifySignature(const Buffer & data,const Buffer & signature)301 Result CTLogVerifier::VerifySignature(const Buffer& data,
302 const Buffer& signature) {
303 Input dataInput;
304 Result rv = BufferToInput(data, dataInput);
305 if (rv != Success) {
306 return rv;
307 }
308 Input signatureInput;
309 rv = BufferToInput(signature, signatureInput);
310 if (rv != Success) {
311 return rv;
312 }
313 return VerifySignature(dataInput, signatureInput);
314 }
315
316 } // namespace ct
317 } // namespace mozilla
318