1#![deny(missing_docs)]
7use ethereum_types::U256;
8use serde::{Deserialize, Serialize};
9use std::str::FromStr;
10use thiserror::Error;
11
12#[cfg(feature = "single-party")]
13use k256::{ecdsa::RecoveryId, FieldBytes};
14
15#[derive(Debug, Error)]
17pub enum SignatureError {
18 #[error("invalid signature length, got {0}, expected 65")]
20 InvalidLength(usize),
21
22 #[error("recovery identifier is expected")]
24 RecoveryId,
25
26 #[error(transparent)]
28 DecodingError(#[from] hex::FromHexError),
29
30 #[cfg(feature = "single-party")]
32 #[error(transparent)]
33 Ecdsa(#[from] k256::ecdsa::Error),
34}
35
36#[derive(
41 Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Copy,
42)]
43pub struct Signature {
44 pub r: U256,
46 pub s: U256,
48 pub v: u64,
50}
51
52impl Signature {
53 pub fn new_normalized(r: U256, s: U256, v: u64) -> Self {
55 debug_assert!(v == 0 || v == 1);
56 Self { r, s, v }
57 }
58
59 pub fn new_electrum(r: U256, s: U256, v: u64) -> Self {
61 debug_assert!(v == 27 || v == 28);
62 Self { r, s, v }
63 }
64
65 pub fn new_eip155(r: U256, s: U256, v: u64) -> Self {
67 debug_assert!(v >= 35);
68 Self { r, s, v }
69 }
70
71 pub fn is_normalized(&self) -> bool {
74 self.v == 0 || self.v == 1
75 }
76
77 pub fn is_electrum(&self) -> bool {
80 self.v == 27 || self.v == 28
81 }
82
83 pub fn is_eip155(&self) -> bool {
86 self.v >= 35
87 }
88
89 pub fn normalize(self) -> Self {
94 assert!(self.is_electrum());
95 Self {
96 r: self.r,
97 s: self.s,
98 v: self.v - 27,
99 }
100 }
101
102 pub fn normalize_eip155(self, chain_id: u64) -> Self {
109 if self.v >= 35 + (chain_id * 2) {
110 Self {
111 r: self.r,
112 s: self.s,
113 v: self.v - chain_id * 2 - 35,
114 }
115 } else {
116 panic!("cannot safely normalize signature recovery identifier")
117 }
118 }
119
120 pub fn into_electrum(self) -> Self {
124 assert!(self.is_normalized());
125 Self {
126 r: self.r,
127 s: self.s,
128 v: self.v + 27,
129 }
130 }
131
132 pub fn into_eip155(self, chain_id: u64) -> Self {
138 assert!(self.is_normalized());
139 Self {
140 r: self.r,
141 s: self.s,
142 v: self.v + 35 + chain_id * 2,
143 }
144 }
145
146 pub fn to_bytes(&self) -> [u8; 65] {
150 if !self.is_normalized() {
151 panic!("signature must be normalized to convert to byte array");
152 }
153
154 let mut out = [0u8; 64];
155 let mut r: [u8; 32] = [0u8; 32];
156 let mut s: [u8; 32] = [0u8; 32];
157 self.r.to_big_endian(&mut r);
158 self.s.to_big_endian(&mut s);
159 let (left, right) = out.split_at_mut(32);
160 left.copy_from_slice(&r);
161 right.copy_from_slice(&s);
162
163 let mut result = [0u8; 65];
164 let (left, right) = result.split_at_mut(64);
165 left.copy_from_slice(&out);
166 right[0] = self.v as u8;
167 result
168 }
169}
170
171impl<'a> TryFrom<&'a [u8]> for Signature {
172 type Error = SignatureError;
173
174 fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
178 if bytes.len() != 65 {
179 return Err(SignatureError::InvalidLength(bytes.len()));
180 }
181
182 let v = bytes[64];
183 let r = U256::from_big_endian(&bytes[0..32]);
184 let s = U256::from_big_endian(&bytes[32..64]);
185
186 Ok(Signature { r, s, v: v.into() })
187 }
188}
189
190impl FromStr for Signature {
191 type Err = SignatureError;
192
193 fn from_str(s: &str) -> Result<Self, Self::Err> {
194 let s = s.strip_prefix("0x").unwrap_or(s);
195 let bytes = hex::decode(s)?;
196 Signature::try_from(&bytes[..])
197 }
198}
199
200impl From<[u8; 65]> for Signature {
201 fn from(value: [u8; 65]) -> Self {
202 let r = &value[0..32];
203 let s = &value[32..64];
204 let v = &value[64];
205 Self {
206 r: U256::from_big_endian(r),
207 s: U256::from_big_endian(s),
208 v: *v as u64,
209 }
210 }
211}
212
213#[cfg(feature = "single-party")]
214impl TryFrom<(k256::ecdsa::Signature, Option<RecoveryId>)> for Signature {
215 type Error = SignatureError;
216
217 fn try_from(
218 sig: (k256::ecdsa::Signature, Option<RecoveryId>),
219 ) -> Result<Self, Self::Error> {
220 let r_bytes: FieldBytes = sig.0.r().into();
221 let s_bytes: FieldBytes = sig.0.s().into();
222 let v: u8 = sig.1.ok_or(SignatureError::RecoveryId)?.into();
223 Ok(Self {
224 r: U256::from_big_endian(r_bytes.as_slice()),
225 s: U256::from_big_endian(s_bytes.as_slice()),
226 v: v as u64,
227 })
228 }
229}
230
231#[cfg(feature = "single-party")]
232impl TryFrom<Signature> for (k256::ecdsa::Signature, RecoveryId) {
233 type Error = SignatureError;
234 fn try_from(value: Signature) -> Result<Self, Self::Error> {
235 let mut r: [u8; 32] = [0u8; 32];
236 let mut s: [u8; 32] = [0u8; 32];
237 value.r.to_big_endian(&mut r);
238 value.s.to_big_endian(&mut s);
239 let signature = k256::ecdsa::Signature::from_scalars(r, s)?;
240 let recid = RecoveryId::try_from(value.v as u8)?;
241 Ok((signature, recid))
242 }
243}
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248
249 #[test]
250 fn signature_into_electrum() {
251 let sig = Signature {
252 r: Default::default(),
253 s: Default::default(),
254 v: 1,
255 };
256 let electrum = sig.into_electrum();
257 assert_eq!(28, electrum.v);
258 }
259
260 #[test]
261 fn signature_from_electrum() {
262 let electrum = Signature {
263 r: Default::default(),
264 s: Default::default(),
265 v: 37,
266 };
267 let sig = electrum.normalize_eip155(1);
268 assert_eq!(0, sig.v);
269 }
270
271 #[test]
272 fn signature_into_eip155() {
273 let sig = Signature {
274 r: Default::default(),
275 s: Default::default(),
276 v: 1,
277 };
278 let eip155 = sig.into_eip155(1337u64);
279 assert_eq!(2710, eip155.v);
280 }
281}