Skip to content

Commit 85ba5f9

Browse files
committed
impl conversion traits for all uints for Scalar
including core::num::NonZero<_>
1 parent 1a28fc5 commit 85ba5f9

File tree

6 files changed

+157
-54
lines changed

6 files changed

+157
-54
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# CHANGELOG
22

3-
## Unreleased
3+
## UNRELEASED
44

5+
- Add From/TryFrom conversions for `Scalar` to all unsigned integer types
56
- Upgrade to bincode v2
67
- MSRV 1.63 -> 1.85
78

arithmetic_macros/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ fn compile_s(path: &Ident, node: Node) -> proc_macro2::TokenStream {
7373
quote_spanned! { node.span => #path::Scalar::<#path::marker::Secret, _>::zero() }
7474
} else {
7575
quote_spanned! { node.span =>
76-
#path::Scalar::<#path::marker::Secret, #path::marker::NonZero>::from_non_zero_u32(unsafe {
77-
core::num::NonZeroU32::new_unchecked(#lit_int)
76+
#path::Scalar::<#path::marker::Secret, #path::marker::NonZero>::from(unsafe {
77+
core::num::NonZero::new_unchecked(#lit_int)
7878
})
7979
}
8080
}

secp256kfun/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ let B = Point::random(&mut rand::thread_rng());
139139

140140
// Alice commits to her secret value x with randomness r
141141
let r = Scalar::random(&mut rand::thread_rng());
142-
let x = Scalar::<Secret, Zero>::from(42);
142+
let x = Scalar::<Secret, Zero>::from(42u32);
143143
let commitment = pedersen_commit(A, &B, &r, &x);
144144

145145
// Imagine Later on, Bob receives the public opening (r,x) for commitment. He

secp256kfun/src/backend/k256_impl.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,6 @@ impl BackendScalar for Scalar {
1515
-Scalar::ONE
1616
}
1717

18-
fn from_u32(int: u32) -> Self {
19-
Self::from(int)
20-
}
21-
2218
fn zero() -> Self {
2319
Scalar::ZERO
2420
}

secp256kfun/src/backend/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ pub use k256_impl::*;
55

66
pub trait BackendScalar: Sized {
77
fn minus_one() -> Self;
8-
fn from_u32(int: u32) -> Self;
98
fn zero() -> Self;
109
fn from_bytes_mod_order(bytes: [u8; 32]) -> Self;
1110
fn from_bytes(bytes: [u8; 32]) -> Option<Self>;

secp256kfun/src/scalar.rs

Lines changed: 152 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
use crate::{backend, hash::HashInto, marker::*, op};
33
use core::{
44
marker::PhantomData,
5-
num::NonZeroU32,
65
ops::{AddAssign, MulAssign, SubAssign},
76
};
87
use digest::{self, generic_array::typenum::U32};
@@ -185,7 +184,7 @@ impl<S> Scalar<S, NonZero> {
185184

186185
/// Returns the integer `1` as a `Scalar`.
187186
pub fn one() -> Self {
188-
Scalar::from(1).non_zero().unwrap()
187+
Scalar::<S, Zero>::from(1u32).non_zero().unwrap()
189188
}
190189

191190
/// Returns the integer -1 (modulo the curve order) as a `Scalar`.
@@ -221,13 +220,6 @@ impl<S> Scalar<S, NonZero> {
221220
pub fn mark_zero_choice<Z: ZeroChoice>(self) -> Scalar<S, Z> {
222221
Scalar::from_inner(self.0)
223222
}
224-
225-
/// Converts a [`NonZeroU32`] into a `Scalar<Secret,NonZero>`.
226-
///
227-
/// [`NonZeroU32`]: core::num::NonZeroU32
228-
pub fn from_non_zero_u32(int: core::num::NonZeroU32) -> Self {
229-
Self::from_inner(backend::BackendScalar::from_u32(int.get()))
230-
}
231223
}
232224

233225
impl Scalar<Secret, NonZero> {
@@ -344,18 +336,6 @@ impl<Z1, Z2, S1, S2> PartialEq<Scalar<S2, Z2>> for Scalar<S1, Z1> {
344336

345337
impl<Z, S> Eq for Scalar<Z, S> {}
346338

347-
impl<S> From<u32> for Scalar<S, Zero> {
348-
fn from(int: u32) -> Self {
349-
Self::from_inner(backend::BackendScalar::from_u32(int))
350-
}
351-
}
352-
353-
impl<S> From<NonZeroU32> for Scalar<S, NonZero> {
354-
fn from(int: NonZeroU32) -> Self {
355-
Self::from_inner(backend::BackendScalar::from_u32(int.into()))
356-
}
357-
}
358-
359339
crate::impl_fromstr_deserialize! {
360340
name => "secp256k1 scalar",
361341
fn from_bytes<S, Z: ZeroChoice>(bytes: [u8;32]) -> Option<Scalar<S,Z>> {
@@ -405,7 +385,7 @@ where
405385
S: Secrecy,
406386
{
407387
fn default() -> Self {
408-
Self::from_inner(backend::BackendScalar::from_u32(1))
388+
Self::one()
409389
}
410390
}
411391

@@ -470,10 +450,109 @@ impl<Z> Ord for Scalar<Public, Z> {
470450
}
471451
}
472452

453+
mod conversion_impls {
454+
use super::*;
455+
use core::{any::type_name, convert::TryFrom, fmt, marker::PhantomData, mem};
456+
use subtle::ConstantTimeEq;
457+
458+
/// Returned when a `Scalar` value exceeds the range of the target integer.
459+
#[derive(Clone, Copy, PartialEq, Eq)]
460+
pub struct ScalarTooLarge<T>(PhantomData<T>);
461+
462+
impl<T> core::fmt::Display for ScalarTooLarge<T> {
463+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
464+
write!(f, "scalar value does not fit into {}", type_name::<T>())
465+
}
466+
}
467+
468+
impl<T> core::fmt::Debug for ScalarTooLarge<T> {
469+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
470+
f.debug_tuple("ScalarTooLarge")
471+
.field(&type_name::<T>())
472+
.finish()
473+
}
474+
}
475+
476+
#[cfg(feature = "std")]
477+
impl<T> std::error::Error for ScalarTooLarge<T> {}
478+
479+
/// Implements `From<$t> for $scalar` **and**
480+
/// `TryFrom<$scalar> for $t` for every `$t` supplied.
481+
macro_rules! impl_scalar_conversions {
482+
($($t:ty),+ $(,)?) => {
483+
$(
484+
impl<S> From<$t> for Scalar<S, Zero> {
485+
fn from(value: $t) -> Self {
486+
// big-endian integer → 32-byte array
487+
let mut bytes = [0u8; 32];
488+
let int_bytes = value.to_be_bytes();
489+
bytes[32 - int_bytes.len() ..].copy_from_slice(&int_bytes);
490+
Scalar::<S, Zero>::from_bytes(bytes).unwrap()
491+
}
492+
}
493+
494+
impl<S, Z> TryFrom<Scalar<S, Z>> for $t {
495+
type Error = ScalarTooLarge<$t>;
496+
497+
fn try_from(value: Scalar<S, Z>) -> Result<Self, Self::Error> {
498+
let bytes = value.to_bytes();
499+
500+
// Overflow check: any non-zero in the high 32−N bytes fails.
501+
let high = &bytes[.. 32 - mem::size_of::<$t>()];
502+
if high.ct_eq(&[0x0;32 - mem::size_of::<$t>()]).into() {
503+
// Safe: the slice is exactly the right length.
504+
let mut buf = [0u8; mem::size_of::<$t>()];
505+
buf.copy_from_slice(&bytes[32 - mem::size_of::<$t>() ..]);
506+
Ok(<$t>::from_be_bytes(buf))
507+
} else {
508+
Err(ScalarTooLarge(PhantomData))
509+
}
510+
511+
}
512+
}
513+
514+
impl<S> From<core::num::NonZero<$t>> for Scalar<S, NonZero> {
515+
fn from(value: core::num::NonZero<$t>) -> Self {
516+
// big-endian integer → 32-byte array
517+
let mut bytes = [0u8; 32];
518+
let int_bytes = value.get().to_be_bytes();
519+
bytes[32 - int_bytes.len() ..].copy_from_slice(&int_bytes);
520+
Scalar::<S, Zero>::from_bytes(bytes).unwrap().non_zero().unwrap()
521+
}
522+
}
523+
524+
impl<S> TryFrom<Scalar<S, NonZero>> for core::num::NonZero<$t> {
525+
type Error = ScalarTooLarge<$t>;
526+
527+
fn try_from(value: Scalar<S, NonZero>) -> Result<Self, Self::Error> {
528+
let bytes = value.to_bytes();
529+
530+
// Overflow check: any non-zero in the high 32−N bytes fails.
531+
let high = &bytes[.. 32 - mem::size_of::<$t>()];
532+
if high.ct_eq(&[0x0;32 - mem::size_of::<$t>()]).into() {
533+
// Safe: the slice is exactly the right length.
534+
let mut buf = [0u8; mem::size_of::<$t>()];
535+
buf.copy_from_slice(&bytes[32 - mem::size_of::<$t>() ..]);
536+
Ok(core::num::NonZero::new(<$t>::from_be_bytes(buf)).unwrap())
537+
} else {
538+
Err(ScalarTooLarge(PhantomData))
539+
}
540+
541+
}
542+
}
543+
544+
)*
545+
};
546+
}
547+
548+
impl_scalar_conversions!(u8, u16, u32, u64, usize, u128);
549+
}
550+
473551
#[cfg(test)]
474552
mod test {
475553
use super::*;
476554
use crate::{hex, s};
555+
#[cfg(feature = "alloc")]
477556
use proptest::prelude::*;
478557
#[cfg(target_arch = "wasm32")]
479558
use wasm_bindgen_test::wasm_bindgen_test as test;
@@ -503,11 +582,12 @@ mod test {
503582
assert_ne!(scalar_1, scalar_2);
504583
}
505584

585+
#[cfg(feature = "alloc")]
506586
proptest! {
507587
#[test]
508588
fn invert(x in any::<Scalar>(), y in any::<Scalar<Public>>()) {
509-
assert_eq!(s!(x * { x.invert() }), s!(1));
510-
assert_eq!(s!(y * { y.invert() }), s!(1));
589+
prop_assert_eq!(s!(x * { x.invert() }), s!(1));
590+
prop_assert_eq!(s!(y * { y.invert() }), s!(1));
511591
}
512592

513593
#[test]
@@ -516,24 +596,48 @@ mod test {
516596
c in any::<Scalar<Public,Zero>>(),
517597
d in any::<Scalar<Secret,Zero>>(),
518598
) {
519-
assert_eq!(s!(a - a), s!(0));
520-
assert_eq!(s!(b - b), s!(0));
521-
assert_eq!(s!(c - c), s!(0));
522-
assert_eq!(s!(d - d), s!(0));
523-
assert_eq!(s!(a - a), s!(-a + a));
524-
assert_eq!(s!(a - b), s!(-b + a));
525-
assert_eq!(s!(a - c), s!(-c + a));
526-
assert_eq!(s!(a - d), s!(-d + a));
599+
prop_assert_eq!(s!(a - a), s!(0));
600+
prop_assert_eq!(s!(b - b), s!(0));
601+
prop_assert_eq!(s!(c - c), s!(0));
602+
prop_assert_eq!(s!(d - d), s!(0));
603+
prop_assert_eq!(s!(a - a), s!(-a + a));
604+
prop_assert_eq!(s!(a - b), s!(-b + a));
605+
prop_assert_eq!(s!(a - c), s!(-c + a));
606+
prop_assert_eq!(s!(a - d), s!(-d + a));
527607

528608
if a != b {
529-
assert_ne!(s!(a - b), s!(b - a));
609+
prop_assert_ne!(s!(a - b), s!(b - a));
530610
}
531611

532612
if c != d {
533-
assert_ne!(s!(c - d), s!(d - c));
613+
prop_assert_ne!(s!(c - d), s!(d - c));
534614
}
535615
}
536616

617+
/// Any `u128` should convert to a `Scalar` and back loss-lessly.
618+
#[test]
619+
fn u128_roundtrip(xs in any::<u128>()) {
620+
// u128 → Scalar
621+
let s: Scalar<Public, Zero> = xs.into();
622+
623+
// Scalar → u128
624+
let back = u128::try_from(s)
625+
.expect("a u128 always fits inside a 256-bit scalar");
626+
627+
prop_assert_eq!(xs, back);
628+
}
629+
630+
#[test]
631+
fn nz_u128_roundtrip(xs in any::<core::num::NonZero<u128>>()) {
632+
// u128 → Scalar
633+
let s: Scalar<Public, NonZero> = xs.into();
634+
635+
// Scalar → u128
636+
let back = core::num::NonZero::<u128>::try_from(s)
637+
.expect("a u128 always fits inside a 256-bit scalar");
638+
639+
prop_assert_eq!(xs, back);
640+
}
537641

538642
}
539643

@@ -566,7 +670,10 @@ mod test {
566670

567671
#[test]
568672
fn zero() {
569-
assert_eq!(Scalar::<Secret, Zero>::zero(), Scalar::<Secret, _>::from(0));
673+
assert_eq!(
674+
Scalar::<Secret, Zero>::zero(),
675+
Scalar::<Secret, _>::from(0u32)
676+
);
570677
}
571678

572679
#[test]
@@ -610,7 +717,7 @@ mod test {
610717
.as_ref()
611718
)
612719
.unwrap(),
613-
Scalar::<Secret, _>::from(1)
720+
Scalar::<Secret, _>::from(1u32)
614721
)
615722
}
616723

@@ -629,21 +736,21 @@ mod test {
629736

630737
#[test]
631738
fn assign_tests() {
632-
let mut a = Scalar::<Secret, _>::from(42);
633-
let b = Scalar::<Secret, _>::from(1337).public();
739+
let mut a = Scalar::<Secret, _>::from(42u8);
740+
let b = Scalar::<Secret, _>::from(1337u16).public();
634741
a += b;
635-
assert_eq!(a, Scalar::<Secret, _>::from(1379));
742+
assert_eq!(a, Scalar::<Secret, _>::from(1379u16));
636743
a -= b;
637-
assert_eq!(a, Scalar::<Secret, _>::from(42));
744+
assert_eq!(a, Scalar::<Secret, _>::from(42u32));
638745
a *= b;
639-
assert_eq!(a, Scalar::<Secret, _>::from(42 * 1337));
746+
assert_eq!(a, Scalar::<Secret, _>::from(42u16 * 1337u16));
640747
}
641748

642749
#[test]
643750
fn scalar_ord() {
644-
assert!(Scalar::<Public, _>::from(1337) > Scalar::<Public, _>::from(42));
645-
assert!(Scalar::<Public, _>::from(42) < Scalar::<Public, _>::from(1337));
646-
assert!(Scalar::<Public, _>::from(41) < Scalar::<Public, _>::from(42));
647-
assert!(Scalar::<Public, _>::from(42) <= Scalar::<Public, _>::from(42));
751+
assert!(Scalar::<Public, _>::from(1337u32) > Scalar::<Public, _>::from(42u8));
752+
assert!(Scalar::<Public, _>::from(42u32) < Scalar::<Public, _>::from(1337u16));
753+
assert!(Scalar::<Public, _>::from(41u32) < Scalar::<Public, _>::from(42u32));
754+
assert!(Scalar::<Public, _>::from(42u32) <= Scalar::<Public, _>::from(42u32));
648755
}
649756
}

0 commit comments

Comments
 (0)