Skip to content

Commit

Permalink
Merge pull request #4 from axiom-crypto/feat/to_from_u64_digits
Browse files Browse the repository at this point in the history
Add field conversion to/from `[u64;4]`
  • Loading branch information
jonathanpwang authored Aug 13, 2023
2 parents b38ecbf + 146c186 commit 64130f3
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 71 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ env:
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true


jobs:
test:
Expand All @@ -42,7 +41,7 @@ jobs:
strategy:
matrix:
include:
- feature:
- feature:
- feature: default
steps:
- uses: actions/checkout@v2
Expand Down Expand Up @@ -95,7 +94,8 @@ jobs:
bench:
if: github.event.pull_request.draft == false
name: Bench
runs-on: ubuntu-latest
timeout-minutes: 30
runs-on: ubuntu-latest-m
strategy:
matrix:
include:
Expand Down
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 64130f3

Please sign in to comment.