Browse Source

custom data for password files, increase requirement to dart 2.7, xml 3.7

remove-cryptography-dependency
Herbert Poul 5 years ago
parent
commit
53adad5ceb
  1. 6
      example/pubspec.lock
  2. 2
      lib/kdbx.dart
  3. 35
      lib/src/internal/extension_utils.dart
  4. 37
      lib/src/kdbx_custom_data.dart
  5. 31
      lib/src/kdbx_format.dart
  6. 42
      lib/src/kdbx_meta.dart
  7. 11
      lib/src/kdbx_xml.dart
  8. 10
      pubspec.yaml
  9. 2
      test/kdbx_test.dart

6
example/pubspec.lock

@ -117,7 +117,7 @@ packages:
name: petitparser
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.0"
version: "3.0.0"
pointycastle:
dependency: transitive
description:
@ -192,6 +192,6 @@ packages:
name: xml
url: "https://pub.dartlang.org"
source: hosted
version: "3.5.0"
version: "3.7.0"
sdks:
dart: ">=2.4.0 <3.0.0"
dart: ">=2.7.0 <3.0.0"

2
lib/kdbx.dart

@ -4,6 +4,7 @@ library kdbx;
export 'src/crypto/protected_value.dart'
show ProtectedValue, StringValue, PlainValue;
export 'src/kdbx_consts.dart';
export 'src/kdbx_custom_data.dart';
export 'src/kdbx_entry.dart';
export 'src/kdbx_format.dart';
export 'src/kdbx_header.dart'
@ -12,4 +13,5 @@ export 'src/kdbx_header.dart'
KdbxInvalidKeyException,
KdbxCorruptedFileException,
KdbxUnsupportedException;
export 'src/kdbx_meta.dart';
export 'src/kdbx_object.dart';

35
lib/src/internal/extension_utils.dart

@ -0,0 +1,35 @@
import 'package:kdbx/src/kdbx_xml.dart';
import 'package:xml/xml.dart' as xml;
extension XmlElementExt on xml.XmlElement {
xml.XmlElement singleElement(String nodeName,
{xml.XmlElement Function() orElse}) {
final elements = findElements(nodeName);
if (elements.isEmpty) {
if (orElse != null) {
final ret = orElse();
children.add(ret);
return ret;
} else {
return null;
}
}
return elements.single;
}
String singleTextNode(String nodeName) {
return findElements(nodeName).single.text;
}
/// If an element child with the given name already exists,
/// it will be removed and the given element will be added.
/// otherwise it will be only added.
void replaceSingle(xml.XmlElement element) {
XmlUtils.removeChildrenByName(this, element.name.local);
children.add(element);
}
}
extension ObjectExt<T> on T {
R let<R>(R Function(T that) op) => op(this);
}

37
lib/src/kdbx_custom_data.dart

@ -0,0 +1,37 @@
import 'package:kdbx/src/kdbx_object.dart';
import 'package:kdbx/src/kdbx_xml.dart';
import 'package:xml/xml.dart' as xml;
import 'package:kdbx/src/internal/extension_utils.dart';
class KdbxCustomData extends KdbxNode {
KdbxCustomData.create()
: data = {},
super.create(TAG_NAME);
KdbxCustomData.read(xml.XmlElement node)
: data = Map.fromEntries(
node.findElements(KdbxXml.NODE_CUSTOM_DATA_ITEM).map((el) {
final key = el.singleTextNode(KdbxXml.NODE_KEY);
final value = el.singleTextNode(KdbxXml.NODE_VALUE);
return MapEntry(key, value);
})),
super.read(node);
static const String TAG_NAME = 'CustomData';
final Map<String, String> data;
@override
xml.XmlElement toXml() {
final el = super.toXml();
el.children.clear();
el.children.addAll(
data.entries
.map((e) => XmlUtils.createNode(KdbxXml.NODE_CUSTOM_DATA_ITEM, [
XmlUtils.createTextNode(KdbxXml.NODE_KEY, e.key),
XmlUtils.createTextNode(KdbxXml.NODE_VALUE, e.value),
])),
);
return el;
}
}

31
lib/src/kdbx_format.dart

@ -11,13 +11,14 @@ 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:kdbx/src/kdbx_meta.dart';
import 'package:kdbx/src/kdbx_object.dart';
import 'package:kdbx/src/kdbx_xml.dart';
import 'package:logging/logging.dart';
import 'package:meta/meta.dart';
import 'package:pointycastle/export.dart';
import 'package:xml/xml.dart' as xml;
import 'kdbx_object.dart';
import 'package:kdbx/src/internal/extension_utils.dart';
final _logger = Logger('kdbx.format');
@ -37,6 +38,7 @@ abstract class Credentials {
class KeyFileComposite implements Credentials {
KeyFileComposite({@required this.password, @required this.keyFile});
PasswordCredentials password;
KeyFileCredentials keyFile;
@ -79,6 +81,7 @@ class KeyFileCredentials implements CredentialsPart {
return KeyFileCredentials._(ProtectedValue.fromBinary(bytes));
}
}
KeyFileCredentials._(this._keyFileValue);
static final RegExp _hexValuePattern = RegExp(r'/^[a-f\d]{64}$/i');
@ -136,6 +139,7 @@ class KdbxFile {
final Set<KdbxObject> dirtyObjects = {};
final StreamController<Set<KdbxObject>> _dirtyObjectsChanged =
StreamController<Set<KdbxObject>>.broadcast();
Stream<Set<KdbxObject>> get dirtyObjectsChanged =>
_dirtyObjectsChanged.stream;
@ -273,28 +277,13 @@ class KdbxBody extends KdbxNode {
}
}
class KdbxMeta extends KdbxNode {
KdbxMeta.create({@required String databaseName}) : super.create('Meta') {
this.databaseName.set(databaseName);
}
KdbxMeta.read(xml.XmlElement node) : super.read(node);
StringNode get databaseName => StringNode(this, 'DatabaseName');
Base64Node get headerHash => Base64Node(this, 'HeaderHash');
@override
// ignore: unnecessary_overrides
xml.XmlElement toXml() {
return super.toXml();
}
}
class KdbxFormat {
static KdbxFile create(Credentials credentials, String name) {
final header = KdbxHeader.create();
final meta = KdbxMeta.create(databaseName: name);
final meta = KdbxMeta.create(
databaseName: name,
generator: 'AuthPass',
);
final rootGroup = KdbxGroup.create(parent: null, name: name);
final body = KdbxBody.create(meta, rootGroup);
return KdbxFile(credentials, header, body);

42
lib/src/kdbx_meta.dart

@ -0,0 +1,42 @@
import 'package:kdbx/src/internal/extension_utils.dart';
import 'package:kdbx/src/kdbx_custom_data.dart';
import 'package:kdbx/src/kdbx_object.dart';
import 'package:kdbx/src/kdbx_xml.dart';
import 'package:meta/meta.dart';
import 'package:xml/xml.dart' as xml;
class KdbxMeta extends KdbxNode {
KdbxMeta.create({
@required String databaseName,
String generator = 'kdbx.dart',
}) : customData = KdbxCustomData.create(),
super.create('Meta') {
this.databaseName.set(databaseName);
this.generator.set(generator);
}
KdbxMeta.read(xml.XmlElement node)
: customData = node
.singleElement('CustomData')
?.let((e) => KdbxCustomData.read(e)) ??
KdbxCustomData.create(),
super.read(node);
final KdbxCustomData customData;
StringNode get generator => StringNode(this, 'Generator');
StringNode get databaseName => StringNode(this, 'DatabaseName');
Base64Node get headerHash => Base64Node(this, 'HeaderHash');
BooleanNode get recycleBinEnabled => BooleanNode(this, 'RecycleBinEnabled');
UuidNode get recycleBinUUID => UuidNode(this, 'RecycleBinUUID');
DateTimeUtcNode get recycleBinChanged =>
DateTimeUtcNode(this, 'RecycleBinChanged');
@override
xml.XmlElement toXml() => super.toXml()..replaceSingle(customData.toXml());
}

11
lib/src/kdbx_xml.dart

@ -12,6 +12,8 @@ class KdbxXml {
static const NODE_VALUE = 'Value';
static const ATTR_PROTECTED = 'Protected';
static const NODE_HISTORY = 'History';
static const NODE_CUSTOM_DATA_ITEM = 'Item';
}
abstract class KdbxSubNode<T> {
@ -163,6 +165,15 @@ class XmlUtils {
node.children
.removeWhere((node) => node is XmlElement && node.name.local == name);
}
static XmlElement createTextNode(String localName, String value) =>
createNode(localName, [XmlText(value)]);
static XmlElement createNode(
String localName, [
List<XmlNode> children = const [],
]) =>
XmlElement(XmlName(localName))..children.addAll(children);
}
class DateTimeUtils {

10
pubspec.yaml

@ -4,16 +4,16 @@ version: 0.2.1
homepage: https://github.com/authpass/kdbx.dart
environment:
sdk: '>=2.4.0 <3.0.0'
sdk: '>=2.7.0 <3.0.0'
dependencies:
flutter:
sdk: flutter
# flutter:
# sdk: flutter
# path: ^1.6.0
logging: '>=0.11.3+2 <1.0.0'
crypto: '>=2.0.0 <3.0.0'
pointycastle: '>=1.0.1 <2.0.0'
xml: '>=3.5.0 <4.0.0'
xml: '>=3.7.0 <4.0.0'
uuid: '>=2.0.0 <3.0.0'
meta: '>=1.0.0 <2.0.0'
clock: '>=1.0.0 <2.0.0'
@ -27,8 +27,6 @@ dependencies:
logging_appenders: '>=0.1.0 <1.0.0'
dev_dependencies:
flutter_test:
sdk: flutter
pedantic: '>=1.7.0 <2.0.0'
test: '>=1.6.0 <2.0.0'

2
test/kdbx_test.dart

@ -37,7 +37,7 @@ void main() {
ProtectedValue.fromString('asdf'), keyFileBytes);
final data = await File('test/password-and-keyfile.kdbx').readAsBytes();
final file = KdbxFormat.read(data, cred);
expect(file.body.rootGroup.entries.length, 1);
expect(file.body.rootGroup.entries, hasLength(2));
});
});

Loading…
Cancel
Save