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