remote-block-device-backup/remote-block-device-backup-.../src/keys.rs

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()
}
}