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