diff --git a/CHANGELOG.md b/CHANGELOG.md index c3db4498..2df643a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Replace removed `CtOption::into_option` with `Option::from` +- Reject non-boolean values in `Choice` wrapper (`From` masks input, `Serializable::from_bytes` and rkyv `CheckBytes` reject values other than 0/1) ### Changed diff --git a/Makefile b/Makefile index bda973d7..0fa5b4dc 100644 --- a/Makefile +++ b/Makefile @@ -6,13 +6,13 @@ test: ## Run tests (std + no_std) @cargo test --no-default-features clippy: ## Run clippy - @cargo clippy --all-features -- -D warnings + @cargo clippy --all-features --features rkyv/size_32 -- -D warnings fmt: ## Format code @cargo +nightly fmt --all check: ## Type-check - @cargo check --all-features + @cargo check --all-features --features rkyv/size_32 doc: ## Generate docs @cargo doc --no-deps diff --git a/src/dusk/choice.rs b/src/dusk/choice.rs index c8133da9..0b084eeb 100644 --- a/src/dusk/choice.rs +++ b/src/dusk/choice.rs @@ -1,20 +1,18 @@ -use core::convert::Infallible; +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) DUSK NETWORK. All rights reserved. -use dusk_bytes::Serializable; +use dusk_bytes::{Error as BytesError, Serializable}; use subtle::ConditionallySelectable; -#[cfg(feature = "rkyv-impl")] -use bytecheck::CheckBytes; #[cfg(feature = "rkyv-impl")] use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; /// Wrapper for a [`subtle::Choice`] #[derive(Copy, Clone, Debug)] -#[cfg_attr( - feature = "rkyv-impl", - derive(Archive, RkyvSerialize, RkyvDeserialize), - archive_attr(derive(CheckBytes)) -)] +#[cfg_attr(feature = "rkyv-impl", derive(Archive, RkyvSerialize, RkyvDeserialize))] pub struct Choice(u8); impl Choice { @@ -30,12 +28,15 @@ impl ConditionallySelectable for Choice { } impl Serializable<1> for Choice { - type Error = Infallible; + type Error = BytesError; fn from_bytes(buf: &[u8; Self::SIZE]) -> Result where Self: Sized, { + if buf[0] > 1 { + return Err(BytesError::InvalidData); + } Ok(Self(buf[0])) } @@ -46,7 +47,7 @@ impl Serializable<1> for Choice { impl From for Choice { fn from(int: u8) -> Self { - Self(int) + Self(int & 1) } } @@ -73,3 +74,92 @@ impl From for bool { subtle::Choice::from(c.0).into() } } + +#[cfg(feature = "rkyv-impl")] +const _: () = { + use bytecheck::CheckBytes; + + impl CheckBytes for ArchivedChoice { + type Error = bytecheck::StructCheckError; + + unsafe fn check_bytes<'a>( + value: *const Self, + _context: &mut C, + ) -> Result<&'a Self, Self::Error> { + let byte = (*value).0; + if byte > 1 { + return Err(bytecheck::StructCheckError { + field_name: "0", + inner: bytecheck::ErrorBox::new(bytecheck::BoolCheckError { + invalid_value: byte, + }), + }); + } + Ok(&*value) + } + } +}; + +#[cfg(test)] +mod tests { + use super::*; + use dusk_bytes::Serializable; + + #[test] + fn from_u8_masks_input() { + assert_eq!(Choice::from(0u8).unwrap_u8(), 0); + assert_eq!(Choice::from(1u8).unwrap_u8(), 1); + assert_eq!(Choice::from(2u8).unwrap_u8(), 0); + assert_eq!(Choice::from(3u8).unwrap_u8(), 1); + assert_eq!(Choice::from(255u8).unwrap_u8(), 1); + } + + #[test] + fn serializable_rejects_invalid() { + assert!(Choice::from_bytes(&[0]).is_ok()); + assert!(Choice::from_bytes(&[1]).is_ok()); + assert!(Choice::from_bytes(&[2]).is_err()); + assert!(Choice::from_bytes(&[255]).is_err()); + } + + #[cfg(feature = "rkyv-impl")] + mod rkyv_tests { + use super::*; + use bytecheck::CheckBytes; + use rkyv::ser::serializers::AllocSerializer; + use rkyv::ser::Serializer; + use rkyv::{archived_root, Archived}; + + #[test] + fn rkyv_round_trip_valid() { + for val in [0u8, 1u8] { + let choice = Choice(val); + let mut serializer = AllocSerializer::<256>::default(); + serializer + .serialize_value(&choice) + .expect("failed to serialize"); + let bytes = serializer.into_serializer().into_inner(); + let archived = unsafe { archived_root::(&bytes) }; + assert_eq!(archived.0, val); + // Validate via CheckBytes + let ptr = archived as *const Archived; + let result = + unsafe { as CheckBytes<()>>::check_bytes(ptr, &mut ()) }; + assert!(result.is_ok()); + } + } + + #[test] + fn rkyv_rejects_invalid_choice() { + for invalid in [2u8, 128, 255] { + let ptr = &invalid as *const u8 as *const Archived; + let result = + unsafe { as CheckBytes<()>>::check_bytes(ptr, &mut ()) }; + assert!( + result.is_err(), + "expected rkyv validation to reject byte {invalid}" + ); + } + } + } +} diff --git a/src/dusk/multiscalar_mul.rs b/src/dusk/multiscalar_mul.rs index 07f050d5..4dc0c628 100644 --- a/src/dusk/multiscalar_mul.rs +++ b/src/dusk/multiscalar_mul.rs @@ -113,10 +113,10 @@ fn to_radix_2w_size_hint(w: usize) -> usize { debug_assert!(w <= 8); let digits_count = match w { - 6 => (256 + w - 1) / w, - 7 => (256 + w - 1) / w, + 6 => 256_usize.div_ceil(w), + 7 => 256_usize.div_ceil(w), // See comment in to_radix_2w on handling the terminal carry. - 8 => (256 + w - 1) / w + 1, + 8 => 256_usize.div_ceil(w) + 1, _ => panic!("invalid radix parameter"), }; @@ -140,8 +140,8 @@ fn to_radix_2w(scalar: &Scalar, w: usize) -> [i8; 43] { let mut carry = 0u64; let mut digits = [0i8; 43]; - let digits_count = (256 + w - 1) / w; - for i in 0..digits_count { + let digits_count = 256_usize.div_ceil(w); + for (i, digit) in digits[..digits_count].iter_mut().enumerate() { // Construct a buffer of bits of the scalar, starting at `bit_offset`. let bit_offset = i * w; let u64_idx = bit_offset / 64; @@ -161,7 +161,7 @@ fn to_radix_2w(scalar: &Scalar, w: usize) -> [i8; 43] { // Recenter coefficients from [0,2^w) to [-2^w/2, 2^w/2) carry = (coef + (radix / 2)) >> w; - digits[i] = ((coef as i64) - (carry << w) as i64) as i8; + *digit = ((coef as i64) - (carry << w) as i64) as i8; } // When w < 8, we can fold the final carry onto the last digit d, diff --git a/src/fp.rs b/src/fp.rs index 247c96d8..7d72b0c1 100644 --- a/src/fp.rs +++ b/src/fp.rs @@ -1,6 +1,8 @@ //! This module provides an implementation of the BLS12-381 base field `GF(p)` //! where `p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab` +#![allow(clippy::needless_borrow, clippy::needless_lifetimes)] + mod dusk; use core::fmt; diff --git a/src/fp12.rs b/src/fp12.rs index 116684ea..0aa6b939 100644 --- a/src/fp12.rs +++ b/src/fp12.rs @@ -1,3 +1,4 @@ +#![allow(clippy::needless_lifetimes)] use crate::fp::*; use crate::fp2::*; use crate::fp6::*; diff --git a/src/fp2.rs b/src/fp2.rs index 8320d345..34f9542b 100644 --- a/src/fp2.rs +++ b/src/fp2.rs @@ -1,5 +1,7 @@ //! This module implements arithmetic over the quadratic extension field Fp2. +#![allow(clippy::needless_borrow, clippy::needless_lifetimes)] + #[cfg(feature = "serde")] mod dusk; diff --git a/src/fp2/dusk/mod.rs b/src/fp2/dusk/mod.rs index db142c33..a54e0851 100644 --- a/src/fp2/dusk/mod.rs +++ b/src/fp2/dusk/mod.rs @@ -60,7 +60,7 @@ mod serde_support { c1 = Some(map.next_value()?); } } - field => return Err(SerdeError::unknown_field(field, &FIELDS)), + field => return Err(SerdeError::unknown_field(field, FIELDS)), } } Ok(Fp2 { diff --git a/src/fp6.rs b/src/fp6.rs index 832b261f..e30b8e52 100644 --- a/src/fp6.rs +++ b/src/fp6.rs @@ -1,3 +1,4 @@ +#![allow(clippy::needless_lifetimes)] #[cfg(feature = "serde")] mod dusk; diff --git a/src/fp6/dusk/mod.rs b/src/fp6/dusk/mod.rs index 171d5a99..5b18d901 100644 --- a/src/fp6/dusk/mod.rs +++ b/src/fp6/dusk/mod.rs @@ -65,7 +65,7 @@ mod serde_support { c2 = Some(map.next_value()?); } } - field => return Err(SerdeError::unknown_field(field, &FIELDS)), + field => return Err(SerdeError::unknown_field(field, FIELDS)), } } Ok(Fp6 { diff --git a/src/g1.rs b/src/g1.rs index a6bfbdeb..5ed89a78 100644 --- a/src/g1.rs +++ b/src/g1.rs @@ -1,5 +1,7 @@ //! This module provides an implementation of the $\mathbb{G}_1$ group of BLS12-381. +#![allow(clippy::needless_lifetimes, unused_attributes)] + mod dusk; use core::borrow::Borrow; diff --git a/src/g2.rs b/src/g2.rs index 5f1043e8..a332e48f 100644 --- a/src/g2.rs +++ b/src/g2.rs @@ -1,5 +1,7 @@ //! This module provides an implementation of the $\mathbb{G}_2$ group of BLS12-381. +#![allow(clippy::needless_borrow, clippy::needless_lifetimes, unused_attributes)] + mod dusk; use core::borrow::Borrow; diff --git a/src/hash_to_curve/expand_msg.rs b/src/hash_to_curve/expand_msg.rs index 7187d46b..735668c7 100644 --- a/src/hash_to_curve/expand_msg.rs +++ b/src/hash_to_curve/expand_msg.rs @@ -1,6 +1,8 @@ //! This module implements message expansion consistent with the //! hash-to-curve RFC drafts 7 through 10 +#![allow(clippy::manual_div_ceil, clippy::needless_borrows_for_generic_args)] + use core::{ fmt::{self, Debug, Formatter}, marker::PhantomData, diff --git a/src/notes/design.rs b/src/notes/design.rs index 685a7e75..1e79353e 100644 --- a/src/notes/design.rs +++ b/src/notes/design.rs @@ -63,8 +63,8 @@ //! //! ## Nontrivial third root of unity //! -//! To use the fast subgroup check algorithm for $\mathbb{G_1}$ from https://eprint.iacr.org/2019/814.pdf and -//! https://eprint.iacr.org/2021/1130, it is necessary to find a nontrivial cube root of +//! To use the fast subgroup check algorithm for $\mathbb{G_1}$ from and +//! , it is necessary to find a nontrivial cube root of //! unity β in Fp to define the endomorphism: //! $$(x, y) \rightarrow (\beta x, y)$$ //! which is equivalent to @@ -88,8 +88,8 @@ //! //! ## Psi //! -//! To use the fast subgroup check algorithm for $\mathbb{G_2}$ from https://eprint.iacr.org/2019/814.pdf and -//! https://eprint.iacr.org/2021/1130, it is necessary to find the endomorphism: +//! To use the fast subgroup check algorithm for $\mathbb{G_2}$ from and +//! , it is necessary to find the endomorphism: //! //! $$(x, y, z) \rightarrow (x^q \psi_x, y^q \psi_y, z^q)$$ //! diff --git a/src/pairings.rs b/src/pairings.rs index 8c554c49..0c3d1fd4 100644 --- a/src/pairings.rs +++ b/src/pairings.rs @@ -1,3 +1,4 @@ +#![allow(clippy::needless_lifetimes, unused_attributes)] #[cfg(all(feature = "alloc", feature = "pairing"))] mod dusk; diff --git a/src/pairings/dusk/mod.rs b/src/pairings/dusk/mod.rs index 2ec81b30..0eede661 100644 --- a/src/pairings/dusk/mod.rs +++ b/src/pairings/dusk/mod.rs @@ -142,7 +142,7 @@ mod serde_support { coeffs = Some(map.next_value()?); } } - field => return Err(SerdeError::unknown_field(field, &FIELDS)), + field => return Err(SerdeError::unknown_field(field, FIELDS)), } } Ok(G2Prepared { diff --git a/src/scalar.rs b/src/scalar.rs index af274031..6c59412e 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -1,6 +1,8 @@ //! This module provides an implementation of the BLS12-381 scalar field $\mathbb{F}_q$ //! where `q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001` +#![allow(clippy::needless_borrow, clippy::needless_lifetimes, unused_attributes)] + mod dusk; use core::fmt; diff --git a/src/scalar/dusk.rs b/src/scalar/dusk.rs index 4119657e..69679584 100644 --- a/src/scalar/dusk.rs +++ b/src/scalar/dusk.rs @@ -11,7 +11,7 @@ use core::ops::{BitAnd, BitXor}; use dusk_bytes::{Error as BytesError, Serializable}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; -use super::{Scalar, R2}; +use super::Scalar; /// Orders scalars by comparing their internal Montgomery-form limbs /// lexicographically (most-significant limb first). @@ -154,7 +154,7 @@ pub const GEN_Y: Scalar = Scalar([ 0xCD482CC3FD6FF4D, ]); -impl<'a, 'b> BitXor<&'b Scalar> for &'a Scalar { +impl<'b> BitXor<&'b Scalar> for &Scalar { type Output = Scalar; fn bitxor(self, rhs: &'b Scalar) -> Scalar { @@ -185,7 +185,7 @@ impl BitAnd for Scalar { } } -impl<'a, 'b> BitAnd<&'b Scalar> for &'a Scalar { +impl<'b> BitAnd<&'b Scalar> for &Scalar { type Output = Scalar; fn bitand(self, rhs: &'b Scalar) -> Scalar { @@ -278,7 +278,7 @@ impl Scalar { /// /// By treating the output of the BLAKE2b hash as a random oracle, this /// implementation follows the first conversion of - /// https://hackmd.io/zV6qe1_oSU-kYU6Tt7pO7Q with concrete numbers: + /// with concrete numbers: /// ```text /// p = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 /// p = 52435875175126190479447740508185965837690552500527637822603658699938581184513 @@ -339,170 +339,177 @@ impl Scalar { } } -#[test] -fn test_partial_ord() { - let one = Scalar::one(); - assert!(one < -one); -} +#[cfg(test)] +mod tests { + use super::*; + use crate::scalar::R2; -#[test] -fn test_xor() { - let a = Scalar::from(500u64); - let b = Scalar::from(499u64); - let res = Scalar::from(7u64); - assert_eq!(&a ^ &b, res); -} + #[test] + fn test_partial_ord() { + let one = Scalar::one(); + assert!(one < -one); + } -#[test] -fn test_and() { - let a = Scalar::one(); - let b = Scalar::one(); - let res = Scalar::one(); - assert_eq!(&a & &b, res); - assert_eq!(a & -a, Scalar::zero()); -} + #[test] + fn test_xor() { + let a = Scalar::from(500u64); + let b = Scalar::from(499u64); + let res = Scalar::from(7u64); + assert_eq!(&a ^ &b, res); + } -#[test] -fn test_iter_sum() { - let scalars = vec![Scalar::one(), Scalar::one()]; - let res: Scalar = scalars.iter().sum(); - assert_eq!(res, Scalar::one() + Scalar::one()); -} + #[test] + fn test_and() { + let a = Scalar::one(); + let b = Scalar::one(); + let res = Scalar::one(); + assert_eq!(&a & &b, res); + assert_eq!(a & -a, Scalar::zero()); + } -#[test] -fn test_iter_prod() { - let scalars = vec![Scalar::one() + Scalar::one(), Scalar::one() + Scalar::one()]; - let res: Scalar = scalars.iter().product(); - assert_eq!(res, Scalar::from(4u64)); -} + #[test] + fn test_iter_sum() { + let scalars = vec![Scalar::one(), Scalar::one()]; + let res: Scalar = scalars.iter().sum(); + assert_eq!(res, Scalar::one() + Scalar::one()); + } -#[test] -fn bit_repr() { - let two_pow_128 = Scalar::from(2u64).pow(&[128, 0, 0, 0]); - let two_pow_128_bits = [ - 0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]; - assert_eq!(&two_pow_128.to_bits()[..], &two_pow_128_bits[..]); - - let two_pow_128_minus_rand = Scalar::from(2u64).pow(&[128, 0, 0, 0]) - Scalar::from(7568589u64); - let two_pow_128_bits = [ - 1u8, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - ]; - assert_eq!( - &two_pow_128_minus_rand.to_bits()[..128], - &two_pow_128_bits[..] - ) -} + #[test] + fn test_iter_prod() { + let scalars = vec![Scalar::one() + Scalar::one(), Scalar::one() + Scalar::one()]; + let res: Scalar = scalars.iter().product(); + assert_eq!(res, Scalar::from(4u64)); + } -#[test] -fn pow_of_two_test() { - let two = Scalar::from(2u64); - for i in 0..1000 { - assert_eq!(Scalar::pow_of_2(i as u64), two.pow(&[i as u64, 0, 0, 0])); + #[test] + fn bit_repr() { + let two_pow_128 = Scalar::from(2u64).pow(&[128, 0, 0, 0]); + let two_pow_128_bits = [ + 0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + assert_eq!(&two_pow_128.to_bits()[..], &two_pow_128_bits[..]); + + let two_pow_128_minus_rand = + Scalar::from(2u64).pow(&[128, 0, 0, 0]) - Scalar::from(7568589u64); + let two_pow_128_bits = [ + 1u8, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + ]; + assert_eq!( + &two_pow_128_minus_rand.to_bits()[..128], + &two_pow_128_bits[..] + ) } -} -#[test] -fn test_scalar_eq_and_hash() { - use sha3::{Digest, Keccak256}; - - let r0 = Scalar::from_raw([ - 0x1fff_3231_233f_fffd, - 0x4884_b7fa_0003_4802, - 0x998c_4fef_ecbc_4ff3, - 0x1824_b159_acc5_0562, - ]); - let r1 = Scalar::from_raw([ - 0x1fff_3231_233f_fffd, - 0x4884_b7fa_0003_4802, - 0x998c_4fef_ecbc_4ff3, - 0x1824_b159_acc5_0562, - ]); - let r2 = Scalar::from(7); - - // Check PartialEq - assert!(r0 == r1); - assert!(r0 != r2); - - let hash_r0 = Keccak256::digest(&r0.to_bytes()); - let hash_r1 = Keccak256::digest(&r1.to_bytes()); - let hash_r2 = Keccak256::digest(&r2.to_bytes()); - - // Check if hash results are consistent with PartialEq results - assert_eq!(hash_r0, hash_r1); - assert_ne!(hash_r0, hash_r2); -} + #[test] + fn pow_of_two_test() { + let two = Scalar::from(2u64); + for i in 0..1000 { + assert_eq!(Scalar::pow_of_2(i as u64), two.pow(&[i as u64, 0, 0, 0])); + } + } -#[test] -fn test_to_be_bytes() { - assert_eq!( - Scalar::zero().to_be_bytes(), - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ] - ); + #[test] + fn test_scalar_eq_and_hash() { + use sha3::{Digest, Keccak256}; + + let r0 = Scalar::from_raw([ + 0x1fff_3231_233f_fffd, + 0x4884_b7fa_0003_4802, + 0x998c_4fef_ecbc_4ff3, + 0x1824_b159_acc5_0562, + ]); + let r1 = Scalar::from_raw([ + 0x1fff_3231_233f_fffd, + 0x4884_b7fa_0003_4802, + 0x998c_4fef_ecbc_4ff3, + 0x1824_b159_acc5_0562, + ]); + let r2 = Scalar::from(7); + + // Check PartialEq + assert!(r0 == r1); + assert!(r0 != r2); + + let hash_r0 = Keccak256::digest(&r0.to_bytes()); + let hash_r1 = Keccak256::digest(&r1.to_bytes()); + let hash_r2 = Keccak256::digest(&r2.to_bytes()); + + // Check if hash results are consistent with PartialEq results + assert_eq!(hash_r0, hash_r1); + assert_ne!(hash_r0, hash_r2); + } - assert_eq!( - Scalar::one().to_be_bytes(), - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1 - ] - ); - - assert_eq!( - R2.to_be_bytes(), - [ - 24, 36, 177, 89, 172, 197, 5, 111, 153, 140, 79, 239, 236, 188, 79, 245, 88, 132, 183, - 250, 0, 3, 72, 2, 0, 0, 0, 1, 255, 255, 255, 254 - ] - ); - - assert_eq!( - (-&Scalar::one()).to_be_bytes(), - [ - 115, 237, 167, 83, 41, 157, 125, 72, 51, 57, 216, 8, 9, 161, 216, 5, 83, 189, 164, 2, - 255, 254, 91, 254, 255, 255, 255, 255, 0, 0, 0, 0 - ] - ); -} + #[test] + fn test_to_be_bytes() { + assert_eq!( + Scalar::zero().to_be_bytes(), + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 + ] + ); + + assert_eq!( + Scalar::one().to_be_bytes(), + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1 + ] + ); + + assert_eq!( + R2.to_be_bytes(), + [ + 24, 36, 177, 89, 172, 197, 5, 111, 153, 140, 79, 239, 236, 188, 79, 245, 88, 132, + 183, 250, 0, 3, 72, 2, 0, 0, 0, 1, 255, 255, 255, 254 + ] + ); + + assert_eq!( + (-&Scalar::one()).to_be_bytes(), + [ + 115, 237, 167, 83, 41, 157, 125, 72, 51, 57, 216, 8, 9, 161, 216, 5, 83, 189, 164, + 2, 255, 254, 91, 254, 255, 255, 255, 255, 0, 0, 0, 0 + ] + ); + } -#[cfg(all(test, feature = "alloc"))] -mod fuzz { - use alloc::vec::Vec; + #[cfg(feature = "alloc")] + mod fuzz { + use alloc::vec::Vec; - use crate::scalar::{Scalar, MODULUS}; - use crate::util::sbb; + use crate::scalar::{Scalar, MODULUS}; + use crate::util::sbb; - fn is_scalar_in_range(scalar: &Scalar) -> bool { - // subtraction against modulus must underflow - let borrow = scalar - .0 - .iter() - .zip(MODULUS.0.iter()) - .fold(0, |borrow, (&s, &m)| sbb(s, m, borrow).1); + fn is_scalar_in_range(scalar: &Scalar) -> bool { + // subtraction against modulus must underflow + let borrow = scalar + .0 + .iter() + .zip(MODULUS.0.iter()) + .fold(0, |borrow, (&s, &m)| sbb(s, m, borrow).1); - borrow == u64::MAX - } + borrow == u64::MAX + } - quickcheck::quickcheck! { - fn prop_scalar_from_raw_bytes(bytes: Vec) -> bool { - let scalar = Scalar::hash_to_scalar(&bytes); + quickcheck::quickcheck! { + fn prop_scalar_from_raw_bytes(bytes: Vec) -> bool { + let scalar = Scalar::hash_to_scalar(&bytes); - is_scalar_in_range(&scalar) + is_scalar_in_range(&scalar) + } } } }