Skip to content

Commit 40a7c43

Browse files
committed
Add serde impl for KeyPair
The impl is added as a module instead of being a direct implementation since it uses the global context and users should be aware that.
1 parent 24a9c9c commit 40a7c43

File tree

1 file changed

+97
-0
lines changed

1 file changed

+97
-0
lines changed

src/schnorrsig.rs

+97
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,18 @@ impl str::FromStr for Signature {
7676
}
7777

7878
/// Opaque data structure that holds a keypair consisting of a secret and a public key.
79+
///
80+
/// # Serde support
81+
/// [`Serialize`] and [`Deserialize`] are not implemented for this type, even with the `serde`
82+
/// feature active. This is due to security considerations, see the [`serde_keypair`] documentation
83+
/// for details.
84+
///
85+
/// If the `serde` and `global-context[-less-secure]` features are active `KeyPair`s can be serialized and
86+
/// deserialized by annotating them with `#[serde(with = "secp256k1::schnorrsig::serde_keypair")]`
87+
/// inside structs or enums for which [`Serialize`] and [`Deserialize`] are being derived.
88+
///
89+
/// [`Deserialize`]: serde::Deserialize
90+
/// [`Serialize`]: serde::Serialize
7991
#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)]
8092
pub struct KeyPair(ffi::KeyPair);
8193

@@ -596,6 +608,45 @@ impl<C: Signing> Secp256k1<C> {
596608
}
597609
}
598610

611+
/// Serde implementation for the [`KeyPair`] type.
612+
///
613+
/// Only the secret key part of the [`KeyPair`] is serialized using the [`SecretKey`] serde
614+
/// implementation, meaning the public key has to be regenerated on deserialization.
615+
///
616+
/// **Attention:** The deserialization algorithm uses the [global context] to generate the public key
617+
/// belonging to the secret key to form a [`KeyPair`]. The typical caveats regarding use of the
618+
/// [global context] with secret data apply.
619+
///
620+
/// [`SecretKey`]: crate::SecretKey
621+
/// [global context]: crate::SECP256K1
622+
#[cfg(all(feature = "global-context-less-secure", feature = "serde"))]
623+
pub mod serde_keypair {
624+
use serde::{Deserialize, Deserializer, Serialize, Serializer};
625+
use schnorrsig::KeyPair;
626+
use SecretKey;
627+
628+
#[allow(missing_docs)]
629+
pub fn serialize<S>(key: &KeyPair, serializer: S) -> Result<S::Ok, S::Error>
630+
where
631+
S: Serializer,
632+
{
633+
SecretKey::from_keypair(key).serialize(serializer)
634+
}
635+
636+
#[allow(missing_docs)]
637+
pub fn deserialize<'de, D>(deserializer: D) -> Result<KeyPair, D::Error>
638+
where
639+
D: Deserializer<'de>,
640+
{
641+
let secret_key = SecretKey::deserialize(deserializer)?;
642+
643+
Ok(KeyPair::from_secret_key(
644+
&crate::SECP256K1,
645+
secret_key,
646+
))
647+
}
648+
}
649+
599650
#[cfg(test)]
600651
mod tests {
601652
use super::super::Error::InvalidPublicKey;
@@ -935,4 +986,50 @@ mod tests {
935986
assert_eq!(pk1.serialize()[..], kpk1.serialize()[1..]);
936987
assert_eq!(pk2.serialize()[..], kpk2.serialize()[1..]);
937988
}
989+
990+
#[test]
991+
#[cfg(all(feature = "global-context-less-secure", feature = "serde"))]
992+
fn test_serde_keypair() {
993+
use serde::{Deserialize, Deserializer, Serialize, Serializer};
994+
use serde_test::{Configure, Token, assert_tokens};
995+
use super::serde_keypair;
996+
997+
// Normally users would derive the serde traits, but we can't easily enable the serde macros
998+
// here, so they are implemented manually to be able to test the behaviour.
999+
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1000+
struct KeyPairWrapper(KeyPair);
1001+
1002+
impl<'de> Deserialize<'de> for KeyPairWrapper {
1003+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1004+
where D: Deserializer<'de> {
1005+
serde_keypair::deserialize(deserializer).map(KeyPairWrapper)
1006+
}
1007+
}
1008+
1009+
impl Serialize for KeyPairWrapper {
1010+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
1011+
serde_keypair::serialize(&self.0, serializer)
1012+
}
1013+
}
1014+
1015+
static SK_BYTES: [u8; 32] = [
1016+
1, 1, 1, 1, 1, 1, 1, 1,
1017+
0, 1, 2, 3, 4, 5, 6, 7,
1018+
0xff, 0xff, 0, 0, 0xff, 0xff, 0, 0,
1019+
99, 99, 99, 99, 99, 99, 99, 99
1020+
];
1021+
static SK_STR: &'static str = "\
1022+
01010101010101010001020304050607ffff0000ffff00006363636363636363\
1023+
";
1024+
1025+
let sk = KeyPairWrapper(KeyPair::from_seckey_slice(&crate::SECP256K1, &SK_BYTES).unwrap());
1026+
1027+
assert_tokens(&sk.compact(), &[Token::BorrowedBytes(&SK_BYTES[..])]);
1028+
assert_tokens(&sk.compact(), &[Token::Bytes(&SK_BYTES)]);
1029+
assert_tokens(&sk.compact(), &[Token::ByteBuf(&SK_BYTES)]);
1030+
1031+
assert_tokens(&sk.readable(), &[Token::BorrowedStr(SK_STR)]);
1032+
assert_tokens(&sk.readable(), &[Token::Str(SK_STR)]);
1033+
assert_tokens(&sk.readable(), &[Token::String(SK_STR)]);
1034+
}
9381035
}

0 commit comments

Comments
 (0)