Skip to content

Commit

Permalink
fix: audit (#126)
Browse files Browse the repository at this point in the history
* add verify function

* fix dex single_asset_provide to use more explicit condition

* fix nft to mintable by collection object owner

* fix tests and add comments
  • Loading branch information
beer-1 authored Sep 19, 2024
1 parent 7513151 commit 50ed630
Show file tree
Hide file tree
Showing 33 changed files with 797 additions and 477 deletions.
1 change: 1 addition & 0 deletions crates/gas/src/initia_stdlib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ crate::macros::define_gas_parameters!(
[crypto_ed25519_per_msg_byte_hashing: InternalGasPerByte, "crypto.ed25519.per_msg_byte_hashing", 220],

[crypto_secp256k1_base: InternalGas, "crypto.secp256k1.base", 551],
[crypto_secp256k1_per_sig_verify: InternalGasPerArg, "crypto.secp256k1.per_sig_verify", 981492],
[crypto_secp256k1_per_ecdsa_recover: InternalGasPerArg, "crypto.secp256k1.per_ecdsa_recover", 5918360],
[crypto_secp256k1_per_pubkey_deserialize: InternalGasPerArg, "crypto.secp256k1.per_pubkey_deserialize", 139688],
[crypto_secp256k1_per_sig_deserialize: InternalGasPerArg, "crypto.secp256k1.per_sig_deserialize", 1378],
Expand Down
66 changes: 59 additions & 7 deletions crates/natives/src/crypto/secp256k1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ use move_vm_runtime::native_functions::NativeFunction;
use move_vm_types::{loaded_data::runtime_types::Type, values::Value};

use libsecp256k1::{
recover, util::MESSAGE_SIZE, util::SIGNATURE_SIZE, Message, RecoveryId, Signature,
recover,
util::{COMPRESSED_PUBLIC_KEY_SIZE, MESSAGE_SIZE, SIGNATURE_SIZE},
verify, Message, PublicKey, RecoveryId, Signature,
};

use smallvec::{smallvec, SmallVec};
Expand Down Expand Up @@ -33,6 +35,56 @@ fn read_hash(data: &[u8]) -> Result<[u8; MESSAGE_SIZE], TryFromSliceError> {
data.try_into()
}

fn read_pubkey(data: &[u8]) -> Result<[u8; COMPRESSED_PUBLIC_KEY_SIZE], TryFromSliceError> {
data.try_into()
}

pub fn native_verify(
context: &mut SafeNativeContext,
_ty_args: Vec<Type>,
mut arguments: VecDeque<Value>,
) -> SafeNativeResult<SmallVec<[Value; 1]>> {
let gas_params = &context.native_gas_params.initia_stdlib;
context.charge(gas_params.crypto_secp256k1_base)?;

debug_assert!(_ty_args.is_empty());
debug_assert!(arguments.len() == 3);

let signature = safely_pop_arg!(arguments, Vec<u8>);
let pubkey = safely_pop_arg!(arguments, Vec<u8>);
let message = safely_pop_arg!(arguments, Vec<u8>);

let msg = match read_hash(&message) {
Ok(mh) => Message::parse(&mh),
Err(_) => {
return Err(SafeNativeError::Abort {
abort_code: UNABLE_TO_DESERIALIZE,
});
}
};

context.charge(gas_params.crypto_secp256k1_per_pubkey_deserialize * NumArgs::one())?;
let pk = match read_pubkey(&pubkey) {
Ok(pk) => match PublicKey::parse_compressed(&pk) {
Ok(pk) => pk,
Err(_) => return Ok(smallvec![Value::bool(false)]),
},
Err(_) => return Ok(smallvec![Value::bool(false)]),
};

context.charge(gas_params.crypto_secp256k1_per_sig_deserialize * NumArgs::one())?;
let sig = match read_signature(&signature) {
Ok(sig) => match Signature::parse_standard(&sig) {
Ok(sig) => sig,
Err(_) => return Ok(smallvec![Value::bool(false)]),
},
Err(_) => return Ok(smallvec![Value::bool(false)]),
};

context.charge(gas_params.crypto_secp256k1_per_sig_verify * NumArgs::one())?;
Ok(smallvec![Value::bool(verify(&msg, &sig, &pk))])
}

pub fn native_recover_public_key(
context: &mut SafeNativeContext,
_ty_args: Vec<Type>,
Expand Down Expand Up @@ -102,7 +154,7 @@ pub fn native_recover_public_key(
use rand_core::OsRng;

#[cfg(feature = "testing")]
use libsecp256k1::{sign, PublicKey, SecretKey};
use libsecp256k1::{sign, SecretKey};

#[cfg(feature = "testing")]
pub fn native_test_only_generate_keys(
Expand Down Expand Up @@ -151,18 +203,18 @@ pub fn make_all(
builder: &SafeNativeBuilder,
) -> impl Iterator<Item = (String, NativeFunction)> + '_ {
let mut natives = vec![];
natives.extend([(
"recover_public_key_internal",
native_recover_public_key as RawSafeNative,
)]);
natives.extend([
("verify_internal", native_verify as RawSafeNative),
("recover_public_key_internal", native_recover_public_key),
]);

#[cfg(feature = "testing")]
natives.extend([
(
"generate_keys",
native_test_only_generate_keys as RawSafeNative,
),
("sign", native_test_only_sign as RawSafeNative),
("sign", native_test_only_sign),
]);

builder.make_named_natives(natives)
Expand Down
Binary file modified precompile/binaries/minlib/collection.mv
Binary file not shown.
Binary file modified precompile/binaries/minlib/dex.mv
Binary file not shown.
Binary file modified precompile/binaries/minlib/initia_nft.mv
Binary file not shown.
Binary file modified precompile/binaries/minlib/nft.mv
Binary file not shown.
Binary file modified precompile/binaries/minlib/object.mv
Binary file not shown.
Binary file modified precompile/binaries/minlib/secp256k1.mv
Binary file not shown.
Binary file modified precompile/binaries/minlib/soul_bound_token.mv
Binary file not shown.
Binary file modified precompile/binaries/stdlib/collection.mv
Binary file not shown.
Binary file modified precompile/binaries/stdlib/dex.mv
Binary file not shown.
Binary file modified precompile/binaries/stdlib/initia_nft.mv
Binary file not shown.
Binary file modified precompile/binaries/stdlib/nft.mv
Binary file not shown.
Binary file modified precompile/binaries/stdlib/object.mv
Binary file not shown.
Binary file modified precompile/binaries/stdlib/secp256k1.mv
Binary file not shown.
Binary file modified precompile/binaries/stdlib/soul_bound_token.mv
Binary file not shown.
2 changes: 1 addition & 1 deletion precompile/modules/initia_stdlib/sources/block.move
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ module initia_std::block {
vm: &signer, _fake_block_hash: address
) {
if (!exists<HasGenesisBlock>(signer::address_of(vm))) {
move_to(vm, HasGenesisBlock{});
move_to(vm, HasGenesisBlock {});
return
};

Expand Down
53 changes: 53 additions & 0 deletions precompile/modules/initia_stdlib/sources/crypto/secp256k1.move
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,24 @@ module initia_std::secp256k1 {
sig.bytes
}

/// Returns `true` if the signature can verify the public key on the message
public fun verify(
message: vector<u8>,
public_key: &ECDSACompressedPublicKey,
signature: &ECDSASignature
): bool {
assert!(
std::vector::length(&message) == MESSAGE_SIZE,
std::error::invalid_argument(E_DESERIALIZE)
);

return verify_internal(
message,
public_key.bytes,
signature.bytes
)
}

/// Recovers the signer's raw (64-byte) public key from a secp256k1 ECDSA `signature` given the `recovery_id` and the signed
/// `message` (32 byte digest).
///
Expand Down Expand Up @@ -151,6 +169,18 @@ module initia_std::secp256k1 {
// Native functions
//

/// Returns `true` if `signature` verifies on `public_key` and `message`
/// and returns `false` otherwise.
///
/// - `message`: A 32-byte hashed message.
/// - `public_key`: A compressed public key in bytes.
/// - `signature`: A 64-byte ECDSA signature.
native fun verify_internal(
message: vector<u8>,
public_key: vector<u8>,
signature: vector<u8>
): bool;

/// Returns `(public_key, true)` if `signature` verifies on `message` under the recovered `public_key`
/// and returns `([], false)` otherwise.
native fun recover_public_key_internal(
Expand All @@ -172,6 +202,29 @@ module initia_std::secp256k1 {
// Tests
//

#[test]
fun test_secp256k1_sign_verify() {
use std::hash;

let (sk, vk) = generate_keys(true);
let pk = ecdsa_compressed_public_key_from_bytes(vk);

let msg: vector<u8> = hash::sha2_256(b"test initia secp256k1");
let (_rid, sig_bytes) = sign(msg, sk);
let sig = ecdsa_signature_from_bytes(sig_bytes);
assert!(verify(msg, &pk, &sig), 1);

// Test with an incorrect message
let wrong_msg: vector<u8> = hash::sha2_256(b"wrong message");
assert!(!verify(wrong_msg, &pk, &sig), 2);

// Test with an incorrect signature
let invalid_sig_bytes = sig_bytes;
*std::vector::borrow_mut(&mut invalid_sig_bytes, 0) = 0xFF; // Corrupt the signature
let invalid_sig = ecdsa_signature_from_bytes(invalid_sig_bytes);
assert!(!verify(msg, &pk, &invalid_sig), 3);
}

#[test]
fun test_gen_sign_recover() {
use std::hash;
Expand Down
Loading

0 comments on commit 50ed630

Please sign in to comment.