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