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"
12 #include "CertVerifier.h"
14 #include "mozpkix/Result.h"
15 #include "mozpkix/pkixnss.h"
16 #include "mozpkix/pkixutil.h"
18 using namespace mozilla::pkix
;
23 // A TrustDomain used to extract the SCT log signature parameters
24 // given its subjectPublicKeyInfo.
25 // Only RSASSA-PKCS1v15 with SHA-256 and ECDSA (using the NIST P-256 curve)
26 // with SHA-256 are allowed.
27 // RSA keys must be at least 2048 bits.
28 // See See RFC 6962, Section 2.1.4.
29 class SignatureParamsTrustDomain final
: public TrustDomain
{
31 SignatureParamsTrustDomain()
32 : mSignatureAlgorithm(DigitallySigned::SignatureAlgorithm::Anonymous
) {}
34 pkix::Result
GetCertTrust(EndEntityOrCA
, const CertPolicyId
&, Input
,
35 TrustLevel
&) override
{
36 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE
;
39 pkix::Result
FindIssuer(Input
, IssuerChecker
&, Time
) override
{
40 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE
;
43 pkix::Result
CheckRevocation(EndEntityOrCA
, const CertID
&, Time
, Duration
,
44 const Input
*, const Input
*,
45 const Input
*) override
{
46 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE
;
49 pkix::Result
IsChainValid(const DERArray
&, Time
,
50 const CertPolicyId
&) override
{
51 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE
;
54 pkix::Result
DigestBuf(Input
, DigestAlgorithm
, uint8_t*, size_t) override
{
55 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE
;
58 pkix::Result
CheckSignatureDigestAlgorithm(DigestAlgorithm
, EndEntityOrCA
,
60 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE
;
63 pkix::Result
CheckECDSACurveIsAcceptable(EndEntityOrCA
,
64 NamedCurve curve
) override
{
65 assert(mSignatureAlgorithm
==
66 DigitallySigned::SignatureAlgorithm::Anonymous
);
67 if (curve
!= NamedCurve::secp256r1
) {
68 return pkix::Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE
;
70 mSignatureAlgorithm
= DigitallySigned::SignatureAlgorithm::ECDSA
;
74 pkix::Result
VerifyECDSASignedData(Input
, DigestAlgorithm
, Input
,
76 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE
;
79 pkix::Result
CheckRSAPublicKeyModulusSizeInBits(
80 EndEntityOrCA
, unsigned int modulusSizeInBits
) override
{
81 assert(mSignatureAlgorithm
==
82 DigitallySigned::SignatureAlgorithm::Anonymous
);
83 // Require RSA keys of at least 2048 bits. See RFC 6962, Section 2.1.4.
84 if (modulusSizeInBits
< 2048) {
85 return pkix::Result::ERROR_INADEQUATE_KEY_SIZE
;
87 mSignatureAlgorithm
= DigitallySigned::SignatureAlgorithm::RSA
;
91 pkix::Result
VerifyRSAPKCS1SignedData(Input
, DigestAlgorithm
, Input
,
93 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE
;
96 pkix::Result
VerifyRSAPSSSignedData(Input
, DigestAlgorithm
, Input
,
98 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE
;
101 pkix::Result
CheckValidityIsAcceptable(Time
, Time
, EndEntityOrCA
,
102 KeyPurposeId
) override
{
103 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE
;
106 pkix::Result
NetscapeStepUpMatchesServerAuth(Time
, bool&) override
{
107 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE
;
110 void NoteAuxiliaryExtension(AuxiliaryExtension
, Input
) override
{}
112 DigitallySigned::SignatureAlgorithm mSignatureAlgorithm
;
115 CTLogVerifier::CTLogVerifier(CTLogOperatorId operatorId
, CTLogState state
,
117 : mSignatureAlgorithm(DigitallySigned::SignatureAlgorithm::Anonymous
),
118 mOperatorId(operatorId
),
120 mTimestamp(timestamp
) {}
122 pkix::Result
CTLogVerifier::Init(Input subjectPublicKeyInfo
) {
123 SignatureParamsTrustDomain trustDomain
;
124 pkix::Result rv
= CheckSubjectPublicKeyInfo(subjectPublicKeyInfo
, trustDomain
,
125 EndEntityOrCA::MustBeEndEntity
);
129 mSignatureAlgorithm
= trustDomain
.mSignatureAlgorithm
;
131 InputToBuffer(subjectPublicKeyInfo
, mSubjectPublicKeyInfo
);
133 mKeyId
.resize(SHA256_LENGTH
);
134 rv
= DigestBufNSS(subjectPublicKeyInfo
, DigestAlgorithm::sha256
,
135 mKeyId
.data(), mKeyId
.size());
143 pkix::Result
CTLogVerifier::Verify(const LogEntry
& entry
,
144 const SignedCertificateTimestamp
& sct
,
145 SignatureCache
* signatureCache
) {
146 if (mKeyId
.empty() || sct
.logId
!= mKeyId
|| !signatureCache
) {
147 return pkix::Result::FATAL_ERROR_INVALID_ARGS
;
149 if (!SignatureParametersMatch(sct
.signature
)) {
150 return pkix::Result::FATAL_ERROR_INVALID_ARGS
;
153 Buffer serializedLogEntry
;
154 pkix::Result rv
= EncodeLogEntry(entry
, serializedLogEntry
);
160 rv
= BufferToInput(serializedLogEntry
, logEntryInput
);
165 // sct.extensions may be empty. If it is, sctExtensionsInput will remain in
166 // its default state, which is valid but of length 0.
167 Input sctExtensionsInput
;
168 if (!sct
.extensions
.empty()) {
169 rv
= sctExtensionsInput
.Init(sct
.extensions
.data(), sct
.extensions
.size());
175 Buffer serializedData
;
176 rv
= EncodeV1SCTSignedData(sct
.timestamp
, logEntryInput
, sctExtensionsInput
,
181 return VerifySignature(serializedData
, sct
.signature
.signatureData
,
185 bool CTLogVerifier::SignatureParametersMatch(const DigitallySigned
& signature
) {
186 return signature
.SignatureParametersMatch(
187 DigitallySigned::HashAlgorithm::SHA256
, mSignatureAlgorithm
);
190 pkix::Result
CTLogVerifier::VerifySignature(Input data
, Input signature
,
191 SignatureCache
* signatureCache
) {
193 pkix::Result rv
= BufferToInput(mSubjectPublicKeyInfo
, spki
);
198 switch (mSignatureAlgorithm
) {
199 case DigitallySigned::SignatureAlgorithm::RSA
:
200 rv
= psm::VerifySignedDataWithCache(
201 der::PublicKeyAlgorithm::RSA_PKCS1
,
202 mozilla::glean::sct_signature_cache::total
,
203 mozilla::glean::sct_signature_cache::hits
, data
,
204 DigestAlgorithm::sha256
, signature
, spki
, signatureCache
, nullptr);
206 case DigitallySigned::SignatureAlgorithm::ECDSA
:
207 rv
= psm::VerifySignedDataWithCache(
208 der::PublicKeyAlgorithm::ECDSA
,
209 mozilla::glean::sct_signature_cache::total
,
210 mozilla::glean::sct_signature_cache::hits
, data
,
211 DigestAlgorithm::sha256
, signature
, spki
, signatureCache
, nullptr);
213 // We do not expect new values added to this enum any time soon,
214 // so just listing all the available ones seems to be the easiest way
215 // to suppress warning C4061 on MSVC (which expects all values of the
216 // enum to be explicitly handled).
217 case DigitallySigned::SignatureAlgorithm::Anonymous
:
218 case DigitallySigned::SignatureAlgorithm::DSA
:
221 return pkix::Result::FATAL_ERROR_INVALID_ARGS
;
224 if (IsFatalError(rv
)) {
227 // If the error is non-fatal, we assume the signature was invalid.
228 return pkix::Result::ERROR_BAD_SIGNATURE
;
233 pkix::Result
CTLogVerifier::VerifySignature(const Buffer
& data
,
234 const Buffer
& signature
,
235 SignatureCache
* signatureCache
) {
237 pkix::Result rv
= BufferToInput(data
, dataInput
);
241 Input signatureInput
;
242 rv
= BufferToInput(signature
, signatureInput
);
246 return VerifySignature(dataInput
, signatureInput
, signatureCache
);
250 } // namespace mozilla