Skip to content

Commit 4b8ce2a

Browse files
committed
add basic serde support for Scalar, G1, G2 with human readable encoding
1 parent 4df4518 commit 4b8ce2a

File tree

3 files changed

+205
-0
lines changed

3 files changed

+205
-0
lines changed

Cargo.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ hex = "0.4"
2222
rand_xorshift = "0.3"
2323
sha2 = "0.9"
2424
sha3 = "0.9"
25+
serde_json = "1.0.114"
2526

2627
[[bench]]
2728
name = "groups"
@@ -63,6 +64,19 @@ version = "1.4"
6364
default-features = false
6465
optional = true
6566

67+
[dependencies.serde]
68+
version = "1.0.197"
69+
default-features = false
70+
optional = true
71+
72+
[dependencies.serde-big-array]
73+
version = "0.5.1"
74+
optional = true
75+
76+
[dependencies.hex-conservative]
77+
version = "0.2.0"
78+
optional = true
79+
6680
[features]
6781
default = ["groups", "pairings", "alloc", "bits"]
6882
bits = ["ff/bits"]
@@ -71,3 +85,4 @@ pairings = ["groups", "pairing"]
7185
alloc = ["group/alloc"]
7286
experimental = ["digest"]
7387
nightly = ["subtle/nightly"]
88+
serde = ["dep:serde", "serde-big-array", "hex-conservative"]

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,6 @@ pub(crate) use digest::generic_array;
8888

8989
#[cfg(feature = "experimental")]
9090
pub mod hash_to_curve;
91+
92+
#[cfg(feature = "serde")]
93+
mod serde;

src/serde.rs

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
use alloc::string::String;
2+
3+
use group::Curve;
4+
use hex_conservative::{DisplayHex, FromHex};
5+
use serde::de::Error;
6+
use serde::{Deserialize, Deserializer, Serialize, Serializer};
7+
use serde_big_array::BigArray;
8+
9+
use crate::g1::{G1Affine, G1Projective};
10+
use crate::g2::{G2Affine, G2Projective};
11+
use crate::scalar::Scalar;
12+
13+
impl Serialize for Scalar {
14+
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
15+
where
16+
S: Serializer,
17+
{
18+
let byte_array = self.to_bytes();
19+
20+
if s.is_human_readable() {
21+
s.serialize_str(&DisplayHex::to_lower_hex_string(byte_array.as_slice()))
22+
} else {
23+
Serialize::serialize(&byte_array, s)
24+
}
25+
}
26+
}
27+
28+
impl<'d> Deserialize<'d> for Scalar {
29+
fn deserialize<D>(d: D) -> Result<Self, D::Error>
30+
where
31+
D: Deserializer<'d>,
32+
{
33+
let byte_array = if d.is_human_readable() {
34+
<[u8; 32] as FromHex>::from_hex(&<String>::deserialize(d)?)
35+
.map_err(serde::de::Error::custom)?
36+
} else {
37+
<[u8; 32] as Deserialize>::deserialize(d)?
38+
};
39+
40+
Option::from(Scalar::from_bytes(&byte_array))
41+
.ok_or(D::Error::custom("Could not decode scalar"))
42+
}
43+
}
44+
45+
impl Serialize for G1Affine {
46+
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
47+
where
48+
S: Serializer,
49+
{
50+
let byte_array = self.to_compressed();
51+
52+
if s.is_human_readable() {
53+
s.serialize_str(&DisplayHex::to_lower_hex_string(byte_array.as_slice()))
54+
} else {
55+
BigArray::serialize(&byte_array, s)
56+
}
57+
}
58+
}
59+
60+
impl<'d> Deserialize<'d> for G1Affine {
61+
fn deserialize<D>(d: D) -> Result<Self, D::Error>
62+
where
63+
D: Deserializer<'d>,
64+
{
65+
let byte_array = if d.is_human_readable() {
66+
<[u8; 48] as FromHex>::from_hex(&<String>::deserialize(d)?)
67+
.map_err(serde::de::Error::custom)?
68+
} else {
69+
<[u8; 48] as BigArray<u8>>::deserialize(d)?
70+
};
71+
72+
Option::from(G1Affine::from_compressed(&byte_array)).ok_or(D::Error::custom(
73+
"Could not decode compressed group element",
74+
))
75+
}
76+
}
77+
78+
impl Serialize for G2Affine {
79+
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
80+
where
81+
S: Serializer,
82+
{
83+
let byte_array = self.to_compressed();
84+
85+
if s.is_human_readable() {
86+
s.serialize_str(&DisplayHex::to_lower_hex_string(byte_array.as_slice()))
87+
} else {
88+
BigArray::serialize(&byte_array, s)
89+
}
90+
}
91+
}
92+
93+
impl<'d> Deserialize<'d> for G2Affine {
94+
fn deserialize<D>(d: D) -> Result<Self, D::Error>
95+
where
96+
D: Deserializer<'d>,
97+
{
98+
let byte_array = if d.is_human_readable() {
99+
<[u8; 96] as FromHex>::from_hex(&<String>::deserialize(d)?)
100+
.map_err(serde::de::Error::custom)?
101+
} else {
102+
<[u8; 96] as BigArray<u8>>::deserialize(d)?
103+
};
104+
105+
Option::from(G2Affine::from_compressed(&byte_array)).ok_or(D::Error::custom(
106+
"Could not decode compressed group element",
107+
))
108+
}
109+
}
110+
111+
impl Serialize for G1Projective {
112+
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
113+
where
114+
S: Serializer,
115+
{
116+
self.to_affine().serialize(s)
117+
}
118+
}
119+
120+
impl<'d> Deserialize<'d> for G1Projective {
121+
fn deserialize<D>(d: D) -> Result<Self, D::Error>
122+
where
123+
D: Deserializer<'d>,
124+
{
125+
Ok(G1Affine::deserialize(d)?.into())
126+
}
127+
}
128+
129+
impl Serialize for G2Projective {
130+
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
131+
where
132+
S: Serializer,
133+
{
134+
self.to_affine().serialize(s)
135+
}
136+
}
137+
138+
impl<'d> Deserialize<'d> for G2Projective {
139+
fn deserialize<D>(d: D) -> Result<Self, D::Error>
140+
where
141+
D: Deserializer<'d>,
142+
{
143+
Ok(G2Affine::deserialize(d)?.into())
144+
}
145+
}
146+
147+
#[test]
148+
fn serde_json_scalar_roundtrip() {
149+
let serialized = serde_json::to_string(&Scalar::zero()).unwrap();
150+
151+
assert_eq!(
152+
serialized,
153+
"\"0000000000000000000000000000000000000000000000000000000000000000\""
154+
);
155+
156+
let deserialized: Scalar = serde_json::from_str(&serialized).unwrap();
157+
158+
assert_eq!(deserialized, Scalar::zero());
159+
}
160+
161+
#[test]
162+
fn serde_json_g1_roundtrip() {
163+
let serialized = serde_json::to_string(&G1Affine::generator()).unwrap();
164+
165+
assert_eq!(
166+
serialized,
167+
"\"97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb\""
168+
);
169+
170+
let deserialized: G1Affine = serde_json::from_str(&serialized).unwrap();
171+
172+
assert_eq!(deserialized, G1Affine::generator());
173+
}
174+
175+
#[test]
176+
fn serde_json_g2_roundtrip() {
177+
let serialized = serde_json::to_string(&G2Affine::generator()).unwrap();
178+
179+
assert_eq!(
180+
serialized,
181+
"\"93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8\""
182+
);
183+
184+
let deserialized: G2Affine = serde_json::from_str(&serialized).unwrap();
185+
186+
assert_eq!(deserialized, G2Affine::generator());
187+
}

0 commit comments

Comments
 (0)