Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PrimeField::from_bytes_mod_order #164

Merged
merged 17 commits into from
Jan 9, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ The main features of this release are:
if the top bits of the `u8` value do *not* correspond to one of the possible outputs of `Flags::u8_bitmask`, then these methods output `None`, whereas before they output
a default value.
Downstream users other than `ark-curves` should not see breakage unless they rely on these methods/traits explicitly.
- #164 (ark-ff) Add a method `from_bytes_mod_order` to the `PrimeField` trait. Only users who implement prime fields without using the macros in ark-ff will see breakage.
ValarDragon marked this conversation as resolved.
Show resolved Hide resolved

### Features
- #20 (ark-poly) Add structs/traits for multivariate polynomials
Expand Down
25 changes: 25 additions & 0 deletions ff/src/fields/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,31 @@ macro_rules! impl_Fp {
}
}

ValarDragon marked this conversation as resolved.
Show resolved Hide resolved
fn from_bytes_mod_order(bytes: &[u8]) -> Self {
use ark_std::{cmp::min, vec::Vec};
let num_modulus_bytes = ((P::MODULUS_BITS + 7) / 8) as usize;
let num_bytes_to_directly_convert = min(num_modulus_bytes - 1, bytes.len());
// Copy the leading big-endian bytes directly into a field element.
// The number of bytes directly converted must be less than the
// number of bytes needed to represent the modulus, as we must begin
// modular reduction once the data is of the same number of bytes as the modulus.
let mut bytes_to_directly_convert = Vec::new();
bytes_to_directly_convert.extend(bytes[..num_bytes_to_directly_convert].iter().rev());
// Guaranteed to not be None, as the input is less than the modulus size.
let mut res = Self::from_random_bytes(&bytes_to_directly_convert).unwrap();

// Update the result, byte by byte.
// We go through existing field arithmetic, which handles the reduction.
// TODO: If we need higher speeds, parse more bytes at once, or implement
// modular multiplication by a u64
let window_size = Self::from(256u64);
for i in num_bytes_to_directly_convert..bytes.len() {
res *= window_size;
res += Self::from(bytes[i]);
}
res
}

impl_field_into_repr!($limbs, $BigIntegerType);
}

Expand Down
119 changes: 119 additions & 0 deletions ff/src/fields/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,10 @@ pub trait PrimeField:
/// Returns the underlying representation of the prime field element.
fn into_repr(&self) -> Self::BigInt;

/// Reads bytes in big-endian, and converts them to a field element.
ValarDragon marked this conversation as resolved.
Show resolved Hide resolved
/// If the bytes are larger than the modulus, it will reduce them.
fn from_bytes_mod_order(bytes: &[u8]) -> Self;

/// Return the QNR^t, for t defined by
/// `2^s * t = MODULUS - 1`, and t coprime to 2.
fn qnr_to_t() -> Self {
Expand Down Expand Up @@ -609,4 +613,119 @@ mod no_std_tests {
);
}
}

#[test]
fn test_from_bytes_mod_order() {
// Each test vector is a [u8], string pair,
// such that test_field::from_bytes_mod_order(&u8) = F::from_str(string)
// modulus num bits is 255, and therefore 32 bytes
// Test vectors generated form scripts/test_vectors.py
let test_vectors = vec![
// 0
(vec![0u8], "0"),
// 1
(vec![1u8], "1"),
// 255
(vec![255u8], "255"),
// 256
(vec![1u8, 0u8], "256"),
// 65791
(vec![1u8, 0u8, 255u8], "65791"),
// 204827637402836681560342736360101429053478720705186085244545541796635082752
(
vec![
115u8, 237u8, 167u8, 83u8, 41u8, 157u8, 125u8, 72u8, 51u8, 57u8, 216u8, 8u8,
9u8, 161u8, 216u8, 5u8, 83u8, 189u8, 164u8, 2u8, 255u8, 254u8, 91u8, 254u8,
255u8, 255u8, 255u8, 255u8, 0u8, 0u8, 0u8,
],
"204827637402836681560342736360101429053478720705186085244545541796635082752",
),
// 204827637402836681560342736360101429053478720705186085244545541796635082753
(
vec![
115u8, 237u8, 167u8, 83u8, 41u8, 157u8, 125u8, 72u8, 51u8, 57u8, 216u8, 8u8,
9u8, 161u8, 216u8, 5u8, 83u8, 189u8, 164u8, 2u8, 255u8, 254u8, 91u8, 254u8,
255u8, 255u8, 255u8, 255u8, 0u8, 0u8, 1u8,
],
"204827637402836681560342736360101429053478720705186085244545541796635082753",
),
// 52435875175126190479447740508185965837690552500527637822603658699938581184512
(
vec![
115u8, 237u8, 167u8, 83u8, 41u8, 157u8, 125u8, 72u8, 51u8, 57u8, 216u8, 8u8,
9u8, 161u8, 216u8, 5u8, 83u8, 189u8, 164u8, 2u8, 255u8, 254u8, 91u8, 254u8,
255u8, 255u8, 255u8, 255u8, 0u8, 0u8, 0u8, 0u8,
],
"52435875175126190479447740508185965837690552500527637822603658699938581184512",
),
// 52435875175126190479447740508185965837690552500527637822603658699938581184513
(
vec![
115u8, 237u8, 167u8, 83u8, 41u8, 157u8, 125u8, 72u8, 51u8, 57u8, 216u8, 8u8,
9u8, 161u8, 216u8, 5u8, 83u8, 189u8, 164u8, 2u8, 255u8, 254u8, 91u8, 254u8,
255u8, 255u8, 255u8, 255u8, 0u8, 0u8, 0u8, 1u8,
],
"0",
),
// 52435875175126190479447740508185965837690552500527637822603658699938581184514
(
vec![
115u8, 237u8, 167u8, 83u8, 41u8, 157u8, 125u8, 72u8, 51u8, 57u8, 216u8, 8u8,
9u8, 161u8, 216u8, 5u8, 83u8, 189u8, 164u8, 2u8, 255u8, 254u8, 91u8, 254u8,
255u8, 255u8, 255u8, 255u8, 0u8, 0u8, 0u8, 2u8,
],
"1",
),
// 104871750350252380958895481016371931675381105001055275645207317399877162369026
(
vec![
231u8, 219u8, 78u8, 166u8, 83u8, 58u8, 250u8, 144u8, 102u8, 115u8, 176u8, 16u8,
19u8, 67u8, 176u8, 10u8, 167u8, 123u8, 72u8, 5u8, 255u8, 252u8, 183u8, 253u8,
255u8, 255u8, 255u8, 254u8, 0u8, 0u8, 0u8, 2u8,
],
"0",
),
// 13423584044832304762738621570095607254448781440135075282586536627184276783235328
(
vec![
115u8, 237u8, 167u8, 83u8, 41u8, 157u8, 125u8, 72u8, 51u8, 57u8, 216u8, 8u8,
9u8, 161u8, 216u8, 5u8, 83u8, 189u8, 164u8, 2u8, 255u8, 254u8, 91u8, 254u8,
255u8, 255u8, 255u8, 255u8, 0u8, 0u8, 0u8, 1u8, 0u8,
],
"0",
),
// 115792089237316195423570985008687907853269984665640564039457584007913129639953
(
vec![
1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8,
0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8,
17u8,
],
"10920338887063814464675503992315976177888879664585288394250266608035967270927",
),
// 168227964412442385903018725516873873690960537166168201862061242707851710824468
(
vec![
1u8, 115u8, 237u8, 167u8, 83u8, 41u8, 157u8, 125u8, 72u8, 51u8, 57u8, 216u8,
8u8, 9u8, 161u8, 216u8, 5u8, 83u8, 189u8, 164u8, 2u8, 255u8, 254u8, 91u8,
254u8, 255u8, 255u8, 255u8, 255u8, 0u8, 0u8, 0u8, 20u8,
],
"10920338887063814464675503992315976177888879664585288394250266608035967270929",
),
// 29695210719928072218913619902732290376274806626904512031923745164725699769008210
(
vec![
1u8, 0u8, 115u8, 237u8, 167u8, 83u8, 41u8, 157u8, 125u8, 72u8, 51u8, 57u8,
216u8, 8u8, 9u8, 161u8, 216u8, 5u8, 83u8, 189u8, 164u8, 2u8, 255u8, 254u8,
91u8, 254u8, 255u8, 255u8, 255u8, 255u8, 0u8, 0u8, 0u8, 82u8,
],
"16505370806648407546198775099033712141953911605869024330074340560462818573852",
),
];
for i in test_vectors {
let expected = Fr::from_str(i.1).unwrap();
let actual = Fr::from_bytes_mod_order(&i.0);
assert_eq!(expected, actual, "failed on test {:?}", i.1);
}
}
}
45 changes: 45 additions & 0 deletions scripts/test_vectors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@

def generate_from_bytes_mod_order_test_vector(modulus):
def gen_vector(number):
byte_arr = convert_int_to_byte_vec(number)
s = str(number % modulus)
return "(" + byte_arr + ", \"" + s + "\"),"
data = ["vec!["]

small_values_to_test = [0, 1, 255, 256, 256*256 + 255]
modulus_bits = int((len(bin(modulus)[2:]) + 7) / 8) * 8
values_to_test = small_values_to_test + [
modulus >> 8,
(modulus >> 8) + 1,
modulus - 1,
modulus,
modulus + 1,
modulus * 2,
modulus * 256,
17 + (1 << modulus_bits),
19 + (1 << modulus_bits) + modulus,
81 + (1 << modulus_bits) * 256 + modulus]

for i in values_to_test:
data += ["// " + str(i)]
data += [gen_vector(i)]

data += ["];"]
return '\n'.join(data)

def convert_int_to_byte_vec(number):
s = bin(number)[2:]
num_bytes = int((len(s) + 7) / 8)
s = s.zfill(num_bytes * 8)

byte_arr = []
for i in range(num_bytes):
byte = s[i*8: (i+1)*8]
i = int(byte, 2)
byte_arr += [str(i) + "u8"]

data = ', '.join(byte_arr)
return "vec![" + data + "]"

bls12_fr_mod = 52435875175126190479447740508185965837690552500527637822603658699938581184513
print(generate_from_bytes_mod_order_test_vector(bls12_fr_mod))