123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
- #include "pkix/pkixder.h"
- #include "pkix/pkixutil.h"
- namespace mozilla { namespace pkix { namespace der {
- // Too complicated to be inline
- Result
- ReadTagAndGetValue(Reader& input, /*out*/ uint8_t& tag, /*out*/ Input& value)
- {
- Result rv;
- rv = input.Read(tag);
- if (rv != Success) {
- return rv;
- }
- if ((tag & 0x1F) == 0x1F) {
- return Result::ERROR_BAD_DER; // high tag number form not allowed
- }
- uint16_t length;
- // The short form of length is a single byte with the high order bit set
- // to zero. The long form of length is one byte with the high order bit
- // set, followed by N bytes, where N is encoded in the lowest 7 bits of
- // the first byte.
- uint8_t length1;
- rv = input.Read(length1);
- if (rv != Success) {
- return rv;
- }
- if (!(length1 & 0x80)) {
- length = length1;
- } else if (length1 == 0x81) {
- uint8_t length2;
- rv = input.Read(length2);
- if (rv != Success) {
- return rv;
- }
- if (length2 < 128) {
- // Not shortest possible encoding
- return Result::ERROR_BAD_DER;
- }
- length = length2;
- } else if (length1 == 0x82) {
- rv = input.Read(length);
- if (rv != Success) {
- return rv;
- }
- if (length < 256) {
- // Not shortest possible encoding
- return Result::ERROR_BAD_DER;
- }
- } else {
- // We don't support lengths larger than 2^16 - 1.
- return Result::ERROR_BAD_DER;
- }
- return input.Skip(length, value);
- }
- static Result
- OptionalNull(Reader& input)
- {
- if (input.Peek(NULLTag)) {
- return Null(input);
- }
- return Success;
- }
- namespace {
- Result
- AlgorithmIdentifierValue(Reader& input, /*out*/ Reader& algorithmOIDValue)
- {
- Result rv = ExpectTagAndGetValue(input, der::OIDTag, algorithmOIDValue);
- if (rv != Success) {
- return rv;
- }
- return OptionalNull(input);
- }
- } // namespace
- Result
- SignatureAlgorithmIdentifierValue(Reader& input,
- /*out*/ PublicKeyAlgorithm& publicKeyAlgorithm,
- /*out*/ DigestAlgorithm& digestAlgorithm)
- {
- // RFC 5758 Section 3.2 (ECDSA with SHA-2), and RFC 3279 Section 2.2.3
- // (ECDSA with SHA-1) say that parameters must be omitted.
- //
- // RFC 4055 Section 5 and RFC 3279 Section 2.2.1 both say that parameters for
- // RSA must be encoded as NULL; we relax that requirement by allowing the
- // NULL to be omitted, to match all the other signature algorithms we support
- // and for compatibility.
- Reader algorithmID;
- Result rv = AlgorithmIdentifierValue(input, algorithmID);
- if (rv != Success) {
- return rv;
- }
- // RFC 5758 Section 3.2 (ecdsa-with-SHA224 is intentionally excluded)
- // python DottedOIDToCode.py ecdsa-with-SHA256 1.2.840.10045.4.3.2
- static const uint8_t ecdsa_with_SHA256[] = {
- 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02
- };
- // python DottedOIDToCode.py ecdsa-with-SHA384 1.2.840.10045.4.3.3
- static const uint8_t ecdsa_with_SHA384[] = {
- 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03
- };
- // python DottedOIDToCode.py ecdsa-with-SHA512 1.2.840.10045.4.3.4
- static const uint8_t ecdsa_with_SHA512[] = {
- 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04
- };
- // RFC 4055 Section 5 (sha224WithRSAEncryption is intentionally excluded)
- // python DottedOIDToCode.py sha256WithRSAEncryption 1.2.840.113549.1.1.11
- static const uint8_t sha256WithRSAEncryption[] = {
- 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b
- };
- // python DottedOIDToCode.py sha384WithRSAEncryption 1.2.840.113549.1.1.12
- static const uint8_t sha384WithRSAEncryption[] = {
- 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c
- };
- // python DottedOIDToCode.py sha512WithRSAEncryption 1.2.840.113549.1.1.13
- static const uint8_t sha512WithRSAEncryption[] = {
- 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d
- };
- // RFC 3279 Section 2.2.1
- // python DottedOIDToCode.py sha-1WithRSAEncryption 1.2.840.113549.1.1.5
- static const uint8_t sha_1WithRSAEncryption[] = {
- 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05
- };
- // NIST Open Systems Environment (OSE) Implementor's Workshop (OIW)
- // http://www.oiw.org/agreements/stable/12s-9412.txt (no longer works).
- // http://www.imc.org/ietf-pkix/old-archive-97/msg01166.html
- // We need to support this this non-PKIX OID for compatibility.
- // python DottedOIDToCode.py sha1WithRSASignature 1.3.14.3.2.29
- static const uint8_t sha1WithRSASignature[] = {
- 0x2b, 0x0e, 0x03, 0x02, 0x1d
- };
- // RFC 3279 Section 2.2.3
- // python DottedOIDToCode.py ecdsa-with-SHA1 1.2.840.10045.4.1
- static const uint8_t ecdsa_with_SHA1[] = {
- 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01
- };
- // Matching is attempted based on a rough estimate of the commonality of the
- // algorithm, to minimize the number of MatchRest calls.
- if (algorithmID.MatchRest(sha256WithRSAEncryption)) {
- publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1;
- digestAlgorithm = DigestAlgorithm::sha256;
- } else if (algorithmID.MatchRest(ecdsa_with_SHA256)) {
- publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA;
- digestAlgorithm = DigestAlgorithm::sha256;
- } else if (algorithmID.MatchRest(sha_1WithRSAEncryption)) {
- publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1;
- digestAlgorithm = DigestAlgorithm::sha1;
- } else if (algorithmID.MatchRest(ecdsa_with_SHA1)) {
- publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA;
- digestAlgorithm = DigestAlgorithm::sha1;
- } else if (algorithmID.MatchRest(ecdsa_with_SHA384)) {
- publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA;
- digestAlgorithm = DigestAlgorithm::sha384;
- } else if (algorithmID.MatchRest(ecdsa_with_SHA512)) {
- publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA;
- digestAlgorithm = DigestAlgorithm::sha512;
- } else if (algorithmID.MatchRest(sha384WithRSAEncryption)) {
- publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1;
- digestAlgorithm = DigestAlgorithm::sha384;
- } else if (algorithmID.MatchRest(sha512WithRSAEncryption)) {
- publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1;
- digestAlgorithm = DigestAlgorithm::sha512;
- } else if (algorithmID.MatchRest(sha1WithRSASignature)) {
- // XXX(bug 1042479): recognize this old OID for compatibility.
- publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1;
- digestAlgorithm = DigestAlgorithm::sha1;
- } else {
- return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
- }
- return Success;
- }
- Result
- DigestAlgorithmIdentifier(Reader& input, /*out*/ DigestAlgorithm& algorithm)
- {
- return der::Nested(input, SEQUENCE, [&algorithm](Reader& r) -> Result {
- Reader algorithmID;
- Result rv = AlgorithmIdentifierValue(r, algorithmID);
- if (rv != Success) {
- return rv;
- }
- // RFC 4055 Section 2.1
- // python DottedOIDToCode.py id-sha1 1.3.14.3.2.26
- static const uint8_t id_sha1[] = {
- 0x2b, 0x0e, 0x03, 0x02, 0x1a
- };
- // python DottedOIDToCode.py id-sha256 2.16.840.1.101.3.4.2.1
- static const uint8_t id_sha256[] = {
- 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01
- };
- // python DottedOIDToCode.py id-sha384 2.16.840.1.101.3.4.2.2
- static const uint8_t id_sha384[] = {
- 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02
- };
- // python DottedOIDToCode.py id-sha512 2.16.840.1.101.3.4.2.3
- static const uint8_t id_sha512[] = {
- 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03
- };
- // Matching is attempted based on a rough estimate of the commonality of the
- // algorithm, to minimize the number of MatchRest calls.
- if (algorithmID.MatchRest(id_sha1)) {
- algorithm = DigestAlgorithm::sha1;
- } else if (algorithmID.MatchRest(id_sha256)) {
- algorithm = DigestAlgorithm::sha256;
- } else if (algorithmID.MatchRest(id_sha384)) {
- algorithm = DigestAlgorithm::sha384;
- } else if (algorithmID.MatchRest(id_sha512)) {
- algorithm = DigestAlgorithm::sha512;
- } else {
- return Result::ERROR_INVALID_ALGORITHM;
- }
- return Success;
- });
- }
- Result
- SignedData(Reader& input, /*out*/ Reader& tbs,
- /*out*/ SignedDataWithSignature& signedData)
- {
- Reader::Mark mark(input.GetMark());
- Result rv;
- rv = ExpectTagAndGetValue(input, SEQUENCE, tbs);
- if (rv != Success) {
- return rv;
- }
- rv = input.GetInput(mark, signedData.data);
- if (rv != Success) {
- return rv;
- }
- rv = ExpectTagAndGetValue(input, der::SEQUENCE, signedData.algorithm);
- if (rv != Success) {
- return rv;
- }
- rv = BitStringWithNoUnusedBits(input, signedData.signature);
- if (rv == Result::ERROR_BAD_DER) {
- rv = Result::ERROR_BAD_SIGNATURE;
- }
- return rv;
- }
- Result
- BitStringWithNoUnusedBits(Reader& input, /*out*/ Input& value)
- {
- Reader valueWithUnusedBits;
- Result rv = ExpectTagAndGetValue(input, BIT_STRING, valueWithUnusedBits);
- if (rv != Success) {
- return rv;
- }
- uint8_t unusedBitsAtEnd;
- if (valueWithUnusedBits.Read(unusedBitsAtEnd) != Success) {
- return Result::ERROR_BAD_DER;
- }
- // XXX: Really the constraint should be that unusedBitsAtEnd must be less
- // than 7. But, we suspect there are no real-world values in OCSP responses
- // or certificates with non-zero unused bits. It seems like NSS assumes this
- // in various places, so we enforce it too in order to simplify this code. If
- // we find compatibility issues, we'll know we're wrong and we'll have to
- // figure out how to shift the bits around.
- if (unusedBitsAtEnd != 0) {
- return Result::ERROR_BAD_DER;
- }
- return valueWithUnusedBits.SkipToEnd(value);
- }
- static inline Result
- ReadDigit(Reader& input, /*out*/ unsigned int& value)
- {
- uint8_t b;
- if (input.Read(b) != Success) {
- return Result::ERROR_INVALID_DER_TIME;
- }
- if (b < '0' || b > '9') {
- return Result::ERROR_INVALID_DER_TIME;
- }
- value = static_cast<unsigned int>(b - static_cast<uint8_t>('0'));
- return Success;
- }
- static inline Result
- ReadTwoDigits(Reader& input, unsigned int minValue, unsigned int maxValue,
- /*out*/ unsigned int& value)
- {
- unsigned int hi;
- Result rv = ReadDigit(input, hi);
- if (rv != Success) {
- return rv;
- }
- unsigned int lo;
- rv = ReadDigit(input, lo);
- if (rv != Success) {
- return rv;
- }
- value = (hi * 10) + lo;
- if (value < minValue || value > maxValue) {
- return Result::ERROR_INVALID_DER_TIME;
- }
- return Success;
- }
- namespace internal {
- // We parse GeneralizedTime and UTCTime according to RFC 5280 and we do not
- // accept all time formats allowed in the ASN.1 spec. That is,
- // GeneralizedTime must always be in the format YYYYMMDDHHMMSSZ and UTCTime
- // must always be in the format YYMMDDHHMMSSZ. Timezone formats of the form
- // +HH:MM or -HH:MM or NOT accepted.
- Result
- TimeChoice(Reader& tagged, uint8_t expectedTag, /*out*/ Time& time)
- {
- unsigned int days;
- Reader input;
- Result rv = ExpectTagAndGetValue(tagged, expectedTag, input);
- if (rv != Success) {
- return rv;
- }
- unsigned int yearHi;
- unsigned int yearLo;
- if (expectedTag == GENERALIZED_TIME) {
- rv = ReadTwoDigits(input, 0, 99, yearHi);
- if (rv != Success) {
- return rv;
- }
- rv = ReadTwoDigits(input, 0, 99, yearLo);
- if (rv != Success) {
- return rv;
- }
- } else if (expectedTag == UTCTime) {
- rv = ReadTwoDigits(input, 0, 99, yearLo);
- if (rv != Success) {
- return rv;
- }
- yearHi = yearLo >= 50u ? 19u : 20u;
- } else {
- return NotReached("invalid tag given to TimeChoice",
- Result::ERROR_INVALID_DER_TIME);
- }
- unsigned int year = (yearHi * 100u) + yearLo;
- if (year < 1970u) {
- // We don't support dates before January 1, 1970 because that is the epoch.
- return Result::ERROR_INVALID_DER_TIME;
- }
- days = DaysBeforeYear(year);
- unsigned int month;
- rv = ReadTwoDigits(input, 1u, 12u, month);
- if (rv != Success) {
- return rv;
- }
- unsigned int daysInMonth;
- static const unsigned int jan = 31u;
- const unsigned int feb = ((year % 4u == 0u) &&
- ((year % 100u != 0u) || (year % 400u == 0u)))
- ? 29u
- : 28u;
- static const unsigned int mar = 31u;
- static const unsigned int apr = 30u;
- static const unsigned int may = 31u;
- static const unsigned int jun = 30u;
- static const unsigned int jul = 31u;
- static const unsigned int aug = 31u;
- static const unsigned int sep = 30u;
- static const unsigned int oct = 31u;
- static const unsigned int nov = 30u;
- static const unsigned int dec = 31u;
- switch (month) {
- case 1: daysInMonth = jan; break;
- case 2: daysInMonth = feb; days += jan; break;
- case 3: daysInMonth = mar; days += jan + feb; break;
- case 4: daysInMonth = apr; days += jan + feb + mar; break;
- case 5: daysInMonth = may; days += jan + feb + mar + apr; break;
- case 6: daysInMonth = jun; days += jan + feb + mar + apr + may; break;
- case 7: daysInMonth = jul; days += jan + feb + mar + apr + may + jun;
- break;
- case 8: daysInMonth = aug; days += jan + feb + mar + apr + may + jun +
- jul;
- break;
- case 9: daysInMonth = sep; days += jan + feb + mar + apr + may + jun +
- jul + aug;
- break;
- case 10: daysInMonth = oct; days += jan + feb + mar + apr + may + jun +
- jul + aug + sep;
- break;
- case 11: daysInMonth = nov; days += jan + feb + mar + apr + may + jun +
- jul + aug + sep + oct;
- break;
- case 12: daysInMonth = dec; days += jan + feb + mar + apr + may + jun +
- jul + aug + sep + oct + nov;
- break;
- default:
- return NotReached("month already bounds-checked by ReadTwoDigits",
- Result::FATAL_ERROR_INVALID_STATE);
- }
- unsigned int dayOfMonth;
- rv = ReadTwoDigits(input, 1u, daysInMonth, dayOfMonth);
- if (rv != Success) {
- return rv;
- }
- days += dayOfMonth - 1;
- unsigned int hours;
- rv = ReadTwoDigits(input, 0u, 23u, hours);
- if (rv != Success) {
- return rv;
- }
- unsigned int minutes;
- rv = ReadTwoDigits(input, 0u, 59u, minutes);
- if (rv != Success) {
- return rv;
- }
- unsigned int seconds;
- rv = ReadTwoDigits(input, 0u, 59u, seconds);
- if (rv != Success) {
- return rv;
- }
- uint8_t b;
- if (input.Read(b) != Success) {
- return Result::ERROR_INVALID_DER_TIME;
- }
- if (b != 'Z') {
- return Result::ERROR_INVALID_DER_TIME;
- }
- if (End(input) != Success) {
- return Result::ERROR_INVALID_DER_TIME;
- }
- uint64_t totalSeconds = (static_cast<uint64_t>(days) * 24u * 60u * 60u) +
- (static_cast<uint64_t>(hours) * 60u * 60u) +
- (static_cast<uint64_t>(minutes) * 60u) +
- seconds;
- time = TimeFromElapsedSecondsAD(totalSeconds);
- return Success;
- }
- Result
- IntegralBytes(Reader& input, uint8_t tag,
- IntegralValueRestriction valueRestriction,
- /*out*/ Input& value,
- /*optional out*/ Input::size_type* significantBytes)
- {
- Result rv = ExpectTagAndGetValue(input, tag, value);
- if (rv != Success) {
- return rv;
- }
- Reader reader(value);
- // There must be at least one byte in the value. (Zero is encoded with a
- // single 0x00 value byte.)
- uint8_t firstByte;
- rv = reader.Read(firstByte);
- if (rv != Success) {
- if (rv == Result::ERROR_BAD_DER) {
- return Result::ERROR_INVALID_INTEGER_ENCODING;
- }
- return rv;
- }
- // If there is a byte after an initial 0x00/0xFF, then the initial byte
- // indicates a positive/negative integer value with its high bit set/unset.
- bool prefixed = !reader.AtEnd() && (firstByte == 0 || firstByte == 0xff);
- if (prefixed) {
- uint8_t nextByte;
- if (reader.Read(nextByte) != Success) {
- return NotReached("Read of one byte failed but not at end.",
- Result::FATAL_ERROR_LIBRARY_FAILURE);
- }
- if ((firstByte & 0x80) == (nextByte & 0x80)) {
- return Result::ERROR_INVALID_INTEGER_ENCODING;
- }
- }
- switch (valueRestriction) {
- case IntegralValueRestriction::MustBe0To127:
- if (value.GetLength() != 1 || (firstByte & 0x80) != 0) {
- return Result::ERROR_INVALID_INTEGER_ENCODING;
- }
- break;
- case IntegralValueRestriction::MustBePositive:
- if ((value.GetLength() == 1 && firstByte == 0) ||
- (firstByte & 0x80) != 0) {
- return Result::ERROR_INVALID_INTEGER_ENCODING;
- }
- break;
- case IntegralValueRestriction::NoRestriction:
- break;
- }
- if (significantBytes) {
- *significantBytes = value.GetLength();
- if (prefixed) {
- assert(*significantBytes > 1);
- --*significantBytes;
- }
- assert(*significantBytes > 0);
- }
- return Success;
- }
- // This parser will only parse values between 0..127. If this range is
- // increased then callers will need to be changed.
- Result
- IntegralValue(Reader& input, uint8_t tag, /*out*/ uint8_t& value)
- {
- // Conveniently, all the Integers that we actually have to be able to parse
- // are positive and very small. Consequently, this parser is *much* simpler
- // than a general Integer parser would need to be.
- Input valueBytes;
- Result rv = IntegralBytes(input, tag, IntegralValueRestriction::MustBe0To127,
- valueBytes, nullptr);
- if (rv != Success) {
- return rv;
- }
- Reader valueReader(valueBytes);
- rv = valueReader.Read(value);
- if (rv != Success) {
- return NotReached("IntegralBytes already validated the value.", rv);
- }
- rv = End(valueReader);
- assert(rv == Success); // guaranteed by IntegralBytes's range checks.
- return rv;
- }
- } // namespace internal
- Result
- OptionalVersion(Reader& input, /*out*/ Version& version)
- {
- static const uint8_t TAG = CONTEXT_SPECIFIC | CONSTRUCTED | 0;
- if (!input.Peek(TAG)) {
- version = Version::v1;
- return Success;
- }
- return Nested(input, TAG, [&version](Reader& value) -> Result {
- uint8_t integerValue;
- Result rv = Integer(value, integerValue);
- if (rv != Success) {
- return rv;
- }
- // XXX(bug 1031093): We shouldn't accept an explicit encoding of v1,
- // but we do here for compatibility reasons.
- switch (integerValue) {
- case static_cast<uint8_t>(Version::v3): version = Version::v3; break;
- case static_cast<uint8_t>(Version::v2): version = Version::v2; break;
- case static_cast<uint8_t>(Version::v1): version = Version::v1; break;
- case static_cast<uint8_t>(Version::v4): version = Version::v4; break;
- default:
- return Result::ERROR_BAD_DER;
- }
- return Success;
- });
- }
- } } } // namespace mozilla::pkix::der
|