| /* -*- 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 <cstdint> |
| #include <cstring> |
| #include <ctime> |
| #include <string> |
| |
| #include "mozpkix/pkixtypes.h" |
| |
| namespace mozilla { |
| namespace pkix { |
| namespace test { |
| |
| typedef std::basic_string<uint8_t> ByteString; |
| |
| inline bool ENCODING_FAILED(const ByteString& bs) { return bs.empty(); } |
| |
| template <size_t L> |
| inline ByteString BytesToByteString(const uint8_t (&bytes)[L]) { |
| return ByteString(bytes, L); |
| } |
| |
| // XXX: Ideally, we should define this instead: |
| // |
| // template <typename T, std::size_t N> |
| // 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 <size_t N> |
| 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<const uint8_t*>(&value), N - 1)); |
| } |
| |
| template <size_t N> |
| 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<const uint8_t*>(&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<const uint8_t*>(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<const uint8_t*>(value), std::strlen(value)), |
| encodingTag); |
| } |
| |
| ByteString emailAddress(const ByteString&); |
| |
| inline ByteString emailAddress(const char* value) { |
| return emailAddress( |
| ByteString(reinterpret_cast<const uint8_t*>(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 <size_t L> |
| inline ByteString RFC822Name(const char (&bytes)[L]) { |
| return RFC822Name( |
| ByteString(reinterpret_cast<const uint8_t*>(&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 <size_t L> |
| inline ByteString DNSName(const char (&bytes)[L]) { |
| return DNSName(ByteString(reinterpret_cast<const uint8_t*>(&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 <size_t L> |
| 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<TestKeyPair> 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 |