Skip to main content

crypto_dispatch/
registry.rs

1// SPDX-FileCopyrightText: Copyright © 2026 ReallyMe LLC. All rights reserved
2//
3// SPDX-License-Identifier: Apache-2.0
4
5use zeroize::Zeroizing;
6
7#[cfg(any(
8    feature = "aes",
9    feature = "aes-gcm-siv",
10    feature = "chacha20-poly1305"
11))]
12use crate::traits::AeadCipherAlgorithm;
13use crate::traits::AeadParams;
14#[cfg(any(
15    feature = "ed25519",
16    feature = "p256",
17    feature = "p384",
18    feature = "p521",
19    feature = "secp256k1",
20    feature = "ml-dsa-44",
21    feature = "ml-dsa-65",
22    feature = "ml-dsa-87"
23))]
24use crate::traits::SignatureAlgorithm;
25use crate::AlgorithmError;
26use crypto_core::{AeadAlgorithm, Algorithm};
27
28//
29// -----------------------------------------------------------------------------
30// Keypair generation (ALL ALGORITHMS)
31// -----------------------------------------------------------------------------
32
33/// Generate a raw keypair for the given algorithm.
34///
35/// This is supported for:
36/// - signature algorithms
37/// - key agreement algorithms
38/// - KEM algorithms
39///
40/// Returns (public_key, secret_key); the secret half zeroizes on drop.
41pub fn generate_keypair(alg: Algorithm) -> Result<(Vec<u8>, Zeroizing<Vec<u8>>), AlgorithmError> {
42    match alg {
43        Algorithm::Ed25519 => {
44            #[cfg(feature = "ed25519")]
45            {
46                crate::algorithms::ed25519::Ed25519Algo::generate_keypair()
47            }
48            #[cfg(not(feature = "ed25519"))]
49            {
50                Err(AlgorithmError::UnsupportedAlgorithm(alg))
51            }
52        }
53        Algorithm::P256 => {
54            #[cfg(feature = "p256")]
55            {
56                crate::algorithms::p256::P256Algo::generate_keypair()
57            }
58            #[cfg(not(feature = "p256"))]
59            {
60                Err(AlgorithmError::UnsupportedAlgorithm(alg))
61            }
62        }
63        Algorithm::P384 => {
64            #[cfg(feature = "p384")]
65            {
66                crate::algorithms::p384::P384Algo::generate_keypair()
67            }
68            #[cfg(not(feature = "p384"))]
69            {
70                Err(AlgorithmError::UnsupportedAlgorithm(alg))
71            }
72        }
73        Algorithm::P521 => {
74            #[cfg(feature = "p521")]
75            {
76                crate::algorithms::p521::P521Algo::generate_keypair()
77            }
78            #[cfg(not(feature = "p521"))]
79            {
80                Err(AlgorithmError::UnsupportedAlgorithm(alg))
81            }
82        }
83        Algorithm::Secp256k1 => {
84            #[cfg(feature = "secp256k1")]
85            {
86                crate::algorithms::secp256k1::Secp256k1Algo::generate_keypair()
87            }
88            #[cfg(not(feature = "secp256k1"))]
89            {
90                Err(AlgorithmError::UnsupportedAlgorithm(alg))
91            }
92        }
93        Algorithm::MlDsa44 => {
94            #[cfg(feature = "ml-dsa-44")]
95            {
96                crate::algorithms::ml_dsa_44::MlDsa44Algo::generate_keypair()
97            }
98            #[cfg(not(feature = "ml-dsa-44"))]
99            {
100                Err(AlgorithmError::UnsupportedAlgorithm(alg))
101            }
102        }
103        Algorithm::MlDsa65 => {
104            #[cfg(feature = "ml-dsa-65")]
105            {
106                crate::algorithms::ml_dsa_65::MlDsa65Algo::generate_keypair()
107            }
108            #[cfg(not(feature = "ml-dsa-65"))]
109            {
110                Err(AlgorithmError::UnsupportedAlgorithm(alg))
111            }
112        }
113        Algorithm::MlDsa87 => {
114            #[cfg(feature = "ml-dsa-87")]
115            {
116                crate::algorithms::ml_dsa_87::MlDsa87Algo::generate_keypair()
117            }
118            #[cfg(not(feature = "ml-dsa-87"))]
119            {
120                Err(AlgorithmError::UnsupportedAlgorithm(alg))
121            }
122        }
123        Algorithm::X25519 => {
124            #[cfg(feature = "x25519")]
125            {
126                crate::algorithms::x25519::X25519Algo::generate_keypair()
127            }
128            #[cfg(not(feature = "x25519"))]
129            {
130                Err(AlgorithmError::UnsupportedAlgorithm(alg))
131            }
132        }
133        Algorithm::MlKem512 => {
134            #[cfg(feature = "ml-kem-512")]
135            {
136                crate::algorithms::ml_kem_512::MlKem512Algo::generate_keypair()
137            }
138            #[cfg(not(feature = "ml-kem-512"))]
139            {
140                Err(AlgorithmError::UnsupportedAlgorithm(alg))
141            }
142        }
143        Algorithm::MlKem768 => {
144            #[cfg(feature = "ml-kem-768")]
145            {
146                crate::algorithms::ml_kem_768::MlKem768Algo::generate_keypair()
147            }
148            #[cfg(not(feature = "ml-kem-768"))]
149            {
150                Err(AlgorithmError::UnsupportedAlgorithm(alg))
151            }
152        }
153        Algorithm::MlKem1024 => {
154            #[cfg(feature = "ml-kem-1024")]
155            {
156                crate::algorithms::ml_kem_1024::MlKem1024Algo::generate_keypair()
157            }
158            #[cfg(not(feature = "ml-kem-1024"))]
159            {
160                Err(AlgorithmError::UnsupportedAlgorithm(alg))
161            }
162        }
163        Algorithm::XWing768 => {
164            #[cfg(feature = "x-wing")]
165            {
166                crate::algorithms::x_wing::XWing768Algo::generate_keypair()
167            }
168            #[cfg(not(feature = "x-wing"))]
169            {
170                Err(AlgorithmError::UnsupportedAlgorithm(alg))
171            }
172        }
173        Algorithm::XWing1024 => {
174            #[cfg(feature = "x-wing")]
175            {
176                crate::algorithms::x_wing::XWing1024Algo::generate_keypair()
177            }
178            #[cfg(not(feature = "x-wing"))]
179            {
180                Err(AlgorithmError::UnsupportedAlgorithm(alg))
181            }
182        }
183    }
184}
185
186//
187// -----------------------------------------------------------------------------
188// Signature algorithms ONLY
189// -----------------------------------------------------------------------------
190
191/// Sign `msg` with `secret` under the selected signature algorithm,
192/// returning the detached signature bytes.
193///
194/// # Examples
195///
196/// ```
197/// use crypto_core::Algorithm;
198/// use crypto_dispatch::{generate_keypair, sign, verify};
199///
200/// # fn main() -> Result<(), crypto_dispatch::AlgorithmError> {
201/// let (public, secret) = generate_keypair(Algorithm::Ed25519)?;
202/// let signature = sign(Algorithm::Ed25519, &secret, b"message")?;
203/// verify(Algorithm::Ed25519, &public, b"message", &signature)?;
204/// # Ok(())
205/// # }
206/// ```
207pub fn sign(alg: Algorithm, secret: &[u8], msg: &[u8]) -> Result<Vec<u8>, AlgorithmError> {
208    #[cfg(not(any(
209        feature = "ed25519",
210        feature = "p256",
211        feature = "p384",
212        feature = "p521",
213        feature = "secp256k1",
214        feature = "ml-dsa-44",
215        feature = "ml-dsa-65",
216        feature = "ml-dsa-87"
217    )))]
218    let _ = (secret, msg);
219
220    match alg {
221        Algorithm::Ed25519 => {
222            #[cfg(feature = "ed25519")]
223            {
224                crate::algorithms::ed25519::Ed25519Algo::sign(secret, msg)
225            }
226            #[cfg(not(feature = "ed25519"))]
227            {
228                Err(AlgorithmError::UnsupportedAlgorithm(alg))
229            }
230        }
231        Algorithm::P256 => {
232            #[cfg(feature = "p256")]
233            {
234                crate::algorithms::p256::P256Algo::sign(secret, msg)
235            }
236            #[cfg(not(feature = "p256"))]
237            {
238                Err(AlgorithmError::UnsupportedAlgorithm(alg))
239            }
240        }
241        Algorithm::P384 => {
242            #[cfg(feature = "p384")]
243            {
244                crate::algorithms::p384::P384Algo::sign(secret, msg)
245            }
246            #[cfg(not(feature = "p384"))]
247            {
248                Err(AlgorithmError::UnsupportedAlgorithm(alg))
249            }
250        }
251        Algorithm::P521 => {
252            #[cfg(feature = "p521")]
253            {
254                crate::algorithms::p521::P521Algo::sign(secret, msg)
255            }
256            #[cfg(not(feature = "p521"))]
257            {
258                Err(AlgorithmError::UnsupportedAlgorithm(alg))
259            }
260        }
261        Algorithm::Secp256k1 => {
262            #[cfg(feature = "secp256k1")]
263            {
264                crate::algorithms::secp256k1::Secp256k1Algo::sign(secret, msg)
265            }
266            #[cfg(not(feature = "secp256k1"))]
267            {
268                Err(AlgorithmError::UnsupportedAlgorithm(alg))
269            }
270        }
271        Algorithm::MlDsa44 => {
272            #[cfg(feature = "ml-dsa-44")]
273            {
274                crate::algorithms::ml_dsa_44::MlDsa44Algo::sign(secret, msg)
275            }
276            #[cfg(not(feature = "ml-dsa-44"))]
277            {
278                Err(AlgorithmError::UnsupportedAlgorithm(alg))
279            }
280        }
281        Algorithm::MlDsa65 => {
282            #[cfg(feature = "ml-dsa-65")]
283            {
284                crate::algorithms::ml_dsa_65::MlDsa65Algo::sign(secret, msg)
285            }
286            #[cfg(not(feature = "ml-dsa-65"))]
287            {
288                Err(AlgorithmError::UnsupportedAlgorithm(alg))
289            }
290        }
291        Algorithm::MlDsa87 => {
292            #[cfg(feature = "ml-dsa-87")]
293            {
294                crate::algorithms::ml_dsa_87::MlDsa87Algo::sign(secret, msg)
295            }
296            #[cfg(not(feature = "ml-dsa-87"))]
297            {
298                Err(AlgorithmError::UnsupportedAlgorithm(alg))
299            }
300        }
301        _ => Err(AlgorithmError::UnsupportedAlgorithm(alg)),
302    }
303}
304
305/// Verify a detached signature.
306///
307/// Fails closed: a signature that does not verify is an
308/// [`AlgorithmError::SignatureInvalid`] error, never a boolean, so a
309/// forgotten result check cannot be mistaken for success.
310///
311/// # Examples
312///
313/// ```
314/// use crypto_core::Algorithm;
315/// use crypto_dispatch::{generate_keypair, sign, verify};
316///
317/// # fn main() -> Result<(), crypto_dispatch::AlgorithmError> {
318/// let (public, secret) = generate_keypair(Algorithm::Ed25519)?;
319/// let signature = sign(Algorithm::Ed25519, &secret, b"message")?;
320///
321/// // The signed message verifies.
322/// verify(Algorithm::Ed25519, &public, b"message", &signature)?;
323///
324/// // A different message returns Err, never `Ok(false)`.
325/// assert!(verify(Algorithm::Ed25519, &public, b"tampered", &signature).is_err());
326/// # Ok(())
327/// # }
328/// ```
329pub fn verify(alg: Algorithm, public: &[u8], msg: &[u8], sig: &[u8]) -> Result<(), AlgorithmError> {
330    #[cfg(not(any(
331        feature = "ed25519",
332        feature = "p256",
333        feature = "p384",
334        feature = "p521",
335        feature = "secp256k1",
336        feature = "ml-dsa-44",
337        feature = "ml-dsa-65",
338        feature = "ml-dsa-87"
339    )))]
340    let _ = (public, msg, sig);
341
342    match alg {
343        Algorithm::Ed25519 => {
344            #[cfg(feature = "ed25519")]
345            {
346                crate::algorithms::ed25519::Ed25519Algo::verify(public, msg, sig)
347            }
348            #[cfg(not(feature = "ed25519"))]
349            {
350                Err(AlgorithmError::UnsupportedAlgorithm(alg))
351            }
352        }
353        Algorithm::P256 => {
354            #[cfg(feature = "p256")]
355            {
356                crate::algorithms::p256::P256Algo::verify(public, msg, sig)
357            }
358            #[cfg(not(feature = "p256"))]
359            {
360                Err(AlgorithmError::UnsupportedAlgorithm(alg))
361            }
362        }
363        Algorithm::P384 => {
364            #[cfg(feature = "p384")]
365            {
366                crate::algorithms::p384::P384Algo::verify(public, msg, sig)
367            }
368            #[cfg(not(feature = "p384"))]
369            {
370                Err(AlgorithmError::UnsupportedAlgorithm(alg))
371            }
372        }
373        Algorithm::P521 => {
374            #[cfg(feature = "p521")]
375            {
376                crate::algorithms::p521::P521Algo::verify(public, msg, sig)
377            }
378            #[cfg(not(feature = "p521"))]
379            {
380                Err(AlgorithmError::UnsupportedAlgorithm(alg))
381            }
382        }
383        Algorithm::Secp256k1 => {
384            #[cfg(feature = "secp256k1")]
385            {
386                crate::algorithms::secp256k1::Secp256k1Algo::verify(public, msg, sig)
387            }
388            #[cfg(not(feature = "secp256k1"))]
389            {
390                Err(AlgorithmError::UnsupportedAlgorithm(alg))
391            }
392        }
393        Algorithm::MlDsa44 => {
394            #[cfg(feature = "ml-dsa-44")]
395            {
396                crate::algorithms::ml_dsa_44::MlDsa44Algo::verify(public, msg, sig)
397            }
398            #[cfg(not(feature = "ml-dsa-44"))]
399            {
400                Err(AlgorithmError::UnsupportedAlgorithm(alg))
401            }
402        }
403        Algorithm::MlDsa65 => {
404            #[cfg(feature = "ml-dsa-65")]
405            {
406                crate::algorithms::ml_dsa_65::MlDsa65Algo::verify(public, msg, sig)
407            }
408            #[cfg(not(feature = "ml-dsa-65"))]
409            {
410                Err(AlgorithmError::UnsupportedAlgorithm(alg))
411            }
412        }
413        Algorithm::MlDsa87 => {
414            #[cfg(feature = "ml-dsa-87")]
415            {
416                crate::algorithms::ml_dsa_87::MlDsa87Algo::verify(public, msg, sig)
417            }
418            #[cfg(not(feature = "ml-dsa-87"))]
419            {
420                Err(AlgorithmError::UnsupportedAlgorithm(alg))
421            }
422        }
423        _ => Err(AlgorithmError::UnsupportedAlgorithm(alg)),
424    }
425}
426
427//
428// -----------------------------------------------------------------------------
429// ECDH / DH key agreement
430// -----------------------------------------------------------------------------
431
432/// Derive a Diffie–Hellman shared secret. The returned secret zeroizes on
433/// drop.
434pub fn derive_shared_secret(
435    alg: Algorithm,
436    secret_key: &[u8],
437    public_key: &[u8],
438) -> Result<Zeroizing<Vec<u8>>, AlgorithmError> {
439    #[cfg(not(any(
440        feature = "p256",
441        feature = "p384",
442        feature = "p521",
443        feature = "x25519"
444    )))]
445    let _ = (secret_key, public_key);
446
447    match alg {
448        Algorithm::P256 => {
449            #[cfg(feature = "p256")]
450            {
451                crate::algorithms::p256::P256Algo::derive_shared_secret(secret_key, public_key)
452            }
453            #[cfg(not(feature = "p256"))]
454            {
455                Err(AlgorithmError::UnsupportedAlgorithm(alg))
456            }
457        }
458        Algorithm::P384 => {
459            #[cfg(feature = "p384")]
460            {
461                crate::algorithms::p384::P384Algo::derive_shared_secret(secret_key, public_key)
462            }
463            #[cfg(not(feature = "p384"))]
464            {
465                Err(AlgorithmError::UnsupportedAlgorithm(alg))
466            }
467        }
468        Algorithm::P521 => {
469            #[cfg(feature = "p521")]
470            {
471                crate::algorithms::p521::P521Algo::derive_shared_secret(secret_key, public_key)
472            }
473            #[cfg(not(feature = "p521"))]
474            {
475                Err(AlgorithmError::UnsupportedAlgorithm(alg))
476            }
477        }
478        Algorithm::X25519 => {
479            #[cfg(feature = "x25519")]
480            {
481                crate::algorithms::x25519::X25519Algo::derive_shared_secret(secret_key, public_key)
482            }
483            #[cfg(not(feature = "x25519"))]
484            {
485                Err(AlgorithmError::UnsupportedAlgorithm(alg))
486            }
487        }
488        _ => Err(AlgorithmError::UnsupportedAlgorithm(alg)),
489    }
490}
491
492//
493// -----------------------------------------------------------------------------
494// POST-QUANTUM KEM
495// -----------------------------------------------------------------------------
496
497/// Returns (shared_secret, ciphertext); the shared secret zeroizes on drop.
498pub fn kem_encapsulate(
499    alg: Algorithm,
500    public_key: &[u8],
501) -> Result<(Zeroizing<Vec<u8>>, Vec<u8>), AlgorithmError> {
502    #[cfg(not(any(
503        feature = "ml-kem-512",
504        feature = "ml-kem-768",
505        feature = "ml-kem-1024",
506        feature = "x-wing"
507    )))]
508    let _ = public_key;
509
510    match alg {
511        Algorithm::MlKem512 => {
512            #[cfg(feature = "ml-kem-512")]
513            {
514                crate::algorithms::ml_kem_512::MlKem512Algo::encapsulate(public_key)
515            }
516            #[cfg(not(feature = "ml-kem-512"))]
517            {
518                Err(AlgorithmError::UnsupportedAlgorithm(alg))
519            }
520        }
521        Algorithm::MlKem768 => {
522            #[cfg(feature = "ml-kem-768")]
523            {
524                crate::algorithms::ml_kem_768::MlKem768Algo::encapsulate(public_key)
525            }
526            #[cfg(not(feature = "ml-kem-768"))]
527            {
528                Err(AlgorithmError::UnsupportedAlgorithm(alg))
529            }
530        }
531        Algorithm::MlKem1024 => {
532            #[cfg(feature = "ml-kem-1024")]
533            {
534                crate::algorithms::ml_kem_1024::MlKem1024Algo::encapsulate(public_key)
535            }
536            #[cfg(not(feature = "ml-kem-1024"))]
537            {
538                Err(AlgorithmError::UnsupportedAlgorithm(alg))
539            }
540        }
541        Algorithm::XWing768 => {
542            #[cfg(feature = "x-wing")]
543            {
544                crate::algorithms::x_wing::XWing768Algo::encapsulate(public_key)
545            }
546            #[cfg(not(feature = "x-wing"))]
547            {
548                Err(AlgorithmError::UnsupportedAlgorithm(alg))
549            }
550        }
551        Algorithm::XWing1024 => {
552            #[cfg(feature = "x-wing")]
553            {
554                crate::algorithms::x_wing::XWing1024Algo::encapsulate(public_key)
555            }
556            #[cfg(not(feature = "x-wing"))]
557            {
558                Err(AlgorithmError::UnsupportedAlgorithm(alg))
559            }
560        }
561        _ => Err(AlgorithmError::UnsupportedAlgorithm(alg)),
562    }
563}
564
565/// Decapsulate a KEM ciphertext. The returned shared secret zeroizes on drop.
566pub fn kem_decapsulate(
567    alg: Algorithm,
568    ciphertext: &[u8],
569    secret_key: &[u8],
570) -> Result<Zeroizing<Vec<u8>>, AlgorithmError> {
571    #[cfg(not(any(
572        feature = "ml-kem-512",
573        feature = "ml-kem-768",
574        feature = "ml-kem-1024",
575        feature = "x-wing"
576    )))]
577    let _ = (ciphertext, secret_key);
578
579    match alg {
580        Algorithm::MlKem512 => {
581            #[cfg(feature = "ml-kem-512")]
582            {
583                crate::algorithms::ml_kem_512::MlKem512Algo::decapsulate(ciphertext, secret_key)
584            }
585            #[cfg(not(feature = "ml-kem-512"))]
586            {
587                Err(AlgorithmError::UnsupportedAlgorithm(alg))
588            }
589        }
590        Algorithm::MlKem768 => {
591            #[cfg(feature = "ml-kem-768")]
592            {
593                crate::algorithms::ml_kem_768::MlKem768Algo::decapsulate(ciphertext, secret_key)
594            }
595            #[cfg(not(feature = "ml-kem-768"))]
596            {
597                Err(AlgorithmError::UnsupportedAlgorithm(alg))
598            }
599        }
600        Algorithm::MlKem1024 => {
601            #[cfg(feature = "ml-kem-1024")]
602            {
603                crate::algorithms::ml_kem_1024::MlKem1024Algo::decapsulate(ciphertext, secret_key)
604            }
605            #[cfg(not(feature = "ml-kem-1024"))]
606            {
607                Err(AlgorithmError::UnsupportedAlgorithm(alg))
608            }
609        }
610        Algorithm::XWing768 => {
611            #[cfg(feature = "x-wing")]
612            {
613                crate::algorithms::x_wing::XWing768Algo::decapsulate(ciphertext, secret_key)
614            }
615            #[cfg(not(feature = "x-wing"))]
616            {
617                Err(AlgorithmError::UnsupportedAlgorithm(alg))
618            }
619        }
620        Algorithm::XWing1024 => {
621            #[cfg(feature = "x-wing")]
622            {
623                crate::algorithms::x_wing::XWing1024Algo::decapsulate(ciphertext, secret_key)
624            }
625            #[cfg(not(feature = "x-wing"))]
626            {
627                Err(AlgorithmError::UnsupportedAlgorithm(alg))
628            }
629        }
630        _ => Err(AlgorithmError::UnsupportedAlgorithm(alg)),
631    }
632}
633
634//
635// -----------------------------------------------------------------------------
636// Symmetric AEAD
637// -----------------------------------------------------------------------------
638
639/// Encrypt `plaintext` with the selected AEAD. Returns
640/// `ciphertext || tag`.
641///
642/// # Examples
643///
644/// ```
645/// use crypto_core::AeadAlgorithm;
646/// use crypto_dispatch::{aead_decrypt, aead_encrypt, AeadParams};
647///
648/// # fn main() -> Result<(), crypto_dispatch::AlgorithmError> {
649/// let key = [0x42u8; 32];
650/// let nonce = [0x24u8; 12];
651/// let params = AeadParams { key: &key, nonce: &nonce, aad: b"context" };
652///
653/// let sealed = aead_encrypt(AeadAlgorithm::Aes256Gcm, &params, b"plaintext")?;
654/// let opened = aead_decrypt(AeadAlgorithm::Aes256Gcm, &params, &sealed)?;
655/// assert_eq!(opened.as_slice(), b"plaintext");
656/// # Ok(())
657/// # }
658/// ```
659pub fn aead_encrypt(
660    alg: AeadAlgorithm,
661    params: &AeadParams<'_>,
662    plaintext: &[u8],
663) -> Result<Vec<u8>, AlgorithmError> {
664    #[cfg(not(any(
665        feature = "aes",
666        feature = "aes-gcm-siv",
667        feature = "chacha20-poly1305"
668    )))]
669    let _ = (params, plaintext);
670
671    match alg {
672        AeadAlgorithm::Aes128Gcm => {
673            #[cfg(feature = "aes")]
674            {
675                crate::algorithms::aes256_gcm::Aes128GcmAlgo::encrypt(params, plaintext)
676            }
677            #[cfg(not(feature = "aes"))]
678            {
679                Err(AlgorithmError::UnsupportedAeadAlgorithm(alg))
680            }
681        }
682        AeadAlgorithm::Aes192Gcm => {
683            #[cfg(feature = "aes")]
684            {
685                crate::algorithms::aes256_gcm::Aes192GcmAlgo::encrypt(params, plaintext)
686            }
687            #[cfg(not(feature = "aes"))]
688            {
689                Err(AlgorithmError::UnsupportedAeadAlgorithm(alg))
690            }
691        }
692        AeadAlgorithm::Aes256Gcm => {
693            #[cfg(feature = "aes")]
694            {
695                crate::algorithms::aes256_gcm::Aes256GcmAlgo::encrypt(params, plaintext)
696            }
697            #[cfg(not(feature = "aes"))]
698            {
699                Err(AlgorithmError::UnsupportedAeadAlgorithm(alg))
700            }
701        }
702        AeadAlgorithm::Aes256GcmSiv => {
703            #[cfg(feature = "aes-gcm-siv")]
704            {
705                crate::algorithms::aes256_gcm_siv::Aes256GcmSivAlgo::encrypt(params, plaintext)
706            }
707            #[cfg(not(feature = "aes-gcm-siv"))]
708            {
709                Err(AlgorithmError::UnsupportedAeadAlgorithm(alg))
710            }
711        }
712        AeadAlgorithm::ChaCha20Poly1305 => {
713            #[cfg(feature = "chacha20-poly1305")]
714            {
715                crate::algorithms::chacha20_poly1305::ChaCha20Poly1305Algo::encrypt(
716                    params, plaintext,
717                )
718            }
719            #[cfg(not(feature = "chacha20-poly1305"))]
720            {
721                Err(AlgorithmError::UnsupportedAeadAlgorithm(alg))
722            }
723        }
724        AeadAlgorithm::XChaCha20Poly1305 => {
725            #[cfg(feature = "chacha20-poly1305")]
726            {
727                crate::algorithms::chacha20_poly1305::XChaCha20Poly1305Algo::encrypt(
728                    params, plaintext,
729                )
730            }
731            #[cfg(not(feature = "chacha20-poly1305"))]
732            {
733                Err(AlgorithmError::UnsupportedAeadAlgorithm(alg))
734            }
735        }
736    }
737}
738
739/// Decrypt and authenticate `ciphertext || tag` with the selected AEAD.
740/// The returned plaintext is zeroized on drop.
741///
742/// Fails closed: a tampered ciphertext, tag, nonce, or AAD returns an error
743/// instead of any plaintext.
744///
745/// # Examples
746///
747/// ```
748/// use crypto_core::AeadAlgorithm;
749/// use crypto_dispatch::{aead_decrypt, aead_encrypt, AeadParams};
750///
751/// # fn main() -> Result<(), crypto_dispatch::AlgorithmError> {
752/// let key = [0x42u8; 32];
753/// let nonce = [0x24u8; 12];
754/// let params = AeadParams { key: &key, nonce: &nonce, aad: b"context" };
755///
756/// let sealed = aead_encrypt(AeadAlgorithm::Aes256GcmSiv, &params, b"plaintext")?;
757/// let opened = aead_decrypt(AeadAlgorithm::Aes256GcmSiv, &params, &sealed)?;
758/// assert_eq!(opened.as_slice(), b"plaintext");
759/// # Ok(())
760/// # }
761/// ```
762pub fn aead_decrypt(
763    alg: AeadAlgorithm,
764    params: &AeadParams<'_>,
765    ciphertext_with_tag: &[u8],
766) -> Result<Zeroizing<Vec<u8>>, AlgorithmError> {
767    #[cfg(not(any(
768        feature = "aes",
769        feature = "aes-gcm-siv",
770        feature = "chacha20-poly1305"
771    )))]
772    let _ = (params, ciphertext_with_tag);
773
774    match alg {
775        AeadAlgorithm::Aes128Gcm => {
776            #[cfg(feature = "aes")]
777            {
778                crate::algorithms::aes256_gcm::Aes128GcmAlgo::decrypt(params, ciphertext_with_tag)
779            }
780            #[cfg(not(feature = "aes"))]
781            {
782                Err(AlgorithmError::UnsupportedAeadAlgorithm(alg))
783            }
784        }
785        AeadAlgorithm::Aes192Gcm => {
786            #[cfg(feature = "aes")]
787            {
788                crate::algorithms::aes256_gcm::Aes192GcmAlgo::decrypt(params, ciphertext_with_tag)
789            }
790            #[cfg(not(feature = "aes"))]
791            {
792                Err(AlgorithmError::UnsupportedAeadAlgorithm(alg))
793            }
794        }
795        AeadAlgorithm::Aes256Gcm => {
796            #[cfg(feature = "aes")]
797            {
798                crate::algorithms::aes256_gcm::Aes256GcmAlgo::decrypt(params, ciphertext_with_tag)
799            }
800            #[cfg(not(feature = "aes"))]
801            {
802                Err(AlgorithmError::UnsupportedAeadAlgorithm(alg))
803            }
804        }
805        AeadAlgorithm::Aes256GcmSiv => {
806            #[cfg(feature = "aes-gcm-siv")]
807            {
808                crate::algorithms::aes256_gcm_siv::Aes256GcmSivAlgo::decrypt(
809                    params,
810                    ciphertext_with_tag,
811                )
812            }
813            #[cfg(not(feature = "aes-gcm-siv"))]
814            {
815                Err(AlgorithmError::UnsupportedAeadAlgorithm(alg))
816            }
817        }
818        AeadAlgorithm::ChaCha20Poly1305 => {
819            #[cfg(feature = "chacha20-poly1305")]
820            {
821                crate::algorithms::chacha20_poly1305::ChaCha20Poly1305Algo::decrypt(
822                    params,
823                    ciphertext_with_tag,
824                )
825            }
826            #[cfg(not(feature = "chacha20-poly1305"))]
827            {
828                Err(AlgorithmError::UnsupportedAeadAlgorithm(alg))
829            }
830        }
831        AeadAlgorithm::XChaCha20Poly1305 => {
832            #[cfg(feature = "chacha20-poly1305")]
833            {
834                crate::algorithms::chacha20_poly1305::XChaCha20Poly1305Algo::decrypt(
835                    params,
836                    ciphertext_with_tag,
837                )
838            }
839            #[cfg(not(feature = "chacha20-poly1305"))]
840            {
841                Err(AlgorithmError::UnsupportedAeadAlgorithm(alg))
842            }
843        }
844    }
845}