common_runtime/
signature.rs

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
use common_primitives::{node, signatures::UnifiedSignature, utils::wrap_binary_data};
use node::EIP712Encode;
use sp_runtime::{traits::Verify, AccountId32, MultiSignature};
extern crate alloc;
use crate::constants::CHAIN_ID;
use sp_core::Encode;

pub fn check_signature<P>(signature: &MultiSignature, signer: AccountId32, payload: &P) -> bool
where
	P: Encode + EIP712Encode,
{
	let unified_signature: UnifiedSignature = signature.clone().into();
	let scale_encoded = payload.encode();
	let verify_signature = |payload: &[u8]| unified_signature.verify(payload, &signer.clone());

	if verify_signature(&scale_encoded) {
		return true;
	}

	match unified_signature {
		// we don't need to check the wrapped bytes for ethereum signatures but we need to check EIP-712 ones
		UnifiedSignature::Ecdsa(_) => verify_signature(&payload.encode_eip_712(CHAIN_ID)),
		_ => {
			let wrapped_payload = wrap_binary_data(scale_encoded);
			verify_signature(&wrapped_payload)
		},
	}
}

#[cfg(test)]
mod tests {
	use super::*;
	use common_primitives::signatures::UnifiedSigner;
	use sp_core::{ecdsa, keccak_256, sr25519, Pair};
	use sp_runtime::traits::IdentifyAccount;
	/// A wrapped vec that allow different encodings for signature checks
	#[derive(Clone, Debug, Encode)]
	pub struct TestArrayWrapper(pub [u8; 12]);

	impl EIP712Encode for TestArrayWrapper {
		fn encode_eip_712(&self, _chain_id: u32) -> Box<[u8]> {
			// not used in test but required to be implemented
			Vec::new().into_boxed_slice()
		}
	}

	#[test]
	fn test_verify_signature_with_wrapped_bytes() {
		let (key_pair_delegator, _) = sr25519::Pair::generate();

		let payload = b"test_payload";
		let encode_add_provider_data = wrap_binary_data(payload.to_vec());

		let signature: MultiSignature = key_pair_delegator.sign(&encode_add_provider_data).into();

		assert!(check_signature(
			&signature,
			key_pair_delegator.public().into(),
			&TestArrayWrapper(payload.clone())
		));
	}

	#[test]
	fn test_verify_signature_without_wrapped_bytes() {
		let (signer, _) = sr25519::Pair::generate();

		let payload = b"test_payload";

		let signature: MultiSignature = signer.sign(payload.as_slice()).into();

		assert!(check_signature(
			&signature,
			signer.public().into(),
			&TestArrayWrapper(payload.clone())
		));
	}

	#[test]
	fn test_check_signature_with_invalid_signature() {
		let (signer, _) = sr25519::Pair::generate();

		let payload = b"test_payload";

		let signature: MultiSignature = signer.sign(payload.as_slice()).into();

		let invalid_payload = b"fake_payload";

		assert!(!check_signature(
			&signature,
			signer.public().into(),
			&TestArrayWrapper(invalid_payload.clone())
		));
	}

	#[test]
	fn test_ethereum_verify_signature_without_wrapped_bytes_should_work() {
		let (signer, _) = ecdsa::Pair::generate();

		let payload = b"test_payload";

		let signature: MultiSignature =
			signer.sign_prehashed(&keccak_256(&payload.to_vec())).into();
		let unified_signer = UnifiedSigner::from(signer.public());

		assert!(check_signature(
			&signature,
			unified_signer.into_account(),
			&TestArrayWrapper(payload.clone())
		));
	}

	#[test]
	fn test_ethereum_verify_signature_wrapped_bytes_should_fail() {
		let (signer, _) = ecdsa::Pair::generate();

		let payload = b"test_payload";
		let encode_add_provider_data = wrap_binary_data(payload.to_vec());
		let signature: MultiSignature =
			signer.sign_prehashed(&keccak_256(&encode_add_provider_data)).into();
		let unified_signer = UnifiedSigner::from(signer.public());

		assert!(!check_signature(
			&signature,
			unified_signer.into_account(),
			&TestArrayWrapper(payload.clone())
		));
	}
}