Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/security/certverifier/CTSerialization.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 "CTSerialization.h"
8
#include "CTUtils.h"
9
10
#include <stdint.h>
11
12
#include "mozilla/Assertions.h"
13
#include "mozilla/Move.h"
14
#include "mozilla/TypeTraits.h"
15
16
namespace mozilla { namespace ct {
17
18
using namespace mozilla::pkix;
19
20
typedef mozilla::pkix::Result Result;
21
22
// Note: length is always specified in bytes.
23
// Signed Certificate Timestamp (SCT) Version length
24
static const size_t kVersionLength = 1;
25
26
// Members of a V1 SCT
27
static const size_t kLogIdLength = 32;
28
static const size_t kTimestampLength = 8;
29
static const size_t kExtensionsLengthBytes = 2;
30
static const size_t kHashAlgorithmLength = 1;
31
static const size_t kSigAlgorithmLength = 1;
32
static const size_t kSignatureLengthBytes = 2;
33
34
// Members of the digitally-signed struct of a V1 SCT
35
static const size_t kSignatureTypeLength = 1;
36
static const size_t kLogEntryTypeLength = 2;
37
static const size_t kAsn1CertificateLengthBytes = 3;
38
static const size_t kTbsCertificateLengthBytes = 3;
39
40
static const size_t kSCTListLengthBytes = 2;
41
static const size_t kSerializedSCTLengthBytes = 2;
42
43
// Members of digitally-signed struct of a STH
44
static const size_t kTreeSizeLength = 8;
45
46
// Length of sha256RootHash buffer of SignedTreeHead
47
static const size_t kSthRootHashLength = 32;
48
49
enum class SignatureType {
50
  CertificateTimestamp = 0,
51
  TreeHash = 1,
52
};
53
54
// Reads a TLS-encoded variable length unsigned integer from |in|.
55
// The integer is expected to be in big-endian order, which is used by TLS.
56
// Note: does not check if the output parameter overflows while reading.
57
// |length| indicates the size (in bytes) of the serialized integer.
58
static Result
59
UncheckedReadUint(size_t length, Reader& in, uint64_t& out)
60
0
{
61
0
  uint64_t result = 0;
62
0
  for (size_t i = 0; i < length; ++i) {
63
0
    uint8_t value;
64
0
    Result rv = in.Read(value);
65
0
    if (rv != Success) {
66
0
      return rv;
67
0
    }
68
0
    result = (result << 8) | value;
69
0
  }
70
0
  out = result;
71
0
  return Success;
72
0
}
73
74
// Performs overflow sanity checks and calls UncheckedReadUint.
75
template <size_t length, typename T>
76
Result
77
ReadUint(Reader& in, T& out)
78
0
{
79
0
  uint64_t value;
80
0
  static_assert(mozilla::IsUnsigned<T>::value, "T must be unsigned");
81
0
  static_assert(length <= 8, "At most 8 byte integers can be read");
82
0
  static_assert(sizeof(T) >= length, "T must be able to hold <length> bytes");
83
0
  Result rv = UncheckedReadUint(length, in, value);
84
0
  if (rv != Success) {
85
0
    return rv;
86
0
  }
87
0
  out = static_cast<T>(value);
88
0
  return Success;
89
0
}
Unexecuted instantiation: mozilla::pkix::Result mozilla::ct::ReadUint<1ul, unsigned int>(mozilla::pkix::Reader&, unsigned int&)
Unexecuted instantiation: mozilla::pkix::Result mozilla::ct::ReadUint<1ul, unsigned long>(mozilla::pkix::Reader&, unsigned long&)
Unexecuted instantiation: mozilla::pkix::Result mozilla::ct::ReadUint<8ul, unsigned long>(mozilla::pkix::Reader&, unsigned long&)
Unexecuted instantiation: mozilla::pkix::Result mozilla::ct::ReadUint<2ul, unsigned long>(mozilla::pkix::Reader&, unsigned long&)
90
91
// Reads |length| bytes from |in|.
92
static Result
93
ReadFixedBytes(size_t length, Reader& in, Input& out)
94
0
{
95
0
  return in.Skip(length, out);
96
0
}
97
98
// Reads a length-prefixed variable amount of bytes from |in|, updating |out|
99
// on success. |prefixLength| indicates the number of bytes needed to represent
100
// the length.
101
template <size_t prefixLength>
102
Result
103
ReadVariableBytes(Reader& in, Input& out)
104
0
{
105
0
  size_t length;
106
0
  Result rv = ReadUint<prefixLength>(in, length);
107
0
  if (rv != Success) {
108
0
    return rv;
109
0
  }
110
0
  return ReadFixedBytes(length, in, out);
111
0
}
Unexecuted instantiation: mozilla::pkix::Result mozilla::ct::ReadVariableBytes<1ul>(mozilla::pkix::Reader&, mozilla::pkix::Input&)
Unexecuted instantiation: mozilla::pkix::Result mozilla::ct::ReadVariableBytes<2ul>(mozilla::pkix::Reader&, mozilla::pkix::Input&)
112
113
// Reads a serialized hash algorithm.
114
static Result
115
ReadHashAlgorithm(Reader& in, DigitallySigned::HashAlgorithm& out)
116
0
{
117
0
  unsigned int value;
118
0
  Result rv = ReadUint<kHashAlgorithmLength>(in, value);
119
0
  if (rv != Success) {
120
0
    return rv;
121
0
  }
122
0
  DigitallySigned::HashAlgorithm algo =
123
0
    static_cast<DigitallySigned::HashAlgorithm>(value);
124
0
  switch (algo) {
125
0
    case DigitallySigned::HashAlgorithm::None:
126
0
    case DigitallySigned::HashAlgorithm::MD5:
127
0
    case DigitallySigned::HashAlgorithm::SHA1:
128
0
    case DigitallySigned::HashAlgorithm::SHA224:
129
0
    case DigitallySigned::HashAlgorithm::SHA256:
130
0
    case DigitallySigned::HashAlgorithm::SHA384:
131
0
    case DigitallySigned::HashAlgorithm::SHA512:
132
0
      out = algo;
133
0
      return Success;
134
0
  }
135
0
  return Result::ERROR_BAD_DER;
136
0
}
137
138
// Reads a serialized signature algorithm.
139
static Result
140
ReadSignatureAlgorithm(Reader& in, DigitallySigned::SignatureAlgorithm& out)
141
0
{
142
0
  unsigned int value;
143
0
  Result rv = ReadUint<kSigAlgorithmLength>(in, value);
144
0
  if (rv != Success) {
145
0
    return rv;
146
0
  }
147
0
  DigitallySigned::SignatureAlgorithm algo =
148
0
    static_cast<DigitallySigned::SignatureAlgorithm>(value);
149
0
  switch (algo) {
150
0
    case DigitallySigned::SignatureAlgorithm::Anonymous:
151
0
    case DigitallySigned::SignatureAlgorithm::RSA:
152
0
    case DigitallySigned::SignatureAlgorithm::DSA:
153
0
    case DigitallySigned::SignatureAlgorithm::ECDSA:
154
0
      out = algo;
155
0
      return Success;
156
0
  }
157
0
  return Result::ERROR_BAD_DER;
158
0
}
159
160
// Reads a serialized version enum.
161
static Result
162
ReadVersion(Reader& in, SignedCertificateTimestamp::Version& out)
163
0
{
164
0
  unsigned int value;
165
0
  Result rv = ReadUint<kVersionLength>(in, value);
166
0
  if (rv != Success) {
167
0
    return rv;
168
0
  }
169
0
  SignedCertificateTimestamp::Version version =
170
0
    static_cast<SignedCertificateTimestamp::Version>(value);
171
0
  switch (version) {
172
0
    case SignedCertificateTimestamp::Version::V1:
173
0
      out = version;
174
0
      return Success;
175
0
  }
176
0
  return Result::ERROR_BAD_DER;
177
0
}
178
179
// Writes a TLS-encoded variable length unsigned integer to |output|.
180
// Note: range/overflow checks are not performed on the input parameters.
181
// |length| indicates the size (in bytes) of the integer to be written.
182
// |value| the value itself to be written.
183
static Result
184
UncheckedWriteUint(size_t length, uint64_t value, Buffer& output)
185
0
{
186
0
  if (!output.reserve(length + output.length())) {
187
0
    return Result::FATAL_ERROR_NO_MEMORY;
188
0
  }
189
0
  for (; length > 0; --length) {
190
0
    uint8_t nextByte = (value >> ((length - 1) * 8)) & 0xFF;
191
0
    output.infallibleAppend(nextByte);
192
0
  }
193
0
  return Success;
194
0
}
195
196
// Performs sanity checks on T and calls UncheckedWriteUint.
197
template <size_t length, typename T>
198
static inline Result
199
WriteUint(T value, Buffer& output)
200
0
{
201
0
  static_assert(length <= 8, "At most 8 byte integers can be written");
202
0
  static_assert(sizeof(T) >= length, "T must be able to hold <length> bytes");
203
0
  if (mozilla::IsSigned<T>::value) {
204
0
    // We accept signed integer types assuming the actual value is non-negative.
205
0
    if (value < 0) {
206
0
      return Result::FATAL_ERROR_INVALID_ARGS;
207
0
    }
208
0
  }
209
0
  if (sizeof(T) > length) {
210
0
    // We allow the value variable to take more bytes than is written,
211
0
    // but the unwritten bytes must be zero.
212
0
    // Note: when "sizeof(T) == length" holds, "value >> (length * 8)" is
213
0
    // undefined since the shift is too big. On some compilers, this would
214
0
    // produce a warning even though the actual code is unreachable.
215
0
    if (value >> (length * 8 - 1) > 1) {
216
0
      return Result::FATAL_ERROR_INVALID_ARGS;
217
0
    }
218
0
  }
219
0
  return UncheckedWriteUint(length, static_cast<uint64_t>(value), output);
220
0
}
Unexecuted instantiation: Unified_cpp_certverifier0.cpp:mozilla::pkix::Result mozilla::ct::WriteUint<3ul, unsigned long>(unsigned long, mozilla::Vector<unsigned char, 0ul, mozilla::MallocAllocPolicy>&)
Unexecuted instantiation: Unified_cpp_certverifier0.cpp:mozilla::pkix::Result mozilla::ct::WriteUint<1ul, unsigned int>(unsigned int, mozilla::Vector<unsigned char, 0ul, mozilla::MallocAllocPolicy>&)
Unexecuted instantiation: Unified_cpp_certverifier0.cpp:mozilla::pkix::Result mozilla::ct::WriteUint<2ul, unsigned int>(unsigned int, mozilla::Vector<unsigned char, 0ul, mozilla::MallocAllocPolicy>&)
Unexecuted instantiation: Unified_cpp_certverifier0.cpp:mozilla::pkix::Result mozilla::ct::WriteUint<8ul, unsigned long>(unsigned long, mozilla::Vector<unsigned char, 0ul, mozilla::MallocAllocPolicy>&)
Unexecuted instantiation: Unified_cpp_certverifier0.cpp:mozilla::pkix::Result mozilla::ct::WriteUint<2ul, unsigned long>(unsigned long, mozilla::Vector<unsigned char, 0ul, mozilla::MallocAllocPolicy>&)
221
222
// Writes an array to |output| from |input|.
223
// Should be used in one of two cases:
224
// * The length of |input| has already been encoded into the |output| stream.
225
// * The length of |input| is fixed and the reader is expected to specify that
226
// length when reading.
227
// If the length of |input| is dynamic and data is expected to follow it,
228
// WriteVariableBytes must be used.
229
static Result
230
WriteEncodedBytes(Input input, Buffer& output)
231
0
{
232
0
  if (!output.append(input.UnsafeGetData(), input.GetLength())) {
233
0
    return Result::FATAL_ERROR_NO_MEMORY;
234
0
  }
235
0
  return Success;
236
0
}
237
238
// Same as above, but the source data is in a Buffer.
239
static Result
240
WriteEncodedBytes(const Buffer& source, Buffer& output)
241
0
{
242
0
  if (!output.appendAll(source)) {
243
0
    return Result::FATAL_ERROR_NO_MEMORY;
244
0
  }
245
0
  return Success;
246
0
}
247
248
// A variable-length byte array is prefixed by its length when serialized.
249
// This writes the length prefix.
250
// |prefixLength| indicates the number of bytes needed to represent the length.
251
// |dataLength| is the length of the byte array following the prefix.
252
// Fails if |dataLength| is more than 2^|prefixLength| - 1.
253
template <size_t prefixLength>
254
static Result
255
WriteVariableBytesPrefix(size_t dataLength, Buffer& output)
256
0
{
257
0
  const size_t maxAllowedInputSize =
258
0
    static_cast<size_t>(((1 << (prefixLength * 8)) - 1));
259
0
  if (dataLength > maxAllowedInputSize) {
260
0
    return Result::FATAL_ERROR_INVALID_ARGS;
261
0
  }
262
0
263
0
  return WriteUint<prefixLength>(dataLength, output);
264
0
}
Unexecuted instantiation: Unified_cpp_certverifier0.cpp:mozilla::pkix::Result mozilla::ct::WriteVariableBytesPrefix<3ul>(unsigned long, mozilla::Vector<unsigned char, 0ul, mozilla::MallocAllocPolicy>&)
Unexecuted instantiation: Unified_cpp_certverifier0.cpp:mozilla::pkix::Result mozilla::ct::WriteVariableBytesPrefix<2ul>(unsigned long, mozilla::Vector<unsigned char, 0ul, mozilla::MallocAllocPolicy>&)
265
266
// Writes a variable-length array to |output|.
267
// |prefixLength| indicates the number of bytes needed to represent the length.
268
// |input| is the array itself.
269
// Fails if the size of |input| is more than 2^|prefixLength| - 1.
270
template <size_t prefixLength>
271
static Result
272
WriteVariableBytes(Input input, Buffer& output)
273
0
{
274
0
  Result rv = WriteVariableBytesPrefix<prefixLength>(input.GetLength(), output);
275
0
  if (rv != Success) {
276
0
    return rv;
277
0
  }
278
0
  return WriteEncodedBytes(input, output);
279
0
}
Unexecuted instantiation: Unified_cpp_certverifier0.cpp:mozilla::pkix::Result mozilla::ct::WriteVariableBytes<3ul>(mozilla::pkix::Input, mozilla::Vector<unsigned char, 0ul, mozilla::MallocAllocPolicy>&)
Unexecuted instantiation: Unified_cpp_certverifier0.cpp:mozilla::pkix::Result mozilla::ct::WriteVariableBytes<2ul>(mozilla::pkix::Input, mozilla::Vector<unsigned char, 0ul, mozilla::MallocAllocPolicy>&)
280
281
// Same as above, but the source data is in a Buffer.
282
template <size_t prefixLength>
283
static Result
284
WriteVariableBytes(const Buffer& source, Buffer& output)
285
0
{
286
0
  Input input;
287
0
  Result rv = BufferToInput(source, input);
288
0
  if (rv != Success) {
289
0
    return rv;
290
0
  }
291
0
  return WriteVariableBytes<prefixLength>(input, output);
292
0
}
Unexecuted instantiation: Unified_cpp_certverifier0.cpp:mozilla::pkix::Result mozilla::ct::WriteVariableBytes<3ul>(mozilla::Vector<unsigned char, 0ul, mozilla::MallocAllocPolicy> const&, mozilla::Vector<unsigned char, 0ul, mozilla::MallocAllocPolicy>&)
Unexecuted instantiation: Unified_cpp_certverifier0.cpp:mozilla::pkix::Result mozilla::ct::WriteVariableBytes<2ul>(mozilla::Vector<unsigned char, 0ul, mozilla::MallocAllocPolicy> const&, mozilla::Vector<unsigned char, 0ul, mozilla::MallocAllocPolicy>&)
293
294
// Writes a LogEntry of type X.509 cert to |output|.
295
// |input| is the LogEntry containing the certificate.
296
static Result
297
EncodeAsn1CertLogEntry(const LogEntry& entry, Buffer& output)
298
0
{
299
0
  return WriteVariableBytes<kAsn1CertificateLengthBytes>(entry.leafCertificate,
300
0
                                                         output);
301
0
}
302
303
// Writes a LogEntry of type PreCertificate to |output|.
304
// |input| is the LogEntry containing the TBSCertificate and issuer key hash.
305
static Result
306
EncodePrecertLogEntry(const LogEntry& entry, Buffer& output)
307
0
{
308
0
  if (entry.issuerKeyHash.length() != kLogIdLength) {
309
0
    return Result::FATAL_ERROR_INVALID_ARGS;
310
0
  }
311
0
  Result rv = WriteEncodedBytes(entry.issuerKeyHash, output);
312
0
  if (rv != Success) {
313
0
    return rv;
314
0
  }
315
0
  return WriteVariableBytes<kTbsCertificateLengthBytes>(entry.tbsCertificate,
316
0
                                                        output);
317
0
}
318
319
320
Result
321
EncodeDigitallySigned(const DigitallySigned& data, Buffer& output)
322
0
{
323
0
  Result rv = WriteUint<kHashAlgorithmLength>(
324
0
    static_cast<unsigned int>(data.hashAlgorithm), output);
325
0
  if (rv != Success) {
326
0
    return rv;
327
0
  }
328
0
  rv = WriteUint<kSigAlgorithmLength>(
329
0
    static_cast<unsigned int>(data.signatureAlgorithm), output);
330
0
  if (rv != Success) {
331
0
    return rv;
332
0
  }
333
0
  return WriteVariableBytes<kSignatureLengthBytes>(data.signatureData, output);
334
0
}
335
336
Result
337
DecodeDigitallySigned(Reader& reader, DigitallySigned& output)
338
0
{
339
0
  DigitallySigned result;
340
0
341
0
  Result rv = ReadHashAlgorithm(reader, result.hashAlgorithm);
342
0
  if (rv != Success) {
343
0
    return rv;
344
0
  }
345
0
  rv = ReadSignatureAlgorithm(reader, result.signatureAlgorithm);
346
0
  if (rv != Success) {
347
0
    return rv;
348
0
  }
349
0
350
0
  Input signatureData;
351
0
  rv = ReadVariableBytes<kSignatureLengthBytes>(reader, signatureData);
352
0
  if (rv != Success) {
353
0
    return rv;
354
0
  }
355
0
  rv = InputToBuffer(signatureData, result.signatureData);
356
0
  if (rv != Success) {
357
0
    return rv;
358
0
  }
359
0
360
0
  output = std::move(result);
361
0
  return Success;
362
0
}
363
364
Result
365
EncodeLogEntry(const LogEntry& entry, Buffer& output)
366
0
{
367
0
  Result rv = WriteUint<kLogEntryTypeLength>(
368
0
    static_cast<unsigned int>(entry.type), output);
369
0
  if (rv != Success) {
370
0
    return rv;
371
0
  }
372
0
  switch (entry.type) {
373
0
    case LogEntry::Type::X509:
374
0
      return EncodeAsn1CertLogEntry(entry, output);
375
0
    case LogEntry::Type::Precert:
376
0
      return EncodePrecertLogEntry(entry, output);
377
0
    default:
378
0
      MOZ_ASSERT_UNREACHABLE("Unexpected LogEntry type");
379
0
  }
380
0
  return Result::ERROR_BAD_DER;
381
0
}
382
383
static Result
384
WriteTimeSinceEpoch(uint64_t timestamp, Buffer& output)
385
0
{
386
0
  return WriteUint<kTimestampLength>(timestamp, output);
387
0
}
388
389
Result
390
EncodeV1SCTSignedData(uint64_t timestamp, Input serializedLogEntry,
391
                      Input extensions, Buffer& output)
392
0
{
393
0
  Result rv = WriteUint<kVersionLength>(static_cast<unsigned int>(
394
0
    SignedCertificateTimestamp::Version::V1), output);
395
0
  if (rv != Success) {
396
0
    return rv;
397
0
  }
398
0
  rv = WriteUint<kSignatureTypeLength>(static_cast<unsigned int>(
399
0
    SignatureType::CertificateTimestamp), output);
400
0
  if (rv != Success) {
401
0
    return rv;
402
0
  }
403
0
  rv = WriteTimeSinceEpoch(timestamp, output);
404
0
  if (rv != Success) {
405
0
    return rv;
406
0
  }
407
0
  // NOTE: serializedLogEntry must already be serialized and contain the
408
0
  // length as the prefix.
409
0
  rv = WriteEncodedBytes(serializedLogEntry, output);
410
0
  if (rv != Success) {
411
0
    return rv;
412
0
  }
413
0
  return WriteVariableBytes<kExtensionsLengthBytes>(extensions, output);
414
0
}
415
416
Result
417
EncodeTreeHeadSignature(const SignedTreeHead& signedTreeHead,
418
                        Buffer& output)
419
0
{
420
0
  Result rv = WriteUint<kVersionLength>(
421
0
    static_cast<unsigned int>(signedTreeHead.version), output);
422
0
  if (rv != Success) {
423
0
    return rv;
424
0
  }
425
0
  rv = WriteUint<kSignatureTypeLength>(
426
0
    static_cast<unsigned int>(SignatureType::TreeHash), output);
427
0
  if (rv != Success) {
428
0
    return rv;
429
0
  }
430
0
  rv = WriteTimeSinceEpoch(signedTreeHead.timestamp, output);
431
0
  if (rv != Success) {
432
0
    return rv;
433
0
  }
434
0
  rv = WriteUint<kTreeSizeLength>(signedTreeHead.treeSize, output);
435
0
  if (rv != Success) {
436
0
    return rv;
437
0
  }
438
0
  if (signedTreeHead.sha256RootHash.length() != kSthRootHashLength) {
439
0
    return Result::FATAL_ERROR_INVALID_ARGS;
440
0
  }
441
0
  return WriteEncodedBytes(signedTreeHead.sha256RootHash, output);
442
0
}
443
444
Result
445
DecodeSCTList(Input input, Reader& listReader)
446
0
{
447
0
  Reader inputReader(input);
448
0
  Input listData;
449
0
  Result rv = ReadVariableBytes<kSCTListLengthBytes>(inputReader, listData);
450
0
  if (rv != Success) {
451
0
    return rv;
452
0
  }
453
0
  return listReader.Init(listData);
454
0
}
455
456
Result
457
ReadSCTListItem(Reader& listReader, Input& output)
458
0
{
459
0
  if (listReader.AtEnd()) {
460
0
    return Result::FATAL_ERROR_INVALID_ARGS;
461
0
  }
462
0
463
0
  Result rv = ReadVariableBytes<kSerializedSCTLengthBytes>(listReader, output);
464
0
  if (rv != Success) {
465
0
    return rv;
466
0
  }
467
0
  if (output.GetLength() == 0) {
468
0
    return Result::ERROR_BAD_DER;
469
0
  }
470
0
  return Success;
471
0
}
472
473
Result
474
DecodeSignedCertificateTimestamp(Reader& reader,
475
                                 SignedCertificateTimestamp& output)
476
0
{
477
0
  SignedCertificateTimestamp result;
478
0
479
0
  Result rv = ReadVersion(reader, result.version);
480
0
  if (rv != Success) {
481
0
    return rv;
482
0
  }
483
0
484
0
  uint64_t timestamp;
485
0
  Input logId;
486
0
  Input extensions;
487
0
488
0
  rv = ReadFixedBytes(kLogIdLength, reader, logId);
489
0
  if (rv != Success) {
490
0
    return rv;
491
0
  }
492
0
  rv = ReadUint<kTimestampLength>(reader, timestamp);
493
0
  if (rv != Success) {
494
0
    return rv;
495
0
  }
496
0
  rv = ReadVariableBytes<kExtensionsLengthBytes>(reader, extensions);
497
0
  if (rv != Success) {
498
0
    return rv;
499
0
  }
500
0
  rv = DecodeDigitallySigned(reader, result.signature);
501
0
  if (rv != Success) {
502
0
    return rv;
503
0
  }
504
0
505
0
  rv = InputToBuffer(logId, result.logId);
506
0
  if (rv != Success) {
507
0
    return rv;
508
0
  }
509
0
  rv = InputToBuffer(extensions, result.extensions);
510
0
  if (rv != Success) {
511
0
    return rv;
512
0
  }
513
0
  result.timestamp = timestamp;
514
0
515
0
  output = std::move(result);
516
0
  return Success;
517
0
}
518
519
Result
520
EncodeSCTList(const Vector<pkix::Input>& scts, Buffer& output)
521
0
{
522
0
  // Find out the total size of the SCT list to be written so we can
523
0
  // write the prefix for the list before writing its contents.
524
0
  size_t sctListLength = 0;
525
0
  for (auto& sct : scts) {
526
0
    sctListLength +=
527
0
      /* data size */ sct.GetLength() +
528
0
      /* length prefix size */ kSerializedSCTLengthBytes;
529
0
  }
530
0
531
0
  if (!output.reserve(kSCTListLengthBytes + sctListLength)) {
532
0
    return Result::FATAL_ERROR_NO_MEMORY;
533
0
  }
534
0
535
0
  // Write the prefix for the SCT list.
536
0
  Result rv = WriteVariableBytesPrefix<kSCTListLengthBytes>(sctListLength,
537
0
                                                            output);
538
0
  if (rv != Success) {
539
0
    return rv;
540
0
  }
541
0
  // Now write each SCT from the list.
542
0
  for (auto& sct : scts) {
543
0
    rv = WriteVariableBytes<kSerializedSCTLengthBytes>(sct, output);
544
0
    if (rv != Success) {
545
0
      return rv;
546
0
    }
547
0
  }
548
0
  return Success;
549
0
}
550
551
} } // namespace mozilla::ct