Skip to content

Commit

Permalink
fix(example): encrypted seed import (#16)
Browse files Browse the repository at this point in the history
* example: show a snackbar for login exceptions

I'm assuming that auth state changes to a null user results in the error messages being cleared from the status bar

* fix encrypted mnemonic json parsing

* validate passwords - remove non-allowed characters

getMnemonic function fails upon registration if the password contains '<', '>', or '&'

---------

Co-authored-by: CharlVS <[email protected]>
  • Loading branch information
takenagain and CharlVS authored Dec 5, 2024
1 parent 63dbdbd commit 94b050b
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 39 deletions.
2 changes: 1 addition & 1 deletion packages/komodo_defi_framework/app_build/build_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"coins": {
"fetch_at_build_enabled": true,
"update_commit_on_build": true,
"bundled_coins_repo_commit": "5e12a42ea95f8f50667291c7adae881eef513bf0",
"bundled_coins_repo_commit": "d7906da4ab0283ea7dcd22d8b5157a8a46eac0f2",
"coins_repo_api_url": "https://api.github.com/repos/KomodoPlatform/coins",
"coins_repo_content_url": "https://komodoplatform.github.io/coins",
"coins_repo_branch": "master",
Expand Down
21 changes: 18 additions & 3 deletions packages/komodo_defi_sdk/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,20 @@ class _KomodoAppState extends State<KomodoApp> {
});
} on AuthException catch (e) {
setState(() {
_scaffoldKey.currentState?.showSnackBar(
SnackBar(
content: Text('Auth Error: (${e.type}) ${e.message}'),
),
);
_statusMessage = 'Auth Error: (${e.type}) ${e.message}';
});
} catch (e) {
setState(() {
_scaffoldKey.currentState?.showSnackBar(
SnackBar(
content: Text('An unexpected error occurred: $e'),
),
);
_statusMessage = 'An unexpected error occurred: $e';
});
}
Expand Down Expand Up @@ -254,11 +264,11 @@ class _KomodoAppState extends State<KomodoApp> {
TextFormField(
controller: _walletNameController,
decoration: const InputDecoration(labelText: 'Wallet Name'),
validator: notEmptyValidator,
validator: passwordValidator,
),
TextFormField(
controller: _passwordController,
validator: notEmptyValidator,
validator: passwordValidator,
decoration: InputDecoration(
labelText: 'Password',
suffixIcon: IconButton(
Expand Down Expand Up @@ -723,10 +733,15 @@ class _KomodoAppState extends State<KomodoApp> {
);
}

String? notEmptyValidator(String? input, {String? fieldName}) {
String? passwordValidator(String? input, {String? fieldName}) {
if (input == null || input.isEmpty) {
return 'Please enter a ${fieldName ?? 'value'}.';
}

if (input.contains(RegExp('[<>&]'))) {
return "Invalid password: contains '<', '>', or '&'";
}

return null;
}

Expand Down
56 changes: 38 additions & 18 deletions packages/komodo_defi_sdk/example/web/kdf/res/kdf_wrapper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
import 'dart:async';
// This is a web-specific file, so it's safe to ignore this warning
// ignore: avoid_web_libraries_in_flutter
import 'dart:js' as js;
import 'dart:js_interop';
import 'dart:js_interop_unsafe';

import 'package:flutter/services.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
import 'package:js/js_util.dart';
import 'package:web/web.dart';

class KdfPlugin {
static void registerWith(Registrar registrar) {
Expand Down Expand Up @@ -57,38 +58,57 @@ class KdfPlugin {

final completer = Completer<void>();

final script =
js.context['document'].callMethod('createElement', ['script']);
script['src'] = 'kdf/kdflib.js';
script['onload'] = js.allowInterop(() {
_libraryLoaded = true;
completer.complete();
});
script['onerror'] = js.allowInterop((event) {
completer.completeError('Failed to load kdflib.js');
});
final script = (document.createElement('script') as HTMLScriptElement)
..src = 'kdf/kdflib.js'
..onload = () {
_libraryLoaded = true;
completer.complete();
}.toJS
..onerror = (event) {
completer.completeError('Failed to load kdflib.js');
}.toJS;

js.context['document']['head'].callMethod('appendChild', [script]);
document.head!.appendChild(script);

return completer.future;
}

Future<int> _mm2Main(String conf, Function logCallback) async {
await _ensureLoaded();
return dartify(
js.context.callMethod('mm2_main', [conf, js.allowInterop(logCallback)]),
)! as int;

try {
final jsCallback = logCallback.toJS;
final jsResponse = globalContext.callMethod(
'mm2_main'.toJS,
[conf.toJS, jsCallback].toJS,
);
if (jsResponse == null) {
throw Exception('mm2_main call returned null');
}

final dynamic dartResponse = (jsResponse as JSAny?).dartify();
if (dartResponse == null) {
throw Exception('Failed to convert mm2_main response to Dart');
}

return dartResponse as int;
} catch (e) {
throw Exception('Error in mm2_main: $e\nConfig: $conf');
}
}

int _mm2MainStatus() {
if (!_libraryLoaded) {
throw StateError('KDF library not loaded. Call ensureLoaded() first.');
}
return js.context.callMethod('mm2_main_status') as int;

final jsResult = globalContext.callMethod('mm2_main_status'.toJS);
return jsResult.dartify()! as int;
}

Future<int> _mm2Stop() async {
await _ensureLoaded();
return js.context.callMethod('mm2_stop') as int;
final jsResult = globalContext.callMethod('mm2_stop'.toJS);
return jsResult.dartify()! as int;
}
}
36 changes: 19 additions & 17 deletions packages/komodo_defi_types/lib/src/cryptography/mnemonic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,18 +84,14 @@ class EncryptedMnemonicData {

// Factory method to create EncryptedMnemonicData from JSON
factory EncryptedMnemonicData.fromJson(Map<String, dynamic> json) {
final encryptedData =
json.valueOrNull<JsonMap>('encrypted_mnemonic_data') ??
(throw ArgumentError('Invalid encrypted mnemonic data'));

return EncryptedMnemonicData(
encryptionAlgorithm: encryptedData.value<String>('encryption_algorithm'),
encryptionAlgorithm: json.value<String>('encryption_algorithm'),
keyDerivationDetails: Argon2Details.fromJson(
encryptedData.value<JsonMap>('key_derivation_details'),
json.value<JsonMap>('key_derivation_details'),
),
iv: encryptedData.value<String>('iv'),
ciphertext: encryptedData.value<String>('ciphertext'),
tag: encryptedData.value<String>('tag'),
iv: json.value<String>('iv'),
ciphertext: json.value<String>('ciphertext'),
tag: json.value<String>('tag'),
);
}

Expand Down Expand Up @@ -160,15 +156,21 @@ class Argon2Details {

// Factory method to create Argon2Details from JSON
factory Argon2Details.fromJson(Map<String, dynamic> json) {
// final argon2Params = json.value<JsonMap>('Argon2', 'params');
var argon2Params = json;
var argon2Salt = json;
if (json['Argon2'] != null) {
argon2Params = json.value<JsonMap>('Argon2', 'params');
argon2Salt = json.value<JsonMap>('Argon2');
}

return Argon2Details(
algorithm: json.value<String>('algorithm'),
version: int.parse(json['version'].toString()),
mCost: json.value<int>('m_cost'),
tCost: json.value<int>('t_cost'),
pCost: json.value<int>('p_cost'),
saltAes: json.value<String>('salt_aes'),
saltHmac: json.value<String>('salt_hmac'),
algorithm: argon2Params.value<String>('algorithm'),
version: int.parse(argon2Params['version'].toString()),
mCost: argon2Params.value<int>('m_cost'),
tCost: argon2Params.value<int>('t_cost'),
pCost: argon2Params.value<int>('p_cost'),
saltAes: argon2Salt.value<String>('salt_aes'),
saltHmac: argon2Salt.value<String>('salt_hmac'),
);
}

Expand Down

0 comments on commit 94b050b

Please sign in to comment.