From 14fe0105ae88cd5f5ecc6f5668e0c16a8aa43cdc Mon Sep 17 00:00:00 2001 From: Herbert Poul Date: Thu, 22 Aug 2019 01:16:40 +0200 Subject: [PATCH] started to parse groups and entries. --- bin/kdbx.dart | 47 ++- example/kdbx_example.dart | 3 +- lib/kdbx.dart | 8 +- lib/src/crypto/protected_value.dart | 41 ++- lib/src/kdbx_base.dart | 6 - lib/src/kdbx_entry.dart | 23 +- lib/src/kdbx_format.dart | 21 +- lib/src/kdbx_group.dart | 28 +- lib/src/kdbx_header.dart | 28 +- lib/src/kdbx_object.dart | 33 ++ pubspec.yaml | 2 + test/FooBar.3.content.xml | 485 ++++++++++++++++++++++++++++ test/FooBar.kdbx | Bin 1358 -> 2078 bytes test/kdbx_test.dart | 10 +- 14 files changed, 686 insertions(+), 49 deletions(-) delete mode 100644 lib/src/kdbx_base.dart create mode 100644 lib/src/kdbx_object.dart create mode 100644 test/FooBar.3.content.xml diff --git a/bin/kdbx.dart b/bin/kdbx.dart index 2a12b9a..c5a9966 100644 --- a/bin/kdbx.dart +++ b/bin/kdbx.dart @@ -5,6 +5,7 @@ import 'package:args/args.dart'; import 'package:args/command_runner.dart'; import 'package:kdbx/src/crypto/protected_value.dart'; import 'package:kdbx/src/kdbx_format.dart'; +import 'package:kdbx/src/kdbx_group.dart'; import 'package:kdbx/src/kdbx_header.dart'; import 'package:logging/logging.dart'; import 'package:logging_appenders/logging_appenders.dart'; @@ -36,9 +37,11 @@ void main(List arguments) { } class KdbxCommandRunner extends CommandRunner { - KdbxCommandRunner(String executableName, String description) : super(executableName, description) { + KdbxCommandRunner(String executableName, String description) + : super(executableName, description) { argParser.addFlag('verbose', abbr: 'v'); addCommand(CatCommand()); + addCommand(DumpXmlCommand()); } @override @@ -69,8 +72,10 @@ abstract class KdbxFileCommand extends Command { usageException('Required argument: --input'); } final bytes = await File(inputFile).readAsBytes(); - final password = prompts.get('Password for $inputFile', conceal: true, validate: (str) => str.isNotEmpty); - final file = await KdbxFormat.read(bytes, Credentials(ProtectedValue.fromString(password))); + final password = prompts.get('Password for $inputFile', + conceal: true, validate: (str) => str.isNotEmpty); + final file = await KdbxFormat.read( + bytes, Credentials(ProtectedValue.fromString(password))); return runWithFile(file); } @@ -78,14 +83,48 @@ abstract class KdbxFileCommand extends Command { } class CatCommand extends KdbxFileCommand { + CatCommand() { + argParser.addFlag('decrypt', help: 'Force decryption of all protected strings.'); + } + @override String get description => 'outputs all entries from file.'; @override String get name => 'cat'; + bool get forceDecrypt => argResults['decrypt'] as bool; + + @override + Future runWithFile(KdbxFile file) async { + catGroup(file.body.rootGroup); + } + + void catGroup(KdbxGroup group, {int depth = 0}) { + final indent = ' ' * depth; + print('$indent + ${group.name} (${group.uuid})'); + for (final group in group.groups) { + catGroup(group, depth: depth + 1); + } + for (final entry in group.entries) { + final value = entry.strings['Password']; + print('$indent `- ${entry.strings['Title']?.getText()}: ${forceDecrypt ? value?.getText() : value?.toString()}'); + } + } +} + +class DumpXmlCommand extends KdbxFileCommand { + @override + String get description => 'Outputs the xml body unencrypted.'; + + @override + String get name => 'dumpXml'; + + @override + List get aliases => ['dump', 'xml']; + @override Future runWithFile(KdbxFile file) async { - _logger.severe('running'); + print(file.body.xmlDocument.toXmlString(pretty: true)); } } diff --git a/example/kdbx_example.dart b/example/kdbx_example.dart index cb932ef..0453429 100644 --- a/example/kdbx_example.dart +++ b/example/kdbx_example.dart @@ -1,6 +1,5 @@ import 'package:kdbx/kdbx.dart'; void main() { - var awesome = Awesome(); - print('awesome: ${awesome.isAwesome}'); + KdbxFormat.read(null, null); } diff --git a/lib/kdbx.dart b/lib/kdbx.dart index f20b988..b0b57af 100644 --- a/lib/kdbx.dart +++ b/lib/kdbx.dart @@ -1,8 +1,4 @@ -/// Support for doing something awesome. -/// -/// More dartdocs go here. +/// dart library for reading keepass file format (kdbx). library kdbx; -export 'src/kdbx_base.dart'; - -// TODO: Export any libraries intended for clients of this package. +export 'src/kdbx_format.dart'; diff --git a/lib/src/crypto/protected_value.dart b/lib/src/crypto/protected_value.dart index 452aae8..ad35f32 100644 --- a/lib/src/crypto/protected_value.dart +++ b/lib/src/crypto/protected_value.dart @@ -4,7 +4,29 @@ import 'dart:typed_data'; import 'package:crypto/crypto.dart'; -class ProtectedValue { +abstract class StringValue { + /// retrieves the (decrypted) stored value. + String getText(); +} + +class PlainValue implements StringValue { + PlainValue(this.text); + + final String text; + + @override + String getText() { + return text; + } + + @override + String toString() { + return 'PlainValue{text: $text}'; + } + +} + +class ProtectedValue implements StringValue { ProtectedValue(this._value, this._salt); factory ProtectedValue.fromString(String value) { @@ -20,17 +42,30 @@ class ProtectedValue { final Uint8List _salt; Uint8List get binaryValue => _xor(_value, _salt); + Uint8List get hash => sha256.convert(binaryValue).bytes as Uint8List; static Uint8List _randomBytes(int length) { - return Uint8List.fromList(List.generate(length, (i) => random.nextInt(0xff))); + return Uint8List.fromList( + List.generate(length, (i) => random.nextInt(0xff))); } + static Uint8List _xor(Uint8List a, Uint8List b) { assert(a.length == b.length); final ret = Uint8List(a.length); - for (int i = 0 ; i < a.length ; i++) { + for (int i = 0; i < a.length; i++) { ret[i] = a[i] ^ b[i]; } return ret; } + + @override + String getText() { + return utf8.decode(binaryValue); + } + + @override + String toString() { + return 'ProtectedValue{${base64.encode(hash)}}'; + } } diff --git a/lib/src/kdbx_base.dart b/lib/src/kdbx_base.dart deleted file mode 100644 index e8a6f15..0000000 --- a/lib/src/kdbx_base.dart +++ /dev/null @@ -1,6 +0,0 @@ -// TODO: Put public facing types in this file. - -/// Checks if you are awesome. Spoiler: you are. -class Awesome { - bool get isAwesome => true; -} diff --git a/lib/src/kdbx_entry.dart b/lib/src/kdbx_entry.dart index 249a875..9d36e76 100644 --- a/lib/src/kdbx_entry.dart +++ b/lib/src/kdbx_entry.dart @@ -1,5 +1,22 @@ +import 'package:kdbx/src/crypto/protected_value.dart'; +import 'package:kdbx/src/kdbx_format.dart'; +import 'package:kdbx/src/kdbx_group.dart'; +import 'package:kdbx/src/kdbx_object.dart'; +import 'package:xml/xml.dart'; +class KdbxEntry extends KdbxObject { + KdbxEntry.read(this.parent, XmlElement node) : super.read(node) { + strings = Map.fromEntries(node.findElements('String').map((el) { + final key = el.findElements('Key').single.text; + final valueNode = el.findElements('Value').single; + if (valueNode.getAttribute('Protected')?.toLowerCase() == 'true') { + return MapEntry(key, KdbxFile.protectedValueForNode(valueNode)); + } else { + return MapEntry(key, PlainValue(valueNode.text)); + } + })); + } -class KdbxEntry { - -} \ No newline at end of file + KdbxGroup parent; + Map strings; +} diff --git a/lib/src/kdbx_format.dart b/lib/src/kdbx_format.dart index 657f0f6..b4c71d6 100644 --- a/lib/src/kdbx_format.dart +++ b/lib/src/kdbx_format.dart @@ -7,6 +7,7 @@ import 'package:kdbx/src/crypto/protected_salt_generator.dart'; import 'package:kdbx/src/crypto/protected_value.dart'; import 'package:kdbx/src/internal/byte_utils.dart'; import 'package:kdbx/src/internal/crypto_utils.dart'; +import 'package:kdbx/src/kdbx_group.dart'; import 'package:kdbx/src/kdbx_header.dart'; import 'package:logging/logging.dart'; import 'package:pointycastle/export.dart'; @@ -19,24 +20,26 @@ class KdbxFile { static final protectedValues = Expando(); + static ProtectedValue protectedValueForNode(xml.XmlElement node) { + return protectedValues[node]; + } + final Credentials credentials; final KdbxHeader header; final KdbxBody body; } class KdbxBody { - KdbxBody(this.xmlDocument, this.meta); + KdbxBody(this.xmlDocument, this.meta, this.rootGroup); final xml.XmlDocument xmlDocument; final KdbxMeta meta; + final KdbxGroup rootGroup; } -class KdbxMeta { - -} +class KdbxMeta {} class KdbxFormat { - static Future read(Uint8List input, Credentials credentials) async { final reader = ReaderHelper(input); final header = await KdbxHeader.read(reader); @@ -57,7 +60,8 @@ class KdbxFormat { final string = utf8.decode(xml); return KdbxFile(credentials, header, _loadXml(header, string)); } else { - return KdbxFile(credentials, header, _loadXml(header, utf8.decode(blocks))); + return KdbxFile( + credentials, header, _loadXml(header, utf8.decode(blocks))); } } @@ -82,9 +86,10 @@ class KdbxFormat { final keePassFile = document.findElements('KeePassFile').single; final meta = keePassFile.findElements('Meta').single; - final groupRoot = keePassFile.findElements('Root').single; + final root = keePassFile.findElements('Root').single; + final rootGroup = KdbxGroup.read(null, root.findElements('Group').single); _logger.fine('got meta: ${meta.toXmlString(pretty: true)}'); - return KdbxBody(document, KdbxMeta()); + return KdbxBody(document, KdbxMeta(), rootGroup); } static Uint8List _decryptContent( diff --git a/lib/src/kdbx_group.dart b/lib/src/kdbx_group.dart index 01fb46a..dd6d2f5 100644 --- a/lib/src/kdbx_group.dart +++ b/lib/src/kdbx_group.dart @@ -1,4 +1,28 @@ +import 'package:kdbx/src/kdbx_entry.dart'; +import 'package:xml/xml.dart'; -class KdbxGroup { +import 'kdbx_object.dart'; -} \ No newline at end of file +final _builder = XmlBuilder(); + +class KdbxGroup extends KdbxObject { + KdbxGroup(this.parent) : super.create('Group'); + + KdbxGroup.read(this.parent, XmlElement node) : super.read(node) { + node + .findElements('Group') + .map((el) => KdbxGroup.read(this, el)) + .forEach(groups.add); + node + .findElements('Entry') + .map((el) => KdbxEntry.read(this, el)) + .forEach(entries.add); + } + + /// null if this is the root group. + final KdbxGroup parent; + final List groups = []; + final List entries = []; + + String get name => text('Name') ?? ''; +} diff --git a/lib/src/kdbx_header.dart b/lib/src/kdbx_header.dart index b883e97..8944d09 100644 --- a/lib/src/kdbx_header.dart +++ b/lib/src/kdbx_header.dart @@ -51,14 +51,20 @@ class HeaderField { } class KdbxHeader { - KdbxHeader({this.sig1, this.sig2, this.versionMinor, this.versionMajor, this.fields}); + KdbxHeader( + {this.sig1, + this.sig2, + this.versionMinor, + this.versionMajor, + this.fields}); static Future read(ReaderHelper reader) async { // reading signature final sig1 = reader.readUint32(); final sig2 = reader.readUint32(); if (!(sig1 == Consts.FileMagic && sig2 == Consts.Sig2Kdbx)) { - throw UnsupportedError('Unsupported file structure. ${ByteUtils.toHex(sig1)}, ${ByteUtils.toHex(sig2)}'); + throw UnsupportedError( + 'Unsupported file structure. ${ByteUtils.toHex(sig1)}, ${ByteUtils.toHex(sig2)}'); } // reading version @@ -66,7 +72,8 @@ class KdbxHeader { final versionMajor = reader.readUint16(); _logger.finer('Reading version: $versionMajor.$versionMinor'); - final headerFields = Map.fromEntries(readField(reader, versionMajor).map((field) => MapEntry(field.field, field))); + final headerFields = Map.fromEntries(readField(reader, versionMajor) + .map((field) => MapEntry(field.field, field))); return KdbxHeader( sig1: sig1, sig2: sig2, @@ -76,10 +83,12 @@ class KdbxHeader { ); } - static Iterable readField(ReaderHelper reader, int versionMajor) sync* { + static Iterable readField( + ReaderHelper reader, int versionMajor) sync* { while (true) { final headerId = reader.readUint8(); - final int bodySize = versionMajor >= 4 ? reader.readUint32() : reader.readUint16(); + final int bodySize = + versionMajor >= 4 ? reader.readUint32() : reader.readUint16(); _logger.finer('Read header ${HeaderFields.values[headerId]}'); final bodyBytes = bodySize > 0 ? reader.readBytes(bodySize) : null; if (headerId > 0) { @@ -108,7 +117,8 @@ class KdbxHeader { } PotectedValueEncryption get innerRandomStreamEncryption => - PotectedValueEncryption.values[fields[HeaderFields.InnerRandomStreamID].bytes.asUint32List().single]; + PotectedValueEncryption.values[ + fields[HeaderFields.InnerRandomStreamID].bytes.asUint32List().single]; } class Credentials { @@ -148,7 +158,8 @@ class HashedBlockReader { final blockSize = reader.readUint32(); if (blockSize > 0) { final blockData = reader.readBytes(blockSize).asUint8List(); - if (!ByteUtils.eq(crypto.sha256.convert(blockData).bytes as Uint8List, blockHash.asUint8List())) { + if (!ByteUtils.eq(crypto.sha256.convert(blockData).bytes as Uint8List, + blockHash.asUint8List())) { throw KdbxCorruptedFileException(); } yield blockData; @@ -165,7 +176,8 @@ class ReaderHelper { final Uint8List data; int pos = 0; - ByteBuffer _nextByteBuffer(int byteCount) => data.sublist(pos, pos += byteCount).buffer; + ByteBuffer _nextByteBuffer(int byteCount) => + data.sublist(pos, pos += byteCount).buffer; int readUint32() => _nextByteBuffer(4).asUint32List().first; diff --git a/lib/src/kdbx_object.dart b/lib/src/kdbx_object.dart new file mode 100644 index 0000000..895b9b4 --- /dev/null +++ b/lib/src/kdbx_object.dart @@ -0,0 +1,33 @@ +import 'package:meta/meta.dart'; +import 'package:uuid/uuid.dart'; +import 'package:xml/xml.dart'; + +class KdbxTimes { + KdbxTimes.read(this.node); + + XmlElement node; + + DateTime get creationTime => _readTime('CreationTime'); + + DateTime _readTime(String nodeName) => + DateTime.parse(node.findElements(nodeName).single.text); +} + +abstract class KdbxObject { + KdbxObject.create(String nodeName) + : uuid = Uuid().v4(), + node = XmlElement(XmlName(nodeName)); + + KdbxObject.read(this.node) { + uuid = node.findElements('UUID').single.text; + } + + final XmlElement node; + String uuid; + + @protected + String text(String nodeName) => _opt(nodeName)?.text; + + XmlElement _opt(String nodeName) => + node.findElements(nodeName).singleWhere((x) => true, orElse: () => null); +} diff --git a/pubspec.yaml b/pubspec.yaml index fe5a52e..ee04939 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,6 +13,8 @@ dependencies: crypto: '>=2.0.0 <3.0.0' pointycastle: '>=1.0.1 <2.0.0' xml: '>=3.5.0 <4.0.0' + uuid: '>=2.0.0 <3.0.0' + meta: '>=1.0.0 <2.0.0' # required for bin/ args: '>1.5.0 <2.0.0' diff --git a/test/FooBar.3.content.xml b/test/FooBar.3.content.xml new file mode 100644 index 0000000..da0c541 --- /dev/null +++ b/test/FooBar.3.content.xml @@ -0,0 +1,485 @@ + + + + KdbxWeb + DS/kVWzLk+yB13QmD3d3Si1zn9t445cp9ZVnwxhp+ro= + FooBar + 2019-08-20T13:16:06Z + + 2019-08-20T13:15:47Z + + 2019-08-20T13:15:47Z + 365 + + 2019-08-20T13:16:03Z + -1 + -1 + True + dVSBC/BAx70qcsy6XkrGJA== + 2019-08-20T13:15:47Z + AAAAAAAAAAAAAAAAAAAAAA== + 2019-08-20T13:15:47Z + 10 + 6291456 + + + + False + False + True + False + False + + + + + + + + LAQMkihXTkxhA2D2tE40Fg== + FooBar + + 49 + + 2019-08-20T13:15:47Z + 2019-08-20T13:16:06Z + 2019-08-20T13:16:06Z + 2019-08-20T13:15:47Z + False + 0 + 2019-08-20T13:15:47Z + + True + + null + null + AAAAAAAAAAAAAAAAAAAAAA== + + dVSBC/BAx70qcsy6XkrGJA== + Recycle Bin + + 43 + + 2019-08-20T13:15:47Z + 2019-08-20T13:15:47Z + 2019-08-20T13:15:47Z + 2019-08-20T13:15:47Z + False + 0 + 2019-08-20T13:15:47Z + + True + + False + False + AAAAAAAAAAAAAAAAAAAAAA== + + QQywiz/v9idlTOqUJQsHSw== + Sub Group 2 delete me + + 48 + + 2019-08-21T20:52:19Z + 2019-08-21T20:52:34Z + 2019-08-21T20:52:34Z + 2019-08-21T20:52:19Z + False + 0 + 2019-08-21T20:52:43Z + + True + + null + null + AAAAAAAAAAAAAAAAAAAAAA== + + + 5j0iOoJDoaGNVDsuZ16EkQ== + 0 + + + + + + 2019-08-21T20:51:50Z + 2019-08-21T20:52:01Z + 2019-08-21T20:52:01Z + 2019-08-21T20:51:50Z + False + 0 + 2019-08-21T20:52:03Z + + + Title + something to delete + + + UserName + deleteme + + + Password + KHEormXPgg== + + + URL + + + + Notes + + + + True + 0 + + + + 5j0iOoJDoaGNVDsuZ16EkQ== + 0 + + + + + + 2019-08-21T20:51:50Z + 2019-08-21T20:51:58Z + 2019-08-21T20:51:58Z + 2019-08-21T20:51:50Z + False + 0 + 2019-08-21T20:51:50Z + + + Title + something to delete + + + UserName + deleteme + + + Password + + + + URL + + + + Notes + + + + True + 0 + + + + + + + NUCSPwdmXYRkOdpvLVAK+Q== + First Custom Group + + 48 + + 2019-08-21T20:49:18Z + 2019-08-21T20:49:31Z + 2019-08-21T20:49:31Z + 2019-08-21T20:49:18Z + False + 0 + 2019-08-21T20:49:18Z + + True + + null + null + AAAAAAAAAAAAAAAAAAAAAA== + + EGhdPVJumK8MjlRPaK+bvQ== + 0 + + + + + + 2019-08-21T20:49:55Z + 2019-08-21T20:51:47Z + 2019-08-21T20:51:47Z + 2019-08-21T20:49:55Z + False + 0 + 2019-08-21T20:49:55Z + + + Title + testing stuff + + + UserName + umläut üser + + + Password + oTIZFyRSWhcaZElzhb3cvw== + + + URL + + + + Notes + + + + emojifun + 😂️ + + + emojifun1 + blubb + + + New Field + another new field. + + + New Field1 + di8rpVnnLJxwBXiKhPXH + + + True + 0 + + + + EGhdPVJumK8MjlRPaK+bvQ== + 0 + + + + + + 2019-08-21T20:49:55Z + 2019-08-21T20:50:46Z + 2019-08-21T20:50:46Z + 2019-08-21T20:49:55Z + False + 0 + 2019-08-21T20:49:55Z + + + Title + testing deleting stuff + + + UserName + umläut üser + + + Password + k3Oqaa7OIHvRgwc/7nXu + + + URL + + + + Notes + + + + emojifun + 😂️ + + + emojifun1 + blubb + + + True + 0 + + + + EGhdPVJumK8MjlRPaK+bvQ== + 0 + + + + + + 2019-08-21T20:49:55Z + 2019-08-21T20:51:06Z + 2019-08-21T20:51:06Z + 2019-08-21T20:49:55Z + False + 0 + 2019-08-21T20:49:55Z + + + Title + testing deleting stuff + + + UserName + umläut üser + + + Password + KaYaF6SQwa4SpHhxRPT0Cw== + + + URL + + + + Notes + + + + emojifun + 😂️ + + + emojifun1 + blubb + + + True + 0 + + + + + + + W3rvv7uZya9KAUDF5QFx5Q== + Second Group + + 48 + + 2019-08-21T20:49:46Z + 2019-08-21T20:49:48Z + 2019-08-21T20:49:48Z + 2019-08-21T20:49:46Z + False + 0 + 2019-08-21T20:49:46Z + + True + + null + null + AAAAAAAAAAAAAAAAAAAAAA== + + Dqa10jVPgVTLhXCje3BODw== + Sub Group + + 48 + + 2019-08-21T20:52:08Z + 2019-08-21T20:52:11Z + 2019-08-21T20:52:11Z + 2019-08-21T20:52:08Z + False + 0 + 2019-08-21T20:52:08Z + + True + + null + null + AAAAAAAAAAAAAAAAAAAAAA== + + 0d2zDwBH5OniOoRWstAQZg== + 0 + + + + + + 2019-08-21T20:52:48Z + 2019-08-21T20:52:52Z + 2019-08-21T20:52:52Z + 2019-08-21T20:52:48Z + False + 0 + 2019-08-21T20:52:48Z + + + Title + Entry in Sub Group + + + UserName + + + + Password + + + + URL + + + + Notes + + + + True + 0 + + + + + + + RMUeiV24MDcrIY8qQYRAlg== + 0 + + + + + + 2019-08-21T00:17:25Z + 2019-08-21T00:17:36Z + 2019-08-21T00:17:36Z + 2019-08-21T00:17:25Z + False + 0 + 2019-08-21T00:17:25Z + + + Title + loremipsum.com + + + UserName + foo + + + Password + CRTX + + + URL + + + + Notes + + + + True + 0 + + + + + + + diff --git a/test/FooBar.kdbx b/test/FooBar.kdbx index e352347576233a4acd5508470e6590e81193c275..465440acf2d131d1ffbcbf1abfc412f83866e58c 100644 GIT binary patch delta 2060 zcmV+n2=n*O3Z4*A;mE>{ybJ^W1KKZNOhnfW-0D42B zTbmJ#6VZx9r@CM~{l4RR#r-Z}Ie;KP2nM!dCI$!qa2Ego000002M_=lIl1io2@M8F z6l(Wnyw(=NjgufVcewLw}1m7a5+TTGkW(6~VAK_f#&nj-28Id*00IC2000C40MM-pF`*J0f*Q;3jdfm; zu+bbmCpoO|ZaFB%}Eg?1FuOm{?c9tDmW{_J`LyV{F?ng_+m_&6KAIH1FMT zI0tAr3fKE2M_Z|{(mn1J4gG`ysZG0xlHl`eYd4)w;Q&N$m^>7Fc2!tZQ|1N7-tUwF zn}%<@lYhWYp|*l2wHI5vQM(CoEj{`UjFpnBotWE>rnA^dLCdN*_JW$K3f*{%{ID&~ zsrvJ(4)gV$AJ?0)05}?6`AVfK@{c@qYiKFzJnNGI1K#Sl{Po~u&f?@rs@13TMZ+L> z4vqJu2JEPa^kRf9<-)OSvrt`C+;B>&hf@n**LMOKM<`q`MV@_+XC z0JoW54D9s`fXydMCeDc`4rh5)*~h!-%r1cyv|JfZTw^#qV(3g5Z_S)8{u+nZMOs`O zPu`$%UpiW0%e}x*AYZ!*SUSBOH?D)K%sz^@zCZzTAPsBBD7F-e<*IL~;67*+MAmw@ zD)%W=N)l$-2&oc2i9#RJNsu)~mw$IMm+2wG7xLSljm_q!s5*zG2Onle?wjI_&ZH{s zt3Pm)v!F)vnsHcQz7+Xrx!-jfd#-TMKiX|;Q+uWvExdNEzAW;st%O^U94JrY8uZnsZv^bZB2@QI#1?iUiQ#MN@vcpY^-bTKdqiG7fk28Mw|yV0ZY`n?@I za+V$NkWU)rMAw}^4w#p2=zrGmn3xF)A>$dQ`=Pm+vTD|2^M-Mv_&(`8I4Qt$7xcG9 zmONRaL(x_`fL#gFyWl2C2An9-!L|$ajlHx0#mOUyf$M;Z*yVQDYw40Z zVDhIQ*YU~K=BhHK6l)T&n1hPsZ;!Q^pMC9FK1d(&Xc>Lspax@KX~A%lFDkqI7;_L%tjFDaHJJJ_?9YJj}b zA3hCXPKDTF>m0X$ihrdf+c=H5o$eoIkv!n$X@j2TAPWY~!wOP{Mi>}ae~4 z+)eUBuG51Zz6m~P2c=QE7#UD6ev^k9%alX2@U-BkzI0d_D)%Ez=qExot(ubFP#3s9 zj{B1yz*|W(_HQ3ey}AGzi{hz9?q!oT!q;Cx+BMJJC4VyO8KCzeM&-_U;Vm!* zsi0p2pvnjNwK(A1@YDrJ!6FSp`m>*02$w`oXlSd8*gIlXT+Nd?CmE)IX&RNQWfq}b zbE^juis4q3LenqgwGEH^pRoQAM;=x*vX$t^zvS!F8pL|_kovxXuaPbP)|?%tg{f0I!r@#KYg#rNjACyFr{<%^F`N=WsIfl(p= zG*t~lB$Bl(7INlm8^*U+;~SXkvy;A!XeUKW76vD{M50doZpT!f8hqp3mE_T`wy@>j q2|Q2(>+~vKlwLBKP%}!u^I;&Bc(zaTSq;%jIvDZ2k_CIr)PT!$N&7_r delta 1334 zcmV-61-9d_Y+Jbrjc7MFSb4SDpAONL81Qtg4 zZFT-pDVo$My3>po@Op2r=>DgkT#ffL76OoL!vot%^?8~+Uz|E*MA}iizmI+ielWN>; z-jw!0(OHwEkAL#85G~=a$IHw4B1SPl`r4FqM$0yNNiI)d!o@0H^w1GGU1yfsk7DTs z&Xg`Ln0>&8aab}_$1Y^YbN$@WZW}g7#a;Y4R^F6`(gBOi`q1QiIKz!$66+=dY92GJXMc=VbP*cVD6(k_`ypY3mymTV zXjnJyJFk|G7pRT1GJT!cU4a`cbo4QF_%glTtW5|Iw<~Aj2_6FH&s@WdDazm}p% z9Ff@l3cY!QU zTa@&zV!8f|fIf@E;Kj8Z$znKEKoopRFORTPY=86|2)jJnG|YW@rVod^hZxMK;bc6> zPrUMC3%3IIl^FT7?a|fA)yW$-JZ_{{Ilb~f4{%-NHaR4=%n$`pD?~E z|9>s5oy_mIFIUtSKz6Y{&a+Af2tn9xIAkOdVNTl;dn^w;bv$iVb_n2oMVAIJ+RYkj z;_Owf`o97^SmHQKA2$|3J_jM9-k+y64RFFZt*CL;>~35xRDGrMC?p1-vUrgabij<@ zr#+7spd*~c4_W*+w}Ew^a>i;c&@jR(fq#6wp@#{%97rX`{D3dlq-2-ppsh7O%hN@X z*~UK0PtB#6)Qt6|Ntz96p!ek_E5%n;j_d+fp4-UGB=XJ^F4Ps5widbp`83f{pX*v{B#}xCM`BhOeREr!piVwBL-rp{cEjP?F&(Wo7&A z4`Xp+g~?h%a*XMi_LsTuw2paTNPkkdDcQB$#F>|+N+Uo`d5og@QO;L?+ zw%6a6L%x1Ut?*4D__Bx}%C`hTspxElA*$0-W=GHH%#4@fey z6B!+<%*$MH(Ugb5=f*Xu!qs7ny`}WvG)!W;UHeGSzxso@3%+D#AFEeoLN9A)9y@{l sTjD{-gX9tGBv&{1D>9y&8|)DMniBP)gFeIZ`{YwasQGEfRhmwY{h3ICAOHXW diff --git a/test/kdbx_test.dart b/test/kdbx_test.dart index 2072d98..a9ef8d5 100644 --- a/test/kdbx_test.dart +++ b/test/kdbx_test.dart @@ -12,16 +12,12 @@ void main() { Logger.root.level = Level.ALL; Logger.root.onRecord.listen(PrintAppender().logListener()); group('A group of tests', () { - Awesome awesome; - - setUp(() { - awesome = Awesome(); - }); + setUp(() {}); test('First Test', () async { final data = await File('test/FooBar.kdbx').readAsBytes(); - await KdbxFormat.read(data, Credentials(ProtectedValue.fromString('FooBar'))); - expect(awesome.isAwesome, isTrue); + await KdbxFormat.read( + data, Credentials(ProtectedValue.fromString('FooBar'))); }); }); }