Skip to content

Commit

Permalink
feat: add ed25519_verify host function
Browse files Browse the repository at this point in the history
  • Loading branch information
medicz committed Mar 28, 2023
1 parent 89f8c2d commit 679284b
Show file tree
Hide file tree
Showing 9 changed files with 142 additions and 18 deletions.
15 changes: 7 additions & 8 deletions near-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,25 +31,24 @@ schemars = { version = "0.8.8", optional = true }
wee_alloc = { version = "0.4.5", default-features = false, optional = true }

# Used for caching, might be worth porting only functionality needed.
once_cell = { version = "1.8", default-features = false }
once_cell = { version = "1.17", default-features = false }

near-abi = { version = "0.3.0", features = ["__chunked-entries"], optional = true }

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
# alt_bn128 feature will need to be removed on the next version update (now stabilized)
near-vm-logic = { version = "0.14", optional = true, features = ["protocol_feature_alt_bn128"] }
near-primitives-core = { version = "0.14", optional = true }
near-primitives = { version = "0.14", optional = true }
near-crypto = { version = "0.14", optional = true }
near-vm-logic = { version = "0.16", optional = true }
near-primitives-core = { version = "0.16", optional = true }
near-primitives = { version = "0.16", optional = true }
near-crypto = { version = "0.16", optional = true }

[dev-dependencies]
rand = "0.8.4"
trybuild = "1.0"
rustversion = "1.0"
rand_xorshift = "0.3"
quickcheck = "1.0"
arbitrary = { version = ">=1.0, <1.1.4", features = ["derive"] }
derive_arbitrary = ">=1.0, <=1.1.6"
arbitrary = { version = "1.2.3", features = ["derive"] }
derive_arbitrary = "1.2.3"
hex = { version = "0.4.3", features = ["serde"] }

[features]
Expand Down
58 changes: 58 additions & 0 deletions near-sdk/src/environment/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,20 @@ pub fn ecrecover(
}
}

/// Verifies signature of message using provided ED25519 Public Key
pub fn ed25519_verify(signature: &[u8; 64], message: &[u8], public_key: &[u8; 32]) -> bool {
unsafe {
sys::ed25519_verify(
signature.len() as _,
signature.as_ptr() as _,
message.len() as _,
message.as_ptr() as _,
public_key.len() as _,
public_key.as_ptr() as _,
) == 1
}
}

// ################
// # Promises API #
// ################
Expand Down Expand Up @@ -1050,4 +1064,48 @@ mod tests {
.build());
assert_eq!(super::signer_account_pk(), key);
}

#[test]
fn ed25519_verify() {
const SIGNATURE: [u8; 64] = [
145, 193, 203, 18, 114, 227, 14, 117, 33, 213, 121, 66, 130, 14, 25, 4, 36, 120, 46,
142, 226, 215, 7, 66, 122, 112, 97, 30, 249, 135, 61, 165, 221, 249, 252, 23, 105, 40,
56, 70, 31, 152, 236, 141, 154, 122, 207, 20, 75, 118, 79, 90, 168, 6, 221, 122, 213,
29, 126, 196, 216, 104, 191, 6,
];

const BAD_SIGNATURE: [u8; 64] = [1; 64];

// create a forged signature with the `s` scalar not properly reduced
// https://docs.rs/ed25519/latest/src/ed25519/lib.rs.html#302
const FORGED_SIGNATURE: [u8; 64] = {
let mut sig = SIGNATURE;
sig[63] = 0b1110_0001;
sig
};

const PUBLIC_KEY: [u8; 32] = [
32, 122, 6, 120, 146, 130, 30, 37, 215, 112, 241, 251, 160, 196, 124, 17, 255, 75, 129,
62, 84, 22, 46, 206, 158, 184, 57, 224, 118, 35, 26, 182,
];

// create a forged public key to force a PointDecompressionError
// https://docs.rs/ed25519-dalek/latest/src/ed25519_dalek/public.rs.html#142
const FORGED_PUBLIC_KEY: [u8; 32] = {
let mut key = PUBLIC_KEY;
key[31] = 0b1110_0001;
key
};

// 32 bytes message
const MESSAGE: [u8; 32] = [
107, 97, 106, 100, 108, 102, 107, 106, 97, 108, 107, 102, 106, 97, 107, 108, 102, 106,
100, 107, 108, 97, 100, 106, 102, 107, 108, 106, 97, 100, 115, 107,
];

assert!(super::ed25519_verify(&SIGNATURE, &MESSAGE, &PUBLIC_KEY));
assert!(!super::ed25519_verify(&BAD_SIGNATURE, &MESSAGE, &FORGED_PUBLIC_KEY));
assert!(!super::ed25519_verify(&SIGNATURE, &MESSAGE, &FORGED_PUBLIC_KEY));
assert!(!super::ed25519_verify(&FORGED_SIGNATURE, &MESSAGE, &PUBLIC_KEY));
}
}
8 changes: 6 additions & 2 deletions near-sdk/src/environment/mock/external.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use near_primitives::types::TrieNodesCount;
use near_primitives_core::hash::{hash, CryptoHash};
use near_primitives_core::types::{AccountId, Balance};
use near_vm_logic::{External, ValuePtr};
use near_vm_logic::{External, StorageGetMode, ValuePtr};
use std::collections::HashMap;

type Result<T> = ::core::result::Result<T, near_vm_logic::VMLogicError>;
Expand Down Expand Up @@ -41,7 +41,11 @@ impl External for SdkExternal {
Ok(())
}

fn storage_get(&self, key: &[u8]) -> Result<Option<Box<dyn ValuePtr>>> {
fn storage_get(
&self,
key: &[u8],
_storage_get_mode: StorageGetMode,
) -> Result<Option<Box<dyn ValuePtr>>> {
Ok(self
.fake_trie
.get(key)
Expand Down
31 changes: 28 additions & 3 deletions near-sdk/src/environment/mock/mocked_blockchain.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use super::{Receipt, SdkExternal};
// TODO replace with near_vm_logic::mocks::mock_memory::MockedMemory after updating version from 0.16
use crate::mock::mocked_memory::MockedMemory;
use crate::mock::VmAction;
use crate::test_utils::VMContextBuilder;
use crate::types::{Balance, PromiseResult};
use crate::{Gas, RuntimeFeesConfig};
use crate::{PublicKey, VMContext};
use near_crypto::PublicKey as VmPublicKey;
use near_primitives::transaction::Action as PrimitivesAction;
use near_vm_logic::mocks::mock_memory::MockedMemory;
use near_vm_logic::types::PromiseResult as VmPromiseResult;
use near_vm_logic::{External, MemoryLike, VMConfig, VMLogic};
use std::cell::RefCell;
Expand Down Expand Up @@ -118,7 +119,7 @@ fn sdk_context_to_vm_context(context: VMContext) -> near_vm_logic::VMContext {
signer_account_pk: context.signer_account_pk.into_bytes(),
predecessor_account_id: context.predecessor_account_id.as_str().parse().unwrap(),
input: context.input,
block_index: context.block_index,
block_height: context.block_height,
block_timestamp: context.block_timestamp,
epoch_height: context.epoch_height,
account_balance: context.account_balance,
Expand Down Expand Up @@ -173,12 +174,16 @@ fn action_to_sdk_action(action: &PrimitivesAction) -> VmAction {
PrimitivesAction::DeleteAccount(a) => {
VmAction::DeleteAccount { beneficiary_id: a.beneficiary_id.parse().unwrap() }
}
PrimitivesAction::Delegate(_d) => {
panic!("Unimplemented")
}
}
}

fn pub_key_conversion(key: &VmPublicKey) -> PublicKey {
// Hack by serializing and deserializing the key. This format should be consistent.
String::from(key).parse().unwrap()
let key_bytes = [&[key.key_type() as u8], key.key_data()].concat();
PublicKey::try_from(key_bytes).unwrap()
}

#[cfg(not(target_arch = "wasm32"))]
Expand Down Expand Up @@ -291,6 +296,26 @@ mod mock_chain {
})
}
#[no_mangle]
extern "C" fn ed25519_verify(
signature_len: u64,
signature_ptr: u64,
message_len: u64,
message_ptr: u64,
public_key_len: u64,
public_key_ptr: u64,
) -> u64 {
with_mock_interface(|b| {
b.ed25519_verify(
signature_len,
signature_ptr,
message_len,
message_ptr,
public_key_len,
public_key_ptr,
)
})
}
#[no_mangle]
extern "C" fn value_return(value_len: u64, value_ptr: u64) {
with_mock_interface(|b| b.value_return(value_len, value_ptr))
}
Expand Down
29 changes: 29 additions & 0 deletions near-sdk/src/environment/mock/mocked_memory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//!This file is to be removed once near-vm-logic is updated from version 0.16
use near_vm_logic::{MemSlice, MemoryLike};

#[derive(Default)]
pub struct MockedMemory {}

impl MemoryLike for MockedMemory {
fn fits_memory(&self, _slice: MemSlice) -> Result<(), ()> {
Ok(())
}

fn read_memory(&self, ptr: u64, buffer: &mut [u8]) -> Result<(), ()> {
let src = unsafe { std::slice::from_raw_parts(ptr as *const u8, buffer.len()) };
buffer.copy_from_slice(src);
Ok(())
}

fn write_memory(&mut self, ptr: u64, buffer: &[u8]) -> Result<(), ()> {
let dest = unsafe { std::slice::from_raw_parts_mut(ptr as *mut u8, buffer.len()) };
dest.copy_from_slice(buffer);
Ok(())
}

fn view_memory(&self, slice: MemSlice) -> Result<std::borrow::Cow<[u8]>, ()> {
let src = unsafe { std::slice::from_raw_parts(slice.ptr as *const u8, slice.len as usize) };

Some(src).map(std::borrow::Cow::Borrowed).ok_or(())
}
}
1 change: 1 addition & 0 deletions near-sdk/src/environment/mock/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod external;
mod mocked_blockchain;
mod mocked_memory;
mod receipt;

pub(crate) use self::external::SdkExternal;
Expand Down
2 changes: 1 addition & 1 deletion near-sdk/src/store/tree_map/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ where
}
}

fn get_entry_mut<'a, 'b, K, V, H>(map: &'b mut LookupMap<K, V, H>, key: &'a K) -> (&'a K, &'a mut V)
fn get_entry_mut<'a, K, V, H>(map: & mut LookupMap<K, V, H>, key: &'a K) -> (&'a K, &'a mut V)
where
K: BorshSerialize + Ord + BorshDeserialize + Clone,
V: BorshSerialize + BorshDeserialize,
Expand Down
8 changes: 4 additions & 4 deletions near-sdk/src/test_utils/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub struct VMContext {
/// Encoded as base64 string to be able to pass input in borsh binary format.
pub input: Vec<u8>,
/// The current block height.
pub block_index: BlockHeight,
pub block_height: BlockHeight,
/// The current block timestamp (number of non-leap-nanoseconds since January 1, 1970 0:00:00 UTC).
pub block_timestamp: u64,
/// The current epoch height.
Expand Down Expand Up @@ -90,7 +90,7 @@ impl VMContextBuilder {
signer_account_pk: vec![0u8; 33].try_into().unwrap(),
predecessor_account_id: bob(),
input: vec![],
block_index: 0,
block_height: 0,
block_timestamp: 0,
epoch_height: 0,
account_balance: 10u128.pow(26),
Expand Down Expand Up @@ -125,8 +125,8 @@ impl VMContextBuilder {
self
}

pub fn block_index(&mut self, block_index: BlockHeight) -> &mut Self {
self.context.block_index = block_index;
pub fn block_height(&mut self, block_height: BlockHeight) -> &mut Self {
self.context.block_height = block_height;
self
}

Expand Down
8 changes: 8 additions & 0 deletions sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ extern "C" {
malleability_flag: u64,
register_id: u64,
) -> u64;
pub fn ed25519_verify(
sig_len: u64,
sig_ptr: u64,
msg_len: u64,
msg_ptr: u64,
pub_key_len: u64,
pub_key_ptr: u64,
) -> u64;
// #####################
// # Miscellaneous API #
// #####################
Expand Down

0 comments on commit 679284b

Please sign in to comment.