/* -*- 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 "pkixtestutil.h" #include #include "cryptohi.h" #include "keyhi.h" #include "nss.h" #include "pk11pub.h" #include "pkix/pkixnss.h" #include "secerr.h" namespace mozilla { namespace pkix { namespace test { namespace { typedef ScopedPtr ScopedSECKEYPublicKey; typedef ScopedPtr ScopedSECKEYPrivateKey; Result InitNSSIfNeeded() { if (NSS_NoDB_Init(nullptr) != SECSuccess) { return MapPRErrorCodeToResult(PR_GetError()); } return Success; } class NSSTestKeyPair : public TestKeyPair { public: // NSSTestKeyPair takes ownership of privateKey. NSSTestKeyPair(const ByteString& spki, const ByteString& spk, SECKEYPrivateKey* privateKey) : TestKeyPair(spki, spk) , privateKey(privateKey) { } virtual Result SignData(const ByteString& tbs, SignatureAlgorithm signatureAlgorithm, /*out*/ ByteString& signature) const { SECOidTag signatureAlgorithmOidTag; switch (signatureAlgorithm) { case SignatureAlgorithm::rsa_pkcs1_with_sha256: signatureAlgorithmOidTag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION; break; default: return Result::FATAL_ERROR_INVALID_ARGS; } SECItem signatureItem; if (SEC_SignData(&signatureItem, tbs.data(), tbs.length(), privateKey.get(), signatureAlgorithmOidTag) != SECSuccess) { return MapPRErrorCodeToResult(PR_GetError()); } signature.assign(signatureItem.data, signatureItem.len); SECITEM_FreeItem(&signatureItem, false); return Success; } virtual TestKeyPair* Clone() const { ScopedSECKEYPrivateKey privateKeyCopy(SECKEY_CopyPrivateKey(privateKey.get())); if (!privateKeyCopy) { return nullptr; } return new (std::nothrow) NSSTestKeyPair(subjectPublicKeyInfo, subjectPublicKey, privateKeyCopy.release()); } private: ScopedSECKEYPrivateKey privateKey; }; } // unnamed namespace // This private function is also used by Gecko's PSM test framework // (OCSPCommon.cpp). // // Ownership of privateKey is transfered. TestKeyPair* CreateTestKeyPair(const ByteString& spki, const ByteString& spk, SECKEYPrivateKey* privateKey) { return new (std::nothrow) NSSTestKeyPair(spki, spk, privateKey); } TestKeyPair* GenerateKeyPair() { if (InitNSSIfNeeded() != Success) { return nullptr; } ScopedPtr slot(PK11_GetInternalSlot()); if (!slot) { return nullptr; } // Bug 1012786: PK11_GenerateKeyPair can fail if there is insufficient // entropy to generate a random key. Attempting to add some entropy and // retrying appears to solve this issue. for (uint32_t retries = 0; retries < 10; retries++) { PK11RSAGenParams params; params.keySizeInBits = 2048; params.pe = 3; SECKEYPublicKey* publicKeyTemp = nullptr; ScopedSECKEYPrivateKey privateKey(PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN, ¶ms, &publicKeyTemp, false, true, nullptr)); ScopedSECKEYPublicKey publicKey(publicKeyTemp); if (privateKey) { ScopedSECItem spkiDER(SECKEY_EncodeDERSubjectPublicKeyInfo(publicKey.get())); if (!spkiDER) { return nullptr; } ScopedPtr spki(SECKEY_CreateSubjectPublicKeyInfo(publicKey.get())); if (!spki) { return nullptr; } SECItem spkDER = spki->subjectPublicKey; DER_ConvertBitString(&spkDER); // bits to bytes return CreateTestKeyPair(ByteString(spkiDER->data, spkiDER->len), ByteString(spkDER.data, spkDER.len), privateKey.release()); } assert(!publicKeyTemp); if (PR_GetError() != SEC_ERROR_PKCS11_FUNCTION_FAILED) { break; } // Since these keys are only for testing, we don't need them to be good, // random keys. // https://xkcd.com/221/ static const uint8_t RANDOM_NUMBER[] = { 4, 4, 4, 4, 4, 4, 4, 4 }; if (PK11_RandomUpdate((void*) &RANDOM_NUMBER, sizeof(RANDOM_NUMBER)) != SECSuccess) { break; } } return nullptr; } ByteString SHA1(const ByteString& toHash) { if (InitNSSIfNeeded() != Success) { return ENCODING_FAILED; } uint8_t digestBuf[SHA1_LENGTH]; SECStatus srv = PK11_HashBuf(SEC_OID_SHA1, digestBuf, toHash.data(), static_cast(toHash.length())); if (srv != SECSuccess) { return ENCODING_FAILED; } return ByteString(digestBuf, sizeof(digestBuf)); } Result TestCheckPublicKey(Input subjectPublicKeyInfo) { Result rv = InitNSSIfNeeded(); if (rv != Success) { return rv; } return CheckPublicKey(subjectPublicKeyInfo); } Result TestVerifySignedData(const SignedDataWithSignature& signedData, Input subjectPublicKeyInfo) { Result rv = InitNSSIfNeeded(); if (rv != Success) { return rv; } return VerifySignedData(signedData, subjectPublicKeyInfo, nullptr); } Result TestDigestBuf(Input item, /*out*/ uint8_t* digestBuf, size_t digestBufLen) { Result rv = InitNSSIfNeeded(); if (rv != Success) { return rv; } return DigestBuf(item, digestBuf, digestBufLen); } } } } // namespace mozilla::pkix::test