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