use std::path::Path;
use ed25519_dalek::{SigningKey, VerifyingKey};
use rand::rngs::OsRng;
use pim_core::NodeId;
#[derive(Debug)]
pub struct Identity {
signing_key: SigningKey,
verifying_key: VerifyingKey,
node_id: NodeId,
}
impl Identity {
pub fn generate() -> Self {
let signing_key = SigningKey::generate(&mut OsRng);
Self::from_signing_key(signing_key)
}
fn from_signing_key(signing_key: SigningKey) -> Self {
let verifying_key = signing_key.verifying_key();
let node_id = NodeId::from_public_key(&verifying_key.to_bytes());
Self {
signing_key,
verifying_key,
node_id,
}
}
pub fn save(&self, path: &Path) -> Result<(), std::io::Error> {
self.save_internal(path, true)
}
fn save_internal(&self, path: &Path, overwrite: bool) -> Result<(), std::io::Error> {
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}
let mut options = std::fs::OpenOptions::new();
options.write(true);
if overwrite {
options.create(true).truncate(true);
} else {
options.create_new(true);
}
#[cfg(unix)]
{
use std::os::unix::fs::OpenOptionsExt;
options.mode(0o600);
}
use std::io::Write;
let mut file = options.open(path)?;
file.write_all(&self.signing_key.to_bytes())
}
pub fn load(path: &Path) -> Result<Self, std::io::Error> {
let bytes = std::fs::read(path)?;
if bytes.len() != 32 {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("key file must be 32 bytes, got {}", bytes.len()),
));
}
let mut seed = [0u8; 32];
seed.copy_from_slice(&bytes);
let signing_key = SigningKey::from_bytes(&seed);
Ok(Self::from_signing_key(signing_key))
}
pub fn load_or_generate(path: &Path) -> Result<Self, std::io::Error> {
match Self::load(path) {
Ok(identity) => Ok(identity),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
let identity = Self::generate();
match identity.save_internal(path, false) {
Ok(()) => Ok(identity),
Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {
Self::load(path)
}
Err(e) => Err(e),
}
}
Err(e) => Err(e),
}
}
pub fn node_id(&self) -> NodeId {
self.node_id
}
pub fn signing_key(&self) -> &SigningKey {
&self.signing_key
}
pub fn verifying_key(&self) -> &VerifyingKey {
&self.verifying_key
}
pub fn public_key_bytes(&self) -> [u8; 32] {
self.verifying_key.to_bytes()
}
}
#[cfg(test)]
mod tests;