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}