Implements and documents serialization, closes #11.
This commit is contained in:
parent
e72660056e
commit
c618240c91
@ -55,3 +55,17 @@ y = 1339506544944476473020471379941921221584933875938349620426543736416511423956
|
||||
x = 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758*u + 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160
|
||||
y = 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582*u + 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905
|
||||
```
|
||||
|
||||
### Serialization
|
||||
|
||||
* Fq elements are encoded in big-endian form. They occupy 48 bytes in this form.
|
||||
* Fq2 elements are encoded in big-endian form, meaning that the Fq element c0 + c1 * u is represented by the Fq element c1 followed by the Fq element c0. This means Fq2 elements occupy 96 bytes in this form.
|
||||
* The group G1 uses Fq elements for coordinates. The group G2 uses Fq2 elements for coordinates.
|
||||
* G1 and G2 elements can be encoded in uncompressed form (the x-coordinate followed by the y-coordinate) or in compressed form (just the x-coordinate). G1 elements occupy 96 bytes in uncompressed form, and 48 bytes in compressed form. G2 elements occupy 192 bytes in uncompressed form, and 96 bytes in compressed form.
|
||||
|
||||
The most-significant three bits of a G1 or G2 encoding should be masked away before the coordinate(s) are interpreted. These bits are used to unambiguously represent the underlying element:
|
||||
|
||||
* The most significant bit, when set, indicates that the point is in compressed form. Otherwise, the point is in uncompressed form.
|
||||
* The second-most significant bit indicates that the point is at infinity. If this bit is set, the remaining bits of the group element's encoding should be set to zero.
|
||||
* The third-most significant bit is set if (and only if) this point is in compressed form _and_ it is not the point at infinity _and_ its y-coordinate is the lexicographically largest of the two associated with the encoded x-coordinate.
|
||||
|
||||
|
@ -586,11 +586,40 @@ pub mod g1 {
|
||||
fn into_affine_unchecked(&self) -> Result<G1Affine, ()> {
|
||||
use byteorder::{ReadBytesExt, BigEndian};
|
||||
|
||||
// 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(())
|
||||
}
|
||||
|
||||
if copy[0] & (1 << 6) != 0 {
|
||||
// This is the point at infinity, which means that if we mask away
|
||||
// the first two bits, the entire representation should consist
|
||||
// of zeroes.
|
||||
copy[0] &= 0x3f;
|
||||
|
||||
if copy.iter().all(|b| *b == 0) {
|
||||
Ok(G1Affine::zero())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
} 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(())
|
||||
}
|
||||
|
||||
// Unset the three most significant bits.
|
||||
copy[0] &= 0x1f;
|
||||
|
||||
let mut x = FqRepr([0; 6]);
|
||||
let mut y = FqRepr([0; 6]);
|
||||
|
||||
{
|
||||
let mut reader = &self.0[..];
|
||||
let mut reader = ©[..];
|
||||
|
||||
for b in x.0.iter_mut().rev() {
|
||||
*b = reader.read_u64::<BigEndian>().unwrap();
|
||||
@ -607,16 +636,17 @@ pub mod g1 {
|
||||
infinity: false
|
||||
})
|
||||
}
|
||||
fn from_affine(affine: G1Affine) -> Result<Self, ()> {
|
||||
use byteorder::{WriteBytesExt, BigEndian};
|
||||
|
||||
if affine.is_zero() {
|
||||
return Err(())
|
||||
}
|
||||
fn from_affine(affine: G1Affine) -> Self {
|
||||
use byteorder::{WriteBytesExt, BigEndian};
|
||||
|
||||
let mut res = Self::empty();
|
||||
|
||||
{
|
||||
if affine.is_zero() {
|
||||
// Set the second-most significant bit to indicate this point
|
||||
// is at infinity.
|
||||
res.0[0] |= 1 << 6;
|
||||
} else {
|
||||
let mut writer = &mut res.0[..];
|
||||
|
||||
for digit in affine.x.into_repr().as_ref().iter().rev() {
|
||||
@ -628,7 +658,7 @@ pub mod g1 {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
@ -662,12 +692,24 @@ pub mod g1 {
|
||||
return Err(())
|
||||
}
|
||||
|
||||
if copy[0] & (1 << 6) != 0 {
|
||||
// This is the point at infinity, which means that if we mask away
|
||||
// the first two bits, the entire representation should consist
|
||||
// of zeroes.
|
||||
copy[0] &= 0x3f;
|
||||
|
||||
if copy.iter().all(|b| *b == 0) {
|
||||
Ok(G1Affine::zero())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
} else {
|
||||
// Determine if the intended y coordinate must be greater
|
||||
// lexicographically.
|
||||
let greatest = copy[0] & (1 << 6) != 0;
|
||||
let greatest = copy[0] & (1 << 5) != 0;
|
||||
|
||||
// Unset the two most significant bits.
|
||||
copy[0] &= 0x3f;
|
||||
// Unset the three most significant bits.
|
||||
copy[0] &= 0x1f;
|
||||
|
||||
let mut x = FqRepr([0; 6]);
|
||||
|
||||
@ -709,15 +751,17 @@ pub mod g1 {
|
||||
}
|
||||
}
|
||||
}
|
||||
fn from_affine(affine: G1Affine) -> Result<Self, ()> {
|
||||
use byteorder::{WriteBytesExt, BigEndian};
|
||||
|
||||
if affine.is_zero() {
|
||||
return Err(())
|
||||
}
|
||||
fn from_affine(affine: G1Affine) -> Self {
|
||||
use byteorder::{WriteBytesExt, BigEndian};
|
||||
|
||||
let mut res = Self::empty();
|
||||
|
||||
if affine.is_zero() {
|
||||
// Set the second-most significant bit to indicate this point
|
||||
// is at infinity.
|
||||
res.0[0] |= 1 << 6;
|
||||
} else {
|
||||
{
|
||||
let mut writer = &mut res.0[..];
|
||||
|
||||
@ -726,21 +770,20 @@ pub mod g1 {
|
||||
}
|
||||
}
|
||||
|
||||
// Distinguish this from an uncompressed element.
|
||||
res.0[0] |= 1 << 7; // Set highest bit.
|
||||
|
||||
{
|
||||
let mut negy = affine.y;
|
||||
negy.negate();
|
||||
|
||||
// If the correct y coordinate is the largest (lexicographically),
|
||||
// the bit should be set.
|
||||
// Set the third most significant bit if the correct y-coordinate
|
||||
// is lexicographically largest.
|
||||
if affine.y > negy {
|
||||
res.0[0] |= 1 << 6; // Set second highest bit.
|
||||
res.0[0] |= 1 << 5;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
// Set highest bit to distinguish this as a compressed element.
|
||||
res.0[0] |= 1 << 7;
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
@ -1080,13 +1123,42 @@ pub mod g2 {
|
||||
fn into_affine_unchecked(&self) -> Result<G2Affine, ()> {
|
||||
use byteorder::{ReadBytesExt, BigEndian};
|
||||
|
||||
let mut x_c1 = FqRepr([0; 6]);
|
||||
// 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(())
|
||||
}
|
||||
|
||||
if copy[0] & (1 << 6) != 0 {
|
||||
// This is the point at infinity, which means that if we mask away
|
||||
// the first two bits, the entire representation should consist
|
||||
// of zeroes.
|
||||
copy[0] &= 0x3f;
|
||||
|
||||
if copy.iter().all(|b| *b == 0) {
|
||||
Ok(G2Affine::zero())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
} 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(())
|
||||
}
|
||||
|
||||
// Unset the three most significant bits.
|
||||
copy[0] &= 0x1f;
|
||||
|
||||
let mut x_c0 = FqRepr([0; 6]);
|
||||
let mut y_c1 = FqRepr([0; 6]);
|
||||
let mut x_c1 = FqRepr([0; 6]);
|
||||
let mut y_c0 = FqRepr([0; 6]);
|
||||
let mut y_c1 = FqRepr([0; 6]);
|
||||
|
||||
{
|
||||
let mut reader = &self.0[..];
|
||||
let mut reader = ©[..];
|
||||
|
||||
for b in x_c1.0.iter_mut().rev() {
|
||||
*b = reader.read_u64::<BigEndian>().unwrap();
|
||||
@ -1117,16 +1189,17 @@ pub mod g2 {
|
||||
infinity: false
|
||||
})
|
||||
}
|
||||
fn from_affine(affine: G2Affine) -> Result<Self, ()> {
|
||||
use byteorder::{WriteBytesExt, BigEndian};
|
||||
|
||||
if affine.is_zero() {
|
||||
return Err(())
|
||||
}
|
||||
fn from_affine(affine: G2Affine) -> Self {
|
||||
use byteorder::{WriteBytesExt, BigEndian};
|
||||
|
||||
let mut res = Self::empty();
|
||||
|
||||
{
|
||||
if affine.is_zero() {
|
||||
// Set the second-most significant bit to indicate this point
|
||||
// is at infinity.
|
||||
res.0[0] |= 1 << 6;
|
||||
} else {
|
||||
let mut writer = &mut res.0[..];
|
||||
|
||||
for digit in affine.x.c1.into_repr().as_ref().iter().rev() {
|
||||
@ -1146,7 +1219,7 @@ pub mod g2 {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
@ -1180,12 +1253,24 @@ pub mod g2 {
|
||||
return Err(())
|
||||
}
|
||||
|
||||
if copy[0] & (1 << 6) != 0 {
|
||||
// This is the point at infinity, which means that if we mask away
|
||||
// the first two bits, the entire representation should consist
|
||||
// of zeroes.
|
||||
copy[0] &= 0x3f;
|
||||
|
||||
if copy.iter().all(|b| *b == 0) {
|
||||
Ok(G2Affine::zero())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
} else {
|
||||
// Determine if the intended y coordinate must be greater
|
||||
// lexicographically.
|
||||
let greatest = copy[0] & (1 << 6) != 0;
|
||||
let greatest = copy[0] & (1 << 5) != 0;
|
||||
|
||||
// Unset the two most significant bits.
|
||||
copy[0] &= 0x3f;
|
||||
// Unset the three most significant bits.
|
||||
copy[0] &= 0x1f;
|
||||
|
||||
let mut x_c1 = FqRepr([0; 6]);
|
||||
let mut x_c0 = FqRepr([0; 6]);
|
||||
@ -1235,15 +1320,17 @@ pub mod g2 {
|
||||
}
|
||||
}
|
||||
}
|
||||
fn from_affine(affine: G2Affine) -> Result<Self, ()> {
|
||||
use byteorder::{WriteBytesExt, BigEndian};
|
||||
|
||||
if affine.is_zero() {
|
||||
return Err(())
|
||||
}
|
||||
fn from_affine(affine: G2Affine) -> Self {
|
||||
use byteorder::{WriteBytesExt, BigEndian};
|
||||
|
||||
let mut res = Self::empty();
|
||||
|
||||
if affine.is_zero() {
|
||||
// Set the second-most significant bit to indicate this point
|
||||
// is at infinity.
|
||||
res.0[0] |= 1 << 6;
|
||||
} else {
|
||||
{
|
||||
let mut writer = &mut res.0[..];
|
||||
|
||||
@ -1256,21 +1343,20 @@ pub mod g2 {
|
||||
}
|
||||
}
|
||||
|
||||
// Distinguish this from an uncompressed element.
|
||||
res.0[0] |= 1 << 7; // Set highest bit.
|
||||
|
||||
{
|
||||
let mut negy = affine.y;
|
||||
negy.negate();
|
||||
|
||||
// If the correct y coordinate is the largest (lexicographically),
|
||||
// the bit should be set.
|
||||
// Set the third most significant bit if the correct y-coordinate
|
||||
// is lexicographically largest.
|
||||
if affine.y > negy {
|
||||
res.0[0] |= 1 << 6; // Set second highest bit.
|
||||
res.0[0] |= 1 << 5;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
// Set highest bit to distinguish this as a compressed element.
|
||||
res.0[0] |= 1 << 7;
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
|
Binary file not shown.
BIN
src/bls12_381/tests/g1_compressed_valid_test_vectors.dat
Normal file
BIN
src/bls12_381/tests/g1_compressed_valid_test_vectors.dat
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -3,14 +3,14 @@ use ::*;
|
||||
|
||||
fn test_vectors<G: CurveProjective, E: EncodedPoint<Affine=G::Affine>>(expected: &[u8])
|
||||
{
|
||||
let mut e = G::one();
|
||||
let mut e = G::zero();
|
||||
|
||||
let mut v = vec![];
|
||||
{
|
||||
let mut expected = expected;
|
||||
for _ in 0..1000 {
|
||||
let e_affine = e.into_affine();
|
||||
let encoded = E::from_affine(e_affine).unwrap();
|
||||
let encoded = E::from_affine(e_affine);
|
||||
v.extend_from_slice(encoded.as_ref());
|
||||
|
||||
let mut decoded = E::empty();
|
||||
@ -27,22 +27,22 @@ fn test_vectors<G: CurveProjective, E: EncodedPoint<Affine=G::Affine>>(expected:
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_g1_uncompressed_vectors() {
|
||||
test_vectors::<G1, G1Uncompressed>(include_bytes!("g1_uncompressed_test_vectors.dat"));
|
||||
fn test_g1_uncompressed_valid_vectors() {
|
||||
test_vectors::<G1, G1Uncompressed>(include_bytes!("g1_uncompressed_valid_test_vectors.dat"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_g1_compressed_vectors() {
|
||||
test_vectors::<G1, G1Compressed>(include_bytes!("g1_compressed_test_vectors.dat"));
|
||||
fn test_g1_compressed_valid_vectors() {
|
||||
test_vectors::<G1, G1Compressed>(include_bytes!("g1_compressed_valid_test_vectors.dat"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_g2_uncompressed_vectors() {
|
||||
test_vectors::<G2, G2Uncompressed>(include_bytes!("g2_uncompressed_test_vectors.dat"));
|
||||
fn test_g2_uncompressed_valid_vectors() {
|
||||
test_vectors::<G2, G2Uncompressed>(include_bytes!("g2_uncompressed_valid_test_vectors.dat"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_g2_compressed_vectors() {
|
||||
test_vectors::<G2, G2Compressed>(include_bytes!("g2_compressed_test_vectors.dat"));
|
||||
fn test_g2_compressed_valid_vectors() {
|
||||
test_vectors::<G2, G2Compressed>(include_bytes!("g2_compressed_valid_test_vectors.dat"));
|
||||
}
|
||||
|
||||
|
@ -184,13 +184,13 @@ pub trait CurveAffine: Copy +
|
||||
|
||||
/// Converts this element into its compressed encoding, so long as it's not
|
||||
/// the point at infinity.
|
||||
fn into_compressed(&self) -> Result<Self::Compressed, ()> {
|
||||
fn into_compressed(&self) -> Self::Compressed {
|
||||
<Self::Compressed as EncodedPoint>::from_affine(*self)
|
||||
}
|
||||
|
||||
/// Converts this element into its uncompressed encoding, so long as it's not
|
||||
/// the point at infinity.
|
||||
fn into_uncompressed(&self) -> Result<Self::Uncompressed, ()> {
|
||||
fn into_uncompressed(&self) -> Self::Uncompressed {
|
||||
<Self::Uncompressed as EncodedPoint>::from_affine(*self)
|
||||
}
|
||||
}
|
||||
@ -230,7 +230,7 @@ pub trait EncodedPoint: Sized +
|
||||
|
||||
/// Creates an `EncodedPoint` from an affine point, as long as the
|
||||
/// point is not the point at infinity.
|
||||
fn from_affine(affine: Self::Affine) -> Result<Self, ()>;
|
||||
fn from_affine(affine: Self::Affine) -> Self;
|
||||
}
|
||||
|
||||
/// This trait represents an element of a field.
|
||||
|
@ -299,25 +299,32 @@ fn random_transformation_tests<G: CurveProjective>() {
|
||||
|
||||
fn random_encoding_tests<G: CurveAffine>()
|
||||
{
|
||||
assert!(G::zero().into_compressed().is_err());
|
||||
assert!(G::zero().into_uncompressed().is_err());
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
assert_eq!(
|
||||
G::zero().into_uncompressed().into_affine().unwrap(),
|
||||
G::zero()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
G::zero().into_compressed().into_affine().unwrap(),
|
||||
G::zero()
|
||||
);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let mut r = G::Projective::rand(&mut rng).into_affine();
|
||||
|
||||
let uncompressed = r.into_uncompressed().unwrap();
|
||||
let uncompressed = r.into_uncompressed();
|
||||
let de_uncompressed = uncompressed.into_affine().unwrap();
|
||||
assert_eq!(de_uncompressed, r);
|
||||
|
||||
let compressed = r.into_compressed().unwrap();
|
||||
let compressed = r.into_compressed();
|
||||
let de_compressed = compressed.into_affine().unwrap();
|
||||
assert_eq!(de_compressed, r);
|
||||
|
||||
r.negate();
|
||||
|
||||
let compressed = r.into_compressed().unwrap();
|
||||
let compressed = r.into_compressed();
|
||||
let de_compressed = compressed.into_affine().unwrap();
|
||||
assert_eq!(de_compressed, r);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user