Bug 1928997: Update tabs icon in Unified Search popup r=desktop-theme-reviewers,daleh...
[gecko.git] / security / manager / ssl / OSKeyStore.cpp
blob796da4741c2f7f93549269b3802f945413314da8
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
7 #include "OSKeyStore.h"
9 #include "mozilla/Base64.h"
10 #include "mozilla/dom/Promise.h"
11 #include "nsThreadUtils.h"
12 #include "nsXPCOM.h"
13 #include "pk11pub.h"
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
26 NS_IMPL_ISUPPORTS(OSKeyStore, nsIOSKeyStore)
28 using namespace mozilla;
29 using dom::Promise;
31 OSKeyStore::OSKeyStore() : mKs(nullptr) {
32 MOZ_ASSERT(NS_IsMainThread());
33 if (NS_WARN_IF(!NS_IsMainThread())) {
34 return;
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());
47 #else
48 mKs.reset(new NSSKeyStore());
49 #endif
51 (void)NS_CreateBackgroundTaskQueue(
52 "OSKeyStore", getter_AddRefs(mBackgroundSerialEventTarget));
55 static nsresult GenerateRandom(std::vector<uint8_t>& r) {
56 if (r.empty()) {
57 return NS_ERROR_INVALID_ARG;
59 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
60 if (!slot) {
61 return NS_ERROR_FAILURE;
64 SECStatus srv = PK11_GenerateRandomOnSlot(slot.get(), r.data(), r.size());
65 if (srv != SECSuccess) {
66 r.clear();
67 return NS_ERROR_FAILURE;
70 return NS_OK;
73 nsresult OSKeyStore::SecretAvailable(const nsACString& aLabel,
74 /* out */ bool* aAvailable) {
75 NS_ENSURE_STATE(mKs);
76 *aAvailable = mKs->SecretAvailable(aLabel);
77 return NS_OK;
80 nsresult OSKeyStore::GenerateSecret(const nsACString& aLabel,
81 /* out */ nsACString& aRecoveryPhrase) {
82 NS_ENSURE_STATE(mKs);
83 size_t keyByteLength = mKs->GetKeyByteLength();
84 std::vector<uint8_t> secret(keyByteLength);
85 nsresult rv = GenerateRandom(secret);
86 if (NS_FAILED(rv) || secret.size() != keyByteLength) {
87 return NS_ERROR_FAILURE;
89 nsAutoCString secretString;
90 secretString.Assign(BitwiseCast<char*, uint8_t*>(secret.data()),
91 secret.size());
93 nsCString base64;
94 rv = Base64Encode(secretString, base64);
95 if (NS_FAILED(rv)) {
96 return rv;
99 rv = mKs->StoreSecret(secretString, aLabel);
100 if (NS_FAILED(rv)) {
101 return rv;
104 aRecoveryPhrase = std::move(base64);
105 return NS_OK;
108 nsresult OSKeyStore::RecoverSecret(const nsACString& aLabel,
109 const nsACString& aRecoveryPhrase) {
110 NS_ENSURE_STATE(mKs);
111 nsAutoCString secret;
112 nsresult rv = Base64Decode(aRecoveryPhrase, secret);
113 if (NS_FAILED(rv)) {
114 return rv;
116 if (secret.Length() != mKs->GetKeyByteLength()) {
117 return NS_ERROR_INVALID_ARG;
119 rv = mKs->StoreSecret(secret, aLabel);
120 if (NS_FAILED(rv)) {
121 return rv;
124 return NS_OK;
127 nsresult OSKeyStore::DeleteSecret(const nsACString& aLabel) {
128 NS_ENSURE_STATE(mKs);
129 return mKs->DeleteSecret(aLabel);
132 nsresult OSKeyStore::RetrieveRecoveryPhrase(
133 const nsACString& aLabel,
134 /* out */ nsACString& aRecoveryPhrase) {
135 NS_ENSURE_STATE(mKs);
136 nsAutoCString secretString;
137 nsresult rv = mKs->RetrieveSecret(aLabel, secretString);
138 if (NS_FAILED(rv)) {
139 return rv;
142 nsCString recoveryPhrase;
143 rv = Base64Encode(secretString, recoveryPhrase);
144 if (NS_FAILED(rv)) {
145 return rv;
148 aRecoveryPhrase = std::move(recoveryPhrase);
149 return NS_OK;
152 enum Cipher { Encrypt = true, Decrypt = false };
154 nsresult OSKeyStore::EncryptBytes(const nsACString& aLabel,
155 const std::vector<uint8_t>& aInBytes,
156 /*out*/ nsACString& aEncryptedBase64Text) {
157 NS_ENSURE_STATE(mKs);
159 aEncryptedBase64Text.Truncate();
160 std::vector<uint8_t> outBytes;
161 nsresult rv =
162 mKs->EncryptDecrypt(aLabel, aInBytes, outBytes, Cipher::Encrypt);
163 if (NS_FAILED(rv)) {
164 return rv;
166 nsAutoCString ciphertext;
167 ciphertext.Assign(BitwiseCast<char*, uint8_t*>(outBytes.data()),
168 outBytes.size());
170 nsCString base64ciphertext;
171 rv = Base64Encode(ciphertext, base64ciphertext);
172 if (NS_FAILED(rv)) {
173 return rv;
175 aEncryptedBase64Text = std::move(base64ciphertext);
176 return NS_OK;
179 nsresult OSKeyStore::DecryptBytes(const nsACString& aLabel,
180 const nsACString& aEncryptedBase64Text,
181 /*out*/ uint32_t* outLen,
182 /*out*/ uint8_t** outBytes) {
183 NS_ENSURE_STATE(mKs);
184 NS_ENSURE_ARG_POINTER(outLen);
185 NS_ENSURE_ARG_POINTER(outBytes);
186 *outLen = 0;
187 *outBytes = nullptr;
189 nsAutoCString ciphertext;
190 nsresult rv = Base64Decode(aEncryptedBase64Text, ciphertext);
191 if (NS_FAILED(rv)) {
192 return rv;
194 uint8_t* tmp = BitwiseCast<uint8_t*, const char*>(ciphertext.BeginReading());
195 const std::vector<uint8_t> ciphertextBytes(tmp, tmp + ciphertext.Length());
196 std::vector<uint8_t> plaintextBytes;
197 rv = mKs->EncryptDecrypt(aLabel, ciphertextBytes, plaintextBytes,
198 Cipher::Decrypt);
199 if (NS_FAILED(rv)) {
200 return rv;
203 *outBytes = (uint8_t*)moz_xmalloc(plaintextBytes.size());
204 memcpy(*outBytes, plaintextBytes.data(), plaintextBytes.size());
205 *outLen = plaintextBytes.size();
206 return NS_OK;
209 // Async interfaces that return promises because the key store implementation
210 // might block, e.g. asking for a password.
212 nsresult GetPromise(JSContext* aCx, /* out */ RefPtr<Promise>& aPromise) {
213 nsIGlobalObject* globalObject = xpc::CurrentNativeGlobal(aCx);
214 if (NS_WARN_IF(!globalObject)) {
215 return NS_ERROR_UNEXPECTED;
217 ErrorResult result;
218 aPromise = Promise::Create(globalObject, result);
219 if (NS_WARN_IF(result.Failed())) {
220 return result.StealNSResult();
222 return NS_OK;
225 void BackgroundGenerateSecret(const nsACString& aLabel,
226 RefPtr<Promise>& aPromise,
227 RefPtr<OSKeyStore> self) {
228 nsAutoCString recovery;
229 nsresult rv = self->GenerateSecret(aLabel, recovery);
230 nsAutoString recoveryString;
231 if (NS_SUCCEEDED(rv)) {
232 CopyUTF8toUTF16(recovery, recoveryString);
234 nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
235 "BackgroundGenerateSecreteOSKSResolve",
236 [rv, aPromise = std::move(aPromise), recoveryString]() {
237 if (NS_FAILED(rv)) {
238 aPromise->MaybeReject(rv);
239 } else {
240 aPromise->MaybeResolve(recoveryString);
242 }));
243 NS_DispatchToMainThread(runnable.forget());
246 NS_IMETHODIMP
247 OSKeyStore::AsyncGenerateSecret(const nsACString& aLabel, JSContext* aCx,
248 Promise** promiseOut) {
249 MOZ_ASSERT(NS_IsMainThread());
250 if (!NS_IsMainThread()) {
251 return NS_ERROR_NOT_SAME_THREAD;
254 NS_ENSURE_ARG_POINTER(aCx);
256 if (!mBackgroundSerialEventTarget) {
257 return NS_ERROR_NOT_AVAILABLE;
260 RefPtr<Promise> promiseHandle;
261 nsresult rv = GetPromise(aCx, promiseHandle);
262 if (NS_FAILED(rv)) {
263 return rv;
266 RefPtr<OSKeyStore> self = this;
267 nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
268 "BackgroundGenerateSecret",
269 [self, promiseHandle, aLabel = nsAutoCString(aLabel)]() mutable {
270 BackgroundGenerateSecret(aLabel, promiseHandle, self);
271 }));
273 promiseHandle.forget(promiseOut);
274 return mBackgroundSerialEventTarget->Dispatch(runnable.forget(),
275 NS_DISPATCH_EVENT_MAY_BLOCK);
278 void BackgroundSecretAvailable(const nsACString& aLabel,
279 RefPtr<Promise>& aPromise,
280 RefPtr<OSKeyStore> self) {
281 bool available = false;
282 nsresult rv = self->SecretAvailable(aLabel, &available);
283 nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
284 "BackgroundSecreteAvailableOSKSResolve",
285 [rv, aPromise = std::move(aPromise), available = available]() {
286 if (NS_FAILED(rv)) {
287 aPromise->MaybeReject(rv);
288 } else {
289 aPromise->MaybeResolve(available);
291 }));
292 NS_DispatchToMainThread(runnable.forget());
295 NS_IMETHODIMP
296 OSKeyStore::AsyncSecretAvailable(const nsACString& aLabel, JSContext* aCx,
297 Promise** promiseOut) {
298 MOZ_ASSERT(NS_IsMainThread());
299 if (!NS_IsMainThread()) {
300 return NS_ERROR_NOT_SAME_THREAD;
303 NS_ENSURE_ARG_POINTER(aCx);
305 if (!mBackgroundSerialEventTarget) {
306 return NS_ERROR_NOT_AVAILABLE;
309 RefPtr<Promise> promiseHandle;
310 nsresult rv = GetPromise(aCx, promiseHandle);
311 if (NS_FAILED(rv)) {
312 return rv;
315 RefPtr<OSKeyStore> self = this;
316 nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
317 "BackgroundSecretAvailable",
318 [self, promiseHandle, aLabel = nsAutoCString(aLabel)]() mutable {
319 BackgroundSecretAvailable(aLabel, promiseHandle, self);
320 }));
322 promiseHandle.forget(promiseOut);
323 return mBackgroundSerialEventTarget->Dispatch(runnable.forget(),
324 NS_DISPATCH_EVENT_MAY_BLOCK);
327 void BackgroundRecoverSecret(const nsACString& aLabel,
328 const nsACString& aRecoveryPhrase,
329 RefPtr<Promise>& aPromise,
330 RefPtr<OSKeyStore> self) {
331 nsresult rv = self->RecoverSecret(aLabel, aRecoveryPhrase);
332 nsCOMPtr<nsIRunnable> runnable(
333 NS_NewRunnableFunction("BackgroundRecoverSecreteOSKSResolve",
334 [rv, aPromise = std::move(aPromise)]() {
335 if (NS_FAILED(rv)) {
336 aPromise->MaybeReject(rv);
337 } else {
338 aPromise->MaybeResolveWithUndefined();
340 }));
341 NS_DispatchToMainThread(runnable.forget());
344 NS_IMETHODIMP
345 OSKeyStore::AsyncRecoverSecret(const nsACString& aLabel,
346 const nsACString& aRecoveryPhrase,
347 JSContext* aCx, Promise** promiseOut) {
348 MOZ_ASSERT(NS_IsMainThread());
349 if (!NS_IsMainThread()) {
350 return NS_ERROR_NOT_SAME_THREAD;
353 NS_ENSURE_ARG_POINTER(aCx);
355 if (!mBackgroundSerialEventTarget) {
356 return NS_ERROR_NOT_AVAILABLE;
359 RefPtr<Promise> promiseHandle;
360 nsresult rv = GetPromise(aCx, promiseHandle);
361 if (NS_FAILED(rv)) {
362 return rv;
365 RefPtr<OSKeyStore> self = this;
366 nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
367 "BackgroundRecoverSecret",
368 [self, promiseHandle, aLabel = nsAutoCString(aLabel),
369 aRecoveryPhrase = nsAutoCString(aRecoveryPhrase)]() mutable {
370 BackgroundRecoverSecret(aLabel, aRecoveryPhrase, promiseHandle, self);
371 }));
373 promiseHandle.forget(promiseOut);
374 return mBackgroundSerialEventTarget->Dispatch(runnable.forget(),
375 NS_DISPATCH_EVENT_MAY_BLOCK);
378 void BackgroundDeleteSecret(const nsACString& aLabel, RefPtr<Promise>& aPromise,
379 RefPtr<OSKeyStore> self) {
380 nsresult rv = self->DeleteSecret(aLabel);
381 nsCOMPtr<nsIRunnable> runnable(
382 NS_NewRunnableFunction("BackgroundDeleteSecreteOSKSResolve",
383 [rv, aPromise = std::move(aPromise)]() {
384 if (NS_FAILED(rv)) {
385 aPromise->MaybeReject(rv);
386 } else {
387 aPromise->MaybeResolveWithUndefined();
389 }));
390 NS_DispatchToMainThread(runnable.forget());
393 NS_IMETHODIMP
394 OSKeyStore::AsyncDeleteSecret(const nsACString& aLabel, JSContext* aCx,
395 Promise** promiseOut) {
396 MOZ_ASSERT(NS_IsMainThread());
397 if (!NS_IsMainThread()) {
398 return NS_ERROR_NOT_SAME_THREAD;
401 NS_ENSURE_ARG_POINTER(aCx);
403 if (!mBackgroundSerialEventTarget) {
404 return NS_ERROR_NOT_AVAILABLE;
407 RefPtr<Promise> promiseHandle;
408 nsresult rv = GetPromise(aCx, promiseHandle);
409 if (NS_FAILED(rv)) {
410 return rv;
413 RefPtr<OSKeyStore> self = this;
414 nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
415 "BackgroundDeleteSecret",
416 [self, promiseHandle, aLabel = nsAutoCString(aLabel)]() mutable {
417 BackgroundDeleteSecret(aLabel, promiseHandle, self);
418 }));
420 promiseHandle.forget(promiseOut);
421 return mBackgroundSerialEventTarget->Dispatch(runnable.forget(),
422 NS_DISPATCH_EVENT_MAY_BLOCK);
425 static void BackgroundEncryptBytes(const nsACString& aLabel,
426 const std::vector<uint8_t>& aInBytes,
427 RefPtr<Promise>& aPromise,
428 RefPtr<OSKeyStore> self) {
429 nsAutoCString ciphertext;
430 nsresult rv = self->EncryptBytes(aLabel, aInBytes, ciphertext);
431 nsAutoString ctext;
432 CopyUTF8toUTF16(ciphertext, ctext);
434 nsCOMPtr<nsIRunnable> runnable(
435 NS_NewRunnableFunction("BackgroundEncryptOSKSResolve",
436 [rv, aPromise = std::move(aPromise), ctext]() {
437 if (NS_FAILED(rv)) {
438 aPromise->MaybeReject(rv);
439 } else {
440 aPromise->MaybeResolve(ctext);
442 }));
443 NS_DispatchToMainThread(runnable.forget());
446 NS_IMETHODIMP
447 OSKeyStore::AsyncEncryptBytes(const nsACString& aLabel,
448 const nsTArray<uint8_t>& inBytes, JSContext* aCx,
449 Promise** promiseOut) {
450 MOZ_ASSERT(NS_IsMainThread());
451 if (!NS_IsMainThread()) {
452 return NS_ERROR_NOT_SAME_THREAD;
455 NS_ENSURE_ARG_POINTER(aCx);
457 if (!mBackgroundSerialEventTarget) {
458 return NS_ERROR_NOT_AVAILABLE;
461 RefPtr<Promise> promiseHandle;
462 nsresult rv = GetPromise(aCx, promiseHandle);
463 if (NS_FAILED(rv)) {
464 return rv;
467 RefPtr<OSKeyStore> self = this;
468 nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
469 "BackgroundEncryptBytes",
470 [promiseHandle,
471 inBytes = std::vector<uint8_t>(inBytes.Elements(),
472 inBytes.Elements() + inBytes.Length()),
473 aLabel = nsAutoCString(aLabel), self]() mutable {
474 BackgroundEncryptBytes(aLabel, inBytes, promiseHandle, self);
475 }));
477 promiseHandle.forget(promiseOut);
478 return mBackgroundSerialEventTarget->Dispatch(runnable.forget(),
479 NS_DISPATCH_EVENT_MAY_BLOCK);
482 void BackgroundDecryptBytes(const nsACString& aLabel,
483 const nsACString& aEncryptedBase64Text,
484 RefPtr<Promise>& aPromise,
485 RefPtr<OSKeyStore> self) {
486 uint8_t* plaintext = nullptr;
487 uint32_t plaintextLen = 0;
488 nsresult rv = self->DecryptBytes(aLabel, aEncryptedBase64Text, &plaintextLen,
489 &plaintext);
490 nsTArray<uint8_t> plain;
491 if (plaintext) {
492 MOZ_ASSERT(plaintextLen > 0);
493 plain.AppendElements(plaintext, plaintextLen);
494 free(plaintext);
497 nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
498 "BackgroundDecryptOSKSResolve",
499 [rv, aPromise = std::move(aPromise), plain = std::move(plain)]() {
500 if (NS_FAILED(rv)) {
501 aPromise->MaybeReject(rv);
502 } else {
503 aPromise->MaybeResolve(plain);
505 }));
506 NS_DispatchToMainThread(runnable.forget());
509 NS_IMETHODIMP
510 OSKeyStore::AsyncDecryptBytes(const nsACString& aLabel,
511 const nsACString& aEncryptedBase64Text,
512 JSContext* aCx, Promise** promiseOut) {
513 MOZ_ASSERT(NS_IsMainThread());
514 if (!NS_IsMainThread()) {
515 return NS_ERROR_NOT_SAME_THREAD;
518 NS_ENSURE_ARG_POINTER(aCx);
520 if (!mBackgroundSerialEventTarget) {
521 return NS_ERROR_NOT_AVAILABLE;
524 RefPtr<Promise> promiseHandle;
525 nsresult rv = GetPromise(aCx, promiseHandle);
526 if (NS_FAILED(rv)) {
527 return rv;
530 RefPtr<OSKeyStore> self = this;
531 nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
532 "BackgroundDecryptBytes",
533 [promiseHandle, self,
534 aEncryptedBase64Text = nsAutoCString(aEncryptedBase64Text),
535 aLabel = nsAutoCString(aLabel)]() mutable {
536 BackgroundDecryptBytes(aLabel, aEncryptedBase64Text, promiseHandle,
537 self);
538 }));
540 promiseHandle.forget(promiseOut);
541 return mBackgroundSerialEventTarget->Dispatch(runnable.forget(),
542 NS_DISPATCH_EVENT_MAY_BLOCK);
545 void BackgroundGetRecoveryPhrase(const nsACString& aLabel,
546 RefPtr<Promise>& aPromise,
547 const RefPtr<OSKeyStore>& self) {
548 nsAutoCString recoveryPhrase;
549 nsresult rv = self->RetrieveRecoveryPhrase(aLabel, recoveryPhrase);
550 nsAutoString exportedRecoveryPhrase;
551 if (NS_SUCCEEDED(rv)) {
552 CopyUTF8toUTF16(recoveryPhrase, exportedRecoveryPhrase);
554 nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
555 "BackgroundRetrieveRecoveryPhraseResolve",
556 [rv, aPromise = std::move(aPromise), exportedRecoveryPhrase]() {
557 if (NS_FAILED(rv)) {
558 aPromise->MaybeReject(rv);
559 } else {
560 aPromise->MaybeResolve(exportedRecoveryPhrase);
562 }));
563 NS_DispatchToMainThread(runnable.forget());
566 NS_IMETHODIMP
567 OSKeyStore::AsyncGetRecoveryPhrase(const nsACString& aLabel, JSContext* aCx,
568 Promise** promiseOut) {
569 MOZ_ASSERT(NS_IsMainThread());
570 if (!NS_IsMainThread()) {
571 return NS_ERROR_NOT_SAME_THREAD;
574 NS_ENSURE_ARG_POINTER(aCx);
576 if (!mBackgroundSerialEventTarget) {
577 return NS_ERROR_NOT_AVAILABLE;
580 RefPtr<Promise> promiseHandle;
581 nsresult rv = GetPromise(aCx, promiseHandle);
582 if (NS_FAILED(rv)) {
583 return rv;
586 RefPtr<OSKeyStore> self = this;
587 nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
588 "BackgroundGetRecoveryPhrase",
589 [promiseHandle, self, aLabel = nsAutoCString(aLabel)]() mutable {
590 BackgroundGetRecoveryPhrase(aLabel, promiseHandle, self);
591 }));
593 promiseHandle.forget(promiseOut);
594 return mBackgroundSerialEventTarget->Dispatch(runnable.forget(),
595 NS_DISPATCH_EVENT_MAY_BLOCK);
598 // Generic AES-GCM cipher wrapper for NSS functions.
600 nsresult AbstractOSKeyStore::BuildAesGcmKey(std::vector<uint8_t> aKeyBytes,
601 /* out */ UniquePK11SymKey& aKey) {
602 if (aKeyBytes.size() != mKeyByteLength) {
603 return NS_ERROR_INVALID_ARG;
606 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
607 if (!slot) {
608 return NS_ERROR_FAILURE;
611 UniqueSECItem key =
612 UniqueSECItem(SECITEM_AllocItem(nullptr, nullptr, mKeyByteLength));
613 if (!key) {
614 return NS_ERROR_FAILURE;
616 key->type = siBuffer;
617 memcpy(key->data, aKeyBytes.data(), mKeyByteLength);
618 key->len = mKeyByteLength;
620 UniquePK11SymKey symKey(
621 PK11_ImportSymKey(slot.get(), CKM_AES_GCM, PK11_OriginUnwrap,
622 CKA_DECRYPT | CKA_ENCRYPT, key.get(), nullptr));
624 if (!symKey) {
625 return NS_ERROR_FAILURE;
627 aKey.swap(symKey);
629 return NS_OK;
632 nsresult AbstractOSKeyStore::DoCipher(const UniquePK11SymKey& aSymKey,
633 const std::vector<uint8_t>& inBytes,
634 std::vector<uint8_t>& outBytes,
635 bool encrypt) {
636 NS_ENSURE_ARG_POINTER(aSymKey);
637 outBytes.clear();
639 // Build params.
640 // We need to get the IV from inBytes if we decrypt.
641 if (!encrypt && (inBytes.size() < mIVLength || inBytes.empty())) {
642 return NS_ERROR_INVALID_ARG;
645 const uint8_t* ivp = nullptr;
646 std::vector<uint8_t> ivBuf;
647 if (encrypt) {
648 // Generate a new IV.
649 ivBuf.resize(mIVLength);
650 nsresult rv = GenerateRandom(ivBuf);
651 if (NS_FAILED(rv) || ivBuf.size() != mIVLength) {
652 return NS_ERROR_FAILURE;
654 ivp = ivBuf.data();
655 } else {
656 // An IV was passed in. Use the first mIVLength bytes from inBytes as IV.
657 ivp = inBytes.data();
660 CK_GCM_PARAMS gcm_params;
661 gcm_params.pIv = const_cast<unsigned char*>(ivp);
662 gcm_params.ulIvLen = mIVLength;
663 gcm_params.ulIvBits = gcm_params.ulIvLen * 8;
664 gcm_params.ulTagBits = 128;
665 gcm_params.pAAD = nullptr;
666 gcm_params.ulAADLen = 0;
668 SECItem paramsItem = {siBuffer, reinterpret_cast<unsigned char*>(&gcm_params),
669 sizeof(CK_GCM_PARAMS)};
671 size_t blockLength = 16;
672 outBytes.resize(inBytes.size() + blockLength);
673 unsigned int outLen = 0;
674 SECStatus srv = SECFailure;
675 if (encrypt) {
676 srv = PK11_Encrypt(aSymKey.get(), CKM_AES_GCM, &paramsItem, outBytes.data(),
677 &outLen, inBytes.size() + blockLength, inBytes.data(),
678 inBytes.size());
679 // Prepend the used IV to the ciphertext.
680 Unused << outBytes.insert(outBytes.begin(), ivp, ivp + mIVLength);
681 outLen += mIVLength;
682 } else {
683 // Remove the IV from the input.
684 std::vector<uint8_t> input(inBytes);
685 input.erase(input.begin(), input.begin() + mIVLength);
686 srv = PK11_Decrypt(aSymKey.get(), CKM_AES_GCM, &paramsItem, outBytes.data(),
687 &outLen, input.size() + blockLength, input.data(),
688 input.size());
690 if (srv != SECSuccess || outLen > outBytes.size()) {
691 outBytes.clear();
692 return NS_ERROR_FAILURE;
694 if (outLen < outBytes.size()) {
695 outBytes.resize(outLen);
698 return NS_OK;
701 bool AbstractOSKeyStore::SecretAvailable(const nsACString& aLabel) {
702 nsAutoCString secret;
703 nsresult rv = RetrieveSecret(aLabel, secret);
704 if (NS_FAILED(rv) || secret.Length() == 0) {
705 return false;
707 return true;
710 nsresult AbstractOSKeyStore::EncryptDecrypt(const nsACString& aLabel,
711 const std::vector<uint8_t>& inBytes,
712 std::vector<uint8_t>& outBytes,
713 bool encrypt) {
714 nsAutoCString secret;
715 nsresult rv = RetrieveSecret(aLabel, secret);
716 if (NS_FAILED(rv) || secret.Length() == 0) {
717 return NS_ERROR_FAILURE;
720 uint8_t* p = BitwiseCast<uint8_t*, const char*>(secret.BeginReading());
721 std::vector<uint8_t> buf(p, p + secret.Length());
722 UniquePK11SymKey symKey;
723 rv = BuildAesGcmKey(buf, symKey);
724 if (NS_FAILED(rv)) {
725 return NS_ERROR_FAILURE;
727 return DoCipher(symKey, inBytes, outBytes, encrypt);