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
use crate::Error;
use fuel_types::Bytes64;
use core::ops::Deref;
use core::{fmt, str};
#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(transparent)]
/// Secp256k1 signature implementation
pub struct Signature(Bytes64);
impl Signature {
/// Memory length of the type
pub const LEN: usize = Bytes64::LEN;
/// Add a conversion from arbitrary slices into owned
///
/// # Safety
///
/// There is no guarantee the provided bytes will be a valid signature. Internally, some FFI
/// calls to `secp256k1` are performed and we might have undefined behavior in case the bytes
/// are not canonically encoded to a valid `secp256k1` signature.
pub unsafe fn from_bytes_unchecked(bytes: [u8; Self::LEN]) -> Self {
Self(bytes.into())
}
/// Add a conversion from arbitrary slices into owned
///
/// # Safety
///
/// This function will not panic if the length of the slice is smaller than
/// `Self::LEN`. Instead, it will cause undefined behavior and read random
/// disowned bytes.
///
/// There is no guarantee the provided bytes will be a valid signature. Internally, some FFI
/// calls to `secp256k1` are performed and we might have undefined behavior in case the bytes
/// are not canonically encoded to a valid `secp256k1` signature.
pub unsafe fn from_slice_unchecked(bytes: &[u8]) -> Self {
Self(Bytes64::from_slice_unchecked(bytes))
}
/// Copy-free reference cast
///
/// There is no guarantee the provided bytes will fit the field.
///
/// # Safety
///
/// Inputs smaller than `Self::LEN` will cause undefined behavior.
pub unsafe fn as_ref_unchecked(bytes: &[u8]) -> &Self {
// The interpreter will frequently make references to keys and values using
// logically checked slices.
//
// This function will avoid unnecessary copy to owned slices for the interpreter
// access
&*(bytes.as_ptr() as *const Self)
}
}
impl Deref for Signature {
type Target = [u8; Signature::LEN];
fn deref(&self) -> &[u8; Signature::LEN] {
self.0.deref()
}
}
impl AsRef<[u8]> for Signature {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl AsMut<[u8]> for Signature {
fn as_mut(&mut self) -> &mut [u8] {
self.0.as_mut()
}
}
impl From<Signature> for [u8; Signature::LEN] {
fn from(salt: Signature) -> [u8; Signature::LEN] {
salt.0.into()
}
}
impl fmt::LowerHex for Signature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl fmt::UpperHex for Signature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl fmt::Debug for Signature {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl fmt::Display for Signature {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl From<Bytes64> for Signature {
fn from(b: Bytes64) -> Self {
Self(b)
}
}
impl From<Signature> for Bytes64 {
fn from(s: Signature) -> Self {
s.0
}
}
impl str::FromStr for Signature {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Bytes64::from_str(s)
.map_err(|_| Error::InvalidSignature)
.map(|s| s.into())
}
}
#[cfg(feature = "std")]
mod use_std {
use crate::{Error, Message, PublicKey, SecretKey, Signature};
use lazy_static::lazy_static;
use secp256k1::{
ecdsa::{RecoverableSignature as SecpRecoverableSignature, RecoveryId},
Secp256k1,
};
use std::borrow::Borrow;
lazy_static! {
static ref SIGNING_SECP: Secp256k1<secp256k1::SignOnly> = Secp256k1::signing_only();
static ref RECOVER_SECP: Secp256k1<secp256k1::All> = Secp256k1::new();
}
impl Signature {
// Internal API - this isn't meant to be made public because some assumptions and pre-checks
// are performed prior to this call
pub(crate) fn to_secp(&mut self) -> SecpRecoverableSignature {
let v = (self.as_mut()[32] >> 7) as i32;
self.truncate_recovery_id();
let v = RecoveryId::from_i32(v)
.unwrap_or_else(|_| RecoveryId::from_i32(0).expect("0 is infallible recovery ID"));
let signature = SecpRecoverableSignature::from_compact(self.as_ref(), v).unwrap_or_else(|_| {
SecpRecoverableSignature::from_compact(&[0u8; 64], v).expect("Zeroed signature is infallible")
});
signature
}
pub(crate) fn from_secp(signature: SecpRecoverableSignature) -> Self {
let (v, mut signature) = signature.serialize_compact();
let v = v.to_i32();
signature[32] |= (v << 7) as u8;
// Safety: the security of this call reflects the security of secp256k1 FFI
unsafe { Signature::from_bytes_unchecked(signature) }
}
/// Truncate the recovery id from the signature, producing a valid `secp256k1`
/// representation.
pub(crate) fn truncate_recovery_id(&mut self) {
self.as_mut()[32] &= 0x7f;
}
/// Sign a given message and compress the `v` to the signature
///
/// The compression scheme is described in
/// <https://github.com/FuelLabs/fuel-specs/blob/master/src/protocol/cryptographic_primitives.md>
pub fn sign(secret: &SecretKey, message: &Message) -> Self {
let secret = secret.borrow();
let message = message.to_secp();
let signature = SIGNING_SECP.sign_ecdsa_recoverable(&message, secret);
Signature::from_secp(signature)
}
/// Recover the public key from a signature performed with
/// [`Signature::sign`]
///
/// It takes the signature as owned because this operation is not idempotent. The taken
/// signature will not be recoverable. Signatures are meant to be single use, so this
/// avoids unnecessary copy.
pub fn recover(mut self, message: &Message) -> Result<PublicKey, Error> {
let signature = self.to_secp();
let message = message.to_secp();
let pk = RECOVER_SECP
.recover_ecdsa(&message, &signature)
.map(|pk| PublicKey::from_secp(&pk))?;
Ok(pk)
}
/// Verify a signature produced by [`Signature::sign`]
///
/// It takes the signature as owned because this operation is not idempotent. The taken
/// signature will not be recoverable. Signatures are meant to be single use, so this
/// avoids unnecessary copy.
pub fn verify(self, pk: &PublicKey, message: &Message) -> Result<(), Error> {
// TODO evaluate if its worthy to use native verify
//
// https://github.com/FuelLabs/fuel-crypto/issues/4
self.recover(message)
.and_then(|pk_p| (pk == &pk_p).then_some(()).ok_or(Error::InvalidSignature))
}
}
}