Auto merge of #17 - ebfull:various-improvements, r=ebfull
Group encoding negative test vectors Closes #10 Also simplifies the encoding code, which has the side-effect of being useful for testing. Also adds more descriptive error reporting throughout the API. Also ensures use of Debug/Display are consistent with standard expectations.
This commit is contained in:
commit
a6528a7876
|
@ -1,5 +1,6 @@
|
|||
macro_rules! curve_impl {
|
||||
(
|
||||
$name:expr,
|
||||
$projective:ident,
|
||||
$affine:ident,
|
||||
$prepared:ident,
|
||||
|
@ -15,6 +16,17 @@ macro_rules! curve_impl {
|
|||
pub(crate) infinity: bool
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for $affine
|
||||
{
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
if self.infinity {
|
||||
write!(f, "{}(Infinity)", $name)
|
||||
} else {
|
||||
write!(f, "{}(x={}, y={})", $name, self.x, self.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq)]
|
||||
pub struct $projective {
|
||||
pub(crate) x: $basefield,
|
||||
|
@ -22,6 +34,13 @@ macro_rules! curve_impl {
|
|||
pub(crate) z: $basefield
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for $projective
|
||||
{
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "{}", self.into_affine())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for $projective {
|
||||
fn eq(&self, other: &$projective) -> bool {
|
||||
if self.is_zero() {
|
||||
|
@ -111,10 +130,6 @@ macro_rules! curve_impl {
|
|||
self.infinity
|
||||
}
|
||||
|
||||
fn is_valid(&self) -> bool {
|
||||
self.is_on_curve() && self.is_in_correct_subgroup()
|
||||
}
|
||||
|
||||
fn mul<S: Into<<Self::Scalar as PrimeField>::Repr>>(&self, by: S) -> $projective {
|
||||
let mut res = $projective::zero();
|
||||
|
||||
|
@ -560,12 +575,19 @@ macro_rules! curve_impl {
|
|||
pub mod g1 {
|
||||
use rand::{Rand, Rng};
|
||||
use super::super::{Fq, Fr, FrRepr, FqRepr};
|
||||
use ::{CurveProjective, CurveAffine, PrimeField, SqrtField, PrimeFieldRepr, Field, BitIterator, EncodedPoint};
|
||||
use ::{CurveProjective, CurveAffine, PrimeField, SqrtField, PrimeFieldRepr, Field, BitIterator, EncodedPoint, GroupDecodingError};
|
||||
|
||||
curve_impl!(G1, G1Affine, G1Prepared, Fq, Fr, G1Uncompressed, G1Compressed);
|
||||
curve_impl!("G1", G1, G1Affine, G1Prepared, Fq, Fr, G1Uncompressed, G1Compressed);
|
||||
|
||||
#[derive(Copy)]
|
||||
pub struct G1Uncompressed([u8; 96]);
|
||||
|
||||
impl Clone for G1Uncompressed {
|
||||
fn clone(&self) -> G1Uncompressed {
|
||||
G1Uncompressed(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for G1Uncompressed {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0
|
||||
|
@ -583,15 +605,24 @@ pub mod g1 {
|
|||
|
||||
fn empty() -> Self { G1Uncompressed([0; 96]) }
|
||||
fn size() -> usize { 96 }
|
||||
fn into_affine_unchecked(&self) -> Result<G1Affine, ()> {
|
||||
use byteorder::{ReadBytesExt, BigEndian};
|
||||
fn into_affine(&self) -> Result<G1Affine, GroupDecodingError> {
|
||||
let affine = self.into_affine_unchecked()?;
|
||||
|
||||
if !affine.is_on_curve() {
|
||||
Err(GroupDecodingError::NotOnCurve)
|
||||
} else if !affine.is_in_correct_subgroup() {
|
||||
Err(GroupDecodingError::NotInSubgroup)
|
||||
} else {
|
||||
Ok(affine)
|
||||
}
|
||||
}
|
||||
fn into_affine_unchecked(&self) -> Result<G1Affine, GroupDecodingError> {
|
||||
// Create a copy of this representation.
|
||||
let mut copy = self.0;
|
||||
|
||||
if copy[0] & (1 << 7) != 0 {
|
||||
// Distinguisher bit is set, but this should be uncompressed!
|
||||
return Err(())
|
||||
return Err(GroupDecodingError::UnexpectedCompressionMode)
|
||||
}
|
||||
|
||||
if copy[0] & (1 << 6) != 0 {
|
||||
|
@ -603,13 +634,13 @@ pub mod g1 {
|
|||
if copy.iter().all(|b| *b == 0) {
|
||||
Ok(G1Affine::zero())
|
||||
} else {
|
||||
Err(())
|
||||
Err(GroupDecodingError::UnexpectedInformation)
|
||||
}
|
||||
} else {
|
||||
if copy[0] & (1 << 5) != 0 {
|
||||
// The bit indicating the y-coordinate should be lexicographically
|
||||
// largest is set, but this is an uncompressed element.
|
||||
return Err(())
|
||||
return Err(GroupDecodingError::UnexpectedInformation)
|
||||
}
|
||||
|
||||
// Unset the three most significant bits.
|
||||
|
@ -621,25 +652,18 @@ pub mod g1 {
|
|||
{
|
||||
let mut reader = ©[..];
|
||||
|
||||
for b in x.0.iter_mut().rev() {
|
||||
*b = reader.read_u64::<BigEndian>().unwrap();
|
||||
}
|
||||
|
||||
for b in y.0.iter_mut().rev() {
|
||||
*b = reader.read_u64::<BigEndian>().unwrap();
|
||||
}
|
||||
x.read_be(&mut reader).unwrap();
|
||||
y.read_be(&mut reader).unwrap();
|
||||
}
|
||||
|
||||
Ok(G1Affine {
|
||||
x: Fq::from_repr(x)?,
|
||||
y: Fq::from_repr(y)?,
|
||||
x: Fq::from_repr(x).map_err(|e| GroupDecodingError::CoordinateDecodingError("x coordinate", e))?,
|
||||
y: Fq::from_repr(y).map_err(|e| GroupDecodingError::CoordinateDecodingError("y coordinate", e))?,
|
||||
infinity: false
|
||||
})
|
||||
}
|
||||
}
|
||||
fn from_affine(affine: G1Affine) -> Self {
|
||||
use byteorder::{WriteBytesExt, BigEndian};
|
||||
|
||||
let mut res = Self::empty();
|
||||
|
||||
if affine.is_zero() {
|
||||
|
@ -649,21 +673,23 @@ pub mod g1 {
|
|||
} else {
|
||||
let mut writer = &mut res.0[..];
|
||||
|
||||
for digit in affine.x.into_repr().as_ref().iter().rev() {
|
||||
writer.write_u64::<BigEndian>(*digit).unwrap();
|
||||
}
|
||||
|
||||
for digit in affine.y.into_repr().as_ref().iter().rev() {
|
||||
writer.write_u64::<BigEndian>(*digit).unwrap();
|
||||
}
|
||||
affine.x.into_repr().write_be(&mut writer).unwrap();
|
||||
affine.y.into_repr().write_be(&mut writer).unwrap();
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy)]
|
||||
pub struct G1Compressed([u8; 48]);
|
||||
|
||||
impl Clone for G1Compressed {
|
||||
fn clone(&self) -> G1Compressed {
|
||||
G1Compressed(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for G1Compressed {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0
|
||||
|
@ -681,15 +707,24 @@ pub mod g1 {
|
|||
|
||||
fn empty() -> Self { G1Compressed([0; 48]) }
|
||||
fn size() -> usize { 48 }
|
||||
fn into_affine_unchecked(&self) -> Result<G1Affine, ()> {
|
||||
use byteorder::{ReadBytesExt, BigEndian};
|
||||
fn into_affine(&self) -> Result<G1Affine, GroupDecodingError> {
|
||||
let affine = self.into_affine_unchecked()?;
|
||||
|
||||
// NB: Decompression guarantees that it is on the curve already.
|
||||
|
||||
if !affine.is_in_correct_subgroup() {
|
||||
Err(GroupDecodingError::NotInSubgroup)
|
||||
} else {
|
||||
Ok(affine)
|
||||
}
|
||||
}
|
||||
fn into_affine_unchecked(&self) -> Result<G1Affine, GroupDecodingError> {
|
||||
// Create a copy of this representation.
|
||||
let mut copy = self.0;
|
||||
|
||||
if copy[0] & (1 << 7) == 0 {
|
||||
// Distinguisher bit isn't set.
|
||||
return Err(())
|
||||
return Err(GroupDecodingError::UnexpectedCompressionMode)
|
||||
}
|
||||
|
||||
if copy[0] & (1 << 6) != 0 {
|
||||
|
@ -701,7 +736,7 @@ pub mod g1 {
|
|||
if copy.iter().all(|b| *b == 0) {
|
||||
Ok(G1Affine::zero())
|
||||
} else {
|
||||
Err(())
|
||||
Err(GroupDecodingError::UnexpectedInformation)
|
||||
}
|
||||
} else {
|
||||
// Determine if the intended y coordinate must be greater
|
||||
|
@ -716,13 +751,11 @@ pub mod g1 {
|
|||
{
|
||||
let mut reader = ©[..];
|
||||
|
||||
for b in x.0.iter_mut().rev() {
|
||||
*b = reader.read_u64::<BigEndian>().unwrap();
|
||||
}
|
||||
x.read_be(&mut reader).unwrap();
|
||||
}
|
||||
|
||||
// Interpret as Fq element.
|
||||
let x = Fq::from_repr(x)?;
|
||||
let x = Fq::from_repr(x).map_err(|e| GroupDecodingError::CoordinateDecodingError("x coordinate", e))?;
|
||||
|
||||
// Compute x^3 + b
|
||||
let mut x3b = x;
|
||||
|
@ -747,14 +780,12 @@ pub mod g1 {
|
|||
},
|
||||
None => {
|
||||
// Point must not be on the curve.
|
||||
Err(())
|
||||
Err(GroupDecodingError::NotOnCurve)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn from_affine(affine: G1Affine) -> Self {
|
||||
use byteorder::{WriteBytesExt, BigEndian};
|
||||
|
||||
let mut res = Self::empty();
|
||||
|
||||
if affine.is_zero() {
|
||||
|
@ -765,9 +796,7 @@ pub mod g1 {
|
|||
{
|
||||
let mut writer = &mut res.0[..];
|
||||
|
||||
for digit in affine.x.into_repr().as_ref().iter().rev() {
|
||||
writer.write_u64::<BigEndian>(*digit).unwrap();
|
||||
}
|
||||
affine.x.into_repr().write_be(&mut writer).unwrap();
|
||||
}
|
||||
|
||||
let mut negy = affine.y;
|
||||
|
@ -873,7 +902,7 @@ pub mod g1 {
|
|||
infinity: false
|
||||
};
|
||||
|
||||
assert!(!p.is_valid());
|
||||
assert!(!p.is_in_correct_subgroup());
|
||||
|
||||
let mut g1 = G1::zero();
|
||||
|
||||
|
@ -895,7 +924,7 @@ pub mod g1 {
|
|||
assert_eq!(i, 4);
|
||||
let g1 = G1Affine::from(g1);
|
||||
|
||||
assert!(g1.is_valid());
|
||||
assert!(g1.is_in_correct_subgroup());
|
||||
|
||||
assert_eq!(g1, G1Affine::one());
|
||||
break;
|
||||
|
@ -918,7 +947,6 @@ pub mod g1 {
|
|||
};
|
||||
assert!(!p.is_on_curve());
|
||||
assert!(p.is_in_correct_subgroup());
|
||||
assert!(!p.is_valid());
|
||||
}
|
||||
|
||||
// Reject point on a twist (b = 3)
|
||||
|
@ -930,7 +958,6 @@ pub mod g1 {
|
|||
};
|
||||
assert!(!p.is_on_curve());
|
||||
assert!(!p.is_in_correct_subgroup());
|
||||
assert!(!p.is_valid());
|
||||
}
|
||||
|
||||
// Reject point in an invalid subgroup
|
||||
|
@ -943,7 +970,6 @@ pub mod g1 {
|
|||
};
|
||||
assert!(p.is_on_curve());
|
||||
assert!(!p.is_in_correct_subgroup());
|
||||
assert!(!p.is_valid());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1019,9 +1045,9 @@ pub mod g1 {
|
|||
infinity: false
|
||||
};
|
||||
|
||||
assert!(a.is_valid());
|
||||
assert!(b.is_valid());
|
||||
assert!(c.is_valid());
|
||||
assert!(a.is_on_curve() && a.is_in_correct_subgroup());
|
||||
assert!(b.is_on_curve() && b.is_in_correct_subgroup());
|
||||
assert!(c.is_on_curve() && c.is_in_correct_subgroup());
|
||||
|
||||
let mut tmp1 = a.into_projective();
|
||||
tmp1.add_assign(&b.into_projective());
|
||||
|
@ -1097,12 +1123,19 @@ pub mod g1 {
|
|||
pub mod g2 {
|
||||
use rand::{Rand, Rng};
|
||||
use super::super::{Fq2, Fr, Fq, FrRepr, FqRepr};
|
||||
use ::{CurveProjective, CurveAffine, PrimeField, SqrtField, PrimeFieldRepr, Field, BitIterator, EncodedPoint};
|
||||
use ::{CurveProjective, CurveAffine, PrimeField, SqrtField, PrimeFieldRepr, Field, BitIterator, EncodedPoint, GroupDecodingError};
|
||||
|
||||
curve_impl!(G2, G2Affine, G2Prepared, Fq2, Fr, G2Uncompressed, G2Compressed);
|
||||
curve_impl!("G2", G2, G2Affine, G2Prepared, Fq2, Fr, G2Uncompressed, G2Compressed);
|
||||
|
||||
#[derive(Copy)]
|
||||
pub struct G2Uncompressed([u8; 192]);
|
||||
|
||||
impl Clone for G2Uncompressed {
|
||||
fn clone(&self) -> G2Uncompressed {
|
||||
G2Uncompressed(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for G2Uncompressed {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0
|
||||
|
@ -1120,15 +1153,24 @@ pub mod g2 {
|
|||
|
||||
fn empty() -> Self { G2Uncompressed([0; 192]) }
|
||||
fn size() -> usize { 192 }
|
||||
fn into_affine_unchecked(&self) -> Result<G2Affine, ()> {
|
||||
use byteorder::{ReadBytesExt, BigEndian};
|
||||
fn into_affine(&self) -> Result<G2Affine, GroupDecodingError> {
|
||||
let affine = self.into_affine_unchecked()?;
|
||||
|
||||
if !affine.is_on_curve() {
|
||||
Err(GroupDecodingError::NotOnCurve)
|
||||
} else if !affine.is_in_correct_subgroup() {
|
||||
Err(GroupDecodingError::NotInSubgroup)
|
||||
} else {
|
||||
Ok(affine)
|
||||
}
|
||||
}
|
||||
fn into_affine_unchecked(&self) -> Result<G2Affine, GroupDecodingError> {
|
||||
// Create a copy of this representation.
|
||||
let mut copy = self.0;
|
||||
|
||||
if copy[0] & (1 << 7) != 0 {
|
||||
// Distinguisher bit is set, but this should be uncompressed!
|
||||
return Err(())
|
||||
return Err(GroupDecodingError::UnexpectedCompressionMode)
|
||||
}
|
||||
|
||||
if copy[0] & (1 << 6) != 0 {
|
||||
|
@ -1140,13 +1182,13 @@ pub mod g2 {
|
|||
if copy.iter().all(|b| *b == 0) {
|
||||
Ok(G2Affine::zero())
|
||||
} else {
|
||||
Err(())
|
||||
Err(GroupDecodingError::UnexpectedInformation)
|
||||
}
|
||||
} else {
|
||||
if copy[0] & (1 << 5) != 0 {
|
||||
// The bit indicating the y-coordinate should be lexicographically
|
||||
// largest is set, but this is an uncompressed element.
|
||||
return Err(())
|
||||
return Err(GroupDecodingError::UnexpectedInformation)
|
||||
}
|
||||
|
||||
// Unset the three most significant bits.
|
||||
|
@ -1160,39 +1202,26 @@ pub mod g2 {
|
|||
{
|
||||
let mut reader = ©[..];
|
||||
|
||||
for b in x_c1.0.iter_mut().rev() {
|
||||
*b = reader.read_u64::<BigEndian>().unwrap();
|
||||
}
|
||||
|
||||
for b in x_c0.0.iter_mut().rev() {
|
||||
*b = reader.read_u64::<BigEndian>().unwrap();
|
||||
}
|
||||
|
||||
for b in y_c1.0.iter_mut().rev() {
|
||||
*b = reader.read_u64::<BigEndian>().unwrap();
|
||||
}
|
||||
|
||||
for b in y_c0.0.iter_mut().rev() {
|
||||
*b = reader.read_u64::<BigEndian>().unwrap();
|
||||
}
|
||||
x_c1.read_be(&mut reader).unwrap();
|
||||
x_c0.read_be(&mut reader).unwrap();
|
||||
y_c1.read_be(&mut reader).unwrap();
|
||||
y_c0.read_be(&mut reader).unwrap();
|
||||
}
|
||||
|
||||
Ok(G2Affine {
|
||||
x: Fq2 {
|
||||
c0: Fq::from_repr(x_c0)?,
|
||||
c1: Fq::from_repr(x_c1)?
|
||||
c0: Fq::from_repr(x_c0).map_err(|e| GroupDecodingError::CoordinateDecodingError("x coordinate (c0)", e))?,
|
||||
c1: Fq::from_repr(x_c1).map_err(|e| GroupDecodingError::CoordinateDecodingError("x coordinate (c1)", e))?,
|
||||
},
|
||||
y: Fq2 {
|
||||
c0: Fq::from_repr(y_c0)?,
|
||||
c1: Fq::from_repr(y_c1)?
|
||||
c0: Fq::from_repr(y_c0).map_err(|e| GroupDecodingError::CoordinateDecodingError("y coordinate (c0)", e))?,
|
||||
c1: Fq::from_repr(y_c1).map_err(|e| GroupDecodingError::CoordinateDecodingError("y coordinate (c1)", e))?,
|
||||
},
|
||||
infinity: false
|
||||
})
|
||||
}
|
||||
}
|
||||
fn from_affine(affine: G2Affine) -> Self {
|
||||
use byteorder::{WriteBytesExt, BigEndian};
|
||||
|
||||
let mut res = Self::empty();
|
||||
|
||||
if affine.is_zero() {
|
||||
|
@ -1202,29 +1231,25 @@ pub mod g2 {
|
|||
} else {
|
||||
let mut writer = &mut res.0[..];
|
||||
|
||||
for digit in affine.x.c1.into_repr().as_ref().iter().rev() {
|
||||
writer.write_u64::<BigEndian>(*digit).unwrap();
|
||||
}
|
||||
|
||||
for digit in affine.x.c0.into_repr().as_ref().iter().rev() {
|
||||
writer.write_u64::<BigEndian>(*digit).unwrap();
|
||||
}
|
||||
|
||||
for digit in affine.y.c1.into_repr().as_ref().iter().rev() {
|
||||
writer.write_u64::<BigEndian>(*digit).unwrap();
|
||||
}
|
||||
|
||||
for digit in affine.y.c0.into_repr().as_ref().iter().rev() {
|
||||
writer.write_u64::<BigEndian>(*digit).unwrap();
|
||||
}
|
||||
affine.x.c1.into_repr().write_be(&mut writer).unwrap();
|
||||
affine.x.c0.into_repr().write_be(&mut writer).unwrap();
|
||||
affine.y.c1.into_repr().write_be(&mut writer).unwrap();
|
||||
affine.y.c0.into_repr().write_be(&mut writer).unwrap();
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy)]
|
||||
pub struct G2Compressed([u8; 96]);
|
||||
|
||||
impl Clone for G2Compressed {
|
||||
fn clone(&self) -> G2Compressed {
|
||||
G2Compressed(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for G2Compressed {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0
|
||||
|
@ -1242,15 +1267,24 @@ pub mod g2 {
|
|||
|
||||
fn empty() -> Self { G2Compressed([0; 96]) }
|
||||
fn size() -> usize { 96 }
|
||||
fn into_affine_unchecked(&self) -> Result<G2Affine, ()> {
|
||||
use byteorder::{ReadBytesExt, BigEndian};
|
||||
fn into_affine(&self) -> Result<G2Affine, GroupDecodingError> {
|
||||
let affine = self.into_affine_unchecked()?;
|
||||
|
||||
// NB: Decompression guarantees that it is on the curve already.
|
||||
|
||||
if !affine.is_in_correct_subgroup() {
|
||||
Err(GroupDecodingError::NotInSubgroup)
|
||||
} else {
|
||||
Ok(affine)
|
||||
}
|
||||
}
|
||||
fn into_affine_unchecked(&self) -> Result<G2Affine, GroupDecodingError> {
|
||||
// Create a copy of this representation.
|
||||
let mut copy = self.0;
|
||||
|
||||
if copy[0] & (1 << 7) == 0 {
|
||||
// Distinguisher bit isn't set.
|
||||
return Err(())
|
||||
return Err(GroupDecodingError::UnexpectedCompressionMode)
|
||||
}
|
||||
|
||||
if copy[0] & (1 << 6) != 0 {
|
||||
|
@ -1262,7 +1296,7 @@ pub mod g2 {
|
|||
if copy.iter().all(|b| *b == 0) {
|
||||
Ok(G2Affine::zero())
|
||||
} else {
|
||||
Err(())
|
||||
Err(GroupDecodingError::UnexpectedInformation)
|
||||
}
|
||||
} else {
|
||||
// Determine if the intended y coordinate must be greater
|
||||
|
@ -1278,19 +1312,14 @@ pub mod g2 {
|
|||
{
|
||||
let mut reader = ©[..];
|
||||
|
||||
for b in x_c1.0.iter_mut().rev() {
|
||||
*b = reader.read_u64::<BigEndian>().unwrap();
|
||||
}
|
||||
|
||||
for b in x_c0.0.iter_mut().rev() {
|
||||
*b = reader.read_u64::<BigEndian>().unwrap();
|
||||
}
|
||||
x_c1.read_be(&mut reader).unwrap();
|
||||
x_c0.read_be(&mut reader).unwrap();
|
||||
}
|
||||
|
||||
// Interpret as Fq element.
|
||||
let x = Fq2 {
|
||||
c0: Fq::from_repr(x_c0)?,
|
||||
c1: Fq::from_repr(x_c1)?
|
||||
c0: Fq::from_repr(x_c0).map_err(|e| GroupDecodingError::CoordinateDecodingError("x coordinate (c0)", e))?,
|
||||
c1: Fq::from_repr(x_c1).map_err(|e| GroupDecodingError::CoordinateDecodingError("x coordinate (c1)", e))?
|
||||
};
|
||||
|
||||
// Compute x^3 + b
|
||||
|
@ -1316,14 +1345,12 @@ pub mod g2 {
|
|||
},
|
||||
None => {
|
||||
// Point must not be on the curve.
|
||||
Err(())
|
||||
Err(GroupDecodingError::NotOnCurve)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn from_affine(affine: G2Affine) -> Self {
|
||||
use byteorder::{WriteBytesExt, BigEndian};
|
||||
|
||||
let mut res = Self::empty();
|
||||
|
||||
if affine.is_zero() {
|
||||
|
@ -1334,13 +1361,8 @@ pub mod g2 {
|
|||
{
|
||||
let mut writer = &mut res.0[..];
|
||||
|
||||
for digit in affine.x.c1.into_repr().as_ref().iter().rev() {
|
||||
writer.write_u64::<BigEndian>(*digit).unwrap();
|
||||
}
|
||||
|
||||
for digit in affine.x.c0.into_repr().as_ref().iter().rev() {
|
||||
writer.write_u64::<BigEndian>(*digit).unwrap();
|
||||
}
|
||||
affine.x.c1.into_repr().write_be(&mut writer).unwrap();
|
||||
affine.x.c0.into_repr().write_be(&mut writer).unwrap();
|
||||
}
|
||||
|
||||
let mut negy = affine.y;
|
||||
|
@ -1446,7 +1468,7 @@ pub mod g2 {
|
|||
infinity: false
|
||||
};
|
||||
|
||||
assert!(!p.is_valid());
|
||||
assert!(!p.is_in_correct_subgroup());
|
||||
|
||||
let mut g2 = G2::zero();
|
||||
|
||||
|
@ -1468,7 +1490,7 @@ pub mod g2 {
|
|||
assert_eq!(i, 2);
|
||||
let g2 = G2Affine::from(g2);
|
||||
|
||||
assert!(g2.is_valid());
|
||||
assert!(g2.is_in_correct_subgroup());
|
||||
|
||||
assert_eq!(g2, G2Affine::one());
|
||||
break;
|
||||
|
@ -1497,7 +1519,6 @@ pub mod g2 {
|
|||
};
|
||||
assert!(!p.is_on_curve());
|
||||
assert!(p.is_in_correct_subgroup());
|
||||
assert!(!p.is_valid());
|
||||
}
|
||||
|
||||
// Reject point on a twist (b = 2 * (u + 1))
|
||||
|
@ -1515,7 +1536,6 @@ pub mod g2 {
|
|||
};
|
||||
assert!(!p.is_on_curve());
|
||||
assert!(!p.is_in_correct_subgroup());
|
||||
assert!(!p.is_valid());
|
||||
}
|
||||
|
||||
// Reject point in an invalid subgroup
|
||||
|
@ -1534,7 +1554,6 @@ pub mod g2 {
|
|||
};
|
||||
assert!(p.is_on_curve());
|
||||
assert!(!p.is_in_correct_subgroup());
|
||||
assert!(!p.is_valid());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use ::{Field, PrimeField, SqrtField, PrimeFieldRepr};
|
||||
use ::{Field, PrimeField, SqrtField, PrimeFieldRepr, PrimeFieldDecodingError};
|
||||
use std::cmp::Ordering;
|
||||
use super::fq2::Fq2;
|
||||
|
||||
|
@ -192,7 +192,7 @@ pub const FROBENIUS_COEFF_FQ12_C1: [Fq2; 12] = [
|
|||
// -((2**384) mod q) mod q
|
||||
pub const NEGATIVE_ONE: Fq = Fq(FqRepr([0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x7e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x40ab3263eff0206]));
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Default)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
|
||||
pub struct FqRepr(pub [u64; 6]);
|
||||
|
||||
impl ::rand::Rand for FqRepr {
|
||||
|
@ -202,7 +202,7 @@ impl ::rand::Rand for FqRepr {
|
|||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Debug for FqRepr
|
||||
impl ::std::fmt::Display for FqRepr
|
||||
{
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
try!(write!(f, "0x"));
|
||||
|
@ -221,6 +221,13 @@ impl AsRef<[u64]> for FqRepr {
|
|||
}
|
||||
}
|
||||
|
||||
impl AsMut<[u64]> for FqRepr {
|
||||
#[inline(always)]
|
||||
fn as_mut(&mut self) -> &mut [u64] {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for FqRepr {
|
||||
#[inline(always)]
|
||||
fn from(val: u64) -> FqRepr {
|
||||
|
@ -355,7 +362,7 @@ impl PrimeFieldRepr for FqRepr {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Fq(FqRepr);
|
||||
|
||||
/// `Fq` elements are ordered lexicographically.
|
||||
|
@ -373,10 +380,10 @@ impl PartialOrd for Fq {
|
|||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Debug for Fq
|
||||
impl ::std::fmt::Display for Fq
|
||||
{
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "Fq({:?})", self.into_repr())
|
||||
write!(f, "Fq({})", self.into_repr())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -401,14 +408,14 @@ impl From<Fq> for FqRepr {
|
|||
impl PrimeField for Fq {
|
||||
type Repr = FqRepr;
|
||||
|
||||
fn from_repr(r: FqRepr) -> Result<Fq, ()> {
|
||||
fn from_repr(r: FqRepr) -> Result<Fq, PrimeFieldDecodingError> {
|
||||
let mut r = Fq(r);
|
||||
if r.is_valid() {
|
||||
r.mul_assign(&Fq(R2));
|
||||
|
||||
Ok(r)
|
||||
} else {
|
||||
Err(())
|
||||
Err(PrimeFieldDecodingError::NotInField(format!("{}", r.0)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1676,33 +1683,33 @@ fn bench_fq_from_repr(b: &mut ::test::Bencher) {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_fq_repr_debug() {
|
||||
fn test_fq_repr_display() {
|
||||
assert_eq!(
|
||||
format!("{:?}", FqRepr([0xa956babf9301ea24, 0x39a8f184f3535c7b, 0xb38d35b3f6779585, 0x676cc4eef4c46f2c, 0xb1d4aad87651e694, 0x1947f0d5f4fe325a])),
|
||||
format!("{}", FqRepr([0xa956babf9301ea24, 0x39a8f184f3535c7b, 0xb38d35b3f6779585, 0x676cc4eef4c46f2c, 0xb1d4aad87651e694, 0x1947f0d5f4fe325a])),
|
||||
"0x1947f0d5f4fe325ab1d4aad87651e694676cc4eef4c46f2cb38d35b3f677958539a8f184f3535c7ba956babf9301ea24".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{:?}", FqRepr([0xb4171485fd8622dd, 0x864229a6edec7ec5, 0xc57f7bdcf8dfb707, 0x6db7ff0ecea4584a, 0xf8d8578c4a57132d, 0x6eb66d42d9fcaaa])),
|
||||
format!("{}", FqRepr([0xb4171485fd8622dd, 0x864229a6edec7ec5, 0xc57f7bdcf8dfb707, 0x6db7ff0ecea4584a, 0xf8d8578c4a57132d, 0x6eb66d42d9fcaaa])),
|
||||
"0x06eb66d42d9fcaaaf8d8578c4a57132d6db7ff0ecea4584ac57f7bdcf8dfb707864229a6edec7ec5b4171485fd8622dd".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{:?}", FqRepr([0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff])),
|
||||
format!("{}", FqRepr([0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff])),
|
||||
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{:?}", FqRepr([0, 0, 0, 0, 0, 0])),
|
||||
format!("{}", FqRepr([0, 0, 0, 0, 0, 0])),
|
||||
"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fq_debug() {
|
||||
fn test_fq_display() {
|
||||
assert_eq!(
|
||||
format!("{:?}", Fq::from_repr(FqRepr([0xa956babf9301ea24, 0x39a8f184f3535c7b, 0xb38d35b3f6779585, 0x676cc4eef4c46f2c, 0xb1d4aad87651e694, 0x1947f0d5f4fe325a])).unwrap()),
|
||||
format!("{}", Fq::from_repr(FqRepr([0xa956babf9301ea24, 0x39a8f184f3535c7b, 0xb38d35b3f6779585, 0x676cc4eef4c46f2c, 0xb1d4aad87651e694, 0x1947f0d5f4fe325a])).unwrap()),
|
||||
"Fq(0x1947f0d5f4fe325ab1d4aad87651e694676cc4eef4c46f2cb38d35b3f677958539a8f184f3535c7ba956babf9301ea24)".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{:?}", Fq::from_repr(FqRepr([0xe28e79396ac2bbf8, 0x413f6f7f06ea87eb, 0xa4b62af4a792a689, 0xb7f89f88f59c1dc5, 0x9a551859b1e43a9a, 0x6c9f5a1060de974])).unwrap()),
|
||||
format!("{}", Fq::from_repr(FqRepr([0xe28e79396ac2bbf8, 0x413f6f7f06ea87eb, 0xa4b62af4a792a689, 0xb7f89f88f59c1dc5, 0x9a551859b1e43a9a, 0x6c9f5a1060de974])).unwrap()),
|
||||
"Fq(0x06c9f5a1060de9749a551859b1e43a9ab7f89f88f59c1dc5a4b62af4a792a689413f6f7f06ea87ebe28e79396ac2bbf8)".to_string()
|
||||
);
|
||||
}
|
||||
|
@ -1740,6 +1747,6 @@ fn test_fq_ordering() {
|
|||
// FqRepr's ordering is well-tested, but we still need to make sure the Fq
|
||||
// elements aren't being compared in Montgomery form.
|
||||
for i in 0..100 {
|
||||
assert!(Fq::from_repr(FqRepr::from(i+1)) > Fq::from_repr(FqRepr::from(i)));
|
||||
assert!(Fq::from_repr(FqRepr::from(i+1)).unwrap() > Fq::from_repr(FqRepr::from(i)).unwrap());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,13 @@ pub struct Fq12 {
|
|||
pub c1: Fq6
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for Fq12
|
||||
{
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "Fq12({} + {} * w)", self.c0, self.c1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Rand for Fq12 {
|
||||
fn rand<R: Rng>(rng: &mut R) -> Self {
|
||||
Fq12 {
|
||||
|
|
|
@ -11,6 +11,13 @@ pub struct Fq2 {
|
|||
pub c1: Fq
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for Fq2
|
||||
{
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "Fq2({} + {} * u)", self.c0, self.c1)
|
||||
}
|
||||
}
|
||||
|
||||
/// `Fq2` elements are ordered lexicographically.
|
||||
impl Ord for Fq2 {
|
||||
#[inline(always)]
|
||||
|
|
|
@ -11,6 +11,13 @@ pub struct Fq6 {
|
|||
pub c2: Fq2
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for Fq6
|
||||
{
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "Fq6({} + {} * v, {} * v^2)", self.c0, self.c1, self.c2)
|
||||
}
|
||||
}
|
||||
|
||||
impl Rand for Fq6 {
|
||||
fn rand<R: Rng>(rng: &mut R) -> Self {
|
||||
Fq6 {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use ::{Field, PrimeField, SqrtField, PrimeFieldRepr};
|
||||
use ::{Field, PrimeField, SqrtField, PrimeFieldRepr, PrimeFieldDecodingError};
|
||||
|
||||
// r = 52435875175126190479447740508185965837690552500527637822603658699938581184513
|
||||
const MODULUS: FrRepr = FrRepr([0xffffffff00000001, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48]);
|
||||
|
@ -28,7 +28,7 @@ const S: usize = 32;
|
|||
// 2^s root of unity computed by GENERATOR^t
|
||||
const ROOT_OF_UNITY: FrRepr = FrRepr([0xb9b58d8c5f0e466a, 0x5b1b4c801819d7ec, 0xaf53ae352a31e64, 0x5bf3adda19e9b27b]);
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Default)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
|
||||
pub struct FrRepr(pub [u64; 4]);
|
||||
|
||||
impl ::rand::Rand for FrRepr {
|
||||
|
@ -38,7 +38,7 @@ impl ::rand::Rand for FrRepr {
|
|||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Debug for FrRepr
|
||||
impl ::std::fmt::Display for FrRepr
|
||||
{
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
try!(write!(f, "0x"));
|
||||
|
@ -57,6 +57,13 @@ impl AsRef<[u64]> for FrRepr {
|
|||
}
|
||||
}
|
||||
|
||||
impl AsMut<[u64]> for FrRepr {
|
||||
#[inline(always)]
|
||||
fn as_mut(&mut self) -> &mut [u64] {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for FrRepr {
|
||||
#[inline(always)]
|
||||
fn from(val: u64) -> FrRepr {
|
||||
|
@ -191,13 +198,13 @@ impl PrimeFieldRepr for FrRepr {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Fr(FrRepr);
|
||||
|
||||
impl ::std::fmt::Debug for Fr
|
||||
impl ::std::fmt::Display for Fr
|
||||
{
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "Fr({:?})", self.into_repr())
|
||||
write!(f, "Fr({})", self.into_repr())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,14 +229,14 @@ impl From<Fr> for FrRepr {
|
|||
impl PrimeField for Fr {
|
||||
type Repr = FrRepr;
|
||||
|
||||
fn from_repr(r: FrRepr) -> Result<Fr, ()> {
|
||||
fn from_repr(r: FrRepr) -> Result<Fr, PrimeFieldDecodingError> {
|
||||
let mut r = Fr(r);
|
||||
if r.is_valid() {
|
||||
r.mul_assign(&Fr(R2));
|
||||
|
||||
Ok(r)
|
||||
} else {
|
||||
Err(())
|
||||
Err(PrimeFieldDecodingError::NotInField(format!("{}", r.0)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1388,33 +1395,33 @@ fn bench_fr_from_repr(b: &mut ::test::Bencher) {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_fr_repr_debug() {
|
||||
fn test_fr_repr_display() {
|
||||
assert_eq!(
|
||||
format!("{:?}", FrRepr([0x2829c242fa826143, 0x1f32cf4dd4330917, 0x932e4e479d168cd9, 0x513c77587f563f64])),
|
||||
format!("{}", FrRepr([0x2829c242fa826143, 0x1f32cf4dd4330917, 0x932e4e479d168cd9, 0x513c77587f563f64])),
|
||||
"0x513c77587f563f64932e4e479d168cd91f32cf4dd43309172829c242fa826143".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{:?}", FrRepr([0x25ebe3a3ad3c0c6a, 0x6990e39d092e817c, 0x941f900d42f5658e, 0x44f8a103b38a71e0])),
|
||||
format!("{}", FrRepr([0x25ebe3a3ad3c0c6a, 0x6990e39d092e817c, 0x941f900d42f5658e, 0x44f8a103b38a71e0])),
|
||||
"0x44f8a103b38a71e0941f900d42f5658e6990e39d092e817c25ebe3a3ad3c0c6a".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{:?}", FrRepr([0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff])),
|
||||
format!("{}", FrRepr([0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff])),
|
||||
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{:?}", FrRepr([0, 0, 0, 0])),
|
||||
format!("{}", FrRepr([0, 0, 0, 0])),
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fr_debug() {
|
||||
fn test_fr_display() {
|
||||
assert_eq!(
|
||||
format!("{:?}", Fr::from_repr(FrRepr([0xc3cae746a3b5ecc7, 0x185ec8eb3f5b5aee, 0x684499ffe4b9dd99, 0x7c9bba7afb68faa])).unwrap()),
|
||||
format!("{}", Fr::from_repr(FrRepr([0xc3cae746a3b5ecc7, 0x185ec8eb3f5b5aee, 0x684499ffe4b9dd99, 0x7c9bba7afb68faa])).unwrap()),
|
||||
"Fr(0x07c9bba7afb68faa684499ffe4b9dd99185ec8eb3f5b5aeec3cae746a3b5ecc7)".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{:?}", Fr::from_repr(FrRepr([0x44c71298ff198106, 0xb0ad10817df79b6a, 0xd034a80a2b74132b, 0x41cf9a1336f50719])).unwrap()),
|
||||
format!("{}", Fr::from_repr(FrRepr([0x44c71298ff198106, 0xb0ad10817df79b6a, 0xd034a80a2b74132b, 0x41cf9a1336f50719])).unwrap()),
|
||||
"Fr(0x41cf9a1336f50719d034a80a2b74132bb0ad10817df79b6a44c71298ff198106)".to_string()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -46,3 +46,508 @@ fn test_g2_compressed_valid_vectors() {
|
|||
test_vectors::<G2, G2Compressed>(include_bytes!("g2_compressed_valid_test_vectors.dat"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_g1_uncompressed_invalid_vectors() {
|
||||
{
|
||||
let z = G1Affine::zero().into_uncompressed();
|
||||
|
||||
{
|
||||
let mut z = z;
|
||||
z.as_mut()[0] |= 0b1000_0000;
|
||||
if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because we expected an uncompressed point");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut z = z;
|
||||
z.as_mut()[0] |= 0b0010_0000;
|
||||
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because the parity bit should not be set if the point is at infinity");
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0..G1Uncompressed::size() {
|
||||
let mut z = z;
|
||||
z.as_mut()[i] |= 0b0000_0001;
|
||||
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let o = G1Affine::one().into_uncompressed();
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
o.as_mut()[0] |= 0b1000_0000;
|
||||
if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because we expected an uncompressed point");
|
||||
}
|
||||
}
|
||||
|
||||
let m = Fq::char();
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
m.write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
|
||||
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||
assert_eq!(coordinate, "x coordinate");
|
||||
} else {
|
||||
panic!("should have rejected the point")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
m.write_be(&mut o.as_mut()[48..]).unwrap();
|
||||
|
||||
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||
assert_eq!(coordinate, "y coordinate");
|
||||
} else {
|
||||
panic!("should have rejected the point")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let m = Fq::zero().into_repr();
|
||||
|
||||
let mut o = o;
|
||||
m.write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
|
||||
if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because it isn't on the curve")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
let mut x = Fq::one();
|
||||
|
||||
loop {
|
||||
let mut x3b = x;
|
||||
x3b.square();
|
||||
x3b.mul_assign(&x);
|
||||
x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API?
|
||||
|
||||
if let Some(y) = x3b.sqrt() {
|
||||
// We know this is on the curve, but it's likely not going to be in the correct subgroup.
|
||||
x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
y.into_repr().write_be(&mut o.as_mut()[48..]).unwrap();
|
||||
|
||||
if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() {
|
||||
break
|
||||
} else {
|
||||
panic!("should have rejected the point because it isn't in the correct subgroup")
|
||||
}
|
||||
} else {
|
||||
x.add_assign(&Fq::one());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_g2_uncompressed_invalid_vectors() {
|
||||
{
|
||||
let z = G2Affine::zero().into_uncompressed();
|
||||
|
||||
{
|
||||
let mut z = z;
|
||||
z.as_mut()[0] |= 0b1000_0000;
|
||||
if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because we expected an uncompressed point");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut z = z;
|
||||
z.as_mut()[0] |= 0b0010_0000;
|
||||
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because the parity bit should not be set if the point is at infinity");
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0..G2Uncompressed::size() {
|
||||
let mut z = z;
|
||||
z.as_mut()[i] |= 0b0000_0001;
|
||||
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let o = G2Affine::one().into_uncompressed();
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
o.as_mut()[0] |= 0b1000_0000;
|
||||
if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because we expected an uncompressed point");
|
||||
}
|
||||
}
|
||||
|
||||
let m = Fq::char();
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
m.write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
|
||||
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||
assert_eq!(coordinate, "x coordinate (c1)");
|
||||
} else {
|
||||
panic!("should have rejected the point")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
m.write_be(&mut o.as_mut()[48..]).unwrap();
|
||||
|
||||
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||
assert_eq!(coordinate, "x coordinate (c0)");
|
||||
} else {
|
||||
panic!("should have rejected the point")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
m.write_be(&mut o.as_mut()[96..]).unwrap();
|
||||
|
||||
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||
assert_eq!(coordinate, "y coordinate (c1)");
|
||||
} else {
|
||||
panic!("should have rejected the point")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
m.write_be(&mut o.as_mut()[144..]).unwrap();
|
||||
|
||||
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||
assert_eq!(coordinate, "y coordinate (c0)");
|
||||
} else {
|
||||
panic!("should have rejected the point")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let m = Fq::zero().into_repr();
|
||||
|
||||
let mut o = o;
|
||||
m.write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
m.write_be(&mut o.as_mut()[48..]).unwrap();
|
||||
|
||||
if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because it isn't on the curve")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
let mut x = Fq2::one();
|
||||
|
||||
loop {
|
||||
let mut x3b = x;
|
||||
x3b.square();
|
||||
x3b.mul_assign(&x);
|
||||
x3b.add_assign(&Fq2 {
|
||||
c0: Fq::from_repr(FqRepr::from(4)).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr::from(4)).unwrap()
|
||||
}); // TODO: perhaps expose coeff_b through API?
|
||||
|
||||
if let Some(y) = x3b.sqrt() {
|
||||
// We know this is on the curve, but it's likely not going to be in the correct subgroup.
|
||||
x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap();
|
||||
y.c1.into_repr().write_be(&mut o.as_mut()[96..]).unwrap();
|
||||
y.c0.into_repr().write_be(&mut o.as_mut()[144..]).unwrap();
|
||||
|
||||
if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() {
|
||||
break
|
||||
} else {
|
||||
panic!("should have rejected the point because it isn't in the correct subgroup")
|
||||
}
|
||||
} else {
|
||||
x.add_assign(&Fq2::one());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_g1_compressed_invalid_vectors() {
|
||||
{
|
||||
let z = G1Affine::zero().into_compressed();
|
||||
|
||||
{
|
||||
let mut z = z;
|
||||
z.as_mut()[0] &= 0b0111_1111;
|
||||
if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because we expected a compressed point");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut z = z;
|
||||
z.as_mut()[0] |= 0b0010_0000;
|
||||
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because the parity bit should not be set if the point is at infinity");
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0..G1Compressed::size() {
|
||||
let mut z = z;
|
||||
z.as_mut()[i] |= 0b0000_0001;
|
||||
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let o = G1Affine::one().into_compressed();
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
o.as_mut()[0] &= 0b0111_1111;
|
||||
if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because we expected a compressed point");
|
||||
}
|
||||
}
|
||||
|
||||
let m = Fq::char();
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
m.write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
o.as_mut()[0] |= 0b1000_0000;
|
||||
|
||||
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||
assert_eq!(coordinate, "x coordinate");
|
||||
} else {
|
||||
panic!("should have rejected the point")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
let mut x = Fq::one();
|
||||
|
||||
loop {
|
||||
let mut x3b = x;
|
||||
x3b.square();
|
||||
x3b.mul_assign(&x);
|
||||
x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API?
|
||||
|
||||
if let Some(_) = x3b.sqrt() {
|
||||
x.add_assign(&Fq::one());
|
||||
} else {
|
||||
x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
o.as_mut()[0] |= 0b1000_0000;
|
||||
|
||||
if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() {
|
||||
break
|
||||
} else {
|
||||
panic!("should have rejected the point because it isn't on the curve")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
let mut x = Fq::one();
|
||||
|
||||
loop {
|
||||
let mut x3b = x;
|
||||
x3b.square();
|
||||
x3b.mul_assign(&x);
|
||||
x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API?
|
||||
|
||||
if let Some(_) = x3b.sqrt() {
|
||||
// We know this is on the curve, but it's likely not going to be in the correct subgroup.
|
||||
x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
o.as_mut()[0] |= 0b1000_0000;
|
||||
|
||||
if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() {
|
||||
break
|
||||
} else {
|
||||
panic!("should have rejected the point because it isn't in the correct subgroup")
|
||||
}
|
||||
} else {
|
||||
x.add_assign(&Fq::one());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_g2_compressed_invalid_vectors() {
|
||||
{
|
||||
let z = G2Affine::zero().into_compressed();
|
||||
|
||||
{
|
||||
let mut z = z;
|
||||
z.as_mut()[0] &= 0b0111_1111;
|
||||
if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because we expected a compressed point");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut z = z;
|
||||
z.as_mut()[0] |= 0b0010_0000;
|
||||
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because the parity bit should not be set if the point is at infinity");
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0..G2Compressed::size() {
|
||||
let mut z = z;
|
||||
z.as_mut()[i] |= 0b0000_0001;
|
||||
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let o = G2Affine::one().into_compressed();
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
o.as_mut()[0] &= 0b0111_1111;
|
||||
if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() {
|
||||
// :)
|
||||
} else {
|
||||
panic!("should have rejected the point because we expected a compressed point");
|
||||
}
|
||||
}
|
||||
|
||||
let m = Fq::char();
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
m.write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
o.as_mut()[0] |= 0b1000_0000;
|
||||
|
||||
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||
assert_eq!(coordinate, "x coordinate (c1)");
|
||||
} else {
|
||||
panic!("should have rejected the point")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
m.write_be(&mut o.as_mut()[48..]).unwrap();
|
||||
o.as_mut()[0] |= 0b1000_0000;
|
||||
|
||||
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
|
||||
assert_eq!(coordinate, "x coordinate (c0)");
|
||||
} else {
|
||||
panic!("should have rejected the point")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
let mut x = Fq2 {
|
||||
c0: Fq::one(),
|
||||
c1: Fq::one()
|
||||
};
|
||||
|
||||
loop {
|
||||
let mut x3b = x;
|
||||
x3b.square();
|
||||
x3b.mul_assign(&x);
|
||||
x3b.add_assign(&Fq2 {
|
||||
c0: Fq::from_repr(FqRepr::from(4)).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr::from(4)).unwrap(),
|
||||
}); // TODO: perhaps expose coeff_b through API?
|
||||
|
||||
if let Some(_) = x3b.sqrt() {
|
||||
x.add_assign(&Fq2::one());
|
||||
} else {
|
||||
x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap();
|
||||
o.as_mut()[0] |= 0b1000_0000;
|
||||
|
||||
if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() {
|
||||
break
|
||||
} else {
|
||||
panic!("should have rejected the point because it isn't on the curve")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut o = o;
|
||||
let mut x = Fq2 {
|
||||
c0: Fq::one(),
|
||||
c1: Fq::one()
|
||||
};
|
||||
|
||||
loop {
|
||||
let mut x3b = x;
|
||||
x3b.square();
|
||||
x3b.mul_assign(&x);
|
||||
x3b.add_assign(&Fq2 {
|
||||
c0: Fq::from_repr(FqRepr::from(4)).unwrap(),
|
||||
c1: Fq::from_repr(FqRepr::from(4)).unwrap(),
|
||||
}); // TODO: perhaps expose coeff_b through API?
|
||||
|
||||
if let Some(_) = x3b.sqrt() {
|
||||
// We know this is on the curve, but it's likely not going to be in the correct subgroup.
|
||||
x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap();
|
||||
o.as_mut()[0] |= 0b1000_0000;
|
||||
|
||||
if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() {
|
||||
break
|
||||
} else {
|
||||
panic!("should have rejected the point because it isn't in the correct subgroup")
|
||||
}
|
||||
} else {
|
||||
x.add_assign(&Fq2::one());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
126
src/lib.rs
126
src/lib.rs
|
@ -28,6 +28,8 @@ pub mod bls12_381;
|
|||
pub mod wnaf;
|
||||
|
||||
use std::fmt;
|
||||
use std::error::Error;
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
/// An "engine" is a collection of types (fields, elliptic curve groups, etc.)
|
||||
/// with well-defined relationships. In particular, the G1/G2 curve groups are
|
||||
|
@ -91,6 +93,7 @@ pub trait CurveProjective: PartialEq +
|
|||
Send +
|
||||
Sync +
|
||||
fmt::Debug +
|
||||
fmt::Display +
|
||||
rand::Rand +
|
||||
'static
|
||||
{
|
||||
|
@ -158,6 +161,7 @@ pub trait CurveAffine: Copy +
|
|||
Send +
|
||||
Sync +
|
||||
fmt::Debug +
|
||||
fmt::Display +
|
||||
PartialEq +
|
||||
Eq +
|
||||
'static
|
||||
|
@ -179,9 +183,6 @@ pub trait CurveAffine: Copy +
|
|||
/// additive identity.
|
||||
fn is_zero(&self) -> bool;
|
||||
|
||||
/// Determines if this point is on the curve and in the correct subgroup.
|
||||
fn is_valid(&self) -> bool;
|
||||
|
||||
/// Negates this element.
|
||||
fn negate(&mut self);
|
||||
|
||||
|
@ -213,6 +214,8 @@ pub trait EncodedPoint: Sized +
|
|||
Sync +
|
||||
AsRef<[u8]> +
|
||||
AsMut<[u8]> +
|
||||
Clone +
|
||||
Copy +
|
||||
'static
|
||||
{
|
||||
type Affine: CurveAffine;
|
||||
|
@ -224,21 +227,17 @@ pub trait EncodedPoint: Sized +
|
|||
fn size() -> usize;
|
||||
|
||||
/// Converts an `EncodedPoint` into a `CurveAffine` element,
|
||||
/// if the point is valid.
|
||||
fn into_affine(&self) -> Result<Self::Affine, ()> {
|
||||
let affine = self.into_affine_unchecked()?;
|
||||
|
||||
if affine.is_valid() {
|
||||
Ok(affine)
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
/// if the encoding represents a valid element.
|
||||
fn into_affine(&self) -> Result<Self::Affine, GroupDecodingError>;
|
||||
|
||||
/// Converts an `EncodedPoint` into a `CurveAffine` element,
|
||||
/// without checking if it's a valid point. Caller must be careful
|
||||
/// when using this, as misuse can violate API invariants.
|
||||
fn into_affine_unchecked(&self) -> Result<Self::Affine, ()>;
|
||||
/// without guaranteeing that the encoding represents a valid
|
||||
/// element. This is useful when the caller knows the encoding is
|
||||
/// valid already.
|
||||
///
|
||||
/// If the encoding is invalid, this can break API invariants,
|
||||
/// so caution is strongly encouraged.
|
||||
fn into_affine_unchecked(&self) -> Result<Self::Affine, GroupDecodingError>;
|
||||
|
||||
/// Creates an `EncodedPoint` from an affine point, as long as the
|
||||
/// point is not the point at infinity.
|
||||
|
@ -253,6 +252,7 @@ pub trait Field: Sized +
|
|||
Send +
|
||||
Sync +
|
||||
fmt::Debug +
|
||||
fmt::Display +
|
||||
'static +
|
||||
rand::Rand
|
||||
{
|
||||
|
@ -333,9 +333,11 @@ pub trait PrimeFieldRepr: Sized +
|
|||
Send +
|
||||
Sync +
|
||||
fmt::Debug +
|
||||
fmt::Display +
|
||||
'static +
|
||||
rand::Rand +
|
||||
AsRef<[u64]> +
|
||||
AsMut<[u64]> +
|
||||
From<u64>
|
||||
{
|
||||
/// Subtract another reprensetation from this one, returning the borrow bit.
|
||||
|
@ -366,6 +368,96 @@ pub trait PrimeFieldRepr: Sized +
|
|||
/// Performs a leftwise bitshift of this number, effectively multiplying
|
||||
/// it by 2. Overflow is ignored.
|
||||
fn mul2(&mut self);
|
||||
|
||||
/// Writes this `PrimeFieldRepr` as a big endian integer. Always writes
|
||||
/// `(num_bits` / 8) bytes.
|
||||
fn write_be<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
use byteorder::{WriteBytesExt, BigEndian};
|
||||
|
||||
for digit in self.as_ref().iter().rev() {
|
||||
writer.write_u64::<BigEndian>(*digit)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reads a big endian integer occupying (`num_bits` / 8) bytes into this
|
||||
/// representation.
|
||||
fn read_be<R: Read>(&mut self, mut reader: R) -> io::Result<()> {
|
||||
use byteorder::{ReadBytesExt, BigEndian};
|
||||
|
||||
for digit in self.as_mut().iter_mut().rev() {
|
||||
*digit = reader.read_u64::<BigEndian>()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// An error that may occur when trying to interpret a `PrimeFieldRepr` as a
|
||||
/// `PrimeField` element.
|
||||
#[derive(Debug)]
|
||||
pub enum PrimeFieldDecodingError {
|
||||
/// The encoded value is not in the field
|
||||
NotInField(String)
|
||||
}
|
||||
|
||||
impl Error for PrimeFieldDecodingError {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
PrimeFieldDecodingError::NotInField(..) => "not an element of the field"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PrimeFieldDecodingError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
PrimeFieldDecodingError::NotInField(ref repr) => {
|
||||
write!(f, "{} is not an element of the field", repr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An error that may occur when trying to decode an `EncodedPoint`.
|
||||
#[derive(Debug)]
|
||||
pub enum GroupDecodingError {
|
||||
/// The coordinate(s) do not lie on the curve.
|
||||
NotOnCurve,
|
||||
/// The element is not part of the r-order subgroup.
|
||||
NotInSubgroup,
|
||||
/// One of the coordinates could not be decoded
|
||||
CoordinateDecodingError(&'static str, PrimeFieldDecodingError),
|
||||
/// The compression mode of the encoded elemnet was not as expected
|
||||
UnexpectedCompressionMode,
|
||||
/// The encoding contained bits that should not have been set
|
||||
UnexpectedInformation
|
||||
}
|
||||
|
||||
impl Error for GroupDecodingError {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
GroupDecodingError::NotOnCurve => "coordinate(s) do not lie on the curve",
|
||||
GroupDecodingError::NotInSubgroup => "the element is not part of an r-order subgroup",
|
||||
GroupDecodingError::CoordinateDecodingError(..) => "coordinate(s) could not be decoded",
|
||||
GroupDecodingError::UnexpectedCompressionMode => "encoding has unexpected compression mode",
|
||||
GroupDecodingError::UnexpectedInformation => "encoding has unexpected information"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for GroupDecodingError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
GroupDecodingError::CoordinateDecodingError(description, ref err) => {
|
||||
write!(f, "{} decoding error: {}", description, err)
|
||||
},
|
||||
_ => {
|
||||
write!(f, "{}", self.description())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This represents an element of a prime field.
|
||||
|
@ -376,7 +468,7 @@ pub trait PrimeField: Field
|
|||
type Repr: PrimeFieldRepr + From<Self>;
|
||||
|
||||
/// Convert this prime field element into a biginteger representation.
|
||||
fn from_repr(Self::Repr) -> Result<Self, ()>;
|
||||
fn from_repr(Self::Repr) -> Result<Self, PrimeFieldDecodingError>;
|
||||
|
||||
/// Convert a biginteger reprensentation into a prime field element, if
|
||||
/// the number is an element of the field.
|
||||
|
|
Loading…
Reference in New Issue