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 "mozilla/Base64.h"
10 #include "mozilla/SyncRunnable.h"
11 #include "nsNSSComponent.h"
12 #include "nsPK11TokenDB.h"
13 #include "nsXULAppAPI.h"
14 
15 /* Implementing OSKeyStore when there is no platform specific one.
16  * This key store instead puts the keys into the NSS DB.
17  */
18 
19 using namespace mozilla;
20 using mozilla::SyncRunnable;
21 
22 LazyLogModule gNSSKeyStoreLog("nsskeystore");
23 
NSSKeyStore()24 NSSKeyStore::NSSKeyStore() {
25   MOZ_ASSERT(XRE_IsParentProcess());
26   if (!XRE_IsParentProcess()) {
27     // This shouldn't happen as this is only initialised when creating the
28     // OSKeyStore, which is ParentProcessOnly.
29     return;
30   }
31   Unused << EnsureNSSInitializedChromeOrContent();
32   Unused << InitToken();
33 }
34 NSSKeyStore::~NSSKeyStore() = default;
35 
InitToken()36 nsresult NSSKeyStore::InitToken() {
37   if (!mSlot) {
38     mSlot = UniquePK11SlotInfo(PK11_GetInternalKeySlot());
39     if (!mSlot) {
40       MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug,
41               ("Error getting internal key slot"));
42       return NS_ERROR_NOT_AVAILABLE;
43     }
44   }
45   return NS_OK;
46 }
47 
NSSKeyStoreMainThreadLock(PK11SlotInfo * aSlot)48 nsresult NSSKeyStoreMainThreadLock(PK11SlotInfo* aSlot) {
49   nsCOMPtr<nsIPK11Token> token = new nsPK11Token(aSlot);
50   return token->LogoutSimple();
51 }
52 
Lock()53 nsresult NSSKeyStore::Lock() {
54   NS_ENSURE_STATE(mSlot);
55 
56   if (!NS_IsMainThread()) {
57     nsCOMPtr<nsIThread> mainThread;
58     nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
59     if (NS_FAILED(rv)) {
60       return NS_ERROR_FAILURE;
61     }
62 
63     // Forward to the main thread synchronously.
64     SyncRunnable::DispatchToThread(
65         mainThread, new SyncRunnable(NS_NewRunnableFunction(
66                         "NSSKeyStoreMainThreadLock", [slot = mSlot.get()]() {
67                           NSSKeyStoreMainThreadLock(slot);
68                         })));
69 
70     return NS_OK;
71   }
72 
73   return NSSKeyStoreMainThreadLock(mSlot.get());
74 }
75 
NSSKeyStoreMainThreadUnlock(PK11SlotInfo * aSlot)76 nsresult NSSKeyStoreMainThreadUnlock(PK11SlotInfo* aSlot) {
77   nsCOMPtr<nsIPK11Token> token = new nsPK11Token(aSlot);
78   return NS_FAILED(token->Login(false /* force */)) ? NS_ERROR_FAILURE : NS_OK;
79 }
80 
Unlock()81 nsresult NSSKeyStore::Unlock() {
82   NS_ENSURE_STATE(mSlot);
83 
84   if (!NS_IsMainThread()) {
85     nsCOMPtr<nsIThread> mainThread;
86     nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
87     if (NS_FAILED(rv)) {
88       return NS_ERROR_FAILURE;
89     }
90 
91     // Forward to the main thread synchronously.
92     nsresult result = NS_ERROR_FAILURE;
93     SyncRunnable::DispatchToThread(
94         mainThread, new SyncRunnable(NS_NewRunnableFunction(
95                         "NSSKeyStoreMainThreadUnlock",
96                         [slot = mSlot.get(), result = &result]() {
97                           *result = NSSKeyStoreMainThreadUnlock(slot);
98                         })));
99 
100     return result;
101   }
102 
103   return NSSKeyStoreMainThreadUnlock(mSlot.get());
104 }
105 
StoreSecret(const nsACString & aSecret,const nsACString & aLabel)106 nsresult NSSKeyStore::StoreSecret(const nsACString& aSecret,
107                                   const nsACString& aLabel) {
108   NS_ENSURE_STATE(mSlot);
109   if (NS_FAILED(Unlock())) {
110     MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug, ("Error unlocking NSS key db"));
111     return NS_ERROR_FAILURE;
112   }
113 
114   // It is possible for multiple keys to have the same nickname in NSS. To
115   // prevent the problem of not knowing which key to use in the future, simply
116   // delete all keys with this nickname before storing a new one.
117   nsresult rv = DeleteSecret(aLabel);
118   if (NS_FAILED(rv)) {
119     MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug,
120             ("DeleteSecret before StoreSecret failed"));
121     return rv;
122   }
123 
124   uint8_t* p = BitwiseCast<uint8_t*, const char*>(aSecret.BeginReading());
125   UniqueSECItem key(SECITEM_AllocItem(nullptr, nullptr, aSecret.Length()));
126   if (!key) {
127     return NS_ERROR_OUT_OF_MEMORY;
128   }
129   key->type = siBuffer;
130   memcpy(key->data, p, aSecret.Length());
131   key->len = aSecret.Length();
132   UniquePK11SymKey symKey(
133       PK11_ImportSymKey(mSlot.get(), CKM_AES_GCM, PK11_OriginUnwrap,
134                         CKA_DECRYPT | CKA_ENCRYPT, key.get(), nullptr));
135   if (!symKey) {
136     MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug, ("Error creating NSS SymKey"));
137     return NS_ERROR_FAILURE;
138   }
139   UniquePK11SymKey storedKey(
140       PK11_ConvertSessionSymKeyToTokenSymKey(symKey.get(), nullptr));
141   if (!storedKey) {
142     MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug,
143             ("Error storing NSS SymKey in DB"));
144     return NS_ERROR_FAILURE;
145   }
146   SECStatus srv =
147       PK11_SetSymKeyNickname(storedKey.get(), PromiseFlatCString(aLabel).get());
148   if (srv != SECSuccess) {
149     MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug, ("Error naming NSS SymKey"));
150     (void)PK11_DeleteTokenSymKey(storedKey.get());
151     return NS_ERROR_FAILURE;
152   }
153 
154   return NS_OK;
155 }
156 
DeleteSecret(const nsACString & aLabel)157 nsresult NSSKeyStore::DeleteSecret(const nsACString& aLabel) {
158   NS_ENSURE_STATE(mSlot);
159   if (NS_FAILED(Unlock())) {
160     MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug, ("Error unlocking NSS key db"));
161     return NS_ERROR_FAILURE;
162   }
163 
164   UniquePK11SymKey symKey(PK11_ListFixedKeysInSlot(
165       mSlot.get(), const_cast<char*>(PromiseFlatCString(aLabel).get()),
166       nullptr));
167   if (!symKey) {
168     // Couldn't find the key or something is wrong. Be nice.
169     return NS_OK;
170   }
171   for (PK11SymKey* tmp = symKey.get(); tmp; tmp = PK11_GetNextSymKey(tmp)) {
172     SECStatus srv = PK11_DeleteTokenSymKey(tmp);
173     if (srv != SECSuccess) {
174       MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug, ("Error deleting NSS SymKey"));
175       return NS_ERROR_FAILURE;
176     }
177   }
178   return NS_OK;
179 }
180 
SecretAvailable(const nsACString & aLabel)181 bool NSSKeyStore::SecretAvailable(const nsACString& aLabel) {
182   if (!mSlot) {
183     return false;
184   }
185   if (NS_FAILED(Unlock())) {
186     MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug, ("Error unlocking NSS key db"));
187     return false;
188   }
189 
190   UniquePK11SymKey symKey(PK11_ListFixedKeysInSlot(
191       mSlot.get(), const_cast<char*>(PromiseFlatCString(aLabel).get()),
192       nullptr));
193   if (!symKey) {
194     return false;
195   }
196   return true;
197 }
198 
EncryptDecrypt(const nsACString & aLabel,const std::vector<uint8_t> & inBytes,std::vector<uint8_t> & outBytes,bool encrypt)199 nsresult NSSKeyStore::EncryptDecrypt(const nsACString& aLabel,
200                                      const std::vector<uint8_t>& inBytes,
201                                      std::vector<uint8_t>& outBytes,
202                                      bool encrypt) {
203   NS_ENSURE_STATE(mSlot);
204   if (NS_FAILED(Unlock())) {
205     MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug, ("Error unlocking NSS key db"));
206     return NS_ERROR_FAILURE;
207   }
208 
209   UniquePK11SymKey symKey(PK11_ListFixedKeysInSlot(
210       mSlot.get(), const_cast<char*>(PromiseFlatCString(aLabel).get()),
211       nullptr));
212   if (!symKey) {
213     MOZ_LOG(gNSSKeyStoreLog, LogLevel::Debug,
214             ("Error finding key for given label"));
215     return NS_ERROR_FAILURE;
216   }
217   return DoCipher(symKey, inBytes, outBytes, encrypt);
218 }
219 
220 // Because NSSKeyStore overrides AbstractOSKeyStore's EncryptDecrypt and
221 // SecretAvailable functions, this isn't necessary.
RetrieveSecret(const nsACString & aLabel,nsACString & aSecret)222 nsresult NSSKeyStore::RetrieveSecret(const nsACString& aLabel,
223                                      /* out */ nsACString& aSecret) {
224   return NS_ERROR_NOT_IMPLEMENTED;
225 }
226