use ed25519_compact::{KeyPair, Noise, PublicKey, Seed, Signature};
use flmodules::nodeids::U256;
use serde_derive::{Deserialize, Serialize};
use serde_with::{base64::Base64, serde_as};
use std::{
convert::TryFrom,
fmt::{Debug, Error, Formatter},
};
use thiserror::Error;
#[derive(Debug, Clone)]
pub struct ConnectionConfig {
signal_server: Option<String>,
stun_server: Option<HostLogin>,
turn_server: Option<HostLogin>,
}
impl Default for ConnectionConfig {
fn default() -> Self {
Self {
signal_server: Some("ws://localhost:8765".into()),
stun_server: Some(HostLogin {
url: "stun:stun.l.google.com:19302".into(),
login: None,
}),
turn_server: None,
}
}
}
impl ConnectionConfig {
pub fn new(
signal_server: Option<String>,
stun_server: Option<HostLogin>,
turn_server: Option<HostLogin>,
) -> Self {
Self {
signal_server,
stun_server,
turn_server,
}
}
pub fn from_signal(url: &str) -> Self {
Self {
signal_server: Some(url.into()),
stun_server: None,
turn_server: None,
}
}
pub fn signal(&self) -> String {
self.signal_server
.as_ref()
.unwrap_or(&"ws://localhost:8765".into())
.clone()
}
pub fn stun(&self) -> HostLogin {
self.stun_server
.as_ref()
.unwrap_or(&HostLogin::from_url("stun:stun.l.google.com:19302"))
.clone()
}
pub fn turn(&self) -> Option<HostLogin> {
self.turn_server.clone()
}
}
#[derive(Debug, Clone)]
pub struct HostLogin {
pub url: String,
pub login: Option<Login>,
}
impl HostLogin {
pub fn from_url(url: &str) -> Self {
Self {
url: url.into(),
login: None,
}
}
}
#[derive(Debug, Clone)]
pub struct Login {
pub user: String,
pub pass: String,
}
#[derive(Error, Debug)]
pub enum ConfigError {
#[error("Didn't find public key")]
PublicKeyMissing,
#[error("Couldn't decode")]
NoInfo,
#[error(transparent)]
DecodeToml1(#[from] toml::de::Error),
#[error(transparent)]
DecodeToml2(#[from] toml::ser::Error),
}
#[serde_as]
#[derive(Deserialize, Serialize, Clone)]
pub struct NodeInfo {
pub name: String,
pub client: String,
#[serde_as(as = "Base64")]
pub pubkey: Vec<u8>,
}
#[derive(Deserialize, Serialize, Clone)]
enum NodeInfoSave {
NodeInfoV2(NodeInfo),
}
impl NodeInfoSave {
fn to_latest(self) -> NodeInfo {
match self {
NodeInfoSave::NodeInfoV2(ni) => ni,
}
}
}
impl NodeInfo {
pub fn new(pubkey: PublicKey) -> NodeInfo {
NodeInfo {
name: names::Generator::default().next().unwrap(),
client: "libc".to_string(),
pubkey: pubkey.as_ref().to_vec(),
}
}
pub fn get_id(&self) -> U256 {
let a: [u8; PublicKey::BYTES] = self.pubkey.clone().try_into().unwrap();
U256::from(a)
}
pub fn verify(&self, msg: &[u8], sig_bytes: &[u8]) -> bool {
let pubkey = PublicKey::from_slice(&self.pubkey).unwrap();
let sig = match Signature::from_slice(sig_bytes) {
Ok(sig) => sig,
Err(_) => return false,
};
pubkey.verify(msg, &sig).is_ok()
}
pub fn decode(data: &str) -> Result<Self, ConfigError> {
if let Ok(info) = serde_yaml::from_str::<NodeInfoSave>(data) {
return Ok(info.to_latest());
}
NodeInfoV1::from_str(data)
.ok_or(ConfigError::NoInfo)
.map(|i| i.into())
}
pub fn encode(&self) -> String {
serde_yaml::to_string(&NodeInfoSave::NodeInfoV2(self.clone())).unwrap()
}
}
#[derive(Deserialize, Serialize, Clone)]
pub struct NodeInfoV1 {
pub info: String,
pub client: String,
pub pubkey: Vec<u8>,
}
impl NodeInfoV1 {
fn from_str(data: &str) -> Option<Self> {
if let Ok(info) = serde_json::from_str::<NodeInfoV1>(data) {
return Some(info);
}
if let Ok(info) = toml::from_str::<NodeInfoV1>(data) {
return Some(info);
}
None
}
}
impl From<NodeInfoV1> for NodeInfo {
fn from(ni: NodeInfoV1) -> Self {
NodeInfo {
name: ni.info,
client: ni.client,
pubkey: ni.pubkey,
}
}
}
impl TryFrom<NodeInfoToml> for NodeInfo {
type Error = ConfigError;
fn try_from(nit: NodeInfoToml) -> Result<Self, ConfigError> {
Ok(NodeInfo {
name: nit.info,
client: nit.client,
pubkey: nit.pubkey.ok_or(ConfigError::PublicKeyMissing)?,
})
}
}
impl Debug for NodeInfo {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
let pubkey: String = self.pubkey.iter().map(|b| format!("{:02x}", b)).collect();
write!(
f,
"NodeInfo: {{ info: '{}', client: '{}', pubkey: {} }}",
self.name, self.client, pubkey
)
}
}
impl PartialEq for NodeInfo {
fn eq(&self, other: &Self) -> bool {
self.get_id() == other.get_id()
}
}
#[serde_as]
#[derive(Debug, Serialize, Deserialize)]
pub struct NodeConfig {
pub info: NodeInfo,
#[serde_as(as = "Base64")]
pub keypair: Vec<u8>,
}
impl Default for NodeConfig {
fn default() -> Self {
Self::new()
}
}
impl NodeConfig {
pub fn new() -> Self {
let keypair = KeyPair::from_seed(Seed::default());
NodeConfig {
info: NodeInfo::new(keypair.pk),
keypair: keypair.as_ref().to_vec(),
}
}
pub fn encode(&self) -> String {
serde_yaml::to_string(&NodeConfigSave::NodeConfigV1(self.clone())).unwrap()
}
pub fn decode(data: &str) -> Result<Self, ConfigError> {
if let Ok(nc) = serde_yaml::from_str::<NodeConfigSave>(data) {
return Ok(nc.to_latest());
}
Self::from_toml(data)
}
pub fn sign(&self, hash: [u8; 32]) -> Vec<u8> {
let keypair = KeyPair::from_slice(&self.keypair).unwrap();
keypair.sk.sign(&hash, Some(Noise::default())).to_vec()
}
fn from_toml(data: &str) -> Result<Self, ConfigError> {
let t: Toml = if !data.is_empty() {
toml::from_str(data)?
} else {
return Ok(NodeConfig::new());
};
let nct = t.v1.unwrap();
let keypair = match nct.keypair {
Some(kp) => kp,
None => KeyPair::generate().as_ref().into(),
};
let kp = KeyPair::from_slice(&keypair).unwrap();
let our_node = match nct.our_node {
Some(mut on) => {
on.pubkey.replace(kp.pk.as_ref().to_vec());
NodeInfo::try_from(on)?
}
None => NodeInfo::new(kp.pk),
};
Ok(NodeConfig {
info: our_node,
keypair,
})
}
}
impl Clone for NodeConfig {
fn clone(&self) -> Self {
NodeConfig {
info: self.info.clone(),
keypair: self.keypair.clone(),
}
}
}
#[derive(Debug, Deserialize, Serialize)]
enum NodeConfigSave {
NodeConfigV1(NodeConfig),
}
impl NodeConfigSave {
fn to_latest(self) -> NodeConfig {
match self {
NodeConfigSave::NodeConfigV1(nc) => nc,
}
}
}
#[derive(Debug, Deserialize, Serialize)]
struct NodeConfigToml {
pub keypair: Option<Vec<u8>>,
pub our_node: Option<NodeInfoToml>,
}
impl From<&NodeConfig> for NodeConfigToml {
fn from(nc: &NodeConfig) -> Self {
NodeConfigToml {
our_node: Some((&nc.info).into()),
keypair: Some(nc.keypair.clone()),
}
}
}
#[derive(Debug, Deserialize, Serialize)]
struct NodeInfoToml {
pub id: Option<U256>,
pub info: String,
pub client: String,
pub pubkey: Option<Vec<u8>>,
}
impl From<&NodeInfo> for NodeInfoToml {
fn from(ni: &NodeInfo) -> Self {
NodeInfoToml {
id: None,
info: ni.name.clone(),
client: ni.client.clone(),
pubkey: Some(ni.pubkey.clone()),
}
}
}
#[derive(Debug, Deserialize, Serialize)]
struct Toml {
v1: Option<NodeConfigToml>,
}
#[cfg(test)]
mod tests {
use flarch::start_logging;
use super::*;
#[test]
fn save_load() -> Result<(), ConfigError> {
start_logging();
let nc = NodeConfig::new();
let nc_str = nc.encode();
log::debug!("NodeConfig is: {nc_str}");
let nc_clone = NodeConfig::decode(&nc_str)?;
assert_eq!(nc.keypair, nc_clone.keypair);
assert_eq!(nc.info.pubkey, nc_clone.info.pubkey);
Ok(())
}
}