CTObjectsExtractor.cpp (12607B)
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 "CTObjectsExtractor.h" 8 9 #include <limits> 10 #include <vector> 11 12 #include "hasht.h" 13 #include "mozpkix/pkixnss.h" 14 #include "mozpkix/pkixutil.h" 15 16 namespace mozilla { 17 namespace ct { 18 19 using namespace mozilla::pkix; 20 21 // Holds a non-owning pointer to a byte buffer and allows writing chunks of data 22 // to the buffer, placing the later chunks after the earlier ones 23 // in a stream-like fashion. 24 // Note that writing to Output always succeeds. If the internal buffer 25 // overflows, an error flag is turned on and you won't be able to retrieve 26 // the final data. 27 class Output { 28 public: 29 Output(uint8_t* buffer, size_t length) 30 : begin(buffer), 31 end(buffer + length), 32 current(begin), 33 overflowed(false) {} 34 35 template <size_t N> 36 explicit Output(uint8_t (&buffer)[N]) : Output(buffer, N) {} 37 38 void Write(Input data) { Write(data.UnsafeGetData(), data.GetLength()); } 39 40 void Write(uint8_t b) { Write(&b, 1); } 41 42 bool IsOverflowed() const { return overflowed; } 43 44 Result GetInput(/*out*/ Input& input) const { 45 if (overflowed || current < begin) { 46 return Result::FATAL_ERROR_INVALID_STATE; 47 } 48 size_t length = static_cast<size_t>(current - begin); 49 return input.Init(begin, length); 50 } 51 52 private: 53 uint8_t* begin; 54 uint8_t* end; 55 uint8_t* current; 56 bool overflowed; 57 58 Output(const Output&) = delete; 59 void operator=(const Output&) = delete; 60 61 void Write(const uint8_t* data, size_t length) { 62 if (end < current) { 63 overflowed = true; 64 } 65 size_t available = static_cast<size_t>(end - current); 66 if (available < length) { 67 overflowed = true; 68 } 69 if (overflowed) { 70 return; 71 } 72 memcpy(current, data, length); 73 current += length; 74 } 75 }; 76 77 // For reference: 78 // 79 // Certificate ::= SEQUENCE { 80 // tbsCertificate TBSCertificate, 81 // signatureAlgorithm AlgorithmIdentifier, 82 // signatureValue BIT STRING } 83 // 84 // TBSCertificate ::= SEQUENCE { 85 // version [0] EXPLICIT Version DEFAULT v1, 86 // serialNumber CertificateSerialNumber, 87 // signature AlgorithmIdentifier, 88 // issuer Name, 89 // validity Validity, 90 // subject Name, 91 // subjectPublicKeyInfo SubjectPublicKeyInfo, 92 // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, 93 // -- If present, version MUST be v2 or v3 94 // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, 95 // -- If present, version MUST be v2 or v3 96 // extensions [3] EXPLICIT Extensions OPTIONAL 97 // -- If present, version MUST be v3 98 // } 99 100 // python DottedOIDToCode.py id-embeddedSctList 1.3.6.1.4.1.11129.2.4.2 101 // See Section 3.3 of RFC 6962. 102 static const uint8_t EMBEDDED_SCT_LIST_OID[] = {0x2b, 0x06, 0x01, 0x04, 0x01, 103 0xd6, 0x79, 0x02, 0x04, 0x02}; 104 // Maximum length of DER TLV header 105 static const size_t MAX_TLV_HEADER_LENGTH = 4; 106 // DER tag of the "extensions [3]" field from TBSCertificate 107 static const uint8_t EXTENSIONS_CONTEXT_TAG = 108 der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 3; 109 110 Result CheckForInputSizeTypeOverflow(size_t length) { 111 if (length > std::numeric_limits<Input::size_type>::max()) { 112 return Result::FATAL_ERROR_INVALID_STATE; 113 } 114 return Success; 115 } 116 117 // Given a leaf certificate, extracts the DER-encoded TBSCertificate component 118 // of the corresponding Precertificate. 119 // Basically, the extractor needs to remove the embedded SCTs extension 120 // from the certificate and return its TBSCertificate. We do it in an ad hoc 121 // manner by breaking the source DER into several parts and then joining 122 // the right parts, taking care to update the relevant TLV headers. 123 // See WriteOutput for more details on the parts involved. 124 class PrecertTBSExtractor { 125 public: 126 // |buffer| is the buffer to be used for writing the output. Since the 127 // required buffer size is not generally known in advance, it's best 128 // to use at least the size of the input certificate DER. 129 PrecertTBSExtractor(Input der, uint8_t* buffer, size_t bufferLength) 130 : mDER(der), mOutput(buffer, bufferLength) {} 131 132 // Performs the extraction. 133 Result Init() { 134 Reader tbsReader; 135 Result rv = GetTBSCertificate(tbsReader); 136 if (rv != Success) { 137 return rv; 138 } 139 140 rv = ExtractTLVsBeforeExtensions(tbsReader); 141 if (rv != Success) { 142 return rv; 143 } 144 145 rv = ExtractOptionalExtensionsExceptSCTs(tbsReader); 146 if (rv != Success) { 147 return rv; 148 } 149 150 return WriteOutput(); 151 } 152 153 // Use to retrieve the result after a successful call to Init. 154 // The returned Input points to the buffer supplied in the constructor. 155 Input GetPrecertTBS() { return mPrecertTBS; } 156 157 private: 158 Result GetTBSCertificate(Reader& tbsReader) { 159 Reader certificateReader; 160 Result rv = 161 der::ExpectTagAndGetValueAtEnd(mDER, der::SEQUENCE, certificateReader); 162 if (rv != Success) { 163 return rv; 164 } 165 return ExpectTagAndGetValue(certificateReader, der::SEQUENCE, tbsReader); 166 } 167 168 Result ExtractTLVsBeforeExtensions(Reader& tbsReader) { 169 Reader::Mark tbsBegin = tbsReader.GetMark(); 170 while (!tbsReader.AtEnd()) { 171 if (tbsReader.Peek(EXTENSIONS_CONTEXT_TAG)) { 172 break; 173 } 174 uint8_t tag; 175 Input tagValue; 176 Result rv = der::ReadTagAndGetValue(tbsReader, tag, tagValue); 177 if (rv != Success) { 178 return rv; 179 } 180 } 181 return tbsReader.GetInput(tbsBegin, mTLVsBeforeExtensions); 182 } 183 184 Result ExtractOptionalExtensionsExceptSCTs(Reader& tbsReader) { 185 if (!tbsReader.Peek(EXTENSIONS_CONTEXT_TAG)) { 186 return Success; 187 } 188 189 Reader extensionsContextReader; 190 Result rv = der::ExpectTagAndGetValueAtEnd( 191 tbsReader, EXTENSIONS_CONTEXT_TAG, extensionsContextReader); 192 if (rv != Success) { 193 return rv; 194 } 195 196 Reader extensionsReader; 197 rv = der::ExpectTagAndGetValueAtEnd(extensionsContextReader, der::SEQUENCE, 198 extensionsReader); 199 if (rv != Success) { 200 return rv; 201 } 202 203 while (!extensionsReader.AtEnd()) { 204 Reader::Mark extensionTLVBegin = extensionsReader.GetMark(); 205 Reader extension; 206 rv = 207 der::ExpectTagAndGetValue(extensionsReader, der::SEQUENCE, extension); 208 if (rv != Success) { 209 return rv; 210 } 211 Reader extensionID; 212 rv = der::ExpectTagAndGetValue(extension, der::OIDTag, extensionID); 213 if (rv != Success) { 214 return rv; 215 } 216 if (!extensionID.MatchRest(EMBEDDED_SCT_LIST_OID)) { 217 Input extensionTLV; 218 rv = extensionsReader.GetInput(extensionTLVBegin, extensionTLV); 219 if (rv != Success) { 220 return rv; 221 } 222 mExtensionTLVs.push_back(std::move(extensionTLV)); 223 } 224 } 225 return Success; 226 } 227 228 Result WriteOutput() { 229 // What should be written here: 230 // 231 // TBSCertificate ::= SEQUENCE (TLV with header |tbsHeader|) 232 // dump of |mTLVsBeforeExtensions| 233 // extensions [3] OPTIONAL (TLV with header |extensionsContextHeader|) 234 // SEQUENCE (TLV with with header |extensionsHeader|) 235 // dump of |mExtensionTLVs| 236 237 Result rv; 238 if (!mExtensionTLVs.empty()) { 239 uint8_t tbsHeaderBuffer[MAX_TLV_HEADER_LENGTH]; 240 uint8_t extensionsContextHeaderBuffer[MAX_TLV_HEADER_LENGTH]; 241 uint8_t extensionsHeaderBuffer[MAX_TLV_HEADER_LENGTH]; 242 243 Input tbsHeader; 244 Input extensionsContextHeader; 245 Input extensionsHeader; 246 247 // Count the total size of the extensions. Note that since 248 // the extensions data is contained within mDER (an Input), 249 // their combined length won't overflow Input::size_type. 250 Input::size_type extensionsValueLength = 0; 251 for (auto& extensionTLV : mExtensionTLVs) { 252 extensionsValueLength += extensionTLV.GetLength(); 253 } 254 255 rv = MakeTLVHeader(der::SEQUENCE, extensionsValueLength, 256 extensionsHeaderBuffer, extensionsHeader); 257 if (rv != Success) { 258 return rv; 259 } 260 // Since we're getting these extensions from a certificate that has 261 // already fit in an Input, this shouldn't overflow. 262 size_t extensionsContextLengthAsSizeT = 263 static_cast<size_t>(extensionsHeader.GetLength()) + 264 static_cast<size_t>(extensionsValueLength); 265 rv = CheckForInputSizeTypeOverflow(extensionsContextLengthAsSizeT); 266 if (rv != Success) { 267 return rv; 268 } 269 Input::size_type extensionsContextLength = 270 static_cast<Input::size_type>(extensionsContextLengthAsSizeT); 271 rv = 272 MakeTLVHeader(EXTENSIONS_CONTEXT_TAG, extensionsContextLength, 273 extensionsContextHeaderBuffer, extensionsContextHeader); 274 if (rv != Success) { 275 return rv; 276 } 277 size_t tbsLengthAsSizeT = 278 static_cast<size_t>(mTLVsBeforeExtensions.GetLength()) + 279 static_cast<size_t>(extensionsContextHeader.GetLength()) + 280 static_cast<size_t>(extensionsHeader.GetLength()) + 281 static_cast<size_t>(extensionsValueLength); 282 rv = CheckForInputSizeTypeOverflow(tbsLengthAsSizeT); 283 if (rv != Success) { 284 return rv; 285 } 286 Input::size_type tbsLength = 287 static_cast<Input::size_type>(tbsLengthAsSizeT); 288 rv = MakeTLVHeader(der::SEQUENCE, tbsLength, tbsHeaderBuffer, tbsHeader); 289 if (rv != Success) { 290 return rv; 291 } 292 293 mOutput.Write(tbsHeader); 294 mOutput.Write(mTLVsBeforeExtensions); 295 mOutput.Write(extensionsContextHeader); 296 mOutput.Write(extensionsHeader); 297 for (auto& extensionTLV : mExtensionTLVs) { 298 mOutput.Write(extensionTLV); 299 } 300 } else { 301 uint8_t tbsHeaderBuffer[MAX_TLV_HEADER_LENGTH]; 302 Input tbsHeader; 303 rv = MakeTLVHeader(der::SEQUENCE, mTLVsBeforeExtensions.GetLength(), 304 tbsHeaderBuffer, tbsHeader); 305 if (rv != Success) { 306 return rv; 307 } 308 mOutput.Write(tbsHeader); 309 mOutput.Write(mTLVsBeforeExtensions); 310 } 311 312 return mOutput.GetInput(mPrecertTBS); 313 } 314 315 Result MakeTLVHeader(uint8_t tag, size_t length, 316 uint8_t (&buffer)[MAX_TLV_HEADER_LENGTH], 317 /*out*/ Input& header) { 318 Output output(buffer); 319 output.Write(tag); 320 if (length < 128) { 321 output.Write(static_cast<uint8_t>(length)); 322 } else if (length < 256) { 323 output.Write(0x81u); 324 output.Write(static_cast<uint8_t>(length)); 325 } else if (length < 65536) { 326 output.Write(0x82u); 327 output.Write(static_cast<uint8_t>(length / 256)); 328 output.Write(static_cast<uint8_t>(length % 256)); 329 } else { 330 return Result::FATAL_ERROR_INVALID_ARGS; 331 } 332 return output.GetInput(header); 333 } 334 335 Input mDER; 336 Input mTLVsBeforeExtensions; 337 std::vector<Input> mExtensionTLVs; 338 Output mOutput; 339 Input mPrecertTBS; 340 }; 341 342 Result GetPrecertLogEntry(Input leafCertificate, 343 Input issuerSubjectPublicKeyInfo, LogEntry& output) { 344 assert(leafCertificate.GetLength() > 0); 345 assert(issuerSubjectPublicKeyInfo.GetLength() > 0); 346 output.Reset(); 347 348 Buffer precertTBSBuffer; 349 precertTBSBuffer.resize(leafCertificate.GetLength()); 350 351 PrecertTBSExtractor extractor(leafCertificate, precertTBSBuffer.data(), 352 precertTBSBuffer.size()); 353 Result rv = extractor.Init(); 354 if (rv != Success) { 355 return rv; 356 } 357 Input precertTBS(extractor.GetPrecertTBS()); 358 assert(precertTBS.UnsafeGetData() == precertTBSBuffer.data()); 359 assert(precertTBS.GetLength() <= precertTBSBuffer.size()); 360 precertTBSBuffer.resize(precertTBS.GetLength()); 361 362 output.type = LogEntry::Type::Precert; 363 output.tbsCertificate = std::move(precertTBSBuffer); 364 365 output.issuerKeyHash.resize(SHA256_LENGTH); 366 return DigestBufNSS(issuerSubjectPublicKeyInfo, DigestAlgorithm::sha256, 367 output.issuerKeyHash.data(), output.issuerKeyHash.size()); 368 } 369 370 void GetX509LogEntry(Input leafCertificate, LogEntry& output) { 371 assert(leafCertificate.GetLength() > 0); 372 output.Reset(); 373 output.type = LogEntry::Type::X509; 374 InputToBuffer(leafCertificate, output.leafCertificate); 375 } 376 377 } // namespace ct 378 } // namespace mozilla