2022-10-30 19:29:31 -04:00
|
|
|
mod envelope;
|
|
|
|
mod key;
|
|
|
|
mod receipt;
|
|
|
|
mod value;
|
|
|
|
|
|
|
|
pub mod tests;
|
|
|
|
|
|
|
|
pub use envelope::*;
|
|
|
|
pub use key::*;
|
|
|
|
pub use receipt::*;
|
|
|
|
pub use value::*;
|
|
|
|
|
|
|
|
pub const MIN_CRYPTO_VERSION: u8 = 0u8;
|
|
|
|
pub const MAX_CRYPTO_VERSION: u8 = 0u8;
|
|
|
|
|
2021-11-22 11:28:30 -05:00
|
|
|
use crate::xx::*;
|
|
|
|
use crate::*;
|
2022-06-11 18:47:58 -04:00
|
|
|
use chacha20::cipher::{KeyIvInit, StreamCipher};
|
2022-04-03 21:35:14 -04:00
|
|
|
use chacha20::XChaCha20;
|
2021-11-22 11:28:30 -05:00
|
|
|
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;
|
2022-04-03 21:35:14 -04:00
|
|
|
pub const AEAD_OVERHEAD: usize = 16;
|
2021-11-22 11:28:30 -05:00
|
|
|
|
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,
|
2022-07-14 16:57:34 -04:00
|
|
|
flush_future: Option<SendPinBoxFuture<()>>,
|
2021-11-22 11:28:30 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[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))),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-10 17:36:50 -04:00
|
|
|
pub async fn init(&self) -> EyreResult<()> {
|
2021-11-22 11:28:30 -05:00
|
|
|
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-06-27 23:46:29 -04:00
|
|
|
let flush_future = intf::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(())
|
|
|
|
}
|
|
|
|
|
2022-07-10 17:36:50 -04:00
|
|
|
pub async fn flush(&self) -> EyreResult<()> {
|
2021-11-22 11:28:30 -05:00
|
|
|
//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-07-10 17:36:50 -04:00
|
|
|
fn ed25519_to_x25519_pk(key: &ed::PublicKey) -> Result<xd::PublicKey, VeilidAPIError> {
|
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()
|
2022-07-10 17:36:50 -04:00
|
|
|
.ok_or_else(|| VeilidAPIError::internal("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-07-10 17:36:50 -04:00
|
|
|
fn ed25519_to_x25519_sk(key: &ed::SecretKey) -> Result<xd::StaticSecret, VeilidAPIError> {
|
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-07-10 17:36:50 -04:00
|
|
|
let lowbytes: [u8; 32] = bytes[0..32].try_into().map_err(VeilidAPIError::internal)?;
|
2021-11-22 11:28:30 -05:00
|
|
|
Ok(xd::StaticSecret::from(lowbytes))
|
|
|
|
}
|
|
|
|
|
2022-07-10 17:36:50 -04:00
|
|
|
pub fn cached_dh(
|
|
|
|
&self,
|
|
|
|
key: &DHTKey,
|
|
|
|
secret: &DHTKeySecret,
|
|
|
|
) -> Result<SharedSecret, VeilidAPIError> {
|
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-07-10 17:36:50 -04:00
|
|
|
pub fn compute_dh(key: &DHTKey, secret: &DHTKeySecret) -> Result<SharedSecret, VeilidAPIError> {
|
2021-11-22 11:28:30 -05:00
|
|
|
assert!(key.valid);
|
|
|
|
assert!(secret.valid);
|
2022-07-10 17:36:50 -04:00
|
|
|
let pk_ed = ed::PublicKey::from_bytes(&key.bytes).map_err(VeilidAPIError::internal)?;
|
2021-11-22 11:28:30 -05:00
|
|
|
let pk_xd = Self::ed25519_to_x25519_pk(&pk_ed)?;
|
2022-07-10 17:36:50 -04:00
|
|
|
let sk_ed = ed::SecretKey::from_bytes(&secret.bytes).map_err(VeilidAPIError::internal)?;
|
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];
|
2022-06-27 23:46:29 -04:00
|
|
|
intf::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];
|
2022-06-27 23:46:29 -04:00
|
|
|
intf::random_bytes(&mut s).unwrap();
|
2021-11-22 11:28:30 -05:00
|
|
|
s
|
|
|
|
}
|
|
|
|
|
2022-04-03 21:35:14 -04:00
|
|
|
pub fn decrypt_in_place_aead(
|
2021-11-22 11:28:30 -05:00
|
|
|
body: &mut Vec<u8>,
|
|
|
|
nonce: &Nonce,
|
|
|
|
shared_secret: &SharedSecret,
|
|
|
|
associated_data: Option<&[u8]>,
|
2022-07-12 12:45:54 -04:00
|
|
|
) -> Result<(), VeilidAPIError> {
|
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)
|
2022-07-12 12:45:54 -04:00
|
|
|
.map_err(VeilidAPIError::generic)
|
2021-11-22 11:28:30 -05:00
|
|
|
}
|
|
|
|
|
2022-04-03 21:35:14 -04:00
|
|
|
pub fn decrypt_aead(
|
2021-11-22 11:28:30 -05:00
|
|
|
body: &[u8],
|
|
|
|
nonce: &Nonce,
|
|
|
|
shared_secret: &SharedSecret,
|
|
|
|
associated_data: Option<&[u8]>,
|
2022-07-12 12:45:54 -04:00
|
|
|
) -> Result<Vec<u8>, VeilidAPIError> {
|
2021-11-22 11:28:30 -05:00
|
|
|
let mut out = body.to_vec();
|
2022-04-03 21:35:14 -04:00
|
|
|
Self::decrypt_in_place_aead(&mut out, nonce, shared_secret, associated_data)
|
2022-02-06 21:18:42 -05:00
|
|
|
.map_err(map_to_string)
|
2022-07-12 12:45:54 -04:00
|
|
|
.map_err(VeilidAPIError::generic)?;
|
2021-11-22 11:28:30 -05:00
|
|
|
Ok(out)
|
|
|
|
}
|
|
|
|
|
2022-04-03 21:35:14 -04:00
|
|
|
pub fn encrypt_in_place_aead(
|
2021-11-22 11:28:30 -05:00
|
|
|
body: &mut Vec<u8>,
|
|
|
|
nonce: &Nonce,
|
|
|
|
shared_secret: &SharedSecret,
|
|
|
|
associated_data: Option<&[u8]>,
|
2022-07-12 12:45:54 -04:00
|
|
|
) -> Result<(), VeilidAPIError> {
|
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)
|
2022-07-12 12:45:54 -04:00
|
|
|
.map_err(VeilidAPIError::generic)
|
2021-11-22 11:28:30 -05:00
|
|
|
}
|
|
|
|
|
2022-04-03 21:35:14 -04:00
|
|
|
pub fn encrypt_aead(
|
2021-11-22 11:28:30 -05:00
|
|
|
body: &[u8],
|
|
|
|
nonce: &Nonce,
|
|
|
|
shared_secret: &SharedSecret,
|
|
|
|
associated_data: Option<&[u8]>,
|
2022-07-12 12:45:54 -04:00
|
|
|
) -> Result<Vec<u8>, VeilidAPIError> {
|
2021-11-22 11:28:30 -05:00
|
|
|
let mut out = body.to_vec();
|
2022-04-03 21:35:14 -04:00
|
|
|
Self::encrypt_in_place_aead(&mut out, nonce, shared_secret, associated_data)
|
2022-02-06 21:18:42 -05:00
|
|
|
.map_err(map_to_string)
|
2022-07-12 12:45:54 -04:00
|
|
|
.map_err(VeilidAPIError::generic)?;
|
2021-11-22 11:28:30 -05:00
|
|
|
Ok(out)
|
|
|
|
}
|
2022-04-03 21:35:14 -04:00
|
|
|
|
|
|
|
pub fn crypt_in_place_no_auth(body: &mut Vec<u8>, nonce: &Nonce, shared_secret: &SharedSecret) {
|
|
|
|
let mut cipher = XChaCha20::new(shared_secret.into(), nonce.into());
|
|
|
|
cipher.apply_keystream(body);
|
|
|
|
}
|
|
|
|
|
2022-06-29 22:17:19 -04:00
|
|
|
pub fn crypt_b2b_no_auth(
|
|
|
|
in_buf: &[u8],
|
|
|
|
nonce: &Nonce,
|
|
|
|
shared_secret: &SharedSecret,
|
|
|
|
) -> Vec<u8> {
|
|
|
|
let mut cipher = XChaCha20::new(shared_secret.into(), nonce.into());
|
|
|
|
// Allocate uninitialized memory, aligned to 8 byte boundary because capnp is faster this way
|
|
|
|
// and the Vec returned here will be used to hold decrypted rpc messages
|
|
|
|
let mut out_buf = unsafe { aligned_8_u8_vec_uninit(in_buf.len()) };
|
|
|
|
cipher.apply_keystream_b2b(in_buf, &mut out_buf).unwrap();
|
|
|
|
out_buf
|
|
|
|
}
|
|
|
|
|
2022-04-03 21:35:14 -04:00
|
|
|
pub fn crypt_no_auth(body: &[u8], nonce: &Nonce, shared_secret: &SharedSecret) -> Vec<u8> {
|
2022-06-29 22:17:19 -04:00
|
|
|
Self::crypt_b2b_no_auth(body, nonce, shared_secret)
|
2022-04-03 21:35:14 -04:00
|
|
|
}
|
2021-11-22 11:28:30 -05:00
|
|
|
}
|