Bug 1839316: part 5) Guard the "fetchpriority" attribute behind a pref. r=kershaw...
[gecko.git] / security / ct / CTSerialization.cpp
blobed6cbb891a43df5c488184591a2d693b1fbd6f41
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 enum class SignatureType {
42 CertificateTimestamp = 0,
43 TreeHash = 1,
46 // Reads a serialized hash algorithm.
47 static Result ReadHashAlgorithm(Reader& in,
48 DigitallySigned::HashAlgorithm& out) {
49 unsigned int value;
50 Result rv = ReadUint<kHashAlgorithmLength>(in, value);
51 if (rv != Success) {
52 return rv;
54 DigitallySigned::HashAlgorithm algo =
55 static_cast<DigitallySigned::HashAlgorithm>(value);
56 switch (algo) {
57 case DigitallySigned::HashAlgorithm::None:
58 case DigitallySigned::HashAlgorithm::MD5:
59 case DigitallySigned::HashAlgorithm::SHA1:
60 case DigitallySigned::HashAlgorithm::SHA224:
61 case DigitallySigned::HashAlgorithm::SHA256:
62 case DigitallySigned::HashAlgorithm::SHA384:
63 case DigitallySigned::HashAlgorithm::SHA512:
64 out = algo;
65 return Success;
67 return Result::ERROR_BAD_DER;
70 // Reads a serialized signature algorithm.
71 static Result ReadSignatureAlgorithm(Reader& in,
72 DigitallySigned::SignatureAlgorithm& out) {
73 unsigned int value;
74 Result rv = ReadUint<kSigAlgorithmLength>(in, value);
75 if (rv != Success) {
76 return rv;
78 DigitallySigned::SignatureAlgorithm algo =
79 static_cast<DigitallySigned::SignatureAlgorithm>(value);
80 switch (algo) {
81 case DigitallySigned::SignatureAlgorithm::Anonymous:
82 case DigitallySigned::SignatureAlgorithm::RSA:
83 case DigitallySigned::SignatureAlgorithm::DSA:
84 case DigitallySigned::SignatureAlgorithm::ECDSA:
85 out = algo;
86 return Success;
88 return Result::ERROR_BAD_DER;
91 // Reads a serialized version enum.
92 static Result ReadVersion(Reader& in,
93 SignedCertificateTimestamp::Version& out) {
94 unsigned int value;
95 Result rv = ReadUint<kVersionLength>(in, value);
96 if (rv != Success) {
97 return rv;
99 SignedCertificateTimestamp::Version version =
100 static_cast<SignedCertificateTimestamp::Version>(value);
101 switch (version) {
102 case SignedCertificateTimestamp::Version::V1:
103 out = version;
104 return Success;
106 return Result::ERROR_BAD_DER;
109 // Writes a TLS-encoded variable length unsigned integer to |output|.
110 // Note: range/overflow checks are not performed on the input parameters.
111 // |length| indicates the size (in bytes) of the integer to be written.
112 // |value| the value itself to be written.
113 static Result UncheckedWriteUint(size_t length, uint64_t value,
114 Buffer& output) {
115 output.reserve(length + output.size());
116 for (; length > 0; --length) {
117 uint8_t nextByte = (value >> ((length - 1) * 8)) & 0xFF;
118 output.push_back(nextByte);
120 return Success;
123 // Performs sanity checks on T and calls UncheckedWriteUint.
124 template <size_t length, typename T>
125 static inline Result WriteUint(T value, Buffer& output) {
126 static_assert(length <= 8, "At most 8 byte integers can be written");
127 static_assert(sizeof(T) >= length, "T must be able to hold <length> bytes");
128 if (std::is_signed<T>::value) {
129 // We accept signed integer types assuming the actual value is non-negative.
130 if (value < 0) {
131 return Result::FATAL_ERROR_INVALID_ARGS;
134 if (sizeof(T) > length) {
135 // We allow the value variable to take more bytes than is written,
136 // but the unwritten bytes must be zero.
137 // Note: when "sizeof(T) == length" holds, "value >> (length * 8)" is
138 // undefined since the shift is too big. On some compilers, this would
139 // produce a warning even though the actual code is unreachable.
140 if (value >> (length * 8 - 1) > 1) {
141 return Result::FATAL_ERROR_INVALID_ARGS;
144 return UncheckedWriteUint(length, static_cast<uint64_t>(value), output);
147 // Writes an array to |output| from |input|.
148 // Should be used in one of two cases:
149 // * The length of |input| has already been encoded into the |output| stream.
150 // * The length of |input| is fixed and the reader is expected to specify that
151 // length when reading.
152 // If the length of |input| is dynamic and data is expected to follow it,
153 // WriteVariableBytes must be used.
154 static void WriteEncodedBytes(Input input, Buffer& output) {
155 output.insert(output.end(), input.UnsafeGetData(),
156 input.UnsafeGetData() + input.GetLength());
159 // Same as above, but the source data is in a Buffer.
160 static void WriteEncodedBytes(const Buffer& source, Buffer& output) {
161 output.insert(output.end(), source.begin(), source.end());
164 // A variable-length byte array is prefixed by its length when serialized.
165 // This writes the length prefix.
166 // |prefixLength| indicates the number of bytes needed to represent the length.
167 // |dataLength| is the length of the byte array following the prefix.
168 // Fails if |dataLength| is more than 2^|prefixLength| - 1.
169 template <size_t prefixLength>
170 static Result WriteVariableBytesPrefix(size_t dataLength, Buffer& output) {
171 const size_t maxAllowedInputSize =
172 static_cast<size_t>(((1 << (prefixLength * 8)) - 1));
173 if (dataLength > maxAllowedInputSize) {
174 return Result::FATAL_ERROR_INVALID_ARGS;
177 return WriteUint<prefixLength>(dataLength, output);
180 // Writes a variable-length array to |output|.
181 // |prefixLength| indicates the number of bytes needed to represent the length.
182 // |input| is the array itself.
183 // Fails if the size of |input| is more than 2^|prefixLength| - 1.
184 template <size_t prefixLength>
185 static Result WriteVariableBytes(Input input, Buffer& output) {
186 Result rv = WriteVariableBytesPrefix<prefixLength>(input.GetLength(), output);
187 if (rv != Success) {
188 return rv;
190 WriteEncodedBytes(input, output);
191 return Success;
194 // Same as above, but the source data is in a Buffer.
195 template <size_t prefixLength>
196 static Result WriteVariableBytes(const Buffer& source, Buffer& output) {
197 Input input;
198 Result rv = BufferToInput(source, input);
199 if (rv != Success) {
200 return rv;
202 return WriteVariableBytes<prefixLength>(input, output);
205 // Writes a LogEntry of type X.509 cert to |output|.
206 // |input| is the LogEntry containing the certificate.
207 static Result EncodeAsn1CertLogEntry(const LogEntry& entry, Buffer& output) {
208 return WriteVariableBytes<kAsn1CertificateLengthBytes>(entry.leafCertificate,
209 output);
212 // Writes a LogEntry of type PreCertificate to |output|.
213 // |input| is the LogEntry containing the TBSCertificate and issuer key hash.
214 static Result EncodePrecertLogEntry(const LogEntry& entry, Buffer& output) {
215 if (entry.issuerKeyHash.size() != kLogIdLength) {
216 return Result::FATAL_ERROR_INVALID_ARGS;
218 WriteEncodedBytes(entry.issuerKeyHash, output);
219 return WriteVariableBytes<kTbsCertificateLengthBytes>(entry.tbsCertificate,
220 output);
223 Result EncodeDigitallySigned(const DigitallySigned& data, Buffer& output) {
224 Result rv = WriteUint<kHashAlgorithmLength>(
225 static_cast<unsigned int>(data.hashAlgorithm), output);
226 if (rv != Success) {
227 return rv;
229 rv = WriteUint<kSigAlgorithmLength>(
230 static_cast<unsigned int>(data.signatureAlgorithm), output);
231 if (rv != Success) {
232 return rv;
234 return WriteVariableBytes<kSignatureLengthBytes>(data.signatureData, output);
237 Result DecodeDigitallySigned(Reader& reader, DigitallySigned& output) {
238 DigitallySigned result;
240 Result rv = ReadHashAlgorithm(reader, result.hashAlgorithm);
241 if (rv != Success) {
242 return rv;
244 rv = ReadSignatureAlgorithm(reader, result.signatureAlgorithm);
245 if (rv != Success) {
246 return rv;
249 Input signatureData;
250 rv = ReadVariableBytes<kSignatureLengthBytes>(reader, signatureData);
251 if (rv != Success) {
252 return rv;
254 InputToBuffer(signatureData, result.signatureData);
256 output = std::move(result);
257 return Success;
260 Result EncodeLogEntry(const LogEntry& entry, Buffer& output) {
261 Result rv = WriteUint<kLogEntryTypeLength>(
262 static_cast<unsigned int>(entry.type), output);
263 if (rv != Success) {
264 return rv;
266 switch (entry.type) {
267 case LogEntry::Type::X509:
268 return EncodeAsn1CertLogEntry(entry, output);
269 case LogEntry::Type::Precert:
270 return EncodePrecertLogEntry(entry, output);
271 default:
272 assert(false);
274 return Result::ERROR_BAD_DER;
277 static Result WriteTimeSinceEpoch(uint64_t timestamp, Buffer& output) {
278 return WriteUint<kTimestampLength>(timestamp, output);
281 Result EncodeV1SCTSignedData(uint64_t timestamp, Input serializedLogEntry,
282 Input extensions, Buffer& output) {
283 Result rv = WriteUint<kVersionLength>(
284 static_cast<unsigned int>(SignedCertificateTimestamp::Version::V1),
285 output);
286 if (rv != Success) {
287 return rv;
289 rv = WriteUint<kSignatureTypeLength>(
290 static_cast<unsigned int>(SignatureType::CertificateTimestamp), output);
291 if (rv != Success) {
292 return rv;
294 rv = WriteTimeSinceEpoch(timestamp, output);
295 if (rv != Success) {
296 return rv;
298 // NOTE: serializedLogEntry must already be serialized and contain the
299 // length as the prefix.
300 WriteEncodedBytes(serializedLogEntry, output);
301 return WriteVariableBytes<kExtensionsLengthBytes>(extensions, output);
304 Result DecodeSCTList(Input input, Reader& listReader) {
305 Reader inputReader(input);
306 Input listData;
307 Result rv = ReadVariableBytes<kSCTListLengthBytes>(inputReader, listData);
308 if (rv != Success) {
309 return rv;
311 return listReader.Init(listData);
314 Result ReadSCTListItem(Reader& listReader, Input& output) {
315 if (listReader.AtEnd()) {
316 return Result::FATAL_ERROR_INVALID_ARGS;
319 Result rv = ReadVariableBytes<kSerializedSCTLengthBytes>(listReader, output);
320 if (rv != Success) {
321 return rv;
323 if (output.GetLength() == 0) {
324 return Result::ERROR_BAD_DER;
326 return Success;
329 Result DecodeSignedCertificateTimestamp(Reader& reader,
330 SignedCertificateTimestamp& output) {
331 SignedCertificateTimestamp result;
333 Result rv = ReadVersion(reader, result.version);
334 if (rv != Success) {
335 return rv;
338 uint64_t timestamp;
339 Input logId;
340 Input extensions;
342 rv = ReadFixedBytes(kLogIdLength, reader, logId);
343 if (rv != Success) {
344 return rv;
346 rv = ReadUint<kTimestampLength>(reader, timestamp);
347 if (rv != Success) {
348 return rv;
350 rv = ReadVariableBytes<kExtensionsLengthBytes>(reader, extensions);
351 if (rv != Success) {
352 return rv;
354 rv = DecodeDigitallySigned(reader, result.signature);
355 if (rv != Success) {
356 return rv;
359 InputToBuffer(logId, result.logId);
360 InputToBuffer(extensions, result.extensions);
361 result.timestamp = timestamp;
363 output = std::move(result);
364 return Success;
367 Result EncodeSCTList(const std::vector<pkix::Input>& scts, Buffer& output) {
368 // Find out the total size of the SCT list to be written so we can
369 // write the prefix for the list before writing its contents.
370 size_t sctListLength = 0;
371 for (auto& sct : scts) {
372 sctListLength +=
373 /* data size */ sct.GetLength() +
374 /* length prefix size */ kSerializedSCTLengthBytes;
377 output.reserve(kSCTListLengthBytes + sctListLength);
379 // Write the prefix for the SCT list.
380 Result rv =
381 WriteVariableBytesPrefix<kSCTListLengthBytes>(sctListLength, output);
382 if (rv != Success) {
383 return rv;
385 // Now write each SCT from the list.
386 for (auto& sct : scts) {
387 rv = WriteVariableBytes<kSerializedSCTLengthBytes>(sct, output);
388 if (rv != Success) {
389 return rv;
392 return Success;
395 } // namespace ct
396 } // namespace mozilla