Merge rust-bitcoin/rust-secp256k1#418: Implement de/serialization for SharedSecret
de65fb2f1e
Implement de/serialization for SharedSecret (Tobin Harding) Pull request description: As we do for other keys implement serde de/serialization for the `SharedSecret`. Please note, this adds `from_slice` and `from_bytes` (borrowed and owner respectively) because I needed to use them. Doing so treads on @dspicher's toes because he is in the process of implementing an owned conversion method for `SharedSecret`. The fair thing to do would be let https://github.com/rust-bitcoin/rust-secp256k1/pull/417 resolve and merge before merging this one (I can rebase). ~Side note, its kind of rubbish that `BytesVisitor` deserializes into a buffer (presumably) then we reallocate and copy the buffer to use the borrowed conversion method due to the serde function signature `fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<Self::Value, E>`~ (I was bumbling nonsense.) Closes: #416 ACKs for top commit: apoelstra: ACKde65fb2f1e
Tree-SHA512: 3d484f160d8a459a867f645736713984bad982429236ac5351c20b6c21b42cec86e68009fe7adf062912037cf7e747e5b15357a5fd7900e52169f208a4e56710
This commit is contained in:
commit
49905b0d44
85
src/ecdh.rs
85
src/ecdh.rs
|
@ -15,9 +15,10 @@
|
||||||
//! Support for shared secret computations.
|
//! Support for shared secret computations.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use core::ptr;
|
use core::{ptr, str};
|
||||||
use core::borrow::Borrow;
|
use core::borrow::Borrow;
|
||||||
|
|
||||||
|
use {Error, from_hex};
|
||||||
use key::{SecretKey, PublicKey};
|
use key::{SecretKey, PublicKey};
|
||||||
use ffi::{self, CPtr};
|
use ffi::{self, CPtr};
|
||||||
use secp256k1_sys::types::{c_int, c_uchar, c_void};
|
use secp256k1_sys::types::{c_int, c_uchar, c_void};
|
||||||
|
@ -72,10 +73,35 @@ impl SharedSecret {
|
||||||
self.0
|
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 {
|
pub fn from_bytes(bytes: [u8; SHARED_SECRET_SIZE]) -> SharedSecret {
|
||||||
SharedSecret(bytes)
|
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 {
|
impl Borrow<[u8]> for SharedSecret {
|
||||||
|
@ -145,6 +171,36 @@ unsafe extern "C" fn c_callback(output: *mut c_uchar, x: *const c_uchar, y: *con
|
||||||
1
|
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)]
|
#[cfg(test)]
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
@ -207,6 +263,31 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(secret_bh.as_inner(), secret_sys.as_ref());
|
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"))]
|
#[cfg(all(test, feature = "unstable"))]
|
||||||
|
|
|
@ -352,6 +352,8 @@ pub enum Error {
|
||||||
InvalidSignature,
|
InvalidSignature,
|
||||||
/// Bad secret key.
|
/// Bad secret key.
|
||||||
InvalidSecretKey,
|
InvalidSecretKey,
|
||||||
|
/// Bad shared secret.
|
||||||
|
InvalidSharedSecret,
|
||||||
/// Bad recovery id.
|
/// Bad recovery id.
|
||||||
InvalidRecoveryId,
|
InvalidRecoveryId,
|
||||||
/// Invalid tweak for `add_*_assign` or `mul_*_assign`.
|
/// Invalid tweak for `add_*_assign` or `mul_*_assign`.
|
||||||
|
@ -372,6 +374,7 @@ impl Error {
|
||||||
Error::InvalidPublicKey => "secp: malformed public key",
|
Error::InvalidPublicKey => "secp: malformed public key",
|
||||||
Error::InvalidSignature => "secp: malformed signature",
|
Error::InvalidSignature => "secp: malformed signature",
|
||||||
Error::InvalidSecretKey => "secp: malformed or out-of-range secret key",
|
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::InvalidRecoveryId => "secp: bad recovery id",
|
||||||
Error::InvalidTweak => "secp: bad tweak",
|
Error::InvalidTweak => "secp: bad tweak",
|
||||||
Error::NotEnoughMemory => "secp: not enough memory allocated",
|
Error::NotEnoughMemory => "secp: not enough memory allocated",
|
||||||
|
@ -399,6 +402,7 @@ impl std::error::Error for Error {
|
||||||
Error::InvalidPublicKey => None,
|
Error::InvalidPublicKey => None,
|
||||||
Error::InvalidSignature => None,
|
Error::InvalidSignature => None,
|
||||||
Error::InvalidSecretKey => None,
|
Error::InvalidSecretKey => None,
|
||||||
|
Error::InvalidSharedSecret => None,
|
||||||
Error::InvalidRecoveryId => None,
|
Error::InvalidRecoveryId => None,
|
||||||
Error::InvalidTweak => None,
|
Error::InvalidTweak => None,
|
||||||
Error::NotEnoughMemory => None,
|
Error::NotEnoughMemory => None,
|
||||||
|
|
Loading…
Reference in New Issue