/* -*- 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 : 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 <typename Decoder>
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 <typename Decoder>
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 <typename Decoder>
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 <uint8_t Len>
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 <typename ExtensionHandler>
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