create wallet and open wallet working from all apps

Niko 2 years ago
parent 284621e4b5
commit 593d9af16d
  1. 16
      Cargo.lock
  2. 1
      ng-app/src-tauri/Cargo.toml
  3. 49
      ng-app/src-tauri/src/lib.rs
  4. 2
      ng-app/src/App.svelte
  5. 6
      ng-app/src/api.ts
  6. 3
      ng-app/src/lib/Greet.svelte
  7. 335
      ng-app/src/routes/Grid.svelte
  8. 21
      ng-app/src/styles.css
  9. 1
      ng-sdk-js/Cargo.toml
  10. 72
      ng-sdk-js/src/lib.rs
  11. 4
      ng-wallet/Cargo.toml
  12. 181
      ng-wallet/src/lib.rs
  13. 61
      ng-wallet/src/types.rs
  14. 3
      p2p-repo/Cargo.toml
  15. 2
      p2p-repo/src/utils.rs

16
Cargo.lock generated

@ -2488,6 +2488,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"async-std", "async-std",
"debug_print", "debug_print",
"ng-wallet",
"p2p-net", "p2p-net",
"p2p-repo", "p2p-repo",
"serde", "serde",
@ -2506,6 +2507,7 @@ dependencies = [
"getrandom 0.1.16", "getrandom 0.1.16",
"gloo-timers", "gloo-timers",
"js-sys", "js-sys",
"ng-wallet",
"p2p-client-ws", "p2p-client-ws",
"p2p-net", "p2p-net",
"p2p-repo", "p2p-repo",
@ -2527,6 +2529,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"aes-gcm-siv", "aes-gcm-siv",
"argon2", "argon2",
"async-std",
"base64-url", "base64-url",
"chacha20poly1305", "chacha20poly1305",
"debug_print", "debug_print",
@ -2540,6 +2543,7 @@ dependencies = [
"serde-big-array", "serde-big-array",
"serde_bare", "serde_bare",
"serde_bytes", "serde_bytes",
"web-time",
] ]
[[package]] [[package]]
@ -2842,6 +2846,7 @@ dependencies = [
"serde", "serde",
"serde_bare", "serde_bare",
"serde_bytes", "serde_bytes",
"web-time",
] ]
[[package]] [[package]]
@ -5056,6 +5061,17 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "web-time"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19353897b48e2c4d849a2d73cb0aeb16dc2be4e00c565abfc11eb65a806e47de"
dependencies = [
"js-sys",
"once_cell",
"wasm-bindgen",
]
[[package]] [[package]]
name = "webkit2gtk" name = "webkit2gtk"
version = "0.19.2" version = "0.19.2"

@ -23,6 +23,7 @@ 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" }
async-std = { version = "1.12.0", features = ["attributes","unstable"] } async-std = { version = "1.12.0", features = ["attributes","unstable"] }
[features] [features]

@ -7,6 +7,8 @@
// notice may not be copied, modified, or distributed except // notice may not be copied, modified, or distributed except
// according to those terms. // according to those terms.
use async_std::stream::StreamExt; use async_std::stream::StreamExt;
use ng_wallet::types::*;
use ng_wallet::*;
use p2p_net::broker::*; use p2p_net::broker::*;
use p2p_net::log; use p2p_net::log;
use p2p_net::utils::{spawn_and_log_error, Receiver, ResultSend}; use p2p_net::utils::{spawn_and_log_error, Receiver, ResultSend};
@ -21,10 +23,10 @@ pub use mobile::*;
pub type SetupHook = Box<dyn FnOnce(&mut App) -> Result<(), Box<dyn std::error::Error>> + Send>; pub type SetupHook = Box<dyn FnOnce(&mut App) -> Result<(), Box<dyn std::error::Error>> + Send>;
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command // Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
#[tauri::command(rename_all = "snake_case")] // #[tauri::command(rename_all = "snake_case")]
fn greet(name: &str) -> String { // fn greet(name: &str) -> String {
format!("Hello, {}! You've been greeted from Rust!", name) // format!("Hello, {}! You've been greeted from Rust!", name)
} // }
#[tauri::command(rename_all = "snake_case")] #[tauri::command(rename_all = "snake_case")]
async fn test() -> Result<(), ()> { async fn test() -> Result<(), ()> {
@ -33,9 +35,34 @@ async fn test() -> Result<(), ()> {
} }
#[tauri::command(rename_all = "snake_case")] #[tauri::command(rename_all = "snake_case")]
async fn create_wallet(name: &str) -> Result<String, ()> { async fn wallet_gen_shuffle_for_pazzle_opening(pazzle_length: u8) -> Result<ShuffledPazzle, ()> {
log!("create wallet from rust {}", name); log!(
Ok(format!("create wallet from rust {}", name)) "wallet_gen_shuffle_for_pazzle_opening from rust {}",
pazzle_length
);
Ok(gen_shuffle_for_pazzle_opening(pazzle_length))
}
#[tauri::command(rename_all = "snake_case")]
async fn wallet_gen_shuffle_for_pin() -> Result<Vec<u8>, ()> {
log!("wallet_gen_shuffle_for_pin from rust");
Ok(gen_shuffle_for_pin())
}
#[tauri::command(rename_all = "snake_case")]
async fn wallet_open_wallet_with_pazzle(
wallet: Wallet,
pazzle: Vec<u8>,
pin: [u8; 4],
) -> Result<EncryptedWallet, String> {
log!("wallet_open_wallet_with_pazzle from rust {:?}", pazzle);
open_wallet_with_pazzle(wallet, pazzle, pin).map_err(|e| e.to_string())
}
#[tauri::command(rename_all = "snake_case")]
async fn wallet_create_wallet(params: CreateWalletV0) -> Result<CreateWalletResultV0, String> {
log!("wallet_create_wallet from rust {:?}", params);
create_wallet_v0(params).await.map_err(|e| e.to_string())
} }
#[tauri::command(rename_all = "snake_case")] #[tauri::command(rename_all = "snake_case")]
@ -138,11 +165,13 @@ impl AppBuilder {
}) })
.invoke_handler(tauri::generate_handler![ .invoke_handler(tauri::generate_handler![
test, test,
greet,
create_wallet,
doc_sync_branch, doc_sync_branch,
cancel_doc_sync_branch, cancel_doc_sync_branch,
doc_get_file_from_store_with_object_ref doc_get_file_from_store_with_object_ref,
wallet_gen_shuffle_for_pazzle_opening,
wallet_gen_shuffle_for_pin,
wallet_open_wallet_with_pazzle,
wallet_create_wallet,
]) ])
.run(tauri::generate_context!()) .run(tauri::generate_context!())
.expect("error while running tauri application"); .expect("error while running tauri application");

@ -11,7 +11,7 @@
<script lang="ts"> <script lang="ts">
// this line is needed to have the SDK working when compiling for a single file bundle (pnpm filebuild) // this line is needed to have the SDK working when compiling for a single file bundle (pnpm filebuild)
//import * as api from "ng-sdk-js"; // import * as api from "ng-sdk-js";
import Router from "svelte-spa-router"; import Router from "svelte-spa-router";
import { onMount, tick } from "svelte"; import { onMount, tick } from "svelte";

@ -10,8 +10,12 @@ import {createAsyncProxy} from "async-proxy";
import { writable } from "svelte/store"; import { writable } from "svelte/store";
const mapping = { const mapping = {
"create_wallet": [ "name" ],
"doc_get_file_from_store_with_object_ref": [ "nuri","obj_ref" ], "doc_get_file_from_store_with_object_ref": [ "nuri","obj_ref" ],
"wallet_gen_shuffle_for_pazzle_opening": ["pazzle_length"],
"wallet_gen_shuffle_for_pin": [],
"wallet_open_wallet_with_pazzle": ["wallet","pazzle","pin"],
"wallet_create_wallet": ["params"],
"test": [ ] "test": [ ]
} }

@ -25,6 +25,7 @@
return cache; return cache;
} }
try { try {
//console.log(JSON.stringify(ref));
let file = await ng.doc_get_file_from_store_with_object_ref("ng:", ref); let file = await ng.doc_get_file_from_store_with_object_ref("ng:", ref);
console.log(file); console.log(file);
var blob = new Blob([file["File"].V0.content], { var blob = new Blob([file["File"].V0.content], {
@ -40,7 +41,7 @@
} }
async function greet() { async function greet() {
greetMsg = await ng.create_wallet(name); //greetMsg = await ng.create_wallet(name);
// cancel = await ng.doc_sync_branch("ok", async (commit) => { // cancel = await ng.doc_sync_branch("ok", async (commit) => {
// console.log(commit); // console.log(commit);
// try { // try {

@ -10,6 +10,9 @@
--> -->
<script lang="ts"> <script lang="ts">
import { onMount } from "svelte";
import ng from "../api";
let face = [ let face = [
{ {
hexcode: "1f600", hexcode: "1f600",
@ -435,7 +438,7 @@
{ {
hexcode: "1f3c7", hexcode: "1f3c7",
shortcode: "horse_racing", shortcode: "horse_racing",
code: "horse", code: "horse_riding",
}, },
{ {
hexcode: "26f7", hexcode: "26f7",
@ -986,7 +989,7 @@
{ {
hexcode: "26f5", hexcode: "26f5",
shortcode: "sailboat", shortcode: "sailboat",
code: "boat", code: "sailboat",
}, },
{ {
@ -1255,47 +1258,6 @@
}, },
]; ];
let emojis = {
face,
face_unwell,
face_costume,
emotion,
body,
sport,
mammal,
fauna,
flora,
greens,
foods,
travel,
sky,
play,
house,
};
let emojis2 = {};
let emoji_cat = [
"face",
"face_unwell",
"face_costume",
"emotion",
"body",
"sport",
"mammal",
"fauna",
"flora",
"greens",
"foods",
"travel",
"sky",
"play",
"house",
];
let display = "body";
import { onMount } from "svelte";
onMount(async () => { onMount(async () => {
face[0].svg = await import("../assets/pazzle/emoji_u1f600.svg?component"); face[0].svg = await import("../assets/pazzle/emoji_u1f600.svg?component");
face[1].svg = await import("../assets/pazzle/emoji_u1f602.svg?component"); face[1].svg = await import("../assets/pazzle/emoji_u1f602.svg?component");
@ -1737,23 +1699,284 @@
house[13].svg = await import("../assets/pazzle/emoji_u1f9fd.svg?component"); house[13].svg = await import("../assets/pazzle/emoji_u1f9fd.svg?component");
house[14].svg = await import("../assets/pazzle/emoji_u1f6d2.svg?component"); house[14].svg = await import("../assets/pazzle/emoji_u1f6d2.svg?component");
for (const cat of emoji_cat) { shuffle = await ng.wallet_gen_shuffle_for_pazzle_opening(pazzle_length);
emojis2[cat] = emojis[cat];
for (const [idx, cat_idx] of shuffle.category_indices.entries()) {
let cat = emojis[emoji_cat[cat_idx]];
let items = [];
for (const id of shuffle.emoji_indices[idx]) {
items.push(cat[id]);
}
emojis2.push(items);
}
emojis2 = emojis2;
//console.log(JSON.stringify(await ng.test_create_wallet()));
//console.log(await ng.test_create_wallet());
let ref = {
id: {
Blake3Digest32: [
228, 228, 181, 117, 36, 206, 41, 223, 130, 96, 85, 195, 104, 137, 78,
145, 42, 176, 58, 244, 111, 97, 246, 39, 11, 76, 135, 150, 188, 111,
66, 33,
],
},
key: {
ChaCha20Key: [
100, 243, 39, 242, 203, 131, 102, 50, 9, 54, 248, 113, 4, 160, 28, 45,
73, 56, 217, 112, 95, 150, 144, 137, 9, 57, 106, 5, 39, 202, 146, 94,
],
},
};
let img = await ng.doc_get_file_from_store_with_object_ref("ng:", ref);
let c = {
security_img: Array.from(img["File"].V0.content),
security_txt: " know yourself ",
pin: [5, 2, 9, 1],
pazzle_length: 9,
send_bootstrap: undefined,
send_wallet: false,
peer_id: {
Ed25519PubKey: [
119, 251, 253, 29, 135, 199, 254, 50, 134, 67, 1, 208, 117, 196, 167,
107, 2, 113, 98, 243, 49, 90, 7, 0, 157, 58, 14, 187, 14, 3, 116, 86,
],
},
nonce: 0,
};
try {
let res = await ng.wallet_create_wallet(c);
console.log(res);
wallet = res.wallet;
for (const emoji of res.pazzle) {
let cat = (emoji & 240) >> 4;
let idx = emoji & 15;
console.log(emoji_cat[cat], emojis[emoji_cat[cat]][idx].code);
}
} catch (e) {
console.error(e);
} }
//await start_pin();
}); });
let wallet;
let emojis = {
face,
face_unwell,
face_costume,
emotion,
body,
sport,
mammal,
fauna,
flora,
greens,
foods,
travel,
sky,
play,
house,
};
let emojis2 = [];
let emoji_cat = [
"face",
"sport",
"mammal",
"fauna",
"flora",
"greens",
"foods",
"travel",
"sky",
"body",
"face_unwell",
"house",
"play",
"face_costume",
"emotion",
];
let shuffle;
let step = "pazzle";
let pazzle_length = 9;
let display = 0;
let selection = [];
let pin_code = [];
let ordered = [];
let last_one = {};
let shuffle_pin;
function order() {
step = "order";
last_one = {};
for (let i = 0; i < pazzle_length; i++) {
last_one[i] = true;
}
}
async function start_pin() {
console.log(ordered);
shuffle_pin = await ng.wallet_gen_shuffle_for_pin();
step = "pin";
console.log(shuffle_pin);
}
function select(val) {
//console.log(emojis2[display][val]);
let cat_idx = shuffle.category_indices[display];
let cat = emojis[emoji_cat[cat_idx]];
let idx = shuffle.emoji_indices[display][val];
console.log(cat_idx, emoji_cat[cat_idx], idx, cat[idx].code);
selection.push({ cat: cat_idx, index: idx });
console.log(selection);
if (display == pazzle_length - 1) {
order();
} else {
display = display + 1;
}
}
async function finish() {
step = "end";
let pazzle = [];
for (const emoji of ordered) {
pazzle.push((emoji.cat << 4) + emoji.index);
}
console.log(pazzle);
// open the wallet
try {
let secret_wallet = await ng.wallet_open_wallet_with_pazzle(
wallet,
pazzle,
pin_code
);
console.log(secret_wallet);
} catch (e) {
console.error(e);
}
// display the result
}
async function pin(val) {
console.log(val);
pin_code.push(val);
if (pin_code.length == 4) {
await finish();
}
}
async function select_order(val, pos) {
delete last_one[pos];
console.log(last_one);
console.log(val);
ordered.push(val);
val.sel = ordered.length;
selection = selection;
if (ordered.length == pazzle_length - 1) {
let last = selection[Object.keys(last_one)[0]];
ordered.push(last);
last.sel = ordered.length;
selection = selection;
console.log(last);
await start_pin();
}
}
</script> </script>
<div class="h-screen aspect-[3/5] pazzleline max-w-[500px] min-w-[200px]"> {#if step == "pazzle"}
{#each [0, 1, 2, 3, 4] as row} <div class="h-screen aspect-[3/5] pazzleline max-w-[500px] min-w-[200px]">
<div class="columns-3 gap-0"> {#each [0, 1, 2, 3, 4] as row}
{#each emojis2[display]?.slice(0 + row * 3, 3 + row * 3) || [] as emoji} <div class="columns-3 gap-0">
<div class="w-full aspect-square emoji"> {#each emojis2[display]?.slice(0 + row * 3, 3 + row * 3) || [] as emoji, i}
<svelte:component this={emoji.svg?.default} /> <div
</div> role="button"
{/each} tabindex="0"
</div> class="w-full aspect-square emoji"
{/each} on:click={() => select(row * 3 + i)}
</div> on:keypress={() => select(row * 3 + i)}
>
<svelte:component this={emoji.svg?.default} />
</div>
{/each}
</div>
{/each}
</div>
{:else if step == "order"}
<!-- console.log(cat_idx, emoji_cat[cat_idx], idx, cat[idx].code); -->
<div class="h-screen aspect-[3/3] pazzleline max-w-[500px] min-w-[200px]">
{#each [0, 1, 2] as row}
<div class="columns-3 gap-0">
{#each selection.slice(0 + row * 3, 3 + row * 3) || [] as emoji, i}
{#if !emoji.sel}
<div
role="button"
tabindex="0"
class="w-full aspect-square emoji"
on:click={() => select_order(emoji, row * 3 + i)}
on:keypress={() => select_order(emoji, row * 3 + i)}
>
<svelte:component
this={emojis[emoji_cat[emoji.cat]][emoji.index].svg?.default}
/>
</div>
{:else}
<div class="w-full aspect-square opacity-25 select-none sel-emoji">
<svelte:component
this={emojis[emoji_cat[emoji.cat]][emoji.index].svg?.default}
/>
<span class="sel drop-shadow-[2px_2px_2px_rgba(255,255,255,1)]"
>{emoji.sel}</span
>
</div>
{/if}
{/each}
</div>
{/each}
</div>
{:else if step == "pin"}
<div class="aspect-[5/2] pazzleline max-w-[800px] min-w-[200px] mt-20">
{#each [0, 1] as row}
<div class="columns-5 gap-0">
{#each shuffle_pin.slice(0 + row * 5, 5 + row * 5) as num, i}
<div
role="button"
tabindex="0"
class="w-full aspect-square pin align-bottom text-9xl"
on:click={async () => await pin(num)}
on:keypress={async () => await pin(num)}
>
<span>{num}</span>
</div>
{/each}
</div>
{/each}
</div>
{:else if step == "end"}{/if}
<style> <style>
</style> </style>

@ -30,8 +30,29 @@
margin-left: auto; margin-left: auto;
} }
.pin {
cursor: pointer;
text-align: center;
}
.sel {
position: relative;
top: -56%;
font-size: 100px;
left: 30%;
font-weight: 700;
}
.sel-emoji {
overflow: hidden;
}
.emoji { .emoji {
cursor: pointer; cursor: pointer;
/* padding: 0;
margin: 0;
border: 0;
box-shadow: none; */
} }
.container2 { .container2 {

@ -19,6 +19,7 @@ ws_stream_wasm = "0.7"
p2p-net = { path = "../p2p-net" } p2p-net = { path = "../p2p-net" }
p2p-repo = { path = "../p2p-repo" } p2p-repo = { path = "../p2p-repo" }
p2p-client-ws = { path = "../p2p-client-ws" } p2p-client-ws = { path = "../p2p-client-ws" }
ng-wallet = { path = "../ng-wallet" }
async-std = { version = "1.12.0", features = ["attributes","unstable"] } async-std = { version = "1.12.0", features = ["attributes","unstable"] }
futures = "0.3.24" futures = "0.3.24"
pharos = "0.5" pharos = "0.5"

@ -15,6 +15,8 @@ use async_std::task;
use async_std::stream::StreamExt; use async_std::stream::StreamExt;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use js_sys::Uint8Array; use js_sys::Uint8Array;
use ng_wallet::types::*;
use ng_wallet::*;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use p2p_client_ws::remote_ws_wasm::ConnectionWebSocket; use p2p_client_ws::remote_ws_wasm::ConnectionWebSocket;
use p2p_net::broker::*; use p2p_net::broker::*;
@ -31,9 +33,68 @@ use std::sync::Arc;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::{future_to_promise, JsFuture}; use wasm_bindgen_futures::{future_to_promise, JsFuture};
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen] #[wasm_bindgen]
extern "C" { pub fn wallet_gen_shuffle_for_pazzle_opening(pazzle_length: u8) -> JsValue {
pub fn alert(s: &str); let res = gen_shuffle_for_pazzle_opening(pazzle_length);
serde_wasm_bindgen::to_value(&res).unwrap()
}
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
pub fn wallet_gen_shuffle_for_pin() -> Vec<u8> {
gen_shuffle_for_pin()
}
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
pub fn wallet_open_wallet_with_pazzle(
js_wallet: JsValue,
pazzle: Vec<u8>,
js_pin: JsValue,
) -> Result<JsValue, JsValue> {
let wallet = serde_wasm_bindgen::from_value::<Wallet>(js_wallet)
.map_err(|_| "Deserialization error of wallet")?;
let pin = serde_wasm_bindgen::from_value::<[u8; 4]>(js_pin)
.map_err(|_| "Deserialization error of pin")?;
let res = open_wallet_with_pazzle(wallet, pazzle, pin);
match res {
Ok(r) => Ok(serde_wasm_bindgen::to_value(&r).unwrap()),
Err(e) => Err(e.to_string().into()),
}
}
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
pub async fn wallet_create_wallet(js_params: JsValue) -> Result<JsValue, JsValue> {
let params = serde_wasm_bindgen::from_value::<CreateWalletV0>(js_params)
.map_err(|_| "Deserialization error of args")?;
let res = create_wallet_v0(params).await;
match res {
Ok(r) => Ok(serde_wasm_bindgen::to_value(&r).unwrap()),
Err(e) => Err(e.to_string().into()),
}
}
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
pub fn test_create_wallet() -> JsValue {
let pin = [5, 2, 9, 1];
let r = CreateWalletV0::new(
vec![50u8; 20],
" know yourself ".to_string(),
pin,
9,
None,
false,
PubKey::Ed25519PubKey([
119, 251, 253, 29, 135, 199, 254, 50, 134, 67, 1, 208, 117, 196, 167, 107, 2, 113, 98,
243, 49, 90, 7, 0, 157, 58, 14, 187, 14, 3, 116, 86,
]),
0,
);
serde_wasm_bindgen::to_value(&r).unwrap()
} }
#[cfg(wasmpack_target = "nodejs")] #[cfg(wasmpack_target = "nodejs")]
@ -48,13 +109,6 @@ extern "C" {
fn random(max: usize) -> usize; fn random(max: usize) -> usize;
} }
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
pub async fn create_wallet(s: String) -> String {
log!("create wallet {} {}", s, BROKER.read().await.test());
format!("create wallet from js {}", s)
}
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
#[wasm_bindgen] #[wasm_bindgen]
pub async fn test() { pub async fn test() {

@ -24,4 +24,6 @@ chacha20poly1305 = "0.10.1"
argon2 = "0.5.0" argon2 = "0.5.0"
safe-transmute = "0.11.2" safe-transmute = "0.11.2"
aes-gcm-siv = {version = "0.11.1", features = ["aes","heapless","getrandom","std"] } aes-gcm-siv = {version = "0.11.1", features = ["aes","heapless","getrandom","std"] }
base64-url = "2.0.0" base64-url = "2.0.0"
async-std = { version = "1.12.0", features = ["attributes","unstable"] }
web-time = "0.2.0"

@ -12,6 +12,8 @@
#[macro_use] #[macro_use]
extern crate p2p_net; extern crate p2p_net;
use p2p_net::log;
pub mod types; pub mod types;
pub mod bip39; pub mod bip39;
@ -32,7 +34,7 @@ use safe_transmute::transmute_to_bytes;
use p2p_repo::types::{PubKey, Site, SiteType, Timestamp}; use p2p_repo::types::{PubKey, Site, SiteType, Timestamp};
use p2p_repo::utils::{generate_keypair, now_timestamp, sign, verify}; use p2p_repo::utils::{generate_keypair, now_timestamp, sign, verify};
use rand::{thread_rng, Rng}; use rand::prelude::*;
use serde_bare::{from_slice, to_vec}; use serde_bare::{from_slice, to_vec};
pub fn enc_master_key( pub fn enc_master_key(
@ -167,12 +169,20 @@ pub fn derive_key_from_pass(pass: Vec<u8>, salt: [u8; 16], wallet_id: WalletId)
argon.hash_password_into(&pass, &salt, &mut out).unwrap(); argon.hash_password_into(&pass, &salt, &mut out).unwrap();
out out
} }
use web_time::Instant;
pub fn open_wallet_with_pazzle( pub fn open_wallet_with_pazzle(
wallet: Wallet, wallet: Wallet,
pazzle: Vec<u8>, pazzle: Vec<u8>,
pin: [u8; 4], pin: [u8; 4],
) -> Result<EncryptedWallet, NgWalletError> { ) -> Result<EncryptedWallet, NgWalletError> {
// each digit shouldnt be greater than 9
if pin[0] > 9 || pin[1] > 9 || pin[2] > 9 || pin[3] > 9 {
return Err(NgWalletError::InvalidPin);
}
let opening_pazzle = Instant::now();
verify(&wallet.content_as_bytes(), wallet.sig(), wallet.id()) verify(&wallet.content_as_bytes(), wallet.sig(), wallet.id())
.map_err(|e| NgWalletError::InvalidSignature)?; .map_err(|e| NgWalletError::InvalidSignature)?;
@ -191,6 +201,11 @@ pub fn open_wallet_with_pazzle(
v0.id, v0.id,
)?; )?;
log!(
"opening of wallet with pazzle took: {} ms",
opening_pazzle.elapsed().as_millis()
);
Ok(EncryptedWallet::V0(dec_encrypted_block( Ok(EncryptedWallet::V0(dec_encrypted_block(
v0.content.encrypted, v0.content.encrypted,
master_key, master_key,
@ -246,65 +261,106 @@ pub fn display_mnemonic(mnemonic: &[u16; 12]) -> Vec<String> {
res res
} }
pub fn display_pazzle(pazzle: Vec<u8>) -> Vec<String> {
// let res: Vec<String> = pazzle
// .into_iter()
// .map(|i| String::from(bip39_wordlist[*i as usize]))
// .collect();
// res
unimplemented!();
}
pub fn gen_shuffle_for_pazzle_opening(pazzle_length: u8) -> ShuffledPazzle {
let mut rng = rand::thread_rng();
let mut category_indices: Vec<u8> = (0..pazzle_length).collect();
//log!("{:?}", category_indices);
category_indices.shuffle(&mut rng);
//log!("{:?}", category_indices);
let mut emoji_indices: Vec<Vec<u8>> = Vec::with_capacity(pazzle_length.into());
for _ in 0..pazzle_length {
let mut idx: Vec<u8> = (0..15).collect();
//log!("{:?}", idx);
idx.shuffle(&mut rng);
//log!("{:?}", idx);
emoji_indices.push(idx)
}
ShuffledPazzle {
category_indices,
emoji_indices,
}
}
pub fn gen_shuffle_for_pin() -> Vec<u8> {
let mut rng = rand::thread_rng();
let mut digits: Vec<u8> = (0..10).collect();
//log!("{:?}", digits);
digits.shuffle(&mut rng);
//log!("{:?}", digits);
digits
}
/// creates a Wallet from a pin, a security text and image (with option to send the bootstrap and wallet to nextgraph.one) /// creates a Wallet from a pin, a security text and image (with option to send the bootstrap and wallet to nextgraph.one)
/// and returns the Wallet, the pazzle and the mnemonic /// and returns the Wallet, the pazzle and the mnemonic
pub fn create_wallet_v0( pub async fn create_wallet_v0(
security_img: Vec<u8>, params: CreateWalletV0,
security_txt: String, ) -> Result<CreateWalletResultV0, NgWalletError> {
pin: [u8; 4],
pazzle_length: u8,
send_bootstrap: Option<&Bootstrap>,
send_wallet: bool,
peer_id: PubKey,
nonce: u64,
) -> Result<(Wallet, Vec<u8>, [u16; 12]), NgWalletError> {
// TODO : use some automatically zeroed variable for the 2 first arguments, and for the returned values // TODO : use some automatically zeroed variable for the 2 first arguments, and for the returned values
let creating_pazzle = Instant::now();
// pazzle_length can only be 9, 12, or 15 // pazzle_length can only be 9, 12, or 15
if (pazzle_length != 9 && pazzle_length != 12 && pazzle_length != 15) { if (params.pazzle_length != 9 && params.pazzle_length != 12 && params.pazzle_length != 15) {
return Err(NgWalletError::InvalidPazzleLength); return Err(NgWalletError::InvalidPazzleLength);
} }
// cannot submit wallet if we don't submit also the bootstrap // cannot submit wallet if we don't submit also the bootstrap
if send_bootstrap.is_none() && send_wallet { if params.send_bootstrap.is_none() && params.send_wallet {
return Err(NgWalletError::SubmissionError); return Err(NgWalletError::SubmissionError);
} }
// check validity of PIN // check validity of PIN
// shouldn't start with 0 // shouldn't start with 0
if pin[0] == 0 { if params.pin[0] == 0 {
return Err(NgWalletError::InvalidPin); return Err(NgWalletError::InvalidPin);
} }
// each digit shouldnt be greater than 9 // each digit shouldnt be greater than 9
if pin[0] > 9 || pin[1] > 9 || pin[2] > 9 || pin[3] > 9 { if params.pin[0] > 9 || params.pin[1] > 9 || params.pin[2] > 9 || params.pin[3] > 9 {
return Err(NgWalletError::InvalidPin); return Err(NgWalletError::InvalidPin);
} }
// check for uniqueness of each digit // check for uniqueness of each digit
if pin[1] == pin[0] if params.pin[1] == params.pin[0]
|| pin[1] == pin[2] || params.pin[1] == params.pin[2]
|| pin[1] == pin[3] || params.pin[1] == params.pin[3]
|| pin[2] == pin[0] || params.pin[2] == params.pin[0]
|| pin[2] == pin[3] || params.pin[2] == params.pin[3]
|| pin[3] == pin[0] || params.pin[3] == params.pin[0]
{ {
return Err(NgWalletError::InvalidPin); return Err(NgWalletError::InvalidPin);
} }
// check for ascending series // check for ascending series
if pin[1] == pin[0] + 1 && pin[2] == pin[1] + 1 && pin[3] == pin[2] + 1 { 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); return Err(NgWalletError::InvalidPin);
} }
// check for descending series // check for descending series
if pin[3] >= 3 && pin[2] == pin[3] - 1 && pin[1] == pin[2] - 1 && pin[0] == pin[1] - 1 { 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); return Err(NgWalletError::InvalidPin);
} }
// check validity of security text // check validity of security text
let words: Vec<_> = security_txt.split_whitespace().collect(); let words: Vec<_> = params.security_txt.split_whitespace().collect();
let new_string = words.join(" "); let new_string = words.join(" ");
let count = new_string.chars().count(); let count = new_string.chars().count();
if count < 10 || count > 100 { if count < 10 || count > 100 {
@ -312,7 +368,7 @@ pub fn create_wallet_v0(
} }
// check validity of image // check validity of image
let decoded_img = ImageReader::new(Cursor::new(security_img)) let decoded_img = ImageReader::new(Cursor::new(params.security_img))
.with_guessed_format() .with_guessed_format()
.map_err(|e| NgWalletError::InvalidSecurityImage)? .map_err(|e| NgWalletError::InvalidSecurityImage)?
.decode() .decode()
@ -339,10 +395,14 @@ pub fn create_wallet_v0(
// let mut pazzle_random = vec![0u8; pazzle_length.into()]; // let mut pazzle_random = vec![0u8; pazzle_length.into()];
// getrandom::getrandom(&mut pazzle_random).map_err(|e| NgWalletError::InternalError)?; // getrandom::getrandom(&mut pazzle_random).map_err(|e| NgWalletError::InternalError)?;
let mut pazzle = vec![0u8; pazzle_length.into()];
let mut ran = thread_rng(); let mut ran = thread_rng();
for i in &mut pazzle {
*i = ran.gen_range(0, 16); let mut category_indices: Vec<u8> = (0..params.pazzle_length).collect();
category_indices.shuffle(&mut ran);
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);
} }
//println!("pazzle {:?}", pazzle); //println!("pazzle {:?}", pazzle);
@ -361,7 +421,7 @@ pub fn create_wallet_v0(
let encrypted_block = EncryptedWalletV0 { let encrypted_block = EncryptedWalletV0 {
pazzle: pazzle.clone(), pazzle: pazzle.clone(),
mnemonic, mnemonic,
pin, pin: params.pin,
sites: vec![site], sites: vec![site],
}; };
@ -375,13 +435,13 @@ pub fn create_wallet_v0(
//println!("salt_mnemonic {:?}", salt_mnemonic); //println!("salt_mnemonic {:?}", salt_mnemonic);
let pazzle_key = derive_key_from_pass( let pazzle_key = derive_key_from_pass(
[pazzle.clone(), pin.to_vec()].concat(), [pazzle.clone(), params.pin.to_vec()].concat(),
salt_pazzle, salt_pazzle,
wallet_id, wallet_id,
); );
let mnemonic_key = derive_key_from_pass( let mnemonic_key = derive_key_from_pass(
[transmute_to_bytes(&mnemonic), &pin].concat(), [transmute_to_bytes(&mnemonic), &params.pin].concat(),
salt_mnemonic, salt_mnemonic,
wallet_id, wallet_id,
); );
@ -394,11 +454,12 @@ pub fn create_wallet_v0(
let enc_master_key_mnemonic = enc_master_key(master_key, mnemonic_key, 0, wallet_id)?; let enc_master_key_mnemonic = enc_master_key(master_key, mnemonic_key, 0, wallet_id)?;
let timestamp = now_timestamp(); let timestamp = now_timestamp();
let encrypted = enc_encrypted_block( let encrypted = enc_encrypted_block(
&encrypted_block, &encrypted_block,
master_key, master_key,
peer_id, params.peer_id,
nonce, params.nonce,
timestamp, timestamp,
wallet_id, wallet_id,
)?; )?;
@ -412,12 +473,13 @@ pub fn create_wallet_v0(
enc_master_key_mnemonic, enc_master_key_mnemonic,
master_nonce: 0, master_nonce: 0,
timestamp, timestamp,
peer_id, peer_id: params.peer_id,
nonce, nonce: params.nonce,
encrypted, encrypted,
}; };
let ser_wallet = serde_bare::to_vec(&wallet_content).unwrap(); let ser_wallet = serde_bare::to_vec(&wallet_content).unwrap();
let sig = sign(wallet_key, wallet_id, &ser_wallet).unwrap(); let sig = sign(wallet_key, wallet_id, &ser_wallet).unwrap();
let wallet_v0 = WalletV0 { let wallet_v0 = WalletV0 {
@ -442,7 +504,16 @@ pub fn create_wallet_v0(
// TODO send bootstrap (if) // TODO send bootstrap (if)
// TODO send wallet (if) // TODO send wallet (if)
Ok((Wallet::V0(wallet_v0), pazzle, mnemonic)) log!(
"creating of wallet took: {} ms",
creating_pazzle.elapsed().as_millis()
);
Ok(CreateWalletResultV0 {
wallet: Wallet::V0(wallet_v0),
pazzle,
mnemonic,
})
} }
#[cfg(test)] #[cfg(test)]
@ -456,7 +527,19 @@ mod tests {
use std::time::Instant; use std::time::Instant;
#[test] #[test]
fn create_wallet() { fn test_gen_shuffle() {
let shuffle = gen_shuffle_for_pazzle_opening(9);
log!("{:?}", shuffle);
let shuffle = gen_shuffle_for_pazzle_opening(12);
log!("{:?}", shuffle);
let shuffle = gen_shuffle_for_pazzle_opening(15);
log!("{:?}", shuffle);
let digits = gen_shuffle_for_pin();
let digits = gen_shuffle_for_pin();
}
#[async_std::test]
async fn create_wallet() {
// loading an image file from disk // loading an image file from disk
let f = File::open("tests/valid_security_image.jpg") let f = File::open("tests/valid_security_image.jpg")
.expect("open of tests/valid_security_image.jpg"); .expect("open of tests/valid_security_image.jpg");
@ -471,7 +554,7 @@ mod tests {
let creation = Instant::now(); let creation = Instant::now();
let res = create_wallet_v0( let res = create_wallet_v0(CreateWalletV0::new(
img_buffer, img_buffer,
" know yourself ".to_string(), " know yourself ".to_string(),
pin, pin,
@ -483,7 +566,8 @@ mod tests {
98, 243, 49, 90, 7, 0, 157, 58, 14, 187, 14, 3, 116, 86, 98, 243, 49, 90, 7, 0, 157, 58, 14, 187, 14, 3, 116, 86,
]), ]),
0, 0,
) ))
.await
.expect("create_wallet_v0"); .expect("create_wallet_v0");
log!( log!(
@ -492,18 +576,19 @@ mod tests {
); );
log!("-----------------------------"); log!("-----------------------------");
let (wallet, pazzle, mnemonic) = res;
let mut file = File::create("tests/wallet.ngw").expect("open wallet write file"); let mut file = File::create("tests/wallet.ngw").expect("open wallet write file");
let ser_wallet = to_vec(&NgFile::V0(NgFileV0::Wallet(wallet.clone()))).unwrap(); let ser_wallet = to_vec(&NgFile::V0(NgFileV0::Wallet(res.wallet.clone()))).unwrap();
file.write_all(&ser_wallet); file.write_all(&ser_wallet);
log!("wallet id: {:?}", base64_url::encode(&wallet.id().slice())); log!(
log!("pazzle {:?}", pazzle); "wallet id: {:?}",
log!("mnemonic {:?}", display_mnemonic(&mnemonic)); base64_url::encode(&res.wallet.id().slice())
);
log!("pazzle {:?}", res.pazzle);
log!("mnemonic {:?}", display_mnemonic(&res.mnemonic));
log!("pin {:?}", pin); log!("pin {:?}", pin);
if let Wallet::V0(v0) = wallet { if let Wallet::V0(v0) = res.wallet {
log!("security text: {:?}", v0.content.security_txt); log!("security text: {:?}", v0.content.security_txt);
let mut file = let mut file =
@ -523,7 +608,7 @@ mod tests {
let opening_mnemonic = Instant::now(); let opening_mnemonic = Instant::now();
let w = open_wallet_with_mnemonic(Wallet::V0(v0.clone()), mnemonic, pin) let w = open_wallet_with_mnemonic(Wallet::V0(v0.clone()), res.mnemonic, pin)
.expect("open with mnemonic"); .expect("open with mnemonic");
//println!("encrypted part {:?}", w); //println!("encrypted part {:?}", w);
@ -533,7 +618,7 @@ mod tests {
); );
let opening_pazzle = Instant::now(); let opening_pazzle = Instant::now();
let w = open_wallet_with_pazzle(Wallet::V0(v0.clone()), pazzle, pin) let w = open_wallet_with_pazzle(Wallet::V0(v0.clone()), res.pazzle, pin)
.expect("open with pazzle"); .expect("open with pazzle");
//println!("encrypted part {:?}", w); //println!("encrypted part {:?}", w);

@ -7,6 +7,8 @@
// notice may not be copied, modified, or distributed except // notice may not be copied, modified, or distributed except
// according to those terms. // according to those terms.
use std::fmt;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_big_array::BigArray; use serde_big_array::BigArray;
@ -209,9 +211,56 @@ impl AddWallet {
} }
} }
/// Create Wallet Version 0, used by the API create_wallet_v0
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CreateWalletV0 {
#[serde(with = "serde_bytes")]
pub security_img: Vec<u8>,
pub security_txt: String,
pub pin: [u8; 4],
pub pazzle_length: u8,
pub send_bootstrap: Option<Bootstrap>,
pub send_wallet: bool,
pub peer_id: PubKey,
pub nonce: u64,
}
impl CreateWalletV0 {
pub fn new(
security_img: Vec<u8>,
security_txt: String,
pin: [u8; 4],
pazzle_length: u8,
send_bootstrap: Option<Bootstrap>,
send_wallet: bool,
peer_id: PubKey,
nonce: u64,
) -> Self {
CreateWalletV0 {
security_img,
security_txt,
pin,
pazzle_length,
send_bootstrap,
send_wallet,
peer_id,
nonce,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CreateWalletResultV0 {
pub wallet: Wallet,
pub pazzle: Vec<u8>,
pub mnemonic: [u16; 12],
}
#[derive(Debug, Eq, PartialEq, Clone)] #[derive(Debug, Eq, PartialEq, Clone)]
pub enum NgWalletError { pub enum NgWalletError {
InvalidPin, InvalidPin,
InvalidPazzle,
InvalidPazzleLength, InvalidPazzleLength,
InvalidSecurityImage, InvalidSecurityImage,
InvalidSecurityText, InvalidSecurityText,
@ -222,6 +271,12 @@ pub enum NgWalletError {
InvalidSignature, InvalidSignature,
} }
impl fmt::Display for NgWalletError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum NgFileV0 { pub enum NgFileV0 {
Wallet(Wallet), Wallet(Wallet),
@ -231,3 +286,9 @@ pub enum NgFileV0 {
pub enum NgFile { pub enum NgFile {
V0(NgFileV0), V0(NgFileV0),
} }
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ShuffledPazzle {
pub category_indices: Vec<u8>,
pub emoji_indices: Vec<Vec<u8>>,
}

@ -19,4 +19,5 @@ fastbloom-rs = "0.5.3"
debug_print = "1.0.0" debug_print = "1.0.0"
hex = "0.4.3" hex = "0.4.3"
futures = "0.3.24" futures = "0.3.24"
base64-url = "2.0.0" base64-url = "2.0.0"
web-time = "0.2.0"

@ -15,7 +15,7 @@ use crate::types::*;
use ed25519_dalek::*; use ed25519_dalek::*;
use futures::channel::mpsc; use futures::channel::mpsc;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use std::time::{SystemTime, UNIX_EPOCH}; use web_time::{SystemTime, UNIX_EPOCH};
pub fn generate_null_keypair() -> (PrivKey, PubKey) { pub fn generate_null_keypair() -> (PrivKey, PubKey) {
let master_key: [u8; 32] = [0; 32]; let master_key: [u8; 32] = [0; 32];

Loading…
Cancel
Save