Herbert Poul
5 years ago
15 changed files with 344 additions and 96 deletions
@ -0,0 +1,42 @@ |
|||||||
|
import 'dart:convert'; |
||||||
|
import 'dart:io'; |
||||||
|
import 'dart:typed_data'; |
||||||
|
|
||||||
|
import 'package:kdbx/src/kdbx_header.dart'; |
||||||
|
import 'package:kdbx/src/kdbx_xml.dart'; |
||||||
|
import 'package:meta/meta.dart'; |
||||||
|
import 'package:xml/xml.dart'; |
||||||
|
|
||||||
|
class KdbxBinary { |
||||||
|
KdbxBinary({this.isInline, this.isProtected, this.value}); |
||||||
|
final bool isInline; |
||||||
|
final bool isProtected; |
||||||
|
final Uint8List value; |
||||||
|
|
||||||
|
static KdbxBinary readBinaryInnerHeader(InnerHeaderField field) { |
||||||
|
final flags = field.bytes[0]; |
||||||
|
final isProtected = flags & 0x01 == 0x01; |
||||||
|
final value = Uint8List.sublistView(field.bytes, 1); |
||||||
|
return KdbxBinary( |
||||||
|
isInline: false, |
||||||
|
isProtected: isProtected, |
||||||
|
value: value, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
static KdbxBinary readBinaryXml(XmlElement valueNode, |
||||||
|
{@required bool isInline}) { |
||||||
|
assert(isInline != null); |
||||||
|
final isProtected = valueNode.getAttributeBool(KdbxXml.ATTR_PROTECTED); |
||||||
|
final isCompressed = valueNode.getAttributeBool(KdbxXml.ATTR_COMPRESSED); |
||||||
|
var value = base64.decode(valueNode.text.trim()); |
||||||
|
if (isCompressed) { |
||||||
|
value = gzip.decode(value) as Uint8List; |
||||||
|
} |
||||||
|
return KdbxBinary( |
||||||
|
isInline: isInline, |
||||||
|
isProtected: isProtected, |
||||||
|
value: value, |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
//typedef HashStuff = Pointer<Utf8> Function(Pointer<Utf8> str); |
||||||
|
import 'dart:ffi'; |
||||||
|
import 'dart:io'; |
||||||
|
|
||||||
|
import 'package:ffi/ffi.dart'; |
||||||
|
import 'package:kdbx/kdbx.dart'; |
||||||
|
|
||||||
|
typedef Argon2HashNative = Pointer<Utf8> Function( |
||||||
|
Pointer<Uint8> key, |
||||||
|
IntPtr keyLen, |
||||||
|
Pointer<Uint8> salt, |
||||||
|
Uint64 saltlen, |
||||||
|
Uint32 m_cost, // memory cost |
||||||
|
Uint32 t_cost, // time cost (number iterations) |
||||||
|
Uint32 parallelism, |
||||||
|
IntPtr hashlen, |
||||||
|
Uint8 type, |
||||||
|
Uint32 version, |
||||||
|
); |
||||||
|
typedef Argon2Hash = Pointer<Utf8> Function( |
||||||
|
Pointer<Uint8> key, |
||||||
|
int keyLen, |
||||||
|
Pointer<Uint8> salt, |
||||||
|
int saltlen, |
||||||
|
int m_cost, // memory cost |
||||||
|
int t_cost, // time cost (number iterations) |
||||||
|
int parallelism, |
||||||
|
int hashlen, |
||||||
|
int type, |
||||||
|
int version, |
||||||
|
); |
||||||
|
|
||||||
|
class Argon2Test extends Argon2Base { |
||||||
|
Argon2Test() { |
||||||
|
final argon2lib = Platform.isMacOS |
||||||
|
? DynamicLibrary.open('libargon2_ffi.dylib') |
||||||
|
: DynamicLibrary.open('./libargon2_ffi.so'); |
||||||
|
argon2hash = argon2lib |
||||||
|
.lookup<NativeFunction<Argon2HashNative>>('hp_argon2_hash') |
||||||
|
.asFunction(); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Argon2Hash argon2hash; |
||||||
|
} |
||||||
|
|
||||||
|
class TestUtil { |
||||||
|
static Future<KdbxFile> readKdbxFile( |
||||||
|
String filePath, { |
||||||
|
String password = 'asdf', |
||||||
|
}) async { |
||||||
|
final kdbxFormat = KdbxFormat(Argon2Test()); |
||||||
|
final data = await File(filePath).readAsBytes(); |
||||||
|
final file = await kdbxFormat.read( |
||||||
|
data, Credentials(ProtectedValue.fromString(password))); |
||||||
|
return file; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,69 @@ |
|||||||
|
import 'dart:convert'; |
||||||
|
import 'dart:typed_data'; |
||||||
|
|
||||||
|
import 'package:kdbx/kdbx.dart'; |
||||||
|
import 'package:logging/logging.dart'; |
||||||
|
import 'package:logging_appenders/logging_appenders.dart'; |
||||||
|
import 'package:test/test.dart'; |
||||||
|
|
||||||
|
import 'internal/test_utils.dart'; |
||||||
|
|
||||||
|
void main() { |
||||||
|
Logger.root.level = Level.ALL; |
||||||
|
PrintAppender().attachToLogger(Logger.root); |
||||||
|
|
||||||
|
group('kdbx3 attachment', () { |
||||||
|
test('read binary', () async { |
||||||
|
final file = await TestUtil.readKdbxFile('test/keepass2binaries.kdbx'); |
||||||
|
final entry = file.body.rootGroup.entries.first; |
||||||
|
final binaries = entry.binaryEntries; |
||||||
|
expect(binaries, hasLength(3)); |
||||||
|
for (final binary in binaries) { |
||||||
|
switch (binary.key.key) { |
||||||
|
case 'example1.txt': |
||||||
|
expect(utf8.decode(binary.value.value), 'content1 example\n\n'); |
||||||
|
break; |
||||||
|
case 'example2.txt': |
||||||
|
expect(utf8.decode(binary.value.value), 'content2 example\n\n'); |
||||||
|
break; |
||||||
|
case 'keepasslogo.jpeg': |
||||||
|
expect(binary.value.value, hasLength(7092)); |
||||||
|
break; |
||||||
|
default: |
||||||
|
fail('invalid key. ${binary.key}'); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
}); |
||||||
|
group('kdbx4 attachment', () { |
||||||
|
test('read binary', () async { |
||||||
|
final file = |
||||||
|
await TestUtil.readKdbxFile('test/keepass2kdbx4binaries.kdbx'); |
||||||
|
void expectBinary(KdbxEntry entry, String key, dynamic matcher) { |
||||||
|
final binaries = entry.binaryEntries; |
||||||
|
expect(binaries, hasLength(1)); |
||||||
|
final binary = binaries.first; |
||||||
|
expect(binary.key.key, key); |
||||||
|
expect(binary.value.value, matcher); |
||||||
|
} |
||||||
|
|
||||||
|
expect(file.body.rootGroup.entries, hasLength(2)); |
||||||
|
expectBinary(file.body.rootGroup.entries.first, 'example2.txt', |
||||||
|
IsUtf8String('content2 example\n\n')); |
||||||
|
expectBinary(file.body.rootGroup.entries.last, 'keepasslogo.jpeg', |
||||||
|
hasLength(7092)); |
||||||
|
}); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
class IsUtf8String extends CustomMatcher { |
||||||
|
IsUtf8String(dynamic matcher) : super('is utf8 string', 'utf8', matcher); |
||||||
|
|
||||||
|
@override |
||||||
|
Object featureValueOf(dynamic actual) { |
||||||
|
if (actual is Uint8List) { |
||||||
|
return utf8.decode(actual); |
||||||
|
} |
||||||
|
return super.featureValueOf(actual); |
||||||
|
} |
||||||
|
} |
Binary file not shown.
Binary file not shown.
Loading…
Reference in new issue