1use std::borrow::Cow;
6use std::error;
7
8use fvm_ipld_encoding::repr::*;
9use fvm_ipld_encoding::{Error as EncodingError, de, ser, strict_bytes};
10use num_derive::FromPrimitive;
11use num_traits::FromPrimitive;
12use thiserror::Error;
13
14use crate::address::Error as AddressError;
15
16pub const BLS_SIG_LEN: usize = 96;
18pub const BLS_PUB_LEN: usize = 48;
20
21pub const SECP_SIG_LEN: usize = 65;
23pub const SECP_PUB_LEN: usize = 65;
25pub const SECP_SIG_MESSAGE_HASH_SIZE: usize = 32;
27
28#[derive(
30 Clone, Debug, PartialEq, FromPrimitive, Copy, Eq, Serialize_repr, Deserialize_repr, Hash,
31)]
32#[repr(u8)]
33pub enum SignatureType {
34 Secp256k1 = 1,
35 BLS = 2,
36}
37
38#[derive(Clone, Debug, PartialEq, Eq, Hash)]
40pub struct Signature {
41 pub sig_type: SignatureType,
42 pub bytes: Vec<u8>,
43}
44
45impl ser::Serialize for Signature {
46 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
47 where
48 S: ser::Serializer,
49 {
50 let mut bytes = Vec::with_capacity(self.bytes.len() + 1);
51 bytes.push(self.sig_type as u8);
53 bytes.extend_from_slice(&self.bytes);
54
55 strict_bytes::Serialize::serialize(&bytes, serializer)
56 }
57}
58
59impl<'de> de::Deserialize<'de> for Signature {
60 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
61 where
62 D: de::Deserializer<'de>,
63 {
64 let bytes: Cow<'de, [u8]> = strict_bytes::Deserialize::deserialize(deserializer)?;
65 if bytes.is_empty() {
66 return Err(de::Error::custom("Cannot deserialize empty bytes"));
67 }
68
69 let sig_type = SignatureType::from_u8(bytes[0])
71 .ok_or_else(|| de::Error::custom("Invalid signature type byte (must be 1 or 2)"))?;
72
73 Ok(Signature {
74 bytes: bytes[1..].to_vec(),
75 sig_type,
76 })
77 }
78}
79
80impl Signature {
81 pub fn new_secp256k1(bytes: Vec<u8>) -> Self {
83 Self {
84 sig_type: SignatureType::Secp256k1,
85 bytes,
86 }
87 }
88
89 pub fn new_bls(bytes: Vec<u8>) -> Self {
91 Self {
92 sig_type: SignatureType::BLS,
93 bytes,
94 }
95 }
96
97 pub fn bytes(&self) -> &[u8] {
99 &self.bytes
100 }
101
102 pub fn signature_type(&self) -> SignatureType {
104 self.sig_type
105 }
106}
107
108#[cfg(feature = "arb")]
109impl quickcheck::Arbitrary for SignatureType {
110 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
111 if bool::arbitrary(g) {
112 SignatureType::Secp256k1
113 } else {
114 SignatureType::BLS
115 }
116 }
117}
118
119#[cfg(feature = "arb")]
120impl quickcheck::Arbitrary for Signature {
121 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
122 Self {
123 bytes: Vec::arbitrary(g),
124 sig_type: SignatureType::arbitrary(g),
125 }
126 }
127}
128
129#[cfg(feature = "crypto")]
130impl Signature {
131 pub fn verify(&self, data: &[u8], addr: &crate::address::Address) -> Result<(), String> {
133 verify(self.sig_type, &self.bytes, data, addr)
134 }
135}
136
137#[cfg(feature = "crypto")]
138pub fn verify(
139 sig_type: SignatureType,
140 sig_data: &[u8],
141 data: &[u8],
142 addr: &crate::address::Address,
143) -> Result<(), String> {
144 match sig_type {
145 SignatureType::BLS => self::ops::verify_bls_sig(sig_data, data, addr),
146 SignatureType::Secp256k1 => self::ops::verify_secp256k1_sig(sig_data, data, addr),
147 }
148}
149
150#[cfg(feature = "crypto")]
151pub mod ops {
152 use bls_signatures::{
153 PublicKey as BlsPubKey, Serialize, Signature as BlsSignature, verify_messages,
154 };
155 use k256::ecdsa::{RecoveryId, Signature as EcdsaSignature, VerifyingKey};
156
157 use super::{Error, SECP_PUB_LEN, SECP_SIG_LEN, SECP_SIG_MESSAGE_HASH_SIZE};
158 use crate::address::{Address, Protocol};
159
160 pub fn verify_bls_sig(signature: &[u8], data: &[u8], addr: &Address) -> Result<(), String> {
162 if addr.protocol() != Protocol::BLS {
163 return Err(format!(
164 "cannot validate a BLS signature against a {} address",
165 addr.protocol()
166 ));
167 }
168
169 let pub_k = addr.payload_bytes();
170
171 let pk = BlsPubKey::from_bytes(&pub_k).map_err(|e| e.to_string())?;
173
174 let sig = BlsSignature::from_bytes(signature).map_err(|e| e.to_string())?;
176
177 if verify_messages(&sig, &[data], &[pk]) {
179 Ok(())
180 } else {
181 Err(format!(
182 "bls signature verification failed for addr: {}",
183 addr
184 ))
185 }
186 }
187
188 pub fn verify_bls_aggregate(
191 aggregate_sig: &[u8; super::BLS_SIG_LEN],
192 pub_keys: &[[u8; super::BLS_PUB_LEN]],
193 plaintexts: &[&[u8]],
194 ) -> Result<bool, String> {
195 let (num_pub_keys, num_plaintexts) = (pub_keys.len(), plaintexts.len());
197 if num_pub_keys != num_plaintexts {
198 return Err(format!(
199 "unequal numbers of public keys ({num_pub_keys}) and plaintexts ({num_plaintexts})",
200 ));
201 }
202 if num_pub_keys == 0 {
203 return Ok(true);
204 }
205
206 let sig = BlsSignature::from_bytes(aggregate_sig)
208 .map_err(|_| "bls aggregate signature bytes are invalid G2 curve point".to_string())?;
209
210 let pub_keys = pub_keys
212 .iter()
213 .map(|pub_key| BlsPubKey::from_bytes(pub_key.as_slice()))
214 .collect::<Result<Vec<_>, _>>()
215 .map_err(|_| "bls public key bytes are invalid G2 curve point".to_string())?;
216
217 Ok(bls_signatures::verify_messages(&sig, plaintexts, &pub_keys))
218 }
219
220 pub fn verify_secp256k1_sig(
222 signature: &[u8],
223 data: &[u8],
224 addr: &Address,
225 ) -> Result<(), String> {
226 if addr.protocol() != Protocol::Secp256k1 {
227 return Err(format!(
228 "cannot validate a secp256k1 signature against a {} address",
229 addr.protocol()
230 ));
231 }
232
233 if signature.len() != SECP_SIG_LEN {
234 return Err(format!(
235 "Invalid Secp256k1 signature length. Was {}, must be 65",
236 signature.len()
237 ));
238 }
239
240 let hash = blake2b_simd::Params::new()
242 .hash_length(32)
243 .to_state()
244 .update(data)
245 .finalize();
246
247 let mut sig = [0u8; SECP_SIG_LEN];
249 sig[..].copy_from_slice(signature);
250 let rec_addr = ecrecover(hash.as_bytes().try_into().expect("fixed array size"), &sig)
251 .map_err(|e| e.to_string())?;
252
253 if &rec_addr == addr {
255 Ok(())
256 } else {
257 Err("Secp signature verification failed".to_owned())
258 }
259 }
260
261 pub fn recover_secp_public_key(
263 hash: &[u8; SECP_SIG_MESSAGE_HASH_SIZE],
264 signature: &[u8; SECP_SIG_LEN],
265 ) -> Result<[u8; SECP_PUB_LEN], Error> {
266 let mut rec_byte = signature[64];
268
269 let mut signature = EcdsaSignature::from_slice(&signature[..64])
271 .map_err(|e| Error::SigningError(format!("Invalid signature: {}", e)))?;
272
273 if let Some(normalized) = signature.normalize_s() {
275 signature = normalized;
276 rec_byte ^= 1;
277 }
278
279 let recovery_id = RecoveryId::try_from(rec_byte)
281 .map_err(|e| Error::InvalidRecovery(format!("Invalid recovery ID: {}", e)))?;
282
283 let pk = VerifyingKey::recover_from_prehash(&hash[..], &signature, recovery_id)
285 .map_err(|e| Error::InvalidRecovery(format!("Failed to recover key: {}", e)))?;
286 Ok(pk
287 .to_encoded_point(false)
288 .as_bytes()
289 .try_into()
290 .expect("expected the key to be 65 bytes"))
291 }
292
293 pub fn ecrecover(hash: &[u8; 32], signature: &[u8; SECP_SIG_LEN]) -> Result<Address, Error> {
295 let key = recover_secp_public_key(hash, signature)?;
297 let addr = Address::new_secp256k1(&key)?;
298 Ok(addr)
299 }
300}
301
302#[cfg(all(test, feature = "crypto"))]
303mod tests {
304 use bls_signatures::{PrivateKey, Serialize, Signature as BlsSignature};
305 use k256::ecdsa::SigningKey;
306 use multihash_codetable::Code;
307 use multihash_codetable::MultihashDigest;
308 use rand::{Rng, SeedableRng};
309 use rand_chacha::ChaCha8Rng;
310
311 use super::ops::recover_secp_public_key;
312 use super::*;
313 use crate::Address;
314 use crate::crypto::signature::ops::{ecrecover, verify_bls_aggregate};
315
316 #[test]
317 fn bls_agg_verify() {
318 let num_sigs = 10;
320 let message_length = num_sigs * 64;
321
322 let rng = &mut ChaCha8Rng::seed_from_u64(11);
323
324 let msg = (0..message_length)
325 .map(|_| rng.r#gen())
326 .collect::<Vec<u8>>();
327 let data: Vec<&[u8]> = (0..num_sigs).map(|x| &msg[x * 64..(x + 1) * 64]).collect();
328
329 let private_keys: Vec<PrivateKey> =
330 (0..num_sigs).map(|_| PrivateKey::generate(rng)).collect();
331 let public_keys: Vec<[u8; BLS_PUB_LEN]> = private_keys
332 .iter()
333 .map(|x| {
334 x.public_key()
335 .as_bytes()
336 .try_into()
337 .expect("public key bytes to array conversion should not fail")
338 })
339 .collect();
340
341 let signatures: Vec<BlsSignature> = (0..num_sigs)
342 .map(|x| private_keys[x].sign(data[x]))
343 .collect();
344
345 let agg_sig: [u8; BLS_SIG_LEN] = bls_signatures::aggregate(&signatures)
346 .expect("bls signature aggregation should not fail")
347 .as_bytes()
348 .try_into()
349 .expect("bls aggregate signature to bytes array should not fail");
350
351 assert!(verify_bls_aggregate(&agg_sig, &public_keys, &data).unwrap());
352 }
353
354 #[test]
355 fn recover_pubkey() {
356 let rng = &mut ChaCha8Rng::seed_from_u64(8);
357
358 let signing_key = SigningKey::random(rng);
360 let verifying_key = signing_key.verifying_key();
361
362 let hash: [u8; 32] = blake2b_simd::Params::new()
363 .hash_length(32)
364 .to_state()
365 .update(&[42, 43])
366 .finalize()
367 .as_bytes()
368 .try_into()
369 .expect("fixed array size");
370
371 let (signature, recovery_id) = signing_key
373 .sign_prehash_recoverable(&hash)
374 .expect("signing should not fail");
375
376 let mut sig_bytes = [0u8; 65];
378 sig_bytes[..64].copy_from_slice(&signature.to_bytes());
379 sig_bytes[64] = recovery_id.to_byte();
380
381 let recovered_key = recover_secp_public_key(&hash, &sig_bytes).unwrap();
383 let encoded_point = verifying_key.to_encoded_point(false);
384 let target_key = encoded_point.as_bytes();
385 assert_eq!(target_key, &recovered_key[..]);
386 }
387
388 #[test]
390 fn secp_ecrecover_testvector() {
391 let hash: [u8; 32] =
392 hex::decode("18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c")
393 .unwrap()
394 .try_into()
395 .unwrap();
396 let recbyte = 0x1c - 27;
397 let r = hex::decode("73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f")
398 .unwrap();
399 let s = hex::decode("eeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549")
400 .unwrap();
401 let expected = hex::decode("a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap();
402
403 let mut sig = [0u8; SECP_SIG_LEN];
404 sig[..32].copy_from_slice(&r);
405 sig[32..64].copy_from_slice(&s);
406 sig[64] = recbyte;
407
408 let recovered = recover_secp_public_key(&hash, &sig).unwrap();
409 let hashed = Code::Keccak256.digest(&recovered[1..]);
410 assert_eq!(expected, &hashed.digest()[12..]);
411 }
412
413 #[test]
414 fn secp_ecrecover() {
415 let rng = &mut ChaCha8Rng::seed_from_u64(8);
416
417 let signing_key = SigningKey::random(rng);
419 let verifying_key = signing_key.verifying_key();
420
421 let encoded_point = verifying_key.to_encoded_point(false);
423 let secp_addr = Address::new_secp256k1(encoded_point.as_bytes()).unwrap();
424
425 let hash: [u8; 32] = blake2b_simd::Params::new()
426 .hash_length(32)
427 .to_state()
428 .update(&[8, 8])
429 .finalize()
430 .as_bytes()
431 .try_into()
432 .expect("fixed array size");
433
434 let (signature, recovery_id) = signing_key
436 .sign_prehash_recoverable(&hash)
437 .expect("signing should not fail");
438
439 let mut sig_bytes = [0u8; 65];
441 sig_bytes[..64].copy_from_slice(&signature.to_bytes());
442 sig_bytes[64] = recovery_id.to_byte();
443
444 assert_eq!(ecrecover(&hash, &sig_bytes).unwrap(), secp_addr);
445 }
446}
447
448#[derive(Debug, PartialEq, Eq, Error)]
450pub enum Error {
451 #[error("Failed to sign data {0}")]
453 SigningError(String),
454 #[error("Could not recover public key from signature: {0}")]
456 InvalidRecovery(String),
457 #[error("Invalid generated pub key to create address: {0}")]
459 InvalidPubKey(#[from] AddressError),
460}
461
462impl From<Box<dyn error::Error>> for Error {
463 fn from(err: Box<dyn error::Error>) -> Error {
464 Error::SigningError(err.to_string())
466 }
467}
468
469impl From<EncodingError> for Error {
470 fn from(err: EncodingError) -> Error {
471 Error::SigningError(err.to_string())
473 }
474}