Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/security/certverifier/CTObjectsExtractor.cpp
Line
Count
Source (jump to first uncovered line)
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
  Output(uint8_t* buffer, size_t length)
33
    : begin(buffer)
34
    , end(buffer + length)
35
    , current(buffer, begin, end)
36
    , overflowed(false)
37
0
  {
38
0
  }
39
40
  template <size_t N>
41
  explicit Output(uint8_t (&buffer)[N])
42
    : Output(buffer, N)
43
0
  {
44
0
  }
45
46
  void Write(Input data)
47
0
  {
48
0
    Write(data.UnsafeGetData(), data.GetLength());
49
0
  }
50
51
  void Write(uint8_t b)
52
0
  {
53
0
    Write(&b, 1);
54
0
  }
55
56
0
  bool IsOverflowed() const { return overflowed; }
57
58
  Result GetInput(/*out*/ Input& input) const
59
0
  {
60
0
    if (overflowed) {
61
0
      return Result::FATAL_ERROR_INVALID_STATE;
62
0
    }
63
0
    size_t length = AssertedCast<size_t>(current.get() - begin);
64
0
    return input.Init(begin, length);
65
0
  }
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
  void Write(const uint8_t* data, size_t length)
77
0
  {
78
0
    size_t available = AssertedCast<size_t>(end - current.get());
79
0
    if (available < length) {
80
0
      overflowed = true;
81
0
    }
82
0
    if (overflowed) {
83
0
      return;
84
0
    }
85
0
    PodCopy(current.get(), data, length);
86
0
    current += length;
87
0
  }
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
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
  PrecertTBSExtractor(Input der, uint8_t* buffer, size_t bufferLength)
138
    : mDER(der)
139
    , mOutput(buffer, bufferLength)
140
0
  {
141
0
  }
142
143
  // Performs the extraction.
144
  Result Init()
145
0
  {
146
0
    Reader tbsReader;
147
0
    Result rv = GetTBSCertificate(tbsReader);
148
0
    if (rv != Success) {
149
0
      return rv;
150
0
    }
151
0
152
0
    rv = ExtractTLVsBeforeExtensions(tbsReader);
153
0
    if (rv != Success) {
154
0
      return rv;
155
0
    }
156
0
157
0
    rv = ExtractOptionalExtensionsExceptSCTs(tbsReader);
158
0
    if (rv != Success) {
159
0
      return rv;
160
0
    }
161
0
162
0
    return WriteOutput();
163
0
  }
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
  Input GetPrecertTBS()
168
0
  {
169
0
    return mPrecertTBS;
170
0
  }
171
172
private:
173
  Result GetTBSCertificate(Reader& tbsReader)
174
0
  {
175
0
    Reader certificateReader;
176
0
    Result rv = der::ExpectTagAndGetValueAtEnd(mDER, der::SEQUENCE,
177
0
                                               certificateReader);
178
0
    if (rv != Success) {
179
0
      return rv;
180
0
    }
181
0
    return ExpectTagAndGetValue(certificateReader, der::SEQUENCE, tbsReader);
182
0
  }
183
184
  Result ExtractTLVsBeforeExtensions(Reader& tbsReader)
185
0
  {
186
0
    Reader::Mark tbsBegin = tbsReader.GetMark();
187
0
    while (!tbsReader.AtEnd()) {
188
0
      if (tbsReader.Peek(EXTENSIONS_CONTEXT_TAG)) {
189
0
        break;
190
0
      }
191
0
      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
0
      }
197
0
    }
198
0
    return tbsReader.GetInput(tbsBegin, mTLVsBeforeExtensions);
199
0
  }
200
201
  Result ExtractOptionalExtensionsExceptSCTs(Reader& tbsReader)
202
0
  {
203
0
    if (!tbsReader.Peek(EXTENSIONS_CONTEXT_TAG)) {
204
0
      return Success;
205
0
    }
206
0
207
0
    Reader extensionsContextReader;
208
0
    Result rv = der::ExpectTagAndGetValueAtEnd(tbsReader,
209
0
                                               EXTENSIONS_CONTEXT_TAG,
210
0
                                               extensionsContextReader);
211
0
    if (rv != Success) {
212
0
      return rv;
213
0
    }
214
0
215
0
    Reader extensionsReader;
216
0
    rv = der::ExpectTagAndGetValueAtEnd(extensionsContextReader, der::SEQUENCE,
217
0
                                        extensionsReader);
218
0
    if (rv != Success) {
219
0
      return rv;
220
0
    }
221
0
222
0
    while (!extensionsReader.AtEnd()) {
223
0
      Reader::Mark extensionTLVBegin = extensionsReader.GetMark();
224
0
      Reader extension;
225
0
      rv = der::ExpectTagAndGetValue(extensionsReader, der::SEQUENCE,
226
0
                                     extension);
227
0
      if (rv != Success) {
228
0
        return rv;
229
0
      }
230
0
      Reader extensionID;
231
0
      rv = der::ExpectTagAndGetValue(extension, der::OIDTag, extensionID);
232
0
      if (rv != Success) {
233
0
        return rv;
234
0
      }
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
0
        }
241
0
        if (!mExtensionTLVs.append(std::move(extensionTLV))) {
242
0
          return Result::FATAL_ERROR_NO_MEMORY;
243
0
        }
244
0
      }
245
0
    }
246
0
    return Success;
247
0
  }
248
249
  Result WriteOutput()
250
0
  {
251
0
    // What should be written here:
252
0
    //
253
0
    // TBSCertificate ::= SEQUENCE (TLV with header |tbsHeader|)
254
0
    //   dump of |mTLVsBeforeExtensions|
255
0
    //   extensions [3] OPTIONAL (TLV with header |extensionsContextHeader|)
256
0
    //     SEQUENCE (TLV with with header |extensionsHeader|)
257
0
    //       dump of |mExtensionTLVs|
258
0
259
0
    Result rv;
260
0
    if (mExtensionTLVs.length() > 0) {
261
0
      uint8_t tbsHeaderBuffer[MAX_TLV_HEADER_LENGTH];
262
0
      uint8_t extensionsContextHeaderBuffer[MAX_TLV_HEADER_LENGTH];
263
0
      uint8_t extensionsHeaderBuffer[MAX_TLV_HEADER_LENGTH];
264
0
265
0
      Input tbsHeader;
266
0
      Input extensionsContextHeader;
267
0
      Input extensionsHeader;
268
0
269
0
      // Count the total size of the extensions. Note that since
270
0
      // the extensions data is contained within mDER (an Input),
271
0
      // 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
0
      }
276
0
277
0
      rv = MakeTLVHeader(der::SEQUENCE, extensionsValueLength,
278
0
                         extensionsHeaderBuffer, extensionsHeader);
279
0
      if (rv != Success) {
280
0
        return rv;
281
0
      }
282
0
      Input::size_type extensionsContextLength =
283
0
        AssertedCast<Input::size_type>(extensionsHeader.GetLength() +
284
0
                                       extensionsValueLength);
285
0
      rv = MakeTLVHeader(EXTENSIONS_CONTEXT_TAG,
286
0
                         extensionsContextLength,
287
0
                         extensionsContextHeaderBuffer,
288
0
                         extensionsContextHeader);
289
0
      if (rv != Success) {
290
0
        return rv;
291
0
      }
292
0
      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
0
      }
301
0
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
0
      }
309
0
    } else {
310
0
      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
0
      }
317
0
      mOutput.Write(tbsHeader);
318
0
      mOutput.Write(mTLVsBeforeExtensions);
319
0
    }
320
0
321
0
    return mOutput.GetInput(mPrecertTBS);
322
0
  }
323
324
  Result MakeTLVHeader(uint8_t tag, size_t length,
325
                       uint8_t (&buffer)[MAX_TLV_HEADER_LENGTH],
326
                       /*out*/ Input& header)
327
0
  {
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
0
    } else {
340
0
      return Result::FATAL_ERROR_INVALID_ARGS;
341
0
    }
342
0
    return output.GetInput(header);
343
0
  }
344
345
  Input mDER;
346
  Input mTLVsBeforeExtensions;
347
  Vector<Input, 16> mExtensionTLVs;
348
  Output mOutput;
349
  Input mPrecertTBS;
350
};
351
352
Result
353
GetPrecertLogEntry(Input leafCertificate, Input issuerSubjectPublicKeyInfo,
354
                   LogEntry& output)
355
0
{
356
0
  MOZ_ASSERT(leafCertificate.GetLength() > 0);
357
0
  MOZ_ASSERT(issuerSubjectPublicKeyInfo.GetLength() > 0);
358
0
  output.Reset();
359
0
360
0
  Buffer precertTBSBuffer;
361
0
  if (!precertTBSBuffer.resize(leafCertificate.GetLength())) {
362
0
    return Result::FATAL_ERROR_NO_MEMORY;
363
0
  }
364
0
365
0
  PrecertTBSExtractor extractor(leafCertificate,
366
0
                                precertTBSBuffer.begin(),
367
0
                                precertTBSBuffer.length());
368
0
  Result rv = extractor.Init();
369
0
  if (rv != Success) {
370
0
    return rv;
371
0
  }
372
0
  Input precertTBS(extractor.GetPrecertTBS());
373
0
  MOZ_ASSERT(precertTBS.UnsafeGetData() == precertTBSBuffer.begin());
374
0
  precertTBSBuffer.shrinkTo(precertTBS.GetLength());
375
0
376
0
  output.type = LogEntry::Type::Precert;
377
0
  output.tbsCertificate = std::move(precertTBSBuffer);
378
0
379
0
  if (!output.issuerKeyHash.resizeUninitialized(SHA256_LENGTH)) {
380
0
    return Result::FATAL_ERROR_NO_MEMORY;
381
0
  }
382
0
  return DigestBufNSS(issuerSubjectPublicKeyInfo, DigestAlgorithm::sha256,
383
0
                      output.issuerKeyHash.begin(),
384
0
                      output.issuerKeyHash.length());
385
0
}
386
387
Result
388
GetX509LogEntry(Input leafCertificate, LogEntry& output)
389
0
{
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
0
}
395
396
} } // namespace mozilla::ct