CTLogVerifier.cpp (8618B)
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 "CertVerifier.h" 13 #include "hasht.h" 14 #include "mozpkix/Result.h" 15 #include "mozpkix/pkixnss.h" 16 #include "mozpkix/pkixutil.h" 17 #include "mozilla/glean/SecurityCtMetrics.h" 18 19 using namespace mozilla::pkix; 20 21 namespace mozilla { 22 namespace ct { 23 24 // A TrustDomain used to extract the SCT log signature parameters 25 // given its subjectPublicKeyInfo. 26 // Only RSASSA-PKCS1v15 with SHA-256 and ECDSA (using the NIST P-256 curve) 27 // with SHA-256 are allowed. 28 // RSA keys must be at least 2048 bits. 29 // See See RFC 6962, Section 2.1.4. 30 class SignatureParamsTrustDomain final : public TrustDomain { 31 public: 32 SignatureParamsTrustDomain() 33 : mSignatureAlgorithm(DigitallySigned::SignatureAlgorithm::Anonymous) {} 34 35 pkix::Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input, 36 TrustLevel&) override { 37 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; 38 } 39 40 pkix::Result FindIssuer(Input, IssuerChecker&, Time) override { 41 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; 42 } 43 44 pkix::Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration, 45 const Input*, const Input*) override { 46 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; 47 } 48 49 pkix::Result IsChainValid(const DERArray&, Time, 50 const CertPolicyId&) override { 51 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; 52 } 53 54 pkix::Result DigestBuf(Input, DigestAlgorithm, uint8_t*, size_t) override { 55 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; 56 } 57 58 pkix::Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA, 59 Time) override { 60 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; 61 } 62 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; 69 } 70 mSignatureAlgorithm = DigitallySigned::SignatureAlgorithm::ECDSA; 71 return Success; 72 } 73 74 pkix::Result VerifyECDSASignedData(Input, DigestAlgorithm, Input, 75 Input) override { 76 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; 77 } 78 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; 86 } 87 mSignatureAlgorithm = DigitallySigned::SignatureAlgorithm::RSA; 88 return Success; 89 } 90 91 pkix::Result VerifyRSAPKCS1SignedData(Input, DigestAlgorithm, Input, 92 Input) override { 93 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; 94 } 95 96 pkix::Result VerifyRSAPSSSignedData(Input, DigestAlgorithm, Input, 97 Input) override { 98 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; 99 } 100 101 pkix::Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA, 102 KeyPurposeId) override { 103 return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; 104 } 105 106 void NoteAuxiliaryExtension(AuxiliaryExtension, Input) override {} 107 108 DigitallySigned::SignatureAlgorithm mSignatureAlgorithm; 109 }; 110 111 CTLogVerifier::CTLogVerifier(CTLogOperatorId operatorId, CTLogState state, 112 CTLogFormat format, uint64_t timestamp) 113 : mSignatureAlgorithm(DigitallySigned::SignatureAlgorithm::Anonymous), 114 mOperatorId(operatorId), 115 mState(state), 116 mFormat(format), 117 mTimestamp(timestamp) {} 118 119 pkix::Result CTLogVerifier::Init(Input subjectPublicKeyInfo) { 120 SignatureParamsTrustDomain trustDomain; 121 pkix::Result rv = CheckSubjectPublicKeyInfo(subjectPublicKeyInfo, trustDomain, 122 EndEntityOrCA::MustBeEndEntity); 123 if (rv != Success) { 124 return rv; 125 } 126 mSignatureAlgorithm = trustDomain.mSignatureAlgorithm; 127 128 InputToBuffer(subjectPublicKeyInfo, mSubjectPublicKeyInfo); 129 130 mKeyId.resize(SHA256_LENGTH); 131 rv = DigestBufNSS(subjectPublicKeyInfo, DigestAlgorithm::sha256, 132 mKeyId.data(), mKeyId.size()); 133 if (rv != Success) { 134 return rv; 135 } 136 137 return Success; 138 } 139 140 pkix::Result CTLogVerifier::Verify(const LogEntry& entry, 141 const SignedCertificateTimestamp& sct, 142 SignatureCache* signatureCache) { 143 if (mKeyId.empty() || sct.logId != mKeyId || !signatureCache) { 144 return pkix::Result::FATAL_ERROR_INVALID_ARGS; 145 } 146 if (!SignatureParametersMatch(sct.signature)) { 147 return pkix::Result::FATAL_ERROR_INVALID_ARGS; 148 } 149 150 Buffer serializedLogEntry; 151 pkix::Result rv = EncodeLogEntry(entry, serializedLogEntry); 152 if (rv != Success) { 153 return rv; 154 } 155 156 Input logEntryInput; 157 rv = BufferToInput(serializedLogEntry, logEntryInput); 158 if (rv != Success) { 159 return rv; 160 } 161 162 // sct.extensions may be empty. If it is, sctExtensionsInput will remain in 163 // its default state, which is valid but of length 0. 164 Input sctExtensionsInput; 165 if (!sct.extensions.empty()) { 166 rv = sctExtensionsInput.Init(sct.extensions.data(), sct.extensions.size()); 167 if (rv != Success) { 168 return rv; 169 } 170 } 171 172 Buffer serializedData; 173 rv = EncodeV1SCTSignedData(sct.timestamp, logEntryInput, sctExtensionsInput, 174 serializedData); 175 if (rv != Success) { 176 return rv; 177 } 178 return VerifySignature(serializedData, sct.signature.signatureData, 179 signatureCache); 180 } 181 182 bool CTLogVerifier::SignatureParametersMatch(const DigitallySigned& signature) { 183 return signature.SignatureParametersMatch( 184 DigitallySigned::HashAlgorithm::SHA256, mSignatureAlgorithm); 185 } 186 187 pkix::Result CTLogVerifier::VerifySignature(Input data, Input signature, 188 SignatureCache* signatureCache) { 189 Input spki; 190 pkix::Result rv = BufferToInput(mSubjectPublicKeyInfo, spki); 191 if (rv != Success) { 192 return rv; 193 } 194 195 switch (mSignatureAlgorithm) { 196 case DigitallySigned::SignatureAlgorithm::RSA: 197 rv = psm::VerifySignedDataWithCache( 198 der::PublicKeyAlgorithm::RSA_PKCS1, 199 mozilla::glean::sct_signature_cache::total, 200 mozilla::glean::sct_signature_cache::hits, data, 201 DigestAlgorithm::sha256, signature, spki, signatureCache, nullptr); 202 break; 203 case DigitallySigned::SignatureAlgorithm::ECDSA: 204 rv = psm::VerifySignedDataWithCache( 205 der::PublicKeyAlgorithm::ECDSA, 206 mozilla::glean::sct_signature_cache::total, 207 mozilla::glean::sct_signature_cache::hits, data, 208 DigestAlgorithm::sha256, signature, spki, signatureCache, nullptr); 209 break; 210 // We do not expect new values added to this enum any time soon, 211 // so just listing all the available ones seems to be the easiest way 212 // to suppress warning C4061 on MSVC (which expects all values of the 213 // enum to be explicitly handled). 214 case DigitallySigned::SignatureAlgorithm::Anonymous: 215 case DigitallySigned::SignatureAlgorithm::DSA: 216 default: 217 assert(false); 218 return pkix::Result::FATAL_ERROR_INVALID_ARGS; 219 } 220 if (rv != Success) { 221 if (IsFatalError(rv)) { 222 return rv; 223 } 224 // If the error is non-fatal, we assume the signature was invalid. 225 return pkix::Result::ERROR_BAD_SIGNATURE; 226 } 227 return Success; 228 } 229 230 pkix::Result CTLogVerifier::VerifySignature(const Buffer& data, 231 const Buffer& signature, 232 SignatureCache* signatureCache) { 233 Input dataInput; 234 pkix::Result rv = BufferToInput(data, dataInput); 235 if (rv != Success) { 236 return rv; 237 } 238 Input signatureInput; 239 rv = BufferToInput(signature, signatureInput); 240 if (rv != Success) { 241 return rv; 242 } 243 return VerifySignature(dataInput, signatureInput, signatureCache); 244 } 245 246 } // namespace ct 247 } // namespace mozilla