fuel_crypto/secp256/
signature.rs

1use super::{
2    backend::k1,
3    signature_format::decode_signature,
4};
5use crate::{
6    Error,
7    Message,
8    PublicKey,
9    SecretKey,
10};
11
12use fuel_types::Bytes64;
13
14use core::{
15    fmt,
16    ops::Deref,
17    str,
18};
19
20/// Compressed-form Secp256k1 signature.
21#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23#[repr(transparent)]
24pub struct Signature(Bytes64);
25
26impl Signature {
27    /// Memory length of the type in bytes.
28    pub const LEN: usize = Bytes64::LEN;
29
30    /// Construct a `Signature` directly from its bytes.
31    ///
32    /// This constructor expects the given bytes to be a valid signature. No signing is
33    /// performed.
34    pub fn from_bytes(bytes: [u8; Self::LEN]) -> Self {
35        Self(bytes.into())
36    }
37
38    /// Construct a `Signature` reference directly from a reference to its bytes.
39    ///
40    /// This constructor expects the given bytes to be a valid signature. No signing is
41    /// performed.
42    pub fn from_bytes_ref(bytes: &[u8; Self::LEN]) -> &Self {
43        // TODO: Wrap this unsafe conversion safely in `fuel_types::Bytes64`.
44        #[allow(unsafe_code)]
45        unsafe {
46            &*(bytes.as_ptr() as *const Self)
47        }
48    }
49
50    /// Removes the recovery id from the signature.
51    pub fn remove_recovery_id(&self) -> [u8; Self::LEN] {
52        let (signature, _recovery_id) = decode_signature(self.0.into());
53        signature
54    }
55
56    /// Kept temporarily for backwards compatibility.
57    #[deprecated = "Use `Signature::from_bytes` instead"]
58    pub fn from_bytes_unchecked(bytes: [u8; Self::LEN]) -> Self {
59        Self::from_bytes(bytes)
60    }
61}
62
63impl Deref for Signature {
64    type Target = [u8; Signature::LEN];
65
66    fn deref(&self) -> &[u8; Signature::LEN] {
67        self.0.deref()
68    }
69}
70
71impl AsRef<[u8]> for Signature {
72    fn as_ref(&self) -> &[u8] {
73        self.0.as_ref()
74    }
75}
76
77impl AsMut<[u8]> for Signature {
78    fn as_mut(&mut self) -> &mut [u8] {
79        self.0.as_mut()
80    }
81}
82
83impl fmt::LowerHex for Signature {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        self.0.fmt(f)
86    }
87}
88
89impl fmt::UpperHex for Signature {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        self.0.fmt(f)
92    }
93}
94
95impl fmt::Debug for Signature {
96    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
97        self.0.fmt(f)
98    }
99}
100
101impl fmt::Display for Signature {
102    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
103        self.0.fmt(f)
104    }
105}
106
107impl From<Signature> for [u8; Signature::LEN] {
108    fn from(salt: Signature) -> [u8; Signature::LEN] {
109        salt.0.into()
110    }
111}
112
113impl From<Signature> for Bytes64 {
114    fn from(s: Signature) -> Self {
115        s.0
116    }
117}
118
119impl str::FromStr for Signature {
120    type Err = Error;
121
122    /// Parse a `Signature` directly from its bytes encoded as hex in a string.
123    ///
124    /// This constructor does not perform any signing.
125    fn from_str(s: &str) -> Result<Self, Self::Err> {
126        Bytes64::from_str(s)
127            .map_err(|_| Error::InvalidSignature)
128            .map(|s| Self::from_bytes(s.into()))
129    }
130}
131
132/// Secp256k1 methods
133impl Signature {
134    /// Produce secp256k1 signature
135    pub fn sign(secret: &SecretKey, message: &Message) -> Self {
136        Self(Bytes64::from(k1::sign(secret, message)))
137    }
138
139    /// Recover secp256k1 public key from a signature performed with
140    pub fn recover(&self, message: &Message) -> Result<PublicKey, Error> {
141        k1::recover(*self.0, message)
142    }
143
144    /// Verify that a signature matches given public key
145    pub fn verify(&self, public_key: &PublicKey, message: &Message) -> Result<(), Error> {
146        k1::verify(*self.0, **public_key, message)
147    }
148}
149
150#[cfg(test)]
151mod tests {
152    use crate::Signature;
153    use test_case::test_case;
154
155    #[test_case(0x00 => 0x00)]
156    #[test_case(0x7E => 0x7E)]
157    #[test_case(0x7F => 0x7F)]
158    #[test_case(0x80 => 0x00)]
159    #[test_case(0xFF => 0x7F)]
160    fn removes_recovery_id(s_byte: u8) -> u8 {
161        let mut sig_bytes = [0u8; 64];
162        sig_bytes[32..].fill(s_byte);
163
164        let signature = Signature::from_bytes(sig_bytes);
165        let without_recovery_id = signature.remove_recovery_id();
166        without_recovery_id[32]
167    }
168}