use k256::ecdsa::signature::hazmat::PrehashVerifier;
use k256::ecdsa::{self, RecoveryId, VerifyingKey};
use crate::ec::private_key::PrivateKey;
use crate::ec::public_key::PublicKey;
use crate::PrimitivesError;
const CURVE_ORDER: [u8; 32] = [
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36,
0x41, 0x41,
];
const HALF_ORDER: [u8; 32] = [
0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x5D, 0x57, 0x6E, 0x73, 0x57, 0xA4, 0x50, 0x1D, 0xDF, 0xE9, 0x2F, 0x46, 0x68, 0x1B,
0x20, 0xA0,
];
#[derive(Clone, Debug)]
pub struct Signature {
r: [u8; 32],
s: [u8; 32],
}
impl Signature {
pub fn new(r: [u8; 32], s: [u8; 32]) -> Self {
Signature { r, s }
}
pub fn r(&self) -> &[u8; 32] {
&self.r
}
pub fn s(&self) -> &[u8; 32] {
&self.s
}
pub fn from_der(bytes: &[u8]) -> Result<Self, PrimitivesError> {
if bytes.len() < 8 {
return Err(PrimitivesError::InvalidSignature(
"malformed signature: too short".to_string(),
));
}
if bytes[0] != 0x30 {
return Err(PrimitivesError::InvalidSignature(
"malformed signature: no header magic".to_string(),
));
}
let sig_len = bytes[1] as usize;
if sig_len + 2 > bytes.len() || sig_len + 2 < 8 {
return Err(PrimitivesError::InvalidSignature(
"malformed signature: bad length".to_string(),
));
}
let data = &bytes[..sig_len + 2];
let mut idx = 2;
if data[idx] != 0x02 {
return Err(PrimitivesError::InvalidSignature(
"malformed signature: no 1st int marker".to_string(),
));
}
idx += 1;
let r_len = data[idx] as usize;
idx += 1;
if r_len == 0 || idx + r_len > data.len() - 3 {
return Err(PrimitivesError::InvalidSignature(
"malformed signature: bogus R length".to_string(),
));
}
let r_bytes = &data[idx..idx + r_len];
idx += r_len;
if data[idx] != 0x02 {
return Err(PrimitivesError::InvalidSignature(
"malformed signature: no 2nd int marker".to_string(),
));
}
idx += 1;
let s_len = data[idx] as usize;
idx += 1;
if s_len == 0 || idx + s_len > data.len() {
return Err(PrimitivesError::InvalidSignature(
"malformed signature: bogus S length".to_string(),
));
}
let s_bytes = &data[idx..idx + s_len];
let r = to_32_bytes(r_bytes)?;
let s = to_32_bytes(s_bytes)?;
if is_zero(&r) {
return Err(PrimitivesError::InvalidSignature(
"signature R is zero".to_string(),
));
}
if is_zero(&s) {
return Err(PrimitivesError::InvalidSignature(
"signature S is zero".to_string(),
));
}
if !is_less_than(&r, &CURVE_ORDER) {
return Err(PrimitivesError::InvalidSignature(
"signature R is >= curve.N".to_string(),
));
}
if !is_less_than(&s, &CURVE_ORDER) {
return Err(PrimitivesError::InvalidSignature(
"signature S is >= curve.N".to_string(),
));
}
Ok(Signature { r, s })
}
pub fn to_der(&self) -> Vec<u8> {
let s = if is_greater_than(&self.s, &HALF_ORDER) {
subtract_from_order(&self.s)
} else {
self.s
};
let rb = canonicalize_int(&self.r);
let sb = canonicalize_int(&s);
let total_len = 6 + rb.len() + sb.len();
let mut out = Vec::with_capacity(total_len);
out.push(0x30);
out.push((total_len - 2) as u8);
out.push(0x02);
out.push(rb.len() as u8);
out.extend_from_slice(&rb);
out.push(0x02);
out.push(sb.len() as u8);
out.extend_from_slice(&sb);
out
}
pub fn from_compact(bytes: &[u8]) -> Result<Self, PrimitivesError> {
if bytes.len() != 65 {
return Err(PrimitivesError::InvalidSignature(
"invalid compact signature size".to_string(),
));
}
let mut r = [0u8; 32];
let mut s = [0u8; 32];
r.copy_from_slice(&bytes[1..33]);
s.copy_from_slice(&bytes[33..65]);
Ok(Signature { r, s })
}
pub fn to_compact(&self, hash: &[u8], priv_key: &PrivateKey) -> Vec<u8> {
let signing_key = priv_key.signing_key();
if let Ok((k256_sig, recovery_id)) =
signing_key.sign_prehash_recoverable(hash)
{
let mut result = vec![0u8; 65];
let recid_byte = 27 + recovery_id.to_byte() + 4; result[0] = recid_byte;
let (r_bytes, s_bytes) = k256_sig.split_bytes();
result[1..33].copy_from_slice(&r_bytes);
result[33..65].copy_from_slice(&s_bytes);
return result;
}
let mut result = vec![0u8; 65];
result[0] = 27 + 4; result[1..33].copy_from_slice(&self.r);
result[33..65].copy_from_slice(&self.s);
result
}
fn normalize_hash(hash: &[u8]) -> [u8; 32] {
let mut padded = [0u8; 32];
if hash.len() >= 32 {
padded.copy_from_slice(&hash[..32]);
} else {
padded[32 - hash.len()..].copy_from_slice(hash);
}
padded
}
pub fn sign(hash: &[u8], priv_key: &PrivateKey) -> Result<Self, PrimitivesError> {
let signing_key = priv_key.signing_key();
let padded = Self::normalize_hash(hash);
let (k256_sig, _recovery_id) = signing_key
.sign_prehash_recoverable(&padded)
?;
let (r_bytes, s_bytes) = k256_sig.split_bytes();
let mut r = [0u8; 32];
let mut s = [0u8; 32];
r.copy_from_slice(&r_bytes);
s.copy_from_slice(&s_bytes);
if is_greater_than(&s, &HALF_ORDER) {
s = subtract_from_order(&s);
}
Ok(Signature { r, s })
}
pub fn verify(&self, hash: &[u8], pub_key: &PublicKey) -> bool {
let k256_sig = match ecdsa::Signature::from_scalars(
k256::FieldBytes::from(self.r),
k256::FieldBytes::from(self.s),
) {
Ok(sig) => sig,
Err(_) => return false,
};
let padded = Self::normalize_hash(hash);
pub_key
.verifying_key()
.verify_prehash(&padded, &k256_sig)
.is_ok()
}
pub fn recover_public_key(
compact_sig: &[u8],
hash: &[u8],
) -> Result<PublicKey, PrimitivesError> {
if compact_sig.len() != 65 {
return Err(PrimitivesError::InvalidSignature(
"invalid compact signature size".to_string(),
));
}
let header = compact_sig[0];
let iteration = (header - 27) & !4u8;
let recovery_id = RecoveryId::from_byte(iteration)
.ok_or_else(|| PrimitivesError::InvalidSignature("invalid recovery id".to_string()))?;
let k256_sig = ecdsa::Signature::from_scalars(
*k256::FieldBytes::from_slice(&compact_sig[1..33]),
*k256::FieldBytes::from_slice(&compact_sig[33..65]),
)
?;
let padded = Self::normalize_hash(hash);
let recovered_key =
VerifyingKey::recover_from_prehash(&padded, &k256_sig, recovery_id)
?;
PublicKey::from_bytes(
recovered_key
.to_encoded_point(false)
.as_bytes(),
)
}
}
impl PartialEq for Signature {
fn eq(&self, other: &Self) -> bool {
self.r == other.r && self.s == other.s
}
}
impl Eq for Signature {}
fn canonicalize_int(val: &[u8; 32]) -> Vec<u8> {
let mut start = 0;
while start < 31 && val[start] == 0 {
start += 1;
}
let trimmed = &val[start..];
if trimmed.is_empty() {
return vec![0x00];
}
if trimmed[0] & 0x80 != 0 {
let mut out = Vec::with_capacity(trimmed.len() + 1);
out.push(0x00);
out.extend_from_slice(trimmed);
out
} else {
trimmed.to_vec()
}
}
fn to_32_bytes(bytes: &[u8]) -> Result<[u8; 32], PrimitivesError> {
let mut trimmed = bytes;
while trimmed.len() > 1 && trimmed[0] == 0 {
trimmed = &trimmed[1..];
}
if trimmed.len() > 32 {
return Err(PrimitivesError::InvalidSignature(
"integer value too large for 32 bytes".to_string(),
));
}
let mut out = [0u8; 32];
out[32 - trimmed.len()..].copy_from_slice(trimmed);
Ok(out)
}
fn is_zero(val: &[u8; 32]) -> bool {
val.iter().all(|&b| b == 0)
}
fn is_less_than(a: &[u8; 32], b: &[u8; 32]) -> bool {
for i in 0..32 {
if a[i] < b[i] {
return true;
}
if a[i] > b[i] {
return false;
}
}
false }
fn is_greater_than(a: &[u8; 32], b: &[u8; 32]) -> bool {
for i in 0..32 {
if a[i] > b[i] {
return true;
}
if a[i] < b[i] {
return false;
}
}
false }
fn subtract_from_order(val: &[u8; 32]) -> [u8; 32] {
let mut result = [0u8; 32];
let mut borrow: i32 = 0;
for i in (0..32).rev() {
let diff = CURVE_ORDER[i] as i32 - val[i] as i32 - borrow;
if diff < 0 {
result[i] = (diff + 256) as u8;
borrow = 1;
} else {
result[i] = diff as u8;
borrow = 0;
}
}
result
}
#[cfg(test)]
mod tests {
use super::*;
use crate::hash::sha256;
#[test]
fn test_signatures_der_parsing() {
let valid_sig: Vec<u8> = vec![
0x30, 0x44, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61,
0xa1, 0xd3, 0xa1, 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, 0x24, 0xc6,
0xc6, 0x15, 0x48, 0xab, 0x5f, 0xb8, 0xcd, 0x41, 0x02, 0x20, 0x18, 0x15, 0x22, 0xec,
0x8e, 0xca, 0x07, 0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, 0x83, 0x1c,
0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09,
];
assert!(Signature::from_der(&valid_sig).is_ok());
assert!(Signature::from_der(&[]).is_err());
let mut bad_magic = valid_sig.clone();
bad_magic[0] = 0x31;
assert!(Signature::from_der(&bad_magic).is_err());
let mut bad_marker = valid_sig.clone();
bad_marker[2] = 0x03;
assert!(Signature::from_der(&bad_marker).is_err());
}
#[test]
fn test_signature_serialize() {
let sig = Signature::new(
hex_to_32(
"4e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd41",
),
hex_to_32(
"181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d09",
),
);
let expected = hex::decode(
"304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd41\
0220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d09",
)
.unwrap();
assert_eq!(sig.to_der(), expected, "valid 1");
let sig = Signature::new(
hex_to_32(
"a196ed0e7ebcbe7b63fe1d8eecbdbde03a67ceba4fc8f6482bdcb9606a911404",
),
hex_to_32(
"971729c7fa944b465b35250c6570a2f31acbb14b13d1565fab7330dcb2b3dfb1",
),
);
let expected = hex::decode(
"3045022100a196ed0e7ebcbe7b63fe1d8eecbdbde03a67ceba4fc8f6482bdcb9606a911404\
022068e8d638056bb4b9a4cadaf39a8f5d0b9fe32b9b9b7749dc145f2db01d826190",
)
.unwrap();
assert_eq!(sig.to_der(), expected, "valid 4 - low-S normalization");
let sig = Signature::new([0u8; 32], [0u8; 32]);
let expected: Vec<u8> = vec![0x30, 0x06, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00];
assert_eq!(sig.to_der(), expected, "zero signature");
}
#[test]
fn test_rfc6979() {
let tests = vec![
(
"cca9fbcc1b41e5a95d369eaa6ddcff73b61a4efaa279cfc6567e8daa39cbaf50",
"sample",
"3045022100af340daf02cc15c8d5d08d7735dfe6b98a474ed373bdb5fbecf7571be52b384202205009fb27f37034a9b24b707b7c6b79ca23ddef9e25f7282e8a797efe53a8f124",
),
(
"0000000000000000000000000000000000000000000000000000000000000001",
"Satoshi Nakamoto",
"3045022100934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d802202442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5",
),
(
"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
"Satoshi Nakamoto",
"3045022100fd567d121db66e382991534ada77a6bd3106f0a1098c231e47993447cd6af2d002206b39cd0eb1bc8603e159ef5c20a5c8ad685a45b06ce9bebed3f153d10d93bed5",
),
(
"f8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181",
"Alan Turing",
"304402207063ae83e7f62bbb171798131b4a0564b956930092b33b07b395615d9ec7e15c022058dfcc1e00a35e1572f366ffe34ba0fc47db1e7189759b9fb233c5b05ab388ea",
),
(
"0000000000000000000000000000000000000000000000000000000000000001",
"All those moments will be lost in time, like tears in rain. Time to die...",
"30450221008600dbd41e348fe5c9465ab92d23e3db8b98b873beecd930736488696438cb6b0220547fe64427496db33bf66019dacbf0039c04199abb0122918601db38a72cfc21",
),
(
"e91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2",
"There is a computer disease that anybody who works with computers knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is that you 'play' with them!",
"3045022100b552edd27580141f3b2a5463048cb7cd3e047b97c9f98076c32dbdf85a68718b0220279fa72dd19bfae05577e06c7c0c1900c371fcd5893f7e1d56a37d30174671f6",
),
];
for (key_hex, msg, expected_sig_hex) in &tests {
let priv_key = PrivateKey::from_bytes(&hex::decode(key_hex).unwrap()).unwrap();
let hash = sha256(msg.as_bytes());
let sig = priv_key.sign(&hash).unwrap();
let sig_bytes = sig.to_der();
let expected_bytes = hex::decode(expected_sig_hex).unwrap();
assert_eq!(
hex::encode(&sig_bytes),
hex::encode(&expected_bytes),
"RFC6979 test for message '{}'",
msg
);
assert!(priv_key.pub_key().verify(&hash, &sig));
}
}
#[test]
fn test_sign_compact() {
for _ in 0..10 {
let priv_key = PrivateKey::new();
let hash = crate::hash::sha256d(b"test data for compact signature");
let sig = priv_key.sign(&hash).unwrap();
let compact = sig.to_compact(&hash, &priv_key);
assert_eq!(compact.len(), 65);
let recovered = Signature::recover_public_key(&compact, &hash).unwrap();
assert_eq!(
recovered.to_compressed(),
priv_key.pub_key().to_compressed(),
"recovered public key should match"
);
}
}
#[test]
fn test_signature_is_equal() {
let sig1 = Signature::new(
hex_to_32(
"4e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd41",
),
hex_to_32(
"181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d09",
),
);
let sig2 = Signature::new(
hex_to_32(
"a196ed0e7ebcbe7b63fe1d8eecbdbde03a67ceba4fc8f6482bdcb9606a911404",
),
hex_to_32(
"971729c7fa944b465b35250c6570a2f31acbb14b13d1565fab7330dcb2b3dfb1",
),
);
assert_eq!(sig1, sig1);
assert_ne!(sig1, sig2);
}
fn hex_to_32(s: &str) -> [u8; 32] {
let bytes = hex::decode(s).unwrap();
let mut out = [0u8; 32];
if bytes.len() > 32 {
out.copy_from_slice(&bytes[bytes.len() - 32..]);
} else {
out[32 - bytes.len()..].copy_from_slice(&bytes);
}
out
}
}