summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/OSKeyStore.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'security/manager/ssl/OSKeyStore.cpp')
-rw-r--r--security/manager/ssl/OSKeyStore.cpp726
1 files changed, 726 insertions, 0 deletions
diff --git a/security/manager/ssl/OSKeyStore.cpp b/security/manager/ssl/OSKeyStore.cpp
new file mode 100644
index 0000000000..db4a46292d
--- /dev/null
+++ b/security/manager/ssl/OSKeyStore.cpp
@@ -0,0 +1,726 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "OSKeyStore.h"
+
+#include "mozilla/Base64.h"
+#include "mozilla/dom/Promise.h"
+#include "nsThreadUtils.h"
+#include "nsXPCOM.h"
+#include "pk11pub.h"
+
+#if defined(XP_MACOSX)
+# include "KeychainSecret.h"
+#elif defined(XP_WIN)
+# include "CredentialManagerSecret.h"
+#elif defined(MOZ_WIDGET_GTK)
+# include "LibSecret.h"
+# include "NSSKeyStore.h"
+#else
+# include "NSSKeyStore.h"
+#endif
+
+NS_IMPL_ISUPPORTS(OSKeyStore, nsIOSKeyStore)
+
+using namespace mozilla;
+using dom::Promise;
+
+OSKeyStore::OSKeyStore() : mKs(nullptr), mKsIsNSSKeyStore(false) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (NS_WARN_IF(!NS_IsMainThread())) {
+ return;
+ }
+
+#if defined(XP_MACOSX)
+ mKs.reset(new KeychainSecret());
+#elif defined(XP_WIN)
+ mKs.reset(new CredentialManagerSecret());
+#elif defined(MOZ_WIDGET_GTK)
+ if (NS_SUCCEEDED(MaybeLoadLibSecret())) {
+ mKs.reset(new LibSecret());
+ } else {
+ mKs.reset(new NSSKeyStore());
+ mKsIsNSSKeyStore = true;
+ }
+#else
+ mKs.reset(new NSSKeyStore());
+ mKsIsNSSKeyStore = true;
+#endif
+}
+
+static nsresult GenerateRandom(std::vector<uint8_t>& r) {
+ if (r.empty()) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ UniquePK11SlotInfo slot(PK11_GetInternalSlot());
+ if (!slot) {
+ return NS_ERROR_FAILURE;
+ }
+
+ SECStatus srv = PK11_GenerateRandomOnSlot(slot.get(), r.data(), r.size());
+ if (srv != SECSuccess) {
+ r.clear();
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult OSKeyStore::SecretAvailable(const nsACString& aLabel,
+ /* out */ bool* aAvailable) {
+ NS_ENSURE_STATE(mKs);
+ *aAvailable = mKs->SecretAvailable(aLabel);
+ return NS_OK;
+}
+
+nsresult OSKeyStore::GenerateSecret(const nsACString& aLabel,
+ /* out */ nsACString& aRecoveryPhrase) {
+ NS_ENSURE_STATE(mKs);
+ size_t keyByteLength = mKs->GetKeyByteLength();
+ std::vector<uint8_t> secret(keyByteLength);
+ nsresult rv = GenerateRandom(secret);
+ if (NS_FAILED(rv) || secret.size() != keyByteLength) {
+ return NS_ERROR_FAILURE;
+ }
+ nsAutoCString secretString;
+ secretString.Assign(BitwiseCast<char*, uint8_t*>(secret.data()),
+ secret.size());
+
+ nsCString base64;
+ rv = Base64Encode(secretString, base64);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = mKs->StoreSecret(secretString, aLabel);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ aRecoveryPhrase = std::move(base64);
+ return NS_OK;
+}
+
+nsresult OSKeyStore::RecoverSecret(const nsACString& aLabel,
+ const nsACString& aRecoveryPhrase) {
+ NS_ENSURE_STATE(mKs);
+ nsAutoCString secret;
+ nsresult rv = Base64Decode(aRecoveryPhrase, secret);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (secret.Length() != mKs->GetKeyByteLength()) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ rv = mKs->StoreSecret(secret, aLabel);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult OSKeyStore::DeleteSecret(const nsACString& aLabel) {
+ NS_ENSURE_STATE(mKs);
+ return mKs->DeleteSecret(aLabel);
+}
+
+enum Cipher { Encrypt = true, Decrypt = false };
+
+nsresult OSKeyStore::EncryptBytes(const nsACString& aLabel,
+ const std::vector<uint8_t>& aInBytes,
+ /*out*/ nsACString& aEncryptedBase64Text) {
+ NS_ENSURE_STATE(mKs);
+
+ aEncryptedBase64Text.Truncate();
+ std::vector<uint8_t> outBytes;
+ nsresult rv =
+ mKs->EncryptDecrypt(aLabel, aInBytes, outBytes, Cipher::Encrypt);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ nsAutoCString ciphertext;
+ ciphertext.Assign(BitwiseCast<char*, uint8_t*>(outBytes.data()),
+ outBytes.size());
+
+ nsCString base64ciphertext;
+ rv = Base64Encode(ciphertext, base64ciphertext);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ aEncryptedBase64Text = std::move(base64ciphertext);
+ return NS_OK;
+}
+
+nsresult OSKeyStore::DecryptBytes(const nsACString& aLabel,
+ const nsACString& aEncryptedBase64Text,
+ /*out*/ uint32_t* outLen,
+ /*out*/ uint8_t** outBytes) {
+ NS_ENSURE_STATE(mKs);
+ NS_ENSURE_ARG_POINTER(outLen);
+ NS_ENSURE_ARG_POINTER(outBytes);
+ *outLen = 0;
+ *outBytes = nullptr;
+
+ nsAutoCString ciphertext;
+ nsresult rv = Base64Decode(aEncryptedBase64Text, ciphertext);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ uint8_t* tmp = BitwiseCast<uint8_t*, const char*>(ciphertext.BeginReading());
+ const std::vector<uint8_t> ciphertextBytes(tmp, tmp + ciphertext.Length());
+ std::vector<uint8_t> plaintextBytes;
+ rv = mKs->EncryptDecrypt(aLabel, ciphertextBytes, plaintextBytes,
+ Cipher::Decrypt);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ *outBytes = (uint8_t*)moz_xmalloc(plaintextBytes.size());
+ memcpy(*outBytes, plaintextBytes.data(), plaintextBytes.size());
+ *outLen = plaintextBytes.size();
+ return NS_OK;
+}
+
+nsresult OSKeyStore::Lock() {
+ NS_ENSURE_STATE(mKs);
+ return mKs->Lock();
+}
+
+nsresult OSKeyStore::Unlock() {
+ NS_ENSURE_STATE(mKs);
+ return mKs->Unlock();
+}
+
+NS_IMETHODIMP
+OSKeyStore::GetIsNSSKeyStore(bool* aNSSKeyStore) {
+ NS_ENSURE_ARG_POINTER(aNSSKeyStore);
+ *aNSSKeyStore = mKsIsNSSKeyStore;
+ return NS_OK;
+}
+
+// Async interfaces that return promises because the key store implementation
+// might block, e.g. asking for a password.
+
+nsresult GetPromise(JSContext* aCx, /* out */ RefPtr<Promise>& aPromise) {
+ nsIGlobalObject* globalObject = xpc::CurrentNativeGlobal(aCx);
+ if (NS_WARN_IF(!globalObject)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ ErrorResult result;
+ aPromise = Promise::Create(globalObject, result);
+ if (NS_WARN_IF(result.Failed())) {
+ return result.StealNSResult();
+ }
+ return NS_OK;
+}
+
+void BackgroundUnlock(RefPtr<Promise>& aPromise, RefPtr<OSKeyStore> self) {
+ nsAutoCString recovery;
+ nsresult rv = self->Unlock();
+ nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
+ "BackgroundUnlockOSKSResolve", [rv, aPromise = std::move(aPromise)]() {
+ if (NS_FAILED(rv)) {
+ aPromise->MaybeReject(rv);
+ } else {
+ aPromise->MaybeResolveWithUndefined();
+ }
+ }));
+ NS_DispatchToMainThread(runnable.forget());
+}
+
+NS_IMETHODIMP
+OSKeyStore::AsyncUnlock(JSContext* aCx, Promise** promiseOut) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!NS_IsMainThread()) {
+ return NS_ERROR_NOT_SAME_THREAD;
+ }
+
+ NS_ENSURE_ARG_POINTER(aCx);
+
+ RefPtr<Promise> promiseHandle;
+ nsresult rv = GetPromise(aCx, promiseHandle);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ RefPtr<OSKeyStore> self = this;
+ nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
+ "BackgroundUnlock", [self, promiseHandle]() mutable {
+ BackgroundUnlock(promiseHandle, self);
+ }));
+
+ promiseHandle.forget(promiseOut);
+ return NS_DispatchBackgroundTask(runnable.forget(),
+ NS_DISPATCH_EVENT_MAY_BLOCK);
+}
+
+void BackgroundLock(RefPtr<Promise>& aPromise, RefPtr<OSKeyStore> self) {
+ nsresult rv = self->Lock();
+ nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
+ "BackgroundLockOSKSResolve", [rv, aPromise = std::move(aPromise)]() {
+ if (NS_FAILED(rv)) {
+ aPromise->MaybeReject(rv);
+ } else {
+ aPromise->MaybeResolveWithUndefined();
+ }
+ }));
+ NS_DispatchToMainThread(runnable.forget());
+}
+
+NS_IMETHODIMP
+OSKeyStore::AsyncLock(JSContext* aCx, Promise** promiseOut) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!NS_IsMainThread()) {
+ return NS_ERROR_NOT_SAME_THREAD;
+ }
+
+ NS_ENSURE_ARG_POINTER(aCx);
+
+ RefPtr<Promise> promiseHandle;
+ nsresult rv = GetPromise(aCx, promiseHandle);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ RefPtr<OSKeyStore> self = this;
+ nsCOMPtr<nsIRunnable> runnable(
+ NS_NewRunnableFunction("BackgroundLock", [self, promiseHandle]() mutable {
+ BackgroundLock(promiseHandle, self);
+ }));
+
+ promiseHandle.forget(promiseOut);
+ return NS_DispatchBackgroundTask(runnable.forget(),
+ NS_DISPATCH_EVENT_MAY_BLOCK);
+}
+
+void BackgroundGenerateSecret(const nsACString& aLabel,
+ RefPtr<Promise>& aPromise,
+ RefPtr<OSKeyStore> self) {
+ nsAutoCString recovery;
+ nsresult rv = self->GenerateSecret(aLabel, recovery);
+ nsAutoString recoveryString;
+ if (NS_SUCCEEDED(rv)) {
+ CopyUTF8toUTF16(recovery, recoveryString);
+ }
+ nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
+ "BackgroundGenerateSecreteOSKSResolve",
+ [rv, aPromise = std::move(aPromise), recoveryString]() {
+ if (NS_FAILED(rv)) {
+ aPromise->MaybeReject(rv);
+ } else {
+ aPromise->MaybeResolve(recoveryString);
+ }
+ }));
+ NS_DispatchToMainThread(runnable.forget());
+}
+
+NS_IMETHODIMP
+OSKeyStore::AsyncGenerateSecret(const nsACString& aLabel, JSContext* aCx,
+ Promise** promiseOut) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!NS_IsMainThread()) {
+ return NS_ERROR_NOT_SAME_THREAD;
+ }
+
+ NS_ENSURE_ARG_POINTER(aCx);
+
+ RefPtr<Promise> promiseHandle;
+ nsresult rv = GetPromise(aCx, promiseHandle);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ RefPtr<OSKeyStore> self = this;
+ nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
+ "BackgroundGenerateSecret",
+ [self, promiseHandle, aLabel = nsAutoCString(aLabel)]() mutable {
+ BackgroundGenerateSecret(aLabel, promiseHandle, self);
+ }));
+
+ promiseHandle.forget(promiseOut);
+ return NS_DispatchBackgroundTask(runnable.forget(),
+ NS_DISPATCH_EVENT_MAY_BLOCK);
+}
+
+void BackgroundSecretAvailable(const nsACString& aLabel,
+ RefPtr<Promise>& aPromise,
+ RefPtr<OSKeyStore> self) {
+ bool available = false;
+ nsresult rv = self->SecretAvailable(aLabel, &available);
+ nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
+ "BackgroundSecreteAvailableOSKSResolve",
+ [rv, aPromise = std::move(aPromise), available = available]() {
+ if (NS_FAILED(rv)) {
+ aPromise->MaybeReject(rv);
+ } else {
+ aPromise->MaybeResolve(available);
+ }
+ }));
+ NS_DispatchToMainThread(runnable.forget());
+}
+
+NS_IMETHODIMP
+OSKeyStore::AsyncSecretAvailable(const nsACString& aLabel, JSContext* aCx,
+ Promise** promiseOut) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!NS_IsMainThread()) {
+ return NS_ERROR_NOT_SAME_THREAD;
+ }
+
+ NS_ENSURE_ARG_POINTER(aCx);
+
+ RefPtr<Promise> promiseHandle;
+ nsresult rv = GetPromise(aCx, promiseHandle);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ RefPtr<OSKeyStore> self = this;
+ nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
+ "BackgroundSecretAvailable",
+ [self, promiseHandle, aLabel = nsAutoCString(aLabel)]() mutable {
+ BackgroundSecretAvailable(aLabel, promiseHandle, self);
+ }));
+
+ promiseHandle.forget(promiseOut);
+ return NS_DispatchBackgroundTask(runnable.forget(),
+ NS_DISPATCH_EVENT_MAY_BLOCK);
+}
+
+void BackgroundRecoverSecret(const nsACString& aLabel,
+ const nsACString& aRecoveryPhrase,
+ RefPtr<Promise>& aPromise,
+ RefPtr<OSKeyStore> self) {
+ nsresult rv = self->RecoverSecret(aLabel, aRecoveryPhrase);
+ nsCOMPtr<nsIRunnable> runnable(
+ NS_NewRunnableFunction("BackgroundRecoverSecreteOSKSResolve",
+ [rv, aPromise = std::move(aPromise)]() {
+ if (NS_FAILED(rv)) {
+ aPromise->MaybeReject(rv);
+ } else {
+ aPromise->MaybeResolveWithUndefined();
+ }
+ }));
+ NS_DispatchToMainThread(runnable.forget());
+}
+
+NS_IMETHODIMP
+OSKeyStore::AsyncRecoverSecret(const nsACString& aLabel,
+ const nsACString& aRecoveryPhrase,
+ JSContext* aCx, Promise** promiseOut) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!NS_IsMainThread()) {
+ return NS_ERROR_NOT_SAME_THREAD;
+ }
+
+ NS_ENSURE_ARG_POINTER(aCx);
+
+ RefPtr<Promise> promiseHandle;
+ nsresult rv = GetPromise(aCx, promiseHandle);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ RefPtr<OSKeyStore> self = this;
+ nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
+ "BackgroundRecoverSecret",
+ [self, promiseHandle, aLabel = nsAutoCString(aLabel),
+ aRecoveryPhrase = nsAutoCString(aRecoveryPhrase)]() mutable {
+ BackgroundRecoverSecret(aLabel, aRecoveryPhrase, promiseHandle, self);
+ }));
+
+ promiseHandle.forget(promiseOut);
+ return NS_DispatchBackgroundTask(runnable.forget(),
+ NS_DISPATCH_EVENT_MAY_BLOCK);
+}
+
+void BackgroundDeleteSecret(const nsACString& aLabel, RefPtr<Promise>& aPromise,
+ RefPtr<OSKeyStore> self) {
+ nsresult rv = self->DeleteSecret(aLabel);
+ nsCOMPtr<nsIRunnable> runnable(
+ NS_NewRunnableFunction("BackgroundDeleteSecreteOSKSResolve",
+ [rv, aPromise = std::move(aPromise)]() {
+ if (NS_FAILED(rv)) {
+ aPromise->MaybeReject(rv);
+ } else {
+ aPromise->MaybeResolveWithUndefined();
+ }
+ }));
+ NS_DispatchToMainThread(runnable.forget());
+}
+
+NS_IMETHODIMP
+OSKeyStore::AsyncDeleteSecret(const nsACString& aLabel, JSContext* aCx,
+ Promise** promiseOut) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!NS_IsMainThread()) {
+ return NS_ERROR_NOT_SAME_THREAD;
+ }
+
+ NS_ENSURE_ARG_POINTER(aCx);
+
+ RefPtr<Promise> promiseHandle;
+ nsresult rv = GetPromise(aCx, promiseHandle);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ RefPtr<OSKeyStore> self = this;
+ nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
+ "BackgroundDeleteSecret",
+ [self, promiseHandle, aLabel = nsAutoCString(aLabel)]() mutable {
+ BackgroundDeleteSecret(aLabel, promiseHandle, self);
+ }));
+
+ promiseHandle.forget(promiseOut);
+ return NS_DispatchBackgroundTask(runnable.forget(),
+ NS_DISPATCH_EVENT_MAY_BLOCK);
+}
+
+static void BackgroundEncryptBytes(const nsACString& aLabel,
+ const std::vector<uint8_t>& aInBytes,
+ RefPtr<Promise>& aPromise,
+ RefPtr<OSKeyStore> self) {
+ nsAutoCString ciphertext;
+ nsresult rv = self->EncryptBytes(aLabel, aInBytes, ciphertext);
+ nsAutoString ctext;
+ CopyUTF8toUTF16(ciphertext, ctext);
+
+ nsCOMPtr<nsIRunnable> runnable(
+ NS_NewRunnableFunction("BackgroundEncryptOSKSResolve",
+ [rv, aPromise = std::move(aPromise), ctext]() {
+ if (NS_FAILED(rv)) {
+ aPromise->MaybeReject(rv);
+ } else {
+ aPromise->MaybeResolve(ctext);
+ }
+ }));
+ NS_DispatchToMainThread(runnable.forget());
+}
+
+NS_IMETHODIMP
+OSKeyStore::AsyncEncryptBytes(const nsACString& aLabel,
+ const nsTArray<uint8_t>& inBytes, JSContext* aCx,
+ Promise** promiseOut) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!NS_IsMainThread()) {
+ return NS_ERROR_NOT_SAME_THREAD;
+ }
+
+ NS_ENSURE_ARG_POINTER(aCx);
+
+ RefPtr<Promise> promiseHandle;
+ nsresult rv = GetPromise(aCx, promiseHandle);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ RefPtr<OSKeyStore> self = this;
+ nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
+ "BackgroundEncryptBytes",
+ [promiseHandle,
+ inBytes = std::vector<uint8_t>(inBytes.Elements(),
+ inBytes.Elements() + inBytes.Length()),
+ aLabel = nsAutoCString(aLabel), self]() mutable {
+ BackgroundEncryptBytes(aLabel, inBytes, promiseHandle, self);
+ }));
+
+ promiseHandle.forget(promiseOut);
+ return NS_DispatchBackgroundTask(runnable.forget(),
+ NS_DISPATCH_EVENT_MAY_BLOCK);
+}
+
+void BackgroundDecryptBytes(const nsACString& aLabel,
+ const nsACString& aEncryptedBase64Text,
+ RefPtr<Promise>& aPromise,
+ RefPtr<OSKeyStore> self) {
+ uint8_t* plaintext = nullptr;
+ uint32_t plaintextLen = 0;
+ nsresult rv = self->DecryptBytes(aLabel, aEncryptedBase64Text, &plaintextLen,
+ &plaintext);
+ nsTArray<uint8_t> plain;
+ if (plaintext) {
+ MOZ_ASSERT(plaintextLen > 0);
+ plain.AppendElements(plaintext, plaintextLen);
+ free(plaintext);
+ }
+
+ nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
+ "BackgroundDecryptOSKSResolve",
+ [rv, aPromise = std::move(aPromise), plain = std::move(plain)]() {
+ if (NS_FAILED(rv)) {
+ aPromise->MaybeReject(rv);
+ } else {
+ aPromise->MaybeResolve(plain);
+ }
+ }));
+ NS_DispatchToMainThread(runnable.forget());
+}
+
+NS_IMETHODIMP
+OSKeyStore::AsyncDecryptBytes(const nsACString& aLabel,
+ const nsACString& aEncryptedBase64Text,
+ JSContext* aCx, Promise** promiseOut) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!NS_IsMainThread()) {
+ return NS_ERROR_NOT_SAME_THREAD;
+ }
+
+ NS_ENSURE_ARG_POINTER(aCx);
+
+ RefPtr<Promise> promiseHandle;
+ nsresult rv = GetPromise(aCx, promiseHandle);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ RefPtr<OSKeyStore> self = this;
+ nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
+ "BackgroundDecryptBytes",
+ [promiseHandle, self,
+ aEncryptedBase64Text = nsAutoCString(aEncryptedBase64Text),
+ aLabel = nsAutoCString(aLabel)]() mutable {
+ BackgroundDecryptBytes(aLabel, aEncryptedBase64Text, promiseHandle,
+ self);
+ }));
+
+ promiseHandle.forget(promiseOut);
+ return NS_DispatchBackgroundTask(runnable.forget(),
+ NS_DISPATCH_EVENT_MAY_BLOCK);
+}
+
+// Generic AES-GCM cipher wrapper for NSS functions.
+
+nsresult AbstractOSKeyStore::BuildAesGcmKey(std::vector<uint8_t> aKeyBytes,
+ /* out */ UniquePK11SymKey& aKey) {
+ if (aKeyBytes.size() != mKeyByteLength) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ UniquePK11SlotInfo slot(PK11_GetInternalSlot());
+ if (!slot) {
+ return NS_ERROR_FAILURE;
+ }
+
+ UniqueSECItem key =
+ UniqueSECItem(SECITEM_AllocItem(nullptr, nullptr, mKeyByteLength));
+ if (!key) {
+ return NS_ERROR_FAILURE;
+ }
+ key->type = siBuffer;
+ memcpy(key->data, aKeyBytes.data(), mKeyByteLength);
+ key->len = mKeyByteLength;
+
+ UniquePK11SymKey symKey(
+ PK11_ImportSymKey(slot.get(), CKM_AES_GCM, PK11_OriginUnwrap,
+ CKA_DECRYPT | CKA_ENCRYPT, key.get(), nullptr));
+
+ if (!symKey) {
+ return NS_ERROR_FAILURE;
+ }
+ aKey.swap(symKey);
+
+ return NS_OK;
+}
+
+nsresult AbstractOSKeyStore::DoCipher(const UniquePK11SymKey& aSymKey,
+ const std::vector<uint8_t>& inBytes,
+ std::vector<uint8_t>& outBytes,
+ bool encrypt) {
+ NS_ENSURE_ARG_POINTER(aSymKey);
+ outBytes.clear();
+
+ // Build params.
+ // We need to get the IV from inBytes if we decrypt.
+ if (!encrypt && (inBytes.size() < mIVLength || inBytes.empty())) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ const uint8_t* ivp = nullptr;
+ std::vector<uint8_t> ivBuf;
+ if (encrypt) {
+ // Generate a new IV.
+ ivBuf.resize(mIVLength);
+ nsresult rv = GenerateRandom(ivBuf);
+ if (NS_FAILED(rv) || ivBuf.size() != mIVLength) {
+ return NS_ERROR_FAILURE;
+ }
+ ivp = ivBuf.data();
+ } else {
+ // An IV was passed in. Use the first mIVLength bytes from inBytes as IV.
+ ivp = inBytes.data();
+ }
+
+ CK_GCM_PARAMS gcm_params;
+ gcm_params.pIv = const_cast<unsigned char*>(ivp);
+ gcm_params.ulIvLen = mIVLength;
+ gcm_params.ulIvBits = gcm_params.ulIvLen * 8;
+ gcm_params.ulTagBits = 128;
+ gcm_params.pAAD = nullptr;
+ gcm_params.ulAADLen = 0;
+
+ SECItem paramsItem = {siBuffer, reinterpret_cast<unsigned char*>(&gcm_params),
+ sizeof(CK_GCM_PARAMS)};
+
+ size_t blockLength = 16;
+ outBytes.resize(inBytes.size() + blockLength);
+ unsigned int outLen = 0;
+ SECStatus srv = SECFailure;
+ if (encrypt) {
+ srv = PK11_Encrypt(aSymKey.get(), CKM_AES_GCM, &paramsItem, outBytes.data(),
+ &outLen, inBytes.size() + blockLength, inBytes.data(),
+ inBytes.size());
+ // Prepend the used IV to the ciphertext.
+ Unused << outBytes.insert(outBytes.begin(), ivp, ivp + mIVLength);
+ outLen += mIVLength;
+ } else {
+ // Remove the IV from the input.
+ std::vector<uint8_t> input(inBytes);
+ input.erase(input.begin(), input.begin() + mIVLength);
+ srv = PK11_Decrypt(aSymKey.get(), CKM_AES_GCM, &paramsItem, outBytes.data(),
+ &outLen, input.size() + blockLength, input.data(),
+ input.size());
+ }
+ if (srv != SECSuccess || outLen > outBytes.size()) {
+ outBytes.clear();
+ return NS_ERROR_FAILURE;
+ }
+ if (outLen < outBytes.size()) {
+ outBytes.resize(outLen);
+ }
+
+ return NS_OK;
+}
+
+bool AbstractOSKeyStore::SecretAvailable(const nsACString& aLabel) {
+ nsAutoCString secret;
+ nsresult rv = RetrieveSecret(aLabel, secret);
+ if (NS_FAILED(rv) || secret.Length() == 0) {
+ return false;
+ }
+ return true;
+}
+
+nsresult AbstractOSKeyStore::EncryptDecrypt(const nsACString& aLabel,
+ const std::vector<uint8_t>& inBytes,
+ std::vector<uint8_t>& outBytes,
+ bool encrypt) {
+ nsAutoCString secret;
+ nsresult rv = RetrieveSecret(aLabel, secret);
+ if (NS_FAILED(rv) || secret.Length() == 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ uint8_t* p = BitwiseCast<uint8_t*, const char*>(secret.BeginReading());
+ std::vector<uint8_t> buf(p, p + secret.Length());
+ UniquePK11SymKey symKey;
+ rv = BuildAesGcmKey(buf, symKey);
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+ return DoCipher(symKey, inBytes, outBytes, encrypt);
+}