Skip to content

Commit

Permalink
Add custom serde for all structs (#75)
Browse files Browse the repository at this point in the history
* add custom serde for scalar point and field

* use while let instead of loop with single match branch

* impl custom serde with tests for ecdsa signature and public key types

* add custom serde for schnorr sig

* remove unnecessary context from ecsda signature, since it costs very little to create and having it there causes problems

* remove hardwired std::fmt

* add Display for structs and errors that were missing it

* inc major version for release
  • Loading branch information
xoloki authored Nov 28, 2023
1 parent b489fab commit 32ba1c9
Show file tree
Hide file tree
Showing 7 changed files with 535 additions and 39 deletions.
3 changes: 2 additions & 1 deletion p256k1/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "p256k1"
version = "5.5.0"
version = "6.0.0"
edition = "2021"
authors = ["Joey Yandle <[email protected]>"]
license = "Apache-2.0"
Expand Down Expand Up @@ -40,6 +40,7 @@ syn = { version = "2.0.10", features = ["full"] }
[dev-dependencies]
libc = "0.2"
criterion = "0.4.0"
serde_json = "1.0"

[[bench]]
name = "point_bench"
Expand Down
111 changes: 95 additions & 16 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,8 +25,8 @@ pub enum Error {
/// Error converting a scalar
Conversion(ConversionError),
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "{:?}", self)
}
}
Expand All @@ -36,23 +40,22 @@ impl From<TryFromSliceError> for Error {
/**
Signature is a wrapper around libsecp256k1's secp256k1_ecdsa_signature struct.
*/
#[derive(Debug, Clone)]
pub struct Signature {
/// The wrapped libsecp256k1 signature
pub signature: secp256k1_ecdsa_signature,
/// The context associated with the signature
pub context: Context,
}

impl Signature {
/// Construct an ECDSA signature
pub fn new(hash: &[u8], sec_key: &Scalar) -> Result<Self, Error> {
let context = Context::default();
let mut sig = Self {
signature: secp256k1_ecdsa_signature { data: [0; 64] },
context: Context::default(),
};
if unsafe {
secp256k1_ecdsa_sign(
sig.context.context,
context.context,
&mut sig.signature,
hash.as_ptr(),
sec_key.to_bytes().as_ptr(),
Expand All @@ -68,9 +71,11 @@ impl Signature {

/// Verify an ECDSA signature
pub fn verify(&self, hash: &[u8], pub_key: &PublicKey) -> bool {
let context = Context::default();

1 == unsafe {
secp256k1_ecdsa_verify(
self.context.context,
context.context,
&self.signature,
hash.as_ptr(),
&pub_key.key,
Expand All @@ -80,11 +85,12 @@ impl Signature {

/// Returns the signature's deserialized underlying data
pub fn to_bytes(&self) -> [u8; 64] {
let context = Context::default();
let mut bytes = [0u8; 64];
//Deserialize the signature's data
unsafe {
secp256k1_ecdsa_signature_serialize_compact(
self.context.context,
context.context,
bytes.as_mut_ptr(),
&self.signature,
);
Expand All @@ -93,6 +99,63 @@ impl Signature {
}
}

impl Display for Signature {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "{}", bs58::encode(self.to_bytes()).into_string())
}
}

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")
}

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))),
}
}

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 All @@ -108,14 +171,14 @@ impl TryFrom<[u8; 64]> for Signature {
/// Create an ECDSA signature given an array of signed data.
/// Note it also serializes the data in compact (64 byte) format
fn try_from(input: [u8; 64]) -> Result<Self, Self::Error> {
let context = Context::default();
let mut sig = Self {
signature: secp256k1_ecdsa_signature { data: [0u8; 64] },
context: Context::default(),
};
//Attempt to serialize the data into the signature
let parsed = unsafe {
secp256k1_ecdsa_signature_parse_compact(
sig.context.context,
context.context,
&mut sig.signature,
input.as_ptr(),
)
Expand Down Expand Up @@ -196,7 +259,6 @@ mod tests {

let sig_from_struct = Signature {
signature: secp256k1_ecdsa_signature { data: bytes },
context: Context::default(),
};
let sig_from_slice = Signature::try_from(bytes.as_slice()).unwrap();
let sig_from_array = Signature::try_from(bytes).unwrap();
Expand All @@ -215,7 +277,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 +288,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));
}
}
74 changes: 73 additions & 1 deletion p256k1/src/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ use core::{
};
use num_traits::{One, Zero};
use rand_core::{CryptoRng, RngCore};
use serde::{
de::{self, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};

use crate::_rename::{
secp256k1_fe_add, secp256k1_fe_cmp_var, secp256k1_fe_get_b32, secp256k1_fe_inv,
Expand All @@ -34,7 +38,13 @@ pub enum Error {
Conversion(ConversionError),
}

#[derive(Copy, Clone, Debug, serde::Serialize, serde::Deserialize)]
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "{:?}", self)
}
}

#[derive(Copy, Clone, Debug)]
/**
Element is a wrapper around libsecp256k1's internal secp256k1_fe struct. It provides a field element, which is like a scalar but not necessarily reduced modulo the group order
*/
Expand Down Expand Up @@ -160,6 +170,58 @@ impl PartialEq for Element {

impl Eq for Element {}

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

struct ElementVisitor;

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 field element for the secp256k1 curve")
}

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

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 Element {
fn deserialize<D>(deserializer: D) -> Result<Element, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_bytes(ElementVisitor)
}
}

impl Hash for Element {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write(&self.to_bytes()[..]);
Expand Down Expand Up @@ -608,4 +670,14 @@ mod tests {
assert_eq!(a, c);
assert_eq!(s, t);
}

#[test]
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");
let y = serde_json::from_str(&s).expect("failed to deserialize");

assert_eq!(x, y);
}
}
Loading

0 comments on commit 32ba1c9

Please sign in to comment.