download recovery PDF

master
Niko PLP 3 months ago
parent 81eeae8947
commit 76ef3eee4d
  1. 1
      Cargo.lock
  2. 2
      nextgraph/examples/in_memory.rs
  3. 2
      nextgraph/examples/persistent.rs
  4. 14
      nextgraph/src/local_broker.rs
  5. 1
      ng-app/src-tauri/Cargo.toml
  6. 16
      ng-app/src-tauri/src/lib.rs
  7. 2
      ng-app/src/App.svelte
  8. 2
      ng-app/src/lib/Login.svelte
  9. 3
      ng-app/src/locales/en.json
  10. 69
      ng-app/src/routes/WalletCreate.svelte
  11. 8
      ng-sdk-js/src/lib.rs
  12. 4
      ng-wallet/src/lib.rs
  13. 21
      ng-wallet/src/types.rs

1
Cargo.lock generated

@ -3323,6 +3323,7 @@ dependencies = [
"tauri-plugin-barcode-scanner", "tauri-plugin-barcode-scanner",
"tauri-plugin-window", "tauri-plugin-window",
"tauri-utils", "tauri-utils",
"zeroize",
] ]
[[package]] [[package]]

@ -55,6 +55,8 @@ async fn main() -> std::io::Result<()> {
core_bootstrap: BootstrapContentV0::new_localhost(peer_id_of_server_broker), core_bootstrap: BootstrapContentV0::new_localhost(peer_id_of_server_broker),
core_registration: None, core_registration: None,
additional_bootstrap: None, additional_bootstrap: None,
pdf: false,
device_name: "test".to_string(),
}) })
.await?; .await?;

@ -63,6 +63,8 @@ async fn main() -> std::io::Result<()> {
core_bootstrap: BootstrapContentV0::new_localhost(peer_id_of_server_broker), core_bootstrap: BootstrapContentV0::new_localhost(peer_id_of_server_broker),
core_registration: None, core_registration: None,
additional_bootstrap: None, additional_bootstrap: None,
pdf: false,
device_name: "test".to_string(),
}) })
.await?; .await?;

@ -1570,10 +1570,20 @@ pub async fn wallet_create_v0(params: CreateWalletV0) -> Result<CreateWalletResu
// let session = broker.opened_sessions_list[session_info.session_id as usize] // let session = broker.opened_sessions_list[session_info.session_id as usize]
// .as_mut() // .as_mut()
// .unwrap(); // .unwrap();
let with_pdf = intermediate.pdf;
let pin = intermediate.pin;
let (mut res, site, brokers) = let (mut res, site, brokers) =
create_wallet_second_step_v0(intermediate, &mut session.verifier).await?; create_wallet_second_step_v0(intermediate, &mut session.verifier).await?;
if with_pdf {
let wallet_recovery =
wallet_to_wallet_recovery(&res.wallet, res.pazzle.clone(), res.mnemonic, pin);
if let Ok(pdf_buffer) = wallet_recovery_pdf(wallet_recovery, 600).await {
res.pdf_file = pdf_buffer;
};
}
//log_info!("VERIFIER DUMP {:?}", session.verifier); //log_info!("VERIFIER DUMP {:?}", session.verifier);
broker.wallets.get_mut(&res.wallet_name).unwrap().wallet = res.wallet.clone(); broker.wallets.get_mut(&res.wallet_name).unwrap().wallet = res.wallet.clone();
@ -2857,6 +2867,8 @@ mod test {
core_bootstrap: BootstrapContentV0::new_localhost(peer_id_of_server_broker), core_bootstrap: BootstrapContentV0::new_localhost(peer_id_of_server_broker),
core_registration: None, core_registration: None,
additional_bootstrap: None, additional_bootstrap: None,
pdf: false,
device_name: "test".to_string(),
}) })
.await .await
.expect("wallet_create_v0"); .expect("wallet_create_v0");

@ -30,6 +30,7 @@ serde_json = "1.0"
serde_bytes = "0.11.7" serde_bytes = "0.11.7"
async-std = { version = "1.12.0", features = ["attributes", "unstable"] } async-std = { version = "1.12.0", features = ["attributes", "unstable"] }
sys-locale = { version = "0.3.1" } sys-locale = { version = "0.3.1" }
zeroize = { version = "1.7.0", features = ["zeroize_derive"] }
ng-async-tungstenite = { git = "https://git.nextgraph.org/NextGraph/async-tungstenite.git", branch = "nextgraph", features = ["async-std-runtime", "async-native-tls"] } ng-async-tungstenite = { git = "https://git.nextgraph.org/NextGraph/async-tungstenite.git", branch = "nextgraph", features = ["async-std-runtime", "async-native-tls"] }
tauri = { version = "2.0.0-alpha.14", features = [] } tauri = { version = "2.0.0-alpha.14", features = [] }
# add the "devtools" feature if devtools in the production build should be activated # add the "devtools" feature if devtools in the production build should be activated

@ -18,6 +18,7 @@ use sys_locale::get_locales;
use tauri::scope::ipc::RemoteDomainAccessScope; use tauri::scope::ipc::RemoteDomainAccessScope;
use tauri::utils::config::WindowConfig; use tauri::utils::config::WindowConfig;
use tauri::{path::BaseDirectory, App, Manager}; use tauri::{path::BaseDirectory, App, Manager};
use zeroize::Zeroize;
use ng_repo::errors::NgError; use ng_repo::errors::NgError;
use ng_repo::log::*; use ng_repo::log::*;
@ -160,6 +161,7 @@ async fn wallet_create(
//log_debug!("wallet_create from rust {:?}", params); //log_debug!("wallet_create from rust {:?}", params);
params.result_with_wallet_file = !params.local_save; params.result_with_wallet_file = !params.local_save;
let local_save = params.local_save; let local_save = params.local_save;
let pdf = params.pdf;
let mut cwr = nextgraph::local_broker::wallet_create_v0(params) let mut cwr = nextgraph::local_broker::wallet_create_v0(params)
.await .await
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
@ -173,8 +175,22 @@ async fn wallet_create(
) )
.unwrap(); .unwrap();
let _r = write(path, &cwr.wallet_file); let _r = write(path, &cwr.wallet_file);
cwr.wallet_file.zeroize();
cwr.wallet_file = vec![]; cwr.wallet_file = vec![];
} }
if pdf {
// save pdf file to Downloads folder
let path = app
.path()
.resolve(
format!("wallet-{}.pdf", cwr.wallet_name),
BaseDirectory::Download,
)
.unwrap();
let _r = write(path, &cwr.pdf_file);
cwr.pdf_file.zeroize();
cwr.pdf_file = vec![];
}
Ok(cwr) Ok(cwr)
} }

@ -151,7 +151,7 @@
window.wallet_channel = wallet_channel; window.wallet_channel = wallet_channel;
wallet_channel.postMessage({ cmd: "startup" }, location.href); wallet_channel.postMessage({ cmd: "startup" }, location.href);
wallet_channel.onmessage = async (event) => { wallet_channel.onmessage = async (event) => {
//console.log(event.data.cmd, event.data); // console.log(event.data.cmd, event.data);
if (!location.href.startsWith(event.origin)) return; if (!location.href.startsWith(event.origin)) return;
switch (event.data.cmd) { switch (event.data.cmd) {
case "startup": case "startup":

@ -411,7 +411,7 @@
<div class="max-w-xl lg:px-8 mx-auto px-4 text-primary-700"> <div class="max-w-xl lg:px-8 mx-auto px-4 text-primary-700">
<div class="flex flex-col justify-centerspace-x-12 mt-4 mb-4"> <div class="flex flex-col justify-centerspace-x-12 mt-4 mb-4">
<!-- Device Name, if trusted--> <!-- Device Name, if trusted-->
{#if for_import} {#if for_import && trusted}
<label for="device-name-input" class="text-sm text-black"> <label for="device-name-input" class="text-sm text-black">
{$t("pages.login.device_name_label")} {$t("pages.login.device_name_label")}
</label> </label>

@ -468,6 +468,9 @@
"download_wallet_description": "Please download your wallet and keep it in a safe location", "download_wallet_description": "Please download your wallet and keep it in a safe location",
"download_wallet": "Download my wallet", "download_wallet": "Download my wallet",
"download_wallet_done": "Your wallet file has been downloaded into your \"Downloads\" folder, with the name<br /><span class=\"text-black\"> {download_name}</span ><br /> <span class=\"font-bold\" >Please move it to a safe and durable place.</span ><br />", "download_wallet_done": "Your wallet file has been downloaded into your \"Downloads\" folder, with the name<br /><span class=\"text-black\"> {download_name}</span ><br /> <span class=\"font-bold\" >Please move it to a safe and durable place.</span ><br />",
"download_pdf_description": "Please download your Recovery PDF, print it, and delete it.",
"download_pdf": "Download my PDF",
"download_pdf_done": "Your Recovery PDF file has been downloaded into your \"Downloads\" folder, with the name<br /><span class=\"text-black\"> {pdf_name}</span ><br /> <span class=\"font-bold\" >Please print it and then delete it.</span ><br />",
"your_pazzle": "Here below is your Pazzle. <br /> The <span class=\"font-bold\">order</span> of each image is <span class=\"font-bold\">important</span>!", "your_pazzle": "Here below is your Pazzle. <br /> The <span class=\"font-bold\">order</span> of each image is <span class=\"font-bold\">important</span>!",
"your_mnemonic": "And here is your mnemonic (your alternative passphrase):", "your_mnemonic": "And here is your mnemonic (your alternative passphrase):",
"unlock_tips_1": "You can use both the pazzle or the mnemonic to unlock your wallet. The pazzle is easier to remember. The mnemonic is useful in some special cases. We recommend that you use the pazzle. <span class=\"font-bold text-xl\" >Copy both on a piece of paper.</span > You should try to memorize the pazzle. Once you did, you won't need the paper anymore.", "unlock_tips_1": "You can use both the pazzle or the mnemonic to unlock your wallet. The pazzle is easier to remember. The mnemonic is useful in some special cases. We recommend that you use the pazzle. <span class=\"font-bold text-xl\" >Copy both on a piece of paper.</span > You should try to memorize the pazzle. Once you did, you won't need the paper anymore.",

@ -128,8 +128,11 @@
let ready; let ready;
let download_link; let download_link;
let download_name; let download_name;
let pdf_link;
let pdf_name;
let cloud_link; let cloud_link;
let animateDownload = true; let animateDownload = true;
let animatePdf = true;
let invitation; let invitation;
let pre_invitation; let pre_invitation;
@ -228,7 +231,7 @@
trusted: true, trusted: true,
cloud: false, cloud: false,
bootstrap: false, bootstrap: false,
pdf: false, pdf: !mobile,
}; };
await tick(); await tick();
scrollToTop(); scrollToTop();
@ -257,7 +260,8 @@
core_bootstrap: invitation.V0.bootstrap, core_bootstrap: invitation.V0.bootstrap,
core_registration, core_registration,
additional_bootstrap, additional_bootstrap,
//TODO: device_name, device_name,
pdf: options.pdf
}; };
//console.log("do wallet with params", params); //console.log("do wallet with params", params);
try { try {
@ -275,11 +279,12 @@
window.wallet_channel.postMessage(new_in_mem, location.href); window.wallet_channel.postMessage(new_in_mem, location.href);
} }
} }
console.log("pazzle", ready.pazzle); // console.log("pazzle", ready.pazzle);
console.log("pazzle words", display_pazzle(ready.pazzle)); // console.log("pazzle words", display_pazzle(ready.pazzle));
console.log("mnemonic", ready.mnemonic); // console.log("mnemonic", ready.mnemonic);
console.log("mnemonic words", ready.mnemonic_str); // console.log("mnemonic words", ready.mnemonic_str);
download_name = "wallet-" + ready.wallet_name + ".ngw"; download_name = "wallet-" + ready.wallet_name + ".ngw";
pdf_name = "wallet-" + ready.wallet_name + ".pdf";
if (options.cloud) { if (options.cloud) {
cloud_link = "https://nextgraph.one/#/w/" + ready.wallet_name; cloud_link = "https://nextgraph.one/#/w/" + ready.wallet_name;
} }
@ -289,6 +294,12 @@
}); });
download_link = URL.createObjectURL(blob); download_link = URL.createObjectURL(blob);
} }
if (ready.pdf_file.length) {
const blob = new Blob([ready.pdf_file], {
type: "application/octet-stream",
});
pdf_link = URL.createObjectURL(blob);
}
} catch (e) { } catch (e) {
console.error(e); console.error(e);
error = e; error = e;
@ -1518,7 +1529,7 @@
{#if !tauri_platform}{@html $t( {#if !tauri_platform}{@html $t(
"pages.login.trust_device_allow_cookies" "pages.login.trust_device_allow_cookies"
)}{/if}<br /><br /> )}{/if}<br /><br />
<Toggle bind:checked={options.trusted} <Toggle bind:checked={options.trusted} on:change={()=> {if (!options.trusted) options.pdf=false;}}
>{@html $t( >{@html $t(
"pages.wallet_create.save_wallet_options.trust_toggle" "pages.wallet_create.save_wallet_options.trust_toggle"
)}</Toggle )}</Toggle
@ -1575,7 +1586,7 @@
"pages.wallet_create.save_wallet_options.pdf_description" "pages.wallet_create.save_wallet_options.pdf_description"
)} )}
<br /><br /> <br /><br />
<Toggle disabled bind:checked={options.pdf} <Toggle bind:checked={options.pdf}
>{@html $t( >{@html $t(
"pages.wallet_create.save_wallet_options.pdf_toggle" "pages.wallet_create.save_wallet_options.pdf_toggle"
)}</Toggle )}</Toggle
@ -1718,6 +1729,47 @@
values: { download_name }, values: { download_name },
})} })}
{/if} {/if}
{#if pdf_link}
{@html $t(
"pages.wallet_create.download_pdf_description"
)}<br />
<a
href={pdf_link}
target="_blank"
download={pdf_name}
>
<button
tabindex="-1"
class:animate-bounce={animatePdf}
on:click={() => (animatePdf = false)}
class="mt-10 mb-8 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55"
>
<svg
class="w-8 h-8 mr-2 -ml-1"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m.75 12l3 3m0 0l3-3m-3 3v-6m-1.5-9H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z"
/>
</svg>
{@html $t("pages.wallet_create.download_pdf")}
</button>
</a>
<br />
{:else if options.pdf}
{@html $t("pages.wallet_create.download_pdf_done", {
values: { pdf_name },
})}
{/if}
<!-- Pazzle --> <!-- Pazzle -->
{@html $t("pages.wallet_create.your_pazzle")} {@html $t("pages.wallet_create.your_pazzle")}
</div> </div>
@ -1854,6 +1906,7 @@
creating = false; creating = false;
error = undefined; error = undefined;
animateDownload = true; animateDownload = true;
animatePdf = true;
}} }}
> >
{$t("buttons.start_over")} {$t("buttons.start_over")}

@ -566,9 +566,11 @@ pub async fn add_in_memory_wallet(lws_js: JsValue) -> Result<(), String> {
if !lws.in_memory { if !lws.in_memory {
return Err("This is not an in memory wallet".to_string()); return Err("This is not an in memory wallet".to_string());
} }
nextgraph::local_broker::wallet_add(lws) match nextgraph::local_broker::wallet_add(lws).await {
.await Ok(_) => Ok(()),
.map_err(|e: NgError| e.to_string()) Err(NgError::WalletAlreadyAdded) => Ok(()),
Err(e) => Err(e.to_string()),
}
} }
#[cfg(not(wasmpack_target = "nodejs"))] #[cfg(not(wasmpack_target = "nodejs"))]

@ -548,6 +548,7 @@ pub fn create_wallet_first_step_v0(
core_bootstrap: params.core_bootstrap.clone(), core_bootstrap: params.core_bootstrap.clone(),
core_registration: params.core_registration, core_registration: params.core_registration,
additional_bootstrap: params.additional_bootstrap.clone(), additional_bootstrap: params.additional_bootstrap.clone(),
pdf: params.pdf,
}; };
Ok(intermediary) Ok(intermediary)
} }
@ -773,6 +774,7 @@ pub async fn create_wallet_second_step_v0(
user, user,
in_memory: params.in_memory, in_memory: params.in_memory,
session_id: 0, session_id: 0,
pdf_file: vec![],
}, },
site, site,
brokers, brokers,
@ -832,6 +834,8 @@ mod test {
BootstrapContentV0::new_localhost(PubKey::nil()), BootstrapContentV0::new_localhost(PubKey::nil()),
None, None,
None, None,
false,
"test".to_string(),
)) ))
.expect("create_wallet_first_step_v0"); .expect("create_wallet_first_step_v0");

@ -1248,6 +1248,14 @@ pub struct CreateWalletV0 {
#[zeroize(skip)] #[zeroize(skip)]
/// Bootstrap of another server that you might use in order to connect to NextGraph network. It can be another interface on the same `core` server. /// Bootstrap of another server that you might use in order to connect to NextGraph network. It can be another interface on the same `core` server.
pub additional_bootstrap: Option<BootstrapContentV0>, pub additional_bootstrap: Option<BootstrapContentV0>,
#[zeroize(skip)]
/// Should generate a recovery PDF containing all the information of the wallet in plain text.
pub pdf: bool,
#[zeroize(skip)]
/// short name of the device
pub device_name: String,
} }
impl CreateWalletV0 { impl CreateWalletV0 {
@ -1261,6 +1269,8 @@ impl CreateWalletV0 {
core_bootstrap: BootstrapContentV0, core_bootstrap: BootstrapContentV0,
core_registration: Option<[u8; 32]>, core_registration: Option<[u8; 32]>,
additional_bootstrap: Option<BootstrapContentV0>, additional_bootstrap: Option<BootstrapContentV0>,
pdf: bool,
device_name: String,
) -> Self { ) -> Self {
CreateWalletV0 { CreateWalletV0 {
result_with_wallet_file: false, result_with_wallet_file: false,
@ -1274,6 +1284,8 @@ impl CreateWalletV0 {
core_bootstrap, core_bootstrap,
core_registration, core_registration,
additional_bootstrap, additional_bootstrap,
pdf,
device_name,
} }
} }
} }
@ -1324,6 +1336,10 @@ pub struct CreateWalletResultV0 {
pub in_memory: bool, pub in_memory: bool,
pub session_id: u64, pub session_id: u64,
#[serde(with = "serde_bytes")]
/// The PDF file that can be printed by the user
pub pdf_file: Vec<u8>,
} }
impl CreateWalletResultV0 { impl CreateWalletResultV0 {
@ -1369,6 +1385,8 @@ pub struct CreateWalletIntermediaryV0 {
pub core_registration: Option<[u8; 32]>, pub core_registration: Option<[u8; 32]>,
#[zeroize(skip)] #[zeroize(skip)]
pub additional_bootstrap: Option<BootstrapContentV0>, pub additional_bootstrap: Option<BootstrapContentV0>,
#[zeroize(skip)]
pub pdf: bool,
} }
#[derive(Debug, Eq, PartialEq, Clone)] #[derive(Debug, Eq, PartialEq, Clone)]
@ -1441,8 +1459,9 @@ pub struct NgQRCodeWalletTransferV0 {
pub is_rendezvous: bool, pub is_rendezvous: bool,
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Zeroize, ZeroizeOnDrop, Serialize, Deserialize)]
pub struct NgQRCodeWalletRecoveryV0 { pub struct NgQRCodeWalletRecoveryV0 {
#[zeroize(skip)]
pub wallet: WalletContentV0, //of which security_img is emptied pub wallet: WalletContentV0, //of which security_img is emptied
pub pazzle: Vec<u8>, pub pazzle: Vec<u8>,
pub mnemonic: [u16; 12], pub mnemonic: [u16; 12],

Loading…
Cancel
Save