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_binary_format::safe_assert;
18use move_binary_format::safe_assert_eq;
19use move_core_types::gas_algebra::InternalGas;
20use move_vm_runtime::{
21 execution::{
22 Type,
23 values::{self, Value, VectorRef},
24 },
25 natives::functions::NativeResult,
26 pop_arg,
27};
28use move_vm_runtime::{native_charge_gas_early_exit, natives::functions::NativeContext};
29use rand::SeedableRng;
30use rand::rngs::StdRng;
31use smallvec::smallvec;
32use std::collections::VecDeque;
33use sui_types::crypto::KeypairTraits;
34
35pub const FAIL_TO_RECOVER_PUBKEY: u64 = 0;
36pub const INVALID_SIGNATURE: u64 = 1;
37pub const INVALID_PUBKEY: u64 = 2;
38pub const INVALID_PRIVKEY: u64 = 3;
39pub const INVALID_HASH_FUNCTION: u64 = 4;
40pub const INVALID_SEED: u64 = 5;
41
42pub const KECCAK256: u8 = 0;
43pub const SHA256: u8 = 1;
44
45const KECCAK256_BLOCK_SIZE: usize = 136;
46const SHA256_BLOCK_SIZE: usize = 64;
47const SEED_LENGTH: usize = 32;
48
49#[derive(Clone)]
50pub struct EcdsaK1EcrecoverCostParams {
51 pub ecdsa_k1_ecrecover_keccak256_cost_base: InternalGas,
53 pub ecdsa_k1_ecrecover_keccak256_msg_cost_per_byte: InternalGas,
55 pub ecdsa_k1_ecrecover_keccak256_msg_cost_per_block: InternalGas,
57
58 pub ecdsa_k1_ecrecover_sha256_cost_base: InternalGas,
60 pub ecdsa_k1_ecrecover_sha256_msg_cost_per_byte: InternalGas,
62 pub ecdsa_k1_ecrecover_sha256_msg_cost_per_block: InternalGas,
64}
65pub fn ecrecover(
77 context: &mut NativeContext,
78 ty_args: Vec<Type>,
79 mut args: VecDeque<Value>,
80) -> PartialVMResult<NativeResult> {
81 safe_assert!(ty_args.is_empty());
82 safe_assert_eq!(args.len(), 3);
83
84 let hash = pop_arg!(args, u8);
85
86 let (ecdsa_k1_ecrecover_cost_params, crypto_invalid_arguments_cost) = {
88 let cost_table: &NativesCostTable = get_extension!(context)?;
89 (
90 cost_table.ecdsa_k1_ecrecover_cost_params.clone(),
91 cost_table.crypto_invalid_arguments_cost,
92 )
93 };
94 let (base_cost, cost_per_byte, cost_per_block, block_size) = match hash {
95 KECCAK256 => (
96 ecdsa_k1_ecrecover_cost_params.ecdsa_k1_ecrecover_keccak256_cost_base,
97 ecdsa_k1_ecrecover_cost_params.ecdsa_k1_ecrecover_keccak256_msg_cost_per_byte,
98 ecdsa_k1_ecrecover_cost_params.ecdsa_k1_ecrecover_keccak256_msg_cost_per_block,
99 KECCAK256_BLOCK_SIZE,
100 ),
101 SHA256 => (
102 ecdsa_k1_ecrecover_cost_params.ecdsa_k1_ecrecover_sha256_cost_base,
103 ecdsa_k1_ecrecover_cost_params.ecdsa_k1_ecrecover_sha256_msg_cost_per_byte,
104 ecdsa_k1_ecrecover_cost_params.ecdsa_k1_ecrecover_sha256_msg_cost_per_block,
105 SHA256_BLOCK_SIZE,
106 ),
107 _ => {
108 context.charge_gas(crypto_invalid_arguments_cost)?;
110 return Ok(NativeResult::err(
111 context.gas_used(),
112 FAIL_TO_RECOVER_PUBKEY,
113 ));
114 }
115 };
116
117 native_charge_gas_early_exit!(context, base_cost);
119
120 let msg = pop_arg!(args, VectorRef);
121 let signature = pop_arg!(args, VectorRef);
122
123 let msg_ref = msg.as_bytes_ref()?;
124 let signature_ref = signature.as_bytes_ref()?;
125
126 native_charge_gas_early_exit!(
128 context,
129 cost_per_byte.saturating_mul(msg_ref.len() as u64)
130 + cost_per_block.saturating_mul(msg_ref.len().div_ceil(block_size) as u64)
131 );
132
133 let cost = context.gas_used();
134
135 let Ok(sig) = <Secp256k1RecoverableSignature as ToFromBytes>::from_bytes(&signature_ref) else {
136 return Ok(NativeResult::err(cost, INVALID_SIGNATURE));
137 };
138
139 let pk = match hash {
140 KECCAK256 => sig.recover_with_hash::<Keccak256>(&msg_ref),
141 SHA256 => sig.recover_with_hash::<Sha256>(&msg_ref),
142 _ => Err(FastCryptoError::InvalidInput), };
144
145 match pk {
146 Ok(pk) => Ok(NativeResult::ok(
147 cost,
148 smallvec![Value::vector_u8(pk.as_bytes().to_vec())],
149 )),
150 Err(_) => Ok(NativeResult::err(cost, FAIL_TO_RECOVER_PUBKEY)),
151 }
152}
153
154#[derive(Clone)]
155pub struct EcdsaK1DecompressPubkeyCostParams {
156 pub ecdsa_k1_decompress_pubkey_cost_base: InternalGas,
157}
158pub fn decompress_pubkey(
159 context: &mut NativeContext,
160 ty_args: Vec<Type>,
161 mut args: VecDeque<Value>,
162) -> PartialVMResult<NativeResult> {
163 safe_assert!(ty_args.is_empty());
164 safe_assert_eq!(args.len(), 1);
165
166 let ecdsa_k1_decompress_pubkey_cost_params = get_extension!(context, NativesCostTable)?
168 .ecdsa_k1_decompress_pubkey_cost_params
169 .clone();
170 native_charge_gas_early_exit!(
172 context,
173 ecdsa_k1_decompress_pubkey_cost_params.ecdsa_k1_decompress_pubkey_cost_base
174 );
175
176 let pubkey = pop_arg!(args, VectorRef);
177 let pubkey_ref = pubkey.as_bytes_ref()?;
178
179 let cost = context.gas_used();
180
181 match Secp256k1PublicKey::from_bytes(&pubkey_ref) {
182 Ok(pubkey) => {
183 let uncompressed = &pubkey.pubkey.serialize_uncompressed();
184 Ok(NativeResult::ok(
185 cost,
186 smallvec![Value::vector_u8(uncompressed.to_vec())],
187 ))
188 }
189 Err(_) => Ok(NativeResult::err(cost, INVALID_PUBKEY)),
190 }
191}
192
193#[derive(Clone)]
194pub struct EcdsaK1Secp256k1VerifyCostParams {
195 pub ecdsa_k1_secp256k1_verify_keccak256_cost_base: InternalGas,
197 pub ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_byte: InternalGas,
199 pub ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_block: InternalGas,
201
202 pub ecdsa_k1_secp256k1_verify_sha256_cost_base: InternalGas,
204 pub ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_byte: InternalGas,
206 pub ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_block: InternalGas,
208}
209pub fn secp256k1_verify(
221 context: &mut NativeContext,
222 ty_args: Vec<Type>,
223 mut args: VecDeque<Value>,
224) -> PartialVMResult<NativeResult> {
225 safe_assert!(ty_args.is_empty());
226 safe_assert_eq!(args.len(), 4);
227
228 let hash = pop_arg!(args, u8);
229
230 let (ecdsa_k1_secp256k1_verify_cost_params, crypto_invalid_arguments_cost) = {
232 let cost_table: &NativesCostTable = get_extension!(context)?;
233 (
234 cost_table.ecdsa_k1_secp256k1_verify_cost_params.clone(),
235 cost_table.crypto_invalid_arguments_cost,
236 )
237 };
238
239 let (base_cost, cost_per_byte, cost_per_block, block_size) = match hash {
240 KECCAK256 => (
241 ecdsa_k1_secp256k1_verify_cost_params.ecdsa_k1_secp256k1_verify_keccak256_cost_base,
242 ecdsa_k1_secp256k1_verify_cost_params
243 .ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_byte,
244 ecdsa_k1_secp256k1_verify_cost_params
245 .ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_block,
246 KECCAK256_BLOCK_SIZE,
247 ),
248 SHA256 => (
249 ecdsa_k1_secp256k1_verify_cost_params.ecdsa_k1_secp256k1_verify_sha256_cost_base,
250 ecdsa_k1_secp256k1_verify_cost_params
251 .ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_byte,
252 ecdsa_k1_secp256k1_verify_cost_params
253 .ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_block,
254 SHA256_BLOCK_SIZE,
255 ),
256 _ => {
257 context.charge_gas(crypto_invalid_arguments_cost)?;
259
260 return Ok(NativeResult::ok(
261 context.gas_used(),
262 smallvec![Value::bool(false)],
263 ));
264 }
265 };
266 native_charge_gas_early_exit!(context, base_cost);
268
269 let msg = pop_arg!(args, VectorRef);
270 let public_key_bytes = pop_arg!(args, VectorRef);
271 let signature_bytes = pop_arg!(args, VectorRef);
272
273 let msg_ref = msg.as_bytes_ref()?;
274 let public_key_bytes_ref = public_key_bytes.as_bytes_ref()?;
275 let signature_bytes_ref = signature_bytes.as_bytes_ref()?;
276
277 native_charge_gas_early_exit!(
279 context,
280 cost_per_byte.saturating_mul(msg_ref.len() as u64)
281 + cost_per_block.saturating_mul(msg_ref.len().div_ceil(block_size) as u64)
282 );
283
284 let cost = context.gas_used();
285
286 let Ok(sig) = <Secp256k1Signature as ToFromBytes>::from_bytes(&signature_bytes_ref) else {
287 return Ok(NativeResult::ok(cost, smallvec![Value::bool(false)]));
288 };
289
290 let Ok(pk) = <Secp256k1PublicKey as ToFromBytes>::from_bytes(&public_key_bytes_ref) else {
291 return Ok(NativeResult::ok(cost, smallvec![Value::bool(false)]));
292 };
293
294 let result = match hash {
295 KECCAK256 => pk.verify_with_hash::<Keccak256>(&msg_ref, &sig).is_ok(),
296 SHA256 => pk.verify_with_hash::<Sha256>(&msg_ref, &sig).is_ok(),
297 _ => false,
298 };
299
300 Ok(NativeResult::ok(cost, smallvec![Value::bool(result)]))
301}
302
303pub fn secp256k1_sign(
311 _context: &mut NativeContext,
312 ty_args: Vec<Type>,
313 mut args: VecDeque<Value>,
314) -> PartialVMResult<NativeResult> {
315 safe_assert!(ty_args.is_empty());
316 safe_assert_eq!(args.len(), 4);
317
318 let cost = 0.into();
321
322 let recoverable = pop_arg!(args, bool);
323 let hash = pop_arg!(args, u8);
324 let msg = pop_arg!(args, VectorRef);
325 let private_key_bytes = pop_arg!(args, VectorRef);
326
327 let msg_ref = msg.as_bytes_ref()?;
328 let private_key_bytes_ref = private_key_bytes.as_bytes_ref()?;
329
330 let sk = match <Secp256k1PrivateKey as ToFromBytes>::from_bytes(&private_key_bytes_ref) {
331 Ok(sk) => sk,
332 Err(_) => return Ok(NativeResult::err(cost, INVALID_PRIVKEY)),
333 };
334
335 let kp = Secp256k1KeyPair::from(sk);
336
337 let signature = match (hash, recoverable) {
338 (KECCAK256, true) => kp
339 .sign_recoverable_with_hash::<Keccak256>(&msg_ref)
340 .as_bytes()
341 .to_vec(),
342 (KECCAK256, false) => kp.sign_with_hash::<Keccak256>(&msg_ref).as_bytes().to_vec(),
343 (SHA256, true) => kp
344 .sign_recoverable_with_hash::<Sha256>(&msg_ref)
345 .as_bytes()
346 .to_vec(),
347 (SHA256, false) => kp.sign_with_hash::<Sha256>(&msg_ref).as_bytes().to_vec(),
348 _ => return Ok(NativeResult::err(cost, INVALID_HASH_FUNCTION)),
349 };
350
351 Ok(NativeResult::ok(
352 cost,
353 smallvec![Value::vector_u8(signature)],
354 ))
355}
356
357pub fn secp256k1_keypair_from_seed(
364 _context: &mut NativeContext,
365 ty_args: Vec<Type>,
366 mut args: VecDeque<Value>,
367) -> PartialVMResult<NativeResult> {
368 safe_assert!(ty_args.is_empty());
369 safe_assert_eq!(args.len(), 1);
370
371 let cost = 0.into();
374
375 let seed = pop_arg!(args, VectorRef);
376 let seed_ref = seed.as_bytes_ref()?;
377
378 if seed_ref.len() != SEED_LENGTH {
379 return Ok(NativeResult::err(cost, INVALID_SEED));
380 }
381 let mut seed_array = [0u8; SEED_LENGTH];
382 seed_array.clone_from_slice(&seed_ref);
383
384 let kp = Secp256k1KeyPair::generate(&mut StdRng::from_seed(seed_array));
385
386 let pk_bytes = kp.public().as_bytes().to_vec();
387 let sk_bytes = kp.private().as_bytes().to_vec();
388
389 Ok(NativeResult::ok(
390 cost,
391 smallvec![Value::struct_(values::Struct::pack(vec![
392 Value::vector_u8(sk_bytes),
393 Value::vector_u8(pk_bytes),
394 ]))],
395 ))
396}