From 8f9c9eeb91ca7330266ddfc007af0c947990ae72 Mon Sep 17 00:00:00 2001 From: Rexios Date: Thu, 15 Jan 2026 09:29:09 -0500 Subject: [PATCH 01/15] Add test --- hive/test/integration/isolate_test.dart | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/hive/test/integration/isolate_test.dart b/hive/test/integration/isolate_test.dart index 68f8d09d..75caf2ba 100644 --- a/hive/test/integration/isolate_test.dart +++ b/hive/test/integration/isolate_test.dart @@ -234,6 +234,27 @@ void main() { final box = await hive.openBox('test'); expect(await box.get('key'), 'value'); }); + + test('Encrypted Hive data is compatible with IsolatedHive', () async { + final dir = await getTempDir(); + final cipher = HiveAesCipher(Hive.generateSecureKey()); + + final hive = HiveImpl(); + addTearDown(hive.close); + hive.init(dir.path); + + final box = await hive.openBox('test', encryptionCipher: cipher); + await box.put('key', 'value'); + await box.close(); + + final isolatedHive = IsolatedHiveImpl(); + addTearDown(isolatedHive.close); + await isolatedHive.init(dir.path, isolateNameServer: StubIns()); + + final isolatedBox = + await isolatedHive.openBox('test', encryptionCipher: cipher); + expect(await isolatedBox.get('key'), 'value'); + }); }, onPlatform: { 'chrome': Skip('Isolates are not supported on web'), From 16ab24d53b57dd8c002a1e0d6c3195f32649e7e8 Mon Sep 17 00:00:00 2001 From: Rexios Date: Thu, 15 Jan 2026 10:47:53 -0500 Subject: [PATCH 02/15] Add tests --- hive/test/integration/isolate_test.dart | 51 ++++++++++++++++++++----- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/hive/test/integration/isolate_test.dart b/hive/test/integration/isolate_test.dart index 75caf2ba..bd5755eb 100644 --- a/hive/test/integration/isolate_test.dart +++ b/hive/test/integration/isolate_test.dart @@ -235,29 +235,60 @@ void main() { expect(await box.get('key'), 'value'); }); - test('Encrypted Hive data is compatible with IsolatedHive', () async { + test('Encrypted IsolatedHive data compatable with Hive', () async { final dir = await getTempDir(); final cipher = HiveAesCipher(Hive.generateSecureKey()); - final hive = HiveImpl(); - addTearDown(hive.close); - hive.init(dir.path); - - final box = await hive.openBox('test', encryptionCipher: cipher); - await box.put('key', 'value'); - await box.close(); - final isolatedHive = IsolatedHiveImpl(); addTearDown(isolatedHive.close); await isolatedHive.init(dir.path, isolateNameServer: StubIns()); final isolatedBox = await isolatedHive.openBox('test', encryptionCipher: cipher); - expect(await isolatedBox.get('key'), 'value'); + await isolatedBox.put('key', 'value'); + await isolatedBox.close(); + + final hive = HiveImpl(); + addTearDown(hive.close); + hive.init(dir.path); + + final box = await hive.openBox('test', encryptionCipher: cipher); + expect(await box.get('key'), 'value'); }); + + test( + 'IsolatedHive data encrypted with no keyCrc is readable', + () async { + final dir = await getTempDir(); + final key = Hive.generateSecureKey(); + + final isolatedHive = IsolatedHiveImpl(); + addTearDown(isolatedHive.close); + await isolatedHive.init(dir.path, isolateNameServer: StubIns()); + + final box = await isolatedHive.openBox('test', + encryptionCipher: ZeroKeyCrcCipher(key)); + await box.put('key', 'value'); + await box.close(); + + await isolatedHive.openBox( + 'test', + encryptionCipher: HiveAesCipher(key), + ); + expect(await box.get('key'), 'value'); + }, + ); }, onPlatform: { 'chrome': Skip('Isolates are not supported on web'), }, ); } + +/// Test cipher that always returns a zero key CRC +class ZeroKeyCrcCipher extends HiveAesCipher { + ZeroKeyCrcCipher(super.key); + + @override + int calculateKeyCrc() => 0; +} From 8ca1225f746b40bb80f69c571b39d5f830c33bc6 Mon Sep 17 00:00:00 2001 From: Rexios Date: Thu, 15 Jan 2026 11:02:52 -0500 Subject: [PATCH 03/15] Add keyCrc parameter --- .../backend/js/native/storage_backend_js.dart | 7 +- hive/lib/src/backend/storage_backend.dart | 3 +- .../src/backend/storage_backend_memory.dart | 4 +- .../src/backend/vm/storage_backend_vm.dart | 19 ++++-- hive/lib/src/binary/binary_reader_impl.dart | 24 +++++-- hive/lib/src/binary/frame_helper.dart | 2 + hive/lib/src/box/box_base_impl.dart | 6 ++ hive/lib/src/box/lazy_box_impl.dart | 3 +- hive/lib/src/io/frame_io_helper.dart | 12 +++- .../handler/isolated_hive_handler.dart | 67 +++++++++++-------- .../impl/isolated_hive_impl_vm.dart | 39 ++++++----- hive/test/integration/isolate_test.dart | 6 +- .../backend/vm/storage_backend_vm_test.dart | 1 + hive/test/tests/io/frame_io_helper_test.dart | 4 +- 14 files changed, 132 insertions(+), 65 deletions(-) diff --git a/hive/lib/src/backend/js/native/storage_backend_js.dart b/hive/lib/src/backend/js/native/storage_backend_js.dart index 638ccbda..af7e5bc6 100644 --- a/hive/lib/src/backend/js/native/storage_backend_js.dart +++ b/hive/lib/src/backend/js/native/storage_backend_js.dart @@ -166,6 +166,7 @@ class StorageBackendJs extends StorageBackend { Keystore keystore, bool lazy, { bool isolated = false, + int? keyCrc, }) async { _registry = registry; final keys = await getKeys(); @@ -186,7 +187,11 @@ class StorageBackendJs extends StorageBackend { } @override - Future readValue(Frame frame, {bool verbatim = false}) async { + Future readValue( + Frame frame, { + bool verbatim = false, + int? keyCrc, + }) async { final value = await getStore(false).get(frame.key.jsify()).asFuture(); return decodeValue(value); } diff --git a/hive/lib/src/backend/storage_backend.dart b/hive/lib/src/backend/storage_backend.dart index 79bb4330..bca27434 100644 --- a/hive/lib/src/backend/storage_backend.dart +++ b/hive/lib/src/backend/storage_backend.dart @@ -22,10 +22,11 @@ abstract class StorageBackend { Keystore keystore, bool lazy, { bool isolated = false, + int? keyCrc, }); /// Read value from backend - Future readValue(Frame frame, {bool verbatim = false}); + Future readValue(Frame frame, {bool verbatim = false, int? keyCrc}); /// Write a list of frames to the backend Future writeFrames(List frames, {bool verbatim = false}); diff --git a/hive/lib/src/backend/storage_backend_memory.dart b/hive/lib/src/backend/storage_backend_memory.dart index 02dd667e..fb07cb98 100644 --- a/hive/lib/src/backend/storage_backend_memory.dart +++ b/hive/lib/src/backend/storage_backend_memory.dart @@ -31,12 +31,14 @@ class StorageBackendMemory extends StorageBackend { Keystore? keystore, bool lazy, { bool isolated = false, + int? keyCrc, }) { final recoveryOffset = _frameHelper.framesFromBytes( _bytes!, // Initialized at constructor and nulled after initialization keystore, registry, _cipher, + keyCrc: keyCrc, ); if (recoveryOffset != -1) { @@ -49,7 +51,7 @@ class StorageBackendMemory extends StorageBackend { } @override - Future readValue(Frame frame, {bool verbatim = false}) { + Future readValue(Frame frame, {bool verbatim = false, int? keyCrc}) { throw UnsupportedError('This operation is unsupported for memory boxes.'); } diff --git a/hive/lib/src/backend/vm/storage_backend_vm.dart b/hive/lib/src/backend/vm/storage_backend_vm.dart index 8f1e8902..4ef859e0 100644 --- a/hive/lib/src/backend/vm/storage_backend_vm.dart +++ b/hive/lib/src/backend/vm/storage_backend_vm.dart @@ -105,6 +105,7 @@ RECOMMENDED ACTIONS: Keystore keystore, bool lazy, { bool isolated = false, + int? keyCrc, }) async { this.registry = registry; @@ -133,9 +134,11 @@ RECOMMENDED ACTIONS: registry, _cipher, verbatim: isolated, + keyCrc: keyCrc, ); } else { - recoveryOffset = await _frameHelper.keysFromFile(path, keystore, _cipher); + recoveryOffset = + await _frameHelper.keysFromFile(path, keystore, _cipher, keyCrc); } if (recoveryOffset != -1) { @@ -151,15 +154,23 @@ RECOMMENDED ACTIONS: } @override - Future readValue(Frame frame, {bool verbatim = false}) { + Future readValue( + Frame frame, { + bool verbatim = false, + int? keyCrc, + }) { return _sync.syncRead(() async { await readRaf.setPosition(frame.offset); final bytes = await readRaf.read(frame.length!); final reader = BinaryReaderImpl(bytes, registry); - final readFrame = - reader.readFrame(cipher: _cipher, lazy: false, verbatim: verbatim); + final readFrame = reader.readFrame( + cipher: _cipher, + lazy: false, + verbatim: verbatim, + keyCrc: keyCrc, + ); if (readFrame == null) { throw HiveError( diff --git a/hive/lib/src/binary/binary_reader_impl.dart b/hive/lib/src/binary/binary_reader_impl.dart index ea15bd3a..6202217b 100644 --- a/hive/lib/src/binary/binary_reader_impl.dart +++ b/hive/lib/src/binary/binary_reader_impl.dart @@ -262,6 +262,7 @@ class BinaryReaderImpl extends BinaryReader { bool lazy = false, int frameOffset = 0, bool verbatim = false, + int? keyCrc, }) { // frame length is stored on 4 bytes if (availableBytes < 4) return null; @@ -274,15 +275,30 @@ class BinaryReaderImpl extends BinaryReader { if (availableBytes < frameLength - 4) return null; final crc = _buffer.readUint32(_offset + frameLength - 8); + final crcOffset = _offset - 4; + final crcLength = frameLength - 4; final computedCrc = Crc32.compute( _buffer, - offset: _offset - 4, - length: frameLength - 4, - crc: cipher?.calculateKeyCrc() ?? 0, + offset: crcOffset, + length: crcLength, + crc: keyCrc ?? cipher?.calculateKeyCrc() ?? 0, ); // frame is corrupted or provided chiper is different - if (computedCrc != crc) return null; + if (computedCrc != crc) { + if (keyCrc != null) { + // Attempt to calculate the crc without the key crc + // This maintains compatibility with data written by IsolatedHive before keyCrc was introduced + final computedCrc2 = Crc32.compute( + _buffer, + offset: crcOffset, + length: crcLength, + ); + if (computedCrc2 != crc) return null; + } else { + return null; + } + } _limitAvailableBytes(frameLength - 8); Frame frame; diff --git a/hive/lib/src/binary/frame_helper.dart b/hive/lib/src/binary/frame_helper.dart index 525333ae..35248b57 100644 --- a/hive/lib/src/binary/frame_helper.dart +++ b/hive/lib/src/binary/frame_helper.dart @@ -13,6 +13,7 @@ class FrameHelper { TypeRegistry registry, HiveCipher? cipher, { bool verbatim = false, + int? keyCrc, }) { final reader = BinaryReaderImpl(bytes, registry); @@ -24,6 +25,7 @@ class FrameHelper { lazy: false, frameOffset: frameOffset, verbatim: verbatim, + keyCrc: keyCrc, ); if (frame == null) return frameOffset; diff --git a/hive/lib/src/box/box_base_impl.dart b/hive/lib/src/box/box_base_impl.dart index 77fb9c60..b25c3719 100644 --- a/hive/lib/src/box/box_base_impl.dart +++ b/hive/lib/src/box/box_base_impl.dart @@ -36,6 +36,11 @@ abstract class BoxBaseImpl implements BoxBase, InspectableBox { /// Whether this box is isolated final bool isolated; + /// They key CRC for the box + /// + /// Used by [IsolatedHive] to support opening encrypted boxes + int? keyCrc; + var _open = true; /// Not part of public API @@ -104,6 +109,7 @@ abstract class BoxBaseImpl implements BoxBase, InspectableBox { keystore, lazy, isolated: isolated, + keyCrc: keyCrc, ); } diff --git a/hive/lib/src/box/lazy_box_impl.dart b/hive/lib/src/box/lazy_box_impl.dart index 0a3f13b8..60a9a2d9 100644 --- a/hive/lib/src/box/lazy_box_impl.dart +++ b/hive/lib/src/box/lazy_box_impl.dart @@ -25,7 +25,8 @@ class LazyBoxImpl extends BoxBaseImpl implements LazyBox { final frame = keystore.get(key); if (frame != null) { - final value = await backend.readValue(frame, verbatim: isolated); + final value = + await backend.readValue(frame, verbatim: isolated, keyCrc: keyCrc); if (value is HiveObjectMixin) { value.init(key, this); } diff --git a/hive/lib/src/io/frame_io_helper.dart b/hive/lib/src/io/frame_io_helper.dart index 7cb86215..c3817853 100644 --- a/hive/lib/src/io/frame_io_helper.dart +++ b/hive/lib/src/io/frame_io_helper.dart @@ -28,11 +28,12 @@ class FrameIoHelper extends FrameHelper { String path, Keystore keystore, HiveCipher? cipher, + int? keyCrc, ) async { final raf = await openFile(path); final fileReader = BufferedFileReader(raf); try { - return await _KeyReader(fileReader).readKeys(keystore, cipher); + return await _KeyReader(fileReader).readKeys(keystore, cipher, keyCrc); } finally { await raf.close(); } @@ -45,6 +46,7 @@ class FrameIoHelper extends FrameHelper { TypeRegistry registry, HiveCipher? cipher, { bool verbatim = false, + int? keyCrc, }) async { final bytes = await readFile(path); return framesFromBytes( @@ -53,6 +55,7 @@ class FrameIoHelper extends FrameHelper { registry, cipher, verbatim: verbatim, + keyCrc: keyCrc, ); } } @@ -64,7 +67,11 @@ class _KeyReader { _KeyReader(this.fileReader); - Future readKeys(Keystore keystore, HiveCipher? cipher) async { + Future readKeys( + Keystore keystore, + HiveCipher? cipher, + int? keyCrc, + ) async { await _load(4); while (true) { final frameOffset = fileReader.offset; @@ -88,6 +95,7 @@ class _KeyReader { cipher: cipher, lazy: true, frameOffset: frameOffset, + keyCrc: keyCrc, ); if (frame == null) return frameOffset; diff --git a/hive/lib/src/isolate/handler/isolated_hive_handler.dart b/hive/lib/src/isolate/handler/isolated_hive_handler.dart index f1aa7b08..c3ed09bd 100644 --- a/hive/lib/src/isolate/handler/isolated_hive_handler.dart +++ b/hive/lib/src/isolate/handler/isolated_hive_handler.dart @@ -1,4 +1,5 @@ import 'package:hive_ce/hive_ce.dart'; +import 'package:hive_ce/src/box/box_base_impl.dart'; import 'package:hive_ce/src/box/default_compaction_strategy.dart'; import 'package:hive_ce/src/box/default_key_comparator.dart'; import 'package:hive_ce/src/hive_impl.dart'; @@ -20,40 +21,52 @@ Future handleHiveMethodCall( Logger.level = LoggerLevel.values.byName(loggerLevel); case 'openBox': final name = call.arguments['name']; + final lazy = call.arguments['lazy']; + if (boxHandlers.containsKey(name)) { // Ensure this is a valid `openBox` call - Hive.box(name); + if (lazy) { + Hive.lazyBox(name); + } else { + Hive.box(name); + } return; } - final box = await Hive.openBox( - name, - keyComparator: call.arguments['keyComparator'] ?? defaultKeyComparator, - compactionStrategy: - call.arguments['compactionStrategy'] ?? defaultCompactionStrategy, - crashRecovery: call.arguments['crashRecovery'], - path: call.arguments['path'], - bytes: call.arguments['bytes'], - collection: call.arguments['collection'], - ); - boxHandlers[name] = IsolatedBoxHandler(box, connection); - case 'openLazyBox': - final name = call.arguments['name']; - if (boxHandlers.containsKey(name)) { - // Ensure this is a valid `openLazyBox` call - Hive.lazyBox(name); - return; + final keyComparator = + call.arguments['keyComparator'] ?? defaultKeyComparator; + final compactionStrategy = + call.arguments['compactionStrategy'] ?? defaultCompactionStrategy; + final crashRecovery = call.arguments['crashRecovery']; + final path = call.arguments['path']; + final bytes = call.arguments['bytes']; + final collection = call.arguments['collection']; + + final BoxBase box; + if (lazy) { + box = await Hive.openLazyBox( + name, + keyComparator: keyComparator, + compactionStrategy: compactionStrategy, + crashRecovery: crashRecovery, + path: path, + collection: collection, + ); + } else { + box = await Hive.openBox( + name, + keyComparator: keyComparator, + compactionStrategy: compactionStrategy, + crashRecovery: crashRecovery, + path: path, + bytes: bytes, + collection: collection, + ); } - final box = await Hive.openLazyBox( - name, - keyComparator: call.arguments['keyComparator'] ?? defaultKeyComparator, - compactionStrategy: - call.arguments['compactionStrategy'] ?? defaultCompactionStrategy, - crashRecovery: call.arguments['crashRecovery'], - path: call.arguments['path'], - collection: call.arguments['collection'], - ); + final keyCrc = call.arguments['keyCrc']; + (box as BoxBaseImpl).keyCrc = keyCrc; + boxHandlers[name] = IsolatedBoxHandler(box, connection); case 'deleteBoxFromDisk': await Hive.deleteBoxFromDisk( diff --git a/hive/lib/src/isolate/isolated_hive_impl/impl/isolated_hive_impl_vm.dart b/hive/lib/src/isolate/isolated_hive_impl/impl/isolated_hive_impl_vm.dart index adfab3f8..cdde438e 100644 --- a/hive/lib/src/isolate/isolated_hive_impl/impl/isolated_hive_impl_vm.dart +++ b/hive/lib/src/isolate/isolated_hive_impl/impl/isolated_hive_impl_vm.dart @@ -122,34 +122,33 @@ class IsolatedHiveImpl extends TypeRegistryImpl try { final params = { 'name': name, + 'lazy': lazy, 'keyComparator': comparator, 'compactionStrategy': compaction, 'crashRecovery': recovery, 'path': path, 'bytes': bytes, 'collection': collection, + 'keyCrc': cipher?.calculateKeyCrc(), }; - final IsolatedBoxBaseImpl newBox; - if (lazy) { - await _hiveChannel.invokeMethod('openLazyBox', params); - newBox = IsolatedLazyBoxImpl( - this, - name, - cipher, - connection, - _boxChannel, - ); - } else { - await _hiveChannel.invokeMethod('openBox', params); - newBox = IsolatedBoxImpl( - this, - name, - cipher, - connection, - _boxChannel, - ); - } + await _hiveChannel.invokeMethod('openBox', params); + + final newBox = lazy + ? IsolatedLazyBoxImpl( + this, + name, + cipher, + connection, + _boxChannel, + ) + : IsolatedBoxImpl( + this, + name, + cipher, + connection, + _boxChannel, + ); _boxes[name] = newBox; diff --git a/hive/test/integration/isolate_test.dart b/hive/test/integration/isolate_test.dart index bd5755eb..dda13fd1 100644 --- a/hive/test/integration/isolate_test.dart +++ b/hive/test/integration/isolate_test.dart @@ -266,8 +266,10 @@ void main() { addTearDown(isolatedHive.close); await isolatedHive.init(dir.path, isolateNameServer: StubIns()); - final box = await isolatedHive.openBox('test', - encryptionCipher: ZeroKeyCrcCipher(key)); + final box = await isolatedHive.openBox( + 'test', + encryptionCipher: ZeroKeyCrcCipher(key), + ); await box.put('key', 'value'); await box.close(); diff --git a/hive/test/tests/backend/vm/storage_backend_vm_test.dart b/hive/test/tests/backend/vm/storage_backend_vm_test.dart index bd0786b0..7072f92d 100644 --- a/hive/test/tests/backend/vm/storage_backend_vm_test.dart +++ b/hive/test/tests/backend/vm/storage_backend_vm_test.dart @@ -136,6 +136,7 @@ void main() { any(), any(), any(), + any(), ), ).thenAnswer((i) => Future.value(recoveryOffset)); return helper; diff --git a/hive/test/tests/io/frame_io_helper_test.dart b/hive/test/tests/io/frame_io_helper_test.dart index 60b644e3..7af0c946 100644 --- a/hive/test/tests/io/frame_io_helper_test.dart +++ b/hive/test/tests/io/frame_io_helper_test.dart @@ -44,7 +44,7 @@ void main() { final keystore = Keystore.debug(); final ioHelper = _FrameIoHelperTest(_getBytes(frameBytes)); final recoveryOffset = - await ioHelper.keysFromFile('null', keystore, null); + await ioHelper.keysFromFile('null', keystore, null, null); expect(recoveryOffset, -1); final testKeystore = Keystore.debug( @@ -58,7 +58,7 @@ void main() { final keystore = Keystore.debug(); final ioHelper = _FrameIoHelperTest(_getBytes(frameBytesEncrypted)); final recoveryOffset = - await ioHelper.keysFromFile('null', keystore, testCipher); + await ioHelper.keysFromFile('null', keystore, testCipher, null); expect(recoveryOffset, -1); final testKeystore = Keystore.debug( From 792e245b67abe34062f10337781c2067a9abf0e8 Mon Sep 17 00:00:00 2001 From: Rexios Date: Thu, 15 Jan 2026 11:30:13 -0500 Subject: [PATCH 04/15] It works --- .../backend/js/native/backend_manager.dart | 1 + hive/lib/src/backend/storage_backend.dart | 4 ++-- .../src/backend/storage_backend_memory.dart | 6 +++--- .../lib/src/backend/stub/backend_manager.dart | 1 + hive/lib/src/backend/vm/backend_manager.dart | 4 +++- .../src/backend/vm/storage_backend_vm.dart | 19 ++++++++++------- hive/lib/src/binary/binary_reader_impl.dart | 2 +- hive/lib/src/binary/binary_writer_impl.dart | 9 ++++++-- hive/lib/src/binary/frame_helper.dart | 6 +++--- hive/lib/src/box/box_base_impl.dart | 6 ------ hive/lib/src/box/lazy_box_impl.dart | 3 +-- hive/lib/src/hive_impl.dart | 8 ++++++- hive/lib/src/io/frame_io_helper.dart | 6 +++--- .../handler/isolated_hive_handler.dart | 11 +++++----- .../impl/isolated_hive_impl_vm.dart | 2 +- hive/test/integration/isolate_test.dart | 21 +++++++++++++++++++ .../backend/storage_backend_memory_test.dart | 18 ++++++++-------- .../backend/vm/storage_backend_vm_test.dart | 3 +++ hive/test/tests/io/frame_io_helper_test.dart | 10 +++++++-- 19 files changed, 91 insertions(+), 49 deletions(-) diff --git a/hive/lib/src/backend/js/native/backend_manager.dart b/hive/lib/src/backend/js/native/backend_manager.dart index 5d8f4c4c..2442f935 100644 --- a/hive/lib/src/backend/js/native/backend_manager.dart +++ b/hive/lib/src/backend/js/native/backend_manager.dart @@ -18,6 +18,7 @@ class BackendManager implements BackendManagerInterface { String? path, bool crashRecovery, HiveCipher? cipher, + int? keyCrc, String? collection, ) async { // compatibility for old store format diff --git a/hive/lib/src/backend/storage_backend.dart b/hive/lib/src/backend/storage_backend.dart index bca27434..211ec832 100644 --- a/hive/lib/src/backend/storage_backend.dart +++ b/hive/lib/src/backend/storage_backend.dart @@ -22,11 +22,10 @@ abstract class StorageBackend { Keystore keystore, bool lazy, { bool isolated = false, - int? keyCrc, }); /// Read value from backend - Future readValue(Frame frame, {bool verbatim = false, int? keyCrc}); + Future readValue(Frame frame, {bool verbatim = false}); /// Write a list of frames to the backend Future writeFrames(List frames, {bool verbatim = false}); @@ -55,6 +54,7 @@ abstract class BackendManagerInterface { String? path, bool crashRecovery, HiveCipher? cipher, + int? keyCrc, String? collection, ); diff --git a/hive/lib/src/backend/storage_backend_memory.dart b/hive/lib/src/backend/storage_backend_memory.dart index fb07cb98..c5c70b7e 100644 --- a/hive/lib/src/backend/storage_backend_memory.dart +++ b/hive/lib/src/backend/storage_backend_memory.dart @@ -9,13 +9,14 @@ import 'package:hive_ce/src/box/keystore.dart'; /// In-memory Storage backend class StorageBackendMemory extends StorageBackend { final HiveCipher? _cipher; + final int? _keyCrc; final FrameHelper _frameHelper; Uint8List? _bytes; /// Not part of public API - StorageBackendMemory(Uint8List? bytes, this._cipher) + StorageBackendMemory(Uint8List? bytes, this._cipher, this._keyCrc) : _bytes = bytes, _frameHelper = FrameHelper(); @@ -31,14 +32,13 @@ class StorageBackendMemory extends StorageBackend { Keystore? keystore, bool lazy, { bool isolated = false, - int? keyCrc, }) { final recoveryOffset = _frameHelper.framesFromBytes( _bytes!, // Initialized at constructor and nulled after initialization keystore, registry, _cipher, - keyCrc: keyCrc, + _keyCrc, ); if (recoveryOffset != -1) { diff --git a/hive/lib/src/backend/stub/backend_manager.dart b/hive/lib/src/backend/stub/backend_manager.dart index dad11cc0..df385b75 100644 --- a/hive/lib/src/backend/stub/backend_manager.dart +++ b/hive/lib/src/backend/stub/backend_manager.dart @@ -17,6 +17,7 @@ class BackendManager implements BackendManagerInterface { String? path, bool crashRecovery, HiveCipher? cipher, + int? keyCrc, String? collection, ) { throw UnimplementedError(); diff --git a/hive/lib/src/backend/vm/backend_manager.dart b/hive/lib/src/backend/vm/backend_manager.dart index e376e59f..aeb4f28e 100644 --- a/hive/lib/src/backend/vm/backend_manager.dart +++ b/hive/lib/src/backend/vm/backend_manager.dart @@ -23,6 +23,7 @@ class BackendManager implements BackendManagerInterface { String? path, bool crashRecovery, HiveCipher? cipher, + int? keyCrc, String? collection, ) async { if (path == null) { @@ -45,7 +46,8 @@ class BackendManager implements BackendManagerInterface { final file = await findHiveFileAndCleanUp(name, path); final lockFile = File('$path$_delimiter$name.lock'); - final backend = StorageBackendVm(file, lockFile, crashRecovery, cipher); + final backend = + StorageBackendVm(file, lockFile, crashRecovery, cipher, keyCrc); await backend.open(); return backend; } diff --git a/hive/lib/src/backend/vm/storage_backend_vm.dart b/hive/lib/src/backend/vm/storage_backend_vm.dart index 4ef859e0..25daba91 100644 --- a/hive/lib/src/backend/vm/storage_backend_vm.dart +++ b/hive/lib/src/backend/vm/storage_backend_vm.dart @@ -37,6 +37,7 @@ RECOMMENDED ACTIONS: final File _lockFile; final bool _crashRecovery; final HiveCipher? _cipher; + final int? _keyCrc; final FrameIoHelper _frameHelper; final ReadWriteSync _sync; @@ -73,6 +74,7 @@ RECOMMENDED ACTIONS: this._lockFile, this._crashRecovery, this._cipher, + this._keyCrc, ) : _frameHelper = FrameIoHelper(), _sync = ReadWriteSync(); @@ -82,6 +84,7 @@ RECOMMENDED ACTIONS: this._lockFile, this._crashRecovery, this._cipher, + this._keyCrc, this._frameHelper, this._sync, ); @@ -105,7 +108,6 @@ RECOMMENDED ACTIONS: Keystore keystore, bool lazy, { bool isolated = false, - int? keyCrc, }) async { this.registry = registry; @@ -133,12 +135,12 @@ RECOMMENDED ACTIONS: keystore, registry, _cipher, + _keyCrc, verbatim: isolated, - keyCrc: keyCrc, ); } else { recoveryOffset = - await _frameHelper.keysFromFile(path, keystore, _cipher, keyCrc); + await _frameHelper.keysFromFile(path, keystore, _cipher, _keyCrc); } if (recoveryOffset != -1) { @@ -157,7 +159,6 @@ RECOMMENDED ACTIONS: Future readValue( Frame frame, { bool verbatim = false, - int? keyCrc, }) { return _sync.syncRead(() async { await readRaf.setPosition(frame.offset); @@ -169,7 +170,7 @@ RECOMMENDED ACTIONS: cipher: _cipher, lazy: false, verbatim: verbatim, - keyCrc: keyCrc, + keyCrc: _keyCrc, ); if (readFrame == null) { @@ -188,8 +189,12 @@ RECOMMENDED ACTIONS: final writer = BinaryWriterImpl(registry); for (final frame in frames) { - frame.length = - writer.writeFrame(frame, cipher: _cipher, verbatim: verbatim); + frame.length = writer.writeFrame( + frame, + cipher: _cipher, + keyCrc: _keyCrc, + verbatim: verbatim, + ); } try { diff --git a/hive/lib/src/binary/binary_reader_impl.dart b/hive/lib/src/binary/binary_reader_impl.dart index 6202217b..e32d5eb8 100644 --- a/hive/lib/src/binary/binary_reader_impl.dart +++ b/hive/lib/src/binary/binary_reader_impl.dart @@ -259,10 +259,10 @@ class BinaryReaderImpl extends BinaryReader { /// Not part of public API Frame? readFrame({ HiveCipher? cipher, + int? keyCrc, bool lazy = false, int frameOffset = 0, bool verbatim = false, - int? keyCrc, }) { // frame length is stored on 4 bytes if (availableBytes < 4) return null; diff --git a/hive/lib/src/binary/binary_writer_impl.dart b/hive/lib/src/binary/binary_writer_impl.dart index cdebc275..47a01d1b 100644 --- a/hive/lib/src/binary/binary_writer_impl.dart +++ b/hive/lib/src/binary/binary_writer_impl.dart @@ -265,7 +265,12 @@ class BinaryWriterImpl extends BinaryWriter { } /// Not part of public API - int writeFrame(Frame frame, {HiveCipher? cipher, bool verbatim = false}) { + int writeFrame( + Frame frame, { + HiveCipher? cipher, + int? keyCrc, + bool verbatim = false, + }) { final startOffset = _offset; _reserveBytes(4); _offset += 4; // reserve bytes for length @@ -289,7 +294,7 @@ class BinaryWriterImpl extends BinaryWriter { _buffer, offset: startOffset, length: frameLength - 4, - crc: cipher?.calculateKeyCrc() ?? 0, + crc: keyCrc ?? cipher?.calculateKeyCrc() ?? 0, ); writeUint32(crc); diff --git a/hive/lib/src/binary/frame_helper.dart b/hive/lib/src/binary/frame_helper.dart index 35248b57..2af7795c 100644 --- a/hive/lib/src/binary/frame_helper.dart +++ b/hive/lib/src/binary/frame_helper.dart @@ -11,9 +11,9 @@ class FrameHelper { Uint8List bytes, Keystore? keystore, TypeRegistry registry, - HiveCipher? cipher, { + HiveCipher? cipher, + int? keyCrc, { bool verbatim = false, - int? keyCrc, }) { final reader = BinaryReaderImpl(bytes, registry); @@ -22,10 +22,10 @@ class FrameHelper { final frame = reader.readFrame( cipher: cipher, + keyCrc: keyCrc, lazy: false, frameOffset: frameOffset, verbatim: verbatim, - keyCrc: keyCrc, ); if (frame == null) return frameOffset; diff --git a/hive/lib/src/box/box_base_impl.dart b/hive/lib/src/box/box_base_impl.dart index b25c3719..77fb9c60 100644 --- a/hive/lib/src/box/box_base_impl.dart +++ b/hive/lib/src/box/box_base_impl.dart @@ -36,11 +36,6 @@ abstract class BoxBaseImpl implements BoxBase, InspectableBox { /// Whether this box is isolated final bool isolated; - /// They key CRC for the box - /// - /// Used by [IsolatedHive] to support opening encrypted boxes - int? keyCrc; - var _open = true; /// Not part of public API @@ -109,7 +104,6 @@ abstract class BoxBaseImpl implements BoxBase, InspectableBox { keystore, lazy, isolated: isolated, - keyCrc: keyCrc, ); } diff --git a/hive/lib/src/box/lazy_box_impl.dart b/hive/lib/src/box/lazy_box_impl.dart index 60a9a2d9..0a3f13b8 100644 --- a/hive/lib/src/box/lazy_box_impl.dart +++ b/hive/lib/src/box/lazy_box_impl.dart @@ -25,8 +25,7 @@ class LazyBoxImpl extends BoxBaseImpl implements LazyBox { final frame = keystore.get(key); if (frame != null) { - final value = - await backend.readValue(frame, verbatim: isolated, keyCrc: keyCrc); + final value = await backend.readValue(frame, verbatim: isolated); if (value is HiveObjectMixin) { value.init(key, this); } diff --git a/hive/lib/src/hive_impl.dart b/hive/lib/src/hive_impl.dart index 3aa00449..ce1bf262 100644 --- a/hive/lib/src/hive_impl.dart +++ b/hive/lib/src/hive_impl.dart @@ -79,6 +79,7 @@ RECOMMENDED ACTIONS: String name, bool lazy, HiveCipher? cipher, + int? keyCrc, KeyComparator comparator, CompactionStrategy compaction, bool recovery, @@ -117,13 +118,14 @@ RECOMMENDED ACTIONS: try { StorageBackend backend; if (bytes != null) { - backend = StorageBackendMemory(bytes, cipher); + backend = StorageBackendMemory(bytes, cipher, keyCrc); } else { backend = await _manager.open( name, path ?? homePath, recovery, cipher, + keyCrc, collection, ); } @@ -170,6 +172,7 @@ RECOMMENDED ACTIONS: Future> openBox( String name, { HiveCipher? encryptionCipher, + int? keyCrc, KeyComparator keyComparator = defaultKeyComparator, CompactionStrategy compactionStrategy = defaultCompactionStrategy, bool crashRecovery = true, @@ -185,6 +188,7 @@ RECOMMENDED ACTIONS: name, false, encryptionCipher, + keyCrc, keyComparator, compactionStrategy, crashRecovery, @@ -198,6 +202,7 @@ RECOMMENDED ACTIONS: Future> openLazyBox( String name, { HiveCipher? encryptionCipher, + int? keyCrc, KeyComparator keyComparator = defaultKeyComparator, CompactionStrategy compactionStrategy = defaultCompactionStrategy, bool crashRecovery = true, @@ -212,6 +217,7 @@ RECOMMENDED ACTIONS: name, true, encryptionCipher, + keyCrc, keyComparator, compactionStrategy, crashRecovery, diff --git a/hive/lib/src/io/frame_io_helper.dart b/hive/lib/src/io/frame_io_helper.dart index c3817853..dd60d3cb 100644 --- a/hive/lib/src/io/frame_io_helper.dart +++ b/hive/lib/src/io/frame_io_helper.dart @@ -44,9 +44,9 @@ class FrameIoHelper extends FrameHelper { String path, Keystore keystore, TypeRegistry registry, - HiveCipher? cipher, { + HiveCipher? cipher, + int? keyCrc, { bool verbatim = false, - int? keyCrc, }) async { final bytes = await readFile(path); return framesFromBytes( @@ -54,8 +54,8 @@ class FrameIoHelper extends FrameHelper { keystore, registry, cipher, + keyCrc, verbatim: verbatim, - keyCrc: keyCrc, ); } } diff --git a/hive/lib/src/isolate/handler/isolated_hive_handler.dart b/hive/lib/src/isolate/handler/isolated_hive_handler.dart index c3ed09bd..fc23d8dc 100644 --- a/hive/lib/src/isolate/handler/isolated_hive_handler.dart +++ b/hive/lib/src/isolate/handler/isolated_hive_handler.dart @@ -1,5 +1,4 @@ import 'package:hive_ce/hive_ce.dart'; -import 'package:hive_ce/src/box/box_base_impl.dart'; import 'package:hive_ce/src/box/default_compaction_strategy.dart'; import 'package:hive_ce/src/box/default_key_comparator.dart'; import 'package:hive_ce/src/hive_impl.dart'; @@ -33,6 +32,7 @@ Future handleHiveMethodCall( return; } + final keyCrc = call.arguments['keyCrc']; final keyComparator = call.arguments['keyComparator'] ?? defaultKeyComparator; final compactionStrategy = @@ -44,8 +44,9 @@ Future handleHiveMethodCall( final BoxBase box; if (lazy) { - box = await Hive.openLazyBox( + box = await (Hive as HiveImpl).openLazyBox( name, + keyCrc: keyCrc, keyComparator: keyComparator, compactionStrategy: compactionStrategy, crashRecovery: crashRecovery, @@ -53,8 +54,9 @@ Future handleHiveMethodCall( collection: collection, ); } else { - box = await Hive.openBox( + box = await (Hive as HiveImpl).openBox( name, + keyCrc: keyCrc, keyComparator: keyComparator, compactionStrategy: compactionStrategy, crashRecovery: crashRecovery, @@ -64,9 +66,6 @@ Future handleHiveMethodCall( ); } - final keyCrc = call.arguments['keyCrc']; - (box as BoxBaseImpl).keyCrc = keyCrc; - boxHandlers[name] = IsolatedBoxHandler(box, connection); case 'deleteBoxFromDisk': await Hive.deleteBoxFromDisk( diff --git a/hive/lib/src/isolate/isolated_hive_impl/impl/isolated_hive_impl_vm.dart b/hive/lib/src/isolate/isolated_hive_impl/impl/isolated_hive_impl_vm.dart index cdde438e..b45c41fc 100644 --- a/hive/lib/src/isolate/isolated_hive_impl/impl/isolated_hive_impl_vm.dart +++ b/hive/lib/src/isolate/isolated_hive_impl/impl/isolated_hive_impl_vm.dart @@ -122,6 +122,7 @@ class IsolatedHiveImpl extends TypeRegistryImpl try { final params = { 'name': name, + 'keyCrc': cipher?.calculateKeyCrc(), 'lazy': lazy, 'keyComparator': comparator, 'compactionStrategy': compaction, @@ -129,7 +130,6 @@ class IsolatedHiveImpl extends TypeRegistryImpl 'path': path, 'bytes': bytes, 'collection': collection, - 'keyCrc': cipher?.calculateKeyCrc(), }; await _hiveChannel.invokeMethod('openBox', params); diff --git a/hive/test/integration/isolate_test.dart b/hive/test/integration/isolate_test.dart index dda13fd1..646e108a 100644 --- a/hive/test/integration/isolate_test.dart +++ b/hive/test/integration/isolate_test.dart @@ -256,6 +256,27 @@ void main() { expect(await box.get('key'), 'value'); }); + test('Encrypted Hive data compatable with IsolatedHive', () async { + final dir = await getTempDir(); + final cipher = HiveAesCipher(Hive.generateSecureKey()); + + final hive = HiveImpl(); + addTearDown(hive.close); + hive.init(dir.path); + + final box = await hive.openBox('test', encryptionCipher: cipher); + await box.put('key', 'value'); + await box.close(); + + final isolatedHive = IsolatedHiveImpl(); + addTearDown(isolatedHive.close); + await isolatedHive.init(dir.path, isolateNameServer: StubIns()); + + final isolatedBox = + await isolatedHive.openBox('test', encryptionCipher: cipher); + expect(await isolatedBox.get('key'), 'value'); + }); + test( 'IsolatedHive data encrypted with no keyCrc is readable', () async { diff --git a/hive/test/tests/backend/storage_backend_memory_test.dart b/hive/test/tests/backend/storage_backend_memory_test.dart index 57f3943b..83641587 100644 --- a/hive/test/tests/backend/storage_backend_memory_test.dart +++ b/hive/test/tests/backend/storage_backend_memory_test.dart @@ -10,19 +10,19 @@ import '../common.dart'; void main() { group('StorageBackendMemory', () { test('.path is null', () { - final backend = StorageBackendMemory(null, null); + final backend = StorageBackendMemory(null, null, null); expect(backend.path, null); }); test('.supportsCompaction is false', () { - final backend = StorageBackendMemory(null, null); + final backend = StorageBackendMemory(null, null, null); expect(backend.supportsCompaction, false); }); group('.initialize()', () { test('throws if frames cannot be decoded', () { final bytes = Uint8List.fromList([1, 2, 3, 4]); - final backend = StorageBackendMemory(bytes, null); + final backend = StorageBackendMemory(bytes, null, null); expect( () => backend.initialize(TypeRegistryImpl.nullImpl, null, false), throwsHiveError(['Wrong checksum']), @@ -31,7 +31,7 @@ void main() { }); test('.readValue() throws UnsupportedError', () { - final backend = StorageBackendMemory(null, null); + final backend = StorageBackendMemory(null, null, null); expect( () => backend.readValue(Frame('key', 'val')), throwsUnsupportedError, @@ -39,27 +39,27 @@ void main() { }); test('.writeFrames() does nothing', () async { - final backend = StorageBackendMemory(null, null); + final backend = StorageBackendMemory(null, null, null); await backend.writeFrames([Frame('key', 'val')]); }); test('.compact() throws UnsupportedError', () { - final backend = StorageBackendMemory(null, null); + final backend = StorageBackendMemory(null, null, null); expect(() => backend.compact([]), throwsUnsupportedError); }); test('.clear() does nothing', () async { - final backend = StorageBackendMemory(null, null); + final backend = StorageBackendMemory(null, null, null); await backend.clear(); }); test('.close() does nothing', () async { - final backend = StorageBackendMemory(null, null); + final backend = StorageBackendMemory(null, null, null); await backend.close(); }); test('.deleteFromDisk() throws UnsupportedError', () { - final backend = StorageBackendMemory(null, null); + final backend = StorageBackendMemory(null, null, null); expect(backend.deleteFromDisk, throwsUnsupportedError); }); }); diff --git a/hive/test/tests/backend/vm/storage_backend_vm_test.dart b/hive/test/tests/backend/vm/storage_backend_vm_test.dart index 7072f92d..66cf7b1b 100644 --- a/hive/test/tests/backend/vm/storage_backend_vm_test.dart +++ b/hive/test/tests/backend/vm/storage_backend_vm_test.dart @@ -40,6 +40,7 @@ StorageBackendVm _getBackend({ File? lockFile, bool crashRecovery = false, HiveCipher? cipher, + int? keyCrc, FrameIoHelper? ioHelper, ReadWriteSync? sync, RandomAccessFile? readRaf, @@ -50,6 +51,7 @@ StorageBackendVm _getBackend({ lockFile ?? MockFile(), crashRecovery, cipher, + keyCrc, ioHelper ?? MockFrameIoHelper(), sync ?? ReadWriteSync(), ); @@ -129,6 +131,7 @@ void main() { any(), any(), any(), + any(), ), ).thenAnswer((i) => Future.value(recoveryOffset)); when( diff --git a/hive/test/tests/io/frame_io_helper_test.dart b/hive/test/tests/io/frame_io_helper_test.dart index 7af0c946..16e74f4a 100644 --- a/hive/test/tests/io/frame_io_helper_test.dart +++ b/hive/test/tests/io/frame_io_helper_test.dart @@ -77,8 +77,13 @@ void main() { test('frame', () async { final keystore = Keystore.debug(); final ioHelper = _FrameIoHelperTest(_getBytes(frameBytes)); - final recoveryOffset = - await ioHelper.framesFromFile('null', keystore, testRegistry, null); + final recoveryOffset = await ioHelper.framesFromFile( + 'null', + keystore, + testRegistry, + null, + null, + ); expect(recoveryOffset, -1); final testKeystore = Keystore.debug( @@ -96,6 +101,7 @@ void main() { keystore, testRegistry, testCipher, + null, ); expect(recoveryOffset, -1); From 8faaa1362e68bf94d6e39034ad782b100790c544 Mon Sep 17 00:00:00 2001 From: Rexios Date: Thu, 15 Jan 2026 14:09:18 -0500 Subject: [PATCH 05/15] Consolidate warnings --- .../src/backend/vm/storage_backend_vm.dart | 17 +---- hive/lib/src/binary/binary_reader_impl.dart | 8 +++ hive/lib/src/binary/binary_writer_impl.dart | 8 +-- hive/lib/src/hive_impl.dart | 16 +---- .../isolated_hive_impl/hive_isolate.dart | 15 ---- .../impl/isolated_hive_impl_vm.dart | 2 +- hive/lib/src/util/logger.dart | 71 +++++++++++++++++++ hive/test/integration/isolate_test.dart | 10 +-- .../test/tests/binary/binary_writer_test.dart | 3 +- 9 files changed, 90 insertions(+), 60 deletions(-) diff --git a/hive/lib/src/backend/vm/storage_backend_vm.dart b/hive/lib/src/backend/vm/storage_backend_vm.dart index 25daba91..21fde688 100644 --- a/hive/lib/src/backend/vm/storage_backend_vm.dart +++ b/hive/lib/src/backend/vm/storage_backend_vm.dart @@ -18,21 +18,6 @@ import 'package:meta/meta.dart'; /// Storage backend for the Dart VM class StorageBackendVm extends StorageBackend { - /// Warning for existing lock of unmatched isolation - @visibleForTesting - static const unmatchedIsolationWarning = ''' -⚠️ WARNING: HIVE MULTI-ISOLATE RISK DETECTED ⚠️ - -You are opening this box with Hive, but this box was previously opened with -IsolatedHive. This can lead to DATA CORRUPTION as Hive boxes are not designed -for concurrent access across isolates. Each isolate would maintain its own box -cache, potentially causing data inconsistency and corruption. - -RECOMMENDED ACTIONS: -- ALWAYS use IsolatedHive to perform box operations when working with multiple - isolates -'''; - final File _file; final File _lockFile; final bool _crashRecovery; @@ -119,7 +104,7 @@ RECOMMENDED ACTIONS: props = LockProps(); } if (Logger.unmatchedIsolationWarning && props.isolated && !isolated) { - Logger.w(unmatchedIsolationWarning); + Logger.w(HiveWarning.unmatchedIsolation); } } diff --git a/hive/lib/src/binary/binary_reader_impl.dart b/hive/lib/src/binary/binary_reader_impl.dart index e32d5eb8..976c83a5 100644 --- a/hive/lib/src/binary/binary_reader_impl.dart +++ b/hive/lib/src/binary/binary_reader_impl.dart @@ -7,6 +7,7 @@ import 'package:hive_ce/src/crypto/crc32.dart'; import 'package:hive_ce/src/object/hive_list_impl.dart'; import 'package:hive_ce/src/registry/type_registry_impl.dart'; import 'package:hive_ce/src/util/extensions.dart'; +import 'package:hive_ce/src/util/logger.dart'; import 'package:meta/meta.dart'; /// Not part of public API @@ -19,6 +20,8 @@ class BinaryReaderImpl extends BinaryReader { int _bufferLimit; var _offset = 0; + var _crcRecalculationWarningPrinted = false; + /// Not part of public API BinaryReaderImpl(this._buffer, TypeRegistry typeRegistry, [int? bufferLength]) : _byteData = ByteData.view(_buffer.buffer, _buffer.offsetInBytes), @@ -295,6 +298,11 @@ class BinaryReaderImpl extends BinaryReader { length: crcLength, ); if (computedCrc2 != crc) return null; + + if (!_crcRecalculationWarningPrinted) { + Logger.w(HiveWarning.crcRecalculationNeeded); + _crcRecalculationWarningPrinted = true; + } } else { return null; } diff --git a/hive/lib/src/binary/binary_writer_impl.dart b/hive/lib/src/binary/binary_writer_impl.dart index 47a01d1b..0db1bf11 100644 --- a/hive/lib/src/binary/binary_writer_impl.dart +++ b/hive/lib/src/binary/binary_writer_impl.dart @@ -18,12 +18,6 @@ class BinaryWriterImpl extends BinaryWriter { /// The maximum integer that can be stored in a 64 bit float (2^53) static const maxInt = 9007199254740992; - /// Warning message printed when attempting to store an integer that is too large - static const intWarning = - 'WARNING: Writing integer values greater than 2^53 will result in precision loss. ' - 'This is due to Hive storing all numbers as 64 bit floats. ' - 'Consider using a BigInt.'; - /// The type registry to use for writing values final TypeRegistryImpl typeRegistry; var _buffer = Uint8List(_initBufferSize); @@ -107,7 +101,7 @@ class BinaryWriterImpl extends BinaryWriter { @override void writeInt(int value) { // Web truncates values greater than 2^53 to 2^53 - if (kDebugMode && value >= maxInt) Logger.w(intWarning); + if (kDebugMode && value >= maxInt) Logger.w(HiveWarning.bigInt); writeDouble(value.toDouble()); } diff --git a/hive/lib/src/hive_impl.dart b/hive/lib/src/hive_impl.dart index ce1bf262..2df0d258 100644 --- a/hive/lib/src/hive_impl.dart +++ b/hive/lib/src/hive_impl.dart @@ -23,20 +23,6 @@ import 'package:hive_ce/src/backend/storage_backend.dart'; /// Not part of public API class HiveImpl extends TypeRegistryImpl implements HiveInterface { - /// Warning message printed when accessing Hive from an unsafe isolate - @visibleForTesting - static final unsafeIsolateWarning = ''' -⚠️ WARNING: HIVE MULTI-ISOLATE RISK DETECTED ⚠️ - -Accessing Hive from an unsafe isolate (current isolate: "$isolateDebugName") -This can lead to DATA CORRUPTION as Hive boxes are not designed for concurrent -access across isolates. Each isolate would maintain its own box cache, -potentially causing data inconsistency and corruption. - -RECOMMENDED ACTIONS: -- Use IsolatedHive instead - -'''; static final BackendManagerInterface _defaultBackendManager = BackendManager.select(); @@ -69,7 +55,7 @@ RECOMMENDED ACTIONS: }) { if (Logger.unsafeIsolateWarning && !{'main', hiveIsolateName}.contains(isolateDebugName)) { - Logger.w(unsafeIsolateWarning); + Logger.w(HiveWarning.unsafeIsolate); } homePath = path; _managerOverride = BackendManager.select(backendPreference); diff --git a/hive/lib/src/isolate/isolated_hive_impl/hive_isolate.dart b/hive/lib/src/isolate/isolated_hive_impl/hive_isolate.dart index 21f70821..1e62b49c 100644 --- a/hive/lib/src/isolate/isolated_hive_impl/hive_isolate.dart +++ b/hive/lib/src/isolate/isolated_hive_impl/hive_isolate.dart @@ -7,21 +7,6 @@ import 'package:meta/meta.dart'; /// /// Used for testing abstract class HiveIsolate { - /// Warning message printed when using [IsolatedHive] without an [IsolateNameServer] - static final noIsolateNameServerWarning = ''' -⚠️ WARNING: HIVE MULTI-ISOLATE RISK DETECTED ⚠️ - -Using IsolatedHive without an IsolateNameServer is unsafe. This can lead to -DATA CORRUPTION as Hive boxes are not designed for concurrent access across -isolates. Using an IsolateNameServer allows IsolatedHive to maintain a single -isolate for all Hive operations. - -RECOMMENDED ACTIONS: -- Initialize IsolatedHive with IsolatedHive.initFlutter from hive_ce_flutter -- Provide your own IsolateNameServer - -'''; - /// Access to the isolate connection for testing @visibleForTesting IsolateConnection get connection; diff --git a/hive/lib/src/isolate/isolated_hive_impl/impl/isolated_hive_impl_vm.dart b/hive/lib/src/isolate/isolated_hive_impl/impl/isolated_hive_impl_vm.dart index b45c41fc..f18085ba 100644 --- a/hive/lib/src/isolate/isolated_hive_impl/impl/isolated_hive_impl_vm.dart +++ b/hive/lib/src/isolate/isolated_hive_impl/impl/isolated_hive_impl_vm.dart @@ -57,7 +57,7 @@ class IsolatedHiveImpl extends TypeRegistryImpl _isolateNameServer = isolateNameServer; if (Logger.noIsolateNameServerWarning && _isolateNameServer == null) { - Logger.w(HiveIsolate.noIsolateNameServerWarning); + Logger.w(HiveWarning.noIsolateNameServer); } final send = diff --git a/hive/lib/src/util/logger.dart b/hive/lib/src/util/logger.dart index fbe7bb98..f64a002e 100644 --- a/hive/lib/src/util/logger.dart +++ b/hive/lib/src/util/logger.dart @@ -1,3 +1,4 @@ +import 'package:hive_ce/src/isolate/isolate_debug_name/isolate_debug_name.dart'; import 'package:hive_ce/src/util/debug_utils.dart'; /// Configures the logging behavior of Hive @@ -14,6 +15,9 @@ abstract class Logger { /// If the no isolate name server warning is enabled static var noIsolateNameServerWarning = true; + /// If the crc recalculation warning is enabled + static var crcRecalculationWarning = true; + /// Log a verbose message static void v(Object? message) { if (level.index > LoggerLevel.verbose.index) return; @@ -65,3 +69,70 @@ enum LoggerLevel { /// Errors error; } + +/// Warning messages from Hive +abstract class HiveWarning { + const HiveWarning._(); + + /// Warning message printed when attempting to store an integer that is too large + static const bigInt = + 'WARNING: Writing integer values greater than 2^53 will result in precision loss.' + ' This is due to Hive storing all numbers as 64 bit floats.' + ' Consider using a BigInt.'; + + /// Warning message printed when accessing Hive from an unsafe isolate + static final unsafeIsolate = ''' +⚠️ WARNING: HIVE MULTI-ISOLATE RISK DETECTED ⚠️ + +Accessing Hive from an unsafe isolate (current isolate: "$isolateDebugName") +This can lead to DATA CORRUPTION as Hive boxes are not designed for concurrent +access across isolates. Each isolate would maintain its own box cache, +potentially causing data inconsistency and corruption. + +RECOMMENDED ACTIONS: +- Use IsolatedHive instead + +'''; + + /// Warning for existing lock of unmatched isolation + static const unmatchedIsolation = ''' +⚠️ WARNING: HIVE MULTI-ISOLATE RISK DETECTED ⚠️ + +You are opening this box with Hive, but this box was previously opened with +IsolatedHive. This can lead to DATA CORRUPTION as Hive boxes are not designed +for concurrent access across isolates. Each isolate would maintain its own box +cache, potentially causing data inconsistency and corruption. + +RECOMMENDED ACTIONS: +- ALWAYS use IsolatedHive to perform box operations when working with multiple + isolates +'''; + + /// Warning message printed when using [IsolatedHive] without an [IsolateNameServer] + static final noIsolateNameServer = ''' +⚠️ WARNING: HIVE MULTI-ISOLATE RISK DETECTED ⚠️ + +Using IsolatedHive without an IsolateNameServer is unsafe. This can lead to +DATA CORRUPTION as Hive boxes are not designed for concurrent access across +isolates. Using an IsolateNameServer allows IsolatedHive to maintain a single +isolate for all Hive operations. + +RECOMMENDED ACTIONS: +- Initialize IsolatedHive with IsolatedHive.initFlutter from hive_ce_flutter +- Provide your own IsolateNameServer + +'''; + + /// Warning message printed when CRC recalculation is needed + static const crcRecalculationNeeded = + 'WARNING: CRC recalculation needed for frame.' + ' This happens when IsolatedHive was used with encryption before it was properly handled.' + ' IsolatedHive will continue to work, but read performance may be degraded for old entries.' + ' To restore performance, rewrite all box entries.' + ' This only needs to be done once.' + '\n\nEXAMPLE\n\n' + ''' +for (final key in await box.keys) { + await box.put(key, await box.get(key)); +}'''; +} diff --git a/hive/test/integration/isolate_test.dart b/hive/test/integration/isolate_test.dart index 646e108a..d5d1b594 100644 --- a/hive/test/integration/isolate_test.dart +++ b/hive/test/integration/isolate_test.dart @@ -5,11 +5,11 @@ import 'dart:async'; import 'dart:isolate'; import 'package:hive_ce/hive_ce.dart'; -import 'package:hive_ce/src/backend/vm/storage_backend_vm.dart'; import 'package:hive_ce/src/hive_impl.dart'; import 'package:hive_ce/src/isolate/handler/isolate_entry_point.dart'; import 'package:hive_ce/src/isolate/isolated_hive_impl/hive_isolate.dart'; import 'package:hive_ce/src/isolate/isolated_hive_impl/isolated_hive_impl.dart'; +import 'package:hive_ce/src/util/logger.dart'; import 'package:isolate_channel/isolate_channel.dart'; import 'package:test/test.dart'; @@ -122,7 +122,7 @@ void main() { group('warnings', () { test('unsafe isolate', () async { final patchedWarning = - HiveImpl.unsafeIsolateWarning.replaceFirst(isolateNameRegex, ''); + HiveWarning.unsafeIsolate.replaceFirst(isolateNameRegex, ''); final safeOutput = await Isolate.run( debugName: 'main', @@ -172,7 +172,7 @@ void main() { await captureOutput(() => IsolatedHiveImpl().init(null)).toList(); expect( unsafeOutput, - contains(HiveIsolate.noIsolateNameServerWarning), + contains(HiveWarning.noIsolateNameServer), ); final safeOutput = await captureOutput( @@ -200,7 +200,7 @@ void main() { expect( output, - contains(StorageBackendVm.unmatchedIsolationWarning), + contains(HiveWarning.unmatchedIsolation), ); await IsolatedHive.openBox('box2'); @@ -211,7 +211,7 @@ void main() { expect( ignoredOutput, - isNot(contains(StorageBackendVm.unmatchedIsolationWarning)), + isNot(contains(HiveWarning.unmatchedIsolation)), ); }); }); diff --git a/hive/test/tests/binary/binary_writer_test.dart b/hive/test/tests/binary/binary_writer_test.dart index 33ed6ade..25cdacb8 100644 --- a/hive/test/tests/binary/binary_writer_test.dart +++ b/hive/test/tests/binary/binary_writer_test.dart @@ -6,6 +6,7 @@ import 'package:hive_ce/src/binary/binary_writer_impl.dart'; import 'package:hive_ce/src/binary/frame.dart'; import 'package:hive_ce/src/object/hive_object.dart'; import 'package:hive_ce/src/registry/type_registry_impl.dart'; +import 'package:hive_ce/src/util/logger.dart'; import 'package:mocktail/mocktail.dart'; import 'package:test/test.dart'; @@ -160,7 +161,7 @@ void main() { final output2 = await captureOutput(() => bw.writeInt(BinaryWriterImpl.maxInt)) .toList(); - expect(output2, contains(BinaryWriterImpl.intWarning)); + expect(output2, contains(HiveWarning.bigInt)); bw = getWriter(); bw.writeInt(BinaryWriterImpl.maxInt + 1); From 64b61e29dff6cf15d5c2877e7300e4924d113aa0 Mon Sep 17 00:00:00 2001 From: Rexios Date: Thu, 15 Jan 2026 14:09:40 -0500 Subject: [PATCH 06/15] Cleanup --- hive/lib/src/binary/binary_reader_impl.dart | 3 ++- hive/lib/src/binary/binary_writer_impl.dart | 5 +++-- hive/lib/src/util/logger.dart | 3 +++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/hive/lib/src/binary/binary_reader_impl.dart b/hive/lib/src/binary/binary_reader_impl.dart index 976c83a5..9b39239b 100644 --- a/hive/lib/src/binary/binary_reader_impl.dart +++ b/hive/lib/src/binary/binary_reader_impl.dart @@ -299,7 +299,8 @@ class BinaryReaderImpl extends BinaryReader { ); if (computedCrc2 != crc) return null; - if (!_crcRecalculationWarningPrinted) { + if (Logger.crcRecalculationWarning && + !_crcRecalculationWarningPrinted) { Logger.w(HiveWarning.crcRecalculationNeeded); _crcRecalculationWarningPrinted = true; } diff --git a/hive/lib/src/binary/binary_writer_impl.dart b/hive/lib/src/binary/binary_writer_impl.dart index 0db1bf11..70b8aec5 100644 --- a/hive/lib/src/binary/binary_writer_impl.dart +++ b/hive/lib/src/binary/binary_writer_impl.dart @@ -6,7 +6,6 @@ import 'package:hive_ce/src/binary/frame.dart'; import 'package:hive_ce/src/crypto/crc32.dart'; import 'package:hive_ce/src/object/hive_list_impl.dart'; import 'package:hive_ce/src/registry/type_registry_impl.dart'; -import 'package:hive_ce/src/util/debug_utils.dart'; import 'package:hive_ce/src/util/extensions.dart'; import 'package:hive_ce/src/util/logger.dart'; import 'package:meta/meta.dart'; @@ -101,7 +100,9 @@ class BinaryWriterImpl extends BinaryWriter { @override void writeInt(int value) { // Web truncates values greater than 2^53 to 2^53 - if (kDebugMode && value >= maxInt) Logger.w(HiveWarning.bigInt); + if (HiveLogger.bigIntWarning && value >= maxInt) { + Logger.w(HiveWarning.bigInt); + } writeDouble(value.toDouble()); } diff --git a/hive/lib/src/util/logger.dart b/hive/lib/src/util/logger.dart index f64a002e..d7fb2175 100644 --- a/hive/lib/src/util/logger.dart +++ b/hive/lib/src/util/logger.dart @@ -6,6 +6,9 @@ abstract class Logger { /// The overall logging level static var level = kDebugMode ? LoggerLevel.debug : LoggerLevel.info; + /// If the big int warning is enabled + static var bigIntWarning = true; + /// If the unsafe isolate warning is enabled static var unsafeIsolateWarning = true; From a4e0a1f633877852e24447b688f76c389277276c Mon Sep 17 00:00:00 2001 From: Rexios Date: Thu, 15 Jan 2026 14:14:43 -0500 Subject: [PATCH 07/15] Testing --- hive/lib/src/util/logger.dart | 2 +- hive/test/integration/isolate_test.dart | 27 ++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/hive/lib/src/util/logger.dart b/hive/lib/src/util/logger.dart index d7fb2175..bfca7566 100644 --- a/hive/lib/src/util/logger.dart +++ b/hive/lib/src/util/logger.dart @@ -133,7 +133,7 @@ RECOMMENDED ACTIONS: ' IsolatedHive will continue to work, but read performance may be degraded for old entries.' ' To restore performance, rewrite all box entries.' ' This only needs to be done once.' - '\n\nEXAMPLE\n\n' + '\n\nEXAMPLE:\n' ''' for (final key in await box.keys) { await box.put(key, await box.get(key)); diff --git a/hive/test/integration/isolate_test.dart b/hive/test/integration/isolate_test.dart index d5d1b594..ce887646 100644 --- a/hive/test/integration/isolate_test.dart +++ b/hive/test/integration/isolate_test.dart @@ -235,6 +235,25 @@ void main() { expect(await box.get('key'), 'value'); }); + test('Hive data compatable with IsolatedHive', () async { + final dir = await getTempDir(); + + final hive = HiveImpl(); + addTearDown(hive.close); + hive.init(dir.path); + + final box = await hive.openBox('test'); + await box.put('key', 'value'); + await box.close(); + + final isolatedHive = IsolatedHiveImpl(); + addTearDown(isolatedHive.close); + await isolatedHive.init(dir.path, isolateNameServer: StubIns()); + + final isolatedBox = await isolatedHive.openBox('test'); + expect(await isolatedBox.get('key'), 'value'); + }); + test('Encrypted IsolatedHive data compatable with Hive', () async { final dir = await getTempDir(); final cipher = HiveAesCipher(Hive.generateSecureKey()); @@ -294,9 +313,11 @@ void main() { await box.put('key', 'value'); await box.close(); - await isolatedHive.openBox( - 'test', - encryptionCipher: HiveAesCipher(key), + captureOutput( + () => isolatedHive.openBox( + 'test', + encryptionCipher: HiveAesCipher(key), + ), ); expect(await box.get('key'), 'value'); }, From 6c89aed1841634f5762cd2bc19a5bfa339b60bae Mon Sep 17 00:00:00 2001 From: Rexios Date: Thu, 15 Jan 2026 14:14:43 -0500 Subject: [PATCH 08/15] Cleanup --- hive/lib/src/util/logger.dart | 2 +- hive/test/integration/isolate_test.dart | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/hive/lib/src/util/logger.dart b/hive/lib/src/util/logger.dart index d7fb2175..bfca7566 100644 --- a/hive/lib/src/util/logger.dart +++ b/hive/lib/src/util/logger.dart @@ -133,7 +133,7 @@ RECOMMENDED ACTIONS: ' IsolatedHive will continue to work, but read performance may be degraded for old entries.' ' To restore performance, rewrite all box entries.' ' This only needs to be done once.' - '\n\nEXAMPLE\n\n' + '\n\nEXAMPLE:\n' ''' for (final key in await box.keys) { await box.put(key, await box.get(key)); diff --git a/hive/test/integration/isolate_test.dart b/hive/test/integration/isolate_test.dart index d5d1b594..9c17586c 100644 --- a/hive/test/integration/isolate_test.dart +++ b/hive/test/integration/isolate_test.dart @@ -235,6 +235,25 @@ void main() { expect(await box.get('key'), 'value'); }); + test('Hive data compatable with IsolatedHive', () async { + final dir = await getTempDir(); + + final hive = HiveImpl(); + addTearDown(hive.close); + hive.init(dir.path); + + final box = await hive.openBox('test'); + await box.put('key', 'value'); + await box.close(); + + final isolatedHive = IsolatedHiveImpl(); + addTearDown(isolatedHive.close); + await isolatedHive.init(dir.path, isolateNameServer: StubIns()); + + final isolatedBox = await isolatedHive.openBox('test'); + expect(await isolatedBox.get('key'), 'value'); + }); + test('Encrypted IsolatedHive data compatable with Hive', () async { final dir = await getTempDir(); final cipher = HiveAesCipher(Hive.generateSecureKey()); From 291fbbd3f71f885c5656da880e1577a589eecd7b Mon Sep 17 00:00:00 2001 From: Rexios Date: Thu, 15 Jan 2026 14:25:34 -0500 Subject: [PATCH 09/15] Fix test --- hive/test/integration/isolate_test.dart | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/hive/test/integration/isolate_test.dart b/hive/test/integration/isolate_test.dart index ce887646..34fc5070 100644 --- a/hive/test/integration/isolate_test.dart +++ b/hive/test/integration/isolate_test.dart @@ -313,13 +313,11 @@ void main() { await box.put('key', 'value'); await box.close(); - captureOutput( - () => isolatedHive.openBox( - 'test', - encryptionCipher: HiveAesCipher(key), - ), + final box2 = await isolatedHive.openBox( + 'test', + encryptionCipher: HiveAesCipher(key), ); - expect(await box.get('key'), 'value'); + expect(await box2.get('key'), 'value'); }, ); }, From b778b471c667765f39869bc41db77586b95d809b Mon Sep 17 00:00:00 2001 From: Rexios Date: Thu, 15 Jan 2026 14:30:58 -0500 Subject: [PATCH 10/15] Add more tests --- hive/test/integration/isolate_test.dart | 46 +++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/hive/test/integration/isolate_test.dart b/hive/test/integration/isolate_test.dart index 34fc5070..37fab500 100644 --- a/hive/test/integration/isolate_test.dart +++ b/hive/test/integration/isolate_test.dart @@ -320,6 +320,52 @@ void main() { expect(await box2.get('key'), 'value'); }, ); + + test('Encrypted IsolatedBox data compatable with IsolatedLazyBox', + () async { + final dir = await getTempDir(); + final key = Hive.generateSecureKey(); + + final isolatedHive = IsolatedHiveImpl(); + addTearDown(isolatedHive.close); + await isolatedHive.init(dir.path, isolateNameServer: StubIns()); + + final box = await isolatedHive.openBox( + 'test', + encryptionCipher: HiveAesCipher(key), + ); + await box.put('key', 'value'); + await box.close(); + + final lazyBox = await isolatedHive.openLazyBox( + 'test', + encryptionCipher: HiveAesCipher(key), + ); + expect(await lazyBox.get('key'), 'value'); + }); + + test('Encrypted IsolatedLazyBox data compatable with IsolatedBox', + () async { + final dir = await getTempDir(); + final key = Hive.generateSecureKey(); + + final isolatedHive = IsolatedHiveImpl(); + addTearDown(isolatedHive.close); + await isolatedHive.init(dir.path, isolateNameServer: StubIns()); + + final lazyBox = await isolatedHive.openLazyBox( + 'test', + encryptionCipher: HiveAesCipher(key), + ); + await lazyBox.put('key', 'value'); + await lazyBox.close(); + + final box = await isolatedHive.openBox( + 'test', + encryptionCipher: HiveAesCipher(key), + ); + expect(await box.get('key'), 'value'); + }); }, onPlatform: { 'chrome': Skip('Isolates are not supported on web'), From 6bb75b82a2393c4a37a008c07f25987d1948266c Mon Sep 17 00:00:00 2001 From: Rexios Date: Thu, 15 Jan 2026 14:53:21 -0500 Subject: [PATCH 11/15] Cleanup --- .../src/backend/js/native/storage_backend_js.dart | 2 -- hive/lib/src/backend/storage_backend_memory.dart | 2 +- hive/lib/src/binary/binary_writer_impl.dart | 2 +- hive/lib/src/hive_impl.dart | 1 - hive/test/integration/isolate_test.dart | 12 ++++++------ 5 files changed, 8 insertions(+), 11 deletions(-) diff --git a/hive/lib/src/backend/js/native/storage_backend_js.dart b/hive/lib/src/backend/js/native/storage_backend_js.dart index af7e5bc6..9c210665 100644 --- a/hive/lib/src/backend/js/native/storage_backend_js.dart +++ b/hive/lib/src/backend/js/native/storage_backend_js.dart @@ -166,7 +166,6 @@ class StorageBackendJs extends StorageBackend { Keystore keystore, bool lazy, { bool isolated = false, - int? keyCrc, }) async { _registry = registry; final keys = await getKeys(); @@ -190,7 +189,6 @@ class StorageBackendJs extends StorageBackend { Future readValue( Frame frame, { bool verbatim = false, - int? keyCrc, }) async { final value = await getStore(false).get(frame.key.jsify()).asFuture(); return decodeValue(value); diff --git a/hive/lib/src/backend/storage_backend_memory.dart b/hive/lib/src/backend/storage_backend_memory.dart index c5c70b7e..81256888 100644 --- a/hive/lib/src/backend/storage_backend_memory.dart +++ b/hive/lib/src/backend/storage_backend_memory.dart @@ -51,7 +51,7 @@ class StorageBackendMemory extends StorageBackend { } @override - Future readValue(Frame frame, {bool verbatim = false, int? keyCrc}) { + Future readValue(Frame frame, {bool verbatim = false}) { throw UnsupportedError('This operation is unsupported for memory boxes.'); } diff --git a/hive/lib/src/binary/binary_writer_impl.dart b/hive/lib/src/binary/binary_writer_impl.dart index 70b8aec5..c151e627 100644 --- a/hive/lib/src/binary/binary_writer_impl.dart +++ b/hive/lib/src/binary/binary_writer_impl.dart @@ -100,7 +100,7 @@ class BinaryWriterImpl extends BinaryWriter { @override void writeInt(int value) { // Web truncates values greater than 2^53 to 2^53 - if (HiveLogger.bigIntWarning && value >= maxInt) { + if (Logger.bigIntWarning && value >= maxInt) { Logger.w(HiveWarning.bigInt); } writeDouble(value.toDouble()); diff --git a/hive/lib/src/hive_impl.dart b/hive/lib/src/hive_impl.dart index 2df0d258..3bc27820 100644 --- a/hive/lib/src/hive_impl.dart +++ b/hive/lib/src/hive_impl.dart @@ -23,7 +23,6 @@ import 'package:hive_ce/src/backend/storage_backend.dart'; /// Not part of public API class HiveImpl extends TypeRegistryImpl implements HiveInterface { - static final BackendManagerInterface _defaultBackendManager = BackendManager.select(); diff --git a/hive/test/integration/isolate_test.dart b/hive/test/integration/isolate_test.dart index 37fab500..2531d81c 100644 --- a/hive/test/integration/isolate_test.dart +++ b/hive/test/integration/isolate_test.dart @@ -216,7 +216,7 @@ void main() { }); }); - test('IsolatedHive data compatable with Hive', () async { + test('IsolatedHive data compatible with Hive', () async { final dir = await getTempDir(); final isolatedHive = IsolatedHiveImpl(); @@ -235,7 +235,7 @@ void main() { expect(await box.get('key'), 'value'); }); - test('Hive data compatable with IsolatedHive', () async { + test('Hive data compatible with IsolatedHive', () async { final dir = await getTempDir(); final hive = HiveImpl(); @@ -254,7 +254,7 @@ void main() { expect(await isolatedBox.get('key'), 'value'); }); - test('Encrypted IsolatedHive data compatable with Hive', () async { + test('Encrypted IsolatedHive data compatible with Hive', () async { final dir = await getTempDir(); final cipher = HiveAesCipher(Hive.generateSecureKey()); @@ -275,7 +275,7 @@ void main() { expect(await box.get('key'), 'value'); }); - test('Encrypted Hive data compatable with IsolatedHive', () async { + test('Encrypted Hive data compatible with IsolatedHive', () async { final dir = await getTempDir(); final cipher = HiveAesCipher(Hive.generateSecureKey()); @@ -321,7 +321,7 @@ void main() { }, ); - test('Encrypted IsolatedBox data compatable with IsolatedLazyBox', + test('Encrypted IsolatedBox data compatible with IsolatedLazyBox', () async { final dir = await getTempDir(); final key = Hive.generateSecureKey(); @@ -344,7 +344,7 @@ void main() { expect(await lazyBox.get('key'), 'value'); }); - test('Encrypted IsolatedLazyBox data compatable with IsolatedBox', + test('Encrypted IsolatedLazyBox data compatible with IsolatedBox', () async { final dir = await getTempDir(); final key = Hive.generateSecureKey(); From f6df712e0be358cf114c7c0d8fe801338237cf44 Mon Sep 17 00:00:00 2001 From: Rexios Date: Thu, 15 Jan 2026 14:54:10 -0500 Subject: [PATCH 12/15] Cleanup --- hive/lib/src/backend/vm/storage_backend_vm.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hive/lib/src/backend/vm/storage_backend_vm.dart b/hive/lib/src/backend/vm/storage_backend_vm.dart index 21fde688..146376ca 100644 --- a/hive/lib/src/backend/vm/storage_backend_vm.dart +++ b/hive/lib/src/backend/vm/storage_backend_vm.dart @@ -153,9 +153,9 @@ class StorageBackendVm extends StorageBackend { final reader = BinaryReaderImpl(bytes, registry); final readFrame = reader.readFrame( cipher: _cipher, + keyCrc: _keyCrc, lazy: false, verbatim: verbatim, - keyCrc: _keyCrc, ); if (readFrame == null) { From aab2625c8fe3ddd909906a9ff1e8f4da819cfa58 Mon Sep 17 00:00:00 2001 From: Rexios Date: Thu, 15 Jan 2026 14:59:37 -0500 Subject: [PATCH 13/15] Cleanup --- hive/lib/src/backend/js/native/storage_backend_js.dart | 5 +---- hive/lib/src/backend/vm/storage_backend_vm.dart | 5 +---- hive/lib/src/binary/binary_reader_impl.dart | 9 ++++----- hive/lib/src/io/frame_io_helper.dart | 2 +- .../isolated_hive_impl/impl/isolated_hive_impl_vm.dart | 2 +- hive/lib/src/util/logger.dart | 9 ++++----- 6 files changed, 12 insertions(+), 20 deletions(-) diff --git a/hive/lib/src/backend/js/native/storage_backend_js.dart b/hive/lib/src/backend/js/native/storage_backend_js.dart index 9c210665..638ccbda 100644 --- a/hive/lib/src/backend/js/native/storage_backend_js.dart +++ b/hive/lib/src/backend/js/native/storage_backend_js.dart @@ -186,10 +186,7 @@ class StorageBackendJs extends StorageBackend { } @override - Future readValue( - Frame frame, { - bool verbatim = false, - }) async { + Future readValue(Frame frame, {bool verbatim = false}) async { final value = await getStore(false).get(frame.key.jsify()).asFuture(); return decodeValue(value); } diff --git a/hive/lib/src/backend/vm/storage_backend_vm.dart b/hive/lib/src/backend/vm/storage_backend_vm.dart index 146376ca..583bf150 100644 --- a/hive/lib/src/backend/vm/storage_backend_vm.dart +++ b/hive/lib/src/backend/vm/storage_backend_vm.dart @@ -141,10 +141,7 @@ class StorageBackendVm extends StorageBackend { } @override - Future readValue( - Frame frame, { - bool verbatim = false, - }) { + Future readValue(Frame frame, {bool verbatim = false}) { return _sync.syncRead(() async { await readRaf.setPosition(frame.offset); diff --git a/hive/lib/src/binary/binary_reader_impl.dart b/hive/lib/src/binary/binary_reader_impl.dart index 9b39239b..9bb60bc3 100644 --- a/hive/lib/src/binary/binary_reader_impl.dart +++ b/hive/lib/src/binary/binary_reader_impl.dart @@ -20,7 +20,7 @@ class BinaryReaderImpl extends BinaryReader { int _bufferLimit; var _offset = 0; - var _crcRecalculationWarningPrinted = false; + var _crcRecomputeWarningPrinted = false; /// Not part of public API BinaryReaderImpl(this._buffer, TypeRegistry typeRegistry, [int? bufferLength]) @@ -299,10 +299,9 @@ class BinaryReaderImpl extends BinaryReader { ); if (computedCrc2 != crc) return null; - if (Logger.crcRecalculationWarning && - !_crcRecalculationWarningPrinted) { - Logger.w(HiveWarning.crcRecalculationNeeded); - _crcRecalculationWarningPrinted = true; + if (Logger.crcRecomputeWarning && !_crcRecomputeWarningPrinted) { + Logger.w(HiveWarning.crcRecomputeNeeded); + _crcRecomputeWarningPrinted = true; } } else { return null; diff --git a/hive/lib/src/io/frame_io_helper.dart b/hive/lib/src/io/frame_io_helper.dart index dd60d3cb..7bda68af 100644 --- a/hive/lib/src/io/frame_io_helper.dart +++ b/hive/lib/src/io/frame_io_helper.dart @@ -93,9 +93,9 @@ class _KeyReader { final frame = _reader.readFrame( cipher: cipher, + keyCrc: keyCrc, lazy: true, frameOffset: frameOffset, - keyCrc: keyCrc, ); if (frame == null) return frameOffset; diff --git a/hive/lib/src/isolate/isolated_hive_impl/impl/isolated_hive_impl_vm.dart b/hive/lib/src/isolate/isolated_hive_impl/impl/isolated_hive_impl_vm.dart index f18085ba..acb72c78 100644 --- a/hive/lib/src/isolate/isolated_hive_impl/impl/isolated_hive_impl_vm.dart +++ b/hive/lib/src/isolate/isolated_hive_impl/impl/isolated_hive_impl_vm.dart @@ -122,8 +122,8 @@ class IsolatedHiveImpl extends TypeRegistryImpl try { final params = { 'name': name, - 'keyCrc': cipher?.calculateKeyCrc(), 'lazy': lazy, + 'keyCrc': cipher?.calculateKeyCrc(), 'keyComparator': comparator, 'compactionStrategy': compaction, 'crashRecovery': recovery, diff --git a/hive/lib/src/util/logger.dart b/hive/lib/src/util/logger.dart index bfca7566..c18419d1 100644 --- a/hive/lib/src/util/logger.dart +++ b/hive/lib/src/util/logger.dart @@ -18,8 +18,8 @@ abstract class Logger { /// If the no isolate name server warning is enabled static var noIsolateNameServerWarning = true; - /// If the crc recalculation warning is enabled - static var crcRecalculationWarning = true; + /// If the crc recompute warning is enabled + static var crcRecomputeWarning = true; /// Log a verbose message static void v(Object? message) { @@ -126,9 +126,8 @@ RECOMMENDED ACTIONS: '''; - /// Warning message printed when CRC recalculation is needed - static const crcRecalculationNeeded = - 'WARNING: CRC recalculation needed for frame.' + /// Warning message printed when CRC recompute is needed + static const crcRecomputeNeeded = 'WARNING: CRC recompute needed for frame.' ' This happens when IsolatedHive was used with encryption before it was properly handled.' ' IsolatedHive will continue to work, but read performance may be degraded for old entries.' ' To restore performance, rewrite all box entries.' From ec2521796a8344e47c717fa1ab38db54535cf4bb Mon Sep 17 00:00:00 2001 From: Rexios Date: Thu, 15 Jan 2026 15:01:05 -0500 Subject: [PATCH 14/15] Cleanup --- hive/lib/src/binary/binary_reader_impl.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hive/lib/src/binary/binary_reader_impl.dart b/hive/lib/src/binary/binary_reader_impl.dart index 9bb60bc3..19140a69 100644 --- a/hive/lib/src/binary/binary_reader_impl.dart +++ b/hive/lib/src/binary/binary_reader_impl.dart @@ -290,7 +290,7 @@ class BinaryReaderImpl extends BinaryReader { // frame is corrupted or provided chiper is different if (computedCrc != crc) { if (keyCrc != null) { - // Attempt to calculate the crc without the key crc + // Attempt to compute the crc without the key crc // This maintains compatibility with data written by IsolatedHive before keyCrc was introduced final computedCrc2 = Crc32.compute( _buffer, From 082aa107e84256244509afd53a39607a3c118c08 Mon Sep 17 00:00:00 2001 From: Rexios Date: Thu, 15 Jan 2026 15:14:22 -0500 Subject: [PATCH 15/15] Changelog --- hive/CHANGELOG.md | 3 ++- hive/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/hive/CHANGELOG.md b/hive/CHANGELOG.md index eedc3a4d..43c975b4 100644 --- a/hive/CHANGELOG.md +++ b/hive/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.18.0 +- Fixes an issue with IsolatedHive encryption - Automatically disables the unsafe isolate warning when running in a test ## 2.17.0 diff --git a/hive/pubspec.yaml b/hive/pubspec.yaml index 08d4c5b4..c50e6fd5 100644 --- a/hive/pubspec.yaml +++ b/hive/pubspec.yaml @@ -1,6 +1,6 @@ name: hive_ce description: Hive Community Edition - A spiritual continuation of Hive v2 -version: 2.17.0 +version: 2.18.0 homepage: https://github.com/IO-Design-Team/hive_ce/tree/main/hive topics: