mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 14:52:16 +00:00
bug 1430906
- don't hold around a test key forever in mozilla::pkix gtests r=franziskus
Before this patch, mozilla::pkix gtests would generate a public/private key pair and stash it in a global variable. Since this wasn't part of XPCOM nor tracked by the PSM/NSS shutdown machinery, it wouldn't get released at the appropriate time. The solution to this is to generate the key and then essentially export it as data, so no NSS objects are held alive. Since NSS considers private keys stored in the persistent database sensitive and won't export them in the clear, we "encrypt" the key material with an empty password so we can import it when necessary. (While the gtests don't use persistent keys, the test utilties in the gtests are also used by some xpcshell tests that do use persistent keys, hence the need to encrypt the key material.) --HG-- extra : rebase_source : df10c25a462a3ba0396f5ba4a43a52fb924548ff extra : amend_source : d95722891e49a99c471046cd9c758e914a02838e
This commit is contained in:
parent
b5e28b4ecd
commit
5bfa1a1ac8
@ -7,19 +7,11 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "pkixtestutil.h"
|
||||
#include "pkixtestnss.h"
|
||||
#include "TLSServer.h"
|
||||
#include "secder.h"
|
||||
#include "secerr.h"
|
||||
|
||||
namespace mozilla { namespace pkix { namespace test {
|
||||
|
||||
// Ownership of privateKey is transfered.
|
||||
TestKeyPair* CreateTestKeyPair(const TestPublicKeyAlgorithm publicKeyAlg,
|
||||
const SECKEYPublicKey& publicKey,
|
||||
SECKEYPrivateKey* privateKey);
|
||||
|
||||
} } } // namespace mozilla::pkix::test
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::pkix;
|
||||
using namespace mozilla::pkix::test;
|
||||
@ -28,15 +20,15 @@ using namespace mozilla::test;
|
||||
static TestKeyPair*
|
||||
CreateTestKeyPairFromCert(const UniqueCERTCertificate& cert)
|
||||
{
|
||||
UniqueSECKEYPrivateKey privateKey(PK11_FindKeyByAnyCert(cert.get(), nullptr));
|
||||
ScopedSECKEYPrivateKey privateKey(PK11_FindKeyByAnyCert(cert.get(), nullptr));
|
||||
if (!privateKey) {
|
||||
return nullptr;
|
||||
}
|
||||
UniqueSECKEYPublicKey publicKey(CERT_ExtractPublicKey(cert.get()));
|
||||
ScopedSECKEYPublicKey publicKey(CERT_ExtractPublicKey(cert.get()));
|
||||
if (!publicKey) {
|
||||
return nullptr;
|
||||
}
|
||||
return CreateTestKeyPair(RSA_PKCS1(), *publicKey.get(), privateKey.release());
|
||||
return CreateTestKeyPair(RSA_PKCS1(), publicKey, privateKey);
|
||||
}
|
||||
|
||||
SECItemArray*
|
||||
|
@ -390,6 +390,7 @@ PK11_ImportCert
|
||||
PK11_ImportCertForKey
|
||||
PK11_ImportCRL
|
||||
PK11_ImportDERPrivateKeyInfoAndReturnKey
|
||||
PK11_ImportEncryptedPrivateKeyInfoAndReturnKey
|
||||
PK11_ImportPublicKey
|
||||
PK11_ImportSymKey
|
||||
PK11_InitPin
|
||||
|
@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
#include "pkixtestutil.h"
|
||||
#include "pkixtestnss.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
@ -42,17 +43,18 @@ namespace mozilla { namespace pkix { namespace test {
|
||||
|
||||
namespace {
|
||||
|
||||
typedef ScopedPtr<SECKEYPublicKey, SECKEY_DestroyPublicKey>
|
||||
ScopedSECKEYPublicKey;
|
||||
typedef ScopedPtr<SECKEYPrivateKey, SECKEY_DestroyPrivateKey>
|
||||
ScopedSECKEYPrivateKey;
|
||||
|
||||
inline void
|
||||
SECITEM_FreeItem_true(SECItem* item)
|
||||
{
|
||||
SECITEM_FreeItem(item, true);
|
||||
}
|
||||
|
||||
inline void
|
||||
SECKEY_DestroyEncryptedPrivateKeyInfo_true(SECKEYEncryptedPrivateKeyInfo* e)
|
||||
{
|
||||
SECKEY_DestroyEncryptedPrivateKeyInfo(e, true);
|
||||
}
|
||||
|
||||
typedef mozilla::pkix::ScopedPtr<SECItem, SECITEM_FreeItem_true> ScopedSECItem;
|
||||
|
||||
TestKeyPair* GenerateKeyPairInner();
|
||||
@ -78,12 +80,15 @@ InitReusedKeyPair()
|
||||
class NSSTestKeyPair final : public TestKeyPair
|
||||
{
|
||||
public:
|
||||
// NSSTestKeyPair takes ownership of privateKey.
|
||||
NSSTestKeyPair(const TestPublicKeyAlgorithm& publicKeyAlg,
|
||||
const ByteString& spk,
|
||||
SECKEYPrivateKey* privateKey)
|
||||
const ByteString& encryptedPrivateKey,
|
||||
const ByteString& encryptionAlgorithm,
|
||||
const ByteString& encryptionParams)
|
||||
: TestKeyPair(publicKeyAlg, spk)
|
||||
, privateKey(privateKey)
|
||||
, encryptedPrivateKey(encryptedPrivateKey)
|
||||
, encryptionAlgorithm(encryptionAlgorithm)
|
||||
, encryptionParams(encryptionParams)
|
||||
{
|
||||
}
|
||||
|
||||
@ -121,10 +126,50 @@ public:
|
||||
abort();
|
||||
}
|
||||
|
||||
ScopedPtr<PK11SlotInfo, PK11_FreeSlot> 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()),
|
||||
privateKey.get(), oidTag) != SECSuccess) {
|
||||
scopedPrivateKey.get(), oidTag) != SECSuccess) {
|
||||
return MapPRErrorCodeToResult(PR_GetError());
|
||||
}
|
||||
signature.assign(signatureItem.data, signatureItem.len);
|
||||
@ -134,40 +179,64 @@ public:
|
||||
|
||||
TestKeyPair* Clone() const override
|
||||
{
|
||||
ScopedSECKEYPrivateKey
|
||||
privateKeyCopy(SECKEY_CopyPrivateKey(privateKey.get()));
|
||||
if (!privateKeyCopy) {
|
||||
return nullptr;
|
||||
}
|
||||
return new (std::nothrow) NSSTestKeyPair(publicKeyAlg,
|
||||
subjectPublicKey,
|
||||
privateKeyCopy.release());
|
||||
encryptedPrivateKey,
|
||||
encryptionAlgorithm,
|
||||
encryptionParams);
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedSECKEYPrivateKey privateKey;
|
||||
const ByteString encryptedPrivateKey;
|
||||
const ByteString encryptionAlgorithm;
|
||||
const ByteString encryptionParams;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// This private function is also used by Gecko's PSM test framework
|
||||
// (OCSPCommon.cpp).
|
||||
//
|
||||
// Ownership of privateKey is transfered.
|
||||
TestKeyPair* CreateTestKeyPair(const TestPublicKeyAlgorithm publicKeyAlg,
|
||||
const SECKEYPublicKey& publicKey,
|
||||
SECKEYPrivateKey* privateKey)
|
||||
const ScopedSECKEYPublicKey& publicKey,
|
||||
const ScopedSECKEYPrivateKey& privateKey)
|
||||
{
|
||||
ScopedPtr<CERTSubjectPublicKeyInfo, SECKEY_DestroySubjectPublicKeyInfo>
|
||||
spki(SECKEY_CreateSubjectPublicKeyInfo(&publicKey));
|
||||
spki(SECKEY_CreateSubjectPublicKeyInfo(publicKey.get()));
|
||||
if (!spki) {
|
||||
return nullptr;
|
||||
}
|
||||
SECItem spkDER = spki->subjectPublicKey;
|
||||
DER_ConvertBitString(&spkDER); // bits to bytes
|
||||
return new (std::nothrow) NSSTestKeyPair(publicKeyAlg,
|
||||
ScopedPtr<PK11SlotInfo, PK11_FreeSlot> 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 };
|
||||
ScopedPtr<SECKEYEncryptedPrivateKeyInfo,
|
||||
SECKEY_DestroyEncryptedPrivateKeyInfo_true> 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),
|
||||
privateKey);
|
||||
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 {
|
||||
@ -194,7 +263,7 @@ GenerateKeyPairInner()
|
||||
nullptr));
|
||||
ScopedSECKEYPublicKey publicKey(publicKeyTemp);
|
||||
if (privateKey) {
|
||||
return CreateTestKeyPair(RSA_PKCS1(), *publicKey, privateKey.release());
|
||||
return CreateTestKeyPair(RSA_PKCS1(), publicKey, privateKey);
|
||||
}
|
||||
|
||||
assert(!publicKeyTemp);
|
||||
@ -275,7 +344,7 @@ GenerateDSSKeyPair()
|
||||
return nullptr;
|
||||
}
|
||||
ScopedSECKEYPublicKey publicKey(publicKeyTemp);
|
||||
return CreateTestKeyPair(DSS(), *publicKey, privateKey.release());
|
||||
return CreateTestKeyPair(DSS(), publicKey, privateKey);
|
||||
}
|
||||
|
||||
Result
|
||||
|
49
security/pkix/test/lib/pkixtestnss.h
Normal file
49
security/pkix/test/lib/pkixtestnss.h
Normal file
@ -0,0 +1,49 @@
|
||||
/* -*- 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 2018 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.
|
||||
*/
|
||||
|
||||
// This file provides some implementation-specific test utilities. This is only
|
||||
// necessary because some PSM xpcshell test utilities overlap in functionality
|
||||
// with these test utilities, so the underlying implementation is shared.
|
||||
|
||||
#ifndef mozilla_pkix_test_pkixtestnss_h
|
||||
#define mozilla_pkix_test_pkixtestnss_h
|
||||
|
||||
#include "keyhi.h"
|
||||
#include "keythi.h"
|
||||
#include "pkixtestutil.h"
|
||||
|
||||
namespace mozilla { namespace pkix { namespace test {
|
||||
|
||||
typedef ScopedPtr<SECKEYPublicKey, SECKEY_DestroyPublicKey>
|
||||
ScopedSECKEYPublicKey;
|
||||
typedef ScopedPtr<SECKEYPrivateKey, SECKEY_DestroyPrivateKey>
|
||||
ScopedSECKEYPrivateKey;
|
||||
|
||||
TestKeyPair* CreateTestKeyPair(const TestPublicKeyAlgorithm publicKeyAlg,
|
||||
const ScopedSECKEYPublicKey& publicKey,
|
||||
const ScopedSECKEYPrivateKey& privateKey);
|
||||
|
||||
} } } // namespace mozilla::pkix::test
|
||||
|
||||
#endif // mozilla_pkix_test_pkixtestnss_h
|
@ -22,8 +22,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef mozilla_pkix_test_pkixtestutils_h
|
||||
#define mozilla_pkix_test_pkixtestutils_h
|
||||
#ifndef mozilla_pkix_test_pkixtestutil_h
|
||||
#define mozilla_pkix_test_pkixtestutil_h
|
||||
|
||||
#include <ctime>
|
||||
#include <stdint.h> // Some Mozilla-supported compilers lack <cstdint>
|
||||
@ -445,4 +445,4 @@ ByteString CreateEncodedOCSPResponse(OCSPResponseContext& context);
|
||||
|
||||
} } } // namespace mozilla::pkix::test
|
||||
|
||||
#endif // mozilla_pkix_test_pkixtestutils_h
|
||||
#endif // mozilla_pkix_test_pkixtestutil_h
|
||||
|
Loading…
Reference in New Issue
Block a user