Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Affine serialization #687

Merged
merged 7 commits into from
Dec 18, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
235 changes: 185 additions & 50 deletions math/src/elliptic_curve/short_weierstrass/point.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,7 @@ impl<E: IsShortWeierstrass> IsGroup for ShortWeierstrassProjectivePoint<E> {
#[derive(PartialEq)]
pub enum PointFormat {
Projective,
// TO DO:
// Uncompressed,
Uncompressed,
// Compressed,
}

Expand All @@ -233,7 +232,7 @@ where
{
/// Serialize the points in the given format
#[cfg(feature = "std")]
pub fn serialize(&self, _point_format: PointFormat, endianness: Endianness) -> Vec<u8> {
pub fn serialize(&self, point_format: PointFormat, endianness: Endianness) -> Vec<u8> {
// TODO: Add more compact serialization formats
// Uncompressed affine / Compressed

Expand All @@ -242,59 +241,101 @@ where
let y_bytes: Vec<u8>;
let z_bytes: Vec<u8>;

let [x, y, z] = self.coordinates();
if endianness == Endianness::BigEndian {
x_bytes = x.to_bytes_be();
y_bytes = y.to_bytes_be();
z_bytes = z.to_bytes_be();
} else {
x_bytes = x.to_bytes_le();
y_bytes = y.to_bytes_le();
z_bytes = z.to_bytes_le();
match point_format {
PointFormat::Projective => {
let [x, y, z] = self.coordinates();
if endianness == Endianness::BigEndian {
x_bytes = x.to_bytes_be();
y_bytes = y.to_bytes_be();
z_bytes = z.to_bytes_be();
} else {
x_bytes = x.to_bytes_le();
y_bytes = y.to_bytes_le();
z_bytes = z.to_bytes_le();
}
bytes.extend(&x_bytes);
bytes.extend(&y_bytes);
bytes.extend(&z_bytes);
}
PointFormat::Uncompressed => {
let affine_representation = self.to_affine();
let [x, y, _z] = affine_representation.coordinates();
if endianness == Endianness::BigEndian {
x_bytes = x.to_bytes_be();
y_bytes = y.to_bytes_be();
} else {
x_bytes = x.to_bytes_le();
y_bytes = y.to_bytes_le();
}
bytes.extend(&x_bytes);
bytes.extend(&y_bytes);
}
}

bytes.extend(&x_bytes);
bytes.extend(&y_bytes);
bytes.extend(&z_bytes);

bytes
}

pub fn deserialize(
bytes: &[u8],
_point_format: PointFormat,
point_format: PointFormat,
endianness: Endianness,
) -> Result<Self, DeserializationError> {
if bytes.len() % 3 != 0 {
return Err(DeserializationError::InvalidAmountOfBytes);
}
match point_format {
PointFormat::Projective => {
if bytes.len() % 3 != 0 {
return Err(DeserializationError::InvalidAmountOfBytes);
}

let len = bytes.len() / 3;
let x: FieldElement<E::BaseField>;
let y: FieldElement<E::BaseField>;
let z: FieldElement<E::BaseField>;
let len = bytes.len() / 3;
let x: FieldElement<E::BaseField>;
let y: FieldElement<E::BaseField>;
let z: FieldElement<E::BaseField>;

if endianness == Endianness::BigEndian {
x = ByteConversion::from_bytes_be(&bytes[..len])?;
y = ByteConversion::from_bytes_be(&bytes[len..len * 2])?;
z = ByteConversion::from_bytes_be(&bytes[len * 2..])?;
} else {
x = ByteConversion::from_bytes_le(&bytes[..len])?;
y = ByteConversion::from_bytes_le(&bytes[len..len * 2])?;
z = ByteConversion::from_bytes_le(&bytes[len * 2..])?;
}
if endianness == Endianness::BigEndian {
x = ByteConversion::from_bytes_be(&bytes[..len])?;
y = ByteConversion::from_bytes_be(&bytes[len..len * 2])?;
z = ByteConversion::from_bytes_be(&bytes[len * 2..])?;
} else {
x = ByteConversion::from_bytes_le(&bytes[..len])?;
y = ByteConversion::from_bytes_le(&bytes[len..len * 2])?;
z = ByteConversion::from_bytes_le(&bytes[len * 2..])?;
}

if z == FieldElement::zero() {
let point = Self::new([x, y, z]);
if point.is_neutral_element() {
Ok(point)
} else {
Err(DeserializationError::FieldFromBytesError)
if z == FieldElement::zero() {
let point = Self::new([x, y, z]);
if point.is_neutral_element() {
Ok(point)
} else {
Err(DeserializationError::FieldFromBytesError)
}
} else if E::defining_equation(&(&x / &z), &(&y / &z)) == FieldElement::zero() {
Ok(Self::new([x, y, z]))
} else {
Err(DeserializationError::FieldFromBytesError)
}
}
PointFormat::Uncompressed => {
if bytes.len() % 2 != 0 {
return Err(DeserializationError::InvalidAmountOfBytes);
}

let len = bytes.len() / 2;
let x: FieldElement<E::BaseField>;
let y: FieldElement<E::BaseField>;

if endianness == Endianness::BigEndian {
x = ByteConversion::from_bytes_be(&bytes[..len])?;
y = ByteConversion::from_bytes_be(&bytes[len..])?;
} else {
x = ByteConversion::from_bytes_le(&bytes[..len])?;
y = ByteConversion::from_bytes_le(&bytes[len..])?;
}

if E::defining_equation(&x, &y) == FieldElement::zero() {
Ok(Self::new([x, y, FieldElement::one()]))
} else {
Err(DeserializationError::FieldFromBytesError)
}
}
} else if E::defining_equation(&(&x / &z), &(&y / &z)) == FieldElement::zero() {
Ok(Self::new([x, y, z]))
} else {
Err(DeserializationError::FieldFromBytesError)
}
}
}
Expand Down Expand Up @@ -347,7 +388,7 @@ mod tests {

#[cfg(feature = "std")]
#[test]
fn byte_conversion_from_and_to_be() {
fn byte_conversion_from_and_to_be_projective() {
let expected_point = point();
let bytes_be = expected_point.serialize(PointFormat::Projective, Endianness::BigEndian);

Expand All @@ -361,7 +402,20 @@ mod tests {

#[cfg(feature = "std")]
#[test]
fn byte_conversion_from_and_to_le() {
fn byte_conversion_from_and_to_be_uncompressed() {
let expected_point = point();
let bytes_be = expected_point.serialize(PointFormat::Uncompressed, Endianness::BigEndian);
let result = ShortWeierstrassProjectivePoint::deserialize(
&bytes_be,
PointFormat::Uncompressed,
Endianness::BigEndian,
);
assert_eq!(expected_point, result.unwrap());
}

#[cfg(feature = "std")]
#[test]
fn byte_conversion_from_and_to_le_projective() {
let expected_point = point();
let bytes_be = expected_point.serialize(PointFormat::Projective, Endianness::LittleEndian);

Expand All @@ -375,7 +429,22 @@ mod tests {

#[cfg(feature = "std")]
#[test]
fn byte_conversion_from_and_to_with_mixed_le_and_be_does_not_work() {
fn byte_conversion_from_and_to_le_uncompressed() {
let expected_point = point();
let bytes_be =
expected_point.serialize(PointFormat::Uncompressed, Endianness::LittleEndian);

let result = ShortWeierstrassProjectivePoint::deserialize(
&bytes_be,
PointFormat::Uncompressed,
Endianness::LittleEndian,
);
assert_eq!(expected_point, result.unwrap());
}

#[cfg(feature = "std")]
#[test]
fn byte_conversion_from_and_to_with_mixed_le_and_be_does_not_work_projective() {
let bytes = point().serialize(PointFormat::Projective, Endianness::LittleEndian);

let result = ShortWeierstrassProjectivePoint::<BLS12381Curve>::deserialize(
Expand All @@ -392,7 +461,24 @@ mod tests {

#[cfg(feature = "std")]
#[test]
fn byte_conversion_from_and_to_with_mixed_be_and_le_does_not_work() {
fn byte_conversion_from_and_to_with_mixed_le_and_be_does_not_work_uncompressed() {
let bytes = point().serialize(PointFormat::Uncompressed, Endianness::LittleEndian);

let result = ShortWeierstrassProjectivePoint::<BLS12381Curve>::deserialize(
&bytes,
PointFormat::Uncompressed,
Endianness::BigEndian,
);

assert_eq!(
result.unwrap_err(),
DeserializationError::FieldFromBytesError
);
}

#[cfg(feature = "std")]
#[test]
fn byte_conversion_from_and_to_with_mixed_be_and_le_does_not_work_projective() {
let bytes = point().serialize(PointFormat::Projective, Endianness::BigEndian);

let result = ShortWeierstrassProjectivePoint::<BLS12381Curve>::deserialize(
Expand All @@ -407,8 +493,25 @@ mod tests {
);
}

#[cfg(feature = "std")]
#[test]
fn byte_conversion_from_and_to_with_mixed_be_and_le_does_not_work_uncompressed() {
let bytes = point().serialize(PointFormat::Uncompressed, Endianness::BigEndian);

let result = ShortWeierstrassProjectivePoint::<BLS12381Curve>::deserialize(
&bytes,
PointFormat::Uncompressed,
Endianness::LittleEndian,
);

assert_eq!(
result.unwrap_err(),
DeserializationError::FieldFromBytesError
);
}

#[test]
fn cannot_create_point_from_wrong_number_of_bytes_le() {
fn cannot_create_point_from_wrong_number_of_bytes_le_projective() {
let bytes = &[0_u8; 13];

let result = ShortWeierstrassProjectivePoint::<BLS12381Curve>::deserialize(
Expand All @@ -424,7 +527,23 @@ mod tests {
}

#[test]
fn cannot_create_point_from_wrong_number_of_bytes_be() {
fn cannot_create_point_from_wrong_number_of_bytes_le_uncompressed() {
let bytes = &[0_u8; 13];

let result = ShortWeierstrassProjectivePoint::<BLS12381Curve>::deserialize(
bytes,
PointFormat::Uncompressed,
Endianness::LittleEndian,
);

assert_eq!(
result.unwrap_err(),
DeserializationError::InvalidAmountOfBytes
);
}

#[test]
fn cannot_create_point_from_wrong_number_of_bytes_be_projective() {
let bytes = &[0_u8; 13];

let result = ShortWeierstrassProjectivePoint::<BLS12381Curve>::deserialize(
Expand All @@ -438,4 +557,20 @@ mod tests {
DeserializationError::InvalidAmountOfBytes
);
}

#[test]
fn cannot_create_point_from_wrong_number_of_bytes_be_uncompressed() {
let bytes = &[0_u8; 13];

let result = ShortWeierstrassProjectivePoint::<BLS12381Curve>::deserialize(
bytes,
PointFormat::Uncompressed,
Endianness::BigEndian,
);

assert_eq!(
result.unwrap_err(),
DeserializationError::InvalidAmountOfBytes
);
}
}
Loading