Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement de/serialization for SharedSecret #418

Merged
merged 1 commit into from Mar 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
85 changes: 83 additions & 2 deletions src/ecdh.rs
Expand Up @@ -15,9 +15,10 @@
//! Support for shared secret computations.
//!

use core::ptr;
use core::{ptr, str};
use core::borrow::Borrow;

use {Error, from_hex};
use key::{SecretKey, PublicKey};
use ffi::{self, CPtr};
use secp256k1_sys::types::{c_int, c_uchar, c_void};
Expand Down Expand Up @@ -72,10 +73,35 @@ impl SharedSecret {
self.0
}

/// Creates a shared secret from a byte serialization.
/// Creates a shared secret from `bytes` array.
#[inline]
pub fn from_bytes(bytes: [u8; SHARED_SECRET_SIZE]) -> SharedSecret {
SharedSecret(bytes)
}

/// Creates a shared secret from `bytes` slice.
#[inline]
pub fn from_slice(bytes: &[u8]) -> Result<SharedSecret, Error> {
match bytes.len() {
SHARED_SECRET_SIZE => {
let mut ret = [0u8; SHARED_SECRET_SIZE];
ret[..].copy_from_slice(bytes);
Ok(SharedSecret(ret))
}
_ => Err(Error::InvalidSharedSecret)
}
}
}

impl str::FromStr for SharedSecret {
type Err = Error;
fn from_str(s: &str) -> Result<SharedSecret, Error> {
let mut res = [0u8; SHARED_SECRET_SIZE];
match from_hex(s, &mut res) {
Ok(SHARED_SECRET_SIZE) => Ok(SharedSecret::from_bytes(res)),
_ => Err(Error::InvalidSharedSecret)
}
}
}

impl Borrow<[u8]> for SharedSecret {
Expand Down Expand Up @@ -145,6 +171,36 @@ unsafe extern "C" fn c_callback(output: *mut c_uchar, x: *const c_uchar, y: *con
1
}

#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl ::serde::Serialize for SharedSecret {
fn serialize<S: ::serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
if s.is_human_readable() {
let mut buf = [0u8; SHARED_SECRET_SIZE * 2];
s.serialize_str(::to_hex(&self.0, &mut buf).expect("fixed-size hex serialization"))
} else {
s.serialize_bytes(&self.as_ref()[..])
}
}
}

#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl<'de> ::serde::Deserialize<'de> for SharedSecret {
fn deserialize<D: ::serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
if d.is_human_readable() {
d.deserialize_str(super::serde_util::FromStrVisitor::new(
"a hex string representing 32 byte SharedSecret"
))
} else {
d.deserialize_bytes(super::serde_util::BytesVisitor::new(
"raw 32 bytes SharedSecret",
SharedSecret::from_slice
))
}
}
}

#[cfg(test)]
#[allow(unused_imports)]
mod tests {
Expand Down Expand Up @@ -207,6 +263,31 @@ mod tests {

assert_eq!(secret_bh.as_inner(), secret_sys.as_ref());
}

#[test]
#[cfg(all(feature = "serde", any(feature = "alloc", feature = "std")))]
fn serde() {
use serde_test::{Configure, Token, assert_tokens};
static 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 STR: &'static str = "\
01010101010101010001020304050607ffff0000ffff00006363636363636363\
";

let secret = SharedSecret::from_slice(&BYTES).unwrap();

assert_tokens(&secret.compact(), &[Token::BorrowedBytes(&BYTES[..])]);
assert_tokens(&secret.compact(), &[Token::Bytes(&BYTES)]);
assert_tokens(&secret.compact(), &[Token::ByteBuf(&BYTES)]);

assert_tokens(&secret.readable(), &[Token::BorrowedStr(STR)]);
assert_tokens(&secret.readable(), &[Token::Str(STR)]);
assert_tokens(&secret.readable(), &[Token::String(STR)]);
}
}

#[cfg(all(test, feature = "unstable"))]
Expand Down
4 changes: 4 additions & 0 deletions src/lib.rs
Expand Up @@ -352,6 +352,8 @@ pub enum Error {
InvalidSignature,
/// Bad secret key.
InvalidSecretKey,
/// Bad shared secret.
InvalidSharedSecret,
/// Bad recovery id.
InvalidRecoveryId,
/// Invalid tweak for `add_*_assign` or `mul_*_assign`.
Expand All @@ -372,6 +374,7 @@ impl Error {
Error::InvalidPublicKey => "secp: malformed public key",
Error::InvalidSignature => "secp: malformed signature",
Error::InvalidSecretKey => "secp: malformed or out-of-range secret key",
Error::InvalidSharedSecret => "secp: malformed or out-of-range shared secret",
Error::InvalidRecoveryId => "secp: bad recovery id",
Error::InvalidTweak => "secp: bad tweak",
Error::NotEnoughMemory => "secp: not enough memory allocated",
Expand Down Expand Up @@ -399,6 +402,7 @@ impl std::error::Error for Error {
Error::InvalidPublicKey => None,
Error::InvalidSignature => None,
Error::InvalidSecretKey => None,
Error::InvalidSharedSecret => None,
Error::InvalidRecoveryId => None,
Error::InvalidTweak => None,
Error::NotEnoughMemory => None,
Expand Down