Skip to main content

veilid_core/crypto/crypto_system/vld0/
mod.rs

1pub mod sizes;
2
3use super::*;
4
5use argon2::{
6    password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, Salt, SaltString},
7    Argon2,
8};
9use chacha20::cipher::{KeyIvInit, StreamCipher};
10use chacha20::XChaCha20;
11use chacha20poly1305 as ch;
12use chacha20poly1305::aead::AeadInOut as _;
13use chacha20poly1305::KeyInit as _;
14use curve25519_dalek::digest::Digest as _;
15use ed25519_dalek as ed;
16use x25519_dalek as xd;
17
18pub use chacha20poly1305::aead::Buffer;
19
20pub struct BufferWrapper<'a> {
21    buffer: &'a mut dyn CryptoSystemBuffer,
22}
23
24impl<'a> BufferWrapper<'a> {
25    pub fn new(buffer: &'a mut dyn CryptoSystemBuffer) -> Self {
26        Self { buffer }
27    }
28}
29impl<'a> AsRef<[u8]> for BufferWrapper<'a> {
30    fn as_ref(&self) -> &[u8] {
31        self.buffer.as_ref()
32    }
33}
34impl<'a> AsMut<[u8]> for BufferWrapper<'a> {
35    fn as_mut(&mut self) -> &mut [u8] {
36        self.buffer.as_mut()
37    }
38}
39impl<'a> ch::aead::Buffer for BufferWrapper<'a> {
40    fn len(&self) -> usize {
41        self.buffer.len()
42    }
43    fn is_empty(&self) -> bool {
44        self.buffer.is_empty()
45    }
46    fn extend_from_slice(&mut self, other: &[u8]) -> Result<(), ch::Error> {
47        self.buffer.extend_from_slice(other);
48        Ok(())
49    }
50    fn truncate(&mut self, len: usize) {
51        self.buffer.truncate(len)
52    }
53}
54
55const VLD0_DOMAIN_SIGN: &[u8] = b"VLD0_SIGN";
56const VLD0_DOMAIN_CRYPT: &[u8] = b"VLD0_CRYPT";
57
58const VLD0_AEAD_OVERHEAD: usize = 16;
59pub const CRYPTO_KIND_VLD0: CryptoKind = CryptoKind::new(*b"VLD0");
60pub const CRYPTO_KIND_VLD0_FOURCC: u32 = u32::from_be_bytes(*b"VLD0");
61pub use sizes::*;
62
63fn public_to_x25519_pk(public: &PublicKey) -> VeilidAPIResult<xd::PublicKey> {
64    let pk_ed = ed::VerifyingKey::from_bytes(
65        public
66            .ref_value()
67            .as_ref()
68            .try_into()
69            .map_err(VeilidAPIError::internal)?,
70    )
71    .map_err(VeilidAPIError::internal)?;
72    Ok(xd::PublicKey::from(*pk_ed.to_montgomery().as_bytes()))
73}
74fn secret_to_x25519_sk(secret: &SecretKey) -> VeilidAPIResult<xd::StaticSecret> {
75    // NOTE: ed::SigningKey.to_scalar() does not produce an unreduced scalar, we want the raw bytes here
76    // See https://github.com/dalek-cryptography/curve25519-dalek/issues/565
77    let hash: [u8; VLD0_SIGNATURE_LENGTH] = ed::Sha512::default()
78        .chain_update(secret.ref_value().bytes())
79        .finalize()
80        .into();
81    let mut output = [0u8; VLD0_SECRET_KEY_LENGTH];
82    output.copy_from_slice(&hash[..VLD0_SECRET_KEY_LENGTH]);
83
84    Ok(xd::StaticSecret::from(output))
85}
86
87pub(crate) fn vld0_generate_keypair() -> KeyPair {
88    let mut csprng = VeilidRng {};
89    let signing_key = ed::SigningKey::generate(&mut csprng);
90    let verifying_key = signing_key.verifying_key();
91    let public_key = BarePublicKey::new(&verifying_key.to_bytes());
92    let secret_key = BareSecretKey::new(&signing_key.to_bytes());
93
94    KeyPair::new(CRYPTO_KIND_VLD0, BareKeyPair::new(public_key, secret_key))
95}
96
97/// V0 CryptoSystem
98pub(crate) struct CryptoSystemVLD0 {
99    registry: VeilidComponentRegistry,
100}
101
102impl CryptoSystemVLD0 {
103    #[must_use]
104    pub(crate) fn new(registry: VeilidComponentRegistry) -> Self {
105        Self { registry }
106    }
107}
108
109impl CryptoSystem for CryptoSystemVLD0 {
110    // Accessors
111    fn kind(&self) -> CryptoKind {
112        CRYPTO_KIND_VLD0
113    }
114
115    fn crypto(&self) -> VeilidComponentGuard<'_, Crypto> {
116        self.registry.lookup::<Crypto>().unwrap_or_log()
117    }
118
119    // Cached Operations
120    #[cfg_attr(feature = "instrument", instrument(level = "trace", skip_all, fields(__VEILID_LOG_KEY = self.registry.log_key())))]
121    fn cached_dh(&self, key: &PublicKey, secret: &SecretKey) -> VeilidAPIResult<SharedSecret> {
122        self.crypto()
123            .cached_dh_internal::<CryptoSystemVLD0>(self, key, secret)
124    }
125
126    // Generation
127    #[cfg_attr(
128        feature = "instrument",
129        instrument(level = "trace", target = "crypto", skip_all, fields(__VEILID_LOG_KEY = self.registry.log_key()))
130    )]
131    fn random_bytes(&self, len: usize) -> Vec<u8> {
132        let mut bytes = unsafe { unaligned_u8_vec_uninit(len) };
133        random_bytes(bytes.as_mut());
134        bytes
135    }
136
137    #[cfg_attr(
138        feature = "instrument",
139        instrument(level = "trace", target = "crypto", skip_all, fields(__VEILID_LOG_KEY = self.registry.log_key()))
140    )]
141    fn hash_password(&self, password: &[u8], salt: &[u8]) -> VeilidAPIResult<String> {
142        if salt.len() < Salt::MIN_LENGTH || salt.len() > Salt::MAX_LENGTH {
143            apibail_generic!("invalid salt length");
144        }
145
146        // Hash password to PHC string ($argon2id$v=19$...)
147        let salt = SaltString::encode_b64(salt).map_err(VeilidAPIError::generic)?;
148
149        // Argon2 with default params (Argon2id v19)
150        let argon2 = Argon2::default();
151
152        let password_hash = argon2
153            .hash_password(password, &salt)
154            .map_err(VeilidAPIError::generic)?
155            .to_string();
156        Ok(password_hash)
157    }
158    #[cfg_attr(
159        feature = "instrument",
160        instrument(level = "trace", target = "crypto", skip_all, fields(__VEILID_LOG_KEY = self.registry.log_key()))
161    )]
162    fn verify_password(&self, password: &[u8], password_hash: &str) -> VeilidAPIResult<bool> {
163        let parsed_hash = PasswordHash::new(password_hash).map_err(VeilidAPIError::generic)?;
164        // Argon2 with default params (Argon2id v19)
165        let argon2 = Argon2::default();
166
167        Ok(argon2.verify_password(password, &parsed_hash).is_ok())
168    }
169
170    #[cfg_attr(
171        feature = "instrument",
172        instrument(level = "trace", target = "crypto", skip_all, fields(__VEILID_LOG_KEY = self.registry.log_key()))
173    )]
174    fn derive_shared_secret(&self, password: &[u8], salt: &[u8]) -> VeilidAPIResult<SharedSecret> {
175        if salt.len() < Salt::MIN_LENGTH || salt.len() > Salt::MAX_LENGTH {
176            apibail_generic!("invalid salt length");
177        }
178
179        // Argon2 with default params (Argon2id v19)
180        let argon2 = Argon2::default();
181
182        let mut output_key_material = [0u8; VLD0_SHARED_SECRET_LENGTH];
183        argon2
184            .hash_password_into(password, salt, &mut output_key_material)
185            .map_err(VeilidAPIError::generic)?;
186        Ok(SharedSecret::new(
187            CRYPTO_KIND_VLD0,
188            BareSharedSecret::new(&output_key_material),
189        ))
190    }
191
192    #[cfg_attr(
193        feature = "instrument",
194        instrument(level = "trace", target = "crypto", skip_all, fields(__VEILID_LOG_KEY = self.registry.log_key()))
195    )]
196    fn random_nonce(&self) -> Nonce {
197        let mut nonce = [0u8; VLD0_NONCE_LENGTH];
198        random_bytes(&mut nonce);
199        Nonce::new(&nonce)
200    }
201
202    #[cfg_attr(
203        feature = "instrument",
204        instrument(level = "trace", target = "crypto", skip_all, fields(__VEILID_LOG_KEY = self.registry.log_key()))
205    )]
206    fn random_shared_secret(&self) -> SharedSecret {
207        let mut s = [0u8; VLD0_SHARED_SECRET_LENGTH];
208        random_bytes(&mut s);
209        SharedSecret::new(CRYPTO_KIND_VLD0, BareSharedSecret::new(&s))
210    }
211
212    #[cfg_attr(
213        feature = "instrument",
214        instrument(level = "trace", target = "crypto", skip_all, fields(__VEILID_LOG_KEY = self.registry.log_key()))
215    )]
216    fn compute_dh(&self, key: &PublicKey, secret: &SecretKey) -> VeilidAPIResult<SharedSecret> {
217        let pk_xd = public_to_x25519_pk(key)?;
218        let sk_xd = secret_to_x25519_sk(secret)?;
219
220        let dh = sk_xd.diffie_hellman(&pk_xd);
221        if !dh.was_contributory() {
222            apibail_generic!("dh was not contributory");
223        }
224        let dh_bytes = dh.to_bytes();
225
226        let mut hasher = blake3::Hasher::new();
227        hasher.update(VLD0_DOMAIN_CRYPT);
228        hasher.update(&dh_bytes);
229        let output = hasher.finalize();
230
231        Ok(SharedSecret::new(
232            CRYPTO_KIND_VLD0,
233            BareSharedSecret::new(output.as_bytes()),
234        ))
235    }
236
237    #[cfg_attr(
238        feature = "instrument",
239        instrument(level = "trace", target = "crypto", skip_all, fields(__VEILID_LOG_KEY = self.registry.log_key()))
240    )]
241    fn generate_keypair(&self) -> KeyPair {
242        vld0_generate_keypair()
243    }
244
245    #[cfg_attr(
246        feature = "instrument",
247        instrument(level = "trace", target = "crypto", skip_all, fields(__VEILID_LOG_KEY = self.registry.log_key()))
248    )]
249    fn generate_hash(&self, data: &[u8]) -> HashDigest {
250        HashDigest::new(
251            CRYPTO_KIND_VLD0,
252            BareHashDigest::new(blake3::hash(data).as_bytes()),
253        )
254    }
255
256    #[cfg_attr(
257        feature = "instrument",
258        instrument(level = "trace", target = "crypto", skip_all, fields(__VEILID_LOG_KEY = self.registry.log_key()))
259    )]
260    fn generate_hash_reader(&self, reader: &mut dyn std::io::Read) -> VeilidAPIResult<PublicKey> {
261        let mut hasher = blake3::Hasher::new();
262        std::io::copy(reader, &mut hasher).map_err(VeilidAPIError::generic)?;
263        Ok(PublicKey::new(
264            CRYPTO_KIND_VLD0,
265            BarePublicKey::new(hasher.finalize().as_bytes()),
266        ))
267    }
268
269    // Validation
270    fn shared_secret_length(&self) -> usize {
271        VLD0_SHARED_SECRET_LENGTH
272    }
273    fn nonce_length(&self) -> usize {
274        VLD0_NONCE_LENGTH
275    }
276    fn hash_digest_length(&self) -> usize {
277        VLD0_HASH_DIGEST_LENGTH
278    }
279    fn public_key_length(&self) -> usize {
280        VLD0_PUBLIC_KEY_LENGTH
281    }
282    fn secret_key_length(&self) -> usize {
283        VLD0_SECRET_KEY_LENGTH
284    }
285    fn signature_length(&self) -> usize {
286        VLD0_SIGNATURE_LENGTH
287    }
288    fn default_salt_length(&self) -> usize {
289        16
290    }
291    fn aead_overhead(&self) -> usize {
292        VLD0_AEAD_OVERHEAD
293    }
294
295    #[cfg_attr(
296        feature = "instrument",
297        instrument(level = "trace", target = "crypto", skip_all, fields(__VEILID_LOG_KEY = self.registry.log_key()))
298    )]
299    fn validate_keypair(
300        &self,
301        public_key: &PublicKey,
302        secret_key: &SecretKey,
303    ) -> VeilidAPIResult<bool> {
304        self.check_public_key(public_key)?;
305        self.check_secret_key(secret_key)?;
306
307        let data = vec![0u8; 512];
308        let Ok(sig) = self.sign(public_key, secret_key, &data) else {
309            return Ok(false);
310        };
311        let Ok(v) = self.verify(public_key, &data, &sig) else {
312            return Ok(false);
313        };
314        Ok(v)
315    }
316
317    #[cfg_attr(
318        feature = "instrument",
319        instrument(level = "trace", target = "crypto", skip_all, fields(__VEILID_LOG_KEY = self.registry.log_key()))
320    )]
321    fn validate_hash(&self, data: &[u8], hash_digest: &HashDigest) -> VeilidAPIResult<bool> {
322        self.check_hash_digest(hash_digest)?;
323
324        let bytes = *blake3::hash(data).as_bytes();
325
326        Ok(bytes == *hash_digest.ref_value().bytes())
327    }
328
329    #[cfg_attr(
330        feature = "instrument",
331        instrument(level = "trace", target = "crypto", skip_all, fields(__VEILID_LOG_KEY = self.registry.log_key()))
332    )]
333    fn validate_hash_reader(
334        &self,
335        reader: &mut dyn std::io::Read,
336        hash_digest: &HashDigest,
337    ) -> VeilidAPIResult<bool> {
338        self.check_hash_digest(hash_digest)?;
339
340        let mut hasher = blake3::Hasher::new();
341        std::io::copy(reader, &mut hasher).map_err(VeilidAPIError::generic)?;
342        let bytes = *hasher.finalize().as_bytes();
343        Ok(bytes == *hash_digest.ref_value().bytes())
344    }
345
346    // Authentication
347    #[cfg_attr(
348        feature = "instrument",
349        instrument(level = "trace", target = "crypto", skip_all, fields(__VEILID_LOG_KEY = self.registry.log_key()))
350    )]
351    fn sign(
352        &self,
353        public_key: &PublicKey,
354        secret_key: &SecretKey,
355        data: &[u8],
356    ) -> VeilidAPIResult<Signature> {
357        self.check_public_key(public_key)?;
358        self.check_secret_key(secret_key)?;
359
360        let mut kpb = BytesMut::with_capacity(VLD0_SECRET_KEY_LENGTH + VLD0_PUBLIC_KEY_LENGTH);
361
362        kpb.extend_from_slice(secret_key.ref_value().as_ref());
363        kpb.extend_from_slice(public_key.ref_value().as_ref());
364        let keypair = ed::SigningKey::from_keypair_bytes(kpb.as_ref().try_into().unwrap_or_log())
365            .map_err(|e| VeilidAPIError::parse_error("Keypair is invalid", e))?;
366
367        let mut dig: ed::Sha512 = ed::Sha512::default();
368        dig.update(data);
369
370        let sig_bytes = keypair
371            .sign_prehashed(dig, Some(VLD0_DOMAIN_SIGN))
372            .map_err(VeilidAPIError::internal)?;
373
374        let sig = Signature::new(CRYPTO_KIND_VLD0, BareSignature::new(&sig_bytes.to_bytes()));
375
376        Ok(sig)
377    }
378
379    fn sign_in_place(
380        &self,
381        public_key: &PublicKey,
382        secret_key: &SecretKey,
383        data: &mut [u8],
384        range: Range<usize>,
385        sig_idx: usize,
386    ) -> VeilidAPIResult<()> {
387        self.check_public_key(public_key)?;
388        self.check_secret_key(secret_key)?;
389
390        let mut kpb = BytesMut::with_capacity(VLD0_SECRET_KEY_LENGTH + VLD0_PUBLIC_KEY_LENGTH);
391
392        kpb.extend_from_slice(secret_key.ref_value().as_ref());
393        kpb.extend_from_slice(public_key.ref_value().as_ref());
394        let keypair = ed::SigningKey::from_keypair_bytes(kpb.as_ref().try_into().unwrap_or_log())
395            .map_err(|e| VeilidAPIError::parse_error("Keypair is invalid", e))?;
396
397        let mut dig: ed::Sha512 = ed::Sha512::default();
398
399        dig.update(
400            data.get(range)
401                .ok_or_else(|| VeilidAPIError::internal("range is out of bounds"))?,
402        );
403
404        let sig_bytes = keypair
405            .sign_prehashed(dig, Some(VLD0_DOMAIN_SIGN))
406            .map_err(VeilidAPIError::internal)?;
407
408        data.get_mut(sig_idx..sig_idx + VLD0_SIGNATURE_LENGTH)
409            .ok_or_else(|| VeilidAPIError::internal("signature index is out of bounds"))?
410            .copy_from_slice(&sig_bytes.to_bytes());
411
412        Ok(())
413    }
414
415    #[cfg_attr(
416        feature = "instrument",
417        instrument(level = "trace", target = "crypto", skip_all, fields(__VEILID_LOG_KEY = self.registry.log_key()))
418    )]
419    fn verify(
420        &self,
421        public_key: &PublicKey,
422        data: &[u8],
423        signature: &Signature,
424    ) -> VeilidAPIResult<bool> {
425        self.check_public_key(public_key)?;
426        self.check_signature(signature)?;
427
428        let pk = ed::VerifyingKey::from_bytes(
429            public_key
430                .ref_value()
431                .as_ref()
432                .try_into()
433                .map_err(VeilidAPIError::internal)?,
434        )
435        .map_err(|e| VeilidAPIError::parse_error("Public key is invalid", e))?;
436        let sig = ed::Signature::from_bytes(
437            signature
438                .ref_value()
439                .as_ref()
440                .try_into()
441                .map_err(VeilidAPIError::internal)?,
442        );
443
444        let mut dig: ed::Sha512 = ed::Sha512::default();
445        dig.update(data);
446
447        if pk
448            .verify_prehashed_strict(dig, Some(VLD0_DOMAIN_SIGN), &sig)
449            .is_err()
450        {
451            return Ok(false);
452        }
453        Ok(true)
454    }
455
456    fn verify_in_place(
457        &self,
458        public_key: &PublicKey,
459        data: &[u8],
460        range: Range<usize>,
461        sig_idx: usize,
462    ) -> VeilidAPIResult<bool> {
463        self.check_public_key(public_key)?;
464
465        let pk = ed::VerifyingKey::from_bytes(
466            public_key
467                .ref_value()
468                .as_ref()
469                .try_into()
470                .map_err(VeilidAPIError::internal)?,
471        )
472        .map_err(|e| VeilidAPIError::parse_error("Public key is invalid", e))?;
473
474        let mut dig: ed::Sha512 = ed::Sha512::default();
475        dig.update(
476            data.get(range)
477                .ok_or_else(|| VeilidAPIError::internal("range is out of bounds"))?,
478        );
479
480        let sig = data
481            .get(sig_idx..sig_idx + VLD0_SIGNATURE_LENGTH)
482            .ok_or_else(|| VeilidAPIError::internal("signature index is out of bounds"))?;
483        let sig = ed::Signature::from_bytes(sig.try_into().unwrap_or_log());
484        if pk
485            .verify_prehashed_strict(dig, Some(VLD0_DOMAIN_SIGN), &sig)
486            .is_err()
487        {
488            return Ok(false);
489        }
490        Ok(true)
491    }
492
493    // AEAD Encrypt/Decrypt
494    #[cfg_attr(
495        feature = "instrument",
496        instrument(level = "trace", target = "crypto", skip_all, fields(__VEILID_LOG_KEY = self.registry.log_key()))
497    )]
498    fn decrypt_in_place_aead(
499        &self,
500        body: &mut dyn CryptoSystemBuffer,
501        nonce: &Nonce,
502        shared_secret: &SharedSecret,
503        associated_data: Option<&[u8]>,
504    ) -> VeilidAPIResult<()> {
505        self.check_shared_secret(shared_secret)?;
506
507        let shared_secret_bytes: [u8; VLD0_SHARED_SECRET_LENGTH] = shared_secret
508            .ref_value()
509            .as_ref()
510            .try_into()
511            .map_err(VeilidAPIError::internal)?;
512        let nonce_bytes: [u8; VLD0_NONCE_LENGTH] = nonce
513            .as_ref()
514            .try_into()
515            .map_err(VeilidAPIError::internal)?;
516
517        let key = ch::Key::from(shared_secret_bytes);
518        let xnonce = ch::XNonce::from(nonce_bytes);
519        let aead = ch::XChaCha20Poly1305::new(&key);
520        let mut buffer = BufferWrapper::new(body);
521        aead.decrypt_in_place(
522            &xnonce,
523            associated_data.unwrap_or(b""),
524            &mut buffer as &mut dyn ch::aead::Buffer,
525        )
526        .map_err(map_to_string)
527        .map_err(VeilidAPIError::generic)
528    }
529
530    #[cfg_attr(
531        feature = "instrument",
532        instrument(level = "trace", target = "crypto", skip_all, fields(__VEILID_LOG_KEY = self.registry.log_key()))
533    )]
534    fn decrypt_aead(
535        &self,
536        body: &[u8],
537        nonce: &Nonce,
538        shared_secret: &SharedSecret,
539        associated_data: Option<&[u8]>,
540    ) -> VeilidAPIResult<Vec<u8>> {
541        self.check_nonce(nonce)?;
542        self.check_shared_secret(shared_secret)?;
543
544        let mut out = body.to_vec();
545        self.decrypt_in_place_aead(&mut out, nonce, shared_secret, associated_data)
546            .map_err(map_to_string)
547            .map_err(VeilidAPIError::generic)?;
548        Ok(out)
549    }
550
551    #[cfg_attr(
552        feature = "instrument",
553        instrument(level = "trace", target = "crypto", skip_all, fields(__VEILID_LOG_KEY = self.registry.log_key()))
554    )]
555    fn encrypt_in_place_aead(
556        &self,
557        body: &mut dyn CryptoSystemBuffer,
558        nonce: &Nonce,
559        shared_secret: &SharedSecret,
560        associated_data: Option<&[u8]>,
561    ) -> VeilidAPIResult<()> {
562        self.check_nonce(nonce)?;
563        self.check_shared_secret(shared_secret)?;
564
565        let shared_secret_bytes: [u8; VLD0_SHARED_SECRET_LENGTH] = shared_secret
566            .ref_value()
567            .as_ref()
568            .try_into()
569            .map_err(VeilidAPIError::internal)?;
570        let nonce_bytes: [u8; VLD0_NONCE_LENGTH] = nonce
571            .as_ref()
572            .try_into()
573            .map_err(VeilidAPIError::internal)?;
574
575        let key = ch::Key::from(shared_secret_bytes);
576        let xnonce = ch::XNonce::from(nonce_bytes);
577        let aead = ch::XChaCha20Poly1305::new(&key);
578
579        let mut buffer = BufferWrapper::new(body);
580        aead.encrypt_in_place(&xnonce, associated_data.unwrap_or(b""), &mut buffer)
581            .map_err(map_to_string)
582            .map_err(VeilidAPIError::generic)
583    }
584
585    #[cfg_attr(
586        feature = "instrument",
587        instrument(level = "trace", target = "crypto", skip_all, fields(__VEILID_LOG_KEY = self.registry.log_key()))
588    )]
589    fn encrypt_aead(
590        &self,
591        body: &[u8],
592        nonce: &Nonce,
593        shared_secret: &SharedSecret,
594        associated_data: Option<&[u8]>,
595    ) -> VeilidAPIResult<Vec<u8>> {
596        self.check_nonce(nonce)?;
597        self.check_shared_secret(shared_secret)?;
598
599        let mut out = body.to_vec();
600        self.encrypt_in_place_aead(&mut out, nonce, shared_secret, associated_data)
601            .map_err(map_to_string)
602            .map_err(VeilidAPIError::generic)?;
603        Ok(out)
604    }
605
606    // NoAuth Encrypt/Decrypt
607    #[cfg_attr(
608        feature = "instrument",
609        instrument(level = "trace", target = "crypto", skip_all, fields(__VEILID_LOG_KEY = self.registry.log_key()))
610    )]
611    fn crypt_in_place_no_auth(
612        &self,
613        body: &mut [u8],
614        nonce: &Nonce,
615        shared_secret: &SharedSecret,
616    ) -> VeilidAPIResult<()> {
617        self.check_nonce(nonce)?;
618        self.check_shared_secret(shared_secret)?;
619
620        let shared_secret_bytes: [u8; VLD0_SHARED_SECRET_LENGTH] = shared_secret
621            .ref_value()
622            .as_ref()
623            .try_into()
624            .map_err(VeilidAPIError::internal)?;
625        let nonce_bytes: [u8; VLD0_NONCE_LENGTH] = nonce
626            .as_ref()
627            .try_into()
628            .map_err(VeilidAPIError::internal)?;
629        let key = ch::Key::from(shared_secret_bytes);
630        let xnonce = ch::XNonce::from(nonce_bytes);
631
632        let mut cipher = <XChaCha20 as KeyIvInit>::new(&key, &xnonce);
633        cipher.apply_keystream(body);
634        Ok(())
635    }
636
637    #[cfg_attr(
638        feature = "instrument",
639        instrument(level = "trace", target = "crypto", skip_all, fields(__VEILID_LOG_KEY = self.registry.log_key()))
640    )]
641    fn crypt_b2b_no_auth(
642        &self,
643        in_buf: &[u8],
644        out_buf: &mut [u8],
645        nonce: &Nonce,
646        shared_secret: &SharedSecret,
647    ) -> VeilidAPIResult<()> {
648        self.check_nonce(nonce)?;
649        self.check_shared_secret(shared_secret)?;
650
651        let shared_secret_bytes: [u8; VLD0_SHARED_SECRET_LENGTH] = shared_secret
652            .ref_value()
653            .as_ref()
654            .try_into()
655            .map_err(VeilidAPIError::internal)?;
656        let nonce_bytes: [u8; VLD0_NONCE_LENGTH] = nonce
657            .as_ref()
658            .try_into()
659            .map_err(VeilidAPIError::internal)?;
660        let key = ch::Key::from(shared_secret_bytes);
661        let xnonce = ch::XNonce::from(nonce_bytes);
662
663        let mut cipher = <XChaCha20 as KeyIvInit>::new(&key, &xnonce);
664        cipher.apply_keystream_b2b(in_buf, out_buf);
665        Ok(())
666    }
667
668    #[cfg_attr(
669        feature = "instrument",
670        instrument(level = "trace", target = "crypto", skip_all, fields(__VEILID_LOG_KEY = self.registry.log_key()))
671    )]
672    fn crypt_no_auth_aligned_8(
673        &self,
674        in_buf: &[u8],
675        nonce: &Nonce,
676        shared_secret: &SharedSecret,
677    ) -> VeilidAPIResult<Vec<u8>> {
678        self.check_nonce(nonce)?;
679        self.check_shared_secret(shared_secret)?;
680
681        let mut out_buf = unsafe { aligned_8_u8_vec_uninit(in_buf.len()) };
682        self.crypt_b2b_no_auth(in_buf, &mut out_buf, nonce, shared_secret)?;
683        Ok(out_buf)
684    }
685
686    #[cfg_attr(
687        feature = "instrument",
688        instrument(level = "trace", target = "crypto", skip_all, fields(__VEILID_LOG_KEY = self.registry.log_key()))
689    )]
690    fn crypt_no_auth_unaligned(
691        &self,
692        in_buf: &[u8],
693        nonce: &Nonce,
694        shared_secret: &SharedSecret,
695    ) -> VeilidAPIResult<Vec<u8>> {
696        self.check_nonce(nonce)?;
697        self.check_shared_secret(shared_secret)?;
698
699        let mut out_buf = unsafe { unaligned_u8_vec_uninit(in_buf.len()) };
700        self.crypt_b2b_no_auth(in_buf, &mut out_buf, nonce, shared_secret)?;
701        Ok(out_buf)
702    }
703}