/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This code is made available to you under your choice of the following sets * of licensing terms: */ /* 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/. */ /* Copyright 2013 Mozilla Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef mozilla_pkix__pkixder_h #define mozilla_pkix__pkixder_h // Expect* functions advance the input mark and return Success if the input // matches the given criteria; they fail with the input mark in an undefined // state if the input does not match the criteria. // // Match* functions advance the input mark and return true if the input matches // the given criteria; they return false without changing the input mark if the // input does not match the criteria. // // Skip* functions unconditionally advance the input mark and return Success if // they are able to do so; otherwise they fail with the input mark in an // undefined state. #include "pkix/Input.h" #include "pkix/pkixtypes.h" namespace mozilla { namespace pkix { namespace der { enum Class : uint8_t { UNIVERSAL = 0 << 6, // APPLICATION = 1 << 6, // unused CONTEXT_SPECIFIC = 2 << 6, // PRIVATE = 3 << 6 // unused }; enum Constructed { CONSTRUCTED = 1 << 5 }; enum Tag { BOOLEAN = UNIVERSAL | 0x01, INTEGER = UNIVERSAL | 0x02, BIT_STRING = UNIVERSAL | 0x03, OCTET_STRING = UNIVERSAL | 0x04, NULLTag = UNIVERSAL | 0x05, OIDTag = UNIVERSAL | 0x06, ENUMERATED = UNIVERSAL | 0x0a, UTF8String = UNIVERSAL | 0x0c, SEQUENCE = UNIVERSAL | CONSTRUCTED | 0x10, // 0x30 SET = UNIVERSAL | CONSTRUCTED | 0x11, // 0x31 PrintableString = UNIVERSAL | 0x13, TeletexString = UNIVERSAL | 0x14, IA5String = UNIVERSAL | 0x16, UTCTime = UNIVERSAL | 0x17, GENERALIZED_TIME = UNIVERSAL | 0x18, }; enum class EmptyAllowed { No = 0, Yes = 1 }; Result ReadTagAndGetValue(Reader& input, /*out*/ uint8_t& tag, /*out*/ Input& value); Result End(Reader& input); inline Result ExpectTagAndGetValue(Reader& input, uint8_t tag, /*out*/ Input& value) { uint8_t actualTag; Result rv = ReadTagAndGetValue(input, actualTag, value); if (rv != Success) { return rv; } if (tag != actualTag) { return Result::ERROR_BAD_DER; } return Success; } inline Result ExpectTagAndGetValue(Reader& input, uint8_t tag, /*out*/ Reader& value) { Input valueInput; Result rv = ExpectTagAndGetValue(input, tag, valueInput); if (rv != Success) { return rv; } return value.Init(valueInput); } inline Result ExpectTagAndEmptyValue(Reader& input, uint8_t tag) { Reader value; Result rv = ExpectTagAndGetValue(input, tag, value); if (rv != Success) { return rv; } return End(value); } inline Result ExpectTagAndSkipValue(Reader& input, uint8_t tag) { Input ignoredValue; return ExpectTagAndGetValue(input, tag, ignoredValue); } // Like ExpectTagAndGetValue, except the output Input will contain the // encoded tag and length along with the value. inline Result ExpectTagAndGetTLV(Reader& input, uint8_t tag, /*out*/ Input& tlv) { Reader::Mark mark(input.GetMark()); Result rv = ExpectTagAndSkipValue(input, tag); if (rv != Success) { return rv; } return input.GetInput(mark, tlv); } inline Result End(Reader& input) { if (!input.AtEnd()) { return Result::ERROR_BAD_DER; } return Success; } template inline Result Nested(Reader& input, uint8_t tag, Decoder decoder) { Reader nested; Result rv = ExpectTagAndGetValue(input, tag, nested); if (rv != Success) { return rv; } rv = decoder(nested); if (rv != Success) { return rv; } return End(nested); } template inline Result Nested(Reader& input, uint8_t outerTag, uint8_t innerTag, Decoder decoder) { Reader nestedInput; Result rv = ExpectTagAndGetValue(input, outerTag, nestedInput); if (rv != Success) { return rv; } rv = Nested(nestedInput, innerTag, decoder); if (rv != Success) { return rv; } return End(nestedInput); } // This can be used to decode constructs like this: // // ... // foos SEQUENCE OF Foo, // ... // Foo ::= SEQUENCE { // } // // using a call like this: // // rv = NestedOf(input, SEQEUENCE, SEQUENCE, bind(_1, Foo)); // // Result Foo(Reader& input) { // } // // In this example, Foo will get called once for each element of foos. // template inline Result NestedOf(Reader& input, uint8_t outerTag, uint8_t innerTag, EmptyAllowed mayBeEmpty, Decoder decoder) { Reader inner; Result rv = ExpectTagAndGetValue(input, outerTag, inner); if (rv != Success) { return rv; } if (inner.AtEnd()) { if (mayBeEmpty != EmptyAllowed::Yes) { return Result::ERROR_BAD_DER; } return Success; } do { rv = Nested(inner, innerTag, decoder); if (rv != Success) { return rv; } } while (!inner.AtEnd()); return Success; } // Often, a function will need to decode an Input or Reader that contains // DER-encoded data wrapped in a SEQUENCE (or similar) with nothing after it. // This function reduces the boilerplate necessary for stripping the outermost // SEQUENCE (or similar) and ensuring that nothing follows it. inline Result ExpectTagAndGetValueAtEnd(Reader& outer, uint8_t expectedTag, /*out*/ Reader& inner) { Result rv = der::ExpectTagAndGetValue(outer, expectedTag, inner); if (rv != Success) { return rv; } return der::End(outer); } // Similar to the above, but takes an Input instead of a Reader&. inline Result ExpectTagAndGetValueAtEnd(Input outer, uint8_t expectedTag, /*out*/ Reader& inner) { Reader outerReader(outer); return ExpectTagAndGetValueAtEnd(outerReader, expectedTag, inner); } // Universal types namespace internal { // This parser will only parse values between 0..127. If this range is // increased then callers will need to be changed. template inline Result IntegralValue(Reader& input, uint8_t tag, 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. Reader valueReader; Result rv = ExpectTagAndGetValue(input, tag, valueReader); if (rv != Success) { return rv; } uint8_t valueByte; rv = valueReader.Read(valueByte); if (rv != Success) { return rv; } if (valueByte & 0x80) { // negative return Result::ERROR_BAD_DER; } value = valueByte; return End(valueReader); } } // namespace internal Result BitStringWithNoUnusedBits(Reader& input, /*out*/ Input& value); inline Result Boolean(Reader& input, /*out*/ bool& value) { Reader valueReader; Result rv = ExpectTagAndGetValue(input, BOOLEAN, valueReader); if (rv != Success) { return rv; } uint8_t intValue; rv = valueReader.Read(intValue); if (rv != Success) { return rv; } rv = End(valueReader); if (rv != Success) { return rv; } switch (intValue) { case 0: value = false; return Success; case 0xFF: value = true; return Success; default: return Result::ERROR_BAD_DER; } } // This is for BOOLEAN DEFAULT FALSE. // The standard stipulates that "The encoding of a set value or sequence value // shall not include an encoding for any component value which is equal to its // default value." However, it appears to be common that other libraries // incorrectly include the value of a BOOLEAN even when it's equal to the // default value, so we allow invalid explicit encodings here. inline Result OptionalBoolean(Reader& input, /*out*/ bool& value) { value = false; if (input.Peek(BOOLEAN)) { Result rv = Boolean(input, value); if (rv != Success) { return rv; } } return Success; } // This parser will only parse values between 0..127. If this range is // increased then callers will need to be changed. inline Result Enumerated(Reader& input, uint8_t& value) { return internal::IntegralValue(input, ENUMERATED | 0, value); } namespace internal { // internal::TimeChoice implements the shared functionality of GeneralizedTime // and TimeChoice. tag must be either UTCTime or GENERALIZED_TIME. // // Only times from 1970-01-01-00:00:00 onward are accepted, in order to // eliminate the chance for complications in converting times to traditional // time formats that start at 1970. Result TimeChoice(Reader& input, uint8_t tag, /*out*/ Time& time); } // namespace internal // Only times from 1970-01-01-00:00:00 onward are accepted, in order to // eliminate the chance for complications in converting times to traditional // time formats that start at 1970. inline Result GeneralizedTime(Reader& input, /*out*/ Time& time) { return internal::TimeChoice(input, GENERALIZED_TIME, time); } // Only times from 1970-01-01-00:00:00 onward are accepted, in order to // eliminate the chance for complications in converting times to traditional // time formats that start at 1970. inline Result TimeChoice(Reader& input, /*out*/ Time& time) { uint8_t expectedTag = input.Peek(UTCTime) ? UTCTime : GENERALIZED_TIME; return internal::TimeChoice(input, expectedTag, time); } // This parser will only parse values between 0..127. If this range is // increased then callers will need to be changed. inline Result Integer(Reader& input, /*out*/ uint8_t& value) { return internal::IntegralValue(input, INTEGER, value); } // This parser will only parse values between 0..127. If this range is // increased then callers will need to be changed. The default value must be // -1; defaultValue is only a parameter to make it clear in the calling code // what the default value is. inline Result OptionalInteger(Reader& input, long defaultValue, /*out*/ long& value) { // If we need to support a different default value in the future, we need to // test that parsedValue != defaultValue. if (defaultValue != -1) { return Result::FATAL_ERROR_INVALID_ARGS; } if (!input.Peek(INTEGER)) { value = defaultValue; return Success; } uint8_t parsedValue; Result rv = Integer(input, parsedValue); if (rv != Success) { return rv; } value = parsedValue; return Success; } inline Result Null(Reader& input) { return ExpectTagAndEmptyValue(input, NULLTag); } template Result OID(Reader& input, const uint8_t (&expectedOid)[Len]) { Reader value; Result rv = ExpectTagAndGetValue(input, OIDTag, value); if (rv != Success) { return rv; } if (!value.MatchRest(expectedOid)) { return Result::ERROR_BAD_DER; } return Success; } // PKI-specific types inline Result CertificateSerialNumber(Reader& input, /*out*/ Input& value) { // http://tools.ietf.org/html/rfc5280#section-4.1.2.2: // // * "The serial number MUST be a positive integer assigned by the CA to // each certificate." // * "Certificate users MUST be able to handle serialNumber values up to 20 // octets. Conforming CAs MUST NOT use serialNumber values longer than 20 // octets." // * "Note: Non-conforming CAs may issue certificates with serial numbers // that are negative or zero. Certificate users SHOULD be prepared to // gracefully handle such certificates." Result rv = ExpectTagAndGetValue(input, INTEGER, value); if (rv != Success) { return rv; } if (value.GetLength() == 0) { return Result::ERROR_BAD_DER; } // Check for overly-long encodings. If the first byte is 0x00 then the high // bit on the second byte must be 1; otherwise the same *positive* value // could be encoded without the leading 0x00 byte. If the first byte is 0xFF // then the second byte must NOT have its high bit set; otherwise the same // *negative* value could be encoded without the leading 0xFF byte. if (value.GetLength() > 1) { Reader valueInput(value); uint8_t firstByte; rv = valueInput.Read(firstByte); if (rv != Success) { return rv; } uint8_t secondByte; rv = valueInput.Read(secondByte); if (rv != Success) { return rv; } if ((firstByte == 0x00 && (secondByte & 0x80) == 0) || (firstByte == 0xff && (secondByte & 0x80) != 0)) { return Result::ERROR_BAD_DER; } } return Success; } // x.509 and OCSP both use this same version numbering scheme, though OCSP // only supports v1. enum class Version { v1 = 0, v2 = 1, v3 = 2, v4 = 3 }; // X.509 Certificate and OCSP ResponseData both use this // "[0] EXPLICIT Version DEFAULT " construct, but with // different default versions. inline 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; } Reader value; Result rv = ExpectTagAndGetValue(input, TAG, value); if (rv != Success) { return rv; } uint8_t integerValue; rv = Integer(value, integerValue); if (rv != Success) { return rv; } rv = End(value); if (rv != Success) { return rv; } switch (integerValue) { case static_cast(Version::v3): version = Version::v3; break; case static_cast(Version::v2): version = Version::v2; break; // XXX(bug 1031093): We shouldn't accept an explicit encoding of v1, but we // do here for compatibility reasons. case static_cast(Version::v1): version = Version::v1; break; case static_cast(Version::v4): version = Version::v4; break; default: return Result::ERROR_BAD_DER; } return Success; } template inline Result OptionalExtensions(Reader& input, uint8_t tag, ExtensionHandler extensionHandler) { if (!input.Peek(tag)) { return Success; } Result rv; Reader extensions; { Reader tagged; rv = ExpectTagAndGetValue(input, tag, tagged); if (rv != Success) { return rv; } rv = ExpectTagAndGetValue(tagged, SEQUENCE, extensions); if (rv != Success) { return rv; } rv = End(tagged); if (rv != Success) { return rv; } } // Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension // // TODO(bug 997994): According to the specification, there should never be // an empty sequence of extensions but we've found OCSP responses that have // that (see bug 991898). while (!extensions.AtEnd()) { Reader extension; rv = ExpectTagAndGetValue(extensions, SEQUENCE, extension); if (rv != Success) { return rv; } // Extension ::= SEQUENCE { // extnID OBJECT IDENTIFIER, // critical BOOLEAN DEFAULT FALSE, // extnValue OCTET STRING // } Reader extnID; rv = ExpectTagAndGetValue(extension, OIDTag, extnID); if (rv != Success) { return rv; } bool critical; rv = OptionalBoolean(extension, critical); if (rv != Success) { return rv; } Input extnValue; rv = ExpectTagAndGetValue(extension, OCTET_STRING, extnValue); if (rv != Success) { return rv; } rv = End(extension); if (rv != Success) { return rv; } bool understood = false; rv = extensionHandler(extnID, extnValue, critical, understood); if (rv != Success) { return rv; } if (critical && !understood) { return Result::ERROR_UNKNOWN_CRITICAL_EXTENSION; } } return Success; } Result DigestAlgorithmIdentifier(Reader& input, /*out*/ DigestAlgorithm& algorithm); Result SignatureAlgorithmIdentifier(Reader& input, /*out*/ SignatureAlgorithm& algorithm); // Parses a SEQUENCE into tbs and then parses an AlgorithmIdentifier followed // by a BIT STRING into signedData. This handles the commonality between // parsing the signed/signature fields of certificates and OCSP responses. In // the case of an OCSP response, the caller needs to parse the certs // separately. // // Certificate ::= SEQUENCE { // tbsCertificate TBSCertificate, // signatureAlgorithm AlgorithmIdentifier, // signatureValue BIT STRING } // // BasicOCSPResponse ::= SEQUENCE { // tbsResponseData ResponseData, // signatureAlgorithm AlgorithmIdentifier, // signature BIT STRING, // certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } Result SignedData(Reader& input, /*out*/ Reader& tbs, /*out*/ SignedDataWithSignature& signedDataWithSignature); } } } // namespace mozilla::pkix::der #endif // mozilla_pkix__pkixder_h