280 lines
8.7 KiB
Rust
Raw Normal View History

2021-11-22 11:28:30 -05:00
use super::key::*;
use crate::intf::*;
use crate::xx::*;
use crate::*;
use chacha20poly1305 as ch;
use chacha20poly1305::aead::{AeadInPlace, NewAead};
use core::convert::TryInto;
use curve25519_dalek as cd;
use ed25519_dalek as ed;
2022-03-20 10:52:03 -04:00
use hashlink::linked_hash_map::Entry;
use hashlink::LruCache;
2021-11-22 11:28:30 -05:00
use serde::{Deserialize, Serialize};
use x25519_dalek as xd;
pub type SharedSecret = [u8; 32];
pub type Nonce = [u8; 24];
const DH_CACHE_SIZE: usize = 1024;
pub const ENCRYPTION_OVERHEAD: usize = 16;
2022-03-20 10:52:03 -04:00
#[derive(Serialize, Deserialize, PartialEq, Eq, Hash)]
struct DHCacheKey {
2021-11-22 11:28:30 -05:00
key: DHTKey,
secret: DHTKeySecret,
2022-03-20 10:52:03 -04:00
}
#[derive(Serialize, Deserialize)]
struct DHCacheValue {
2021-11-22 11:28:30 -05:00
shared_secret: SharedSecret,
}
2022-03-20 10:52:03 -04:00
type DHCache = LruCache<DHCacheKey, DHCacheValue>;
2021-11-22 11:28:30 -05:00
fn cache_to_bytes(cache: &DHCache) -> Vec<u8> {
let cnt: usize = cache.len();
let mut out: Vec<u8> = Vec::with_capacity(cnt * (32 + 32 + 32));
for e in cache.iter() {
2022-03-20 10:52:03 -04:00
out.extend(&e.0.key.bytes);
out.extend(&e.0.secret.bytes);
out.extend(&e.1.shared_secret);
2021-11-22 11:28:30 -05:00
}
let mut rev: Vec<u8> = Vec::with_capacity(out.len());
for d in out.chunks(32 + 32 + 32).rev() {
rev.extend(d);
}
rev
}
fn bytes_to_cache(bytes: &[u8], cache: &mut DHCache) {
for d in bytes.chunks(32 + 32 + 32) {
2022-03-20 10:52:03 -04:00
let k = DHCacheKey {
2021-11-22 11:28:30 -05:00
key: DHTKey::new(d[0..32].try_into().expect("asdf")),
secret: DHTKeySecret::new(d[32..64].try_into().expect("asdf")),
2022-03-20 10:52:03 -04:00
};
let v = DHCacheValue {
2021-11-22 11:28:30 -05:00
shared_secret: d[64..96].try_into().expect("asdf"),
};
2022-03-20 10:52:03 -04:00
cache.insert(k, v);
2021-11-22 11:28:30 -05:00
}
}
struct CryptoInner {
table_store: TableStore,
node_id: DHTKey,
node_id_secret: DHTKeySecret,
dh_cache: DHCache,
flush_future: Option<SystemPinBoxFuture<()>>,
}
#[derive(Clone)]
pub struct Crypto {
config: VeilidConfig,
inner: Arc<Mutex<CryptoInner>>,
}
impl Crypto {
fn new_inner(table_store: TableStore) -> CryptoInner {
CryptoInner {
2021-11-26 11:50:49 -05:00
table_store,
2021-11-22 11:28:30 -05:00
node_id: Default::default(),
node_id_secret: Default::default(),
2022-03-20 10:52:03 -04:00
dh_cache: DHCache::new(DH_CACHE_SIZE),
2021-11-22 11:28:30 -05:00
flush_future: None,
}
}
pub fn new(config: VeilidConfig, table_store: TableStore) -> Self {
Self {
2021-11-26 11:50:49 -05:00
config,
2021-11-22 11:28:30 -05:00
inner: Arc::new(Mutex::new(Self::new_inner(table_store))),
}
}
pub async fn init(&self) -> Result<(), String> {
trace!("Crypto::init");
// make local copy of node id for easy access
2022-02-06 21:18:42 -05:00
let (table_store, node_id) = {
let mut inner = self.inner.lock();
let c = self.config.get();
inner.node_id = c.network.node_id;
inner.node_id_secret = c.network.node_id_secret;
(inner.table_store.clone(), c.network.node_id)
};
2021-11-22 11:28:30 -05:00
// load caches if they are valid for this node id
2022-02-06 21:18:42 -05:00
let mut db = table_store.open("crypto_caches", 1).await?;
2021-11-22 11:28:30 -05:00
let caches_valid = match db.load(0, b"node_id").await? {
2022-02-06 21:18:42 -05:00
Some(v) => v.as_slice() == node_id.bytes,
2021-11-22 11:28:30 -05:00
None => false,
};
if caches_valid {
2021-11-26 11:50:49 -05:00
if let Some(b) = db.load(0, b"dh_cache").await? {
2022-02-06 21:18:42 -05:00
let mut inner = self.inner.lock();
2021-11-26 11:50:49 -05:00
bytes_to_cache(&b, &mut inner.dh_cache);
}
2021-11-22 11:28:30 -05:00
} else {
drop(db);
2022-02-06 21:18:42 -05:00
table_store.delete("crypto_caches").await?;
db = table_store.open("crypto_caches", 1).await?;
db.store(0, b"node_id", &node_id.bytes).await?;
2021-11-22 11:28:30 -05:00
}
// Schedule flushing
let this = self.clone();
2022-02-06 21:18:42 -05:00
let flush_future = interval(60000, move || {
2021-11-22 11:28:30 -05:00
let this = this.clone();
async move {
if let Err(e) = this.flush().await {
warn!("flush failed: {}", e);
}
}
2022-02-06 21:18:42 -05:00
});
self.inner.lock().flush_future = Some(flush_future);
2021-11-22 11:28:30 -05:00
Ok(())
}
pub async fn flush(&self) -> Result<(), String> {
//trace!("Crypto::flush");
let (table_store, cache_bytes) = {
let inner = self.inner.lock();
let cache_bytes = cache_to_bytes(&inner.dh_cache);
(inner.table_store.clone(), cache_bytes)
};
let db = table_store.open("crypto_caches", 1).await?;
db.store(0, b"dh_cache", &cache_bytes).await?;
Ok(())
}
pub async fn terminate(&self) {
trace!("Crypto::terminate");
let flush_future = self.inner.lock().flush_future.take();
if let Some(f) = flush_future {
f.await;
}
trace!("starting termination flush");
match self.flush().await {
Ok(_) => {
trace!("finished termination flush");
}
Err(e) => {
error!("failed termination flush: {}", e);
}
};
}
2022-02-06 21:18:42 -05:00
fn ed25519_to_x25519_pk(key: &ed::PublicKey) -> Result<xd::PublicKey, String> {
2021-11-22 11:28:30 -05:00
let bytes = key.to_bytes();
let compressed = cd::edwards::CompressedEdwardsY(bytes);
2022-02-06 21:18:42 -05:00
let point = compressed
.decompress()
.ok_or_else(fn_string!("ed25519_to_x25519_pk failed"))?;
2021-11-22 11:28:30 -05:00
let mp = point.to_montgomery();
Ok(xd::PublicKey::from(mp.to_bytes()))
}
2022-02-06 21:18:42 -05:00
fn ed25519_to_x25519_sk(key: &ed::SecretKey) -> Result<xd::StaticSecret, String> {
2021-11-22 11:28:30 -05:00
let exp = ed::ExpandedSecretKey::from(key);
let bytes: [u8; ed::EXPANDED_SECRET_KEY_LENGTH] = exp.to_bytes();
2022-02-06 21:18:42 -05:00
let lowbytes: [u8; 32] = bytes[0..32].try_into().map_err(map_to_string)?;
2021-11-22 11:28:30 -05:00
Ok(xd::StaticSecret::from(lowbytes))
}
2022-02-06 21:18:42 -05:00
pub fn cached_dh(&self, key: &DHTKey, secret: &DHTKeySecret) -> Result<SharedSecret, String> {
2022-03-20 10:52:03 -04:00
Ok(
match self.inner.lock().dh_cache.entry(DHCacheKey {
key: *key,
secret: *secret,
}) {
Entry::Occupied(e) => e.get().shared_secret,
Entry::Vacant(e) => {
let shared_secret = Self::compute_dh(key, secret)?;
e.insert(DHCacheValue { shared_secret });
shared_secret
}
},
)
2021-11-22 11:28:30 -05:00
}
///////////
// These are safe to use regardless of initialization status
2022-02-06 21:18:42 -05:00
pub fn compute_dh(key: &DHTKey, secret: &DHTKeySecret) -> Result<SharedSecret, String> {
2021-11-22 11:28:30 -05:00
assert!(key.valid);
assert!(secret.valid);
2022-02-06 21:18:42 -05:00
let pk_ed = ed::PublicKey::from_bytes(&key.bytes).map_err(map_to_string)?;
2021-11-22 11:28:30 -05:00
let pk_xd = Self::ed25519_to_x25519_pk(&pk_ed)?;
2022-02-06 21:18:42 -05:00
let sk_ed = ed::SecretKey::from_bytes(&secret.bytes).map_err(map_to_string)?;
2021-11-22 11:28:30 -05:00
let sk_xd = Self::ed25519_to_x25519_sk(&sk_ed)?;
Ok(sk_xd.diffie_hellman(&pk_xd).to_bytes())
}
pub fn get_random_nonce() -> Nonce {
let mut nonce = [0u8; 24];
2021-12-14 09:48:33 -05:00
random_bytes(&mut nonce).unwrap();
2021-11-22 11:28:30 -05:00
nonce
}
pub fn get_random_secret() -> SharedSecret {
let mut s = [0u8; 32];
2021-12-14 09:48:33 -05:00
random_bytes(&mut s).unwrap();
2021-11-22 11:28:30 -05:00
s
}
pub fn decrypt_in_place(
body: &mut Vec<u8>,
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
2022-02-06 21:18:42 -05:00
) -> Result<(), String> {
2021-11-26 11:50:49 -05:00
let key = ch::Key::from(*shared_secret);
let xnonce = ch::XNonce::from(*nonce);
2021-11-22 11:28:30 -05:00
let aead = ch::XChaCha20Poly1305::new(&key);
aead.decrypt_in_place(&xnonce, associated_data.unwrap_or(b""), body)
2022-02-06 21:18:42 -05:00
.map_err(map_to_string)
.map_err(logthru_crypto!())
2021-11-22 11:28:30 -05:00
}
pub fn decrypt(
body: &[u8],
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
2022-02-06 21:18:42 -05:00
) -> Result<Vec<u8>, String> {
2021-11-22 11:28:30 -05:00
let mut out = body.to_vec();
2022-02-06 21:18:42 -05:00
Self::decrypt_in_place(&mut out, nonce, shared_secret, associated_data)
.map_err(map_to_string)
.map_err(logthru_crypto!())?;
2021-11-22 11:28:30 -05:00
Ok(out)
}
pub fn encrypt_in_place(
body: &mut Vec<u8>,
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
2022-02-06 21:18:42 -05:00
) -> Result<(), String> {
2021-11-26 11:50:49 -05:00
let key = ch::Key::from(*shared_secret);
let xnonce = ch::XNonce::from(*nonce);
2021-11-22 11:28:30 -05:00
let aead = ch::XChaCha20Poly1305::new(&key);
aead.encrypt_in_place(&xnonce, associated_data.unwrap_or(b""), body)
2022-02-06 21:18:42 -05:00
.map_err(map_to_string)
.map_err(logthru_crypto!())
2021-11-22 11:28:30 -05:00
}
pub fn encrypt(
body: &[u8],
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
2022-02-06 21:18:42 -05:00
) -> Result<Vec<u8>, String> {
2021-11-22 11:28:30 -05:00
let mut out = body.to_vec();
2022-02-06 21:18:42 -05:00
Self::encrypt_in_place(&mut out, nonce, shared_secret, associated_data)
.map_err(map_to_string)
.map_err(logthru_crypto!())?;
2021-11-22 11:28:30 -05:00
Ok(out)
}
}