/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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 : uint8_t { 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 code like this: // // Result Foo(Reader& r) { /*...*/ } // // rv = der::NestedOf(input, der::SEQEUENCE, der::SEQUENCE, Foo); // // or: // // Result Bar(Reader& r, int value) { /*...*/ } // // int value = /*...*/; // // rv = der::NestedOf(input, der::SEQUENCE, [value](Reader& r) { // return Bar(r, value); // }); // // In these examples the function 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 { enum class IntegralValueRestriction { NoRestriction, MustBePositive, MustBe0To127, }; Result IntegralBytes(Reader& input, uint8_t tag, IntegralValueRestriction valueRestriction, /*out*/ Input& value, /*optional out*/ Input::size_type* significantBytes = nullptr); // 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); } // 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); } // Parse a DER integer value into value. Empty values, negative values, and // zero are rejected. If significantBytes is not null, then it will be set to // the number of significant bytes in the value (the length of the value, less // the length of any leading padding), which is useful for key size checks. inline Result PositiveInteger(Reader& input, /*out*/ Input& value, /*optional out*/ Input::size_type* significantBytes = nullptr) { return internal::IntegralBytes( input, INTEGER, internal::IntegralValueRestriction::MustBePositive, value, significantBytes); } // 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." return internal::IntegralBytes( input, INTEGER, internal::IntegralValueRestriction::NoRestriction, value); } // 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 // "[0] EXPLICIT Version DEFAULT v1". Although an explicit encoding of v1 is // illegal, we support it because some real-world OCSP responses explicitly // encode it. Result OptionalVersion(Reader& input, /*out*/ Version& version); template inline Result OptionalExtensions(Reader& input, uint8_t tag, ExtensionHandler extensionHandler) { if (!input.Peek(tag)) { return Success; } return Nested(input, tag, [extensionHandler](Reader& tagged) { // 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). return NestedOf(tagged, SEQUENCE, SEQUENCE, EmptyAllowed::Yes, [extensionHandler](Reader& extension) -> Result { // Extension ::= SEQUENCE { // extnID OBJECT IDENTIFIER, // critical BOOLEAN DEFAULT FALSE, // extnValue OCTET STRING // } Reader extnID; Result 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; } 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); enum class PublicKeyAlgorithm { RSA_PKCS1, ECDSA, }; Result SignatureAlgorithmIdentifierValue( Reader& input, /*out*/ PublicKeyAlgorithm& publicKeyAlgorithm, /*out*/ DigestAlgorithm& digestAlgorithm); struct SignedDataWithSignature final { public: Input data; Input algorithm; Input signature; void operator=(const SignedDataWithSignature&) = delete; }; // 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. // // Note that signatureAlgorithm is NOT parsed or validated. // // 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