Skip to content

Commit

Permalink
Merge pull request #14 from zenon-network/feature/normalize
Browse files Browse the repository at this point in the history
Generalize `KeyFile` to `EncryptedFile`
  • Loading branch information
alienc0der authored Feb 22, 2024
2 parents 5967f38 + 12f98c3 commit e888691
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 52 deletions.
4 changes: 2 additions & 2 deletions example/example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ Future<void> main() async {
var keyPair = keyStore.getKeyPair(0);
var privateKey = keyPair.getPrivateKey();
var publicKey = await keyPair.getPublicKey();
var address = await keyPair.address;
var address = await keyPair.getAddress();

print('Cryptography examples');
print('entropy: ${keyStore.entropy}');
print('private key: ${HEX.encode(privateKey!)}');
print('public key: ${HEX.encode(publicKey)}');
print('address: $address');
print('core bytes: ${HEX.encode(address!.core!)}\n');
print('core bytes: ${HEX.encode(address.core!)}\n');
print('Network examples');
print('chain and network identifier: ' + getChainIdentifier().toString());
}
6 changes: 6 additions & 0 deletions lib/src/wallet/constants.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Metadata
const String baseAddressKey = 'baseAddress';
const String walletTypeKey = 'walletType';

// KeyStore
const String keyStoreWalletType = 'keystore';
48 changes: 23 additions & 25 deletions lib/src/wallet/keyfile.dart → lib/src/wallet/encryptedfile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,21 @@ import 'package:argon2_ffi_base/argon2_ffi_base.dart';
import 'package:cryptography/cryptography.dart';
import 'package:cryptography/cryptography.dart' as cryptography;
import 'package:hex/hex.dart';
import 'package:znn_sdk_dart/src/argon2/argon2.dart';
import 'package:znn_sdk_dart/src/model/primitives.dart';
import 'package:znn_sdk_dart/src/wallet/exceptions.dart';
import 'package:znn_sdk_dart/src/wallet/keystore.dart';
import 'package:znn_sdk_dart/znn_sdk_dart.dart';

class KeyFile {
Address? baseAddress;
class EncryptedFile {
Map<String, dynamic>? metadata;
_Crypto? crypto;
int? timestamp;
int? version;

KeyFile({this.baseAddress, this.crypto, this.timestamp, this.version});
EncryptedFile({this.metadata, this.crypto, this.timestamp, this.version});

static Future<KeyFile> encrypt(KeyStore store, String password) async {
static Future<EncryptedFile> encrypt(List<int> data, String password,
{Map<String, dynamic>? metadata}) async {
var timestamp = ((DateTime.now()).millisecondsSinceEpoch / 1000).round();

var stored = KeyFile(
baseAddress: await store.getKeyPair().address,
var stored = EncryptedFile(
metadata: metadata,
timestamp: timestamp,
version: 1,
crypto: _Crypto(
Expand All @@ -31,10 +28,10 @@ class KeyFile {
cipherName: 'aes-256-gcm',
kdf: 'argon2.IDKey',
nonce: Uint8List(0)));
return stored._encryptEntropy(store, password);
return stored._encryptData(data, password);
}

Future<KeyStore> decrypt(String password) async {
Future<List<int>> decrypt(String password) async {
try {
var key = initArgon2().argon2(Argon2Arguments(
Uint8List.fromList(utf8.encode(password)),
Expand All @@ -46,7 +43,7 @@ class KeyFile {
2,
13));
final algorithm = cryptography.AesGcm.with256bits();
var entropy = await algorithm.decrypt(
var data = await algorithm.decrypt(
cryptography.SecretBox(
crypto!.cipherData!.sublist(0, crypto!.cipherData!.length - 16),
nonce: crypto!.nonce!,
Expand All @@ -56,24 +53,25 @@ class KeyFile {
secretKey: cryptography.SecretKey(key),
aad: utf8.encode('zenon'));

return KeyStore.fromEntropy(HEX.encode(entropy));
return data;
} on SecretBoxAuthenticationError {
throw IncorrectPasswordException();
} catch (e) {
rethrow;
}
}

KeyFile.fromJson(Map<String, dynamic> json) {
baseAddress = Address.parse(json['baseAddress']);
crypto = json['crypto'] != null ? _Crypto.fromJson(json['crypto']) : null;
timestamp = json['timestamp'];
version = json['version'];
EncryptedFile.fromJson(Map<String, dynamic> json) {
final data = {...json};
crypto =
json['crypto'] != null ? _Crypto.fromJson(data.remove('crypto')) : null;
timestamp = data.remove('timestamp');
version = data.remove('version');
metadata = data.isNotEmpty ? {...data} : null;
}

Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data['baseAddress'] = baseAddress.toString();
final data = metadata != null ? {...metadata!} : <String, dynamic>{};
if (crypto != null) {
data['crypto'] = crypto!.toJson();
}
Expand All @@ -87,7 +85,7 @@ class KeyFile {
return toJson().toString();
}

Future<KeyFile> _encryptEntropy(KeyStore store, String password) async {
Future<EncryptedFile> _encryptData(List<int> data, String password) async {
var salt_1 = await cryptography.SecretKeyData.random(length: 16).extract();
var salt = Uint8List.fromList(salt_1.bytes);
var nonce_1 = await cryptography.SecretKeyData.random(length: 12).extract();
Expand All @@ -103,7 +101,7 @@ class KeyFile {
13));

final algorithm = cryptography.AesGcm.with256bits();
var encrypted = await algorithm.encrypt(HEX.decode(store.entropy),
var encrypted = await algorithm.encrypt(data,
secretKey: cryptography.SecretKey(key),
nonce: nonce,
aad: utf8.encode('zenon'));
Expand All @@ -120,7 +118,7 @@ Uint8List _fromHexString(String s) {
}

String _toHexString(Uint8List l) {
return '0x' + HEX.encode(l);
return '0x${HEX.encode(l)}';
}

class _Crypto {
Expand Down
4 changes: 2 additions & 2 deletions lib/src/wallet/exceptions.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import 'package:znn_sdk_dart/src/global.dart';

class InvalidKeyStorePath implements Exception {
class WalletException implements Exception {
String message;

InvalidKeyStorePath(this.message);
WalletException(this.message);

@override
String toString() {
Expand Down
2 changes: 1 addition & 1 deletion lib/src/wallet/keypair.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import 'package:znn_sdk_dart/src/crypto/crypto.dart';
import 'package:znn_sdk_dart/src/model/primitives.dart';
import 'package:znn_sdk_dart/src/wallet/interfaces.dart';
import 'package:znn_sdk_dart/src/model/nom/account_block_template.dart';
import 'package:znn_sdk_dart/src/utils/block.dart';

class KeyPair implements WalletAccount {
List<int>? privateKey;
List<int>? publicKey;
Expand Down
2 changes: 1 addition & 1 deletion lib/src/wallet/keystore.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class KeyStoreDefinition implements WalletDefinition {

KeyStoreDefinition({required this.file}) {
if (!file.existsSync()) {
throw InvalidKeyStorePath('Given keyStore does not exist ($file)');
throw WalletException('Given keyStore does not exist ($file)');
}
}

Expand Down
39 changes: 20 additions & 19 deletions lib/src/wallet/manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import 'dart:convert';
import 'dart:io';
import 'dart:isolate';

import 'package:hex/hex.dart';
import 'package:path/path.dart' as path;
import 'package:znn_sdk_dart/src/global.dart';
import 'package:znn_sdk_dart/src/wallet/constants.dart';
import 'package:znn_sdk_dart/src/wallet/exceptions.dart';
import 'package:znn_sdk_dart/src/wallet/keyfile.dart';
import 'package:znn_sdk_dart/src/wallet/keystore.dart';
import 'package:znn_sdk_dart/src/wallet/interfaces.dart';
import 'package:znn_sdk_dart/src/wallet/encryptedfile.dart';

class KeyStoreOptions implements WalletOptions {
final String decryptionPassword;
Expand All @@ -25,13 +27,17 @@ class SaveKeyStoreArguments {
}

void saveKeyStoreFunction(SaveKeyStoreArguments args) async {
var encrypted = await KeyFile.encrypt(args.store, args.password);
var baseAddress = await (await args.store.getAccount()).getAddress();
var encrypted = await EncryptedFile.encrypt(
HEX.decode(args.store.entropy), args.password, metadata: {
baseAddressKey: baseAddress.toString(),
walletTypeKey: keyStoreWalletType
});
args.port.send(json.encode(encrypted));
}

class KeyStoreManager implements WalletManager {
final Directory walletPath;
KeyStore? keyStoreInUse;

KeyStoreManager({required this.walletPath});

Expand Down Expand Up @@ -69,25 +75,20 @@ class KeyStoreManager implements WalletManager {
return completer.future;
}

void setKeyStore(KeyStore keyStore) {
keyStoreInUse = keyStore;
}

String? getMnemonicInUse() {
if (keyStoreInUse == null) {
throw ArgumentError('The keyStore in use is null');
}
return keyStoreInUse!.mnemonic;
}

Future<KeyStore> readKeyStore(String password, File keyStoreFile) async {
if (!keyStoreFile.existsSync()) {
throw InvalidKeyStorePath(
'Given keyStore does not exist ($keyStoreFile)');
throw WalletException('Given keyStore does not exist ($keyStoreFile)');
}

var content = await keyStoreFile.readAsString();
return KeyFile.fromJson(json.decode(content)).decrypt(password);
var file = EncryptedFile.fromJson(json.decode(content));
if (file.metadata != null &&
file.metadata![walletTypeKey] != null &&
file.metadata![walletTypeKey] != keyStoreWalletType) {
throw WalletException(
'Wallet type (${file.metadata![walletTypeKey]}) is not supported');
}
var seed = await file.decrypt(password);
return KeyStore.fromEntropy(HEX.encode(seed));
}

Future<KeyStoreDefinition?> findKeyStore(String name) async {
Expand All @@ -96,7 +97,7 @@ class KeyStoreManager implements WalletManager {
if (file is File) {
return KeyStoreDefinition(file: file);
} else {
throw InvalidKeyStorePath('Given keyStore is not a file ($name)');
throw WalletException('Given keyStore is not a file ($name)');
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion lib/src/wallet/wallet.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export 'constants.dart';
export 'derivation.dart';
export 'encryptedfile.dart';
export 'exceptions.dart';
export 'keyfile.dart';
export 'keypair.dart';
export 'keystore.dart';
export 'manager.dart';
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: znn_sdk_dart
description: Zenon SDK for Dart and Flutter
version: 0.0.6
version: 0.0.7

environment:
sdk: '>=2.14.0 <3.0.0'
Expand Down
57 changes: 57 additions & 0 deletions test/wallet/encryptedfile_metadata_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import 'dart:convert';
import 'package:test/test.dart';
import 'package:znn_sdk_dart/znn_sdk_dart.dart';

void main() async {
var keyStoreString = '''
{
"crypto": {
"argon2Params": {
"salt": "0x5b85100f186953332faddeaf2b6d68de"
},
"cipherData": "0xcfe2e1aa498229aaf78ab9634592a19c1f45ad5178ed4d530fdeef8fe528e4b78955a389e0d1e832fd0c21346b3a45cd",
"cipherName": "aes-256-gcm",
"kdf": "argon2.IDKey",
"nonce": "0x86d6b27ba67a72238af12958"
},
"timestamp": 1707418422,
"version": 1
}
''';
var keyStoreStringWithMeta = '''
{
"baseAddress": "z1qqjnwjjpnue8xmmpanz6csze6tcmtzzdtfsww7",
"walletType": "keystore",
"crypto": {
"argon2Params": {
"salt": "0x5b85100f186953332faddeaf2b6d68de"
},
"cipherData": "0xcfe2e1aa498229aaf78ab9634592a19c1f45ad5178ed4d530fdeef8fe528e4b78955a389e0d1e832fd0c21346b3a45cd",
"cipherName": "aes-256-gcm",
"kdf": "argon2.IDKey",
"nonce": "0x86d6b27ba67a72238af12958"
},
"timestamp": 1707418422,
"version": 1
}
''';
var encryptedFile = EncryptedFile.fromJson(jsonDecode(keyStoreString));
test('same json', () {
expect(encryptedFile.toJson(), jsonDecode(keyStoreString));
});
test('same metadata', () {
expect(encryptedFile.metadata, null);
});

var encryptedFileWithMeta =
EncryptedFile.fromJson(jsonDecode(keyStoreStringWithMeta));
test('same json', () {
expect(encryptedFileWithMeta.toJson(), jsonDecode(keyStoreStringWithMeta));
});
test('same metadata', () {
expect(encryptedFileWithMeta.metadata, {
"baseAddress": "z1qqjnwjjpnue8xmmpanz6csze6tcmtzzdtfsww7",
"walletType": "keystore",
});
});
}

0 comments on commit e888691

Please sign in to comment.