diff --git a/CHANGELOG.md b/CHANGELOG.md index a101742..75a493e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.3.1 + +- Fixed error handling nil UUIDs in recycle bin. + Also, only create recycle bin on demand (when deleting items). + ## 0.3.0+1 - Minor fixes for kdbx 4.x diff --git a/example/pubspec.lock b/example/pubspec.lock index 9637e5a..2e98f26 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -8,6 +8,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.5.2" + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.4.1" charcode: dependency: transitive description: @@ -28,7 +35,7 @@ packages: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.14.11" + version: "1.14.12" convert: dependency: transitive description: @@ -49,7 +56,14 @@ packages: name: crypto url: "https://pub.dartlang.org" source: hosted - version: "2.1.2" + version: "2.1.4" + cryptography: + dependency: transitive + description: + name: cryptography + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.2" dio: dependency: transitive description: @@ -57,6 +71,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.16" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" + ffi_helper: + dependency: transitive + description: + name: ffi_helper + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.0" flutter: dependency: "direct main" description: flutter @@ -76,13 +104,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.3.3" + isolate: + dependency: transitive + description: + name: isolate + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" kdbx: dependency: "direct main" description: path: ".." relative: true source: path - version: "0.2.1" + version: "0.3.0" logging: dependency: transitive description: diff --git a/lib/src/kdbx_dao.dart b/lib/src/kdbx_dao.dart index 6c96a2d..1664635 100644 --- a/lib/src/kdbx_dao.dart +++ b/lib/src/kdbx_dao.dart @@ -32,27 +32,38 @@ extension KdbxDao on KdbxFile { return group; } + /// Returns the recycle bin, if it exists, null otherwise. KdbxGroup get recycleBin { final uuid = body.meta.recycleBinUUID.get(); - if (uuid == null) { - return _createRecycleBin(); + if (uuid?.isNil != false) { + return null; } - _logger.finer(() { - final groupDebug = body.rootGroup - .getAllGroups() - .map((g) => '${g.uuid}: ${g.name}') - .join('\n'); - return 'All Groups: $groupDebug'; - }); - return findGroupByUuid(uuid); + try { + return findGroupByUuid(uuid); + } catch (e, stackTrace) { + _logger.warning(() { + final groupDebug = body.rootGroup + .getAllGroups() + .map((g) => '${g.uuid}: ${g.name}') + .join('\n'); + return 'All Groups: $groupDebug'; + }); + _logger.severe('Inconsistency error, uuid $uuid not found in groups.', e, + stackTrace); + rethrow; + } + } + + KdbxGroup getRecycleBinOrCreate() { + return recycleBin ?? _createRecycleBin(); } void deleteGroup(KdbxGroup group) { - move(group, recycleBin); + move(group, getRecycleBinOrCreate()); } void deleteEntry(KdbxEntry entry) { - move(entry, recycleBin); + move(entry, getRecycleBinOrCreate()); } void move(KdbxObject kdbxObject, KdbxGroup toGroup) { diff --git a/lib/src/kdbx_object.dart b/lib/src/kdbx_object.dart index 6585017..f40f692 100644 --- a/lib/src/kdbx_object.dart +++ b/lib/src/kdbx_object.dart @@ -125,6 +125,11 @@ class KdbxUuid { KdbxUuid.random() : this(base64.encode(uuidGenerator.parse(uuidGenerator.v4()))); + /// https://tools.ietf.org/html/rfc4122.html#section-4.1.7 + /// > The nil UUID is special form of UUID that is specified to have all + /// 128 bits set to zero. + static const NIL = KdbxUuid('AAAAAAAAAAAAAAAAAAAAAA=='); + static final Uuid uuidGenerator = Uuid(options: {'grng': UuidUtil.cryptoRNG}); @@ -142,4 +147,7 @@ class KdbxUuid { @override int get hashCode => uuid.hashCode; + + /// Whether this is the [NIL] uuid. + bool get isNil => this == NIL; } diff --git a/pubspec.yaml b/pubspec.yaml index 13318b9..e287702 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: kdbx description: KeepassX format implementation in pure dart. (kdbx 3.x and 4.x support). -version: 0.3.0 +version: 0.3.1 homepage: https://github.com/authpass/kdbx.dart environment: diff --git a/test/kdbx4_test.dart b/test/kdbx4_test.dart index 4555494..b9f76ea 100644 --- a/test/kdbx4_test.dart +++ b/test/kdbx4_test.dart @@ -52,6 +52,17 @@ class Argon2Test extends Argon2Base { Argon2Hash argon2hash; } +Future _readKdbxFile( + String filePath, { + String password = 'asdf', +}) async { + final kdbxFormat = KdbxFormat(Argon2Test()); + final data = await File(filePath).readAsBytes(); + final file = await kdbxFormat.read( + data, Credentials(ProtectedValue.fromString(password))); + return file; +} + void main() { Logger.root.level = Level.ALL; PrintAppender().attachToLogger(Logger.root); @@ -132,6 +143,21 @@ void main() { File('test_output_chacha20.kdbx').writeAsBytesSync(output); }); }); + group('recycle bin test', () { + test('empty recycle bin with "zero" uuid', () async { + final file = await _readKdbxFile('test/keepass2test.kdbx'); + final recycleBin = file.recycleBin; + expect(recycleBin, isNull); + }); + test('check deleting item', () async { + final file = await _readKdbxFile('test/keepass2test.kdbx'); + expect(file.recycleBin, isNull); + final entry = file.body.rootGroup.getAllEntries().first; + file.deleteEntry(entry); + expect(file.recycleBin, isNotNull); + expect(entry.parent, equals(file.recycleBin)); + }); + }); } KdbxEntry _createEntry( diff --git a/test/keepass2test.kdbx b/test/keepass2test.kdbx new file mode 100644 index 0000000..d7a370b Binary files /dev/null and b/test/keepass2test.kdbx differ