iota_move_natives_latest/crypto/
ecdsa_r1.rs1use std::collections::VecDeque;
6
7use fastcrypto::{
8 error::FastCryptoError,
9 hash::{Keccak256, Sha256},
10 secp256r1::{
11 Secp256r1PublicKey, Secp256r1Signature, recoverable::Secp256r1RecoverableSignature,
12 },
13 traits::{RecoverableSignature, ToFromBytes},
14};
15use move_binary_format::errors::PartialVMResult;
16use move_core_types::gas_algebra::InternalGas;
17use move_vm_runtime::{native_charge_gas_early_exit, native_functions::NativeContext};
18use move_vm_types::{
19 loaded_data::runtime_types::Type,
20 natives::function::NativeResult,
21 pop_arg,
22 values::{Value, VectorRef},
23};
24use smallvec::smallvec;
25
26use crate::NativesCostTable;
27
28pub const FAIL_TO_RECOVER_PUBKEY: u64 = 0;
29pub const INVALID_SIGNATURE: u64 = 1;
30
31pub const KECCAK256: u8 = 0;
32pub const SHA256: u8 = 1;
33
34const KECCAK256_BLOCK_SIZE: usize = 136;
35const SHA256_BLOCK_SIZE: usize = 64;
36
37#[derive(Clone)]
38pub struct EcdsaR1EcrecoverCostParams {
39 pub ecdsa_r1_ecrecover_keccak256_cost_base: InternalGas,
42 pub ecdsa_r1_ecrecover_keccak256_msg_cost_per_byte: InternalGas,
44 pub ecdsa_r1_ecrecover_keccak256_msg_cost_per_block: InternalGas,
47
48 pub ecdsa_r1_ecrecover_sha256_cost_base: InternalGas,
51 pub ecdsa_r1_ecrecover_sha256_msg_cost_per_byte: InternalGas,
53 pub ecdsa_r1_ecrecover_sha256_msg_cost_per_block: InternalGas,
56}
57pub fn ecrecover(
75 context: &mut NativeContext,
76 ty_args: Vec<Type>,
77 mut args: VecDeque<Value>,
78) -> PartialVMResult<NativeResult> {
79 debug_assert!(ty_args.is_empty());
80 debug_assert!(args.len() == 3);
81
82 let hash = pop_arg!(args, u8);
83
84 let (ecdsa_r1_ecrecover_cost_params, crypto_invalid_arguments_cost) = {
86 let cost_table = &context.extensions().get::<NativesCostTable>();
87 (
88 cost_table.ecdsa_r1_ecrecover_cost_params.clone(),
89 cost_table.crypto_invalid_arguments_cost,
90 )
91 };
92
93 let (base_cost, cost_per_byte, cost_per_block, block_size) = match hash {
94 KECCAK256 => (
95 ecdsa_r1_ecrecover_cost_params.ecdsa_r1_ecrecover_keccak256_cost_base,
96 ecdsa_r1_ecrecover_cost_params.ecdsa_r1_ecrecover_keccak256_msg_cost_per_byte,
97 ecdsa_r1_ecrecover_cost_params.ecdsa_r1_ecrecover_keccak256_msg_cost_per_block,
98 KECCAK256_BLOCK_SIZE,
99 ),
100 SHA256 => (
101 ecdsa_r1_ecrecover_cost_params.ecdsa_r1_ecrecover_sha256_cost_base,
102 ecdsa_r1_ecrecover_cost_params.ecdsa_r1_ecrecover_sha256_msg_cost_per_byte,
103 ecdsa_r1_ecrecover_cost_params.ecdsa_r1_ecrecover_sha256_msg_cost_per_block,
104 SHA256_BLOCK_SIZE,
105 ),
106 _ => {
107 context.charge_gas(crypto_invalid_arguments_cost);
110
111 return Ok(NativeResult::err(
112 context.gas_used(),
113 FAIL_TO_RECOVER_PUBKEY,
114 ));
115 }
116 };
117
118 native_charge_gas_early_exit!(context, base_cost);
120
121 let msg = pop_arg!(args, VectorRef);
122 let signature = pop_arg!(args, VectorRef);
123
124 let msg_ref = msg.as_bytes_ref();
125 let signature_ref = signature.as_bytes_ref();
126
127 native_charge_gas_early_exit!(
129 context,
130 cost_per_byte * (msg_ref.len() as u64).into()
131 + cost_per_block * (msg_ref.len().div_ceil(block_size) as u64).into()
132 );
133
134 let cost = context.gas_used();
135
136 let Ok(sig) = <Secp256r1RecoverableSignature as ToFromBytes>::from_bytes(&signature_ref) else {
137 return Ok(NativeResult::err(cost, INVALID_SIGNATURE));
138 };
139
140 let pk = match hash {
141 KECCAK256 => sig.recover_with_hash::<Keccak256>(&msg_ref),
142 SHA256 => sig.recover_with_hash::<Sha256>(&msg_ref),
143 _ => Err(FastCryptoError::InvalidInput),
144 };
145
146 match pk {
147 Ok(pk) => Ok(NativeResult::ok(
148 cost,
149 smallvec![Value::vector_u8(pk.as_bytes().to_vec())],
150 )),
151 Err(_) => Ok(NativeResult::err(cost, FAIL_TO_RECOVER_PUBKEY)),
152 }
153}
154
155#[derive(Clone)]
156pub struct EcdsaR1Secp256R1VerifyCostParams {
157 pub ecdsa_r1_secp256r1_verify_keccak256_cost_base: InternalGas,
160 pub ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_byte: InternalGas,
162 pub ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_block: InternalGas,
165
166 pub ecdsa_r1_secp256r1_verify_sha256_cost_base: InternalGas,
169 pub ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_byte: InternalGas,
171 pub ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_block: InternalGas,
174}
175pub fn secp256r1_verify(
195 context: &mut NativeContext,
196 ty_args: Vec<Type>,
197 mut args: VecDeque<Value>,
198) -> PartialVMResult<NativeResult> {
199 debug_assert!(ty_args.is_empty());
200 debug_assert!(args.len() == 4);
201 let (ecdsa_r1_secp256_r1_verify_cost_params, crypto_invalid_arguments_cost) = {
203 let cost_table = &context.extensions().get::<NativesCostTable>();
204 (
205 cost_table.ecdsa_r1_secp256_r1_verify_cost_params.clone(),
206 cost_table.crypto_invalid_arguments_cost,
207 )
208 };
209 let hash = pop_arg!(args, u8);
210 let (base_cost, cost_per_byte, cost_per_block, block_size) = match hash {
211 KECCAK256 => (
212 ecdsa_r1_secp256_r1_verify_cost_params.ecdsa_r1_secp256r1_verify_keccak256_cost_base,
213 ecdsa_r1_secp256_r1_verify_cost_params
214 .ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_byte,
215 ecdsa_r1_secp256_r1_verify_cost_params
216 .ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_block,
217 KECCAK256_BLOCK_SIZE,
218 ),
219 SHA256 => (
220 ecdsa_r1_secp256_r1_verify_cost_params.ecdsa_r1_secp256r1_verify_sha256_cost_base,
221 ecdsa_r1_secp256_r1_verify_cost_params
222 .ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_byte,
223 ecdsa_r1_secp256_r1_verify_cost_params
224 .ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_block,
225 SHA256_BLOCK_SIZE,
226 ),
227 _ => {
228 context.charge_gas(crypto_invalid_arguments_cost);
231 return Ok(NativeResult::ok(
232 context.gas_used(),
233 smallvec![Value::bool(false)],
234 ));
235 }
236 };
237
238 native_charge_gas_early_exit!(context, base_cost);
240
241 let msg = pop_arg!(args, VectorRef);
242 let public_key_bytes = pop_arg!(args, VectorRef);
243 let signature_bytes = pop_arg!(args, VectorRef);
244
245 let msg_ref = msg.as_bytes_ref();
246 let public_key_bytes_ref = public_key_bytes.as_bytes_ref();
247 let signature_bytes_ref = signature_bytes.as_bytes_ref();
248
249 native_charge_gas_early_exit!(
251 context,
252 cost_per_byte * (msg_ref.len() as u64).into()
253 + cost_per_block * (msg_ref.len().div_ceil(block_size) as u64).into()
254 );
255
256 let cost = context.gas_used();
257
258 let Ok(sig) = <Secp256r1Signature as ToFromBytes>::from_bytes(&signature_bytes_ref) else {
259 return Ok(NativeResult::ok(cost, smallvec![Value::bool(false)]));
260 };
261
262 let Ok(pk) = <Secp256r1PublicKey as ToFromBytes>::from_bytes(&public_key_bytes_ref) else {
263 return Ok(NativeResult::ok(cost, smallvec![Value::bool(false)]));
264 };
265
266 let result = match hash {
267 KECCAK256 => pk.verify_with_hash::<Keccak256>(&msg_ref, &sig).is_ok(),
268 SHA256 => pk.verify_with_hash::<Sha256>(&msg_ref, &sig).is_ok(),
269 _ => false,
270 };
271
272 Ok(NativeResult::ok(cost, smallvec![Value::bool(result)]))
273}