wallet_recovery_pdf

pull/33/head
Niko PLP 3 months ago
parent 46c6228de2
commit 8b2558cd9c
  1. 144
      Cargo.lock
  2. 2
      nextgraph/Cargo.toml
  3. 472
      nextgraph/src/local_broker.rs
  4. 6
      ng-app/src-tauri/src/lib.rs
  5. 3
      ng-app/src/api.ts
  6. 3
      ng-app/src/locales/en.json
  7. 2
      ng-net/src/broker.rs
  8. 2
      ng-oxigraph/build.rs
  9. 1
      ng-repo/src/errors.rs
  10. 6
      ng-sdk-js/src/lib.rs
  11. 2
      ng-storage-rocksdb/build.rs
  12. 2
      ng-verifier/build.rs
  13. 1
      ng-verifier/src/lib.rs
  14. 15
      ng-wallet/src/bip39.rs
  15. 13
      ng-wallet/src/types.rs
  16. 6
      ngaccount/src/main.rs

144
Cargo.lock generated

@ -520,6 +520,12 @@ version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]] [[package]]
name = "base64-url" name = "base64-url"
version = "2.0.0" version = "2.0.0"
@ -1421,6 +1427,12 @@ version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
[[package]]
name = "data-url"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a"
[[package]] [[package]]
name = "debug_print" name = "debug_print"
version = "1.0.0" version = "1.0.0"
@ -1791,6 +1803,12 @@ dependencies = [
"miniz_oxide", "miniz_oxide",
] ]
[[package]]
name = "float-cmp"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
[[package]] [[package]]
name = "flume" name = "flume"
version = "0.10.14" version = "0.10.14"
@ -2620,6 +2638,12 @@ dependencies = [
"tiff", "tiff",
] ]
[[package]]
name = "imagesize"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "1.9.3" version = "1.9.3"
@ -2830,6 +2854,16 @@ dependencies = [
"selectors", "selectors",
] ]
[[package]]
name = "kurbo"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e5aa9f0f96a938266bdb12928a67169e8d22c6a786fda8ed984b85e6ba93c3c"
dependencies = [
"arrayvec",
"smallvec",
]
[[package]] [[package]]
name = "kv-log-macro" name = "kv-log-macro"
version = "1.0.7" version = "1.0.7"
@ -3271,10 +3305,12 @@ dependencies = [
"ng-verifier", "ng-verifier",
"ng-wallet", "ng-wallet",
"once_cell", "once_cell",
"pdf-writer",
"qrcode", "qrcode",
"serde_bare", "serde_bare",
"serde_bytes", "serde_bytes",
"serde_json", "serde_json",
"svg2pdf",
"web-time", "web-time",
"whoami", "whoami",
"zeroize", "zeroize",
@ -4046,6 +4082,18 @@ version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79"
[[package]]
name = "pdf-writer"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af6a7882fda7808481d43c51cadfc3ec934c6af72612a1fe6985ce329a2f0469"
dependencies = [
"bitflags 2.5.0",
"itoa 1.0.6",
"memchr",
"ryu",
]
[[package]] [[package]]
name = "peeking_take_while" name = "peeking_take_while"
version = "0.1.2" version = "0.1.2"
@ -4203,6 +4251,12 @@ dependencies = [
"siphasher 0.3.10", "siphasher 0.3.10",
] ]
[[package]]
name = "pico-args"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
[[package]] [[package]]
name = "pin-project" name = "pin-project"
version = "1.1.0" version = "1.1.0"
@ -4711,6 +4765,12 @@ dependencies = [
"winreg 0.10.1", "winreg 0.10.1",
] ]
[[package]]
name = "roxmltree"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
[[package]] [[package]]
name = "rust-embed" name = "rust-embed"
version = "6.7.0" version = "6.7.0"
@ -5160,6 +5220,15 @@ version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f"
[[package]]
name = "simplecss"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d"
dependencies = [
"log",
]
[[package]] [[package]]
name = "siphasher" name = "siphasher"
version = "0.3.10" version = "0.3.10"
@ -5292,6 +5361,15 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strict-num"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
dependencies = [
"float-cmp",
]
[[package]] [[package]]
name = "string_cache" name = "string_cache"
version = "0.8.7" version = "0.8.7"
@ -5330,6 +5408,29 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "svg2pdf"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e31565956eb1dc398c0d9776ee1d1bac4e34759af63dcbe0520df32313a5b53b"
dependencies = [
"log",
"miniz_oxide",
"once_cell",
"pdf-writer",
"usvg",
]
[[package]]
name = "svgtypes"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fae3064df9b89391c9a76a0425a69d124aee9c5c28455204709e72c39868a43c"
dependencies = [
"kurbo",
"siphasher 1.0.1",
]
[[package]] [[package]]
name = "swift-rs" name = "swift-rs"
version = "1.0.6" version = "1.0.6"
@ -5854,6 +5955,17 @@ dependencies = [
"crunchy", "crunchy",
] ]
[[package]]
name = "tiny-skia-path"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93"
dependencies = [
"arrayref",
"bytemuck",
"strict-num",
]
[[package]] [[package]]
name = "tinytemplate" name = "tinytemplate"
version = "1.2.1" version = "1.2.1"
@ -6194,6 +6306,28 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "usvg"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b84ea542ae85c715f07b082438a4231c3760539d902e11d093847a0b22963032"
dependencies = [
"base64 0.22.1",
"data-url",
"flate2",
"imagesize",
"kurbo",
"log",
"pico-args",
"roxmltree",
"simplecss",
"siphasher 1.0.1",
"strict-num",
"svgtypes",
"tiny-skia-path",
"xmlwriter",
]
[[package]] [[package]]
name = "utf-8" name = "utf-8"
version = "0.7.6" version = "0.7.6"
@ -6568,9 +6702,9 @@ dependencies = [
[[package]] [[package]]
name = "weezl" name = "weezl"
version = "0.1.7" version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
[[package]] [[package]]
name = "whoami" name = "whoami"
@ -7042,6 +7176,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "xmlwriter"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
[[package]] [[package]]
name = "xsalsa20poly1305" name = "xsalsa20poly1305"
version = "0.9.1" version = "0.9.1"

@ -30,6 +30,8 @@ lazy_static = "1.4.0"
web-time = "0.2.0" web-time = "0.2.0"
whoami = "1.5.1" whoami = "1.5.1"
qrcode = { version = "0.14.1", default-features = false, features = ["svg"] } qrcode = { version = "0.14.1", default-features = false, features = ["svg"] }
svg2pdf = { version = "0.11.0", default-features = false }
pdf-writer = "0.10.0"
ng-repo = { path = "../ng-repo", version = "0.1.0-preview.1" } ng-repo = { path = "../ng-repo", version = "0.1.0-preview.1" }
ng-net = { path = "../ng-net", version = "0.1.0-preview.1" } ng-net = { path = "../ng-net", version = "0.1.0-preview.1" }
ng-wallet = { path = "../ng-wallet", version = "0.1.0-preview.5" } ng-wallet = { path = "../ng-wallet", version = "0.1.0-preview.5" }

@ -16,12 +16,14 @@ use async_once_cell::OnceCell;
use async_std::sync::{Arc, Mutex, RwLock}; use async_std::sync::{Arc, Mutex, RwLock};
use futures::channel::mpsc; use futures::channel::mpsc;
use futures::SinkExt; use futures::SinkExt;
use lazy_static::lazy_static;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use pdf_writer::{Content, Finish, Name, Pdf, Rect, Ref, Str};
use qrcode::{render::svg, QrCode};
use serde_bare::to_vec; use serde_bare::to_vec;
use serde_json::json; use serde_json::json;
use svg2pdf::{ConversionOptions, PageOptions};
use zeroize::Zeroize; use zeroize::Zeroize;
use qrcode::{render::svg, QrCode};
use lazy_static::lazy_static;
use ng_repo::block_storage::BlockStorage; use ng_repo::block_storage::BlockStorage;
use ng_repo::block_storage::HashMapBlockStorage; use ng_repo::block_storage::HashMapBlockStorage;
@ -29,20 +31,20 @@ use ng_repo::errors::{NgError, ProtocolError};
use ng_repo::log::*; use ng_repo::log::*;
use ng_repo::os_info::get_os_info; use ng_repo::os_info::get_os_info;
use ng_repo::types::*; use ng_repo::types::*;
use ng_repo::utils::{derive_key, generate_keypair, encrypt_in_place}; use ng_repo::utils::{derive_key, encrypt_in_place, generate_keypair};
use ng_net::{actor::*,actors::admin::*}; use ng_net::app_protocol::*;
use ng_net::broker::*; use ng_net::broker::*;
use ng_net::connection::{ClientConfig, IConnect, NoiseFSM, StartConfig, AppConfig}; use ng_net::connection::{AppConfig, ClientConfig, IConnect, NoiseFSM, StartConfig};
use ng_net::types::*; use ng_net::types::*;
use ng_net::app_protocol::*;
use ng_net::utils::{Receiver, Sender}; use ng_net::utils::{Receiver, Sender};
use ng_net::{actor::*, actors::admin::*};
use ng_verifier::types::*; use ng_verifier::types::*;
use ng_verifier::verifier::Verifier; use ng_verifier::verifier::Verifier;
use ng_wallet::emojis::encode_pazzle;
use ng_wallet::bip39::encode_mnemonic; use ng_wallet::bip39::encode_mnemonic;
use ng_wallet::emojis::encode_pazzle;
use ng_wallet::{create_wallet_first_step_v0, create_wallet_second_step_v0, types::*}; use ng_wallet::{create_wallet_first_step_v0, create_wallet_second_step_v0, types::*};
#[cfg(not(target_family = "wasm"))] #[cfg(not(target_family = "wasm"))]
@ -272,15 +274,36 @@ struct RemoteSession {
impl RemoteSession { impl RemoteSession {
pub(crate) async fn send_request(&self, req: AppRequest) -> Result<AppResponse, NgError> { pub(crate) async fn send_request(&self, req: AppRequest) -> Result<AppResponse, NgError> {
match BROKER.read().await.request::<AppRequest, AppResponse>(&Some(self.user_id), &Some(self.remote_peer_id), req).await { match BROKER
.read()
.await
.request::<AppRequest, AppResponse>(
&Some(self.user_id),
&Some(self.remote_peer_id),
req,
)
.await
{
Err(e) => Err(e), Err(e) => Err(e),
Ok(SoS::Stream(_)) => Err(NgError::InvalidResponse), Ok(SoS::Stream(_)) => Err(NgError::InvalidResponse),
Ok(SoS::Single(res)) => Ok(res), Ok(SoS::Single(res)) => Ok(res),
} }
} }
pub(crate) async fn send_request_stream(&self, req: AppRequest) -> Result<(Receiver<AppResponse>, CancelFn), NgError> { pub(crate) async fn send_request_stream(
match BROKER.read().await.request::<AppRequest, AppResponse>(&Some(self.user_id), &Some(self.remote_peer_id), req).await { &self,
req: AppRequest,
) -> Result<(Receiver<AppResponse>, CancelFn), NgError> {
match BROKER
.read()
.await
.request::<AppRequest, AppResponse>(
&Some(self.user_id),
&Some(self.remote_peer_id),
req,
)
.await
{
Err(e) => Err(e), Err(e) => Err(e),
Ok(SoS::Single(_)) => Err(NgError::InvalidResponse), Ok(SoS::Single(_)) => Err(NgError::InvalidResponse),
Ok(SoS::Stream(stream)) => { Ok(SoS::Stream(stream)) => {
@ -289,7 +312,7 @@ impl RemoteSession {
//TODO: implement CancelStream in AppRequest //TODO: implement CancelStream in AppRequest
}); });
Ok((stream, fnonce)) Ok((stream, fnonce))
}, }
} }
} }
} }
@ -299,8 +322,7 @@ struct HeadlessSession {
user_id: UserId, user_id: UserId,
} }
impl HeadlessSession { impl HeadlessSession {}
}
#[derive(Debug)] #[derive(Debug)]
struct Session { struct Session {
@ -401,9 +423,7 @@ impl SessionConfig {
} }
#[doc(hidden)] #[doc(hidden)]
pub fn new_headless( pub fn new_headless(user_id: UserId) -> Self {
user_id: UserId,
) -> Self {
SessionConfig::HeadlessV0(user_id) SessionConfig::HeadlessV0(user_id)
} }
@ -451,7 +471,7 @@ impl SessionConfig {
if match self { if match self {
Self::HeadlessV0(_) => { Self::HeadlessV0(_) => {
panic!("don't call session_start on a Headless LocalBroker") panic!("don't call session_start on a Headless LocalBroker")
}, }
_ => match local_broker_config { _ => match local_broker_config {
LocalBrokerConfig::InMemory => { LocalBrokerConfig::InMemory => {
self.set_verifier_type(VerifierType::Memory); self.set_verifier_type(VerifierType::Memory);
@ -471,7 +491,6 @@ impl SessionConfig {
panic!("don't call session_start on a Headless LocalBroker") panic!("don't call session_start on a Headless LocalBroker")
} }
}, },
} { } {
Ok(()) Ok(())
} else { } else {
@ -606,33 +625,56 @@ impl LocalBroker {
} }
async fn connect_remote_broker(&mut self) -> Result<(), NgError> { async fn connect_remote_broker(&mut self) -> Result<(), NgError> {
self.err_if_not_headless()?; self.err_if_not_headless()?;
if self.headless_connected_to_remote_broker {return Ok(())} if self.headless_connected_to_remote_broker {
return Ok(());
}
let info = get_client_info(ClientType::NodeService); let info = get_client_info(ClientType::NodeService);
let config = self.config.headless_config(); let config = self.config.headless_config();
BROKER.write().await.connect( BROKER
.write()
.await
.connect(
Arc::new(Box::new(ConnectionWebSocket {})), Arc::new(Box::new(ConnectionWebSocket {})),
config.client_peer_key.as_ref().unwrap().clone(), config.client_peer_key.as_ref().unwrap().clone(),
config.client_peer_key.as_ref().unwrap().to_pub(), config.client_peer_key.as_ref().unwrap().to_pub(),
config.server_peer_id, config.server_peer_id,
StartConfig::App(AppConfig{user_priv:None, info, addr:config.server_addr}) StartConfig::App(AppConfig {
).await?; user_priv: None,
info,
addr: config.server_addr,
}),
)
.await?;
self.headless_connected_to_remote_broker = true; self.headless_connected_to_remote_broker = true;
Ok(()) Ok(())
} }
pub(crate) async fn send_request_headless<A: Into<ProtocolMessage> + std::fmt::Debug + Sync + Send + 'static, pub(crate) async fn send_request_headless<
B: TryFrom<ProtocolMessage, Error = ProtocolError> + std::fmt::Debug + Sync + Send + 'static,>(&self, req: A) -> Result<B, NgError> { A: Into<ProtocolMessage> + std::fmt::Debug + Sync + Send + 'static,
B: TryFrom<ProtocolMessage, Error = ProtocolError> + std::fmt::Debug + Sync + Send + 'static,
>(
&self,
req: A,
) -> Result<B, NgError> {
self.err_if_not_headless()?; self.err_if_not_headless()?;
match BROKER.read().await.request::<A, B>(&None, &Some(self.config.headless_config().server_peer_id), req).await { match BROKER
.read()
.await
.request::<A, B>(
&None,
&Some(self.config.headless_config().server_peer_id),
req,
)
.await
{
Err(e) => Err(e), Err(e) => Err(e),
Ok(SoS::Stream(_)) => Err(NgError::InvalidResponse), Ok(SoS::Stream(_)) => Err(NgError::InvalidResponse),
Ok(SoS::Single(res)) => Ok(res), Ok(SoS::Single(res)) => Ok(res),
@ -640,11 +682,25 @@ impl LocalBroker {
} }
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) async fn send_request_stream_headless<A: Into<ProtocolMessage> + std::fmt::Debug + Sync + Send + 'static, pub(crate) async fn send_request_stream_headless<
B: TryFrom<ProtocolMessage, Error = ProtocolError> + std::fmt::Debug + Sync + Send + 'static,>(&self, req: A) -> Result<(Receiver<B>, CancelFn), NgError> { A: Into<ProtocolMessage> + std::fmt::Debug + Sync + Send + 'static,
B: TryFrom<ProtocolMessage, Error = ProtocolError> + std::fmt::Debug + Sync + Send + 'static,
>(
&self,
req: A,
) -> Result<(Receiver<B>, CancelFn), NgError> {
self.err_if_not_headless()?; self.err_if_not_headless()?;
match BROKER.read().await.request::<A, B>(&None, &Some(self.config.headless_config().server_peer_id), req).await { match BROKER
.read()
.await
.request::<A, B>(
&None,
&Some(self.config.headless_config().server_peer_id),
req,
)
.await
{
Err(e) => Err(e), Err(e) => Err(e),
Ok(SoS::Single(_)) => Err(NgError::InvalidResponse), Ok(SoS::Single(_)) => Err(NgError::InvalidResponse),
Ok(SoS::Stream(stream)) => { Ok(SoS::Stream(stream)) => {
@ -653,7 +709,7 @@ impl LocalBroker {
//TODO: implement CancelStream in AppRequest //TODO: implement CancelStream in AppRequest
}); });
Ok((stream, fnonce)) Ok((stream, fnonce))
}, }
} }
} }
@ -700,12 +756,13 @@ impl LocalBroker {
#[allow(dead_code)] #[allow(dead_code)]
fn to_external_session_id(session_id: u64, is_remote: bool) -> u64 { fn to_external_session_id(session_id: u64, is_remote: bool) -> u64 {
let mut ext = (session_id) << 1; let mut ext = (session_id) << 1;
if !is_remote {ext += 1;} if !is_remote {
ext += 1;
}
ext ext
} }
fn user_to_local_session_id_for_mut(&self, user_id: &UserId) -> Result<usize, NgError> { fn user_to_local_session_id_for_mut(&self, user_id: &UserId) -> Result<usize, NgError> {
let session_id = self let session_id = self
.opened_sessions .opened_sessions
.get(user_id) .get(user_id)
@ -714,7 +771,9 @@ impl LocalBroker {
} }
fn get_local_session_id_for_mut(&self, session_id: u64) -> Result<usize, NgError> { fn get_local_session_id_for_mut(&self, session_id: u64) -> Result<usize, NgError> {
let _ = Self::is_local_session(session_id).then_some(true).ok_or(NgError::SessionNotFound)?; let _ = Self::is_local_session(session_id)
.then_some(true)
.ok_or(NgError::SessionNotFound)?;
let session_id = Self::to_real_session_id(session_id) as usize; let session_id = Self::to_real_session_id(session_id) as usize;
if session_id >= self.opened_sessions_list.len() { if session_id >= self.opened_sessions_list.len() {
return Err(NgError::InvalidArgument); return Err(NgError::InvalidArgument);
@ -723,7 +782,6 @@ impl LocalBroker {
} }
fn get_real_session_id_for_mut(&self, session_id: u64) -> Result<(usize, bool), NgError> { fn get_real_session_id_for_mut(&self, session_id: u64) -> Result<(usize, bool), NgError> {
let is_remote = Self::is_remote_session(session_id); let is_remote = Self::is_remote_session(session_id);
let session_id = Self::to_real_session_id(session_id) as usize; let session_id = Self::to_real_session_id(session_id) as usize;
if is_remote { if is_remote {
@ -739,7 +797,9 @@ impl LocalBroker {
} }
fn get_session(&self, session_id: u64) -> Result<&Session, NgError> { fn get_session(&self, session_id: u64) -> Result<&Session, NgError> {
let _ = Self::is_local_session(session_id).then_some(true).ok_or(NgError::SessionNotFound)?; let _ = Self::is_local_session(session_id)
.then_some(true)
.ok_or(NgError::SessionNotFound)?;
let session_id = Self::to_real_session_id(session_id); let session_id = Self::to_real_session_id(session_id);
if session_id as usize >= self.opened_sessions_list.len() { if session_id as usize >= self.opened_sessions_list.len() {
return Err(NgError::InvalidArgument); return Err(NgError::InvalidArgument);
@ -751,32 +811,35 @@ impl LocalBroker {
#[allow(dead_code)] #[allow(dead_code)]
fn get_headless_session(&self, session_id: u64) -> Result<&HeadlessSession, NgError> { fn get_headless_session(&self, session_id: u64) -> Result<&HeadlessSession, NgError> {
self.err_if_not_headless()?; self.err_if_not_headless()?;
self self.headless_sessions
.headless_sessions
.get(&session_id) .get(&session_id)
.ok_or(NgError::SessionNotFound) .ok_or(NgError::SessionNotFound)
} }
#[allow(dead_code)] #[allow(dead_code)]
fn get_headless_session_by_user(&self, user_id: &UserId) -> Result<&HeadlessSession, NgError> { fn get_headless_session_by_user(&self, user_id: &UserId) -> Result<&HeadlessSession, NgError> {
self.err_if_not_headless()?; self.err_if_not_headless()?;
let session_id = self.opened_sessions.get(user_id).ok_or(NgError::SessionNotFound)?; let session_id = self
.opened_sessions
.get(user_id)
.ok_or(NgError::SessionNotFound)?;
self.get_headless_session(*session_id) self.get_headless_session(*session_id)
} }
fn remove_headless_session(&mut self, user_id: &UserId) -> Result<(u64,HeadlessSession), NgError> { fn remove_headless_session(
&mut self,
user_id: &UserId,
) -> Result<(u64, HeadlessSession), NgError> {
self.err_if_not_headless()?; self.err_if_not_headless()?;
let session_id = self.opened_sessions.remove(user_id).ok_or(NgError::SessionNotFound)?; let session_id = self
.opened_sessions
.remove(user_id)
.ok_or(NgError::SessionNotFound)?;
let session = self let session = self
.headless_sessions .headless_sessions
@ -787,7 +850,9 @@ impl LocalBroker {
#[allow(dead_code)] #[allow(dead_code)]
fn get_remote_session(&self, session_id: u64) -> Result<&RemoteSession, NgError> { fn get_remote_session(&self, session_id: u64) -> Result<&RemoteSession, NgError> {
let _ = Self::is_remote_session(session_id).then_some(true).ok_or(NgError::SessionNotFound)?; let _ = Self::is_remote_session(session_id)
.then_some(true)
.ok_or(NgError::SessionNotFound)?;
let session_id = Self::to_real_session_id(session_id); let session_id = Self::to_real_session_id(session_id);
if session_id as usize >= self.remote_sessions_list.len() { if session_id as usize >= self.remote_sessions_list.len() {
return Err(NgError::InvalidArgument); return Err(NgError::InvalidArgument);
@ -972,7 +1037,6 @@ impl LocalBroker {
} }
fn add_headless_session(&mut self, session: HeadlessSession) -> Result<SessionInfo, NgError> { fn add_headless_session(&mut self, session: HeadlessSession) -> Result<SessionInfo, NgError> {
let user_id = session.user_id; let user_id = session.user_id;
let mut first_available: u64 = 0; let mut first_available: u64 = 0;
@ -1494,14 +1558,121 @@ pub async fn wallet_add(lws: LocalWalletStorageV0) -> Result<(), NgError> {
} }
Ok(()) Ok(())
} }
pub fn wallet_to_wallet_recovery(
wallet: &Wallet,
pazzle: Vec<u8>,
mnemonic: [u16; 12],
pin: [u8; 4],
) -> NgQRCodeWalletRecoveryV0 {
match wallet {
Wallet::V0(v0) => {
let mut content = v0.content.clone();
content.security_img = vec![];
NgQRCodeWalletRecoveryV0 {
wallet: content,
pazzle,
mnemonic,
pin,
}
}
_ => unimplemented!(),
}
}
/// Generates the Recovery PDF containing the Wallet, PIN, Pazzle and Mnemonic.
pub async fn wallet_recovery_pdf(
content: NgQRCodeWalletRecoveryV0,
size: u32,
) -> Result<Vec<u8>, NgError> {
let ser = serde_bare::to_vec(&content)?;
if ser.len() > 2_953 {
return Err(NgError::InvalidPayload);
}
let recovery_str = base64_url::encode(&ser);
let wallet_svg = match QrCode::with_error_correction_level(&ser, qrcode::EcLevel::M) {
Ok(qr) => {
let svg = qr
.render()
.max_dimensions(size, size)
.dark_color(svg::Color("#000000"))
.light_color(svg::Color("#ffffff"))
.build();
svg
}
Err(_e) => return Err(NgError::BrokerError),
};
let options = svg2pdf::usvg::Options::default();
let tree = svg2pdf::usvg::Tree::from_str(&wallet_svg, &options)
.map_err(|e| NgError::WalletError(e.to_string()))?;
// PDF uses A4 format (21cm x 29.7cm)
// TODO: instead of to_pdf in the next line, do to_chunk, and then add the text below the SVG.
// the SVG should take all the width of the A4 (so that only 29.7-21 = 8cm remains below the SVG, for all the following)
// the text is :
// - one line with : "PIN = 1234 pazzle = cat_slug:emoji_slug cat_slug:emoji_slug ...[x9]"
// - one line with the 9 emoji SVGs (with size so they fit in one line, width of the A4)
// - one line with : "mnemonic = [12 words of mnemonic]"
// - one line with recovery_str (it is quite long. choose a font size that make it fit here so the whole document is only one page)
// you can use the methods of pdf_writer library.
let (mut chunk, reference) = svg2pdf::to_chunk(&tree, ConversionOptions::default());
// probably then: add the text with chunk.stream() or chunk.indirect()
//let pdf_buf = svg2pdf::to_pdf(&tree, ConversionOptions::default(), PageOptions::default());
// Define some indirect reference ids we'll use.
let catalog_id = Ref::new(1000);
let page_tree_id = Ref::new(1001);
let page_id = Ref::new(1002);
let font_id = Ref::new(1003);
let content_id = Ref::new(1004);
let font_name = Name(b"F1");
let mut content = Content::new();
content.begin_text();
content.set_font(font_name, 14.0);
content.next_line(108.0, 734.0);
content.show(Str(b"Hello World from Rust!"));
content.end_text();
// Write a document catalog and a page tree with one A4 page that uses no resources.
let mut pdf = Pdf::new();
pdf.stream(content_id, &content.finish());
pdf.catalog(catalog_id).pages(page_tree_id);
pdf.pages(page_tree_id).kids([page_id]).count(1);
{
let mut page = pdf.page(page_id);
let mut page_resources = page
.parent(page_tree_id)
.media_box(Rect::new(0.0, 0.0, 595.0, 842.0))
.resources();
page_resources.fonts().pair(font_name, font_id);
page_resources.finish();
page.contents(content_id);
page.finish();
}
pdf.type1_font(font_id).base_font(Name(b"Courier"));
pdf.extend(&chunk);
let pdf_buf = pdf.finish();
Ok(pdf_buf)
}
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
lazy_static! { lazy_static! {
static ref NEXTGRAPH_EU: BrokerServerV0 = BrokerServerV0 { static ref NEXTGRAPH_EU: BrokerServerV0 = BrokerServerV0 {
server_type: BrokerServerTypeV0::Localhost(14400), server_type: BrokerServerTypeV0::Localhost(14400),
can_verify: false, can_verify: false,
can_forward: false, can_forward: false,
peer_id: ng_repo::utils::decode_key({use crate::local_broker_dev_env::PEER_ID; PEER_ID}).unwrap(), peer_id: ng_repo::utils::decode_key({
use crate::local_broker_dev_env::PEER_ID;
PEER_ID
})
.unwrap(),
}; };
} }
@ -1511,9 +1682,9 @@ lazy_static! {
server_type: BrokerServerTypeV0::Domain("nextgraph.eu".to_string()), server_type: BrokerServerTypeV0::Domain("nextgraph.eu".to_string()),
can_verify: false, can_verify: false,
can_forward: false, can_forward: false,
peer_id: ng_repo::utils::decode_key("FtdzuDYGewfXWdoPuXIPb0wnd0SAg1WoA2B14S7jW3MA").unwrap(), peer_id: ng_repo::utils::decode_key("FtdzuDYGewfXWdoPuXIPb0wnd0SAg1WoA2B14S7jW3MA")
.unwrap(),
}; };
} }
/// Obtains a Wallet object from a QRCode or a TextCode. /// Obtains a Wallet object from a QRCode or a TextCode.
@ -1522,16 +1693,22 @@ lazy_static! {
/// with the help of the function [wallet_open_with_pazzle_words] /// with the help of the function [wallet_open_with_pazzle_words]
/// followed by [wallet_import] /// followed by [wallet_import]
pub async fn wallet_import_from_code(code: String) -> Result<Wallet, NgError> { pub async fn wallet_import_from_code(code: String) -> Result<Wallet, NgError> {
let qr = NgQRCode::from_code(code)?; let qr = NgQRCode::from_code(code)?;
match qr { match qr {
NgQRCode::V0(NgQRCodeV0{broker, rendezvous, secret_key, is_rendezvous}) => { NgQRCode::WalletTransferV0(NgQRCodeWalletTransferV0 {
broker,
rendezvous,
secret_key,
is_rendezvous,
}) => {
let wallet: ExportedWallet = do_ext_call( let wallet: ExportedWallet = do_ext_call(
&broker, &broker,
ExtWalletGetExportV0 { ExtWalletGetExportV0 {
id: rendezvous, id: rendezvous,
is_rendezvous is_rendezvous,
}).await?; },
)
.await?;
let mut buf = wallet.0.into_vec(); let mut buf = wallet.0.into_vec();
encrypt_in_place(&mut buf, *secret_key.slice(), [0; 12]); encrypt_in_place(&mut buf, *secret_key.slice(), [0; 12]);
@ -1548,8 +1725,8 @@ pub async fn wallet_import_from_code(code: String) -> Result<Wallet, NgError> {
} else { } else {
Err(NgError::WalletAlreadyAdded) Err(NgError::WalletAlreadyAdded)
} }
} }
_ => Err(NgError::IncompatibleQrCode),
} }
} }
@ -1560,11 +1737,11 @@ pub async fn wallet_import_from_code(code: String) -> Result<Wallet, NgError> {
/// ///
/// Returns the QRcode in SVG format, and the code (a string) to be used with [wallet_import_from_code] /// Returns the QRcode in SVG format, and the code (a string) to be used with [wallet_import_from_code]
pub async fn wallet_import_rendezvous(size: u32) -> Result<(String, String), NgError> { pub async fn wallet_import_rendezvous(size: u32) -> Result<(String, String), NgError> {
let code = NgQRCode::V0(NgQRCodeV0 { let code = NgQRCode::WalletTransferV0(NgQRCodeWalletTransferV0 {
broker: NEXTGRAPH_EU.clone(), broker: NEXTGRAPH_EU.clone(),
rendezvous: SymKey::random(), rendezvous: SymKey::random(),
secret_key: SymKey::random(), secret_key: SymKey::random(),
is_rendezvous: true is_rendezvous: true,
}); });
let code_string = code.to_code(); let code_string = code.to_code();
@ -1577,8 +1754,8 @@ pub async fn wallet_import_rendezvous(size: u32) -> Result<(String,String), NgEr
.light_color(svg::Color("#ffffff")) .light_color(svg::Color("#ffffff"))
.build(); .build();
svg svg
}, }
Err(_e) => {return Err(NgError::BrokerError)} Err(_e) => return Err(NgError::BrokerError),
}; };
Ok((code_svg, code_string)) Ok((code_svg, code_string))
@ -1590,23 +1767,21 @@ pub async fn wallet_import_rendezvous(size: u32) -> Result<(String,String), NgEr
/// ///
/// Returns the TextCode /// Returns the TextCode
pub async fn wallet_export_get_textcode(session_id: u64) -> Result<String, NgError> { pub async fn wallet_export_get_textcode(session_id: u64) -> Result<String, NgError> {
let broker = match LOCAL_BROKER.get() { let broker = match LOCAL_BROKER.get() {
None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized),
Some(Ok(broker)) => broker.read().await, Some(Ok(broker)) => broker.read().await,
}; };
match &broker.config { match &broker.config {
LocalBrokerConfig::Headless(_) => { LocalBrokerConfig::Headless(_) => return Err(NgError::LocalBrokerIsHeadless),
return Err(NgError::LocalBrokerIsHeadless)
},
_ => { _ => {
let (real_session_id, is_remote) = broker.get_real_session_id_for_mut(session_id)?; let (real_session_id, is_remote) = broker.get_real_session_id_for_mut(session_id)?;
if is_remote { if is_remote {
return Err(NgError::NotImplemented); return Err(NgError::NotImplemented);
} else { } else {
let session = broker.opened_sessions_list[real_session_id].as_ref() let session = broker.opened_sessions_list[real_session_id]
.as_ref()
.ok_or(NgError::SessionNotFound)?; .ok_or(NgError::SessionNotFound)?;
let wallet_name = session.config.wallet_name(); let wallet_name = session.config.wallet_name();
@ -1617,20 +1792,31 @@ pub async fn wallet_export_get_textcode(session_id: u64) -> Result<String, NgErr
let wallet = &lws.wallet; let wallet = &lws.wallet;
let secret_key = SymKey::random(); let secret_key = SymKey::random();
let rendezvous = SymKey::random(); let rendezvous = SymKey::random();
let code = NgQRCode::V0(NgQRCodeV0 { let code = NgQRCode::WalletTransferV0(NgQRCodeWalletTransferV0 {
broker: NEXTGRAPH_EU.clone(), broker: NEXTGRAPH_EU.clone(),
rendezvous: rendezvous.clone(), rendezvous: rendezvous.clone(),
secret_key: secret_key.clone(), secret_key: secret_key.clone(),
is_rendezvous: false is_rendezvous: false,
}); });
let code_string = code.to_code(); let code_string = code.to_code();
let mut wallet_ser = serde_bare::to_vec(wallet)?; let mut wallet_ser = serde_bare::to_vec(wallet)?;
encrypt_in_place(&mut wallet_ser, *secret_key.slice(), [0; 12]); encrypt_in_place(&mut wallet_ser, *secret_key.slice(), [0; 12]);
let exported_wallet = ExportedWallet(serde_bytes::ByteBuf::from(wallet_ser)); let exported_wallet =
match session.verifier.client_request::<WalletPutExport, ()>(WalletPutExport::V0(WalletPutExportV0{wallet:exported_wallet, rendezvous_id:rendezvous, is_rendezvous:false})).await { ExportedWallet(serde_bytes::ByteBuf::from(wallet_ser));
match session
.verifier
.client_request::<WalletPutExport, ()>(WalletPutExport::V0(
WalletPutExportV0 {
wallet: exported_wallet,
rendezvous_id: rendezvous,
is_rendezvous: false,
},
))
.await
{
Err(e) => Err(e), Err(e) => Err(e),
Ok(SoS::Stream(_)) => Err(NgError::InvalidResponse), Ok(SoS::Stream(_)) => Err(NgError::InvalidResponse),
Ok(SoS::Single(_)) => Ok(code_string) Ok(SoS::Single(_)) => Ok(code_string),
} }
} }
} }
@ -1645,7 +1831,6 @@ pub async fn wallet_export_get_textcode(session_id: u64) -> Result<String, NgErr
/// ///
/// Returns the QRcode in SVG format /// Returns the QRcode in SVG format
pub async fn wallet_export_get_qrcode(session_id: u64, size: u32) -> Result<String, NgError> { pub async fn wallet_export_get_qrcode(session_id: u64, size: u32) -> Result<String, NgError> {
let code_string = wallet_export_get_textcode(session_id).await?; let code_string = wallet_export_get_textcode(session_id).await?;
let code_svg = match QrCode::with_error_correction_level(&code_string, qrcode::EcLevel::M) { let code_svg = match QrCode::with_error_correction_level(&code_string, qrcode::EcLevel::M) {
@ -1657,23 +1842,25 @@ pub async fn wallet_export_get_qrcode(session_id: u64, size: u32) -> Result<Stri
.light_color(svg::Color("#ffffff")) .light_color(svg::Color("#ffffff"))
.build(); .build();
svg svg
}, }
Err(_e) => {return Err(NgError::BrokerError)} Err(_e) => return Err(NgError::BrokerError),
}; };
Ok(code_svg) Ok(code_svg)
} }
/// Puts the Wallet to the rendezvous ID that was scanned /// Puts the Wallet to the rendezvous ID that was scanned
/// ///
/// The rendezvous ID is valid for 5 min. /// The rendezvous ID is valid for 5 min.
pub async fn wallet_export_rendezvous(session_id: u64, code: String) -> Result<(), NgError> { pub async fn wallet_export_rendezvous(session_id: u64, code: String) -> Result<(), NgError> {
let qr = NgQRCode::from_code(code)?; let qr = NgQRCode::from_code(code)?;
match qr { match qr {
NgQRCode::V0(NgQRCodeV0{broker: _, rendezvous, secret_key, is_rendezvous}) => { NgQRCode::WalletTransferV0(NgQRCodeWalletTransferV0 {
broker: _,
rendezvous,
secret_key,
is_rendezvous,
}) => {
if !is_rendezvous { if !is_rendezvous {
return Err(NgError::NotARendezVous); return Err(NgError::NotARendezVous);
} }
@ -1684,16 +1871,16 @@ pub async fn wallet_export_rendezvous(session_id: u64, code: String) -> Result<(
}; };
match &broker.config { match &broker.config {
LocalBrokerConfig::Headless(_) => { LocalBrokerConfig::Headless(_) => return Err(NgError::LocalBrokerIsHeadless),
return Err(NgError::LocalBrokerIsHeadless)
},
_ => { _ => {
let (real_session_id, is_remote) = broker.get_real_session_id_for_mut(session_id)?; let (real_session_id, is_remote) =
broker.get_real_session_id_for_mut(session_id)?;
if is_remote { if is_remote {
return Err(NgError::NotImplemented); return Err(NgError::NotImplemented);
} else { } else {
let session = broker.opened_sessions_list[real_session_id].as_ref() let session = broker.opened_sessions_list[real_session_id]
.as_ref()
.ok_or(NgError::SessionNotFound)?; .ok_or(NgError::SessionNotFound)?;
let wallet_name = session.config.wallet_name(); let wallet_name = session.config.wallet_name();
@ -1705,15 +1892,26 @@ pub async fn wallet_export_rendezvous(session_id: u64, code: String) -> Result<(
let mut wallet_ser = serde_bare::to_vec(wallet)?; let mut wallet_ser = serde_bare::to_vec(wallet)?;
encrypt_in_place(&mut wallet_ser, *secret_key.slice(), [0; 12]); encrypt_in_place(&mut wallet_ser, *secret_key.slice(), [0; 12]);
let exported_wallet = ExportedWallet(serde_bytes::ByteBuf::from(wallet_ser)); let exported_wallet =
ExportedWallet(serde_bytes::ByteBuf::from(wallet_ser));
// TODO: send the WalletPutExport client request to the broker received from QRcode. for now it is cheer luck that all clients are connected to nextgraph.eu. // TODO: send the WalletPutExport client request to the broker received from QRcode. for now it is cheer luck that all clients are connected to nextgraph.eu.
// if the user doesn't have an account with nextgraph.eu, their broker should relay the request (core protocol ?) // if the user doesn't have an account with nextgraph.eu, their broker should relay the request (core protocol ?)
match session.verifier.client_request::<WalletPutExport, ()>(WalletPutExport::V0(WalletPutExportV0{wallet:exported_wallet, rendezvous_id:rendezvous, is_rendezvous:true})).await { match session
.verifier
.client_request::<WalletPutExport, ()>(WalletPutExport::V0(
WalletPutExportV0 {
wallet: exported_wallet,
rendezvous_id: rendezvous,
is_rendezvous: true,
},
))
.await
{
Err(e) => Err(e), Err(e) => Err(e),
Ok(SoS::Stream(_)) => Err(NgError::InvalidResponse), Ok(SoS::Stream(_)) => Err(NgError::InvalidResponse),
Ok(SoS::Single(_)) => Ok(()) Ok(SoS::Single(_)) => Ok(()),
} }
} }
} }
@ -1721,6 +1919,7 @@ pub async fn wallet_export_rendezvous(session_id: u64, code: String) -> Result<(
} }
} }
} }
_ => Err(NgError::IncompatibleQrCode),
} }
} }
@ -1787,27 +1986,6 @@ pub fn wallet_open_with_pazzle(
Ok(opened_wallet) Ok(opened_wallet)
} }
#[doc(hidden)]
/// This is a bit hard to use as the mnemonic words are encoded in u16.
/// prefer the function wallet_open_with_mnemonic_words
pub fn wallet_open_with_mnemonic(
wallet: &Wallet,
mnemonic: Vec<u16>,
pin: [u8; 4],
) -> Result<SensitiveWallet, NgError> {
if mnemonic.len() != 12 {
return Err(NgError::InvalidMnemonic);
}
// Convert from vec to array.
let mut mnemonic_arr = [0u16; 12];
for (place, element) in mnemonic_arr.iter_mut().zip(mnemonic.iter()) {
*place = *element;
}
let opened_wallet = ng_wallet::open_wallet_with_mnemonic(wallet, mnemonic_arr, pin)?;
Ok(opened_wallet)
}
/// Opens a wallet by providing an ordered list of words, and the pin. /// Opens a wallet by providing an ordered list of words, and the pin.
/// ///
/// If you are opening a wallet that is already known to the LocalBroker, you must then call [wallet_was_opened]. /// If you are opening a wallet that is already known to the LocalBroker, you must then call [wallet_was_opened].
@ -1822,7 +2000,6 @@ pub fn wallet_open_with_pazzle_words(
wallet_open_with_pazzle(wallet, encode_pazzle(pazzle_words)?, pin) wallet_open_with_pazzle(wallet, encode_pazzle(pazzle_words)?, pin)
} }
/// Opens a wallet by providing an ordered list of mnemonic words, and the pin. /// Opens a wallet by providing an ordered list of mnemonic words, and the pin.
/// ///
/// If you are opening a wallet that is already known to the LocalBroker, you must then call [wallet_was_opened]. /// If you are opening a wallet that is already known to the LocalBroker, you must then call [wallet_was_opened].
@ -1832,9 +2009,11 @@ pub fn wallet_open_with_mnemonic_words(
mnemonic: &Vec<String>, mnemonic: &Vec<String>,
pin: [u8; 4], pin: [u8; 4],
) -> Result<SensitiveWallet, NgError> { ) -> Result<SensitiveWallet, NgError> {
let encoded: Vec<u16> = encode_mnemonic(mnemonic)?; Ok(ng_wallet::open_wallet_with_mnemonic(
wallet,
wallet_open_with_mnemonic(wallet, encoded, pin) encode_mnemonic(mnemonic)?,
pin,
)?)
} }
/// Imports a wallet into the LocalBroker so the user can then access its content. /// Imports a wallet into the LocalBroker so the user can then access its content.
@ -1931,7 +2110,6 @@ pub async fn session_start(config: SessionConfig) -> Result<SessionInfo, NgError
// TODO: implement SessionConfig::WithCredentials . VerifierType::Remote => it needs to establish a connection to remote here, then send the AppSessionStart in it. // TODO: implement SessionConfig::WithCredentials . VerifierType::Remote => it needs to establish a connection to remote here, then send the AppSessionStart in it.
// also, it is using broker.remote_sessions.get // also, it is using broker.remote_sessions.get
_ => { _ => {
if config.is_remote() || config.is_with_credentials() { if config.is_remote() || config.is_with_credentials() {
unimplemented!(); unimplemented!();
} }
@ -2185,7 +2363,6 @@ pub async fn session_stop(user_id: &UserId) -> Result<(), NgError> {
match broker.config { match broker.config {
LocalBrokerConfig::Headless(_) => { LocalBrokerConfig::Headless(_) => {
let (session_id, _) = broker.remove_headless_session(user_id)?; let (session_id, _) = broker.remove_headless_session(user_id)?;
let request = AppSessionStop::V0(AppSessionStopV0 { let request = AppSessionStop::V0(AppSessionStopV0 {
@ -2223,13 +2400,15 @@ pub async fn session_headless_stop(session_id: u64, force_close: bool) -> Result
match broker.config { match broker.config {
LocalBrokerConfig::Headless(_) => { LocalBrokerConfig::Headless(_) => {
let session = broker let session = broker
.headless_sessions .headless_sessions
.remove(&session_id) .remove(&session_id)
.ok_or(NgError::SessionNotFound)?; .ok_or(NgError::SessionNotFound)?;
let _ = broker.opened_sessions.remove(&session.user_id).ok_or(NgError::SessionNotFound)?; let _ = broker
.opened_sessions
.remove(&session.user_id)
.ok_or(NgError::SessionNotFound)?;
let request = AppSessionStop::V0(AppSessionStopV0 { let request = AppSessionStop::V0(AppSessionStopV0 {
session_id, session_id,
@ -2342,11 +2521,10 @@ pub async fn app_request(request: AppRequest) -> Result<AppResponse, NgError> {
Some(Ok(broker)) => broker.write().await, Some(Ok(broker)) => broker.write().await,
}; };
match &broker.config { match &broker.config {
LocalBrokerConfig::Headless(_) => { LocalBrokerConfig::Headless(_) => broker.send_request_headless(request).await,
broker.send_request_headless(request).await
},
_ => { _ => {
let (real_session_id, is_remote) = broker.get_real_session_id_for_mut(request.session_id())?; let (real_session_id, is_remote) =
broker.get_real_session_id_for_mut(request.session_id())?;
if is_remote { if is_remote {
let session = broker.remote_sessions_list[real_session_id] let session = broker.remote_sessions_list[real_session_id]
@ -2372,11 +2550,10 @@ pub async fn app_request_stream(
Some(Ok(broker)) => broker.write().await, Some(Ok(broker)) => broker.write().await,
}; };
match &broker.config { match &broker.config {
LocalBrokerConfig::Headless(_) => { LocalBrokerConfig::Headless(_) => broker.send_request_stream_headless(request).await,
broker.send_request_stream_headless(request).await
},
_ => { _ => {
let (real_session_id, is_remote) = broker.get_real_session_id_for_mut(request.session_id())?; let (real_session_id, is_remote) =
broker.get_real_session_id_for_mut(request.session_id())?;
if is_remote { if is_remote {
let session = broker.remote_sessions_list[real_session_id] let session = broker.remote_sessions_list[real_session_id]
@ -2447,11 +2624,7 @@ async fn do_admin_call<
async fn do_ext_call< async fn do_ext_call<
A: Into<ProtocolMessage> + Into<ExtRequestContentV0> + std::fmt::Debug + Sync + Send + 'static, A: Into<ProtocolMessage> + Into<ExtRequestContentV0> + std::fmt::Debug + Sync + Send + 'static,
B: TryFrom<ProtocolMessage, Error = ProtocolError> B: TryFrom<ProtocolMessage, Error = ProtocolError> + std::fmt::Debug + Sync + Send + 'static,
+ std::fmt::Debug
+ Sync
+ Send
+ 'static,
>( >(
broker_server: &BrokerServerV0, broker_server: &BrokerServerV0,
cmd: A, cmd: A,
@ -2469,23 +2642,23 @@ async fn do_ext_call<
} }
#[doc(hidden)] #[doc(hidden)]
pub async fn admin_create_user(server_peer_id: DirectPeerId, admin_user_key: PrivKey, server_addr: BindAddress) -> Result<UserId, ProtocolError> { pub async fn admin_create_user(
server_peer_id: DirectPeerId,
admin_user_key: PrivKey,
server_addr: BindAddress,
) -> Result<UserId, ProtocolError> {
let res = do_admin_call( let res = do_admin_call(
server_peer_id, server_peer_id,
admin_user_key, admin_user_key,
server_addr, server_addr,
CreateUser::V0(CreateUserV0 { CreateUser::V0(CreateUserV0 {}),
}),
) )
.await?; .await?;
match res { match res {
AdminResponseContentV0::UserId(id) => Ok(id), AdminResponseContentV0::UserId(id) => Ok(id),
_ => Err(ProtocolError::InvalidValue) _ => Err(ProtocolError::InvalidValue),
} }
} }
#[allow(unused_imports)] #[allow(unused_imports)]
@ -2717,4 +2890,35 @@ mod test {
// closes the wallet // closes the wallet
wallet_close(&wallet_name).await.expect("wallet_close"); wallet_close(&wallet_name).await.expect("wallet_close");
} }
#[async_std::test]
async fn recovery_pdf() {
let wallet_file = read("tests/wallet.ngw").expect("read wallet file");
init_local_broker(Box::new(|| LocalBrokerConfig::InMemory)).await;
let wallet = wallet_read_file(wallet_file)
.await
.expect("wallet_read_file");
let pazzle_string = read_to_string("tests/wallet.pazzle").expect("read pazzle file");
let pazzle_words = pazzle_string.split(' ').map(|s| s.to_string()).collect();
let mnemonic_string = read_to_string("tests/wallet.mnemonic").expect("read mnemonic file");
let mnemonic_words = mnemonic_string.split(' ').map(|s| s.to_string()).collect();
let pin: [u8; 4] = [1, 2, 1, 2];
let pazzle = encode_pazzle(&pazzle_words).expect("encode_pazzle");
let mnemonic = encode_mnemonic(&mnemonic_words).expect("encode_mnemonic");
let wallet_recovery = wallet_to_wallet_recovery(&wallet, pazzle, mnemonic, pin);
let pdf_buffer = wallet_recovery_pdf(wallet_recovery, 600)
.await
.expect("wallet_recovery_pdf");
let mut file =
File::create("tests/recovery.pdf").expect("open for write recovery.pdf file");
file.write_all(&pdf_buffer).expect("write of recovery.pdf");
}
} }

@ -111,12 +111,12 @@ async fn wallet_open_with_pazzle(
#[tauri::command(rename_all = "snake_case")] #[tauri::command(rename_all = "snake_case")]
async fn wallet_open_with_mnemonic( async fn wallet_open_with_mnemonic(
wallet: Wallet, wallet: Wallet,
mnemonic: Vec<u16>, mnemonic: [u16; 12],
pin: [u8; 4], pin: [u8; 4],
_app: tauri::AppHandle, _app: tauri::AppHandle,
) -> Result<SensitiveWallet, String> { ) -> Result<SensitiveWallet, String> {
let wallet = nextgraph::local_broker::wallet_open_with_mnemonic(&wallet, mnemonic, pin) let wallet =
.map_err(|e| e.to_string())?; ng_wallet::open_wallet_with_mnemonic(&wallet, mnemonic, pin).map_err(|e| e.to_string())?;
Ok(wallet) Ok(wallet)
} }

@ -17,6 +17,7 @@ const mapping = {
"wallet_gen_shuffle_for_pin": [], "wallet_gen_shuffle_for_pin": [],
"wallet_open_with_pazzle": ["wallet","pazzle","pin"], "wallet_open_with_pazzle": ["wallet","pazzle","pin"],
"wallet_open_with_mnemonic_words": ["wallet","mnemonic_words","pin"], "wallet_open_with_mnemonic_words": ["wallet","mnemonic_words","pin"],
"wallet_open_with_mnemonic": ["wallet","mnemonic","pin"],
"wallet_was_opened": ["opened_wallet"], "wallet_was_opened": ["opened_wallet"],
"wallet_create": ["params"], "wallet_create": ["params"],
"wallet_read_file": ["file"], "wallet_read_file": ["file"],
@ -189,7 +190,7 @@ const handler = {
return false; return false;
} else if (path[0] === "get_local_url") { } else if (path[0] === "get_local_url") {
return false; return false;
} else if (path[0] === "wallet_open_with_pazzle" || path[0] === "wallet_open_with_mnemonic_words") { } else if (path[0] === "wallet_open_with_pazzle" || path[0] === "wallet_open_with_mnemonic_words" || path[0] === "wallet_open_with_mnemonic") {
let arg:any = {}; let arg:any = {};
args.map((el,ix) => arg[mapping[path[0]][ix]]=el) args.map((el,ix) => arg[mapping[path[0]][ix]]=el)
let img = Array.from(new Uint8Array(arg.wallet.V0.content.security_img)); let img = Array.from(new Uint8Array(arg.wallet.V0.content.security_img));

@ -348,7 +348,8 @@
"InvalidNuri": "Invalid NextGraph URI.", "InvalidNuri": "Invalid NextGraph URI.",
"InvalidTarget": "Cannot resolve target.", "InvalidTarget": "Cannot resolve target.",
"ExportWalletTimeOut": "Export of wallet has expired.", "ExportWalletTimeOut": "Export of wallet has expired.",
"ConnectionError": "Could not connect to the server." "ConnectionError": "Could not connect to the server.",
"IncompatibleQrCode": "You scanned a NextGraph QR-Code that is of the wrong type"
}, },
"connectivity": { "connectivity": {
"stopped": "Stopped", "stopped": "Stopped",

@ -639,7 +639,7 @@ impl Broker {
let res = join.next().await; let res = join.next().await;
match res { match res {
Some(Either::Right(remote_peer_id)) => { Some(Either::Right(remote_peer_id)) => {
let res = join.next().await; let _res = join.next().await;
// if res.is_some() // if res.is_some()
// && res.as_ref().unwrap().as_ref().unwrap_left() == &NetError::Closing // && res.as_ref().unwrap().as_ref().unwrap_left() == &NetError::Closing

@ -1,7 +1,5 @@
fn main() { fn main() {
if std::env::var("DOCS_RS").is_ok() { if std::env::var("DOCS_RS").is_ok() {
println!("cargo:rustc-cfg=docsrs"); println!("cargo:rustc-cfg=docsrs");
} }
} }

@ -87,6 +87,7 @@ pub enum NgError {
InvalidQrCode, InvalidQrCode,
NotImplemented, NotImplemented,
NotARendezVous, NotARendezVous,
IncompatibleQrCode,
} }
impl Error for NgError {} impl Error for NgError {}

@ -148,14 +148,16 @@ pub fn wallet_open_with_pazzle(
#[wasm_bindgen] #[wasm_bindgen]
pub fn wallet_open_with_mnemonic( pub fn wallet_open_with_mnemonic(
wallet: JsValue, wallet: JsValue,
mnemonic: Vec<u16>, mnemonic: JsValue,
pin: JsValue, pin: JsValue,
) -> Result<JsValue, JsValue> { ) -> Result<JsValue, JsValue> {
let encrypted_wallet = serde_wasm_bindgen::from_value::<Wallet>(wallet) let encrypted_wallet = serde_wasm_bindgen::from_value::<Wallet>(wallet)
.map_err(|_| "Deserialization error of wallet")?; .map_err(|_| "Deserialization error of wallet")?;
let pin = serde_wasm_bindgen::from_value::<[u8; 4]>(pin) let pin = serde_wasm_bindgen::from_value::<[u8; 4]>(pin)
.map_err(|_| "Deserialization error of pin")?; .map_err(|_| "Deserialization error of pin")?;
let res = nextgraph::local_broker::wallet_open_with_mnemonic(&encrypted_wallet, mnemonic, pin); let mnemonic = serde_wasm_bindgen::from_value::<[u16; 12]>(mnemonic)
.map_err(|_| "Deserialization error of mnemonic")?;
let res = ng_wallet::open_wallet_with_mnemonic(&encrypted_wallet, mnemonic, pin);
match res { match res {
Ok(r) => Ok(r Ok(r) => Ok(r
.serialize(&serde_wasm_bindgen::Serializer::new().serialize_maps_as_objects(true)) .serialize(&serde_wasm_bindgen::Serializer::new().serialize_maps_as_objects(true))

@ -1,7 +1,5 @@
fn main() { fn main() {
if std::env::var("DOCS_RS").is_ok() { if std::env::var("DOCS_RS").is_ok() {
println!("cargo:rustc-cfg=docsrs"); println!("cargo:rustc-cfg=docsrs");
} }
} }

@ -1,7 +1,5 @@
fn main() { fn main() {
if std::env::var("DOCS_RS").is_ok() { if std::env::var("DOCS_RS").is_ok() {
println!("cargo:rustc-cfg=docsrs"); println!("cargo:rustc-cfg=docsrs");
} }
} }

@ -1,4 +1,3 @@
pub mod types; pub mod types;
pub mod site; pub mod site;

@ -227,14 +227,15 @@ lazy_static! {
} }
/// Taking a list of bip39 words, returns a list of u16 codes /// Taking a list of bip39 words, returns a list of u16 codes
pub fn encode_mnemonic(words: &Vec<String>) -> Result<Vec<u16>, NgError> { pub fn encode_mnemonic(words: &Vec<String>) -> Result<[u16; 12], NgError> {
let mut res = vec![]; if words.len() != 12 {
for word in words { return Err(NgError::InvalidMnemonic);
res.push( }
*BIP39_WORD_MAP let mut res = [0u16; 12];
for (idx, word) in words.iter().enumerate() {
res[idx] = *BIP39_WORD_MAP
.get(word.as_str()) .get(word.as_str())
.ok_or(NgError::InvalidMnemonic)?, .ok_or(NgError::InvalidMnemonic)?;
);
} }
Ok(res) Ok(res)
} }

@ -1428,16 +1428,25 @@ pub struct ShuffledPazzle {
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct NgQRCodeV0 { pub struct NgQRCodeWalletTransferV0 {
pub broker: BrokerServerV0, pub broker: BrokerServerV0,
pub rendezvous: SymKey, // Rendez-vous ID pub rendezvous: SymKey, // Rendez-vous ID
pub secret_key: SymKey, pub secret_key: SymKey,
pub is_rendezvous: bool, pub is_rendezvous: bool,
} }
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct NgQRCodeWalletRecoveryV0 {
pub wallet: WalletContentV0, //of which security_img is emptied
pub pazzle: Vec<u8>,
pub mnemonic: [u16; 12],
pub pin: [u8; 4],
}
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum NgQRCode { pub enum NgQRCode {
V0(NgQRCodeV0), WalletTransferV0(NgQRCodeWalletTransferV0),
WalletRecoveryV0(NgQRCodeWalletRecoveryV0),
} }
impl NgQRCode { impl NgQRCode {

@ -12,17 +12,17 @@ extern crate anyhow;
mod types; mod types;
use std::convert::Infallible; use std::convert::Infallible;
use std::env;
use std::net::IpAddr; use std::net::IpAddr;
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use std::{env};
use duration_str::parse; use duration_str::parse;
use rust_embed::RustEmbed;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use warp::http::header::{HeaderMap, HeaderValue}; use warp::http::header::{HeaderMap, HeaderValue};
use warp::reply::Response; use warp::reply::Response;
use warp::{Filter, Reply}; use warp::{Filter, Reply};
use rust_embed::RustEmbed;
use ng_repo::log::*; use ng_repo::log::*;
use ng_repo::types::*; use ng_repo::types::*;
@ -37,7 +37,6 @@ use ng_net::types::{
use ng_client_ws::remote_ws::ConnectionWebSocket; use ng_client_ws::remote_ws::ConnectionWebSocket;
#[derive(RustEmbed)] #[derive(RustEmbed)]
#[folder = "web/dist"] #[folder = "web/dist"]
struct Static; struct Static;
@ -261,7 +260,6 @@ async fn main() -> anyhow::Result<()> {
"default-src 'self' data:; connect-src ipc: https://ipc.localhost 'self' http://localhost:3031", "default-src 'self' data:; connect-src ipc: https://ipc.localhost 'self' http://localhost:3031",
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
"default-src 'self' data:; connect-src ipc: https://ipc.localhost 'self'", "default-src 'self' data:; connect-src ipc: https://ipc.localhost 'self'",
), ),
); );

Loading…
Cancel
Save