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