CTSerialization.cpp (12963B)
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 #include <type_traits> 12 13 namespace mozilla { 14 namespace ct { 15 16 using namespace mozilla::pkix; 17 18 typedef mozilla::pkix::Result Result; 19 20 // Note: length is always specified in bytes. 21 // Signed Certificate Timestamp (SCT) Version length 22 static const size_t kVersionLength = 1; 23 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; 31 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; 37 38 static const size_t kSCTListLengthBytes = 2; 39 static const size_t kSerializedSCTLengthBytes = 2; 40 41 enum class SignatureType { 42 CertificateTimestamp = 0, 43 TreeHash = 1, 44 }; 45 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; 53 } 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; 66 } 67 return Result::ERROR_BAD_DER; 68 } 69 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; 77 } 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; 87 } 88 return Result::ERROR_BAD_DER; 89 } 90 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; 98 } 99 SignedCertificateTimestamp::Version version = 100 static_cast<SignedCertificateTimestamp::Version>(value); 101 switch (version) { 102 case SignedCertificateTimestamp::Version::V1: 103 out = version; 104 return Success; 105 } 106 return Result::ERROR_BAD_DER; 107 } 108 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); 119 } 120 return Success; 121 } 122 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; 132 } 133 } 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; 142 } 143 } 144 return UncheckedWriteUint(length, static_cast<uint64_t>(value), output); 145 } 146 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()); 157 } 158 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()); 162 } 163 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; 175 } 176 177 return WriteUint<prefixLength>(dataLength, output); 178 } 179 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; 189 } 190 WriteEncodedBytes(input, output); 191 return Success; 192 } 193 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; 201 } 202 return WriteVariableBytes<prefixLength>(input, output); 203 } 204 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); 210 } 211 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; 217 } 218 WriteEncodedBytes(entry.issuerKeyHash, output); 219 return WriteVariableBytes<kTbsCertificateLengthBytes>(entry.tbsCertificate, 220 output); 221 } 222 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; 228 } 229 rv = WriteUint<kSigAlgorithmLength>( 230 static_cast<unsigned int>(data.signatureAlgorithm), output); 231 if (rv != Success) { 232 return rv; 233 } 234 return WriteVariableBytes<kSignatureLengthBytes>(data.signatureData, output); 235 } 236 237 Result DecodeDigitallySigned(Reader& reader, DigitallySigned& output) { 238 DigitallySigned result; 239 240 Result rv = ReadHashAlgorithm(reader, result.hashAlgorithm); 241 if (rv != Success) { 242 return rv; 243 } 244 rv = ReadSignatureAlgorithm(reader, result.signatureAlgorithm); 245 if (rv != Success) { 246 return rv; 247 } 248 249 Input signatureData; 250 rv = ReadVariableBytes<kSignatureLengthBytes>(reader, signatureData); 251 if (rv != Success) { 252 return rv; 253 } 254 InputToBuffer(signatureData, result.signatureData); 255 256 output = std::move(result); 257 return Success; 258 } 259 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; 265 } 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); 273 } 274 return Result::ERROR_BAD_DER; 275 } 276 277 static Result WriteTimeSinceEpoch(uint64_t timestamp, Buffer& output) { 278 return WriteUint<kTimestampLength>(timestamp, output); 279 } 280 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; 288 } 289 rv = WriteUint<kSignatureTypeLength>( 290 static_cast<unsigned int>(SignatureType::CertificateTimestamp), output); 291 if (rv != Success) { 292 return rv; 293 } 294 rv = WriteTimeSinceEpoch(timestamp, output); 295 if (rv != Success) { 296 return rv; 297 } 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); 302 } 303 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; 310 } 311 return listReader.Init(listData); 312 } 313 314 Result ReadSCTListItem(Reader& listReader, Input& output) { 315 if (listReader.AtEnd()) { 316 return Result::FATAL_ERROR_INVALID_ARGS; 317 } 318 319 Result rv = ReadVariableBytes<kSerializedSCTLengthBytes>(listReader, output); 320 if (rv != Success) { 321 return rv; 322 } 323 if (output.GetLength() == 0) { 324 return Result::ERROR_BAD_DER; 325 } 326 return Success; 327 } 328 329 Result DecodeSignedCertificateTimestamp(Reader& reader, 330 SignedCertificateTimestamp& output) { 331 SignedCertificateTimestamp result; 332 333 Result rv = ReadVersion(reader, result.version); 334 if (rv != Success) { 335 return rv; 336 } 337 338 uint64_t timestamp; 339 Input logId; 340 Input extensions; 341 342 rv = ReadFixedBytes(kLogIdLength, reader, logId); 343 if (rv != Success) { 344 return rv; 345 } 346 rv = ReadUint<kTimestampLength>(reader, timestamp); 347 if (rv != Success) { 348 return rv; 349 } 350 rv = ReadVariableBytes<kExtensionsLengthBytes>(reader, extensions); 351 if (rv != Success) { 352 return rv; 353 } 354 rv = DecodeDigitallySigned(reader, result.signature); 355 if (rv != Success) { 356 return rv; 357 } 358 359 InputToBuffer(logId, result.logId); 360 InputToBuffer(extensions, result.extensions); 361 result.timestamp = timestamp; 362 363 rv = result.DecodeExtensions(); 364 if (rv != Success) { 365 return rv; 366 } 367 368 output = std::move(result); 369 return Success; 370 } 371 372 Result EncodeSCTList(const std::vector<pkix::Input>& scts, Buffer& output) { 373 // Find out the total size of the SCT list to be written so we can 374 // write the prefix for the list before writing its contents. 375 size_t sctListLength = 0; 376 for (auto& sct : scts) { 377 sctListLength += 378 /* data size */ sct.GetLength() + 379 /* length prefix size */ kSerializedSCTLengthBytes; 380 } 381 382 output.reserve(kSCTListLengthBytes + sctListLength); 383 384 // Write the prefix for the SCT list. 385 Result rv = 386 WriteVariableBytesPrefix<kSCTListLengthBytes>(sctListLength, output); 387 if (rv != Success) { 388 return rv; 389 } 390 // Now write each SCT from the list. 391 for (auto& sct : scts) { 392 rv = WriteVariableBytes<kSerializedSCTLengthBytes>(sct, output); 393 if (rv != Success) { 394 return rv; 395 } 396 } 397 return Success; 398 } 399 400 } // namespace ct 401 } // namespace mozilla