1use subtle::ConstantTimeEq;
2
3use crate::constants::{
4 CRYPTO_CORE_ED25519_BYTES, CRYPTO_CORE_HCHACHA20_INPUTBYTES, CRYPTO_CORE_HCHACHA20_KEYBYTES,
5 CRYPTO_CORE_HCHACHA20_OUTPUTBYTES, CRYPTO_CORE_HSALSA20_INPUTBYTES,
6 CRYPTO_CORE_HSALSA20_KEYBYTES, CRYPTO_CORE_HSALSA20_OUTPUTBYTES, CRYPTO_SCALARMULT_BYTES,
7 CRYPTO_SCALARMULT_SCALARBYTES,
8};
9use crate::edwards25519::Point;
10use crate::error::Error;
11use crate::scalarmult_curve25519::{
12 crypto_scalarmult_curve25519, crypto_scalarmult_curve25519_base,
13};
14use crate::types::*;
15use crate::utils::{SIGMA, load_u32_le};
16
17pub type HChaCha20Input = [u8; CRYPTO_CORE_HCHACHA20_INPUTBYTES];
19pub type HChaCha20Key = [u8; CRYPTO_CORE_HCHACHA20_KEYBYTES];
21pub type HChaCha20Output = [u8; CRYPTO_CORE_HCHACHA20_OUTPUTBYTES];
23pub type HSalsa20Input = [u8; CRYPTO_CORE_HSALSA20_INPUTBYTES];
25pub type HSalsa20Key = [u8; CRYPTO_CORE_HSALSA20_KEYBYTES];
27pub type HSalsa20Output = [u8; CRYPTO_CORE_HSALSA20_OUTPUTBYTES];
29pub type Ed25519Point = [u8; CRYPTO_CORE_ED25519_BYTES];
31
32pub fn crypto_scalarmult_base(
36 q: &mut [u8; CRYPTO_SCALARMULT_BYTES],
37 n: &[u8; CRYPTO_SCALARMULT_SCALARBYTES],
38) {
39 crypto_scalarmult_curve25519_base(q, n)
40}
41
42pub fn crypto_scalarmult(
52 q: &mut [u8; CRYPTO_SCALARMULT_BYTES],
53 n: &[u8; CRYPTO_SCALARMULT_SCALARBYTES],
54 p: &[u8; CRYPTO_SCALARMULT_BYTES],
55) -> Result<(), Error> {
56 crypto_scalarmult_curve25519(q, n, p);
57
58 if q.ct_eq(&[0u8; CRYPTO_SCALARMULT_BYTES]).into() {
59 Err(Error::invalid_key(crate::ErrorContext::Curve25519PublicKey))
60 } else {
61 Ok(())
62 }
63}
64
65pub(crate) fn crypto_scalarmult_and_base(
73 q: &mut [u8; CRYPTO_SCALARMULT_BYTES],
74 public: &mut [u8; CRYPTO_SCALARMULT_BYTES],
75 n: &[u8; CRYPTO_SCALARMULT_SCALARBYTES],
76 p: &[u8; CRYPTO_SCALARMULT_BYTES],
77) -> Result<(), Error> {
78 crate::scalarmult_curve25519::crypto_scalarmult_curve25519_and_base(q, public, n, p);
79
80 if q.ct_eq(&[0u8; CRYPTO_SCALARMULT_BYTES]).into() {
81 Err(Error::invalid_key(crate::ErrorContext::Curve25519PublicKey))
82 } else {
83 Ok(())
84 }
85}
86
87pub fn crypto_core_hchacha20(
91 output: &mut HChaCha20Output,
92 input: &HChaCha20Input,
93 key: &HChaCha20Key,
94 constants: Option<(u32, u32, u32, u32)>,
95) {
96 let input = input.as_array();
97 let key = key.as_array();
98 let (c0, c1, c2, c3) = constants.unwrap_or((SIGMA[0], SIGMA[1], SIGMA[2], SIGMA[3]));
99 let (k, _) = key.as_chunks::<4>();
103 let (n, _) = input.as_chunks::<4>();
104 let mut x = [c0, c1, c2, c3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
105 x[4] = u32::from_le_bytes(k[0]);
106 x[5] = u32::from_le_bytes(k[1]);
107 x[6] = u32::from_le_bytes(k[2]);
108 x[7] = u32::from_le_bytes(k[3]);
109 x[8] = u32::from_le_bytes(k[4]);
110 x[9] = u32::from_le_bytes(k[5]);
111 x[10] = u32::from_le_bytes(k[6]);
112 x[11] = u32::from_le_bytes(k[7]);
113 x[12] = u32::from_le_bytes(n[0]);
114 x[13] = u32::from_le_bytes(n[1]);
115 x[14] = u32::from_le_bytes(n[2]);
116 x[15] = u32::from_le_bytes(n[3]);
117
118 crate::chacha20::rounds(&mut x);
119
120 let (out, _) = output.as_chunks_mut::<4>();
122 out[0] = x[0].to_le_bytes();
123 out[1] = x[1].to_le_bytes();
124 out[2] = x[2].to_le_bytes();
125 out[3] = x[3].to_le_bytes();
126 out[4] = x[12].to_le_bytes();
127 out[5] = x[13].to_le_bytes();
128 out[6] = x[14].to_le_bytes();
129 out[7] = x[15].to_le_bytes();
130}
131
132#[must_use]
155pub fn crypto_core_ed25519_is_valid_point(p: &Ed25519Point) -> bool {
156 decompress_prime_order_ed25519_point(p).is_some()
157}
158
159pub(crate) fn decompress_prime_order_ed25519_point(p: &Ed25519Point) -> Option<Point> {
162 decompress_canonical_ed25519_point(p)
163 .filter(|point| !point.is_small_order() && ed25519_is_torsion_free(point))
164}
165
166pub(crate) fn ed25519_is_torsion_free(point: &Point) -> bool {
175 point.is_torsion_free_vartime()
176}
177
178pub(crate) fn decompress_canonical_ed25519_point(p: &Ed25519Point) -> Option<Point> {
185 if !is_canonical_ed25519_encoding(p) {
186 return None;
187 }
188 Point::decompress(p)
189}
190
191fn is_canonical_ed25519_encoding(p: &Ed25519Point) -> bool {
198 let sign = p[31] >> 7;
199 let y_top = p[31] & 0x7f;
200 let y_middle_all_ones = p[1..31].iter().all(|&b| b == 0xff);
201 let y_middle_all_zero = p[1..31].iter().all(|&b| b == 0);
202
203 let y_at_least_p = y_top == 0x7f && y_middle_all_ones && p[0] >= 0xed;
206 let y_is_one = y_top == 0 && y_middle_all_zero && p[0] == 1;
207 let y_is_minus_one = y_top == 0x7f && y_middle_all_ones && p[0] == 0xec;
208
209 !y_at_least_p && !(sign == 1 && (y_is_one || y_is_minus_one))
210}
211
212#[inline]
213fn salsa20_rotl32(x: u32, y: u32, rot: u32) -> u32 {
214 x.wrapping_add(y).rotate_left(rot)
215}
216
217pub fn crypto_core_hsalsa20(
221 output: &mut HSalsa20Output,
222 input: &HSalsa20Input,
223 key: &HSalsa20Key,
224 constants: Option<(u32, u32, u32, u32)>,
225) {
226 let (mut x0, mut x5, mut x10, mut x15) =
227 constants.unwrap_or((SIGMA[0], SIGMA[1], SIGMA[2], SIGMA[3]));
228 let (
229 mut x1,
230 mut x2,
231 mut x3,
232 mut x4,
233 mut x11,
234 mut x12,
235 mut x13,
236 mut x14,
237 mut x6,
238 mut x7,
239 mut x8,
240 mut x9,
241 ) = (
242 load_u32_le(&key[0..4]),
243 load_u32_le(&key[4..8]),
244 load_u32_le(&key[8..12]),
245 load_u32_le(&key[12..16]),
246 load_u32_le(&key[16..20]),
247 load_u32_le(&key[20..24]),
248 load_u32_le(&key[24..28]),
249 load_u32_le(&key[28..32]),
250 load_u32_le(&input[0..4]),
251 load_u32_le(&input[4..8]),
252 load_u32_le(&input[8..12]),
253 load_u32_le(&input[12..16]),
254 );
255
256 for _ in (0..20).step_by(2) {
257 x4 ^= salsa20_rotl32(x0, x12, 7);
258 x8 ^= salsa20_rotl32(x4, x0, 9);
259 x12 ^= salsa20_rotl32(x8, x4, 13);
260 x0 ^= salsa20_rotl32(x12, x8, 18);
261 x9 ^= salsa20_rotl32(x5, x1, 7);
262 x13 ^= salsa20_rotl32(x9, x5, 9);
263 x1 ^= salsa20_rotl32(x13, x9, 13);
264 x5 ^= salsa20_rotl32(x1, x13, 18);
265 x14 ^= salsa20_rotl32(x10, x6, 7);
266 x2 ^= salsa20_rotl32(x14, x10, 9);
267 x6 ^= salsa20_rotl32(x2, x14, 13);
268 x10 ^= salsa20_rotl32(x6, x2, 18);
269 x3 ^= salsa20_rotl32(x15, x11, 7);
270 x7 ^= salsa20_rotl32(x3, x15, 9);
271 x11 ^= salsa20_rotl32(x7, x3, 13);
272 x15 ^= salsa20_rotl32(x11, x7, 18);
273 x1 ^= salsa20_rotl32(x0, x3, 7);
274 x2 ^= salsa20_rotl32(x1, x0, 9);
275 x3 ^= salsa20_rotl32(x2, x1, 13);
276 x0 ^= salsa20_rotl32(x3, x2, 18);
277 x6 ^= salsa20_rotl32(x5, x4, 7);
278 x7 ^= salsa20_rotl32(x6, x5, 9);
279 x4 ^= salsa20_rotl32(x7, x6, 13);
280 x5 ^= salsa20_rotl32(x4, x7, 18);
281 x11 ^= salsa20_rotl32(x10, x9, 7);
282 x8 ^= salsa20_rotl32(x11, x10, 9);
283 x9 ^= salsa20_rotl32(x8, x11, 13);
284 x10 ^= salsa20_rotl32(x9, x8, 18);
285 x12 ^= salsa20_rotl32(x15, x14, 7);
286 x13 ^= salsa20_rotl32(x12, x15, 9);
287 x14 ^= salsa20_rotl32(x13, x12, 13);
288 x15 ^= salsa20_rotl32(x14, x13, 18);
289 }
290
291 let (out, _) = output.as_chunks_mut::<4>();
294 out[0] = x0.to_le_bytes();
295 out[1] = x5.to_le_bytes();
296 out[2] = x10.to_le_bytes();
297 out[3] = x15.to_le_bytes();
298 out[4] = x6.to_le_bytes();
299 out[5] = x7.to_le_bytes();
300 out[6] = x8.to_le_bytes();
301 out[7] = x9.to_le_bytes();
302}
303
304#[cfg(test)]
305mod tests {
306 use curve25519_dalek::edwards::CompressedEdwardsY;
307
308 use super::*;
309 use crate::classic::crypto_sign::crypto_sign_keypair;
310 use crate::edwards25519::test_vectors::{
311 IDENTITY, NONCANONICAL_IDENTITY, mixed_order_point, torsion_point,
312 };
313 use crate::scalarmult_curve25519::test_vectors::low_order_u_encodings;
314 use crate::test_prelude::*;
315
316 #[test]
317 fn test_crypto_core_ed25519_is_valid_point() {
318 let basepoint = curve25519_dalek::constants::ED25519_BASEPOINT_COMPRESSED.to_bytes();
319 assert!(crypto_core_ed25519_is_valid_point(&basepoint));
320
321 let mut negative_basepoint = basepoint;
322 negative_basepoint[31] |= 0x80;
323 assert!(
324 crypto_core_ed25519_is_valid_point(&negative_basepoint),
325 "the high bit is a valid x-coordinate sign bit"
326 );
327
328 assert!(!crypto_core_ed25519_is_valid_point(&IDENTITY));
329
330 assert!(
331 decompress_canonical_ed25519_point(&NONCANONICAL_IDENTITY).is_none(),
332 "p + 1 must not be accepted as an alternate encoding of the identity"
333 );
334 assert!(!crypto_core_ed25519_is_valid_point(&NONCANONICAL_IDENTITY));
335
336 let torsion = torsion_point();
337 assert!(torsion.is_small_order());
338 assert!(!crypto_core_ed25519_is_valid_point(
339 &torsion.compress().to_bytes()
340 ));
341
342 let mixed_order = mixed_order_point();
343 assert!(!mixed_order.is_small_order());
344 assert!(!mixed_order.is_torsion_free());
345 assert!(!crypto_core_ed25519_is_valid_point(
346 &mixed_order.compress().to_bytes()
347 ));
348
349 let mut point_not_on_curve = [0u8; CRYPTO_CORE_ED25519_BYTES];
350 point_not_on_curve[0] = 2;
351 assert!(!crypto_core_ed25519_is_valid_point(&point_not_on_curve));
352 assert!(!crypto_core_ed25519_is_valid_point(
353 &[0u8; CRYPTO_CORE_ED25519_BYTES]
354 ));
355 }
356
357 #[test]
358 fn test_generated_ed25519_keys_are_valid_points() {
359 for _ in 0..25 {
360 let (ed25519_pk, _) = crypto_sign_keypair();
361 assert!(crypto_core_ed25519_is_valid_point(&ed25519_pk));
362 }
363 }
364
365 #[test]
368 fn test_torsion_check_matches_dalek() {
369 let mut points = vec![curve25519_dalek::constants::ED25519_BASEPOINT_POINT];
370 for _ in 0..if cfg!(miri) { 2 } else { 32 } {
373 let (pk, _) = crypto_sign_keypair();
374 points.push(CompressedEdwardsY(pk).decompress().unwrap());
375 }
376 let prime_order = points.clone();
377 for torsion in curve25519_dalek::constants::EIGHT_TORSION {
378 points.push(torsion);
379 for point in &prime_order {
380 points.push(point + torsion);
381 }
382 }
383 for point in points {
384 let ours = Point::decompress(&point.compress().to_bytes()).unwrap();
385 assert_eq!(ed25519_is_torsion_free(&ours), point.is_torsion_free());
386 }
387 }
388
389 #[test]
392 fn test_canonical_encoding_check_matches_recompression() {
393 let round_trip = |p: &Ed25519Point| {
394 let compressed = CompressedEdwardsY(*p);
395 compressed
396 .decompress()
397 .is_some_and(|point| point.compress() == compressed)
398 };
399 let mut cases = Vec::new();
400 for low in 0xebu8..=0xff {
403 let mut point = [0xff; CRYPTO_CORE_ED25519_BYTES];
404 point[0] = low;
405 point[31] = 0x7f;
406 cases.push(point);
407 }
408 for low in 0u8..=20 {
409 let mut point = [0; CRYPTO_CORE_ED25519_BYTES];
410 point[0] = low;
411 cases.push(point);
412 }
413 cases.push(curve25519_dalek::constants::ED25519_BASEPOINT_COMPRESSED.to_bytes());
414 for torsion in curve25519_dalek::constants::EIGHT_TORSION {
415 cases.push(torsion.compress().to_bytes());
416 }
417 for _ in 0..if cfg!(miri) { 4 } else { 64 } {
418 cases.push(crypto_sign_keypair().0);
419 let mut random = [0u8; CRYPTO_CORE_ED25519_BYTES];
420 crate::rng::copy_randombytes(&mut random);
421 cases.push(random);
422 }
423 for mut point in cases {
424 for sign in [0u8, 0x80] {
425 point[31] = (point[31] & 0x7f) | sign;
426 assert_eq!(
427 decompress_canonical_ed25519_point(&point).is_some(),
428 round_trip(&point),
429 "{point:02x?}"
430 );
431 }
432 }
433 }
434
435 fn legacy_libsodium_mixed_order_points() -> [[u8; CRYPTO_CORE_ED25519_BYTES]; 2] {
439 let mut y_is_nine = [0u8; CRYPTO_CORE_ED25519_BYTES];
440 y_is_nine[0] = 9;
441 let mut order_two_coset = [0x99; CRYPTO_CORE_ED25519_BYTES];
442 order_two_coset[0] = 0x95;
443 [y_is_nine, order_two_coset]
444 }
445
446 #[test]
447 fn test_crypto_core_ed25519_rejects_legacy_libsodium_mixed_order_points() {
448 for point in legacy_libsodium_mixed_order_points() {
449 let decoded = decompress_canonical_ed25519_point(&point)
450 .expect("regression vector must be a canonical curve point");
451 assert!(!decoded.is_small_order());
452 assert!(!ed25519_is_torsion_free(&decoded));
453 assert!(!crypto_core_ed25519_is_valid_point(&point));
454 }
455 }
456
457 #[test]
461 fn test_crypto_scalarmult_rejects_low_order_points() {
462 let scalar = [0x42; CRYPTO_SCALARMULT_SCALARBYTES];
463
464 for public_key in low_order_u_encodings() {
465 let mut shared_secret = [0xa5; CRYPTO_SCALARMULT_BYTES];
466 assert!(
467 matches!(
468 crypto_scalarmult(&mut shared_secret, &scalar, &public_key),
469 Err(Error::InvalidKey {
470 context: crate::ErrorContext::Curve25519PublicKey,
471 })
472 ),
473 "{public_key:02x?}"
474 );
475 assert_eq!(shared_secret, [0u8; CRYPTO_SCALARMULT_BYTES]);
476 }
477 }
478
479 #[test]
480 fn test_crypto_scalarmult_ignores_public_key_high_bit() {
481 let scalar = [0x42; CRYPTO_SCALARMULT_SCALARBYTES];
482 let mut canonical = [0u8; CRYPTO_SCALARMULT_BYTES];
483 canonical[0] = 9;
484 let mut high_bit_set = canonical;
485 high_bit_set[CRYPTO_SCALARMULT_BYTES - 1] = 0x80;
486 let mut canonical_secret = [0u8; CRYPTO_SCALARMULT_BYTES];
487 let mut high_bit_secret = [0u8; CRYPTO_SCALARMULT_BYTES];
488
489 crypto_scalarmult(&mut canonical_secret, &scalar, &canonical).unwrap();
490 crypto_scalarmult(&mut high_bit_secret, &scalar, &high_bit_set).unwrap();
491
492 assert_eq!(canonical_secret, high_bit_secret);
493 }
494
495 const CUSTOM_CONSTANTS: Constants = (0x0123_4567, 0x89ab_cdef, 0xfedc_ba98, 0x7654_3210);
498
499 const SIGMA_CONSTANTS: Constants = (SIGMA[0], SIGMA[1], SIGMA[2], SIGMA[3]);
501
502 type Constants = (u32, u32, u32, u32);
504
505 #[test]
510 fn test_crypto_core_hchacha20_known_answers() {
511 let key: HChaCha20Key = core::array::from_fn(|i| i as u8);
512 let input: HChaCha20Input = hex::decode("000000090000004a0000000031415927")
513 .unwrap()
514 .try_into()
515 .unwrap();
516 let default = "82413b4227b27bfed30e42508a877d73a0f9e4d58a74a853c12ec41326d3ecdc";
517 for (constants, expected) in [
518 (None, default),
519 (Some(SIGMA_CONSTANTS), default),
520 (
521 Some(CUSTOM_CONSTANTS),
522 "e887f97849587bad0f41aa2b5596fe9967e2785acc6ecc13c7c4e4a02016bd76",
523 ),
524 ] {
525 let mut output = HChaCha20Output::default();
526 crypto_core_hchacha20(&mut output, &input, &key, constants);
527 assert_eq!(hex::encode(output), expected, "{constants:x?}");
528 }
529 }
530
531 #[test]
537 fn test_crypto_core_hsalsa20_known_answers() {
538 let shared = "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742";
539 let firstkey = "1b27556473e985d462cd51197a9a46c76009549eac6474f206c4ee0844f68389";
540 let nonce_prefix = "69696ee955b62b73cd62bda875fc73d6";
541 let zero = "00000000000000000000000000000000";
542 for (key, input, constants, expected) in [
543 (shared, zero, None, firstkey),
544 (shared, zero, Some(SIGMA_CONSTANTS), firstkey),
545 (
546 firstkey,
547 nonce_prefix,
548 None,
549 "dc908dda0b9344a953629b733820778880f3ceb421bb61b91cbd4c3e66256ce4",
550 ),
551 (
552 shared,
553 zero,
554 Some(CUSTOM_CONSTANTS),
555 "7ee2bcfe4eec7e4e59e64a4e7b0a97a001f2ad95c10247115f0d261afa7c092c",
556 ),
557 ] {
558 let key: HSalsa20Key = hex::decode(key).unwrap().try_into().unwrap();
559 let input: HSalsa20Input = hex::decode(input).unwrap().try_into().unwrap();
560 let mut output = HSalsa20Output::default();
561 crypto_core_hsalsa20(&mut output, &input, &key, constants);
562 assert_eq!(hex::encode(output), expected, "{constants:x?}");
563 }
564 }
565
566 #[cfg(dryoc_native_tests)]
567 mod native_tests {
568 use super::*;
569 use crate::classic::crypto_box::*;
570 use crate::scalarmult_curve25519::test_vectors::field_prime_plus;
571
572 #[test]
573 fn test_crypto_core_ed25519_is_valid_point_matches_libsodium() {
574 use libsodium_sys::crypto_core_ed25519_is_valid_point as sodium_is_valid_point;
575
576 crate::native_test_util::init();
577
578 let basepoint = curve25519_dalek::constants::ED25519_BASEPOINT_COMPRESSED.to_bytes();
579 let mut negative_basepoint = basepoint;
580 negative_basepoint[31] |= 0x80;
581 let torsion = torsion_point();
582 let mixed_order = mixed_order_point().compress().to_bytes();
583
584 for point in [
585 basepoint,
586 negative_basepoint,
587 IDENTITY,
588 NONCANONICAL_IDENTITY,
589 torsion.compress().to_bytes(),
590 mixed_order,
591 [0u8; CRYPTO_CORE_ED25519_BYTES],
592 ]
593 .into_iter()
594 .chain(legacy_libsodium_mixed_order_points())
595 {
596 let dryoc_result = crypto_core_ed25519_is_valid_point(&point);
597 let sodium_result = unsafe { sodium_is_valid_point(point.as_ptr()) } == 1;
598 assert_eq!(dryoc_result, sodium_result, "point: {point:02x?}");
599 }
600
601 for _ in 0..20 {
602 let (public_key, _) = crypto_sign_keypair();
603 let sodium_result = unsafe { sodium_is_valid_point(public_key.as_ptr()) } == 1;
604 assert!(sodium_result);
605 assert_eq!(
606 crypto_core_ed25519_is_valid_point(&public_key),
607 sodium_result
608 );
609 }
610 }
611
612 #[test]
613 fn test_crypto_scalarmult_base() {
614 use base64::Engine as _;
615 use base64::engine::general_purpose;
616 for _ in 0..20 {
617 use crate::native_test_util::scalarmult_curve25519_base;
618
619 let (pk, sk) = crypto_box_keypair();
620
621 let mut public_key = [0u8; CRYPTO_SCALARMULT_BYTES];
622 crypto_scalarmult_base(&mut public_key, &sk);
623
624 assert_eq!(&pk, &public_key);
625
626 let ge = scalarmult_curve25519_base(&sk);
627
628 assert_eq!(
629 general_purpose::STANDARD.encode(ge),
630 general_purpose::STANDARD.encode(public_key)
631 );
632 }
633 }
634
635 #[test]
636 fn test_crypto_scalarmult() {
637 use base64::Engine as _;
638 use base64::engine::general_purpose;
639 for _ in 0..20 {
640 use crate::native_test_util::scalarmult_curve25519;
641
642 let (_our_pk, our_sk) = crypto_box_keypair();
643 let (their_pk, _their_sk) = crypto_box_keypair();
644
645 let mut shared_secret = [0u8; CRYPTO_SCALARMULT_BYTES];
646 crypto_scalarmult(&mut shared_secret, &our_sk, &their_pk)
647 .expect("scalarmult failed");
648
649 let ge = scalarmult_curve25519(&our_sk, &their_pk).expect("scalarmult failed");
650
651 assert_eq!(
652 general_purpose::STANDARD.encode(ge),
653 general_purpose::STANDARD.encode(shared_secret)
654 );
655 }
656 }
657
658 #[test]
662 fn test_crypto_scalarmult_low_order_compatibility() {
663 use libsodium_sys::crypto_scalarmult as sodium_scalarmult;
664
665 crate::native_test_util::init();
666
667 let mut rng = crate::utils::test_util::XorShift64::new(0x3c6e_f372_fe94_f82b);
668 let scalars = [[0u8; 32], [0xff; 32], [0x42; 32], rng.next_bytes32()];
669
670 for public_key in low_order_u_encodings() {
671 for scalar in scalars {
672 let mut shared_secret = [0u8; CRYPTO_SCALARMULT_BYTES];
673 assert!(
674 crypto_scalarmult(&mut shared_secret, &scalar, &public_key).is_err(),
675 "{public_key:02x?}"
676 );
677 let mut sodium_secret = [0u8; CRYPTO_SCALARMULT_BYTES];
678 let sodium_result = unsafe {
679 sodium_scalarmult(
680 sodium_secret.as_mut_ptr(),
681 scalar.as_ptr(),
682 public_key.as_ptr(),
683 )
684 };
685 assert_eq!(sodium_result, -1, "{public_key:02x?}");
686 }
687 }
688 }
689
690 #[test]
694 fn test_crypto_scalarmult_noncanonical_compatibility() {
695 use libsodium_sys::crypto_scalarmult as sodium_scalarmult;
696
697 crate::native_test_util::init();
698
699 let mut rng = crate::utils::test_util::XorShift64::new(0xa54f_f53a_5f1d_36f1);
700 for j in 2..=18u8 {
702 let scalar = rng.next_bytes32();
703 let mut canonical = [0u8; CRYPTO_SCALARMULT_BYTES];
704 canonical[0] = j;
705 let mut expected = [0u8; CRYPTO_SCALARMULT_BYTES];
706 crypto_scalarmult(&mut expected, &scalar, &canonical).unwrap();
707
708 let unreduced = field_prime_plus(j as i8);
709 let mut unreduced_high = unreduced;
710 unreduced_high[31] |= 0x80;
711 for public_key in [unreduced, unreduced_high] {
712 let mut shared_secret = [0u8; CRYPTO_SCALARMULT_BYTES];
713 crypto_scalarmult(&mut shared_secret, &scalar, &public_key).unwrap();
714 let mut sodium_secret = [0u8; CRYPTO_SCALARMULT_BYTES];
715 let sodium_result = unsafe {
716 sodium_scalarmult(
717 sodium_secret.as_mut_ptr(),
718 scalar.as_ptr(),
719 public_key.as_ptr(),
720 )
721 };
722 assert_eq!(sodium_result, 0, "j {j}");
723 assert_eq!(shared_secret, sodium_secret, "j {j}");
724 assert_eq!(shared_secret, expected, "j {j}");
725 }
726 }
727 }
728
729 fn constant_cases() -> [(Option<Constants>, Option<[u8; 16]>); 3] {
733 fn le_bytes((c0, c1, c2, c3): Constants) -> [u8; 16] {
734 let mut bytes = [0u8; 16];
735 for (chunk, word) in bytes
736 .as_chunks_mut::<4>()
737 .0
738 .iter_mut()
739 .zip([c0, c1, c2, c3])
740 {
741 *chunk = word.to_le_bytes();
742 }
743 bytes
744 }
745
746 let mut random = [0u8; 16];
747 crate::rng::copy_randombytes(&mut random);
748 let word = |i: usize| load_u32_le(&random[4 * i..4 * i + 4]);
749 let random = (word(0), word(1), word(2), word(3));
750 let sigma = (SIGMA[0], SIGMA[1], SIGMA[2], SIGMA[3]);
751 [
752 (None, None),
753 (Some(sigma), Some(le_bytes(sigma))),
754 (Some(random), Some(le_bytes(random))),
755 ]
756 }
757
758 #[test]
759 fn test_crypto_core_hchacha20() {
760 use libsodium_sys::crypto_core_hchacha20 as so_crypto_core_hchacha20;
761
762 use crate::rng::copy_randombytes;
763
764 crate::native_test_util::init();
765
766 for _ in 0..10 {
767 let mut key = [0u8; CRYPTO_CORE_HCHACHA20_KEYBYTES];
768 let mut data = [0u8; CRYPTO_CORE_HCHACHA20_INPUTBYTES];
769 copy_randombytes(&mut key);
770 copy_randombytes(&mut data);
771
772 for (constants, c) in constant_cases() {
773 let mut out = [0u8; CRYPTO_CORE_HCHACHA20_OUTPUTBYTES];
774 crypto_core_hchacha20(&mut out, &data, &key, constants);
775
776 let mut so_out = [0u8; CRYPTO_CORE_HCHACHA20_OUTPUTBYTES];
777 let ret = unsafe {
778 so_crypto_core_hchacha20(
779 so_out.as_mut_ptr(),
780 data.as_ptr(),
781 key.as_ptr(),
782 c.as_ref().map_or(core::ptr::null(), |c| c.as_ptr()),
783 )
784 };
785 assert_eq!(ret, 0);
786 assert_eq!(out, so_out, "{constants:x?}");
787 }
788 }
789 }
790
791 #[test]
792 fn test_crypto_core_hsalsa20() {
793 use libsodium_sys::crypto_core_hsalsa20 as so_crypto_core_hsalsa20;
794
795 use crate::rng::copy_randombytes;
796
797 crate::native_test_util::init();
798
799 for _ in 0..10 {
800 let mut key = [0u8; CRYPTO_CORE_HSALSA20_KEYBYTES];
801 let mut data = [0u8; CRYPTO_CORE_HSALSA20_INPUTBYTES];
802 copy_randombytes(&mut key);
803 copy_randombytes(&mut data);
804
805 for (constants, c) in constant_cases() {
806 let mut out = [0u8; CRYPTO_CORE_HSALSA20_OUTPUTBYTES];
807 crypto_core_hsalsa20(&mut out, &data, &key, constants);
808
809 let mut so_out = [0u8; CRYPTO_CORE_HSALSA20_OUTPUTBYTES];
810 let ret = unsafe {
811 so_crypto_core_hsalsa20(
812 so_out.as_mut_ptr(),
813 data.as_ptr(),
814 key.as_ptr(),
815 c.as_ref().map_or(core::ptr::null(), |c| c.as_ptr()),
816 )
817 };
818 assert_eq!(ret, 0);
819 assert_eq!(out, so_out, "{constants:x?}");
820 }
821 }
822 }
823 }
824}