iota_move_natives_latest/crypto/
ecdsa_k1.rs1use std::collections::VecDeque;
6
7use fastcrypto::{
8 error::FastCryptoError,
9 hash::{Keccak256, Sha256},
10 secp256k1::{
11 Secp256k1KeyPair, Secp256k1PrivateKey, Secp256k1PublicKey, Secp256k1Signature,
12 recoverable::Secp256k1RecoverableSignature,
13 },
14 traits::{RecoverableSignature, RecoverableSigner, ToFromBytes},
15};
16use iota_types::crypto::KeypairTraits;
17use move_binary_format::errors::PartialVMResult;
18use move_core_types::gas_algebra::InternalGas;
19use move_vm_runtime::{native_charge_gas_early_exit, native_functions::NativeContext};
20use move_vm_types::{
21 loaded_data::runtime_types::Type,
22 natives::function::NativeResult,
23 pop_arg,
24 values::{self, Value, VectorRef},
25};
26use rand::{SeedableRng, rngs::StdRng};
27use smallvec::smallvec;
28
29use crate::NativesCostTable;
30
31pub const FAIL_TO_RECOVER_PUBKEY: u64 = 0;
32pub const INVALID_SIGNATURE: u64 = 1;
33pub const INVALID_PUBKEY: u64 = 2;
34pub const INVALID_PRIVKEY: u64 = 3;
35pub const INVALID_HASH_FUNCTION: u64 = 4;
36pub const INVALID_SEED: u64 = 5;
37
38pub const KECCAK256: u8 = 0;
39pub const SHA256: u8 = 1;
40
41const KECCAK256_BLOCK_SIZE: usize = 136;
42const SHA256_BLOCK_SIZE: usize = 64;
43const SEED_LENGTH: usize = 32;
44
45#[derive(Clone)]
46pub struct EcdsaK1EcrecoverCostParams {
47 pub ecdsa_k1_ecrecover_keccak256_cost_base: InternalGas,
50 pub ecdsa_k1_ecrecover_keccak256_msg_cost_per_byte: InternalGas,
52 pub ecdsa_k1_ecrecover_keccak256_msg_cost_per_block: InternalGas,
55
56 pub ecdsa_k1_ecrecover_sha256_cost_base: InternalGas,
59 pub ecdsa_k1_ecrecover_sha256_msg_cost_per_byte: InternalGas,
61 pub ecdsa_k1_ecrecover_sha256_msg_cost_per_block: InternalGas,
64}
65pub fn ecrecover(
83 context: &mut NativeContext,
84 ty_args: Vec<Type>,
85 mut args: VecDeque<Value>,
86) -> PartialVMResult<NativeResult> {
87 debug_assert!(ty_args.is_empty());
88 debug_assert!(args.len() == 3);
89
90 let hash = pop_arg!(args, u8);
91
92 let (ecdsa_k1_ecrecover_cost_params, crypto_invalid_arguments_cost) = {
94 let cost_table = &context.extensions().get::<NativesCostTable>();
95 (
96 cost_table.ecdsa_k1_ecrecover_cost_params.clone(),
97 cost_table.crypto_invalid_arguments_cost,
98 )
99 };
100 let (base_cost, cost_per_byte, cost_per_block, block_size) = match hash {
101 KECCAK256 => (
102 ecdsa_k1_ecrecover_cost_params.ecdsa_k1_ecrecover_keccak256_cost_base,
103 ecdsa_k1_ecrecover_cost_params.ecdsa_k1_ecrecover_keccak256_msg_cost_per_byte,
104 ecdsa_k1_ecrecover_cost_params.ecdsa_k1_ecrecover_keccak256_msg_cost_per_block,
105 KECCAK256_BLOCK_SIZE,
106 ),
107 SHA256 => (
108 ecdsa_k1_ecrecover_cost_params.ecdsa_k1_ecrecover_sha256_cost_base,
109 ecdsa_k1_ecrecover_cost_params.ecdsa_k1_ecrecover_sha256_msg_cost_per_byte,
110 ecdsa_k1_ecrecover_cost_params.ecdsa_k1_ecrecover_sha256_msg_cost_per_block,
111 SHA256_BLOCK_SIZE,
112 ),
113 _ => {
114 context.charge_gas(crypto_invalid_arguments_cost);
117 return Ok(NativeResult::err(
118 context.gas_used(),
119 FAIL_TO_RECOVER_PUBKEY,
120 ));
121 }
122 };
123
124 native_charge_gas_early_exit!(context, base_cost);
126
127 let msg = pop_arg!(args, VectorRef);
128 let signature = pop_arg!(args, VectorRef);
129
130 let msg_ref = msg.as_bytes_ref();
131 let signature_ref = signature.as_bytes_ref();
132
133 native_charge_gas_early_exit!(
135 context,
136 cost_per_byte * (msg_ref.len() as u64).into()
137 + cost_per_block * (msg_ref.len().div_ceil(block_size) as u64).into()
138 );
139
140 let cost = context.gas_used();
141
142 let Ok(sig) = <Secp256k1RecoverableSignature as ToFromBytes>::from_bytes(&signature_ref) else {
143 return Ok(NativeResult::err(cost, INVALID_SIGNATURE));
144 };
145
146 let pk = match hash {
147 KECCAK256 => sig.recover_with_hash::<Keccak256>(&msg_ref),
148 SHA256 => sig.recover_with_hash::<Sha256>(&msg_ref),
149 _ => Err(FastCryptoError::InvalidInput), };
151
152 match pk {
153 Ok(pk) => Ok(NativeResult::ok(
154 cost,
155 smallvec![Value::vector_u8(pk.as_bytes().to_vec())],
156 )),
157 Err(_) => Ok(NativeResult::err(cost, FAIL_TO_RECOVER_PUBKEY)),
158 }
159}
160
161#[derive(Clone)]
162pub struct EcdsaK1DecompressPubkeyCostParams {
163 pub ecdsa_k1_decompress_pubkey_cost_base: InternalGas,
164}
165pub fn decompress_pubkey(
166 context: &mut NativeContext,
167 ty_args: Vec<Type>,
168 mut args: VecDeque<Value>,
169) -> PartialVMResult<NativeResult> {
170 debug_assert!(ty_args.is_empty());
171 debug_assert!(args.len() == 1);
172
173 let ecdsa_k1_decompress_pubkey_cost_params = &context
175 .extensions()
176 .get::<NativesCostTable>()
177 .ecdsa_k1_decompress_pubkey_cost_params
178 .clone();
179 native_charge_gas_early_exit!(
181 context,
182 ecdsa_k1_decompress_pubkey_cost_params.ecdsa_k1_decompress_pubkey_cost_base
183 );
184
185 let pubkey = pop_arg!(args, VectorRef);
186 let pubkey_ref = pubkey.as_bytes_ref();
187
188 let cost = context.gas_used();
189
190 match Secp256k1PublicKey::from_bytes(&pubkey_ref) {
191 Ok(pubkey) => {
192 let uncompressed = &pubkey.pubkey.serialize_uncompressed();
193 Ok(NativeResult::ok(
194 cost,
195 smallvec![Value::vector_u8(uncompressed.to_vec())],
196 ))
197 }
198 Err(_) => Ok(NativeResult::err(cost, INVALID_PUBKEY)),
199 }
200}
201
202#[derive(Clone)]
203pub struct EcdsaK1Secp256k1VerifyCostParams {
204 pub ecdsa_k1_secp256k1_verify_keccak256_cost_base: InternalGas,
207 pub ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_byte: InternalGas,
209 pub ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_block: InternalGas,
212
213 pub ecdsa_k1_secp256k1_verify_sha256_cost_base: InternalGas,
216 pub ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_byte: InternalGas,
218 pub ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_block: InternalGas,
221}
222pub fn secp256k1_verify(
242 context: &mut NativeContext,
243 ty_args: Vec<Type>,
244 mut args: VecDeque<Value>,
245) -> PartialVMResult<NativeResult> {
246 debug_assert!(ty_args.is_empty());
247 debug_assert!(args.len() == 4);
248
249 let hash = pop_arg!(args, u8);
250
251 let (ecdsa_k1_secp256k1_verify_cost_params, crypto_invalid_arguments_cost) = {
253 let cost_table = &context.extensions().get::<NativesCostTable>();
254 (
255 cost_table.ecdsa_k1_secp256k1_verify_cost_params.clone(),
256 cost_table.crypto_invalid_arguments_cost,
257 )
258 };
259
260 let (base_cost, cost_per_byte, cost_per_block, block_size) = match hash {
261 KECCAK256 => (
262 ecdsa_k1_secp256k1_verify_cost_params.ecdsa_k1_secp256k1_verify_keccak256_cost_base,
263 ecdsa_k1_secp256k1_verify_cost_params
264 .ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_byte,
265 ecdsa_k1_secp256k1_verify_cost_params
266 .ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_block,
267 KECCAK256_BLOCK_SIZE,
268 ),
269 SHA256 => (
270 ecdsa_k1_secp256k1_verify_cost_params.ecdsa_k1_secp256k1_verify_sha256_cost_base,
271 ecdsa_k1_secp256k1_verify_cost_params
272 .ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_byte,
273 ecdsa_k1_secp256k1_verify_cost_params
274 .ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_block,
275 SHA256_BLOCK_SIZE,
276 ),
277 _ => {
278 context.charge_gas(crypto_invalid_arguments_cost);
281
282 return Ok(NativeResult::ok(
283 context.gas_used(),
284 smallvec![Value::bool(false)],
285 ));
286 }
287 };
288 native_charge_gas_early_exit!(context, base_cost);
290
291 let msg = pop_arg!(args, VectorRef);
292 let public_key_bytes = pop_arg!(args, VectorRef);
293 let signature_bytes = pop_arg!(args, VectorRef);
294
295 let msg_ref = msg.as_bytes_ref();
296 let public_key_bytes_ref = public_key_bytes.as_bytes_ref();
297 let signature_bytes_ref = signature_bytes.as_bytes_ref();
298
299 native_charge_gas_early_exit!(
301 context,
302 cost_per_byte * (msg_ref.len() as u64).into()
303 + cost_per_block * (msg_ref.len().div_ceil(block_size) as u64).into()
304 );
305
306 let cost = context.gas_used();
307
308 let Ok(sig) = <Secp256k1Signature as ToFromBytes>::from_bytes(&signature_bytes_ref) else {
309 return Ok(NativeResult::ok(cost, smallvec![Value::bool(false)]));
310 };
311
312 let Ok(pk) = <Secp256k1PublicKey as ToFromBytes>::from_bytes(&public_key_bytes_ref) else {
313 return Ok(NativeResult::ok(cost, smallvec![Value::bool(false)]));
314 };
315
316 let result = match hash {
317 KECCAK256 => pk.verify_with_hash::<Keccak256>(&msg_ref, &sig).is_ok(),
318 SHA256 => pk.verify_with_hash::<Sha256>(&msg_ref, &sig).is_ok(),
319 _ => false,
320 };
321
322 Ok(NativeResult::ok(cost, smallvec![Value::bool(result)]))
323}
324
325pub fn secp256k1_sign(
336 _context: &mut NativeContext,
337 ty_args: Vec<Type>,
338 mut args: VecDeque<Value>,
339) -> PartialVMResult<NativeResult> {
340 debug_assert!(ty_args.is_empty());
341 debug_assert!(args.len() == 4);
342
343 let cost = 0.into();
346
347 let recoverable = pop_arg!(args, bool);
348 let hash = pop_arg!(args, u8);
349 let msg = pop_arg!(args, VectorRef);
350 let private_key_bytes = pop_arg!(args, VectorRef);
351
352 let msg_ref = msg.as_bytes_ref();
353 let private_key_bytes_ref = private_key_bytes.as_bytes_ref();
354
355 let sk = match <Secp256k1PrivateKey as ToFromBytes>::from_bytes(&private_key_bytes_ref) {
356 Ok(sk) => sk,
357 Err(_) => return Ok(NativeResult::err(cost, INVALID_PRIVKEY)),
358 };
359
360 let kp = Secp256k1KeyPair::from(sk);
361
362 let signature = match (hash, recoverable) {
363 (KECCAK256, true) => kp
364 .sign_recoverable_with_hash::<Keccak256>(&msg_ref)
365 .as_bytes()
366 .to_vec(),
367 (KECCAK256, false) => kp.sign_with_hash::<Keccak256>(&msg_ref).as_bytes().to_vec(),
368 (SHA256, true) => kp
369 .sign_recoverable_with_hash::<Sha256>(&msg_ref)
370 .as_bytes()
371 .to_vec(),
372 (SHA256, false) => kp.sign_with_hash::<Sha256>(&msg_ref).as_bytes().to_vec(),
373 _ => return Ok(NativeResult::err(cost, INVALID_HASH_FUNCTION)),
374 };
375
376 Ok(NativeResult::ok(
377 cost,
378 smallvec![Value::vector_u8(signature)],
379 ))
380}
381
382pub fn secp256k1_keypair_from_seed(
390 _context: &mut NativeContext,
391 ty_args: Vec<Type>,
392 mut args: VecDeque<Value>,
393) -> PartialVMResult<NativeResult> {
394 debug_assert!(ty_args.is_empty());
395 debug_assert!(args.len() == 1);
396
397 let cost = 0.into();
400
401 let seed = pop_arg!(args, VectorRef);
402 let seed_ref = seed.as_bytes_ref();
403
404 if seed_ref.len() != SEED_LENGTH {
405 return Ok(NativeResult::err(cost, INVALID_SEED));
406 }
407 let mut seed_array = [0u8; SEED_LENGTH];
408 seed_array.clone_from_slice(&seed_ref);
409
410 let kp = Secp256k1KeyPair::generate(&mut StdRng::from_seed(seed_array));
411
412 let pk_bytes = kp.public().as_bytes().to_vec();
413 let sk_bytes = kp.private().as_bytes().to_vec();
414
415 Ok(NativeResult::ok(
416 cost,
417 smallvec![Value::struct_(values::Struct::pack(vec![
418 Value::vector_u8(sk_bytes),
419 Value::vector_u8(pk_bytes),
420 ]))],
421 ))
422}