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, &paramsItem, 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, &paramsItem, 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