zeroize everything

pull/19/head
Niko PLP 1 year ago
parent dfdfbedee8
commit 240b836624
  1. 4
      Cargo.lock
  2. 2
      README.md
  3. 6
      ng-sdk-js/src/lib.rs
  4. 3
      ng-wallet/Cargo.toml
  5. 102
      ng-wallet/src/lib.rs
  6. 168
      ng-wallet/src/types.rs
  7. 14
      ngcli/src/main.rs
  8. 3
      ngd/Cargo.toml
  9. 2
      ngd/src/cli.rs
  10. 78
      ngd/src/main.rs
  11. 12
      p2p-broker/src/server_ws.rs
  12. 11
      p2p-client-ws/src/remote_ws.rs
  13. 1
      p2p-net/Cargo.toml
  14. 57
      p2p-net/src/broker.rs
  15. 36
      p2p-net/src/connection.rs
  16. 4
      p2p-net/src/lib.rs
  17. 66
      p2p-net/src/site.rs
  18. 238
      p2p-net/src/types.rs
  19. 97
      p2p-net/src/utils.rs
  20. 1
      p2p-repo/Cargo.toml
  21. 2
      p2p-repo/src/block.rs
  22. 90
      p2p-repo/src/branch.rs
  23. 10
      p2p-repo/src/commit.rs
  24. 2
      p2p-repo/src/lib.rs
  25. 19
      p2p-repo/src/object.rs
  26. 79
      p2p-repo/src/site.rs
  27. 112
      p2p-repo/src/types.rs
  28. 124
      p2p-repo/src/utils.rs

4
Cargo.lock generated

@ -2784,6 +2784,7 @@ dependencies = [
"serde_bare",
"serde_bytes",
"web-time",
"zeroize",
]
[[package]]
@ -2823,6 +2824,7 @@ dependencies = [
"serde_bare",
"serde_bytes",
"serde_json",
"zeroize",
]
[[package]]
@ -3108,6 +3110,7 @@ dependencies = [
"async-broadcast",
"async-std",
"async-trait",
"base64-url",
"blake3",
"default-net",
"ed25519-dalek",
@ -3150,6 +3153,7 @@ dependencies = [
"slice_as_array",
"wasm-bindgen",
"web-time",
"zeroize",
]
[[package]]

@ -103,7 +103,7 @@ wasm-pack test --chrome --headless
Test Rust websocket
```
cargo test --package p2p-client-ws --lib -- --nocapture
cargo test --package p2p-client-ws --lib -- remote_ws::test::test_ws --nocapture
```
### Build release binaries

@ -22,7 +22,7 @@ use p2p_client_ws::remote_ws_wasm::ConnectionWebSocket;
use p2p_net::broker::*;
use p2p_net::connection::{ClientConfig, StartConfig};
use p2p_net::types::{DirectPeerId, IP};
use p2p_net::utils::{gen_ed_keys, spawn_and_log_error, Receiver, ResultSend, Sender};
use p2p_net::utils::{spawn_and_log_error, Receiver, ResultSend, Sender};
use p2p_net::WS_PORT;
use p2p_repo::log::*;
use p2p_repo::types::*;
@ -57,7 +57,7 @@ pub fn wallet_open_wallet_with_pazzle(
) -> 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)
let mut 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 {
@ -230,7 +230,7 @@ pub async fn start() {
//let keys = p2p_net::utils::gen_dh_keys();
//let pub_key = PubKey::Ed25519PubKey(keys.1);
let keys = gen_ed_keys();
let keys = generate_keypair();
let x_from_ed = keys.1.to_dh_from_ed();
log_info!("Pub from X {}", x_from_ed);

@ -26,4 +26,5 @@ aes-gcm-siv = {version = "0.11.1", features = ["aes","heapless","getrandom","std
base64-url = "2.0.0"
async-std = { version = "1.12.0", features = ["attributes","unstable"] }
web-time = "0.2.0"
lazy_static = "1.4.0"
lazy_static = "1.4.0"
zeroize = { version = "1.6.0", features = ["zeroize_derive"] }

@ -19,7 +19,7 @@ pub mod bip39;
pub mod emojis;
use std::io::Cursor;
use std::{collections::HashMap, io::Cursor};
use crate::bip39::bip39_wordlist;
use crate::types::*;
@ -29,29 +29,32 @@ use aes_gcm_siv::{
};
use argon2::{Algorithm, Argon2, AssociatedData, ParamsBuilder, Version};
use chacha20poly1305::XChaCha20Poly1305;
use zeroize::{Zeroize, ZeroizeOnDrop};
use image::{imageops::FilterType, io::Reader as ImageReader, ImageOutputFormat};
use safe_transmute::transmute_to_bytes;
use p2p_net::types::{SiteType, SiteV0};
use p2p_repo::log::*;
use p2p_repo::types::{PubKey, Site, SiteType, Timestamp};
use p2p_repo::types::{PubKey, Timestamp};
use p2p_repo::utils::{generate_keypair, now_timestamp, sign, verify};
use rand::prelude::*;
use serde_bare::{from_slice, to_vec};
use web_time::Instant;
pub fn enc_master_key(
master_key: [u8; 32],
key: [u8; 32],
master_key: &[u8; 32],
key: &[u8; 32],
nonce: u8,
wallet_id: WalletId,
) -> Result<[u8; 48], NgWalletError> {
let cipher = Aes256GcmSiv::new(&key.into());
let cipher = Aes256GcmSiv::new(key.into());
let mut nonce_buffer = [0u8; 12];
nonce_buffer[0] = nonce;
let nonce = Nonce::from_slice(&nonce_buffer);
let mut buffer: HeaplessVec<u8, 48> = HeaplessVec::new(); // Note: buffer needs 16-bytes overhead for auth tag
buffer.extend_from_slice(&master_key);
buffer.extend_from_slice(master_key);
// Encrypt `buffer` in-place, replacing the plaintext contents with ciphertext
cipher
@ -65,11 +68,11 @@ pub fn enc_master_key(
pub fn dec_master_key(
ciphertext: [u8; 48],
key: [u8; 32],
key: &[u8; 32],
nonce: u8,
wallet_id: WalletId,
) -> Result<[u8; 32], NgWalletError> {
let cipher = Aes256GcmSiv::new(&key.into());
let cipher = Aes256GcmSiv::new(key.into());
let mut nonce_buffer = [0u8; 12];
nonce_buffer[0] = nonce;
let nonce = Nonce::from_slice(&nonce_buffer);
@ -97,7 +100,7 @@ fn gen_associated_data(timestamp: Timestamp, wallet_id: WalletId) -> Vec<u8> {
pub fn enc_encrypted_block(
block: &EncryptedWalletV0,
master_key: [u8; 32],
master_key: &[u8; 32],
peer_id: PubKey,
nonce: u64,
timestamp: Timestamp,
@ -107,7 +110,7 @@ pub fn enc_encrypted_block(
let nonce_buffer: [u8; 24] = gen_nonce(peer_id, nonce);
let cipher = XChaCha20Poly1305::new(&master_key.into());
let cipher = XChaCha20Poly1305::new(master_key.into());
let mut buffer: Vec<u8> = Vec::with_capacity(ser_encrypted_block.len() + 16); // Note: buffer needs 16-bytes overhead for auth tag
buffer.extend_from_slice(&ser_encrypted_block);
@ -129,7 +132,7 @@ pub fn enc_encrypted_block(
pub fn dec_encrypted_block(
mut ciphertext: Vec<u8>,
master_key: [u8; 32],
master_key: &mut [u8; 32],
peer_id: PubKey,
nonce: u64,
timestamp: Timestamp,
@ -137,7 +140,7 @@ pub fn dec_encrypted_block(
) -> Result<EncryptedWalletV0, NgWalletError> {
let nonce_buffer: [u8; 24] = gen_nonce(peer_id, nonce);
let cipher = XChaCha20Poly1305::new(&master_key.into());
let cipher = XChaCha20Poly1305::new(master_key.as_ref().into());
// Decrypt `ciphertext` in-place, replacing its ciphertext context with the original plaintext
cipher
@ -154,10 +157,11 @@ pub fn dec_encrypted_block(
let decrypted_block =
from_slice::<EncryptedWalletV0>(&ciphertext).map_err(|e| NgWalletError::DecryptionError)?;
master_key.zeroize();
Ok(decrypted_block)
}
pub fn derive_key_from_pass(pass: Vec<u8>, salt: [u8; 16], wallet_id: WalletId) -> [u8; 32] {
pub fn derive_key_from_pass(mut pass: Vec<u8>, salt: [u8; 16], wallet_id: WalletId) -> [u8; 32] {
let params = ParamsBuilder::new()
.m_cost(50 * 1024)
.t_cost(2)
@ -169,14 +173,14 @@ pub fn derive_key_from_pass(pass: Vec<u8>, salt: [u8; 16], wallet_id: WalletId)
let argon = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
let mut out = [0u8; 32];
argon.hash_password_into(&pass, &salt, &mut out).unwrap();
pass.zeroize();
out
}
use web_time::Instant;
pub fn open_wallet_with_pazzle(
wallet: Wallet,
pazzle: Vec<u8>,
pin: [u8; 4],
mut pin: [u8; 4],
) -> Result<EncryptedWallet, NgWalletError> {
// each digit shouldnt be greater than 9
if pin[0] > 9 || pin[1] > 9 || pin[2] > 9 || pin[3] > 9 {
@ -190,18 +194,21 @@ pub fn open_wallet_with_pazzle(
match wallet {
Wallet::V0(v0) => {
let pazzle_key = derive_key_from_pass(
let mut pazzle_key = derive_key_from_pass(
[pazzle, pin.to_vec()].concat(),
v0.content.salt_pazzle,
v0.id,
);
//pazzle.zeroize();
pin.zeroize();
let master_key = dec_master_key(
let mut master_key = dec_master_key(
v0.content.enc_master_key_pazzle,
pazzle_key,
&pazzle_key,
v0.content.master_nonce,
v0.id,
)?;
pazzle_key.zeroize();
log_info!(
"opening of wallet with pazzle took: {} ms",
@ -210,7 +217,7 @@ pub fn open_wallet_with_pazzle(
Ok(EncryptedWallet::V0(dec_encrypted_block(
v0.content.encrypted,
master_key,
&mut master_key,
v0.content.peer_id,
v0.content.nonce,
v0.content.timestamp,
@ -222,30 +229,33 @@ pub fn open_wallet_with_pazzle(
pub fn open_wallet_with_mnemonic(
wallet: Wallet,
mnemonic: [u16; 12],
pin: [u8; 4],
mut mnemonic: [u16; 12],
mut pin: [u8; 4],
) -> Result<EncryptedWallet, NgWalletError> {
verify(&wallet.content_as_bytes(), wallet.sig(), wallet.id())
.map_err(|e| NgWalletError::InvalidSignature)?;
match wallet {
Wallet::V0(v0) => {
let mnemonic_key = derive_key_from_pass(
let mut mnemonic_key = derive_key_from_pass(
[transmute_to_bytes(&mnemonic), &pin].concat(),
v0.content.salt_mnemonic,
v0.id,
);
mnemonic.zeroize();
pin.zeroize();
let master_key = dec_master_key(
let mut master_key = dec_master_key(
v0.content.enc_master_key_mnemonic,
mnemonic_key,
&mnemonic_key,
v0.content.master_nonce,
v0.id,
)?;
mnemonic_key.zeroize();
Ok(EncryptedWallet::V0(dec_encrypted_block(
v0.content.encrypted,
master_key,
&mut master_key,
v0.content.peer_id,
v0.content.nonce,
v0.content.timestamp,
@ -335,10 +345,10 @@ pub async fn create_wallet_v0(
let creating_pazzle = Instant::now();
// pazzle_length can only be 9, 12, or 15
if (params.pazzle_length != 9
if params.pazzle_length != 9
&& params.pazzle_length != 12
&& params.pazzle_length != 15
&& params.pazzle_length != 0)
&& params.pazzle_length != 0
{
return Err(NgWalletError::InvalidPazzleLength);
}
@ -395,7 +405,7 @@ pub async fn create_wallet_v0(
}
// check validity of image
let decoded_img = ImageReader::new(Cursor::new(params.security_img))
let decoded_img = ImageReader::new(Cursor::new(&params.security_img))
.with_guessed_format()
.map_err(|e| NgWalletError::InvalidSecurityImage)?
.decode()
@ -419,9 +429,9 @@ pub async fn create_wallet_v0(
// creating the wallet keys
let (wallet_key, wallet_id) = generate_keypair();
let (wallet_privkey, wallet_id) = generate_keypair();
let site = Site::create(SiteType::Individual).map_err(|e| NgWalletError::InternalError)?;
let site = SiteV0::create(SiteType::Individual).map_err(|e| NgWalletError::InternalError)?;
// let mut pazzle_random = vec![0u8; pazzle_length.into()];
// getrandom::getrandom(&mut pazzle_random).map_err(|e| NgWalletError::InternalError)?;
@ -450,10 +460,15 @@ pub async fn create_wallet_v0(
//.clone(),
let encrypted_block = EncryptedWalletV0 {
wallet_privkey: wallet_privkey.clone(),
pazzle: pazzle.clone(),
mnemonic,
pin: params.pin,
sites: vec![site],
brokers: vec![], //TODO add the broker here
clients: vec![], // TODO add a client here
overlay_core_overrides: HashMap::new(),
third_parties: HashMap::new(),
};
let mut master_key = [0u8; 32];
@ -464,13 +479,14 @@ pub async fn create_wallet_v0(
if params.pazzle_length > 0 {
getrandom::getrandom(&mut salt_pazzle).map_err(|e| NgWalletError::InternalError)?;
let pazzle_key = derive_key_from_pass(
let mut pazzle_key = derive_key_from_pass(
[pazzle.clone(), params.pin.to_vec()].concat(),
salt_pazzle,
wallet_id,
);
enc_master_key_pazzle = enc_master_key(master_key, pazzle_key, 0, wallet_id)?;
enc_master_key_pazzle = enc_master_key(&master_key, &pazzle_key, 0, wallet_id)?;
pazzle_key.zeroize();
}
let mut salt_mnemonic = [0u8; 16];
@ -479,24 +495,26 @@ pub async fn create_wallet_v0(
//log_debug!("salt_pazzle {:?}", salt_pazzle);
//log_debug!("salt_mnemonic {:?}", salt_mnemonic);
let mnemonic_key = derive_key_from_pass(
let mut mnemonic_key = derive_key_from_pass(
[transmute_to_bytes(&mnemonic), &params.pin].concat(),
salt_mnemonic,
wallet_id,
);
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)?;
mnemonic_key.zeroize();
let timestamp = now_timestamp();
let encrypted = enc_encrypted_block(
&encrypted_block,
master_key,
&master_key,
params.peer_id,
params.nonce,
timestamp,
wallet_id,
)?;
master_key.zeroize();
let wallet_content = WalletContentV0 {
security_img: cursor.into_inner(),
@ -515,7 +533,7 @@ pub async fn create_wallet_v0(
let ser_wallet = serde_bare::to_vec(&wallet_content).unwrap();
let sig = sign(wallet_key, wallet_id, &ser_wallet).unwrap();
let sig = sign(&wallet_privkey, &wallet_id, &ser_wallet).unwrap();
let wallet_v0 = WalletV0 {
/// ID
@ -544,7 +562,7 @@ pub async fn create_wallet_v0(
creating_pazzle.elapsed().as_millis()
);
let wallet = Wallet::V0(wallet_v0);
let wallet_file = match (params.result_with_wallet_file) {
let wallet_file = match params.result_with_wallet_file {
false => vec![], // TODO: save locally
true => to_vec(&NgFile::V0(NgFileV0::Wallet(wallet.clone()))).unwrap(),
};
@ -552,13 +570,13 @@ pub async fn create_wallet_v0(
wallet: wallet,
wallet_file,
pazzle,
mnemonic,
mnemonic: mnemonic.clone(),
wallet_name: base64_url::encode(&wallet_id.slice()),
})
}
#[cfg(test)]
mod tests {
mod test {
use super::*;
use p2p_repo::utils::generate_keypair;
use std::fs::File;
@ -634,7 +652,7 @@ mod tests {
log_info!("mnemonic {:?}", display_mnemonic(&res.mnemonic));
log_info!("pin {:?}", pin);
if let Wallet::V0(v0) = res.wallet {
if let Wallet::V0(v0) = &res.wallet {
log_info!("security text: {:?}", v0.content.security_txt);
let mut file =
@ -654,7 +672,7 @@ mod tests {
let opening_mnemonic = Instant::now();
let w = open_wallet_with_mnemonic(Wallet::V0(v0.clone()), res.mnemonic, pin)
let w = open_wallet_with_mnemonic(Wallet::V0(v0.clone()), res.mnemonic, pin.clone())
.expect("open with mnemonic");
//log_debug!("encrypted part {:?}", w);
@ -665,7 +683,7 @@ mod tests {
if v0.content.pazzle_length > 0 {
let opening_pazzle = Instant::now();
let w = open_wallet_with_pazzle(Wallet::V0(v0.clone()), res.pazzle, pin)
let w = open_wallet_with_pazzle(Wallet::V0(v0.clone()), res.pazzle.clone(), pin)
.expect("open with pazzle");
log_info!(
"opening of wallet with pazzle took: {} ms",

@ -7,12 +7,14 @@
// notice may not be copied, modified, or distributed except
// according to those terms.
use std::fmt;
use std::{collections::HashMap, fmt};
use web_time::SystemTime;
use zeroize::{Zeroize, ZeroizeOnDrop};
use serde::{Deserialize, Serialize};
use serde_big_array::BigArray;
use p2p_net::types::{BootstrapContentV0, BrokerServerV0};
use p2p_net::types::*;
use p2p_repo::types::*;
/// WalletId is a PubKey
@ -58,9 +60,22 @@ impl Bootstrap {
}
}
/// EncryptedWallet block Version 0
/// Device info Version 0
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ClientV0 {
pub priv_key: PrivKey,
pub storage_master_key: SymKey,
/// list of users that should be opened automatically (at launch, after wallet opened) on this device
pub auto_open: Vec<Identity>,
}
/// EncryptedWallet block Version 0
#[derive(Clone, Zeroize, ZeroizeOnDrop, Debug, Serialize, Deserialize)]
pub struct EncryptedWalletV0 {
pub wallet_privkey: PrivKey,
#[serde(with = "serde_bytes")]
pub pazzle: Vec<u8>,
@ -69,7 +84,24 @@ pub struct EncryptedWalletV0 {
pub pin: [u8; 4],
// first in the list is the main Site (Personal)
pub sites: Vec<Site>,
#[zeroize(skip)]
pub sites: Vec<SiteV0>,
// list of brokers and their connection details
#[zeroize(skip)]
pub brokers: Vec<BrokerInfoV0>,
// list of all devices of the user
#[zeroize(skip)]
pub clients: Vec<ClientV0>,
#[zeroize(skip)]
pub overlay_core_overrides: HashMap<OverlayId, Vec<PubKey>>,
/// third parties data saved in the wallet. the string (key) in the hashmap should be unique among vendors.
/// the format of the byte array (value) is up to the vendor, to serde as needed.
#[zeroize(skip)]
pub third_parties: HashMap<String, Vec<u8>>,
}
/// EncryptedWallet block
@ -107,15 +139,125 @@ pub struct WalletContentV0 {
pub timestamp: Timestamp,
// the peerId that update this version of the Wallet. this value is truncated by half and concatenated with the nonce
// the peerId that updated this version of the Wallet. this value is truncated by half and concatenated with the nonce
pub peer_id: PubKey,
pub nonce: u64,
// EncryptedWallet content encrypted with XChaCha20Poly1305, AD = timestamp and walletID
// WalletLog0 content encrypted with XChaCha20Poly1305, AD = timestamp and walletID
#[serde(with = "serde_bytes")]
pub encrypted: Vec<u8>,
}
/// Wallet Log
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct WalletLog0 {
pub log: Vec<(SystemTime, WalletOperationV0)>,
}
/// WalletOperation
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum WalletOperationV0 {
CreateWalletV0(WalletOpCreateV0),
AddSiteV0(SiteV0),
RemoveSiteV0(Identity),
AddBrokerV0(BrokerInfoV0),
RemoveBrokerV0(BrokerInfoV0),
AddClientV0(ClientV0),
AddOverlayCoreOverrideV0((OverlayId, Vec<PubKey>)),
RemoveOverlayCoreOverrideV0(OverlayId),
AddSiteCoreV0((Identity, PubKey)),
RemoveSiteCoreV0((Identity, PubKey)),
AddSiteBootstrapV0((Identity, PubKey)),
RemoveSiteBootstrapV0((Identity, PubKey)),
AddThirdPartyDataV0((String, Vec<u8>)),
RemoveThirdPartyDataV0(String),
SetSiteRBDRefV0((Identity, ObjectRef)),
SetSiteRepoSecretV0((Identity, SymKey)),
}
/// WalletOp Create V0
/// first operation in the log
/// also serialized and encoded in Rescue QRcode
#[derive(Clone, Zeroize, ZeroizeOnDrop, Debug, Serialize, Deserialize)]
pub struct WalletOpCreateV0 {
pub wallet_privkey: PrivKey,
#[serde(with = "serde_bytes")]
pub pazzle: Vec<u8>,
pub mnemonic: [u16; 12],
pub pin: [u8; 4],
#[zeroize(skip)]
pub personal_site: SiteV0,
// list of brokers and their connection details
#[zeroize(skip)]
pub brokers: Vec<BrokerInfoV0>,
#[zeroize(skip)]
pub client: ClientV0,
}
/// Reduced Wallet content Version 0, for Login QRcode
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ReducedWalletContentV0 {
/// can be 9, 12 or 15 (or 0, in this case salt_pazzle and enc_master_key_pazzle are filled with zeros and should not be used)
pub pazzle_length: u8,
pub salt_pazzle: [u8; 16],
pub salt_mnemonic: [u8; 16],
// encrypted master keys. first is encrypted with pazzle, second is encrypted with mnemonic
// AD = wallet_id
#[serde(with = "BigArray")]
pub enc_master_key_pazzle: [u8; 48],
#[serde(with = "BigArray")]
pub enc_master_key_mnemonic: [u8; 48],
// nonce for the encryption of masterkey
// incremented only if the masterkey changes
// be very careful with incrementing this, as a conflict would result in total loss of crypto guarantees.
pub master_nonce: u8,
pub timestamp: Timestamp,
// the peerId that updated this version of the Wallet. this value is truncated by half and concatenated with the nonce
pub peer_id: PubKey,
pub nonce: u64,
// ReducedEncryptedWalletV0 content encrypted with XChaCha20Poly1305, AD = timestamp and walletID
#[serde(with = "serde_bytes")]
pub encrypted: Vec<u8>,
}
/// Broker Info Version 0
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum BrokerInfoV0 {
ServerV0(BrokerServerV0),
CoreV0(BrokerCoreV0),
}
/// ReducedEncryptedWallet block Version 0
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ReducedEncryptedWalletV0 {
// main Site (Personal)
pub personal_site: ReducedSiteV0,
// list of brokers and their connection details
pub brokers: Vec<BrokerInfoV0>,
pub client: ClientV0,
}
/// ReducedEncryptedWallet block
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ReducedEncryptedWallet {
V0(ReducedEncryptedWalletV0),
}
/// Wallet Version 0
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct WalletV0 {
@ -193,18 +335,25 @@ impl AddWallet {
}
/// Create Wallet Version 0, used by the API create_wallet_v0
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Zeroize, ZeroizeOnDrop, Debug, Serialize, Deserialize)]
pub struct CreateWalletV0 {
#[zeroize(skip)]
#[serde(with = "serde_bytes")]
pub security_img: Vec<u8>,
pub security_txt: String,
pub pin: [u8; 4],
pub pazzle_length: u8,
#[zeroize(skip)]
pub send_bootstrap: Option<Bootstrap>,
#[zeroize(skip)]
pub send_wallet: bool,
#[zeroize(skip)]
pub result_with_wallet_file: bool,
#[zeroize(skip)]
pub local_save: bool,
#[zeroize(skip)]
pub peer_id: PubKey,
#[zeroize(skip)]
pub nonce: u64,
}
@ -234,13 +383,16 @@ impl CreateWalletV0 {
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Zeroize, ZeroizeOnDrop, Debug, Serialize, Deserialize)]
pub struct CreateWalletResultV0 {
#[zeroize(skip)]
pub wallet: Wallet,
#[serde(with = "serde_bytes")]
#[zeroize(skip)]
pub wallet_file: Vec<u8>,
pub pazzle: Vec<u8>,
pub mnemonic: [u16; 12],
#[zeroize(skip)]
pub wallet_name: String,
}

@ -599,7 +599,7 @@ mod test {
use async_std::task;
use p2p_broker::server_ws::*;
use p2p_net::utils::{gen_dh_keys, Sensitive, U8Array};
use p2p_net::utils::gen_dh_keys;
use p2p_net::WS_PORT;
use p2p_repo::log::*;
use p2p_repo::types::PubKey;
@ -609,17 +609,11 @@ mod test {
let keys = gen_dh_keys();
// log_debug!("Public key of node: {:?}", keys.1);
// log_debug!("Private key of node: {:?}", keys.0.as_slice());
let pubkey = PubKey::Ed25519PubKey(keys.1);
log_debug!("Public key of node: {:?}", pubkey);
log_debug!("Private key of node: {:?}", keys.0.as_slice());
log_debug!("Public key of node: {}", keys.1);
log_debug!("Private key of node: {}", keys.0);
let thr = task::spawn(run_server_accept_one(
"127.0.0.1",
WS_PORT,
keys.0.as_slice(),
pubkey,
));
let thr = task::spawn(run_server_accept_one("127.0.0.1", WS_PORT, keys.0, pubkey));
// time for the server to start
std::thread::sleep(std::time::Duration::from_secs(2));

@ -22,4 +22,5 @@ base64-url = "2.0.0"
serde_json = "1.0"
regex = "1.8.4"
lazy_static = "1.4.0"
addr = "0.15.6"
addr = "0.15.6"
zeroize = { version = "1.6.0" }

@ -72,7 +72,7 @@ pub(crate) struct Cli {
pub public: Option<String>,
/// When --public is used, this option will disallow clients to connect to the public interface too. Otherwise, by default, they can. Should be used in combination with a --domain option
#[arg(long, requires("public"), conflicts_with("private"))]
#[arg(long, conflicts_with("private"))]
pub public_without_clients: bool,
/// When --public is used with a public IPV6, this option will bind the IPV6 to the private interface. This is how DMZ work for IpV6

@ -24,8 +24,7 @@ use p2p_net::utils::is_public_ip;
use p2p_net::utils::is_public_ipv4;
use p2p_net::utils::is_public_ipv6;
use p2p_net::utils::{
gen_dh_keys, is_ipv4_global, is_ipv4_private, is_ipv6_global, is_ipv6_private, keypair_from_ed,
keys_from_bytes, Dual25519Keys, Sensitive, U8Array,
gen_dh_keys, is_ipv4_global, is_ipv4_private, is_ipv6_global, is_ipv6_private,
};
use p2p_net::{WS_PORT, WS_PORT_REVERSE_PROXY};
use p2p_repo::log::*;
@ -38,6 +37,7 @@ use std::fs::{read_to_string, write};
use std::io::ErrorKind;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::path::PathBuf;
use zeroize::Zeroize;
use addr::parser::DnsName;
use addr::psl::List;
@ -287,11 +287,12 @@ fn parse_domain_and_port(
fn prepare_accept_forward_for_domain(domain: String, args: &Cli) -> Result<AcceptForwardForV0, ()> {
if args.domain_peer.is_some() {
let key_ser = base64_url::decode(args.domain_peer.as_ref().unwrap()).map_err(|_| ())?;
let key = serde_bare::from_slice::<PrivKey>(&key_ser);
let key = decode_key(args.domain_peer.as_ref().unwrap().as_str())?;
args.domain_peer.as_mut().unwrap().zeroize();
Ok(AcceptForwardForV0::PublicDomainPeer((
domain,
key.unwrap(),
PrivKey::Ed25519PrivKey(key),
"".to_string(),
)))
} else {
@ -348,16 +349,10 @@ async fn main_inner() -> Result<(), ()> {
let key_from_file: Option<[u8; 32]>;
let res = |key_path| -> Result<[u8; 32], &str> {
let file = read_to_string(key_path).map_err(|_| "")?;
decode_key(
&file
.lines()
.nth(0)
.ok_or("empty file")?
.to_string()
.trim()
.to_string(),
)
.map_err(|_| "invalid file")
let first_line = file.lines().nth(0).ok_or("empty file")?;
let res = decode_key(first_line.trim()).map_err(|_| "invalid file");
first_line.zeroize();
res
}(&key_path);
if res.is_err() && res.unwrap_err().len() > 0 {
@ -373,24 +368,25 @@ async fn main_inner() -> Result<(), ()> {
Some(key_string) => {
if key_from_file.is_some() {
log_err!("provided --key option will not be used as a key file is already present");
gen_broker_keys(Some(key_from_file.unwrap()))
gen_broker_keys(key_from_file)
} else {
let res = decode_key(key_string.as_str())
.map_err(|_| log_err!("provided key is invalid. cannot start"))?;
if args.save_key {
let master_key = base64_url::encode(&res);
write(key_path.clone(), master_key).map_err(|e| {
log_err!("cannot save key to file. {}.cannot start", e.to_string())
})?;
master_key.zeroize();
log_info!("The key has been saved to {}", key_path.to_str().unwrap());
}
gen_broker_keys(Some(res))
}
args.key.as_mut().unwrap().zeroize();
}
None => {
if key_from_file.is_some() {
gen_broker_keys(Some(key_from_file.unwrap()))
gen_broker_keys(key_from_file)
} else {
let res = gen_broker_keys(None);
let master_key = base64_url::encode(&res[0]);
@ -405,11 +401,17 @@ async fn main_inner() -> Result<(), ()> {
log_err!("At your request, the key wasn't saved. If you want to save it to disk, use ---save-key");
log_err!("provide it again to the next start of ngd with --key option or NG_SERVER_KEY env variable");
}
master_key.zeroize();
res
}
}
};
key_from_file.and_then(|mut key| {
key.zeroize();
None
});
// DEALING WITH CONFIG
// reading config from file, if any
@ -701,7 +703,9 @@ async fn main_inner() -> Result<(), ()> {
}
Some(inter) => {
overlays_config.core = BrokerOverlayPermission::AllRegisteredUser;
overlays_config.server = BrokerOverlayPermission::AllRegisteredUser;
if !args.public_without_clients {
overlays_config.server = BrokerOverlayPermission::AllRegisteredUser;
}
if listeners.last().is_some()
&& listeners.last().unwrap().interface_name == inter.name
@ -719,6 +723,7 @@ async fn main_inner() -> Result<(), ()> {
let mut listener =
ListenerV0::new_direct(inter, !args.no_ipv6, arg_value.1);
listener.accept_direct = false;
listener.refuse_clients = args.public_without_clients;
listener.serve_app = false;
listener.accept_forward_for =
AcceptForwardForV0::PublicDyn((public_port, 60, "".to_string()));
@ -939,37 +944,16 @@ async fn main_inner() -> Result<(), ()> {
}
}
// let keys = gen_keys();
// let pub_key = PubKey::Ed25519PubKey(keys.1);
// let (ed_priv_key, ed_pub_key) = generate_keypair();
//let duals = Dual25519Keys::generate();
// let eds = keypair_from_ed(duals.ed25519_priv, duals.ed25519_pub);
// let test_vector: Vec<u8> = vec![71, 51, 206, 126, 9, 84, 132];
// let sig = sign(eds.0, eds.1, &test_vector).unwrap();
// verify(&test_vector, sig, eds.1).unwrap();
// let privkey = duals.x25519_priv;
// let pubkey = PubKey::Ed25519PubKey(duals.x25519_public);
let (privkey, pubkey) = keys_from_bytes(keys[1]);
//let duals = Dual25519Keys::from_sensitive(privkey);
//let eds = keypair_from_ed(duals.ed25519_priv, duals.ed25519_pub);
//let xpriv = duals.x25519_priv;
//let xpub = PubKey::X25519PubKey(duals.x25519_public);
// let priv_key: PrivKey = privkey
// .as_slice()
// .try_into()
// .map_err(|_| log_err!("Private key of peer has invalid array"))?;
let (privkey, pubkey) = ed_keypair_from_priv_bytes(keys[1]);
keys[1].zeroize();
keys[0].zeroize();
log_info!("PeerId of node: {}", pubkey);
//let privkey_: PrivKey = xpriv.to_owned().try_into().unwrap();
//debug_println!("Private key of peer: {}", privkey_.to_string());
//let x_from_ed = eds.1.to_dh_from_ed();
//log_info!("Pub from X {}", x_from_ed);
//debug_println!("Private key of peer: {}", privkey.to_string());
//let x_from_ed = pubkey.to_dh_from_ed();
//log_info!("du Pubkey from ed: {}", x_from_ed);
match config.unwrap() {
DaemonConfig::V0(v0) => {

@ -514,7 +514,7 @@ impl Callback for SecurityCallback {
}
}
pub async fn accept(tcp: TcpStream, peer_priv_key: Sensitive<[u8; 32]>) {
pub async fn accept(tcp: TcpStream, peer_priv_key: PrivKey) {
let remote_addr = tcp.peer_addr().unwrap();
let remote_bind_address: BindAddress = (&remote_addr).into();
@ -554,7 +554,7 @@ pub async fn accept(tcp: TcpStream, peer_priv_key: Sensitive<[u8; 32]>) {
pub async fn run_server_accept_one(
addr: &str,
port: u16,
peer_priv_key: Sensitive<[u8; 32]>,
peer_priv_key: PrivKey,
peer_pub_key: PubKey,
) -> std::io::Result<()> {
let addrs = format!("{}:{}", addr, port);
@ -580,7 +580,7 @@ pub async fn run_server_accept_one(
}
pub async fn run_server_v0(
peer_priv_key: Sensitive<[u8; 32]>,
peer_priv_key: PrivKey,
peer_id: PubKey,
wallet_master_key: Sensitive<[u8; 32]>,
config: DaemonConfigV0,
@ -792,11 +792,7 @@ pub async fn run_server_v0(
// TODO : select on the shutdown stream too
while let Some(tcp) = incoming.next().await {
// TODO select peer_priv_ket according to config. if --domain-peer present and the connection is for that listener (PublicDomainPeer) then use the peer configured there
accept(
tcp.unwrap(),
Sensitive::<[u8; 32]>::from_slice(peer_priv_key.deref()),
)
.await;
accept(tcp.unwrap(), peer_priv_key.clone()).await;
}
Ok(())

@ -27,7 +27,7 @@ use futures::{FutureExt, SinkExt};
use async_std::task;
use p2p_net::errors::*;
use p2p_net::types::*;
use p2p_net::utils::{gen_ed_keys, spawn_and_log_error, Receiver, ResultSend, Sender, Sensitive};
use p2p_net::utils::{spawn_and_log_error, Receiver, ResultSend, Sender, Sensitive};
use p2p_net::{connection::*, WS_PORT};
use p2p_repo::log::*;
use p2p_repo::types::*;
@ -130,7 +130,7 @@ impl IAccept for ConnectionWebSocket {
&self,
remote_bind_address: BindAddress,
local_bind_address: BindAddress,
peer_privk: Sensitive<[u8; 32]>,
peer_privk: PrivKey,
socket: Self::Socket,
) -> Result<ConnectionBase, NetError> {
let mut cnx = ConnectionBase::new(ConnectionDir::Server, TransportProtocol::WS);
@ -311,15 +311,10 @@ mod test {
#[async_std::test]
pub async fn test_ws() -> Result<(), NgError> {
// let mut random_buf = [0u8; 32];
// getrandom::getrandom(&mut random_buf).unwrap();
let server_key: PubKey = "X0nh-gOTGKSx0yL0LYJviOWRNacyqIzjQW_LKdK6opU".try_into()?;
log_debug!("server_key:{}", server_key);
//let keys = p2p_net::utils::gen_dh_keys();
//let pub_key = PubKey::Ed25519PubKey(keys.1);
let keys = gen_ed_keys();
let keys = generate_keypair();
let x_from_ed = keys.1.to_dh_from_ed();
log_info!("Pub from X {}", x_from_ed);

@ -27,6 +27,7 @@ ed25519-dalek = "1.0.1"
either = "1.8.1"
reqwest = { version = "0.11.18", features = ["json"] }
url = "2.4.0"
base64-url = "2.0.0"
[target.'cfg(target_arch = "wasm32")'.dependencies.getrandom]
version = "0.2.7"

@ -63,7 +63,8 @@ pub static BROKER: Lazy<Arc<RwLock<Broker>>> = Lazy::new(|| Arc::new(RwLock::new
pub struct Broker {
direct_connections: HashMap<IP, DirectConnection>,
peers: HashMap<X25519PubKey, BrokerPeerInfo>,
/// tuple of optional userId and peer key in montgomery form. userId is always None on the server side.
peers: HashMap<(Option<PubKey>, X25519PubKey), BrokerPeerInfo>,
/// (local,remote) -> ConnectionBase
anonymous_connections: HashMap<(BindAddress, BindAddress), ConnectionBase>,
#[cfg(not(target_arch = "wasm32"))]
@ -145,9 +146,10 @@ impl Broker {
}
}
Authorization::ExtMessage => Err(ProtocolError::AccessDenied),
Authorization::Client => Err(ProtocolError::AccessDenied),
Authorization::Client(_) => Err(ProtocolError::AccessDenied),
Authorization::Core => Err(ProtocolError::AccessDenied),
Authorization::Admin => Err(ProtocolError::AccessDenied),
Authorization::Admin(_) => Err(ProtocolError::AccessDenied),
Authorization::OverlayJoin(_) => Err(ProtocolError::AccessDenied),
}
}
@ -215,7 +217,7 @@ impl Broker {
112, 95, 150, 144, 137, 9, 57, 106, 5, 39, 202, 146, 94,
]),
};
let refs = vec![obj_ref];
let refs = vec![obj_ref.clone()];
let metadata = vec![5u8; 55];
let expiry = None;
@ -225,12 +227,12 @@ impl Broker {
member_privkey,
member_pubkey,
1,
obj_ref,
obj_ref.clone(),
vec![],
vec![],
refs,
metadata,
obj_ref,
obj_ref.clone(),
expiry,
)
.unwrap();
@ -247,8 +249,8 @@ impl Broker {
(rx, tx.clone())
}
pub fn reconnecting(&mut self, peer_id: &DirectPeerId) {
let peerinfo = self.peers.get_mut(&peer_id.to_dh_slice());
pub fn reconnecting(&mut self, peer_id: &DirectPeerId, user: Option<PubKey>) {
let peerinfo = self.peers.get_mut(&(user, peer_id.to_dh_slice()));
match peerinfo {
Some(info) => match &info.connected {
PeerConnection::NONE => {}
@ -263,8 +265,8 @@ impl Broker {
None => {}
}
}
pub fn remove_peer_id(&mut self, peer_id: &DirectPeerId) {
let removed = self.peers.remove(&peer_id.to_dh_slice());
pub fn remove_peer_id(&mut self, peer_id: &DirectPeerId, user: Option<PubKey>) {
let removed = self.peers.remove(&(user, peer_id.to_dh_slice()));
match removed {
Some(info) => match info.connected {
PeerConnection::NONE => {}
@ -366,7 +368,11 @@ impl Broker {
anonymous = Vec::from_iter(broker.anonymous_connections.keys().cloned());
}
for peer_id in peer_ids {
BROKER.write().await.close_peer_connection_x(&peer_id).await;
BROKER
.write()
.await
.close_peer_connection_x(peer_id.1, peer_id.0)
.await;
}
for anon in anonymous {
BROKER.write().await.close_anonymous(anon.1, anon.0).await;
@ -422,7 +428,7 @@ impl Broker {
Some(Either::Right(remote_peer_id)) => {
let res = join.next().await;
log_info!("SOCKET IS CLOSED {:?} peer_id: {:?}", res, remote_peer_id);
BROKER.write().await.remove_peer_id(&remote_peer_id);
BROKER.write().await.remove_peer_id(&remote_peer_id, None);
}
_ => {
log_info!(
@ -478,7 +484,7 @@ impl Broker {
lastPeerAdvert: None,
connected,
};
self.peers.insert(remote_peer_id.to_dh_slice(), bpi);
self.peers.insert((None, remote_peer_id.to_dh_slice()), bpi);
Ok(())
}
@ -498,7 +504,7 @@ impl Broker {
pub async fn connect(
&mut self,
cnx: Box<dyn IConnect>,
peer_privk: Sensitive<[u8; 32]>,
peer_privk: PrivKey,
peer_pubk: PubKey,
remote_peer_id: DirectPeerId,
config: StartConfig,
@ -514,7 +520,7 @@ impl Broker {
let mut connection = cnx
.open(
config.get_url(),
Sensitive::<[u8; 32]>::from_slice(peer_privk.deref()),
peer_privk.clone(),
peer_pubk,
remote_peer_id,
config.clone(),
@ -544,12 +550,13 @@ impl Broker {
lastPeerAdvert: None,
connected,
};
self.peers.insert(remote_peer_id.to_dh_slice(), bpi);
self.peers
.insert((config.get_user(), remote_peer_id.to_dh_slice()), bpi);
async fn watch_close(
mut join: Receiver<Either<NetError, PubKey>>,
cnx: Box<dyn IConnect>,
peer_privk: Sensitive<[u8; 32]>,
peer_privk: PrivKey,
peer_pubkey: PubKey,
remote_peer_id: DirectPeerId,
config: StartConfig,
@ -563,7 +570,7 @@ impl Broker {
{
// we intend to reconnect
let mut broker = BROKER.write().await;
broker.reconnecting(&remote_peer_id);
broker.reconnecting(&remote_peer_id, config.get_user());
// TODO: deal with cycle error https://users.rust-lang.org/t/recursive-async-method-causes-cycle-error/84628/5
// let result = broker
// .connect(cnx, ip, core, peer_pubk, peer_privk, remote_peer_id)
@ -572,7 +579,10 @@ impl Broker {
// TODO: deal with error and incremental backoff
} else {
log_info!("REMOVED");
BROKER.write().await.remove_peer_id(&remote_peer_id);
BROKER
.write()
.await
.remove_peer_id(&remote_peer_id, config.get_user());
}
}
.await;
@ -589,8 +599,8 @@ impl Broker {
Ok(())
}
pub async fn close_peer_connection_x(&mut self, peer_id: &X25519PubKey) {
if let Some(peer) = self.peers.get_mut(peer_id) {
pub async fn close_peer_connection_x(&mut self, peer_id: X25519PubKey, user: Option<PubKey>) {
if let Some(peer) = self.peers.get_mut(&(user, peer_id)) {