MultiLogCTVerifier.cpp (5886B)
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 "MultiLogCTVerifier.h" 8 9 #include "CTObjectsExtractor.h" 10 #include "CTSerialization.h" 11 #include "mozilla/StaticPrefs_security.h" 12 13 namespace mozilla { 14 namespace ct { 15 16 using namespace mozilla::pkix; 17 18 MultiLogCTVerifier::MultiLogCTVerifier() 19 : mSignatureCache(signature_cache_new( 20 StaticPrefs::security_pki_sct_signature_cache_size()), 21 signature_cache_free) {} 22 23 void MultiLogCTVerifier::AddLog(CTLogVerifier&& log) { 24 mLogs.push_back(std::move(log)); 25 } 26 27 pkix::Result MultiLogCTVerifier::Verify( 28 Input cert, Input issuerSubjectPublicKeyInfo, Input sctListFromCert, 29 Input sctListFromOCSPResponse, Input sctListFromTLSExtension, Time time, 30 Maybe<Time> distrustAfterTime, CTVerifyResult& result) { 31 assert(cert.GetLength() > 0); 32 result.Reset(); 33 34 pkix::Result rv; 35 36 // Verify embedded SCTs 37 if (issuerSubjectPublicKeyInfo.GetLength() > 0 && 38 sctListFromCert.GetLength() > 0) { 39 LogEntry precertEntry; 40 rv = GetPrecertLogEntry(cert, issuerSubjectPublicKeyInfo, precertEntry); 41 if (rv != Success) { 42 return rv; 43 } 44 rv = VerifySCTs(sctListFromCert, precertEntry, SCTOrigin::Embedded, time, 45 distrustAfterTime, result); 46 if (rv != Success) { 47 return rv; 48 } 49 } 50 51 LogEntry x509Entry; 52 GetX509LogEntry(cert, x509Entry); 53 54 // Verify SCTs from a stapled OCSP response 55 if (sctListFromOCSPResponse.GetLength() > 0) { 56 rv = VerifySCTs(sctListFromOCSPResponse, x509Entry, SCTOrigin::OCSPResponse, 57 time, distrustAfterTime, result); 58 if (rv != Success) { 59 return rv; 60 } 61 } 62 63 // Verify SCTs from a TLS extension 64 if (sctListFromTLSExtension.GetLength() > 0) { 65 rv = VerifySCTs(sctListFromTLSExtension, x509Entry, SCTOrigin::TLSExtension, 66 time, distrustAfterTime, result); 67 if (rv != Success) { 68 return rv; 69 } 70 } 71 return Success; 72 } 73 74 void DecodeSCTs(Input encodedSctList, 75 std::vector<SignedCertificateTimestamp>& decodedSCTs, 76 size_t& decodingErrors) { 77 decodedSCTs.clear(); 78 79 Reader listReader; 80 pkix::Result rv = DecodeSCTList(encodedSctList, listReader); 81 if (rv != Success) { 82 decodingErrors++; 83 return; 84 } 85 86 while (!listReader.AtEnd()) { 87 Input encodedSct; 88 rv = ReadSCTListItem(listReader, encodedSct); 89 if (rv != Success) { 90 decodingErrors++; 91 return; 92 } 93 94 Reader encodedSctReader(encodedSct); 95 SignedCertificateTimestamp sct; 96 rv = DecodeSignedCertificateTimestamp(encodedSctReader, sct); 97 if (rv != Success) { 98 decodingErrors++; 99 continue; 100 } 101 decodedSCTs.push_back(std::move(sct)); 102 } 103 } 104 105 pkix::Result MultiLogCTVerifier::VerifySCTs(Input encodedSctList, 106 const LogEntry& expectedEntry, 107 SCTOrigin origin, Time time, 108 Maybe<Time> distrustAfterTime, 109 CTVerifyResult& result) { 110 std::vector<SignedCertificateTimestamp> decodedSCTs; 111 DecodeSCTs(encodedSctList, decodedSCTs, result.decodingErrors); 112 for (auto sct : decodedSCTs) { 113 pkix::Result rv = VerifySingleSCT(std::move(sct), expectedEntry, origin, 114 time, distrustAfterTime, result); 115 if (rv != Success) { 116 return rv; 117 } 118 } 119 return Success; 120 } 121 122 pkix::Result MultiLogCTVerifier::VerifySingleSCT( 123 SignedCertificateTimestamp&& sct, const LogEntry& expectedEntry, 124 SCTOrigin origin, Time time, Maybe<Time> distrustAfterTime, 125 CTVerifyResult& result) { 126 switch (origin) { 127 case SCTOrigin::Embedded: 128 result.embeddedSCTs++; 129 break; 130 case SCTOrigin::TLSExtension: 131 result.sctsFromTLSHandshake++; 132 break; 133 case SCTOrigin::OCSPResponse: 134 result.sctsFromOCSP++; 135 break; 136 } 137 138 CTLogVerifier* matchingLog = nullptr; 139 for (auto& log : mLogs) { 140 if (log.keyId() == sct.logId) { 141 matchingLog = &log; 142 break; 143 } 144 } 145 146 if (!matchingLog) { 147 // SCT does not match any known log. 148 result.sctsFromUnknownLogs++; 149 return Success; 150 } 151 152 if (!matchingLog->SignatureParametersMatch(sct.signature)) { 153 // SCT signature parameters do not match the log's. 154 result.sctsWithInvalidSignatures++; 155 return Success; 156 } 157 158 pkix::Result rv = 159 matchingLog->Verify(expectedEntry, sct, mSignatureCache.get()); 160 if (rv != Success) { 161 if (rv == pkix::Result::ERROR_BAD_SIGNATURE) { 162 result.sctsWithInvalidSignatures++; 163 return Success; 164 } 165 return rv; 166 } 167 168 // Make sure the timestamp is legitimate (not in the future). 169 // SCT's |timestamp| is measured in milliseconds since the epoch, 170 // ignoring leap seconds. When converting it to a second-level precision 171 // pkix::Time, we round down. 172 Time sctTime = TimeFromEpochInSeconds(sct.timestamp / 1000u); 173 if (sctTime > time) { 174 result.sctsWithInvalidTimestamps++; 175 return Success; 176 } 177 178 // If the root has a distrustAfter time, ensure that the SCT's timestamp is 179 // not after that time. 180 if (distrustAfterTime.isSome() && sctTime > distrustAfterTime.value()) { 181 result.sctsWithDistrustedTimestamps++; 182 return Success; 183 } 184 185 VerifiedSCT verifiedSct(std::move(sct), origin, matchingLog->operatorId(), 186 matchingLog->state(), matchingLog->format(), 187 matchingLog->timestamp()); 188 result.verifiedScts.push_back(std::move(verifiedSct)); 189 return Success; 190 } 191 192 } // namespace ct 193 } // namespace mozilla