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