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"
11 #include "CTSerialization.h"
13 #include "mozpkix/pkixnss.h"
14 #include "mozpkix/pkixutil.h"
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
{
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
,
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
;
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
;
85 Result
VerifyRSAPKCS1SignedData(Input
, DigestAlgorithm
, Input
,
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
),
111 mDisqualified(false),
112 mDisqualificationTime(UINT64_MAX
) {}
114 Result
CTLogVerifier::Init(Input subjectPublicKeyInfo
,
115 CTLogOperatorId operatorId
, CTLogStatus logStatus
,
116 uint64_t disqualificationTime
) {
118 case CTLogStatus::Included
:
119 mDisqualified
= false;
120 mDisqualificationTime
= UINT64_MAX
;
122 case CTLogStatus::Disqualified
:
123 mDisqualified
= true;
124 mDisqualificationTime
= disqualificationTime
;
126 case CTLogStatus::Unknown
:
129 return Result::FATAL_ERROR_INVALID_ARGS
;
132 SignatureParamsTrustDomain trustDomain
;
133 Result rv
= CheckSubjectPublicKeyInfo(subjectPublicKeyInfo
, trustDomain
,
134 EndEntityOrCA::MustBeEndEntity
);
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
));
149 return MapPRErrorCodeToResult(PR_GetError());
151 mPublicECKey
.reset(SECKEY_ExtractPublicKey(spki
.get()));
153 return MapPRErrorCodeToResult(PR_GetError());
155 UniquePK11SlotInfo
slot(PK11_GetInternalSlot());
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());
165 mPublicECKey
.reset(nullptr);
168 mKeyId
.resize(SHA256_LENGTH
);
169 rv
= DigestBufNSS(subjectPublicKeyInfo
, DigestAlgorithm::sha256
,
170 mKeyId
.data(), mKeyId
.size());
175 mOperatorId
= operatorId
;
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
);
195 rv
= BufferToInput(serializedLogEntry
, logEntryInput
);
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());
210 Buffer serializedData
;
211 rv
= EncodeV1SCTSignedData(sct
.timestamp
, logEntryInput
, sctExtensionsInput
,
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
) {
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
));
246 PK11_VerifyWithMechanism(pubkey
.get(), CKM_ECDSA_SHA256
, nullptr,
247 signatureSECItem
.get(), &dataSECItem
, nullptr);
248 if (srv
!= SECSuccess
) {
249 return MapPRErrorCodeToResult(PR_GetError());
254 Result
CTLogVerifier::VerifySignature(Input data
, Input signature
) {
256 Result rv
= BufferToInput(mSubjectPublicKeyInfo
, spki
);
261 switch (mSignatureAlgorithm
) {
262 case DigitallySigned::SignatureAlgorithm::RSA
:
263 rv
= VerifyRSAPKCS1SignedDataNSS(data
, DigestAlgorithm::sha256
, signature
,
266 case DigitallySigned::SignatureAlgorithm::ECDSA
:
267 rv
= FasterVerifyECDSASignedDataNSS(data
, signature
, mPublicECKey
);
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
:
277 return Result::FATAL_ERROR_INVALID_ARGS
;
280 if (IsFatalError(rv
)) {
283 // If the error is non-fatal, we assume the signature was invalid.
284 return Result::ERROR_BAD_SIGNATURE
;
289 Result
CTLogVerifier::VerifySignature(const Buffer
& data
,
290 const Buffer
& signature
) {
292 Result rv
= BufferToInput(data
, dataInput
);
296 Input signatureInput
;
297 rv
= BufferToInput(signature
, signatureInput
);
301 return VerifySignature(dataInput
, signatureInput
);
305 } // namespace mozilla