pyth-crosschain/third_party/solana/Add_crypto_syscalls.patch

733 lines
20 KiB
Diff

Index: Cargo.lock
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- Cargo.lock (revision d84010e4afe5f79c812ff7da349460dc690a8392)
+++ Cargo.lock (revision aa30fbafe08be98cfbea6772040d5f3b073877b0)
@@ -274,12 +274,22 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
dependencies = [
- "block-padding",
+ "block-padding 0.1.5",
"byte-tools",
"byteorder",
"generic-array 0.12.3",
]
+[[package]]
+name = "block-buffer"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
+dependencies = [
+ "block-padding 0.2.1",
+ "generic-array 0.14.3",
+]
+
[[package]]
name = "block-padding"
version = "0.1.5"
@@ -289,6 +299,12 @@
"byte-tools",
]
+[[package]]
+name = "block-padding"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
+
[[package]]
name = "bs58"
version = "0.3.1"
@@ -633,6 +649,12 @@
"lazy_static",
]
+[[package]]
+name = "crunchy"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
+
[[package]]
name = "crypto-mac"
version = "0.7.0"
@@ -682,7 +704,7 @@
checksum = "5d85653f070353a16313d0046f173f70d1aadd5b42600a14de626f0dfb3473a5"
dependencies = [
"byteorder",
- "digest",
+ "digest 0.8.1",
"rand_core 0.5.1",
"subtle 2.2.2",
"zeroize",
@@ -725,6 +747,15 @@
"generic-array 0.12.3",
]
+[[package]]
+name = "digest"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
+dependencies = [
+ "generic-array 0.14.3",
+]
+
[[package]]
name = "dir-diff"
version = "0.3.2"
@@ -1316,7 +1347,18 @@
checksum = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695"
dependencies = [
"crypto-mac",
- "digest",
+ "digest 0.8.1",
+]
+
+[[package]]
+name = "hmac-drbg"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6e570451493f10f6581b48cdd530413b63ea9e780f544bfd3bdcaa0d89d1a7b"
+dependencies = [
+ "digest 0.8.1",
+ "generic-array 0.12.3",
+ "hmac",
]
[[package]]
@@ -1750,6 +1792,12 @@
"ws",
]
+[[package]]
+name = "keccak"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7"
+
[[package]]
name = "kernel32-sys"
version = "0.2.2"
@@ -1818,6 +1866,22 @@
"libc",
]
+[[package]]
+name = "libsecp256k1"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fc1e2c808481a63dc6da2074752fdd4336a3c8fcc68b83db6f1fd5224ae7962"
+dependencies = [
+ "arrayref",
+ "crunchy",
+ "digest 0.8.1",
+ "hmac-drbg",
+ "rand 0.7.3",
+ "sha2",
+ "subtle 2.2.2",
+ "typenum",
+]
+
[[package]]
name = "linked-hash-map"
version = "0.5.3"
@@ -2146,6 +2210,12 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
+[[package]]
+name = "opaque-debug"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
+
[[package]]
name = "openssl"
version = "0.10.29"
@@ -3098,10 +3168,10 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
dependencies = [
- "block-buffer",
- "digest",
+ "block-buffer 0.7.3",
+ "digest 0.8.1",
"fake-simd",
- "opaque-debug",
+ "opaque-debug 0.2.3",
]
[[package]]
@@ -3116,10 +3186,22 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69"
dependencies = [
- "block-buffer",
- "digest",
+ "block-buffer 0.7.3",
+ "digest 0.8.1",
"fake-simd",
- "opaque-debug",
+ "opaque-debug 0.2.3",
+]
+
+[[package]]
+name = "sha3"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809"
+dependencies = [
+ "block-buffer 0.9.0",
+ "digest 0.9.0",
+ "keccak",
+ "opaque-debug 0.3.0",
]
[[package]]
@@ -3376,12 +3458,16 @@
name = "solana-bpf-loader-program"
version = "1.4.0"
dependencies = [
+ "arrayref",
"bincode",
"byteorder",
+ "hex",
+ "libsecp256k1",
"num-derive 0.3.0",
"num-traits",
"rand 0.7.3",
"rustversion",
+ "sha3",
"solana-runtime",
"solana-sdk 1.4.0",
"solana_rbpf",
Index: programs/bpf_loader/Cargo.toml
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- programs/bpf_loader/Cargo.toml (revision d84010e4afe5f79c812ff7da349460dc690a8392)
+++ programs/bpf_loader/Cargo.toml (revision aa30fbafe08be98cfbea6772040d5f3b073877b0)
@@ -17,6 +17,10 @@
solana-sdk = { path = "../../sdk", version = "1.4.0" }
solana_rbpf = "=0.1.28"
thiserror = "1.0"
+libsecp256k1 = "0.3.5"
+sha3 = "0.9.1"
+arrayref = "0.3.6"
+hex = "0.4.2"
[dev-dependencies]
rand = "0.7.3"
Index: programs/bpf_loader/src/crypto.rs
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- programs/bpf_loader/src/crypto.rs (revision aa30fbafe08be98cfbea6772040d5f3b073877b0)
+++ programs/bpf_loader/src/crypto.rs (revision aa30fbafe08be98cfbea6772040d5f3b073877b0)
@@ -0,0 +1,171 @@
+pub extern crate secp256k1;
+
+use std::io::Write;
+
+use num_traits::AsPrimitive;
+use secp256k1::curve::{Affine, ECMULT_CONTEXT, ECMULT_GEN_CONTEXT, ECMultGenContext, Field, Jacobian, Scalar};
+use sha3::Digest;
+
+use self::secp256k1::{Error, PublicKey};
+
+pub enum SchnorrError {
+ InvalidPubKey
+}
+
+#[repr(C)]
+pub struct EcrecoverInput {
+ pub signature: [u8; 65],
+ pub message: [u8; 32],
+}
+
+#[repr(C)]
+pub struct EcrecoverOutput {
+ pub address: [u8; 20],
+}
+
+#[repr(C)]
+pub struct SchnorrifyInput {
+ message: [u8; 32],
+ addr: [u8; 20],
+ signature: [u8; 32],
+ pub_key: [u8; 64],
+}
+
+impl SchnorrifyInput {
+ pub fn verify(&self) -> bool {
+ let sig = SchnorrSignature {
+ address: self.addr,
+ signature: self.signature,
+ };
+
+ let pub_key = SchnorrPublicKey::deserialize(&self.pub_key);
+
+ match sig.verify_signature(&self.message, &pub_key) {
+ Ok(res) => res,
+ Err(_) => false
+ }
+ }
+}
+
+pub struct SchnorrPublicKey {
+ x: Field,
+ y: Field,
+}
+
+pub struct SchnorrSignature {
+ pub address: [u8; 20],
+ pub signature: [u8; 32],
+}
+
+impl SchnorrPublicKey {
+ pub fn deserialize(d: &[u8; 64]) -> SchnorrPublicKey {
+ let mut x = Field::default();
+ let mut y = Field::default();
+ x.set_b32(array_ref![d,0,32]);
+ y.set_b32(array_ref![d,32,32]);
+ x.normalize();
+ y.normalize();
+ SchnorrPublicKey {
+ x,
+ y,
+ }
+ }
+}
+
+impl SchnorrSignature {
+ pub fn verify_signature(&self, msg: &[u8; 32], pub_key: &SchnorrPublicKey) -> Result<bool, SchnorrError> {
+ let mut af = Affine::default();
+ af.set_xy(&pub_key.x, &pub_key.y);
+ let mut pubkey_j: Jacobian = Jacobian::default();
+ pubkey_j.set_ge(&af);
+
+ // Verify that the pubkey is a curve point
+ let mut elem = Affine::default();
+ if !elem.set_xo_var(&pub_key.x, pub_key.y.is_odd()) {
+ return Err(SchnorrError::InvalidPubKey);
+ }
+ elem.y.normalize();
+
+ // Make sure that the ordinates are equal
+ if elem.y.b32() != pub_key.y.b32() {
+ return Err(SchnorrError::InvalidPubKey);
+ }
+
+ // Generate the challenge
+ let mut h = sha3::Keccak256::default();
+ h.write(&pub_key.x.b32()); // pub key x coordinate
+ h.write(&[(pub_key.y.is_odd()).as_()]); // y parity
+ h.write(msg); // msg
+ h.write(&self.address); // nonceTimesGeneratorAddress
+ let challenge = h.finalize();
+
+ let mut e = Scalar::default();
+ e.set_b32(array_ref![challenge,0,32]);
+
+ let mut s = Scalar::default();
+ s.set_b32(&self.signature);
+
+ // Calculate s x G + e x P
+ let mut k: Jacobian = Jacobian::default();
+ ECMULT_CONTEXT.ecmult(&mut k, &pubkey_j, &e, &s);
+
+ let r = jacobian_to_normalized_affine(&k);
+
+ // Generate Ethereum address from calculated point
+ let eth_addr = affine_to_eth_address(&r);
+
+ // Verify that addr(k) == sig.address
+ Ok(eth_addr == self.address)
+ }
+}
+
+fn affine_to_eth_address(a: &Affine) -> [u8; 20] {
+ let mut h = sha3::Keccak256::default();
+ h.write(a.x.b32().as_ref()); // result key x coordinate
+ h.write(a.y.b32().as_ref()); // result key y coordinate
+
+ let out = h.finalize();
+ let mut out_addr = [0; 20];
+ out_addr.copy_from_slice(&out[12..]);
+
+ out_addr
+}
+
+fn jacobian_to_normalized_affine(j: &Jacobian) -> Affine {
+ let mut r = Affine::default();
+ r.set_gej(j);
+ r.x.normalize();
+ r.y.normalize();
+
+ r
+}
+
+#[cfg(test)]
+mod tests {
+ use std::io::Write;
+
+ use hex;
+
+ use crate::crypto::{SchnorrPublicKey, SchnorrSignature};
+
+ #[test]
+ fn verify_signature() {
+ let msggg = hex::decode("0194fdc2fa2ffcc041d3ff12045b73c86e4ff95ff662a5eee82abdf44a2d0b75").unwrap();
+ let siggg = hex::decode("ee5884a66454baca985f4453c05394214a75dc38956ea39f12cc429f081aae4b").unwrap();
+ let addr = hex::decode("9addd8a38fea7e1b94550e5bc249309a633dfa63").unwrap();
+ let pb = hex::decode("ae92ce7553993f04400c6976f8cd4540ae076bf0131eec8b35ae0ff9fc577a901de834d0f62ae6ecbeec2124595b06bce078b8133b4dda3855cf346feb2b2ca2").unwrap();
+
+ let sig = SchnorrSignature {
+ address: *array_ref![addr,0,20],
+ signature: *array_ref![siggg,0,32],
+ };
+
+ let pub_key = SchnorrPublicKey::deserialize(array_ref![pb,0,64]);
+
+ let msg = array_ref![msggg,0,32];
+ match sig.verify_signature(msg, &pub_key) {
+ Ok(res) => assert!(res, "signature should be valid"),
+ Err(err) => assert!(false, "signature verification failed")
+ }
+ }
+}
Index: programs/bpf_loader/src/lib.rs
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- programs/bpf_loader/src/lib.rs (revision d84010e4afe5f79c812ff7da349460dc690a8392)
+++ programs/bpf_loader/src/lib.rs (revision aa30fbafe08be98cfbea6772040d5f3b073877b0)
@@ -4,6 +4,10 @@
pub mod deprecated;
pub mod serialization;
pub mod syscalls;
+pub mod crypto;
+
+#[macro_use]
+extern crate arrayref;
use crate::{
bpf_verifier::VerifierError,
Index: programs/bpf_loader/src/syscalls.rs
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- programs/bpf_loader/src/syscalls.rs (revision d84010e4afe5f79c812ff7da349460dc690a8392)
+++ programs/bpf_loader/src/syscalls.rs (revision aa30fbafe08be98cfbea6772040d5f3b073877b0)
@@ -1,10 +1,23 @@
-use crate::{alloc, BPFError};
-use alloc::Alloc;
+use std::{
+ alloc::Layout,
+ cell::{RefCell, RefMut},
+ convert::TryFrom,
+ mem::{align_of, size_of},
+ rc::Rc,
+ slice::from_raw_parts_mut,
+ str::{from_utf8, Utf8Error},
+};
+
+use secp256k1::{Error, PublicKey, RecoveryId, Signature};
+use sha3::Digest;
use solana_rbpf::{
- ebpf::{EbpfError, SyscallObject, ELF_INSN_DUMP_OFFSET, MM_HEAP_START},
- memory_region::{translate_addr, MemoryRegion},
+ ebpf::{EbpfError, ELF_INSN_DUMP_OFFSET, MM_HEAP_START, SyscallObject},
EbpfVm,
+ memory_region::{MemoryRegion, translate_addr},
};
+use thiserror::Error as ThisError;
+
+use alloc::Alloc;
use solana_runtime::message_processor::MessageProcessor;
use solana_sdk::{
account::Account,
@@ -18,16 +31,16 @@
program_error::ProgramError,
pubkey::{Pubkey, PubkeyError},
};
-use std::{
- alloc::Layout,
- cell::{RefCell, RefMut},
- convert::TryFrom,
- mem::{align_of, size_of},
- rc::Rc,
- slice::from_raw_parts_mut,
- str::{from_utf8, Utf8Error},
-};
-use thiserror::Error as ThisError;
+
+use crate::{alloc, BPFError};
+/// Program heap allocators are intended to allocate/free from a given
+/// chunk of memory. The specific allocator implementation is
+/// selectable at build-time.
+/// Only one allocator is currently supported
+
+/// Simple bump allocator, never frees
+use crate::allocator_bump::BPFAllocator;
+use crate::crypto::{EcrecoverInput, EcrecoverOutput, SchnorrifyInput};
/// Error definitions
#[derive(Debug, ThisError)]
@@ -53,20 +66,13 @@
#[error("Unaligned pointer")]
UnalignedPointer,
}
+
impl From<SyscallError> for EbpfError<BPFError> {
fn from(error: SyscallError) -> Self {
EbpfError::UserError(error.into())
}
}
-/// Program heap allocators are intended to allocate/free from a given
-/// chunk of memory. The specific allocator implementation is
-/// selectable at build-time.
-/// Only one allocator is currently supported
-
-/// Simple bump allocator, never frees
-use crate::allocator_bump::BPFAllocator;
-
/// Default program heap size, allocators
/// are expected to enforce this
const DEFAULT_HEAP_SIZE: usize = 32 * 1024;
@@ -114,6 +120,20 @@
invoke_context: invoke_context.clone(),
}),
)?;
+
+ // Signature verification
+ vm.register_syscall_with_context_ex(
+ "sol_verify_ethschnorr",
+ Box::new(SyscallSchorrify {
+ invoke_context: invoke_context.clone(),
+ }),
+ )?;
+ vm.register_syscall_with_context_ex(
+ "sol_ecrecover",
+ Box::new(SyscallEcrecover {
+ invoke_context: invoke_context.clone(),
+ }),
+ )?;
// Memory allocator
let heap = vec![0_u8; DEFAULT_HEAP_SIZE];
@@ -256,6 +276,7 @@
pub struct SyscallLog {
logger: Rc<RefCell<dyn Logger>>,
}
+
impl SyscallObject<BPFError> for SyscallLog {
fn call(
&mut self,
@@ -285,6 +306,7 @@
pub struct SyscallLogU64 {
logger: Rc<RefCell<dyn Logger>>,
}
+
impl SyscallObject<BPFError> for SyscallLogU64 {
fn call(
&mut self,
@@ -319,6 +341,7 @@
pub struct SyscallSolAllocFree {
allocator: BPFAllocator,
}
+
impl SyscallObject<BPFError> for SyscallSolAllocFree {
fn call(
&mut self,
@@ -373,6 +396,105 @@
Ok(0)
}
+/// Verify a ETH optimized Schnorr Signature
+pub struct SyscallEcrecover<'a> {
+ invoke_context: Rc<RefCell<&'a mut dyn InvokeContext>>,
+}
+
+impl<'a> SyscallEcrecover<'a> {
+ fn get_context_mut(&self) -> Result<RefMut<&'a mut dyn InvokeContext>, EbpfError<BPFError>> {
+ self.invoke_context
+ .try_borrow_mut()
+ .map_err(|_| SyscallError::InvokeContextBorrowFailed.into())
+ }
+}
+
+impl<'a> SyscallObject<BPFError> for SyscallEcrecover<'a> {
+ fn call(
+ &mut self,
+ input: u64,
+ output: u64,
+ _arg3: u64,
+ _arg4: u64,
+ _arg5: u64,
+ ro_regions: &[MemoryRegion],
+ _rw_regions: &[MemoryRegion],
+ ) -> Result<u64, EbpfError<BPFError>> {
+ let input = translate_type!(
+ EcrecoverInput,
+ input,
+ ro_regions
+ )?;
+
+ let mut output = translate_type_mut!(
+ EcrecoverOutput,
+ output,
+ ro_regions
+ )?;
+
+ let signature = match secp256k1::Signature::parse_slice(&input.signature[..64]) {
+ Ok(v) => v,
+ Err(_) => {
+ return Ok(0);
+ }
+ };
+
+ let recovery_id = match secp256k1::RecoveryId::parse(input.signature[64]) {
+ Ok(v) => v,
+ Err(_) => { return Ok(0); }
+ };
+
+ match secp256k1::recover(&secp256k1::Message::parse(&input.message), &signature,
+ &recovery_id) {
+ Ok(v) => {
+ let mut addr = [0u8; 20];
+ addr.copy_from_slice(&sha3::Keccak256::digest(&v.serialize())[12..]);
+ output.address = addr;
+ }
+ Err(_) => {
+ return Ok(0);
+ }
+ }
+
+ Ok(1)
+ }
+}
+
+/// Verify a ETH optimized Schnorr Signature
+pub struct SyscallSchorrify<'a> {
+ invoke_context: Rc<RefCell<&'a mut dyn InvokeContext>>,
+}
+
+impl<'a> SyscallSchorrify<'a> {
+ fn get_context_mut(&self) -> Result<RefMut<&'a mut dyn InvokeContext>, EbpfError<BPFError>> {
+ self.invoke_context
+ .try_borrow_mut()
+ .map_err(|_| SyscallError::InvokeContextBorrowFailed.into())
+ }
+}
+
+impl<'a> SyscallObject<BPFError> for SyscallSchorrify<'a> {
+ fn call(
+ &mut self,
+ addr: u64,
+ _arg2: u64,
+ _arg3: u64,
+ _arg4: u64,
+ _arg5: u64,
+ ro_regions: &[MemoryRegion],
+ _rw_regions: &[MemoryRegion],
+ ) -> Result<u64, EbpfError<BPFError>> {
+ let input = translate_type!(
+ SchnorrifyInput,
+ addr,
+ ro_regions
+ )?;
+ let res = input.verify();
+
+ Ok(res as u64)
+ }
+}
+
// Cross-program invocation syscalls
struct AccountReferences<'a> {
@@ -415,6 +537,7 @@
callers_keyed_accounts: &'a [KeyedAccount<'a>],
invoke_context: Rc<RefCell<&'a mut dyn InvokeContext>>,
}
+
impl<'a> SyscallProcessInstruction<'a> for SyscallProcessInstructionRust<'a> {
fn get_context_mut(&self) -> Result<RefMut<&'a mut dyn InvokeContext>, EbpfError<BPFError>> {
self.invoke_context
@@ -436,7 +559,7 @@
ix.accounts.len(),
ro_regions
)?
- .to_vec();
+ .to_vec();
let data = translate_slice!(u8, ix.data.as_ptr(), ix.data.len(), ro_regions)?.to_vec();
Ok(Instruction {
program_id: ix.program_id,
@@ -551,6 +674,7 @@
}
}
}
+
impl<'a> SyscallObject<BPFError> for SyscallProcessInstructionRust<'a> {
fn call(
&mut self,
@@ -626,6 +750,7 @@
callers_keyed_accounts: &'a [KeyedAccount<'a>],
invoke_context: Rc<RefCell<&'a mut dyn InvokeContext>>,
}
+
impl<'a> SyscallProcessInstruction<'a> for SyscallProcessSolInstructionC<'a> {
fn get_context_mut(&self) -> Result<RefMut<&'a mut dyn InvokeContext>, EbpfError<BPFError>> {
self.invoke_context
@@ -763,6 +888,7 @@
}
}
}
+
impl<'a> SyscallObject<BPFError> for SyscallProcessSolInstructionC<'a> {
fn call(
&mut self,
@@ -814,10 +940,10 @@
}
if account.is_signer && // If message indicates account is signed
- !( // one of the following needs to be true:
- keyed_account.signer_key().is_some() // Signed in the parent instruction
- || signers.contains(&account.pubkey) // Signed by the program
- ) {
+ !( // one of the following needs to be true:
+ keyed_account.signer_key().is_some() // Signed in the parent instruction
+ || signers.contains(&account.pubkey) // Signed by the program
+ ) {
return Err(SyscallError::PrivilegeEscalation.into());
}
}
@@ -1034,7 +1160,7 @@
assert_eq!(string, "Gaggablaghblagh!");
Ok(42)
})
- .unwrap()
+ .unwrap()
);
}
@@ -1066,7 +1192,7 @@
&[ro_region],
&[rw_region],
)
- .unwrap();
+ .unwrap();
}
#[test]