veilid_core/crypto/
mod.rs1mod crypto_system;
2mod dh_cache;
3mod envelope;
4mod guard;
5mod receipt;
6mod types;
7
8#[cfg(any(test, feature = "test-util"))]
9#[doc(hidden)]
10pub mod tests_crypto;
11
12pub use crypto_system::*;
13use dh_cache::*;
14pub(crate) use envelope::*;
15pub use guard::*;
16pub(crate) use receipt::*;
17pub use types::*;
18
19use super::*;
20use core::convert::TryInto;
21use hashlink::linked_hash_map::Entry;
22use hashlink::LruCache;
23
24impl_veilid_log_facility!("crypto");
25
26cfg_if! {
27 if #[cfg(all(feature = "enable-crypto-none", feature = "enable-crypto-vld0"))] {
28 pub const VALID_CRYPTO_KINDS: [CryptoKind; 2] = [CRYPTO_KIND_VLD0, CRYPTO_KIND_NONE];
30 }
31 else if #[cfg(feature = "enable-crypto-none")] {
32 pub const VALID_CRYPTO_KINDS: [CryptoKind; 1] = [CRYPTO_KIND_NONE];
34 }
35 else if #[cfg(feature = "enable-crypto-vld0")] {
36 pub const VALID_CRYPTO_KINDS: [CryptoKind; 1] = [CRYPTO_KIND_VLD0];
38 }
39 else {
44 compile_error!("No crypto kinds enabled, specify an enable-crypto- feature");
45 }
46}
47pub const MAX_CRYPTO_KINDS: usize = 3;
49
50pub(crate) fn best_crypto_kind() -> CryptoKind {
52 VALID_CRYPTO_KINDS[0]
53}
54
55struct CryptoInner {
56 dh_cache: DHCache,
57 dh_cache_misses: usize,
58 dh_cache_hits: usize,
59 dh_cache_lru: usize,
60}
61
62impl fmt::Debug for CryptoInner {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 f.debug_struct("CryptoInner")
65 .field("dh_cache_misses", &self.dh_cache_misses)
67 .field("dh_cache_hits", &self.dh_cache_hits)
68 .field("dh_cache_lru", &self.dh_cache_lru)
69 .finish()
72 }
73}
74
75#[must_use]
77pub struct Crypto {
78 registry: VeilidComponentRegistry,
79 inner: Mutex<CryptoInner>,
80 #[cfg(feature = "enable-crypto-vld0")]
81 crypto_vld0: Arc<dyn CryptoSystem + Send + Sync>,
82 #[cfg(feature = "enable-crypto-none")]
83 crypto_none: Arc<dyn CryptoSystem + Send + Sync>,
84}
85
86impl_veilid_component!(Crypto);
87
88impl fmt::Debug for Crypto {
89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 f.debug_struct("Crypto")
91 .field("inner", &self.inner)
93 .finish()
96 }
97}
98
99impl Crypto {
100 fn new_inner() -> CryptoInner {
101 CryptoInner {
102 dh_cache: DHCache::new(DH_CACHE_SIZE),
103 dh_cache_misses: 0,
104 dh_cache_hits: 0,
105 dh_cache_lru: 0,
106 }
107 }
108
109 pub(crate) fn new(registry: VeilidComponentRegistry) -> Self {
110 Self {
111 registry: registry.clone(),
112 inner: Mutex::new(Self::new_inner()),
113 #[cfg(feature = "enable-crypto-vld0")]
114 crypto_vld0: Arc::new(vld0::CryptoSystemVLD0::new(registry.clone())),
115 #[cfg(feature = "enable-crypto-none")]
116 crypto_none: Arc::new(none::CryptoSystemNONE::new(registry.clone())),
117 }
118 }
119
120 fn log_facilities_impl(&self) -> VeilidComponentLogFacilities {
121 VeilidComponentLogFacilities::new().with_facility(
122 VeilidComponentLogFacility::try_new_with_tags("crypto", ["#common"]).unwrap(),
123 )
124 }
125
126 #[cfg_attr(
127 feature = "instrument",
128 instrument(level = "trace", target = "crypto", skip_all, err, fields(__VEILID_LOG_KEY = self.log_key()))
129 )]
130 #[allow(clippy::unused_async)]
131 async fn init_async(&self) -> EyreResult<()> {
132 Ok(())
134 }
135
136 #[cfg_attr(
137 feature = "instrument",
138 instrument(level = "trace", target = "crypto", skip_all, err, fields(__VEILID_LOG_KEY = self.log_key()))
139 )]
140 #[allow(clippy::unused_async)]
141 async fn post_init_async(&self) -> EyreResult<()> {
142 Ok(())
143 }
144
145 #[allow(clippy::unused_async)]
146 async fn pre_terminate_async(&self) {}
147
148 #[expect(clippy::unused_async)]
149 async fn terminate_async(&self) {
150 }
152
153 pub fn get(&self, kind: CryptoKind) -> Option<CryptoSystemGuard<'_>> {
158 match kind {
159 #[cfg(feature = "enable-crypto-vld0")]
160 CRYPTO_KIND_VLD0 => Some(CryptoSystemGuard::new(self.crypto_vld0.clone())),
161 #[cfg(feature = "enable-crypto-none")]
162 CRYPTO_KIND_NONE => Some(CryptoSystemGuard::new(self.crypto_none.clone())),
163 _ => None,
164 }
165 }
166
167 pub fn get_async(&self, kind: CryptoKind) -> Option<AsyncCryptoSystemGuard<'_>> {
172 self.get(kind).map(|x| x.as_async())
173 }
174
175 pub(crate) fn best(&self) -> CryptoSystemGuard<'_> {
177 self.get(best_crypto_kind()).unwrap_or_log()
178 }
179
180 pub(crate) fn best_async(&self) -> AsyncCryptoSystemGuard<'_> {
182 self.get_async(best_crypto_kind()).unwrap_or_log()
183 }
184
185 pub fn check_shared_secret(&self, secret: &SharedSecret) -> VeilidAPIResult<()> {
191 let Some(vcrypto) = self.get(secret.kind()) else {
192 apibail_generic!("unsupported crypto kind");
193 };
194 vcrypto.check_shared_secret(secret)
195 }
196
197 pub fn check_hash_digest(&self, hash: &HashDigest) -> VeilidAPIResult<()> {
201 let Some(vcrypto) = self.get(hash.kind()) else {
202 apibail_generic!("unsupported crypto kind");
203 };
204 vcrypto.check_hash_digest(hash)
205 }
206 pub fn check_public_key(&self, key: &PublicKey) -> VeilidAPIResult<()> {
210 let Some(vcrypto) = self.get(key.kind()) else {
211 apibail_generic!("unsupported crypto kind");
212 };
213 vcrypto.check_public_key(key)
214 }
215 pub fn check_secret_key(&self, key: &SecretKey) -> VeilidAPIResult<()> {
219 let Some(vcrypto) = self.get(key.kind()) else {
220 apibail_generic!("unsupported crypto kind");
221 };
222 vcrypto.check_secret_key(key)
223 }
224 pub fn check_signature(&self, signature: &Signature) -> VeilidAPIResult<()> {
228 let Some(vcrypto) = self.get(signature.kind()) else {
229 apibail_generic!("unsupported crypto kind");
230 };
231 vcrypto.check_signature(signature)
232 }
233 pub fn check_keypair(&self, key_pair: &KeyPair) -> VeilidAPIResult<()> {
238 let Some(vcrypto) = self.get(key_pair.kind()) else {
239 apibail_generic!("unsupported crypto kind");
240 };
241 vcrypto.check_keypair(key_pair)
242 }
243
244 pub fn verify_signatures(
254 &self,
255 public_keys: &[PublicKey],
256 data: &[u8],
257 signatures: &[Signature],
258 ) -> VeilidAPIResult<Option<PublicKeyGroup>> {
259 let mut out = PublicKeyGroup::with_capacity(public_keys.len());
260 for signature in signatures {
261 for public_key in public_keys {
262 if public_key.kind() == signature.kind() {
263 if let Some(vcrypto) = self.get(signature.kind()) {
264 if !vcrypto.verify(public_key, data, signature)? {
265 return Ok(None);
266 }
267 out.add(public_key.clone());
268 }
269 }
270 }
271 }
272 Ok(Some(out))
273 }
274
275 pub fn generate_signatures<F, R>(
284 &self,
285 data: &[u8],
286 key_pairs: &[KeyPair],
287 transform: F,
288 ) -> VeilidAPIResult<Vec<R>>
289 where
290 F: Fn(&KeyPair, Signature) -> R,
291 {
292 let mut out = Vec::<R>::with_capacity(key_pairs.len());
293 for kp in key_pairs {
294 if let Some(vcrypto) = self.get(kp.kind()) {
295 let sig = vcrypto.sign(&kp.key(), &kp.secret(), data)?;
296 out.push(transform(kp, sig))
297 }
298 }
299 Ok(out)
300 }
301
302 pub fn generate_keypair(crypto_kind: CryptoKind) -> VeilidAPIResult<KeyPair> {
307 #[cfg(feature = "enable-crypto-vld0")]
308 if crypto_kind == CRYPTO_KIND_VLD0 {
309 let kp = vld0_generate_keypair();
310 return Ok(kp);
311 }
312 #[cfg(feature = "enable-crypto-none")]
313 if crypto_kind == CRYPTO_KIND_NONE {
314 let kp = none_generate_keypair();
315 return Ok(kp);
316 }
317 Err(VeilidAPIError::generic("invalid crypto kind"))
318 }
319
320 fn cached_dh_internal<T: CryptoSystem>(
323 &self,
324 vcrypto: &T,
325 key: &PublicKey,
326 secret: &SecretKey,
327 ) -> VeilidAPIResult<SharedSecret> {
328 vcrypto.check_public_key(key)?;
329 vcrypto.check_secret_key(secret)?;
330
331 let dh_cache_key = DHCacheKey {
332 key: key.clone(),
333 secret: secret.clone(),
334 };
335
336 {
337 let inner = &mut *self.inner.lock();
338 if let Some(value) = inner.dh_cache.get(&dh_cache_key) {
339 inner.dh_cache_hits += 1;
340 return Ok(value.shared_secret.clone());
341 }
342 }
343 let shared_secret = vcrypto.compute_dh(key, secret)?;
344
345 {
346 let inner = &mut *self.inner.lock();
347 let res = inner.dh_cache.entry_with_callback(dh_cache_key, |_, _| {
348 inner.dh_cache_lru += 1;
349 });
350 match res {
351 Entry::Occupied(_) => {
352 inner.dh_cache_hits += 1;
353 }
354 Entry::Vacant(e) => {
355 inner.dh_cache_misses += 1;
356 e.insert(DHCacheValue {
357 shared_secret: shared_secret.clone(),
358 });
359 }
360 }
361 }
362
363 Ok(shared_secret)
364 }
365
366 pub(crate) fn validate_crypto_kind(kind: CryptoKind) -> VeilidAPIResult<()> {
367 if !VALID_CRYPTO_KINDS.contains(&kind) {
368 apibail_generic!("invalid crypto kind");
369 }
370 Ok(())
371 }
372
373 #[cfg_attr(not(feature = "debug-api"), expect(dead_code))]
374 pub(crate) fn debug_info_nodeinfo(&self) -> String {
375 let inner = self.inner.lock();
376 format!(
377 "Crypto Stats:\n DH Cache Hits/Misses/LRU: {} / {} / {}",
378 inner.dh_cache_hits, inner.dh_cache_misses, inner.dh_cache_lru
379 )
380 }
381}