diff options
Diffstat (limited to 'security/pkix/include/pkix-test/pkixtestutil.h')
-rw-r--r-- | security/pkix/include/pkix-test/pkixtestutil.h | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/security/pkix/include/pkix-test/pkixtestutil.h b/security/pkix/include/pkix-test/pkixtestutil.h new file mode 100644 index 0000000000..1841e9380c --- /dev/null +++ b/security/pkix/include/pkix-test/pkixtestutil.h @@ -0,0 +1,388 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. + */ + +#ifndef mozilla_pkix_test_pkixtestutil_h +#define mozilla_pkix_test_pkixtestutil_h + +#include <cstdint> +#include <cstring> +#include <ctime> +#include <string> + +#include "pkix/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 |