122 lines
3.9 KiB
Rust
122 lines
3.9 KiB
Rust
use pem::encode as pem_encode;
|
|
use pem::parse as pem_parse;
|
|
use std::convert::TryInto;
|
|
use std::fs;
|
|
use std::io;
|
|
use std::path::PathBuf;
|
|
|
|
use crate::crc32handy;
|
|
|
|
#[derive(new)]
|
|
pub struct Curve25519Repository {
|
|
location: PathBuf,
|
|
}
|
|
|
|
impl Default for Curve25519Repository {
|
|
fn default() -> Self {
|
|
Self::new(
|
|
dirs::home_dir()
|
|
.unwrap_or(PathBuf::from("."))
|
|
.join(".blkbak"),
|
|
)
|
|
}
|
|
}
|
|
|
|
const PRIVATE_KEY_TAG: &str = "PRIVATE CURVE25519-DALEK KEY";
|
|
|
|
impl Curve25519Repository {
|
|
pub fn location_authorized_keys(&self) -> PathBuf {
|
|
self.location.join("authorized_keys")
|
|
}
|
|
pub fn location_curve25519key(&self) -> PathBuf {
|
|
self.location.join("id_curve25519.key")
|
|
}
|
|
pub fn location_curve25519pub(&self) -> PathBuf {
|
|
self.location.join("id_curve25519.pub")
|
|
}
|
|
pub fn dh_private_load(&self) -> io::Result<x25519_dalek::StaticSecret> {
|
|
let crt: pem::Pem = pem_parse(fs::read(self.location_curve25519key())?)
|
|
.map_err(|_| io::Error::from(io::ErrorKind::InvalidData))?;
|
|
if crt.tag != PRIVATE_KEY_TAG || crt.contents.len() != 32 {
|
|
Err(io::ErrorKind::InvalidData.into())
|
|
} else {
|
|
let mut arr = [0u8; 32];
|
|
arr.copy_from_slice(&crt.contents);
|
|
Ok(x25519_dalek::StaticSecret::from(arr))
|
|
}
|
|
}
|
|
|
|
pub fn dh_private_generate(&self) -> x25519_dalek::StaticSecret {
|
|
x25519_dalek::StaticSecret::new(&mut rand::rngs::OsRng::default())
|
|
}
|
|
|
|
pub fn dh_private_store(&self, key: &x25519_dalek::StaticSecret) -> io::Result<()> {
|
|
if !self.location.exists() {
|
|
fs::create_dir_all(&self.location)?;
|
|
}
|
|
let encoded = pem_encode(&pem::Pem {
|
|
tag: PRIVATE_KEY_TAG.to_string(),
|
|
contents: key.to_bytes().to_vec(),
|
|
});
|
|
fs::write(self.location_curve25519key(), encoded)?;
|
|
fs::write(
|
|
self.location_curve25519pub(),
|
|
format!(
|
|
"{} {} {}",
|
|
"blkbak-curve25519-dalek",
|
|
base64::encode(x25519_dalek::PublicKey::from(key).to_bytes()),
|
|
base16::encode_upper(&crc32handy(&key.to_bytes()).to_be_bytes()),
|
|
),
|
|
)
|
|
}
|
|
|
|
pub fn dh_private_load_generating(&self) -> io::Result<x25519_dalek::StaticSecret> {
|
|
if let Ok(loaded) = self.dh_private_load() {
|
|
Ok(loaded)
|
|
} else {
|
|
let generated = self.dh_private_generate();
|
|
self.dh_private_store(&generated)?;
|
|
Ok(generated)
|
|
}
|
|
}
|
|
|
|
pub fn dh_public_authorizeds(&self) -> Vec<(u32, x25519_dalek::PublicKey)> {
|
|
fs::read_to_string(self.location_authorized_keys())
|
|
.unwrap_or("".to_string())
|
|
.lines()
|
|
.map(|x| x.trim().split(' ').collect::<Vec<&str>>())
|
|
.filter(|x| x.len() == 3)
|
|
.map(|x| (x[0].to_string(), base64::decode(x[1]), base16::decode(x[2])))
|
|
.filter(|x| x.1.is_ok())
|
|
.filter(|x| x.2.is_ok())
|
|
.map(|x| (x.0, x.1.unwrap(), x.2.unwrap()))
|
|
.filter(|x| x.1.len() == 32)
|
|
.filter(|x| x.2.len() == 4)
|
|
.map(|x| {
|
|
(
|
|
x.0,
|
|
x.1,
|
|
u32::from_be_bytes(
|
|
x.2.split_at(std::mem::size_of::<u32>())
|
|
.0
|
|
.try_into()
|
|
.unwrap(),
|
|
),
|
|
)
|
|
})
|
|
.map(|x| {
|
|
let k: [u8; 32] = x.1.split_at(32).0.try_into().unwrap();
|
|
(x.2, x25519_dalek::PublicKey::from(k))
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
pub fn dh_public_authorized(&self, digest: u32) -> Option<x25519_dalek::PublicKey> {
|
|
self.dh_public_authorizeds()
|
|
.into_iter()
|
|
.filter(|x| x.0 == digest)
|
|
.map(|x| x.1)
|
|
.next()
|
|
}
|
|
}
|