1use std::marker::PhantomData;
2
3use hopr_primitive_types::prelude::{GeneralError::ParseError, *};
4use sha2::Sha512;
5
6use crate::prelude::*;
7
8const ECDSA_SIGNATURE_SIZE: usize = 64;
9
10type RawSignature = ([u8; ECDSA_SIGNATURE_SIZE], u8);
11
12pub trait EcdsaEngine {
14 fn sign_hash(hash: &Hash, chain_keypair: &ChainKeypair) -> Result<RawSignature, CryptoError>;
16 fn verify_hash(signature: &RawSignature, hash: &Hash, public_key: &PublicKey) -> Result<bool, CryptoError>;
18 fn recover_from_hash(signature: &RawSignature, hash: &Hash) -> Result<PublicKey, CryptoError>;
20}
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26pub struct K256EcdsaSigningEngine;
27impl EcdsaEngine for K256EcdsaSigningEngine {
28 fn sign_hash(hash: &Hash, chain_keypair: &ChainKeypair) -> Result<RawSignature, CryptoError> {
29 let key = k256::ecdsa::SigningKey::from_bytes(chain_keypair.secret().as_ref().into())
30 .map_err(|_| CryptoError::InvalidInputValue("chain_keypair"))?;
31 let (sig, rec) = key
32 .sign_prehash_recoverable(hash.as_ref())
33 .map_err(|_| CryptoError::CalculationError)?;
34
35 Ok((sig.to_bytes().into(), rec.to_byte()))
36 }
37
38 fn verify_hash(signature: &RawSignature, hash: &Hash, public_key: &PublicKey) -> Result<bool, CryptoError> {
39 use k256::ecdsa::signature::hazmat::PrehashVerifier;
40
41 let pub_key = k256::ecdsa::VerifyingKey::from_sec1_bytes(&public_key.to_uncompressed_bytes())
42 .map_err(|_| CryptoError::InvalidInputValue("public key"))?;
43
44 if let Ok(signature) = k256::ecdsa::Signature::try_from(signature.0.as_ref()) {
45 Ok(pub_key.verify_prehash(hash.as_ref(), &signature).is_ok())
46 } else {
47 Err(CryptoError::InvalidInputValue("signature"))
48 }
49 }
50
51 fn recover_from_hash(signature: &RawSignature, hash: &Hash) -> Result<PublicKey, CryptoError> {
52 let sig = k256::ecdsa::Signature::from_bytes(&signature.0.into())
53 .map_err(|_| CryptoError::InvalidInputValue("signature.sig"))?;
54
55 let recid = k256::ecdsa::RecoveryId::try_from(signature.1)
56 .map_err(|_| CryptoError::InvalidInputValue("signature.recid"))?;
57
58 let recovered_key = k256::ecdsa::VerifyingKey::recover_from_prehash(hash.as_ref(), &sig, recid)
59 .map_err(|_| CryptoError::SignatureVerification)?;
60
61 (*recovered_key.as_affine()).try_into()
62 }
63}
64
65#[derive(Clone, Copy, Debug, PartialEq, Eq)]
69pub struct NativeEcdsaSigningEngine;
70
71impl EcdsaEngine for NativeEcdsaSigningEngine {
72 fn sign_hash(hash: &Hash, chain_keypair: &ChainKeypair) -> Result<RawSignature, CryptoError> {
73 let sk = secp256k1::SecretKey::from_byte_array(chain_keypair.secret().clone().into())
74 .map_err(|_| CryptoError::InvalidInputValue("chain_keypair"))?;
75
76 let sig =
77 secp256k1::global::SECP256K1.sign_ecdsa_recoverable(secp256k1::Message::from_digest(hash.into()), &sk);
78 let (recid, sig) = sig.serialize_compact();
79 Ok((sig, i32::from(recid) as u8))
80 }
81
82 fn verify_hash(signature: &RawSignature, hash: &Hash, public_key: &PublicKey) -> Result<bool, CryptoError> {
83 let pk = secp256k1::PublicKey::from_slice(&public_key.to_uncompressed_bytes())
84 .map_err(|_| CryptoError::InvalidInputValue("public key"))?;
85
86 let sig = secp256k1::ecdsa::Signature::from_compact(&signature.0)
87 .map_err(|_| CryptoError::InvalidInputValue("signature"))?;
88
89 Ok(secp256k1::global::SECP256K1
90 .verify_ecdsa(secp256k1::Message::from_digest(hash.into()), &sig, &pk)
91 .is_ok())
92 }
93
94 fn recover_from_hash(signature: &RawSignature, hash: &Hash) -> Result<PublicKey, CryptoError> {
95 let sig = secp256k1::ecdsa::RecoverableSignature::from_compact(
96 &signature.0,
97 secp256k1::ecdsa::RecoveryId::from_u8_masked(signature.1),
98 )
99 .map_err(|_| CryptoError::InvalidInputValue("signature"))?;
100
101 let pk = secp256k1::global::SECP256K1
102 .recover_ecdsa(secp256k1::Message::from_digest(hash.into()), &sig)
103 .map_err(|_| CryptoError::SignatureVerification)?;
104
105 PublicKey::try_from(pk.serialize_uncompressed().as_ref()).map_err(|_| CryptoError::CalculationError)
106 }
107}
108
109#[derive(Clone, Copy, Debug, PartialEq, Eq)]
121#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
122pub struct ChainSignature<E>(
123 #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))] [u8; ECDSA_SIGNATURE_SIZE],
124 PhantomData<E>,
125);
126
127impl<E: EcdsaEngine> ChainSignature<E> {
128 fn new(raw: RawSignature) -> Self {
129 let mut ret = Self(raw.0, PhantomData);
130
131 let parity = raw.1 & 0x01;
133 ret.0[Self::SIZE / 2] &= 0x7f;
134 ret.0[Self::SIZE / 2] |= parity << 7;
135
136 ret
137 }
138
139 pub fn raw_signature(&self) -> RawSignature {
142 let mut raw_sig = self.0;
143 let parity: u8 = (raw_sig[Self::SIZE / 2] & 0x80 != 0).into();
144 raw_sig[Self::SIZE / 2] &= 0x7f;
145 (raw_sig, parity)
146 }
147
148 #[inline]
150 pub fn sign_message(message: &[u8], chain_keypair: &ChainKeypair) -> Self {
151 Self::sign_hash(&Hash::create(&[message]), chain_keypair)
152 }
153
154 #[inline]
156 pub fn sign_hash(hash: &Hash, chain_keypair: &ChainKeypair) -> Self {
157 Self::new(
158 E::sign_hash(hash, chain_keypair).expect("signing cannot fail: keypair always contains a valid secret key"),
159 )
160 }
161
162 #[inline]
164 pub fn verify_message(&self, message: &[u8], public_key: &PublicKey) -> crate::errors::Result<bool> {
165 self.verify_hash(&Hash::create(&[message]), public_key)
166 }
167
168 #[inline]
170 pub fn verify_hash(&self, hash: &Hash, public_key: &PublicKey) -> crate::errors::Result<bool> {
171 E::verify_hash(&self.raw_signature(), hash, public_key)
172 }
173
174 #[inline]
176 pub fn recover_from_msg(&self, msg: &[u8]) -> crate::errors::Result<PublicKey> {
177 self.recover_from_hash(&Hash::create(&[msg]))
178 }
179
180 pub fn recover_from_hash(&self, hash: &Hash) -> crate::errors::Result<PublicKey> {
182 let (sig, parity) = self.raw_signature();
184 for alt in [0u8, 2u8] {
185 if let Ok(pk) = E::recover_from_hash(&(sig, parity | alt), hash) {
186 return Ok(pk);
187 }
188 }
189 Err(CryptoError::CalculationError)
190 }
191}
192
193impl<E> AsRef<[u8]> for ChainSignature<E> {
194 fn as_ref(&self) -> &[u8] {
195 &self.0
196 }
197}
198
199impl<E> TryFrom<&[u8]> for ChainSignature<E> {
200 type Error = GeneralError;
201
202 fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
203 Ok(Self(
204 value.try_into().map_err(|_| ParseError("Signature".into()))?,
205 PhantomData,
206 ))
207 }
208}
209
210impl<E> BytesRepresentable for ChainSignature<E> {
211 const SIZE: usize = ECDSA_SIGNATURE_SIZE;
212}
213
214#[cfg(not(feature = "rust-ecdsa"))]
215pub type Signature = ChainSignature<NativeEcdsaSigningEngine>;
216
217#[cfg(feature = "rust-ecdsa")]
218pub type Signature = ChainSignature<K256EcdsaSigningEngine>;
219
220#[derive(Clone, Copy, Debug, PartialEq, Eq)]
222#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
223pub struct OffchainSignature(#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))] [u8; Self::SIZE]);
224
225impl OffchainSignature {
226 pub fn sign_message(msg: &[u8], signing_keypair: &OffchainKeypair) -> Self {
228 let expanded_sk = ed25519_dalek::hazmat::ExpandedSecretKey::from(
230 &ed25519_dalek::SecretKey::try_from(signing_keypair.secret().as_ref())
231 .expect("cannot fail: OffchainKeypair always contains a valid secret key"),
232 );
233
234 let verifying = ed25519_dalek::VerifyingKey::from(signing_keypair.public().edwards);
237
238 ed25519_dalek::hazmat::raw_sign::<Sha512>(&expanded_sk, msg, &verifying).into()
239 }
240
241 pub fn verify_message(&self, msg: &[u8], public_key: &OffchainPublicKey) -> bool {
243 let sgn = ed25519_dalek::Signature::from_slice(&self.0)
244 .expect("cannot fail: OffchainSignature always contains a valid signature");
245 let pk = ed25519_dalek::VerifyingKey::from(public_key.edwards);
246 pk.verify_strict(msg, &sgn).is_ok()
247 }
248
249 pub fn verify_batch<'a, I: IntoIterator<Item = ((&'a [u8], OffchainSignature), OffchainPublicKey)>>(
251 entries: I,
252 ) -> bool {
253 let (signed_msgs, pub_keys): (Vec<(&[u8], OffchainSignature)>, Vec<ed25519_dalek::VerifyingKey>) = entries
254 .into_iter()
255 .map(|(a, b)| (a, ed25519_dalek::VerifyingKey::from(b.edwards)))
256 .unzip();
257
258 let (msgs, signatures): (Vec<&[u8]>, Vec<ed25519_dalek::Signature>) = signed_msgs
259 .into_iter()
260 .map(|(a, b)| (a, ed25519_dalek::Signature::from_bytes(&b.0)))
261 .unzip();
262
263 ed25519_dalek::verify_batch(&msgs, &signatures, &pub_keys).is_ok()
264 }
265}
266
267impl AsRef<[u8]> for OffchainSignature {
268 fn as_ref(&self) -> &[u8] {
269 &self.0
270 }
271}
272
273impl TryFrom<&[u8]> for OffchainSignature {
274 type Error = GeneralError;
275
276 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
277 Ok(ed25519_dalek::Signature::from_slice(value)
278 .map_err(|_| ParseError("OffchainSignature".into()))?
279 .into())
280 }
281}
282
283impl BytesRepresentable for OffchainSignature {
284 const SIZE: usize = ed25519_dalek::Signature::BYTE_SIZE;
286}
287
288impl From<ed25519_dalek::Signature> for OffchainSignature {
289 fn from(value: ed25519_dalek::Signature) -> Self {
290 let mut ret = Self([0u8; Self::SIZE]);
291 ret.0.copy_from_slice(value.to_bytes().as_ref());
292 ret
293 }
294}
295
296impl TryFrom<([u8; 32], [u8; 32])> for OffchainSignature {
297 type Error = GeneralError;
298
299 fn try_from(value: ([u8; 32], [u8; 32])) -> std::result::Result<Self, Self::Error> {
300 Ok(ed25519_dalek::Signature::from_components(value.0, value.1).into())
301 }
302}
303
304#[cfg(test)]
305mod tests {
306 use ed25519_dalek::Signer;
307 use hex_literal::hex;
308
309 use super::*;
310 use crate::keypairs::Keypair;
311
312 const PRIVATE_KEY: [u8; 32] = hex!("e17fe86ce6e99f4806715b0c9412f8dad89334bf07f72d5834207a9d8f19d7f8");
313
314 #[test]
315 fn chain_signature_serialize() -> anyhow::Result<()> {
316 let msg = b"test000000";
317 let kp = ChainKeypair::from_secret(&PRIVATE_KEY)?;
318 let sgn = Signature::sign_message(msg, &kp);
319
320 let deserialized = Signature::try_from(sgn.as_ref())?;
321 assert_eq!(sgn, deserialized, "signatures don't match");
322
323 Ok(())
324 }
325
326 #[test]
327 fn chain_signature_engines_must_be_compatible() -> anyhow::Result<()> {
328 let msg = b"test12345";
329 let ck = ChainKeypair::random();
330 let sgn_1 = ChainSignature::<NativeEcdsaSigningEngine>::sign_message(msg, &ck);
331 let sgn_2 = ChainSignature::<K256EcdsaSigningEngine>::sign_message(msg, &ck);
332
333 let sgn_1_k256 = ChainSignature::<K256EcdsaSigningEngine>::try_from(sgn_1.as_ref())?;
334 let sgn_2_native = ChainSignature::<NativeEcdsaSigningEngine>::try_from(sgn_2.as_ref())?;
335
336 assert!(sgn_1_k256.verify_message(msg, ck.public())?);
337 assert!(sgn_2_native.verify_message(msg, ck.public())?);
338
339 Ok(())
340 }
341
342 #[test]
343 fn chain_signature_sign_and_recover() -> anyhow::Result<()> {
344 let msg = hex!("eff80b9f035b1d369c6a60f362ac7c8b8c3b61b76d151d1be535145ccaa3e83e");
345 let hash = Hash::create(&[&msg]);
346
347 let kp = ChainKeypair::from_secret(&PRIVATE_KEY)?;
348
349 let signature1 = Signature::sign_message(&msg, &kp);
350 let signature2 = Signature::sign_hash(&hash, &kp);
351
352 let pub_key1 = PublicKey::from_privkey(&PRIVATE_KEY)?;
353 let pub_key2 = signature1.recover_from_msg(&msg)?;
354 let pub_key3 = signature2.recover_from_hash(&hash)?;
355
356 assert_eq!(pub_key1, *kp.public());
357 assert_eq!(pub_key1, pub_key2, "recovered public key does not match");
358 assert_eq!(pub_key1, pub_key3, "recovered public key does not match");
359
360 assert!(
361 signature1.verify_message(&msg, &pub_key1)?,
362 "signature 1 verification failed with pub key 1"
363 );
364 assert!(
365 signature1.verify_message(&msg, &pub_key2)?,
366 "signature 1 verification failed with pub key 2"
367 );
368 assert!(
369 signature1.verify_message(&msg, &pub_key3)?,
370 "signature 1 verification failed with pub key 3"
371 );
372
373 assert!(
374 signature2.verify_hash(&hash, &pub_key1)?,
375 "signature 2 verification failed with pub key 1"
376 );
377 assert!(
378 signature2.verify_hash(&hash, &pub_key2)?,
379 "signature 2 verification failed with pub key 2"
380 );
381 assert!(
382 signature2.verify_hash(&hash, &pub_key3)?,
383 "signature 2 verification failed with pub key 3"
384 );
385
386 Ok(())
387 }
388
389 #[test]
390 fn offchain_signature_signing() -> anyhow::Result<()> {
391 let msg = b"test12345";
392 let keypair = OffchainKeypair::from_secret(&PRIVATE_KEY)?;
393
394 let key = ed25519_dalek::SecretKey::try_from(PRIVATE_KEY)?;
395 let kp = ed25519_dalek::SigningKey::from_bytes(&key);
396 let pk = ed25519_dalek::VerifyingKey::from(&kp);
397
398 let sgn = kp.sign(msg);
399 assert!(pk.verify_strict(msg, &sgn).is_ok(), "blomp");
400
401 let sgn_1 = OffchainSignature::sign_message(msg, &keypair);
402 let sgn_2 = OffchainSignature::try_from(sgn_1.as_ref())?;
403
404 assert!(
405 sgn_1.verify_message(msg, keypair.public()),
406 "cannot verify message via sig 1"
407 );
408 assert!(
409 sgn_2.verify_message(msg, keypair.public()),
410 "cannot verify message via sig 2"
411 );
412 assert_eq!(sgn_1, sgn_2, "signatures must be equal");
413
414 let keypair = OffchainKeypair::from_secret(&PRIVATE_KEY)?;
415 let sig = OffchainSignature::sign_message("my test msg".as_bytes(), &keypair);
416 assert!(sig.verify_message("my test msg".as_bytes(), keypair.public()));
417
418 Ok(())
419 }
420
421 #[test]
422 fn offchain_signature_batch_verify() -> anyhow::Result<()> {
423 let msgs = (0..100)
424 .map(|i| format!("test_msg_{i}").as_bytes().to_vec())
425 .collect::<Vec<_>>();
426
427 let tuples = (0..100)
428 .map(|i| {
429 let kp = OffchainKeypair::random();
430 let sig = OffchainSignature::sign_message(&msgs[i], &kp);
431 ((msgs[i].as_slice(), sig), *kp.public())
432 })
433 .collect::<Vec<_>>();
434
435 assert!(OffchainSignature::verify_batch(tuples));
436 Ok(())
437 }
438}