Source code

Revision control

Copy as Markdown

Other Tools

/* 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/. */
#ifndef mozilla_security_lockstore_LockstoreService_h
#define mozilla_security_lockstore_LockstoreService_h
#include "mozilla/Mutex.h"
#include "mozilla/Result.h"
#include "mozilla/security/lockstore/lockstore_ffi_generated.h"
#include "nsCOMPtr.h"
#include "nsILockstore.h"
#include "nsIObserver.h"
#include "nsString.h"
#include "nsTArray.h"
namespace mozilla::security::lockstore {
class LockstoreService final : public nsILockstore, public nsIObserver {
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSILOCKSTORE
NS_DECL_NSIOBSERVER
LockstoreService();
// Registered as the singleton's `init_method` in components.conf.
nsresult Init();
// Reachable from C++ consumers without going through XPCOM. The
// keystore is locked and closed at `profile-before-change` /
// `xpcom-shutdown` via the registered observers; the service object
// itself is released when the XPCOM component manager drops its
// singleton ref. Callers may hold a `RefPtr` past shutdown, but
// operations on it will fail with `NS_ERROR_NOT_AVAILABLE`.
static already_AddRefed<LockstoreService> GetSingleton();
// ---------------------------------------------------------------------
// Synchronous C++ tier
//
// In-tree off-main-thread C++ consumers (mls_gk, SQLite encryption,
// …) call these directly. Each method invokes the FFI on the calling
// thread under `mMutex` and returns the result. Debug builds assert
// off-main-thread; callers on the main thread should wrap with
// `NS_DispatchBackgroundTask` themselves.
//
// The XPCOM tier (`NS_IMETHODIMP …` methods declared by
// `NS_DECL_NSILOCKSTORE`) is implemented on top of these via
// `ImplXpcomMethod`, which performs the dispatch + DOM-Promise bridge
// for JS callers.
// ---------------------------------------------------------------------
nsresult DoUnlockKek(const nsACString& aKekRef, const nsACString& aSecret,
uint32_t aTimeoutMs);
nsresult DoLockKek(const nsACString& aKekRef);
nsresult DoLock();
nsresult DoCreateDek(const nsACString& aCollection, const nsACString& aKekRef,
bool aExtractable);
nsresult DoImportDek(const nsACString& aCollection, const nsACString& aKekRef,
const nsTArray<uint8_t>& aDekBytes, bool aExtractable);
Result<bool, nsresult> DoIsDekExtractable(const nsACString& aCollection);
nsresult DoDeleteDek(const nsACString& aCollection);
nsresult DoAddKek(const nsACString& aCollection,
const nsACString& aFromKekRef, const nsACString& aToKekRef);
nsresult DoRemoveKek(const nsACString& aCollection,
const nsACString& aKekRef);
nsresult DoSwitchKek(const nsACString& aCollection,
const nsACString& aOldKekRef,
const nsACString& aNewKekRef);
Result<nsTArray<nsCString>, nsresult> DoListDeks();
Result<nsTArray<nsCString>, nsresult> DoListKeks(const nsACString& aDekName);
Result<nsTArray<uint8_t>, nsresult> DoEncrypt(
const nsACString& aCollection, const nsACString& aKekRef,
const nsTArray<uint8_t>& aPlaintext);
Result<nsTArray<uint8_t>, nsresult> DoDecrypt(
const nsACString& aCollection, const nsACString& aKekRef,
const nsTArray<uint8_t>& aCiphertext);
Result<nsTArray<uint8_t>, nsresult> DoGetDek(const nsACString& aCollection,
const nsACString& aKekRef);
Result<nsCString, nsresult> DoCreateKek(const nsACString& aKekType,
const nsACString& aIdentifier,
const nsACString& aSecret,
uint32_t aCacheTimeoutMs);
nsresult DoDeleteKek(const nsACString& aKekRef);
private:
~LockstoreService();
// Opens the keystore against the current profile if not already open.
// Called under mMutex by every FFI-touching method.
nsresult EnsureOpenLocked() MOZ_REQUIRES(mMutex);
// Cached UTF-8 absolute path of the keystore parent directory
// (`<profile>`). Resolved once on the main thread in `Init()` because
// `nsIDirectoryService::Get` asserts main-thread, and the sync tier
// runs off-main. Const after Init — no synchronisation needed.
nsCString mProfilePath;
// Protects mKeystore and mShutdown. Held across every FFI call so a
// shutdown racing with an in-flight operation cannot free the handle
// out from under the worker. Serialises every call into the FFI:
// concurrent XPCOM / C++ direct callers queue on this mutex, which
// is why no dedicated serial event target is needed.
//
// Lock ordering: acquired *before* any call into the Rust FFI, which
// has its own `Keystore::connection_lock`. Always mMutex first, then
// any Rust-side lock — never the reverse.
Mutex mMutex;
// FFI handle for the per-profile keystore. Null between construction
// and the first FFI dispatch; opened lazily by `EnsureOpenLocked`
// and closed in `Observe()` (on shutdown) and the destructor.
KeystoreHandle* mKeystore MOZ_GUARDED_BY(mMutex);
// Latches true on the first `profile-before-change` /
// `xpcom-shutdown` notification. Subsequent calls through
// `EnsureOpenLocked` short-circuit with `NS_ERROR_NOT_AVAILABLE`, so
// no new FFI work starts after shutdown.
bool mShutdown MOZ_GUARDED_BY(mMutex);
};
} // namespace mozilla::security::lockstore
#endif // mozilla_security_lockstore_LockstoreService_h