diff --git a/lib/src/kdbx_format.dart b/lib/src/kdbx_format.dart index caf37db..0e926e2 100644 --- a/lib/src/kdbx_format.dart +++ b/lib/src/kdbx_format.dart @@ -5,6 +5,7 @@ import 'dart:typed_data'; import 'package:convert/convert.dart' as convert; import 'package:crypto/crypto.dart' as crypto; +import 'package:cryptography/cryptography.dart' as cryptography; import 'package:kdbx/kdbx.dart'; import 'package:kdbx/src/crypto/argon2.dart'; import 'package:kdbx/src/crypto/key_encrypter_kdf.dart'; @@ -285,8 +286,8 @@ class KdbxBody extends KdbxNode { return result; } else if (cipherId == CryptoConsts.CIPHER_IDS[Cipher.chaCha20].uuid) { _logger.fine('We need chacha20'); - // TODO can we combine this with _encryptV3? - throw UnsupportedError('Unsupported cipher chacha20 for kdbx 4.x'); + return kdbxFile.kdbxFormat + .transformContentV4ChaCha20(header, compressedBytes, cipherKey); } else { throw UnsupportedError('Unsupported cipherId $cipherId'); } @@ -509,12 +510,21 @@ class KdbxFormat { return result; } else if (cipherId == CryptoConsts.CIPHER_IDS[Cipher.chaCha20].uuid) { _logger.fine('We need chacha20'); - throw UnsupportedError('chacha20 not yet supported $cipherId'); +// throw UnsupportedError('chacha20 not yet supported $cipherId'); + return transformContentV4ChaCha20(header, encrypted, cipherKey); } else { throw UnsupportedError('Unsupported cipherId $cipherId'); } } + Uint8List transformContentV4ChaCha20( + KdbxHeader header, Uint8List encrypted, Uint8List cipherKey) { + final encryptionIv = header.fields[HeaderFields.EncryptionIV].bytes; + final key = cryptography.SecretKey(cipherKey); + final nonce = cryptography.SecretKey(encryptionIv); + return cryptography.chacha20.decrypt(encrypted, key, nonce: nonce); + } + // Uint8List _transformDataV4Aes() { // } diff --git a/test/chacha20.kdbx b/test/chacha20.kdbx new file mode 100644 index 0000000..bc488fa Binary files /dev/null and b/test/chacha20.kdbx differ diff --git a/test/kdbx4_test.dart b/test/kdbx4_test.dart index c8b15bb..e89db92 100644 --- a/test/kdbx4_test.dart +++ b/test/kdbx4_test.dart @@ -88,6 +88,12 @@ void main() { Credentials.composite(ProtectedValue.fromString('asdf'), keyFile)); expect(file.body.rootGroup.entries, hasLength(1)); }); + test('Reading chacha20', () async { + final data = await File('test/chacha20.kdbx').readAsBytes(); + final file = + kdbxFormat.read(data, Credentials(ProtectedValue.fromString('asdf'))); + expect(file.body.rootGroup.entries, hasLength(1)); + }); }); group('Writing', () { test('Create and save', () { @@ -98,22 +104,8 @@ void main() { header: KdbxHeader.createV4(), ); final rootGroup = kdbx.body.rootGroup; - { - final entry = KdbxEntry.create(kdbx, rootGroup); - rootGroup.addEntry(entry); - entry.setString(KdbxKey('Username'), PlainValue('user1')); - entry.setString( - KdbxKey('Password'), ProtectedValue.fromString('LoremIpsum')); - } - { - final entry = KdbxEntry.create(kdbx, rootGroup); - rootGroup.addEntry(entry); - entry.setString(KdbxKey('Username'), PlainValue('user2')); - entry.setString( - KdbxKey('Password'), - ProtectedValue.fromString('Second Password'), - ); - } + _createEntry(kdbx, rootGroup, 'user1', 'LoremIpsum'); + _createEntry(kdbx, rootGroup, 'user2', 'Second Password'); final saved = kdbx.save(); final loadedKdbx = kdbxFormat.read( @@ -126,5 +118,26 @@ void main() { final file = kdbxFormat.read(data, Credentials(ProtectedValue.fromString('asdf'))); }); + test('write chacha20', () async { + final data = await File('test/chacha20.kdbx').readAsBytes(); + final file = + kdbxFormat.read(data, Credentials(ProtectedValue.fromString('asdf'))); + expect(file.body.rootGroup.entries, hasLength(1)); + _createEntry(file, file.body.rootGroup, 'user1', 'LoremIpsum'); + + // and try to write it. + final output = file.save(); + expect(output, isNotNull); + File('test_output_chacha20.kdbx').writeAsBytesSync(output); + }); }); } + +KdbxEntry _createEntry( + KdbxFile file, KdbxGroup group, String username, String password) { + final entry = KdbxEntry.create(file, group); + group.addEntry(entry); + entry.setString(KdbxKey('UserName'), PlainValue(username)); + entry.setString(KdbxKey('Password'), ProtectedValue.fromString(password)); + return entry; +} diff --git a/test_output_chacha20.kdbx b/test_output_chacha20.kdbx new file mode 100644 index 0000000..4997401 Binary files /dev/null and b/test_output_chacha20.kdbx differ