pallet_evm_precompile_p256verify/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
21
22extern crate alloc;
23
24use alloc::vec::Vec;
25use core::marker::PhantomData;
26use fp_evm::{
27 ExitError, ExitSucceed, Precompile, PrecompileHandle, PrecompileOutput, PrecompileResult,
28};
29use frame_support::{traits::Get, weights::Weight};
30use p256::ecdsa::{signature::hazmat::PrehashVerifier, Signature, VerifyingKey};
31
32pub struct P256Verify<W: Get<Weight>>(PhantomData<W>);
33
34impl<W: Get<Weight>> P256Verify<W> {
35 const INPUT_LENGTH: usize = 160;
37
38 #[inline]
40 fn handle_cost(handle: &mut impl PrecompileHandle) -> Result<(), ExitError> {
41 let weight = W::get();
42 handle.record_external_cost(Some(weight.ref_time()), None, None)
43 }
44
45 #[inline]
48 fn message_hash(input: &[u8]) -> &[u8] {
49 &input[..32]
50 }
51
52 #[inline]
54 fn signature(input: &[u8]) -> &[u8] {
55 &input[32..96]
56 }
57
58 #[inline]
60 fn public_key(input: &[u8]) -> &[u8] {
61 &input[96..160]
62 }
63
64 fn verify_from_input(input: &[u8]) -> Option<()> {
66 if input.len() != Self::INPUT_LENGTH {
73 return None;
74 }
75
76 let message_hash = Self::message_hash(input);
77 let signature = Self::signature(input);
78 let public_key = Self::public_key(input);
79
80 let mut uncompressed_pk = [0u8; 65];
81 uncompressed_pk[0] = 0x04;
83 uncompressed_pk[1..].copy_from_slice(public_key);
84
85 let signature = Signature::from_slice(signature).ok()?;
87
88 let public_key = VerifyingKey::from_sec1_bytes(&uncompressed_pk).ok()?;
89
90 public_key.verify_prehash(message_hash, &signature).ok()
91 }
92}
93
94impl<W: Get<Weight>> Precompile for P256Verify<W> {
97 fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult {
98 Self::handle_cost(handle)?;
99
100 let result = if Self::verify_from_input(handle.input()).is_some() {
101 let mut result = [0u8; 32];
103 result[31] = 1;
104
105 result.to_vec()
106 } else {
107 Vec::new()
109 };
110
111 Ok(PrecompileOutput {
112 exit_status: ExitSucceed::Returned,
113 output: result.to_vec(),
114 })
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121 use fp_evm::Context;
122 use frame_support::parameter_types;
123 use hex_literal::hex;
124 use precompile_utils::testing::MockHandle;
125
126 parameter_types! {
127 pub const DummyWeight: Weight = Weight::from_parts(3450, 0);
128 }
129
130 fn prepare_handle(input: Vec<u8>, cost: u64) -> impl PrecompileHandle {
131 let context: Context = Context {
132 address: Default::default(),
133 caller: Default::default(),
134 apparent_value: From::from(0),
135 };
136
137 let mut handle = MockHandle::new(Default::default(), context);
138 handle.input = input;
139 handle.gas_limit = cost;
140
141 handle
142 }
143
144 #[test]
145 fn test_valid_signature() {
146 let inputs = vec![
147 (
148 true,
149 hex!(
150 "b5a77e7a90aa14e0bf5f337f06f597148676424fae26e175c6e5621c34351955289f"
151 "319789da424845c9eac935245fcddd805950e2f02506d09be7e411199556d2621444"
152 "75b1fa46ad85250728c600c53dfd10f8b3f4adf140e27241aec3c2da3a81046703fc"
153 "cf468b48b145f939efdbb96c3786db712b3113bb2488ef286cdcef8afe82d200a5bb"
154 "36b5462166e8ce77f2d831a52ef2135b2af188110beaefb1"
155 )
156 .to_vec(),
157 ),
158 (
159 true,
160 hex!(
161 "4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73b"
162 "d4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03"
163 "009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c61"
164 "8202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4"
165 "ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e"
166 )
167 .to_vec(),
168 ),
169 (
170 false,
171 hex!(
172 "afec5769b5cf4e310a7d150508e82fb8e3eda1c2c94c61492d3bd8aea99e06c9e22466"
173 "e928fdccef0de49e3503d2657d00494a00e764fd437bdafa05f5922b1fbbb77c6817cc"
174 "f50748419477e843d5bac67e6a70e97dde5a57e0c983b777e1ad31a80482dadf89de63"
175 "02b1988c82c29544c9c07bb910596158f6062517eb089a2f54c9a0f348752950094d32"
176 "28d3b940258c75fe2a413cb70baa21dc2e352fc5"
177 )
178 .to_vec(),
179 ),
180 (
181 false,
182 hex!(
183 "3cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4"
184 "903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009d"
185 "f8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fc"
186 "fe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971"
187 "a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e"
188 )
189 .to_vec(),
190 ),
191 (false, hex!("4cee90eb86eaa050036147a12d49004b6a").to_vec()),
192 ];
193 for input in inputs {
194 let cost = 3450;
195 let mut handle = prepare_handle(input.1.clone(), cost);
196
197 let mut success_result = [0u8; 32];
198 success_result[31] = 1;
199
200 let unsuccessful_result = Vec::<u8>::new();
201
202 match (input.0, P256Verify::<DummyWeight>::execute(&mut handle)) {
203 (true, Ok(result)) => assert_eq!(result.output, success_result.to_vec()),
204 (false, Ok(result)) => assert_eq!(result.output, unsuccessful_result),
205 (_, Err(_)) => panic!("Test not expected to fail for input: {:?}", input),
206 }
207 }
208 }
209}