Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  pkixtestutil.cpp   Sprache: C

 
/* -*- 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.
 */


#include "mozpkix/test/pkixtestutil.h"

#include <cerrno>
#include <cstdio>
#include <limits>
#include <new>
#include <sstream>
#include <cstdlib>

#include "mozpkix/pkixder.h"
#include "mozpkix/pkixutil.h"

using namespace std;

namespace mozilla { namespace pkix { namespace test {

namespace {

struct ScopedMaybeDeleteFile {
  void operator()(FILE* f) {
    if (f) {
      (void)fclose(f);
    }
  }
};
typedef std::unique_ptr<FILE, ScopedMaybeDeleteFile> ScopedFILE;

FILE*
OpenFile(const string& dir, const string& filename, const string& mode)
{
  string path = dir + '/' + filename;

  ScopedFILE file;
#ifdef _MSC_VER
  {
    FILE* rawFile;
    errno_t error = fopen_s(&rawFile, path.c_str(), mode.c_str());
    if (error) {
      // TODO: map error to NSPR error code
      rawFile = nullptr;
    }
    file.reset(rawFile);
  }
#else
  file.reset(fopen(path.c_str(), mode.c_str()));
#endif
  return file.release();
}

// namespace

bool
InputEqualsByteString(Input input, const ByteString& bs)
{
  Input bsInput;
  if (bsInput.Init(bs.data(), bs.length()) != Success) {
    // Init can only fail if it is given a bad pointer or if the input is too
    // long, which won't ever happen. Plus, if it does, it is ok to call abort
    // since this is only test code.
    abort();
  }
  return InputsAreEqual(input, bsInput);
}

ByteString
InputToByteString(Input input)
{
  ByteString result;
  Reader reader(input);
  for (;;) {
    uint8_t b;
    if (reader.Read(b) != Success) {
      return result;
    }
    result.push_back(b);
  }
}

Result
TamperOnce(/*in/out*/ ByteString& item, const ByteString& from,
           const ByteString& to)
{
  if (from.length() < 8) {
    return Result::FATAL_ERROR_INVALID_ARGS;
  }
  if (from.length() != to.length()) {
    return Result::FATAL_ERROR_INVALID_ARGS;
  }
  size_t pos = item.find(from);
  if (pos == string::npos) {
    return Result::FATAL_ERROR_INVALID_ARGS; // No matches.
  }
  if (item.find(from, pos + from.length()) != string::npos) {
    return Result::FATAL_ERROR_INVALID_ARGS; // More than once match.
  }
  item.replace(pos, from.length(), to);
  return Success;
}

// Given a tag and a value, generates a DER-encoded tag-length-value item.
ByteString
TLV(uint8_t tag, size_t length, const ByteString& value)
{
  ByteString result;
  result.push_back(tag);

  if (value.length() < 128) {
    result.push_back(static_cast<uint8_t>(length));
  } else if (value.length() < 256) {
    result.push_back(0x81u);
    result.push_back(static_cast<uint8_t>(length));
  } else if (value.length() < 65536) {
    result.push_back(0x82u);
    result.push_back(static_cast<uint8_t>(length / 256));
    result.push_back(static_cast<uint8_t>(length % 256));
  } else {
    // It is MUCH more convenient for TLV to be infallible than for it to have
    // "proper" error handling.
    abort();
  }
  result.append(value);
  return result;
}

OCSPResponseExtension::OCSPResponseExtension()
  : id()
  , critical(false)
  , value()
  , next(nullptr)
{
}

OCSPResponseContext::OCSPResponseContext(const CertID& aCertID, time_t time)
  : certID(aCertID)
  , certIDHashAlgorithm(DigestAlgorithm::sha1)
  , certIDHashAlgorithmEncoded(ByteString())
  , responseStatus(successful)
  , skipResponseBytes(false)
  , producedAt(time)
  , singleExtensions(nullptr)
  , responseExtensions(nullptr)
  , trailingResponseData(nullptr)
  , includeEmptyExtensions(false)
  , signatureAlgorithm(sha256WithRSAEncryption())
  , badSignature(false)
  , certs(nullptr)

  , certStatus(good)
  , revocationTime(0)
  , thisUpdate(time)
  , nextUpdate(time + static_cast<time_t>(Time::ONE_DAY_IN_SECONDS))
  , includeNextUpdate(true)
{
}

static ByteString ResponseBytes(OCSPResponseContext& context);
static ByteString BasicOCSPResponse(OCSPResponseContext& context);
static ByteString ResponseData(OCSPResponseContext& context);
static ByteString ResponderID(OCSPResponseContext& context);
static ByteString KeyHash(const ByteString& subjectPublicKeyInfo);
static ByteString SingleResponse(OCSPResponseContext& context);
static ByteString CertID(OCSPResponseContext& context);
static ByteString CertStatus(OCSPResponseContext& context);

static ByteString
HASH(const ByteString& toHash, DigestAlgorithm digestAlgorithm)
{
  uint8_t digestBuf[MAX_DIGEST_SIZE_IN_BYTES];
  Input input;
  if (input.Init(toHash.data(), toHash.length()) != Success) {
    abort();
  }
  size_t digestLen = DigestAlgorithmToSizeInBytes(digestAlgorithm);
  assert(digestLen <= sizeof(digestBuf));
  Result rv = TestDigestBuf(input, digestAlgorithm, digestBuf, digestLen);
  if (rv != Success) {
    abort();
  }
  return ByteString(digestBuf, digestLen);
}

static ByteString
HashedOctetString(const ByteString& bytes, DigestAlgorithm digestAlgorithm)
{
  ByteString digest(HASH(bytes, digestAlgorithm));
  if (ENCODING_FAILED(digest)) {
    return ByteString();
  }
  return TLV(der::OCTET_STRING, digest);
}

static ByteString
BitString(const ByteString& rawBytes, bool corrupt)
{
  ByteString prefixed;
  // We have to add a byte at the beginning indicating no unused bits.
  // TODO: add ability to have bit strings of bit length not divisible by 8,
  // resulting in unused bits in the bitstring encoding
  prefixed.push_back(0);
  prefixed.append(rawBytes);
  if (corrupt) {
    assert(prefixed.length() > 8);
    prefixed[8]++;
  }
  return TLV(der::BIT_STRING, prefixed);
}

ByteString
Boolean(bool value)
{
  ByteString encodedValue;
  encodedValue.push_back(value ? 0xffu : 0x00u);
  return TLV(der::BOOLEAN, encodedValue);
}

ByteString
Integer(long value)
{
  if (value < 0 || value > 127) {
    // TODO: add encoding of larger values
    // It is MUCH more convenient for Integer to be infallible than for it to
    // have "proper" error handling.
    abort();
  }

  ByteString encodedValue;
  encodedValue.push_back(static_cast<uint8_t>(value));
  return TLV(der::INTEGER, encodedValue);
}

enum TimeEncoding { UTCTime = 0, GeneralizedTime = 1 };

// Windows doesn't provide gmtime_r, but it provides something very similar.
#if defined(_WINDOWS) && (!defined(_POSIX_C_SOURCE) || !defined(_POSIX_THREAD_SAFE_FUNCTIONS))
static tm*
gmtime_r(const time_t* t, /*out*/ tm* exploded)
{
  if (gmtime_s(exploded, t) != 0) {
    return nullptr;
  }
  return exploded;
}
#endif

// http://tools.ietf.org/html/rfc5280#section-4.1.2.5
// UTCTime:           YYMMDDHHMMSSZ (years 1950-2049 only)
// GeneralizedTime: YYYYMMDDHHMMSSZ
//
// This assumes that time/time_t are POSIX-compliant in that time() returns
// the number of seconds since the Unix epoch.
static ByteString
TimeToEncodedTime(time_t time, TimeEncoding encoding)
{
  assert(encoding == UTCTime || encoding == GeneralizedTime);

  tm exploded;
  if (!gmtime_r(&time, &exploded)) {
    return ByteString();
  }

  if (exploded.tm_sec >= 60) {
    // round down for leap seconds
    exploded.tm_sec = 59;
  }

  // exploded.tm_year is the year offset by 1900.
  int year = exploded.tm_year + 1900;

  if (encoding == UTCTime && (year < 1950 || year >= 2050)) {
    return ByteString();
  }

  ByteString value;

  if (encoding == GeneralizedTime) {
    value.push_back(static_cast<uint8_t>('0' + (year / 1000)));
    value.push_back(static_cast<uint8_t>('0' + ((year % 1000) / 100)));
  }

  value.push_back(static_cast<uint8_t>('0' + ((year % 100) / 10)));
  value.push_back(static_cast<uint8_t>('0' + (year % 10)));
  value.push_back(static_cast<uint8_t>('0' + ((exploded.tm_mon + 1) / 10)));
  value.push_back(static_cast<uint8_t>('0' + ((exploded.tm_mon + 1) % 10)));
  value.push_back(static_cast<uint8_t>('0' + (exploded.tm_mday / 10)));
  value.push_back(static_cast<uint8_t>('0' + (exploded.tm_mday % 10)));
  value.push_back(static_cast<uint8_t>('0' + (exploded.tm_hour / 10)));
  value.push_back(static_cast<uint8_t>('0' + (exploded.tm_hour % 10)));
  value.push_back(static_cast<uint8_t>('0' + (exploded.tm_min / 10)));
  value.push_back(static_cast<uint8_t>('0' + (exploded.tm_min % 10)));
  value.push_back(static_cast<uint8_t>('0' + (exploded.tm_sec / 10)));
  value.push_back(static_cast<uint8_t>('0' + (exploded.tm_sec % 10)));
  value.push_back('Z');

  return TLV(encoding == GeneralizedTime ? der::GENERALIZED_TIME : der::UTCTime,
             value);
}

static ByteString
TimeToGeneralizedTime(time_t time)
{
  return TimeToEncodedTime(time, GeneralizedTime);
}

// http://tools.ietf.org/html/rfc5280#section-4.1.2.5: "CAs conforming to this
// profile MUST always encode certificate validity dates through the year 2049
// as UTCTime; certificate validity dates in 2050 or later MUST be encoded as
// GeneralizedTime." (This is a special case of the rule that we must always
// use the shortest possible encoding.)
static ByteString
TimeToTimeChoice(time_t time)
{
  tm exploded;
  if (!gmtime_r(&time, &exploded)) {
    return ByteString();
  }
  TimeEncoding encoding = (exploded.tm_year + 1900 >= 1950 &&
                           exploded.tm_year + 1900 < 2050)
                        ? UTCTime
                        : GeneralizedTime;

  return TimeToEncodedTime(time, encoding);
}

Time
YMDHMS(uint16_t year, uint16_t month, uint16_t day,
       uint16_t hour, uint16_t minutes, uint16_t seconds)
{
  assert(year <= 9999);
  assert(month >= 1);
  assert(month <= 12);
  assert(day >= 1);
  assert(hour < 24);
  assert(minutes < 60);
  assert(seconds < 60);

  uint64_t days = DaysBeforeYear(year);

  {
    static const int16_t DAYS_IN_MONTH[] = {
      31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
    };

    int16_t i = 1;
    for (;;) {
      int16_t daysInMonth = DAYS_IN_MONTH[i - 1];
      if (i == 2 &&
          ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)))) {
        // Add leap day
        ++daysInMonth;
      }
      if (i == month) {
        assert(day <= daysInMonth);
        break;
      }
      days += daysInMonth;
      ++i;
    }
  }

  days += (day - 1);

  uint64_t totalSeconds = days * Time::ONE_DAY_IN_SECONDS;
  totalSeconds += hour * 60 * 60;
  totalSeconds += minutes * 60;
  totalSeconds += seconds;
  return TimeFromElapsedSecondsAD(totalSeconds);
}

static ByteString
SignedData(const ByteString& tbsData,
           const TestKeyPair& keyPair,
           const TestSignatureAlgorithm& signatureAlgorithm,
           bool corrupt, /*optional*/ const ByteString* certs)
{
  ByteString signature;
  if (keyPair.SignData(tbsData, signatureAlgorithm, signature) != Success) {
    return ByteString();
  }

  // TODO: add ability to have signatures of bit length not divisible by 8,
  // resulting in unused bits in the bitstring encoding
  ByteString signatureNested(BitString(signature, corrupt));
  if (ENCODING_FAILED(signatureNested)) {
    return ByteString();
  }

  ByteString certsNested;
  if (certs) {
    ByteString certsSequenceValue;
    while (!(*certs).empty()) {
      certsSequenceValue.append(*certs);
      ++certs;
    }
    ByteString certsSequence(TLV(der::SEQUENCE, certsSequenceValue));
    certsNested = TLV(der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0,
                      certsSequence);
  }

  ByteString value;
  value.append(tbsData);
  value.append(signatureAlgorithm.algorithmIdentifier);
  value.append(signatureNested);
  value.append(certsNested);
  return TLV(der::SEQUENCE, value);
}

// Extension  ::=  SEQUENCE  {
//      extnID      OBJECT IDENTIFIER,
//      critical    BOOLEAN DEFAULT FALSE,
//      extnValue   OCTET STRING
//                  -- contains the DER encoding of an ASN.1 value
//                  -- corresponding to the extension type identified
//                  -- by extnID
//      }
static ByteString
Extension(Input extnID, Critical critical, const ByteString& extnValueBytes)
{
  ByteString encoded;

  encoded.append(ByteString(extnID.UnsafeGetData(), extnID.GetLength()));

  if (critical == Critical::Yes) {
    encoded.append(Boolean(true));
  }

  ByteString extnValueSequence(TLV(der::SEQUENCE, extnValueBytes));
  ByteString extnValue(TLV(der::OCTET_STRING, extnValueSequence));
  encoded.append(extnValue);
  return TLV(der::SEQUENCE, encoded);
}

static ByteString
EmptyExtension(Input extnID, Critical critical)
{
  ByteString encoded(extnID.UnsafeGetData(), extnID.GetLength());

  if (critical == Critical::Yes) {
    encoded.append(Boolean(true));
  }

  ByteString extnValue(TLV(der::OCTET_STRING, ByteString()));
  encoded.append(extnValue);
  return TLV(der::SEQUENCE, encoded);
}

std::string
GetEnv(const char* name)
{
  std::string result;

#ifndef _MSC_VER
  // XXX: Not thread safe.
  const char* value = getenv(name);
  if (value) {
    result = value;
  }
#else
  char* value = nullptr;
  size_t valueLength = 0;
  if (_dupenv_s(&value, &valueLength, name) != 0) {
    abort();
  }
  if (value) {
    result = value;
    free(value);
  }
#endif
  return result;
}

void
MaybeLogOutput(const ByteString& result, const char* suffix)
{
  assert(suffix);

  // This allows us to more easily debug the generated output, by creating a
  // file in the directory given by MOZILLA_PKIX_TEST_LOG_DIR for each
  // NOT THREAD-SAFE!!!
  std::string logPath(GetEnv("MOZILLA_PKIX_TEST_LOG_DIR"));
  if (!logPath.empty()) {
    static int counter = 0;

    std::ostringstream counterStream;
    counterStream << counter;
    if (!counterStream) {
      assert(false);
      return;
    }
    string filename = counterStream.str() + '-' + suffix + ".der";

    ++counter;
    ScopedFILE file(OpenFile(logPath, filename, "wb"));
    if (file) {
      (void) fwrite(result.data(), result.length(), 1, file.get());
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
// Certificates

static ByteString TBSCertificate(long version, const ByteString& serialNumber,
                                 const ByteString& signature,
                                 const ByteString& issuer,
                                 time_t notBefore, time_t notAfter,
                                 const ByteString& subject,
                                 const ByteString& subjectPublicKeyInfo,
                                 /*optional*/ const ByteString* extensions);

// Certificate  ::=  SEQUENCE  {
//         tbsCertificate       TBSCertificate,
//         signatureAlgorithm   AlgorithmIdentifier,
//         signatureValue       BIT STRING  }
ByteString
CreateEncodedCertificate(long version,
                         const TestSignatureAlgorithm& signature,
                         const ByteString& serialNumber,
                         const ByteString& issuerNameDER,
                         time_t notBefore, time_t notAfter,
                         const ByteString& subjectNameDER,
                         const TestKeyPair& subjectKeyPair,
                         /*optional*/ const ByteString* extensions,
                         const TestKeyPair& issuerKeyPair,
                         const TestSignatureAlgorithm& signatureAlgorithm)
{
  ByteString tbsCertificate(TBSCertificate(version, serialNumber,
                                           signature.algorithmIdentifier,
                                           issuerNameDER, notBefore,
                                           notAfter, subjectNameDER,
                                           subjectKeyPair.subjectPublicKeyInfo,
                                           extensions));
  if (ENCODING_FAILED(tbsCertificate)) {
    return ByteString();
  }

  ByteString result(SignedData(tbsCertificate, issuerKeyPair,
                               signatureAlgorithm, false, nullptr));
  if (ENCODING_FAILED(result)) {
    return ByteString();
  }

  MaybeLogOutput(result, "cert");

  return result;
}

// TBSCertificate  ::=  SEQUENCE  {
//      version         [0]  Version DEFAULT v1,
//      serialNumber         CertificateSerialNumber,
//      signature            AlgorithmIdentifier,
//      issuer               Name,
//      validity             Validity,
//      subject              Name,
//      subjectPublicKeyInfo SubjectPublicKeyInfo,
//      issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
//                           -- If present, version MUST be v2 or v3
//      subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
//                           -- If present, version MUST be v2 or v3
//      extensions      [3]  Extensions OPTIONAL
//                           -- If present, version MUST be v3 --  }
static ByteString
TBSCertificate(long versionValue,
               const ByteString& serialNumber, const ByteString& signature,
               const ByteString& issuer, time_t notBeforeTime,
               time_t notAfterTime, const ByteString& subject,
               const ByteString& subjectPublicKeyInfo,
               /*optional*/ const ByteString* extensions)
{
  ByteString value;

  if (versionValue != static_cast<long>(der::Version::v1)) {
    ByteString versionInteger(Integer(versionValue));
    ByteString version(TLV(der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0,
                           versionInteger));
    value.append(version);
  }

  value.append(serialNumber);
  value.append(signature);
  value.append(issuer);

  // Validity ::= SEQUENCE {
  //       notBefore      Time,
  //       notAfter       Time }
  ByteString validity;
  {
    ByteString notBefore(TimeToTimeChoice(notBeforeTime));
    if (ENCODING_FAILED(notBefore)) {
      return ByteString();
    }
    ByteString notAfter(TimeToTimeChoice(notAfterTime));
    if (ENCODING_FAILED(notAfter)) {
      return ByteString();
    }
    ByteString validityValue;
    validityValue.append(notBefore);
    validityValue.append(notAfter);
    validity = TLV(der::SEQUENCE, validityValue);
    if (ENCODING_FAILED(validity)) {
      return ByteString();
    }
  }
  value.append(validity);

  value.append(subject);

  value.append(subjectPublicKeyInfo);

  if (extensions) {
    ByteString extensionsValue;
    while (!(*extensions).empty()) {
      extensionsValue.append(*extensions);
      ++extensions;
    }
    ByteString extensionsSequence(TLV(der::SEQUENCE, extensionsValue));
    if (ENCODING_FAILED(extensionsSequence)) {
      return ByteString();
    }
    ByteString extensionsWrapped(
      TLV(der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 3, extensionsSequence));
    if (ENCODING_FAILED(extensionsWrapped)) {
      return ByteString();
    }
    value.append(extensionsWrapped);
  }

  return TLV(der::SEQUENCE, value);
}

// AttributeTypeAndValue ::= SEQUENCE {
//   type     AttributeType,
//   value    AttributeValue }
//
// AttributeType ::= OBJECT IDENTIFIER
//
// AttributeValue ::= ANY -- DEFINED BY AttributeType
//
// DirectoryString ::= CHOICE {
//       teletexString           TeletexString (SIZE (1..MAX)),
//       printableString         PrintableString (SIZE (1..MAX)),
//       universalString         UniversalString (SIZE (1..MAX)),
//       utf8String              UTF8String (SIZE (1..MAX)),
//       bmpString               BMPString (SIZE (1..MAX)) }
template <size_t N>
static ByteString
AVA(const uint8_t (&type)[N], uint8_t directoryStringType,
    const ByteString& value)
{
  ByteString wrappedValue(TLV(directoryStringType, value));
  ByteString ava;
  ava.append(type, N);
  ava.append(wrappedValue);
  return TLV(der::SEQUENCE, ava);
}

ByteString
CN(const ByteString& value, uint8_t encodingTag)
{
  // id-at OBJECT IDENTIFIER ::= { joint-iso-ccitt(2) ds(5) 4 }
  // id-at-commonName        AttributeType ::= { id-at 3 }
  // python DottedOIDToCode.py --tlv id-at-commonName 2.5.4.3
  static const uint8_t tlv_id_at_commonName[] = {
    0x06, 0x03, 0x55, 0x04, 0x03
  };
  return AVA(tlv_id_at_commonName, encodingTag, value);
}

ByteString
OU(const ByteString& value, uint8_t encodingTag)
{
  // id-at OBJECT IDENTIFIER ::= { joint-iso-ccitt(2) ds(5) 4 }
  // id-at-organizationalUnitName AttributeType ::= { id-at 11 }
  // python DottedOIDToCode.py --tlv id-at-organizationalUnitName 2.5.4.11
  static const uint8_t tlv_id_at_organizationalUnitName[] = {
    0x06, 0x03, 0x55, 0x04, 0x0b
  };

  return AVA(tlv_id_at_organizationalUnitName, encodingTag, value);
}

ByteString
emailAddress(const ByteString& value)
{
  // id-emailAddress AttributeType ::= { pkcs-9 1 }
  // python DottedOIDToCode.py --tlv id-emailAddress 1.2.840.113549.1.9.1
  static const uint8_t tlv_id_emailAddress[] = {
    0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01
  };

  return AVA(tlv_id_emailAddress, der::IA5String, value);
}

// RelativeDistinguishedName ::=
//   SET SIZE (1..MAX) OF AttributeTypeAndValue
//
ByteString
RDN(const ByteString& avas)
{
  return TLV(der::SET, avas);
}

// Name ::= CHOICE { -- only one possibility for now --
//   rdnSequence  RDNSequence }
//
// RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
//
ByteString
Name(const ByteString& rdns)
{
  return TLV(der::SEQUENCE, rdns);
}

ByteString
CreateEncodedSerialNumber(long serialNumberValue)
{
  return Integer(serialNumberValue);
}

// BasicConstraints ::= SEQUENCE {
//         cA                      BOOLEAN DEFAULT FALSE,
//         pathLenConstraint       INTEGER (0..MAX) OPTIONAL }
ByteString
CreateEncodedBasicConstraints(bool isCA,
                              /*optional in*/ const long* pathLenConstraintValue,
                              Critical critical)
{
  ByteString value;

  if (isCA) {
    ByteString cA(Boolean(true));
    value.append(cA);
  }

  if (pathLenConstraintValue) {
    ByteString pathLenConstraint(Integer(*pathLenConstraintValue));
    value.append(pathLenConstraint);
  }

  // python DottedOIDToCode.py --tlv id-ce-basicConstraints 2.5.29.19
  static const uint8_t tlv_id_ce_basicConstraints[] = {
    0x06, 0x03, 0x55, 0x1d, 0x13
  };
  return Extension(Input(tlv_id_ce_basicConstraints), critical, value);
}

// ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
// KeyPurposeId ::= OBJECT IDENTIFIER
ByteString
CreateEncodedEKUExtension(Input ekuOID, Critical critical)
{
  ByteString value(ekuOID.UnsafeGetData(), ekuOID.GetLength());

  // python DottedOIDToCode.py --tlv id-ce-extKeyUsage 2.5.29.37
  static const uint8_t tlv_id_ce_extKeyUsage[] = {
    0x06, 0x03, 0x55, 0x1d, 0x25
  };

  return Extension(Input(tlv_id_ce_extKeyUsage), critical, value);
}

// python DottedOIDToCode.py --tlv id-ce-subjectAltName 2.5.29.17
static const uint8_t tlv_id_ce_subjectAltName[] = {
  0x06, 0x03, 0x55, 0x1d, 0x11
};

ByteString
CreateEncodedSubjectAltName(const ByteString& names)
{
  return Extension(Input(tlv_id_ce_subjectAltName), Critical::No, names);
}

ByteString
CreateEncodedEmptySubjectAltName()
{
  return EmptyExtension(Input(tlv_id_ce_subjectAltName), Critical::No);
}

///////////////////////////////////////////////////////////////////////////////
// OCSP responses

ByteString
CreateEncodedOCSPResponse(OCSPResponseContext& context)
{
  if (!context.skipResponseBytes) {
    if (!context.signerKeyPair) {
      return ByteString();
    }
  }

  // OCSPResponse ::= SEQUENCE {
  //    responseStatus          OCSPResponseStatus,
  //    responseBytes       [0] EXPLICIT ResponseBytes OPTIONAL }

  // OCSPResponseStatus ::= ENUMERATED {
  //    successful          (0),  -- Response has valid confirmations
  //    malformedRequest    (1),  -- Illegal confirmation request
  //    internalError       (2),  -- Internal error in issuer
  //    tryLater            (3),  -- Try again later
  //                              -- (4) is not used
  //    sigRequired         (5),  -- Must sign the request
  //    unauthorized        (6)   -- Request unauthorized
  // }
  ByteString reponseStatusValue;
  reponseStatusValue.push_back(context.responseStatus);
  ByteString responseStatus(TLV(der::ENUMERATED, reponseStatusValue));

  ByteString responseBytesNested;
  if (!context.skipResponseBytes) {
    ByteString responseBytes(ResponseBytes(context));
    if (ENCODING_FAILED(responseBytes)) {
      return ByteString();
    }

    responseBytesNested = TLV(der::CONSTRUCTED | der::CONTEXT_SPECIFIC,
                              responseBytes);
  }

  ByteString value;
  value.append(responseStatus);
  value.append(responseBytesNested);
  ByteString result(TLV(der::SEQUENCE, value));

  MaybeLogOutput(result, "ocsp");

  return result;
}

// ResponseBytes ::= SEQUENCE {
//    responseType            OBJECT IDENTIFIER,
//    response                OCTET STRING }
ByteString
ResponseBytes(OCSPResponseContext& context)
{
  // Includes tag and length
  static const uint8_t id_pkix_ocsp_basic_encoded[] = {
    0x06, 0x09, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01
  };
  ByteString response(BasicOCSPResponse(context));
  if (ENCODING_FAILED(response)) {
    return ByteString();
  }
  ByteString responseNested = TLV(der::OCTET_STRING, response);

  ByteString value;
  value.append(id_pkix_ocsp_basic_encoded,
               sizeof(id_pkix_ocsp_basic_encoded));
  value.append(responseNested);
  return TLV(der::SEQUENCE, value);
}

// BasicOCSPResponse ::= SEQUENCE {
//   tbsResponseData          ResponseData,
//   signatureAlgorithm       AlgorithmIdentifier,
//   signature                BIT STRING,
//   certs                [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
ByteString
BasicOCSPResponse(OCSPResponseContext& context)
{
  ByteString tbsResponseData(ResponseData(context));
  if (ENCODING_FAILED(tbsResponseData)) {
    return ByteString();
  }

  return SignedData(tbsResponseData, *context.signerKeyPair,
                    context.signatureAlgorithm, context.badSignature,
                    context.certs);
}

// Extension ::= SEQUENCE {
//   id               OBJECT IDENTIFIER,
//   critical         BOOLEAN DEFAULT FALSE
//   value            OCTET STRING
// }
static ByteString
OCSPExtension(OCSPResponseExtension& extension)
{
  ByteString encoded;
  encoded.append(extension.id);
  if (extension.critical) {
    encoded.append(Boolean(true));
  }
  ByteString value(TLV(der::OCTET_STRING, extension.value));
  encoded.append(value);
  return TLV(der::SEQUENCE, encoded);
}

// Extensions ::= [1] {
//   SEQUENCE OF Extension
// }
static ByteString
OCSPExtensions(OCSPResponseExtension* extensions)
{
  ByteString value;
  for (OCSPResponseExtension* extension = extensions;
       extension; extension = extension->next) {
    ByteString extensionEncoded(OCSPExtension(*extension));
    if (ENCODING_FAILED(extensionEncoded)) {
      return ByteString();
    }
    value.append(extensionEncoded);
  }
  ByteString sequence(TLV(der::SEQUENCE, value));
  return TLV(der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 1, sequence);
}

// ResponseData ::= SEQUENCE {
//    version             [0] EXPLICIT Version DEFAULT v1,
//    responderID             ResponderID,
//    producedAt              GeneralizedTime,
//    responses               SEQUENCE OF SingleResponse,
//    responseExtensions  [1] EXPLICIT Extensions OPTIONAL }
ByteString
ResponseData(OCSPResponseContext& context)
{
  ByteString responderID(ResponderID(context));
  if (ENCODING_FAILED(responderID)) {
    return ByteString();
  }
  ByteString producedAtEncoded(TimeToGeneralizedTime(context.producedAt));
  if (ENCODING_FAILED(producedAtEncoded)) {
    return ByteString();
  }
  ByteString response(SingleResponse(context));
  if (ENCODING_FAILED(response)) {
    return ByteString();
  }
  ByteString responses(TLV(der::SEQUENCE, response));
  ByteString responseExtensions;
  if (context.responseExtensions || context.includeEmptyExtensions) {
    responseExtensions = OCSPExtensions(context.responseExtensions);
  }

  ByteString value;
  value.append(responderID);
  value.append(producedAtEncoded);
  value.append(responses);
  value.append(responseExtensions);
  if (context.trailingResponseData) {
    value.append(*(context.trailingResponseData));
  }
  return TLV(der::SEQUENCE, value);
}

// ResponderID ::= CHOICE {
//    byName              [1] Name,
//    byKey               [2] KeyHash }
// }
ByteString
ResponderID(OCSPResponseContext& context)
{
  ByteString contents;
  uint8_t responderIDType;
  if (!context.signerNameDER.empty()) {
    contents = context.signerNameDER;
    responderIDType = 1; // byName
  } else {
    contents = KeyHash(context.signerKeyPair->subjectPublicKey);
    if (ENCODING_FAILED(contents)) {
      return ByteString();
    }
    responderIDType = 2; // byKey
  }

  // XXX: MSVC 2015 wrongly warns about signed/unsigned conversion without the
  // static_cast.
  uint8_t tag = static_cast<uint8_t>(der::CONSTRUCTED | der::CONTEXT_SPECIFIC |
                                     responderIDType);
  return TLV(tag, contents);
}

// KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
//                          -- (i.e., the SHA-1 hash of the value of the
//                          -- BIT STRING subjectPublicKey [excluding
//                          -- the tag, length, and number of unused
//                          -- bits] in the responder's certificate)
ByteString
KeyHash(const ByteString& subjectPublicKey)
{
  return HashedOctetString(subjectPublicKey, DigestAlgorithm::sha1);
}

// SingleResponse ::= SEQUENCE {
//    certID                  CertID,
//    certStatus              CertStatus,
//    thisUpdate              GeneralizedTime,
//    nextUpdate          [0] EXPLICIT GeneralizedTime OPTIONAL,
//    singleExtensions    [1] EXPLICIT Extensions OPTIONAL }
ByteString
SingleResponse(OCSPResponseContext& context)
{
  ByteString certID(CertID(context));
  if (ENCODING_FAILED(certID)) {
    return ByteString();
  }
  ByteString certStatus(CertStatus(context));
  if (ENCODING_FAILED(certStatus)) {
    return ByteString();
  }
  ByteString thisUpdateEncoded(TimeToGeneralizedTime(context.thisUpdate));
  if (ENCODING_FAILED(thisUpdateEncoded)) {
    return ByteString();
  }
  ByteString nextUpdateEncodedNested;
  if (context.includeNextUpdate) {
    ByteString nextUpdateEncoded(TimeToGeneralizedTime(context.nextUpdate));
    if (ENCODING_FAILED(nextUpdateEncoded)) {
      return ByteString();
    }
    nextUpdateEncodedNested = TLV(der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 0,
                                  nextUpdateEncoded);
  }
  ByteString singleExtensions;
  if (context.singleExtensions || context.includeEmptyExtensions) {
    singleExtensions = OCSPExtensions(context.singleExtensions);
  }

  ByteString value;
  value.append(certID);
  value.append(certStatus);
  value.append(thisUpdateEncoded);
  value.append(nextUpdateEncodedNested);
  value.append(singleExtensions);
  return TLV(der::SEQUENCE, value);
}

// CertID          ::=     SEQUENCE {
//        hashAlgorithm       AlgorithmIdentifier,
//        issuerNameHash      OCTET STRING, -- Hash of issuer's DN
//        issuerKeyHash       OCTET STRING, -- Hash of issuer's public key
//        serialNumber        CertificateSerialNumber }
ByteString
CertID(OCSPResponseContext& context)
{
  ByteString issuerName(context.certID.issuer.UnsafeGetData(),
                        context.certID.issuer.GetLength());
  ByteString issuerNameHash(HashedOctetString(issuerName, context.certIDHashAlgorithm));
  if (ENCODING_FAILED(issuerNameHash)) {
    return ByteString();
  }

  ByteString issuerKeyHash;
  {
    // context.certID.issuerSubjectPublicKeyInfo is the entire
    // SubjectPublicKeyInfo structure, but we need just the subjectPublicKey
    // part.
    Reader input(context.certID.issuerSubjectPublicKeyInfo);
    Reader contents;
    if (der::ExpectTagAndGetValue(input, der::SEQUENCE, contents) != Success) {
      return ByteString();
    }
    // Skip AlgorithmIdentifier
    if (der::ExpectTagAndSkipValue(contents, der::SEQUENCE) != Success) {
      return ByteString();
    }
    Input subjectPublicKey;
    if (der::BitStringWithNoUnusedBits(contents, subjectPublicKey)
          != Success) {
      return ByteString();
    }
    issuerKeyHash = HashedOctetString(ByteString(subjectPublicKey.UnsafeGetData(),
        subjectPublicKey.GetLength()), context.certIDHashAlgorithm);
    if (ENCODING_FAILED(issuerKeyHash)) {
      return ByteString();
    }
  }

  ByteString serialNumberValue(context.certID.serialNumber.UnsafeGetData(),
                               context.certID.serialNumber.GetLength());
  ByteString serialNumber(TLV(der::INTEGER, serialNumberValue));

  // python DottedOIDToCode.py --alg id-sha1 1.3.14.3.2.26
  static const uint8_t alg_id_sha1[] = {
    0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a
  };
  // python DottedOIDToCode.py --alg id-sha256 2.16.840.1.101.3.4.2.1
  static const uint8_t alg_id_sha256[] = {
    0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01
  };
  // python DottedOIDToCode.py --alg id-sha384 2.16.840.1.101.3.4.2.2
  static const uint8_t alg_id_sha384[] = {
    0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02
  };
  // python DottedOIDToCode.py --alg id-sha512 2.16.840.1.101.3.4.2.3
  static const uint8_t alg_id_sha512[] = {
    0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03
  };

  ByteString value;
  if (!context.certIDHashAlgorithmEncoded.empty()) {
    value.append(context.certIDHashAlgorithmEncoded);
  } else {
    switch (context.certIDHashAlgorithm) {
      case DigestAlgorithm::sha1:
        value.append(alg_id_sha1, sizeof(alg_id_sha1));
        break;
      case DigestAlgorithm::sha256:
        value.append(alg_id_sha256, sizeof(alg_id_sha256));
        break;
      case DigestAlgorithm::sha384:
        value.append(alg_id_sha384, sizeof(alg_id_sha384));
        break;
      case DigestAlgorithm::sha512:
        value.append(alg_id_sha512, sizeof(alg_id_sha512));
        break;
      MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
    }
  }
  value.append(issuerNameHash);
  value.append(issuerKeyHash);
  value.append(serialNumber);
  return TLV(der::SEQUENCE, value);
}

// CertStatus ::= CHOICE {
//    good                [0] IMPLICIT NULL,
//    revoked             [1] IMPLICIT RevokedInfo,
//    unknown             [2] IMPLICIT UnknownInfo }
//
// RevokedInfo ::= SEQUENCE {
//    revocationTime              GeneralizedTime,
//    revocationReason    [0]     EXPLICIT CRLReason OPTIONAL }
//
// UnknownInfo ::= NULL
//
ByteString
CertStatus(OCSPResponseContext& context)
{
  switch (context.certStatus) {
    // Both good and unknown are ultimately represented as NULL - the only
    // difference is in the tag that identifies them.
    case 0:
    case 2:
    {
      // XXX: MSVC 2015 wrongly warns about signed/unsigned conversion without
      // the static cast.
      return TLV(static_cast<uint8_t>(der::CONTEXT_SPECIFIC |
                                      context.certStatus), ByteString());
    }
    case 1:
    {
      ByteString revocationTime(TimeToGeneralizedTime(context.revocationTime));
      if (ENCODING_FAILED(revocationTime)) {
        return ByteString();
      }
      // TODO(bug 980536): add support for revocationReason
      return TLV(der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1, revocationTime);
    }
    default:
      assert(false);
      // fall through
  }
  return ByteString();
}

static const ByteString NO_UNUSED_BITS(1, 0x00);

// The SubjectPublicKeyInfo syntax is specified in RFC 5280 Section 4.1.
TestKeyPair::TestKeyPair(const TestPublicKeyAlgorithm& aPublicKeyAlg,
                         const ByteString& spk)
  : publicKeyAlg(aPublicKeyAlg)
  , subjectPublicKeyInfo(TLV(der::SEQUENCE,
                             aPublicKeyAlg.algorithmIdentifier +
                             TLV(der::BIT_STRING, NO_UNUSED_BITS + spk)))
  , subjectPublicKey(spk)
{
}

} } } // namespace mozilla::pkix::test

Messung V0.5
C=85 H=98 G=91

¤ Dauer der Verarbeitung: 0.22 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge