1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
//! Type to represent an ECDSA signature with a recovery identifier.
//!
//! Supports serde and single party signatures.
//!
//! Support for signatures generated by MPC crates will be coming soon.
#![deny(missing_docs)]
use ethereum_types::U256;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
use thiserror::Error;

#[cfg(feature = "single-party")]
use k256::{ecdsa::RecoveryId, FieldBytes};

/// Errors thrown converting to and from signatures.
#[derive(Debug, Error)]
pub enum SignatureError {
    /// Invalid length, secp256k1 signatures are 65 bytes
    #[error("invalid signature length, got {0}, expected 65")]
    InvalidLength(usize),

    /// Expected a recovery identifier.
    #[error("recovery identifier is expected")]
    RecoveryId,

    /// When parsing a signature from string to hex
    #[error(transparent)]
    DecodingError(#[from] hex::FromHexError),

    /// Error generated by the k256 library.
    #[cfg(feature = "single-party")]
    #[error(transparent)]
    Ecdsa(#[from] k256::ecdsa::Error),
}

/// An ECDSA signature with a recovery identifier.
///
/// The recovery identifier may be normalized, in Electrum notation
/// or have EIP155 chain replay protection applied.
#[derive(
    Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Copy,
)]
pub struct Signature {
    /// R value
    pub r: U256,
    /// S value
    pub s: U256,
    /// V value for the recovery identifier
    pub v: u64,
}

impl Signature {
    /// Create a signature with normalized recovery identifier.
    pub fn new_normalized(r: U256, s: U256, v: u64) -> Self {
        debug_assert!(v == 0 || v == 1);
        Self { r, s, v }
    }

    /// Create a signature with electrum recovery identifier.
    pub fn new_electrum(r: U256, s: U256, v: u64) -> Self {
        debug_assert!(v == 27 || v == 28);
        Self { r, s, v }
    }

    /// Create a signature with EIP155 chain replay protection.
    pub fn new_eip155(r: U256, s: U256, v: u64) -> Self {
        debug_assert!(v >= 35);
        Self { r, s, v }
    }

    /// Is the recovery identifier for this signature in
    /// the normalized form (`0` or `1`).
    pub fn is_normalized(&self) -> bool {
        self.v == 0 || self.v == 1
    }

    /// Is the recovery identifier for this signature in
    /// the electrum form (`27` or `28`).
    pub fn is_electrum(&self) -> bool {
        self.v == 27 || self.v == 28
    }

    /// Is the recovery identifier for this signature in
    /// the EIP155 form.
    pub fn is_eip155(&self) -> bool {
        self.v >= 35
    }

    /// Converts this signature into normalized form from an Electrum
    /// signature.
    ///
    /// Panics if this signature is not in Electrum format.
    pub fn normalize(self) -> Self {
        assert!(self.is_electrum());
        Self {
            r: self.r,
            s: self.s,
            v: self.v - 27,
        }
    }

    /// Converts this signature into normalized form from an EIP155
    /// signature.
    ///
    /// Panics if the signature could not be safely normalized for
    /// example if a `chain_id` was supplied that would cause the
    /// existing `v` value to become negative.
    pub fn normalize_eip155(self, chain_id: u64) -> Self {
        if self.v >= 35 + (chain_id * 2) {
            Self {
                r: self.r,
                s: self.s,
                v: self.v - chain_id * 2 - 35,
            }
        } else {
            panic!("cannot safely normalize signature recovery identifier")
        }
    }

    /// Converts this signature into Electrum form.
    ///
    /// Panics if this signature is not in it's normalized form.
    pub fn into_electrum(self) -> Self {
        assert!(self.is_normalized());
        Self {
            r: self.r,
            s: self.s,
            v: self.v + 27,
        }
    }

    /// Converts this signature applying
    /// [EIP155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md)
    /// chain replay protection.
    ///
    /// Panics if this signature is not in it's normalized form.
    pub fn into_eip155(self, chain_id: u64) -> Self {
        assert!(self.is_normalized());
        Self {
            r: self.r,
            s: self.s,
            v: self.v + 35 + chain_id * 2,
        }
    }

    /// Get the bytes for the r, s and v values.
    ///
    /// Panics if this signature is not normalized.
    pub fn to_bytes(&self) -> [u8; 65] {
        if !self.is_normalized() {
            panic!("signature must be normalized to convert to byte array");
        }

        let mut out = [0u8; 64];
        let mut r: [u8; 32] = [0u8; 32];
        let mut s: [u8; 32] = [0u8; 32];
        self.r.to_big_endian(&mut r);
        self.s.to_big_endian(&mut s);
        let (left, right) = out.split_at_mut(32);
        left.copy_from_slice(&r);
        right.copy_from_slice(&s);

        let mut result = [0u8; 65];
        let (left, right) = result.split_at_mut(64);
        left.copy_from_slice(&out);
        right[0] = self.v as u8;
        result
    }
}

impl<'a> TryFrom<&'a [u8]> for Signature {
    type Error = SignatureError;

    /// Parses a raw signature which is expected to be 65 bytes long where
    /// the first 32 bytes is the `r` value, the second 32 bytes the `s` value
    /// and the final byte is the `v` value in 'Electrum' notation.
    fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
        if bytes.len() != 65 {
            return Err(SignatureError::InvalidLength(bytes.len()));
        }

        let v = bytes[64];
        let r = U256::from_big_endian(&bytes[0..32]);
        let s = U256::from_big_endian(&bytes[32..64]);

        Ok(Signature { r, s, v: v.into() })
    }
}

impl FromStr for Signature {
    type Err = SignatureError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let s = s.strip_prefix("0x").unwrap_or(s);
        let bytes = hex::decode(s)?;
        Signature::try_from(&bytes[..])
    }
}

impl From<[u8; 65]> for Signature {
    fn from(value: [u8; 65]) -> Self {
        let r = &value[0..32];
        let s = &value[32..64];
        let v = &value[64];
        Self {
            r: U256::from_big_endian(r),
            s: U256::from_big_endian(s),
            v: *v as u64,
        }
    }
}

#[cfg(feature = "single-party")]
impl TryFrom<(k256::ecdsa::Signature, Option<RecoveryId>)> for Signature {
    type Error = SignatureError;

    fn try_from(
        sig: (k256::ecdsa::Signature, Option<RecoveryId>),
    ) -> Result<Self, Self::Error> {
        let r_bytes: FieldBytes = sig.0.r().into();
        let s_bytes: FieldBytes = sig.0.s().into();
        let v: u8 = sig.1.ok_or(SignatureError::RecoveryId)?.into();
        Ok(Self {
            r: U256::from_big_endian(r_bytes.as_slice()),
            s: U256::from_big_endian(s_bytes.as_slice()),
            v: v as u64,
        })
    }
}

#[cfg(feature = "single-party")]
impl TryFrom<Signature> for (k256::ecdsa::Signature, RecoveryId) {
    type Error = SignatureError;
    fn try_from(value: Signature) -> Result<Self, Self::Error> {
        let mut r: [u8; 32] = [0u8; 32];
        let mut s: [u8; 32] = [0u8; 32];
        value.r.to_big_endian(&mut r);
        value.s.to_big_endian(&mut s);
        let signature = k256::ecdsa::Signature::from_scalars(r, s)?;
        let recid = RecoveryId::try_from(value.v as u8)?;
        Ok((signature, recid))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn signature_into_electrum() {
        let sig = Signature {
            r: Default::default(),
            s: Default::default(),
            v: 1,
        };
        let electrum = sig.into_electrum();
        assert_eq!(28, electrum.v);
    }

    #[test]
    fn signature_from_electrum() {
        let electrum = Signature {
            r: Default::default(),
            s: Default::default(),
            v: 37,
        };
        let sig = electrum.normalize_eip155(1);
        assert_eq!(0, sig.v);
    }

    #[test]
    fn signature_into_eip155() {
        let sig = Signature {
            r: Default::default(),
            s: Default::default(),
            v: 1,
        };
        let eip155 = sig.into_eip155(1337u64);
        assert_eq!(2710, eip155.v);
    }
}