Skip to content

Commit eda2b16

Browse files
committed
fix: fix serialization/deserialization and improve tests
1 parent 1979ab3 commit eda2b16

File tree

2 files changed

+118
-66
lines changed

2 files changed

+118
-66
lines changed

stacks-common/src/util/secp256k1.rs

Lines changed: 69 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ use k256::ecdsa::{
2424
use k256::elliptic_curve::generic_array::GenericArray;
2525
use k256::elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint};
2626
use k256::{EncodedPoint, PublicKey as K256PublicKey, SecretKey as K256SecretKey};
27-
use serde::de::{Deserialize, Error as de_Error};
28-
use serde::Serialize;
2927
use thiserror::Error;
3028

3129
use crate::types::{PrivateKey, PublicKey};
@@ -110,27 +108,19 @@ impl From<(K256Signature, K256RecoveryId)> for RecoverableSignature {
110108
}
111109
}
112110

113-
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
111+
#[derive(Debug, PartialEq, Eq, Clone)]
114112
pub struct Secp256k1PublicKey {
115-
// serde is broken for secp256k1, so do it ourselves
116-
#[serde(
117-
serialize_with = "secp256k1_pubkey_serialize",
118-
deserialize_with = "secp256k1_pubkey_deserialize"
119-
)]
120113
key: K256VerifyingKey,
121114
compressed: bool,
122115
}
116+
impl_byte_array_serde!(Secp256k1PublicKey);
123117

124-
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
118+
#[derive(Debug, PartialEq, Eq, Clone)]
125119
pub struct Secp256k1PrivateKey {
126-
// serde is broken for secp256k1, so do it ourselves
127-
#[serde(
128-
serialize_with = "secp256k1_privkey_serialize",
129-
deserialize_with = "secp256k1_privkey_deserialize"
130-
)]
131120
key: K256SigningKey,
132121
compress_public: bool,
133122
}
123+
impl_byte_array_serde!(Secp256k1PrivateKey);
134124

135125
impl Hash for Secp256k1PublicKey {
136126
fn hash<H: Hasher>(&self, state: &mut H) {
@@ -471,56 +461,6 @@ impl PrivateKey for Secp256k1PrivateKey {
471461
}
472462
}
473463

474-
fn secp256k1_pubkey_serialize<S: serde::Serializer>(
475-
pubk: &K256VerifyingKey,
476-
s: S,
477-
) -> Result<S::Ok, S::Error> {
478-
let public_key = K256PublicKey::from(pubk);
479-
let encoded_point = public_key.to_encoded_point(true); // always serialize as compressed
480-
let key_hex = to_hex(encoded_point.as_bytes());
481-
s.serialize_str(key_hex.as_str())
482-
}
483-
484-
fn secp256k1_pubkey_deserialize<'de, D: serde::Deserializer<'de>>(
485-
d: D,
486-
) -> Result<K256VerifyingKey, D::Error> {
487-
let key_hex = String::deserialize(d)?;
488-
let key_bytes = hex_bytes(&key_hex).map_err(de_Error::custom)?;
489-
490-
let encoded_point = EncodedPoint::from_bytes(&key_bytes).map_err(de_Error::custom)?;
491-
let public_key =
492-
Option::<K256PublicKey>::from(K256PublicKey::from_encoded_point(&encoded_point))
493-
.ok_or_else(|| de_Error::custom("Invalid public key"))?;
494-
Ok(K256VerifyingKey::from(public_key))
495-
}
496-
497-
fn secp256k1_privkey_serialize<S: serde::Serializer>(
498-
privk: &K256SigningKey,
499-
s: S,
500-
) -> Result<S::Ok, S::Error> {
501-
let key_hex = to_hex(privk.to_bytes().as_slice());
502-
s.serialize_str(key_hex.as_str())
503-
}
504-
505-
fn secp256k1_privkey_deserialize<'de, D: serde::Deserializer<'de>>(
506-
d: D,
507-
) -> Result<K256SigningKey, D::Error> {
508-
let key_hex = String::deserialize(d)?;
509-
let key_bytes = hex_bytes(&key_hex).map_err(de_Error::custom)?;
510-
511-
if key_bytes.len() != 32 {
512-
return Err(de_Error::custom("Private key must be 32 bytes"));
513-
}
514-
515-
let mut key_array = [0u8; 32];
516-
key_array.copy_from_slice(&key_bytes);
517-
518-
let secret_key =
519-
K256SecretKey::from_bytes(&GenericArray::from(key_array)).map_err(de_Error::custom)?;
520-
521-
Ok(K256SigningKey::from(secret_key))
522-
}
523-
524464
/// Recovers a public key from a message hash and a recoverable signature.
525465
/// The returned public key is in compressed format (33 bytes).
526466
pub fn secp256k1_recover(
@@ -609,12 +549,39 @@ mod tests {
609549
}
610550

611551
#[test]
612-
fn test_parse_serialize_compressed() {
552+
fn to_rsv_rotates_recovery_byte() {
553+
let mut bytes = [0u8; 65];
554+
for (idx, slot) in bytes.iter_mut().enumerate() {
555+
*slot = idx as u8;
556+
}
557+
let sig = MessageSignature(bytes);
558+
559+
let rsv = sig.to_rsv();
560+
561+
assert_eq!(rsv.len(), 65);
562+
assert_eq!(rsv[0], 1, "R should start where VRS's R begins");
563+
assert_eq!(rsv[63], 64, "S should end with last signature byte");
564+
assert_eq!(rsv[64], 0, "V should move to the tail after rotation");
565+
}
566+
567+
#[test]
568+
fn test_privkey_parse_serialize_compressed() {
613569
let mut t1 = Secp256k1PrivateKey::random();
614570
t1.set_compress_public(true);
615571
let h_comp = t1.to_hex();
572+
let json_comp = serde_json::to_string(&t1).unwrap();
573+
assert_eq!(json_comp, format!("\"{h_comp}\""));
574+
let deser_comp: Secp256k1PrivateKey = serde_json::from_str(&json_comp).unwrap();
575+
assert_eq!(deser_comp.to_hex(), h_comp);
576+
assert!(deser_comp.compress_public());
577+
616578
t1.set_compress_public(false);
617579
let h_uncomp = t1.to_hex();
580+
let json_uncomp = serde_json::to_string(&t1).unwrap();
581+
assert_eq!(json_uncomp, format!("\"{h_uncomp}\""));
582+
let deser_uncomp: Secp256k1PrivateKey = serde_json::from_str(&json_uncomp).unwrap();
583+
assert_eq!(deser_uncomp.to_hex(), h_uncomp);
584+
assert!(!deser_uncomp.compress_public());
618585

619586
assert!(h_comp != h_uncomp);
620587
assert_eq!(h_comp.len(), 66);
@@ -638,6 +605,43 @@ mod tests {
638605
assert_eq!(Secp256k1PrivateKey::from_hex(&h_comp), Ok(t1));
639606
}
640607

608+
#[test]
609+
fn test_pubkey_parse_serialize_compressed() {
610+
let privk = Secp256k1PrivateKey::random();
611+
let mut pubk = Secp256k1PublicKey::from_private(&privk);
612+
613+
pubk.set_compressed(true);
614+
let h_comp = pubk.to_hex();
615+
let json_comp = serde_json::to_string(&pubk).unwrap();
616+
assert_eq!(json_comp, format!("\"{}\"", h_comp));
617+
let deser_comp: Secp256k1PublicKey = serde_json::from_str(&json_comp).unwrap();
618+
assert_eq!(deser_comp.to_hex(), h_comp);
619+
assert!(deser_comp.compressed());
620+
621+
pubk.set_compressed(false);
622+
let h_uncomp = pubk.to_hex();
623+
let json_uncomp = serde_json::to_string(&pubk).unwrap();
624+
assert_eq!(json_uncomp, format!("\"{}\"", h_uncomp));
625+
let deser_uncomp: Secp256k1PublicKey = serde_json::from_str(&json_uncomp).unwrap();
626+
assert_eq!(deser_uncomp.to_hex(), h_uncomp);
627+
assert!(!deser_uncomp.compressed());
628+
629+
assert!(h_comp != h_uncomp);
630+
assert_eq!(h_comp.len(), 66);
631+
assert_eq!(h_uncomp.len(), 130);
632+
633+
assert!(Secp256k1PublicKey::from_hex(&h_comp).unwrap().compressed());
634+
assert!(!Secp256k1PublicKey::from_hex(&h_uncomp)
635+
.unwrap()
636+
.compressed());
637+
638+
assert_eq!(Secp256k1PublicKey::from_hex(&h_uncomp), Ok(pubk.clone()));
639+
640+
pubk.set_compressed(true);
641+
642+
assert_eq!(Secp256k1PublicKey::from_hex(&h_comp), Ok(pubk));
643+
}
644+
641645
#[test]
642646
/// Test the behavior of from_seed using hard-coded values from previous existing integration tests
643647
fn sk_from_seed() {

stacks-common/src/util/secp256r1.rs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,12 +334,23 @@ mod tests {
334334
use super::*;
335335

336336
#[test]
337-
fn test_parse_serialize_compressed() {
337+
fn test_privkey_parse_serialize_compressed() {
338338
let mut t1 = Secp256r1PrivateKey::random();
339339
t1.set_compress_public(true);
340340
let h_comp = t1.to_hex();
341+
let json_comp = serde_json::to_string(&t1).unwrap();
342+
assert_eq!(json_comp, format!("\"{h_comp}\""));
343+
let deser_comp: Secp256r1PrivateKey = serde_json::from_str(&json_comp).unwrap();
344+
assert_eq!(deser_comp.to_hex(), h_comp);
345+
assert!(deser_comp.compress_public());
346+
341347
t1.set_compress_public(false);
342348
let h_uncomp = t1.to_hex();
349+
let json_uncomp = serde_json::to_string(&t1).unwrap();
350+
assert_eq!(json_uncomp, format!("\"{h_uncomp}\""));
351+
let deser_uncomp: Secp256r1PrivateKey = serde_json::from_str(&json_uncomp).unwrap();
352+
assert_eq!(deser_uncomp.to_hex(), h_uncomp);
353+
assert!(!deser_uncomp.compress_public());
343354

344355
assert!(h_comp != h_uncomp);
345356
assert_eq!(h_comp.len(), 66);
@@ -363,6 +374,43 @@ mod tests {
363374
assert_eq!(Secp256r1PrivateKey::from_hex(&h_comp), Ok(t1));
364375
}
365376

377+
#[test]
378+
fn test_pubkey_parse_serialize_compressed() {
379+
let privk = Secp256r1PrivateKey::random();
380+
let mut pubk = Secp256r1PublicKey::from_private(&privk);
381+
382+
pubk.set_compressed(true);
383+
let h_comp = pubk.to_hex();
384+
let json_comp = serde_json::to_string(&pubk).unwrap();
385+
assert_eq!(json_comp, format!("\"{}\"", h_comp));
386+
let deser_comp: Secp256r1PublicKey = serde_json::from_str(&json_comp).unwrap();
387+
assert_eq!(deser_comp.to_hex(), h_comp);
388+
assert!(deser_comp.compressed());
389+
390+
pubk.set_compressed(false);
391+
let h_uncomp = pubk.to_hex();
392+
let json_uncomp = serde_json::to_string(&pubk).unwrap();
393+
assert_eq!(json_uncomp, format!("\"{}\"", h_uncomp));
394+
let deser_uncomp: Secp256r1PublicKey = serde_json::from_str(&json_uncomp).unwrap();
395+
assert_eq!(deser_uncomp.to_hex(), h_uncomp);
396+
assert!(!deser_uncomp.compressed());
397+
398+
assert!(h_comp != h_uncomp);
399+
assert_eq!(h_comp.len(), 66);
400+
assert_eq!(h_uncomp.len(), 130);
401+
402+
assert!(Secp256r1PublicKey::from_hex(&h_comp).unwrap().compressed());
403+
assert!(!Secp256r1PublicKey::from_hex(&h_uncomp)
404+
.unwrap()
405+
.compressed());
406+
407+
assert_eq!(Secp256r1PublicKey::from_hex(&h_uncomp), Ok(pubk.clone()));
408+
409+
pubk.set_compressed(true);
410+
411+
assert_eq!(Secp256r1PublicKey::from_hex(&h_comp), Ok(pubk));
412+
}
413+
366414
#[test]
367415
fn test_from_seed() {
368416
let sk = Secp256r1PrivateKey::from_seed(&[2; 32]);

0 commit comments

Comments
 (0)