Herbert Poul
3 years ago
5 changed files with 126 additions and 114 deletions
@ -0,0 +1,43 @@ |
|||||||
|
import 'dart:typed_data'; |
||||||
|
|
||||||
|
import 'package:kdbx/src/credentials/keyfile.dart'; |
||||||
|
import 'package:kdbx/src/crypto/protected_value.dart'; |
||||||
|
import 'package:kdbx/src/internal/extension_utils.dart'; |
||||||
|
|
||||||
|
abstract class CredentialsPart { |
||||||
|
Uint8List getBinary(); |
||||||
|
} |
||||||
|
|
||||||
|
abstract class Credentials { |
||||||
|
factory Credentials(ProtectedValue password) => |
||||||
|
Credentials.composite(password, null); //PasswordCredentials(password); |
||||||
|
factory Credentials.composite(ProtectedValue? password, Uint8List? keyFile) => |
||||||
|
KeyFileComposite( |
||||||
|
password: password?.let((that) => PasswordCredentials(that)), |
||||||
|
keyFile: keyFile == null ? null : KeyFileCredentials(keyFile), |
||||||
|
); |
||||||
|
|
||||||
|
factory Credentials.fromHash(Uint8List hash) => HashCredentials(hash); |
||||||
|
|
||||||
|
Uint8List getHash(); |
||||||
|
} |
||||||
|
|
||||||
|
class PasswordCredentials implements CredentialsPart { |
||||||
|
PasswordCredentials(this._password); |
||||||
|
|
||||||
|
final ProtectedValue _password; |
||||||
|
|
||||||
|
@override |
||||||
|
Uint8List getBinary() { |
||||||
|
return _password.hash; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class HashCredentials implements Credentials { |
||||||
|
HashCredentials(this.hash); |
||||||
|
|
||||||
|
final Uint8List hash; |
||||||
|
|
||||||
|
@override |
||||||
|
Uint8List getHash() => hash; |
||||||
|
} |
@ -0,0 +1,77 @@ |
|||||||
|
import 'dart:convert'; |
||||||
|
import 'dart:typed_data'; |
||||||
|
|
||||||
|
import 'package:collection/collection.dart' show IterableExtension; |
||||||
|
import 'package:convert/convert.dart' as convert; |
||||||
|
import 'package:kdbx/src/credentials/credentials.dart'; |
||||||
|
import 'package:kdbx/src/crypto/protected_value.dart'; |
||||||
|
import 'package:xml/xml.dart' as xml; |
||||||
|
import 'package:crypto/crypto.dart' as crypto; |
||||||
|
|
||||||
|
import 'package:logging/logging.dart'; |
||||||
|
|
||||||
|
final _logger = Logger('keyfile'); |
||||||
|
|
||||||
|
class KeyFileCredentials implements CredentialsPart { |
||||||
|
factory KeyFileCredentials(Uint8List keyFileContents) { |
||||||
|
try { |
||||||
|
final keyFileAsString = utf8.decode(keyFileContents); |
||||||
|
if (_hexValuePattern.hasMatch(keyFileAsString)) { |
||||||
|
return KeyFileCredentials._(ProtectedValue.fromBinary( |
||||||
|
convert.hex.decode(keyFileAsString) as Uint8List)); |
||||||
|
} |
||||||
|
final xmlContent = xml.XmlDocument.parse(keyFileAsString); |
||||||
|
final metaVersion = |
||||||
|
xmlContent.findAllElements('Version').singleOrNull?.text; |
||||||
|
final key = xmlContent.findAllElements('Key').single; |
||||||
|
final dataString = key.findElements('Data').single; |
||||||
|
final encoded = dataString.text.replaceAll(RegExp(r'\s'), ''); |
||||||
|
Uint8List dataBytes; |
||||||
|
if (metaVersion != null && metaVersion.startsWith('2.')) { |
||||||
|
dataBytes = convert.hex.decode(encoded) as Uint8List; |
||||||
|
} else { |
||||||
|
dataBytes = base64.decode(encoded); |
||||||
|
} |
||||||
|
_logger.finer('Decoded base64 of keyfile.'); |
||||||
|
return KeyFileCredentials._(ProtectedValue.fromBinary(dataBytes)); |
||||||
|
} catch (e, stackTrace) { |
||||||
|
_logger.warning( |
||||||
|
'Unable to parse key file as hex or XML, use as is.', e, stackTrace); |
||||||
|
final bytes = crypto.sha256.convert(keyFileContents).bytes as Uint8List; |
||||||
|
return KeyFileCredentials._(ProtectedValue.fromBinary(bytes)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
KeyFileCredentials._(this._keyFileValue); |
||||||
|
|
||||||
|
static final RegExp _hexValuePattern = |
||||||
|
RegExp(r'^[a-f\d]{64}', caseSensitive: false); |
||||||
|
|
||||||
|
final ProtectedValue _keyFileValue; |
||||||
|
|
||||||
|
@override |
||||||
|
Uint8List getBinary() { |
||||||
|
return _keyFileValue.binaryValue; |
||||||
|
// return crypto.sha256.convert(_keyFileValue.binaryValue).bytes as Uint8List; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class KeyFileComposite implements Credentials { |
||||||
|
KeyFileComposite({required this.password, required this.keyFile}); |
||||||
|
|
||||||
|
PasswordCredentials? password; |
||||||
|
KeyFileCredentials? keyFile; |
||||||
|
|
||||||
|
@override |
||||||
|
Uint8List getHash() { |
||||||
|
final buffer = [...?password?.getBinary(), ...?keyFile?.getBinary()]; |
||||||
|
return crypto.sha256.convert(buffer).bytes as Uint8List; |
||||||
|
|
||||||
|
// final output = convert.AccumulatorSink<crypto.Digest>(); |
||||||
|
// final input = crypto.sha256.startChunkedConversion(output); |
||||||
|
//// input.add(password.getHash()); |
||||||
|
// input.add(buffer); |
||||||
|
// input.close(); |
||||||
|
// return output.events.single.bytes as Uint8List; |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue