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