converting peerIds and privekeys to montgomery for DH purposes

Niko PLP 1 year ago
parent 8efdac469d
commit d98d8d6027
  1. 3
      Cargo.lock
  2. 17
      ng-sdk-js/src/lib.rs
  3. 5
      ngcli/src/main.rs
  4. 1
      ngd/Cargo.toml
  5. 45
      ngd/src/main.rs
  6. 10
      p2p-broker/src/server_ws.rs
  7. 12
      p2p-client-ws/src/remote_ws.rs
  8. 2
      p2p-client-ws/src/remote_ws_wasm.rs
  9. 101
      p2p-net/src/broker.rs
  10. 38
      p2p-net/src/connection.rs
  11. 4
      p2p-net/src/errors.rs
  12. 66
      p2p-net/src/utils.rs
  13. 2
      p2p-repo/Cargo.toml
  14. 2
      p2p-repo/src/commit.rs
  15. 12
      p2p-repo/src/errors.rs
  16. 3
      p2p-repo/src/lib.rs
  17. 9
      p2p-repo/src/object.rs
  18. 54
      p2p-repo/src/types.rs
  19. 34
      p2p-repo/src/utils.rs

3
Cargo.lock generated

@ -2792,7 +2792,6 @@ dependencies = [
"serde_bare",
"serde_bytes",
"serde_json",
"slice_as_array",
]
[[package]]
@ -3057,6 +3056,7 @@ dependencies = [
"base64-url",
"blake3",
"chacha20",
"curve25519-dalek 3.2.0",
"debug_print",
"ed25519-dalek",
"fastbloom-rs",
@ -3068,6 +3068,7 @@ dependencies = [
"serde",
"serde_bare",
"serde_bytes",
"slice_as_array",
"wasm-bindgen",
"web-time",
]

@ -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::{spawn_and_log_error, Receiver, ResultSend, Sender};
use p2p_net::utils::{gen_ed_keys, spawn_and_log_error, Receiver, ResultSend, Sender};
use p2p_repo::log::*;
use p2p_repo::types::*;
use p2p_repo::utils::generate_keypair;
@ -207,13 +207,14 @@ pub async fn start() {
// getrandom::getrandom(&mut random_buf).unwrap();
async fn inner_task() -> ResultSend<()> {
let server_key = PubKey::Ed25519PubKey([
95, 155, 249, 202, 41, 105, 71, 51, 206, 126, 9, 84, 132, 92, 60, 7, 74, 179, 46, 21,
21, 242, 171, 27, 249, 79, 76, 176, 168, 43, 83, 2,
]);
let server_key: PubKey = "KWdmwr4_oO62IFGfKzuyotQOixqXGNWv59CRAGvPTjM".try_into()?;
log_debug!("server_key:{}", server_key);
let keys = p2p_net::utils::gen_keys();
let pub_key = PubKey::Ed25519PubKey(keys.1);
//let keys = p2p_net::utils::gen_dh_keys();
//let pub_key = PubKey::Ed25519PubKey(keys.1);
let keys = gen_ed_keys();
let x_from_ed = keys.1.to_dh_from_ed();
log_info!("Pub from X {}", x_from_ed);
let (client_priv_key, client_pub_key) = generate_keypair();
let (user_priv_key, user_pub_key) = generate_keypair();
@ -228,7 +229,7 @@ pub async fn start() {
IP::try_from(&IpAddr::from_str("127.0.0.1").unwrap()).unwrap(),
None,
keys.0,
pub_key,
keys.1,
server_key,
StartConfig::Client(ClientConfig {
user: user_pub_key,

@ -599,13 +599,14 @@ mod test {
use async_std::task;
use p2p_broker::server_ws::*;
use p2p_net::utils::{gen_keys, Sensitive, U8Array};
use p2p_net::utils::{gen_dh_keys, Sensitive, U8Array};
use p2p_net::WS_PORT;
use p2p_repo::log::*;
use p2p_repo::types::PubKey;
#[async_std::test]
pub async fn test_remote_cnx() -> Result<(), Box<dyn std::error::Error>> {
let keys = gen_keys();
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);

@ -19,7 +19,6 @@ log = "0.4"
env_logger = "0.10"
clap = { version = "4.3.4", features = ["derive","env","string"] }
base64-url = "2.0.0"
slice_as_array = "1.1.0"
serde_json = "1.0"
regex = "1.8.4"
lazy_static = "1.4.0"

@ -6,8 +6,6 @@
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
#[macro_use]
extern crate slice_as_array;
pub mod types;
@ -26,14 +24,14 @@ 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_keys, is_ipv4_global, is_ipv4_private, is_ipv6_global, is_ipv6_private, keys_from_bytes,
Dual25519Keys, Sensitive, U8Array,
gen_dh_keys, is_ipv4_global, is_ipv4_private, is_ipv6_global, is_ipv6_private, keypair_from_ed,
keys_from_bytes, Dual25519Keys, Sensitive, U8Array,
};
use p2p_net::{WS_PORT, WS_PORT_REVERSE_PROXY};
use p2p_repo::log::*;
use p2p_repo::{
types::{PrivKey, PubKey},
utils::{generate_keypair, keypair_from_ed, sign, verify},
utils::{decode_key, generate_keypair, sign, verify},
};
use serde_json::{from_str, to_string_pretty};
use std::fs::{read_to_string, write};
@ -46,13 +44,6 @@ use addr::psl::List;
use lazy_static::lazy_static;
use regex::Regex;
fn decode_key(key_string: &String) -> 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])
.ok_or(())
.map_err(|_| log_err!("key has invalid content array"))?)
}
//For windows: {846EE342-7039-11DE-9D20-806E6F6E6963}
//For the other OSes: en0 lo ...
#[cfg(not(target_os = "windows"))]
@ -384,7 +375,7 @@ async fn main_inner() -> Result<(), ()> {
log_err!("provided --key option will not be used as a key file is already present");
gen_broker_keys(Some(key_from_file.unwrap()))
} else {
let res = decode_key(key_string)
let res = decode_key(key_string.as_str())
.map_err(|_| log_err!("provided key is invalid. cannot start"))?;
if args.save_key {
@ -853,13 +844,8 @@ async fn main_inner() -> Result<(), ()> {
);
return Err(());
}
let vec = base64_url::decode(parts[1])
let pub_key_array = decode_key(parts[1])
.map_err(|_| log_err!("The PEERID provided in the --forward option is invalid"))?;
let pub_key_array = *slice_as_array!(vec.as_slice(), [u8; 32])
.ok_or(())
.map_err(|_| {
log_err!("PEERID provided in the --forward option, has invalid array")
})?;
let peer_id = PubKey::Ed25519PubKey(pub_key_array);
let server_type = if parts[0].len() > 0 {
@ -955,15 +941,22 @@ async fn main_inner() -> Result<(), ()> {
let (privkey, pubkey) = keys_from_bytes(keys[1]);
let priv_key_array = *slice_as_array!(privkey.as_slice(), [u8; 32])
.ok_or(())
.map_err(|_| log_err!("Private key of peer has invalid array"))?;
let priv_key = PrivKey::Ed25519PrivKey(priv_key_array);
let priv_key_ser = serde_bare::to_vec(&priv_key).unwrap();
let prix_key_encoded = base64_url::encode(&priv_key_ser);
//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"))?;
log_info!("PeerId of node: {}", pubkey);
debug_println!("Private key of peer: {}", prix_key_encoded);
//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);
match config.unwrap() {
DaemonConfig::V0(v0) => {

@ -517,7 +517,15 @@ pub async fn accept(tcp: TcpStream, peer_priv_key: Sensitive<[u8; 32]>) {
log_debug!("websocket accepted");
let cws = ConnectionWebSocket {};
let base = cws.accept(peer_priv_key, ws.unwrap()).await.unwrap();
let base = cws
.accept(
remote_bind_address,
local_bind_address,
peer_priv_key,
ws.unwrap(),
)
.await
.unwrap();
let res = BROKER
.write()

@ -60,7 +60,7 @@ impl IConnect for ConnectionWebSocket {
Err(NetError::ConnectionError)
}
Ok((mut websocket, _)) => {
cnx.start_read_loop(peer_privk, Some(remote_peer));
cnx.start_read_loop(None, peer_privk, Some(remote_peer));
let s = cnx.take_sender();
let r = cnx.take_receiver();
let mut shutdown = cnx.set_shutdown();
@ -90,12 +90,18 @@ impl IAccept for ConnectionWebSocket {
type Socket = WebSocketStream<TcpStream>;
async fn accept(
&self,
remote_bind_address: BindAddress,
local_bind_address: BindAddress,
peer_privk: Sensitive<[u8; 32]>,
socket: Self::Socket,
) -> Result<ConnectionBase, NetError> {
let mut cnx = ConnectionBase::new(ConnectionDir::Server, TransportProtocol::WS);
cnx.start_read_loop(peer_privk, None);
cnx.start_read_loop(
Some((local_bind_address, remote_bind_address)),
peer_privk,
None,
);
let s = cnx.take_sender();
let r = cnx.take_receiver();
let mut shutdown = cnx.set_shutdown();
@ -272,7 +278,7 @@ mod test {
21, 242, 171, 27, 249, 79, 76, 176, 168, 43, 83, 2,
]);
let keys = p2p_net::utils::gen_keys();
let keys = p2p_net::utils::gen_dh_keys();
let pub_key = PubKey::Ed25519PubKey(keys.1);
let (client_priv_key, client_pub_key) = generate_keypair();

@ -51,7 +51,7 @@ impl IConnect for ConnectionWebSocket {
NetError::ConnectionError
})?;
cnx.start_read_loop(peer_privk, Some(remote_peer));
cnx.start_read_loop(None, peer_privk, Some(remote_peer));
let mut shutdown = cnx.set_shutdown();
spawn_and_log_error(ws_loop(

@ -63,6 +63,7 @@ pub static BROKER: Lazy<Arc<RwLock<Broker>>> = Lazy::new(|| Arc::new(RwLock::new
pub struct Broker {
direct_connections: HashMap<IP, DirectConnection>,
peers: HashMap<DirectPeerId, BrokerPeerInfo>,
/// (local,remote) -> ConnectionBase
incoming_anonymous_connections: HashMap<(BindAddress, BindAddress), ConnectionBase>,
#[cfg(not(target_arch = "wasm32"))]
listeners: HashMap<String, ListenerInfo>,
@ -338,7 +339,7 @@ impl Broker {
pub async fn accept(
&mut self,
mut connection: ConnectionBase,
connection: ConnectionBase,
remote_bind_address: BindAddress,
local_bind_address: BindAddress,
) -> Result<(), NetError> {
@ -346,43 +347,67 @@ impl Broker {
return Err(NetError::Closing);
}
//self.incoming_anonymous_connections
// let join = connection.take_shutdown();
// let connected = if core.is_some() {
// let dc = DirectConnection {
// ip,
// interface: core.clone().unwrap(),
// remote_peer_id,
// tp: connection.transport_protocol(),
// cnx: connection,
// };
// self.direct_connections.insert(ip, dc);
// PeerConnection::Core(ip)
// } else {
// PeerConnection::Client(connection)
// };
// let bpi = BrokerPeerInfo {
// lastPeerAdvert: None,
// connected,
// };
// self.peers.insert(remote_peer_id, bpi);
// async fn watch_close(
// mut join: Receiver<NetError>,
// remote_peer_id: DirectPeerId,
// ) -> ResultSend<()> {
// async move {
// let res = join.next().await;
// log_info!("SOCKET IS CLOSED {:?} {:?}", res, &remote_peer_id);
// log_info!("REMOVED");
// BROKER.write().await.remove(&remote_peer_id);
// }
// .await;
// Ok(())
// }
// spawn_and_log_error(watch_close(join, remote_peer_id));
if self
.incoming_anonymous_connections
.insert((local_bind_address, remote_bind_address), connection)
.is_some()
{
log_err!(
"internal error. duplicate connection {:?} {:?}",
local_bind_address,
remote_bind_address
);
}
Ok(())
}
pub async fn attach_peer_id(
&mut self,
remote_bind_address: BindAddress,
local_bind_address: BindAddress,
remote_peer_id: PubKey,
core: Option<String>,
) -> Result<(), NetError> {
log_debug!("ATTACH PEER_ID {}", remote_peer_id);
let mut connection = self
.incoming_anonymous_connections
.remove(&(local_bind_address, remote_bind_address))
.ok_or(NetError::InternalError)?;
let join = connection.take_shutdown();
let ip = remote_bind_address.ip;
let connected = if core.is_some() {
let dc = DirectConnection {
ip,
interface: core.clone().unwrap(),
remote_peer_id,
tp: connection.transport_protocol(),
cnx: connection,
};
self.direct_connections.insert(ip, dc);
PeerConnection::Core(ip)
} else {
PeerConnection::Client(connection)
};
let bpi = BrokerPeerInfo {
lastPeerAdvert: None,
connected,
};
self.peers.insert(remote_peer_id, bpi);
async fn watch_close(
mut join: Receiver<NetError>,
remote_peer_id: DirectPeerId,
) -> ResultSend<()> {
async move {
let res = join.next().await;
log_info!("SOCKET IS CLOSED {:?} {:?}", res, &remote_peer_id);
log_info!("REMOVED");
BROKER.write().await.remove(&remote_peer_id);
}
.await;
Ok(())
}
spawn_and_log_error(watch_close(join, remote_peer_id));
Ok(())
}

@ -17,6 +17,7 @@ use std::sync::Arc;
use crate::actor::{Actor, SoS};
use crate::actors::*;
use crate::broker::BROKER;
use crate::errors::NetError;
use crate::errors::ProtocolError;
use crate::types::*;
@ -63,6 +64,8 @@ pub trait IAccept: Send + Sync {
type Socket;
async fn accept(
&self,
remote_bind_address: BindAddress,
local_bind_address: BindAddress,
peer_privk: Sensitive<[u8; 32]>,
socket: Self::Socket,
) -> Result<ConnectionBase, NetError>;
@ -100,6 +103,8 @@ pub struct NoiseFSM {
dir: ConnectionDir,
sender: Sender<ConnectionCommand>,
bind_addresses: Option<(BindAddress, BindAddress)>,
actors: Arc<Mutex<HashMap<i64, Sender<ConnectionCommand>>>>,
noise_handshake_state: Option<HandshakeState<X25519, ChaCha20Poly1305, Blake2b>>,
@ -149,6 +154,7 @@ pub enum StartConfig {
impl NoiseFSM {
pub fn new(
bind_addresses: Option<(BindAddress, BindAddress)>,
tp: TransportProtocol,
dir: ConnectionDir,
actors: Arc<Mutex<HashMap<i64, Sender<ConnectionCommand>>>>,
@ -163,6 +169,7 @@ impl NoiseFSM {
FSMstate::Noise0
},
dir,
bind_addresses,
actors,
sender,
noise_handshake_state: None,
@ -276,9 +283,9 @@ impl NoiseFSM {
noise_xk(),
true,
&[],
Some(self.from.take().unwrap()),
Some(from_ed_priv_to_dh_priv(self.from.take().unwrap())),
None,
Some(*self.to.unwrap().slice()),
Some(*self.to.unwrap().to_dh_from_ed().slice()),
None,
);
@ -304,7 +311,7 @@ impl NoiseFSM {
noise_xk(),
false,
&[],
Some(self.from.take().unwrap()),
Some(from_ed_priv_to_dh_priv(self.from.take().unwrap())),
None,
None,
None,
@ -400,8 +407,21 @@ impl NoiseFSM {
if !handshake.completed() {
return Err(ProtocolError::NoiseHandshakeFailed);
}
self.to = Some(PubKey::Ed25519PubKey(handshake.get_rs().unwrap()));
let peer_id = PubKey::Ed25519PubKey(handshake.get_rs().unwrap());
self.to = Some(peer_id);
let (local_bind_address, remote_bind_address) =
self.bind_addresses.ok_or(ProtocolError::BrokerError)?;
BROKER
.write()
.await
.attach_peer_id(
remote_bind_address,
local_bind_address,
peer_id,
None,
)
.await
.map_err(|_| ProtocolError::BrokerError)?;
let ciphers = handshake.get_ciphers();
self.noise_cipher_state_enc = Some(ciphers.1);
@ -739,7 +759,12 @@ impl ConnectionBase {
}
}
pub fn start_read_loop(&mut self, from: Sensitive<[u8; 32]>, to: Option<PubKey>) {
pub fn start_read_loop(
&mut self,
bind_addresses: Option<(BindAddress, BindAddress)>,
from: Sensitive<[u8; 32]>,
to: Option<PubKey>,
) {
let (sender_tx, sender_rx) = mpsc::unbounded();
let (receiver_tx, receiver_rx) = mpsc::unbounded();
self.sender = Some(sender_rx);
@ -748,6 +773,7 @@ impl ConnectionBase {
self.receiver_tx = Some(receiver_tx);
let fsm = Arc::new(Mutex::new(NoiseFSM::new(
bind_addresses,
self.tp,
self.dir.clone(),
Arc::clone(&self.actors),

@ -29,7 +29,8 @@ pub enum NetError {
ConnectionError,
SerializationError,
ProtocolError,
ConnectionDenied,
AccessDenied,
InternalError,
Closing,
} //MAX 50 NetErrors
@ -104,6 +105,7 @@ impl From<p2p_repo::errors::NgError> for ProtocolError {
match e {
p2p_repo::errors::NgError::InvalidSignature => ProtocolError::InvalidSignature,
p2p_repo::errors::NgError::SerializationError => ProtocolError::SerializationError,
_ => ProtocolError::OtherError,
}
}
}

@ -51,6 +51,15 @@ 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 keys_from_bytes(secret_key: [u8; 32]) -> (Sensitive<[u8; 32]>, PubKey) {
let sk = SecretKey::from_bytes(&secret_key).unwrap();
let pk: PublicKey = (&sk).into();
@ -61,12 +70,23 @@ pub fn keys_from_bytes(secret_key: [u8; 32]) -> (Sensitive<[u8; 32]>, PubKey) {
(priv_key, pub_key)
}
pub fn gen_keys() -> (Sensitive<[u8; 32]>, [u8; 32]) {
pub fn gen_dh_keys() -> (Sensitive<[u8; 32]>, [u8; 32]) {
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()))
}
pub struct Dual25519Keys {
pub x25519_priv: Sensitive<[u8; 32]>,
pub x25519_public: [u8; 32],
@ -74,22 +94,56 @@ 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 ed25519_priv = SecretKey::from_bytes(&x25519_priv.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;
bits[31] |= 64;
let x25519_public = noise_rust_crypto::X25519::pubkey(&bits);
Self {
x25519_priv: bits,
x25519_public,
ed25519_priv,
ed25519_pub,
}
}
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();
x25519_priv[0] &= 248;
x25519_priv[31] &= 127;
x25519_priv[31] |= 64;
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(&x25519_priv);
let x25519_public = noise_rust_crypto::X25519::pubkey(&bits);
Self {
x25519_priv,
x25519_priv: bits,
x25519_public,
ed25519_priv,
ed25519_pub,

@ -25,6 +25,8 @@ futures = "0.3.24"
base64-url = "2.0.0"
web-time = "0.2.0"
wasm-bindgen = "0.2"
slice_as_array = "1.1.0"
curve25519-dalek = "3.2.0"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
debug_print = "1.0.0"

@ -82,6 +82,7 @@ impl CommitV0 {
// sign commit
let kp = match (author_privkey, author_pubkey) {
(PrivKey::Ed25519PrivKey(sk), PubKey::Ed25519PubKey(pk)) => [sk, pk].concat(),
(_, _) => panic!("cannot sign with Montgomery key"),
};
let keypair = Keypair::from_bytes(kp.as_slice())?;
let sig_bytes = keypair.sign(content_ser.as_slice()).to_bytes();
@ -246,6 +247,7 @@ impl Commit {
let content_ser = serde_bare::to_vec(&c.content).unwrap();
let pubkey = match c.content.author {
PubKey::Ed25519PubKey(pk) => pk,
_ => panic!("author cannot have a Montgomery key"),
};
let pk = PublicKey::from_bytes(&pubkey)?;
let sig_bytes = match c.sig {

@ -11,11 +11,23 @@
//! Errors
use core::fmt;
use std::error::Error;
#[derive(Debug, Eq, PartialEq, Clone)]
#[repr(u16)]
pub enum NgError {
InvalidSignature,
SerializationError,
InvalidKey,
}
impl Error for NgError {}
impl fmt::Display for NgError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl From<serde_bare::error::Error> for NgError {

@ -20,6 +20,9 @@ pub mod kcv_store;
pub mod site;
#[macro_use]
extern crate slice_as_array;
pub mod log {
#[cfg(not(target_arch = "wasm32"))]

@ -80,6 +80,7 @@ impl Object {
(PubKey::Ed25519PubKey(pubkey), SymKey::ChaCha20Key(secret)) => {
[pubkey, secret].concat()
}
(_, _) => panic!("cannot sign with Montgomery key"),
};
blake3::derive_key("NextGraph Data BLAKE3 key", key_material.as_slice())
}
@ -575,6 +576,14 @@ mod test {
/// Maximum data that can fit in object.content
const MAX_DATA_PAYLOAD_SIZE: usize = 2097112;
#[test]
pub fn test_pubkey_from_str() {
let pubkey = PubKey::Ed25519PubKey([1u8; 32]);
let str = pubkey.to_string();
let server_key: PubKey = str.as_str().try_into().unwrap();
assert_eq!(server_key, pubkey);
}
/// Test JPEG file
#[test]
pub fn test_jpg() {

@ -13,6 +13,8 @@
//!
//! Corresponds to the BARE schema
use crate::errors::NgError;
use crate::utils::{decode_key, dh_pubkey_from_ed_slice};
use core::fmt;
use serde::{Deserialize, Serialize};
use serde_bare::to_vec;
@ -61,7 +63,7 @@ impl SymKey {
pub type Ed25519PubKey = [u8; 32];
/// Curve25519 public key Montgomery form
pub type Mo25519PubKey = [u8; 32];
pub type X25519PubKey = [u8; 32];
/// Curve25519 private key
pub type Ed25519PrivKey = [u8; 32];
@ -70,24 +72,46 @@ pub type Ed25519PrivKey = [u8; 32];
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum PubKey {
Ed25519PubKey(Ed25519PubKey),
X25519PubKey(X25519PubKey),
}
impl PubKey {
pub fn slice(&self) -> &[u8; 32] {
match self {
PubKey::Ed25519PubKey(o) => o,
PubKey::Ed25519PubKey(o) | PubKey::X25519PubKey(o) => o,
}
}
pub fn to_dh_from_ed(&self) -> PubKey {
match self {
PubKey::Ed25519PubKey(ed) => dh_pubkey_from_ed_slice(ed),
_ => panic!(
"cannot 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)
}
}
impl fmt::Display for PubKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PubKey::Ed25519PubKey(d) => write!(f, "{}", base64_url::encode(d)),
PubKey::Ed25519PubKey(d) | PubKey::X25519PubKey(d) => {
write!(f, "{}", base64_url::encode(d))
}
}
}
}
impl TryFrom<&str> for PubKey {
type Error = NgError;
fn try_from(str: &str) -> Result<Self, NgError> {
let key = decode_key(str).map_err(|_| NgError::InvalidKey)?;
Ok(PubKey::Ed25519PubKey(key))
}
}
/// Private key
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum PrivKey {
@ -102,6 +126,30 @@ impl PrivKey {
}
}
impl From<[u8; 32]> for PrivKey {
fn from(buf: [u8; 32]) -> Self {
let priv_key = PrivKey::Ed25519PrivKey(buf);
priv_key
}
}
impl TryFrom<&[u8]> for PrivKey {
type Error = NgError;
fn try_from(buf: &[u8]) -> Result<Self, NgError> {
let priv_key_array = *slice_as_array!(buf, [u8; 32]).ok_or(NgError::InvalidKey)?;
let priv_key = PrivKey::Ed25519PrivKey(priv_key_array);
Ok(priv_key)
}
}
impl fmt::Display for PrivKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let priv_key_ser = serde_bare::to_vec(self).unwrap();
let prix_key_encoded = base64_url::encode(&priv_key_ser);
write!(f, "{}", prix_key_encoded)
}
}
/// Ed25519 signature
pub type Ed25519Sig = [[u8; 32]; 2];

@ -10,13 +10,22 @@
// according to those terms.
use crate::errors::*;
use crate::log::*;
use crate::types::*;
use curve25519_dalek::edwards::{CompressedEdwardsY, EdwardsPoint};
use ed25519_dalek::*;
use futures::channel::mpsc;
use rand::rngs::OsRng;
use web_time::{SystemTime, UNIX_EPOCH};
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])
.ok_or(())
.map_err(|_| log_err!("key has invalid content array"))?)
}
pub fn generate_null_keypair() -> (PrivKey, PubKey) {
let master_key: [u8; 32] = [0; 32];
let sk = SecretKey::from_bytes(&master_key).unwrap();
@ -44,22 +53,13 @@ pub fn generate_null_keypair() -> (PrivKey, PubKey) {
(priv_key, pub_key)
}
pub fn keypair_from_ed(secret: SecretKey, public: PublicKey) -> (PrivKey, PubKey) {
// 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 = 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);
(priv_key, pub_key)
pub fn dh_pubkey_from_ed_slice(public: &[u8]) -> 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();
PubKey::X25519PubKey(mon_point.to_bytes())
}
pub fn sign(
@ -69,6 +69,7 @@ pub fn sign(
) -> 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 sig_bytes = keypair.sign(content.as_slice()).to_bytes();
@ -82,6 +83,7 @@ pub fn sign(
pub fn verify(content: &Vec<u8>, sig: Sig, pub_key: PubKey) -> Result<(), NgError> {
let pubkey = match pub_key {
PubKey::Ed25519PubKey(pk) => pk,
_ => panic!("cannot verify with Montgomery keys"),
};
let pk = PublicKey::from_bytes(&pubkey)?;
let sig_bytes = match sig {

Loading…
Cancel
Save