Skip to content

Commit

Permalink
Add field conversion to/from [u64;4] (#80)
Browse files Browse the repository at this point in the history
* feat: add field conversion to/from `[u64;4]`

* Added conversion tests
* Added `montgomery_reduce_short` for no-asm
* For bn256, uses assembly conversion when asm feature is on

* fix: remove conflict for asm

* chore: bump rust-toolchain to 1.67.0
  • Loading branch information
jonathanpwang authored Aug 21, 2023
1 parent 2264867 commit 723c976
Show file tree
Hide file tree
Showing 10 changed files with 144 additions and 69 deletions.
2 changes: 1 addition & 1 deletion rust-toolchain
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.63.0
1.67.0
8 changes: 8 additions & 0 deletions src/bn256/assembly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,14 @@ macro_rules! field_arithmetic_asm {
$field([r0, r1, r2, r3])
}
}

impl From<$field> for [u64; 4] {
fn from(elt: $field) -> [u64; 4] {
// Turn into canonical form by computing
// (a.R) / R = a
elt.montgomery_reduce_256().0
}
}
};
}

Expand Down
25 changes: 11 additions & 14 deletions src/bn256/fq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::bn256::assembly::field_arithmetic_asm;
#[cfg(not(feature = "asm"))]
use crate::{field_arithmetic, field_specific};

use crate::arithmetic::{adc, mac, sbb};
use crate::arithmetic::{adc, mac, macx, sbb};
use crate::bn256::LegendreSymbol;
use crate::ff::{Field, FromUniformBytes, PrimeField, WithSmallOrderMulGroup};
use crate::{
Expand Down Expand Up @@ -271,20 +271,12 @@ impl ff::PrimeField for Fq {
}

fn to_repr(&self) -> Self::Repr {
// Turn into canonical form by computing
// (a.R) / R = a

#[cfg(not(feature = "asm"))]
let tmp =
Self::montgomery_reduce(&[self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0]);
#[cfg(feature = "asm")]
let tmp = self.montgomery_reduce_256();

let tmp: [u64; 4] = (*self).into();
let mut res = [0; 32];
res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes());
res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes());
res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes());
res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes());
res[0..8].copy_from_slice(&tmp[0].to_le_bytes());
res[8..16].copy_from_slice(&tmp[1].to_le_bytes());
res[16..24].copy_from_slice(&tmp[2].to_le_bytes());
res[24..32].copy_from_slice(&tmp[3].to_le_bytes());

res
}
Expand Down Expand Up @@ -384,6 +376,11 @@ mod test {
crate::tests::field::random_field_tests::<Fq>("fq".to_string());
}

#[test]
fn test_conversion() {
crate::tests::field::random_conversion_tests::<Fq>("fq".to_string());
}

#[test]
#[cfg(feature = "bits")]
fn test_bits() {
Expand Down
25 changes: 11 additions & 14 deletions src/bn256/fr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub use table::FR_TABLE;
#[cfg(not(feature = "bn256-table"))]
use crate::impl_from_u64;

use crate::arithmetic::{adc, mac, sbb};
use crate::arithmetic::{adc, mac, macx, sbb};
use crate::ff::{FromUniformBytes, PrimeField, WithSmallOrderMulGroup};
use crate::{
field_bits, field_common, impl_add_binop_specify_output, impl_binops_additive,
Expand Down Expand Up @@ -300,20 +300,12 @@ impl ff::PrimeField for Fr {
}

fn to_repr(&self) -> Self::Repr {
// Turn into canonical form by computing
// (a.R) / R = a

#[cfg(not(feature = "asm"))]
let tmp =
Self::montgomery_reduce(&[self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0]);
#[cfg(feature = "asm")]
let tmp = self.montgomery_reduce_256();

let tmp: [u64; 4] = (*self).into();
let mut res = [0; 32];
res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes());
res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes());
res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes());
res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes());
res[0..8].copy_from_slice(&tmp[0].to_le_bytes());
res[8..16].copy_from_slice(&tmp[1].to_le_bytes());
res[16..24].copy_from_slice(&tmp[2].to_le_bytes());
res[24..32].copy_from_slice(&tmp[3].to_le_bytes());

res
}
Expand Down Expand Up @@ -406,6 +398,11 @@ mod test {
);
}

#[test]
fn test_conversion() {
crate::tests::field::random_conversion_tests::<Fr>("fr".to_string());
}

#[test]
#[cfg(feature = "bits")]
fn test_bits() {
Expand Down
49 changes: 49 additions & 0 deletions src/derive/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,12 @@ macro_rules! field_common {
}
}

impl From<[u64; 4]> for $field {
fn from(digits: [u64; 4]) -> Self {
Self::from_raw(digits)
}
}

impl From<$field> for [u8; 32] {
fn from(value: $field) -> [u8; 32] {
value.to_repr()
Expand Down Expand Up @@ -442,6 +448,49 @@ macro_rules! field_arithmetic {

$field([d0 & mask, d1 & mask, d2 & mask, d3 & mask])
}

/// Montgomery reduce where last 4 registers are 0
#[inline(always)]
pub(crate) const fn montgomery_reduce_short(r: &[u64; 4]) -> $field {
// The Montgomery reduction here is based on Algorithm 14.32 in
// Handbook of Applied Cryptography
// <http://cacr.uwaterloo.ca/hac/about/chap14.pdf>.

let k = r[0].wrapping_mul($inv);
let (_, r0) = macx(r[0], k, $modulus.0[0]);
let (r1, r0) = mac(r[1], k, $modulus.0[1], r0);
let (r2, r0) = mac(r[2], k, $modulus.0[2], r0);
let (r3, r0) = mac(r[3], k, $modulus.0[3], r0);

let k = r1.wrapping_mul($inv);
let (_, r1) = macx(r1, k, $modulus.0[0]);
let (r2, r1) = mac(r2, k, $modulus.0[1], r1);
let (r3, r1) = mac(r3, k, $modulus.0[2], r1);
let (r0, r1) = mac(r0, k, $modulus.0[3], r1);

let k = r2.wrapping_mul($inv);
let (_, r2) = macx(r2, k, $modulus.0[0]);
let (r3, r2) = mac(r3, k, $modulus.0[1], r2);
let (r0, r2) = mac(r0, k, $modulus.0[2], r2);
let (r1, r2) = mac(r1, k, $modulus.0[3], r2);

let k = r3.wrapping_mul($inv);
let (_, r3) = macx(r3, k, $modulus.0[0]);
let (r0, r3) = mac(r0, k, $modulus.0[1], r3);
let (r1, r3) = mac(r1, k, $modulus.0[2], r3);
let (r2, r3) = mac(r2, k, $modulus.0[3], r3);

// Result may be within MODULUS of the correct value
(&$field([r0, r1, r2, r3])).sub(&$modulus)
}
}

impl From<$field> for [u64; 4] {
fn from(elt: $field) -> [u64; 4] {
// Turn into canonical form by computing
// (a.R) / R = a
$field::montgomery_reduce_short(&elt.0).0
}
}
};
}
Expand Down
20 changes: 11 additions & 9 deletions src/secp256k1/fp.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::arithmetic::{adc, mac, sbb};
use crate::arithmetic::{adc, mac, macx, sbb};
use crate::ff::{FromUniformBytes, PrimeField, WithSmallOrderMulGroup};
use crate::{
field_arithmetic, field_bits, field_common, field_specific, impl_add_binop_specify_output,
Expand Down Expand Up @@ -255,15 +255,12 @@ impl ff::PrimeField for Fp {
}

fn to_repr(&self) -> Self::Repr {
// Turn into canonical form by computing
// (a.R) / R = a
let tmp = Fp::montgomery_reduce(&[self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0]);

let tmp: [u64; 4] = (*self).into();
let mut res = [0; 32];
res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes());
res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes());
res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes());
res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes());
res[0..8].copy_from_slice(&tmp[0].to_le_bytes());
res[8..16].copy_from_slice(&tmp[1].to_le_bytes());
res[16..24].copy_from_slice(&tmp[2].to_le_bytes());
res[24..32].copy_from_slice(&tmp[3].to_le_bytes());

res
}
Expand Down Expand Up @@ -353,6 +350,11 @@ mod test {
crate::tests::field::random_field_tests::<Fp>("secp256k1 base".to_string());
}

#[test]
fn test_conversion() {
crate::tests::field::random_conversion_tests::<Fp>("secp256k1 base".to_string());
}

#[test]
#[cfg(feature = "bits")]
fn test_bits() {
Expand Down
20 changes: 11 additions & 9 deletions src/secp256k1/fq.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::arithmetic::{adc, mac, sbb};
use crate::arithmetic::{adc, mac, macx, sbb};
use crate::ff::{FromUniformBytes, PrimeField, WithSmallOrderMulGroup};
use crate::{
field_arithmetic, field_bits, field_common, field_specific, impl_add_binop_specify_output,
Expand Down Expand Up @@ -266,15 +266,12 @@ impl ff::PrimeField for Fq {
}

fn to_repr(&self) -> Self::Repr {
// Turn into canonical form by computing
// (a.R) / R = a
let tmp = Fq::montgomery_reduce(&[self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0]);

let tmp: [u64; 4] = (*self).into();
let mut res = [0; 32];
res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes());
res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes());
res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes());
res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes());
res[0..8].copy_from_slice(&tmp[0].to_le_bytes());
res[8..16].copy_from_slice(&tmp[1].to_le_bytes());
res[16..24].copy_from_slice(&tmp[2].to_le_bytes());
res[24..32].copy_from_slice(&tmp[3].to_le_bytes());

res
}
Expand Down Expand Up @@ -360,6 +357,11 @@ mod test {
crate::tests::field::random_field_tests::<Fq>("secp256k1 scalar".to_string());
}

#[test]
fn test_conversion() {
crate::tests::field::random_conversion_tests::<Fq>("secp256k1 scalar".to_string());
}

#[test]
#[cfg(feature = "bits")]
fn test_bits() {
Expand Down
28 changes: 15 additions & 13 deletions src/secp256r1/fp.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::arithmetic::{adc, mac, sbb};
use crate::arithmetic::{adc, mac, macx, sbb};
use crate::ff::{FromUniformBytes, PrimeField, WithSmallOrderMulGroup};
use crate::{
field_arithmetic, field_bits, field_common, field_specific, impl_add_binop_specify_output,
Expand Down Expand Up @@ -273,15 +273,12 @@ impl ff::PrimeField for Fp {
}

fn to_repr(&self) -> Self::Repr {
// Turn into canonical form by computing
// (a.R) / R = a
let tmp = Fp::montgomery_reduce(&[self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0]);

let tmp: [u64; 4] = (*self).into();
let mut res = [0; 32];
res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes());
res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes());
res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes());
res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes());
res[0..8].copy_from_slice(&tmp[0].to_le_bytes());
res[8..16].copy_from_slice(&tmp[1].to_le_bytes());
res[16..24].copy_from_slice(&tmp[2].to_le_bytes());
res[24..32].copy_from_slice(&tmp[3].to_le_bytes());

res
}
Expand Down Expand Up @@ -368,19 +365,24 @@ mod test {

#[test]
fn test_field() {
crate::tests::field::random_field_tests::<Fp>("secp256k1 base".to_string());
crate::tests::field::random_field_tests::<Fp>("secp256r1 base".to_string());
}

#[test]
fn test_conversion() {
crate::tests::field::random_conversion_tests::<Fp>("secp256r1 base".to_string());
}

#[test]
#[cfg(feature = "bits")]
fn test_bits() {
crate::tests::field::random_bits_tests::<Fp>("secp256k1 base".to_string());
crate::tests::field::random_bits_tests::<Fp>("secp256r1 base".to_string());
}

#[test]
fn test_serialization() {
crate::tests::field::random_serialization_test::<Fp>("secp256k1 base".to_string());
crate::tests::field::random_serialization_test::<Fp>("secp256r1 base".to_string());
#[cfg(feature = "derive_serde")]
crate::tests::field::random_serde_test::<Fp>("secp256k1 base".to_string());
crate::tests::field::random_serde_test::<Fp>("secp256r1 base".to_string());
}
}
20 changes: 11 additions & 9 deletions src/secp256r1/fq.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::arithmetic::{adc, mac, sbb};
use crate::arithmetic::{adc, mac, macx, sbb};
use crate::ff::{FromUniformBytes, PrimeField, WithSmallOrderMulGroup};
use core::convert::TryInto;
use core::fmt;
Expand Down Expand Up @@ -262,15 +262,12 @@ impl ff::PrimeField for Fq {
}

fn to_repr(&self) -> Self::Repr {
// Turn into canonical form by computing
// (a.R) / R = a
let tmp = Fq::montgomery_reduce(&[self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0]);

let tmp: [u64; 4] = (*self).into();
let mut res = [0; 32];
res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes());
res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes());
res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes());
res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes());
res[0..8].copy_from_slice(&tmp[0].to_le_bytes());
res[8..16].copy_from_slice(&tmp[1].to_le_bytes());
res[16..24].copy_from_slice(&tmp[2].to_le_bytes());
res[24..32].copy_from_slice(&tmp[3].to_le_bytes());

res
}
Expand Down Expand Up @@ -362,6 +359,11 @@ mod test {
crate::tests::field::random_field_tests::<Fq>("secp256r1 scalar".to_string());
}

#[test]
fn test_conversion() {
crate::tests::field::random_conversion_tests::<Fq>("secp256r1 scalar".to_string());
}

#[test]
fn test_serialization() {
crate::tests::field::random_serialization_test::<Fq>("secp256r1 scalar".to_string());
Expand Down
16 changes: 16 additions & 0 deletions src/tests/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,22 @@ fn random_expansion_tests<F: Field, R: RngCore>(mut rng: R, type_name: String) {
end_timer!(start);
}

pub fn random_conversion_tests<F: ff::PrimeField<Repr = [u8; 32]>>(type_name: String) {
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let _message = format!("conversion {type_name}");
let start = start_timer!(|| _message);
for _ in 0..1000000 {
let a = F::random(&mut rng);
let bytes = a.to_repr();
let b = F::from_repr(bytes).unwrap();
assert_eq!(a, b);
}
end_timer!(start);
}

#[cfg(feature = "bits")]
pub fn random_bits_tests<F: ff::PrimeFieldBits>(type_name: String) {
let mut rng = XorShiftRng::from_seed([
Expand Down

0 comments on commit 723c976

Please sign in to comment.