Skip to content
Open
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
108 changes: 108 additions & 0 deletions src/curves.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
187 changes: 185 additions & 2 deletions src/serde_impl.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -130,14 +134,91 @@ 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::*;

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;

Expand Down Expand Up @@ -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
);
}
}