NSSKeyStore.cpp (6988B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "NSSKeyStore.h" 8 9 #include "ScopedNSSTypes.h" 10 #include "mozilla/AbstractThread.h" 11 #include "mozilla/Base64.h" 12 #include "mozilla/Logging.h" 13 #include "mozilla/SyncRunnable.h" 14 #include "nsIThread.h" 15 #include "nsNSSComponent.h" 16 #include "nsPK11TokenDB.h" 17 #include "nsXULAppAPI.h" 18 19 /* Implementing OSKeyStore when there is no platform specific one. 20 * This key store instead puts the keys into the NSS DB. 21 */ 22 23 using namespace mozilla; 24 using mozilla::SyncRunnable; 25 26 LazyLogModule gNSSKeyStoreLog("nsskeystore"); 27 28 NSSKeyStore::NSSKeyStore() { 29 MOZ_ASSERT(XRE_IsParentProcess()); 30 if (!XRE_IsParentProcess()) { 31 // This shouldn't happen as this is only initialised when creating the 32 // OSKeyStore, which is ParentProcessOnly. 33 return; 34 } 35 (void)EnsureNSSInitializedChromeOrContent(); 36 (void)InitToken(); 37 } 38 NSSKeyStore::~NSSKeyStore() = default; 39 40 nsresult NSSKeyStore::InitToken() { 41 if (!mSlot) { 42 mSlot = UniquePK11SlotInfo(PK11_GetInternalKeySlot()); 43 if (!mSlot) { 44 MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug, 45 ("Error getting internal key slot")); 46 return NS_ERROR_NOT_AVAILABLE; 47 } 48 } 49 return NS_OK; 50 } 51 52 nsresult NSSKeyStore::StoreSecret(const nsACString& aSecret, 53 const nsACString& aLabel) { 54 NS_ENSURE_STATE(mSlot); 55 56 // It is possible for multiple keys to have the same nickname in NSS. To 57 // prevent the problem of not knowing which key to use in the future, simply 58 // delete all keys with this nickname before storing a new one. 59 nsresult rv = DeleteSecret(aLabel); 60 if (NS_FAILED(rv)) { 61 MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug, 62 ("DeleteSecret before StoreSecret failed")); 63 return rv; 64 } 65 66 uint8_t* p = BitwiseCast<uint8_t*, const char*>(aSecret.BeginReading()); 67 UniqueSECItem key(SECITEM_AllocItem(nullptr, nullptr, aSecret.Length())); 68 if (!key) { 69 return NS_ERROR_OUT_OF_MEMORY; 70 } 71 key->type = siBuffer; 72 memcpy(key->data, p, aSecret.Length()); 73 key->len = aSecret.Length(); 74 UniquePK11SymKey symKey( 75 PK11_ImportSymKey(mSlot.get(), CKM_AES_GCM, PK11_OriginUnwrap, 76 CKA_DECRYPT | CKA_ENCRYPT, key.get(), nullptr)); 77 if (!symKey) { 78 MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug, ("Error creating NSS SymKey")); 79 return NS_ERROR_FAILURE; 80 } 81 UniquePK11SymKey storedKey( 82 PK11_ConvertSessionSymKeyToTokenSymKey(symKey.get(), nullptr)); 83 if (!storedKey) { 84 MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug, 85 ("Error storing NSS SymKey in DB")); 86 return NS_ERROR_FAILURE; 87 } 88 SECStatus srv = 89 PK11_SetSymKeyNickname(storedKey.get(), PromiseFlatCString(aLabel).get()); 90 if (srv != SECSuccess) { 91 MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug, ("Error naming NSS SymKey")); 92 (void)PK11_DeleteTokenSymKey(storedKey.get()); 93 return NS_ERROR_FAILURE; 94 } 95 96 return NS_OK; 97 } 98 99 nsresult NSSKeyStore::DeleteSecret(const nsACString& aLabel) { 100 NS_ENSURE_STATE(mSlot); 101 102 UniquePK11SymKey symKey(PK11_ListFixedKeysInSlot( 103 mSlot.get(), const_cast<char*>(PromiseFlatCString(aLabel).get()), 104 nullptr)); 105 if (!symKey) { 106 // Couldn't find the key or something is wrong. Be nice. 107 return NS_OK; 108 } 109 for (PK11SymKey* tmp = symKey.get(); tmp; tmp = PK11_GetNextSymKey(tmp)) { 110 SECStatus srv = PK11_DeleteTokenSymKey(tmp); 111 if (srv != SECSuccess) { 112 MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug, ("Error deleting NSS SymKey")); 113 return NS_ERROR_FAILURE; 114 } 115 } 116 return NS_OK; 117 } 118 119 nsresult NSSKeyStore::SecretAvailable(const nsACString& aLabel) { 120 if (!mSlot) { 121 return NS_ERROR_NOT_AVAILABLE; 122 } 123 124 UniquePK11SymKey symKey(PK11_ListFixedKeysInSlot( 125 mSlot.get(), const_cast<char*>(PromiseFlatCString(aLabel).get()), 126 nullptr)); 127 if (!symKey) { 128 return NS_ERROR_NOT_AVAILABLE; 129 } 130 return NS_OK; 131 } 132 133 nsresult NSSKeyStore::EncryptDecrypt(const nsACString& aLabel, 134 const std::vector<uint8_t>& inBytes, 135 std::vector<uint8_t>& outBytes, 136 bool encrypt) { 137 NS_ENSURE_STATE(mSlot); 138 139 UniquePK11SymKey symKey(PK11_ListFixedKeysInSlot( 140 mSlot.get(), const_cast<char*>(PromiseFlatCString(aLabel).get()), 141 nullptr)); 142 if (!symKey) { 143 MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug, 144 ("Error finding key for given label")); 145 return NS_ERROR_FAILURE; 146 } 147 return DoCipher(symKey, inBytes, outBytes, encrypt); 148 } 149 150 nsresult NSSKeyStore::RetrieveSecret(const nsACString& aLabel, 151 /* out */ nsACString& aSecret) { 152 NS_ENSURE_STATE(mSlot); 153 154 UniquePK11SymKey symKey(PK11_ListFixedKeysInSlot( 155 mSlot.get(), const_cast<char*>(PromiseFlatCString(aLabel).get()), 156 nullptr)); 157 if (!symKey) { 158 MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug, 159 ("Error finding key for given label")); 160 return NS_ERROR_FAILURE; 161 } 162 163 // Unfortunately we can't use PK11_ExtractKeyValue(symKey.get()) here because 164 // softoken marks all token objects of type CKO_SECRET_KEY as sensitive. So 165 // we have to wrap and unwrap symKey to obtain a non-sensitive copy of symKey 166 // as a session object. 167 168 const CK_MECHANISM_TYPE mechanism = CKM_AES_KEY_WRAP_KWP; 169 170 UniquePK11SymKey wrappingKey( 171 PK11_KeyGen(mSlot.get(), CKM_AES_KEY_GEN, nullptr, 16, nullptr)); 172 if (!wrappingKey) { 173 return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); 174 } 175 176 SECItem wrapLen = {siBuffer, nullptr, 0}; 177 nsresult rv = MapSECStatus(PK11_WrapSymKey( 178 mechanism, nullptr, wrappingKey.get(), symKey.get(), &wrapLen)); 179 NS_ENSURE_SUCCESS(rv, rv); 180 181 if (wrapLen.len > INT_MAX - 8) { /* PK11_UnwrapSymKey takes an int keySize */ 182 return NS_ERROR_FAILURE; 183 } 184 185 // Allocate an extra 8 bytes for CKM_AES_KEY_WRAP_KWP overhead. 186 UniqueSECItem wrappedKey( 187 SECITEM_AllocItem(nullptr, nullptr, wrapLen.len + 8)); 188 if (!wrappedKey) { 189 return NS_ERROR_FAILURE; 190 } 191 192 rv = MapSECStatus(PK11_WrapSymKey(mechanism, nullptr, wrappingKey.get(), 193 symKey.get(), wrappedKey.get())); 194 NS_ENSURE_SUCCESS(rv, rv); 195 196 UniquePK11SymKey unwrappedKey(PK11_UnwrapSymKey( 197 wrappingKey.get(), mechanism, nullptr, wrappedKey.get(), CKM_AES_GCM, 198 CKA_DECRYPT, static_cast<int>(wrapLen.len))); 199 if (!unwrappedKey) { 200 return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); 201 } 202 203 rv = MapSECStatus(PK11_ExtractKeyValue(unwrappedKey.get())); 204 NS_ENSURE_SUCCESS(rv, rv); 205 206 const SECItem* keyData = PK11_GetKeyData(unwrappedKey.get()); /* weak ref */ 207 if (!keyData) { 208 return NS_ERROR_FAILURE; 209 } 210 211 aSecret.Assign(reinterpret_cast<char*>(keyData->data), keyData->len); 212 return NS_OK; 213 }