mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-07-30 14:29:19 +00:00
366 lines
11 KiB
C++
366 lines
11 KiB
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 "mozpkix/test/pkixtestnss.h"
|
|
|
|
#include <limits>
|
|
|
|
#include "cryptohi.h"
|
|
#include "keyhi.h"
|
|
#include "nss.h"
|
|
#include "pk11pqg.h"
|
|
#include "pk11pub.h"
|
|
#include "mozpkix/nss_scoped_ptrs.h"
|
|
#include "mozpkix/pkixnss.h"
|
|
#include "mozpkix/pkixder.h"
|
|
#include "mozpkix/pkixutil.h"
|
|
#include "prinit.h"
|
|
#include "secerr.h"
|
|
#include "secitem.h"
|
|
|
|
namespace mozilla { namespace pkix { namespace test {
|
|
|
|
namespace {
|
|
|
|
TestKeyPair* GenerateKeyPairInner();
|
|
|
|
void
|
|
InitNSSIfNeeded()
|
|
{
|
|
if (NSS_NoDB_Init(nullptr) != SECSuccess) {
|
|
abort();
|
|
}
|
|
}
|
|
|
|
static ScopedTestKeyPair reusedKeyPair;
|
|
|
|
PRStatus
|
|
InitReusedKeyPair()
|
|
{
|
|
InitNSSIfNeeded();
|
|
reusedKeyPair.reset(GenerateKeyPairInner());
|
|
return reusedKeyPair ? PR_SUCCESS : PR_FAILURE;
|
|
}
|
|
|
|
class NSSTestKeyPair final : public TestKeyPair
|
|
{
|
|
public:
|
|
NSSTestKeyPair(const TestPublicKeyAlgorithm& aPublicKeyAlg,
|
|
const ByteString& spk,
|
|
const ByteString& aEncryptedPrivateKey,
|
|
const ByteString& aEncryptionAlgorithm,
|
|
const ByteString& aEncryptionParams)
|
|
: TestKeyPair(aPublicKeyAlg, spk)
|
|
, encryptedPrivateKey(aEncryptedPrivateKey)
|
|
, encryptionAlgorithm(aEncryptionAlgorithm)
|
|
, encryptionParams(aEncryptionParams)
|
|
{
|
|
}
|
|
|
|
Result SignData(const ByteString& tbs,
|
|
const TestSignatureAlgorithm& signatureAlgorithm,
|
|
/*out*/ ByteString& signature) const override
|
|
{
|
|
SECOidTag oidTag;
|
|
if (signatureAlgorithm.publicKeyAlg == RSA_PKCS1()) {
|
|
switch (signatureAlgorithm.digestAlg) {
|
|
case TestDigestAlgorithmID::MD2:
|
|
oidTag = SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION;
|
|
break;
|
|
case TestDigestAlgorithmID::MD5:
|
|
oidTag = SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION;
|
|
break;
|
|
case TestDigestAlgorithmID::SHA1:
|
|
oidTag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
|
|
break;
|
|
case TestDigestAlgorithmID::SHA224:
|
|
oidTag = SEC_OID_PKCS1_SHA224_WITH_RSA_ENCRYPTION;
|
|
break;
|
|
case TestDigestAlgorithmID::SHA256:
|
|
oidTag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION;
|
|
break;
|
|
case TestDigestAlgorithmID::SHA384:
|
|
oidTag = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION;
|
|
break;
|
|
case TestDigestAlgorithmID::SHA512:
|
|
oidTag = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION;
|
|
break;
|
|
MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
|
|
}
|
|
} else {
|
|
abort();
|
|
}
|
|
|
|
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
|
|
if (!slot) {
|
|
return MapPRErrorCodeToResult(PR_GetError());
|
|
}
|
|
SECItem encryptedPrivateKeyInfoItem = {
|
|
siBuffer,
|
|
const_cast<uint8_t*>(encryptedPrivateKey.data()),
|
|
static_cast<unsigned int>(encryptedPrivateKey.length())
|
|
};
|
|
SECItem encryptionAlgorithmItem = {
|
|
siBuffer,
|
|
const_cast<uint8_t*>(encryptionAlgorithm.data()),
|
|
static_cast<unsigned int>(encryptionAlgorithm.length())
|
|
};
|
|
SECItem encryptionParamsItem = {
|
|
siBuffer,
|
|
const_cast<uint8_t*>(encryptionParams.data()),
|
|
static_cast<unsigned int>(encryptionParams.length())
|
|
};
|
|
SECKEYEncryptedPrivateKeyInfo encryptedPrivateKeyInfo = {
|
|
nullptr,
|
|
{ encryptionAlgorithmItem, encryptionParamsItem },
|
|
encryptedPrivateKeyInfoItem
|
|
};
|
|
SECItem passwordItem = { siBuffer, nullptr, 0 };
|
|
SECItem publicValueItem = {
|
|
siBuffer,
|
|
const_cast<uint8_t*>(subjectPublicKey.data()),
|
|
static_cast<unsigned int>(subjectPublicKey.length())
|
|
};
|
|
SECKEYPrivateKey* privateKey;
|
|
// This should always be an RSA key (we'll have aborted above if we're not
|
|
// doing an RSA signature).
|
|
if (PK11_ImportEncryptedPrivateKeyInfoAndReturnKey(
|
|
slot.get(), &encryptedPrivateKeyInfo, &passwordItem, nullptr,
|
|
&publicValueItem, false, false, rsaKey, KU_ALL, &privateKey,
|
|
nullptr) != SECSuccess) {
|
|
return MapPRErrorCodeToResult(PR_GetError());
|
|
}
|
|
ScopedSECKEYPrivateKey scopedPrivateKey(privateKey);
|
|
SECItem signatureItem;
|
|
if (SEC_SignData(&signatureItem, tbs.data(),
|
|
static_cast<int>(tbs.length()),
|
|
scopedPrivateKey.get(), oidTag) != SECSuccess) {
|
|
return MapPRErrorCodeToResult(PR_GetError());
|
|
}
|
|
signature.assign(signatureItem.data, signatureItem.len);
|
|
SECITEM_FreeItem(&signatureItem, false);
|
|
return Success;
|
|
}
|
|
|
|
TestKeyPair* Clone() const override
|
|
{
|
|
return new (std::nothrow) NSSTestKeyPair(publicKeyAlg,
|
|
subjectPublicKey,
|
|
encryptedPrivateKey,
|
|
encryptionAlgorithm,
|
|
encryptionParams);
|
|
}
|
|
|
|
private:
|
|
const ByteString encryptedPrivateKey;
|
|
const ByteString encryptionAlgorithm;
|
|
const ByteString encryptionParams;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
// This private function is also used by Gecko's PSM test framework
|
|
// (OCSPCommon.cpp).
|
|
TestKeyPair* CreateTestKeyPair(const TestPublicKeyAlgorithm publicKeyAlg,
|
|
const ScopedSECKEYPublicKey& publicKey,
|
|
const ScopedSECKEYPrivateKey& privateKey)
|
|
{
|
|
ScopedCERTSubjectPublicKeyInfo
|
|
spki(SECKEY_CreateSubjectPublicKeyInfo(publicKey.get()));
|
|
if (!spki) {
|
|
return nullptr;
|
|
}
|
|
SECItem spkDER = spki->subjectPublicKey;
|
|
DER_ConvertBitString(&spkDER); // bits to bytes
|
|
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
|
|
if (!slot) {
|
|
return nullptr;
|
|
}
|
|
// Because NSSTestKeyPair isn't tracked by XPCOM and won't otherwise be aware
|
|
// of shutdown, we don't have a way to release NSS resources at the
|
|
// appropriate time. To work around this, NSSTestKeyPair doesn't hold on to
|
|
// NSS resources. Instead, we export the generated private key part as an
|
|
// encrypted blob (with an empty password and fairly lame encryption). When we
|
|
// need to use it (e.g. to sign something), we decrypt it and create a
|
|
// temporary key object.
|
|
SECItem passwordItem = { siBuffer, nullptr, 0 };
|
|
ScopedSECKEYEncryptedPrivateKeyInfo encryptedPrivateKey(
|
|
PK11_ExportEncryptedPrivKeyInfo(
|
|
slot.get(), SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC,
|
|
&passwordItem, privateKey.get(), 1, nullptr));
|
|
if (!encryptedPrivateKey) {
|
|
return nullptr;
|
|
}
|
|
|
|
return new (std::nothrow) NSSTestKeyPair(
|
|
publicKeyAlg,
|
|
ByteString(spkDER.data, spkDER.len),
|
|
ByteString(encryptedPrivateKey->encryptedData.data,
|
|
encryptedPrivateKey->encryptedData.len),
|
|
ByteString(encryptedPrivateKey->algorithm.algorithm.data,
|
|
encryptedPrivateKey->algorithm.algorithm.len),
|
|
ByteString(encryptedPrivateKey->algorithm.parameters.data,
|
|
encryptedPrivateKey->algorithm.parameters.len));
|
|
}
|
|
|
|
namespace {
|
|
|
|
TestKeyPair*
|
|
GenerateKeyPairInner()
|
|
{
|
|
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
|
|
if (!slot) {
|
|
abort();
|
|
}
|
|
PK11RSAGenParams params;
|
|
params.keySizeInBits = 2048;
|
|
params.pe = 65537;
|
|
|
|
// 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++) {
|
|
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) {
|
|
return CreateTestKeyPair(RSA_PKCS1(), publicKey, privateKey);
|
|
}
|
|
|
|
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(
|
|
const_cast<void*>(reinterpret_cast<const void*>(RANDOM_NUMBER)),
|
|
sizeof(RANDOM_NUMBER)) != SECSuccess) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
abort();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TestKeyPair*
|
|
GenerateKeyPair()
|
|
{
|
|
InitNSSIfNeeded();
|
|
return GenerateKeyPairInner();
|
|
}
|
|
|
|
TestKeyPair*
|
|
CloneReusedKeyPair()
|
|
{
|
|
static PRCallOnceType initCallOnce;
|
|
if (PR_CallOnce(&initCallOnce, InitReusedKeyPair) != PR_SUCCESS) {
|
|
abort();
|
|
}
|
|
assert(reusedKeyPair);
|
|
return reusedKeyPair->Clone();
|
|
}
|
|
|
|
TestKeyPair*
|
|
GenerateDSSKeyPair()
|
|
{
|
|
InitNSSIfNeeded();
|
|
|
|
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
|
|
if (!slot) {
|
|
return nullptr;
|
|
}
|
|
|
|
ByteString p(DSS_P());
|
|
ByteString q(DSS_Q());
|
|
ByteString g(DSS_G());
|
|
|
|
static const PQGParams PARAMS = {
|
|
nullptr,
|
|
{ siBuffer,
|
|
const_cast<uint8_t*>(p.data()),
|
|
static_cast<unsigned int>(p.length())
|
|
},
|
|
{ siBuffer,
|
|
const_cast<uint8_t*>(q.data()),
|
|
static_cast<unsigned int>(q.length())
|
|
},
|
|
{ siBuffer,
|
|
const_cast<uint8_t*>(g.data()),
|
|
static_cast<unsigned int>(g.length())
|
|
}
|
|
};
|
|
|
|
SECKEYPublicKey* publicKeyTemp = nullptr;
|
|
ScopedSECKEYPrivateKey
|
|
privateKey(PK11_GenerateKeyPair(slot.get(), CKM_DSA_KEY_PAIR_GEN,
|
|
const_cast<PQGParams*>(&PARAMS),
|
|
&publicKeyTemp, false, true, nullptr));
|
|
if (!privateKey) {
|
|
return nullptr;
|
|
}
|
|
ScopedSECKEYPublicKey publicKey(publicKeyTemp);
|
|
return CreateTestKeyPair(DSS(), publicKey, privateKey);
|
|
}
|
|
|
|
Result
|
|
TestVerifyECDSASignedDigest(const SignedDigest& signedDigest,
|
|
Input subjectPublicKeyInfo)
|
|
{
|
|
InitNSSIfNeeded();
|
|
return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo,
|
|
nullptr);
|
|
}
|
|
|
|
Result
|
|
TestVerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
|
|
Input subjectPublicKeyInfo)
|
|
{
|
|
InitNSSIfNeeded();
|
|
return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo,
|
|
nullptr);
|
|
}
|
|
|
|
Result
|
|
TestDigestBuf(Input item,
|
|
DigestAlgorithm digestAlg,
|
|
/*out*/ uint8_t* digestBuf,
|
|
size_t digestBufLen)
|
|
{
|
|
InitNSSIfNeeded();
|
|
return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
|
|
}
|
|
|
|
} } } // namespace mozilla::pkix::test
|