Skip to content

Commit

Permalink
impl custom serde with tests for ecdsa signature and public key types
Browse files Browse the repository at this point in the history
  • Loading branch information
xoloki committed Nov 21, 2023
1 parent 615bb44 commit b46e55b
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 18 deletions.
84 changes: 78 additions & 6 deletions p256k1/src/ecdsa.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
use core::fmt::{Debug, Display, Formatter, Result as FmtResult};
use serde::{
de::{self, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
use std::array::TryFromSliceError;

use crate::_rename::{
secp256k1_ecdsa_sign, secp256k1_ecdsa_signature_parse_compact,
secp256k1_ecdsa_signature_serialize_compact, secp256k1_ecdsa_verify,
};
use crate::bindings::secp256k1_ecdsa_signature;
use crate::context::Context;
use crate::errors::ConversionError;
use crate::scalar::Scalar;
use crate::{
bindings::secp256k1_ecdsa_signature, context::Context, errors::ConversionError, scalar::Scalar,
};

pub use crate::keys::{Error as KeyError, PublicKey};

Expand All @@ -21,7 +25,7 @@ pub enum Error {
/// Error converting a scalar
Conversion(ConversionError),
}
impl std::fmt::Display for Error {
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
Expand Down Expand Up @@ -93,6 +97,57 @@ impl Signature {
}
}

impl Serialize for Signature {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_bytes(&self.to_bytes())
}
}

struct SignatureVisitor;

impl<'de> Visitor<'de> for SignatureVisitor {
type Value = Signature;

fn expecting(&self, formatter: &mut Formatter) -> FmtResult {
formatter.write_str("an array of bytes which represents two scalars on the secp256k1 curve")

Check warning on line 115 in p256k1/src/ecdsa.rs

View check run for this annotation

Codecov / codecov/patch

p256k1/src/ecdsa.rs#L114-L115

Added lines #L114 - L115 were not covered by tests
}

fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
where
E: de::Error,
{
match Signature::try_from(value) {
Ok(s) => Ok(s),
Err(e) => Err(E::custom(format!("{:?}", e))),

Check warning on line 124 in p256k1/src/ecdsa.rs

View check run for this annotation

Codecov / codecov/patch

p256k1/src/ecdsa.rs#L124

Added line #L124 was not covered by tests
}
}

fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
let mut v = Vec::new();

while let Ok(Some(x)) = seq.next_element() {
v.push(x);
}

self.visit_bytes(&v)
}
}

impl<'de> Deserialize<'de> for Signature {
fn deserialize<D>(deserializer: D) -> Result<Signature, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_bytes(SignatureVisitor)
}
}

impl TryFrom<&[u8]> for Signature {
type Error = Error;
/// Create an ECDSA signature given a slice of signed data.
Expand Down Expand Up @@ -215,7 +270,7 @@ mod tests {
}

#[test]
fn signature_serde() {
fn manual_serde() {
// Generate random data bytes
let mut rng = OsRng::default();
let mut bytes = [0u8; 64];
Expand All @@ -226,4 +281,21 @@ mod tests {
assert_ne!(sig.signature.data, bytes);
assert_eq!(sig.to_bytes(), bytes);
}

#[test]
fn custom_serde() {
let mut rng = OsRng::default();
let mut hash = [0u8; 32];
rng.fill_bytes(&mut hash);
let private_key = Scalar::random(&mut rng);
let public_key = PublicKey::new(&private_key).expect("failed to create public key");
let sig = Signature::new(&hash, &private_key).expect("failed to create sig");

assert!(sig.verify(&hash, &public_key));

let ssig = serde_json::to_string(&sig).expect("failed to serialize");
let dsig: Signature = serde_json::from_str(&ssig).expect("failed to deserialize");

assert!(dsig.verify(&hash, &public_key));
}
}
5 changes: 3 additions & 2 deletions p256k1/src/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,8 @@ impl<'de> Visitor<'de> for ElementVisitor {
type Value = Element;

fn expecting(&self, formatter: &mut Formatter) -> FmtResult {
formatter.write_str("an array of bytes which represents a point on the secp256k1 curve")
formatter

Check warning on line 182 in p256k1/src/field.rs

View check run for this annotation

Codecov / codecov/patch

p256k1/src/field.rs#L181-L182

Added lines #L181 - L182 were not covered by tests
.write_str("an array of bytes which represents field element for the secp256k1 curve")
}

fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
Expand Down Expand Up @@ -665,7 +666,7 @@ mod tests {
}

#[test]
fn serde() {
fn custom_serde() {
let mut rng = OsRng::default();
let x = Element::random(&mut rng);
let s = serde_json::to_string(&x).expect("failed to serialize");
Expand Down
124 changes: 124 additions & 0 deletions p256k1/src/keys.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use bs58;
use core::fmt::{Debug, Display, Formatter, Result as FmtResult};
use serde::{
de::{self, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
use std::array::TryFromSliceError;

use crate::_rename::{
Expand Down Expand Up @@ -100,6 +104,57 @@ impl Display for PublicKey {
}
}

impl Serialize for PublicKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_bytes(&self.to_bytes())
}
}

struct PublicKeyVisitor;

impl<'de> Visitor<'de> for PublicKeyVisitor {
type Value = PublicKey;

fn expecting(&self, formatter: &mut Formatter) -> FmtResult {
formatter.write_str("an array of bytes which represents a ECDSA public key")

Check warning on line 122 in p256k1/src/keys.rs

View check run for this annotation

Codecov / codecov/patch

p256k1/src/keys.rs#L121-L122

Added lines #L121 - L122 were not covered by tests
}

fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
where
E: de::Error,
{
match PublicKey::try_from(value) {
Ok(s) => Ok(s),
Err(e) => Err(E::custom(format!("{:?}", e))),

Check warning on line 131 in p256k1/src/keys.rs

View check run for this annotation

Codecov / codecov/patch

p256k1/src/keys.rs#L131

Added line #L131 was not covered by tests
}
}

fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
let mut v = Vec::new();

while let Ok(Some(x)) = seq.next_element() {
v.push(x);
}

self.visit_bytes(&v)
}
}

impl<'de> Deserialize<'de> for PublicKey {
fn deserialize<D>(deserializer: D) -> Result<PublicKey, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_bytes(PublicKeyVisitor)
}
}

impl TryFrom<&str> for PublicKey {
type Error = Error;
/// Create a pubkey from the passed byte slice
Expand Down Expand Up @@ -180,6 +235,57 @@ impl Display for XOnlyPublicKey {
}
}

impl Serialize for XOnlyPublicKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_bytes(&self.to_bytes())
}
}

struct XOnlyPublicKeyVisitor;

impl<'de> Visitor<'de> for XOnlyPublicKeyVisitor {
type Value = XOnlyPublicKey;

fn expecting(&self, formatter: &mut Formatter) -> FmtResult {
formatter.write_str("an array of bytes which represents a ECDSA public key")

Check warning on line 253 in p256k1/src/keys.rs

View check run for this annotation

Codecov / codecov/patch

p256k1/src/keys.rs#L252-L253

Added lines #L252 - L253 were not covered by tests
}

fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
where
E: de::Error,
{
match XOnlyPublicKey::try_from(value) {
Ok(s) => Ok(s),
Err(e) => Err(E::custom(format!("{:?}", e))),

Check warning on line 262 in p256k1/src/keys.rs

View check run for this annotation

Codecov / codecov/patch

p256k1/src/keys.rs#L262

Added line #L262 was not covered by tests
}
}

fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
let mut v = Vec::new();

while let Ok(Some(x)) = seq.next_element() {
v.push(x);
}

self.visit_bytes(&v)
}
}

impl<'de> Deserialize<'de> for XOnlyPublicKey {
fn deserialize<D>(deserializer: D) -> Result<XOnlyPublicKey, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_bytes(XOnlyPublicKeyVisitor)
}
}

impl TryFrom<&str> for XOnlyPublicKey {
type Error = Error;
/// Create a pubkey from the passed byte slice
Expand Down Expand Up @@ -386,4 +492,22 @@ mod tests {
assert_eq!(pubkey.to_bytes(), pubkey2.to_bytes());
assert_eq!(pubkey.to_bytes(), pubkey3.to_bytes());
}

#[test]
fn custom_serde() {
let mut rng = OsRng::default();
let private_key = Scalar::random(&mut rng);
let public_key = PublicKey::new(&private_key).expect("failed to create public key");
let ser = serde_json::to_string(&public_key).expect("failed to serialize");
let deser: PublicKey = serde_json::from_str(&ser).expect("failed to deserialize");

assert_eq!(public_key.to_bytes(), deser.to_bytes());

let xonly_public_key =
XOnlyPublicKey::new(&private_key).expect("failed to create XOnlyPublicKey");
let xoser = serde_json::to_string(&xonly_public_key).expect("failed to serialize");
let xodeser: XOnlyPublicKey = serde_json::from_str(&xoser).expect("failed to deserialize");

assert_eq!(xonly_public_key.to_bytes(), xodeser.to_bytes());
}
}
15 changes: 7 additions & 8 deletions p256k1/src/point.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ use serde::{
};
use std::os::raw::c_void;

use crate::_rename::{
secp256k1_ecmult, secp256k1_ecmult_multi_var, secp256k1_fe_get_b32, secp256k1_fe_is_odd,
secp256k1_fe_normalize_var, secp256k1_fe_set_b32, secp256k1_ge_set_xo_var,
secp256k1_gej_add_var, secp256k1_gej_neg, secp256k1_gej_set_ge, secp256k1_scratch_space_create,
secp256k1_scratch_space_destroy,
};
use crate::{
bindings::{
secp256k1_callback, secp256k1_ecmult_multi_callback, secp256k1_fe, secp256k1_ge,
Expand All @@ -30,13 +36,6 @@ use crate::{
traits::MultiMult,
};

use crate::_rename::{
secp256k1_ecmult, secp256k1_ecmult_multi_var, secp256k1_fe_get_b32, secp256k1_fe_is_odd,
secp256k1_fe_normalize_var, secp256k1_fe_set_b32, secp256k1_ge_set_xo_var,
secp256k1_gej_add_var, secp256k1_gej_neg, secp256k1_gej_set_ge, secp256k1_scratch_space_create,
secp256k1_scratch_space_destroy,
};

/// The secp256k1 base point
pub const G: Point = Point {
gej: secp256k1_gej {
Expand Down Expand Up @@ -887,7 +886,7 @@ mod tests {
}

#[test]
fn serde() {
fn custom_serde() {
let mut rng = OsRng::default();
let p = Point::from(Scalar::random(&mut rng));
let s = serde_json::to_string(&p).expect("failed to serialize");
Expand Down
4 changes: 2 additions & 2 deletions p256k1/src/scalar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ impl<'de> Visitor<'de> for ScalarVisitor {
type Value = Scalar;

fn expecting(&self, formatter: &mut Formatter) -> FmtResult {
formatter.write_str("an array of bytes which represents a point on the secp256k1 curve")
formatter.write_str("an array of bytes which represents a scalar for the secp256k1 curve")

Check warning on line 191 in p256k1/src/scalar.rs

View check run for this annotation

Codecov / codecov/patch

p256k1/src/scalar.rs#L190-L191

Added lines #L190 - L191 were not covered by tests
}

fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
Expand Down Expand Up @@ -827,7 +827,7 @@ mod tests {
}

#[test]
fn serde() {
fn custom_serde() {
let mut rng = OsRng::default();
let x = Scalar::random(&mut rng);
let s = serde_json::to_string(&x).expect("failed to serialize");
Expand Down

0 comments on commit b46e55b

Please sign in to comment.