Bug 1731994: part 2) Add some documentation to `ContentPermissionRequestBase`'s const...
[gecko.git] / security / ct / CTSerialization.cpp
blobe6514ce6e52624f962e8ef910a6b2facd8e6d478
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 "CTSerialization.h"
8 #include "CTUtils.h"
10 #include <stdint.h>
11 #include <type_traits>
13 namespace mozilla {
14 namespace ct {
16 using namespace mozilla::pkix;
18 typedef mozilla::pkix::Result Result;
20 // Note: length is always specified in bytes.
21 // Signed Certificate Timestamp (SCT) Version length
22 static const size_t kVersionLength = 1;
24 // Members of a V1 SCT
25 static const size_t kLogIdLength = 32;
26 static const size_t kTimestampLength = 8;
27 static const size_t kExtensionsLengthBytes = 2;
28 static const size_t kHashAlgorithmLength = 1;
29 static const size_t kSigAlgorithmLength = 1;
30 static const size_t kSignatureLengthBytes = 2;
32 // Members of the digitally-signed struct of a V1 SCT
33 static const size_t kSignatureTypeLength = 1;
34 static const size_t kLogEntryTypeLength = 2;
35 static const size_t kAsn1CertificateLengthBytes = 3;
36 static const size_t kTbsCertificateLengthBytes = 3;
38 static const size_t kSCTListLengthBytes = 2;
39 static const size_t kSerializedSCTLengthBytes = 2;
41 // Length of sha256RootHash buffer of SignedTreeHead
42 static const size_t kSthRootHashLength = 32;
44 enum class SignatureType {
45 CertificateTimestamp = 0,
46 TreeHash = 1,
49 // Reads a TLS-encoded variable length unsigned integer from |in|.
50 // The integer is expected to be in big-endian order, which is used by TLS.
51 // Note: does not check if the output parameter overflows while reading.
52 // |length| indicates the size (in bytes) of the serialized integer.
53 static Result UncheckedReadUint(size_t length, Reader& in, uint64_t& out) {
54 uint64_t result = 0;
55 for (size_t i = 0; i < length; ++i) {
56 uint8_t value;
57 Result rv = in.Read(value);
58 if (rv != Success) {
59 return rv;
61 result = (result << 8) | value;
63 out = result;
64 return Success;
67 // Performs overflow sanity checks and calls UncheckedReadUint.
68 template <size_t length, typename T>
69 Result ReadUint(Reader& in, T& out) {
70 uint64_t value;
71 static_assert(std::is_unsigned<T>::value, "T must be unsigned");
72 static_assert(length <= 8, "At most 8 byte integers can be read");
73 static_assert(sizeof(T) >= length, "T must be able to hold <length> bytes");
74 Result rv = UncheckedReadUint(length, in, value);
75 if (rv != Success) {
76 return rv;
78 out = static_cast<T>(value);
79 return Success;
82 // Reads |length| bytes from |in|.
83 static Result ReadFixedBytes(size_t length, Reader& in, Input& out) {
84 return in.Skip(length, out);
87 // Reads a length-prefixed variable amount of bytes from |in|, updating |out|
88 // on success. |prefixLength| indicates the number of bytes needed to represent
89 // the length.
90 template <size_t prefixLength>
91 Result ReadVariableBytes(Reader& in, Input& out) {
92 size_t length;
93 Result rv = ReadUint<prefixLength>(in, length);
94 if (rv != Success) {
95 return rv;
97 return ReadFixedBytes(length, in, out);
100 // Reads a serialized hash algorithm.
101 static Result ReadHashAlgorithm(Reader& in,
102 DigitallySigned::HashAlgorithm& out) {
103 unsigned int value;
104 Result rv = ReadUint<kHashAlgorithmLength>(in, value);
105 if (rv != Success) {
106 return rv;
108 DigitallySigned::HashAlgorithm algo =
109 static_cast<DigitallySigned::HashAlgorithm>(value);
110 switch (algo) {
111 case DigitallySigned::HashAlgorithm::None:
112 case DigitallySigned::HashAlgorithm::MD5:
113 case DigitallySigned::HashAlgorithm::SHA1:
114 case DigitallySigned::HashAlgorithm::SHA224:
115 case DigitallySigned::HashAlgorithm::SHA256:
116 case DigitallySigned::HashAlgorithm::SHA384:
117 case DigitallySigned::HashAlgorithm::SHA512:
118 out = algo;
119 return Success;
121 return Result::ERROR_BAD_DER;
124 // Reads a serialized signature algorithm.
125 static Result ReadSignatureAlgorithm(Reader& in,
126 DigitallySigned::SignatureAlgorithm& out) {
127 unsigned int value;
128 Result rv = ReadUint<kSigAlgorithmLength>(in, value);
129 if (rv != Success) {
130 return rv;
132 DigitallySigned::SignatureAlgorithm algo =
133 static_cast<DigitallySigned::SignatureAlgorithm>(value);
134 switch (algo) {
135 case DigitallySigned::SignatureAlgorithm::Anonymous:
136 case DigitallySigned::SignatureAlgorithm::RSA:
137 case DigitallySigned::SignatureAlgorithm::DSA:
138 case DigitallySigned::SignatureAlgorithm::ECDSA:
139 out = algo;
140 return Success;
142 return Result::ERROR_BAD_DER;
145 // Reads a serialized version enum.
146 static Result ReadVersion(Reader& in,
147 SignedCertificateTimestamp::Version& out) {
148 unsigned int value;
149 Result rv = ReadUint<kVersionLength>(in, value);
150 if (rv != Success) {
151 return rv;
153 SignedCertificateTimestamp::Version version =
154 static_cast<SignedCertificateTimestamp::Version>(value);
155 switch (version) {
156 case SignedCertificateTimestamp::Version::V1:
157 out = version;
158 return Success;
160 return Result::ERROR_BAD_DER;
163 // Writes a TLS-encoded variable length unsigned integer to |output|.
164 // Note: range/overflow checks are not performed on the input parameters.
165 // |length| indicates the size (in bytes) of the integer to be written.
166 // |value| the value itself to be written.
167 static Result UncheckedWriteUint(size_t length, uint64_t value,
168 Buffer& output) {
169 output.reserve(length + output.size());
170 for (; length > 0; --length) {
171 uint8_t nextByte = (value >> ((length - 1) * 8)) & 0xFF;
172 output.push_back(nextByte);
174 return Success;
177 // Performs sanity checks on T and calls UncheckedWriteUint.
178 template <size_t length, typename T>
179 static inline Result WriteUint(T value, Buffer& output) {
180 static_assert(length <= 8, "At most 8 byte integers can be written");
181 static_assert(sizeof(T) >= length, "T must be able to hold <length> bytes");
182 if (std::is_signed<T>::value) {
183 // We accept signed integer types assuming the actual value is non-negative.
184 if (value < 0) {
185 return Result::FATAL_ERROR_INVALID_ARGS;
188 if (sizeof(T) > length) {
189 // We allow the value variable to take more bytes than is written,
190 // but the unwritten bytes must be zero.
191 // Note: when "sizeof(T) == length" holds, "value >> (length * 8)" is
192 // undefined since the shift is too big. On some compilers, this would
193 // produce a warning even though the actual code is unreachable.
194 if (value >> (length * 8 - 1) > 1) {
195 return Result::FATAL_ERROR_INVALID_ARGS;
198 return UncheckedWriteUint(length, static_cast<uint64_t>(value), output);
201 // Writes an array to |output| from |input|.
202 // Should be used in one of two cases:
203 // * The length of |input| has already been encoded into the |output| stream.
204 // * The length of |input| is fixed and the reader is expected to specify that
205 // length when reading.
206 // If the length of |input| is dynamic and data is expected to follow it,
207 // WriteVariableBytes must be used.
208 static void WriteEncodedBytes(Input input, Buffer& output) {
209 output.insert(output.end(), input.UnsafeGetData(),
210 input.UnsafeGetData() + input.GetLength());
213 // Same as above, but the source data is in a Buffer.
214 static void WriteEncodedBytes(const Buffer& source, Buffer& output) {
215 output.insert(output.end(), source.begin(), source.end());
218 // A variable-length byte array is prefixed by its length when serialized.
219 // This writes the length prefix.
220 // |prefixLength| indicates the number of bytes needed to represent the length.
221 // |dataLength| is the length of the byte array following the prefix.
222 // Fails if |dataLength| is more than 2^|prefixLength| - 1.
223 template <size_t prefixLength>
224 static Result WriteVariableBytesPrefix(size_t dataLength, Buffer& output) {
225 const size_t maxAllowedInputSize =
226 static_cast<size_t>(((1 << (prefixLength * 8)) - 1));
227 if (dataLength > maxAllowedInputSize) {
228 return Result::FATAL_ERROR_INVALID_ARGS;
231 return WriteUint<prefixLength>(dataLength, output);
234 // Writes a variable-length array to |output|.
235 // |prefixLength| indicates the number of bytes needed to represent the length.
236 // |input| is the array itself.
237 // Fails if the size of |input| is more than 2^|prefixLength| - 1.
238 template <size_t prefixLength>
239 static Result WriteVariableBytes(Input input, Buffer& output) {
240 Result rv = WriteVariableBytesPrefix<prefixLength>(input.GetLength(), output);
241 if (rv != Success) {
242 return rv;
244 WriteEncodedBytes(input, output);
245 return Success;
248 // Same as above, but the source data is in a Buffer.
249 template <size_t prefixLength>
250 static Result WriteVariableBytes(const Buffer& source, Buffer& output) {
251 Input input;
252 Result rv = BufferToInput(source, input);
253 if (rv != Success) {
254 return rv;
256 return WriteVariableBytes<prefixLength>(input, output);
259 // Writes a LogEntry of type X.509 cert to |output|.
260 // |input| is the LogEntry containing the certificate.
261 static Result EncodeAsn1CertLogEntry(const LogEntry& entry, Buffer& output) {
262 return WriteVariableBytes<kAsn1CertificateLengthBytes>(entry.leafCertificate,
263 output);
266 // Writes a LogEntry of type PreCertificate to |output|.
267 // |input| is the LogEntry containing the TBSCertificate and issuer key hash.
268 static Result EncodePrecertLogEntry(const LogEntry& entry, Buffer& output) {
269 if (entry.issuerKeyHash.size() != kLogIdLength) {
270 return Result::FATAL_ERROR_INVALID_ARGS;
272 WriteEncodedBytes(entry.issuerKeyHash, output);
273 return WriteVariableBytes<kTbsCertificateLengthBytes>(entry.tbsCertificate,
274 output);
277 Result EncodeDigitallySigned(const DigitallySigned& data, Buffer& output) {
278 Result rv = WriteUint<kHashAlgorithmLength>(
279 static_cast<unsigned int>(data.hashAlgorithm), output);
280 if (rv != Success) {
281 return rv;
283 rv = WriteUint<kSigAlgorithmLength>(
284 static_cast<unsigned int>(data.signatureAlgorithm), output);
285 if (rv != Success) {
286 return rv;
288 return WriteVariableBytes<kSignatureLengthBytes>(data.signatureData, output);
291 Result DecodeDigitallySigned(Reader& reader, DigitallySigned& output) {
292 DigitallySigned result;
294 Result rv = ReadHashAlgorithm(reader, result.hashAlgorithm);
295 if (rv != Success) {
296 return rv;
298 rv = ReadSignatureAlgorithm(reader, result.signatureAlgorithm);
299 if (rv != Success) {
300 return rv;
303 Input signatureData;
304 rv = ReadVariableBytes<kSignatureLengthBytes>(reader, signatureData);
305 if (rv != Success) {
306 return rv;
308 InputToBuffer(signatureData, result.signatureData);
310 output = std::move(result);
311 return Success;
314 Result EncodeLogEntry(const LogEntry& entry, Buffer& output) {
315 Result rv = WriteUint<kLogEntryTypeLength>(
316 static_cast<unsigned int>(entry.type), output);
317 if (rv != Success) {
318 return rv;
320 switch (entry.type) {
321 case LogEntry::Type::X509:
322 return EncodeAsn1CertLogEntry(entry, output);
323 case LogEntry::Type::Precert:
324 return EncodePrecertLogEntry(entry, output);
325 default:
326 assert(false);
328 return Result::ERROR_BAD_DER;
331 static Result WriteTimeSinceEpoch(uint64_t timestamp, Buffer& output) {
332 return WriteUint<kTimestampLength>(timestamp, output);
335 Result EncodeV1SCTSignedData(uint64_t timestamp, Input serializedLogEntry,
336 Input extensions, Buffer& output) {
337 Result rv = WriteUint<kVersionLength>(
338 static_cast<unsigned int>(SignedCertificateTimestamp::Version::V1),
339 output);
340 if (rv != Success) {
341 return rv;
343 rv = WriteUint<kSignatureTypeLength>(
344 static_cast<unsigned int>(SignatureType::CertificateTimestamp), output);
345 if (rv != Success) {
346 return rv;
348 rv = WriteTimeSinceEpoch(timestamp, output);
349 if (rv != Success) {
350 return rv;
352 // NOTE: serializedLogEntry must already be serialized and contain the
353 // length as the prefix.
354 WriteEncodedBytes(serializedLogEntry, output);
355 return WriteVariableBytes<kExtensionsLengthBytes>(extensions, output);
358 Result DecodeSCTList(Input input, Reader& listReader) {
359 Reader inputReader(input);
360 Input listData;
361 Result rv = ReadVariableBytes<kSCTListLengthBytes>(inputReader, listData);
362 if (rv != Success) {
363 return rv;
365 return listReader.Init(listData);
368 Result ReadSCTListItem(Reader& listReader, Input& output) {
369 if (listReader.AtEnd()) {
370 return Result::FATAL_ERROR_INVALID_ARGS;
373 Result rv = ReadVariableBytes<kSerializedSCTLengthBytes>(listReader, output);
374 if (rv != Success) {
375 return rv;
377 if (output.GetLength() == 0) {
378 return Result::ERROR_BAD_DER;
380 return Success;
383 Result DecodeSignedCertificateTimestamp(Reader& reader,
384 SignedCertificateTimestamp& output) {
385 SignedCertificateTimestamp result;
387 Result rv = ReadVersion(reader, result.version);
388 if (rv != Success) {
389 return rv;
392 uint64_t timestamp;
393 Input logId;
394 Input extensions;
396 rv = ReadFixedBytes(kLogIdLength, reader, logId);
397 if (rv != Success) {
398 return rv;
400 rv = ReadUint<kTimestampLength>(reader, timestamp);
401 if (rv != Success) {
402 return rv;
404 rv = ReadVariableBytes<kExtensionsLengthBytes>(reader, extensions);
405 if (rv != Success) {
406 return rv;
408 rv = DecodeDigitallySigned(reader, result.signature);
409 if (rv != Success) {
410 return rv;
413 InputToBuffer(logId, result.logId);
414 InputToBuffer(extensions, result.extensions);
415 result.timestamp = timestamp;
417 output = std::move(result);
418 return Success;
421 Result EncodeSCTList(const std::vector<pkix::Input>& scts, Buffer& output) {
422 // Find out the total size of the SCT list to be written so we can
423 // write the prefix for the list before writing its contents.
424 size_t sctListLength = 0;
425 for (auto& sct : scts) {
426 sctListLength +=
427 /* data size */ sct.GetLength() +
428 /* length prefix size */ kSerializedSCTLengthBytes;
431 output.reserve(kSCTListLengthBytes + sctListLength);
433 // Write the prefix for the SCT list.
434 Result rv =
435 WriteVariableBytesPrefix<kSCTListLengthBytes>(sctListLength, output);
436 if (rv != Success) {
437 return rv;
439 // Now write each SCT from the list.
440 for (auto& sct : scts) {
441 rv = WriteVariableBytes<kSerializedSCTLengthBytes>(sct, output);
442 if (rv != Success) {
443 return rv;
446 return Success;
449 } // namespace ct
450 } // namespace mozilla