LCOV - code coverage report
Current view: top level - security/certverifier - CTLogVerifier.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 51 175 29.1 %
Date: 2017-07-14 16:53:18 Functions: 6 24 25.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13