1use chacha20::ChaCha20Legacy;
45use chacha20::cipher::{KeyIvInit, StreamCipher, StreamCipherSeek};
46use subtle::ConstantTimeEq;
47use zeroize::Zeroize;
48
49use crate::classic::crypto_core::{HChaCha20Key, crypto_core_hchacha20};
50use crate::constants::{
51 CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES, CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES,
52 CRYPTO_AEAD_XCHACHA20POLY1305_IETF_MESSAGEBYTES_MAX,
53 CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES, CRYPTO_CORE_HCHACHA20_INPUTBYTES,
54};
55use crate::error::Error;
56use crate::poly1305::{Key as Poly1305Key, Poly1305};
57use crate::rng::copy_randombytes;
58use crate::types::*;
59use crate::utils::pad16;
60
61pub type Mac = [u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES];
63pub type Nonce = [u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES];
65pub type Key = [u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES];
67
68const PAD0: [u8; 16] = [0u8; 16];
69
70pub fn crypto_aead_xchacha20poly1305_ietf_keygen_inplace(key: &mut Key) {
72 copy_randombytes(key)
73}
74
75pub fn crypto_aead_xchacha20poly1305_ietf_keygen() -> Key {
77 Key::generate()
78}
79
80fn validate_message_len(message_len: usize) -> Result<(), Error> {
81 if message_len > CRYPTO_AEAD_XCHACHA20POLY1305_IETF_MESSAGEBYTES_MAX {
82 Err(length_error!(
83 crate::ErrorContext::Message,
84 message_len,
85 max CRYPTO_AEAD_XCHACHA20POLY1305_IETF_MESSAGEBYTES_MAX
86 ))
87 } else {
88 Ok(())
89 }
90}
91
92fn validate_output_len(
93 output_len: usize,
94 expected_len: usize,
95 context: crate::ErrorContext,
96) -> Result<(), Error> {
97 if output_len != expected_len {
98 Err(length_error!(context, output_len, exact expected_len))
99 } else {
100 Ok(())
101 }
102}
103
104fn message_len_from_combined_len(
105 combined_len: usize,
106 context: crate::ErrorContext,
107) -> Result<usize, Error> {
108 if combined_len < CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES {
109 Err(length_error!(context, combined_len, min CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES))
110 } else {
111 let message_len = combined_len - CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES;
112 validate_message_len(message_len)?;
113 Ok(message_len)
114 }
115}
116
117fn chacha20_xietf_ext(nonce: &Nonce, key: &Key) -> ChaCha20Legacy {
118 let mut subkey = HChaCha20Key::default();
119 crypto_core_hchacha20(
120 &mut subkey,
121 ByteArray::as_array(&nonce[..CRYPTO_CORE_HCHACHA20_INPUTBYTES]),
122 key,
123 None,
124 );
125
126 let mut legacy_nonce =
131 [0u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES - CRYPTO_CORE_HCHACHA20_INPUTBYTES];
132 legacy_nonce.copy_from_slice(&nonce[CRYPTO_CORE_HCHACHA20_INPUTBYTES..]);
133
134 let mut chacha_key = subkey.into();
135 let chacha_nonce = legacy_nonce.into();
136 let cipher = ChaCha20Legacy::new(&chacha_key, &chacha_nonce);
137
138 subkey.zeroize();
139 chacha_key.zeroize();
140
141 cipher
142}
143
144fn poly1305_key(cipher: &mut ChaCha20Legacy) -> Poly1305Key {
145 let mut mac_key = Poly1305Key::new();
146 cipher.apply_keystream(&mut mac_key);
147 mac_key
148}
149
150fn compute_mac(mac: &mut Mac, mac_key: &mut Poly1305Key, ciphertext: &[u8], ad: &[u8]) {
151 let mut state = Poly1305::new(mac_key);
152 mac_key.zeroize();
153
154 state.update(ad);
155 state.update(&PAD0[..pad16(ad.len())]);
156 state.update(ciphertext);
157 state.update(&PAD0[..pad16(ciphertext.len())]);
158 state.update(&(ad.len() as u64).to_le_bytes());
159 state.update(&(ciphertext.len() as u64).to_le_bytes());
160 state.finalize(mac);
161}
162
163fn compute_mac_to_array(mac_key: &mut Poly1305Key, ciphertext: &[u8], ad: &[u8]) -> Mac {
164 let mut mac = Mac::default();
165 compute_mac(&mut mac, mac_key, ciphertext, ad);
166 mac
167}
168
169fn verify_mac(mac: &Mac, computed_mac: &Mac) -> Result<(), Error> {
170 if mac.ct_eq(computed_mac).unwrap_u8() == 1 {
171 Ok(())
172 } else {
173 Err(Error::AuthenticationFailed)
174 }
175}
176
177pub fn crypto_aead_xchacha20poly1305_ietf_encrypt_detached(
187 ciphertext: &mut [u8],
188 mac: &mut Mac,
189 message: &[u8],
190 associated_data: Option<&[u8]>,
191 nonce: &Nonce,
192 key: &Key,
193) -> Result<(), Error> {
194 validate_message_len(message.len())?;
195 validate_output_len(
196 ciphertext.len(),
197 message.len(),
198 crate::ErrorContext::Ciphertext,
199 )?;
200
201 let associated_data = associated_data.unwrap_or(&[]);
202 let mut cipher = chacha20_xietf_ext(nonce, key);
203 let mut mac_key = poly1305_key(&mut cipher);
204
205 ciphertext.copy_from_slice(message);
206 cipher.seek(64);
207 cipher.apply_keystream(ciphertext);
208
209 compute_mac(mac, &mut mac_key, ciphertext, associated_data);
210 Ok(())
211}
212
213pub fn crypto_aead_xchacha20poly1305_ietf_encrypt_detached_inplace(
220 data: &mut [u8],
221 mac: &mut Mac,
222 associated_data: Option<&[u8]>,
223 nonce: &Nonce,
224 key: &Key,
225) -> Result<(), Error> {
226 validate_message_len(data.len())?;
227
228 let associated_data = associated_data.unwrap_or(&[]);
229 let mut cipher = chacha20_xietf_ext(nonce, key);
230 let mut mac_key = poly1305_key(&mut cipher);
231
232 cipher.seek(64);
233 cipher.apply_keystream(data);
234
235 compute_mac(mac, &mut mac_key, data, associated_data);
236 Ok(())
237}
238
239pub fn crypto_aead_xchacha20poly1305_ietf_decrypt_detached(
249 message: &mut [u8],
250 ciphertext: &[u8],
251 mac: &Mac,
252 associated_data: Option<&[u8]>,
253 nonce: &Nonce,
254 key: &Key,
255) -> Result<(), Error> {
256 validate_message_len(ciphertext.len())?;
257 validate_output_len(
258 message.len(),
259 ciphertext.len(),
260 crate::ErrorContext::Message,
261 )?;
262
263 let associated_data = associated_data.unwrap_or(&[]);
264 let mut cipher = chacha20_xietf_ext(nonce, key);
265 let mut mac_key = poly1305_key(&mut cipher);
266 let computed_mac = compute_mac_to_array(&mut mac_key, ciphertext, associated_data);
267
268 verify_mac(mac, &computed_mac)?;
269 message.copy_from_slice(ciphertext);
270 cipher.seek(64);
271 cipher.apply_keystream(message);
272 Ok(())
273}
274
275pub fn crypto_aead_xchacha20poly1305_ietf_decrypt_detached_inplace(
283 data: &mut [u8],
284 mac: &Mac,
285 associated_data: Option<&[u8]>,
286 nonce: &Nonce,
287 key: &Key,
288) -> Result<(), Error> {
289 validate_message_len(data.len())?;
290
291 let associated_data = associated_data.unwrap_or(&[]);
292 let mut cipher = chacha20_xietf_ext(nonce, key);
293 let mut mac_key = poly1305_key(&mut cipher);
294 let computed_mac = compute_mac_to_array(&mut mac_key, data, associated_data);
295
296 verify_mac(mac, &computed_mac)?;
297 cipher.seek(64);
298 cipher.apply_keystream(data);
299 Ok(())
300}
301
302pub fn crypto_aead_xchacha20poly1305_ietf_encrypt(
311 ciphertext: &mut [u8],
312 message: &[u8],
313 associated_data: Option<&[u8]>,
314 nonce: &Nonce,
315 key: &Key,
316) -> Result<(), Error> {
317 validate_message_len(message.len())?;
318 validate_output_len(
319 ciphertext.len(),
320 message.len() + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES,
321 crate::ErrorContext::Ciphertext,
322 )?;
323
324 let (ciphertext, mac) = ciphertext.split_at_mut(message.len());
325 let mac = MutByteArray::as_mut_array(mac);
326 crypto_aead_xchacha20poly1305_ietf_encrypt_detached(
327 ciphertext,
328 mac,
329 message,
330 associated_data,
331 nonce,
332 key,
333 )
334}
335
336pub fn crypto_aead_xchacha20poly1305_ietf_decrypt(
345 message: &mut [u8],
346 ciphertext: &[u8],
347 associated_data: Option<&[u8]>,
348 nonce: &Nonce,
349 key: &Key,
350) -> Result<(), Error> {
351 let message_len =
352 message_len_from_combined_len(ciphertext.len(), crate::ErrorContext::Ciphertext)?;
353 validate_output_len(message.len(), message_len, crate::ErrorContext::Message)?;
354
355 let (ciphertext, mac) = ciphertext.split_at(message_len);
356 let mac = ByteArray::as_array(mac);
357 crypto_aead_xchacha20poly1305_ietf_decrypt_detached(
358 message,
359 ciphertext,
360 mac,
361 associated_data,
362 nonce,
363 key,
364 )
365}
366
367pub fn crypto_aead_xchacha20poly1305_ietf_encrypt_inplace(
377 data: &mut [u8],
378 associated_data: Option<&[u8]>,
379 nonce: &Nonce,
380 key: &Key,
381) -> Result<(), Error> {
382 let message_len = message_len_from_combined_len(data.len(), crate::ErrorContext::Data)?;
383 let (data, mac) = data.split_at_mut(message_len);
384 let mac = MutByteArray::as_mut_array(mac);
385 crypto_aead_xchacha20poly1305_ietf_encrypt_detached_inplace(
386 data,
387 mac,
388 associated_data,
389 nonce,
390 key,
391 )
392}
393
394pub fn crypto_aead_xchacha20poly1305_ietf_decrypt_inplace(
404 data: &mut [u8],
405 associated_data: Option<&[u8]>,
406 nonce: &Nonce,
407 key: &Key,
408) -> Result<(), Error> {
409 let message_len = message_len_from_combined_len(data.len(), crate::ErrorContext::Data)?;
410 let (data, mac) = data.split_at_mut(message_len);
411 let mac = ByteArray::as_array(mac);
412 crypto_aead_xchacha20poly1305_ietf_decrypt_detached_inplace(
413 data,
414 mac,
415 associated_data,
416 nonce,
417 key,
418 )
419}
420
421#[cfg(test)]
422mod tests {
423 use super::*;
424
425 const MESSAGE: &[u8] =
426 b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
427 const AD: &[u8] = &[
428 0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
429 ];
430 const KEY: Key = [
431 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
432 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d,
433 0x9e, 0x9f,
434 ];
435 const NONCE: Nonce = [
436 0xf2, 0x8a, 0x50, 0xa7, 0x8a, 0x7e, 0x23, 0xc9, 0xcb, 0xa6, 0x78, 0x34, 0x66, 0xf8, 0x03,
437 0x59, 0x0f, 0x04, 0xe9, 0x22, 0x31, 0xa3, 0x2d, 0x5d,
438 ];
439 const EXPECTED: &[u8] = &[
440 0x20, 0xf1, 0xae, 0x75, 0xe1, 0xe5, 0xe0, 0x00, 0x40, 0x29, 0x4f, 0x0f, 0xb1, 0x0e, 0xbb,
441 0x08, 0x10, 0xc5, 0x93, 0xc7, 0xdb, 0xa4, 0xec, 0x10, 0x4c, 0x1e, 0x5e, 0xf9, 0x50, 0x7f,
442 0xae, 0xef, 0x58, 0xfc, 0x28, 0x98, 0xbb, 0xd0, 0xe4, 0x7b, 0x2f, 0x53, 0x31, 0xfb, 0xc3,
443 0x67, 0xd3, 0xc2, 0x78, 0x4e, 0x36, 0x48, 0xce, 0x1e, 0xaa, 0x77, 0x87, 0xad, 0x18, 0x6d,
444 0xb2, 0x68, 0x5e, 0xe8, 0x9a, 0xe4, 0xd3, 0x44, 0x1f, 0x6e, 0xa0, 0xb2, 0x22, 0x4c, 0xd5,
445 0xa1, 0x34, 0x16, 0x1b, 0x55, 0x4d, 0x8b, 0x48, 0x35, 0x0b, 0x4a, 0xd4, 0x01, 0x15, 0xdb,
446 0x81, 0xea, 0x82, 0x09, 0x68, 0xe9, 0x43, 0x89, 0x2f, 0x2b, 0x80, 0x51, 0xcb, 0x5f, 0x7a,
447 0x86, 0x66, 0xe7, 0xe7, 0xef, 0x7f, 0x84, 0xc0, 0xa2, 0xf8, 0x0a, 0x12, 0xd0, 0x66, 0x80,
448 0xc8, 0xee, 0xbb, 0xd9, 0x30, 0x04, 0x10, 0x9d, 0xe8, 0x42,
449 ];
450
451 #[test]
452 fn test_known_answer() {
453 let mut ciphertext = vec![0u8; MESSAGE.len() + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES];
454 crypto_aead_xchacha20poly1305_ietf_encrypt(
455 &mut ciphertext,
456 MESSAGE,
457 Some(AD),
458 &NONCE,
459 &KEY,
460 )
461 .expect("encrypt");
462 assert_eq!(ciphertext, EXPECTED);
463
464 let mut decrypted = vec![0u8; MESSAGE.len()];
465 crypto_aead_xchacha20poly1305_ietf_decrypt(
466 &mut decrypted,
467 &ciphertext,
468 Some(AD),
469 &NONCE,
470 &KEY,
471 )
472 .expect("decrypt");
473 assert_eq!(decrypted, MESSAGE);
474 }
475
476 #[test]
477 fn test_detached_matches_combined() {
478 let mut combined = vec![0u8; MESSAGE.len() + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES];
479 crypto_aead_xchacha20poly1305_ietf_encrypt(&mut combined, MESSAGE, Some(AD), &NONCE, &KEY)
480 .expect("encrypt");
481
482 let mut detached = vec![0u8; MESSAGE.len()];
483 let mut mac = Mac::default();
484 crypto_aead_xchacha20poly1305_ietf_encrypt_detached(
485 &mut detached,
486 &mut mac,
487 MESSAGE,
488 Some(AD),
489 &NONCE,
490 &KEY,
491 )
492 .expect("detached encrypt");
493
494 assert_eq!(detached, combined[..MESSAGE.len()]);
495 assert_eq!(mac.as_slice(), &combined[MESSAGE.len()..]);
496 }
497
498 #[test]
499 fn test_failures_do_not_mutate_output() {
500 let mut ciphertext = vec![0u8; MESSAGE.len() + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES];
501 crypto_aead_xchacha20poly1305_ietf_encrypt(
502 &mut ciphertext,
503 MESSAGE,
504 Some(AD),
505 &NONCE,
506 &KEY,
507 )
508 .expect("encrypt");
509 ciphertext[0] ^= 1;
510
511 let mut decrypted = vec![0xa5; MESSAGE.len()];
512 let original = decrypted.clone();
513 crypto_aead_xchacha20poly1305_ietf_decrypt(
514 &mut decrypted,
515 &ciphertext,
516 Some(AD),
517 &NONCE,
518 &KEY,
519 )
520 .expect_err("expected auth failure");
521 assert_eq!(decrypted, original);
522 }
523
524 #[test]
525 fn test_empty_message_and_no_aad() {
526 let mut ciphertext = vec![0u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES];
527 crypto_aead_xchacha20poly1305_ietf_encrypt(&mut ciphertext, &[], None, &NONCE, &KEY)
528 .expect("encrypt");
529
530 let mut decrypted = vec![];
531 crypto_aead_xchacha20poly1305_ietf_decrypt(&mut decrypted, &ciphertext, None, &NONCE, &KEY)
532 .expect("decrypt");
533 assert!(decrypted.is_empty());
534 }
535
536 #[test]
537 fn test_wrong_aad_fails() {
538 let mut ciphertext = vec![0u8; MESSAGE.len() + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES];
539 crypto_aead_xchacha20poly1305_ietf_encrypt(
540 &mut ciphertext,
541 MESSAGE,
542 Some(AD),
543 &NONCE,
544 &KEY,
545 )
546 .expect("encrypt");
547
548 let mut decrypted = vec![0u8; MESSAGE.len()];
549 crypto_aead_xchacha20poly1305_ietf_decrypt(
550 &mut decrypted,
551 &ciphertext,
552 Some(b"wrong aad"),
553 &NONCE,
554 &KEY,
555 )
556 .expect_err("expected auth failure");
557 }
558
559 #[test]
560 fn test_wrong_key_and_nonce_fail() {
561 let mut ciphertext = vec![0u8; MESSAGE.len() + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES];
562 crypto_aead_xchacha20poly1305_ietf_encrypt(
563 &mut ciphertext,
564 MESSAGE,
565 Some(AD),
566 &NONCE,
567 &KEY,
568 )
569 .expect("encrypt");
570
571 let mut wrong_key = KEY;
572 wrong_key[0] ^= 1;
573 let mut decrypted = vec![0u8; MESSAGE.len()];
574 crypto_aead_xchacha20poly1305_ietf_decrypt(
575 &mut decrypted,
576 &ciphertext,
577 Some(AD),
578 &NONCE,
579 &wrong_key,
580 )
581 .expect_err("expected wrong key auth failure");
582
583 let mut wrong_nonce = NONCE;
584 wrong_nonce[0] ^= 1;
585 crypto_aead_xchacha20poly1305_ietf_decrypt(
586 &mut decrypted,
587 &ciphertext,
588 Some(AD),
589 &wrong_nonce,
590 &KEY,
591 )
592 .expect_err("expected wrong nonce auth failure");
593 }
594
595 #[test]
596 fn test_wrong_mac_and_short_ciphertext_fail() {
597 let mut ciphertext = vec![0u8; MESSAGE.len()];
598 let mut mac = Mac::default();
599 crypto_aead_xchacha20poly1305_ietf_encrypt_detached(
600 &mut ciphertext,
601 &mut mac,
602 MESSAGE,
603 Some(AD),
604 &NONCE,
605 &KEY,
606 )
607 .expect("detached encrypt");
608
609 mac[0] ^= 1;
610 let mut decrypted = vec![0u8; MESSAGE.len()];
611 crypto_aead_xchacha20poly1305_ietf_decrypt_detached(
612 &mut decrypted,
613 &ciphertext,
614 &mac,
615 Some(AD),
616 &NONCE,
617 &KEY,
618 )
619 .expect_err("expected wrong mac auth failure");
620
621 let mut short_decrypted = vec![];
622 crypto_aead_xchacha20poly1305_ietf_decrypt(
623 &mut short_decrypted,
624 &[0u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES - 1],
625 Some(AD),
626 &NONCE,
627 &KEY,
628 )
629 .expect_err("expected short ciphertext failure");
630 }
631
632 #[test]
633 fn test_inplace_roundtrip() {
634 let mut data = MESSAGE.to_vec();
635 data.resize(MESSAGE.len() + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES, 0);
636 crypto_aead_xchacha20poly1305_ietf_encrypt_inplace(&mut data, Some(AD), &NONCE, &KEY)
637 .expect("inplace encrypt");
638 assert_eq!(data, EXPECTED);
639
640 crypto_aead_xchacha20poly1305_ietf_decrypt_inplace(&mut data, Some(AD), &NONCE, &KEY)
641 .expect("inplace decrypt");
642 assert_eq!(&data[..MESSAGE.len()], MESSAGE);
643 }
644
645 #[test]
646 fn test_xietf_ext_stream_crosses_ietf_counter_boundary() {
647 let mut cipher = chacha20_xietf_ext(&NONCE, &KEY);
648 cipher.seek(64u64 * u64::from(u32::MAX));
649
650 let mut stream = [0u8; 128];
651 cipher.apply_keystream(&mut stream);
652
653 assert_ne!(&stream[..64], &[0u8; 64]);
654 assert_ne!(&stream[64..], &[0u8; 64]);
655 assert_ne!(&stream[..64], &stream[64..]);
656 }
657
658 #[test]
659 fn test_inplace_failures_do_not_mutate_data() {
660 let mut data = MESSAGE.to_vec();
661 data.resize(MESSAGE.len() + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES, 0);
662 crypto_aead_xchacha20poly1305_ietf_encrypt_inplace(&mut data, Some(AD), &NONCE, &KEY)
663 .expect("inplace encrypt");
664 data[MESSAGE.len()] ^= 1;
665 let original = data.clone();
666
667 crypto_aead_xchacha20poly1305_ietf_decrypt_inplace(&mut data, Some(AD), &NONCE, &KEY)
668 .expect_err("expected auth failure");
669 assert_eq!(data, original);
670
671 let mut detached = MESSAGE.to_vec();
672 let mut mac = Mac::default();
673 crypto_aead_xchacha20poly1305_ietf_encrypt_detached_inplace(
674 &mut detached,
675 &mut mac,
676 Some(AD),
677 &NONCE,
678 &KEY,
679 )
680 .expect("detached inplace encrypt");
681 mac[0] ^= 1;
682 let original = detached.clone();
683
684 crypto_aead_xchacha20poly1305_ietf_decrypt_detached_inplace(
685 &mut detached,
686 &mac,
687 Some(AD),
688 &NONCE,
689 &KEY,
690 )
691 .expect_err("expected detached auth failure");
692 assert_eq!(detached, original);
693 }
694
695 #[cfg(dryoc_native_tests)]
696 mod native_tests {
697 use super::*;
698
699 #[test]
700 fn test_sodiumoxide_interop() {
701 use sodiumoxide::crypto::aead::xchacha20poly1305_ietf::{
702 Key as SOKey, Nonce as SONonce, open, seal,
703 };
704
705 let so_key = SOKey::from_slice(&KEY).expect("key");
706 let so_nonce = SONonce::from_slice(&NONCE).expect("nonce");
707
708 let mut ciphertext =
709 vec![0u8; MESSAGE.len() + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES];
710 crypto_aead_xchacha20poly1305_ietf_encrypt(
711 &mut ciphertext,
712 MESSAGE,
713 Some(AD),
714 &NONCE,
715 &KEY,
716 )
717 .expect("encrypt");
718 let so_plaintext =
719 open(&ciphertext, Some(AD), &so_nonce, &so_key).expect("sodiumoxide open");
720 assert_eq!(so_plaintext, MESSAGE);
721
722 let so_ciphertext = seal(MESSAGE, Some(AD), &so_nonce, &so_key);
723 let mut plaintext = vec![0u8; MESSAGE.len()];
724 crypto_aead_xchacha20poly1305_ietf_decrypt(
725 &mut plaintext,
726 &so_ciphertext,
727 Some(AD),
728 &NONCE,
729 &KEY,
730 )
731 .expect("decrypt");
732 assert_eq!(plaintext, MESSAGE);
733 }
734
735 #[test]
736 fn test_counter_boundary_matches_libsodium_xchacha_stream() {
737 use libsodium_sys::crypto_stream_xchacha20_xor_ic;
738
739 let initial_counter = u64::from(u32::MAX);
740 let input = [0u8; 128];
741 let mut expected = [0u8; 128];
742 unsafe {
746 assert_eq!(
747 crypto_stream_xchacha20_xor_ic(
748 expected.as_mut_ptr(),
749 input.as_ptr(),
750 input.len() as u64,
751 NONCE.as_ptr(),
752 initial_counter,
753 KEY.as_ptr(),
754 ),
755 0
756 );
757 }
758
759 let mut actual = [0u8; 128];
760 let mut cipher = chacha20_xietf_ext(&NONCE, &KEY);
761 cipher.seek(64 * initial_counter);
762 cipher.apply_keystream(&mut actual);
763
764 assert_eq!(actual, expected);
765 }
766 }
767}