diff --git a/near-sdk/Cargo.toml b/near-sdk/Cargo.toml index ccf853574..9ce2b2023 100644 --- a/near-sdk/Cargo.toml +++ b/near-sdk/Cargo.toml @@ -37,10 +37,10 @@ near-abi = { version = "0.3.0", features = ["__chunked-entries"], optional = tru [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 = { git = "https://github.com/near/nearcore.git", rev = "6e08a41084c632010b1d4c42132ad58ecf1398a2", optional = true, features = [] } +near-primitives-core = { git = "https://github.com/near/nearcore.git", rev = "6e08a41084c632010b1d4c42132ad58ecf1398a2", optional = true } +near-primitives = { git = "https://github.com/near/nearcore.git", rev = "6e08a41084c632010b1d4c42132ad58ecf1398a2", optional = true } +near-crypto = { git = "https://github.com/near/nearcore.git", rev = "6e08a41084c632010b1d4c42132ad58ecf1398a2", optional = true } [dev-dependencies] rand = "0.8.4" @@ -48,8 +48,8 @@ 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] diff --git a/near-sdk/src/environment/env.rs b/near-sdk/src/environment/env.rs index 109fc11fa..9a952024a 100644 --- a/near-sdk/src/environment/env.rs +++ b/near-sdk/src/environment/env.rs @@ -870,6 +870,20 @@ pub fn is_valid_account_id(account_id: &[u8]) -> bool { !last_char_is_separator } +/// Verifies an ED25519 signature +pub fn ed25519_verify(signature: &[u8], msg: &[u8], pub_key: &[u8]) -> bool { + unsafe { + sys::ed25519_verify( + signature.len() as _, + signature.as_ptr() as _, + msg.len() as _, + msg.as_ptr() as _, + pub_key.len() as _, + pub_key.as_ptr() as _, + ) == 1 + } +} + #[cfg(test)] mod tests { use super::*; @@ -1050,4 +1064,47 @@ 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_eq!(super::ed25519_verify(&BAD_SIGNATURE, &MESSAGE, &FORGED_PUBLIC_KEY), false); + assert_eq!(super::ed25519_verify(&SIGNATURE, &MESSAGE, &FORGED_PUBLIC_KEY), false); + assert_eq!(super::ed25519_verify(&FORGED_SIGNATURE, &MESSAGE, &PUBLIC_KEY), false); + } } diff --git a/near-sdk/src/environment/mock/external.rs b/near-sdk/src/environment/mock/external.rs index 35c152022..db855eada 100644 --- a/near-sdk/src/environment/mock/external.rs +++ b/near-sdk/src/environment/mock/external.rs @@ -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 = ::core::result::Result; @@ -41,7 +41,11 @@ impl External for SdkExternal { Ok(()) } - fn storage_get(&self, key: &[u8]) -> Result>> { + fn storage_get( + &self, + key: &[u8], + _storage_get_mode: StorageGetMode, + ) -> Result>> { Ok(self .fake_trie .get(key) diff --git a/near-sdk/src/environment/mock/mocked_blockchain.rs b/near-sdk/src/environment/mock/mocked_blockchain.rs index e30f652d5..e90496b9c 100644 --- a/near-sdk/src/environment/mock/mocked_blockchain.rs +++ b/near-sdk/src/environment/mock/mocked_blockchain.rs @@ -61,7 +61,7 @@ impl MockedBlockchain { let context = sdk_context_to_vm_context(context); ext.fake_trie = storage; ext.validators = validators.into_iter().map(|(k, v)| (k.parse().unwrap(), v)).collect(); - let memory = memory_opt.unwrap_or_else(|| Box::new(MockedMemory {})); + let memory = memory_opt.unwrap_or_else(|| Box::new(MockedMemory::default())); // TODO: find a better way to del with MockedMemory let promise_results = Box::new(promise_results.into_iter().map(From::from).collect()); let config = Box::new(config); let fees_config = Box::new(fees_config); @@ -118,7 +118,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_index, block_timestamp: context.block_timestamp, epoch_height: context.epoch_height, account_balance: context.account_balance, @@ -178,7 +178,13 @@ fn action_to_sdk_action(action: &PrimitivesAction) -> VmAction { fn pub_key_conversion(key: &VmPublicKey) -> PublicKey { // Hack by serializing and deserializing the key. This format should be consistent. - String::from(key).parse().unwrap() + // String::from(key).parse().unwrap() + let key_type = if key.key_type() as u8 == 0 { 0u8 } else { 1u8 }; + + let mut pubkey_bytes = vec![key_type]; + pubkey_bytes.extend(key.key_data()); + + PublicKey::try_from(pubkey_bytes).unwrap() } #[cfg(not(target_arch = "wasm32"))] @@ -577,4 +583,24 @@ mod mock_chain { extern "C" fn alt_bn128_pairing_check(value_len: u64, value_ptr: u64) -> u64 { with_mock_interface(|b| b.alt_bn128_pairing_check(value_len, value_ptr)) } + #[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, + ) + }) + } } diff --git a/sys/src/lib.rs b/sys/src/lib.rs index 0311fe588..786476308 100644 --- a/sys/src/lib.rs +++ b/sys/src/lib.rs @@ -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 # // #####################