diff --git a/veilid-cli/src/client_api_connection.rs b/veilid-cli/src/client_api_connection.rs index 5999ab22..84687051 100644 --- a/veilid-cli/src/client_api_connection.rs +++ b/veilid-cli/src/client_api_connection.rs @@ -96,6 +96,9 @@ impl veilid_client::Server for VeilidClientImpl { self.comproc.update_route(route); } VeilidUpdate::Shutdown => self.comproc.update_shutdown(), + VeilidUpdate::ValueChange(value_change) => { + self.comproc.update_value_change(value_change); + } } Promise::ok(()) diff --git a/veilid-cli/src/command_processor.rs b/veilid-cli/src/command_processor.rs index 8027eb92..2e32f584 100644 --- a/veilid-cli/src/command_processor.rs +++ b/veilid-cli/src/command_processor.rs @@ -424,6 +424,10 @@ reply - reply to an AppCall not handled directly by the server self.inner().ui.add_node_event(out); } } + pub fn update_value_change(&mut self, value_change: veilid_core::VeilidValueChange) { + let out = format!("Value change: {:?}", value_change); + self.inner().ui.add_node_event(out); + } pub fn update_log(&mut self, log: veilid_core::VeilidLog) { self.inner().ui.add_node_event(format!( diff --git a/veilid-cli/src/peers_table_view.rs b/veilid-cli/src/peers_table_view.rs index 870eaea5..82593c71 100644 --- a/veilid-cli/src/peers_table_view.rs +++ b/veilid-cli/src/peers_table_view.rs @@ -50,7 +50,11 @@ fn format_bps(bps: ByteCount) -> String { impl TableViewItem for PeerTableData { fn to_column(&self, column: PeerTableColumn) -> String { match column { - PeerTableColumn::NodeId => self.node_id.encode(), + PeerTableColumn::NodeId => self + .node_ids + .best() + .map(|n| n.value.encode()) + .unwrap_or_else(|| "???".to_owned()), PeerTableColumn::Address => format!( "{:?}:{}", self.peer_address.protocol_type(), @@ -74,7 +78,21 @@ impl TableViewItem for PeerTableData { Self: Sized, { match column { - PeerTableColumn::NodeId => self.node_id.cmp(&other.node_id), + PeerTableColumn::NodeId => { + let n1 = self + .node_ids + .best() + .map(|n| n.value.encode()) + .unwrap_or_else(|| "???".to_owned()); + + let n2 = other + .node_ids + .best() + .map(|n| n.value.encode()) + .unwrap_or_else(|| "???".to_owned()); + + n1.cmp(&n2) + } PeerTableColumn::Address => self.to_column(column).cmp(&other.to_column(column)), PeerTableColumn::LatencyAvg => self .peer_stats diff --git a/veilid-cli/src/ui.rs b/veilid-cli/src/ui.rs index 71b67a7f..729a7a11 100644 --- a/veilid-cli/src/ui.rs +++ b/veilid-cli/src/ui.rs @@ -881,13 +881,17 @@ impl UI { } pub fn set_config(&mut self, config: VeilidConfigInner) { let mut inner = self.inner.borrow_mut(); - inner.ui_state.node_id.set( - config - .network - .node_id - .map(|x| x.encode()) - .unwrap_or("".to_owned()), - ); + + let tkv: Vec = config + .network + .routing_table + .node_ids + .iter() + .filter_map(|(k, v)| v.node_id.map(|nid| TypedKey::new(*k, nid))) + .collect(); + let tks = TypedKeySet::from(tkv); + + inner.ui_state.node_id.set(tks.to_string()); } pub fn set_connection_state(&mut self, state: ConnectionState) { let mut inner = self.inner.borrow_mut(); diff --git a/veilid-core/src/crypto/byte_array_types.rs b/veilid-core/src/crypto/byte_array_types.rs index 17b32fa4..d8c7bc77 100644 --- a/veilid-core/src/crypto/byte_array_types.rs +++ b/veilid-core/src/crypto/byte_array_types.rs @@ -44,17 +44,29 @@ pub const SHARED_SECRET_LENGTH_ENCODED: usize = 43; /// Length of a route id in bytes #[allow(dead_code)] pub const ROUTE_ID_LENGTH: usize = 32; +/// Length of a route id in bytes afer encoding to base64url +#[allow(dead_code)] +pub const ROUTE_ID_LENGTH_ENCODED: usize = 43; ////////////////////////////////////////////////////////////////////// -pub trait Encodable { +pub trait Encodable +where + Self: Sized, +{ fn encode(&self) -> String; + fn encoded_len() -> usize; + fn try_decode>(input: S) -> Result { + let b = input.as_ref().as_bytes(); + Self::try_decode_bytes(b) + } + fn try_decode_bytes(b: &[u8]) -> Result; } ////////////////////////////////////////////////////////////////////// macro_rules! byte_array_type { - ($name:ident, $size:expr) => { + ($name:ident, $size:expr, $encoded_size:expr) => { #[derive( Clone, Copy, @@ -161,12 +173,16 @@ macro_rules! byte_array_type { } None } + } - pub fn try_decode>(input: S) -> Result { - let b = input.as_ref().as_bytes(); - Self::try_decode_bytes(b) + impl Encodable for $name { + fn encode(&self) -> String { + BASE64URL_NOPAD.encode(&self.bytes) } - pub fn try_decode_bytes(b: &[u8]) -> Result { + fn encoded_len() -> usize { + $encoded_size + } + fn try_decode_bytes(b: &[u8]) -> Result { let mut bytes = [0u8; $size]; let res = BASE64URL_NOPAD.decode_len(b.len()); match res { @@ -187,15 +203,8 @@ macro_rules! byte_array_type { } } } - - impl Encodable for $name { - fn encode(&self) -> String { - BASE64URL_NOPAD.encode(&self.bytes) - } - } impl fmt::Display for $name { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - //write!(f, "{}", String::from(self)) write!(f, "{}", self.encode()) } } @@ -210,12 +219,6 @@ macro_rules! byte_array_type { impl From<&$name> for String { fn from(value: &$name) -> Self { - // let mut s = String::new(); - // for n in 0..($size / 8) { - // let b: [u8; 8] = value.bytes[n * 8..(n + 1) * 8].try_into().unwrap(); - // s.push_str(hex::encode(b).as_str()); - // } - // s value.encode() } } @@ -238,17 +241,6 @@ macro_rules! byte_array_type { impl TryFrom<&str> for $name { type Error = VeilidAPIError; fn try_from(value: &str) -> Result { - // let mut out = $name::default(); - // if value == "" { - // return Ok(out); - // } - // if value.len() != ($size * 2) { - // apibail_generic!(concat!(stringify!($name), " is incorrect length")); - // } - // match hex::decode_to_slice(value, &mut out.bytes) { - // Ok(_) => Ok(out), - // Err(err) => Err(VeilidAPIError::generic(err)), - // } Self::try_decode(value) } } @@ -257,10 +249,18 @@ macro_rules! byte_array_type { ///////////////////////////////////////// -byte_array_type!(PublicKey, PUBLIC_KEY_LENGTH); -byte_array_type!(SecretKey, SECRET_KEY_LENGTH); -byte_array_type!(Signature, SIGNATURE_LENGTH); -byte_array_type!(PublicKeyDistance, PUBLIC_KEY_LENGTH); -byte_array_type!(Nonce, NONCE_LENGTH); -byte_array_type!(SharedSecret, SHARED_SECRET_LENGTH); -byte_array_type!(RouteId, ROUTE_ID_LENGTH); +byte_array_type!(PublicKey, PUBLIC_KEY_LENGTH, PUBLIC_KEY_LENGTH_ENCODED); +byte_array_type!(SecretKey, SECRET_KEY_LENGTH, SECRET_KEY_LENGTH_ENCODED); +byte_array_type!(Signature, SIGNATURE_LENGTH, SIGNATURE_LENGTH_ENCODED); +byte_array_type!( + PublicKeyDistance, + PUBLIC_KEY_LENGTH, + PUBLIC_KEY_LENGTH_ENCODED +); +byte_array_type!(Nonce, NONCE_LENGTH, NONCE_LENGTH_ENCODED); +byte_array_type!( + SharedSecret, + SHARED_SECRET_LENGTH, + SHARED_SECRET_LENGTH_ENCODED +); +byte_array_type!(RouteId, ROUTE_ID_LENGTH, ROUTE_ID_LENGTH_ENCODED); diff --git a/veilid-core/src/crypto/mod.rs b/veilid-core/src/crypto/mod.rs index affa5da9..980f4dfc 100644 --- a/veilid-core/src/crypto/mod.rs +++ b/veilid-core/src/crypto/mod.rs @@ -229,7 +229,7 @@ impl Crypto { for nid in node_ids { if nid.kind == sig.kind { if let Some(vcrypto) = self.get(sig.kind) { - vcrypto.verify(&nid.key, data, &sig.signature)?; + vcrypto.verify(&nid.value, data, &sig.value)?; out.push(nid.kind); } } @@ -253,7 +253,7 @@ impl Crypto { let mut out = Vec::::with_capacity(typed_key_pairs.len()); for kp in typed_key_pairs { if let Some(vcrypto) = self.get(kp.kind) { - let sig = vcrypto.sign(&kp.key, &kp.secret, data)?; + let sig = vcrypto.sign(&kp.value.key, &kp.value.secret, data)?; out.push(transform(kp, sig)) } } diff --git a/veilid-core/src/crypto/types.rs b/veilid-core/src/crypto/types.rs deleted file mode 100644 index e05395e1..00000000 --- a/veilid-core/src/crypto/types.rs +++ /dev/null @@ -1,548 +0,0 @@ -use super::*; - -use core::cmp::{Eq, Ord, PartialEq, PartialOrd}; -use core::convert::TryInto; -use core::fmt; -use core::hash::Hash; - -use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; - -/// Cryptography version fourcc code -pub type CryptoKind = FourCC; - -/// Sort best crypto kinds first -/// Better crypto kinds are 'less', ordered toward the front of a list -pub fn compare_crypto_kind(a: &CryptoKind, b: &CryptoKind) -> cmp::Ordering { - let a_idx = VALID_CRYPTO_KINDS.iter().position(|k| k == a); - let b_idx = VALID_CRYPTO_KINDS.iter().position(|k| k == b); - if let Some(a_idx) = a_idx { - if let Some(b_idx) = b_idx { - // Both are valid, prefer better crypto kind - a_idx.cmp(&b_idx) - } else { - // A is valid, B is not - cmp::Ordering::Less - } - } else if b_idx.is_some() { - // B is valid, A is not - cmp::Ordering::Greater - } else { - // Both are invalid, so use lex comparison - a.cmp(b) - } -} - -/// Intersection of crypto kind vectors -pub fn common_crypto_kinds(a: &[CryptoKind], b: &[CryptoKind]) -> Vec { - let mut out = Vec::new(); - for ack in a { - if b.contains(ack) { - out.push(*ack); - } - } - out -} - -#[derive( - Clone, - Copy, - Debug, - Serialize, - Deserialize, - PartialOrd, - Ord, - PartialEq, - Eq, - Hash, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes, Hash, PartialEq, Eq))] -pub struct KeyPair { - pub key: PublicKey, - pub secret: SecretKey, -} - -impl KeyPair { - pub fn new(key: PublicKey, secret: SecretKey) -> Self { - Self { key, secret } - } -} - -xxx make default template version here for secretkey -and put Vec> in settings -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, RkyvArchive, RkyvSerialize, RkyvDeserialize)] -#[archive_attr(repr(C), derive(CheckBytes, Hash, PartialEq, Eq))] -pub struct TypedKey { - pub kind: CryptoKind, - pub key: PublicKey, -} - -impl TypedKey { - pub fn new(kind: CryptoKind, key: PublicKey) -> Self { - Self { kind, key } - } -} -impl PartialOrd for TypedKey { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for TypedKey { - fn cmp(&self, other: &Self) -> cmp::Ordering { - let x = compare_crypto_kind(&self.kind, &other.kind); - if x != cmp::Ordering::Equal { - return x; - } - self.key.cmp(&other.key) - } -} - -impl fmt::Display for TypedKey { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "{}:{}", self.kind, self.key.encode()) - } -} -impl FromStr for TypedKey { - type Err = VeilidAPIError; - fn from_str(s: &str) -> Result { - let b = s.as_bytes(); - if b.len() != (5 + PUBLIC_KEY_LENGTH_ENCODED) || b[4..5] != b":"[..] { - apibail_parse_error!("invalid typed key", s); - } - let kind: CryptoKind = b[0..4].try_into().expect("should not fail to convert"); - let key = PublicKey::try_decode_bytes(&b[5..])?; - Ok(Self { kind, key }) - } -} -impl<'de> Deserialize<'de> for TypedKey { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let s = ::deserialize(deserializer)?; - FromStr::from_str(&s).map_err(serde::de::Error::custom) - } -} -impl Serialize for TypedKey { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.collect_str(self) - } -} - -#[derive( - Clone, - Debug, - Serialize, - Deserialize, - PartialOrd, - Ord, - PartialEq, - Eq, - Hash, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes, Hash, PartialEq, Eq))] -#[serde(from = "Vec", into = "Vec")] -pub struct TypedKeySet { - items: Vec, -} - -impl TypedKeySet { - pub fn new() -> Self { - Self { items: Vec::new() } - } - pub fn with_capacity(cap: usize) -> Self { - Self { - items: Vec::with_capacity(cap), - } - } - pub fn kinds(&self) -> Vec { - let mut out = Vec::new(); - for tk in &self.items { - out.push(tk.kind); - } - out.sort_by(compare_crypto_kind); - out - } - pub fn keys(&self) -> Vec { - let mut out = Vec::new(); - for tk in &self.items { - out.push(tk.key); - } - out - } - pub fn get(&self, kind: CryptoKind) -> Option { - self.items.iter().find(|x| x.kind == kind).copied() - } - pub fn add(&mut self, typed_key: TypedKey) { - for x in &mut self.items { - if x.kind == typed_key.kind { - *x = typed_key; - return; - } - } - self.items.push(typed_key); - self.items.sort() - } - pub fn add_all(&mut self, typed_keys: &[TypedKey]) { - 'outer: for typed_key in typed_keys { - for x in &mut self.items { - if x.kind == typed_key.kind { - *x = *typed_key; - continue 'outer; - } - } - self.items.push(*typed_key); - } - self.items.sort() - } - pub fn remove(&mut self, kind: CryptoKind) { - if let Some(idx) = self.items.iter().position(|x| x.kind == kind) { - self.items.remove(idx); - } - } - pub fn remove_all(&mut self, kinds: &[CryptoKind]) { - for k in kinds { - self.remove(*k); - } - } - /// Return preferred typed key of our supported crypto kinds - pub fn best(&self) -> Option { - match self.items.first().copied() { - None => None, - Some(k) => { - if !VALID_CRYPTO_KINDS.contains(&k.kind) { - None - } else { - Some(k) - } - } - } - } - pub fn len(&self) -> usize { - self.items.len() - } - pub fn iter(&self) -> core::slice::Iter<'_, TypedKey> { - self.items.iter() - } - pub fn contains(&self, typed_key: &TypedKey) -> bool { - self.items.contains(typed_key) - } - pub fn contains_any(&self, typed_keys: &[TypedKey]) -> bool { - for typed_key in typed_keys { - if self.items.contains(typed_key) { - return true; - } - } - false - } - pub fn contains_key(&self, key: &PublicKey) -> bool { - for tk in &self.items { - if tk.key == *key { - return true; - } - } - false - } -} - -impl core::ops::Deref for TypedKeySet { - type Target = [TypedKey]; - - #[inline] - fn deref(&self) -> &[TypedKey] { - &self.items - } -} - -impl fmt::Display for TypedKeySet { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "[")?; - let mut first = true; - for x in &self.items { - if !first { - write!(f, ",")?; - first = false; - } - write!(f, "{}", x)?; - } - write!(f, "]") - } -} -impl FromStr for TypedKeySet { - type Err = VeilidAPIError; - fn from_str(s: &str) -> Result { - let mut items = Vec::new(); - if s.len() < 2 { - apibail_parse_error!("invalid length", s); - } - if &s[0..1] != "[" || &s[(s.len() - 1)..] != "]" { - apibail_parse_error!("invalid format", s); - } - for x in s[1..s.len() - 1].split(",") { - let tk = TypedKey::from_str(x.trim())?; - items.push(tk); - } - - Ok(Self { items }) - } -} -impl From for TypedKeySet { - fn from(x: TypedKey) -> Self { - let mut tks = TypedKeySet::with_capacity(1); - tks.add(x); - tks - } -} -impl From> for TypedKeySet { - fn from(x: Vec) -> Self { - let mut tks = TypedKeySet::with_capacity(x.len()); - tks.add_all(&x); - tks - } -} -impl Into> for TypedKeySet { - fn into(self) -> Vec { - self.items - } -} - -#[derive( - Clone, - Copy, - Debug, - PartialEq, - Eq, - Hash, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes, Hash, PartialEq, Eq))] -pub struct TypedKeyPair { - pub kind: CryptoKind, - pub key: PublicKey, - pub secret: SecretKey, -} - -impl TypedKeyPair { - pub fn new(kind: CryptoKind, key: PublicKey, secret: SecretKey) -> Self { - Self { kind, key, secret } - } -} - -impl PartialOrd for TypedKeyPair { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for TypedKeyPair { - fn cmp(&self, other: &Self) -> cmp::Ordering { - let x = compare_crypto_kind(&self.kind, &other.kind); - if x != cmp::Ordering::Equal { - return x; - } - let x = self.key.cmp(&other.key); - if x != cmp::Ordering::Equal { - return x; - } - self.secret.cmp(&other.secret) - } -} - -impl fmt::Display for TypedKeyPair { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!( - f, - "{}:{}:{}", - self.kind, - self.key.encode(), - self.secret.encode() - ) - } -} -impl FromStr for TypedKeyPair { - type Err = VeilidAPIError; - fn from_str(s: &str) -> Result { - let b = s.as_bytes(); - if b.len() != (5 + PUBLIC_KEY_LENGTH_ENCODED + 1 + SECRET_KEY_LENGTH_ENCODED) - || b[4..5] != b":"[..] - || b[5 + PUBLIC_KEY_LENGTH_ENCODED..6 + PUBLIC_KEY_LENGTH_ENCODED] != b":"[..] - { - apibail_parse_error!("invalid typed key pair", s); - } - let kind: CryptoKind = b[0..4].try_into().expect("should not fail to convert"); - let key = PublicKey::try_decode_bytes(&b[5..5 + PUBLIC_KEY_LENGTH_ENCODED])?; - let secret = SecretKey::try_decode_bytes(&b[5 + PUBLIC_KEY_LENGTH_ENCODED + 1..])?; - Ok(Self { kind, key, secret }) - } -} - -#[derive( - Clone, - Copy, - Debug, - PartialEq, - Eq, - Hash, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes, Hash, PartialEq, Eq))] -pub struct TypedSignature { - pub kind: CryptoKind, - pub signature: Signature, -} -impl TypedSignature { - pub fn new(kind: CryptoKind, signature: Signature) -> Self { - Self { kind, signature } - } - pub fn from_keyed(tks: &TypedKeySignature) -> Self { - Self { - kind: tks.kind, - signature: tks.signature, - } - } - pub fn from_pair_sig(tkp: &TypedKeyPair, sig: Signature) -> Self { - Self { - kind: tkp.kind, - signature: sig, - } - } -} - -impl PartialOrd for TypedSignature { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for TypedSignature { - fn cmp(&self, other: &Self) -> cmp::Ordering { - let x = compare_crypto_kind(&self.kind, &other.kind); - if x != cmp::Ordering::Equal { - return x; - } - self.signature.cmp(&other.signature) - } -} - -impl fmt::Display for TypedSignature { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "{}:{}", self.kind, self.signature.encode()) - } -} -impl FromStr for TypedSignature { - type Err = VeilidAPIError; - fn from_str(s: &str) -> Result { - let b = s.as_bytes(); - if b.len() != (5 + SIGNATURE_LENGTH_ENCODED) || b[4..5] != b":"[..] { - apibail_parse_error!("invalid typed signature", s); - } - let kind: CryptoKind = b[0..4].try_into()?; - let signature = Signature::try_decode_bytes(&b[5..])?; - Ok(Self { kind, signature }) - } -} - -#[derive( - Clone, - Copy, - Debug, - PartialEq, - Eq, - Hash, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -#[archive_attr(repr(C), derive(CheckBytes, Hash, PartialEq, Eq))] -pub struct TypedKeySignature { - pub kind: CryptoKind, - pub key: PublicKey, - pub signature: Signature, -} - -impl TypedKeySignature { - pub fn new(kind: CryptoKind, key: PublicKey, signature: Signature) -> Self { - Self { - kind, - key, - signature, - } - } - pub fn as_typed_signature(&self) -> TypedSignature { - TypedSignature { - kind: self.kind, - signature: self.signature, - } - } -} - -impl PartialOrd for TypedKeySignature { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for TypedKeySignature { - fn cmp(&self, other: &Self) -> cmp::Ordering { - let x = compare_crypto_kind(&self.kind, &other.kind); - if x != cmp::Ordering::Equal { - return x; - } - let x = self.key.cmp(&other.key); - if x != cmp::Ordering::Equal { - return x; - } - self.signature.cmp(&other.signature) - } -} - -impl fmt::Display for TypedKeySignature { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!( - f, - "{}:{}:{}", - self.kind, - self.key.encode(), - self.signature.encode() - ) - } -} -impl FromStr for TypedKeySignature { - type Err = VeilidAPIError; - fn from_str(s: &str) -> Result { - let b = s.as_bytes(); - if b.len() != (5 + PUBLIC_KEY_LENGTH_ENCODED + 1 + SIGNATURE_LENGTH_ENCODED) - || b[4] != b':' - || b[5 + PUBLIC_KEY_LENGTH_ENCODED] != b':' - { - apibail_parse_error!("invalid typed key signature", s); - } - let kind: CryptoKind = b[0..4].try_into().expect("should not fail to convert"); - let key = PublicKey::try_decode_bytes(&b[5..5 + PUBLIC_KEY_LENGTH_ENCODED])?; - let signature = Signature::try_decode_bytes(&b[5 + PUBLIC_KEY_LENGTH_ENCODED + 1..])?; - Ok(Self { - kind, - key, - signature, - }) - } -} diff --git a/veilid-core/src/crypto/types/crypto_typed.rs b/veilid-core/src/crypto/types/crypto_typed.rs new file mode 100644 index 00000000..b6575769 --- /dev/null +++ b/veilid-core/src/crypto/types/crypto_typed.rs @@ -0,0 +1,184 @@ +use super::*; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, RkyvArchive, RkyvSerialize, RkyvDeserialize)] +#[archive_attr(repr(C), derive(CheckBytes, Hash, PartialEq, Eq))] +pub struct CryptoTyped +where + K: Clone + + Copy + + fmt::Debug + + fmt::Display + + FromStr + + PartialEq + + Eq + + Ord + + PartialOrd + + Hash + + RkyvArchive + + Encodable, + ::Archived: Hash + PartialEq + Eq, +{ + pub kind: CryptoKind, + pub value: K, +} + +impl CryptoTyped +where + K: Clone + + Copy + + fmt::Debug + + fmt::Display + + FromStr + + PartialEq + + Eq + + Ord + + PartialOrd + + Hash + + RkyvArchive + + Encodable, + ::Archived: Hash + PartialEq + Eq, +{ + pub fn new(kind: CryptoKind, value: K) -> Self { + Self { kind, value } + } +} +impl PartialOrd for CryptoTyped +where + K: Clone + + Copy + + fmt::Debug + + fmt::Display + + FromStr + + PartialEq + + Eq + + Ord + + PartialOrd + + Hash + + RkyvArchive + + Encodable, + ::Archived: Hash + PartialEq + Eq, +{ + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for CryptoTyped +where + K: Clone + + Copy + + fmt::Debug + + fmt::Display + + FromStr + + PartialEq + + Eq + + Ord + + PartialOrd + + Hash + + RkyvArchive + + Encodable, + ::Archived: Hash + PartialEq + Eq, +{ + fn cmp(&self, other: &Self) -> cmp::Ordering { + let x = compare_crypto_kind(&self.kind, &other.kind); + if x != cmp::Ordering::Equal { + return x; + } + self.value.cmp(&other.value) + } +} + +impl fmt::Display for CryptoTyped +where + K: Clone + + Copy + + fmt::Debug + + fmt::Display + + FromStr + + PartialEq + + Eq + + Ord + + PartialOrd + + Hash + + RkyvArchive + + Encodable, + ::Archived: Hash + PartialEq + Eq, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "{}:{}", self.kind, self.value) + } +} +impl FromStr for CryptoTyped +where + K: Clone + + Copy + + fmt::Debug + + fmt::Display + + FromStr + + PartialEq + + Eq + + Ord + + PartialOrd + + Hash + + RkyvArchive + + Encodable, + ::Archived: Hash + PartialEq + Eq, +{ + type Err = VeilidAPIError; + fn from_str(s: &str) -> Result { + let b = s.as_bytes(); + if b.len() != (5 + K::encoded_len()) || b[4..5] != b":"[..] { + apibail_parse_error!("invalid typed key", s); + } + let kind: CryptoKind = b[0..4].try_into().expect("should not fail to convert"); + let value = K::try_decode_bytes(&b[5..])?; + Ok(Self { kind, value }) + } +} +impl<'de, K> Deserialize<'de> for CryptoTyped +where + K: Clone + + Copy + + fmt::Debug + + fmt::Display + + FromStr + + PartialEq + + Eq + + Ord + + PartialOrd + + Hash + + RkyvArchive + + Encodable, + ::Archived: Hash + PartialEq + Eq, +{ + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let s = ::deserialize(deserializer)?; + FromStr::from_str(&s).map_err(serde::de::Error::custom) + } +} +impl Serialize for CryptoTyped +where + K: Clone + + Copy + + fmt::Debug + + fmt::Display + + FromStr + + PartialEq + + Eq + + Ord + + PartialOrd + + Hash + + RkyvArchive + + Encodable, + ::Archived: Hash + PartialEq + Eq, +{ + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.collect_str(self) + } +} diff --git a/veilid-core/src/crypto/types/crypto_typed_set.rs b/veilid-core/src/crypto/types/crypto_typed_set.rs new file mode 100644 index 00000000..e1edbe01 --- /dev/null +++ b/veilid-core/src/crypto/types/crypto_typed_set.rs @@ -0,0 +1,303 @@ +use super::*; + +#[derive( + Clone, + Debug, + Serialize, + Deserialize, + PartialOrd, + Ord, + PartialEq, + Eq, + Hash, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, + Default, +)] +#[archive_attr(repr(C), derive(CheckBytes, Hash, PartialEq, Eq))] +#[serde(from = "Vec>", into = "Vec>")] +pub struct CryptoTypedSet +where + K: Clone + + Copy + + fmt::Debug + + fmt::Display + + FromStr + + PartialEq + + Eq + + PartialOrd + + Ord + + Hash + + RkyvArchive + + Encodable, + ::Archived: Hash + PartialEq + Eq, + > as RkyvArchive>::Archived: Hash + PartialEq + Eq, +{ + items: Vec>, +} + +impl CryptoTypedSet +where + K: Clone + + Copy + + fmt::Debug + + fmt::Display + + FromStr + + PartialEq + + Eq + + PartialOrd + + Ord + + Hash + + RkyvArchive + + Encodable, + ::Archived: Hash + PartialEq + Eq, +{ + pub fn new() -> Self { + Self { items: Vec::new() } + } + pub fn with_capacity(cap: usize) -> Self { + Self { + items: Vec::with_capacity(cap), + } + } + pub fn kinds(&self) -> Vec { + let mut out = Vec::new(); + for tk in &self.items { + out.push(tk.kind); + } + out.sort_by(compare_crypto_kind); + out + } + pub fn keys(&self) -> Vec { + let mut out = Vec::new(); + for tk in &self.items { + out.push(tk.value); + } + out + } + pub fn get(&self, kind: CryptoKind) -> Option> { + self.items.iter().find(|x| x.kind == kind).copied() + } + pub fn add(&mut self, typed_key: CryptoTyped) { + for x in &mut self.items { + if x.kind == typed_key.kind { + *x = typed_key; + return; + } + } + self.items.push(typed_key); + self.items.sort() + } + pub fn add_all(&mut self, typed_keys: &[CryptoTyped]) { + 'outer: for typed_key in typed_keys { + for x in &mut self.items { + if x.kind == typed_key.kind { + *x = *typed_key; + continue 'outer; + } + } + self.items.push(*typed_key); + } + self.items.sort() + } + pub fn remove(&mut self, kind: CryptoKind) { + if let Some(idx) = self.items.iter().position(|x| x.kind == kind) { + self.items.remove(idx); + } + } + pub fn remove_all(&mut self, kinds: &[CryptoKind]) { + for k in kinds { + self.remove(*k); + } + } + /// Return preferred typed key of our supported crypto kinds + pub fn best(&self) -> Option> { + match self.items.first().copied() { + None => None, + Some(k) => { + if !VALID_CRYPTO_KINDS.contains(&k.kind) { + None + } else { + Some(k) + } + } + } + } + pub fn len(&self) -> usize { + self.items.len() + } + pub fn iter(&self) -> core::slice::Iter<'_, CryptoTyped> { + self.items.iter() + } + pub fn contains(&self, typed_key: &CryptoTyped) -> bool { + self.items.contains(typed_key) + } + pub fn contains_any(&self, typed_keys: &[CryptoTyped]) -> bool { + for typed_key in typed_keys { + if self.items.contains(typed_key) { + return true; + } + } + false + } + pub fn contains_key(&self, key: &K) -> bool { + for tk in &self.items { + if tk.value == *key { + return true; + } + } + false + } +} + +impl core::ops::Deref for CryptoTypedSet +where + K: Clone + + Copy + + fmt::Debug + + fmt::Display + + FromStr + + PartialEq + + Eq + + PartialOrd + + Ord + + Hash + + RkyvArchive + + Encodable, + ::Archived: Hash + PartialEq + Eq, +{ + type Target = [CryptoTyped]; + + #[inline] + fn deref(&self) -> &[CryptoTyped] { + &self.items + } +} + +impl fmt::Display for CryptoTypedSet +where + K: Clone + + Copy + + fmt::Debug + + fmt::Display + + FromStr + + PartialEq + + Eq + + PartialOrd + + Ord + + Hash + + RkyvArchive + + Encodable, + ::Archived: Hash + PartialEq + Eq, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "[")?; + let mut first = true; + for x in &self.items { + if !first { + write!(f, ",")?; + first = false; + } + write!(f, "{}", x)?; + } + write!(f, "]") + } +} +impl FromStr for CryptoTypedSet +where + K: Clone + + Copy + + fmt::Debug + + fmt::Display + + FromStr + + PartialEq + + Eq + + PartialOrd + + Ord + + Hash + + RkyvArchive + + Encodable, + ::Archived: Hash + PartialEq + Eq, +{ + type Err = VeilidAPIError; + fn from_str(s: &str) -> Result { + let mut items = Vec::new(); + if s.len() < 2 { + apibail_parse_error!("invalid length", s); + } + if &s[0..1] != "[" || &s[(s.len() - 1)..] != "]" { + apibail_parse_error!("invalid format", s); + } + for x in s[1..s.len() - 1].split(",") { + let tk = CryptoTyped::::from_str(x.trim())?; + items.push(tk); + } + + Ok(Self { items }) + } +} +impl From> for CryptoTypedSet +where + K: Clone + + Copy + + fmt::Debug + + fmt::Display + + FromStr + + PartialEq + + Eq + + PartialOrd + + Ord + + Hash + + RkyvArchive + + Encodable, + ::Archived: Hash + PartialEq + Eq, +{ + fn from(x: CryptoTyped) -> Self { + let mut tks = CryptoTypedSet::::with_capacity(1); + tks.add(x); + tks + } +} +impl From>> for CryptoTypedSet +where + K: Clone + + Copy + + fmt::Debug + + fmt::Display + + FromStr + + PartialEq + + Eq + + PartialOrd + + Ord + + Hash + + RkyvArchive + + Encodable, + ::Archived: Hash + PartialEq + Eq, +{ + fn from(x: Vec>) -> Self { + let mut tks = CryptoTypedSet::::with_capacity(x.len()); + tks.add_all(&x); + tks + } +} +impl Into>> for CryptoTypedSet +where + K: Clone + + Copy + + fmt::Debug + + fmt::Display + + FromStr + + PartialEq + + Eq + + PartialOrd + + Ord + + Hash + + RkyvArchive + + Encodable, + ::Archived: Hash + PartialEq + Eq, +{ + fn into(self) -> Vec> { + self.items + } +} diff --git a/veilid-core/src/crypto/types/keypair.rs b/veilid-core/src/crypto/types/keypair.rs new file mode 100644 index 00000000..514afb06 --- /dev/null +++ b/veilid-core/src/crypto/types/keypair.rs @@ -0,0 +1,85 @@ +use super::*; + +#[derive( + Clone, + Copy, + Serialize, + Deserialize, + PartialOrd, + Ord, + PartialEq, + Eq, + Hash, + RkyvArchive, + RkyvSerialize, + RkyvDeserialize, +)] +#[archive_attr(repr(C), derive(CheckBytes, Hash, PartialEq, Eq))] +pub struct KeyPair { + pub key: PublicKey, + pub secret: SecretKey, +} + +impl KeyPair { + pub fn new(key: PublicKey, secret: SecretKey) -> Self { + Self { key, secret } + } +} + +impl Encodable for KeyPair { + fn encode(&self) -> String { + format!("{}:{}", self.key.encode(), self.secret.encode()) + } + fn encoded_len() -> usize { + PublicKey::encoded_len() + 1 + SecretKey::encoded_len() + } + fn try_decode_bytes(b: &[u8]) -> Result { + if b.len() != Self::encoded_len() { + apibail_parse_error!("input has wrong encoded length", format!("len={}", b.len())); + } + let key = PublicKey::try_decode_bytes(&b[0..PublicKey::encoded_len()])?; + let secret = SecretKey::try_decode_bytes(&b[(PublicKey::encoded_len() + 1)..])?; + Ok(KeyPair { key, secret }) + } +} +impl fmt::Display for KeyPair { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.encode()) + } +} + +impl fmt::Debug for KeyPair { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, concat!(stringify!($name), "("))?; + write!(f, "{}", self.encode())?; + write!(f, ")") + } +} + +impl From<&KeyPair> for String { + fn from(value: &KeyPair) -> Self { + value.encode() + } +} + +impl FromStr for KeyPair { + type Err = VeilidAPIError; + + fn from_str(s: &str) -> Result { + KeyPair::try_from(s) + } +} + +impl TryFrom for KeyPair { + type Error = VeilidAPIError; + fn try_from(value: String) -> Result { + KeyPair::try_from(value.as_str()) + } +} + +impl TryFrom<&str> for KeyPair { + type Error = VeilidAPIError; + fn try_from(value: &str) -> Result { + Self::try_decode(value) + } +} diff --git a/veilid-core/src/crypto/types/mod.rs b/veilid-core/src/crypto/types/mod.rs new file mode 100644 index 00000000..355c34b0 --- /dev/null +++ b/veilid-core/src/crypto/types/mod.rs @@ -0,0 +1,59 @@ +use super::*; + +use core::cmp::{Eq, Ord, PartialEq, PartialOrd}; +use core::convert::TryInto; +use core::fmt; +use core::hash::Hash; + +use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; + +/// Cryptography version fourcc code +pub type CryptoKind = FourCC; + +/// Sort best crypto kinds first +/// Better crypto kinds are 'less', ordered toward the front of a list +pub fn compare_crypto_kind(a: &CryptoKind, b: &CryptoKind) -> cmp::Ordering { + let a_idx = VALID_CRYPTO_KINDS.iter().position(|k| k == a); + let b_idx = VALID_CRYPTO_KINDS.iter().position(|k| k == b); + if let Some(a_idx) = a_idx { + if let Some(b_idx) = b_idx { + // Both are valid, prefer better crypto kind + a_idx.cmp(&b_idx) + } else { + // A is valid, B is not + cmp::Ordering::Less + } + } else if b_idx.is_some() { + // B is valid, A is not + cmp::Ordering::Greater + } else { + // Both are invalid, so use lex comparison + a.cmp(b) + } +} + +/// Intersection of crypto kind vectors +pub fn common_crypto_kinds(a: &[CryptoKind], b: &[CryptoKind]) -> Vec { + let mut out = Vec::new(); + for ack in a { + if b.contains(ack) { + out.push(*ack); + } + } + out +} + +mod crypto_typed; +mod crypto_typed_set; +mod keypair; + +pub use crypto_typed::*; +pub use crypto_typed_set::*; +pub use keypair::*; + +pub type TypedKey = CryptoTyped; +pub type TypedSecret = CryptoTyped; +pub type TypedKeyPair = CryptoTyped; +pub type TypedSignature = CryptoTyped; +pub type TypedKeySet = CryptoTypedSet; +pub type TypedSecretSet = CryptoTypedSet; diff --git a/veilid-core/src/crypto/vld0/mod.rs b/veilid-core/src/crypto/vld0/mod.rs index 697c480e..58eb1d49 100644 --- a/veilid-core/src/crypto/vld0/mod.rs +++ b/veilid-core/src/crypto/vld0/mod.rs @@ -32,7 +32,16 @@ fn ed25519_to_x25519_sk(key: &ed::SecretKey) -> Result (PublicKey, SecretKey) { + let mut csprng = VeilidRng {}; + let keypair = ed::Keypair::generate(&mut csprng); + let dht_key = PublicKey::new(keypair.public.to_bytes()); + let dht_key_secret = SecretKey::new(keypair.secret.to_bytes()); + + (dht_key, dht_key_secret) +} + +/// V0 CryptoSystem #[derive(Clone)] pub struct CryptoSystemVLD0 { crypto: Crypto, @@ -87,12 +96,7 @@ impl CryptoSystem for CryptoSystemVLD0 { Ok(SharedSecret::new(sk_xd.diffie_hellman(&pk_xd).to_bytes())) } fn generate_keypair(&self) -> (PublicKey, SecretKey) { - let mut csprng = VeilidRng {}; - let keypair = ed::Keypair::generate(&mut csprng); - let dht_key = PublicKey::new(keypair.public.to_bytes()); - let dht_key_secret = SecretKey::new(keypair.secret.to_bytes()); - - (dht_key, dht_key_secret) + vld0_generate_keypair() } fn generate_hash(&self, data: &[u8]) -> PublicKey { PublicKey::new(*blake3::hash(data).as_bytes()) diff --git a/veilid-core/src/lib.rs b/veilid-core/src/lib.rs index f6cc2f76..39f6bbae 100644 --- a/veilid-core/src/lib.rs +++ b/veilid-core/src/lib.rs @@ -35,6 +35,7 @@ mod veilid_layer_filter; pub use self::api_tracing_layer::ApiTracingLayer; pub use self::core_context::{api_startup, api_startup_json, UpdateCallback}; +pub use self::crypto::vld0_generate_keypair; pub use self::veilid_api::*; pub use self::veilid_config::*; pub use self::veilid_layer_filter::*; diff --git a/veilid-core/src/network_manager/mod.rs b/veilid-core/src/network_manager/mod.rs index 5eca8e76..e9fd9aee 100644 --- a/veilid-core/src/network_manager/mod.rs +++ b/veilid-core/src/network_manager/mod.rs @@ -529,7 +529,7 @@ impl NetworkManager { let node_id = routing_table.node_id(vcrypto.kind()); let node_id_secret = routing_table.node_id_secret(vcrypto.kind()); - let receipt = Receipt::try_new(best_envelope_version(), node_id.kind, nonce, node_id.key, extra_data)?; + let receipt = Receipt::try_new(best_envelope_version(), node_id.kind, nonce, node_id.value, extra_data)?; let out = receipt .to_signed_data(self.crypto(), &node_id_secret) .wrap_err("failed to generate signed receipt")?; @@ -558,7 +558,7 @@ impl NetworkManager { let node_id = routing_table.node_id(vcrypto.kind()); let node_id_secret = routing_table.node_id_secret(vcrypto.kind()); - let receipt = Receipt::try_new(best_envelope_version(), node_id.kind, nonce, node_id.key, extra_data)?; + let receipt = Receipt::try_new(best_envelope_version(), node_id.kind, nonce, node_id.value, extra_data)?; let out = receipt .to_signed_data(self.crypto(), &node_id_secret) .wrap_err("failed to generate signed receipt")?; @@ -761,7 +761,7 @@ impl NetworkManager { let nonce = vcrypto.random_nonce(); // Encode envelope - let envelope = Envelope::new(version, node_id.kind, ts, nonce, node_id.key, dest_node_id.key); + let envelope = Envelope::new(version, node_id.kind, ts, nonce, node_id.value, dest_node_id.value); envelope .to_encrypted_data(self.crypto(), body.as_ref(), &node_id_secret) .wrap_err("envelope failed to encode") @@ -1389,7 +1389,7 @@ impl NetworkManager { let some_relay_nr = if self.check_client_whitelist(sender_id) { // Full relay allowed, do a full resolve_node - match rpc.resolve_node(recipient_id.key).await { + match rpc.resolve_node(recipient_id.value).await { Ok(v) => v, Err(e) => { log_net!(debug "failed to resolve recipient node for relay, dropping outbound relayed packet: {}" ,e); diff --git a/veilid-core/src/routing_table/mod.rs b/veilid-core/src/routing_table/mod.rs index f5049fa9..b9f5b976 100644 --- a/veilid-core/src/routing_table/mod.rs +++ b/veilid-core/src/routing_table/mod.rs @@ -127,7 +127,7 @@ impl RoutingTableUnlockedInner { pub fn node_id_typed_key_pairs(&self) -> Vec { let mut tkps = Vec::new(); for (ck, v) in &self.node_id_keypairs { - tkps.push(TypedKeyPair::new(*ck, v.key, v.secret)); + tkps.push(TypedKeyPair::new(*ck, *v)); } tkps } @@ -139,7 +139,7 @@ impl RoutingTableUnlockedInner { pub fn matches_own_node_id(&self, node_ids: &[TypedKey]) -> bool { for ni in node_ids { if let Some(v) = self.node_id_keypairs.get(&ni.kind) { - if v.key == ni.key { + if v.key == ni.value { return true; } } @@ -163,7 +163,7 @@ impl RoutingTableUnlockedInner { ( node_id.kind, vcrypto - .distance(&node_id.key, &self_node_id) + .distance(&node_id.value, &self_node_id) .first_nonzero_bit() .unwrap(), ) diff --git a/veilid-core/src/routing_table/route_spec_store/route_set_spec_detail.rs b/veilid-core/src/routing_table/route_spec_store/route_set_spec_detail.rs index ca523e24..89449149 100644 --- a/veilid-core/src/routing_table/route_spec_store/route_set_spec_detail.rs +++ b/veilid-core/src/routing_table/route_spec_store/route_set_spec_detail.rs @@ -68,7 +68,7 @@ impl RouteSetSpecDetail { tks } pub fn get_best_route_set_key(&self) -> Option { - self.get_route_set_keys().best().map(|k| k.key) + self.get_route_set_keys().best().map(|k| k.value) } pub fn set_hop_node_refs(&mut self, node_refs: Vec) { self.hop_node_refs = node_refs; @@ -128,7 +128,7 @@ impl RouteSetSpecDetail { let hops = &self.hop_node_refs; let mut cache: Vec = Vec::with_capacity(hops.len() * PUBLIC_KEY_LENGTH); for hop in hops { - cache.extend_from_slice(&hop.best_node_id().key.bytes); + cache.extend_from_slice(&hop.best_node_id().value.bytes); } cache } diff --git a/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs b/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs index f0ce8fcb..1e26c545 100644 --- a/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs +++ b/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs @@ -358,7 +358,7 @@ impl RouteSpecStore { fn route_permutation_to_hop_cache(rti: &RoutingTableInner, nodes: &[NodeRef], perm: &[usize]) -> Vec { let mut cache: Vec = Vec::with_capacity(perm.len() * PUBLIC_KEY_LENGTH); for n in perm { - cache.extend_from_slice(&nodes[*n].locked(rti).best_node_id().key.bytes) + cache.extend_from_slice(&nodes[*n].locked(rti).best_node_id().value.bytes) } cache } @@ -491,7 +491,7 @@ impl RouteSpecStore { for crypto_kind in crypto_kinds.iter().copied() { let vcrypto = self.unlocked_inner.routing_table.crypto().get(crypto_kind).unwrap(); let (public_key, secret_key) = vcrypto.generate_keypair(); - let hops: Vec = route_nodes.iter().map(|v| nodes[*v].node_ids().get(crypto_kind).unwrap().key).collect(); + let hops: Vec = route_nodes.iter().map(|v| nodes[*v].node_ids().get(crypto_kind).unwrap().value).collect(); route_set.insert(public_key, RouteSpecDetail { crypto_kind, @@ -543,16 +543,16 @@ impl RouteSpecStore { return None; }; - let Some(rsid) = inner.content.get_id_by_key(&public_key.key) else { - log_rpc!(debug "route id does not exist: {:?}", public_key.key); + let Some(rsid) = inner.content.get_id_by_key(&public_key.value) else { + log_rpc!(debug "route id does not exist: {:?}", public_key.value); return None; }; let Some(rssd) = inner.content.get_detail(&rsid) else { log_rpc!(debug "route detail does not exist: {:?}", rsid); return None; }; - let Some(rsd) = rssd.get_route_by_key(&public_key.key) else { - log_rpc!(debug "route set {:?} does not have key: {:?}", rsid, public_key.key); + let Some(rsd) = rssd.get_route_by_key(&public_key.value) else { + log_rpc!(debug "route set {:?} does not have key: {:?}", rsid, public_key.value); return None; }; @@ -694,7 +694,7 @@ impl RouteSpecStore { } /// Check if a route id is remote or not - fn is_route_id_remote(&self, id: &RouteId) -> bool { + pub fn is_route_id_remote(&self, id: &RouteId) -> bool { let inner = &mut *self.inner.lock(); let cur_ts = get_aligned_timestamp(); inner.cache.peek_remote_private_route_mut(cur_ts, &id).is_some() @@ -847,7 +847,7 @@ impl RouteSpecStore { let Some(vcrypto) = crypto.get(crypto_kind) else { bail!("crypto not supported for route"); }; - let pr_pubkey = private_route.public_key.key; + let pr_pubkey = private_route.public_key.value; let pr_hopcount = private_route.hop_count as usize; let max_route_hop_count = self.unlocked_inner.max_route_hop_count; @@ -1092,7 +1092,7 @@ impl RouteSpecStore { if let Some(preferred_key) = preferred_rssd.get_route_set_keys().get(crypto_kind) { // Only use the preferred route if it doesn't contain the avoid nodes if !preferred_rssd.contains_nodes(avoid_nodes) { - return Ok(Some(preferred_key.key)); + return Ok(Some(preferred_key.value)); } } } @@ -1131,7 +1131,7 @@ impl RouteSpecStore { sr_route_id }; - let sr_pubkey = inner.content.get_detail(&sr_route_id).unwrap().get_route_set_keys().get(crypto_kind).unwrap().key; + let sr_pubkey = inner.content.get_detail(&sr_route_id).unwrap().get_route_set_keys().get(crypto_kind).unwrap().value; Ok(Some(sr_pubkey)) } @@ -1178,7 +1178,7 @@ impl RouteSpecStore { let Some(node_id) = routing_table.node_ids().get(rsd.crypto_kind) else { bail!("missing node id for crypto kind"); }; - RouteNode::NodeId(node_id.key) + RouteNode::NodeId(node_id.value) } else { let Some(pi) = rti.get_own_peer_info(RoutingDomain::PublicInternet) else { bail!("can't make private routes until our node info is valid"); @@ -1319,7 +1319,7 @@ impl RouteSpecStore { } // ensure this isn't also an allocated route - if inner.content.get_id_by_key(&private_route.public_key.key).is_some() { + if inner.content.get_id_by_key(&private_route.public_key.value).is_some() { bail!("should not import allocated route"); } } @@ -1570,7 +1570,7 @@ impl RouteSpecStore { if best_kind.is_none() || compare_crypto_kind(&tk.kind, best_kind.as_ref().unwrap()) == cmp::Ordering::Less { best_kind = Some(tk.kind); } - idbytes.extend_from_slice(&tk.key.bytes); + idbytes.extend_from_slice(&tk.value.bytes); } let Some(best_kind) = best_kind else { bail!("no compatible crypto kinds in route"); @@ -1591,7 +1591,7 @@ impl RouteSpecStore { if best_kind.is_none() || compare_crypto_kind(&private_route.public_key.kind, best_kind.as_ref().unwrap()) == cmp::Ordering::Less { best_kind = Some(private_route.public_key.kind); } - idbytes.extend_from_slice(&private_route.public_key.key.bytes); + idbytes.extend_from_slice(&private_route.public_key.value.bytes); } let Some(best_kind) = best_kind else { bail!("no compatible crypto kinds in route"); diff --git a/veilid-core/src/routing_table/route_spec_store/route_spec_store_cache.rs b/veilid-core/src/routing_table/route_spec_store/route_spec_store_cache.rs index c91c45a5..b484c084 100644 --- a/veilid-core/src/routing_table/route_spec_store/route_spec_store_cache.rs +++ b/veilid-core/src/routing_table/route_spec_store/route_spec_store_cache.rs @@ -114,14 +114,18 @@ impl RouteSpecStoreCache { /// calculate how many times a node with a particular node id set has been used anywhere in the path of our allocated routes pub fn get_used_node_count(&self, node_ids: &TypedKeySet) -> usize { node_ids.iter().fold(0usize, |acc, k| { - acc + self.used_nodes.get(&k.key).cloned().unwrap_or_default() + acc + self.used_nodes.get(&k.value).cloned().unwrap_or_default() }) } /// calculate how many times a node with a particular node id set has been used at the end of the path of our allocated routes pub fn get_used_end_node_count(&self, node_ids: &TypedKeySet) -> usize { node_ids.iter().fold(0usize, |acc, k| { - acc + self.used_end_nodes.get(&k.key).cloned().unwrap_or_default() + acc + self + .used_end_nodes + .get(&k.value) + .cloned() + .unwrap_or_default() }) } @@ -135,7 +139,7 @@ impl RouteSpecStoreCache { // also store in id by key table for private_route in rprinfo.get_private_routes() { self.remote_private_routes_by_key - .insert(private_route.public_key.key, id.clone()); + .insert(private_route.public_key.value, id.clone()); } let mut dead = None; @@ -149,9 +153,9 @@ impl RouteSpecStoreCache { // Follow the same logic as 'remove_remote_private_route' here for dead_private_route in dead_rpri.get_private_routes() { self.remote_private_routes_by_key - .remove(&dead_private_route.public_key.key) + .remove(&dead_private_route.public_key.value) .unwrap(); - self.invalidate_compiled_route_cache(&dead_private_route.public_key.key); + self.invalidate_compiled_route_cache(&dead_private_route.public_key.value); } self.dead_remote_routes.push(dead_id); } @@ -261,9 +265,9 @@ impl RouteSpecStoreCache { }; for private_route in rprinfo.get_private_routes() { self.remote_private_routes_by_key - .remove(&private_route.public_key.key) + .remove(&private_route.public_key.value) .unwrap(); - self.invalidate_compiled_route_cache(&private_route.public_key.key); + self.invalidate_compiled_route_cache(&private_route.public_key.value); } self.dead_remote_routes.push(id); true @@ -272,7 +276,7 @@ impl RouteSpecStoreCache { /// Stores a compiled 'safety + private' route so we don't have to compile it again later pub fn add_to_compiled_route_cache(&mut self, pr_pubkey: PublicKey, safety_route: SafetyRoute) { let key = CompiledRouteCacheKey { - sr_pubkey: safety_route.public_key.key, + sr_pubkey: safety_route.public_key.value, pr_pubkey, }; diff --git a/veilid-core/src/routing_table/routing_table_inner.rs b/veilid-core/src/routing_table/routing_table_inner.rs index efd8e981..39039f03 100644 --- a/veilid-core/src/routing_table/routing_table_inner.rs +++ b/veilid-core/src/routing_table/routing_table_inner.rs @@ -605,14 +605,14 @@ impl RoutingTableInner { // Remove any old node id for this crypto kind let bucket_index = self.unlocked_inner.calculate_bucket_index(&old_node_id); let bucket = self.get_bucket_mut(bucket_index); - bucket.remove_entry(&old_node_id.key); + bucket.remove_entry(&old_node_id.value); self.unlocked_inner.kick_queue.lock().insert(bucket_index); } // Bucket the entry appropriately let bucket_index = self.unlocked_inner.calculate_bucket_index(node_id); let bucket = self.get_bucket_mut(bucket_index); - bucket.add_existing_entry(node_id.key, entry.clone()); + bucket.add_existing_entry(node_id.value, entry.clone()); // Kick bucket self.unlocked_inner.kick_queue.lock().insert(bucket_index); @@ -649,7 +649,7 @@ impl RoutingTableInner { } let bucket_index = self.unlocked_inner.calculate_bucket_index(node_id); let bucket = self.get_bucket(bucket_index); - if let Some(entry) = bucket.entry(&node_id.key) { + if let Some(entry) = bucket.entry(&node_id.value) { // Best entry is the first one in sorted order that exists from the node id list // Everything else that matches will be overwritten in the bucket and the // existing noderefs will eventually unref and drop the old unindexed bucketentry @@ -680,7 +680,7 @@ impl RoutingTableInner { let first_node_id = node_ids[0]; let bucket_entry = self.unlocked_inner.calculate_bucket_index(&first_node_id); let bucket = self.get_bucket_mut(bucket_entry); - let new_entry = bucket.add_new_entry(first_node_id.key); + let new_entry = bucket.add_new_entry(first_node_id.value); self.unlocked_inner.kick_queue.lock().insert(bucket_entry); // Update the other bucket entries with the remaining node ids @@ -723,7 +723,7 @@ impl RoutingTableInner { let bucket_index = self.unlocked_inner.calculate_bucket_index(&node_id); let bucket = self.get_bucket(bucket_index); bucket - .entry(&node_id.key) + .entry(&node_id.value) .map(|e| NodeRef::new(outer_self, e, None)) } @@ -760,7 +760,7 @@ impl RoutingTableInner { } let bucket_entry = self.unlocked_inner.calculate_bucket_index(&node_id); let bucket = self.get_bucket(bucket_entry); - bucket.entry(&node_id.key).map(f) + bucket.entry(&node_id.value).map(f) } /// Shortcut function to add a node to our routing table if it doesn't exist @@ -1174,8 +1174,8 @@ impl RoutingTableInner { // since multiple cryptosystems are in use, the distance for a key is the shortest // distance to that key over all supported cryptosystems - let da = vcrypto.distance(&a_key.key, &node_id.key); - let db = vcrypto.distance(&b_key.key, &node_id.key); + let da = vcrypto.distance(&a_key.value, &node_id.value); + let db = vcrypto.distance(&b_key.value, &node_id.value); da.cmp(&db) }; diff --git a/veilid-core/src/rpc_processor/coders/typed_key.rs b/veilid-core/src/rpc_processor/coders/typed_key.rs index 1056c042..d6908b94 100644 --- a/veilid-core/src/rpc_processor/coders/typed_key.rs +++ b/veilid-core/src/rpc_processor/coders/typed_key.rs @@ -15,5 +15,5 @@ pub fn decode_typed_key(typed_key: &veilid_capnp::typed_key::Reader) -> Result match q.respond_to() { RespondTo::Sender => None, - RespondTo::PrivateRoute(pr) => Some(pr.public_key.key), + RespondTo::PrivateRoute(pr) => Some(pr.public_key.value), }, RPCOperationKind::Statement(_) | RPCOperationKind::Answer(_) => None, }; diff --git a/veilid-core/src/rpc_processor/rpc_app_call.rs b/veilid-core/src/rpc_processor/rpc_app_call.rs index de3b8fba..4d6b78c4 100644 --- a/veilid-core/src/rpc_processor/rpc_app_call.rs +++ b/veilid-core/src/rpc_processor/rpc_app_call.rs @@ -60,7 +60,7 @@ impl RPCProcessor { let sender = msg .opt_sender_nr .as_ref() - .map(|nr| nr.node_ids().get(crypto_kind).unwrap().key); + .map(|nr| nr.node_ids().get(crypto_kind).unwrap().value); // Register a waiter for this app call let id = msg.operation.op_id(); diff --git a/veilid-core/src/rpc_processor/rpc_app_message.rs b/veilid-core/src/rpc_processor/rpc_app_message.rs index d690e02e..0150a205 100644 --- a/veilid-core/src/rpc_processor/rpc_app_message.rs +++ b/veilid-core/src/rpc_processor/rpc_app_message.rs @@ -37,7 +37,7 @@ impl RPCProcessor { let sender = msg .opt_sender_nr .as_ref() - .map(|nr| nr.node_ids().get(crypto_kind).unwrap().key); + .map(|nr| nr.node_ids().get(crypto_kind).unwrap().value); // Pass the message up through the update callback let message = app_message.message; diff --git a/veilid-core/src/rpc_processor/rpc_route.rs b/veilid-core/src/rpc_processor/rpc_route.rs index c2227f7f..7a944d82 100644 --- a/veilid-core/src/rpc_processor/rpc_route.rs +++ b/veilid-core/src/rpc_processor/rpc_route.rs @@ -111,7 +111,7 @@ impl RPCProcessor { // xxx: punish nodes that send messages that fail to decrypt eventually? How to do this for safety routes? let node_id_secret = self.routing_table.node_id_secret(remote_sr_pubkey.kind); let dh_secret = vcrypto - .cached_dh(&remote_sr_pubkey.key, &node_id_secret) + .cached_dh(&remote_sr_pubkey.value, &node_id_secret) .map_err(RPCError::protocol)?; let body = match vcrypto.decrypt_aead( &routed_operation.data, @@ -131,7 +131,7 @@ impl RPCProcessor { // Pass message to RPC system self.enqueue_safety_routed_message( detail, - remote_sr_pubkey.key, + remote_sr_pubkey.value, routed_operation.sequencing, body, ) @@ -166,7 +166,7 @@ impl RPCProcessor { ( rsd.secret_key, SafetySpec { - preferred_route: rss.get_route_id_for_key(&pr_pubkey.key), + preferred_route: rss.get_route_id_for_key(&pr_pubkey.value), hop_count: rssd.hop_count(), stability: rssd.get_stability(), sequencing: routed_operation.sequencing, @@ -181,7 +181,7 @@ impl RPCProcessor { // Now that things are valid, decrypt the routed operation with DEC(nonce, DH(the SR's public key, the PR's (or node's) secret) // xxx: punish nodes that send messages that fail to decrypt eventually. How to do this for private routes? let dh_secret = vcrypto - .cached_dh(&remote_sr_pubkey.key, &secret_key) + .cached_dh(&remote_sr_pubkey.value, &secret_key) .map_err(RPCError::protocol)?; let body = vcrypto .decrypt_aead( @@ -197,8 +197,8 @@ impl RPCProcessor { // Pass message to RPC system self.enqueue_private_routed_message( detail, - remote_sr_pubkey.key, - pr_pubkey.key, + remote_sr_pubkey.value, + pr_pubkey.value, safety_spec, body, ) @@ -315,7 +315,7 @@ impl RPCProcessor { // Decrypt the blob with DEC(nonce, DH(the PR's public key, this hop's secret) let node_id_secret = self.routing_table.node_id_secret(crypto_kind); let dh_secret = vcrypto - .cached_dh(&pr_pubkey.key, &node_id_secret) + .cached_dh(&pr_pubkey.value, &node_id_secret) .map_err(RPCError::protocol)?; let dec_blob_data = match vcrypto.decrypt_aead( &route_hop_data.blob, @@ -347,7 +347,7 @@ impl RPCProcessor { let node_id = self.routing_table.node_id(crypto_kind); let node_id_secret = self.routing_table.node_id_secret(crypto_kind); let sig = vcrypto - .sign(&node_id.key, &node_id_secret, &route_operation.data) + .sign(&node_id.value, &node_id_secret, &route_operation.data) .map_err(RPCError::internal)?; route_operation.signatures.push(sig); } @@ -394,7 +394,7 @@ impl RPCProcessor { // Decrypt the blob with DEC(nonce, DH(the SR's public key, this hop's secret) let node_id_secret = self.routing_table.node_id_secret(crypto_kind); let dh_secret = vcrypto - .cached_dh(&route.safety_route.public_key.key, &node_id_secret) + .cached_dh(&route.safety_route.public_key.value, &node_id_secret) .map_err(RPCError::protocol)?; let mut dec_blob_data = vcrypto .decrypt_aead( diff --git a/veilid-core/src/tests/common/test_veilid_core.rs b/veilid-core/src/tests/common/test_veilid_core.rs index 41e670d2..38fd7890 100644 --- a/veilid-core/src/tests/common/test_veilid_core.rs +++ b/veilid-core/src/tests/common/test_veilid_core.rs @@ -71,7 +71,7 @@ pub async fn test_signed_node_info() { let sni = SignedDirectNodeInfo::make_signatures( crypto.clone(), - vec![TypedKeyPair::new(ck, pkey, skey)], + vec![TypedKeyPair::new(ck, KeyPair::new(pkey, skey))], node_info.clone(), ) .unwrap(); @@ -107,7 +107,7 @@ pub async fn test_signed_node_info() { let sni2 = SignedRelayedNodeInfo::make_signatures( crypto.clone(), - vec![TypedKeyPair::new(ck, pkey2, skey2)], + vec![TypedKeyPair::new(ck, KeyPair::new(pkey2, skey2))], node_info2.clone(), tks.clone(), sni.clone(), diff --git a/veilid-core/src/veilid_api/types.rs b/veilid-core/src/veilid_api/types.rs index 5bae479c..2f728e2d 100644 --- a/veilid-core/src/veilid_api/types.rs +++ b/veilid-core/src/veilid_api/types.rs @@ -1902,11 +1902,10 @@ impl SignedDirectNodeInfo { ) -> Result { let timestamp = get_aligned_timestamp(); let node_info_bytes = Self::make_signature_bytes(&node_info, timestamp)?; - let typed_signatures = crypto.generate_signatures( - &node_info_bytes, - &typed_key_pairs, - TypedSignature::from_pair_sig, - )?; + let typed_signatures = + crypto.generate_signatures(&node_info_bytes, &typed_key_pairs, |kp, s| { + TypedSignature::new(kp.kind, s) + })?; Ok(Self { node_info, timestamp, @@ -1997,11 +1996,10 @@ impl SignedRelayedNodeInfo { let timestamp = get_aligned_timestamp(); let node_info_bytes = Self::make_signature_bytes(&node_info, &relay_ids, &relay_info, timestamp)?; - let typed_signatures = crypto.generate_signatures( - &node_info_bytes, - &typed_key_pairs, - TypedSignature::from_pair_sig, - )?; + let typed_signatures = + crypto.generate_signatures(&node_info_bytes, &typed_key_pairs, |kp, s| { + TypedSignature::new(kp.kind, s) + })?; Ok(Self { node_info, relay_ids, diff --git a/veilid-core/src/veilid_config.rs b/veilid-core/src/veilid_config.rs index a307e527..f3045a8a 100644 --- a/veilid-core/src/veilid_config.rs +++ b/veilid-core/src/veilid_config.rs @@ -613,38 +613,70 @@ impl VeilidConfig { cb: ConfigCallback, update_cb: UpdateCallback, ) -> Result<(), VeilidAPIError> { - macro_rules! get_config { - ($key:expr) => { - let keyname = &stringify!($key)[6..]; - let v = cb(keyname.to_owned())?; - $key = match v.downcast() { - Ok(v) => *v, - Err(_) => { - apibail_generic!(format!("incorrect type for key {}", keyname)) - } - }; - }; - } - macro_rules! get_config_indexed { - ($key:expr, $index:expr, $subkey:tt) => { - let keyname = format!( - "{}[{}].{}", - &stringify!($key)[6..], - $index, - &stringify!($subkey) - ); - let v = cb(keyname.to_owned())?; - $key.entry($index).or_default().$subkey = match v.downcast() { - Ok(v) => *v, - Err(_) => { - apibail_generic!(format!("incorrect type for key {}", keyname)) - } - }; - }; - } - self.update_cb = Some(update_cb); self.with_mut(|inner| { + // Simple config transformation + macro_rules! get_config { + ($key:expr) => { + let keyname = &stringify!($key)[6..]; + let v = cb(keyname.to_owned())?; + $key = match v.downcast() { + Ok(v) => *v, + Err(_) => { + apibail_generic!(format!("incorrect type for key {}", keyname)) + } + }; + }; + } + // More complicated optional fields for node ids + macro_rules! get_config_node_ids { + () => { + let keys = cb("network.routing_table.node_id".to_owned())?; + let keys: Option = match keys.downcast() { + Ok(v) => *v, + Err(_) => { + apibail_generic!( + "incorrect type for key 'network.routing_table.node_id'".to_owned() + ) + } + }; + let keys = keys.unwrap_or_default(); + + let secrets = cb("network.routing_table.node_id_secret".to_owned())?; + let secrets: Option = match secrets.downcast() { + Ok(v) => *v, + Err(_) => { + apibail_generic!( + "incorrect type for key 'network.routing_table.node_id_secret'" + .to_owned() + ) + } + }; + let secrets = secrets.unwrap_or_default(); + + for ck in VALID_CRYPTO_KINDS { + if let Some(key) = keys.get(ck) { + inner + .network + .routing_table + .node_ids + .entry(ck) + .or_default() + .node_id = Some(key.value); + } + if let Some(secret) = secrets.get(ck) { + inner + .network + .routing_table + .node_ids + .entry(ck) + .or_default() + .node_id_secret = Some(secret.value); + } + } + }; + } + get_config!(inner.program_name); get_config!(inner.namespace); get_config!(inner.capabilities.protocol_udp); @@ -670,11 +702,7 @@ impl VeilidConfig { get_config!(inner.network.max_connection_frequency_per_min); get_config!(inner.network.client_whitelist_timeout_ms); get_config!(inner.network.reverse_connection_receipt_time_ms); - get_config!(inner.network.hole_punch_receipt_time_ms); - for ck in VALID_CRYPTO_KINDS { - get_config_indexed!(inner.network.routing_table.node_ids, ck, node_id); - get_config_indexed!(inner.network.routing_table.node_ids, ck, node_id_secret); - } + get_config_node_ids!(); get_config!(inner.network.routing_table.bootstrap); get_config!(inner.network.routing_table.limit_over_attached); get_config!(inner.network.routing_table.limit_fully_attached); diff --git a/veilid-flutter/lib/veilid.dart b/veilid-flutter/lib/veilid.dart index 919103d5..dac8d6d7 100644 --- a/veilid-flutter/lib/veilid.dart +++ b/veilid-flutter/lib/veilid.dart @@ -1818,19 +1818,19 @@ Sequencing sequencingFromJson(String j) { } ////////////////////////////////////// -/// KeyBlob -class KeyBlob { - final String key; +/// RouteBlob +class RouteBlob { + final String routeId; final Uint8List blob; - KeyBlob(this.key, this.blob); + RouteBlob(this.routeId, this.blob); - KeyBlob.fromJson(dynamic json) - : key = json['key'], + RouteBlob.fromJson(dynamic json) + : routeId = json['route_id'], blob = base64UrlNoPadDecode(json['blob']); Map get json { - return {'key': key, 'blob': base64UrlNoPadEncode(blob)}; + return {'route_id': routeId, 'blob': base64UrlNoPadEncode(blob)}; } } @@ -1918,8 +1918,8 @@ abstract class Veilid { Future routingContext(); // Private route allocation - Future newPrivateRoute(); - Future newCustomPrivateRoute( + Future newPrivateRoute(); + Future newCustomPrivateRoute( Stability stability, Sequencing sequencing); Future importRemotePrivateRoute(Uint8List blob); Future releasePrivateRoute(String key); diff --git a/veilid-flutter/lib/veilid_ffi.dart b/veilid-flutter/lib/veilid_ffi.dart index 4af42a26..f6bee244 100644 --- a/veilid-flutter/lib/veilid_ffi.dart +++ b/veilid-flutter/lib/veilid_ffi.dart @@ -847,21 +847,21 @@ class VeilidFFI implements Veilid { } @override - Future newPrivateRoute() { + Future newPrivateRoute() { final recvPort = ReceivePort("new_private_route"); final sendPort = recvPort.sendPort; _newPrivateRoute(sendPort.nativePort); - return processFutureJson(KeyBlob.fromJson, recvPort.first); + return processFutureJson(RouteBlob.fromJson, recvPort.first); } @override - Future newCustomPrivateRoute( + Future newCustomPrivateRoute( Stability stability, Sequencing sequencing) async { final recvPort = ReceivePort("new_custom_private_route"); final sendPort = recvPort.sendPort; _newCustomPrivateRoute(sendPort.nativePort, stability.json.toNativeUtf8(), sequencing.json.toNativeUtf8()); - final keyblob = await processFutureJson(KeyBlob.fromJson, recvPort.first); + final keyblob = await processFutureJson(RouteBlob.fromJson, recvPort.first); return keyblob; } diff --git a/veilid-flutter/lib/veilid_js.dart b/veilid-flutter/lib/veilid_js.dart index fbfcecba..963a5fca 100644 --- a/veilid-flutter/lib/veilid_js.dart +++ b/veilid-flutter/lib/veilid_js.dart @@ -65,15 +65,15 @@ class VeilidRoutingContextJS implements VeilidRoutingContext { var encodedRequest = base64UrlNoPadEncode(request); return base64UrlNoPadDecode(await _wrapApiPromise(js_util.callMethod( - wasm, "routing_context_app_call", [_ctx.id, encodedRequest]))); + wasm, "routing_context_app_call", [_ctx.id, target, encodedRequest]))); } @override Future appMessage(String target, Uint8List message) { var encodedMessage = base64UrlNoPadEncode(message); - return _wrapApiPromise(js_util.callMethod( - wasm, "routing_context_app_message", [_ctx.id, encodedMessage])); + return _wrapApiPromise(js_util.callMethod(wasm, + "routing_context_app_message", [_ctx.id, target, encodedMessage])); } } @@ -267,14 +267,14 @@ class VeilidJS implements Veilid { } @override - Future newPrivateRoute() async { + Future newPrivateRoute() async { Map blobJson = jsonDecode(await _wrapApiPromise( js_util.callMethod(wasm, "new_private_route", []))); - return KeyBlob.fromJson(blobJson); + return RouteBlob.fromJson(blobJson); } @override - Future newCustomPrivateRoute( + Future newCustomPrivateRoute( Stability stability, Sequencing sequencing) async { var stabilityString = jsonEncode(stability, toEncodable: veilidApiToEncodable); @@ -284,7 +284,7 @@ class VeilidJS implements Veilid { Map blobJson = jsonDecode(await _wrapApiPromise(js_util .callMethod( wasm, "new_private_route", [stabilityString, sequencingString]))); - return KeyBlob.fromJson(blobJson); + return RouteBlob.fromJson(blobJson); } @override diff --git a/veilid-flutter/rust/src/dart_ffi.rs b/veilid-flutter/rust/src/dart_ffi.rs index 84f975ee..bbc4a766 100644 --- a/veilid-flutter/rust/src/dart_ffi.rs +++ b/veilid-flutter/rust/src/dart_ffi.rs @@ -10,12 +10,14 @@ use opentelemetry::*; use opentelemetry_otlp::WithExportConfig; use parking_lot::Mutex; use serde::*; +use std::str::FromStr; use std::collections::BTreeMap; use std::os::raw::c_char; use std::sync::Arc; use tracing::*; use tracing_subscriber::prelude::*; use tracing_subscriber::*; +use veilid_core::Encodable as _; // Globals lazy_static! { @@ -56,6 +58,29 @@ define_string_destructor!(free_string); type APIResult = Result; const APIRESULT_VOID: APIResult<()> = APIResult::Ok(()); +// Parse target +async fn parse_target(s: String) -> APIResult { + + // Is this a route id? + if let Ok(rrid) = veilid_core::RouteId::from_str(&s) { + let veilid_api = get_veilid_api().await?; + let routing_table = veilid_api.routing_table()?; + let rss = routing_table.route_spec_store(); + + // Is this a valid remote route id? (can't target allocated routes) + if rss.is_route_id_remote(&rrid) { + return Ok(veilid_core::Target::PrivateRoute(rrid)); + } + } + + // Is this a node id? + if let Ok(nid) = veilid_core::PublicKey::from_str(&s) { + return Ok(veilid_core::Target::NodeId(nid)); + } + + Err(veilid_core::VeilidAPIError::invalid_target()) +} + ///////////////////////////////////////// // FFI-specific @@ -92,8 +117,8 @@ pub struct VeilidFFIConfig { } #[derive(Debug, Deserialize, Serialize)] -pub struct VeilidFFIKeyBlob { - pub key: veilid_core::TypedKey, +pub struct VeilidFFIRouteBlob { + pub route_id: veilid_core::RouteId, #[serde(with = "veilid_core::json_as_base64")] pub blob: Vec, } @@ -415,10 +440,10 @@ pub extern "C" fn routing_context_with_sequencing(id: u32, sequencing: FfiStr) - new_id } + #[no_mangle] pub extern "C" fn routing_context_app_call(port: i64, id: u32, target: FfiStr, request: FfiStr) { - let target: veilid_core::TypedKey = - veilid_core::deserialize_opt_json(target.into_opt_string()).unwrap(); + let target_string: String = target.into_opt_string().unwrap(); let request: Vec = data_encoding::BASE64URL_NOPAD .decode( request.into_opt_string() @@ -427,10 +452,6 @@ pub extern "C" fn routing_context_app_call(port: i64, id: u32, target: FfiStr, r ) .unwrap(); DartIsolateWrapper::new(port).spawn_result(async move { - let veilid_api = get_veilid_api().await?; - let routing_table = veilid_api.routing_table()?; - let rss = routing_table.route_spec_store(); - let routing_context = { let rc = ROUTING_CONTEXTS.lock(); let Some(routing_context) = rc.get(&id) else { @@ -439,12 +460,7 @@ pub extern "C" fn routing_context_app_call(port: i64, id: u32, target: FfiStr, r routing_context.clone() }; - let target = if rss.get_remote_private_route(&target).is_some() { - veilid_core::Target::PrivateRoute(target) - } else { - veilid_core::Target::NodeId(veilid_core::NodeId::new(target)) - }; - + let target = parse_target(target_string).await?; let answer = routing_context.app_call(target, request).await?; let answer = data_encoding::BASE64URL_NOPAD.encode(&answer); APIResult::Ok(answer) @@ -453,8 +469,7 @@ pub extern "C" fn routing_context_app_call(port: i64, id: u32, target: FfiStr, r #[no_mangle] pub extern "C" fn routing_context_app_message(port: i64, id: u32, target: FfiStr, message: FfiStr) { - let target: veilid_core::TypedKey = - veilid_core::deserialize_opt_json(target.into_opt_string()).unwrap(); + let target_string: String = target.into_opt_string().unwrap(); let message: Vec = data_encoding::BASE64URL_NOPAD .decode( message.into_opt_string() @@ -462,11 +477,7 @@ pub extern "C" fn routing_context_app_message(port: i64, id: u32, target: FfiStr .as_bytes(), ) .unwrap(); - DartIsolateWrapper::new(port).spawn_result(async move { - let veilid_api = get_veilid_api().await?; - let routing_table = veilid_api.routing_table()?; - let rss = routing_table.route_spec_store(); - + DartIsolateWrapper::new(port).spawn_result(async move { let routing_context = { let rc = ROUTING_CONTEXTS.lock(); let Some(routing_context) = rc.get(&id) else { @@ -475,12 +486,7 @@ pub extern "C" fn routing_context_app_message(port: i64, id: u32, target: FfiStr routing_context.clone() }; - let target = if rss.get_remote_private_route(&target).is_some() { - veilid_core::Target::PrivateRoute(target) - } else { - veilid_core::Target::NodeId(veilid_core::NodeId::new(target)) - }; - + let target = parse_target(target_string).await?; routing_context.app_message(target, message).await?; APIRESULT_VOID }); @@ -491,9 +497,9 @@ pub extern "C" fn new_private_route(port: i64) { DartIsolateWrapper::new(port).spawn_result_json(async move { let veilid_api = get_veilid_api().await?; - let (key, blob) = veilid_api.new_private_route().await?; + let (route_id, blob) = veilid_api.new_private_route().await?; - let keyblob = VeilidFFIKeyBlob { key, blob }; + let keyblob = VeilidFFIRouteBlob { route_id, blob }; APIResult::Ok(keyblob) }); @@ -509,11 +515,11 @@ pub extern "C" fn new_custom_private_route(port: i64, stability: FfiStr, sequenc DartIsolateWrapper::new(port).spawn_result_json(async move { let veilid_api = get_veilid_api().await?; - let (key, blob) = veilid_api - .new_custom_private_route(stability, sequencing) + let (route_id, blob) = veilid_api + .new_custom_private_route(&veilid_core::VALID_CRYPTO_KINDS, stability, sequencing) .await?; - let keyblob = VeilidFFIKeyBlob { key, blob }; + let keyblob = VeilidFFIRouteBlob { route_id, blob }; APIResult::Ok(keyblob) }); @@ -531,19 +537,19 @@ pub extern "C" fn import_remote_private_route(port: i64, blob: FfiStr) { DartIsolateWrapper::new(port).spawn_result(async move { let veilid_api = get_veilid_api().await?; - let key = veilid_api.import_remote_private_route(blob)?; + let route_id = veilid_api.import_remote_private_route(blob)?; - APIResult::Ok(key.encode()) + APIResult::Ok(route_id.encode()) }); } #[no_mangle] pub extern "C" fn release_private_route(port: i64, key: FfiStr) { - let key: veilid_core::TypedKey = + let route_id: veilid_core::RouteId = veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); DartIsolateWrapper::new(port).spawn_result(async move { let veilid_api = get_veilid_api().await?; - veilid_api.release_private_route(&key)?; + veilid_api.release_private_route(route_id)?; APIRESULT_VOID }); } diff --git a/veilid-server/src/cmdline.rs b/veilid-server/src/cmdline.rs index 7985a797..9f37de05 100644 --- a/veilid-server/src/cmdline.rs +++ b/veilid-server/src/cmdline.rs @@ -4,7 +4,7 @@ use clap::{Arg, ArgMatches, Command}; use std::ffi::OsStr; use std::path::Path; use std::str::FromStr; -use veilid_core::{SecretKey, TypedKeySet}; +use veilid_core::{TypedKeySet, TypedSecretSet}; fn do_clap_matches(default_config_path: &OsStr) -> Result { let matches = Command::new("veilid-server") @@ -78,17 +78,21 @@ fn do_clap_matches(default_config_path: &OsStr) -> Result EyreResult<(Settings, ArgMatches)> { let tks = TypedKeySet::from_str(v).wrap_err("failed to decode node id set from command line")?; - let buffer = rpassword::prompt_password("Enter secret key (will not echo): ") + let buffer = rpassword::prompt_password("Enter secret key set (will not echo): ") .wrap_err("invalid secret key")?; let buffer = buffer.trim().to_string(); - let s = SecretKey::try_decode(&buffer)?; + let tss = TypedSecretSet::from_str(&buffer).wrap_err("failed to decode secret set")?; - settingsrw.core.network.node_id = Some(k); - settingsrw.core.network.node_id_secret = Some(s); + settingsrw.core.network.routing_table.node_id = Some(tks); + settingsrw.core.network.routing_table.node_id_secret = Some(tss); } if matches.occurrences_of("bootstrap") != 0 { @@ -264,7 +268,7 @@ pub fn process_command_line() -> EyreResult<(Settings, ArgMatches)> { bail!("value not specified for bootstrap"); } }; - settingsrw.core.network.bootstrap = bootstrap_list; + settingsrw.core.network.routing_table.bootstrap = bootstrap_list; } #[cfg(feature = "rt-tokio")] diff --git a/veilid-server/src/main.rs b/veilid-server/src/main.rs index 7dd76d54..ce2c4e24 100644 --- a/veilid-server/src/main.rs +++ b/veilid-server/src/main.rs @@ -20,6 +20,7 @@ use color_eyre::eyre::{bail, ensure, eyre, Result as EyreResult, WrapErr}; use server::*; use tools::*; use tracing::*; +use veilid_core::Encodable as _; use veilid_logs::*; #[allow(clippy::all)] @@ -42,8 +43,8 @@ fn main() -> EyreResult<()> { } // --- Generate DHT Key --- - if matches.occurrences_of("generate-dht-key") != 0 { - let (key, secret) = veilid_core::generate_secret(); + if matches.occurrences_of("generate-key-pair") != 0 { + let (key, secret) = veilid_core::vld0_generate_keypair(); println!("Public: {}\nSecret: {}", key.encode(), secret.encode()); return Ok(()); } diff --git a/veilid-server/src/settings.rs b/veilid-server/src/settings.rs index a2e2565f..4677e777 100644 --- a/veilid-server/src/settings.rs +++ b/veilid-server/src/settings.rs @@ -517,7 +517,7 @@ pub struct Dht { #[derive(Debug, Deserialize, Serialize)] pub struct RoutingTable { pub node_id: Option, - pub node_id_secret: Option, + pub node_id_secret: Option, pub bootstrap: Vec, pub limit_over_attached: u32, pub limit_fully_attached: u32, @@ -857,6 +857,7 @@ impl Settings { } }}; } + set_config_value!(inner.daemon.enabled, value); set_config_value!(inner.client_api.enabled, value); set_config_value!(inner.client_api.listen_address, value); @@ -1057,11 +1058,11 @@ impl Settings { Ok(Box::new(inner.core.network.hole_punch_receipt_time_ms)) } "network.routing_table.node_id" => { - Ok(Box::new(inner.core.network.routing_table.node_id)) - } - "network.routing_table.node_id_secret" => { - Ok(Box::new(inner.core.network.routing_table.node_id_secret)) + Ok(Box::new(inner.core.network.routing_table.node_id.clone())) } + "network.routing_table.node_id_secret" => Ok(Box::new( + inner.core.network.routing_table.node_id_secret.clone(), + )), "network.routing_table.bootstrap" => { Ok(Box::new(inner.core.network.routing_table.bootstrap.clone())) }