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