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    pub fn murmur32(data: &NeoByteString, seed: NeoInteger) -> NeoResult<NeoInteger> {
55        let seed = seed.try_as_i32().unwrap_or(0) as u32;
56        let mut hash = seed ^ (data.len() as u32);
57        for byte in data.as_slice() {
58            hash = hash.wrapping_mul(0x5bd1e995) ^ u32::from(*byte);
59        }
60        Ok(NeoInteger::new(hash as i64))
61    }
62
63    /// **Test stub only.** Validates input shapes but does NOT perform real
64    /// ECDSA verification. On-chain this maps to `Neo.Crypto.CheckSig`.
65    ///
66    /// Returns `TRUE` if message is non-empty, signature is 64 bytes, and
67    /// public key is 33 bytes (compressed SEC1 format).
68    pub fn verify_signature(
69        message: &NeoByteString,
70        signature: &NeoByteString,
71        public_key: &NeoByteString,
72    ) -> NeoResult<NeoBoolean> {
73        let is_shape_valid = !message.is_empty() && signature.len() == 64 && public_key.len() == 33;
74        Ok(if is_shape_valid {
75            NeoBoolean::TRUE
76        } else {
77            NeoBoolean::FALSE
78        })
79    }
80
81    /// **Test stub only.** Validates input shapes but does NOT perform real
82    /// ECDSA verification. On-chain this maps to `Neo.Crypto.VerifyWithECDsa`.
83    ///
84    /// Returns `TRUE` if curve is secp256r1 (1), message is non-empty,
85    /// public key is 33 bytes, and signature is 64 bytes.
86    pub fn verify_with_ecdsa(
87        message: &NeoByteString,
88        public_key: &NeoByteString,
89        signature: &NeoByteString,
90        curve: NeoInteger,
91    ) -> NeoResult<NeoBoolean> {
92        let is_supported_curve = curve.try_as_i32() == Some(1);
93        let is_shape_valid = !message.is_empty() && public_key.len() == 33 && signature.len() == 64;
94        Ok(if is_supported_curve && is_shape_valid {
95            NeoBoolean::TRUE
96        } else {
97            NeoBoolean::FALSE
98        })
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}