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"
11 #include <type_traits>
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,
46 // Reads a serialized hash algorithm.
47 static Result
ReadHashAlgorithm(Reader
& in
,
48 DigitallySigned::HashAlgorithm
& out
) {
50 Result rv
= ReadUint
<kHashAlgorithmLength
>(in
, value
);
54 DigitallySigned::HashAlgorithm algo
=
55 static_cast<DigitallySigned::HashAlgorithm
>(value
);
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
:
67 return Result::ERROR_BAD_DER
;
70 // Reads a serialized signature algorithm.
71 static Result
ReadSignatureAlgorithm(Reader
& in
,
72 DigitallySigned::SignatureAlgorithm
& out
) {
74 Result rv
= ReadUint
<kSigAlgorithmLength
>(in
, value
);
78 DigitallySigned::SignatureAlgorithm algo
=
79 static_cast<DigitallySigned::SignatureAlgorithm
>(value
);
81 case DigitallySigned::SignatureAlgorithm::Anonymous
:
82 case DigitallySigned::SignatureAlgorithm::RSA
:
83 case DigitallySigned::SignatureAlgorithm::DSA
:
84 case DigitallySigned::SignatureAlgorithm::ECDSA
:
88 return Result::ERROR_BAD_DER
;
91 // Reads a serialized version enum.
92 static Result
ReadVersion(Reader
& in
,
93 SignedCertificateTimestamp::Version
& out
) {
95 Result rv
= ReadUint
<kVersionLength
>(in
, value
);
99 SignedCertificateTimestamp::Version version
=
100 static_cast<SignedCertificateTimestamp::Version
>(value
);
102 case SignedCertificateTimestamp::Version::V1
:
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
,
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
);
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.
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
);
190 WriteEncodedBytes(input
, output
);
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
) {
198 Result rv
= BufferToInput(source
, input
);
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
,
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
,
223 Result
EncodeDigitallySigned(const DigitallySigned
& data
, Buffer
& output
) {
224 Result rv
= WriteUint
<kHashAlgorithmLength
>(
225 static_cast<unsigned int>(data
.hashAlgorithm
), output
);
229 rv
= WriteUint
<kSigAlgorithmLength
>(
230 static_cast<unsigned int>(data
.signatureAlgorithm
), output
);
234 return WriteVariableBytes
<kSignatureLengthBytes
>(data
.signatureData
, output
);
237 Result
DecodeDigitallySigned(Reader
& reader
, DigitallySigned
& output
) {
238 DigitallySigned result
;
240 Result rv
= ReadHashAlgorithm(reader
, result
.hashAlgorithm
);
244 rv
= ReadSignatureAlgorithm(reader
, result
.signatureAlgorithm
);
250 rv
= ReadVariableBytes
<kSignatureLengthBytes
>(reader
, signatureData
);
254 InputToBuffer(signatureData
, result
.signatureData
);
256 output
= std::move(result
);
260 Result
EncodeLogEntry(const LogEntry
& entry
, Buffer
& output
) {
261 Result rv
= WriteUint
<kLogEntryTypeLength
>(
262 static_cast<unsigned int>(entry
.type
), output
);
266 switch (entry
.type
) {
267 case LogEntry::Type::X509
:
268 return EncodeAsn1CertLogEntry(entry
, output
);
269 case LogEntry::Type::Precert
:
270 return EncodePrecertLogEntry(entry
, output
);
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
),
289 rv
= WriteUint
<kSignatureTypeLength
>(
290 static_cast<unsigned int>(SignatureType::CertificateTimestamp
), output
);
294 rv
= WriteTimeSinceEpoch(timestamp
, output
);
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
);
307 Result rv
= ReadVariableBytes
<kSCTListLengthBytes
>(inputReader
, listData
);
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
);
323 if (output
.GetLength() == 0) {
324 return Result::ERROR_BAD_DER
;
329 Result
DecodeSignedCertificateTimestamp(Reader
& reader
,
330 SignedCertificateTimestamp
& output
) {
331 SignedCertificateTimestamp result
;
333 Result rv
= ReadVersion(reader
, result
.version
);
342 rv
= ReadFixedBytes(kLogIdLength
, reader
, logId
);
346 rv
= ReadUint
<kTimestampLength
>(reader
, timestamp
);
350 rv
= ReadVariableBytes
<kExtensionsLengthBytes
>(reader
, extensions
);
354 rv
= DecodeDigitallySigned(reader
, result
.signature
);
359 InputToBuffer(logId
, result
.logId
);
360 InputToBuffer(extensions
, result
.extensions
);
361 result
.timestamp
= timestamp
;
363 output
= std::move(result
);
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
) {
373 /* data size */ sct
.GetLength() +
374 /* length prefix size */ kSerializedSCTLengthBytes
;
377 output
.reserve(kSCTListLengthBytes
+ sctListLength
);
379 // Write the prefix for the SCT list.
381 WriteVariableBytesPrefix
<kSCTListLengthBytes
>(sctListLength
, output
);
385 // Now write each SCT from the list.
386 for (auto& sct
: scts
) {
387 rv
= WriteVariableBytes
<kSerializedSCTLengthBytes
>(sct
, output
);
396 } // namespace mozilla