Skip to main content

neo_runtime/
crypto.rs

1// Copyright (c) 2025-2026 R3E Network
2// Licensed under the MIT License
3
4use neo_types::*;
5
6/// Crypto helpers for Neo N3 smart contracts.
7///
8/// **Hash functions** (`sha256`, `ripemd160`, `keccak256`, `keccak512`) are
9/// fully implemented using standard crates and produce correct output.
10///
11/// **Signature verification functions** (`verify_signature`, `verify_with_ecdsa`,
12/// `verify_signature_with_recovery`) are **test stubs only**. They validate input
13/// shapes (lengths) but do **not** perform real cryptographic verification.
14/// In a deployed contract these map to NeoVM syscalls (`Neo.Crypto.CheckSig`,
15/// `Neo.Crypto.VerifyWithECDsa`) which perform real verification on-chain.
16pub struct NeoCrypto;
17
18impl NeoCrypto {
19    pub fn sha256(data: &NeoByteString) -> NeoResult<NeoByteString> {
20        use sha2::{Digest, Sha256};
21
22        let mut hasher = Sha256::new();
23        hasher.update(data.as_slice());
24        Ok(NeoByteString::new(hasher.finalize().to_vec()))
25    }
26
27    pub fn ripemd160(data: &NeoByteString) -> NeoResult<NeoByteString> {
28        use ripemd::{Digest, Ripemd160};
29
30        let mut hasher = Ripemd160::new();
31        hasher.update(data.as_slice());
32        Ok(NeoByteString::new(hasher.finalize().to_vec()))
33    }
34
35    pub fn keccak256(data: &NeoByteString) -> NeoResult<NeoByteString> {
36        use tiny_keccak::{Hasher, Keccak};
37
38        let mut hasher = Keccak::v256();
39        let mut output = [0u8; 32];
40        hasher.update(data.as_slice());
41        hasher.finalize(&mut output);
42        Ok(NeoByteString::new(output.to_vec()))
43    }
44
45    pub fn keccak512(data: &NeoByteString) -> NeoResult<NeoByteString> {
46        use tiny_keccak::{Hasher, Keccak};
47        let mut hasher = Keccak::v512();
48        let mut output = [0u8; 64];
49        hasher.update(data.as_slice());
50        hasher.finalize(&mut output);
51        Ok(NeoByteString::new(output.to_vec()))
52    }
53
54    /// **Non-standard hash — not MurmurHash (D10).** This helper multiplies by
55    /// Murmur's constant but is otherwise unrelated to any reference Murmur
56    /// implementation, and its output will not match `Neo.Crypto.Murmur128`
57    /// (the on-chain native is 128-bit). Do NOT use it for any cross-system
58    /// compatibility. It is retained only for legacy sample code; new code
59    /// should call the on-chain CryptoLib `murmur32` method via
60    /// `System.Contract.Call`.
61    pub fn murmur32(data: &NeoByteString, seed: NeoInteger) -> NeoResult<NeoInteger> {
62        let seed = seed.try_as_i32().unwrap_or(0) as u32;
63        let mut hash = seed ^ (data.len() as u32);
64        for byte in data.as_slice() {
65            hash = hash.wrapping_mul(0x5bd1e995) ^ u32::from(*byte);
66        }
67        Ok(NeoInteger::new(hash as i64))
68    }
69
70    /// **Test stub only.** Validates input shapes but does NOT perform real
71    /// ECDSA verification. On-chain this maps to `Neo.Crypto.CheckSig`.
72    ///
73    /// Returns `FALSE` by default even for well-shaped input (D11: the previous
74    /// shape-check returned TRUE for any well-formed forgery, a security
75    /// footgun that disagreed with the syscall mock's secure default). Tests
76    /// that need a positive result should route through `NeoVMSyscall`'s
77    /// `set_crypto_verification_results` host hook.
78    pub fn verify_signature(
79        message: &NeoByteString,
80        signature: &NeoByteString,
81        public_key: &NeoByteString,
82    ) -> NeoResult<NeoBoolean> {
83        let _ = (message, signature, public_key);
84        Ok(NeoBoolean::FALSE)
85    }
86
87    /// **Test stub only.** Validates input shapes but does NOT perform real
88    /// ECDSA verification. On-chain this maps to `Neo.Crypto.VerifyWithECDsa`.
89    ///
90    /// Returns `FALSE` by default (D11 — see `verify_signature`).
91    pub fn verify_with_ecdsa(
92        message: &NeoByteString,
93        public_key: &NeoByteString,
94        signature: &NeoByteString,
95        curve: NeoInteger,
96    ) -> NeoResult<NeoBoolean> {
97        let _ = (message, public_key, signature, curve);
98        Ok(NeoBoolean::FALSE)
99    }
100
101    /// **Test stub only.** Returns a zero-padded 33-byte "public key" derived
102    /// from the signature bytes. Does NOT perform real ECDSA recovery.
103    pub fn verify_signature_with_recovery(
104        _message: &NeoByteString,
105        signature: &NeoByteString,
106    ) -> NeoResult<NeoByteString> {
107        let mut recovered = signature.as_slice().to_vec();
108        recovered.resize(33, 0u8);
109        Ok(NeoByteString::new(recovered))
110    }
111}