Browse Source

fixed saving of protected values in history entries.

remove-cryptography-dependency
Herbert Poul 5 years ago
parent
commit
d914ff9024
  1. 9
      lib/src/crypto/protected_value.dart
  2. 35
      lib/src/kdbx_entry.dart
  3. 13
      lib/src/kdbx_format.dart
  4. 19
      lib/src/kdbx_object.dart

9
lib/src/crypto/protected_value.dart

@ -24,6 +24,11 @@ class PlainValue implements StringValue {
return 'PlainValue{text: $text}';
}
@override
bool operator ==(dynamic other) => other is PlainValue && other.text == text;
@override
int get hashCode => text.hashCode;
}
class ProtectedValue implements StringValue {
@ -64,6 +69,10 @@ class ProtectedValue implements StringValue {
return utf8.decode(binaryValue);
}
@override
bool operator ==(dynamic other) =>
other is ProtectedValue && other.getText() == getText();
@override
String toString() {
return 'ProtectedValue{${base64.encode(hash)}}';

35
lib/src/kdbx_entry.dart

@ -4,8 +4,11 @@ import 'package:kdbx/src/kdbx_format.dart';
import 'package:kdbx/src/kdbx_group.dart';
import 'package:kdbx/src/kdbx_object.dart';
import 'package:kdbx/src/kdbx_xml.dart';
import 'package:logging/logging.dart';
import 'package:xml/xml.dart';
final _logger = Logger('kdbx.kdbx_entry');
/// Represents a case insensitive (but case preserving) key.
class KdbxKey {
KdbxKey(this.key) : _canonicalKey = key.toLowerCase();
@ -22,11 +25,14 @@ class KdbxKey {
}
class KdbxEntry extends KdbxObject {
KdbxEntry.create(KdbxFile file, this.parent) : super.create(file, 'Entry') {
KdbxEntry.create(KdbxFile file, this.parent)
: isHistoryEntry = false,
super.create(file, 'Entry') {
icon.set(KdbxIcon.Key);
}
KdbxEntry.read(this.parent, XmlElement node) : super.read(node) {
KdbxEntry.read(this.parent, XmlElement node, {this.isHistoryEntry = false})
: super.read(node) {
_strings.addEntries(node.findElements(KdbxXml.NODE_STRING).map((el) {
final key = KdbxKey(el.findElements(KdbxXml.NODE_KEY).single.text);
final valueNode = el.findElements(KdbxXml.NODE_VALUE).single;
@ -39,14 +45,14 @@ class KdbxEntry extends KdbxObject {
}));
}
final bool isHistoryEntry;
List<KdbxEntry> _history;
List<KdbxEntry> get history =>
_history ??
(() {
List<KdbxEntry> get history => _history ??= (() {
return _historyElement
.findElements('Entry')
.map((entry) => KdbxEntry.read(parent, entry))
.map((entry) => KdbxEntry.read(parent, entry, isHistoryEntry: true))
.toList();
})();
@ -70,26 +76,33 @@ class KdbxEntry extends KdbxObject {
@override
XmlElement toXml() {
final el = super.toXml();
XmlUtils.removeChildrenByName(el, KdbxXml.NODE_STRING);
XmlUtils.removeChildrenByName(el, KdbxXml.NODE_HISTORY);
el.children.removeWhere(
(e) => e is XmlElement && e.name.local == KdbxXml.NODE_STRING);
el.children.addAll(stringEntries.map((stringEntry) {
final value = XmlElement(XmlName(KdbxXml.NODE_VALUE));
if (stringEntry.value is ProtectedValue) {
value.attributes
.add(XmlAttribute(XmlName(KdbxXml.ATTR_PROTECTED), 'true'));
.add(XmlAttribute(XmlName(KdbxXml.ATTR_PROTECTED), 'True'));
KdbxFile.setProtectedValueForNode(
value, stringEntry.value as ProtectedValue);
} else {
} else if (stringEntry.value is StringValue) {
value.children.add(XmlText(stringEntry.value.getText()));
}
return XmlElement(XmlName(KdbxXml.NODE_STRING))
..children.addAll([
XmlElement(XmlName(KdbxXml.ATTR_PROTECTED)),
XmlElement(XmlName(KdbxXml.NODE_KEY))
..children.add(XmlText(stringEntry.key.key)),
value,
]);
}));
if (!isHistoryEntry) {
el.children.add(
XmlElement(XmlName(KdbxXml.NODE_HISTORY))
..children.addAll(history.map((e) => e.toXml())),
);
}
return el;
}
@ -104,6 +117,10 @@ class KdbxEntry extends KdbxObject {
StringValue getString(KdbxKey key) => _strings[key];
void setString(KdbxKey key, StringValue value) {
if (_strings[key] == value) {
_logger.finest('Value did not change for $key');
return;
}
isDirty = true;
_strings[key] = value;
}

13
lib/src/kdbx_format.dart

@ -150,16 +150,21 @@ class KdbxBody extends KdbxNode {
xml.XmlDocument generateXml(ProtectedSaltGenerator saltGenerator) {
final rootGroupNode = rootGroup.toXml();
// update protected values...
for (final el in rootGroupNode
.findAllElements('Value')
.where((el) => el.getAttribute('Protected')?.toLowerCase() == 'true')) {
for (final el in rootGroupNode.findAllElements(KdbxXml.NODE_VALUE).where(
(el) =>
el.getAttribute(KdbxXml.ATTR_PROTECTED)?.toLowerCase() == 'true')) {
final pv = KdbxFile.protectedValues[el];
if (pv != null) {
final newValue = saltGenerator.encryptToBase64(pv.getText());
el.children.clear();
el.children.add(xml.XmlText(newValue));
} else {
_logger.warning('Unable to find protected value for $el ${el.parent}');
// assert((() {
// _logger.severe('Unable to find protected value for $el ${el.parent.parent} (children: ${el.children})');
// return false;
// })());
// this is always an error, not just during debug.
throw StateError('Unable to find protected value for $el ${el.parent}');
}
}

19
lib/src/kdbx_object.dart

@ -19,13 +19,16 @@ class ChangeEvent<T> {
mixin Changeable<T> {
final _controller = StreamController<ChangeEvent<T>>.broadcast();
Stream<ChangeEvent<T>> get changes => _controller.stream;
bool _isDirty = false;
set isDirty(bool dirty) {
_isDirty = dirty;
_controller.add(ChangeEvent(object: this as T, isDirty: dirty));
}
bool get isDirty => _isDirty;
}
@ -50,11 +53,14 @@ abstract class KdbxNode with Changeable<KdbxNode> {
abstract class KdbxObject extends KdbxNode {
KdbxObject.create(this.file, String nodeName)
: times = KdbxTimes.create(), super.create(nodeName) {
: times = KdbxTimes.create(),
super.create(nodeName) {
_uuid.set(KdbxUuid.random());
}
KdbxObject.read(XmlElement node) : times = KdbxTimes.read(node.findElements('Times').single),super.read(node);
KdbxObject.read(XmlElement node)
: times = KdbxTimes.read(node.findElements('Times').single),
super.read(node);
/// the file this object is part of. will be set AFTER loading, etc.
KdbxFile file;
@ -62,6 +68,7 @@ abstract class KdbxObject extends KdbxNode {
final KdbxTimes times;
KdbxUuid get uuid => _uuid.get();
UuidNode get _uuid => UuidNode(this, 'UUID');
IconNode get icon => IconNode(this, 'IconID');
@ -88,12 +95,12 @@ abstract class KdbxObject extends KdbxNode {
class KdbxUuid {
const KdbxUuid(this.uuid);
KdbxUuid.random() : this(base64.encode(uuidGenerator.parse(uuidGenerator.v4())));
static final Uuid uuidGenerator = Uuid(options: <String, dynamic>{
'grng': UuidUtil.cryptoRNG
});
KdbxUuid.random()
: this(base64.encode(uuidGenerator.parse(uuidGenerator.v4())));
static final Uuid uuidGenerator =
Uuid(options: <String, dynamic>{'grng': UuidUtil.cryptoRNG});
/// base64 representation of uuid.
final String uuid;

Loading…
Cancel
Save