import wallet file

pull/19/head
Niko PLP 1 year ago
parent 28a0427c3b
commit 85c99329f4
  1. 3
      Cargo.lock
  2. 4
      Cargo.toml
  3. 4
      ng-app/src-tauri/Cargo.toml
  4. 82
      ng-app/src-tauri/src/lib.rs
  5. 10
      ng-app/src/api.ts
  6. 30
      ng-app/src/lib/Login.svelte
  7. 84
      ng-app/src/routes/WalletLogin.svelte
  8. 68
      ng-sdk-js/src/lib.rs
  9. 94
      ng-wallet/src/lib.rs
  10. 101
      ng-wallet/src/types.rs
  11. 1
      p2p-repo/src/errors.rs

3
Cargo.lock generated

@ -5175,7 +5175,8 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-window" name = "tauri-plugin-window"
version = "2.0.0-alpha.1" version = "2.0.0-alpha.1"
source = "git+https://git.nextgraph.org/NextGraph/plugins-workspace.git?branch=window-alpha.1-nextgraph#0beae8162f66bcb8a1ec902738507f7ebe6b8a5e" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06f3c1d40ae1a1b2b837e7f9b8db80aa2ee8c4594f63d729d2abd69f0741225a"
dependencies = [ dependencies = [
"serde", "serde",
"tauri", "tauri",

@ -19,3 +19,7 @@ members = [
[profile.release] [profile.release]
lto = true lto = true
opt-level = 's' opt-level = 's'
[patch.crates-io]
# tauri = { git = "https://github.com/simonhyll/tauri.git", branch="fix/ipc-mixup"}
tauri = { git = "https://git.nextgraph.org/NextGraph/tauri.git", branch="alpha.11-nextgraph", features = ["no-ipc-custom-protocol"] }

@ -21,13 +21,15 @@ tauri-build = { version = "2.0.0-alpha.6", features = [] }
[dependencies] [dependencies]
tauri = { git = "https://git.nextgraph.org/NextGraph/tauri.git", branch="alpha.11-nextgraph", features = ["no-ipc-custom-protocol"] } tauri = { git = "https://git.nextgraph.org/NextGraph/tauri.git", branch="alpha.11-nextgraph", features = ["no-ipc-custom-protocol"] }
# tauri = { git = "https://github.com/simonhyll/tauri.git", branch="fix/ipc-mixup", features = [] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
p2p-repo = { path = "../../p2p-repo" } p2p-repo = { path = "../../p2p-repo" }
p2p-net = { path = "../../p2p-net" } p2p-net = { path = "../../p2p-net" }
ng-wallet = { path = "../../ng-wallet" } ng-wallet = { path = "../../ng-wallet" }
async-std = { version = "1.12.0", features = ["attributes", "unstable"] } async-std = { version = "1.12.0", features = ["attributes", "unstable"] }
tauri-plugin-window = { git = "https://git.nextgraph.org/NextGraph/plugins-workspace.git", branch="window-alpha.1-nextgraph" } # tauri-plugin-window = { git = "https://git.nextgraph.org/NextGraph/plugins-workspace.git", branch="window-alpha.1-nextgraph" }
tauri-plugin-window = "2.0.0-alpha.1"
[features] [features]
# this feature is used for production builds or when `devPath` points to the filesystem # this feature is used for production builds or when `devPath` points to the filesystem

@ -12,6 +12,7 @@ use ng_wallet::*;
use p2p_net::broker::*; use p2p_net::broker::*;
use p2p_net::types::{CreateAccountBSP, Invitation}; use p2p_net::types::{CreateAccountBSP, Invitation};
use p2p_net::utils::{decode_invitation_string, spawn_and_log_error, Receiver, ResultSend}; use p2p_net::utils::{decode_invitation_string, spawn_and_log_error, Receiver, ResultSend};
use p2p_repo::errors::NgError;
use p2p_repo::log::*; use p2p_repo::log::*;
use p2p_repo::types::*; use p2p_repo::types::*;
use std::collections::HashMap; use std::collections::HashMap;
@ -115,12 +116,13 @@ async fn save_wallet_locally(
.path() .path()
.resolve("wallets", BaseDirectory::AppLocalData) .resolve("wallets", BaseDirectory::AppLocalData)
.map_err(|_| ())?; .map_err(|_| ())?;
let sws = save_new_session(&res.wallet_name, res.wallet.id(), res.user, app.clone())?; let mut wallets: HashMap<String, LocalWalletStorageV0> =
let mut wallets: HashMap<String, LocalWalletStorageV0> = get_wallets_from_localstorage(app) get_wallets_from_localstorage(app.clone())
.await .await
.unwrap_or(Some(HashMap::new())) .unwrap_or(Some(HashMap::new()))
.unwrap_or(HashMap::new()); .unwrap_or(HashMap::new());
// TODO: check that the wallet is not already present in localStorage // check that the wallet is not already present in localStorage
if wallets.get(&res.wallet_name).is_none() {
let lws: LocalWalletStorageV0 = res.into(); let lws: LocalWalletStorageV0 = res.into();
wallets.insert(res.wallet_name.clone(), lws); wallets.insert(res.wallet_name.clone(), lws);
let lws_ser = LocalWalletStorage::v0_to_vec(wallets); let lws_ser = LocalWalletStorage::v0_to_vec(wallets);
@ -129,7 +131,77 @@ async fn save_wallet_locally(
log_debug!("write {:?} {}", path, r.unwrap_err()); log_debug!("write {:?} {}", path, r.unwrap_err());
return Err(()); return Err(());
} }
let sws = save_new_session(&res.wallet_name, res.wallet.id(), res.user, app)?;
Ok(sws) Ok(sws)
} else {
Err(())
}
}
#[tauri::command(rename_all = "snake_case")]
async fn wallet_open_file(file: Vec<u8>, app: tauri::AppHandle) -> Result<Wallet, String> {
let ngf: NgFile = file.try_into().map_err(|e: NgError| e.to_string())?;
if let NgFile::V0(NgFileV0::Wallet(wallet)) = ngf {
let mut wallets: HashMap<String, LocalWalletStorageV0> =
get_wallets_from_localstorage(app.clone())
.await
.unwrap_or(Some(HashMap::new()))
.unwrap_or(HashMap::new());
// check that the wallet is not already present in localStorage
let wallet_name = wallet.name();
if wallets.get(&wallet_name).is_none() {
Ok(wallet)
} else {
Err("Wallet already present on this device".to_string())
}
} else {
Err("File does not contain a wallet".to_string())
}
}
#[tauri::command(rename_all = "snake_case")]
async fn wallet_import(
previous_wallet: Wallet,
opened_wallet: EncryptedWallet,
app: tauri::AppHandle,
) -> Result<(), String> {
let path = app
.path()
.resolve("wallets", BaseDirectory::AppLocalData)
.map_err(|_| "wallet directory error".to_string())?;
let mut wallets: HashMap<String, LocalWalletStorageV0> =
get_wallets_from_localstorage(app.clone())
.await
.unwrap_or(Some(HashMap::new()))
.unwrap_or(HashMap::new());
// check that the wallet is not already present in localStorage
let EncryptedWallet::V0(mut opened_wallet_v0) = opened_wallet;
let wallet_name = opened_wallet_v0.wallet_id.clone();
if wallets.get(&wallet_name).is_none() {
let session = save_new_session(
&wallet_name,
opened_wallet_v0.wallet_privkey.to_pub(),
opened_wallet_v0.personal_site,
app,
)
.map_err(|_| "Cannot create new session".to_string())?;
let (wallet, client) = opened_wallet_v0
.import(previous_wallet, session)
.map_err(|e| e.to_string())?;
let lws = LocalWalletStorageV0::new(wallet, client);
wallets.insert(wallet_name, lws);
let lws_ser = LocalWalletStorage::v0_to_vec(wallets);
let r = write(path.clone(), &lws_ser);
if r.is_err() {
log_debug!("write {:?} {}", path, r.unwrap_err());
Err("Write error".to_string())
} else {
Ok(())
}
} else {
Err("Already present on this device".to_string())
}
} }
#[tauri::command(rename_all = "snake_case")] #[tauri::command(rename_all = "snake_case")]
@ -161,7 +233,7 @@ fn save_new_session(
.map_err(|_| ())?; .map_err(|_| ())?;
let session_v0 = create_new_session(wallet_id, user); let session_v0 = create_new_session(wallet_id, user);
if session_v0.is_err() { if session_v0.is_err() {
log_debug!("create_new_session {}", session_v0.unwrap_err()); log_debug!("create_new_session failed {}", session_v0.unwrap_err());
return Err(()); return Err(());
} }
let sws = session_v0.unwrap(); let sws = session_v0.unwrap();
@ -356,6 +428,8 @@ impl AppBuilder {
wallet_gen_shuffle_for_pin, wallet_gen_shuffle_for_pin,
wallet_open_wallet_with_pazzle, wallet_open_wallet_with_pazzle,
wallet_create_wallet, wallet_create_wallet,
wallet_open_file,
wallet_import,
encode_create_account, encode_create_account,
get_local_session, get_local_session,
get_wallets_from_localstorage, get_wallets_from_localstorage,

@ -18,6 +18,8 @@ const mapping = {
"wallet_gen_shuffle_for_pin": [], "wallet_gen_shuffle_for_pin": [],
"wallet_open_wallet_with_pazzle": ["wallet","pazzle","pin"], "wallet_open_wallet_with_pazzle": ["wallet","pazzle","pin"],
"wallet_create_wallet": ["params"], "wallet_create_wallet": ["params"],
"wallet_open_file": ["file"],
"wallet_import": ["previous_wallet","opened_wallet"],
"encode_create_account": ["payload"], "encode_create_account": ["payload"],
"get_local_session": ["id","key","user"], "get_local_session": ["id","key","user"],
"get_wallets_from_localstorage": [], "get_wallets_from_localstorage": [],
@ -122,6 +124,14 @@ const handler = {
params.result_with_wallet_file = false; params.result_with_wallet_file = false;
params.security_img = Array.from(new Uint8Array(params.security_img)); params.security_img = Array.from(new Uint8Array(params.security_img));
return await tauri.invoke(path[0],{params}) return await tauri.invoke(path[0],{params})
} else if (path[0] === "wallet_open_file") {
let file = args[0];
file = Array.from(new Uint8Array(file));
return await tauri.invoke(path[0],{file})
} else if (path[0] === "wallet_import") {
let previous_wallet = args[0];
previous_wallet.V0.content.security_img = Array.from(new Uint8Array(previous_wallet.V0.content.security_img));
return await tauri.invoke(path[0],{previous_wallet, opened_wallet:args[1]})
} else if (path[0] && path[0].startsWith("get_local_bootstrap")) { } else if (path[0] && path[0].startsWith("get_local_bootstrap")) {
return false; return false;
} else if (path[0] === "get_local_url") { } else if (path[0] === "get_local_url") {

@ -23,7 +23,7 @@
onMount(async () => { onMount(async () => {
await load_svg(); await load_svg();
console.log(wallet); //console.log(wallet);
await init(); await init();
}); });
@ -81,10 +81,10 @@
async function start_pin() { async function start_pin() {
pin_code = []; pin_code = [];
console.log(ordered); //console.log(ordered);
shuffle_pin = await ng.wallet_gen_shuffle_for_pin(); shuffle_pin = await ng.wallet_gen_shuffle_for_pin();
step = "pin"; step = "pin";
console.log(shuffle_pin); //console.log(shuffle_pin);
} }
function select(val) { function select(val) {
@ -92,10 +92,10 @@
let cat_idx = shuffle.category_indices[display]; let cat_idx = shuffle.category_indices[display];
let cat = emojis[emoji_cat[cat_idx]]; let cat = emojis[emoji_cat[cat_idx]];
let idx = shuffle.emoji_indices[display][val]; let idx = shuffle.emoji_indices[display][val];
console.log(cat_idx, emoji_cat[cat_idx], idx, cat[idx].code); //console.log(cat_idx, emoji_cat[cat_idx], idx, cat[idx].code);
selection.push({ cat: cat_idx, index: idx }); selection.push({ cat: cat_idx, index: idx });
console.log(selection); //console.log(selection);
if (display == pazzle_length - 1) { if (display == pazzle_length - 1) {
order(); order();
@ -113,8 +113,8 @@
pazzle.push((emoji.cat << 4) + emoji.index); pazzle.push((emoji.cat << 4) + emoji.index);
} }
console.log(pazzle); //console.log(pazzle);
console.log(wallet); //console.log(wallet);
// open the wallet // open the wallet
try { try {
@ -143,7 +143,7 @@
} }
async function pin(val) { async function pin(val) {
console.log(val); //console.log(val);
pin_code.push(val); pin_code.push(val);
if (pin_code.length == 4) { if (pin_code.length == 4) {
await finish(); await finish();
@ -152,8 +152,8 @@
async function select_order(val, pos) { async function select_order(val, pos) {
delete last_one[pos]; delete last_one[pos];
console.log(last_one); //console.log(last_one);
console.log(val); //console.log(val);
ordered.push(val); ordered.push(val);
val.sel = ordered.length; val.sel = ordered.length;
selection = selection; selection = selection;
@ -162,7 +162,7 @@
ordered.push(last); ordered.push(last);
last.sel = ordered.length; last.sel = ordered.length;
selection = selection; selection = selection;
console.log(last); //console.log(last);
await start_pin(); await start_pin();
} }
} }
@ -195,7 +195,9 @@
</div> </div>
{:else if step == "pazzle"} {:else if step == "pazzle"}
<div <div
class="h-screen aspect-[3/5] pazzleline min-w-[350px]" class="h-screen aspect-[3/5] pazzleline"
class:min-w-[350px]={mobile}
class:min-w-[500px]={!mobile}
class:max-w-[360px]={mobile} class:max-w-[360px]={mobile}
class:max-w-[600px]={!mobile} class:max-w-[600px]={!mobile}
> >
@ -218,7 +220,9 @@
{:else if step == "order"} {:else if step == "order"}
<!-- console.log(cat_idx, emoji_cat[cat_idx], idx, cat[idx].code); --> <!-- console.log(cat_idx, emoji_cat[cat_idx], idx, cat[idx].code); -->
<div <div
class="h-screen aspect-[3/3] pazzleline min-w-[350px]" class="h-screen aspect-[3/3] pazzleline"
class:min-w-[350px]={mobile}
class:min-w-[500px]={!mobile}
class:max-w-[360px]={mobile} class:max-w-[360px]={mobile}
class:max-w-[600px]={!mobile} class:max-w-[600px]={!mobile}
> >

@ -14,6 +14,7 @@
import { link, push } from "svelte-spa-router"; import { link, push } from "svelte-spa-router";
import Login from "../lib/Login.svelte"; import Login from "../lib/Login.svelte";
import ng from "../api"; import ng from "../api";
import { Fileupload } from "flowbite-svelte";
// @ts-ignore // @ts-ignore
import Logo from "../assets/nextgraph.svg?component"; import Logo from "../assets/nextgraph.svg?component";
import { import {
@ -27,6 +28,8 @@
let wallet; let wallet;
let selected; let selected;
let step; let step;
let error;
let importing = false;
let wallets_unsub; let wallets_unsub;
let opened_wallets_unsub; let opened_wallets_unsub;
@ -59,7 +62,7 @@
value.wallet.V0.wallet_privkey, value.wallet.V0.wallet_privkey,
value.wallet.V0.personal_site value.wallet.V0.personal_site
); );
console.log(session); //console.log(session);
if (session) { if (session) {
set_active_session(session); set_active_session(session);
loggedin(); loggedin();
@ -80,15 +83,29 @@
if (active_wallet_unsub) active_wallet_unsub(); if (active_wallet_unsub) active_wallet_unsub();
}); });
async function gotError(event) { async function gotError(event) {
importing = false;
console.error(event.detail); console.error(event.detail);
} }
async function gotWallet(event) { async function gotWallet(event) {
console.log(event.detail); //console.log(event.detail);
if (importing) {
try {
await ng.wallet_import(wallet, event.detail.wallet);
let walls = await ng.get_wallets_from_localstorage();
wallets.set(walls);
} catch (e) {
importing = false;
wallet = undefined;
error = e;
return;
}
}
active_wallet.set(event.detail); active_wallet.set(event.detail);
// wallet // wallet
// id // id
} }
function cancelLogin(event) { function cancelLogin(event) {
importing = false;
selected = undefined; selected = undefined;
wallet = undefined; wallet = undefined;
} }
@ -100,9 +117,57 @@
wallet = $wallets[selected]?.wallet; wallet = $wallets[selected]?.wallet;
} }
} }
function handleWalletUpload(event) {
const files = event.target.files;
if (files.length > 0) {
let reader = new FileReader();
reader.readAsArrayBuffer(files[0]);
reader.onload = async (e) => {
try {
//console.log(e.target.result);
wallet = await ng.wallet_open_file(e.target.result);
importing = true;
} catch (e) {
error = e;
}
};
}
}
</script> </script>
{#if wallet} {#if error}
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-red-800">
<svg
class="animate-bounce mt-10 h-16 w-16 mx-auto"
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="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z"
/>
</svg>
<p class="max-w-xl md:mx-auto lg:max-w-2xl mb-5">
An error occurred:<br />{error}
</p>
<button
on:click={() => {
importing = false;
error = undefined;
wallet = undefined;
}}
class="text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:outline-none 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 mb-2"
>
Start over
</button>
</div>
{:else if wallet}
<Login <Login
{wallet} {wallet}
on:error={gotError} on:error={gotError}
@ -146,8 +211,17 @@
{#if $has_wallets}<p class="mt-1">Log in with another wallet</p> {#if $has_wallets}<p class="mt-1">Log in with another wallet</p>
{:else}<p class="mt-1">Import your wallet</p> {:else}<p class="mt-1">Import your wallet</p>
{/if} {/if}
<Fileupload
style="display:none;"
id="import_wallet_file"
accept=".ngw, .txt"
on:change={handleWalletUpload}
/>
<button <button
class=" mt-1 text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none 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-100/55 mb-2" class=" mt-1 text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none 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-100/55 mb-2"
on:click={() => {
document.getElementById("import_wallet_file").click();
}}
> >
<svg <svg
class="w-8 h-8 mr-2 -ml-1" class="w-8 h-8 mr-2 -ml-1"
@ -238,7 +312,7 @@
</div> </div>
</div> </div>
</main> </main>
{:else if step == "security"}{:else if step == "qrcode"}{:else if step == "drop"}{:else if step == "cloud"}{:else if step == "loggedin"}you {:else if step == "security"}{:else if step == "qrcode"}{:else if step == "cloud"}{:else if step == "loggedin"}you
are logged in{/if} are logged in{/if}
<style> <style>
@ -249,7 +323,7 @@
position: relative; position: relative;
cursor: pointer; cursor: pointer;
} }
button { .wallet-box button {
min-width: 250px; min-width: 250px;
} }
.securitytxt { .securitytxt {

@ -30,6 +30,7 @@ use p2p_net::utils::{decode_invitation_string, spawn_and_log_error, Receiver, Re
use p2p_net::utils::{retrieve_local_bootstrap, retrieve_local_url}; use p2p_net::utils::{retrieve_local_bootstrap, retrieve_local_url};
use p2p_net::WS_PORT; use p2p_net::WS_PORT;
use p2p_repo::errors::NgError;
use p2p_repo::log::*; use p2p_repo::log::*;
use p2p_repo::types::*; use p2p_repo::types::*;
use p2p_repo::utils::generate_keypair; use p2p_repo::utils::generate_keypair;
@ -263,6 +264,73 @@ pub async fn wallet_create_wallet(js_params: JsValue) -> Result<JsValue, JsValue
} }
} }
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
pub async fn wallet_open_file(js_file: JsValue) -> Result<JsValue, String> {
let mut file = serde_wasm_bindgen::from_value::<serde_bytes::ByteBuf>(js_file)
.map_err(|_| "Deserialization error of file".to_string())?;
let ngf: NgFile = file
.into_vec()
.try_into()
.map_err(|e: NgError| e.to_string())?;
if let NgFile::V0(NgFileV0::Wallet(wallet)) = ngf {
let wallets: HashMap<String, LocalWalletStorageV0> =
get_local_wallets_v0().unwrap_or(HashMap::new());
// check that the wallet is not already present in localStorage
let wallet_name = wallet.name();
if wallets.get(&wallet_name).is_none() {
Ok(serde_wasm_bindgen::to_value(&wallet).unwrap())
} else {
Err("Wallet already present on this device".to_string())
}
} else {
Err("File does not contain a wallet".to_string())
}
}
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
pub async fn wallet_import(
js_previous_wallet: JsValue, //Wallet,
js_opened_wallet: JsValue, //EncryptedWallet
) -> Result<(), String> {
let previous_wallet = serde_wasm_bindgen::from_value::<Wallet>(js_previous_wallet)
.map_err(|_| "Deserialization error of Wallet".to_string())?;
let mut opened_wallet = serde_wasm_bindgen::from_value::<EncryptedWallet>(js_opened_wallet)
.map_err(|_| "Deserialization error of EncryptedWalletV0".to_string())?;
let EncryptedWallet::V0(mut opened_wallet_v0) = opened_wallet;
let mut wallets: HashMap<String, LocalWalletStorageV0> =
get_local_wallets_v0().unwrap_or(HashMap::new());
// check that the wallet is not already present in localStorage
let wallet_name = opened_wallet_v0.wallet_id.clone();
if wallets.get(&wallet_name).is_none() {
let session = save_new_session(
&wallet_name,
opened_wallet_v0.wallet_privkey.to_pub(),
opened_wallet_v0.personal_site,
)
.map_err(|e| format!("Cannot create new session: {e}"))?;
let (wallet, client) = opened_wallet_v0
.import(previous_wallet, session)
.map_err(|e| e.to_string())?;
let lws = LocalWalletStorageV0::new(wallet, client);
wallets.insert(wallet_name, lws);
let lws_ser = serde_bare::to_vec(&LocalWalletStorage::V0(wallets)).unwrap();
let encoded = base64_url::encode(&lws_ser);
let r = local_save("ng_wallets".to_string(), encoded);
if r.is_some() {
return Err(r.unwrap());
}
Ok(())
} else {
Err("Wallet already present on this device".to_string())
}
}
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
#[wasm_bindgen] #[wasm_bindgen]
pub fn test_create_wallet() -> JsValue { pub fn test_create_wallet() -> JsValue {

@ -35,13 +35,89 @@ use image::{imageops::FilterType, io::Reader as ImageReader, ImageOutputFormat};
use safe_transmute::transmute_to_bytes; use safe_transmute::transmute_to_bytes;
use p2p_net::types::{SiteType, SiteV0}; use p2p_net::types::{SiteType, SiteV0};
use p2p_repo::types::{PubKey, Timestamp}; use p2p_repo::types::{PubKey, Sig, Timestamp};
use p2p_repo::utils::{generate_keypair, now_timestamp, sign, verify}; use p2p_repo::utils::{generate_keypair, now_timestamp, sign, verify};
use p2p_repo::{log::*, types::PrivKey}; use p2p_repo::{log::*, types::PrivKey};
use rand::prelude::*; use rand::prelude::*;
use serde_bare::{from_slice, to_vec}; use serde_bare::{from_slice, to_vec};
use web_time::Instant; use web_time::Instant;
impl Wallet {
pub fn id(&self) -> WalletId {
match self {
Wallet::V0(v0) => v0.id,
}
}
pub fn content_as_bytes(&self) -> Vec<u8> {
match self {
Wallet::V0(v0) => serde_bare::to_vec(&v0.content).unwrap(),
}
}
pub fn sig(&self) -> Sig {
match self {
Wallet::V0(v0) => v0.sig,
}
}
pub fn pazzle_length(&self) -> u8 {
match self {
Wallet::V0(v0) => v0.content.pazzle_length,
}
}
pub fn name(&self) -> String {
match self {
Wallet::V0(v0) => base64_url::encode(&v0.id.slice()),
}
}
pub fn encrypt(
&self,
wallet_log: &WalletLog,
master_key: &[u8; 32],
peer_id: PubKey,
nonce: u64,
wallet_privkey: PrivKey,
) -> Result<Self, NgWalletError> {
let timestamp = now_timestamp();
let wallet_id = self.id();
let encrypted =
enc_wallet_log(wallet_log, master_key, peer_id, nonce, timestamp, wallet_id)?;
let mut wallet_content = match self {
Wallet::V0(v0) => v0.content.clone(),
};
wallet_content.timestamp = timestamp;
wallet_content.peer_id = peer_id;
wallet_content.nonce = nonce;
wallet_content.encrypted = encrypted;
let ser_wallet = serde_bare::to_vec(&wallet_content).unwrap();
let sig = sign(&wallet_privkey, &wallet_id, &ser_wallet).unwrap();
let wallet_v0 = WalletV0 {
/// ID
id: wallet_id,
/// Content
content: wallet_content,
/// Signature over content by wallet's private key
sig,
};
// let content = BootstrapContentV0 { servers: vec![] };
// let ser = serde_bare::to_vec(&content).unwrap();
// let sig = sign(wallet_key, wallet_id, &ser).unwrap();
// let bootstrap = Bootstrap::V0(BootstrapV0 {
// id: wallet_id,
// content,
// sig,
// });
Ok(Wallet::V0(wallet_v0))
}
}
pub fn enc_master_key( pub fn enc_master_key(
master_key: &[u8; 32], master_key: &[u8; 32],
key: &[u8; 32], key: &[u8; 32],
@ -161,7 +237,7 @@ pub fn create_new_session(
pub fn dec_encrypted_block( pub fn dec_encrypted_block(
mut ciphertext: Vec<u8>, mut ciphertext: Vec<u8>,
master_key: &mut [u8; 32], master_key: [u8; 32],
peer_id: PubKey, peer_id: PubKey,
nonce: u64, nonce: u64,
timestamp: Timestamp, timestamp: Timestamp,
@ -183,13 +259,13 @@ pub fn dec_encrypted_block(
let decrypted_log = let decrypted_log =
from_slice::<WalletLog>(&ciphertext).map_err(|e| NgWalletError::DecryptionError)?; from_slice::<WalletLog>(&ciphertext).map_err(|e| NgWalletError::DecryptionError)?;
master_key.zeroize(); //master_key.zeroize(); // this is now donw in the EncryptedWalletV0
// `ciphertext` now contains the decrypted block // `ciphertext` now contains the decrypted block
//log_debug!("decrypted_block {:?}", ciphertext); //log_debug!("decrypted_block {:?}", ciphertext);
match decrypted_log { match decrypted_log {
WalletLog::V0(v0) => v0.reduce(), WalletLog::V0(v0) => v0.reduce(master_key),
} }
} }
@ -244,7 +320,7 @@ pub fn open_wallet_with_pazzle(
//pazzle.zeroize(); //pazzle.zeroize();
pin.zeroize(); pin.zeroize();
let mut master_key = dec_master_key( let master_key = dec_master_key(
v0.content.enc_master_key_pazzle, v0.content.enc_master_key_pazzle,
&pazzle_key, &pazzle_key,
v0.content.master_nonce, v0.content.master_nonce,
@ -259,7 +335,7 @@ pub fn open_wallet_with_pazzle(
Ok(EncryptedWallet::V0(dec_encrypted_block( Ok(EncryptedWallet::V0(dec_encrypted_block(
v0.content.encrypted, v0.content.encrypted,
&mut master_key, master_key,
v0.content.peer_id, v0.content.peer_id,
v0.content.nonce, v0.content.nonce,
v0.content.timestamp, v0.content.timestamp,
@ -287,7 +363,7 @@ pub fn open_wallet_with_mnemonic(
mnemonic.zeroize(); mnemonic.zeroize();
pin.zeroize(); pin.zeroize();
let mut master_key = dec_master_key( let master_key = dec_master_key(
v0.content.enc_master_key_mnemonic, v0.content.enc_master_key_mnemonic,
&mnemonic_key, &mnemonic_key,
v0.content.master_nonce, v0.content.master_nonce,
@ -297,7 +373,7 @@ pub fn open_wallet_with_mnemonic(
Ok(EncryptedWallet::V0(dec_encrypted_block( Ok(EncryptedWallet::V0(dec_encrypted_block(
v0.content.encrypted, v0.content.encrypted,
&mut master_key, master_key,
v0.content.peer_id, v0.content.peer_id,
v0.content.nonce, v0.content.nonce,
v0.content.timestamp, v0.content.timestamp,
@ -497,7 +573,7 @@ pub async fn create_wallet_v0(
let user = site.site_key.to_pub(); let user = site.site_key.to_pub();
// Creating a new client // Creating a new client
let client = ClientV0::new(user); let client = ClientV0::new_with_auto_open(user);
let create_op = WalletOpCreateV0 { let create_op = WalletOpCreateV0 {
wallet_privkey: wallet_privkey.clone(), wallet_privkey: wallet_privkey.clone(),

@ -17,7 +17,9 @@ use serde::{Deserialize, Serialize};
use serde_big_array::BigArray; use serde_big_array::BigArray;
use p2p_net::types::*; use p2p_net::types::*;
use p2p_repo::errors::NgError;
use p2p_repo::types::*; use p2p_repo::types::*;
use p2p_repo::utils::{now_timestamp, sign};
/// WalletId is a PubKey /// WalletId is a PubKey
pub type WalletId = PubKey; pub type WalletId = PubKey;
@ -79,6 +81,17 @@ impl SessionWalletStorageV0 {
users: HashMap::new(), users: HashMap::new(),
} }
} }
pub fn get_first_user_peer_nonce(&self) -> Result<(PubKey, u64), NgWalletError> {
if self.users.len() > 1 {
panic!("get_first_user_peer_nonce does not work as soon as there are more than one user in SessionWalletStorageV0")
};
let first = self.users.values().next();
if first.is_none() {
return Err(NgWalletError::InternalError);
}
let sps = first.unwrap();
Ok((sps.peer_key.to_pub(), sps.last_wallet_nonce))
}
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
@ -107,6 +120,16 @@ impl From<&CreateWalletResultV0> for LocalWalletStorageV0 {
} }
} }
impl LocalWalletStorageV0 {
pub fn new(wallet: Wallet, client: ClientV0) -> Self {
LocalWalletStorageV0 {
bootstrap: BootstrapContent::V0(BootstrapContentV0 { servers: vec![] }),
wallet,
client: client.priv_key.to_pub(),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum LocalWalletStorage { pub enum LocalWalletStorage {
V0(HashMap<String, LocalWalletStorageV0>), V0(HashMap<String, LocalWalletStorageV0>),
@ -143,13 +166,21 @@ impl ClientV0 {
} }
} }
pub fn new(user: PubKey) -> Self { pub fn new_with_auto_open(user: PubKey) -> Self {
ClientV0 { ClientV0 {
priv_key: PrivKey::random_ed(), priv_key: PrivKey::random_ed(),
storage_master_key: SymKey::random(), storage_master_key: SymKey::random(),
auto_open: vec![Identity::IndividualSite(user)], auto_open: vec![Identity::IndividualSite(user)],
} }
} }
pub fn new() -> Self {
ClientV0 {
priv_key: PrivKey::random_ed(),
storage_master_key: SymKey::random(),
auto_open: vec![],
}
}
} }
/// Save to nextgraph.one /// Save to nextgraph.one
@ -199,9 +230,39 @@ pub struct EncryptedWalletV0 {
/// the format of the byte array (value) is up to the vendor, to serde as needed. /// the format of the byte array (value) is up to the vendor, to serde as needed.
#[zeroize(skip)] #[zeroize(skip)]
pub third_parties: HashMap<String, Vec<u8>>, pub third_parties: HashMap<String, Vec<u8>>,
#[zeroize(skip)]
pub log: Option<WalletLogV0>,
pub master_key: Option<[u8; 32]>,
} }
impl EncryptedWalletV0 { impl EncryptedWalletV0 {
pub fn import(
&mut self,
previous_wallet: Wallet,
session: SessionWalletStorageV0,
) -> Result<(Wallet, ClientV0), NgWalletError> {
if self.log.is_none() {
return Err(NgWalletError::InternalError);
}
// Creating a new client
let client = ClientV0::new_with_auto_open(self.personal_site);
self.add_client(client.clone());
let mut log = self.log.as_mut().unwrap();
log.add(WalletOperation::SetClientV0(client.clone()));
let (peer_id, nonce) = session.get_first_user_peer_nonce()?;
Ok((
previous_wallet.encrypt(
&WalletLog::V0(log.clone()),
self.master_key.as_ref().unwrap(),
peer_id,
nonce,
self.wallet_privkey.clone(),
)?,
client,
))
}
pub fn add_site(&mut self, site: SiteV0) { pub fn add_site(&mut self, site: SiteV0) {
let site_id = site.site_key.to_pub(); let site_id = site.site_key.to_pub();
let _ = self.sites.insert(site_id.to_string(), site); let _ = self.sites.insert(site_id.to_string(), site);
@ -312,11 +373,12 @@ impl WalletLogV0 {
} }
/// applies all the operation and produces an encrypted wallet object. /// applies all the operation and produces an encrypted wallet object.
pub fn reduce(&self) -> Result<EncryptedWalletV0, NgWalletError> { pub fn reduce(self, master_key: [u8; 32]) -> Result<EncryptedWalletV0, NgWalletError> {
if self.log.len() < 1 { if self.log.len() < 1 {
Err(NgWalletError::NoCreateWalletPresent) Err(NgWalletError::NoCreateWalletPresent)
} else if let (_, WalletOperation::CreateWalletV0(create_op)) = &self.log[0] { } else if let (_, WalletOperation::CreateWalletV0(create_op)) = &self.log[0] {
let mut wallet: EncryptedWalletV0 = create_op.into(); let mut wallet: EncryptedWalletV0 = create_op.into();
wallet.master_key = Some(master_key);
for op in &self.log { for op in &self.log {
match &op.1 { match &op.1 {
@ -420,6 +482,7 @@ impl WalletLogV0 {
} }
} }
log_debug!("reduced {:?}", wallet); log_debug!("reduced {:?}", wallet);
wallet.log = Some(self);
Ok(wallet) Ok(wallet)
} else { } else {
Err(NgWalletError::NoCreateWalletPresent) Err(NgWalletError::NoCreateWalletPresent)
@ -661,6 +724,8 @@ impl From<&WalletOpCreateV0> for EncryptedWalletV0 {
clients: HashMap::new(), clients: HashMap::new(),
overlay_core_overrides: HashMap::new(), overlay_core_overrides: HashMap::new(),
third_parties: HashMap::new(), third_parties: HashMap::new(),
log: None,
master_key: None,
}; };
wallet.add_site(op.personal_site.clone()); wallet.add_site(op.personal_site.clone());
//wallet.add_brokers(op.brokers.clone()); //wallet.add_brokers(op.brokers.clone());
@ -757,29 +822,6 @@ pub enum Wallet {
V0(WalletV0), V0(WalletV0),
} }
impl Wallet {
pub fn id(&self) -> WalletId {
match self {
Wallet::V0(v0) => v0.id,
}
}
pub fn content_as_bytes(&self) -> Vec<u8> {
match self {
Wallet::V0(v0) => serde_bare::to_vec(&v0.content).unwrap(),
}
}
pub fn sig(&self) -> Sig {
match self {
Wallet::V0(v0) => v0.sig,
}
}
pub fn pazzle_length(&self) -> u8 {
match self {
Wallet::V0(v0) => v0.content.pazzle_length,
}
}
}
/// Add Wallet Version 0 /// Add Wallet Version 0
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AddWalletV0 { pub struct AddWalletV0 {
@ -915,6 +957,7 @@ impl fmt::Display for NgWalletError {
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum NgFileV0 { pub enum NgFileV0 {
Wallet(Wallet), Wallet(Wallet),
Other,
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
@ -922,6 +965,14 @@ pub enum NgFile {
V0(NgFileV0), V0(NgFileV0),
} }
impl TryFrom<Vec<u8>> for NgFile {
type Error = NgError;
fn try_from(file: Vec<u8>) -> Result<Self, Self::Error> {
let ngf: Self = serde_bare::from_slice(&file).map_err(|_| NgError::InvalidFileFormat)?;
Ok(ngf)
}
}
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ShuffledPazzle { pub struct ShuffledPazzle {
pub category_indices: Vec<u8>, pub category_indices: Vec<u8>,

@ -22,6 +22,7 @@ pub enum NgError {
InvalidKey, InvalidKey,
InvalidInvitation, InvalidInvitation,
InvalidCreateAccount, InvalidCreateAccount,
InvalidFileFormat,
} }
impl Error for NgError {} impl Error for NgError {}

Loading…
Cancel
Save