tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }