diff options
author | spang <spang@chromium.org> | 2015-05-01 14:01:57 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-05-01 21:02:38 +0000 |
commit | 9ce3458d9a03b38ca717291d08d564e12fd8254a (patch) | |
tree | b0f4570294cfb11e72f16cf0e424f40313ff2fdb /crypto | |
parent | c10dfc7c662c078a7310e6c7d6041763150ce494 (diff) | |
download | chromium_src-9ce3458d9a03b38ca717291d08d564e12fd8254a.zip chromium_src-9ce3458d9a03b38ca717291d08d564e12fd8254a.tar.gz chromium_src-9ce3458d9a03b38ca717291d08d564e12fd8254a.tar.bz2 |
Revert of Don't use RSAPrivateKey in NSS integration code. (patchset #6 id:100001 of https://codereview.chromium.org/1106103003/)
Reason for revert:
Causes SEGV during login on Chrome OS
BUG=483606
Original issue's description:
> Don't use RSAPrivateKey in NSS integration code.
>
> Currently some NSS platform integration logic transits private keys through
> RSAPrivateKey on CrOS. This prevents incrementally switching RSAPrivateKey to
> BoringSSL while keeping platform integrations on NSS.
>
> The intent of this change is to clarify RSAPrivateKey as a BoringSSL vs NSS
> internal crypto library (use_openssl=0 vs use_openssl=1) abstraction. It's
> primarily to be used with SignatureCreator. Code which uses NSS based on
> use_nss_certs rather than use_openssl because the underlying platform is NSS
> should call NSS routines directly, or introduce different abstractions.
>
> Remove the problematic RSAPrivateKey methods and instead add
> crypto/nss_key_util.h which contains some helper functions for manipulating NSS
> keys. This is sufficient to allow consumers of the removed methods to use NSS
> directly with about as much code. (This should not set back migrating that
> logic to NSS as that code was already very NSS-specific; those APIs assumed
> PK11SlotInfo.)
>
> nss_key_util.h, like nss_util.h, is built whenever NSS is used either
> internally or for platform integrations. This is so rsa_private_key_nss.cc can
> continue to use the helper functions to implement the NSS-agnostic interface.
>
> With this, the chimera CrOS configuration should build. The RSAPrivateKey logic
> is functional with the exception of some logic in components/ownership. That
> will be resolved in a future CL.
>
> BUG=478777
>
> Committed: https://crrev.com/a46a990b2ccae2b66e87b5f76d2866044dc3182e
> Cr-Commit-Position: refs/heads/master@{#327909}
TBR=rsleevi@chromium.org,pneubeck@chromium.org,dpolukhin@chromium.org,caitkp@chromium.org,davidben@chromium.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=483606
Review URL: https://codereview.chromium.org/1118263003
Cr-Commit-Position: refs/heads/master@{#327978}
Diffstat (limited to 'crypto')
-rw-r--r-- | crypto/BUILD.gn | 23 | ||||
-rw-r--r-- | crypto/crypto.gyp | 16 | ||||
-rw-r--r-- | crypto/crypto.gypi | 2 | ||||
-rw-r--r-- | crypto/nss_key_util.cc | 161 | ||||
-rw-r--r-- | crypto/nss_key_util.h | 58 | ||||
-rw-r--r-- | crypto/nss_key_util_unittest.cc | 87 | ||||
-rw-r--r-- | crypto/rsa_private_key.h | 69 | ||||
-rw-r--r-- | crypto/rsa_private_key_nss.cc | 233 | ||||
-rw-r--r-- | crypto/rsa_private_key_nss_unittest.cc | 66 |
9 files changed, 358 insertions, 357 deletions
diff --git a/crypto/BUILD.gn b/crypto/BUILD.gn index f84c4d4..c1eabe6 100644 --- a/crypto/BUILD.gn +++ b/crypto/BUILD.gn @@ -50,8 +50,6 @@ component("crypto") { "mock_apple_keychain.h", "mock_apple_keychain_ios.cc", "mock_apple_keychain_mac.cc", - "nss_key_util.cc", - "nss_key_util.h", "nss_util.cc", "nss_util.h", "nss_util_internal.h", @@ -173,12 +171,10 @@ component("crypto") { ] } - # Some files are built when NSS is used at all, either for the internal crypto - # library or the platform certificate library. + # Remove nss_util when NSS is used for neither the internal crypto library + # nor the platform certificate library. if (use_openssl && !use_nss_certs) { sources -= [ - "nss_key_util.cc", - "nss_key_util.h", "nss_util.cc", "nss_util.h", "nss_util_internal.h", @@ -230,12 +226,12 @@ test("crypto_unittests") { "ghash_unittest.cc", "hkdf_unittest.cc", "hmac_unittest.cc", - "nss_key_util_unittest.cc", "nss_util_unittest.cc", "openssl_bio_string_unittest.cc", "p224_spake_unittest.cc", "p224_unittest.cc", "random_unittest.cc", + "rsa_private_key_nss_unittest.cc", "rsa_private_key_unittest.cc", "secure_hash_unittest.cc", "sha2_unittest.cc", @@ -244,16 +240,15 @@ test("crypto_unittests") { "symmetric_key_unittest.cc", ] - # Some files are built when NSS is used at all, either for the internal crypto - # library or the platform certificate library. + # Remove nss_util when NSS is used for neither the internal crypto library + # nor the platform certificate library. if (use_openssl && !use_nss_certs) { - sources -= [ - "nss_key_util_unittest.cc", - "nss_util_unittest.cc", - ] + sources -= [ "nss_util_unittest.cc" ] } - if (!use_openssl) { + if (use_openssl) { + sources -= [ "rsa_private_key_nss_unittest.cc" ] + } else { sources -= [ "openssl_bio_string_unittest.cc" ] } diff --git a/crypto/crypto.gyp b/crypto/crypto.gyp index e6bff0b..00b59b5 100644 --- a/crypto/crypto.gyp +++ b/crypto/crypto.gyp @@ -143,11 +143,9 @@ ], },], [ 'use_openssl==1 and use_nss_certs==0', { - # Some files are built when NSS is used at all, either for the - # internal crypto library or the platform certificate library. + # NSS is used for neither the internal crypto library nor the + # platform certificate library. 'sources!': [ - 'nss_key_util.cc', - 'nss_key_util.h', 'nss_util.cc', 'nss_util.h', 'nss_util_internal.h', @@ -170,13 +168,13 @@ 'ghash_unittest.cc', 'hkdf_unittest.cc', 'hmac_unittest.cc', - 'nss_key_util_unittest.cc', 'nss_util_unittest.cc', 'openssl_bio_string_unittest.cc', 'p224_unittest.cc', 'p224_spake_unittest.cc', 'random_unittest.cc', 'rsa_private_key_unittest.cc', + 'rsa_private_key_nss_unittest.cc', 'secure_hash_unittest.cc', 'sha2_unittest.cc', 'signature_creator_unittest.cc', @@ -207,10 +205,9 @@ ], }], [ 'use_openssl == 1 and use_nss_certs == 0', { - # Some files are built when NSS is used at all, either for the - # internal crypto library or the platform certificate library. + # nss_util is built if NSS is used for either the internal crypto + # library or the platform certificate library. 'sources!': [ - 'nss_key_util_unittest.cc', 'nss_util_unittest.cc', ], }], @@ -227,6 +224,9 @@ 'dependencies': [ '../third_party/boringssl/boringssl.gyp:boringssl', ], + 'sources!': [ + 'rsa_private_key_nss_unittest.cc', + ], }, { 'sources!': [ 'openssl_bio_string_unittest.cc', diff --git a/crypto/crypto.gypi b/crypto/crypto.gypi index 73b3332..4456b10 100644 --- a/crypto/crypto.gypi +++ b/crypto/crypto.gypi @@ -67,8 +67,6 @@ 'p224_spake.cc', 'p224_spake.h', 'nss_crypto_module_delegate.h', - 'nss_key_util.cc', - 'nss_key_util.h', 'nss_util.cc', 'nss_util.h', 'nss_util_internal.h', diff --git a/crypto/nss_key_util.cc b/crypto/nss_key_util.cc deleted file mode 100644 index 260d3b6..0000000 --- a/crypto/nss_key_util.cc +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "crypto/nss_key_util.h" - -#include <cryptohi.h> -#include <keyhi.h> -#include <pk11pub.h> - -#include "base/logging.h" -#include "base/stl_util.h" -#include "crypto/nss_util.h" - -#if defined(USE_NSS_CERTS) -#include <secmod.h> -#include "crypto/nss_util_internal.h" -#endif - -namespace crypto { - -namespace { - -#if defined(USE_NSS_CERTS) - -struct PublicKeyInfoDeleter { - inline void operator()(CERTSubjectPublicKeyInfo* spki) { - SECKEY_DestroySubjectPublicKeyInfo(spki); - } -}; - -typedef scoped_ptr<CERTSubjectPublicKeyInfo, PublicKeyInfoDeleter> - ScopedPublicKeyInfo; - -// Decodes |input| as a SubjectPublicKeyInfo and returns a SECItem containing -// the CKA_ID of that public key or nullptr on error. -ScopedSECItem MakeIDFromSPKI(const std::vector<uint8_t> input) { - // First, decode and save the public key. - SECItem key_der; - key_der.type = siBuffer; - key_der.data = const_cast<unsigned char*>(vector_as_array(&input)); - key_der.len = input.size(); - - ScopedPublicKeyInfo spki(SECKEY_DecodeDERSubjectPublicKeyInfo(&key_der)); - if (!spki) - return nullptr; - - ScopedSECKEYPublicKey result(SECKEY_ExtractPublicKey(spki.get())); - if (!result) - return nullptr; - - // See pk11_MakeIDFromPublicKey from NSS. For now, only RSA keys are - // supported. - if (SECKEY_GetPublicKeyType(result.get()) != rsaKey) - return nullptr; - - return ScopedSECItem(PK11_MakeIDFromPubKey(&result->u.rsa.modulus)); -} - -#endif // defined(USE_NSS_CERTS) - -} // namespace - -bool GenerateRSAKeyPairNSS(PK11SlotInfo* slot, - uint16_t num_bits, - bool permanent, - ScopedSECKEYPublicKey* public_key, - ScopedSECKEYPrivateKey* private_key) { - PK11RSAGenParams param; - param.keySizeInBits = num_bits; - param.pe = 65537L; - SECKEYPublicKey* public_key_raw = nullptr; - private_key->reset(PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN, - ¶m, &public_key_raw, permanent, - permanent /* sensitive */, nullptr)); - if (!*private_key) - return false; - - public_key->reset(public_key_raw); - return true; -} - -ScopedSECKEYPrivateKey ImportNSSKeyFromPrivateKeyInfo( - PK11SlotInfo* slot, - const std::vector<uint8_t>& input, - bool permanent) { - DCHECK(slot); - - ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); - DCHECK(arena); - - // Excess data is illegal, but NSS silently accepts it, so first ensure that - // |input| consists of a single ASN.1 element. - SECItem input_item; - input_item.data = const_cast<unsigned char*>(vector_as_array(&input)); - input_item.len = input.size(); - SECItem der_private_key_info; - SECStatus rv = - SEC_QuickDERDecodeItem(arena.get(), &der_private_key_info, - SEC_ASN1_GET(SEC_AnyTemplate), &input_item); - if (rv != SECSuccess) - return nullptr; - - // Allow the private key to be used for key unwrapping, data decryption, - // and signature generation. - const unsigned int key_usage = - KU_KEY_ENCIPHERMENT | KU_DATA_ENCIPHERMENT | KU_DIGITAL_SIGNATURE; - SECKEYPrivateKey* key_raw = nullptr; - rv = PK11_ImportDERPrivateKeyInfoAndReturnKey( - slot, &der_private_key_info, nullptr, nullptr, permanent, - permanent /* sensitive */, key_usage, &key_raw, nullptr); - if (rv != SECSuccess) - return nullptr; - return ScopedSECKEYPrivateKey(key_raw); -} - -#if defined(USE_NSS_CERTS) - -ScopedSECKEYPrivateKey FindNSSKeyFromPublicKeyInfo( - const std::vector<uint8_t>& input) { - EnsureNSSInit(); - - ScopedSECItem cka_id(MakeIDFromSPKI(input)); - if (!cka_id) - return nullptr; - - // Search all slots in all modules for the key with the given ID. - AutoSECMODListReadLock auto_lock; - const SECMODModuleList* head = SECMOD_GetDefaultModuleList(); - for (const SECMODModuleList* item = head; item != nullptr; - item = item->next) { - int slot_count = item->module->loaded ? item->module->slotCount : 0; - for (int i = 0; i < slot_count; i++) { - // Look for the key in slot |i|. - ScopedSECKEYPrivateKey key( - PK11_FindKeyByKeyID(item->module->slots[i], cka_id.get(), nullptr)); - if (key) - return key.Pass(); - } - } - - // The key wasn't found in any module. - return nullptr; -} - -ScopedSECKEYPrivateKey FindNSSKeyFromPublicKeyInfoInSlot( - const std::vector<uint8_t>& input, - PK11SlotInfo* slot) { - DCHECK(slot); - - ScopedSECItem cka_id(MakeIDFromSPKI(input)); - if (!cka_id) - return nullptr; - - return ScopedSECKEYPrivateKey( - PK11_FindKeyByKeyID(slot, cka_id.get(), nullptr)); -} - -#endif // defined(USE_NSS_CERTS) - -} // namespace crypto diff --git a/crypto/nss_key_util.h b/crypto/nss_key_util.h deleted file mode 100644 index 12b948d..0000000 --- a/crypto/nss_key_util.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CRYPTO_NSS_KEY_UTIL_H_ -#define CRYPTO_NSS_KEY_UTIL_H_ - -#include <stdint.h> - -#include <vector> - -#include "build/build_config.h" -#include "crypto/crypto_export.h" -#include "crypto/scoped_nss_types.h" - -typedef struct PK11SlotInfoStr PK11SlotInfo; - -namespace crypto { - -// Generates a new RSA keypair of size |num_bits| in |slot|. Returns true on -// success and false on failure. If |permanent| is true, the resulting key is -// permanent and is not exportable in plaintext form. -CRYPTO_EXPORT bool GenerateRSAKeyPairNSS( - PK11SlotInfo* slot, - uint16_t num_bits, - bool permanent, - ScopedSECKEYPublicKey* out_public_key, - ScopedSECKEYPrivateKey* out_private_key); - -// Imports a private key from |input| into |slot|. |input| is interpreted as a -// DER-encoded PrivateKeyInfo block from PKCS #8. Returns nullptr on error. If -// |permanent| is true, the resulting key is permanent and is not exportable in -// plaintext form. -CRYPTO_EXPORT ScopedSECKEYPrivateKey -ImportNSSKeyFromPrivateKeyInfo(PK11SlotInfo* slot, - const std::vector<uint8_t>& input, - bool permanent); - -#if defined(USE_NSS_CERTS) - -// Decodes |input| as a DER-encoded X.509 SubjectPublicKeyInfo and searches for -// the private key half in the key database. Returns the private key on success -// or nullptr on error. -CRYPTO_EXPORT ScopedSECKEYPrivateKey -FindNSSKeyFromPublicKeyInfo(const std::vector<uint8_t>& input); - -// Decodes |input| as a DER-encoded X.509 SubjectPublicKeyInfo and searches for -// the private key half in the slot specified by |slot|. Returns the private key -// on success or nullptr on error. -CRYPTO_EXPORT ScopedSECKEYPrivateKey -FindNSSKeyFromPublicKeyInfoInSlot(const std::vector<uint8_t>& input, - PK11SlotInfo* slot); - -#endif // defined(USE_NSS_CERTS) - -} // namespace crypto - -#endif // CRYPTO_NSS_KEY_UTIL_H_ diff --git a/crypto/nss_key_util_unittest.cc b/crypto/nss_key_util_unittest.cc deleted file mode 100644 index f8de8e2..0000000 --- a/crypto/nss_key_util_unittest.cc +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "crypto/nss_key_util.h" - -#include <keyhi.h> -#include <pk11pub.h> - -#include <vector> - -#include "crypto/nss_util.h" -#include "crypto/scoped_nss_types.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace crypto { - -class NSSKeyUtilTest : public testing::Test { - public: - void SetUp() override { - EnsureNSSInit(); - - internal_slot_.reset(PK11_GetInternalSlot()); - ASSERT_TRUE(internal_slot_); - } - - PK11SlotInfo* internal_slot() { return internal_slot_.get(); } - - private: - ScopedPK11Slot internal_slot_; -}; - -TEST_F(NSSKeyUtilTest, GenerateRSAKeyPairNSS) { - const int kKeySizeBits = 1024; - - ScopedSECKEYPublicKey public_key; - ScopedSECKEYPrivateKey private_key; - ASSERT_TRUE(GenerateRSAKeyPairNSS(internal_slot(), kKeySizeBits, - false /* not permanent */, &public_key, - &private_key)); - - EXPECT_EQ(rsaKey, SECKEY_GetPublicKeyType(public_key.get())); - EXPECT_EQ(rsaKey, SECKEY_GetPrivateKeyType(private_key.get())); - EXPECT_EQ((kKeySizeBits + 7) / 8, - PK11_GetPrivateModulusLen(private_key.get())); -} - -#if defined(USE_NSS_CERTS) -TEST_F(NSSKeyUtilTest, FindNSSKeyFromPublicKeyInfo) { - // Create an NSS keypair, which will put the keys in the user's NSSDB. - ScopedSECKEYPublicKey public_key; - ScopedSECKEYPrivateKey private_key; - ASSERT_TRUE(GenerateRSAKeyPairNSS(internal_slot(), 256, - false /* not permanent */, &public_key, - &private_key)); - - ScopedSECItem item(SECKEY_EncodeDERSubjectPublicKeyInfo(public_key.get())); - ASSERT_TRUE(item); - std::vector<uint8_t> public_key_der(item->data, item->data + item->len); - - ScopedSECKEYPrivateKey private_key2 = - FindNSSKeyFromPublicKeyInfo(public_key_der); - ASSERT_TRUE(private_key2); - EXPECT_EQ(private_key->pkcs11ID, private_key2->pkcs11ID); -} - -TEST_F(NSSKeyUtilTest, FailedFindNSSKeyFromPublicKeyInfo) { - // Create an NSS keypair, which will put the keys in the user's NSSDB. - ScopedSECKEYPublicKey public_key; - ScopedSECKEYPrivateKey private_key; - ASSERT_TRUE(GenerateRSAKeyPairNSS(internal_slot(), 256, - false /* not permanent */, &public_key, - &private_key)); - - ScopedSECItem item(SECKEY_EncodeDERSubjectPublicKeyInfo(public_key.get())); - ASSERT_TRUE(item); - std::vector<uint8_t> public_key_der(item->data, item->data + item->len); - - // Remove the keys from the DB, and make sure we can't find them again. - PK11_DestroyTokenObject(private_key->pkcs11Slot, private_key->pkcs11ID); - PK11_DestroyTokenObject(public_key->pkcs11Slot, public_key->pkcs11ID); - - EXPECT_FALSE(FindNSSKeyFromPublicKeyInfo(public_key_der)); -} -#endif // defined(USE_NSS_CERTS) - -} // namespace crypto diff --git a/crypto/rsa_private_key.h b/crypto/rsa_private_key.h index 637be38..9ab9c57 100644 --- a/crypto/rsa_private_key.h +++ b/crypto/rsa_private_key.h @@ -191,6 +191,44 @@ class CRYPTO_EXPORT RSAPrivateKey { static RSAPrivateKey* CreateFromKey(SECKEYPrivateKey* key); #endif + // TODO(davidben): These functions are used when NSS is the platform key + // store, but they also assume that the internal crypto library is NSS. Split + // out the convenience NSS platform key methods from the logic which expects + // an RSAPrivateKey. See https://crbug.com/478777. +#if defined(USE_NSS_CERTS) && !defined(USE_OPENSSL) + // Create a new random instance in |slot|. Can return NULL if initialization + // fails. The created key is permanent and is not exportable in plaintext + // form. + static RSAPrivateKey* CreateSensitive(PK11SlotInfo* slot, uint16 num_bits); + + // Create a new instance in |slot| by importing an existing private key. The + // format is an ASN.1-encoded PrivateKeyInfo block from PKCS #8. This can + // return NULL if initialization fails. + // The created key is permanent and is not exportable in plaintext form. + static RSAPrivateKey* CreateSensitiveFromPrivateKeyInfo( + PK11SlotInfo* slot, + const std::vector<uint8>& input); + + // Import an existing public key, and then search for the private + // half in the key database. The format of the public key blob is is + // an X509 SubjectPublicKeyInfo block. This can return NULL if + // initialization fails or the private key cannot be found. The + // caller takes ownership of the returned object, but nothing new is + // created in the key database. + static RSAPrivateKey* FindFromPublicKeyInfo( + const std::vector<uint8>& input); + + // Import an existing public key, and then search for the private + // half in the slot specified by |slot|. The format of the public + // key blob is is an X509 SubjectPublicKeyInfo block. This can return + // NULL if initialization fails or the private key cannot be found. + // The caller takes ownership of the returned object, but nothing new + // is created in the slot. + static RSAPrivateKey* FindFromPublicKeyInfoInSlot( + const std::vector<uint8>& input, + PK11SlotInfo* slot); +#endif // USE_NSS_CERTS && !USE_OPENSSL + #if defined(USE_OPENSSL) EVP_PKEY* key() { return key_; } #else @@ -213,9 +251,38 @@ class CRYPTO_EXPORT RSAPrivateKey { FRIEND_TEST_ALL_PREFIXES(RSAPrivateKeyNSSTest, FailedFindFromPublicKey); #endif - // Constructor is private. Use one of the Create*() methods above instead. + // Constructor is private. Use one of the Create*() or Find*() + // methods above instead. RSAPrivateKey(); +#if !defined(USE_OPENSSL) + // Shared helper for Create() and CreateSensitive(). + // TODO(cmasone): consider replacing |permanent| and |sensitive| with a + // flags arg created by ORing together some enumerated values. + // Note: |permanent| is only supported when USE_NSS_CERTS is defined. + static RSAPrivateKey* CreateWithParams(PK11SlotInfo* slot, + uint16 num_bits, + bool permanent, + bool sensitive); + + // Shared helper for CreateFromPrivateKeyInfo() and + // CreateSensitiveFromPrivateKeyInfo(). + // Note: |permanent| is only supported when USE_NSS_CERTS is defined. + static RSAPrivateKey* CreateFromPrivateKeyInfoWithParams( + PK11SlotInfo* slot, + const std::vector<uint8>& input, + bool permanent, + bool sensitive); +#endif + +#if defined(USE_NSS_CERTS) + // Import an existing public key. The format of the public key blob + // is an X509 SubjectPublicKeyInfo block. This can return NULL if + // initialization fails. The caller takes ownership of the returned + // object. Note that this method doesn't initialize the |key_| member. + static RSAPrivateKey* InitPublicPart(const std::vector<uint8>& input); +#endif + #if defined(USE_OPENSSL) EVP_PKEY* key_; #else diff --git a/crypto/rsa_private_key_nss.cc b/crypto/rsa_private_key_nss.cc index 88e55fa..c9e6a87 100644 --- a/crypto/rsa_private_key_nss.cc +++ b/crypto/rsa_private_key_nss.cc @@ -7,6 +7,7 @@ #include <cryptohi.h> #include <keyhi.h> #include <pk11pub.h> +#include <secmod.h> #include <list> @@ -14,8 +15,8 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/strings/string_util.h" -#include "crypto/nss_key_util.h" #include "crypto/nss_util.h" +#include "crypto/nss_util_internal.h" #include "crypto/scoped_nss_types.h" // TODO(rafaelw): Consider using NSS's ASN.1 encoder. @@ -37,6 +38,37 @@ static bool ReadAttribute(SECKEYPrivateKey* key, return true; } +#if defined(USE_NSS_CERTS) +struct PublicKeyInfoDeleter { + inline void operator()(CERTSubjectPublicKeyInfo* spki) { + SECKEY_DestroySubjectPublicKeyInfo(spki); + } +}; + +typedef scoped_ptr<CERTSubjectPublicKeyInfo, PublicKeyInfoDeleter> + ScopedPublicKeyInfo; + +// The function decodes RSA public key from the |input|. +crypto::ScopedSECKEYPublicKey GetRSAPublicKey(const std::vector<uint8>& input) { + // First, decode and save the public key. + SECItem key_der; + key_der.type = siBuffer; + key_der.data = const_cast<unsigned char*>(&input[0]); + key_der.len = input.size(); + + ScopedPublicKeyInfo spki(SECKEY_DecodeDERSubjectPublicKeyInfo(&key_der)); + if (!spki) + return crypto::ScopedSECKEYPublicKey(); + + crypto::ScopedSECKEYPublicKey result(SECKEY_ExtractPublicKey(spki.get())); + + // Make sure the key is an RSA key.. If not, that's an error. + if (!result || result->keyType != rsaKey) + return crypto::ScopedSECKEYPublicKey(); + return result.Pass(); +} +#endif // defined(USE_NSS_CERTS) + } // namespace namespace crypto { @@ -53,22 +85,10 @@ RSAPrivateKey* RSAPrivateKey::Create(uint16 num_bits) { EnsureNSSInit(); ScopedPK11Slot slot(PK11_GetInternalSlot()); - if (!slot) { - NOTREACHED(); - return nullptr; - } - - ScopedSECKEYPublicKey public_key; - ScopedSECKEYPrivateKey private_key; - if (!GenerateRSAKeyPairNSS(slot.get(), num_bits, false /* not permanent */, - &public_key, &private_key)) { - return nullptr; - } - - RSAPrivateKey* rsa_key = new RSAPrivateKey; - rsa_key->public_key_ = public_key.release(); - rsa_key->key_ = private_key.release(); - return rsa_key; + return CreateWithParams(slot.get(), + num_bits, + false /* not permanent */, + false /* not sensitive */); } // static @@ -77,15 +97,11 @@ RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfo( EnsureNSSInit(); ScopedPK11Slot slot(PK11_GetInternalSlot()); - if (!slot) { - NOTREACHED(); - return nullptr; - } - ScopedSECKEYPrivateKey key(ImportNSSKeyFromPrivateKeyInfo( - slot.get(), input, false /* not permanent */)); - if (!key || SECKEY_GetPrivateKeyType(key.get()) != rsaKey) - return nullptr; - return RSAPrivateKey::CreateFromKey(key.get()); + return CreateFromPrivateKeyInfoWithParams( + slot.get(), + input, + false /* not permanent */, + false /* not sensitive */); } // static @@ -104,6 +120,83 @@ RSAPrivateKey* RSAPrivateKey::CreateFromKey(SECKEYPrivateKey* key) { return copy; } +#if defined(USE_NSS_CERTS) +// static +RSAPrivateKey* RSAPrivateKey::CreateSensitive(PK11SlotInfo* slot, + uint16 num_bits) { + return CreateWithParams(slot, + num_bits, + true /* permanent */, + true /* sensitive */); +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateSensitiveFromPrivateKeyInfo( + PK11SlotInfo* slot, + const std::vector<uint8>& input) { + return CreateFromPrivateKeyInfoWithParams(slot, + input, + true /* permanent */, + true /* sensitive */); +} + +// static +RSAPrivateKey* RSAPrivateKey::FindFromPublicKeyInfo( + const std::vector<uint8>& input) { + scoped_ptr<RSAPrivateKey> result(InitPublicPart(input)); + if (!result) + return NULL; + + ScopedSECItem ck_id( + PK11_MakeIDFromPubKey(&(result->public_key_->u.rsa.modulus))); + if (!ck_id.get()) { + NOTREACHED(); + return NULL; + } + + // Search all slots in all modules for the key with the given ID. + AutoSECMODListReadLock auto_lock; + SECMODModuleList* head = SECMOD_GetDefaultModuleList(); + for (SECMODModuleList* item = head; item != NULL; item = item->next) { + int slot_count = item->module->loaded ? item->module->slotCount : 0; + for (int i = 0; i < slot_count; i++) { + // Finally...Look for the key! + result->key_ = PK11_FindKeyByKeyID(item->module->slots[i], + ck_id.get(), NULL); + if (result->key_) + return result.release(); + } + } + + // We didn't find the key. + return NULL; +} + +// static +RSAPrivateKey* RSAPrivateKey::FindFromPublicKeyInfoInSlot( + const std::vector<uint8>& input, + PK11SlotInfo* slot) { + if (!slot) + return NULL; + + scoped_ptr<RSAPrivateKey> result(InitPublicPart(input)); + if (!result) + return NULL; + + ScopedSECItem ck_id( + PK11_MakeIDFromPubKey(&(result->public_key_->u.rsa.modulus))); + if (!ck_id.get()) { + NOTREACHED(); + return NULL; + } + + result->key_ = PK11_FindKeyByKeyID(slot, ck_id.get(), NULL); + if (!result->key_) + return NULL; + return result.release(); +} +#endif + RSAPrivateKey* RSAPrivateKey::Copy() const { RSAPrivateKey* copy = new RSAPrivateKey(); copy->key_ = SECKEY_CopyPrivateKey(key_); @@ -148,4 +241,92 @@ RSAPrivateKey::RSAPrivateKey() : key_(NULL), public_key_(NULL) { EnsureNSSInit(); } +// static +RSAPrivateKey* RSAPrivateKey::CreateWithParams(PK11SlotInfo* slot, + uint16 num_bits, + bool permanent, + bool sensitive) { + if (!slot) + return NULL; + + scoped_ptr<RSAPrivateKey> result(new RSAPrivateKey); + + PK11RSAGenParams param; + param.keySizeInBits = num_bits; + param.pe = 65537L; + result->key_ = PK11_GenerateKeyPair(slot, + CKM_RSA_PKCS_KEY_PAIR_GEN, + ¶m, + &result->public_key_, + permanent, + sensitive, + NULL); + if (!result->key_) + return NULL; + + return result.release(); +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfoWithParams( + PK11SlotInfo* slot, + const std::vector<uint8>& input, + bool permanent, + bool sensitive) { + if (!slot) + return NULL; + + scoped_ptr<RSAPrivateKey> result(new RSAPrivateKey); + + ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); + if (!arena) { + NOTREACHED(); + return NULL; + } + + // Excess data is illegal, but NSS silently accepts it, so first ensure that + // |input| consists of a single ASN.1 element. + SECItem input_item; + input_item.data = const_cast<unsigned char*>(&input.front()); + input_item.len = input.size(); + SECItem der_private_key_info; + SECStatus rv = SEC_QuickDERDecodeItem(arena.get(), &der_private_key_info, + SEC_ASN1_GET(SEC_AnyTemplate), + &input_item); + if (rv != SECSuccess) + return NULL; + + // Allow the private key to be used for key unwrapping, data decryption, + // and signature generation. + const unsigned int key_usage = KU_KEY_ENCIPHERMENT | KU_DATA_ENCIPHERMENT | + KU_DIGITAL_SIGNATURE; + rv = PK11_ImportDERPrivateKeyInfoAndReturnKey( + slot, &der_private_key_info, NULL, NULL, permanent, sensitive, + key_usage, &result->key_, NULL); + if (rv != SECSuccess) + return NULL; + + result->public_key_ = SECKEY_ConvertToPublicKey(result->key_); + if (!result->public_key_) + return NULL; + + return result.release(); +} + +#if defined(USE_NSS_CERTS) +// static +RSAPrivateKey* RSAPrivateKey::InitPublicPart(const std::vector<uint8>& input) { + EnsureNSSInit(); + + scoped_ptr<RSAPrivateKey> result(new RSAPrivateKey()); + result->public_key_ = GetRSAPublicKey(input).release(); + if (!result->public_key_) { + NOTREACHED(); + return NULL; + } + + return result.release(); +} +#endif // defined(USE_NSS_CERTS) + } // namespace crypto diff --git a/crypto/rsa_private_key_nss_unittest.cc b/crypto/rsa_private_key_nss_unittest.cc new file mode 100644 index 0000000..dad6688 --- /dev/null +++ b/crypto/rsa_private_key_nss_unittest.cc @@ -0,0 +1,66 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/rsa_private_key.h" + +#include <keyhi.h> +#include <pk11pub.h> + +#include "base/memory/scoped_ptr.h" +#include "crypto/scoped_test_nss_db.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace crypto { + +// TODO(davidben): These tests assume NSS is used for both the internal crypto +// library and the platform key store. See https://crbug.com/478777. +#if defined(USE_NSS_CERTS) + +class RSAPrivateKeyNSSTest : public testing::Test { + public: + RSAPrivateKeyNSSTest() {} + ~RSAPrivateKeyNSSTest() override {} + + private: + ScopedTestNSSDB test_nssdb_; + + DISALLOW_COPY_AND_ASSIGN(RSAPrivateKeyNSSTest); +}; + +TEST_F(RSAPrivateKeyNSSTest, FindFromPublicKey) { + // Create a keypair, which will put the keys in the user's NSSDB. + scoped_ptr<crypto::RSAPrivateKey> key_pair(RSAPrivateKey::Create(256)); + + std::vector<uint8> public_key; + ASSERT_TRUE(key_pair->ExportPublicKey(&public_key)); + + scoped_ptr<crypto::RSAPrivateKey> key_pair_2( + crypto::RSAPrivateKey::FindFromPublicKeyInfo(public_key)); + + EXPECT_EQ(key_pair->key_->pkcs11ID, key_pair_2->key_->pkcs11ID); +} + +TEST_F(RSAPrivateKeyNSSTest, FailedFindFromPublicKey) { + // Create a keypair, which will put the keys in the user's NSSDB. + scoped_ptr<crypto::RSAPrivateKey> key_pair(RSAPrivateKey::Create(256)); + + std::vector<uint8> public_key; + ASSERT_TRUE(key_pair->ExportPublicKey(&public_key)); + + // Remove the keys from the DB, and make sure we can't find them again. + if (key_pair->key_) { + PK11_DestroyTokenObject(key_pair->key_->pkcs11Slot, + key_pair->key_->pkcs11ID); + } + if (key_pair->public_key_) { + PK11_DestroyTokenObject(key_pair->public_key_->pkcs11Slot, + key_pair->public_key_->pkcs11ID); + } + + EXPECT_EQ(NULL, crypto::RSAPrivateKey::FindFromPublicKeyInfo(public_key)); +} + +#endif // USE_NSS_CERTS + +} // namespace crypto |