tor-browser

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

OSKeyStore.cpp (23467B)


      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 "OSKeyStore.h"
      8 
      9 #include "mozilla/Base64.h"
     10 #include "mozilla/dom/Promise.h"
     11 #include "nsThreadUtils.h"
     12 #include "nsXPCOM.h"
     13 #include "pk11pub.h"
     14 
     15 #if defined(XP_MACOSX)
     16 #  include "KeychainSecret.h"
     17 #elif defined(XP_WIN)
     18 #  include "CredentialManagerSecret.h"
     19 #elif defined(MOZ_WIDGET_GTK)
     20 #  include "LibSecret.h"
     21 #  include "NSSKeyStore.h"
     22 #else
     23 #  include "NSSKeyStore.h"
     24 #endif
     25 
     26 NS_IMPL_ISUPPORTS(OSKeyStore, nsIOSKeyStore)
     27 
     28 using namespace mozilla;
     29 using dom::Promise;
     30 
     31 OSKeyStore::OSKeyStore() : mKs(nullptr) {
     32  MOZ_ASSERT(NS_IsMainThread());
     33  if (NS_WARN_IF(!NS_IsMainThread())) {
     34    return;
     35  }
     36 
     37 #if defined(XP_MACOSX)
     38  mKs.reset(new KeychainSecret());
     39 #elif defined(XP_WIN)
     40  mKs.reset(new CredentialManagerSecret());
     41 #elif defined(MOZ_WIDGET_GTK)
     42  if (NS_SUCCEEDED(MaybeLoadLibSecret())) {
     43    mKs.reset(new LibSecret());
     44  } else {
     45    mKs.reset(new NSSKeyStore());
     46  }
     47 #else
     48  mKs.reset(new NSSKeyStore());
     49 #endif
     50 
     51  (void)NS_CreateBackgroundTaskQueue(
     52      "OSKeyStore", getter_AddRefs(mBackgroundSerialEventTarget));
     53 }
     54 
     55 static nsresult GenerateRandom(std::vector<uint8_t>& r) {
     56  if (r.empty()) {
     57    return NS_ERROR_INVALID_ARG;
     58  }
     59  UniquePK11SlotInfo slot(PK11_GetInternalSlot());
     60  if (!slot) {
     61    return NS_ERROR_FAILURE;
     62  }
     63 
     64  SECStatus srv = PK11_GenerateRandomOnSlot(slot.get(), r.data(), r.size());
     65  if (srv != SECSuccess) {
     66    r.clear();
     67    return NS_ERROR_FAILURE;
     68  }
     69 
     70  return NS_OK;
     71 }
     72 
     73 nsresult OSKeyStore::SecretAvailable(const nsACString& aLabel,
     74                                     /* out */ bool* aAvailable) {
     75  NS_ENSURE_STATE(mKs);
     76  nsresult rv = mKs->SecretAvailable(aLabel);
     77  *aAvailable = false;
     78  if (rv == nsresult::NS_ERROR_NOT_AVAILABLE) {
     79    // This indicates that there was no such entry in the keystore. Returning
     80    // false from this function suggests generating a new entry so this fits.
     81    return NS_OK;
     82  }
     83  // We want to raise other errors
     84  NS_ENSURE_SUCCESS(rv, rv);
     85 
     86  *aAvailable = true;
     87  return NS_OK;
     88 }
     89 
     90 nsresult OSKeyStore::GenerateSecret(const nsACString& aLabel,
     91                                    /* out */ nsACString& aRecoveryPhrase) {
     92  NS_ENSURE_STATE(mKs);
     93  size_t keyByteLength = mKs->GetKeyByteLength();
     94  std::vector<uint8_t> secret(keyByteLength);
     95  nsresult rv = GenerateRandom(secret);
     96  if (NS_FAILED(rv) || secret.size() != keyByteLength) {
     97    return NS_ERROR_FAILURE;
     98  }
     99  nsAutoCString secretString;
    100  secretString.Assign(BitwiseCast<char*, uint8_t*>(secret.data()),
    101                      secret.size());
    102 
    103  nsCString base64;
    104  rv = Base64Encode(secretString, base64);
    105  if (NS_FAILED(rv)) {
    106    return rv;
    107  }
    108 
    109  rv = mKs->StoreSecret(secretString, aLabel);
    110  if (NS_FAILED(rv)) {
    111    return rv;
    112  }
    113 
    114  aRecoveryPhrase = std::move(base64);
    115  return NS_OK;
    116 }
    117 
    118 nsresult OSKeyStore::RecoverSecret(const nsACString& aLabel,
    119                                   const nsACString& aRecoveryPhrase) {
    120  NS_ENSURE_STATE(mKs);
    121  nsAutoCString secret;
    122  nsresult rv = Base64Decode(aRecoveryPhrase, secret);
    123  if (NS_FAILED(rv)) {
    124    return rv;
    125  }
    126  if (secret.Length() != mKs->GetKeyByteLength()) {
    127    return NS_ERROR_INVALID_ARG;
    128  }
    129  rv = mKs->StoreSecret(secret, aLabel);
    130  if (NS_FAILED(rv)) {
    131    return rv;
    132  }
    133 
    134  return NS_OK;
    135 }
    136 
    137 nsresult OSKeyStore::DeleteSecret(const nsACString& aLabel) {
    138  NS_ENSURE_STATE(mKs);
    139  return mKs->DeleteSecret(aLabel);
    140 }
    141 
    142 nsresult OSKeyStore::RetrieveRecoveryPhrase(
    143    const nsACString& aLabel,
    144    /* out */ nsACString& aRecoveryPhrase) {
    145  NS_ENSURE_STATE(mKs);
    146  nsAutoCString secretString;
    147  nsresult rv = mKs->RetrieveSecret(aLabel, secretString);
    148  if (NS_FAILED(rv)) {
    149    return rv;
    150  }
    151 
    152  nsCString recoveryPhrase;
    153  rv = Base64Encode(secretString, recoveryPhrase);
    154  if (NS_FAILED(rv)) {
    155    return rv;
    156  }
    157 
    158  aRecoveryPhrase = std::move(recoveryPhrase);
    159  return NS_OK;
    160 }
    161 
    162 enum Cipher { Encrypt = true, Decrypt = false };
    163 
    164 nsresult OSKeyStore::EncryptBytes(const nsACString& aLabel,
    165                                  const std::vector<uint8_t>& aInBytes,
    166                                  /*out*/ nsACString& aEncryptedBase64Text) {
    167  NS_ENSURE_STATE(mKs);
    168 
    169  aEncryptedBase64Text.Truncate();
    170  std::vector<uint8_t> outBytes;
    171  nsresult rv =
    172      mKs->EncryptDecrypt(aLabel, aInBytes, outBytes, Cipher::Encrypt);
    173  if (NS_FAILED(rv)) {
    174    return rv;
    175  }
    176  nsAutoCString ciphertext;
    177  ciphertext.Assign(BitwiseCast<char*, uint8_t*>(outBytes.data()),
    178                    outBytes.size());
    179 
    180  nsCString base64ciphertext;
    181  rv = Base64Encode(ciphertext, base64ciphertext);
    182  if (NS_FAILED(rv)) {
    183    return rv;
    184  }
    185  aEncryptedBase64Text = std::move(base64ciphertext);
    186  return NS_OK;
    187 }
    188 
    189 nsresult OSKeyStore::DecryptBytes(const nsACString& aLabel,
    190                                  const nsACString& aEncryptedBase64Text,
    191                                  /*out*/ uint32_t* outLen,
    192                                  /*out*/ uint8_t** outBytes) {
    193  NS_ENSURE_STATE(mKs);
    194  NS_ENSURE_ARG_POINTER(outLen);
    195  NS_ENSURE_ARG_POINTER(outBytes);
    196  *outLen = 0;
    197  *outBytes = nullptr;
    198 
    199  nsAutoCString ciphertext;
    200  nsresult rv = Base64Decode(aEncryptedBase64Text, ciphertext);
    201  if (NS_FAILED(rv)) {
    202    return rv;
    203  }
    204  uint8_t* tmp = BitwiseCast<uint8_t*, const char*>(ciphertext.BeginReading());
    205  const std::vector<uint8_t> ciphertextBytes(tmp, tmp + ciphertext.Length());
    206  std::vector<uint8_t> plaintextBytes;
    207  rv = mKs->EncryptDecrypt(aLabel, ciphertextBytes, plaintextBytes,
    208                           Cipher::Decrypt);
    209  if (NS_FAILED(rv)) {
    210    return rv;
    211  }
    212 
    213  *outBytes = (uint8_t*)moz_xmalloc(plaintextBytes.size());
    214  memcpy(*outBytes, plaintextBytes.data(), plaintextBytes.size());
    215  *outLen = plaintextBytes.size();
    216  return NS_OK;
    217 }
    218 
    219 // Async interfaces that return promises because the key store implementation
    220 // might block, e.g. asking for a password.
    221 
    222 nsresult GetPromise(JSContext* aCx, /* out */ RefPtr<Promise>& aPromise) {
    223  nsIGlobalObject* globalObject = xpc::CurrentNativeGlobal(aCx);
    224  if (NS_WARN_IF(!globalObject)) {
    225    return NS_ERROR_UNEXPECTED;
    226  }
    227  ErrorResult result;
    228  aPromise = Promise::Create(globalObject, result);
    229  if (NS_WARN_IF(result.Failed())) {
    230    return result.StealNSResult();
    231  }
    232  return NS_OK;
    233 }
    234 
    235 void BackgroundGenerateSecret(const nsACString& aLabel,
    236                              RefPtr<Promise>& aPromise,
    237                              RefPtr<OSKeyStore> self) {
    238  nsAutoCString recovery;
    239  nsresult rv = self->GenerateSecret(aLabel, recovery);
    240  nsAutoString recoveryString;
    241  if (NS_SUCCEEDED(rv)) {
    242    CopyUTF8toUTF16(recovery, recoveryString);
    243  }
    244  nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
    245      "BackgroundGenerateSecreteOSKSResolve",
    246      [rv, aPromise = std::move(aPromise), recoveryString]() {
    247        if (NS_FAILED(rv)) {
    248          aPromise->MaybeReject(rv);
    249        } else {
    250          aPromise->MaybeResolve(recoveryString);
    251        }
    252      }));
    253  NS_DispatchToMainThread(runnable.forget());
    254 }
    255 
    256 NS_IMETHODIMP
    257 OSKeyStore::AsyncGenerateSecret(const nsACString& aLabel, JSContext* aCx,
    258                                Promise** promiseOut) {
    259  MOZ_ASSERT(NS_IsMainThread());
    260  if (!NS_IsMainThread()) {
    261    return NS_ERROR_NOT_SAME_THREAD;
    262  }
    263 
    264  NS_ENSURE_ARG_POINTER(aCx);
    265 
    266  if (!mBackgroundSerialEventTarget) {
    267    return NS_ERROR_NOT_AVAILABLE;
    268  }
    269 
    270  RefPtr<Promise> promiseHandle;
    271  nsresult rv = GetPromise(aCx, promiseHandle);
    272  if (NS_FAILED(rv)) {
    273    return rv;
    274  }
    275 
    276  RefPtr<OSKeyStore> self = this;
    277  nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
    278      "BackgroundGenerateSecret",
    279      [self, promiseHandle, aLabel = nsAutoCString(aLabel)]() mutable {
    280        BackgroundGenerateSecret(aLabel, promiseHandle, self);
    281      }));
    282 
    283  promiseHandle.forget(promiseOut);
    284  return mBackgroundSerialEventTarget->Dispatch(runnable.forget(),
    285                                                NS_DISPATCH_EVENT_MAY_BLOCK);
    286 }
    287 
    288 void BackgroundSecretAvailable(const nsACString& aLabel,
    289                               RefPtr<Promise>& aPromise,
    290                               RefPtr<OSKeyStore> self) {
    291  bool available = false;
    292  nsresult rv = self->SecretAvailable(aLabel, &available);
    293  nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
    294      "BackgroundSecreteAvailableOSKSResolve",
    295      [rv, aPromise = std::move(aPromise), available = available]() {
    296        if (NS_FAILED(rv)) {
    297          aPromise->MaybeReject(rv);
    298        } else {
    299          aPromise->MaybeResolve(available);
    300        }
    301      }));
    302  NS_DispatchToMainThread(runnable.forget());
    303 }
    304 
    305 NS_IMETHODIMP
    306 OSKeyStore::AsyncSecretAvailable(const nsACString& aLabel, JSContext* aCx,
    307                                 Promise** promiseOut) {
    308  MOZ_ASSERT(NS_IsMainThread());
    309  if (!NS_IsMainThread()) {
    310    return NS_ERROR_NOT_SAME_THREAD;
    311  }
    312 
    313  NS_ENSURE_ARG_POINTER(aCx);
    314 
    315  if (!mBackgroundSerialEventTarget) {
    316    return NS_ERROR_NOT_AVAILABLE;
    317  }
    318 
    319  RefPtr<Promise> promiseHandle;
    320  nsresult rv = GetPromise(aCx, promiseHandle);
    321  if (NS_FAILED(rv)) {
    322    return rv;
    323  }
    324 
    325  RefPtr<OSKeyStore> self = this;
    326  nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
    327      "BackgroundSecretAvailable",
    328      [self, promiseHandle, aLabel = nsAutoCString(aLabel)]() mutable {
    329        BackgroundSecretAvailable(aLabel, promiseHandle, self);
    330      }));
    331 
    332  promiseHandle.forget(promiseOut);
    333  return mBackgroundSerialEventTarget->Dispatch(runnable.forget(),
    334                                                NS_DISPATCH_EVENT_MAY_BLOCK);
    335 }
    336 
    337 void BackgroundRecoverSecret(const nsACString& aLabel,
    338                             const nsACString& aRecoveryPhrase,
    339                             RefPtr<Promise>& aPromise,
    340                             RefPtr<OSKeyStore> self) {
    341  nsresult rv = self->RecoverSecret(aLabel, aRecoveryPhrase);
    342  nsCOMPtr<nsIRunnable> runnable(
    343      NS_NewRunnableFunction("BackgroundRecoverSecreteOSKSResolve",
    344                             [rv, aPromise = std::move(aPromise)]() {
    345                               if (NS_FAILED(rv)) {
    346                                 aPromise->MaybeReject(rv);
    347                               } else {
    348                                 aPromise->MaybeResolveWithUndefined();
    349                               }
    350                             }));
    351  NS_DispatchToMainThread(runnable.forget());
    352 }
    353 
    354 NS_IMETHODIMP
    355 OSKeyStore::AsyncRecoverSecret(const nsACString& aLabel,
    356                               const nsACString& aRecoveryPhrase,
    357                               JSContext* aCx, Promise** promiseOut) {
    358  MOZ_ASSERT(NS_IsMainThread());
    359  if (!NS_IsMainThread()) {
    360    return NS_ERROR_NOT_SAME_THREAD;
    361  }
    362 
    363  NS_ENSURE_ARG_POINTER(aCx);
    364 
    365  if (!mBackgroundSerialEventTarget) {
    366    return NS_ERROR_NOT_AVAILABLE;
    367  }
    368 
    369  RefPtr<Promise> promiseHandle;
    370  nsresult rv = GetPromise(aCx, promiseHandle);
    371  if (NS_FAILED(rv)) {
    372    return rv;
    373  }
    374 
    375  RefPtr<OSKeyStore> self = this;
    376  nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
    377      "BackgroundRecoverSecret",
    378      [self, promiseHandle, aLabel = nsAutoCString(aLabel),
    379       aRecoveryPhrase = nsAutoCString(aRecoveryPhrase)]() mutable {
    380        BackgroundRecoverSecret(aLabel, aRecoveryPhrase, promiseHandle, self);
    381      }));
    382 
    383  promiseHandle.forget(promiseOut);
    384  return mBackgroundSerialEventTarget->Dispatch(runnable.forget(),
    385                                                NS_DISPATCH_EVENT_MAY_BLOCK);
    386 }
    387 
    388 void BackgroundDeleteSecret(const nsACString& aLabel, RefPtr<Promise>& aPromise,
    389                            RefPtr<OSKeyStore> self) {
    390  nsresult rv = self->DeleteSecret(aLabel);
    391  nsCOMPtr<nsIRunnable> runnable(
    392      NS_NewRunnableFunction("BackgroundDeleteSecreteOSKSResolve",
    393                             [rv, aPromise = std::move(aPromise)]() {
    394                               if (NS_FAILED(rv)) {
    395                                 aPromise->MaybeReject(rv);
    396                               } else {
    397                                 aPromise->MaybeResolveWithUndefined();
    398                               }
    399                             }));
    400  NS_DispatchToMainThread(runnable.forget());
    401 }
    402 
    403 NS_IMETHODIMP
    404 OSKeyStore::AsyncDeleteSecret(const nsACString& aLabel, JSContext* aCx,
    405                              Promise** promiseOut) {
    406  MOZ_ASSERT(NS_IsMainThread());
    407  if (!NS_IsMainThread()) {
    408    return NS_ERROR_NOT_SAME_THREAD;
    409  }
    410 
    411  NS_ENSURE_ARG_POINTER(aCx);
    412 
    413  if (!mBackgroundSerialEventTarget) {
    414    return NS_ERROR_NOT_AVAILABLE;
    415  }
    416 
    417  RefPtr<Promise> promiseHandle;
    418  nsresult rv = GetPromise(aCx, promiseHandle);
    419  if (NS_FAILED(rv)) {
    420    return rv;
    421  }
    422 
    423  RefPtr<OSKeyStore> self = this;
    424  nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
    425      "BackgroundDeleteSecret",
    426      [self, promiseHandle, aLabel = nsAutoCString(aLabel)]() mutable {
    427        BackgroundDeleteSecret(aLabel, promiseHandle, self);
    428      }));
    429 
    430  promiseHandle.forget(promiseOut);
    431  return mBackgroundSerialEventTarget->Dispatch(runnable.forget(),
    432                                                NS_DISPATCH_EVENT_MAY_BLOCK);
    433 }
    434 
    435 static void BackgroundEncryptBytes(const nsACString& aLabel,
    436                                   const std::vector<uint8_t>& aInBytes,
    437                                   RefPtr<Promise>& aPromise,
    438                                   RefPtr<OSKeyStore> self) {
    439  nsAutoCString ciphertext;
    440  nsresult rv = self->EncryptBytes(aLabel, aInBytes, ciphertext);
    441  nsAutoString ctext;
    442  CopyUTF8toUTF16(ciphertext, ctext);
    443 
    444  nsCOMPtr<nsIRunnable> runnable(
    445      NS_NewRunnableFunction("BackgroundEncryptOSKSResolve",
    446                             [rv, aPromise = std::move(aPromise), ctext]() {
    447                               if (NS_FAILED(rv)) {
    448                                 aPromise->MaybeReject(rv);
    449                               } else {
    450                                 aPromise->MaybeResolve(ctext);
    451                               }
    452                             }));
    453  NS_DispatchToMainThread(runnable.forget());
    454 }
    455 
    456 NS_IMETHODIMP
    457 OSKeyStore::AsyncEncryptBytes(const nsACString& aLabel,
    458                              const nsTArray<uint8_t>& inBytes, JSContext* aCx,
    459                              Promise** promiseOut) {
    460  MOZ_ASSERT(NS_IsMainThread());
    461  if (!NS_IsMainThread()) {
    462    return NS_ERROR_NOT_SAME_THREAD;
    463  }
    464 
    465  NS_ENSURE_ARG_POINTER(aCx);
    466 
    467  if (!mBackgroundSerialEventTarget) {
    468    return NS_ERROR_NOT_AVAILABLE;
    469  }
    470 
    471  RefPtr<Promise> promiseHandle;
    472  nsresult rv = GetPromise(aCx, promiseHandle);
    473  if (NS_FAILED(rv)) {
    474    return rv;
    475  }
    476 
    477  RefPtr<OSKeyStore> self = this;
    478  nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
    479      "BackgroundEncryptBytes",
    480      [promiseHandle,
    481       inBytes = std::vector<uint8_t>(inBytes.Elements(),
    482                                      inBytes.Elements() + inBytes.Length()),
    483       aLabel = nsAutoCString(aLabel), self]() mutable {
    484        BackgroundEncryptBytes(aLabel, inBytes, promiseHandle, self);
    485      }));
    486 
    487  promiseHandle.forget(promiseOut);
    488  return mBackgroundSerialEventTarget->Dispatch(runnable.forget(),
    489                                                NS_DISPATCH_EVENT_MAY_BLOCK);
    490 }
    491 
    492 void BackgroundDecryptBytes(const nsACString& aLabel,
    493                            const nsACString& aEncryptedBase64Text,
    494                            RefPtr<Promise>& aPromise,
    495                            RefPtr<OSKeyStore> self) {
    496  uint8_t* plaintext = nullptr;
    497  uint32_t plaintextLen = 0;
    498  nsresult rv = self->DecryptBytes(aLabel, aEncryptedBase64Text, &plaintextLen,
    499                                   &plaintext);
    500  nsTArray<uint8_t> plain;
    501  if (plaintext) {
    502    MOZ_ASSERT(plaintextLen > 0);
    503    plain.AppendElements(plaintext, plaintextLen);
    504    free(plaintext);
    505  }
    506 
    507  nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
    508      "BackgroundDecryptOSKSResolve",
    509      [rv, aPromise = std::move(aPromise), plain = std::move(plain)]() {
    510        if (NS_FAILED(rv)) {
    511          aPromise->MaybeReject(rv);
    512        } else {
    513          aPromise->MaybeResolve(plain);
    514        }
    515      }));
    516  NS_DispatchToMainThread(runnable.forget());
    517 }
    518 
    519 NS_IMETHODIMP
    520 OSKeyStore::AsyncDecryptBytes(const nsACString& aLabel,
    521                              const nsACString& aEncryptedBase64Text,
    522                              JSContext* aCx, Promise** promiseOut) {
    523  MOZ_ASSERT(NS_IsMainThread());
    524  if (!NS_IsMainThread()) {
    525    return NS_ERROR_NOT_SAME_THREAD;
    526  }
    527 
    528  NS_ENSURE_ARG_POINTER(aCx);
    529 
    530  if (!mBackgroundSerialEventTarget) {
    531    return NS_ERROR_NOT_AVAILABLE;
    532  }
    533 
    534  RefPtr<Promise> promiseHandle;
    535  nsresult rv = GetPromise(aCx, promiseHandle);
    536  if (NS_FAILED(rv)) {
    537    return rv;
    538  }
    539 
    540  RefPtr<OSKeyStore> self = this;
    541  nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
    542      "BackgroundDecryptBytes",
    543      [promiseHandle, self,
    544       aEncryptedBase64Text = nsAutoCString(aEncryptedBase64Text),
    545       aLabel = nsAutoCString(aLabel)]() mutable {
    546        BackgroundDecryptBytes(aLabel, aEncryptedBase64Text, promiseHandle,
    547                               self);
    548      }));
    549 
    550  promiseHandle.forget(promiseOut);
    551  return mBackgroundSerialEventTarget->Dispatch(runnable.forget(),
    552                                                NS_DISPATCH_EVENT_MAY_BLOCK);
    553 }
    554 
    555 void BackgroundGetRecoveryPhrase(const nsACString& aLabel,
    556                                 RefPtr<Promise>& aPromise,
    557                                 const RefPtr<OSKeyStore>& self) {
    558  nsAutoCString recoveryPhrase;
    559  nsresult rv = self->RetrieveRecoveryPhrase(aLabel, recoveryPhrase);
    560  nsAutoString exportedRecoveryPhrase;
    561  if (NS_SUCCEEDED(rv)) {
    562    CopyUTF8toUTF16(recoveryPhrase, exportedRecoveryPhrase);
    563  }
    564  nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
    565      "BackgroundRetrieveRecoveryPhraseResolve",
    566      [rv, aPromise = std::move(aPromise), exportedRecoveryPhrase]() {
    567        if (NS_FAILED(rv)) {
    568          aPromise->MaybeReject(rv);
    569        } else {
    570          aPromise->MaybeResolve(exportedRecoveryPhrase);
    571        }
    572      }));
    573  NS_DispatchToMainThread(runnable.forget());
    574 }
    575 
    576 NS_IMETHODIMP
    577 OSKeyStore::AsyncGetRecoveryPhrase(const nsACString& aLabel, JSContext* aCx,
    578                                   Promise** promiseOut) {
    579  MOZ_ASSERT(NS_IsMainThread());
    580  if (!NS_IsMainThread()) {
    581    return NS_ERROR_NOT_SAME_THREAD;
    582  }
    583 
    584  NS_ENSURE_ARG_POINTER(aCx);
    585 
    586  if (!mBackgroundSerialEventTarget) {
    587    return NS_ERROR_NOT_AVAILABLE;
    588  }
    589 
    590  RefPtr<Promise> promiseHandle;
    591  nsresult rv = GetPromise(aCx, promiseHandle);
    592  if (NS_FAILED(rv)) {
    593    return rv;
    594  }
    595 
    596  RefPtr<OSKeyStore> self = this;
    597  nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
    598      "BackgroundGetRecoveryPhrase",
    599      [promiseHandle, self, aLabel = nsAutoCString(aLabel)]() mutable {
    600        BackgroundGetRecoveryPhrase(aLabel, promiseHandle, self);
    601      }));
    602 
    603  promiseHandle.forget(promiseOut);
    604  return mBackgroundSerialEventTarget->Dispatch(runnable.forget(),
    605                                                NS_DISPATCH_EVENT_MAY_BLOCK);
    606 }
    607 
    608 // Generic AES-GCM cipher wrapper for NSS functions.
    609 
    610 nsresult AbstractOSKeyStore::BuildAesGcmKey(std::vector<uint8_t> aKeyBytes,
    611                                            /* out */ UniquePK11SymKey& aKey) {
    612  if (aKeyBytes.size() != mKeyByteLength) {
    613    return NS_ERROR_INVALID_ARG;
    614  }
    615 
    616  UniquePK11SlotInfo slot(PK11_GetInternalSlot());
    617  if (!slot) {
    618    return NS_ERROR_FAILURE;
    619  }
    620 
    621  UniqueSECItem key =
    622      UniqueSECItem(SECITEM_AllocItem(nullptr, nullptr, mKeyByteLength));
    623  if (!key) {
    624    return NS_ERROR_FAILURE;
    625  }
    626  key->type = siBuffer;
    627  memcpy(key->data, aKeyBytes.data(), mKeyByteLength);
    628  key->len = mKeyByteLength;
    629 
    630  UniquePK11SymKey symKey(
    631      PK11_ImportSymKey(slot.get(), CKM_AES_GCM, PK11_OriginUnwrap,
    632                        CKA_DECRYPT | CKA_ENCRYPT, key.get(), nullptr));
    633 
    634  if (!symKey) {
    635    return NS_ERROR_FAILURE;
    636  }
    637  aKey.swap(symKey);
    638 
    639  return NS_OK;
    640 }
    641 
    642 nsresult AbstractOSKeyStore::DoCipher(const UniquePK11SymKey& aSymKey,
    643                                      const std::vector<uint8_t>& inBytes,
    644                                      std::vector<uint8_t>& outBytes,
    645                                      bool encrypt) {
    646  NS_ENSURE_ARG_POINTER(aSymKey);
    647  outBytes.clear();
    648 
    649  // Build params.
    650  // We need to get the IV from inBytes if we decrypt.
    651  if (!encrypt && (inBytes.size() < mIVLength || inBytes.empty())) {
    652    return NS_ERROR_INVALID_ARG;
    653  }
    654 
    655  const uint8_t* ivp = nullptr;
    656  std::vector<uint8_t> ivBuf;
    657  if (encrypt) {
    658    // Generate a new IV.
    659    ivBuf.resize(mIVLength);
    660    nsresult rv = GenerateRandom(ivBuf);
    661    if (NS_FAILED(rv) || ivBuf.size() != mIVLength) {
    662      return NS_ERROR_FAILURE;
    663    }
    664    ivp = ivBuf.data();
    665  } else {
    666    // An IV was passed in. Use the first mIVLength bytes from inBytes as IV.
    667    ivp = inBytes.data();
    668  }
    669 
    670  CK_GCM_PARAMS gcm_params;
    671  gcm_params.pIv = const_cast<unsigned char*>(ivp);
    672  gcm_params.ulIvLen = mIVLength;
    673  gcm_params.ulIvBits = gcm_params.ulIvLen * 8;
    674  gcm_params.ulTagBits = 128;
    675  gcm_params.pAAD = nullptr;
    676  gcm_params.ulAADLen = 0;
    677 
    678  SECItem paramsItem = {siBuffer, reinterpret_cast<unsigned char*>(&gcm_params),
    679                        sizeof(CK_GCM_PARAMS)};
    680 
    681  size_t blockLength = 16;
    682  outBytes.resize(inBytes.size() + blockLength);
    683  unsigned int outLen = 0;
    684  SECStatus srv = SECFailure;
    685  if (encrypt) {
    686    srv = PK11_Encrypt(aSymKey.get(), CKM_AES_GCM, &paramsItem, outBytes.data(),
    687                       &outLen, inBytes.size() + blockLength, inBytes.data(),
    688                       inBytes.size());
    689    // Prepend the used IV to the ciphertext.
    690    (void)outBytes.insert(outBytes.begin(), ivp, ivp + mIVLength);
    691    outLen += mIVLength;
    692  } else {
    693    // Remove the IV from the input.
    694    std::vector<uint8_t> input(inBytes);
    695    input.erase(input.begin(), input.begin() + mIVLength);
    696    srv = PK11_Decrypt(aSymKey.get(), CKM_AES_GCM, &paramsItem, outBytes.data(),
    697                       &outLen, input.size() + blockLength, input.data(),
    698                       input.size());
    699  }
    700  if (srv != SECSuccess || outLen > outBytes.size()) {
    701    outBytes.clear();
    702    return NS_ERROR_FAILURE;
    703  }
    704  if (outLen < outBytes.size()) {
    705    outBytes.resize(outLen);
    706  }
    707 
    708  return NS_OK;
    709 }
    710 
    711 nsresult AbstractOSKeyStore::SecretAvailable(const nsACString& aLabel) {
    712  nsAutoCString secret;
    713  nsresult rv = RetrieveSecret(aLabel, secret);
    714  NS_ENSURE_SUCCESS(rv, rv);
    715  if (secret.Length() == 0) {
    716    // This should probably never happen.
    717    MOZ_ASSERT(false, "Secret from OS key store must not have zero length");
    718    return nsresult::NS_ERROR_ILLEGAL_VALUE;
    719  }
    720  return NS_OK;
    721 }
    722 
    723 nsresult AbstractOSKeyStore::EncryptDecrypt(const nsACString& aLabel,
    724                                            const std::vector<uint8_t>& inBytes,
    725                                            std::vector<uint8_t>& outBytes,
    726                                            bool encrypt) {
    727  nsAutoCString secret;
    728  nsresult rv = RetrieveSecret(aLabel, secret);
    729  if (NS_FAILED(rv) || secret.Length() == 0) {
    730    return NS_ERROR_FAILURE;
    731  }
    732 
    733  uint8_t* p = BitwiseCast<uint8_t*, const char*>(secret.BeginReading());
    734  std::vector<uint8_t> buf(p, p + secret.Length());
    735  UniquePK11SymKey symKey;
    736  rv = BuildAesGcmKey(buf, symKey);
    737  if (NS_FAILED(rv)) {
    738    return NS_ERROR_FAILURE;
    739  }
    740  return DoCipher(symKey, inBytes, outBytes, encrypt);
    741 }