From 158169af278c42de93628e060cdadf693bd02adf Mon Sep 17 00:00:00 2001 From: Niko PLP Date: Sat, 25 Oct 2025 15:25:12 +0300 Subject: [PATCH] switching to password based login --- app/ui-common/package.json | 2 +- app/ui-common/src/api.ts | 4 +- app/ui-common/src/lib/Login.svelte | 22 +- .../src/lib/components/PasswordInput.svelte | 10 +- app/ui-common/src/locales/en.json | 18 +- app/ui-common/src/routes/WalletCreate.svelte | 1718 ++--------------- app/ui-common/src/routes/WalletLogin.svelte | 153 +- engine/broker/auth/package.json | 2 +- engine/wallet/src/lib.rs | 326 +++- engine/wallet/src/types.rs | 59 +- infra/ngaccount/web/src/routes/Create.svelte | 4 +- infra/ngnet/bootstrap/package.json | 2 +- infra/ngnet/redir/package.json | 2 +- pnpm-lock.yaml | 24 +- sdk/js/lib-wasm/src/lib.rs | 22 +- sdk/rust/src/local_broker.rs | 37 +- 16 files changed, 601 insertions(+), 1804 deletions(-) diff --git a/app/ui-common/package.json b/app/ui-common/package.json index 94d9e9a0..4ce0adbc 100644 --- a/app/ui-common/package.json +++ b/app/ui-common/package.json @@ -55,7 +55,7 @@ "shx": "^0.3.4", "svelte": "^3.54.0", "svelte-check": "^3.0.0", - "svelte-heros-v2": "^0.10.12", + "svelte-heros-v2": "^1.3.0", "svelte-preprocess": "^5.0.3", "svelte-time": "^0.8.0", "tailwindcss": "^3.3.1", diff --git a/app/ui-common/src/api.ts b/app/ui-common/src/api.ts index 283455a5..4c32cc62 100644 --- a/app/ui-common/src/api.ts +++ b/app/ui-common/src/api.ts @@ -25,12 +25,12 @@ export default api; export const NG_EU_BSP = "https://nextgraph.eu"; export const NG_EU_BSP_REGISTER = import.meta.env.PROD -? "https://account.nextgraph.eu/#/create" +? import.meta.env.NG_ENV_ALT ? "https://pnm.allelo.eco" : "https://account.nextgraph.eu/#/create" : "http://account-dev.nextgraph.eu:5173/#/create"; export const NG_ONE_BSP = "https://nextgraph.one"; export const NG_ONE_BSP_REGISTER = import.meta.env.PROD -? "https://account.nextgraph.one/#/create" +? import.meta.env.NG_ENV_ALT ? "https://account.allelo.eco/#/create" : "https://account.nextgraph.one/#/create" : "http://account-dev.nextgraph.one:5173/#/create"; export const APP_ACCOUNT_REGISTERED_SUFFIX = "/#/user/registered"; diff --git a/app/ui-common/src/lib/Login.svelte b/app/ui-common/src/lib/Login.svelte index bcc45cfa..fd4be2e7 100644 --- a/app/ui-common/src/lib/Login.svelte +++ b/app/ui-common/src/lib/Login.svelte @@ -54,7 +54,6 @@ load_svg(); //console.log(wallet); await init(); - }); async function init() { @@ -256,7 +255,11 @@ } } catch (e) { console.error(e); - if (e.message && e.message.includes("constructor") || (typeof e === "string" && e.includes("constructor") )) e = "BrowserTooOld"; + if ( + (e.message && e.message.includes("constructor")) || + (typeof e === "string" && e.includes("constructor")) + ) + e = "BrowserTooOld"; error = e; step = "end"; dispatch("error", { error: e }); @@ -272,7 +275,10 @@ async function on_pin_key(val) { pin_code = [...pin_code, val]; if (pin_code.length == 4) { - setTimeout(()=>window.document.getElementById("confirm_pin_btn").focus(),50); + setTimeout( + () => window.document.getElementById("confirm_pin_btn").focus(), + 50 + ); } } @@ -590,7 +596,10 @@ class:h-[160px]={!mobile} class:h-[93px]={mobile} class:text-8xl={!mobile} - on:click={async () => {window.document.activeElement.blur(); await on_pin_key(num)}} + on:click={async () => { + window.document.activeElement.blur(); + await on_pin_key(num); + }} disabled={pin_code.length >= 4} > {num} @@ -606,7 +615,10 @@ class:h-[160px]={!mobile} class:h-[93px]={mobile} class:text-8xl={!mobile} - on:click={async () => {window.document.activeElement.blur();await on_pin_key(shuffle_pin[9])}} + on:click={async () => { + window.document.activeElement.blur(); + await on_pin_key(shuffle_pin[9]); + }} disabled={pin_code.length >= 4} > {shuffle_pin[9]} diff --git a/app/ui-common/src/lib/components/PasswordInput.svelte b/app/ui-common/src/lib/components/PasswordInput.svelte index 1470f57c..4b172235 100644 --- a/app/ui-common/src/lib/components/PasswordInput.svelte +++ b/app/ui-common/src/lib/components/PasswordInput.svelte @@ -18,8 +18,8 @@ export let auto_complete: string | undefined = undefined; import { createEventDispatcher } from "svelte"; export let show: boolean = false; - - let input; + export let autofocus = false; + export let input = undefined; let type: "password" | "text" = "password"; $: type = show ? "text" : "password"; @@ -44,7 +44,7 @@ if (e.key == "Enter" || e.keyCode == 13) { dispatch("enter"); } - } + };
@@ -55,7 +55,7 @@ {placeholder} {id} {type} - autofocus + {autofocus} on:input={handleInput} class={`${className} pr-12 text-md block`} autocomplete={auto_complete} @@ -63,7 +63,7 @@ />
{category}", "order_emojis": "Select each image in the correct order", "enter_pin": "Enter your PIN code", @@ -439,7 +441,13 @@ "self_hosted_broker": "Self-hosted broker", "ng_box": "NG Box (owned or invited)", "install_app": "Install the app", - "registration_success": "You have been successfully registered to {broker}", + "registration_success": "You are creating an account at {broker}", + "choose_username": { + "title": "Now choose your username and password", + "warning": "Please note that we do not offer password recovery mechanism for now.
We won't be able to help you if you forget your password." + }, + "type_username_placeholder": "Type in your username", + "type_password_placeholder": "Type in your password", "choose_pin": { "title": "Let's start creating your wallet by choosing a PIN code", "description": "We recommend you to choose a PIN code that you already know very well.
We at NextGraph will never see your PIN.", @@ -591,7 +599,7 @@ "InvalidSignature": "The signature is invalid.", "IncompleteSignature": "The signature is incomplete.", "SerializationError": "The data could not be serialized.", - "EncryptionError": "Your wallet could not be opened. You probably did a mistake.", + "EncryptionError": "Your wallet could not be opened. You probably did a mistake when entering your credentials.", "DecryptionError": "Error with decryption.", "InvalidValue": "The value is invalid.", "ConnectionNotFound": "The connection was not found.", @@ -659,9 +667,13 @@ "WsError": "WebSocket error", "cannot_load_this_file": "Cannot load this file", "graph_not_found": "Graph not found", + "SocialQueryAlreadyStarted": "Social Query already started", + "ContactAlreadyExists": "Contact already added to your account", + "ContactNotFound": "You don't have any contact. We cannot start the Social Query", + "InvalidProfile": "Your profile is incomplete. You should add a name before you can share your profile with others", "no_wasm_on_old_safari": "Your Safari browser is too old (version before 14.1). As a result we cannot load Automerge, needed for this document. Please upgrade your macOS or iOS system", "BrowserTooOld": "Your browser is too old. Please upgrade it, use another browser, or install our native app. If you are using jshelter or another javascript protection mechanism, please deactivate it as we need access to the WebWorker facility of your browser.", - "NoLocalStorage": "You have disabled local storage in your browser. Please allow the current website (and https://nextgraph.net website) to store data in this browser as otherwise we cannot proceed with Wallet creation. After allowing storage, please refresh the current page." + "NoLocalStorage": "You have disabled local storage in your browser. Please allow the current website (and also https://nextgraph.net website) to store data in this browser as otherwise we cannot proceed with Wallet creation. After allowing storage, please refresh the current page." }, "auth":{ "select_broker":"{origin}
wants to access your wallet

Please select your broker in the list below:", diff --git a/app/ui-common/src/routes/WalletCreate.svelte b/app/ui-common/src/routes/WalletCreate.svelte index 0db683ee..daae9f77 100644 --- a/app/ui-common/src/routes/WalletCreate.svelte +++ b/app/ui-common/src/routes/WalletCreate.svelte @@ -18,191 +18,80 @@ --> - -
{#if wait} @@ -637,634 +374,7 @@ {/if}
- {:else if intro} -
-

- {@html $t("pages.wallet_create.wallet_description")} -

-
- {#if $has_wallets} - - {@html $t("pages.wallet_create.has_wallet")} - - {/if} -
-
-

- {$t("pages.wallet_create.wallet_about.title")}  - {$t("pages.wallet_create.please_read")} -

-
    -
  • - - - {@html $t("pages.wallet_create.wallet_about.1")} -
  • -
  • - - - {@html $t("pages.wallet_create.wallet_about.2")} -
  • -
  • - - {@html $t("pages.wallet_create.wallet_about.password")} -
  • -
  • - - {@html $t("pages.wallet_create.wallet_about.mnemonic")} -
  • -
  • - - - {@html $t("pages.wallet_create.wallet_about.pazzle")} -
  • -
  • - - - - {@html $t("pages.wallet_create.wallet_about.3")} -
  • - -
  • - - - {@html $t("pages.wallet_create.wallet_about.5")} -
  • -
  • - - - {@html $t("pages.wallet_create.wallet_about.6")} -
  • -
  • - - - {@html $t("pages.wallet_create.wallet_about.7")} -
  • -
  • - - - {@html $t("pages.wallet_create.wallet_about.8")} -
  • -
  • - - {@html $t("pages.wallet_create.wallet_about.9")} -
  • -
-
-
-
- -
- {:else if !invitation} -
-

- {@html $t("pages.wallet_create.select_server")} -

-
-
-
-

- {$t("pages.wallet_create.broker_about.title")} {@html $t( - "pages.wallet_create.please_read" - )} -

-
    -
  • - - {@html $t("pages.wallet_create.broker_about.1")} -
  • -
  • - - {@html $t("pages.wallet_create.broker_about.2")} -
  • -
  • - - - {@html $t("pages.wallet_create.broker_about.4")} -
  • -
  • - - {@html $t("pages.wallet_create.broker_about.5")} -
  • - -
  • - - - {@html $t("pages.wallet_create.broker_about.7")} -
  • -
-

- {$t("pages.wallet_create.choose_broker")} -

-
-
- {#if pre_invitation} -
- -
- {:else} -
- -
- - - {/if} - -
- -
- {#if false && mobile} -
- -
- {/if} - - {#if !tauri_platform} - -
- - - - {@html $t("pages.wallet_create.broker_about.8")} - -
- - {/if} - - {:else if pin.length < 4} + {:else if !username_pass_ok}
{#if registration_success} @@ -1277,406 +387,38 @@ {/if}

{$t("pages.wallet_create.choose_pin.title")}{$t("pages.wallet_create.choose_username.title")} - {@html $t("pages.wallet_create.choose_pin.description")} + {@html $t("pages.wallet_create.choose_username.warning")}

-

- {$t("pages.wallet_create.choose_pin.rules")} -

-
    -
  • {@html $t("pages.wallet_create.choose_pin.1")}
  • -
  • - {@html $t("pages.wallet_create.choose_pin.2")} -
  • -
  • - {@html $t("pages.wallet_create.choose_pin.3")} -
  • -
- - - {$t("pages.wallet_create.chosen_pin")} - {#each pin as digit}{digit}{/each} - -
- {#each [0, 1, 2] as row} -
- {#each [1, 2, 3] as num} - - {/each} -
- {/each} - -
-
- {:else if pin_confirm.length < 4} - {#if !validate_pin()} - - {$t("pages.wallet_create.pin_invalid")} - - - {:else} -
-

- {$t("pages.wallet_create.confirm_pin")} - {$t("pages.wallet_create.confirm_pin_description")} -

- - {$t("pages.wallet_create.chosen_pin")}: {#each pin_confirm as digit}{digit}{/each} - -
- {#each [0, 1, 2] as row} -
- {#each [1, 2, 3] as num} - - {/each} -
- {/each} - -
-
- {/if} - {:else if !options} -
- {#if pin.toString() === pin_confirm.toString()} - - {$t("pages.wallet_create.pin_confirmed_as")} - {#each pin_confirm as digit}{digit}{/each} - -

- {@html $t( - "pages.wallet_create.choose_security_phrase_and_image.title" - )} -

-

- {@html $t( - "pages.wallet_create.choose_security_phrase_and_image.description" - )} - - {@html $t( - "pages.wallet_create.choose_security_phrase_and_image.warning" - )} - -

-

- {$t( - "pages.wallet_create.choose_security_phrase_and_image.rules_title" - )} -

-
    -
  • - {@html $t( - "pages.wallet_create.choose_security_phrase_and_image.1" - )} -
  • -
  • - {@html $t( - "pages.wallet_create.choose_security_phrase_and_image.2" - )} -
  • -
  • - {@html $t( - "pages.wallet_create.choose_security_phrase_and_image.3" - )} -
  • -
  • - {@html $t( - "pages.wallet_create.choose_security_phrase_and_image.4" - )} -
  • -
  • - {@html $t( - "pages.wallet_create.choose_security_phrase_and_image.5" - )} -
  • -
  • - {@html $t( - "pages.wallet_create.choose_security_phrase_and_image.6" - )} -
  • -
  • - {@html $t( - "pages.wallet_create.choose_security_phrase_and_image.7" - )} -
  • -
  • - {@html $t( - "pages.wallet_create.choose_security_phrase_and_image.8" - )} -
  • -
  • - {@html $t( - "pages.wallet_create.choose_security_phrase_and_image.9" - )} -
  • -
  • - {@html $t( - "pages.wallet_create.choose_security_phrase_and_image.10" - )} -
  • -
- -
- {#if security_txt && security_img} - - {/if} - {#if security_phrase_error} - - {@html $t( - "pages.wallet_create.security_phrase_invalid" - )} - - {/if} - { - event.preventDefault(); - }} - on:change={handleChange} - > -

- {#if is_touch_device} - {$t("pages.wallet_create.tap_to_upload")} - {:else} - {$t("pages.wallet_create.click_to_upload")} - {$t("pages.wallet_create.or_drag_drop")} - {/if} -

- -
- your security - {:else} - - {$t("pages.wallet_create.pins_no_match")} - - - {/if} -
- {:else if !creating} -
-

- {$t("pages.wallet_create.almost_done")}
- {@html $t("pages.wallet_create.save_wallet_options.description")} -

-

- {@html $t( - "pages.wallet_create.save_wallet_options.trust" - )}
- {@html $t( - "pages.wallet_create.save_wallet_options.trust_description" - )} - {#if !tauri_platform}{@html $t( - "pages.login.trust_device_allow_cookies" - )}{/if}

- {if (!options.trusted) options.pdf=false;}} - >{@html $t( - "pages.wallet_create.save_wallet_options.trust_toggle" - )} -

- - {#if options.trusted} -
-

- {@html $t( - "pages.wallet_create.save_wallet_options.device_name_description" - )} -

- - {/if} - -

- {@html $t("pages.wallet_create.save_wallet_options.pdf")} -
- {@html $t( - "pages.wallet_create.save_wallet_options.pdf_description" - )} -

- {@html $t( - "pages.wallet_create.save_wallet_options.pdf_toggle" - )} -

- - + {@html $t("pages.wallet_create.create_wallet_now")} +
{:else if !error} {#if !ready} @@ -1743,193 +484,6 @@ />
-
- {#if download_link} - {@html $t( - "pages.wallet_create.download_wallet_description" - )}
- - - - -
- {:else if !options.trusted} - {@html $t("pages.wallet_create.download_wallet_done", { - values: { download_name }, - })} - {/if} - {#if pdf_link} - {@html $t( - "pages.wallet_create.download_pdf_description" - )}
- - - - -
- {:else if options.pdf} - {@html $t("pages.wallet_create.download_pdf_done", { - values: { pdf_name }, - })} - {/if} - - {@html $t("pages.wallet_create.your_pazzle")} -
-
- {#each pazzle_emojis as emoji, index} -
-
-
- {index + 1} -
-
- -
-
-
-
- {$t("emojis.category." + emoji.cat)} -
-
- {$t("emojis.codes." + emoji.code)} -
-
-
- {/each} -
- -
- - - -
- {@html $t("pages.wallet_create.unlock_tips_1")} -

- {@html $t("pages.wallet_create.unlock_tips_2")} -
- {@html $t("pages.wallet_create.unlock_tips_3", { - values: { platform: tauri_platform || "browser" }, - })} -

-
- -
- - - {@html $t("pages.wallet_create.continue_confirm_description")} - -
- - - - -
-
{/if} {:else} @@ -1956,13 +510,7 @@ + + {:else} + {wallet_entry[1].wallet.V0.content.security_txt} + + {wallet_entry[1].wallet.V0.content.security_txt} + {/if} {/each}
@@ -373,7 +420,7 @@ class:mt-2.5={!without_create} class="text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-1.5 text-center inline-flex items-center justify-center dark:focus:ring-primary-100/55 mb-2" > - + {$t("pages.wallet_login.with_username")} @@ -412,7 +459,7 @@ tabindex="-1" class="mt-1 text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-1.5 text-center inline-flex items-center justify-center dark:focus:ring-primary-100/55 mb-2" > - + {$t("pages.wallet_login.import_qr")} @@ -441,29 +488,29 @@ {#if !without_create} - - - + + {$t("pages.wallet_login.new_wallet")} + + {/if}
diff --git a/engine/broker/auth/package.json b/engine/broker/auth/package.json index 0533197f..c604abfa 100644 --- a/engine/broker/auth/package.json +++ b/engine/broker/auth/package.json @@ -28,7 +28,7 @@ "vite": "^4.3.9", "postcss": "^8.4.23", "postcss-load-config": "^4.0.1", - "svelte-heros-v2": "^0.10.12", + "svelte-heros-v2": "^1.3.0", "svelte-preprocess": "^5.0.3", "tailwindcss": "^3.3.1", "autoprefixer": "^10.4.14", diff --git a/engine/wallet/src/lib.rs b/engine/wallet/src/lib.rs index 30c3a607..f8246702 100644 --- a/engine/wallet/src/lib.rs +++ b/engine/wallet/src/lib.rs @@ -332,13 +332,18 @@ pub fn open_wallet_with_pazzle( match wallet { Wallet::V0(v0) => { + let login = v0 + .content + .pazzle + .as_ref() + .ok_or(NgWalletError::LoginMethodNotSupported)?; pazzle.extend_from_slice(&pin); - let mut pazzle_key = derive_key_from_pass(pazzle, v0.content.salt_pazzle, v0.id); + let mut pazzle_key = derive_key_from_pass(pazzle, login.salt, v0.id); // pazzle is zeroized in derive_key_from_pass pin.zeroize(); let master_key = dec_master_key( - v0.content.enc_master_key_pazzle, + login.enc_master_key, &pazzle_key, v0.content.master_nonce, v0.id, @@ -374,16 +379,21 @@ pub fn open_wallet_with_mnemonic( match wallet { Wallet::V0(v0) => { + let login = v0 + .content + .mnemonic + .as_ref() + .ok_or(NgWalletError::LoginMethodNotSupported)?; let mut mnemonic_key = derive_key_from_pass( [transmute_to_bytes(&mnemonic), &pin].concat(), - v0.content.salt_mnemonic, + login.salt, v0.id, ); mnemonic.zeroize(); pin.zeroize(); let master_key = dec_master_key( - v0.content.enc_master_key_mnemonic, + login.enc_master_key, &mnemonic_key, v0.content.master_nonce, v0.id, @@ -403,6 +413,48 @@ pub fn open_wallet_with_mnemonic( } } +pub fn open_wallet_with_password( + wallet: &Wallet, + mut pass: String, +) -> Result { + verify(&wallet.content_as_bytes(), wallet.sig(), wallet.id()) + .map_err(|_e| NgWalletError::InvalidSignature)?; + + let mut password = pass.trim().to_string(); + pass.zeroize(); + match wallet { + Wallet::V0(v0) => { + let login = v0 + .content + .password + .as_ref() + .ok_or(NgWalletError::LoginMethodNotSupported)?; + + let mut password_key = + derive_key_from_pass(password.as_bytes().to_vec(), login.salt, v0.id); + password.zeroize(); + + let master_key = dec_master_key( + login.enc_master_key, + &password_key, + v0.content.master_nonce, + v0.id, + )?; + password_key.zeroize(); + + Ok(SensitiveWallet::V0(dec_encrypted_block( + v0.content.encrypted.clone(), + master_key, + v0.content.peer_id, + v0.content.nonce, + v0.content.timestamp, + v0.id, + )?)) + } + _ => unimplemented!(), + } +} + pub fn display_mnemonic(mnemonic: &[u16; 12]) -> Vec { let res: Vec = mnemonic .into_iter() @@ -446,7 +498,7 @@ pub fn gen_shuffle_for_pin() -> Vec { pub fn create_wallet_first_step_v0( params: CreateWalletV0, ) -> Result { - // pazzle_length can only be 9, 12, or 15 + // pazzle_length can only be 0, 9, 12, or 15 if params.pazzle_length != 9 //&& params.pazzle_length != 12 //&& params.pazzle_length != 15 @@ -462,68 +514,74 @@ pub fn create_wallet_first_step_v0( // return Err(NgWalletError::InvalidPin); // } - // each digit shouldnt be greater than 9 - if params.pin[0] > 9 || params.pin[1] > 9 || params.pin[2] > 9 || params.pin[3] > 9 { - return Err(NgWalletError::InvalidPin); + if params.pazzle_length == 0 && !params.mnemonic && params.password.is_none() { + return Err(NgWalletError::NoLoginMethod); } - // check for same digit doesnt appear 3 times - if (params.pin[0] == params.pin[1] && params.pin[0] == params.pin[2]) - || (params.pin[0] == params.pin[1] && params.pin[0] == params.pin[3]) - || (params.pin[0] == params.pin[2] && params.pin[0] == params.pin[3]) - || (params.pin[1] == params.pin[2] && params.pin[1] == params.pin[3]) - { - return Err(NgWalletError::InvalidPin); - } + if let Some(pin) = params.pin { + // each digit shouldnt be greater than 9 + if pin[0] > 9 || pin[1] > 9 || pin[2] > 9 || pin[3] > 9 { + return Err(NgWalletError::InvalidPin); + } - // check for ascending series - if params.pin[1] == params.pin[0] + 1 - && params.pin[2] == params.pin[1] + 1 - && params.pin[3] == params.pin[2] + 1 - { - return Err(NgWalletError::InvalidPin); - } + // check for same digit doesnt appear 3 times + if (pin[0] == pin[1] && pin[0] == pin[2]) + || (pin[0] == pin[1] && pin[0] == pin[3]) + || (pin[0] == pin[2] && pin[0] == pin[3]) + || (pin[1] == pin[2] && pin[1] == pin[3]) + { + return Err(NgWalletError::InvalidPin); + } - // check for descending series - if params.pin[3] >= 3 - && params.pin[2] == params.pin[3] - 1 - && params.pin[1] == params.pin[2] - 1 - && params.pin[0] == params.pin[1] - 1 - { - return Err(NgWalletError::InvalidPin); + // check for ascending series + if pin[1] == pin[0] + 1 && pin[2] == pin[1] + 1 && pin[3] == pin[2] + 1 { + return Err(NgWalletError::InvalidPin); + } + + // check for descending series + if pin[3] >= 3 && pin[2] == pin[3] - 1 && pin[1] == pin[2] - 1 && pin[0] == pin[1] - 1 { + return Err(NgWalletError::InvalidPin); + } + } else if params.pazzle_length > 0 || params.mnemonic { + return Err(NgWalletError::MnemonicOrPazzleNeedAPin); } // check validity of security text let words: Vec<_> = params.security_txt.split_whitespace().collect(); let new_string = words.join(" "); let count = new_string.chars().count(); - if count < 10 || count > 100 { + if count < 2 || count > 100 { return Err(NgWalletError::InvalidSecurityText); } // check validity of image - let decoded_img = ImageReader::new(Cursor::new(¶ms.security_img)) - .with_guessed_format() - .map_err(|_e| NgWalletError::InvalidSecurityImage)? - .decode() - .map_err(|_e| NgWalletError::InvalidSecurityImage)?; - - if decoded_img.height() < 150 || decoded_img.width() < 150 { - return Err(NgWalletError::InvalidSecurityImage); - } + let img_vec = if let Some(security_img) = ¶ms.security_img { + let decoded_img = ImageReader::new(Cursor::new(security_img)) + .with_guessed_format() + .map_err(|_e| NgWalletError::InvalidSecurityImage)? + .decode() + .map_err(|_e| NgWalletError::InvalidSecurityImage)?; + + if decoded_img.height() < 150 || decoded_img.width() < 150 { + return Err(NgWalletError::InvalidSecurityImage); + } - let resized_img = if decoded_img.height() == 400 && decoded_img.width() == 400 { - decoded_img + let resized_img = if decoded_img.height() == 400 && decoded_img.width() == 400 { + decoded_img + } else { + decoded_img.resize_to_fill(400, 400, FilterType::Triangle) + }; + let buffer: Vec = Vec::with_capacity(100000); + let mut cursor = Cursor::new(buffer); + resized_img + .write_to(&mut cursor, ImageOutputFormat::Jpeg(72)) + .map_err(|_e| NgWalletError::InvalidSecurityImage)?; + + Some(cursor.into_inner()) } else { - decoded_img.resize_to_fill(400, 400, FilterType::Triangle) + None }; - let buffer: Vec = Vec::with_capacity(100000); - let mut cursor = Cursor::new(buffer); - resized_img - .write_to(&mut cursor, ImageOutputFormat::Jpeg(72)) - .map_err(|_e| NgWalletError::InvalidSecurityImage)?; - // creating the wallet keys let (wallet_privkey, wallet_id) = generate_keypair(); @@ -541,10 +599,12 @@ pub fn create_wallet_first_step_v0( client, user_privkey, in_memory: !params.local_save, - security_img: cursor.into_inner(), + security_img: img_vec, security_txt: new_string, pazzle_length: params.pazzle_length, pin: params.pin, + password: params.password.as_ref().map(|p| p.trim().to_string()), + mnemonic: params.mnemonic, send_bootstrap: params.send_bootstrap, send_wallet: params.send_wallet, result_with_wallet_file: params.result_with_wallet_file, @@ -583,25 +643,34 @@ pub async fn create_wallet_second_step_v0( let mut ran = thread_rng(); - let mut category_indices: Vec = (0..params.pazzle_length).collect(); - category_indices.shuffle(&mut ran); - - let between = Uniform::try_from(0..15).unwrap(); - let mut pazzle = vec![0u8; params.pazzle_length.into()]; - for (ix, i) in pazzle.iter_mut().enumerate() { - //*i = ran.gen_range(0, 15) + (category_indices[ix] << 4); - *i = between.sample(&mut ran) + (category_indices[ix] << 4); - } + let pazzle = if params.pazzle_length > 0 { + let mut category_indices: Vec = (0..params.pazzle_length).collect(); + category_indices.shuffle(&mut ran); - //log_debug!("pazzle {:?}", pazzle); - let between = Uniform::try_from(0..2048).unwrap(); - let mut mnemonic = [0u16; 12]; - for i in &mut mnemonic { - //*i = ran.gen_range(0, 2048); - *i = between.sample(&mut ran); - } + let between = Uniform::try_from(0..15).unwrap(); + let mut pazzle = vec![0u8; params.pazzle_length.into()]; + for (ix, i) in pazzle.iter_mut().enumerate() { + //*i = ran.gen_range(0, 15) + (category_indices[ix] << 4); + *i = between.sample(&mut ran) + (category_indices[ix] << 4); + } + //log_debug!("pazzle {:?}", pazzle); + Some(pazzle) + } else { + None + }; - //log_debug!("mnemonic {:?}", display_mnemonic(&mnemonic)); + let mnemonic = if params.mnemonic { + let between = Uniform::try_from(0..2048).unwrap(); + let mut mnemonic = [0u16; 12]; + for i in &mut mnemonic { + //*i = ran.gen_range(0, 2048); + *i = between.sample(&mut ran); + } + //log_debug!("mnemonic {:?}", display_mnemonic(&mnemonic)); + Some(mnemonic) + } else { + None + }; //slice_as_array!(&mnemonic, [String; 12]) //.ok_or(NgWalletError::InternalError)? @@ -676,35 +745,71 @@ pub async fn create_wallet_second_step_v0( let mut master_key = [0u8; 32]; getrandom::fill(&mut master_key).map_err(|_e| NgWalletError::InternalError)?; - let mut salt_pazzle = [0u8; 16]; - let mut enc_master_key_pazzle = [0u8; 48]; - if params.pazzle_length > 0 { + let pazzle_login = if let Some(pazzle) = &pazzle { + let mut salt_pazzle = [0u8; 16]; + + //log_debug!("salt_pazzle {:?}", salt_pazzle); + getrandom::fill(&mut salt_pazzle).map_err(|_e| NgWalletError::InternalError)?; let mut pazzle_key = derive_key_from_pass( - [pazzle.clone(), params.pin.to_vec()].concat(), + [pazzle.clone(), params.pin.unwrap().to_vec()].concat(), salt_pazzle, wallet_id, ); - enc_master_key_pazzle = enc_master_key(&master_key, &pazzle_key, 0, wallet_id)?; + let enc_master_key_pazzle = enc_master_key(&master_key, &pazzle_key, 0, wallet_id)?; pazzle_key.zeroize(); - } + Some(LoginMethod { + salt: salt_pazzle, + enc_master_key: enc_master_key_pazzle, + }) + } else { + None + }; - let mut salt_mnemonic = [0u8; 16]; - getrandom::fill(&mut salt_mnemonic).map_err(|_e| NgWalletError::InternalError)?; + let mnemonic_login = if let Some(mnemonic) = mnemonic { + let mut salt_mnemonic = [0u8; 16]; + getrandom::fill(&mut salt_mnemonic).map_err(|_e| NgWalletError::InternalError)?; - //log_debug!("salt_pazzle {:?}", salt_pazzle); - //log_debug!("salt_mnemonic {:?}", salt_mnemonic); + //log_debug!("salt_mnemonic {:?}", salt_mnemonic); - let mut mnemonic_key = derive_key_from_pass( - [transmute_to_bytes(&mnemonic), ¶ms.pin].concat(), - salt_mnemonic, - wallet_id, - ); + let mut mnemonic_key = derive_key_from_pass( + [transmute_to_bytes(&mnemonic), ¶ms.pin.unwrap()].concat(), + salt_mnemonic, + wallet_id, + ); + + let enc_master_key_mnemonic = enc_master_key(&master_key, &mnemonic_key, 0, wallet_id)?; + mnemonic_key.zeroize(); + + Some(LoginMethod { + salt: salt_mnemonic, + enc_master_key: enc_master_key_mnemonic, + }) + } else { + None + }; + + let password = if let Some(password) = ¶ms.password { + let mut salt_password = [0u8; 16]; + getrandom::fill(&mut salt_password).map_err(|_e| NgWalletError::InternalError)?; + + //log_debug!("salt_password {:?}", salt_password); + + let mut password_key = + derive_key_from_pass(password.as_bytes().to_vec(), salt_password, wallet_id); + + let enc_master_key_password = enc_master_key(&master_key, &password_key, 0, wallet_id)?; + password_key.zeroize(); - let enc_master_key_mnemonic = enc_master_key(&master_key, &mnemonic_key, 0, wallet_id)?; - mnemonic_key.zeroize(); + Some(LoginMethod { + salt: salt_password, + enc_master_key: enc_master_key_password, + }) + } else { + None + }; let timestamp = now_timestamp(); @@ -720,13 +825,15 @@ pub async fn create_wallet_second_step_v0( master_key.zeroize(); let wallet_content = WalletContentV0 { - security_img: params.security_img.clone(), + security_img: params + .security_img + .as_ref() + .map(|b| serde_bytes::ByteBuf::from(b.as_slice())), security_txt: params.security_txt.clone(), pazzle_length: params.pazzle_length, - salt_pazzle, - salt_mnemonic, - enc_master_key_pazzle, - enc_master_key_mnemonic, + mnemonic: mnemonic_login, + pazzle: pazzle_login, + password, master_nonce: 0, timestamp, peer_id: PubKey::nil(), @@ -736,7 +843,7 @@ pub async fn create_wallet_second_step_v0( let ser_wallet = serde_bare::to_vec(&wallet_content).unwrap(); - let sig = sign(¶ms.wallet_privkey, &wallet_id, &ser_wallet).unwrap(); + let sig: Sig = sign(¶ms.wallet_privkey, &wallet_id, &ser_wallet).unwrap(); let wallet_v0 = WalletV0 { // ID @@ -774,7 +881,7 @@ pub async fn create_wallet_second_step_v0( wallet_file, pazzle, mnemonic: mnemonic.clone(), - mnemonic_str: display_mnemonic(&mnemonic), + mnemonic_str: mnemonic.map_or(vec![], |m| display_mnemonic(&m)), wallet_name: params.wallet_name.clone(), client: params.client.clone(), user, @@ -833,10 +940,12 @@ mod test { let _creation = Instant::now(); let res = create_wallet_first_step_v0(CreateWalletV0::new( - img_buffer, + Some(img_buffer), " know yourself ".to_string(), - pin, + Some(pin), 9, + None, + true, false, false, BootstrapContentV0::new_localhost(PubKey::nil()), @@ -863,16 +972,23 @@ mod test { let _ = file.write_all(&ser_wallet); log_debug!("wallet id: {}", res.wallet.id()); - log_debug!("pazzle {:?}", display_pazzle_one(&res.pazzle)); - log_debug!("mnemonic {:?}", display_mnemonic(&res.mnemonic)); + log_debug!( + "pazzle {:?}", + display_pazzle_one(res.pazzle.as_ref().expect("no pazzle")) + ); + log_debug!( + "mnemonic {:?}", + display_mnemonic(&res.mnemonic.expect("no mnemonic")) + ); log_debug!("pin {:?}", pin); if let Wallet::V0(v0) = &res.wallet { log_debug!("security text: {:?}", v0.content.security_txt); + let img = v0.content.security_img.as_ref().expect("no securit image"); let mut file = File::create("tests/generated_security_image.jpg").expect("open write file"); - let _ = file.write_all(&v0.content.security_img); + let _ = file.write_all(img); let f = File::open("tests/generated_security_image.jpg.compare") .expect("open of generated_security_image.jpg.compare"); @@ -883,12 +999,16 @@ mod test { .read_to_end(&mut generated_security_image_compare) .expect("read of generated_security_image.jpg.compare"); - assert_eq!(v0.content.security_img, generated_security_image_compare); + assert_eq!(img, &generated_security_image_compare); let _opening_mnemonic = Instant::now(); - let _w = open_wallet_with_mnemonic(&Wallet::V0(v0.clone()), res.mnemonic, pin.clone()) - .expect("open with mnemonic"); + let _w = open_wallet_with_mnemonic( + &Wallet::V0(v0.clone()), + res.mnemonic.expect("no mnemonic"), + pin.clone(), + ) + .expect("open with mnemonic"); //log_debug!("encrypted part {:?}", w); log_info!( @@ -898,8 +1018,12 @@ mod test { if v0.content.pazzle_length > 0 { let _opening_pazzle = Instant::now(); - let _w = open_wallet_with_pazzle(&Wallet::V0(v0.clone()), res.pazzle.clone(), pin) - .expect("open with pazzle"); + let _w = open_wallet_with_pazzle( + &Wallet::V0(v0.clone()), + res.pazzle.as_ref().expect("no pazzle").clone(), + pin, + ) + .expect("open with pazzle"); log_info!( "opening of wallet with pazzle took: {} ms", _opening_pazzle.elapsed().as_millis() diff --git a/engine/wallet/src/types.rs b/engine/wallet/src/types.rs index eddf8d75..fd59b1e2 100644 --- a/engine/wallet/src/types.rs +++ b/engine/wallet/src/types.rs @@ -660,27 +660,32 @@ impl SensitiveWalletV0 { } } +/// Login method +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct LoginMethod { + pub salt: [u8; 16], + + // encrypted master keys. + // AD = wallet_id + #[serde(with = "BigArray")] + pub enc_master_key: [u8; 48], +} + /// Wallet content Version 0 #[derive(Clone, Debug, Serialize, Deserialize)] pub struct WalletContentV0 { - #[serde(with = "serde_bytes")] - pub security_img: Vec, + pub security_img: Option, pub security_txt: String, - /// can be 9, 12 or 15 (or 0, in this case salt_pazzle and enc_master_key_pazzle are filled with zeros and should not be used) + /// can be 9, 12 or 15 (or 0, if pazzle is deactivated) pub pazzle_length: u8, - pub salt_pazzle: [u8; 16], + pub pazzle: Option, - pub salt_mnemonic: [u8; 16], + pub mnemonic: Option, - // encrypted master keys. first is encrypted with pazzle, second is encrypted with mnemonic - // AD = wallet_id - #[serde(with = "BigArray")] - pub enc_master_key_pazzle: [u8; 48], - #[serde(with = "BigArray")] - pub enc_master_key_mnemonic: [u8; 48], + pub password: Option, // nonce for the encryption of masterkey // incremented only if the masterkey changes @@ -1243,7 +1248,7 @@ pub struct CreateWalletV0 { /// Please be aware that other users who are sharing the same device, will be able to see this image. #[zeroize(skip)] #[serde(with = "serde_bytes")] - pub security_img: Vec, + pub security_img: Option>, /// A string of characters of minimum length 10. /// This phrase will be presented to the user every time they are about to enter their pazzle and PIN in order to unlock their wallet. /// It should be something the user will remember, but not something too personal. @@ -1256,10 +1261,15 @@ pub struct CreateWalletV0 { /// The PIN and the rest of the Wallet will never be sent to NextGraph or any other third party (check the source code if you don't believe us). /// It cannot be a series like 1234 or 8765. The same digit cannot repeat more than once. By example 4484 is invalid. /// Try to avoid birth date, last digits of phone number, or zip code for privacy concern - pub pin: [u8; 4], + pub pin: Option<[u8; 4]>, /// For now, only 9 is supported. 12 and 15 are planned. /// A value of 0 will deactivate the pazzle mechanism on this Wallet, and only the mnemonic could be used to open it. pub pazzle_length: u8, + + pub password: Option, + + pub mnemonic: bool, + #[zeroize(skip)] /// Not implemented yet. Will send the bootstrap to our cloud servers, if needed pub send_bootstrap: bool, @@ -1294,10 +1304,12 @@ pub struct CreateWalletV0 { impl CreateWalletV0 { pub fn new( - security_img: Vec, + security_img: Option>, security_txt: String, - pin: [u8; 4], + pin: Option<[u8; 4]>, pazzle_length: u8, + password: Option, + mnemonic: bool, send_bootstrap: bool, send_wallet: bool, core_bootstrap: BootstrapContentV0, @@ -1313,6 +1325,8 @@ impl CreateWalletV0 { security_txt, pin, pazzle_length, + password, + mnemonic, send_bootstrap, send_wallet, core_bootstrap, @@ -1351,10 +1365,10 @@ pub struct CreateWalletResultV0 { /// The binary file that can be saved to disk and given to the user pub wallet_file: Vec, /// randomly generated pazzle - pub pazzle: Vec, + pub pazzle: Option>, /// randomly generated mnemonic. It is an alternate way to open the wallet. /// A BIP39 list of 12 words. We argue that the Pazzle is easier to remember than this. - pub mnemonic: [u16; 12], + pub mnemonic: Option<[u16; 12]>, /// The words of the mnemonic, in a human readable form. pub mnemonic_str: Vec, #[zeroize(skip)] @@ -1400,13 +1414,17 @@ pub struct CreateWalletIntermediaryV0 { pub in_memory: bool, #[zeroize(skip)] - pub security_img: Vec, + pub security_img: Option>, pub security_txt: String, pub pazzle_length: u8, - pub pin: [u8; 4], + pub mnemonic: bool, + + pub password: Option, + + pub pin: Option<[u8; 4]>, #[zeroize(skip)] pub send_bootstrap: bool, @@ -1439,6 +1457,9 @@ pub enum NgWalletError { NoCreateWalletPresent, InvalidBootstrap, SerializationError, + MnemonicOrPazzleNeedAPin, + NoLoginMethod, + LoginMethodNotSupported, } impl From for NgError { diff --git a/infra/ngaccount/web/src/routes/Create.svelte b/infra/ngaccount/web/src/routes/Create.svelte index acf97084..583cd05e 100644 --- a/infra/ngaccount/web/src/routes/Create.svelte +++ b/infra/ngaccount/web/src/routes/Create.svelte @@ -28,7 +28,7 @@ const api_url = import.meta.env.PROD ? "api/v1/" : "http://127.0.0.1:3031/api/v1/"; - + async function register() { wait = true; const opts = { @@ -75,7 +75,7 @@ window.location.href = result.url; } else { wait = true; - window.history.go(-1); + window.location.href = document.referrer; } } } diff --git a/infra/ngnet/bootstrap/package.json b/infra/ngnet/bootstrap/package.json index e428e633..d918000c 100644 --- a/infra/ngnet/bootstrap/package.json +++ b/infra/ngnet/bootstrap/package.json @@ -23,7 +23,7 @@ "svelte": "^3.58.0", "postcss": "^8.4.23", "postcss-load-config": "^4.0.1", - "svelte-heros-v2": "^0.10.12", + "svelte-heros-v2": "^1.3.0", "svelte-preprocess": "^5.0.3", "tailwindcss": "^3.3.1", "vite-plugin-svelte-svg": "^2.2.1", diff --git a/infra/ngnet/redir/package.json b/infra/ngnet/redir/package.json index 88d18848..56dfe332 100644 --- a/infra/ngnet/redir/package.json +++ b/infra/ngnet/redir/package.json @@ -26,7 +26,7 @@ "vite": "^4.3.9", "postcss": "^8.4.23", "postcss-load-config": "^4.0.1", - "svelte-heros-v2": "^0.10.12", + "svelte-heros-v2": "^1.3.0", "svelte-preprocess": "^5.0.3", "tailwindcss": "^3.3.1", "autoprefixer": "^10.4.14", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 194db48d..d16e3839 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -203,8 +203,8 @@ importers: specifier: ^3.0.0 version: 3.8.6(@babel/core@7.28.4)(postcss-load-config@4.0.2(postcss@8.5.6))(postcss@8.5.6)(svelte@3.59.2) svelte-heros-v2: - specifier: ^0.10.12 - version: 0.10.12(svelte@3.59.2) + specifier: ^1.3.0 + version: 1.3.0(svelte@3.59.2) svelte-preprocess: specifier: ^5.0.3 version: 5.1.4(@babel/core@7.28.4)(postcss-load-config@4.0.2(postcss@8.5.6))(postcss@8.5.6)(svelte@3.59.2)(typescript@4.9.5) @@ -279,8 +279,8 @@ importers: specifier: ^3.58.0 version: 3.59.2 svelte-heros-v2: - specifier: ^0.10.12 - version: 0.10.12(svelte@3.59.2) + specifier: ^1.3.0 + version: 1.3.0(svelte@3.59.2) svelte-preprocess: specifier: ^5.0.3 version: 5.1.4(@babel/core@7.28.4)(postcss-load-config@4.0.2(postcss@8.5.6))(postcss@8.5.6)(svelte@3.59.2)(typescript@5.9.2) @@ -453,8 +453,8 @@ importers: specifier: ^3.58.0 version: 3.59.2 svelte-heros-v2: - specifier: ^0.10.12 - version: 0.10.12(svelte@3.59.2) + specifier: ^1.3.0 + version: 1.3.0(svelte@3.59.2) svelte-preprocess: specifier: ^5.0.3 version: 5.1.4(@babel/core@7.28.4)(postcss-load-config@4.0.2(postcss@8.5.6))(postcss@8.5.6)(svelte@3.59.2)(typescript@5.9.2) @@ -520,8 +520,8 @@ importers: specifier: ^3.58.0 version: 3.59.2 svelte-heros-v2: - specifier: ^0.10.12 - version: 0.10.12(svelte@3.59.2) + specifier: ^1.3.0 + version: 1.3.0(svelte@3.59.2) svelte-preprocess: specifier: ^5.0.3 version: 5.1.4(@babel/core@7.28.4)(postcss-load-config@4.0.2(postcss@8.5.6))(postcss@8.5.6)(svelte@3.59.2)(typescript@5.9.2) @@ -6546,10 +6546,10 @@ packages: svelte: ^4.0.0 || ^5.0.0-next.0 typescript: '>=5.0.0' - svelte-heros-v2@0.10.12: - resolution: {integrity: sha512-0wspy0z9UFS9f/iPKQQ1JDHlNY6e7h+LVW+wJ0qJnuWDpvsJllmoCX2g0frYbMPDWZJEwh2pkO25Dp3lDGCxGQ==} + svelte-heros-v2@1.3.0: + resolution: {integrity: sha512-H+s2Z907WU8sLG/dOYGfiIq7mxtACm6LM+A8jdcDCWtjyyoOmtL2waZEKKXsLrcwO5g5/D6i0TqSs0UJuchRoA==} peerDependencies: - svelte: ^3.54.0 || ^4.0.0 + svelte: ^3.54.0 || ^4.0.0 || ^5.0.0 svelte-hmr@0.15.3: resolution: {integrity: sha512-41snaPswvSf8TJUhlkoJBekRrABDXDMdpNpT2tfHIv4JuhgvHqLMhEPGtaQn0BmbNSTkuz2Ed20DF2eHw0SmBQ==} @@ -14507,7 +14507,7 @@ snapshots: transitivePeerDependencies: - picomatch - svelte-heros-v2@0.10.12(svelte@3.59.2): + svelte-heros-v2@1.3.0(svelte@3.59.2): dependencies: svelte: 3.59.2 diff --git a/sdk/js/lib-wasm/src/lib.rs b/sdk/js/lib-wasm/src/lib.rs index e7abf813..37680a76 100644 --- a/sdk/js/lib-wasm/src/lib.rs +++ b/sdk/js/lib-wasm/src/lib.rs @@ -175,6 +175,18 @@ pub fn privkey_to_string(privkey: JsValue) -> Result { Ok(format!("{p}")) } +pub fn wallet_open_with_password(wallet: JsValue, password: String) -> Result { + let encrypted_wallet = serde_wasm_bindgen::from_value::(wallet) + .map_err(|_| "Deserialization error of wallet")?; + let res = nextgraph::local_broker::wallet_open_with_password(&encrypted_wallet, password); + match res { + Ok(r) => Ok(r + .serialize(&serde_wasm_bindgen::Serializer::new().serialize_maps_as_objects(true)) + .unwrap()), + Err(e) => Err(e.to_string().into()), + } +} + #[wasm_bindgen] pub fn wallet_open_with_pazzle( wallet: JsValue, @@ -924,7 +936,7 @@ static INIT_LOCAL_BROKER: Lazy> = Lazy::new(|| { pub async fn wallet_create(params: JsValue) -> Result { init_local_broker_with_lazy(&INIT_LOCAL_BROKER).await; let mut params = serde_wasm_bindgen::from_value::(params) - .map_err(|_| "Deserialization error of args")?; + .map_err(|e| format!("Deserialization error of args {e}"))?; params.result_with_wallet_file = true; let res = nextgraph::local_broker::wallet_create_v0(params).await; match res { @@ -2143,10 +2155,12 @@ pub async fn gen_wallet_for_test(ngd_peer_id: String) -> Result let peer_id_of_server_broker = decode_key(&ngd_peer_id).map_err(|e: NgError| e.to_string())?; let wallet_result = wallet_create_v0(CreateWalletV0 { - security_img: Vec::from(EMPTY_IMG), + security_img: None, security_txt: "testsecurityphrase".to_string(), - pin: [1, 2, 1, 2], + pin: Some([1, 2, 1, 2]), pazzle_length: 9, + mnemonic: true, + password: None, send_bootstrap: false, send_wallet: false, result_with_wallet_file: false, @@ -2161,7 +2175,7 @@ pub async fn gen_wallet_for_test(ngd_peer_id: String) -> Result .expect("wallet_create_v0"); let mut mnemonic_words = Vec::with_capacity(12); - display_mnemonic(&wallet_result.mnemonic) + display_mnemonic(&wallet_result.mnemonic.unwrap()) .iter() .for_each(|word| { mnemonic_words.push(word.clone()); diff --git a/sdk/rust/src/local_broker.rs b/sdk/rust/src/local_broker.rs index 75f9f23a..639edc68 100644 --- a/sdk/rust/src/local_broker.rs +++ b/sdk/rust/src/local_broker.rs @@ -1589,6 +1589,8 @@ pub async fn wallet_create_v0(params: CreateWalletV0) -> Result 0; + let has_mnemonic = params.mnemonic; let intermediate = create_wallet_first_step_v0(params)?; let lws: LocalWalletStorageV0 = (&intermediate).into(); @@ -1619,9 +1621,13 @@ pub async fn wallet_create_v0(params: CreateWalletV0) -> Result { let mut content = v0.content.clone(); - content.security_img = vec![]; + content.security_img = None; content.security_txt = String::new(); NgQRCodeWalletRecoveryV0 { wallet: serde_bare::to_vec(&content).unwrap(), @@ -2145,6 +2151,16 @@ pub async fn wallet_get_file(wallet_name: &String) -> Result, NgError> { } } +#[doc(hidden)] +pub fn wallet_open_with_password( + wallet: &Wallet, + password: String, +) -> Result { + let opened_wallet = ng_wallet::open_wallet_with_password(wallet, password)?; + + Ok(opened_wallet) +} + #[doc(hidden)] /// This is a bit hard to use as the pazzle words are encoded in unsigned bytes. /// prefer the function wallet_open_with_pazzle_words @@ -3045,10 +3061,12 @@ mod test { let peer_id_of_server_broker = PubKey::nil(); let wallet_result = wallet_create_v0(CreateWalletV0 { - security_img, + security_img: Some(security_img), security_txt: "know yourself".to_string(), - pin: [1, 2, 1, 2], + pin: Some([1, 2, 1, 2]), pazzle_length: 9, + password: None, + mnemonic: true, send_bootstrap: false, send_wallet: false, result_with_wallet_file: true, @@ -3063,9 +3081,10 @@ mod test { .await .expect("wallet_create_v0"); - let pazzle = display_pazzle(&wallet_result.pazzle); + let pazzle_vec = wallet_result.pazzle.clone().unwrap(); + let pazzle = display_pazzle(&pazzle_vec); let mut pazzle_words = vec![]; - println!("Your pazzle is: {:?}", wallet_result.pazzle); + println!("Your pazzle is: {:?}", pazzle_vec); for emoji in pazzle { println!(" {}:\t{}", emoji.0, emoji.1); pazzle_words.push(emoji.1.to_string()); @@ -3080,7 +3099,7 @@ mod test { println!("Your mnemonic is:"); let mut mnemonic_words = vec![]; - display_mnemonic(&wallet_result.mnemonic) + display_mnemonic(&wallet_result.mnemonic.unwrap()) .iter() .for_each(|word| { mnemonic_words.push(word.clone());