Loading dom/indexedDB/ActorsParent.cpp +72 −111 Original line number Diff line number Diff line Loading @@ -32,7 +32,6 @@ #include "IndexedDBCommon.h" #include "IndexedDatabaseInlines.h" #include "IndexedDatabaseManager.h" #include "IndexedDBCipherKeyManager.h" #include "KeyPath.h" #include "MainThreadUtils.h" #include "ProfilerHelpers.h" Loading Loading @@ -2385,6 +2384,7 @@ class Database final bool IsInPrivateBrowsing() const { AssertIsOnBackgroundThread(); return mInPrivateBrowsing; } Loading Loading @@ -3750,47 +3750,6 @@ class NormalTransactionOp : public TransactionDatabaseOperationBase, const PreprocessResponse& aResponse) final; }; Maybe<CipherKey> IndexedDBCipherKeyManager::Get(const nsCString& aDatabaseID, const nsCString& keyStoreID) { auto lockedPrivateBrowsingInfoHashTable = mPrivateBrowsingInfoHashTable.Lock(); auto dbKeyStore = lockedPrivateBrowsingInfoHashTable->Lookup(aDatabaseID); if (!dbKeyStore) { return Nothing(); } return dbKeyStore->MaybeGet(keyStoreID); } CipherKey IndexedDBCipherKeyManager::Ensure(const nsCString& aDatabaseID, const nsCString& keyStoreID) { auto lockedPrivateBrowsingInfoHashTable = mPrivateBrowsingInfoHashTable.Lock(); auto& dbKeyStore = lockedPrivateBrowsingInfoHashTable->LookupOrInsert(aDatabaseID); return dbKeyStore.LookupOrInsertWith(keyStoreID, [] { // XXX Generate key using proper random data, such that we can ensure // the use of unique IVs per key by discriminating by database's file // id & offset. auto keyOrErr = IndexedDBCipherStrategy::GenerateKey(); // XXX Propagate the error to the caller rather than asserting. return keyOrErr.unwrap(); }); } bool IndexedDBCipherKeyManager::Remove(const nsCString& aDatabaseID) { auto lockedPrivateBrowsingInfoHashTable = mPrivateBrowsingInfoHashTable.Lock(); return lockedPrivateBrowsingInfoHashTable->Remove(aDatabaseID); } // XXX Maybe we can avoid a mutex here by moving all accesses to the background // thread. StaticAutoPtr<IndexedDBCipherKeyManager> gIndexedDBCipherKeyManager; class ObjectStoreAddOrPutRequestOp final : public NormalTransactionOp { friend class TransactionBase; Loading @@ -3806,7 +3765,7 @@ class ObjectStoreAddOrPutRequestOp final : public NormalTransactionOp { #ifdef DEBUG const StructuredCloneFileBase::FileType mType; #endif void EnsureCipherKey(); void AssertInvariants() const; explicit StoredFileInfo(SafeRefPtr<DatabaseFileInfo> aFileInfo); Loading Loading @@ -3940,18 +3899,6 @@ void ObjectStoreAddOrPutRequestOp::StoredFileInfo::AssertInvariants() const { } } void ObjectStoreAddOrPutRequestOp::StoredFileInfo::EnsureCipherKey() { const auto& fileInfo = GetFileInfo(); const auto& fileMgr = fileInfo.Manager(); // no need to generate cipher keys if we are not in PBM if (!fileMgr.IsInPrivateBrowsingMode()) return; nsCString keyId; keyId.AppendInt(fileInfo.Id()); gIndexedDBCipherKeyManager->Ensure(fileMgr.DatabaseID(), keyId); } ObjectStoreAddOrPutRequestOp::StoredFileInfo::StoredFileInfo( SafeRefPtr<DatabaseFileInfo> aFileInfo) : mFileInfo{WrapNotNull(std::move(aFileInfo))}, Loading @@ -3964,7 +3911,6 @@ ObjectStoreAddOrPutRequestOp::StoredFileInfo::StoredFileInfo( AssertIsOnBackgroundThread(); AssertInvariants(); EnsureCipherKey(); MOZ_COUNT_CTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo); } Loading @@ -3980,7 +3926,6 @@ ObjectStoreAddOrPutRequestOp::StoredFileInfo::StoredFileInfo( AssertIsOnBackgroundThread(); AssertInvariants(); EnsureCipherKey(); MOZ_COUNT_CTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo); } Loading @@ -3997,7 +3942,6 @@ ObjectStoreAddOrPutRequestOp::StoredFileInfo::StoredFileInfo( AssertIsOnBackgroundThread(); AssertInvariants(); EnsureCipherKey(); MOZ_COUNT_CTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo); } Loading Loading @@ -5611,26 +5555,19 @@ class EncryptedFileBlobImpl final : public FileBlobImpl { } private: const CipherKey mKey; const CipherKey& mKey; }; RefPtr<BlobImpl> CreateFileBlobImpl(const Database& aDatabase, const nsCOMPtr<nsIFile>& aNativeFile, const DatabaseFileInfo::IdType aId) { if (aDatabase.IsInPrivateBrowsing()) { nsCString cipherKeyId; cipherKeyId.AppendInt(aId); const auto& key = gIndexedDBCipherKeyManager->Get(aDatabase.Id(), cipherKeyId); MOZ_RELEASE_ASSERT(key.isSome()); return MakeRefPtr<EncryptedFileBlobImpl>(aNativeFile, aId, *key); const auto& maybeKey = aDatabase.MaybeKeyRef(); if (maybeKey) { return MakeRefPtr<EncryptedFileBlobImpl>(aNativeFile, aId, *maybeKey); } auto impl = MakeRefPtr<FileBlobImpl>(aNativeFile); impl->SetFileId(aId); return impl; } Loading Loading @@ -6075,6 +6012,12 @@ using DatabaseActorHashtable = StaticAutoPtr<DatabaseActorHashtable> gLiveDatabaseHashtable; using PrivateBrowsingInfoHashtable = nsTHashMap<nsCStringHashKey, CipherKey>; // XXX Maybe we can avoid a mutex here by moving all accesses to the background // thread. StaticAutoPtr<DataMutex<PrivateBrowsingInfoHashtable>> gPrivateBrowsingInfoHashtable; StaticRefPtr<ConnectionPool> gConnectionPool; StaticRefPtr<FileHandleThreadPool> gFileHandleThreadPool; Loading Loading @@ -6108,8 +6051,9 @@ void IncreaseBusyCount() { MOZ_ASSERT(!gLiveDatabaseHashtable); gLiveDatabaseHashtable = new DatabaseActorHashtable(); MOZ_ASSERT(!gIndexedDBCipherKeyManager); gIndexedDBCipherKeyManager = new IndexedDBCipherKeyManager(); MOZ_ASSERT(!gPrivateBrowsingInfoHashtable); gPrivateBrowsingInfoHashtable = new DataMutex<PrivateBrowsingInfoHashtable>( "gPrivateBrowsingInfoHashtable"); MOZ_ASSERT(!gLoggingInfoHashtable); gLoggingInfoHashtable = new DatabaseLoggingInfoHashtable(); Loading Loading @@ -6157,10 +6101,11 @@ void DecreaseBusyCount() { MOZ_ASSERT(!gLiveDatabaseHashtable->Count()); gLiveDatabaseHashtable = nullptr; MOZ_ASSERT(gIndexedDBCipherKeyManager); MOZ_ASSERT(gPrivateBrowsingInfoHashtable); // XXX After we add the private browsing session end listener, we can assert // this. gIndexedDBCipherKeyManager = nullptr; // MOZ_ASSERT(!gPrivateBrowsingInfoHashtable->Count()); gPrivateBrowsingInfoHashtable = nullptr; MOZ_ASSERT(gFactoryOps); MOZ_ASSERT(gFactoryOps->IsEmpty()); Loading Loading @@ -12119,14 +12064,11 @@ DatabaseFileManager::MutexType DatabaseFileManager::sMutex; DatabaseFileManager::DatabaseFileManager( PersistenceType aPersistenceType, const quota::OriginMetadata& aOriginMetadata, const nsAString& aDatabaseName, const nsCString& aDatabaseID, bool aEnforcingQuota, bool aIsInPrivateBrowsingMode) const nsAString& aDatabaseName, bool aEnforcingQuota) : mPersistenceType(aPersistenceType), mOriginMetadata(aOriginMetadata), mDatabaseName(aDatabaseName), mDatabaseID(aDatabaseID), mEnforcingQuota(aEnforcingQuota), mIsInPrivateBrowsingMode(aIsInPrivateBrowsingMode) {} mEnforcingQuota(aEnforcingQuota) {} nsresult DatabaseFileManager::Init(nsIFile* aDirectory, mozIStorageConnection& aConnection) { Loading Loading @@ -15277,7 +15219,22 @@ nsresult FactoryOp::Open() { MOZ_ASSERT(permission == PermissionValue::kPermissionAllowed); if (mInPrivateBrowsing) { gIndexedDBCipherKeyManager->Ensure(mDatabaseId); const auto lockedPrivateBrowsingInfoHashtable = gPrivateBrowsingInfoHashtable->Lock(); lockedPrivateBrowsingInfoHashtable->LookupOrInsertWith(mDatabaseId, [] { IndexedDBCipherStrategy cipherStrategy; // XXX Generate key using proper random data, such that we can ensure // the use of unique IVs per key by discriminating by database's file // id & offset. auto keyOrErr = cipherStrategy.GenerateKey(); // XXX Propagate the error to the caller rather than asserting. MOZ_RELEASE_ASSERT(keyOrErr.isOk()); return keyOrErr.unwrap(); }); } mState = State::FinishOpen; Loading Loading @@ -15885,11 +15842,19 @@ nsresult OpenDatabaseOp::DoDatabaseWork() { CloneFileAndAppend(*dbDirectory, databaseFilenameBase + kFileManagerDirectoryNameSuffix)); Maybe<const CipherKey> maybeKey = mInPrivateBrowsing ? gIndexedDBCipherKeyManager->Get(mDatabaseId) : Nothing(); Maybe<const CipherKey> maybeKey; if (mInPrivateBrowsing) { CipherKey key; { const auto lockedPrivateBrowsingInfoHashtable = gPrivateBrowsingInfoHashtable->Lock(); MOZ_ALWAYS_TRUE( lockedPrivateBrowsingInfoHashtable->Get(mDatabaseId, &key)); } MOZ_RELEASE_ASSERT(mInPrivateBrowsing == maybeKey.isSome()); maybeKey.emplace(std::move(key)); } QM_TRY_UNWRAP( NotNull<nsCOMPtr<mozIStorageConnection>> connection, Loading Loading @@ -15932,8 +15897,7 @@ nsresult OpenDatabaseOp::DoDatabaseWork() { if (!fileManager) { fileManager = MakeSafeRefPtr<DatabaseFileManager>( persistenceType, mOriginMetadata, databaseName, mDatabaseId, mEnforcingQuota, mInPrivateBrowsing); persistenceType, mOriginMetadata, databaseName, mEnforcingQuota); QM_TRY(MOZ_TO_RESULT(fileManager->Init(fmDirectory, *connection))); Loading Loading @@ -16593,11 +16557,19 @@ void OpenDatabaseOp::EnsureDatabaseActor() { mMetadata = info->mMetadata.clonePtr(); } Maybe<const CipherKey> maybeKey = mInPrivateBrowsing ? gIndexedDBCipherKeyManager->Get(mDatabaseId) : Nothing(); Maybe<const CipherKey> maybeKey; if (mInPrivateBrowsing) { CipherKey key; { const auto lockedPrivateBrowsingInfoHashtable = gPrivateBrowsingInfoHashtable->Lock(); MOZ_ALWAYS_TRUE( lockedPrivateBrowsingInfoHashtable->Get(mDatabaseId, &key)); } MOZ_RELEASE_ASSERT(mInPrivateBrowsing == maybeKey.isSome()); maybeKey.emplace(std::move(key)); } // XXX Shouldn't Manager() return already_AddRefed when // PBackgroundIDBFactoryParent is declared refcounted? Loading Loading @@ -16900,11 +16872,16 @@ void DeleteDatabaseOp::LoadPreviousVersion(nsIFile& aDatabaseFile) { return; } const auto maybeKey = mInPrivateBrowsing ? gIndexedDBCipherKeyManager->Get(mDatabaseId) : Nothing(); const auto maybeKey = [this]() -> Maybe<const CipherKey> { CipherKey key; MOZ_RELEASE_ASSERT(mInPrivateBrowsing == maybeKey.isSome()); const auto lockedPrivateBrowsingInfoHashtable = gPrivateBrowsingInfoHashtable->Lock(); if (!lockedPrivateBrowsingInfoHashtable->Get(mDatabaseId, &key)) { return Nothing{}; } return Some(std::move(key)); }(); // Pass -1 as the directoryLockId to disable quota checking, since we might // temporarily exceed quota before deleting the database. Loading Loading @@ -17175,11 +17152,6 @@ nsresult DeleteDatabaseOp::VersionChangeOp::RunOnIOThread() { return rv; } if (mDeleteDatabaseOp->mInPrivateBrowsing) { MOZ_ASSERT( gIndexedDBCipherKeyManager->Remove(mDeleteDatabaseOp->mDatabaseId)); } rv = mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; Loading Loading @@ -19555,22 +19527,11 @@ nsresult ObjectStoreAddOrPutRequestOp::DoDatabaseWork( QM_TRY(OkIf(journalFile), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, IDB_REPORT_INTERNAL_ERR_LAMBDA); nsCString fileKeyId; fileKeyId.AppendInt(fileInfo.Id()); const auto maybeKey = Transaction() .GetDatabase() .GetFileManager() .IsInPrivateBrowsingMode() ? gIndexedDBCipherKeyManager->Get( Transaction().GetDatabase().Id(), fileKeyId) : Nothing(); QM_TRY( MOZ_TO_RESULT(fileHelper->CreateFileFromStream( *file, *journalFile, *inputStream, storedFileInfo.ShouldCompress(), maybeKey)) storedFileInfo.ShouldCompress(), Transaction().GetDatabase().MaybeKeyRef())) .mapErr([](const nsresult rv) { if (NS_ERROR_GET_MODULE(rv) != NS_ERROR_MODULE_DOM_INDEXEDDB) { dom/indexedDB/ActorsParentCommon.h +3 −1 Original line number Diff line number Diff line Loading @@ -20,7 +20,6 @@ #include "mozilla/UniquePtrExtensions.h" #include "mozilla/dom/indexedDB/Key.h" #include "mozilla/dom/quota/IPCStreamCipherStrategy.h" #include "IndexedDBCipherKeyManager.h" #include "nscore.h" #include "nsISupports.h" #include "nsStringFwd.h" Loading @@ -40,6 +39,9 @@ struct StructuredCloneReadInfoParent; extern const nsLiteralString kJournalDirectoryName; using IndexedDBCipherStrategy = quota::IPCStreamCipherStrategy; using CipherKey = IndexedDBCipherStrategy::KeyType; // At the moment, the encrypted stream block size is assumed to be unchangeable // between encrypting and decrypting blobs. This assumptions holds as long as we // only encrypt in private browsing mode, but when we support encryption for Loading dom/indexedDB/DatabaseFileManager.h +2 −7 Original line number Diff line number Diff line Loading @@ -28,13 +28,11 @@ class DatabaseFileManager final const PersistenceType mPersistenceType; const quota::OriginMetadata mOriginMetadata; const nsString mDatabaseName; const nsCString mDatabaseID; LazyInitializedOnce<const nsString> mDirectoryPath; LazyInitializedOnce<const nsString> mJournalDirectoryPath; const bool mEnforcingQuota; const bool mIsInPrivateBrowsingMode; // Lock protecting DatabaseFileManager.mFileInfos. // It's s also used to atomically update DatabaseFileInfo.mRefCnt and Loading @@ -61,9 +59,7 @@ class DatabaseFileManager final DatabaseFileManager(PersistenceType aPersistenceType, const quota::OriginMetadata& aOriginMetadata, const nsAString& aDatabaseName, const nsCString& aDatabaseID, bool aEnforcingQuota, bool aIsInPrivateBrowsingMode); const nsAString& aDatabaseName, bool aEnforcingQuota); PersistenceType Type() const { return mPersistenceType; } Loading @@ -74,8 +70,7 @@ class DatabaseFileManager final const nsACString& Origin() const { return mOriginMetadata.mOrigin; } const nsAString& DatabaseName() const { return mDatabaseName; } const nsCString& DatabaseID() const { return mDatabaseID; } auto IsInPrivateBrowsingMode() const { return mIsInPrivateBrowsingMode; } bool EnforcingQuota() const { return mEnforcingQuota; } nsresult Init(nsIFile* aDirectory, mozIStorageConnection& aConnection); Loading dom/indexedDB/IndexedDBCipherKeyManager.hdeleted 100644 → 0 +0 −52 Original line number Diff line number Diff line /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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_dom_indexeddbcipherKeyManager_h #define mozilla_dom_indexeddbcipherKeyManager_h #include "mozilla/dom/quota/IPCStreamCipherStrategy.h" #include "mozilla/DataMutex.h" #include "nsTHashMap.h" namespace mozilla::dom::indexedDB { namespace { using IndexedDBCipherStrategy = quota::IPCStreamCipherStrategy; using CipherKey = IndexedDBCipherStrategy::KeyType; class IndexedDBCipherKeyManager { // This helper class is used by IndexedDB operations to store/retrieve cipher // keys in private browsing mode. All data in IndexedDB must be encrypted // using a cipher key and unique IV (Initialization Vector). While there's a // separate cipher key for every blob file; any normal key/value pairs get // encrypted using the commmon database key. All keys pertaining to a single // database get stored together using the database id in a hashmap. We are // using a 2-level hashmap here; keys get stored effectively at level 2. // Looking up at level 1 using database id gives us access to hashmap of keys // corresponding to that database which we could use to look up the common // database key and blob keys using "default" and blob file ids respectively. public: using PrivateBrowsingInfoHashtable = nsTHashMap<nsCStringHashKey, nsTHashMap<nsCStringHashKey, CipherKey>>; IndexedDBCipherKeyManager() : mPrivateBrowsingInfoHashTable("IndexedDBCipherKeyManager"){}; Maybe<CipherKey> Get(const nsCString& aDatabaseID, const nsCString& keyStoreID = "default"_ns); CipherKey Ensure(const nsCString& aDatabaseID, const nsCString& keyStoreID = "default"_ns); bool Remove(const nsCString& aDatabaseID); private: DataMutex<PrivateBrowsingInfoHashtable> mPrivateBrowsingInfoHashTable; }; } // namespace } // namespace mozilla::dom::indexedDB #endif // IndexedDBCipherKeyManager_h dom/indexedDB/SchemaUpgrades.cpp +1 −2 Original line number Diff line number Diff line Loading @@ -2862,8 +2862,7 @@ nsresult UpgradeFileIdsFunction::Init(nsIFile* aFMDirectory, // purpose is to store file ids without adding more complexity or code // duplication. auto fileManager = MakeSafeRefPtr<DatabaseFileManager>( PERSISTENCE_TYPE_INVALID, quota::OriginMetadata{}, u""_ns, ""_ns, false, false); PERSISTENCE_TYPE_INVALID, quota::OriginMetadata{}, u""_ns, false); nsresult rv = fileManager->Init(aFMDirectory, aConnection); if (NS_WARN_IF(NS_FAILED(rv))) { Loading Loading
dom/indexedDB/ActorsParent.cpp +72 −111 Original line number Diff line number Diff line Loading @@ -32,7 +32,6 @@ #include "IndexedDBCommon.h" #include "IndexedDatabaseInlines.h" #include "IndexedDatabaseManager.h" #include "IndexedDBCipherKeyManager.h" #include "KeyPath.h" #include "MainThreadUtils.h" #include "ProfilerHelpers.h" Loading Loading @@ -2385,6 +2384,7 @@ class Database final bool IsInPrivateBrowsing() const { AssertIsOnBackgroundThread(); return mInPrivateBrowsing; } Loading Loading @@ -3750,47 +3750,6 @@ class NormalTransactionOp : public TransactionDatabaseOperationBase, const PreprocessResponse& aResponse) final; }; Maybe<CipherKey> IndexedDBCipherKeyManager::Get(const nsCString& aDatabaseID, const nsCString& keyStoreID) { auto lockedPrivateBrowsingInfoHashTable = mPrivateBrowsingInfoHashTable.Lock(); auto dbKeyStore = lockedPrivateBrowsingInfoHashTable->Lookup(aDatabaseID); if (!dbKeyStore) { return Nothing(); } return dbKeyStore->MaybeGet(keyStoreID); } CipherKey IndexedDBCipherKeyManager::Ensure(const nsCString& aDatabaseID, const nsCString& keyStoreID) { auto lockedPrivateBrowsingInfoHashTable = mPrivateBrowsingInfoHashTable.Lock(); auto& dbKeyStore = lockedPrivateBrowsingInfoHashTable->LookupOrInsert(aDatabaseID); return dbKeyStore.LookupOrInsertWith(keyStoreID, [] { // XXX Generate key using proper random data, such that we can ensure // the use of unique IVs per key by discriminating by database's file // id & offset. auto keyOrErr = IndexedDBCipherStrategy::GenerateKey(); // XXX Propagate the error to the caller rather than asserting. return keyOrErr.unwrap(); }); } bool IndexedDBCipherKeyManager::Remove(const nsCString& aDatabaseID) { auto lockedPrivateBrowsingInfoHashTable = mPrivateBrowsingInfoHashTable.Lock(); return lockedPrivateBrowsingInfoHashTable->Remove(aDatabaseID); } // XXX Maybe we can avoid a mutex here by moving all accesses to the background // thread. StaticAutoPtr<IndexedDBCipherKeyManager> gIndexedDBCipherKeyManager; class ObjectStoreAddOrPutRequestOp final : public NormalTransactionOp { friend class TransactionBase; Loading @@ -3806,7 +3765,7 @@ class ObjectStoreAddOrPutRequestOp final : public NormalTransactionOp { #ifdef DEBUG const StructuredCloneFileBase::FileType mType; #endif void EnsureCipherKey(); void AssertInvariants() const; explicit StoredFileInfo(SafeRefPtr<DatabaseFileInfo> aFileInfo); Loading Loading @@ -3940,18 +3899,6 @@ void ObjectStoreAddOrPutRequestOp::StoredFileInfo::AssertInvariants() const { } } void ObjectStoreAddOrPutRequestOp::StoredFileInfo::EnsureCipherKey() { const auto& fileInfo = GetFileInfo(); const auto& fileMgr = fileInfo.Manager(); // no need to generate cipher keys if we are not in PBM if (!fileMgr.IsInPrivateBrowsingMode()) return; nsCString keyId; keyId.AppendInt(fileInfo.Id()); gIndexedDBCipherKeyManager->Ensure(fileMgr.DatabaseID(), keyId); } ObjectStoreAddOrPutRequestOp::StoredFileInfo::StoredFileInfo( SafeRefPtr<DatabaseFileInfo> aFileInfo) : mFileInfo{WrapNotNull(std::move(aFileInfo))}, Loading @@ -3964,7 +3911,6 @@ ObjectStoreAddOrPutRequestOp::StoredFileInfo::StoredFileInfo( AssertIsOnBackgroundThread(); AssertInvariants(); EnsureCipherKey(); MOZ_COUNT_CTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo); } Loading @@ -3980,7 +3926,6 @@ ObjectStoreAddOrPutRequestOp::StoredFileInfo::StoredFileInfo( AssertIsOnBackgroundThread(); AssertInvariants(); EnsureCipherKey(); MOZ_COUNT_CTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo); } Loading @@ -3997,7 +3942,6 @@ ObjectStoreAddOrPutRequestOp::StoredFileInfo::StoredFileInfo( AssertIsOnBackgroundThread(); AssertInvariants(); EnsureCipherKey(); MOZ_COUNT_CTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo); } Loading Loading @@ -5611,26 +5555,19 @@ class EncryptedFileBlobImpl final : public FileBlobImpl { } private: const CipherKey mKey; const CipherKey& mKey; }; RefPtr<BlobImpl> CreateFileBlobImpl(const Database& aDatabase, const nsCOMPtr<nsIFile>& aNativeFile, const DatabaseFileInfo::IdType aId) { if (aDatabase.IsInPrivateBrowsing()) { nsCString cipherKeyId; cipherKeyId.AppendInt(aId); const auto& key = gIndexedDBCipherKeyManager->Get(aDatabase.Id(), cipherKeyId); MOZ_RELEASE_ASSERT(key.isSome()); return MakeRefPtr<EncryptedFileBlobImpl>(aNativeFile, aId, *key); const auto& maybeKey = aDatabase.MaybeKeyRef(); if (maybeKey) { return MakeRefPtr<EncryptedFileBlobImpl>(aNativeFile, aId, *maybeKey); } auto impl = MakeRefPtr<FileBlobImpl>(aNativeFile); impl->SetFileId(aId); return impl; } Loading Loading @@ -6075,6 +6012,12 @@ using DatabaseActorHashtable = StaticAutoPtr<DatabaseActorHashtable> gLiveDatabaseHashtable; using PrivateBrowsingInfoHashtable = nsTHashMap<nsCStringHashKey, CipherKey>; // XXX Maybe we can avoid a mutex here by moving all accesses to the background // thread. StaticAutoPtr<DataMutex<PrivateBrowsingInfoHashtable>> gPrivateBrowsingInfoHashtable; StaticRefPtr<ConnectionPool> gConnectionPool; StaticRefPtr<FileHandleThreadPool> gFileHandleThreadPool; Loading Loading @@ -6108,8 +6051,9 @@ void IncreaseBusyCount() { MOZ_ASSERT(!gLiveDatabaseHashtable); gLiveDatabaseHashtable = new DatabaseActorHashtable(); MOZ_ASSERT(!gIndexedDBCipherKeyManager); gIndexedDBCipherKeyManager = new IndexedDBCipherKeyManager(); MOZ_ASSERT(!gPrivateBrowsingInfoHashtable); gPrivateBrowsingInfoHashtable = new DataMutex<PrivateBrowsingInfoHashtable>( "gPrivateBrowsingInfoHashtable"); MOZ_ASSERT(!gLoggingInfoHashtable); gLoggingInfoHashtable = new DatabaseLoggingInfoHashtable(); Loading Loading @@ -6157,10 +6101,11 @@ void DecreaseBusyCount() { MOZ_ASSERT(!gLiveDatabaseHashtable->Count()); gLiveDatabaseHashtable = nullptr; MOZ_ASSERT(gIndexedDBCipherKeyManager); MOZ_ASSERT(gPrivateBrowsingInfoHashtable); // XXX After we add the private browsing session end listener, we can assert // this. gIndexedDBCipherKeyManager = nullptr; // MOZ_ASSERT(!gPrivateBrowsingInfoHashtable->Count()); gPrivateBrowsingInfoHashtable = nullptr; MOZ_ASSERT(gFactoryOps); MOZ_ASSERT(gFactoryOps->IsEmpty()); Loading Loading @@ -12119,14 +12064,11 @@ DatabaseFileManager::MutexType DatabaseFileManager::sMutex; DatabaseFileManager::DatabaseFileManager( PersistenceType aPersistenceType, const quota::OriginMetadata& aOriginMetadata, const nsAString& aDatabaseName, const nsCString& aDatabaseID, bool aEnforcingQuota, bool aIsInPrivateBrowsingMode) const nsAString& aDatabaseName, bool aEnforcingQuota) : mPersistenceType(aPersistenceType), mOriginMetadata(aOriginMetadata), mDatabaseName(aDatabaseName), mDatabaseID(aDatabaseID), mEnforcingQuota(aEnforcingQuota), mIsInPrivateBrowsingMode(aIsInPrivateBrowsingMode) {} mEnforcingQuota(aEnforcingQuota) {} nsresult DatabaseFileManager::Init(nsIFile* aDirectory, mozIStorageConnection& aConnection) { Loading Loading @@ -15277,7 +15219,22 @@ nsresult FactoryOp::Open() { MOZ_ASSERT(permission == PermissionValue::kPermissionAllowed); if (mInPrivateBrowsing) { gIndexedDBCipherKeyManager->Ensure(mDatabaseId); const auto lockedPrivateBrowsingInfoHashtable = gPrivateBrowsingInfoHashtable->Lock(); lockedPrivateBrowsingInfoHashtable->LookupOrInsertWith(mDatabaseId, [] { IndexedDBCipherStrategy cipherStrategy; // XXX Generate key using proper random data, such that we can ensure // the use of unique IVs per key by discriminating by database's file // id & offset. auto keyOrErr = cipherStrategy.GenerateKey(); // XXX Propagate the error to the caller rather than asserting. MOZ_RELEASE_ASSERT(keyOrErr.isOk()); return keyOrErr.unwrap(); }); } mState = State::FinishOpen; Loading Loading @@ -15885,11 +15842,19 @@ nsresult OpenDatabaseOp::DoDatabaseWork() { CloneFileAndAppend(*dbDirectory, databaseFilenameBase + kFileManagerDirectoryNameSuffix)); Maybe<const CipherKey> maybeKey = mInPrivateBrowsing ? gIndexedDBCipherKeyManager->Get(mDatabaseId) : Nothing(); Maybe<const CipherKey> maybeKey; if (mInPrivateBrowsing) { CipherKey key; { const auto lockedPrivateBrowsingInfoHashtable = gPrivateBrowsingInfoHashtable->Lock(); MOZ_ALWAYS_TRUE( lockedPrivateBrowsingInfoHashtable->Get(mDatabaseId, &key)); } MOZ_RELEASE_ASSERT(mInPrivateBrowsing == maybeKey.isSome()); maybeKey.emplace(std::move(key)); } QM_TRY_UNWRAP( NotNull<nsCOMPtr<mozIStorageConnection>> connection, Loading Loading @@ -15932,8 +15897,7 @@ nsresult OpenDatabaseOp::DoDatabaseWork() { if (!fileManager) { fileManager = MakeSafeRefPtr<DatabaseFileManager>( persistenceType, mOriginMetadata, databaseName, mDatabaseId, mEnforcingQuota, mInPrivateBrowsing); persistenceType, mOriginMetadata, databaseName, mEnforcingQuota); QM_TRY(MOZ_TO_RESULT(fileManager->Init(fmDirectory, *connection))); Loading Loading @@ -16593,11 +16557,19 @@ void OpenDatabaseOp::EnsureDatabaseActor() { mMetadata = info->mMetadata.clonePtr(); } Maybe<const CipherKey> maybeKey = mInPrivateBrowsing ? gIndexedDBCipherKeyManager->Get(mDatabaseId) : Nothing(); Maybe<const CipherKey> maybeKey; if (mInPrivateBrowsing) { CipherKey key; { const auto lockedPrivateBrowsingInfoHashtable = gPrivateBrowsingInfoHashtable->Lock(); MOZ_ALWAYS_TRUE( lockedPrivateBrowsingInfoHashtable->Get(mDatabaseId, &key)); } MOZ_RELEASE_ASSERT(mInPrivateBrowsing == maybeKey.isSome()); maybeKey.emplace(std::move(key)); } // XXX Shouldn't Manager() return already_AddRefed when // PBackgroundIDBFactoryParent is declared refcounted? Loading Loading @@ -16900,11 +16872,16 @@ void DeleteDatabaseOp::LoadPreviousVersion(nsIFile& aDatabaseFile) { return; } const auto maybeKey = mInPrivateBrowsing ? gIndexedDBCipherKeyManager->Get(mDatabaseId) : Nothing(); const auto maybeKey = [this]() -> Maybe<const CipherKey> { CipherKey key; MOZ_RELEASE_ASSERT(mInPrivateBrowsing == maybeKey.isSome()); const auto lockedPrivateBrowsingInfoHashtable = gPrivateBrowsingInfoHashtable->Lock(); if (!lockedPrivateBrowsingInfoHashtable->Get(mDatabaseId, &key)) { return Nothing{}; } return Some(std::move(key)); }(); // Pass -1 as the directoryLockId to disable quota checking, since we might // temporarily exceed quota before deleting the database. Loading Loading @@ -17175,11 +17152,6 @@ nsresult DeleteDatabaseOp::VersionChangeOp::RunOnIOThread() { return rv; } if (mDeleteDatabaseOp->mInPrivateBrowsing) { MOZ_ASSERT( gIndexedDBCipherKeyManager->Remove(mDeleteDatabaseOp->mDatabaseId)); } rv = mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; Loading Loading @@ -19555,22 +19527,11 @@ nsresult ObjectStoreAddOrPutRequestOp::DoDatabaseWork( QM_TRY(OkIf(journalFile), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, IDB_REPORT_INTERNAL_ERR_LAMBDA); nsCString fileKeyId; fileKeyId.AppendInt(fileInfo.Id()); const auto maybeKey = Transaction() .GetDatabase() .GetFileManager() .IsInPrivateBrowsingMode() ? gIndexedDBCipherKeyManager->Get( Transaction().GetDatabase().Id(), fileKeyId) : Nothing(); QM_TRY( MOZ_TO_RESULT(fileHelper->CreateFileFromStream( *file, *journalFile, *inputStream, storedFileInfo.ShouldCompress(), maybeKey)) storedFileInfo.ShouldCompress(), Transaction().GetDatabase().MaybeKeyRef())) .mapErr([](const nsresult rv) { if (NS_ERROR_GET_MODULE(rv) != NS_ERROR_MODULE_DOM_INDEXEDDB) {
dom/indexedDB/ActorsParentCommon.h +3 −1 Original line number Diff line number Diff line Loading @@ -20,7 +20,6 @@ #include "mozilla/UniquePtrExtensions.h" #include "mozilla/dom/indexedDB/Key.h" #include "mozilla/dom/quota/IPCStreamCipherStrategy.h" #include "IndexedDBCipherKeyManager.h" #include "nscore.h" #include "nsISupports.h" #include "nsStringFwd.h" Loading @@ -40,6 +39,9 @@ struct StructuredCloneReadInfoParent; extern const nsLiteralString kJournalDirectoryName; using IndexedDBCipherStrategy = quota::IPCStreamCipherStrategy; using CipherKey = IndexedDBCipherStrategy::KeyType; // At the moment, the encrypted stream block size is assumed to be unchangeable // between encrypting and decrypting blobs. This assumptions holds as long as we // only encrypt in private browsing mode, but when we support encryption for Loading
dom/indexedDB/DatabaseFileManager.h +2 −7 Original line number Diff line number Diff line Loading @@ -28,13 +28,11 @@ class DatabaseFileManager final const PersistenceType mPersistenceType; const quota::OriginMetadata mOriginMetadata; const nsString mDatabaseName; const nsCString mDatabaseID; LazyInitializedOnce<const nsString> mDirectoryPath; LazyInitializedOnce<const nsString> mJournalDirectoryPath; const bool mEnforcingQuota; const bool mIsInPrivateBrowsingMode; // Lock protecting DatabaseFileManager.mFileInfos. // It's s also used to atomically update DatabaseFileInfo.mRefCnt and Loading @@ -61,9 +59,7 @@ class DatabaseFileManager final DatabaseFileManager(PersistenceType aPersistenceType, const quota::OriginMetadata& aOriginMetadata, const nsAString& aDatabaseName, const nsCString& aDatabaseID, bool aEnforcingQuota, bool aIsInPrivateBrowsingMode); const nsAString& aDatabaseName, bool aEnforcingQuota); PersistenceType Type() const { return mPersistenceType; } Loading @@ -74,8 +70,7 @@ class DatabaseFileManager final const nsACString& Origin() const { return mOriginMetadata.mOrigin; } const nsAString& DatabaseName() const { return mDatabaseName; } const nsCString& DatabaseID() const { return mDatabaseID; } auto IsInPrivateBrowsingMode() const { return mIsInPrivateBrowsingMode; } bool EnforcingQuota() const { return mEnforcingQuota; } nsresult Init(nsIFile* aDirectory, mozIStorageConnection& aConnection); Loading
dom/indexedDB/IndexedDBCipherKeyManager.hdeleted 100644 → 0 +0 −52 Original line number Diff line number Diff line /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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_dom_indexeddbcipherKeyManager_h #define mozilla_dom_indexeddbcipherKeyManager_h #include "mozilla/dom/quota/IPCStreamCipherStrategy.h" #include "mozilla/DataMutex.h" #include "nsTHashMap.h" namespace mozilla::dom::indexedDB { namespace { using IndexedDBCipherStrategy = quota::IPCStreamCipherStrategy; using CipherKey = IndexedDBCipherStrategy::KeyType; class IndexedDBCipherKeyManager { // This helper class is used by IndexedDB operations to store/retrieve cipher // keys in private browsing mode. All data in IndexedDB must be encrypted // using a cipher key and unique IV (Initialization Vector). While there's a // separate cipher key for every blob file; any normal key/value pairs get // encrypted using the commmon database key. All keys pertaining to a single // database get stored together using the database id in a hashmap. We are // using a 2-level hashmap here; keys get stored effectively at level 2. // Looking up at level 1 using database id gives us access to hashmap of keys // corresponding to that database which we could use to look up the common // database key and blob keys using "default" and blob file ids respectively. public: using PrivateBrowsingInfoHashtable = nsTHashMap<nsCStringHashKey, nsTHashMap<nsCStringHashKey, CipherKey>>; IndexedDBCipherKeyManager() : mPrivateBrowsingInfoHashTable("IndexedDBCipherKeyManager"){}; Maybe<CipherKey> Get(const nsCString& aDatabaseID, const nsCString& keyStoreID = "default"_ns); CipherKey Ensure(const nsCString& aDatabaseID, const nsCString& keyStoreID = "default"_ns); bool Remove(const nsCString& aDatabaseID); private: DataMutex<PrivateBrowsingInfoHashtable> mPrivateBrowsingInfoHashTable; }; } // namespace } // namespace mozilla::dom::indexedDB #endif // IndexedDBCipherKeyManager_h
dom/indexedDB/SchemaUpgrades.cpp +1 −2 Original line number Diff line number Diff line Loading @@ -2862,8 +2862,7 @@ nsresult UpgradeFileIdsFunction::Init(nsIFile* aFMDirectory, // purpose is to store file ids without adding more complexity or code // duplication. auto fileManager = MakeSafeRefPtr<DatabaseFileManager>( PERSISTENCE_TYPE_INVALID, quota::OriginMetadata{}, u""_ns, ""_ns, false, false); PERSISTENCE_TYPE_INVALID, quota::OriginMetadata{}, u""_ns, false); nsresult rv = fileManager->Init(aFMDirectory, aConnection); if (NS_WARN_IF(NS_FAILED(rv))) { Loading