aurora_engine_precompiles/
secp256r1.rs1use crate::prelude::types::{make_address, Address, EthGas};
6use crate::{EvmPrecompileResult, Precompile, PrecompileOutput, Vec};
7use aurora_evm::{Context, ExitError};
8use p256::{
9 ecdsa::{signature::hazmat::PrehashVerifier, Signature, VerifyingKey},
10 EncodedPoint,
11};
12
13pub const P256VERIFY_BASE_GAS_FEE: u64 = 6900;
15
16const INPUT_LENGTH: usize = 160;
18
19const SUCCESS_RESULT: [u8; 32] = [
21 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,
22];
23
24pub struct Secp256r1;
25
26impl Secp256r1 {
27 pub const ADDRESS: Address = make_address(0, 0x100);
28
29 fn execute(input: &[u8]) -> Option<Vec<u8>> {
49 if input.len() != INPUT_LENGTH {
51 return None;
52 }
53
54 let h_bytes = &input[0..32];
57 let r_bytes = &input[32..64];
59 let s_bytes = &input[64..96];
61 let qx_bytes = &input[96..128];
63 let qy_bytes = &input[128..160];
65
66 let signature = Signature::from_scalars(
70 *p256::FieldBytes::from_slice(r_bytes),
71 *p256::FieldBytes::from_slice(s_bytes),
72 )
73 .ok()?;
74
75 let encoded_point =
85 EncodedPoint::from_affine_coordinates(qx_bytes.into(), qy_bytes.into(), false);
86 let public_key = VerifyingKey::from_encoded_point(&encoded_point).ok()?;
87
88 if public_key.verify_prehash(h_bytes, &signature).is_ok() {
99 Some(Vec::from(&SUCCESS_RESULT[..]))
101 } else {
102 None
104 }
105 }
106}
107
108impl Precompile for Secp256r1 {
109 fn required_gas(_input: &[u8]) -> Result<EthGas, ExitError>
110 where
111 Self: Sized,
112 {
113 Ok(EthGas::new(P256VERIFY_BASE_GAS_FEE))
114 }
115
116 fn run(
117 &self,
118 input: &[u8],
119 target_gas: Option<EthGas>,
120 _context: &Context,
121 _is_static: bool,
122 ) -> EvmPrecompileResult {
123 let cost = Self::required_gas(input)?;
124 if let Some(target_gas) = target_gas {
125 if cost > target_gas {
126 return Err(ExitError::OutOfGas);
127 }
128 }
129
130 let output = Self::execute(input).unwrap_or_default();
132 Ok(PrecompileOutput::without_logs(cost, output))
133 }
134}
135
136#[cfg(test)]
137mod test {
138 use super::*;
139
140 fn context() -> Context {
141 Context {
142 address: Secp256r1::ADDRESS.raw(),
143 caller: Secp256r1::ADDRESS.raw(),
144 apparent_value: 0u128.into(),
145 }
146 }
147
148 #[test]
150 fn test_sig_verify() {
151 let inputs = vec![
152 ("4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e", true),
153 ("3fec5769b5cf4e310a7d150508e82fb8e3eda1c2c94c61492d3bd8aea99e06c9e22466e928fdccef0de49e3503d2657d00494a00e764fd437bdafa05f5922b1fbbb77c6817ccf50748419477e843d5bac67e6a70e97dde5a57e0c983b777e1ad31a80482dadf89de6302b1988c82c29544c9c07bb910596158f6062517eb089a2f54c9a0f348752950094d3228d3b940258c75fe2a413cb70baa21dc2e352fc5", true),
154 ("e775723953ead4a90411a02908fd1a629db584bc600664c609061f221ef6bf7c440066c8626b49daaa7bf2bcc0b74be4f7a1e3dcf0e869f1542fe821498cbf2de73ad398194129f635de4424a07ca715838aefe8fe69d1a391cfa70470795a80dd056866e6e1125aff94413921880c437c9e2570a28ced7267c8beef7e9b2d8d1547d76dfcf4bee592f5fefe10ddfb6aeb0991c5b9dbbee6ec80d11b17c0eb1a", true),
155 ("b5a77e7a90aa14e0bf5f337f06f597148676424fae26e175c6e5621c34351955289f319789da424845c9eac935245fcddd805950e2f02506d09be7e411199556d262144475b1fa46ad85250728c600c53dfd10f8b3f4adf140e27241aec3c2da3a81046703fccf468b48b145f939efdbb96c3786db712b3113bb2488ef286cdcef8afe82d200a5bb36b5462166e8ce77f2d831a52ef2135b2af188110beaefb1", true),
156 ("858b991cfd78f16537fe6d1f4afd10273384db08bdfc843562a22b0626766686f6aec8247599f40bfe01bec0e0ecf17b4319559022d4d9bf007fe929943004eb4866760dedf31b7c691f5ce665f8aae0bda895c23595c834fecc2390a5bcc203b04afcacbb4280713287a2d0c37e23f7513fab898f2c1fefa00ec09a924c335d9b629f1d4fb71901c3e59611afbfea354d101324e894c788d1c01f00b3c251b2", true),
157 ("3cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e", false),
158 ("afec5769b5cf4e310a7d150508e82fb8e3eda1c2c94c61492d3bd8aea99e06c9e22466e928fdccef0de49e3503d2657d00494a00e764fd437bdafa05f5922b1fbbb77c6817ccf50748419477e843d5bac67e6a70e97dde5a57e0c983b777e1ad31a80482dadf89de6302b1988c82c29544c9c07bb910596158f6062517eb089a2f54c9a0f348752950094d3228d3b940258c75fe2a413cb70baa21dc2e352fc5", false),
159 ("f775723953ead4a90411a02908fd1a629db584bc600664c609061f221ef6bf7c440066c8626b49daaa7bf2bcc0b74be4f7a1e3dcf0e869f1542fe821498cbf2de73ad398194129f635de4424a07ca715838aefe8fe69d1a391cfa70470795a80dd056866e6e1125aff94413921880c437c9e2570a28ced7267c8beef7e9b2d8d1547d76dfcf4bee592f5fefe10ddfb6aeb0991c5b9dbbee6ec80d11b17c0eb1a", false),
160 ("c5a77e7a90aa14e0bf5f337f06f597148676424fae26e175c6e5621c34351955289f319789da424845c9eac935245fcddd805950e2f02506d09be7e411199556d262144475b1fa46ad85250728c600c53dfd10f8b3f4adf140e27241aec3c2da3a81046703fccf468b48b145f939efdbb96c3786db712b3113bb2488ef286cdcef8afe82d200a5bb36b5462166e8ce77f2d831a52ef2135b2af188110beaefb1", false),
161 ("958b991cfd78f16537fe6d1f4afd10273384db08bdfc843562a22b0626766686f6aec8247599f40bfe01bec0e0ecf17b4319559022d4d9bf007fe929943004eb4866760dedf31b7c691f5ce665f8aae0bda895c23595c834fecc2390a5bcc203b04afcacbb4280713287a2d0c37e23f7513fab898f2c1fefa00ec09a924c335d9b629f1d4fb71901c3e59611afbfea354d101324e894c788d1c01f00b3c251b2", false),
162 ("4cee90eb86eaa050036147a12d49004b6a", false),
163 ("4cee90eb86eaa050036147a12d49004b6a958b991cfd78f16537fe6d1f4afd10273384db08bdfc843562a22b0626766686f6aec8247599f40bfe01bec0e0ecf17b4319559022d4d9bf007fe929943004eb4866760dedf319", false),
164 ("4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e00", false),
165 ("4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e", false),
166 ("4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", false),
167 ("b5a77e7a90aa14e0bf5f337f06f597148676424fae26e175c6e5621c34351955289f319789da424845c9eac935245fcddd805950e2f02506d09be7e411199556d262144475b1fa46ad85250728c600c53dfd10f8b3f4adf140e27241aec3c2daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaef8afe82d200a5bb36b5462166e8ce77f2d831a52ef2135b2af188110beaefb1", false),
168 ];
169 let p = Secp256r1;
170 for (input_hex, expect_success) in inputs {
171 let input = hex::decode(input_hex).unwrap();
172 let res = p.run(&input, None, &context(), false).unwrap();
173 if expect_success {
174 assert_eq!(
175 res.output,
176 SUCCESS_RESULT.to_vec(),
177 "Input hex: {input_hex}",
178 );
179 } else {
180 assert_eq!(res.output.len(), 0, "Input hex: {input_hex}");
181 }
182 }
183 }
184
185 #[test]
186 fn test_not_enough_gas_errors() {
187 let input_hex = "4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e";
188 let p = Secp256r1;
189
190 let input = hex::decode(input_hex).unwrap();
191 let err = p
192 .run(&input, Some(EthGas::new(2_500)), &context(), false)
193 .unwrap_err();
194 assert_eq!(err, ExitError::OutOfGas);
195 }
196
197 #[test]
198 fn test_eip7951_spec_compliance_edge_cases() {
199 let p = Secp256r1;
200
201 let valid_full_hex = "4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e";
202
203 let h = &valid_full_hex[0..64];
205 let r = &valid_full_hex[64..128];
206 let s = &valid_full_hex[128..192];
207 let qx = &valid_full_hex[192..256];
208 let qy = &valid_full_hex[256..320];
209
210 let val_p = "ffffffff00000001000000000000000000000000ffffffffffffffffffffffff";
212 let val_n = "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551";
213 let val_zero = "0000000000000000000000000000000000000000000000000000000000000000";
214
215 let failures = [
216 format!("{h}{r}{s}{val_zero}{val_zero}"),
219 format!("{h}{r}{s}{val_p}{qy}"),
222 format!("{h}{r}{s}{qx}{val_p}"),
225 format!("{h}{val_zero}{s}{qx}{qy}"),
228 format!("{h}{r}{val_zero}{qx}{qy}"),
231 format!("{h}{val_n}{s}{qx}{qy}"),
234 format!("{h}{r}{val_n}{qx}{qy}"),
237 ];
238
239 for (i, input_hex) in failures.iter().enumerate() {
240 let input = hex::decode(input_hex).unwrap_or_else(|e| {
241 panic!("Failed to decode hex for Case {i}: {e}");
242 });
243
244 assert_eq!(input.len(), 160, "Case {i} input length is not 160 bytes");
246
247 let res = p.run(&input, None, &context(), false).unwrap();
248
249 assert_eq!(
251 res.output.len(),
252 0,
253 "Case {i} expected failure (empty output), got success",
254 );
255 }
256 }
257
258 #[test]
259 fn test_eip7951_input_length_validation() {
260 let p = Secp256r1;
261
262 let valid_hex = "4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e";
264 let valid_bytes = hex::decode(valid_hex).unwrap();
265 assert_eq!(valid_bytes.len(), 160);
266
267 let res_empty = p.run(&[], None, &context(), false).unwrap();
270 assert_eq!(
271 res_empty.output.len(),
272 0,
273 "Empty input should return empty bytes"
274 );
275
276 let input_short = &valid_bytes[0..159];
278 let res_short = p.run(input_short, None, &context(), false).unwrap();
279 assert_eq!(
280 res_short.output.len(),
281 0,
282 "159 bytes input should return empty bytes"
283 );
284
285 let mut input_long = valid_bytes.clone();
287 input_long.push(0x00); let res_long = p.run(&input_long, None, &context(), false).unwrap();
289 assert_eq!(
290 res_long.output.len(),
291 0,
292 "161 bytes input should return empty bytes"
293 );
294
295 let input_32 = &valid_bytes[0..32];
297 let res_32 = p.run(input_32, None, &context(), false).unwrap();
298 assert_eq!(
299 res_32.output.len(),
300 0,
301 "32 bytes input should return empty bytes"
302 );
303 }
304}