Skip to main content

veilid_core/crypto/
guard.rs

1use core::marker::PhantomData;
2use std::ops::Range;
3
4use super::*;
5
6/// Guard to access a particular cryptosystem
7///
8/// Holds an `Arc` to the cryptosystem for its whole lifetime; the cryptosystem stays reachable as
9/// long as the guard (or its derived [`AsyncCryptoSystemGuard`]) is alive.
10#[must_use]
11pub struct CryptoSystemGuard<'a> {
12    crypto_system: Arc<dyn CryptoSystem + Send + Sync>,
13    _phantom: core::marker::PhantomData<&'a (dyn CryptoSystem + Send + Sync)>,
14}
15
16impl<'a> CryptoSystemGuard<'a> {
17    pub(super) fn new(crypto_system: Arc<dyn CryptoSystem + Send + Sync>) -> Self {
18        Self {
19            crypto_system,
20            _phantom: PhantomData,
21        }
22    }
23    /// Convert into an async guard whose operations yield to the executor between work units.
24    ///
25    /// Consumes this guard, moving its held cryptosystem `Arc` into the returned async guard.
26    pub fn as_async(self) -> AsyncCryptoSystemGuard<'a> {
27        AsyncCryptoSystemGuard { guard: self }
28    }
29    /// Get a clone of the inner Arc for use in blocking tasks
30    pub(super) fn clone_arc(&self) -> Arc<dyn CryptoSystem + Send + Sync> {
31        self.crypto_system.clone()
32    }
33}
34
35impl core::ops::Deref for CryptoSystemGuard<'_> {
36    type Target = dyn CryptoSystem + Send + Sync;
37
38    fn deref(&self) -> &Self::Target {
39        self.crypto_system.as_ref()
40    }
41}
42
43/// Async cryptosystem guard to help break up heavy blocking operations
44#[must_use]
45pub struct AsyncCryptoSystemGuard<'a> {
46    guard: CryptoSystemGuard<'a>,
47}
48
49impl AsyncCryptoSystemGuard<'_> {
50    // Accessors
51
52    /// The `CryptoKind` of the guarded cryptosystem.
53    pub fn kind(&self) -> CryptoKind {
54        self.guard.kind()
55    }
56    /// Get a guard on the `Crypto` component that owns this cryptosystem.
57    #[must_use]
58    pub fn crypto(&self) -> VeilidComponentGuard<'_, Crypto> {
59        self.guard.crypto()
60    }
61
62    // Cached Operations
63
64    /// Diffie-Hellman shared secret, served from the `Crypto` DH cache when present.
65    ///
66    /// Local CPU only; awaits a single runtime yield. On a cache miss runs the DH inline (does not
67    /// offload to the rayon pool, unlike [`compute_dh`](Self::compute_dh)).
68    ///
69    /// Errors `VeilidAPIError::Generic` if `key` or `secret` carries the wrong kind or length, or
70    /// (on a cache miss) `VeilidAPIError::Internal` if `key` is not a valid curve point and
71    /// `VeilidAPIError::Generic` if the exchange is non-contributory.
72    pub async fn cached_dh(
73        &self,
74        key: &PublicKey,
75        secret: &SecretKey,
76    ) -> VeilidAPIResult<SharedSecret> {
77        yielding(|| self.guard.cached_dh(key, secret)).await
78    }
79
80    // Generation
81
82    /// Generate `len` cryptographically random bytes.
83    pub async fn random_bytes(&self, len: usize) -> Bytes {
84        yielding(|| self.guard.random_bytes(len).into()).await
85    }
86
87    /// Hash a password with the given salt, producing a verifier string.
88    ///
89    /// CPU-heavy (Argon2); offloaded to the rayon thread pool off-WASM. No network or disk.
90    ///
91    /// Errors `VeilidAPIError::Generic` if `salt` length is outside the Argon2 bounds or the KDF
92    /// fails, `VeilidAPIError::ParseError` if the salt fails base64 encoding.
93    pub async fn hash_password(&self, password: Bytes, salt: Bytes) -> VeilidAPIResult<String> {
94        let cs = self.guard.clone_arc();
95        let salt = salt.to_vec();
96        cpu_yielding(move || cs.hash_password(&password, &salt)).await
97    }
98    /// Verify a password against a hash produced by `hash_password`.
99    ///
100    /// CPU-heavy (Argon2); offloaded to the rayon thread pool off-WASM. No network or disk.
101    ///
102    /// Returns `Ok(false)` on mismatch. Errors `VeilidAPIError::ParseError` if `password_hash` is
103    /// not a valid PHC string.
104    pub async fn verify_password(
105        &self,
106        password: Bytes,
107        password_hash: &str,
108    ) -> VeilidAPIResult<bool> {
109        let cs = self.guard.clone_arc();
110        let password_hash = password_hash.to_string();
111        cpu_yielding(move || cs.verify_password(&password, &password_hash)).await
112    }
113    /// Derive a shared secret deterministically from a password and salt.
114    ///
115    /// CPU-heavy (Argon2) but run inline before a single yield (not offloaded), so it holds the
116    /// thread for the full KDF. No network or disk.
117    ///
118    /// Errors `VeilidAPIError::Generic` if `salt` length is outside the Argon2 bounds or the KDF fails.
119    pub async fn derive_shared_secret(
120        &self,
121        password: Bytes,
122        salt: Bytes,
123    ) -> VeilidAPIResult<SharedSecret> {
124        yielding(|| self.guard.derive_shared_secret(&password, &salt)).await
125    }
126    /// Generate a random nonce.
127    pub async fn random_nonce(&self) -> Nonce {
128        yielding(|| self.guard.random_nonce()).await
129    }
130    /// Generate a random shared secret.
131    pub async fn random_shared_secret(&self) -> SharedSecret {
132        yielding(|| self.guard.random_shared_secret()).await
133    }
134    /// Compute the Diffie-Hellman shared secret for a public key and secret key.
135    ///
136    /// Local CPU only, offloaded to the rayon thread pool off-WASM; uncached, recomputes every call.
137    /// Use [`cached_dh`](Self::cached_dh) to memoize.
138    ///
139    /// Errors `VeilidAPIError::Internal` if `key` is not a valid curve point, `VeilidAPIError::Generic`
140    /// if the exchange is non-contributory (low-order public key).
141    pub async fn compute_dh(
142        &self,
143        key: &PublicKey,
144        secret: &SecretKey,
145    ) -> VeilidAPIResult<SharedSecret> {
146        let cs = self.guard.clone_arc();
147        let key = key.clone();
148        let secret = secret.clone();
149        cpu_yielding(move || cs.compute_dh(&key, &secret)).await
150    }
151    /// Derive a domain-separated shared secret by hashing the DH result together with `domain` and the Veilid API domain.
152    ///
153    /// Local CPU only; the DH step is offloaded to the rayon thread pool off-WASM (see
154    /// [`compute_dh`](Self::compute_dh)).
155    ///
156    /// Errors with the [`compute_dh`](Self::compute_dh) errors if the key exchange fails.
157    pub async fn generate_shared_secret(
158        &self,
159        key: &PublicKey,
160        secret: &SecretKey,
161        domain: Bytes,
162    ) -> VeilidAPIResult<SharedSecret> {
163        let dh = self.compute_dh(key, secret).await?;
164        let data = [
165            dh.ref_value().bytes().as_ref(),
166            domain.as_ref(),
167            VEILID_DOMAIN_API,
168        ]
169        .concat()
170        .into();
171        let hash = self.generate_hash(data).await;
172        Ok(SharedSecret::new(
173            hash.kind(),
174            BareSharedSecret::new(&hash.into_value()),
175        ))
176    }
177
178    /// Seal a plaintext to a recipient KEM encapsulation key with HPKE base mode (RFC 9180),
179    /// single-shot. `aad` is authenticated but not encrypted. Returns a self-describing sealed blob.
180    ///
181    /// Sealing is one-way: only the recipient can open the blob, and the sealer cannot decrypt
182    /// what it just sealed, unlike the DH shared-secret pattern. Callers who already share a
183    /// symmetric key want [`encrypt_aead`](Self::encrypt_aead) instead.
184    ///
185    /// Local CPU only, offloaded to the rayon thread pool off-WASM (a KEM encapsulation always runs).
186    ///
187    /// Errors `VeilidAPIError::InvalidArgument` if `recipient` is not a valid key,
188    /// `VeilidAPIError::Generic` if encapsulation fails (including a low-order key).
189    pub async fn hpke_seal(
190        &self,
191        recipient: &EncapsulationKey,
192        aad: Bytes,
193        plaintext: Bytes,
194    ) -> VeilidAPIResult<Bytes> {
195        let cs = self.guard.clone_arc();
196        let recipient = recipient.clone();
197        cpu_yielding(move || Ok(cs.hpke_seal(&recipient, &aad, &plaintext)?.into())).await
198    }
199
200    /// Open a sealed blob produced by [`hpke_seal`](Self::hpke_seal) with the recipient KEM
201    /// decapsulation key. `aad` must match what was supplied at seal. Only the recipient can
202    /// open a sealed blob; the sealer cannot.
203    ///
204    /// Local CPU only, offloaded to the rayon thread pool off-WASM (a KEM decapsulation always runs).
205    ///
206    /// Errors `VeilidAPIError::ParseError` if the blob is truncated or its version is unknown,
207    /// `VeilidAPIError::InvalidArgument` if the blob's kind is not this cryptosystem's kind or
208    /// `secret` is not a valid key, `VeilidAPIError::Generic` if decryption fails (tampered blob,
209    /// wrong recipient, or mismatched `aad`).
210    pub async fn hpke_open(
211        &self,
212        secret: &DecapsulationKey,
213        aad: Bytes,
214        sealed: Bytes,
215    ) -> VeilidAPIResult<Bytes> {
216        let cs = self.guard.clone_arc();
217        let secret = secret.clone();
218        cpu_yielding(move || Ok(cs.hpke_open(&secret, &aad, &sealed)?.into())).await
219    }
220
221    /// Generate a new keypair.
222    pub async fn generate_keypair(&self) -> KeyPair {
223        yielding(|| self.guard.generate_keypair()).await
224    }
225
226    /// Generate a new KEM key pair.
227    pub async fn generate_kem_keypair(&self) -> KemKeyPair {
228        yielding(|| self.guard.generate_kem_keypair()).await
229    }
230
231    /// Derive the KEM encapsulation key corresponding to a signing public key.
232    ///
233    /// VLD0-only bridge (ed25519 to x25519); kinds whose signing and KEM keys are unrelated error
234    /// `VeilidAPIError::Unimplemented`.
235    ///
236    /// Errors `VeilidAPIError::InvalidArgument` if `key` is not a valid signing public key.
237    pub async fn encapsulation_key_from_signing_key(
238        &self,
239        key: &PublicKey,
240    ) -> VeilidAPIResult<EncapsulationKey> {
241        yielding(|| self.guard.encapsulation_key_from_signing_key(key)).await
242    }
243
244    /// Derive the KEM decapsulation key corresponding to a signing secret key.
245    ///
246    /// VLD0-only bridge (ed25519 to x25519); kinds whose signing and KEM keys are unrelated error
247    /// `VeilidAPIError::Unimplemented`.
248    ///
249    /// Errors `VeilidAPIError::InvalidArgument` if `secret` is not a valid signing secret key.
250    pub async fn decapsulation_key_from_signing_secret(
251        &self,
252        secret: &SecretKey,
253    ) -> VeilidAPIResult<DecapsulationKey> {
254        yielding(|| self.guard.decapsulation_key_from_signing_secret(secret)).await
255    }
256
257    /// Hash a byte buffer.
258    pub async fn generate_hash(&self, data: Bytes) -> HashDigest {
259        yielding(|| self.guard.generate_hash(&data)).await
260    }
261
262    /// Hash the entire contents of a reader.
263    ///
264    /// Errors `VeilidAPIError::Generic` if reading from `reader` fails.
265    pub async fn generate_hash_reader(
266        &self,
267        reader: &mut dyn std::io::Read,
268    ) -> VeilidAPIResult<PublicKey> {
269        yielding(|| self.guard.generate_hash_reader(reader)).await
270    }
271
272    // Validation
273
274    /// Length in bytes of a shared secret.
275    #[must_use]
276    pub fn shared_secret_length(&self) -> usize {
277        self.guard.shared_secret_length()
278    }
279    /// Length in bytes of a nonce.
280    #[must_use]
281    pub fn nonce_length(&self) -> usize {
282        self.guard.nonce_length()
283    }
284    /// Length in bytes of a hash digest.
285    #[must_use]
286    pub fn hash_digest_length(&self) -> usize {
287        self.guard.hash_digest_length()
288    }
289    /// Length in bytes of a public key.
290    #[must_use]
291    pub fn public_key_length(&self) -> usize {
292        self.guard.public_key_length()
293    }
294    /// Length in bytes of a secret key.
295    #[must_use]
296    pub fn secret_key_length(&self) -> usize {
297        self.guard.secret_key_length()
298    }
299    /// Length in bytes of a KEM encapsulation key.
300    #[must_use]
301    pub fn encapsulation_key_length(&self) -> usize {
302        self.guard.encapsulation_key_length()
303    }
304    /// Length in bytes of a KEM decapsulation key.
305    #[must_use]
306    pub fn decapsulation_key_length(&self) -> usize {
307        self.guard.decapsulation_key_length()
308    }
309    /// Length in bytes of a signature.
310    #[must_use]
311    pub fn signature_length(&self) -> usize {
312        self.guard.signature_length()
313    }
314    /// Number of extra bytes an AEAD operation adds to the ciphertext.
315    #[must_use]
316    pub fn aead_overhead(&self) -> usize {
317        self.guard.aead_overhead()
318    }
319    /// Default salt length in bytes for password hashing.
320    #[must_use]
321    pub fn default_salt_length(&self) -> usize {
322        self.guard.default_salt_length()
323    }
324    /// Validate that a shared secret is well-formed for this cryptosystem.
325    ///
326    /// Errors `VeilidAPIError::Generic` if `secret` has the wrong kind or length.
327    pub fn check_shared_secret(&self, secret: &SharedSecret) -> VeilidAPIResult<()> {
328        self.guard.check_shared_secret(secret)
329    }
330    /// Validate that a nonce is well-formed for this cryptosystem.
331    ///
332    /// Errors `VeilidAPIError::Generic` if `nonce` has the wrong length.
333    pub fn check_nonce(&self, nonce: &Nonce) -> VeilidAPIResult<()> {
334        self.guard.check_nonce(nonce)
335    }
336    /// Validate that a hash digest is well-formed for this cryptosystem.
337    ///
338    /// Errors `VeilidAPIError::Generic` if `hash` has the wrong kind or length.
339    pub fn check_hash_digest(&self, hash: &HashDigest) -> VeilidAPIResult<()> {
340        self.guard.check_hash_digest(hash)
341    }
342    /// Validate that a public key is well-formed for this cryptosystem.
343    ///
344    /// Errors `VeilidAPIError::Generic` if `key` has the wrong kind or length.
345    pub fn check_public_key(&self, key: &PublicKey) -> VeilidAPIResult<()> {
346        self.guard.check_public_key(key)
347    }
348    /// Validate that a secret key is well-formed for this cryptosystem.
349    ///
350    /// Errors `VeilidAPIError::Generic` if `key` has the wrong kind or length.
351    pub fn check_secret_key(&self, key: &SecretKey) -> VeilidAPIResult<()> {
352        self.guard.check_secret_key(key)
353    }
354    /// Validate that a signature is well-formed for this cryptosystem.
355    ///
356    /// Errors `VeilidAPIError::Generic` if `signature` has the wrong kind or length.
357    pub fn check_signature(&self, signature: &Signature) -> VeilidAPIResult<()> {
358        self.guard.check_signature(signature)
359    }
360    /// Validate that a keypair is well-formed for this cryptosystem. Structural check only; see
361    /// [`validate_keypair`](Self::validate_keypair).
362    ///
363    /// Errors `VeilidAPIError::Generic` if the pair or either key has the wrong kind or length.
364    pub fn check_keypair(&self, keypair: &KeyPair) -> VeilidAPIResult<()> {
365        self.guard.check_keypair(keypair)
366    }
367    /// Check that a public key and secret key form a valid keypair.
368    ///
369    /// Returns `Ok(false)` if they do not match. Errors `VeilidAPIError::Generic` if `key` or
370    /// `secret` has the wrong kind or length.
371    pub async fn validate_keypair(
372        &self,
373        key: &PublicKey,
374        secret: &SecretKey,
375    ) -> VeilidAPIResult<bool> {
376        yielding(|| self.guard.validate_keypair(key, secret)).await
377    }
378
379    /// Check that a buffer hashes to the given digest.
380    ///
381    /// Errors `VeilidAPIError::Generic` if `hash` has the wrong kind or length.
382    pub async fn validate_hash(&self, data: Bytes, hash: &HashDigest) -> VeilidAPIResult<bool> {
383        yielding(|| self.guard.validate_hash(&data, hash)).await
384    }
385
386    /// Check that a reader's contents hash to the given digest.
387    ///
388    /// Errors `VeilidAPIError::Generic` if `hash` has the wrong kind or length, or if reading from
389    /// `reader` fails.
390    pub async fn validate_hash_reader(
391        &self,
392        reader: &mut dyn std::io::Read,
393        hash: &HashDigest,
394    ) -> VeilidAPIResult<bool> {
395        yielding(|| self.guard.validate_hash_reader(reader, hash)).await
396    }
397
398    // Authentication
399
400    /// Sign a buffer with a keypair, returning a detached signature.
401    ///
402    /// Local CPU only, offloaded to the rayon thread pool off-WASM.
403    ///
404    /// Errors `VeilidAPIError::Generic` if `public_key` or `secret` has the wrong kind or length,
405    /// `VeilidAPIError::ParseError` if they do not form a valid ed25519 keypair,
406    /// `VeilidAPIError::Internal` if signing fails.
407    pub async fn sign(
408        &self,
409        public_key: &PublicKey,
410        secret: &SecretKey,
411        data: Bytes,
412    ) -> VeilidAPIResult<Signature> {
413        let cs = self.guard.clone_arc();
414        let public_key = public_key.clone();
415        let secret = secret.clone();
416        cpu_yielding(move || cs.sign(&public_key, &secret, &data)).await
417    }
418
419    /// Sign the bytes in `range` and write the signature into `data` at `sig_idx`, returning the buffer.
420    ///
421    /// Local CPU only, offloaded to the rayon thread pool off-WASM.
422    ///
423    /// Errors `VeilidAPIError::Generic` if `public_key` or `secret` has the wrong kind or length,
424    /// `VeilidAPIError::ParseError` if they do not form a valid ed25519 keypair or `sig_idx` is out
425    /// of bounds, `VeilidAPIError::InvalidArgument` if `range` is out of bounds,
426    /// `VeilidAPIError::Internal` if signing fails.
427    pub async fn sign_in_place(
428        &self,
429        public_key: &PublicKey,
430        secret: &SecretKey,
431        mut data: BytesMut,
432        range: Range<usize>,
433        sig_idx: usize,
434    ) -> VeilidAPIResult<BytesMut> {
435        let cs = self.guard.clone_arc();
436        let public_key = public_key.clone();
437        let secret = secret.clone();
438        cpu_yielding(move || {
439            cs.sign_in_place(&public_key, &secret, &mut data, range, sig_idx)?;
440            Ok(data)
441        })
442        .await
443    }
444
445    /// Verify a detached signature over a buffer against a public key.
446    ///
447    /// Local CPU only, offloaded to the rayon thread pool off-WASM.
448    ///
449    /// Returns `Ok(false)` if the signature does not match. Errors `VeilidAPIError::Generic` if
450    /// `public_key` or `signature` has the wrong kind or length, `VeilidAPIError::ParseError` if
451    /// `public_key` is not a valid ed25519 point.
452    pub async fn verify(
453        &self,
454        public_key: &PublicKey,
455        data: Bytes,
456        signature: &Signature,
457    ) -> VeilidAPIResult<bool> {
458        let cs = self.guard.clone_arc();
459        let public_key = public_key.clone();
460        let signature = signature.clone();
461        cpu_yielding(move || cs.verify(&public_key, &data, &signature)).await
462    }
463
464    /// Verify the signature at `sig_idx` over the bytes in `range` of `data` against a public key.
465    ///
466    /// Local CPU only, offloaded to the rayon thread pool off-WASM.
467    ///
468    /// Returns `Ok(false)` if the signature does not match. Errors `VeilidAPIError::Generic` if
469    /// `public_key` has the wrong kind or length, `VeilidAPIError::ParseError` if `public_key` is
470    /// not a valid ed25519 point, `VeilidAPIError::Internal` if `range` or `sig_idx` is out of bounds.
471    pub async fn verify_in_place(
472        &self,
473        public_key: &PublicKey,
474        data: Bytes,
475        range: Range<usize>,
476        sig_idx: usize,
477    ) -> VeilidAPIResult<bool> {
478        let cs = self.guard.clone_arc();
479        let public_key = public_key.clone();
480        cpu_yielding(move || cs.verify_in_place(&public_key, &data, range, sig_idx)).await
481    }
482
483    // AEAD Encrypt/Decrypt
484
485    /// Decrypt and authenticate an AEAD ciphertext into a new buffer.
486    ///
487    /// Local CPU only; offloaded to the rayon thread pool off-WASM once the buffer exceeds the scaling
488    /// threshold, else run inline.
489    ///
490    /// Errors `VeilidAPIError::Generic` if `nonce` or `shared_secret` has the wrong kind or length,
491    /// or if authentication fails (tampered ciphertext, wrong key/nonce, or mismatched
492    /// `associated_data`); `VeilidAPIError::Internal` on an internal length conversion failure.
493    pub async fn decrypt_aead(
494        &self,
495        body: Bytes,
496        nonce: &Nonce,
497        shared_secret: &SharedSecret,
498        associated_data: Option<Bytes>,
499    ) -> VeilidAPIResult<Bytes> {
500        let cs = self.guard.clone_arc();
501        let nonce = nonce.clone();
502        let shared_secret = shared_secret.clone();
503        scaled_yielding(body.len(), 1024, 8192, move || {
504            Ok(cs
505                .decrypt_aead(&body, &nonce, &shared_secret, associated_data.as_deref())?
506                .into())
507        })
508        .await
509    }
510    /// Decrypt and authenticate an AEAD ciphertext in place, returning the truncated plaintext buffer.
511    ///
512    /// Local CPU only; offloaded to the rayon thread pool off-WASM for large buffers, else run inline.
513    ///
514    /// Errors `VeilidAPIError::Generic` if `shared_secret` has the wrong kind or length, or if
515    /// authentication fails (tampered ciphertext, wrong key/nonce, or mismatched `associated_data`);
516    /// `VeilidAPIError::Internal` on an internal length conversion failure.
517    pub async fn decrypt_in_place_aead(
518        &self,
519        mut body: BytesMut,
520        nonce: &Nonce,
521        shared_secret: &SharedSecret,
522        associated_data: Option<Bytes>,
523    ) -> VeilidAPIResult<BytesMut> {
524        let cs = self.guard.clone_arc();
525        let nonce = nonce.clone();
526        let shared_secret = shared_secret.clone();
527        scaled_yielding(body.len(), 1024, 8192, move || {
528            cs.decrypt_in_place_aead(
529                &mut body,
530                &nonce,
531                &shared_secret,
532                associated_data.as_deref(),
533            )?;
534
535            Ok(body)
536        })
537        .await
538    }
539
540    /// Encrypt and authenticate a buffer with AEAD into a new ciphertext.
541    ///
542    /// Local CPU only; offloaded to the rayon thread pool off-WASM for large buffers, else run inline.
543    ///
544    /// Errors `VeilidAPIError::Generic` if `nonce` or `shared_secret` has the wrong kind or length;
545    /// `VeilidAPIError::Internal` on an internal length conversion failure.
546    pub async fn encrypt_aead(
547        &self,
548        body: Bytes,
549        nonce: &Nonce,
550        shared_secret: &SharedSecret,
551        associated_data: Option<Bytes>,
552    ) -> VeilidAPIResult<Bytes> {
553        let cs = self.guard.clone_arc();
554        let nonce = nonce.clone();
555        let shared_secret = shared_secret.clone();
556        scaled_yielding(body.len(), 1024, 8192, move || {
557            Ok(cs
558                .encrypt_aead(&body, &nonce, &shared_secret, associated_data.as_deref())?
559                .into())
560        })
561        .await
562    }
563
564    /// Encrypt and authenticate a buffer with AEAD in place, appending the authentication tag.
565    ///
566    /// Local CPU only; offloaded to the rayon thread pool off-WASM for large buffers, else run inline.
567    ///
568    /// Errors `VeilidAPIError::Generic` if `nonce` or `shared_secret` has the wrong kind or length;
569    /// `VeilidAPIError::Internal` on an internal length conversion failure.
570    pub async fn encrypt_in_place_aead(
571        &self,
572        mut body: BytesMut,
573        nonce: &Nonce,
574        shared_secret: &SharedSecret,
575        associated_data: Option<Bytes>,
576    ) -> VeilidAPIResult<BytesMut> {
577        let cs = self.guard.clone_arc();
578        let nonce = nonce.clone();
579        let shared_secret = shared_secret.clone();
580        scaled_yielding(body.len(), 1024, 8192, move || {
581            cs.encrypt_in_place_aead(
582                &mut body,
583                &nonce,
584                &shared_secret,
585                associated_data.as_deref(),
586            )?;
587
588            Ok(body)
589        })
590        .await
591    }
592
593    // NoAuth Encrypt/Decrypt
594
595    /// Unauthenticated buffer-to-buffer crypt: transform `in_buf` into `out_buf` starting at `out_idx`.
596    ///
597    /// Local CPU only; offloaded to the rayon thread pool off-WASM for large buffers, else run inline.
598    ///
599    /// Errors `VeilidAPIError::Generic` if `nonce` or `shared_secret` has the wrong kind or length;
600    /// `VeilidAPIError::Internal` on an internal length conversion failure.
601    pub async fn crypt_b2b_no_auth(
602        &self,
603        in_buf: Bytes,
604        mut out_buf: BytesMut,
605        out_idx: usize,
606        nonce: &Nonce,
607        shared_secret: &SharedSecret,
608    ) -> VeilidAPIResult<BytesMut> {
609        let cs = self.guard.clone_arc();
610        let nonce = nonce.clone();
611        let shared_secret = shared_secret.clone();
612        scaled_yielding(in_buf.len(), 1024, 8192, move || {
613            cs.crypt_b2b_no_auth(
614                &in_buf,
615                &mut out_buf[out_idx..out_idx + in_buf.len()],
616                &nonce,
617                &shared_secret,
618            )?;
619            Ok(out_buf)
620        })
621        .await
622    }
623
624    /// Unauthenticated in-place crypt of the bytes in `range`.
625    ///
626    /// Local CPU only; offloaded to the rayon thread pool off-WASM for large buffers, else run inline.
627    ///
628    /// Errors `VeilidAPIError::Internal` if `range` is out of bounds, `VeilidAPIError::Generic` if
629    /// `nonce` or `shared_secret` has the wrong kind or length.
630    pub async fn crypt_in_place_no_auth(
631        &self,
632        mut body: BytesMut,
633        range: Range<usize>,
634        nonce: &Nonce,
635        shared_secret: &SharedSecret,
636    ) -> VeilidAPIResult<BytesMut> {
637        let cs = self.guard.clone_arc();
638        let nonce = nonce.clone();
639        let shared_secret = shared_secret.clone();
640        scaled_yielding(body.len(), 1024, 8192, move || {
641            cs.crypt_in_place_no_auth(
642                body.as_mut()
643                    .get_mut(range)
644                    .ok_or_else(|| VeilidAPIError::internal("range is out of bounds"))?,
645                &nonce,
646                &shared_secret,
647            )?;
648            Ok(body)
649        })
650        .await
651    }
652
653    /// Unauthenticated crypt into a fresh 8-byte-aligned output buffer.
654    ///
655    /// Local CPU only; offloaded to the rayon thread pool off-WASM for large buffers, else run inline.
656    ///
657    /// Errors `VeilidAPIError::Generic` if `nonce` or `shared_secret` has the wrong kind or length;
658    /// `VeilidAPIError::Internal` on an internal length conversion failure.
659    pub async fn crypt_no_auth_aligned_8(
660        &self,
661        body: Bytes,
662        nonce: &Nonce,
663        shared_secret: &SharedSecret,
664    ) -> VeilidAPIResult<Vec<u8>> {
665        let cs = self.guard.clone_arc();
666        let nonce = nonce.clone();
667        let shared_secret = shared_secret.clone();
668        scaled_yielding(body.len(), 1024, 8192, move || {
669            cs.crypt_no_auth_aligned_8(&body, &nonce, &shared_secret)
670        })
671        .await
672    }
673
674    /// Unauthenticated crypt into a fresh unaligned output buffer.
675    ///
676    /// Local CPU only; offloaded to the rayon thread pool off-WASM for large buffers, else run inline.
677    ///
678    /// Errors `VeilidAPIError::Generic` if `nonce` or `shared_secret` has the wrong kind or length;
679    /// `VeilidAPIError::Internal` on an internal length conversion failure.
680    pub async fn crypt_no_auth_unaligned(
681        &self,
682        body: Bytes,
683        nonce: &Nonce,
684        shared_secret: &SharedSecret,
685    ) -> VeilidAPIResult<Vec<u8>> {
686        let cs = self.guard.clone_arc();
687        let nonce = nonce.clone();
688        let shared_secret = shared_secret.clone();
689        scaled_yielding(body.len(), 1024, 8192, move || {
690            cs.crypt_no_auth_unaligned(&body, &nonce, &shared_secret)
691        })
692        .await
693    }
694}