1use aurora_evm::{Context, ExitError};
6use p256::{
7 EncodedPoint,
8 ecdsa::{Signature, VerifyingKey, signature::hazmat::PrehashVerifier},
9};
10
11use crate::prelude::types::{Address, EthGas, make_address};
12use crate::{EvmPrecompileResult, Precompile, PrecompileOutput, Vec};
13
14pub const P256VERIFY_BASE_GAS_FEE: u64 = 6900;
16
17const INPUT_LENGTH: usize = 160;
19
20const SUCCESS_RESULT: [u8; 32] = [
22 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
23];
24
25pub struct Secp256r1;
26
27impl Secp256r1 {
28 pub const ADDRESS: Address = make_address(0, 0x100);
29
30 fn execute(input: &[u8]) -> Option<Vec<u8>> {
50 if input.len() != INPUT_LENGTH {
52 return None;
53 }
54
55 let h_bytes = &input[0..32];
58 let r_bytes = &input[32..64];
60 let s_bytes = &input[64..96];
62 let qx_bytes = &input[96..128];
64 let qy_bytes = &input[128..160];
66
67 let signature = Signature::from_scalars(
71 *p256::FieldBytes::from_slice(r_bytes),
72 *p256::FieldBytes::from_slice(s_bytes),
73 )
74 .ok()?;
75
76 let encoded_point =
86 EncodedPoint::from_affine_coordinates(qx_bytes.into(), qy_bytes.into(), false);
87 let public_key = VerifyingKey::from_encoded_point(&encoded_point).ok()?;
88
89 if public_key.verify_prehash(h_bytes, &signature).is_ok() {
100 Some(Vec::from(&SUCCESS_RESULT[..]))
102 } else {
103 None
105 }
106 }
107}
108
109impl Precompile for Secp256r1 {
110 fn required_gas(_input: &[u8]) -> Result<EthGas, ExitError>
111 where
112 Self: Sized,
113 {
114 Ok(EthGas::new(P256VERIFY_BASE_GAS_FEE))
115 }
116
117 fn run(
118 &self,
119 input: &[u8],
120 target_gas: Option<EthGas>,
121 _context: &Context,
122 _is_static: bool,
123 ) -> EvmPrecompileResult {
124 let cost = Self::required_gas(input)?;
125 if let Some(target_gas) = target_gas {
126 if cost > target_gas {
127 return Err(ExitError::OutOfGas);
128 }
129 }
130
131 let output = Self::execute(input).unwrap_or_default();
133 Ok(PrecompileOutput::without_logs(cost, output))
134 }
135}
136
137#[cfg(test)]
138mod test {
139 use super::*;
140
141 fn context() -> Context {
142 Context {
143 address: Secp256r1::ADDRESS.raw(),
144 caller: Secp256r1::ADDRESS.raw(),
145 apparent_value: 0u128.into(),
146 }
147 }
148
149 #[test]
151 fn test_sig_verify() {
152 let inputs = vec![
153 (
154 "4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e",
155 true,
156 ),
157 (
158 "3fec5769b5cf4e310a7d150508e82fb8e3eda1c2c94c61492d3bd8aea99e06c9e22466e928fdccef0de49e3503d2657d00494a00e764fd437bdafa05f5922b1fbbb77c6817ccf50748419477e843d5bac67e6a70e97dde5a57e0c983b777e1ad31a80482dadf89de6302b1988c82c29544c9c07bb910596158f6062517eb089a2f54c9a0f348752950094d3228d3b940258c75fe2a413cb70baa21dc2e352fc5",
159 true,
160 ),
161 (
162 "e775723953ead4a90411a02908fd1a629db584bc600664c609061f221ef6bf7c440066c8626b49daaa7bf2bcc0b74be4f7a1e3dcf0e869f1542fe821498cbf2de73ad398194129f635de4424a07ca715838aefe8fe69d1a391cfa70470795a80dd056866e6e1125aff94413921880c437c9e2570a28ced7267c8beef7e9b2d8d1547d76dfcf4bee592f5fefe10ddfb6aeb0991c5b9dbbee6ec80d11b17c0eb1a",
163 true,
164 ),
165 (
166 "b5a77e7a90aa14e0bf5f337f06f597148676424fae26e175c6e5621c34351955289f319789da424845c9eac935245fcddd805950e2f02506d09be7e411199556d262144475b1fa46ad85250728c600c53dfd10f8b3f4adf140e27241aec3c2da3a81046703fccf468b48b145f939efdbb96c3786db712b3113bb2488ef286cdcef8afe82d200a5bb36b5462166e8ce77f2d831a52ef2135b2af188110beaefb1",
167 true,
168 ),
169 (
170 "858b991cfd78f16537fe6d1f4afd10273384db08bdfc843562a22b0626766686f6aec8247599f40bfe01bec0e0ecf17b4319559022d4d9bf007fe929943004eb4866760dedf31b7c691f5ce665f8aae0bda895c23595c834fecc2390a5bcc203b04afcacbb4280713287a2d0c37e23f7513fab898f2c1fefa00ec09a924c335d9b629f1d4fb71901c3e59611afbfea354d101324e894c788d1c01f00b3c251b2",
171 true,
172 ),
173 (
174 "3cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e",
175 false,
176 ),
177 (
178 "afec5769b5cf4e310a7d150508e82fb8e3eda1c2c94c61492d3bd8aea99e06c9e22466e928fdccef0de49e3503d2657d00494a00e764fd437bdafa05f5922b1fbbb77c6817ccf50748419477e843d5bac67e6a70e97dde5a57e0c983b777e1ad31a80482dadf89de6302b1988c82c29544c9c07bb910596158f6062517eb089a2f54c9a0f348752950094d3228d3b940258c75fe2a413cb70baa21dc2e352fc5",
179 false,
180 ),
181 (
182 "f775723953ead4a90411a02908fd1a629db584bc600664c609061f221ef6bf7c440066c8626b49daaa7bf2bcc0b74be4f7a1e3dcf0e869f1542fe821498cbf2de73ad398194129f635de4424a07ca715838aefe8fe69d1a391cfa70470795a80dd056866e6e1125aff94413921880c437c9e2570a28ced7267c8beef7e9b2d8d1547d76dfcf4bee592f5fefe10ddfb6aeb0991c5b9dbbee6ec80d11b17c0eb1a",
183 false,
184 ),
185 (
186 "c5a77e7a90aa14e0bf5f337f06f597148676424fae26e175c6e5621c34351955289f319789da424845c9eac935245fcddd805950e2f02506d09be7e411199556d262144475b1fa46ad85250728c600c53dfd10f8b3f4adf140e27241aec3c2da3a81046703fccf468b48b145f939efdbb96c3786db712b3113bb2488ef286cdcef8afe82d200a5bb36b5462166e8ce77f2d831a52ef2135b2af188110beaefb1",
187 false,
188 ),
189 (
190 "958b991cfd78f16537fe6d1f4afd10273384db08bdfc843562a22b0626766686f6aec8247599f40bfe01bec0e0ecf17b4319559022d4d9bf007fe929943004eb4866760dedf31b7c691f5ce665f8aae0bda895c23595c834fecc2390a5bcc203b04afcacbb4280713287a2d0c37e23f7513fab898f2c1fefa00ec09a924c335d9b629f1d4fb71901c3e59611afbfea354d101324e894c788d1c01f00b3c251b2",
191 false,
192 ),
193 ("4cee90eb86eaa050036147a12d49004b6a", false),
194 (
195 "4cee90eb86eaa050036147a12d49004b6a958b991cfd78f16537fe6d1f4afd10273384db08bdfc843562a22b0626766686f6aec8247599f40bfe01bec0e0ecf17b4319559022d4d9bf007fe929943004eb4866760dedf319",
196 false,
197 ),
198 (
199 "4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e00",
200 false,
201 ),
202 (
203 "4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e",
204 false,
205 ),
206 (
207 "4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
208 false,
209 ),
210 (
211 "b5a77e7a90aa14e0bf5f337f06f597148676424fae26e175c6e5621c34351955289f319789da424845c9eac935245fcddd805950e2f02506d09be7e411199556d262144475b1fa46ad85250728c600c53dfd10f8b3f4adf140e27241aec3c2daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaef8afe82d200a5bb36b5462166e8ce77f2d831a52ef2135b2af188110beaefb1",
212 false,
213 ),
214 ];
215 let p = Secp256r1;
216 for (input_hex, expect_success) in inputs {
217 let input = hex::decode(input_hex).unwrap();
218 let res = p.run(&input, None, &context(), false).unwrap();
219 if expect_success {
220 assert_eq!(
221 res.output,
222 SUCCESS_RESULT.to_vec(),
223 "Input hex: {input_hex}",
224 );
225 } else {
226 assert_eq!(res.output.len(), 0, "Input hex: {input_hex}");
227 }
228 }
229 }
230
231 #[test]
232 fn test_not_enough_gas_errors() {
233 let input_hex = "4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e";
234 let p = Secp256r1;
235
236 let input = hex::decode(input_hex).unwrap();
237 let err = p
238 .run(&input, Some(EthGas::new(2_500)), &context(), false)
239 .unwrap_err();
240 assert_eq!(err, ExitError::OutOfGas);
241 }
242
243 #[test]
244 fn test_eip7951_spec_compliance_edge_cases() {
245 let p = Secp256r1;
246
247 let valid_full_hex = "4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e";
248
249 let h = &valid_full_hex[0..64];
251 let r = &valid_full_hex[64..128];
252 let s = &valid_full_hex[128..192];
253 let qx = &valid_full_hex[192..256];
254 let qy = &valid_full_hex[256..320];
255
256 let val_p = "ffffffff00000001000000000000000000000000ffffffffffffffffffffffff";
258 let val_n = "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551";
259 let val_zero = "0000000000000000000000000000000000000000000000000000000000000000";
260
261 let failures = [
262 format!("{h}{r}{s}{val_zero}{val_zero}"),
265 format!("{h}{r}{s}{val_p}{qy}"),
268 format!("{h}{r}{s}{qx}{val_p}"),
271 format!("{h}{val_zero}{s}{qx}{qy}"),
274 format!("{h}{r}{val_zero}{qx}{qy}"),
277 format!("{h}{val_n}{s}{qx}{qy}"),
280 format!("{h}{r}{val_n}{qx}{qy}"),
283 ];
284
285 for (i, input_hex) in failures.iter().enumerate() {
286 let input = hex::decode(input_hex).unwrap_or_else(|e| {
287 panic!("Failed to decode hex for Case {i}: {e}");
288 });
289
290 assert_eq!(input.len(), 160, "Case {i} input length is not 160 bytes");
292
293 let res = p.run(&input, None, &context(), false).unwrap();
294
295 assert_eq!(
297 res.output.len(),
298 0,
299 "Case {i} expected failure (empty output), got success",
300 );
301 }
302 }
303
304 #[test]
305 fn test_eip7951_input_length_validation() {
306 let p = Secp256r1;
307
308 let valid_hex = "4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e";
310 let valid_bytes = hex::decode(valid_hex).unwrap();
311 assert_eq!(valid_bytes.len(), 160);
312
313 let res_empty = p.run(&[], None, &context(), false).unwrap();
316 assert_eq!(
317 res_empty.output.len(),
318 0,
319 "Empty input should return empty bytes"
320 );
321
322 let input_short = &valid_bytes[0..159];
324 let res_short = p.run(input_short, None, &context(), false).unwrap();
325 assert_eq!(
326 res_short.output.len(),
327 0,
328 "159 bytes input should return empty bytes"
329 );
330
331 let mut input_long = valid_bytes.clone();
333 input_long.push(0x00); let res_long = p.run(&input_long, None, &context(), false).unwrap();
335 assert_eq!(
336 res_long.output.len(),
337 0,
338 "161 bytes input should return empty bytes"
339 );
340
341 let input_32 = &valid_bytes[0..32];
343 let res_32 = p.run(input_32, None, &context(), false).unwrap();
344 assert_eq!(
345 res_32.output.len(),
346 0,
347 "32 bytes input should return empty bytes"
348 );
349 }
350}