From 2478930ec1b6c40f49b9d3113da43ca0bae6b681 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 25 Jul 2018 13:57:36 +0000 Subject: [PATCH] add feature-gated serde 1.0 support for Signature, SecretKey, PublicKey --- .travis.yml | 1 + Cargo.toml | 5 ++++ src/key.rs | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 42 ++++++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+) diff --git a/.travis.yml b/.travis.yml index b8c6705..b0322f4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ script: - cargo build --verbose --features=fuzztarget - cargo build --verbose --features=rand - cargo test --verbose --features=rand + - cargo test --verbose --features="rand serde" - cargo build --verbose - cargo test --verbose - cargo build --release diff --git a/Cargo.toml b/Cargo.toml index 36b3929..ea0ce7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ fuzztarget = [] [dev-dependencies] rand = "0.3" +serde_test = "1.0" [dependencies] libc = "0.2" @@ -34,3 +35,7 @@ libc = "0.2" [dependencies.rand] version = "0.3" optional = true + +[dependencies.serde] +version = "1.0" +optional = true diff --git a/src/key.rs b/src/key.rs index 0aec673..4810e05 100644 --- a/src/key.rs +++ b/src/key.rs @@ -121,6 +121,31 @@ impl SecretKey { } } +#[cfg(feature = "serde")] +impl ::serde::Serialize for SecretKey { + fn serialize(&self, s: S) -> Result { + s.serialize_bytes(&self.0) + } +} + +#[cfg(feature = "serde")] +impl<'de> ::serde::Deserialize<'de> for SecretKey { + fn deserialize>(d: D) -> Result { + use ::serde::de::Error; + + // serde can actually deserialize a 32-byte array directly rather than deserializing + // a byte slice and copying, but it has special code for byte-slices and no special + // code for byte-arrays, meaning this is actually simpler and more efficient + let mut arr = [0; 32]; + let sl: &[u8] = ::serde::Deserialize::deserialize(d)?; + if sl.len() != constants::SECRET_KEY_SIZE { + return Err(D::Error::invalid_length(sl.len(), &"32")); + } + arr.copy_from_slice(sl); + Ok(SecretKey(arr)) + } +} + impl PublicKey { /// Obtains a raw pointer suitable for use with FFI functions #[inline] @@ -254,6 +279,24 @@ impl From for PublicKey { } } +#[cfg(feature = "serde")] +impl ::serde::Serialize for PublicKey { + fn serialize(&self, s: S) -> Result { + s.serialize_bytes(&self.serialize()) + } +} + +#[cfg(feature = "serde")] +impl<'de> ::serde::Deserialize<'de> for PublicKey { + fn deserialize>(d: D) -> Result { + use ::serde::de::Error; + + let secp = Secp256k1::without_caps(); + let sl: &[u8] = ::serde::Deserialize::deserialize(d)?; + PublicKey::from_slice(&secp, sl).map_err(D::Error::custom) + } +} + #[cfg(test)] mod test { use super::super::{Secp256k1}; @@ -529,6 +572,33 @@ mod test { assert!(pk3 <= pk1); assert!(pk1 >= pk3); } + + #[cfg(feature = "serde")] + #[test] + fn test_signature_serde() { + use serde_test::{Token, assert_tokens}; + static SK_BYTES: [u8; 32] = [ + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 2, 3, 4, 5, 6, 7, + 0xff, 0xff, 0, 0, 0xff, 0xff, 0, 0, + 99, 99, 99, 99, 99, 99, 99, 99 + ]; + static PK_BYTES: [u8; 33] = [ + 0x02, + 0x18, 0x84, 0x57, 0x81, 0xf6, 0x31, 0xc4, 0x8f, + 0x1c, 0x97, 0x09, 0xe2, 0x30, 0x92, 0x06, 0x7d, + 0x06, 0x83, 0x7f, 0x30, 0xaa, 0x0c, 0xd0, 0x54, + 0x4a, 0xc8, 0x87, 0xfe, 0x91, 0xdd, 0xd1, 0x66, + ]; + + let s = Secp256k1::new(); + + let sk = SecretKey::from_slice(&s, &SK_BYTES).unwrap(); + let pk = PublicKey::from_secret_key(&s, &sk); + + assert_tokens(&sk, &[Token::BorrowedBytes(&SK_BYTES[..])]); + assert_tokens(&pk, &[Token::BorrowedBytes(&PK_BYTES[..])]); + } } diff --git a/src/lib.rs b/src/lib.rs index 7181635..c4bb8b1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,6 +39,8 @@ #![cfg_attr(all(test, feature = "unstable"), feature(test))] #[cfg(all(test, feature = "unstable"))] extern crate test; #[cfg(any(test, feature = "rand"))] extern crate rand; +#[cfg(feature = "serde")] extern crate serde; +#[cfg(all(test, feature = "serde"))] extern crate serde_test; extern crate libc; @@ -305,6 +307,25 @@ impl ops::Index for Signature { } } +#[cfg(feature = "serde")] +impl ::serde::Serialize for Signature { + fn serialize(&self, s: S) -> Result { + let secp = Secp256k1::without_caps(); + s.serialize_bytes(&self.serialize_der(&secp)) + } +} + +#[cfg(feature = "serde")] +impl<'de> ::serde::Deserialize<'de> for Signature { + fn deserialize>(d: D) -> Result { + use ::serde::de::Error; + + let secp = Secp256k1::without_caps(); + let sl: &[u8] = ::serde::Deserialize::deserialize(d)?; + Signature::from_der(&secp, sl).map_err(D::Error::custom) + } +} + /// A (hashed) message input to an ECDSA signature pub struct Message([u8; constants::MESSAGE_SIZE]); impl_array_newtype!(Message, u8, constants::MESSAGE_SIZE); @@ -903,6 +924,27 @@ mod tests { sig.normalize_s(&secp); assert_eq!(secp.verify(&msg, &sig, &pk), Ok(())); } + + #[cfg(feature = "serde")] + #[test] + fn test_signature_serde() { + use serde_test::{Token, assert_tokens}; + + let s = Secp256k1::new(); + + let msg = Message::from_slice(&[1; 32]).unwrap(); + let sk = SecretKey::from_slice(&s, &[2; 32]).unwrap(); + let sig = s.sign(&msg, &sk); + static SIG_BYTES: [u8; 71] = [ + 48, 69, 2, 33, 0, 157, 11, 173, 87, 103, 25, 211, 42, 231, 107, 237, + 179, 76, 119, 72, 102, 103, 60, 189, 227, 244, 225, 41, 81, 85, 92, 148, + 8, 230, 206, 119, 75, 2, 32, 40, 118, 231, 16, 47, 32, 79, 107, 254, + 226, 108, 150, 124, 57, 38, 206, 112, 44, 249, 125, 75, 1, 0, 98, 225, + 147, 247, 99, 25, 15, 103, 118 + ]; + + assert_tokens(&sig, &[Token::BorrowedBytes(&SIG_BYTES[..])]); + } } #[cfg(all(test, feature = "unstable"))]