Implement Fp/Fq for the Pallas and Vesta curves.

Co-authored-by: Kris Nuttycombe <kris@electriccoin.co>
This commit is contained in:
Sean Bowe 2020-12-03 13:13:40 -07:00
parent ae20f75f7a
commit 7536af8b69
No known key found for this signature in database
GPG Key ID: 95684257D8F8B031
2 changed files with 251 additions and 123 deletions

View File

@ -9,9 +9,9 @@ use crate::arithmetic::{adc, mac, sbb, FieldExt, Group};
/// This represents an element of $\mathbb{F}_p$ where
///
/// `p = 0x40000000000000000000000000000000038aa1276c3f59b9a14064e200000001`
/// `p = 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001`
///
/// is the base field of the Tweedledum curve.
/// is the base field of the Pallas curve.
// The internal representation of this type is four 64-bit unsigned
// integers in little-endian order. `Fp` values are always in
// Montgomery form; i.e., Fp(a) = aR mod p, with R = 2^256.
@ -94,11 +94,11 @@ impl ConditionallySelectable for Fp {
}
/// Constant representing the modulus
/// p = 0x40000000000000000000000000000000038aa1276c3f59b9a14064e200000001
/// p = 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001
const MODULUS: Fp = Fp([
0xa14064e200000001,
0x38aa1276c3f59b9,
0x0,
0x992d30ed00000001,
0x224698fc094cf91b,
0x0000000000000000,
0x4000000000000000,
]);
@ -106,9 +106,9 @@ const MODULUS: Fp = Fp([
#[cfg(not(target_pointer_width = "64"))]
const MODULUS_LIMBS_32: [u32; 8] = [
0x0000_0001,
0xa140_64e2,
0x6c3f_59b9,
0x038a_a127,
0x992d_30ed,
0x094c_f91b,
0x2246_98fc,
0x0000_0000,
0x0000_0000,
0x0000_0000,
@ -164,34 +164,34 @@ impl_binops_additive!(Fp, Fp);
impl_binops_multiplicative!(Fp, Fp);
/// INV = -(p^{-1} mod 2^64) mod 2^64
const INV: u64 = 0xa14064e1ffffffff;
const INV: u64 = 0x992d30ecffffffff;
/// R = 2^256 mod p
const R: Fp = Fp([
0x1c3ed159fffffffd,
0xf5601c89bb41f2d3,
0x34786d38fffffffd,
0x992c350be41914ad,
0xffffffffffffffff,
0x3fffffffffffffff,
]);
/// R^2 = 2^512 mod p
const R2: Fp = Fp([
0x280c9c4000000010,
0x91a4409b5400af74,
0xdd7b28e19094c659,
0xc8ad9107ccca0e,
0x8c78ecb30000000f,
0xd7d30dbd8b0de0e7,
0x7797a99bc3c95d18,
0x96d41af7b9cb714,
]);
/// R^3 = 2^768 mod p
const R3: Fp = Fp([
0x98fb3d144380a737,
0xf9fdbeb55b7eb87c,
0x63f75cb999eafa89,
0x217cb214ebb8fc72,
0xf185a5993a9e10f9,
0xf6a68f3b6ac5b1d1,
0xdf8d1014353fd42c,
0x2ae309222d2d9910,
]);
/// `GENERATOR = 5 mod p` is a generator of the `p - 1` order multiplicative subgroup, and
/// is also a quadratic non-residue.
/// `GENERATOR = 5 mod p` is a generator of the `p - 1` order multiplicative
/// subgroup, or in other words a primitive root of the field.
const GENERATOR: Fp = Fp::from_raw([
0x0000_0000_0000_0005,
0x0000_0000_0000_0000,
@ -199,34 +199,26 @@ const GENERATOR: Fp = Fp::from_raw([
0x0000_0000_0000_0000,
]);
const S: u32 = 33;
const S: u32 = 32;
/// GENERATOR^t where t * 2^s + 1 = p
/// with t odd. In other words, this
/// is a 2^s root of unity.
///
/// `GENERATOR = 5 mod p` is a generator
/// of the p - 1 order multiplicative
/// subgroup.
const ROOT_OF_UNITY: Fp = Fp::from_raw([
0x53de9f31b88837ce,
0xff46e8f3f3ea99d6,
0xf624f2eaaf8c2d57,
0x2ae45117890ee2fc,
0xbdad6fabd87ea32f,
0xea322bf2b7bb7584,
0x362120830561f81a,
0x2bce74deac30ebda,
]);
/// GENERATOR^{2^s} where t * 2^s + 1 = p
/// with t odd. In other words, this
/// is a t root of unity.
///
/// `GENERATOR = 5 mod p` is a generator
/// of the p - 1 order multiplicative
/// subgroup.
const DELTA: Fp = Fp::from_raw([
0x48796f6fde98a425,
0xa99d8b67e918805e,
0x671383de08b5fe3c,
0x1e9372724e80300d,
0x6a6ccd20dd7b9ba2,
0xf5e4f3f13eee5636,
0xbd455b7112a5049d,
0xa757d0f0006ab6c,
]);
impl Default for Fp {
@ -503,7 +495,7 @@ impl ff::Field for Fp {
// https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5)
// w = self^((t - 1) // 2)
let w = self.pow_vartime(&[0xdb0fd66e68501938, 0xe2a849, 0x0, 0x10000000]);
let w = self.pow_vartime(&[0x04a67c8dcc969876, 0x11234c7e, 0x0, 0x20000000]);
let mut v = S;
let mut x = self * w;
@ -544,8 +536,8 @@ impl ff::Field for Fp {
/// failing if the element is zero.
fn invert(&self) -> CtOption<Self> {
let tmp = self.pow_vartime(&[
0xa14064e1ffffffff,
0x38aa1276c3f59b9,
0x992d30ecffffffff,
0x224698fc094cf91b,
0x0,
0x4000000000000000,
]);
@ -646,43 +638,43 @@ impl ff::PrimeField for Fp {
impl FieldExt for Fp {
const ROOT_OF_UNITY: Self = ROOT_OF_UNITY;
const ROOT_OF_UNITY_INV: Self = Fp::from_raw([
0x9246674078fa45bb,
0xd822ebd60888c5ea,
0x56d579133a11731f,
0x1c88fa9e942120bb,
0xf0b87c7db2ce91f6,
0x84a0a1d8859f066f,
0xb4ed8e647196dad1,
0x2cd5282c53116b5c,
]);
const UNROLL_T_EXPONENT: [u64; 4] = [
0x3b3a6633d1897d83,
0x0000000000c93d5b,
0xf000000000000000,
0xe34ab16,
0x955a0a417453113c,
0x0000000022016b89,
0xc000000000000000,
0x3f7ed4c6,
];
const T_EXPONENT: [u64; 4] = [
0xb61facdcd0a03271,
0x0000000001c55093,
0x094cf91b992d30ed,
0x00000000224698fc,
0x0000000000000000,
0x20000000,
0x40000000,
];
const DELTA: Self = DELTA;
const UNROLL_S_EXPONENT: u64 = 0x11cb54e91;
const UNROLL_S_EXPONENT: u64 = 0x204ace5;
const TWO_INV: Self = Fp::from_raw([
0xd0a0327100000001,
0x01c55093b61facdc,
0xcc96987680000001,
0x11234c7e04a67c8d,
0x0000000000000000,
0x2000000000000000,
]);
const RESCUE_ALPHA: u64 = 5;
const RESCUE_INVALPHA: [u64; 4] = [
0x810050b4cccccccd,
0x360880ec56991494,
0xe0f0f3f0cccccccd,
0x4e9ee0c9a10a60e2,
0x3333333333333333,
0x3333333333333333,
];
const ZETA: Self = Fp::from_raw([
0x8598abb3a410c9c8,
0x7881fb239ba41a26,
0x9bebc9146ef83d9a,
0x1508415ab5e97c94,
0x7b7fd22f0201b547,
0x05270d29d19fc7d2,
0xd3552a23a8554e50,
0x2d33357cb532458e,
]);
fn ct_is_zero(&self) -> Choice {
@ -781,11 +773,36 @@ fn test_inv() {
assert_eq!(inv, INV);
}
#[test]
fn test_rescue() {
// NB: TWO_INV is standing in as a "random" field element
assert_eq!(
Fp::TWO_INV
.pow_vartime(&[Fp::RESCUE_ALPHA, 0, 0, 0])
.pow_vartime(&Fp::RESCUE_INVALPHA),
Fp::TWO_INV
);
}
#[test]
fn test_sqrt() {
// NB: TWO_INV is standing in as a "random" field element
let v = (Fp::TWO_INV).square().sqrt().unwrap();
assert!(v == Fp::TWO_INV || (-v) == Fp::TWO_INV);
}
#[test]
fn test_deterministic_sqrt() {
// NB: TWO_INV is standing in as a "random" field element
let v = (Fp::TWO_INV).square().deterministic_sqrt().unwrap();
assert!(v == Fp::TWO_INV || (-v) == Fp::TWO_INV);
}
#[test]
fn test_zeta() {
assert_eq!(
format!("{:?}", Fp::ZETA),
"0x1508415ab5e97c949bebc9146ef83d9a7881fb239ba41a268598abb3a410c9c8"
"0x2d33357cb532458ed3552a23a8554e5005270d29d19fc7d27b7fd22f0201b547"
);
let a = Fp::ZETA;
@ -796,6 +813,14 @@ fn test_zeta() {
assert!(c == Fp::one());
}
#[test]
fn test_root_of_unity() {
assert_eq!(
Fp::ROOT_OF_UNITY.pow_vartime(&[1 << 32, 0, 0, 0]),
Fp::one()
);
}
#[test]
fn test_inv_root_of_unity() {
assert_eq!(Fp::ROOT_OF_UNITY_INV, Fp::ROOT_OF_UNITY.invert().unwrap());
@ -810,3 +835,42 @@ fn test_inv_2() {
fn test_delta() {
assert_eq!(Fp::DELTA, Fp::from(5).pow(&[1u64 << Fp::S, 0, 0, 0]));
}
#[cfg(not(target_pointer_width = "64"))]
#[test]
fn consistent_modulus_limbs() {
for (a, &b) in MODULUS
.0
.iter()
.flat_map(|&limb| {
Some(limb as u32)
.into_iter()
.chain(Some((limb >> 32) as u32))
})
.zip(MODULUS_LIMBS_32.iter())
{
assert_eq!(a, b);
}
}
#[test]
fn test_from_u512() {
assert_eq!(
Fp::from_raw([
0x3daec14d565241d9,
0x0b7af45b6073944b,
0xea5b8bd611a5bd4c,
0x150160330625db3d
]),
Fp::from_u512([
0xee155641297678a1,
0xd83e156bdbfdbe65,
0xd9ccd834c68ba0b5,
0xf508ede312272758,
0x038df7cbf8228e89,
0x3505a1e4a3c74b41,
0xbfa46f775eb82db3,
0x26ebe27e262f471d
])
);
}

View File

@ -9,9 +9,9 @@ use crate::arithmetic::{adc, mac, sbb, FieldExt, Group};
/// This represents an element of $\mathbb{F}_q$ where
///
/// `q = 0x40000000000000000000000000000000038aa127696286c9842cafd400000001`
/// `q = 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001`
///
/// is the base field of the Tweedledee curve.
/// is the base field of the Vesta curve.
// The internal representation of this type is four 64-bit unsigned
// integers in little-endian order. `Fq` values are always in
// Montgomery form; i.e., Fq(a) = aR mod q, with R = 2^256.
@ -94,10 +94,10 @@ impl ConditionallySelectable for Fq {
}
/// Constant representing the modulus
/// q = 0x40000000000000000000000000000000038aa127696286c9842cafd400000001
/// q = 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001
const MODULUS: Fq = Fq([
0x842cafd400000001,
0x38aa127696286c9,
0x8c46eb2100000001,
0x224698fc0994a8dd,
0x0,
0x4000000000000000,
]);
@ -106,9 +106,9 @@ const MODULUS: Fq = Fq([
#[cfg(not(target_pointer_width = "64"))]
const MODULUS_LIMBS_32: [u32; 8] = [
0x0000_0001,
0x842c_afd4,
0x6962_86c9,
0x038a_a127,
0x8c46_eb21,
0x0994_a8dd,
0x2246_98fc,
0x0000_0000,
0x0000_0000,
0x0000_0000,
@ -164,30 +164,30 @@ impl_binops_additive!(Fq, Fq);
impl_binops_multiplicative!(Fq, Fq);
/// INV = -(q^{-1} mod 2^64) mod 2^64
const INV: u64 = 0x842cafd3ffffffff;
const INV: u64 = 0x8c46eb20ffffffff;
/// R = 2^256 mod q
const R: Fq = Fq([
0x7379f083fffffffd,
0xf5601c89c3d86ba3,
0x5b2b3e9cfffffffd,
0x992c350be3420567,
0xffffffffffffffff,
0x3fffffffffffffff,
]);
/// R^2 = 2^512 mod q
const R2: Fq = Fq([
0x8595fa8000000010,
0x7e16a565c6895230,
0xf4c0e6fcb03aa0a2,
0xc8ad9106886013,
0xfc9678ff0000000f,
0x67bb433d891a16e3,
0x7fae231004ccf590,
0x96d41af7ccfdaa9,
]);
/// R^3 = 2^768 mod q
const R3: Fq = Fq([
0xa624f338075cdb5e,
0x57964eacb8fe21f2,
0xcb266d18c0413bc2,
0xa42cdf95f959577,
0x008b421c249dae4c,
0xe13bda50dba41326,
0x88fececb8e15cb63,
0x7dd97a06e6792c8,
]);
/// `GENERATOR = 5 mod q` is a generator of the `q - 1` order multiplicative subgroup, and
@ -199,34 +199,26 @@ const GENERATOR: Fq = Fq::from_raw([
0x0000_0000_0000_0000,
]);
const S: u32 = 34;
const S: u32 = 32;
/// GENERATOR^t where t * 2^s + 1 = q
/// with t odd. In other words, this
/// is a 2^s root of unity.
///
/// `GENERATOR = 5 mod q` is a generator
/// of the q - 1 order multiplicative
/// subgroup.
const ROOT_OF_UNITY: Fq = Fq::from_raw([
0x1cbd3234869d57ec,
0xa287dd1b8084fbf,
0xf1dbcb645a987293,
0x113efc510dc03c0b,
0xa70e2c1102b6d05f,
0x9bb97ea3c106f049,
0x9e5c4dfd492ae26e,
0x2de6a9b8746d3f58,
]);
/// GENERATOR^{2^s} where t * 2^s + 1 = q
/// with t odd. In other words, this
/// is a t root of unity.
///
/// `GENERATOR = 5 mod q` is a generator
/// of the q - 1 order multiplicative
/// subgroup.
const DELTA: Fq = Fq::from_raw([
0x83d2833d15f2bbf9,
0x5127e2ce24a8e69c,
0x4243423589e0a9b5,
0x20daec44973be920,
0x8494392472d1683c,
0xe3ac3376541d1140,
0x06f0a88e7f7949f8,
0x2237d54423724166,
]);
impl Default for Fq {
@ -518,7 +510,7 @@ impl ff::Field for Fq {
// https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5)
// w = self^((t - 1) // 2)
let w = self.pow_vartime(&[0xed2c50d9308595fa, 0x715424, 0x0, 0x8000000]);
let w = self.pow_vartime(&[0x04ca546ec6237590, 0x11234c7e, 0x0, 0x20000000]);
let mut v = S;
let mut x = self * w;
@ -559,8 +551,8 @@ impl ff::Field for Fq {
/// failing if the element is zero.
fn invert(&self) -> CtOption<Self> {
let tmp = self.pow_vartime(&[
0x842cafd3ffffffff,
0x38aa127696286c9,
0x8c46eb20ffffffff,
0x224698fc0994a8dd,
0x0,
0x4000000000000000,
]);
@ -661,43 +653,43 @@ impl ff::PrimeField for Fq {
impl FieldExt for Fq {
const ROOT_OF_UNITY: Self = ROOT_OF_UNITY;
const ROOT_OF_UNITY_INV: Self = Fq::from_raw([
0x824860d3eb30de02,
0xad9f0afd0ea63acc,
0xd250318c11a16fe1,
0x12a9f5e1dd62dabc,
0x57eecda0a84b6836,
0x4ad38b9084b8a80c,
0xf4c8f353124086c1,
0x2235e1a7415bf936,
]);
const UNROLL_T_EXPONENT: [u64; 4] = [
0x9b71de17e6d2d5a0,
0x0000000000296ee0,
0x8c00000000000000,
0x2ecc05e,
0xcc771cc2ac1e1664,
0x00000000062dfe9e,
0xc000000000000000,
0xb89e9c7,
];
const T_EXPONENT: [u64; 4] = [
0xda58a1b2610b2bf5,
0x0000000000e2a849,
0x0994a8dd8c46eb21,
0x00000000224698fc,
0x0000000000000000,
0x10000000,
0x40000000,
];
const DELTA: Self = DELTA;
const UNROLL_S_EXPONENT: u64 = 0x344cfe85d;
const UNROLL_S_EXPONENT: u64 = 0xd1d858e1;
const TWO_INV: Self = Fq::from_raw([
0xc21657ea00000001,
0x01c55093b4b14364,
0xc623759080000001,
0x11234c7e04ca546e,
0x0000000000000000,
0x2000000000000000,
]);
const RESCUE_ALPHA: u64 = 5;
const RESCUE_INVALPHA: [u64; 4] = [
0xd023bfdccccccccd,
0x360880ec544ed23a,
0xd69f2280cccccccd,
0x4e9ee0c9a143ba4a,
0x3333333333333333,
0x3333333333333333,
];
const ZETA: Self = Fq::from_raw([
0x4394c2bd148fa4fd,
0x69cf8de720e52ec1,
0x87ad8b5ff9731ffe,
0x36c66d3a1e049a58,
0x2aa9d2e050aa0e4f,
0x0fed467d47c033af,
0x511db4d81cf70f5a,
0x6819a58283e528e,
]);
fn ct_is_zero(&self) -> Choice {
@ -796,11 +788,36 @@ fn test_inv() {
assert_eq!(inv, INV);
}
#[test]
fn test_rescue() {
// NB: TWO_INV is standing in as a "random" field element
assert_eq!(
Fq::TWO_INV
.pow_vartime(&[Fq::RESCUE_ALPHA, 0, 0, 0])
.pow_vartime(&Fq::RESCUE_INVALPHA),
Fq::TWO_INV
);
}
#[test]
fn test_sqrt() {
// NB: TWO_INV is standing in as a "random" field element
let v = (Fq::TWO_INV).square().sqrt().unwrap();
assert!(v == Fq::TWO_INV || (-v) == Fq::TWO_INV);
}
#[test]
fn test_deterministic_sqrt() {
// NB: TWO_INV is standing in as a "random" field element
let v = (Fq::TWO_INV).square().deterministic_sqrt().unwrap();
assert!(v == Fq::TWO_INV || (-v) == Fq::TWO_INV);
}
#[test]
fn test_zeta() {
assert_eq!(
format!("{:?}", Fq::ZETA),
"0x36c66d3a1e049a5887ad8b5ff9731ffe69cf8de720e52ec14394c2bd148fa4fd"
"0x06819a58283e528e511db4d81cf70f5a0fed467d47c033af2aa9d2e050aa0e4f"
);
let a = Fq::ZETA;
assert!(a != Fq::one());
@ -810,6 +827,14 @@ fn test_zeta() {
assert!(c == Fq::one());
}
#[test]
fn test_root_of_unity() {
assert_eq!(
Fq::ROOT_OF_UNITY.pow_vartime(&[1 << 32, 0, 0, 0]),
Fq::one()
);
}
#[test]
fn test_inv_root_of_unity() {
assert_eq!(Fq::ROOT_OF_UNITY_INV, Fq::ROOT_OF_UNITY.invert().unwrap());
@ -824,3 +849,42 @@ fn test_inv_2() {
fn test_delta() {
assert_eq!(Fq::DELTA, Fq::from(5).pow(&[1u64 << Fq::S, 0, 0, 0]));
}
#[cfg(not(target_pointer_width = "64"))]
#[test]
fn consistent_modulus_limbs() {
for (a, &b) in MODULUS
.0
.iter()
.flat_map(|&limb| {
Some(limb as u32)
.into_iter()
.chain(Some((limb >> 32) as u32))
})
.zip(MODULUS_LIMBS_32.iter())
{
assert_eq!(a, b);
}
}
#[test]
fn test_from_u512() {
assert_eq!(
Fq::from_raw([
0xe22bd0d1b22cc43e,
0x6b84e5b52490a7c8,
0x264262941ac9e229,
0x27dcfdf361ce4254
]),
Fq::from_u512([
0x64a80cce0b5a2369,
0x84f2ef0501bc783c,
0x696e5e63c86bbbde,
0x924072f52dc6cc62,
0x8288a507c8d61128,
0x3b2efb1ef697e3fe,
0x75a4998d06855f27,
0x52ea589e69712cc0
])
);
}