zeroize everything

Niko PLP 10 months ago
parent 1a9df0c747
commit f34d13be0a
  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)) {
match &mut peer.connected {
PeerConnection::Core(_) => {
//TODO
@ -605,8 +615,9 @@ impl Broker {
}
}
pub async fn close_peer_connection(&mut self, peer_id: &DirectPeerId) {
self.close_peer_connection_x(&peer_id.to_dh_slice()).await
pub async fn close_peer_connection(&mut self, peer_id: &DirectPeerId, user: Option<PubKey>) {
self.close_peer_connection_x(peer_id.to_dh_slice(), user)
.await
}
pub async fn close_anonymous(

@ -53,7 +53,7 @@ pub trait IConnect: Send + Sync {
async fn open(
&self,
url: String,
peer_privk: Sensitive<[u8; 32]>,
peer_privk: PrivKey,
peer_pubk: PubKey,
remote_peer: DirectPeerId,
config: StartConfig,
@ -70,7 +70,7 @@ pub trait IAccept: Send + Sync {
&self,
remote_bind_address: BindAddress,
local_bind_address: BindAddress,
peer_privk: Sensitive<[u8; 32]>,
peer_privk: PrivKey,
socket: Self::Socket,
) -> Result<ConnectionBase, NetError>;
}
@ -120,7 +120,7 @@ pub struct NoiseFSM {
noise_cipher_state_enc: Option<CipherState<ChaCha20Poly1305>>,
noise_cipher_state_dec: Option<CipherState<ChaCha20Poly1305>>,
local: Option<Sensitive<[u8; 32]>>,
local: Option<PrivKey>,
remote: Option<PubKey>,
nonce_for_hello: Vec<u8>,
@ -182,6 +182,12 @@ impl StartConfig {
_ => unimplemented!(),
}
}
pub fn get_user(&self) -> Option<PubKey> {
match self {
Self::Client(config) => Some(config.user),
_ => None,
}
}
}
impl NoiseFSM {
@ -191,7 +197,7 @@ impl NoiseFSM {
dir: ConnectionDir,
actors: Arc<Mutex<HashMap<i64, Sender<ConnectionCommand>>>>,
sender: Sender<ConnectionCommand>,
local: Option<Sensitive<[u8; 32]>>,
local: Option<PrivKey>,
remote: Option<PubKey>,
) -> Self {
Self {
@ -280,7 +286,7 @@ impl NoiseFSM {
noise_xk(),
false,
&[],
Some(from_ed_priv_to_dh_priv(self.local.take().unwrap())),
Some(sensitive_from_privkey(self.local.take().unwrap().to_dh())),
None,
None,
None,
@ -363,7 +369,9 @@ impl NoiseFSM {
noise_xk(),
true,
&[],
Some(from_ed_priv_to_dh_priv(self.local.take().unwrap())),
Some(sensitive_from_privkey(
self.local.take().unwrap().to_dh(),
)),
None,
Some(*self.remote.unwrap().to_dh_from_ed().slice()),
None,
@ -410,12 +418,12 @@ impl NoiseFSM {
)
.is_ok()
{
probe_response.peer_id =
Some(ed_sensitive_privkey_to_pubkey(
self.local
.as_ref()
.ok_or(ProtocolError::BrokerError)?,
));
probe_response.peer_id = Some(
self.local
.as_ref()
.ok_or(ProtocolError::BrokerError)?
.to_pub(),
);
}
self.send(ProtocolMessage::ProbeResponse(probe_response))
.await?;
@ -586,7 +594,7 @@ impl NoiseFSM {
};
let ser = serde_bare::to_vec(&content)?;
let sig =
sign(client_config.client_priv, client_config.client, &ser)?;
sign(&client_config.client_priv, &client_config.client, &ser)?;
let client_auth = ClientAuth::V0(ClientAuthV0 {
content,
/// Signature by client key
@ -963,7 +971,7 @@ impl ConnectionBase {
pub fn start_read_loop(
&mut self,
bind_addresses: Option<(BindAddress, BindAddress)>,
local: Option<Sensitive<[u8; 32]>>,
local: Option<PrivKey>,
remote: Option<PubKey>,
) {
let (sender_tx, sender_rx) = mpsc::unbounded();

@ -15,7 +15,7 @@ pub mod types;
pub mod errors;
pub mod broker_connection;
//pub mod broker_connection;
pub mod broker;
@ -29,6 +29,8 @@ pub mod utils;
pub mod tests;
pub mod site;
#[cfg(debug_assertions)]
pub static WS_PORT: u16 = 14400;

@ -0,0 +1,66 @@
/*
* Copyright (c) 2022-2023 Niko Bonnieure, Par le Peuple, NextGraph.org developers
* All rights reserved.
* Licensed under the Apache License, Version 2.0
* <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
* or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
* at your option. All files in the project carrying such
* notice may not be copied, modified, or distributed except
* according to those terms.
*/
use crate::types::{SiteStore, SiteType, SiteV0};
use p2p_repo::errors::NgError;
use p2p_repo::types::{BlockRef, PrivKey, SymKey};
use p2p_repo::utils::{generate_keypair, sign, verify};
impl SiteV0 {
// pub fn site_identity(&self) -> &Identity {
// match site_type {
// SiteType::Individual => {
// Identity::IndividualSite(self.site_key);
// }
// SiteType::Org => {
// Identity::OrgPublic(self.public_key)
// }
// }
// }
pub fn create(site_type: SiteType) -> Result<Self, NgError> {
let site_key = PrivKey::random_ed();
let public_key = PrivKey::random_ed();
let protected_key = PrivKey::random_ed();
let private_key = PrivKey::random_ed();
let public = SiteStore {
key: PrivKey::dummy(),
root_branch_def_ref: BlockRef::dummy(),
repo_secret: SymKey::random(),
};
let protected = SiteStore {
key: PrivKey::dummy(),
root_branch_def_ref: BlockRef::dummy(),
repo_secret: SymKey::random(),
};
let private = SiteStore {
key: PrivKey::dummy(),
root_branch_def_ref: BlockRef::dummy(),
repo_secret: SymKey::random(),
};
Ok(Self {
site_type,
site_key,
public,
protected,
private,
cores: vec![],
bootstraps: vec![],
})
}
}

@ -21,6 +21,7 @@ use crate::{actor::EActor, actors::*, errors::ProtocolError};
use core::fmt;
use p2p_repo::types::*;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::{
any::{Any, TypeId},
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
@ -81,6 +82,80 @@ pub struct Interface {
pub ipv6: Vec<default_net::ip::Ipv6Net>,
}
/// List of Identity types
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum Identity {
OrgSite(PubKey),
IndividualSite(PubKey),
OrgPublic(PubKey),
OrgProtected(PubKey),
OrgPrivate(PubKey),
IndividualPublic(PubKey),
IndividualProtected(PubKey),
IndividualPrivate(PubKey),
Group(RepoId),
Dialog(RepoId),
Document(RepoId),
DialogOverlay(Digest),
}
/// Site type
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum SiteType {
Org,
Individual, // formerly Personal
}
/// Site Store
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct SiteStore {
// pub identity: Identity,
pub key: PrivKey,
// signature with site_key
// pub sig: Sig,
pub root_branch_def_ref: ObjectRef,
pub repo_secret: SymKey,
}
/// Site V0
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct SiteV0 {
pub site_type: SiteType,
// Identity::OrgSite or Identity::IndividualSite
// pub site_identity: Identity,
pub site_key: PrivKey,
// Identity::OrgPublic or Identity::IndividualPublic
pub public: SiteStore,
// Identity::OrgProtected or Identity::IndividualProtected
pub protected: SiteStore,
// Identity::OrgPrivate or Identity::IndividualPrivate
pub private: SiteStore,
pub cores: Vec<PubKey>,
pub bootstraps: Vec<PubKey>,
}
/// Reduced Site (for QRcode)
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct ReducedSiteV0 {
pub site_key: PrivKey,
pub private_site_key: PrivKey,
pub private_site_root_branch_def_ref: ObjectRef,
pub private_site_repo_secret: SymKey,
pub cores: Vec<PubKey>,
pub bootstraps: Vec<PubKey>,
}
/// Bind address
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct BindAddress {
@ -108,6 +183,22 @@ impl From<&SocketAddr> for BindAddress {
}
}
/// Core Broker connection details Version 0
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct BrokerCoreV0 {
/// peerId of the server
pub peer_id: PubKey,
/// network addresses of the broker, typically an IpV4 and an optional IPV6 addr. core broker should not be multi-homed.
pub addrs: Vec<BindAddress>,
}
/// Core Broker connection details
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Hash)]
pub enum BrokerCore {
V0(BrokerCoreV0),
}
/// BrokerServerTypeV0 type
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum BrokerServerTypeV0 {
@ -116,6 +207,7 @@ pub enum BrokerServerTypeV0 {
BoxPublic(Vec<BindAddress>),
BoxPublicDyn(Vec<BindAddress>), // can be empty
Domain(String), // accepts an option trailing ":port" number
//Core(Vec<BindAddress>),
}
/// BrokerServer details Version 0
@ -186,6 +278,107 @@ impl BrokerServerV0 {
})
}
fn first_ipv6_or_ipv4(
ipv4: bool,
ipv6: bool,
addrs: &Vec<BindAddress>,
) -> Option<&BindAddress> {
if ipv6 {
for addr in addrs {
if addr.ip.is_v6() {
return Some(addr);
}
}
}
if ipv4 {
for addr in addrs {
if addr.ip.is_v4() {
return Some(addr);
}
}
}
return None;
}
fn app_ng_one_bootstrap_url(addr: &BindAddress, key: PubKey) -> Option<String> {
let payload = (addr, key);
let payload_ser = serde_bare::to_vec(&payload).ok();
if payload_ser.is_none() {
return None;
}
Some(format!(
"{}?b={}",
APP_NG_ONE_WS_URL,
base64_url::encode(&payload_ser.unwrap())
))
}
fn app_ng_one_bootstrap_url_with_first_ipv6_or_ipv4(
ipv4: bool,
ipv6: bool,
addrs: &Vec<BindAddress>,
key: PubKey,
) -> Option<String> {
if let Some(addr) = Self::first_ipv6_or_ipv4(ipv4, ipv6, addrs) {
return Self::app_ng_one_bootstrap_url(addr, key);
}
None
}
/// set ipv6 only if the browser connected with a remote IPV6. always set ipv4 as a fallback (for now).
pub async fn get_url_for_ngone(&self, ipv4: bool, ipv6: bool) -> Option<String> {
match &self.server_type {
BrokerServerTypeV0::BoxPublic(addrs) => {
Self::app_ng_one_bootstrap_url_with_first_ipv6_or_ipv4(
ipv4,
ipv6,
addrs,
self.peer_id,
)
}
BrokerServerTypeV0::BoxPublicDyn(addrs) => {
let resp = reqwest::get(api_dyn_peer_url(&self.peer_id)).await;
if resp.is_ok() {
let resp = resp.unwrap().json::<Vec<BindAddress>>().await;
if resp.is_ok() {
return Self::app_ng_one_bootstrap_url_with_first_ipv6_or_ipv4(
ipv4,
ipv6,
&resp.unwrap(),
self.peer_id,
);
}
}
if addrs.len() > 0 {
Self::app_ng_one_bootstrap_url_with_first_ipv6_or_ipv4(
ipv4,
ipv6,
&addrs,
self.peer_id,
)
} else {
None
}
}
BrokerServerTypeV0::Domain(domain) => Some(format!("https://{}", domain)),
BrokerServerTypeV0::Localhost(port) => Some(local_ws_url(&port)),
BrokerServerTypeV0::BoxPrivate(_) => {
if ipv6 {
let v6 = self.first_ipv6().map(|v| v.0);
if v6.is_some() {
return v6;
}
}
if ipv4 {
self.first_ipv4().map(|v| v.0)
} else {
None
}
}
_ => None,
}
}
/// on web browser, returns the connection URL and an optional list of BindAddress if a relay is needed
/// filtered by the current location url of the webpage
/// on native apps, returns or the connection URL without optional BindAddress or an empty string with
@ -261,6 +454,7 @@ impl BrokerServerV0 {
} else {
// From native / tauri app
match &self.server_type {
//BrokerServerTypeV0::Core(_) => None,
BrokerServerTypeV0::Localhost(port) => Some((local_ws_url(port), vec![])),
BrokerServerTypeV0::BoxPrivate(addrs) => Some((String::new(), addrs.clone())),
BrokerServerTypeV0::BoxPublic(addrs) => Some((String::new(), addrs.clone())),
@ -503,21 +697,20 @@ impl ListenerV0 {
let mut res: Vec<BrokerServerTypeV0> = vec![];
match self.accept_forward_for {
AcceptForwardForV0::PublicStatic(_) => {
let pub_addrs = self.accept_forward_for.get_public_bind_addresses();
//res.push(BrokerServerTypeV0::Core(pub_addrs.clone()));
if !self.refuse_clients {
res.push(BrokerServerTypeV0::BoxPublic(
self.accept_forward_for.get_public_bind_addresses(),
));
res.push(BrokerServerTypeV0::BoxPublic(pub_addrs));
}
if self.accept_direct {
res.push(BrokerServerTypeV0::BoxPrivate(addrs));
}
}
AcceptForwardForV0::PublicDyn(_) => {
let pub_addrs = self.accept_forward_for.get_public_bind_addresses();
//res.push(BrokerServerTypeV0::Core(pub_addrs.clone()));
if !self.refuse_clients {
res.push(BrokerServerTypeV0::BoxPublicDyn(
// self.accept_forward_for.get_public_bind_addresses(), //FIXME. we should use this, but for now it isnt implemented
vec![],
));
res.push(BrokerServerTypeV0::BoxPublicDyn(pub_addrs));
}
if self.accept_direct {
res.push(BrokerServerTypeV0::BoxPrivate(addrs));
@ -545,8 +738,11 @@ impl ListenerV0 {
AcceptForwardForV0::No => {
if self.if_type == InterfaceType::Loopback {
res.push(BrokerServerTypeV0::Localhost(addrs[0].port));
} else if self.if_type == InterfaceType::Public && !self.refuse_clients {
res.push(BrokerServerTypeV0::BoxPublic(addrs));
} else if self.if_type == InterfaceType::Public {
//res.push(BrokerServerTypeV0::Core(addrs.clone()));
if !self.refuse_clients {
res.push(BrokerServerTypeV0::BoxPublic(addrs));
}
} else if self.if_type == InterfaceType::Private {
res.push(BrokerServerTypeV0::BoxPrivate(addrs));
}
@ -1546,9 +1742,9 @@ impl OverlayJoin {
OverlayJoin::V0(o) => o.repo_pubkey,
}
}
pub fn secret(&self) -> SymKey {
pub fn secret(&self) -> &SymKey {
match self {
OverlayJoin::V0(o) => o.secret,
OverlayJoin::V0(o) => &o.secret,
}
}
pub fn peers(&self) -> &Vec<PeerAdvert> {
@ -2317,9 +2513,10 @@ pub static MAGIC_NG_RESPONSE: [u8; 4] = [89u8, 88u8, 78u8, 75u8];
pub enum Authorization {
Discover,
ExtMessage,
Client,
Core,
Admin,
Client(PubKey),
OverlayJoin(PubKey),
Admin(PubKey),
}
/// ProbeResponse
@ -2557,6 +2754,9 @@ pub struct RepoLinkV0 {
/// Repository secret
pub secret: SymKey,
/// current root branch definition commit
pub root_branch_def_ref: ObjectRef,
/// Peers to connect to
pub peers: Vec<PeerAdvert>,
}
@ -2568,19 +2768,19 @@ pub enum RepoLink {
}
impl RepoLink {
pub fn id(&self) -> PubKey {
pub fn id(&self) -> &PubKey {
match self {
RepoLink::V0(o) => o.id,
RepoLink::V0(o) => &o.id,
}
}
pub fn secret(&self) -> SymKey {
pub fn secret(&self) -> &SymKey {
match self {
RepoLink::V0(o) => o.secret,
RepoLink::V0(o) => &o.secret,
}
}
pub fn peers(&self) -> Vec<PeerAdvert> {
pub fn peers(&self) -> &Vec<PeerAdvert> {
match self {
RepoLink::V0(o) => o.peers.clone(),
RepoLink::V0(o) => &o.peers,
}
}
}

@ -12,11 +12,11 @@
use async_std::task;
use ed25519_dalek::*;
use futures::{channel::mpsc, select, Future, FutureExt, SinkExt};
pub use noise_protocol::U8Array;
use noise_protocol::U8Array;
use noise_protocol::DH;
pub use noise_rust_crypto::sensitive::Sensitive;
use p2p_repo::log::*;
use noise_rust_crypto::sensitive::Sensitive;
use p2p_repo::types::PubKey;
use p2p_repo::{log::*, types::PrivKey};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
#[cfg(target_arch = "wasm32")]
@ -48,50 +48,28 @@ where
})
}
pub type Sender<T> = mpsc::UnboundedSender<T>;
pub type Receiver<T> = mpsc::UnboundedReceiver<T>;
pub fn keypair_from_ed(secret: SecretKey, public: PublicKey) -> (Sensitive<[u8; 32]>, PubKey) {
let ed_priv_key = secret.to_bytes();
let ed_pub_key = public.to_bytes();
//let priv_key = PrivKey::Ed25519PrivKey(ed_priv_key);
let pub_key = PubKey::Ed25519PubKey(ed_pub_key);
let priv_key = Sensitive::<[u8; 32]>::from_slice(&ed_priv_key);
(priv_key, pub_key)
pub fn sensitive_from_privkey(privkey: PrivKey) -> Sensitive<[u8; 32]> {
// we copy the key here, because otherwise the 2 zeroize would conflict. as the drop of the PrivKey might be called before the one of Sensitive
let mut bits: [u8; 32] = [0u8; 32];
bits.copy_from_slice(privkey.slice());
Sensitive::<[u8; 32]>::from_slice(&bits)
}
pub fn ed_sensitive_privkey_to_pubkey(privkey: &Sensitive<[u8; 32]>) -> PubKey {
//TODO FIXME do not create a SecretKey or call into() on it, as this is not using Sensitive<>
let sk = SecretKey::from_bytes(privkey.as_slice()).unwrap();
let pk: PublicKey = (&sk).into();
PubKey::Ed25519PubKey(pk.to_bytes())
pub fn dh_privkey_from_sensitive(privkey: Sensitive<[u8; 32]>) -> PrivKey {
// we copy the key here, because otherwise the 2 zeroize would conflict. as the drop of the Sensitive might be called before the one of PrivKey
let mut bits: [u8; 32] = [0u8; 32];
bits.copy_from_slice(privkey.as_slice());
PrivKey::X25519PrivKey(bits)
}
pub fn keys_from_bytes(secret_key: [u8; 32]) -> (Sensitive<[u8; 32]>, PubKey) {
let sk = SecretKey::from_bytes(&secret_key).unwrap();
let pk: PublicKey = (&sk).into();
let pub_key = PubKey::Ed25519PubKey(pk.to_bytes());
let priv_key = Sensitive::<[u8; 32]>::from_slice(&secret_key);
(priv_key, pub_key)
}
pub type Sender<T> = mpsc::UnboundedSender<T>;
pub type Receiver<T> = mpsc::UnboundedReceiver<T>;
pub fn gen_dh_keys() -> (Sensitive<[u8; 32]>, [u8; 32]) {
pub fn gen_dh_keys() -> (PrivKey, PubKey) {
let pri = noise_rust_crypto::X25519::genkey();
let publ = noise_rust_crypto::X25519::pubkey(&pri);
(pri, publ)
}
pub fn gen_ed_keys() -> (Sensitive<[u8; 32]>, PubKey) {
let mut ed25519_priv = Sensitive::<[u8; 32]>::new();
getrandom::getrandom(&mut *ed25519_priv).expect("getrandom failed");
//TODO FIXME do not create a SecretKey or call into() on it, as this is not using Sensitive<>
let secret = SecretKey::from_bytes(&ed25519_priv.as_slice()).unwrap();
let ed25519_pub: PublicKey = (&secret).into();
(ed25519_priv, PubKey::Ed25519PubKey(ed25519_pub.to_bytes()))
(dh_privkey_from_sensitive(pri), PubKey::X25519PubKey(publ))
}
pub struct Dual25519Keys {
@ -101,47 +79,18 @@ pub struct Dual25519Keys {
pub ed25519_pub: PublicKey,
}
pub fn from_ed_priv_to_dh_priv(private: Sensitive<[u8; 32]>) -> Sensitive<[u8; 32]> {
let ed25519_priv = SecretKey::from_bytes(&private.as_slice()).unwrap();
let exp: ExpandedSecretKey = (&ed25519_priv).into();
let exp_bytes = exp.to_bytes();
let mut bits = Sensitive::<[u8; 32]>::from_slice(&exp_bytes[0..32]);
bits[0] &= 248;
bits[31] &= 127;
bits[31] |= 64;
bits
}
impl Dual25519Keys {
pub fn generate() -> Self {
let mut x25519_priv = Sensitive::<[u8; 32]>::new();
getrandom::getrandom(&mut *x25519_priv).expect("getrandom failed");
let mut random = Sensitive::<[u8; 32]>::new();
getrandom::getrandom(&mut *random).expect("getrandom failed");
let ed25519_priv = SecretKey::from_bytes(&x25519_priv.as_slice()).unwrap();
let ed25519_priv = SecretKey::from_bytes(&random.as_slice()).unwrap();
let exp: ExpandedSecretKey = (&ed25519_priv).into();
let exp_bytes = exp.to_bytes();
let mut exp_bytes = exp.to_bytes();
let ed25519_pub: PublicKey = (&ed25519_priv).into();
let mut bits = Sensitive::<[u8; 32]>::from_slice(&exp_bytes[0..32]);
bits[0] &= 248;
bits[31] &= 127;
bits[31] |= 64;
let x25519_public = noise_rust_crypto::X25519::pubkey(&bits);
Self {
x25519_priv: bits,
x25519_public,
ed25519_priv,
ed25519_pub,
for byte in &mut exp_bytes[32..] {
*byte = 0;
}
}
pub fn from_sensitive(sensitive: Sensitive<[u8; 32]>) -> Self {
let ed25519_priv = SecretKey::from_bytes(&sensitive.as_slice()).unwrap();
let exp: ExpandedSecretKey = (&ed25519_priv).into();
let exp_bytes = exp.to_bytes();
let ed25519_pub: PublicKey = (&ed25519_priv).into();
let mut bits = Sensitive::<[u8; 32]>::from_slice(&exp_bytes[0..32]);
bits[0] &= 248;
bits[31] &= 127;

@ -27,6 +27,7 @@ web-time = "0.2.0"
wasm-bindgen = "0.2"
slice_as_array = "1.1.0"
curve25519-dalek = "3.2.0"
zeroize = { version = "1.6.0", features = ["zeroize_derive"] }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
debug_print = "1.0.0"

@ -110,7 +110,7 @@ impl Block {
/// Get the key
pub fn key(&self) -> Option<SymKey> {
match self {
Block::V0(b) => b.key,
Block::V0(b) => b.key.clone(),
}
}

@ -429,128 +429,128 @@ mod test {
repo_secret.clone(),
&mut store,
);
let ack_body = add_body_ack(vec![], repo_pubkey, repo_secret, &mut store);
let trans_body = add_body_trans(vec![], repo_pubkey, repo_secret, &mut store);
let ack_body = add_body_ack(vec![], repo_pubkey, repo_secret.clone(), &mut store);
let trans_body = add_body_trans(vec![], repo_pubkey, repo_secret.clone(), &mut store);
// create & add commits to store
log_debug!(">> br");
let br = add_commit(
branch_body,
member_privkey,
branch_body.clone(),
member_privkey.clone(),
member_pubkey,
0,
vec![],
vec![],
branch_body,
branch_body.clone(),
repo_pubkey,
repo_secret,
repo_secret.clone(),
&mut store,
);
log_debug!(">> t1");
let t1 = add_commit(
branch_body,
member_privkey,
branch_body.clone(),
member_privkey.clone(),
member_pubkey,
1,
vec![br],
vec![br.clone()],
vec![],
trans_body,
trans_body.clone(),
repo_pubkey,
repo_secret,
repo_secret.clone(),
&mut store,
);
log_debug!(">> t2");
let t2 = add_commit(
branch_body,
member_privkey,
branch_body.clone(),
member_privkey.clone(),
member_pubkey,
2,
vec![br],
vec![br.clone()],
vec![],
trans_body,
trans_body.clone(),
repo_pubkey,
repo_secret,
repo_secret.clone(),
&mut store,
);
log_debug!(">> a3");
let a3 = add_commit(
branch_body,
member_privkey,
branch_body.clone(),
member_privkey.clone(),
member_pubkey,
3,
vec![t1],
vec![t1.clone()],
vec![],
ack_body,
ack_body.clone(),
repo_pubkey,
repo_secret,
repo_secret.clone(),
&mut store,
);
log_debug!(">> t4");
let t4 = add_commit(
branch_body,
member_privkey,
branch_body.clone(),
member_privkey.clone(),
member_pubkey,
4,
vec![t2],
vec![t1],
trans_body,
vec![t2.clone()],
vec![t1.clone()],
trans_body.clone(),
repo_pubkey,
repo_secret,
repo_secret.clone(),
&mut store,
);
log_debug!(">> t5");
let t5 = add_commit(
branch_body,
member_privkey,
branch_body.clone(),
member_privkey.clone(),
member_pubkey,
5,
vec![t1, t2],
vec![t4],
trans_body,
vec![t1.clone(), t2.clone()],
vec![t4.clone()],
trans_body.clone(),
repo_pubkey,
repo_secret,
repo_secret.clone(),
&mut store,
);
log_debug!(">> a6");
let a6 = add_commit(
branch_body,
member_privkey,
branch_body.clone(),
member_privkey.clone(),
member_pubkey,
6,
vec![t4],
vec![t4.clone()],
vec![],
ack_body,
ack_body.clone(),
repo_pubkey,
repo_secret,
repo_secret.clone(),
&mut store,
);
log_debug!(">> a7");
let a7 = add_commit(
branch_body,
member_privkey,
branch_body.clone(),
member_privkey.clone(),
member_pubkey,
7,
vec![t4],
vec![t4.clone()],
vec![],
ack_body,
ack_body.clone(),
repo_pubkey,
repo_secret,
repo_secret.clone(),
&mut store,
);
let c7 = Commit::load(a7, &store).unwrap();
let c7 = Commit::load(a7.clone(), &store).unwrap();
c7.verify(&branch, &store).unwrap();
let mut filter = Filter::new(FilterBuilder::new(10, 0.01));
for commit_ref in [br, t1, t2, a3, t5, a6] {
for commit_ref in [br, t1, t2, a3.clone(), t5.clone(), a6.clone()] {
match commit_ref.id {
ObjectId::Blake3Digest32(d) => filter.add(&d),
}

@ -132,7 +132,7 @@ impl Commit {
/// Load commit from store
pub fn load(commit_ref: ObjectRef, store: &impl RepoStore) -> Result<Commit, CommitLoadError> {
let (id, key) = (commit_ref.id, commit_ref.key);
match Object::load(id, Some(key), store) {
match Object::load(id, Some(key.clone()), store) {
Ok(obj) => {
let content = obj
.content()
@ -142,7 +142,7 @@ impl Commit {
_ => return Err(CommitLoadError::DeserializeError),
};
commit.set_id(id);
commit.set_key(key);
commit.set_key(key.clone());
Ok(commit)
}
Err(ObjectParseError::MissingBlocks(missing)) => {
@ -155,7 +155,7 @@ impl Commit {
/// Load commit body from store
pub fn load_body(&self, store: &impl RepoStore) -> Result<CommitBody, CommitLoadError> {
let content = self.content();
let (id, key) = (content.body.id, content.body.key);
let (id, key) = (content.body.id, content.body.key.clone());
let obj = Object::load(id.clone(), Some(key.clone()), store).map_err(|e| match e {
ObjectParseError::MissingBlocks(missing) => CommitLoadError::MissingBlocks(missing),
_ => CommitLoadError::ObjectParseError,
@ -186,7 +186,7 @@ impl Commit {
/// Get key of parent `Object`
pub fn key(&self) -> Option<SymKey> {
match self {
Commit::V0(c) => c.key,
Commit::V0(c) => c.key.clone(),
}
}
@ -381,7 +381,7 @@ mod test {
id: ObjectId::Blake3Digest32([1; 32]),
key: SymKey::ChaCha20Key([2; 32]),
};
let obj_refs = vec![obj_ref];
let obj_refs = vec![obj_ref.clone()];
let branch = obj_ref.clone();
let deps = obj_refs.clone();
let acks = obj_refs.clone();

@ -18,8 +18,6 @@ pub mod errors;
pub mod kcv_store;
pub mod site;
#[macro_use]
extern crate slice_as_array;

@ -198,9 +198,14 @@ impl Object {
let data_chunk_size = valid_block_size - EMPTY_BLOCK_SIZE - DATA_VARINT_EXTRA;
let mut blocks: Vec<Block> = vec![];
let conv_key = Self::convergence_key(repo_pubkey, repo_secret);
let conv_key = Self::convergence_key(repo_pubkey, repo_secret.clone());
let obj_deps = Self::make_deps(deps.clone(), valid_block_size, repo_pubkey, repo_secret);
let obj_deps = Self::make_deps(
deps.clone(),
valid_block_size,
repo_pubkey,
repo_secret.clone(),
);
let content_ser = serde_bare::to_vec(&content).unwrap();
@ -481,7 +486,7 @@ impl Object {
BlockContentV0::DataChunk(chunk) => {
if leaves.is_some() {
let mut leaf = block.clone();
leaf.set_key(Some(*key));
leaf.set_key(Some(key.clone()));
let l = &mut **leaves.as_mut().unwrap();
l.push(leaf);
}
@ -657,7 +662,7 @@ mod test {
exp,
max_object_size,
repo_pubkey,
repo_secret,
repo_secret.clone(),
);
log_debug!("obj.id: {:?}", obj.id());
@ -800,13 +805,13 @@ mod test {
let id = Digest::Blake3Digest32([0u8; 32]);
let key = SymKey::ChaCha20Key([0u8; 32]);
let one_key = BlockContentV0::InternalNode(vec![key]);
let one_key = BlockContentV0::InternalNode(vec![key.clone()]);
let one_key_ser = serde_bare::to_vec(&one_key).unwrap();
let two_keys = BlockContentV0::InternalNode(vec![key, key]);
let two_keys = BlockContentV0::InternalNode(vec![key.clone(), key.clone()]);
let two_keys_ser = serde_bare::to_vec(&two_keys).unwrap();
let max_keys = BlockContentV0::InternalNode(vec![key; MAX_ARITY_LEAVES]);
let max_keys = BlockContentV0::InternalNode(vec![key.clone(); MAX_ARITY_LEAVES]);
let max_keys_ser = serde_bare::to_vec(&max_keys).unwrap();
let data = BlockContentV0::DataChunk(vec![]);

@ -1,79 +0,0 @@
/*
* Copyright (c) 2022-2023 Niko Bonnieure, Par le Peuple, NextGraph.org developers
* All rights reserved.
* Licensed under the Apache License, Version 2.0
* <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
* or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
* at your option. All files in the project carrying such
* notice may not be copied, modified, or distributed except
* according to those terms.
*/
use crate::errors::NgError;
use crate::types::{Identity, Site, SiteType};
use crate::utils::{generate_keypair, sign, verify};
impl Site {
pub fn create(site_type: SiteType) -> Result<Self, NgError> {
let (site_key, side_id) = generate_keypair();
let (public_key, public_id) = generate_keypair();
let (protected_key, protected_id) = generate_keypair();
let (private_key, private_id) = generate_keypair();
let site_identity;
let public_identity;
let protected_identity;
let private_identity;
match site_type {
SiteType::Individual => {
site_identity = Identity::IndividualSite(side_id);
public_identity = Identity::IndividualPublic(public_id);
protected_identity = Identity::IndividualProtected(protected_id);
private_identity = Identity::IndividualPrivate(private_id);
}
SiteType::Org => {
site_identity = Identity::OrgSite(side_id);
public_identity = Identity::OrgPublic(public_id);
protected_identity = Identity::OrgProtected(protected_id);
private_identity = Identity::OrgPrivate(private_id);
}
}
let public_sig = sign(
site_key,
side_id,
&serde_bare::to_vec(&public_identity).unwrap(),
)?;
let protected_sig = sign(
site_key,
side_id,
&serde_bare::to_vec(&protected_identity).unwrap(),
)?;
let private_sig = sign(
site_key,
side_id,
&serde_bare::to_vec(&private_identity).unwrap(),
)?;
Ok(Self {
site_type,
site_identity,
site_key,
public_identity,
public_key,
public_sig,
protected_identity,
protected_key,
protected_sig,
private_identity,
private_key,
private_sig,
})
}
}

@ -15,13 +15,15 @@
use crate::errors::NgError;
use crate::utils::{
decode_key, dh_pubkey_from_ed_slice, dh_slice_from_ed_slice, ed_privkey_to_pubkey,
decode_key, dh_pubkey_array_from_ed_pubkey_slice, dh_pubkey_from_ed_pubkey_slice,
ed_privkey_to_ed_pubkey, from_ed_privkey_to_dh_privkey, random_key,
};
use core::fmt;
use serde::{Deserialize, Serialize};
use serde_bare::to_vec;
use std::collections::HashMap;
use std::hash::Hash;
use zeroize::{Zeroize, ZeroizeOnDrop};
//
// COMMON TYPES
@ -48,7 +50,7 @@ impl fmt::Display for Digest {
pub type ChaCha20Key = [u8; 32];
/// Symmetric cryptographic key
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[derive(Clone, Zeroize, ZeroizeOnDrop, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum SymKey {
ChaCha20Key(ChaCha20Key),
}
@ -59,6 +61,9 @@ impl SymKey {
SymKey::ChaCha20Key(o) => o,
}
}
pub fn random() -> Self {
SymKey::ChaCha20Key(random_key())
}
}
/// Curve25519 public key Edwards form
@ -67,9 +72,12 @@ pub type Ed25519PubKey = [u8; 32];
/// Curve25519 public key Montgomery form
pub type X25519PubKey = [u8; 32];
/// Curve25519 private key
/// Curve25519 private key Edwards form
pub type Ed25519PrivKey = [u8; 32];
/// Curve25519 private key Montgomery form
pub type X25519PrivKey = [u8; 32];
/// Public key
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum PubKey {
@ -85,17 +93,17 @@ impl PubKey {
}
pub fn to_dh_from_ed(&self) -> PubKey {
match self {
PubKey::Ed25519PubKey(ed) => dh_pubkey_from_ed_slice(ed),
PubKey::Ed25519PubKey(ed) => dh_pubkey_from_ed_pubkey_slice(ed),
_ => panic!(
"cannot convert a Montgomery key to Montgomery. it is already one. check your code"
"there is no need to convert a Montgomery key to Montgomery. it is already one. check your code"
),
}
}
pub fn dh_from_ed_slice(slice: &[u8]) -> PubKey {
dh_pubkey_from_ed_slice(slice)
dh_pubkey_from_ed_pubkey_slice(slice)
}
pub fn to_dh_slice(&self) -> [u8; 32] {
dh_slice_from_ed_slice(self.slice())
dh_pubkey_array_from_ed_pubkey_slice(self.slice())
}
}
@ -118,19 +126,36 @@ impl TryFrom<&str> for PubKey {
}
/// Private key
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[derive(Clone, Zeroize, ZeroizeOnDrop, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum PrivKey {
Ed25519PrivKey(Ed25519PrivKey),
X25519PrivKey(X25519PrivKey),
}
impl PrivKey {
pub fn slice(&self) -> &[u8; 32] {
match self {
PrivKey::Ed25519PrivKey(o) => o,
PrivKey::Ed25519PrivKey(o) | PrivKey::X25519PrivKey(o) => o,
}
}
pub fn to_pub(&self) -> PubKey {
ed_privkey_to_pubkey(self)
match self {
PrivKey::Ed25519PrivKey(_) => ed_privkey_to_ed_pubkey(self),
_ => panic!("X25519PrivKey to pub not implemented"),
}
}
#[deprecated(note = "**Don't use dummy method**")]
pub fn dummy() -> PrivKey {
PrivKey::Ed25519PrivKey([0u8; 32])
}
pub fn to_dh(&self) -> PrivKey {
from_ed_privkey_to_dh_privkey(self)
}
pub fn random_ed() -> Self {
PrivKey::Ed25519PrivKey(random_key())
}
}
@ -220,57 +245,6 @@ pub enum PermissionType {
CHANGE_ACK_CONFIG,
}
/// List of Identity types
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum Identity {
OrgSite(PubKey),
IndividualSite(PubKey),
OrgPublic(PubKey),
OrgProtected(PubKey),
OrgPrivate(PubKey),
IndividualPublic(PubKey),
IndividualProtected(PubKey),
IndividualPrivate(PubKey),
Group(RepoId),
Dialog(RepoId),
Document(RepoId),
DialogOverlay(Digest),
}
/// Site type
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum SiteType {
Org,
Individual, // formerly Personal
}
/// Site
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct Site {
pub site_type: SiteType,
// Identity::OrgSite or Identity::IndividualSite
pub site_identity: Identity,
pub site_key: PrivKey,
// Identity::OrgPublic or Identity::IndividualPublic
pub public_identity: Identity,
pub public_key: PrivKey,
// signature of public_identity with site_key
pub public_sig: Sig,
// Identity::OrgProtected or Identity::IndividualProtected
pub protected_identity: Identity,
pub protected_key: PrivKey,
// signature of protected_identity with site_key
pub protected_sig: Sig,
// Identity::OrgPrivate or Identity::IndividualPrivate
pub private_identity: Identity,
pub private_key: PrivKey,
// signature of private_identity with site_key
pub private_sig: Sig,
}
/// RepoHash:
/// BLAKE3 hash of the RepoId
pub type RepoHash = Digest;
@ -289,7 +263,7 @@ pub type RepoId = PubKey;
pub type BlockId = Digest;
/// Block reference
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct BlockRef {
/// Object ID
pub id: BlockId,
@ -298,6 +272,16 @@ pub struct BlockRef {
pub key: SymKey,
}
impl BlockRef {
#[deprecated(note = "**Don't use dummy method**")]
pub fn dummy() -> Self {
BlockRef {
id: Digest::Blake3Digest32([0u8; 32]),
key: SymKey::ChaCha20Key([0u8; 32]),
}
}
}
/// Object ID
pub type ObjectId = BlockId;
@ -405,13 +389,13 @@ pub enum Repository {
}
/// Add a branch to the repository
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum AddBranch {
V0(ObjectRef),
}
/// Remove a branch from the repository
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum RemoveBranch {
V0(ObjectRef),
}

@ -17,8 +17,37 @@ use curve25519_dalek::edwards::{CompressedEdwardsY, EdwardsPoint};
use ed25519_dalek::*;
use futures::channel::mpsc;
use rand::rngs::OsRng;
use rand::RngCore;
use web_time::{SystemTime, UNIX_EPOCH};
use zeroize::Zeroize;
pub fn ed_keypair_from_priv_bytes(secret_key: [u8; 32]) -> (PrivKey, PubKey) {
let sk = SecretKey::from_bytes(&secret_key).unwrap();
let pk: PublicKey = (&sk).into();
let pub_key = PubKey::Ed25519PubKey(pk.to_bytes());
let priv_key = PrivKey::Ed25519PrivKey(secret_key);
(priv_key, pub_key)
}
pub fn from_ed_privkey_to_dh_privkey(private: &PrivKey) -> PrivKey {
//SecretKey and ExpandedSecretKey are Zeroized at drop
if let PrivKey::Ed25519PrivKey(slice) = private {
let ed25519_priv = SecretKey::from_bytes(slice).unwrap();
let exp: ExpandedSecretKey = (&ed25519_priv).into();
let mut exp_bytes = exp.to_bytes();
exp_bytes[32..].zeroize();
let mut bits = *slice_as_array!(&exp_bytes[0..32], [u8; 32]).unwrap();
bits[0] &= 248;
bits[31] &= 127;
bits[31] |= 64;
// PrivKey takes ownership and will zeroize on drop
PrivKey::X25519PrivKey(bits)
} else {
panic!("this is not an Edmonds privkey")
}
}
/// don't forget to zeroize the string later on
pub fn decode_key(key_string: &str) -> Result<[u8; 32], ()> {
let vec = base64_url::decode(key_string).map_err(|_| log_err!("key has invalid content"))?;
Ok(*slice_as_array!(&vec, [u8; 32])
@ -26,62 +55,75 @@ pub fn decode_key(key_string: &str) -> Result<[u8; 32], ()> {
.map_err(|_| log_err!("key has invalid content array"))?)
}
pub fn ed_privkey_to_pubkey(privkey: &PrivKey) -> PubKey {
pub fn ed_privkey_to_ed_pubkey(privkey: &PrivKey) -> PubKey {
// SecretKey is zeroized on drop (3 lines below) se we are safe
let sk = SecretKey::from_bytes(privkey.slice()).unwrap();
let pk: PublicKey = (&sk).into();
PubKey::Ed25519PubKey(pk.to_bytes())
}
pub fn generate_null_keypair() -> (PrivKey, PubKey) {
/// use with caution. it should be embedded in a zeroize struct in order to be safe
pub fn random_key() -> [u8; 32] {
let mut sk = [0u8; 32];
let mut csprng = OsRng {};
csprng.fill_bytes(&mut sk);
sk
}
pub fn generate_null_ed_keypair() -> (PrivKey, PubKey) {
// we don't use zeroize because... well, it is already a zeroized privkey ;)
let master_key: [u8; 32] = [0; 32];
let sk = SecretKey::from_bytes(&master_key).unwrap();
let pk: PublicKey = (&sk).into();
let keypair = Keypair {
public: pk,
secret: sk,
};
// log_debug!(
// "private key: ({}) {:?}",
// keypair.secret.as_bytes().len(),
// keypair.secret.as_bytes()
// );
// log_debug!(
// "public key: ({}) {:?}",
// keypair.public.as_bytes().len(),
// keypair.public.as_bytes()
// );
let ed_priv_key = keypair.secret.to_bytes();
let ed_pub_key = keypair.public.to_bytes();
let priv_key = PrivKey::Ed25519PrivKey(ed_priv_key);
let pub_key = PubKey::Ed25519PubKey(ed_pub_key);
let priv_key = PrivKey::Ed25519PrivKey(sk.to_bytes());
let pub_key = PubKey::Ed25519PubKey(pk.to_bytes());
(priv_key, pub_key)
}
pub fn dh_pubkey_from_ed_slice(public: &[u8]) -> PubKey {
PubKey::X25519PubKey(dh_slice_from_ed_slice(public))
pub fn dh_pubkey_from_ed_pubkey_slice(public: &[u8]) -> PubKey {
PubKey::X25519PubKey(dh_pubkey_array_from_ed_pubkey_slice(public))
}
pub fn dh_slice_from_ed_slice(public: &[u8]) -> X25519PubKey {
pub fn dh_pubkey_array_from_ed_pubkey_slice(public: &[u8]) -> X25519PubKey {
// the zeroize are not mandatory, because it is a PubKey.
let mut bits: [u8; 32] = [0u8; 32];
bits.copy_from_slice(public);
let compressed = CompressedEdwardsY(bits);
let ed_point: EdwardsPoint = compressed.decompress().unwrap();
let mon_point = ed_point.to_montgomery();
mon_point.to_bytes()
let mut compressed = CompressedEdwardsY(bits);
let mut ed_point: EdwardsPoint = compressed.decompress().unwrap();
compressed.zeroize();
let mut mon_point = ed_point.to_montgomery();
ed_point.zeroize();
let array = mon_point.to_bytes();
mon_point.zeroize();
array
}
pub fn pubkey_privkey_to_keypair(pubkey: &PubKey, privkey: &PrivKey) -> Keypair {
match (privkey, pubkey) {
(PrivKey::Ed25519PrivKey(sk), PubKey::Ed25519PubKey(pk)) => {
let secret = SecretKey::from_bytes(sk).unwrap();
let public = PublicKey::from_bytes(pk).unwrap();
Keypair { secret, public }
}
(_, _) => panic!("cannot sign with Montgomery keys"),
}
}
pub fn keypair_from_ed(secret: SecretKey, public: PublicKey) -> (PrivKey, PubKey) {
let ed_priv_key = secret.to_bytes();
let ed_pub_key = public.to_bytes();
let pub_key = PubKey::Ed25519PubKey(ed_pub_key);
let priv_key = PrivKey::Ed25519PrivKey(ed_priv_key);
(priv_key, pub_key)
}
pub fn sign(
author_privkey: PrivKey,
author_pubkey: PubKey,
author_privkey: &PrivKey,
author_pubkey: &PubKey,
content: &Vec<u8>,
) -> Result<Sig, NgError> {
let kp = match (author_privkey, author_pubkey) {
(PrivKey::Ed25519PrivKey(sk), PubKey::Ed25519PubKey(pk)) => [sk, pk].concat(),
(_, _) => panic!("cannot sign with Montgomery keys"),
};
let keypair = Keypair::from_bytes(kp.as_slice())?;
let keypair = pubkey_privkey_to_keypair(author_pubkey, author_privkey);
let sig_bytes = keypair.sign(content.as_slice()).to_bytes();
let mut it = sig_bytes.chunks_exact(32);
let mut ss: Ed25519Sig = [[0; 32], [0; 32]];
@ -106,16 +148,6 @@ pub fn verify(content: &Vec<u8>, sig: Sig, pub_key: PubKey) -> Result<(), NgErro
pub fn generate_keypair() -> (PrivKey, PubKey) {
let mut csprng = OsRng {};
let keypair: Keypair = Keypair::generate(&mut csprng);
// log_debug!(
// "private key: ({}) {:?}",
// keypair.secret.as_bytes().len(),
// keypair.secret.as_bytes()
// );
// log_debug!(
// "public key: ({}) {:?}",
// keypair.public.as_bytes().len(),
// keypair.public.as_bytes()
// );
let ed_priv_key = keypair.secret.to_bytes();
let ed_pub_key = keypair.public.to_bytes();
let priv_key = PrivKey::Ed25519PrivKey(ed_priv_key);

Loading…
Cancel
Save