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