Skip to content

Commit 7208720

Browse files
committed
Add TryFrom integer conversions for NonZero scalars
- Add ZeroScalar error type for when converting zero to NonZero - Implement TryFrom<integer> for Scalar<S, NonZero> that fails on zero - Keep existing infallible From<integer> for Scalar<S, Zero> - Add comprehensive test coverage for the new conversions
1 parent f930a03 commit 7208720

File tree

1 file changed

+75
-0
lines changed

1 file changed

+75
-0
lines changed

secp256kfun/src/scalar.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,30 @@ mod conversion_impls {
482482
#[cfg(feature = "std")]
483483
impl<T> std::error::Error for ScalarTooLarge<T> {}
484484

485+
/// Error returned when trying to convert a zero value into a NonZero scalar
486+
pub struct ZeroScalar<T>(PhantomData<T>);
487+
488+
impl<T> core::fmt::Display for ZeroScalar<T> {
489+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
490+
write!(
491+
f,
492+
"cannot convert zero {} to NonZero scalar",
493+
type_name::<T>()
494+
)
495+
}
496+
}
497+
498+
impl<T> core::fmt::Debug for ZeroScalar<T> {
499+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
500+
f.debug_tuple("ZeroScalar")
501+
.field(&type_name::<T>())
502+
.finish()
503+
}
504+
}
505+
506+
#[cfg(feature = "std")]
507+
impl<T> std::error::Error for ZeroScalar<T> {}
508+
485509
/// Implements `From<$t> for $scalar` **and**
486510
/// `TryFrom<$scalar> for $t` for every `$t` supplied.
487511
macro_rules! impl_scalar_conversions {
@@ -497,6 +521,27 @@ mod conversion_impls {
497521
}
498522
}
499523

524+
impl<S> TryFrom<$t> for Scalar<S, NonZero> {
525+
type Error = ZeroScalar<$t>;
526+
527+
fn try_from(value: $t) -> Result<Self, Self::Error> {
528+
// big-endian integer → 32-byte array
529+
let mut bytes = [0u8; 32];
530+
let int_bytes = value.to_be_bytes();
531+
bytes[32 - int_bytes.len() ..].copy_from_slice(&int_bytes);
532+
let scalar = Scalar::<S, Zero>::from_bytes(bytes).unwrap();
533+
534+
// Check if value is zero
535+
if value == 0 {
536+
Err(ZeroScalar(PhantomData))
537+
} else {
538+
Ok(scalar.non_zero().unwrap())
539+
}
540+
}
541+
}
542+
543+
544+
500545
impl<S, Z> TryFrom<Scalar<S, Z>> for $t {
501546
type Error = ScalarTooLarge<$t>;
502547

@@ -759,4 +804,34 @@ mod test {
759804
assert!(Scalar::<Public, _>::from(41u32) < Scalar::<Public, _>::from(42u32));
760805
assert!(Scalar::<Public, _>::from(42u32) <= Scalar::<Public, _>::from(42u32));
761806
}
807+
808+
#[test]
809+
fn try_from_zero_to_nonzero() {
810+
use core::convert::TryFrom;
811+
812+
// Test that converting zero to NonZero fails
813+
let result = Scalar::<Secret, NonZero>::try_from(0u32);
814+
assert!(result.is_err());
815+
816+
// Test that converting non-zero to NonZero succeeds
817+
let result = Scalar::<Secret, NonZero>::try_from(42u32);
818+
assert!(result.is_ok());
819+
assert_eq!(
820+
result.unwrap(),
821+
Scalar::<Secret, Zero>::from(42u32).non_zero().unwrap()
822+
);
823+
824+
// Test with different integer types
825+
assert!(Scalar::<Public, NonZero>::try_from(0u8).is_err());
826+
assert!(Scalar::<Public, NonZero>::try_from(0u16).is_err());
827+
assert!(Scalar::<Public, NonZero>::try_from(0u64).is_err());
828+
829+
assert!(Scalar::<Public, NonZero>::try_from(1u8).is_ok());
830+
assert!(Scalar::<Public, NonZero>::try_from(1u16).is_ok());
831+
assert!(Scalar::<Public, NonZero>::try_from(1u64).is_ok());
832+
833+
// Test that infallible From still works for Zero
834+
let _zero_scalar: Scalar<Secret, Zero> = 0u32.into();
835+
let _nonzero_scalar: Scalar<Secret, Zero> = 42u32.into();
836+
}
762837
}

0 commit comments

Comments
 (0)