Files
Benjamin Beurdouche d9677d8635 Bug 2055631 - Rename the "collection" terminology to DEK name in Lockstore r=simonf
Per review, drop the "collection" terminology and talk about DEKs
directly: a DEK is identified by a name, so "collection" /
"collection_name" becomes "dekName" / "dek_name" across the public IDL,
the C++ LockstoreService, the Rust core + FFI, the gtests, the xpcshell
test and the cargo-test suites.

Pure rename with no behaviour change: the runtime DEK-name values, the
on-disk row keys and the per-DEK data namespace are all unchanged; only
identifiers and doc wording move from "collection" to "DEK name".
Verified by the lockstore gtest, xpcshell and cargo-test suites.

Differential Revision: https://phabricator.services.mozilla.com/D309537
2026-07-16 23:37:19 +00:00

629 lines
23 KiB
C++

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "LockstoreService.h"
#include <cstring>
#include <type_traits>
#include <utility>
#include "mozilla/AppShutdown.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Result.h"
#include "mozilla/Services.h"
#include "mozilla/dom/Promise.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsCOMPtr.h"
#include "nsDirectoryServiceUtils.h"
#include "nsIFile.h"
#include "nsIGlobalObject.h"
#include "nsIObserverService.h"
#include "nsProxyRelease.h"
#include "nsServiceManagerUtils.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsThreadUtils.h"
#include "secport.h"
#include "xpcpublic.h"
namespace mozilla::security::lockstore {
using dom::Promise;
NS_IMPL_ISUPPORTS(LockstoreService, nsILockstore, nsIObserver)
LockstoreService::LockstoreService()
: mMutex("LockstoreService::mMutex"),
mKeystore(nullptr),
mShutdown(false) {}
LockstoreService::~LockstoreService() {
// No mutex needed: the destructor runs only after the last refcount
// drops, so no other thread can be racing the in-flight FFI callbacks
// (they each hold a `RefPtr<LockstoreService>`).
if (mKeystore) {
keystore_close(mKeystore);
mKeystore = nullptr;
}
}
nsresult LockstoreService::Init() {
MOZ_ASSERT(NS_IsMainThread());
// A consumer that creates the service at or beyond XPCOMWillShutdown would
// register a teardown observer that never fires and open a keystore that is
// never closed; mark it shut down so EnsureOpenLocked fails closed instead.
// (Mirrors InitEncryptionKeystore's late-init guard.)
if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMWillShutdown)) {
MutexAutoLock lock(mMutex);
mShutdown = true;
return NS_OK;
}
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (os) {
// The profile may not be selected yet if a consumer creates the service in
// early startup (NS_APP_USER_PROFILE_50_DIR not yet resolvable), so resolve
// the path when it becomes available too. profile-do-change fires once per
// process -- profile switching relaunches Firefox -- so the path is
// resolved at most once and stays valid for the process lifetime.
os->AddObserver(this, "profile-do-change", false);
// Tear down last, at XPCOMWillShutdown, so the keystore stays available for
// late profile writes by its consumers (SQLite at-rest encryption, profile
// backup, caches). xpcom-shutdown stays as an idempotent backstop.
os->AddObserver(this, "xpcom-will-shutdown", false);
os->AddObserver(this, "xpcom-shutdown", false);
}
// Common case: the service is created after profile-do-change, so the profile
// is already available -- resolve now. Otherwise the observer above resolves
// it; EnsureOpenLocked fails closed until then.
CacheProfilePathOnMainThread();
return NS_OK;
}
void LockstoreService::CacheProfilePathOnMainThread() {
MOZ_ASSERT(NS_IsMainThread());
{
MutexAutoLock lock(mMutex);
if (!mProfilePath.IsEmpty()) {
// profile-do-change fires once, but stay idempotent.
return;
}
}
// Resolve outside the lock: `nsIDirectoryService::Get` is main-thread only,
// and holding mMutex across it would block any off-main `Do*` waiting on the
// mutex behind the directory lookup.
nsCOMPtr<nsIFile> profileDir;
if (NS_FAILED(NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
getter_AddRefs(profileDir))) ||
!profileDir) {
// Profile not selected yet; resolved later on profile-do-change.
return;
}
nsAutoString widePath;
if (NS_FAILED(profileDir->GetPath(widePath))) {
return;
}
MutexAutoLock lock(mMutex);
CopyUTF16toUTF8(widePath, mProfilePath);
}
// static
already_AddRefed<LockstoreService> LockstoreService::GetSingleton() {
nsCOMPtr<nsILockstore> svc =
do_GetService("@mozilla.org/security/lockstore;1");
if (!svc) {
return nullptr;
}
// The component is registered as a singleton in components.conf and
// LockstoreService is the only implementer of nsILockstore in tree, so
// a downcast is safe.
RefPtr<LockstoreService> ls = static_cast<LockstoreService*>(svc.get());
return ls.forget();
}
nsresult LockstoreService::EnsureOpenLocked() {
mMutex.AssertCurrentThreadOwns();
if (mShutdown) {
return NS_ERROR_NOT_AVAILABLE;
}
if (mKeystore) {
return NS_OK;
}
if (mProfilePath.IsEmpty()) {
// The profile is not available yet -- a consumer reached the FFI before
// profile-do-change resolved the path (only possible if the service is
// created in early startup, before profile selection). Fail closed; the
// caller may retry once the profile is selected.
return NS_ERROR_NOT_AVAILABLE;
}
return keystore_open(&mProfilePath, &mKeystore);
}
// ---------------------------------------------------------------------------
// nsIObserver
// ---------------------------------------------------------------------------
NS_IMETHODIMP
LockstoreService::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData) {
if (!strcmp(aTopic, "profile-do-change")) {
// Resolve the profile path now that the profile is available; covers the
// early-startup case where Init() ran before profile selection.
CacheProfilePathOnMainThread();
return NS_OK;
}
// xpcom-will-shutdown / xpcom-shutdown: tear down the keystore.
// mMutex is held across every FFI call by the sync `Do*` tier, so
// acquiring it here implicitly drains any in-flight background work:
// a running encrypt/decrypt holds the mutex and blocks us until it
// finishes; a runnable that hasn't yet started will see mShutdown ==
// true and fail via `EnsureOpenLocked`.
MutexAutoLock lock(mMutex);
if (mShutdown) {
return NS_OK;
}
mShutdown = true;
if (mKeystore) {
keystore_close(mKeystore);
mKeystore = nullptr;
}
return NS_OK;
}
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
namespace {
nsresult NewDOMPromise(JSContext* aCx, RefPtr<Promise>& aOut) {
nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
if (NS_WARN_IF(!global)) {
return NS_ERROR_UNEXPECTED;
}
ErrorResult err;
aOut = Promise::Create(global, err);
if (NS_WARN_IF(err.Failed())) {
return err.StealNSResult();
}
return NS_OK;
}
// An nsCString that scrubs its heap buffer on destruction. Used to carry
// a secret (password / PIN) through the async dispatch so the copy that
// crosses to the background task -- the longest-lived C++ copy -- is wiped
// once the FFI has consumed it. `Get()` hands back a `const nsACString&`
// so the `Do*` methods keep their existing signatures; `Unwrap` calls it
// during dispatch. The XPConnect marshalling buffer and the JS string
// itself are outside our control.
class ZeroizingCString {
public:
explicit ZeroizingCString(const nsACString& aSrc) : mStr(aSrc) {}
ZeroizingCString(ZeroizingCString&& aOther) = default;
ZeroizingCString& operator=(ZeroizingCString&& aOther) {
if (this != &aOther) {
Wipe();
mStr = std::move(aOther.mStr);
}
return *this;
}
ZeroizingCString(const ZeroizingCString&) = delete;
ZeroizingCString& operator=(const ZeroizingCString&) = delete;
~ZeroizingCString() { Wipe(); }
const nsACString& Get() const { return mStr; }
private:
void Wipe() {
if (mStr.Length() > 0) {
PORT_SafeZero(mStr.BeginWriting(), mStr.Length());
}
}
nsCString mStr;
};
// As `ZeroizingCString`, but for raw key bytes (the imported DEK). Scrubs
// its backing store on destruction; `Get()` hands back a
// `const nsTArray&` for `Unwrap` to feed the `Do*` methods.
class ZeroizingByteArray {
public:
explicit ZeroizingByteArray(nsTArray<uint8_t>&& aArr)
: mArr(std::move(aArr)) {}
ZeroizingByteArray(ZeroizingByteArray&& aOther) = default;
ZeroizingByteArray& operator=(ZeroizingByteArray&& aOther) {
if (this != &aOther) {
Wipe();
mArr = std::move(aOther.mArr);
}
return *this;
}
ZeroizingByteArray(const ZeroizingByteArray&) = delete;
ZeroizingByteArray& operator=(const ZeroizingByteArray&) = delete;
~ZeroizingByteArray() { Wipe(); }
const nsTArray<uint8_t>& Get() const { return mArr; }
private:
void Wipe() {
if (!mArr.IsEmpty()) {
PORT_SafeZero(mArr.Elements(), mArr.Length());
}
}
nsTArray<uint8_t> mArr;
};
// Bridges the stored dispatch arguments to the `Do*` method parameters.
// Non-wrapper storages pass through unchanged; the zeroizing wrappers
// hand back a reference to their backing store via `Get()`.
template <typename T>
const T& Unwrap(const T& aArg) {
return aArg;
}
inline const nsACString& Unwrap(const ZeroizingCString& aArg) {
return aArg.Get();
}
inline const nsTArray<uint8_t>& Unwrap(const ZeroizingByteArray& aArg) {
return aArg.Get();
}
// Dispatches a sync `Do*` method onto a background task and bridges
// the result to a DOM Promise. `Result` is the return type of the
// method — either `nsresult` (resolves with undefined) or
// `mozilla::Result<T, nsresult>` (resolves with T). One template
// covers every method shape; the constexpr branch picks the right
// resolution at the bridge.
template <typename... Storages, typename Result, typename... Args>
nsresult ImplXpcomMethod(LockstoreService* aLockstore, JSContext* aCx,
Promise** aPromise,
Result (LockstoreService::*aMethod)(Args...),
Storages... aArgs) {
MOZ_RELEASE_ASSERT(NS_IsMainThread());
RefPtr<Promise> domPromise;
MOZ_TRY(NewDOMPromise(aCx, domPromise));
// Wrap the DOM promise so its addref / release stay on the main
// thread: the lambdas below run on a background task and a
// main-thread runnable, but capturing a bare `RefPtr<Promise>`
// would bump the refcount on the background thread when the inner
// lambda is constructed.
nsMainThreadPtrHandle<Promise> domHandle(new nsMainThreadPtrHolder<Promise>(
"LockstoreService::ImplXpcomMethod::DOMPromise", domPromise));
nsresult rv = NS_DispatchBackgroundTask(NS_NewRunnableFunction(
"LockstoreService::ImplXpcomMethod",
[self = RefPtr{aLockstore}, aMethod, domHandle,
... args = std::forward<Storages>(aArgs)]() mutable {
auto result = (self.get()->*aMethod)(Unwrap(args)...);
NS_DispatchToMainThread(NS_NewRunnableFunction(
"LockstoreService::ImplXpcomMethod::Resolve",
[domHandle = std::move(domHandle),
result = std::move(result)]() mutable {
if constexpr (std::is_same_v<nsresult, Result>) {
if (NS_FAILED(result)) {
domHandle->MaybeReject(result);
} else {
domHandle->MaybeResolveWithUndefined();
}
} else {
if (result.isErr()) {
domHandle->MaybeReject(result.unwrapErr());
} else {
domHandle->MaybeResolve(std::move(result.unwrap()));
}
}
}));
}));
if (NS_FAILED(rv)) {
// Dispatch failed; surface the failure to JS rather than leaving
// the promise pending forever.
domPromise->MaybeReject(rv);
}
domPromise.forget(aPromise);
return NS_OK;
}
} // namespace
// ---------------------------------------------------------------------------
// Synchronous nsILockstore methods (cheap in-memory state only)
// ---------------------------------------------------------------------------
NS_IMETHODIMP
LockstoreService::IsKekUnlocked(const nsACString& aKekRef, bool* aOut) {
MutexAutoLock lock(mMutex);
MOZ_TRY(EnsureOpenLocked());
return keystore_is_kek_unlocked(mKeystore, &aKekRef, aOut);
}
// ---------------------------------------------------------------------------
// Synchronous C++ tier — invokes the FFI on the calling thread under
// `mMutex`. Asserts off-main-thread in debug builds.
// ---------------------------------------------------------------------------
#define LOCKSTORE_SYNC_PREAMBLE \
MOZ_ASSERT(!NS_IsMainThread(), \
"Synchronous Lockstore I/O is forbidden on the main " \
"thread; dispatch onto a background task instead."); \
MutexAutoLock lock(mMutex); \
MOZ_TRY(EnsureOpenLocked())
nsresult LockstoreService::DoUnlockKek(const nsACString& aKekRef,
const nsACString& aSecret,
uint64_t aTimeoutMs) {
LOCKSTORE_SYNC_PREAMBLE;
return keystore_unlock_kek(mKeystore, &aKekRef, &aSecret, aTimeoutMs);
}
nsresult LockstoreService::DoLockKek(const nsACString& aKekRef) {
LOCKSTORE_SYNC_PREAMBLE;
return keystore_lock_kek(mKeystore, &aKekRef);
}
nsresult LockstoreService::DoLock() {
LOCKSTORE_SYNC_PREAMBLE;
return keystore_lock(mKeystore);
}
nsresult LockstoreService::DoCreateDek(const nsACString& aDekName,
const nsACString& aKekRef,
bool aExtractable, uint32_t aKeySize) {
LOCKSTORE_SYNC_PREAMBLE;
return keystore_create_dek(mKeystore, &aDekName, &aKekRef, aExtractable,
aKeySize);
}
nsresult LockstoreService::DoImportDek(const nsACString& aDekName,
const nsACString& aKekRef,
const nsTArray<uint8_t>& aDekBytes,
bool aExtractable) {
LOCKSTORE_SYNC_PREAMBLE;
return keystore_import_dek(mKeystore, &aDekName, &aKekRef,
aDekBytes.Elements(), aDekBytes.Length(),
aExtractable);
}
Result<bool, nsresult> LockstoreService::DoIsDekExtractable(
const nsACString& aDekName) {
LOCKSTORE_SYNC_PREAMBLE;
bool out = false;
MOZ_TRY(keystore_is_dek_extractable(mKeystore, &aDekName, &out));
return out;
}
nsresult LockstoreService::DoDeleteDek(const nsACString& aDekName) {
LOCKSTORE_SYNC_PREAMBLE;
return keystore_delete_dek(mKeystore, &aDekName);
}
nsresult LockstoreService::DoAddKek(const nsACString& aDekName,
const nsACString& aFromKekRef,
const nsACString& aToKekRef) {
LOCKSTORE_SYNC_PREAMBLE;
return keystore_add_kek(mKeystore, &aDekName, &aFromKekRef, &aToKekRef);
}
nsresult LockstoreService::DoRemoveKek(const nsACString& aDekName,
const nsACString& aKekRef) {
LOCKSTORE_SYNC_PREAMBLE;
return keystore_remove_kek(mKeystore, &aDekName, &aKekRef);
}
nsresult LockstoreService::DoSwitchKek(const nsACString& aDekName,
const nsACString& aOldKekRef,
const nsACString& aNewKekRef) {
LOCKSTORE_SYNC_PREAMBLE;
return keystore_switch_kek(mKeystore, &aDekName, &aOldKekRef, &aNewKekRef);
}
Result<nsTArray<nsCString>, nsresult> LockstoreService::DoListDeks() {
LOCKSTORE_SYNC_PREAMBLE;
nsTArray<nsCString> out;
MOZ_TRY(keystore_list_deks(mKeystore, &out));
return out;
}
Result<nsTArray<nsCString>, nsresult> LockstoreService::DoListKeks(
const nsACString& aDekName) {
LOCKSTORE_SYNC_PREAMBLE;
nsTArray<nsCString> out;
MOZ_TRY(keystore_list_keks(mKeystore, &aDekName, &out));
return out;
}
Result<nsTArray<uint8_t>, nsresult> LockstoreService::DoEncrypt(
const nsACString& aDekName, const nsACString& aKekRef,
const nsTArray<uint8_t>& aPlaintext) {
LOCKSTORE_SYNC_PREAMBLE;
nsTArray<uint8_t> out;
MOZ_TRY(keystore_encrypt(mKeystore, &aDekName, &aKekRef,
aPlaintext.Elements(), aPlaintext.Length(), &out));
return out;
}
Result<nsTArray<uint8_t>, nsresult> LockstoreService::DoDecrypt(
const nsACString& aDekName, const nsACString& aKekRef,
const nsTArray<uint8_t>& aCiphertext) {
LOCKSTORE_SYNC_PREAMBLE;
nsTArray<uint8_t> out;
MOZ_TRY(keystore_decrypt(mKeystore, &aDekName, &aKekRef,
aCiphertext.Elements(), aCiphertext.Length(), &out));
return out;
}
Result<nsTArray<uint8_t>, nsresult> LockstoreService::DoGetDek(
const nsACString& aDekName, const nsACString& aKekRef) {
LOCKSTORE_SYNC_PREAMBLE;
nsTArray<uint8_t> out;
MOZ_TRY(keystore_get_dek(mKeystore, &aDekName, &aKekRef, &out));
return out;
}
Result<nsCString, nsresult> LockstoreService::DoCreateKek(
const nsACString& aKekType, const nsACString& aIdentifier,
const nsACString& aSecret, uint64_t aCacheTimeoutMs) {
LOCKSTORE_SYNC_PREAMBLE;
nsCString out;
MOZ_TRY(keystore_create_kek(mKeystore, &aKekType, &aIdentifier, &aSecret,
aCacheTimeoutMs, &out));
return out;
}
nsresult LockstoreService::DoDeleteKek(const nsACString& aKekRef) {
LOCKSTORE_SYNC_PREAMBLE;
return keystore_delete_kek(mKeystore, &aKekRef);
}
#undef LOCKSTORE_SYNC_PREAMBLE
// ---------------------------------------------------------------------------
// nsILockstore async tier — one-line adapters over the sync C++ tier
// via `ImplXpcomMethod`.
// ---------------------------------------------------------------------------
NS_IMETHODIMP
LockstoreService::UnlockKek(const nsACString& aKekRef,
const nsACString& aSecret, uint64_t aTimeoutMs,
JSContext* aCx, Promise** aPromise) {
return ImplXpcomMethod(this, aCx, aPromise, &LockstoreService::DoUnlockKek,
nsCString{aKekRef}, ZeroizingCString{aSecret},
aTimeoutMs);
}
NS_IMETHODIMP
LockstoreService::LockKek(const nsACString& aKekRef, JSContext* aCx,
Promise** aPromise) {
return ImplXpcomMethod(this, aCx, aPromise, &LockstoreService::DoLockKek,
nsCString{aKekRef});
}
NS_IMETHODIMP
LockstoreService::Lock(JSContext* aCx, Promise** aPromise) {
return ImplXpcomMethod(this, aCx, aPromise, &LockstoreService::DoLock);
}
NS_IMETHODIMP
LockstoreService::CreateDek(const nsACString& aDekName,
const nsACString& aKekRef, bool aExtractable,
uint32_t aKeySize, JSContext* aCx,
Promise** aPromise) {
return ImplXpcomMethod(this, aCx, aPromise, &LockstoreService::DoCreateDek,
nsCString{aDekName}, nsCString{aKekRef}, aExtractable,
aKeySize);
}
NS_IMETHODIMP
LockstoreService::ImportDek(const nsACString& aDekName,
const nsACString& aKekRef,
const nsTArray<uint8_t>& aDekBytes,
bool aExtractable, JSContext* aCx,
Promise** aPromise) {
return ImplXpcomMethod(this, aCx, aPromise, &LockstoreService::DoImportDek,
nsCString{aDekName}, nsCString{aKekRef},
ZeroizingByteArray{aDekBytes.Clone()}, aExtractable);
}
NS_IMETHODIMP
LockstoreService::IsDekExtractable(const nsACString& aDekName, JSContext* aCx,
Promise** aPromise) {
return ImplXpcomMethod(this, aCx, aPromise,
&LockstoreService::DoIsDekExtractable,
nsCString{aDekName});
}
NS_IMETHODIMP
LockstoreService::DeleteDek(const nsACString& aDekName, JSContext* aCx,
Promise** aPromise) {
return ImplXpcomMethod(this, aCx, aPromise, &LockstoreService::DoDeleteDek,
nsCString{aDekName});
}
NS_IMETHODIMP
LockstoreService::AddKek(const nsACString& aDekName,
const nsACString& aFromKekRef,
const nsACString& aToKekRef, JSContext* aCx,
Promise** aPromise) {
return ImplXpcomMethod(this, aCx, aPromise, &LockstoreService::DoAddKek,
nsCString{aDekName}, nsCString{aFromKekRef},
nsCString{aToKekRef});
}
NS_IMETHODIMP
LockstoreService::RemoveKek(const nsACString& aDekName,
const nsACString& aKekRef, JSContext* aCx,
Promise** aPromise) {
return ImplXpcomMethod(this, aCx, aPromise, &LockstoreService::DoRemoveKek,
nsCString{aDekName}, nsCString{aKekRef});
}
NS_IMETHODIMP
LockstoreService::SwitchKek(const nsACString& aDekName,
const nsACString& aOldKekRef,
const nsACString& aNewKekRef, JSContext* aCx,
Promise** aPromise) {
return ImplXpcomMethod(this, aCx, aPromise, &LockstoreService::DoSwitchKek,
nsCString{aDekName}, nsCString{aOldKekRef},
nsCString{aNewKekRef});
}
NS_IMETHODIMP
LockstoreService::ListDeks(JSContext* aCx, Promise** aPromise) {
return ImplXpcomMethod(this, aCx, aPromise, &LockstoreService::DoListDeks);
}
NS_IMETHODIMP
LockstoreService::ListKeks(const nsACString& aDekName, JSContext* aCx,
Promise** aPromise) {
return ImplXpcomMethod(this, aCx, aPromise, &LockstoreService::DoListKeks,
nsCString{aDekName});
}
NS_IMETHODIMP
LockstoreService::Encrypt(const nsACString& aDekName, const nsACString& aKekRef,
const nsTArray<uint8_t>& aPlaintext, JSContext* aCx,
Promise** aPromise) {
return ImplXpcomMethod(this, aCx, aPromise, &LockstoreService::DoEncrypt,
nsCString{aDekName}, nsCString{aKekRef},
aPlaintext.Clone());
}
NS_IMETHODIMP
LockstoreService::Decrypt(const nsACString& aDekName, const nsACString& aKekRef,
const nsTArray<uint8_t>& aCiphertext, JSContext* aCx,
Promise** aPromise) {
return ImplXpcomMethod(this, aCx, aPromise, &LockstoreService::DoDecrypt,
nsCString{aDekName}, nsCString{aKekRef},
aCiphertext.Clone());
}
NS_IMETHODIMP
LockstoreService::GetDek(const nsACString& aDekName, const nsACString& aKekRef,
JSContext* aCx, Promise** aPromise) {
return ImplXpcomMethod(this, aCx, aPromise, &LockstoreService::DoGetDek,
nsCString{aDekName}, nsCString{aKekRef});
}
NS_IMETHODIMP
LockstoreService::CreateKek(const nsACString& aKekType,
const nsACString& aIdentifier,
const nsACString& aSecret, uint64_t aCacheTimeoutMs,
JSContext* aCx, Promise** aPromise) {
return ImplXpcomMethod(this, aCx, aPromise, &LockstoreService::DoCreateKek,
nsCString{aKekType}, nsCString{aIdentifier},
ZeroizingCString{aSecret}, aCacheTimeoutMs);
}
NS_IMETHODIMP
LockstoreService::DeleteKek(const nsACString& aKekRef, JSContext* aCx,
Promise** aPromise) {
return ImplXpcomMethod(this, aCx, aPromise, &LockstoreService::DoDeleteKek,
nsCString{aKekRef});
}
} // namespace mozilla::security::lockstore