Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion hive/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
1 change: 1 addition & 0 deletions hive/lib/src/backend/js/native/backend_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class BackendManager implements BackendManagerInterface {
String? path,
bool crashRecovery,
HiveCipher? cipher,
int? keyCrc,
String? collection,
) async {
// compatibility for old store format
Expand Down
1 change: 1 addition & 0 deletions hive/lib/src/backend/storage_backend.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ abstract class BackendManagerInterface {
String? path,
bool crashRecovery,
HiveCipher? cipher,
int? keyCrc,
String? collection,
);

Expand Down
4 changes: 3 additions & 1 deletion hive/lib/src/backend/storage_backend_memory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -37,6 +38,7 @@ class StorageBackendMemory extends StorageBackend {
keystore,
registry,
_cipher,
_keyCrc,
);

if (recoveryOffset != -1) {
Expand Down
1 change: 1 addition & 0 deletions hive/lib/src/backend/stub/backend_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class BackendManager implements BackendManagerInterface {
String? path,
bool crashRecovery,
HiveCipher? cipher,
int? keyCrc,
String? collection,
) {
throw UnimplementedError();
Expand Down
4 changes: 3 additions & 1 deletion hive/lib/src/backend/vm/backend_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class BackendManager implements BackendManagerInterface {
String? path,
bool crashRecovery,
HiveCipher? cipher,
int? keyCrc,
String? collection,
) async {
if (path == null) {
Expand All @@ -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;
}
Expand Down
40 changes: 19 additions & 21 deletions hive/lib/src/backend/vm/storage_backend_vm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,11 @@ 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;
final HiveCipher? _cipher;
final int? _keyCrc;
final FrameIoHelper _frameHelper;

final ReadWriteSync _sync;
Expand Down Expand Up @@ -73,6 +59,7 @@ RECOMMENDED ACTIONS:
this._lockFile,
this._crashRecovery,
this._cipher,
this._keyCrc,
) : _frameHelper = FrameIoHelper(),
_sync = ReadWriteSync();

Expand All @@ -82,6 +69,7 @@ RECOMMENDED ACTIONS:
this._lockFile,
this._crashRecovery,
this._cipher,
this._keyCrc,
this._frameHelper,
this._sync,
);
Expand Down Expand Up @@ -116,7 +104,7 @@ RECOMMENDED ACTIONS:
props = LockProps();
}
if (Logger.unmatchedIsolationWarning && props.isolated && !isolated) {
Logger.w(unmatchedIsolationWarning);
Logger.w(HiveWarning.unmatchedIsolation);
}
}

Expand All @@ -132,10 +120,12 @@ RECOMMENDED ACTIONS:
keystore,
registry,
_cipher,
_keyCrc,
verbatim: isolated,
);
} else {
recoveryOffset = await _frameHelper.keysFromFile(path, keystore, _cipher);
recoveryOffset =
await _frameHelper.keysFromFile(path, keystore, _cipher, _keyCrc);
}

if (recoveryOffset != -1) {
Expand All @@ -158,8 +148,12 @@ RECOMMENDED ACTIONS:
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,
keyCrc: _keyCrc,
lazy: false,
verbatim: verbatim,
);

if (readFrame == null) {
throw HiveError(
Expand All @@ -177,8 +171,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 {
Expand Down
32 changes: 28 additions & 4 deletions hive/lib/src/binary/binary_reader_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -19,6 +20,8 @@ class BinaryReaderImpl extends BinaryReader {
int _bufferLimit;
var _offset = 0;

var _crcRecomputeWarningPrinted = false;

/// Not part of public API
BinaryReaderImpl(this._buffer, TypeRegistry typeRegistry, [int? bufferLength])
: _byteData = ByteData.view(_buffer.buffer, _buffer.offsetInBytes),
Expand Down Expand Up @@ -259,6 +262,7 @@ class BinaryReaderImpl extends BinaryReader {
/// Not part of public API
Frame? readFrame({
HiveCipher? cipher,
int? keyCrc,
bool lazy = false,
int frameOffset = 0,
bool verbatim = false,
Expand All @@ -274,15 +278,35 @@ 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 compute 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;

if (Logger.crcRecomputeWarning && !_crcRecomputeWarningPrinted) {
Logger.w(HiveWarning.crcRecomputeNeeded);
_crcRecomputeWarningPrinted = true;
}
} else {
return null;
}
}

_limitAvailableBytes(frameLength - 8);
Frame frame;
Expand Down
20 changes: 10 additions & 10 deletions hive/lib/src/binary/binary_writer_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -18,12 +17,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);
Expand Down Expand Up @@ -107,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(intWarning);
if (Logger.bigIntWarning && value >= maxInt) {
Logger.w(HiveWarning.bigInt);
}
writeDouble(value.toDouble());
}

Expand Down Expand Up @@ -265,7 +260,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
Expand All @@ -289,7 +289,7 @@ class BinaryWriterImpl extends BinaryWriter {
_buffer,
offset: startOffset,
length: frameLength - 4,
crc: cipher?.calculateKeyCrc() ?? 0,
crc: keyCrc ?? cipher?.calculateKeyCrc() ?? 0,
);
writeUint32(crc);

Expand Down
4 changes: 3 additions & 1 deletion hive/lib/src/binary/frame_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ class FrameHelper {
Uint8List bytes,
Keystore? keystore,
TypeRegistry registry,
HiveCipher? cipher, {
HiveCipher? cipher,
int? keyCrc, {
bool verbatim = false,
}) {
final reader = BinaryReaderImpl(bytes, registry);
Expand All @@ -21,6 +22,7 @@ class FrameHelper {

final frame = reader.readFrame(
cipher: cipher,
keyCrc: keyCrc,
lazy: false,
frameOffset: frameOffset,
verbatim: verbatim,
Expand Down
25 changes: 8 additions & 17 deletions hive/lib/src/hive_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +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();

Expand Down Expand Up @@ -71,7 +56,7 @@ RECOMMENDED ACTIONS:
!{'main', hiveIsolateName}.contains(isolateDebugName) &&
// Do not print this warning if this code is running in a test
!isolateDebugName.startsWith('test_suite')) {
Logger.w(unsafeIsolateWarning);
Logger.w(HiveWarning.unsafeIsolate);
}
homePath = path;
_managerOverride = BackendManager.select(backendPreference);
Expand All @@ -81,6 +66,7 @@ RECOMMENDED ACTIONS:
String name,
bool lazy,
HiveCipher? cipher,
int? keyCrc,
KeyComparator comparator,
CompactionStrategy compaction,
bool recovery,
Expand Down Expand Up @@ -119,13 +105,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,
);
}
Expand Down Expand Up @@ -172,6 +159,7 @@ RECOMMENDED ACTIONS:
Future<Box<E>> openBox<E>(
String name, {
HiveCipher? encryptionCipher,
int? keyCrc,
KeyComparator keyComparator = defaultKeyComparator,
CompactionStrategy compactionStrategy = defaultCompactionStrategy,
bool crashRecovery = true,
Expand All @@ -187,6 +175,7 @@ RECOMMENDED ACTIONS:
name,
false,
encryptionCipher,
keyCrc,
keyComparator,
compactionStrategy,
crashRecovery,
Expand All @@ -200,6 +189,7 @@ RECOMMENDED ACTIONS:
Future<LazyBox<E>> openLazyBox<E>(
String name, {
HiveCipher? encryptionCipher,
int? keyCrc,
KeyComparator keyComparator = defaultKeyComparator,
CompactionStrategy compactionStrategy = defaultCompactionStrategy,
bool crashRecovery = true,
Expand All @@ -214,6 +204,7 @@ RECOMMENDED ACTIONS:
name,
true,
encryptionCipher,
keyCrc,
keyComparator,
compactionStrategy,
crashRecovery,
Expand Down
Loading
Loading