466 lines
16 KiB
Rust
Raw Normal View History

2023-03-13 16:14:31 -04:00
mod blake3digest512;
2023-02-11 15:54:55 -05:00
mod dh_cache;
2022-10-30 19:29:31 -04:00
mod envelope;
mod guard;
2022-10-30 19:29:31 -04:00
mod receipt;
2023-02-07 21:44:50 -05:00
mod types;
2022-10-30 19:29:31 -04:00
2023-01-29 13:13:50 -05:00
pub mod crypto_system;
2023-03-13 16:14:31 -04:00
#[cfg(feature = "enable-crypto-none")]
pub mod none;
2023-08-29 15:15:47 -05:00
#[doc(hidden)]
2022-10-30 19:29:31 -04:00
pub mod tests;
2023-03-13 16:14:31 -04:00
#[cfg(feature = "enable-crypto-vld0")]
2023-02-07 21:44:50 -05:00
pub mod vld0;
2022-10-30 19:29:31 -04:00
2023-03-13 16:14:31 -04:00
pub use blake3digest512::*;
2023-06-29 20:49:15 -04:00
2023-01-29 13:13:50 -05:00
pub use crypto_system::*;
2022-10-30 19:29:31 -04:00
pub use envelope::*;
pub use guard::*;
2022-10-30 19:29:31 -04:00
pub use receipt::*;
2023-02-07 21:44:50 -05:00
pub use types::*;
2023-03-13 16:14:31 -04:00
#[cfg(feature = "enable-crypto-none")]
pub use none::*;
#[cfg(feature = "enable-crypto-vld0")]
2023-02-07 21:44:50 -05:00
pub use vld0::*;
2022-10-30 19:29:31 -04:00
2023-06-03 18:33:27 -04:00
use super::*;
2021-11-22 11:28:30 -05:00
use core::convert::TryInto;
2023-09-01 21:18:45 -04:00
use dh_cache::*;
2022-03-20 10:52:03 -04:00
use hashlink::linked_hash_map::Entry;
use hashlink::LruCache;
use std::marker::PhantomData;
2023-01-29 13:13:50 -05:00
2025-02-18 23:42:04 +00:00
impl_veilid_log_facility!("crypto");
2023-03-13 16:14:31 -04:00
cfg_if! {
if #[cfg(all(feature = "enable-crypto-none", feature = "enable-crypto-vld0"))] {
/// Crypto kinds in order of preference, best cryptosystem is the first one, worst is the last one
pub const VALID_CRYPTO_KINDS: [CryptoKind; 2] = [CRYPTO_KIND_VLD0, CRYPTO_KIND_NONE];
}
else if #[cfg(feature = "enable-crypto-none")] {
/// Crypto kinds in order of preference, best cryptosystem is the first one, worst is the last one
pub const VALID_CRYPTO_KINDS: [CryptoKind; 1] = [CRYPTO_KIND_NONE];
}
else if #[cfg(feature = "enable-crypto-vld0")] {
/// Crypto kinds in order of preference, best cryptosystem is the first one, worst is the last one
pub const VALID_CRYPTO_KINDS: [CryptoKind; 1] = [CRYPTO_KIND_VLD0];
}
else {
compile_error!("No crypto kinds enabled, specify an enable-crypto- feature");
}
}
2023-02-13 21:12:27 -05:00
/// Number of cryptosystem signatures to keep on structures if many are present beyond the ones we consider valid
pub const MAX_CRYPTO_KINDS: usize = 3;
/// Return the best cryptosystem kind we support
2023-02-11 15:54:55 -05:00
pub fn best_crypto_kind() -> CryptoKind {
VALID_CRYPTO_KINDS[0]
2021-11-22 11:28:30 -05:00
}
2023-03-13 16:14:31 -04:00
/// Version number of envelope format
2023-02-11 15:54:55 -05:00
pub type EnvelopeVersion = u8;
2023-02-15 18:18:08 -05:00
/// Envelope versions in order of preference, best envelope version is the first one, worst is the last one
2023-02-11 15:54:55 -05:00
pub const VALID_ENVELOPE_VERSIONS: [EnvelopeVersion; 1] = [0u8];
2023-02-15 18:18:08 -05:00
/// Number of envelope versions to keep on structures if many are present beyond the ones we consider valid
pub const MAX_ENVELOPE_VERSIONS: usize = 3;
/// Return the best envelope version we support
#[must_use]
2023-02-11 15:54:55 -05:00
pub fn best_envelope_version() -> EnvelopeVersion {
VALID_ENVELOPE_VERSIONS[0]
2021-11-22 11:28:30 -05:00
}
struct CryptoInner {
dh_cache: DHCache,
flush_future: Option<PinBoxFutureStatic<()>>,
2023-02-07 21:44:50 -05:00
}
impl fmt::Debug for CryptoInner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CryptoInner")
//.field("dh_cache", &self.dh_cache)
// .field("flush_future", &self.flush_future)
// .field("crypto_vld0", &self.crypto_vld0)
// .field("crypto_none", &self.crypto_none)
.finish()
}
2021-11-22 11:28:30 -05:00
}
2023-02-07 21:44:50 -05:00
/// Crypto factory implementation
#[must_use]
2021-11-22 11:28:30 -05:00
pub struct Crypto {
registry: VeilidComponentRegistry,
inner: Mutex<CryptoInner>,
#[cfg(feature = "enable-crypto-vld0")]
crypto_vld0: Arc<dyn CryptoSystem + Send + Sync>,
#[cfg(feature = "enable-crypto-none")]
crypto_none: Arc<dyn CryptoSystem + Send + Sync>,
}
impl_veilid_component!(Crypto);
impl fmt::Debug for Crypto {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Crypto")
//.field("registry", &self.registry)
.field("inner", &self.inner)
// .field("crypto_vld0", &self.crypto_vld0)
// .field("crypto_none", &self.crypto_none)
.finish()
}
2021-11-22 11:28:30 -05:00
}
impl Crypto {
2023-02-07 21:44:50 -05:00
fn new_inner() -> CryptoInner {
2021-11-22 11:28:30 -05:00
CryptoInner {
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(registry: VeilidComponentRegistry) -> Self {
Self {
registry: registry.clone(),
inner: Mutex::new(Self::new_inner()),
#[cfg(feature = "enable-crypto-vld0")]
crypto_vld0: Arc::new(vld0::CryptoSystemVLD0::new(registry.clone())),
#[cfg(feature = "enable-crypto-none")]
crypto_none: Arc::new(none::CryptoSystemNONE::new(registry.clone())),
2023-03-13 16:14:31 -04:00
}
2021-11-22 11:28:30 -05:00
}
#[instrument(level = "trace", target = "crypto", skip_all, err)]
async fn init_async(&self) -> EyreResult<()> {
// Nothing to initialize at this time
Ok(())
2023-02-13 21:12:27 -05:00
}
// Setup called by table store after it get initialized
2024-07-03 21:00:12 -04:00
#[instrument(level = "trace", target = "crypto", skip_all, err)]
pub(crate) async fn table_store_setup(&self, table_store: &TableStore) -> EyreResult<()> {
2023-02-07 21:44:50 -05:00
// Init node id from config
if let Err(e) = self.setup_node_ids(table_store).await {
2023-02-07 21:44:50 -05:00
return Err(e).wrap_err("init node id failed");
}
2021-11-22 11:28:30 -05:00
// make local copy of node id for easy access
2023-02-07 21:44:50 -05:00
let mut cache_validity_key: Vec<u8> = Vec::new();
self.config().with(|c| {
2023-03-03 10:55:31 -05:00
for ck in VALID_CRYPTO_KINDS {
2023-06-22 18:31:31 -04:00
if let Some(nid) = c.network.routing_table.node_id.get(ck) {
cache_validity_key.append(&mut nid.value.bytes.to_vec());
}
2023-02-07 21:44:50 -05:00
}
});
2021-11-22 11:28:30 -05:00
// load caches if they are valid for this node id
2023-05-29 19:24:57 +00:00
let mut db = table_store
.open("crypto_caches", 1)
.await
.wrap_err("failed to open crypto_caches")?;
let caches_valid = match db.load(0, b"cache_validity_key").await? {
2023-02-07 21:44:50 -05:00
Some(v) => v == cache_validity_key,
2021-11-22 11:28:30 -05:00
None => false,
};
if caches_valid {
2023-05-29 19:24:57 +00: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?;
2023-02-07 21:44:50 -05:00
db.store(0, b"cache_validity_key", &cache_validity_key)
.await?;
2021-11-22 11:28:30 -05:00
}
Ok(())
}
2021-11-22 11:28:30 -05:00
#[instrument(level = "trace", target = "crypto", skip_all, err)]
async fn post_init_async(&self) -> EyreResult<()> {
2021-11-22 11:28:30 -05:00
// Schedule flushing
let registry = self.registry();
2024-07-20 19:42:25 -04:00
let flush_future = interval("crypto flush", 60000, move || {
let crypto = registry.crypto();
2021-11-22 11:28:30 -05:00
async move {
if let Err(e) = crypto.flush().await {
2025-02-18 23:42:04 +00:00
veilid_log!(crypto warn "flush failed: {}", e);
2021-11-22 11:28:30 -05:00
}
}
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<()> {
2023-02-07 21:44:50 -05:00
let cache_bytes = {
2021-11-22 11:28:30 -05:00
let inner = self.inner.lock();
2023-02-07 21:44:50 -05:00
cache_to_bytes(&inner.dh_cache)
2021-11-22 11:28:30 -05:00
};
let db = self.table_store().open("crypto_caches", 1).await?;
2022-12-28 12:12:04 -05:00
db.store(0, b"dh_cache", &cache_bytes).await?;
2021-11-22 11:28:30 -05:00
Ok(())
}
async fn pre_terminate_async(&self) {
2021-11-22 11:28:30 -05:00
let flush_future = self.inner.lock().flush_future.take();
if let Some(f) = flush_future {
f.await;
}
2025-02-18 23:42:04 +00:00
veilid_log!(self trace "starting termination flush");
2021-11-22 11:28:30 -05:00
match self.flush().await {
Ok(_) => {
2025-02-18 23:42:04 +00:00
veilid_log!(self trace "finished termination flush");
2021-11-22 11:28:30 -05:00
}
Err(e) => {
error!("failed termination flush: {}", e);
}
};
}
#[expect(clippy::unused_async)]
async fn terminate_async(&self) {
// Nothing to terminate at this time
}
2023-02-07 21:44:50 -05:00
/// Factory method to get a specific crypto version
pub fn get(&self, kind: CryptoKind) -> Option<CryptoSystemGuard<'_>> {
2023-02-07 21:44:50 -05:00
match kind {
2023-03-13 16:14:31 -04:00
#[cfg(feature = "enable-crypto-vld0")]
CRYPTO_KIND_VLD0 => Some(CryptoSystemGuard::new(self.crypto_vld0.clone())),
2023-03-13 16:14:31 -04:00
#[cfg(feature = "enable-crypto-none")]
CRYPTO_KIND_NONE => Some(CryptoSystemGuard::new(self.crypto_none.clone())),
2023-02-07 21:44:50 -05:00
_ => None,
}
}
/// Factory method to get a specific crypto version for async use
pub fn get_async(&self, kind: CryptoKind) -> Option<AsyncCryptoSystemGuard<'_>> {
self.get(kind).map(|x| x.as_async())
}
2023-02-11 15:54:55 -05:00
// Factory method to get the best crypto version
pub fn best(&self) -> CryptoSystemGuard<'_> {
2023-02-11 15:54:55 -05:00
self.get(best_crypto_kind()).unwrap()
}
// Factory method to get the best crypto version for async use
pub fn best_async(&self) -> AsyncCryptoSystemGuard<'_> {
self.get_async(best_crypto_kind()).unwrap()
}
2023-02-07 21:44:50 -05:00
/// Signature set verification
/// Returns Some() the set of signature cryptokinds that validate and are supported
/// Returns None if any cryptokinds are supported and do not validate
2023-02-08 16:50:07 -05:00
pub fn verify_signatures(
2023-02-07 21:44:50 -05:00
&self,
public_keys: &[TypedKey],
2023-02-07 21:44:50 -05:00
data: &[u8],
2023-02-08 16:50:07 -05:00
typed_signatures: &[TypedSignature],
) -> VeilidAPIResult<Option<TypedKeyGroup>> {
let mut out = TypedKeyGroup::with_capacity(public_keys.len());
2023-02-08 16:50:07 -05:00
for sig in typed_signatures {
for nid in public_keys {
2023-02-08 16:50:07 -05:00
if nid.kind == sig.kind {
if let Some(vcrypto) = self.get(sig.kind) {
if !vcrypto.verify(&nid.value, data, &sig.value)? {
return Ok(None);
}
2023-03-03 10:55:31 -05:00
out.add(*nid);
2023-02-08 16:50:07 -05:00
}
}
2023-02-07 21:44:50 -05:00
}
}
Ok(Some(out))
2023-02-07 21:44:50 -05:00
}
/// Signature set generation
/// Generates the set of signatures that are supported
/// Any cryptokinds that are not supported are silently dropped
pub fn generate_signatures<F, R>(
&self,
data: &[u8],
2023-02-08 16:50:07 -05:00
typed_key_pairs: &[TypedKeyPair],
2023-02-07 21:44:50 -05:00
transform: F,
2023-05-29 19:24:57 +00:00
) -> VeilidAPIResult<Vec<R>>
2023-02-07 21:44:50 -05:00
where
F: Fn(&TypedKeyPair, Signature) -> R,
{
2023-02-08 16:50:07 -05:00
let mut out = Vec::<R>::with_capacity(typed_key_pairs.len());
for kp in typed_key_pairs {
2023-02-07 21:44:50 -05:00
if let Some(vcrypto) = self.get(kp.kind) {
2023-03-01 15:50:30 -05:00
let sig = vcrypto.sign(&kp.value.key, &kp.value.secret, data)?;
2023-02-07 21:44:50 -05:00
out.push(transform(kp, sig))
}
2023-01-29 13:13:50 -05:00
}
2023-02-07 21:44:50 -05:00
Ok(out)
2021-11-22 11:28:30 -05:00
}
2023-03-03 10:55:31 -05:00
/// Generate keypair
/// Does not require startup/init
2023-05-29 19:24:57 +00:00
pub fn generate_keypair(crypto_kind: CryptoKind) -> VeilidAPIResult<TypedKeyPair> {
2023-03-13 16:14:31 -04:00
#[cfg(feature = "enable-crypto-vld0")]
2023-03-03 10:55:31 -05:00
if crypto_kind == CRYPTO_KIND_VLD0 {
let kp = vld0_generate_keypair();
return Ok(TypedKeyPair::new(crypto_kind, kp));
}
2023-03-13 16:14:31 -04:00
#[cfg(feature = "enable-crypto-none")]
if crypto_kind == CRYPTO_KIND_NONE {
let kp = none_generate_keypair();
return Ok(TypedKeyPair::new(crypto_kind, kp));
}
2023-03-03 10:55:31 -05:00
Err(VeilidAPIError::generic("invalid crypto kind"))
}
2023-01-29 13:13:50 -05:00
// Internal utilities
fn cached_dh_internal<T: CryptoSystem>(
2022-07-10 17:36:50 -04:00
&self,
2023-01-29 13:13:50 -05:00
vcrypto: &T,
2023-02-07 21:44:50 -05:00
key: &PublicKey,
secret: &SecretKey,
2023-05-29 19:24:57 +00:00
) -> VeilidAPIResult<SharedSecret> {
2022-03-20 10:52:03 -04:00
Ok(
2023-09-02 18:50:12 -04:00
match self.inner.lock().dh_cache.entry(DHCacheKey {
key: *key,
secret: *secret,
}) {
2022-03-20 10:52:03 -04:00
Entry::Occupied(e) => e.get().shared_secret,
Entry::Vacant(e) => {
2023-01-29 13:13:50 -05:00
let shared_secret = vcrypto.compute_dh(key, secret)?;
2022-03-20 10:52:03 -04:00
e.insert(DHCacheValue { shared_secret });
shared_secret
}
},
)
2021-11-22 11:28:30 -05:00
}
2023-08-05 11:33:27 -04:00
pub(crate) fn validate_crypto_kind(kind: CryptoKind) -> VeilidAPIResult<()> {
if !VALID_CRYPTO_KINDS.contains(&kind) {
apibail_generic!("invalid crypto kind");
}
Ok(())
}
#[cfg(not(test))]
async fn setup_node_id(
&self,
vcrypto: AsyncCryptoSystemGuard<'_>,
table_store: &TableStore,
) -> VeilidAPIResult<(TypedKey, TypedSecret)> {
let config = self.config();
let ck = vcrypto.kind();
let (mut node_id, mut node_id_secret) = config.with(|c| {
(
c.network.routing_table.node_id.get(ck),
c.network.routing_table.node_id_secret.get(ck),
)
});
// See if node id was previously stored in the table store
let config_table = table_store.open("__veilid_config", 1).await?;
let table_key_node_id = format!("node_id_{}", ck);
let table_key_node_id_secret = format!("node_id_secret_{}", ck);
if node_id.is_none() {
2025-02-18 23:42:04 +00:00
veilid_log!(self debug "pulling {} from storage", table_key_node_id);
if let Ok(Some(stored_node_id)) = config_table
.load_json::<TypedKey>(0, table_key_node_id.as_bytes())
.await
{
2025-02-18 23:42:04 +00:00
veilid_log!(self debug "{} found in storage", table_key_node_id);
node_id = Some(stored_node_id);
} else {
2025-02-18 23:42:04 +00:00
veilid_log!(self debug "{} not found in storage", table_key_node_id);
}
}
// See if node id secret was previously stored in the protected store
if node_id_secret.is_none() {
2025-02-18 23:42:04 +00:00
veilid_log!(self debug "pulling {} from storage", table_key_node_id_secret);
if let Ok(Some(stored_node_id_secret)) = config_table
.load_json::<TypedSecret>(0, table_key_node_id_secret.as_bytes())
.await
{
2025-02-18 23:42:04 +00:00
veilid_log!(self debug "{} found in storage", table_key_node_id_secret);
node_id_secret = Some(stored_node_id_secret);
} else {
2025-02-18 23:42:04 +00:00
veilid_log!(self debug "{} not found in storage", table_key_node_id_secret);
}
}
// If we have a node id from storage, check it
let (node_id, node_id_secret) =
if let (Some(node_id), Some(node_id_secret)) = (node_id, node_id_secret) {
// Validate node id
if !vcrypto
.validate_keypair(&node_id.value, &node_id_secret.value)
.await
{
apibail_generic!(format!(
"node_id_secret_{} and node_id_key_{} don't match",
ck, ck
));
}
(node_id, node_id_secret)
} else {
// If we still don't have a valid node id, generate one
2025-02-18 23:42:04 +00:00
veilid_log!(self debug "generating new node_id_{}", ck);
let kp = vcrypto.generate_keypair().await;
(TypedKey::new(ck, kp.key), TypedSecret::new(ck, kp.secret))
};
2025-02-18 23:42:04 +00:00
veilid_log!(self info "Node Id: {}", node_id);
// Save the node id / secret in storage
config_table
.store_json(0, table_key_node_id.as_bytes(), &node_id)
.await?;
config_table
.store_json(0, table_key_node_id_secret.as_bytes(), &node_id_secret)
.await?;
Ok((node_id, node_id_secret))
}
/// Get the node id from config if one is specified.
/// Must be done -after- protected store is initialized, during table store init
#[cfg_attr(test, allow(unused_variables))]
async fn setup_node_ids(&self, table_store: &TableStore) -> VeilidAPIResult<()> {
let mut out_node_id = TypedKeyGroup::new();
let mut out_node_id_secret = TypedSecretGroup::new();
for ck in VALID_CRYPTO_KINDS {
let vcrypto = self
.get_async(ck)
.expect("Valid crypto kind is not actually valid.");
#[cfg(test)]
let (node_id, node_id_secret) = {
let kp = vcrypto.generate_keypair().await;
(TypedKey::new(ck, kp.key), TypedSecret::new(ck, kp.secret))
};
#[cfg(not(test))]
let (node_id, node_id_secret) = self.setup_node_id(vcrypto, table_store).await?;
// Save for config
out_node_id.add(node_id);
out_node_id_secret.add(node_id_secret);
}
// Commit back to config
self.config().try_with_mut(|c| {
c.network.routing_table.node_id = out_node_id;
c.network.routing_table.node_id_secret = out_node_id_secret;
Ok(())
})?;
Ok(())
}
2021-11-22 11:28:30 -05:00
}