This commit is contained in:
François Garillot 2023-12-20 11:16:42 -07:00 committed by GitHub
commit e77aaa2677
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 294 additions and 2 deletions

View File

@ -62,6 +62,7 @@ ec-gpu = { version = "0.2.0", optional = true }
# serde dependencies
serde_crate = { version = "1.0.16", optional = true, default-features = false, features = ["alloc"], package = "serde" }
hex = { version = "0.4", optional = true, default-features = false, features = ["alloc", "serde"] }
paste = "1.0.12"
[features]
default = ["bits", "sqrt-table"]

View File

@ -269,6 +269,114 @@ macro_rules! new_curve_impl {
}
}
paste::paste! {
/// Uncompressed encoding of the affine representation of a point on the elliptic curve $name.
#[derive(Copy, Clone)]
pub struct [< $name Uncompressed >](pub(crate) [u8; 64]);
impl fmt::Debug for [< $name Uncompressed >] {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0[..].fmt(f)
}
}
impl Default for [< $name Uncompressed >] {
fn default() -> Self {
[< $name Uncompressed >]([0; 64])
}
}
impl AsRef<[u8]> for [< $name Uncompressed >] {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsMut<[u8]> for [< $name Uncompressed >] {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
impl ConstantTimeEq for [< $name Uncompressed >] {
fn ct_eq(&self, other: &Self) -> Choice {
self.0.ct_eq(&other.0)
}
}
impl cmp::Eq for [< $name Uncompressed >] {}
impl PartialEq for [< $name Uncompressed >] {
#[inline]
fn eq(&self, other: &Self) -> bool {
bool::from(self.ct_eq(other))
}
}
impl group::UncompressedEncoding for $name_affine{
type Uncompressed = [< $name Uncompressed >];
fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption<Self> {
Self::from_uncompressed_unchecked(bytes).and_then(|p| CtOption::new(p, p.is_on_curve()))
}
fn from_uncompressed_unchecked(bytes: &Self::Uncompressed) -> CtOption<Self> {
let bytes = &bytes.0;
let infinity_flag_set = Choice::from((bytes[64 - 1] >> 6) & 1);
// Attempt to obtain the x-coordinate
let x = {
let mut tmp = [0; 32];
tmp.copy_from_slice(&bytes[0..32]);
$base::from_repr(tmp)
};
// Attempt to obtain the y-coordinate
let y = {
let mut tmp = [0; 32];
tmp.copy_from_slice(&bytes[32..2*32]);
$base::from_repr(tmp)
};
x.and_then(|x| {
y.and_then(|y| {
// Create a point representing this value
let p = $name_affine::conditional_select(
&$name_affine{
x,
y,
},
&$name_affine::identity(),
infinity_flag_set,
);
CtOption::new(
p,
// If the infinity flag is set, the x and y coordinates should have been zero.
((!infinity_flag_set) | (x.is_zero() & y.is_zero()))
)
})
})
}
fn to_uncompressed(&self) -> Self::Uncompressed {
let mut res = [0; 64];
res[0..32].copy_from_slice(
&$base::conditional_select(&self.x, &$base::zero(), self.is_identity()).to_repr()[..],
);
res[32.. 2*32].copy_from_slice(
&$base::conditional_select(&self.y, &$base::zero(), self.is_identity()).to_repr()[..],
);
res[64 - 1] |= u8::conditional_select(&0u8, &(1u8 << 6), self.is_identity());
[< $name Uncompressed >](res)
}
}
}
impl<'a> From<&'a $name_affine> for $name {
fn from(p: &'a $name_affine) -> $name {
p.to_curve()

View File

@ -1,13 +1,17 @@
use core::fmt;
use ff::PrimeField;
use group::GroupEncoding;
use serde_crate::{
de::Error as DeserializeError, Deserialize, Deserializer, Serialize, Serializer,
de::{Error as DeserializeError, SeqAccess, Visitor},
ser::SerializeTuple,
Deserialize, Deserializer, Serialize, Serializer,
};
use crate::{
curves::{Ep, EpAffine, Eq, EqAffine},
fields::{Fp, Fq},
group::Curve,
EpUncompressed, EqUncompressed,
};
/// Serializes bytes to human readable or compact representation.
@ -130,6 +134,83 @@ impl<'de> Deserialize<'de> for Eq {
}
}
struct ByteArrayVisitor {}
impl<'de> Visitor<'de> for ByteArrayVisitor {
type Value = [u8; 64];
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str(concat!("an array of length ", 64))
}
fn visit_seq<A>(self, mut seq: A) -> Result<[u8; 64], A::Error>
where
A: SeqAccess<'de>,
{
let mut arr = [u8::default(); 64];
for i in 0..64 {
arr[i] = seq
.next_element()?
.ok_or_else(|| DeserializeError::invalid_length(i, &self))?;
}
Ok(arr)
}
}
impl Serialize for EpUncompressed {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
if s.is_human_readable() {
hex::serde::serialize(self.0, s)
} else {
let mut seq = s.serialize_tuple(64)?;
for elem in self.0 {
seq.serialize_element(&elem)?;
}
seq.end()
}
}
}
impl<'de> Deserialize<'de> for EpUncompressed {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
let array = if d.is_human_readable() {
hex::serde::deserialize(d)?
} else {
let visitor = ByteArrayVisitor {};
d.deserialize_tuple(64, visitor)?
};
Ok(Self(array))
}
}
impl Serialize for EqUncompressed {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
if s.is_human_readable() {
hex::serde::serialize(self.0, s)
} else {
let mut seq = s.serialize_tuple(64)?;
for elem in self.0 {
seq.serialize_element(&elem)?;
}
seq.end()
}
}
}
impl<'de> Deserialize<'de> for EqUncompressed {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
let array = if d.is_human_readable() {
hex::serde::deserialize(d)?
} else {
let visitor = ByteArrayVisitor {};
d.deserialize_tuple(64, visitor)?
};
Ok(Self(array))
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -137,7 +218,7 @@ mod tests {
use core::fmt::Debug;
use ff::Field;
use group::{prime::PrimeCurveAffine, Curve, Group};
use group::{prime::PrimeCurveAffine, Curve, Group, UncompressedEncoding};
use rand::SeedableRng;
use rand_xorshift::XorShiftRng;
@ -444,4 +525,106 @@ mod tests {
f
);
}
#[test]
fn serde_ep_uncompressed() {
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..100 {
let f = Ep::random(&mut rng).to_affine().to_uncompressed();
test_roundtrip(&f);
}
let f = Ep::identity().to_affine().to_uncompressed();
test_roundtrip(&f);
assert_eq!(
serde_json::from_slice::<EpUncompressed>(
br#""00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040""#
)
.unwrap(),
f
);
assert_eq!(
bincode::deserialize::<EpUncompressed>(&[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 64
])
.unwrap(),
f
);
let f = Ep::generator().to_affine().to_uncompressed();
test_roundtrip(&f);
assert_eq!(
serde_json::from_slice::<EpUncompressed>(
br#""00000000ed302d991bf94c09fc984622000000000000000000000000000000400200000000000000000000000000000000000000000000000000000000000000""#
)
.unwrap(),
f
);
assert_eq!(
bincode::deserialize::<EpUncompressed>(&[
0, 0, 0, 0, 237, 48, 45, 153, 27, 249, 76, 9, 252, 152, 70, 34, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
])
.unwrap(),
f
);
}
#[test]
fn serde_eq_uncompressed() {
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..100 {
let f = Eq::random(&mut rng).to_affine().to_uncompressed();
test_roundtrip(&f);
}
let f = Eq::identity().to_affine().to_uncompressed();
test_roundtrip(&f);
assert_eq!(
serde_json::from_slice::<EqUncompressed>(
br#""00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040""#
)
.unwrap(),
f
);
assert_eq!(
bincode::deserialize::<EqUncompressed>(&[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 64
])
.unwrap(),
f
);
let f = Eq::generator().to_affine().to_uncompressed();
test_roundtrip(&f);
assert_eq!(
serde_json::from_slice::<EqUncompressed>(
br#""0000000021eb468cdda89409fc984622000000000000000000000000000000400200000000000000000000000000000000000000000000000000000000000000""#
)
.unwrap(),
f
);
assert_eq!(
bincode::deserialize::<EqUncompressed>(&[
0, 0, 0, 0, 33, 235, 70, 140, 221, 168, 148, 9, 252, 152, 70, 34, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
])
.unwrap(),
f
);
}
}