Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ecrecover syscall #17720

Merged
merged 16 commits into from
Jul 7, 2021
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions programs/bpf/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions programs/bpf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ members = [
"rust/ro_modify",
"rust/ro_account_modify",
"rust/sanity",
"rust/secp256k1_recover",
"rust/sha",
"rust/spoof1",
"rust/spoof1_system",
Expand Down
1 change: 1 addition & 0 deletions programs/bpf/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ fn main() {
"ro_modify",
"ro_account_modify",
"sanity",
"secp256k1_recover",
"sha",
"spoof1",
"spoof1_system",
Expand Down
38 changes: 38 additions & 0 deletions programs/bpf/c/src/secp256k1_recover/secp256k1_recover.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* @brief Secp256k1Recover Syscall test
*/
#include <solana_sdk.h>

extern uint64_t entrypoint(const uint8_t *input) {

uint8_t result[SECP256K1_RECOVER_RESULT_LENGTH];
uint8_t expected[] = { 0x42, 0xcd, 0x27, 0xe4, 0x0f, 0xdf, 0x7c, 0x97,
0x0a, 0xa2, 0xca, 0x0b, 0x88, 0x5b, 0x96, 0x0f,
0x8b, 0x62, 0x8a, 0x41, 0xa1, 0x81, 0xe7, 0xe6,
0x8e, 0x03, 0xea, 0x0b, 0x84, 0x20, 0x58, 0x9b,
0x32, 0x06, 0xbd, 0x66, 0x2f, 0x75, 0x65, 0xd6,
0x9d, 0xbd, 0x1d, 0x34, 0x29, 0x6a, 0xd9, 0x35,
0x38, 0xed, 0x86, 0x9e, 0x99, 0x20, 0x43, 0xc3,
0xeb, 0xad, 0x65, 0x50, 0xa0, 0x11, 0x6e, 0x5d};

uint8_t hash[] = { 0xde, 0xa5, 0x66, 0xb6, 0x94, 0x3b, 0xe0, 0xe9,
0x62, 0x53, 0xc2, 0x21, 0x5b, 0x1b, 0xac, 0x69,
0xe7, 0xa8, 0x1e, 0xdb, 0x41, 0xc5, 0x02, 0x8b,
0x4f, 0x5c, 0x45, 0xc5, 0x3b, 0x49, 0x54, 0xd0};
uint8_t signature[] = { 0x97, 0xa4, 0xee, 0x31, 0xfe, 0x82, 0x65, 0x72,
0x9f, 0x4a, 0xa6, 0x7d, 0x24, 0xd4, 0xa7, 0x27,
0xf8, 0xc3, 0x15, 0xa4, 0xc8, 0xf9, 0x80, 0xeb,
0x4c, 0x4d, 0x4a, 0xfa, 0x6e, 0xc9, 0x42, 0x41,
0x5d, 0x10, 0xd9, 0xc2, 0x8a, 0x90, 0xe9, 0x92,
0x9c, 0x52, 0x4b, 0x2c, 0xfb, 0x65, 0xdf, 0xbc,
0xf6, 0x8c, 0xfd, 0x68, 0xdb, 0x17, 0xf9, 0x5d,
0x23, 0x5f, 0x96, 0xd8, 0xf0, 0x72, 0x01, 0x2d};
uint64_t recovery_id = 1;

uint64_t result_code = sol_secp256k1_recover(hash, recovery_id, signature, result);

sol_assert(0 == result_code);
sol_assert(0 == sol_memcmp(result, expected, SHA256_RESULT_LENGTH));

return SUCCESS;
}
19 changes: 19 additions & 0 deletions programs/bpf/rust/secp256k1_recover/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "solana-bpf-rust-secp256k1-recover"
version = "1.8.0"
description = "Solana BPF test program written in Rust"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
documentation = "https://docs.rs/solana-bpf-rust-secp256k1-recover"
edition = "2018"

[dependencies]
solana-program = { path = "../../../../sdk/program", version = "=1.8.0" }

[lib]
crate-type = ["cdylib"]

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
44 changes: 44 additions & 0 deletions programs/bpf/rust/secp256k1_recover/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//! @brief Secp256k1Recover Syscall test

extern crate solana_program;
use solana_program::{custom_panic_default, msg};

fn test_secp256k1_recover() {
use solana_program::secp256k1_recover::secp256k1_recover;

let expected: [u8; 64] = [
0x42, 0xcd, 0x27, 0xe4, 0x0f, 0xdf, 0x7c, 0x97, 0x0a, 0xa2, 0xca, 0x0b, 0x88, 0x5b, 0x96,
0x0f, 0x8b, 0x62, 0x8a, 0x41, 0xa1, 0x81, 0xe7, 0xe6, 0x8e, 0x03, 0xea, 0x0b, 0x84, 0x20,
0x58, 0x9b, 0x32, 0x06, 0xbd, 0x66, 0x2f, 0x75, 0x65, 0xd6, 0x9d, 0xbd, 0x1d, 0x34, 0x29,
0x6a, 0xd9, 0x35, 0x38, 0xed, 0x86, 0x9e, 0x99, 0x20, 0x43, 0xc3, 0xeb, 0xad, 0x65, 0x50,
0xa0, 0x11, 0x6e, 0x5d,
];

let hash: [u8; 32] = [
0xde, 0xa5, 0x66, 0xb6, 0x94, 0x3b, 0xe0, 0xe9, 0x62, 0x53, 0xc2, 0x21, 0x5b, 0x1b, 0xac,
0x69, 0xe7, 0xa8, 0x1e, 0xdb, 0x41, 0xc5, 0x02, 0x8b, 0x4f, 0x5c, 0x45, 0xc5, 0x3b, 0x49,
0x54, 0xd0,
];
let recovery_id: u8 = 1;
let signature: [u8; 64] = [
0x97, 0xa4, 0xee, 0x31, 0xfe, 0x82, 0x65, 0x72, 0x9f, 0x4a, 0xa6, 0x7d, 0x24, 0xd4, 0xa7,
0x27, 0xf8, 0xc3, 0x15, 0xa4, 0xc8, 0xf9, 0x80, 0xeb, 0x4c, 0x4d, 0x4a, 0xfa, 0x6e, 0xc9,
0x42, 0x41, 0x5d, 0x10, 0xd9, 0xc2, 0x8a, 0x90, 0xe9, 0x92, 0x9c, 0x52, 0x4b, 0x2c, 0xfb,
0x65, 0xdf, 0xbc, 0xf6, 0x8c, 0xfd, 0x68, 0xdb, 0x17, 0xf9, 0x5d, 0x23, 0x5f, 0x96, 0xd8,
0xf0, 0x72, 0x01, 0x2d,
];

let public_key = secp256k1_recover(&hash[..], recovery_id, &signature[..]).unwrap();
assert_eq!(public_key.to_bytes(), expected);
}

#[no_mangle]
pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
msg!("secp256k1_recover");

test_secp256k1_recover();

0
}

custom_panic_default!();
6 changes: 5 additions & 1 deletion programs/bpf/tests/programs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,7 @@ fn test_program_bpf_sanity() {
("relative_call", true),
("sanity", true),
("sanity++", true),
("secp256k1_recover", true),
("sha", true),
("struct_pass", true),
("struct_ret", true),
Expand All @@ -460,6 +461,7 @@ fn test_program_bpf_sanity() {
("solana_bpf_rust_param_passing", true),
("solana_bpf_rust_rand", true),
("solana_bpf_rust_sanity", true),
("solana_bpf_rust_secp256k1_recover", true),
("solana_bpf_rust_sha", true),
]);
}
Expand Down Expand Up @@ -1282,6 +1284,7 @@ fn assert_instruction_count() {
("relative_call", 10),
("sanity", 169),
("sanity++", 168),
("secp256k1_recover", 357),
("sha", 1040),
("struct_pass", 8),
("struct_ret", 22),
Expand All @@ -1303,7 +1306,8 @@ fn assert_instruction_count() {
("solana_bpf_rust_param_passing", 46),
("solana_bpf_rust_rand", 481),
("solana_bpf_rust_sanity", 880),
("solana_bpf_rust_sha", 32301),
("solana_bpf_rust_secp256k1_recover", 307),
("solana_bpf_rust_sha", 32333),
]);
}

Expand Down
1 change: 1 addition & 0 deletions programs/bpf_loader/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ log = "0.4.11"
num-derive = "0.3"
num-traits = "0.2"
rand_core = "0.6.2"
libsecp256k1 = "0.5.0"
sha3 = "0.9.1"
solana-measure = { path = "../../measure", version = "=1.8.0" }
solana-runtime = { path = "../../runtime", version = "=1.8.0" }
Expand Down
107 changes: 106 additions & 1 deletion programs/bpf_loader/src/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ use solana_sdk::{
feature_set::{
blake3_syscall_enabled, cpi_data_cost, demote_sysvar_write_locks,
enforce_aligned_host_addrs, keccak256_syscall_enabled, memory_ops_syscalls,
set_upgrade_authority_via_cpi_enabled, sysvar_via_syscall, update_data_on_realloc,
secp256k1_recover_syscall_enabled, set_upgrade_authority_via_cpi_enabled,
sysvar_via_syscall, update_data_on_realloc,
},
hash::{Hasher, HASH_BYTES},
ic_msg,
Expand All @@ -32,6 +33,9 @@ use solana_sdk::{
process_instruction::{self, stable_log, ComputeMeter, InvokeContext, Logger},
pubkey::{Pubkey, PubkeyError, MAX_SEEDS},
rent::Rent,
secp256k1_recover::{
Secp256k1RecoverError, SECP256K1_PUBLIC_KEY_LENGTH, SECP256K1_SIGNATURE_LENGTH,
},
sysvar::{self, fees::Fees, Sysvar, SysvarId},
};
use std::{
Expand Down Expand Up @@ -134,6 +138,11 @@ pub fn register_syscalls(
syscall_registry.register_syscall_by_name(b"sol_keccak256", SyscallKeccak256::call)?;
}

if invoke_context.is_feature_active(&secp256k1_recover_syscall_enabled::id()) {
syscall_registry
.register_syscall_by_name(b"sol_secp256k1_recover", SyscallSecp256k1Recover::call)?;
}

if invoke_context.is_feature_active(&blake3_syscall_enabled::id()) {
syscall_registry.register_syscall_by_name(b"sol_blake3", SyscallBlake3::call)?;
}
Expand Down Expand Up @@ -331,6 +340,16 @@ pub fn bind_syscall_context_objects<'a>(
}),
);

bind_feature_gated_syscall_context_object!(
vm,
invoke_context.is_feature_active(&secp256k1_recover_syscall_enabled::id()),
Box::new(SyscallSecp256k1Recover {
cost: bpf_compute_budget.secp256k1_recover_cost,
compute_meter: invoke_context.get_compute_meter(),
loader_id,
}),
);

let is_sysvar_via_syscall_active = invoke_context.is_feature_active(&sysvar_via_syscall::id());

let invoke_context = Rc::new(RefCell::new(invoke_context));
Expand Down Expand Up @@ -1343,6 +1362,92 @@ impl<'a> SyscallObject<BpfError> for SyscallMemset<'a> {
}
}

/// secp256k1_recover
pub struct SyscallSecp256k1Recover<'a> {
cost: u64,
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
loader_id: &'a Pubkey,
}

impl<'a> SyscallObject<BpfError> for SyscallSecp256k1Recover<'a> {
fn call(
&mut self,
hash_addr: u64,
recovery_id_val: u64,
signature_addr: u64,
result_addr: u64,
_arg5: u64,
memory_mapping: &MemoryMapping,
result: &mut Result<u64, EbpfError<BpfError>>,
) {
question_mark!(self.compute_meter.consume(self.cost), result);

let hash = question_mark!(
translate_slice::<u8>(
memory_mapping,
hash_addr,
keccak::HASH_BYTES as u64,
self.loader_id,
true,
),
result
);
let signature = question_mark!(
translate_slice::<u8>(
memory_mapping,
signature_addr,
SECP256K1_SIGNATURE_LENGTH as u64,
self.loader_id,
true,
),
result
);
let secp256k1_recover_result = question_mark!(
translate_slice_mut::<u8>(
memory_mapping,
result_addr,
SECP256K1_PUBLIC_KEY_LENGTH as u64,
self.loader_id,
true,
),
result
);

let message = match libsecp256k1::Message::parse_slice(hash) {
Ok(msg) => msg,
Err(_) => {
*result = Ok(Secp256k1RecoverError::InvalidHash.into());
return;
}
};
let recovery_id = match libsecp256k1::RecoveryId::parse(recovery_id_val as u8) {
Ok(id) => id,
Err(_) => {
*result = Ok(Secp256k1RecoverError::InvalidRecoveryId.into());
return;
}
};
let signature = match libsecp256k1::Signature::parse_standard_slice(signature) {
Ok(sig) => sig,
Err(_) => {
*result = Ok(Secp256k1RecoverError::InvalidSignature.into());
return;
}
};

let public_key = match libsecp256k1::recover(&message, &signature, &recovery_id) {
Ok(key) => key.serialize(),
Err(_) => {
*result = Ok(Secp256k1RecoverError::InvalidSignature.into());
return;
}
};

secp256k1_recover_result.copy_from_slice(&public_key[1..65]);
*result = Ok(SUCCESS);
}
}

// Blake3
pub struct SyscallBlake3<'a> {
base_cost: u64,
Expand Down
31 changes: 31 additions & 0 deletions sdk/bpf/c/inc/solana_sdk.h
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,37 @@ uint64_t sol_keccak256(
uint8_t *result
);


/** Length of a secp256k1 recover input hash */
#define SECP256K1_RECOVER_HASH_LENGTH 32
/** Length of a secp256k1 input signature */
#define SECP256K1_RECOVER_SIGNATURE_LENGTH 64
/** Length of a secp256k1 recover result */
#define SECP256K1_RECOVER_RESULT_LENGTH 64

/** The hash provided to a sol_secp256k1_recover is invalid */
#define SECP256K1_RECOVER_ERROR_INVALID_HASH 1
/** The recovery_id provided to a sol_secp256k1_recover is invalid */
#define SECP256K1_RECOVER_ERROR_INVALID_RECOVERY_ID 2
/** The signature provided to a sol_secp256k1_recover is invalid */
#define SECP256K1_RECOVER_ERROR_INVALID_SIGNATURE 3

/**
* Recover public key from a signed message.
*
* @param hash Hashed message
* @param recovery_id Tag used for public key recovery from signatures. Can be 0 or 1
* @param signature An ECDSA signature
* @param result 64 byte array to hold the result. A recovered public key
* @return 0 if executed successfully
*/
uint64_t sol_secp256k1_recover(
const uint8_t *hash,
uint64_t recovery_id,
const uint8_t *signature,
uint8_t *result
);

/**
* Length of a Blake3 hash result
*/
Expand Down
1 change: 1 addition & 0 deletions sdk/program/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ thiserror = "1.0"

[target.'cfg(not(target_arch = "bpf"))'.dependencies]
curve25519-dalek = "2.1.0"
libsecp256k1 = "0.5.0"
rand = "0.7.0"
solana-logger = { path = "../../logger", version = "=1.8.0" }

Expand Down
Loading