| // 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 <secmod.h> | 
 | #include <stdint.h> | 
 |  | 
 | #include <memory> | 
 |  | 
 | #include "base/logging.h" | 
 | #include "crypto/nss_util.h" | 
 | #include "crypto/nss_util_internal.h" | 
 |  | 
 | namespace crypto { | 
 |  | 
 | namespace { | 
 |  | 
 | struct PublicKeyInfoDeleter { | 
 |   inline void operator()(CERTSubjectPublicKeyInfo* spki) { | 
 |     SECKEY_DestroySubjectPublicKeyInfo(spki); | 
 |   } | 
 | }; | 
 |  | 
 | typedef std::unique_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(base::span<const uint8_t> input) { | 
 |   // First, decode and save the public key. | 
 |   SECItem key_der; | 
 |   key_der.type = siBuffer; | 
 |   key_der.data = const_cast<unsigned char*>(input.data()); | 
 |   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 and EC keys are | 
 |   // supported. | 
 |   if (SECKEY_GetPublicKeyType(result.get()) == rsaKey) | 
 |     return ScopedSECItem(PK11_MakeIDFromPubKey(&result->u.rsa.modulus)); | 
 |   if (SECKEY_GetPublicKeyType(result.get()) == ecKey) | 
 |     return ScopedSECItem(PK11_MakeIDFromPubKey(&result->u.ec.publicValue)); | 
 |   return nullptr; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | bool GenerateRSAKeyPairNSS(PK11SlotInfo* slot, | 
 |                            uint16_t num_bits, | 
 |                            bool permanent, | 
 |                            ScopedSECKEYPublicKey* public_key, | 
 |                            ScopedSECKEYPrivateKey* private_key) { | 
 |   DCHECK(slot); | 
 |  | 
 |   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; | 
 | } | 
 |  | 
 | bool GenerateECKeyPairNSS(PK11SlotInfo* slot, | 
 |                           const SECOidTag named_curve, | 
 |                           bool permanent, | 
 |                           ScopedSECKEYPublicKey* public_key, | 
 |                           ScopedSECKEYPrivateKey* private_key) { | 
 |   DCHECK(slot); | 
 |  | 
 |   if (named_curve != SEC_OID_ANSIX962_EC_PRIME256V1) { | 
 |     LOG(ERROR) << "SECOidTag: " << named_curve | 
 |                << " is not supported. Only SEC_OID_ANSIX962_EC_PRIME256V1 is " | 
 |                   "supported for elliptic curve key pair generation."; | 
 |     return false; | 
 |   } | 
 |  | 
 |   SECOidData* oid_data = SECOID_FindOIDByTag(named_curve); | 
 |   if (!oid_data) { | 
 |     LOG(ERROR) << "SECOID_FindOIDByTag: " << PORT_GetError(); | 
 |     return false; | 
 |   } | 
 |  | 
 |   std::vector<uint8_t> parameters_buf(2 + oid_data->oid.len); | 
 |   SECKEYECParams ec_parameters = {siDEROID, parameters_buf.data(), | 
 |                                   static_cast<unsigned>(parameters_buf.size())}; | 
 |  | 
 |   ec_parameters.data[0] = SEC_ASN1_OBJECT_ID; | 
 |   ec_parameters.data[1] = oid_data->oid.len; | 
 |   memcpy(ec_parameters.data + 2, oid_data->oid.data, oid_data->oid.len); | 
 |   SECKEYPublicKey* public_key_raw = nullptr; | 
 |   private_key->reset(PK11_GenerateKeyPair(slot, CKM_EC_KEY_PAIR_GEN, | 
 |                                           &ec_parameters, &public_key_raw, | 
 |                                           permanent, permanent, 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*>(input.data()); | 
 |   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); | 
 | } | 
 |  | 
 | ScopedSECKEYPrivateKey FindNSSKeyFromPublicKeyInfo( | 
 |     base::span<const 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; | 
 |     } | 
 |   } | 
 |  | 
 |   // The key wasn't found in any module. | 
 |   return nullptr; | 
 | } | 
 |  | 
 | ScopedSECKEYPrivateKey FindNSSKeyFromPublicKeyInfoInSlot( | 
 |     base::span<const 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)); | 
 | } | 
 |  | 
 | }  // namespace crypto |