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