tronz_primitives/
signature.rs1use core::fmt;
4
5use k256::ecdsa::{RecoveryId, Signature};
6
7use crate::error::SignatureError;
8
9pub const SIGNATURE_LEN: usize = 65;
11
12#[derive(Clone, Copy, PartialEq, Eq, Hash)]
18pub struct RecoverableSignature {
19 r: [u8; 32],
20 s: [u8; 32],
21 v: u8,
22}
23
24impl RecoverableSignature {
25 pub fn from_signature(sig: &Signature, recovery_id: RecoveryId) -> Self {
27 let bytes = sig.to_bytes();
28 let mut r = [0u8; 32];
29 let mut s = [0u8; 32];
30 r.copy_from_slice(&bytes[..32]);
31 s.copy_from_slice(&bytes[32..]);
32 Self {
33 r,
34 s,
35 v: recovery_id.to_byte(),
36 }
37 }
38
39 pub fn from_bytes(bytes: &[u8]) -> Result<Self, SignatureError> {
44 if bytes.len() != SIGNATURE_LEN {
45 return Err(SignatureError::BadLength(bytes.len()));
46 }
47 let v = match bytes[64] {
48 v @ (0 | 1) => v,
49 27 => 0,
50 28 => 1,
51 other => return Err(SignatureError::BadRecoveryId(other)),
52 };
53 let mut r = [0u8; 32];
54 let mut s = [0u8; 32];
55 r.copy_from_slice(&bytes[..32]);
56 s.copy_from_slice(&bytes[32..64]);
57 Ok(Self { r, s, v })
58 }
59
60 pub fn r(&self) -> &[u8; 32] {
62 &self.r
63 }
64
65 pub fn s(&self) -> &[u8; 32] {
67 &self.s
68 }
69
70 pub fn v(&self) -> u8 {
72 self.v
73 }
74
75 pub fn to_bytes(&self) -> [u8; SIGNATURE_LEN] {
77 let mut out = [0u8; SIGNATURE_LEN];
78 out[..32].copy_from_slice(&self.r);
79 out[32..64].copy_from_slice(&self.s);
80 out[64] = self.v;
81 out
82 }
83
84 pub fn split(&self) -> Result<(Signature, RecoveryId), SignatureError> {
86 let mut rs = [0u8; 64];
87 rs[..32].copy_from_slice(&self.r);
88 rs[32..].copy_from_slice(&self.s);
89 let sig = Signature::from_slice(&rs)?;
90 let recid =
91 RecoveryId::from_byte(self.v).ok_or(SignatureError::BadRecoveryId(self.v))?;
92 Ok((sig, recid))
93 }
94}
95
96impl fmt::Debug for RecoverableSignature {
97 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98 write!(f, "RecoverableSignature(0x{})", hex::encode(self.to_bytes()))
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105 use k256::ecdsa::{signature::hazmat::PrehashSigner, SigningKey};
106
107 #[test]
108 fn bytes_roundtrip() {
109 let mut bytes = [7u8; SIGNATURE_LEN];
110 bytes[64] = 1;
111 let sig = RecoverableSignature::from_bytes(&bytes).unwrap();
112 assert_eq!(sig.to_bytes(), bytes);
113 assert_eq!(sig.v(), 1);
114 }
115
116 #[test]
117 fn normalises_eth_v() {
118 let mut bytes = [3u8; SIGNATURE_LEN];
119 bytes[64] = 28;
120 let sig = RecoverableSignature::from_bytes(&bytes).unwrap();
121 assert_eq!(sig.v(), 1);
122 }
123
124 #[test]
125 fn bad_length_and_recid() {
126 assert!(matches!(
127 RecoverableSignature::from_bytes(&[0u8; 10]),
128 Err(SignatureError::BadLength(10))
129 ));
130 let mut bytes = [0u8; SIGNATURE_LEN];
131 bytes[64] = 5;
132 assert!(matches!(
133 RecoverableSignature::from_bytes(&bytes),
134 Err(SignatureError::BadRecoveryId(5))
135 ));
136 }
137
138 #[test]
139 fn from_signature_and_split() {
140 let signing = SigningKey::from_bytes(&[1u8; 32].into()).unwrap();
141 let (sig, recid): (Signature, RecoveryId) =
142 signing.sign_prehash(&[9u8; 32]).unwrap();
143 let rec = RecoverableSignature::from_signature(&sig, recid);
144 let (sig2, recid2) = rec.split().unwrap();
145 assert_eq!(sig, sig2);
146 assert_eq!(recid.to_byte(), recid2.to_byte());
147 }
148}