jubjub: Implement group traits
This commit is contained in:
parent
d11b60030f
commit
eae5df0fb9
|
@ -27,6 +27,11 @@ path = "../ff"
|
|||
version = "0.6"
|
||||
default-features = false
|
||||
|
||||
[dependencies.group]
|
||||
path = "../group"
|
||||
version = "0.6"
|
||||
default-features = false
|
||||
|
||||
[dependencies.rand_core]
|
||||
version = "0.5"
|
||||
default-features = false
|
||||
|
|
|
@ -32,7 +32,17 @@
|
|||
#[macro_use]
|
||||
extern crate std;
|
||||
|
||||
use core::borrow::Borrow;
|
||||
use core::fmt;
|
||||
use core::iter::Sum;
|
||||
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
use ff::Field;
|
||||
use group::{
|
||||
cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup},
|
||||
prime::PrimeGroup,
|
||||
Curve, Group, GroupEncoding, WnafGroup,
|
||||
};
|
||||
use rand_core::RngCore;
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
|
||||
#[macro_use]
|
||||
|
@ -49,12 +59,18 @@ const FR_MODULUS_BYTES: [u8; 32] = [
|
|||
|
||||
/// This represents a Jubjub point in the affine `(u, v)`
|
||||
/// coordinates.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, Eq)]
|
||||
pub struct AffinePoint {
|
||||
u: Fq,
|
||||
v: Fq,
|
||||
}
|
||||
|
||||
impl fmt::Display for AffinePoint {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for AffinePoint {
|
||||
type Output = AffinePoint;
|
||||
|
||||
|
@ -101,7 +117,7 @@ impl ConditionallySelectable for AffinePoint {
|
|||
/// * Add it to an `ExtendedPoint`, `AffineNielsPoint` or `ExtendedNielsPoint`.
|
||||
/// * Double it using `double()`.
|
||||
/// * Compare it with another extended point using `PartialEq` or `ct_eq()`.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, Eq)]
|
||||
pub struct ExtendedPoint {
|
||||
u: Fq,
|
||||
v: Fq,
|
||||
|
@ -110,6 +126,12 @@ pub struct ExtendedPoint {
|
|||
t2: Fq,
|
||||
}
|
||||
|
||||
impl fmt::Display for ExtendedPoint {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstantTimeEq for ExtendedPoint {
|
||||
fn ct_eq(&self, other: &Self) -> Choice {
|
||||
// (u/z, v/z) = (u'/z', v'/z') is implied by
|
||||
|
@ -140,6 +162,18 @@ impl PartialEq for ExtendedPoint {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> Sum<T> for ExtendedPoint
|
||||
where
|
||||
T: Borrow<ExtendedPoint>,
|
||||
{
|
||||
fn sum<I>(iter: I) -> Self
|
||||
where
|
||||
I: Iterator<Item = T>,
|
||||
{
|
||||
iter.fold(Self::identity(), |acc, item| acc + item.borrow())
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for ExtendedPoint {
|
||||
type Output = ExtendedPoint;
|
||||
|
||||
|
@ -367,6 +401,11 @@ impl AffinePoint {
|
|||
}
|
||||
}
|
||||
|
||||
/// Determines if this point is the identity.
|
||||
pub fn is_identity(&self) -> Choice {
|
||||
ExtendedPoint::from(*self).is_identity()
|
||||
}
|
||||
|
||||
/// Multiplies this point by the cofactor, producing an
|
||||
/// `ExtendedPoint`
|
||||
pub fn mul_by_cofactor(&self) -> ExtendedPoint {
|
||||
|
@ -638,6 +677,38 @@ impl ExtendedPoint {
|
|||
self.to_niels().multiply(by)
|
||||
}
|
||||
|
||||
/// Converts a batch of projective elements into affine elements.
|
||||
///
|
||||
/// This function will panic if `p.len() != q.len()`.
|
||||
///
|
||||
/// This costs 5 multiplications per element, and a field inversion.
|
||||
fn batch_normalize(p: &[Self], q: &mut [AffinePoint]) {
|
||||
assert_eq!(p.len(), q.len());
|
||||
|
||||
let mut acc = Fq::one();
|
||||
for (p, q) in p.iter().zip(q.iter_mut()) {
|
||||
// We use the `u` field of `AffinePoint` to store the product
|
||||
// of previous z-coordinates seen.
|
||||
q.u = acc;
|
||||
acc *= &p.z;
|
||||
}
|
||||
|
||||
// This is the inverse, as all z-coordinates are nonzero.
|
||||
acc = acc.invert().unwrap();
|
||||
|
||||
for (p, q) in p.iter().zip(q.iter_mut()).rev() {
|
||||
// Compute tmp = 1/z
|
||||
let tmp = q.u * acc;
|
||||
|
||||
// Cancel out z-coordinate in denominator of `acc`
|
||||
acc *= &p.z;
|
||||
|
||||
// Set the coordinates to the correct value
|
||||
q.u = p.u * &tmp; // Multiply by 1/z
|
||||
q.v = p.v * &tmp; // Multiply by 1/z
|
||||
}
|
||||
}
|
||||
|
||||
/// This is only for debugging purposes and not
|
||||
/// exposed in the public API. Checks that this
|
||||
/// point is on the curve.
|
||||
|
@ -900,6 +971,335 @@ pub fn batch_normalize<'a>(v: &'a mut [ExtendedPoint]) -> impl Iterator<Item = A
|
|||
v.iter().map(|p| AffinePoint { u: p.u, v: p.v })
|
||||
}
|
||||
|
||||
impl<'a, 'b> Mul<&'b Fr> for &'a AffinePoint {
|
||||
type Output = ExtendedPoint;
|
||||
|
||||
fn mul(self, other: &'b Fr) -> ExtendedPoint {
|
||||
self.to_niels().multiply(&other.to_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl_binops_multiplicative_mixed!(AffinePoint, Fr, ExtendedPoint);
|
||||
|
||||
/// This represents a point in the prime-order subgroup of Jubjub, in extended
|
||||
/// coordinates.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub struct SubgroupPoint(ExtendedPoint);
|
||||
|
||||
impl From<SubgroupPoint> for ExtendedPoint {
|
||||
fn from(val: SubgroupPoint) -> ExtendedPoint {
|
||||
val.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a SubgroupPoint> for &'a ExtendedPoint {
|
||||
fn from(val: &'a SubgroupPoint) -> &'a ExtendedPoint {
|
||||
&val.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SubgroupPoint {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConditionallySelectable for SubgroupPoint {
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
SubgroupPoint(ExtendedPoint::conditional_select(&a.0, &b.0, choice))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Sum<T> for SubgroupPoint
|
||||
where
|
||||
T: Borrow<SubgroupPoint>,
|
||||
{
|
||||
fn sum<I>(iter: I) -> Self
|
||||
where
|
||||
I: Iterator<Item = T>,
|
||||
{
|
||||
iter.fold(Self::identity(), |acc, item| acc + item.borrow())
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for SubgroupPoint {
|
||||
type Output = SubgroupPoint;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> SubgroupPoint {
|
||||
SubgroupPoint(-self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for &SubgroupPoint {
|
||||
type Output = SubgroupPoint;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> SubgroupPoint {
|
||||
SubgroupPoint(-self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Add<&'b SubgroupPoint> for &'a ExtendedPoint {
|
||||
type Output = ExtendedPoint;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: &'b SubgroupPoint) -> ExtendedPoint {
|
||||
self + &other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Sub<&'b SubgroupPoint> for &'a ExtendedPoint {
|
||||
type Output = ExtendedPoint;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: &'b SubgroupPoint) -> ExtendedPoint {
|
||||
self - &other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl_binops_additive!(ExtendedPoint, SubgroupPoint);
|
||||
|
||||
impl<'a, 'b> Add<&'b SubgroupPoint> for &'a SubgroupPoint {
|
||||
type Output = SubgroupPoint;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: &'b SubgroupPoint) -> SubgroupPoint {
|
||||
SubgroupPoint(self.0 + &other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Sub<&'b SubgroupPoint> for &'a SubgroupPoint {
|
||||
type Output = SubgroupPoint;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: &'b SubgroupPoint) -> SubgroupPoint {
|
||||
SubgroupPoint(self.0 - &other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl_binops_additive!(SubgroupPoint, SubgroupPoint);
|
||||
|
||||
impl<'a, 'b> Mul<&'b Fr> for &'a SubgroupPoint {
|
||||
type Output = SubgroupPoint;
|
||||
|
||||
fn mul(self, other: &'b Fr) -> SubgroupPoint {
|
||||
SubgroupPoint(self.0.multiply(&other.to_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl_binops_multiplicative!(SubgroupPoint, Fr);
|
||||
|
||||
impl Group for ExtendedPoint {
|
||||
type Scalar = Fr;
|
||||
|
||||
fn random<R: RngCore + ?Sized>(rng: &mut R) -> Self {
|
||||
loop {
|
||||
let v = Fq::random(rng);
|
||||
let flip_sign = rng.next_u32() % 2 != 0;
|
||||
|
||||
// See AffinePoint::from_bytes for details.
|
||||
let v2 = v.square();
|
||||
let p = ((v2 - Fq::one())
|
||||
* ((Fq::one() + EDWARDS_D * v2).invert().unwrap_or(Fq::zero())))
|
||||
.sqrt()
|
||||
.map(|u| AffinePoint {
|
||||
u: if flip_sign { -u } else { u },
|
||||
v,
|
||||
});
|
||||
|
||||
if p.is_some().into() {
|
||||
let p = p.unwrap().to_curve();
|
||||
|
||||
if bool::from(!p.is_identity()) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn identity() -> Self {
|
||||
Self::identity()
|
||||
}
|
||||
|
||||
fn generator() -> Self {
|
||||
AffinePoint::generator().into()
|
||||
}
|
||||
|
||||
fn is_identity(&self) -> Choice {
|
||||
self.is_identity()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn double(&self) -> Self {
|
||||
self.double()
|
||||
}
|
||||
}
|
||||
|
||||
impl Group for SubgroupPoint {
|
||||
type Scalar = Fr;
|
||||
|
||||
fn random<R: RngCore + ?Sized>(rng: &mut R) -> Self {
|
||||
loop {
|
||||
let p = ExtendedPoint::random(rng).clear_cofactor();
|
||||
|
||||
if bool::from(!p.is_identity()) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn identity() -> Self {
|
||||
SubgroupPoint(ExtendedPoint::identity())
|
||||
}
|
||||
|
||||
fn generator() -> Self {
|
||||
ExtendedPoint::generator().clear_cofactor()
|
||||
}
|
||||
|
||||
fn is_identity(&self) -> Choice {
|
||||
self.0.is_identity()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn double(&self) -> Self {
|
||||
SubgroupPoint(self.0.double())
|
||||
}
|
||||
}
|
||||
|
||||
impl WnafGroup for ExtendedPoint {
|
||||
fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize {
|
||||
// Copied from bls12_381::g1, should be updated.
|
||||
const RECOMMENDATIONS: [usize; 12] =
|
||||
[1, 3, 7, 20, 43, 120, 273, 563, 1630, 3128, 7933, 62569];
|
||||
|
||||
let mut ret = 4;
|
||||
for r in &RECOMMENDATIONS {
|
||||
if num_scalars > *r {
|
||||
ret += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl PrimeGroup for SubgroupPoint {}
|
||||
|
||||
impl CofactorGroup for ExtendedPoint {
|
||||
type Subgroup = SubgroupPoint;
|
||||
|
||||
fn clear_cofactor(&self) -> Self::Subgroup {
|
||||
SubgroupPoint(self.mul_by_cofactor())
|
||||
}
|
||||
|
||||
fn into_subgroup(self) -> CtOption<Self::Subgroup> {
|
||||
CtOption::new(SubgroupPoint(self), self.is_torsion_free())
|
||||
}
|
||||
}
|
||||
|
||||
impl Curve for ExtendedPoint {
|
||||
type AffineRepr = AffinePoint;
|
||||
|
||||
fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) {
|
||||
Self::batch_normalize(p, q);
|
||||
}
|
||||
|
||||
fn to_affine(&self) -> Self::AffineRepr {
|
||||
self.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl CofactorCurve for ExtendedPoint {
|
||||
type Affine = AffinePoint;
|
||||
}
|
||||
|
||||
impl CofactorCurveAffine for AffinePoint {
|
||||
type Scalar = Fr;
|
||||
type Curve = ExtendedPoint;
|
||||
|
||||
fn identity() -> Self {
|
||||
Self::identity()
|
||||
}
|
||||
|
||||
fn generator() -> Self {
|
||||
// The point with the lowest positive v-coordinate and positive u-coordinate.
|
||||
AffinePoint {
|
||||
u: Fq::from_raw([
|
||||
0xe4b3_d35d_f1a7_adfe,
|
||||
0xcaf5_5d1b_29bf_81af,
|
||||
0x8b0f_03dd_d60a_8187,
|
||||
0x62ed_cbb8_bf37_87c8,
|
||||
]),
|
||||
v: Fq::from_raw([
|
||||
0x0000_0000_0000_000b,
|
||||
0x0000_0000_0000_0000,
|
||||
0x0000_0000_0000_0000,
|
||||
0x0000_0000_0000_0000,
|
||||
]),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_identity(&self) -> Choice {
|
||||
self.is_identity()
|
||||
}
|
||||
|
||||
fn to_curve(&self) -> Self::Curve {
|
||||
(*self).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl GroupEncoding for ExtendedPoint {
|
||||
type Repr = [u8; 32];
|
||||
|
||||
fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
|
||||
AffinePoint::from_bytes(*bytes).map(Self::from)
|
||||
}
|
||||
|
||||
fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
|
||||
// We can't avoid curve checks when parsing a compressed encoding.
|
||||
AffinePoint::from_bytes(*bytes).map(Self::from)
|
||||
}
|
||||
|
||||
fn to_bytes(&self) -> Self::Repr {
|
||||
AffinePoint::from(self).to_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl GroupEncoding for SubgroupPoint {
|
||||
type Repr = [u8; 32];
|
||||
|
||||
fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
|
||||
ExtendedPoint::from_bytes(bytes).and_then(|p| p.into_subgroup())
|
||||
}
|
||||
|
||||
fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
|
||||
ExtendedPoint::from_bytes_unchecked(bytes).map(SubgroupPoint)
|
||||
}
|
||||
|
||||
fn to_bytes(&self) -> Self::Repr {
|
||||
self.0.to_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl GroupEncoding for AffinePoint {
|
||||
type Repr = [u8; 32];
|
||||
|
||||
fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
|
||||
Self::from_bytes(*bytes)
|
||||
}
|
||||
|
||||
fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
|
||||
Self::from_bytes(*bytes)
|
||||
}
|
||||
|
||||
fn to_bytes(&self) -> Self::Repr {
|
||||
self.to_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_on_curve_var() {
|
||||
assert!(AffinePoint::identity().is_on_curve_vartime());
|
||||
|
@ -1157,6 +1557,7 @@ fn find_curve_generator() {
|
|||
assert!(bool::from(b.is_small_order()));
|
||||
assert!(bool::from(b.is_identity()));
|
||||
assert_eq!(FULL_GENERATOR, a);
|
||||
assert_eq!(AffinePoint::generator(), a);
|
||||
assert!(bool::from(a.mul_by_cofactor().is_torsion_free()));
|
||||
return;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue