summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/NSSKeyStore.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'security/manager/ssl/NSSKeyStore.cpp')
-rw-r--r--security/manager/ssl/NSSKeyStore.cpp228
1 files changed, 228 insertions, 0 deletions
diff --git a/security/manager/ssl/NSSKeyStore.cpp b/security/manager/ssl/NSSKeyStore.cpp
new file mode 100644
index 0000000000..2850a13565
--- /dev/null
+++ b/security/manager/ssl/NSSKeyStore.cpp
@@ -0,0 +1,228 @@
+/* -*- 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 "NSSKeyStore.h"
+
+#include "mozilla/AbstractThread.h"
+#include "mozilla/Base64.h"
+#include "mozilla/Logging.h"
+#include "mozilla/SyncRunnable.h"
+#include "nsIThread.h"
+#include "nsNSSComponent.h"
+#include "nsPK11TokenDB.h"
+#include "nsXULAppAPI.h"
+
+/* Implementing OSKeyStore when there is no platform specific one.
+ * This key store instead puts the keys into the NSS DB.
+ */
+
+using namespace mozilla;
+using mozilla::SyncRunnable;
+
+LazyLogModule gNSSKeyStoreLog("nsskeystore");
+
+NSSKeyStore::NSSKeyStore() {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ if (!XRE_IsParentProcess()) {
+ // This shouldn't happen as this is only initialised when creating the
+ // OSKeyStore, which is ParentProcessOnly.
+ return;
+ }
+ Unused << EnsureNSSInitializedChromeOrContent();
+ Unused << InitToken();
+}
+NSSKeyStore::~NSSKeyStore() = default;
+
+nsresult NSSKeyStore::InitToken() {
+ if (!mSlot) {
+ mSlot = UniquePK11SlotInfo(PK11_GetInternalKeySlot());
+ if (!mSlot) {
+ MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug,
+ ("Error getting internal key slot"));
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ }
+ return NS_OK;
+}
+
+nsresult NSSKeyStoreMainThreadLock(PK11SlotInfo* aSlot) {
+ nsCOMPtr<nsIPK11Token> token = new nsPK11Token(aSlot);
+ return token->LogoutSimple();
+}
+
+nsresult NSSKeyStore::Lock() {
+ NS_ENSURE_STATE(mSlot);
+
+ if (!NS_IsMainThread()) {
+ nsCOMPtr<nsIThread> mainThread;
+ nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Forward to the main thread synchronously.
+ SyncRunnable::DispatchToThread(
+ mainThread, NS_NewRunnableFunction("NSSKeyStoreMainThreadLock",
+ [slot = mSlot.get()]() {
+ NSSKeyStoreMainThreadLock(slot);
+ }));
+
+ return NS_OK;
+ }
+
+ return NSSKeyStoreMainThreadLock(mSlot.get());
+}
+
+nsresult NSSKeyStoreMainThreadUnlock(PK11SlotInfo* aSlot) {
+ nsCOMPtr<nsIPK11Token> token = new nsPK11Token(aSlot);
+ return NS_FAILED(token->Login(false /* force */)) ? NS_ERROR_FAILURE : NS_OK;
+}
+
+nsresult NSSKeyStore::Unlock() {
+ NS_ENSURE_STATE(mSlot);
+
+ if (!NS_IsMainThread()) {
+ nsCOMPtr<nsIThread> mainThread;
+ nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Forward to the main thread synchronously.
+ nsresult result = NS_ERROR_FAILURE;
+ SyncRunnable::DispatchToThread(
+ mainThread,
+ NS_NewRunnableFunction("NSSKeyStoreMainThreadUnlock",
+ [slot = mSlot.get(), result = &result]() {
+ *result = NSSKeyStoreMainThreadUnlock(slot);
+ }));
+
+ return result;
+ }
+
+ return NSSKeyStoreMainThreadUnlock(mSlot.get());
+}
+
+nsresult NSSKeyStore::StoreSecret(const nsACString& aSecret,
+ const nsACString& aLabel) {
+ NS_ENSURE_STATE(mSlot);
+ if (NS_FAILED(Unlock())) {
+ MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug, ("Error unlocking NSS key db"));
+ return NS_ERROR_FAILURE;
+ }
+
+ // It is possible for multiple keys to have the same nickname in NSS. To
+ // prevent the problem of not knowing which key to use in the future, simply
+ // delete all keys with this nickname before storing a new one.
+ nsresult rv = DeleteSecret(aLabel);
+ if (NS_FAILED(rv)) {
+ MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug,
+ ("DeleteSecret before StoreSecret failed"));
+ return rv;
+ }
+
+ uint8_t* p = BitwiseCast<uint8_t*, const char*>(aSecret.BeginReading());
+ UniqueSECItem key(SECITEM_AllocItem(nullptr, nullptr, aSecret.Length()));
+ if (!key) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ key->type = siBuffer;
+ memcpy(key->data, p, aSecret.Length());
+ key->len = aSecret.Length();
+ UniquePK11SymKey symKey(
+ PK11_ImportSymKey(mSlot.get(), CKM_AES_GCM, PK11_OriginUnwrap,
+ CKA_DECRYPT | CKA_ENCRYPT, key.get(), nullptr));
+ if (!symKey) {
+ MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug, ("Error creating NSS SymKey"));
+ return NS_ERROR_FAILURE;
+ }
+ UniquePK11SymKey storedKey(
+ PK11_ConvertSessionSymKeyToTokenSymKey(symKey.get(), nullptr));
+ if (!storedKey) {
+ MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug,
+ ("Error storing NSS SymKey in DB"));
+ return NS_ERROR_FAILURE;
+ }
+ SECStatus srv =
+ PK11_SetSymKeyNickname(storedKey.get(), PromiseFlatCString(aLabel).get());
+ if (srv != SECSuccess) {
+ MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug, ("Error naming NSS SymKey"));
+ (void)PK11_DeleteTokenSymKey(storedKey.get());
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult NSSKeyStore::DeleteSecret(const nsACString& aLabel) {
+ NS_ENSURE_STATE(mSlot);
+ if (NS_FAILED(Unlock())) {
+ MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug, ("Error unlocking NSS key db"));
+ return NS_ERROR_FAILURE;
+ }
+
+ UniquePK11SymKey symKey(PK11_ListFixedKeysInSlot(
+ mSlot.get(), const_cast<char*>(PromiseFlatCString(aLabel).get()),
+ nullptr));
+ if (!symKey) {
+ // Couldn't find the key or something is wrong. Be nice.
+ return NS_OK;
+ }
+ for (PK11SymKey* tmp = symKey.get(); tmp; tmp = PK11_GetNextSymKey(tmp)) {
+ SECStatus srv = PK11_DeleteTokenSymKey(tmp);
+ if (srv != SECSuccess) {
+ MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug, ("Error deleting NSS SymKey"));
+ return NS_ERROR_FAILURE;
+ }
+ }
+ return NS_OK;
+}
+
+bool NSSKeyStore::SecretAvailable(const nsACString& aLabel) {
+ if (!mSlot) {
+ return false;
+ }
+ if (NS_FAILED(Unlock())) {
+ MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug, ("Error unlocking NSS key db"));
+ return false;
+ }
+
+ UniquePK11SymKey symKey(PK11_ListFixedKeysInSlot(
+ mSlot.get(), const_cast<char*>(PromiseFlatCString(aLabel).get()),
+ nullptr));
+ if (!symKey) {
+ return false;
+ }
+ return true;
+}
+
+nsresult NSSKeyStore::EncryptDecrypt(const nsACString& aLabel,
+ const std::vector<uint8_t>& inBytes,
+ std::vector<uint8_t>& outBytes,
+ bool encrypt) {
+ NS_ENSURE_STATE(mSlot);
+ if (NS_FAILED(Unlock())) {
+ MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug, ("Error unlocking NSS key db"));
+ return NS_ERROR_FAILURE;
+ }
+
+ UniquePK11SymKey symKey(PK11_ListFixedKeysInSlot(
+ mSlot.get(), const_cast<char*>(PromiseFlatCString(aLabel).get()),
+ nullptr));
+ if (!symKey) {
+ MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug,
+ ("Error finding key for given label"));
+ return NS_ERROR_FAILURE;
+ }
+ return DoCipher(symKey, inBytes, outBytes, encrypt);
+}
+
+// Because NSSKeyStore overrides AbstractOSKeyStore's EncryptDecrypt and
+// SecretAvailable functions, this isn't necessary.
+nsresult NSSKeyStore::RetrieveSecret(const nsACString& aLabel,
+ /* out */ nsACString& aSecret) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}