/* -*- 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_test_pkixtestutil_h #define mozilla_pkix_test_pkixtestutil_h #include #include #include #include #include "mozpkix/pkixtypes.h" namespace mozilla { namespace pkix { namespace test { typedef std::basic_string ByteString; inline bool ENCODING_FAILED(const ByteString& bs) { return bs.empty(); } template inline ByteString BytesToByteString(const uint8_t (&bytes)[L]) { return ByteString(bytes, L); } // XXX: Ideally, we should define this instead: // // template // constexpr inline std::size_t // ArrayLength(T (&)[N]) // { // return N; // } // // However, we don't because not all supported compilers support constexpr, // and we need to calculate array lengths in static_assert sometimes. // // XXX: Evaluates its argument twice #define MOZILLA_PKIX_ARRAY_LENGTH(x) (sizeof(x) / sizeof((x)[0])) bool InputEqualsByteString(Input input, const ByteString& bs); ByteString InputToByteString(Input input); // python DottedOIDToCode.py --tlv id-kp-OCSPSigning 1.3.6.1.5.5.7.3.9 static const uint8_t tlv_id_kp_OCSPSigning[] = {0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x09}; // python DottedOIDToCode.py --tlv id-kp-serverAuth 1.3.6.1.5.5.7.3.1 static const uint8_t tlv_id_kp_serverAuth[] = {0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01}; enum class TestDigestAlgorithmID { MD2, MD5, SHA1, SHA224, SHA256, SHA384, SHA512, }; struct TestPublicKeyAlgorithm { explicit TestPublicKeyAlgorithm(const ByteString& aAlgorithmIdentifier) : algorithmIdentifier(aAlgorithmIdentifier) {} bool operator==(const TestPublicKeyAlgorithm& other) const { return algorithmIdentifier == other.algorithmIdentifier; } ByteString algorithmIdentifier; }; ByteString DSS_P(); ByteString DSS_Q(); ByteString DSS_G(); TestPublicKeyAlgorithm DSS(); TestPublicKeyAlgorithm RSA_PKCS1(); struct TestSignatureAlgorithm { TestSignatureAlgorithm(const TestPublicKeyAlgorithm& publicKeyAlg, TestDigestAlgorithmID digestAlg, const ByteString& algorithmIdentifier, bool accepted); TestPublicKeyAlgorithm publicKeyAlg; TestDigestAlgorithmID digestAlg; ByteString algorithmIdentifier; bool accepted; }; TestSignatureAlgorithm md2WithRSAEncryption(); TestSignatureAlgorithm md5WithRSAEncryption(); TestSignatureAlgorithm sha1WithRSAEncryption(); TestSignatureAlgorithm sha256WithRSAEncryption(); // e.g. YMDHMS(2016, 12, 31, 1, 23, 45) => 2016-12-31:01:23:45 (GMT) mozilla::pkix::Time YMDHMS(uint16_t year, uint16_t month, uint16_t day, uint16_t hour, uint16_t minutes, uint16_t seconds); ByteString TLV(uint8_t tag, size_t length, const ByteString& value); inline ByteString TLV(uint8_t tag, const ByteString& value) { return TLV(tag, value.length(), value); } // Although we can't enforce it without relying on Cuser-defined literals, // which aren't supported by all of our compilers yet, you should only pass // string literals as the last parameter to the following two functions. template inline ByteString TLV(uint8_t tag, const char (&value)[N]) { static_assert(N > 0, "cannot have string literal of size 0"); assert(value[N - 1] == 0); return TLV(tag, ByteString(reinterpret_cast(&value), N - 1)); } template inline ByteString TLV(uint8_t tag, size_t length, const char (&value)[N]) { static_assert(N > 0, "cannot have string literal of size 0"); assert(value[N - 1] == 0); return TLV(tag, length, ByteString(reinterpret_cast(&value), N - 1)); } ByteString Boolean(bool value); ByteString Integer(long value); ByteString CN(const ByteString&, uint8_t encodingTag = 0x0c /*UTF8String*/); inline ByteString CN(const char* value, uint8_t encodingTag = 0x0c /*UTF8String*/) { return CN( ByteString(reinterpret_cast(value), std::strlen(value)), encodingTag); } ByteString OU(const ByteString&, uint8_t encodingTag = 0x0c /*UTF8String*/); inline ByteString OU(const char* value, uint8_t encodingTag = 0x0c /*UTF8String*/) { return OU( ByteString(reinterpret_cast(value), std::strlen(value)), encodingTag); } ByteString emailAddress(const ByteString&); inline ByteString emailAddress(const char* value) { return emailAddress( ByteString(reinterpret_cast(value), std::strlen(value))); } // RelativeDistinguishedName ::= // SET SIZE (1..MAX) OF AttributeTypeAndValue // ByteString RDN(const ByteString& avas); // Name ::= CHOICE { -- only one possibility for now -- // rdnSequence RDNSequence } // // RDNSequence ::= SEQUENCE OF RelativeDistinguishedName // ByteString Name(const ByteString& rdns); inline ByteString CNToDERName(const ByteString& cn) { return Name(RDN(CN(cn))); } inline ByteString CNToDERName(const char* cn) { return Name(RDN(CN(cn))); } // GeneralName ::= CHOICE { // otherName [0] OtherName, // rfc822Name [1] IA5String, // dNSName [2] IA5String, // x400Address [3] ORAddress, // directoryName [4] Name, // ediPartyName [5] EDIPartyName, // uniformResourceIdentifier [6] IA5String, // iPAddress [7] OCTET STRING, // registeredID [8] OBJECT IDENTIFIER } inline ByteString RFC822Name(const ByteString& name) { // (2 << 6) means "context-specific", 1 is the GeneralName tag. return TLV((2 << 6) | 1, name); } template inline ByteString RFC822Name(const char (&bytes)[L]) { return RFC822Name( ByteString(reinterpret_cast(&bytes), L - 1)); } inline ByteString DNSName(const ByteString& name) { // (2 << 6) means "context-specific", 2 is the GeneralName tag. return TLV((2 << 6) | 2, name); } template inline ByteString DNSName(const char (&bytes)[L]) { return DNSName(ByteString(reinterpret_cast(&bytes), L - 1)); } inline ByteString DirectoryName(const ByteString& name) { // (2 << 6) means "context-specific", (1 << 5) means "constructed", and 4 is // the DirectoryName tag. return TLV((2 << 6) | (1 << 5) | 4, name); } inline ByteString IPAddress() { // (2 << 6) means "context-specific", 7 is the GeneralName tag. return TLV((2 << 6) | 7, ByteString()); } template inline ByteString IPAddress(const uint8_t (&bytes)[L]) { // (2 << 6) means "context-specific", 7 is the GeneralName tag. return TLV((2 << 6) | 7, ByteString(bytes, L)); } // Names should be zero or more GeneralNames, like DNSName and IPAddress return, // concatenated together. // // CreatedEncodedSubjectAltName(ByteString()) results in a SAN with an empty // sequence. CreateEmptyEncodedSubjectName() results in a SAN without any // sequence. ByteString CreateEncodedSubjectAltName(const ByteString& names); ByteString CreateEncodedEmptySubjectAltName(); class TestKeyPair { public: virtual ~TestKeyPair() {} const TestPublicKeyAlgorithm publicKeyAlg; // The DER encoding of the entire SubjectPublicKeyInfo structure. This is // what is encoded in certificates. const ByteString subjectPublicKeyInfo; // The DER encoding of subjectPublicKeyInfo.subjectPublicKey. This is what is // hashed to create CertIDs for OCSP. const ByteString subjectPublicKey; virtual Result SignData(const ByteString& tbs, const TestSignatureAlgorithm& signatureAlgorithm, /*out*/ ByteString& signature) const = 0; virtual TestKeyPair* Clone() const = 0; protected: TestKeyPair(const TestPublicKeyAlgorithm& publicKeyAlg, const ByteString& spk); TestKeyPair(const TestKeyPair&) = delete; void operator=(const TestKeyPair&) = delete; }; TestKeyPair* CloneReusedKeyPair(); TestKeyPair* GenerateKeyPair(); TestKeyPair* GenerateDSSKeyPair(); inline void DeleteTestKeyPair(TestKeyPair* keyPair) { delete keyPair; } typedef std::unique_ptr ScopedTestKeyPair; Result TestVerifyECDSASignedDigest(const SignedDigest& signedDigest, Input subjectPublicKeyInfo); Result TestVerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest, Input subjectPublicKeyInfo); Result TestDigestBuf(Input item, DigestAlgorithm digestAlg, /*out*/ uint8_t* digestBuf, size_t digestBufLen); // Replace one substring in item with another of the same length, but only if // the substring was found exactly once. The "same length" restriction is // useful for avoiding invalidating lengths encoded within the item. The // "only once" restriction is helpful for avoiding making accidental changes. // // The string to search for must be 8 or more bytes long so that it is // extremely unlikely that there will ever be any false positive matches // in digital signatures, keys, hashes, etc. Result TamperOnce(/*in/out*/ ByteString& item, const ByteString& from, const ByteString& to); /////////////////////////////////////////////////////////////////////////////// // Encode Certificates enum Version { v1 = 0, v2 = 1, v3 = 2 }; // signature is assumed to be the DER encoding of an AlgorithmIdentifer. It is // put into the signature field of the TBSCertificate. In most cases, it will // be the same as signatureAlgorithm, which is the algorithm actually used // to sign the certificate. // serialNumber is assumed to be the DER encoding of an INTEGER. // // If extensions is null, then no extensions will be encoded. Otherwise, // extensions must point to an array of ByteStrings, terminated with an empty // ByteString. (If the first item of the array is empty then an empty // Extensions sequence will be encoded.) 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 CreateEncodedSerialNumber(long value); enum class Critical { No = 0, Yes = 1 }; ByteString CreateEncodedBasicConstraints( bool isCA, /*optional in*/ const long* pathLenConstraint, Critical critical); // Creates a DER-encoded extKeyUsage extension with one EKU OID. ByteString CreateEncodedEKUExtension(Input eku, Critical critical); /////////////////////////////////////////////////////////////////////////////// // Encode OCSP responses class OCSPResponseExtension final { public: OCSPResponseExtension(); ByteString id; bool critical; ByteString value; OCSPResponseExtension* next; }; class OCSPResponseContext final { public: OCSPResponseContext(const CertID& certID, std::time_t time); const CertID& certID; // TODO(bug 980538): add a way to specify what certificates are included. // The fields below are in the order that they appear in an OCSP response. enum OCSPResponseStatus { successful = 0, malformedRequest = 1, internalError = 2, tryLater = 3, // 4 is not used sigRequired = 5, unauthorized = 6, }; uint8_t responseStatus; // an OCSPResponseStatus or an invalid value bool skipResponseBytes; // If true, don't include responseBytes // responderID ByteString signerNameDER; // If set, responderID will use the byName // form; otherwise responderID will use the // byKeyHash form. std::time_t producedAt; // SingleResponse extensions (for the certID given in the constructor). OCSPResponseExtension* singleExtensions; // ResponseData extensions. OCSPResponseExtension* responseExtensions; bool includeEmptyExtensions; // If true, include the extension wrapper // regardless of if there are any actual // extensions. ScopedTestKeyPair signerKeyPair; TestSignatureAlgorithm signatureAlgorithm; bool badSignature; // If true, alter the signature to fail verification const ByteString* certs; // optional; array terminated by an empty string // The following fields are on a per-SingleResponse basis. In the future we // may support including multiple SingleResponses per response. enum CertStatus { good = 0, revoked = 1, unknown = 2, }; uint8_t certStatus; // CertStatus or an invalid value std::time_t revocationTime; // For certStatus == revoked std::time_t thisUpdate; std::time_t nextUpdate; bool includeNextUpdate; }; ByteString CreateEncodedOCSPResponse(OCSPResponseContext& context); } } } // namespace mozilla::pkix::test #endif // mozilla_pkix_test_pkixtestutil_h