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/. */
7 #include "CTObjectsExtractor.h"
13 #include "mozpkix/pkixnss.h"
14 #include "mozpkix/pkixutil.h"
19 using namespace mozilla::pkix
;
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
29 Output(uint8_t* buffer
, size_t length
)
36 explicit Output(uint8_t (&buffer
)[N
]) : Output(buffer
, N
) {}
38 void Write(Input data
) { Write(data
.UnsafeGetData(), data
.GetLength()); }
40 void Write(uint8_t b
) { Write(&b
, 1); }
42 bool IsOverflowed() const { return overflowed
; }
44 Result
GetInput(/*out*/ Input
& input
) const {
45 if (overflowed
|| current
< begin
) {
46 return Result::FATAL_ERROR_INVALID_STATE
;
48 size_t length
= static_cast<size_t>(current
- begin
);
49 return input
.Init(begin
, length
);
58 Output(const Output
&) = delete;
59 void operator=(const Output
&) = delete;
61 void Write(const uint8_t* data
, size_t length
) {
65 size_t available
= static_cast<size_t>(end
- current
);
66 if (available
< length
) {
72 memcpy(current
, data
, length
);
79 // Certificate ::= SEQUENCE {
80 // tbsCertificate TBSCertificate,
81 // signatureAlgorithm AlgorithmIdentifier,
82 // signatureValue BIT STRING }
84 // TBSCertificate ::= SEQUENCE {
85 // version [0] EXPLICIT Version DEFAULT v1,
86 // serialNumber CertificateSerialNumber,
87 // signature AlgorithmIdentifier,
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
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;
110 Result
CheckForInputSizeTypeOverflow(size_t length
) {
111 if (length
> std::numeric_limits
<Input::size_type
>::max()) {
112 return Result::FATAL_ERROR_INVALID_STATE
;
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
{
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
) {}
132 // Performs the extraction.
135 Result rv
= GetTBSCertificate(tbsReader
);
140 rv
= ExtractTLVsBeforeExtensions(tbsReader
);
145 rv
= ExtractOptionalExtensionsExceptSCTs(tbsReader
);
150 return WriteOutput();
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
; }
158 Result
GetTBSCertificate(Reader
& tbsReader
) {
159 Reader certificateReader
;
161 der::ExpectTagAndGetValueAtEnd(mDER
, der::SEQUENCE
, certificateReader
);
165 return ExpectTagAndGetValue(certificateReader
, der::SEQUENCE
, tbsReader
);
168 Result
ExtractTLVsBeforeExtensions(Reader
& tbsReader
) {
169 Reader::Mark tbsBegin
= tbsReader
.GetMark();
170 while (!tbsReader
.AtEnd()) {
171 if (tbsReader
.Peek(EXTENSIONS_CONTEXT_TAG
)) {
176 Result rv
= der::ReadTagAndGetValue(tbsReader
, tag
, tagValue
);
181 return tbsReader
.GetInput(tbsBegin
, mTLVsBeforeExtensions
);
184 Result
ExtractOptionalExtensionsExceptSCTs(Reader
& tbsReader
) {
185 if (!tbsReader
.Peek(EXTENSIONS_CONTEXT_TAG
)) {
189 Reader extensionsContextReader
;
190 Result rv
= der::ExpectTagAndGetValueAtEnd(
191 tbsReader
, EXTENSIONS_CONTEXT_TAG
, extensionsContextReader
);
196 Reader extensionsReader
;
197 rv
= der::ExpectTagAndGetValueAtEnd(extensionsContextReader
, der::SEQUENCE
,
203 while (!extensionsReader
.AtEnd()) {
204 Reader::Mark extensionTLVBegin
= extensionsReader
.GetMark();
207 der::ExpectTagAndGetValue(extensionsReader
, der::SEQUENCE
, extension
);
212 rv
= der::ExpectTagAndGetValue(extension
, der::OIDTag
, extensionID
);
216 if (!extensionID
.MatchRest(EMBEDDED_SCT_LIST_OID
)) {
218 rv
= extensionsReader
.GetInput(extensionTLVBegin
, extensionTLV
);
222 mExtensionTLVs
.push_back(std::move(extensionTLV
));
228 Result
WriteOutput() {
229 // What should be written here:
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|
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
];
244 Input extensionsContextHeader
;
245 Input extensionsHeader
;
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();
255 rv
= MakeTLVHeader(der::SEQUENCE
, extensionsValueLength
,
256 extensionsHeaderBuffer
, extensionsHeader
);
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
);
269 Input::size_type extensionsContextLength
=
270 static_cast<Input::size_type
>(extensionsContextLengthAsSizeT
);
272 MakeTLVHeader(EXTENSIONS_CONTEXT_TAG
, extensionsContextLength
,
273 extensionsContextHeaderBuffer
, extensionsContextHeader
);
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
);
286 Input::size_type tbsLength
=
287 static_cast<Input::size_type
>(tbsLengthAsSizeT
);
288 rv
= MakeTLVHeader(der::SEQUENCE
, tbsLength
, tbsHeaderBuffer
, tbsHeader
);
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
);
301 uint8_t tbsHeaderBuffer
[MAX_TLV_HEADER_LENGTH
];
303 rv
= MakeTLVHeader(der::SEQUENCE
, mTLVsBeforeExtensions
.GetLength(),
304 tbsHeaderBuffer
, tbsHeader
);
308 mOutput
.Write(tbsHeader
);
309 mOutput
.Write(mTLVsBeforeExtensions
);
312 return mOutput
.GetInput(mPrecertTBS
);
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
);
321 output
.Write(static_cast<uint8_t>(length
));
322 } else if (length
< 256) {
324 output
.Write(static_cast<uint8_t>(length
));
325 } else if (length
< 65536) {
327 output
.Write(static_cast<uint8_t>(length
/ 256));
328 output
.Write(static_cast<uint8_t>(length
% 256));
330 return Result::FATAL_ERROR_INVALID_ARGS
;
332 return output
.GetInput(header
);
336 Input mTLVsBeforeExtensions
;
337 std::vector
<Input
> mExtensionTLVs
;
342 Result
GetPrecertLogEntry(Input leafCertificate
,
343 Input issuerSubjectPublicKeyInfo
, LogEntry
& output
) {
344 assert(leafCertificate
.GetLength() > 0);
345 assert(issuerSubjectPublicKeyInfo
.GetLength() > 0);
348 Buffer precertTBSBuffer
;
349 precertTBSBuffer
.resize(leafCertificate
.GetLength());
351 PrecertTBSExtractor
extractor(leafCertificate
, precertTBSBuffer
.data(),
352 precertTBSBuffer
.size());
353 Result rv
= extractor
.Init();
357 Input
precertTBS(extractor
.GetPrecertTBS());
358 assert(precertTBS
.UnsafeGetData() == precertTBSBuffer
.data());
359 assert(precertTBS
.GetLength() <= precertTBSBuffer
.size());
360 precertTBSBuffer
.resize(precertTBS
.GetLength());
362 output
.type
= LogEntry::Type::Precert
;
363 output
.tbsCertificate
= std::move(precertTBSBuffer
);
365 output
.issuerKeyHash
.resize(SHA256_LENGTH
);
366 return DigestBufNSS(issuerSubjectPublicKeyInfo
, DigestAlgorithm::sha256
,
367 output
.issuerKeyHash
.data(), output
.issuerKeyHash
.size());
370 void GetX509LogEntry(Input leafCertificate
, LogEntry
& output
) {
371 assert(leafCertificate
.GetLength() > 0);
373 output
.type
= LogEntry::Type::X509
;
374 InputToBuffer(leafCertificate
, output
.leafCertificate
);
378 } // namespace mozilla