/* 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 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& aDekBytes, bool aExtractable); Result 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, nsresult> DoListDeks(); Result, nsresult> DoListKeks(const nsACString& aDekName); Result, nsresult> DoEncrypt( const nsACString& aCollection, const nsACString& aKekRef, const nsTArray& aPlaintext); Result, nsresult> DoDecrypt( const nsACString& aCollection, const nsACString& aKekRef, const nsTArray& aCiphertext); Result, nsresult> DoGetDek(const nsACString& aCollection, const nsACString& aKekRef); Result 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 // (``). 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