sui_move_natives_latest/crypto/
ecdsa_k1.rs1use crate::get_extension;
2use crate::NativesCostTable;
5use fastcrypto::secp256k1::Secp256k1KeyPair;
6use fastcrypto::secp256k1::Secp256k1PrivateKey;
7use fastcrypto::traits::RecoverableSigner;
8use fastcrypto::{
9 error::FastCryptoError,
10 hash::{Keccak256, Sha256},
11 secp256k1::{
12 Secp256k1PublicKey, Secp256k1Signature, recoverable::Secp256k1RecoverableSignature,
13 },
14 traits::{RecoverableSignature, ToFromBytes},
15};
16use move_binary_format::errors::PartialVMResult;
17use move_core_types::gas_algebra::InternalGas;
18use move_vm_runtime::{native_charge_gas_early_exit, native_functions::NativeContext};
19use move_vm_types::{
20 loaded_data::runtime_types::Type,
21 natives::function::NativeResult,
22 pop_arg,
23 values::{self, Value, VectorRef},
24};
25use rand::SeedableRng;
26use rand::rngs::StdRng;
27use smallvec::smallvec;
28use std::collections::VecDeque;
29use sui_types::crypto::KeypairTraits;
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,
49 pub ecdsa_k1_ecrecover_keccak256_msg_cost_per_byte: InternalGas,
51 pub ecdsa_k1_ecrecover_keccak256_msg_cost_per_block: InternalGas,
53
54 pub ecdsa_k1_ecrecover_sha256_cost_base: InternalGas,
56 pub ecdsa_k1_ecrecover_sha256_msg_cost_per_byte: InternalGas,
58 pub ecdsa_k1_ecrecover_sha256_msg_cost_per_block: InternalGas,
60}
61pub fn ecrecover(
73 context: &mut NativeContext,
74 ty_args: Vec<Type>,
75 mut args: VecDeque<Value>,
76) -> PartialVMResult<NativeResult> {
77 debug_assert!(ty_args.is_empty());
78 debug_assert!(args.len() == 3);
79
80 let hash = pop_arg!(args, u8);
81
82 let (ecdsa_k1_ecrecover_cost_params, crypto_invalid_arguments_cost) = {
84 let cost_table: &NativesCostTable = get_extension!(context)?;
85 (
86 cost_table.ecdsa_k1_ecrecover_cost_params.clone(),
87 cost_table.crypto_invalid_arguments_cost,
88 )
89 };
90 let (base_cost, cost_per_byte, cost_per_block, block_size) = match hash {
91 KECCAK256 => (
92 ecdsa_k1_ecrecover_cost_params.ecdsa_k1_ecrecover_keccak256_cost_base,
93 ecdsa_k1_ecrecover_cost_params.ecdsa_k1_ecrecover_keccak256_msg_cost_per_byte,
94 ecdsa_k1_ecrecover_cost_params.ecdsa_k1_ecrecover_keccak256_msg_cost_per_block,
95 KECCAK256_BLOCK_SIZE,
96 ),
97 SHA256 => (
98 ecdsa_k1_ecrecover_cost_params.ecdsa_k1_ecrecover_sha256_cost_base,
99 ecdsa_k1_ecrecover_cost_params.ecdsa_k1_ecrecover_sha256_msg_cost_per_byte,
100 ecdsa_k1_ecrecover_cost_params.ecdsa_k1_ecrecover_sha256_msg_cost_per_block,
101 SHA256_BLOCK_SIZE,
102 ),
103 _ => {
104 context.charge_gas(crypto_invalid_arguments_cost);
106 return Ok(NativeResult::err(
107 context.gas_used(),
108 FAIL_TO_RECOVER_PUBKEY,
109 ));
110 }
111 };
112
113 native_charge_gas_early_exit!(context, base_cost);
115
116 let msg = pop_arg!(args, VectorRef);
117 let signature = pop_arg!(args, VectorRef);
118
119 let msg_ref = msg.as_bytes_ref();
120 let signature_ref = signature.as_bytes_ref();
121
122 native_charge_gas_early_exit!(
124 context,
125 cost_per_byte * (msg_ref.len() as u64).into()
126 + cost_per_block * (msg_ref.len().div_ceil(block_size) as u64).into()
127 );
128
129 let cost = context.gas_used();
130
131 let Ok(sig) = <Secp256k1RecoverableSignature as ToFromBytes>::from_bytes(&signature_ref) else {
132 return Ok(NativeResult::err(cost, INVALID_SIGNATURE));
133 };
134
135 let pk = match hash {
136 KECCAK256 => sig.recover_with_hash::<Keccak256>(&msg_ref),
137 SHA256 => sig.recover_with_hash::<Sha256>(&msg_ref),
138 _ => Err(FastCryptoError::InvalidInput), };
140
141 match pk {
142 Ok(pk) => Ok(NativeResult::ok(
143 cost,
144 smallvec![Value::vector_u8(pk.as_bytes().to_vec())],
145 )),
146 Err(_) => Ok(NativeResult::err(cost, FAIL_TO_RECOVER_PUBKEY)),
147 }
148}
149
150#[derive(Clone)]
151pub struct EcdsaK1DecompressPubkeyCostParams {
152 pub ecdsa_k1_decompress_pubkey_cost_base: InternalGas,
153}
154pub fn decompress_pubkey(
155 context: &mut NativeContext,
156 ty_args: Vec<Type>,
157 mut args: VecDeque<Value>,
158) -> PartialVMResult<NativeResult> {
159 debug_assert!(ty_args.is_empty());
160 debug_assert!(args.len() == 1);
161
162 let ecdsa_k1_decompress_pubkey_cost_params = get_extension!(context, NativesCostTable)?
164 .ecdsa_k1_decompress_pubkey_cost_params
165 .clone();
166 native_charge_gas_early_exit!(
168 context,
169 ecdsa_k1_decompress_pubkey_cost_params.ecdsa_k1_decompress_pubkey_cost_base
170 );
171
172 let pubkey = pop_arg!(args, VectorRef);
173 let pubkey_ref = pubkey.as_bytes_ref();
174
175 let cost = context.gas_used();
176
177 match Secp256k1PublicKey::from_bytes(&pubkey_ref) {
178 Ok(pubkey) => {
179 let uncompressed = &pubkey.pubkey.serialize_uncompressed();
180 Ok(NativeResult::ok(
181 cost,
182 smallvec![Value::vector_u8(uncompressed.to_vec())],
183 ))
184 }
185 Err(_) => Ok(NativeResult::err(cost, INVALID_PUBKEY)),
186 }
187}
188
189#[derive(Clone)]
190pub struct EcdsaK1Secp256k1VerifyCostParams {
191 pub ecdsa_k1_secp256k1_verify_keccak256_cost_base: InternalGas,
193 pub ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_byte: InternalGas,
195 pub ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_block: InternalGas,
197
198 pub ecdsa_k1_secp256k1_verify_sha256_cost_base: InternalGas,
200 pub ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_byte: InternalGas,
202 pub ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_block: InternalGas,
204}
205pub fn secp256k1_verify(
217 context: &mut NativeContext,
218 ty_args: Vec<Type>,
219 mut args: VecDeque<Value>,
220) -> PartialVMResult<NativeResult> {
221 debug_assert!(ty_args.is_empty());
222 debug_assert!(args.len() == 4);
223
224 let hash = pop_arg!(args, u8);
225
226 let (ecdsa_k1_secp256k1_verify_cost_params, crypto_invalid_arguments_cost) = {
228 let cost_table: &NativesCostTable = get_extension!(context)?;
229 (
230 cost_table.ecdsa_k1_secp256k1_verify_cost_params.clone(),
231 cost_table.crypto_invalid_arguments_cost,
232 )
233 };
234
235 let (base_cost, cost_per_byte, cost_per_block, block_size) = match hash {
236 KECCAK256 => (
237 ecdsa_k1_secp256k1_verify_cost_params.ecdsa_k1_secp256k1_verify_keccak256_cost_base,
238 ecdsa_k1_secp256k1_verify_cost_params
239 .ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_byte,
240 ecdsa_k1_secp256k1_verify_cost_params
241 .ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_block,
242 KECCAK256_BLOCK_SIZE,
243 ),
244 SHA256 => (
245 ecdsa_k1_secp256k1_verify_cost_params.ecdsa_k1_secp256k1_verify_sha256_cost_base,
246 ecdsa_k1_secp256k1_verify_cost_params
247 .ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_byte,
248 ecdsa_k1_secp256k1_verify_cost_params
249 .ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_block,
250 SHA256_BLOCK_SIZE,
251 ),
252 _ => {
253 context.charge_gas(crypto_invalid_arguments_cost);
255
256 return Ok(NativeResult::ok(
257 context.gas_used(),
258 smallvec![Value::bool(false)],
259 ));
260 }
261 };
262 native_charge_gas_early_exit!(context, base_cost);
264
265 let msg = pop_arg!(args, VectorRef);
266 let public_key_bytes = pop_arg!(args, VectorRef);
267 let signature_bytes = pop_arg!(args, VectorRef);
268
269 let msg_ref = msg.as_bytes_ref();
270 let public_key_bytes_ref = public_key_bytes.as_bytes_ref();
271 let signature_bytes_ref = signature_bytes.as_bytes_ref();
272
273 native_charge_gas_early_exit!(
275 context,
276 cost_per_byte * (msg_ref.len() as u64).into()
277 + cost_per_block * (msg_ref.len().div_ceil(block_size) as u64).into()
278 );
279
280 let cost = context.gas_used();
281
282 let Ok(sig) = <Secp256k1Signature as ToFromBytes>::from_bytes(&signature_bytes_ref) else {
283 return Ok(NativeResult::ok(cost, smallvec![Value::bool(false)]));
284 };
285
286 let Ok(pk) = <Secp256k1PublicKey as ToFromBytes>::from_bytes(&public_key_bytes_ref) else {
287 return Ok(NativeResult::ok(cost, smallvec![Value::bool(false)]));
288 };
289
290 let result = match hash {
291 KECCAK256 => pk.verify_with_hash::<Keccak256>(&msg_ref, &sig).is_ok(),
292 SHA256 => pk.verify_with_hash::<Sha256>(&msg_ref, &sig).is_ok(),
293 _ => false,
294 };
295
296 Ok(NativeResult::ok(cost, smallvec![Value::bool(result)]))
297}
298
299pub fn secp256k1_sign(
307 _context: &mut NativeContext,
308 ty_args: Vec<Type>,
309 mut args: VecDeque<Value>,
310) -> PartialVMResult<NativeResult> {
311 debug_assert!(ty_args.is_empty());
312 debug_assert!(args.len() == 4);
313
314 let cost = 0.into();
317
318 let recoverable = pop_arg!(args, bool);
319 let hash = pop_arg!(args, u8);
320 let msg = pop_arg!(args, VectorRef);
321 let private_key_bytes = pop_arg!(args, VectorRef);
322
323 let msg_ref = msg.as_bytes_ref();
324 let private_key_bytes_ref = private_key_bytes.as_bytes_ref();
325
326 let sk = match <Secp256k1PrivateKey as ToFromBytes>::from_bytes(&private_key_bytes_ref) {
327 Ok(sk) => sk,
328 Err(_) => return Ok(NativeResult::err(cost, INVALID_PRIVKEY)),
329 };
330
331 let kp = Secp256k1KeyPair::from(sk);
332
333 let signature = match (hash, recoverable) {
334 (KECCAK256, true) => kp
335 .sign_recoverable_with_hash::<Keccak256>(&msg_ref)
336 .as_bytes()
337 .to_vec(),
338 (KECCAK256, false) => kp.sign_with_hash::<Keccak256>(&msg_ref).as_bytes().to_vec(),
339 (SHA256, true) => kp
340 .sign_recoverable_with_hash::<Sha256>(&msg_ref)
341 .as_bytes()
342 .to_vec(),
343 (SHA256, false) => kp.sign_with_hash::<Sha256>(&msg_ref).as_bytes().to_vec(),
344 _ => return Ok(NativeResult::err(cost, INVALID_HASH_FUNCTION)),
345 };
346
347 Ok(NativeResult::ok(
348 cost,
349 smallvec![Value::vector_u8(signature)],
350 ))
351}
352
353pub fn secp256k1_keypair_from_seed(
360 _context: &mut NativeContext,
361 ty_args: Vec<Type>,
362 mut args: VecDeque<Value>,
363) -> PartialVMResult<NativeResult> {
364 debug_assert!(ty_args.is_empty());
365 debug_assert!(args.len() == 1);
366
367 let cost = 0.into();
370
371 let seed = pop_arg!(args, VectorRef);
372 let seed_ref = seed.as_bytes_ref();
373
374 if seed_ref.len() != SEED_LENGTH {
375 return Ok(NativeResult::err(cost, INVALID_SEED));
376 }
377 let mut seed_array = [0u8; SEED_LENGTH];
378 seed_array.clone_from_slice(&seed_ref);
379
380 let kp = Secp256k1KeyPair::generate(&mut StdRng::from_seed(seed_array));
381
382 let pk_bytes = kp.public().as_bytes().to_vec();
383 let sk_bytes = kp.private().as_bytes().to_vec();
384
385 Ok(NativeResult::ok(
386 cost,
387 smallvec![Value::struct_(values::Struct::pack(vec![
388 Value::vector_u8(sk_bytes),
389 Value::vector_u8(pk_bytes),
390 ]))],
391 ))
392}