Skip to main content

soroban_sdk/
crypto.rs

1//! Crypto contains functions for cryptographic functions.
2
3use crate::{
4    env::internal::{self, BytesObject},
5    unwrap::UnwrapInfallible,
6    Bytes, BytesN, ConversionError, Env, IntoVal, Symbol, TryFromVal, TryIntoVal, Val, Vec, U256,
7};
8
9pub mod bls12_381;
10pub mod bn254;
11pub(crate) mod utils;
12pub use bn254::Fr as BnScalar;
13
14/// A `BytesN<N>` generated by a cryptographic hash function.
15///
16/// The `Hash<N>` type contains a `BytesN<N>` and can only be constructed in
17/// contexts where the value has been generated by a secure cryptographic
18/// function. As a result, the type is only found as a return value of calling
19/// [`sha256`][Crypto::sha256], [`keccak256`][Crypto::keccak256], or via
20/// implementing [`CustomAccountInterface`][crate::auth::CustomAccountInterface]
21/// since the `__check_auth` is guaranteed to receive a hash from a secure
22/// cryptographic hash function as its first parameter.
23///
24/// **__Note:_** A Hash should not be used with storage, since no guarantee can
25/// be made about the Bytes stored as to whether they were in fact from a secure
26/// cryptographic hash function.
27#[derive(Clone)]
28#[repr(transparent)]
29pub struct Hash<const N: usize>(BytesN<N>);
30
31impl<const N: usize> Hash<N> {
32    /// Constructs a new `Hash` from a fixed-length bytes array.
33    ///
34    /// This is intended for test-only, since `Hash` type is only meant to be
35    /// constructed via secure manners.
36    #[cfg(test)]
37    pub(crate) fn from_bytes(bytes: BytesN<N>) -> Self {
38        Self(bytes)
39    }
40
41    /// Returns a [`BytesN`] containing the bytes in this hash.
42    #[inline(always)]
43    pub fn to_bytes(&self) -> BytesN<N> {
44        self.0.clone()
45    }
46
47    /// Returns an array containing the bytes in this hash.
48    #[inline(always)]
49    pub fn to_array(&self) -> [u8; N] {
50        self.0.to_array()
51    }
52
53    pub fn as_val(&self) -> &Val {
54        self.0.as_val()
55    }
56
57    pub fn to_val(&self) -> Val {
58        self.0.to_val()
59    }
60
61    pub fn as_object(&self) -> &BytesObject {
62        self.0.as_object()
63    }
64
65    pub fn to_object(&self) -> BytesObject {
66        self.0.to_object()
67    }
68}
69
70impl<const N: usize> IntoVal<Env, Val> for Hash<N> {
71    fn into_val(&self, e: &Env) -> Val {
72        self.0.into_val(e)
73    }
74}
75
76impl<const N: usize> IntoVal<Env, BytesN<N>> for Hash<N> {
77    fn into_val(&self, _e: &Env) -> BytesN<N> {
78        self.0.clone()
79    }
80}
81
82impl<const N: usize> From<Hash<N>> for Bytes {
83    fn from(v: Hash<N>) -> Self {
84        v.0.into()
85    }
86}
87
88impl<const N: usize> From<Hash<N>> for BytesN<N> {
89    fn from(v: Hash<N>) -> Self {
90        v.0
91    }
92}
93
94impl<const N: usize> Into<[u8; N]> for Hash<N> {
95    fn into(self) -> [u8; N] {
96        self.0.into()
97    }
98}
99
100#[allow(deprecated)]
101impl<const N: usize> crate::TryFromValForContractFn<Env, Val> for Hash<N> {
102    type Error = ConversionError;
103
104    fn try_from_val_for_contract_fn(env: &Env, v: &Val) -> Result<Self, Self::Error> {
105        Ok(Hash(BytesN::<N>::try_from_val(env, v)?))
106    }
107}
108
109/// Crypto provides access to cryptographic functions.
110pub struct Crypto {
111    env: Env,
112}
113
114impl Crypto {
115    pub(crate) fn new(env: &Env) -> Crypto {
116        Crypto { env: env.clone() }
117    }
118
119    pub fn env(&self) -> &Env {
120        &self.env
121    }
122
123    /// Returns the SHA-256 hash of the data.
124    pub fn sha256(&self, data: &Bytes) -> Hash<32> {
125        let env = self.env();
126        let bin = internal::Env::compute_hash_sha256(env, data.into()).unwrap_infallible();
127        unsafe { Hash(BytesN::unchecked_new(env.clone(), bin)) }
128    }
129
130    /// Returns the Keccak-256 hash of the data.
131    pub fn keccak256(&self, data: &Bytes) -> Hash<32> {
132        let env = self.env();
133        let bin = internal::Env::compute_hash_keccak256(env, data.into()).unwrap_infallible();
134        unsafe { Hash(BytesN::unchecked_new(env.clone(), bin)) }
135    }
136
137    /// Verifies an ed25519 signature.
138    ///
139    /// The signature is verified as a valid signature of the message by the
140    /// ed25519 public key.
141    ///
142    /// ### Panics
143    ///
144    /// If the signature verification fails.
145    pub fn ed25519_verify(&self, public_key: &BytesN<32>, message: &Bytes, signature: &BytesN<64>) {
146        let env = self.env();
147        let _ = internal::Env::verify_sig_ed25519(
148            env,
149            public_key.to_object(),
150            message.to_object(),
151            signature.to_object(),
152        );
153    }
154
155    /// Recovers the ECDSA secp256k1 public key.
156    ///
157    /// The public key returned is the SEC-1-encoded ECDSA secp256k1 public key
158    /// that produced the 64-byte signature over a given 32-byte message digest,
159    /// for a given recovery_id byte.
160    pub fn secp256k1_recover(
161        &self,
162        message_digest: &Hash<32>,
163        signature: &BytesN<64>,
164        recorvery_id: u32,
165    ) -> BytesN<65> {
166        let env = self.env();
167        CryptoHazmat::new(env).secp256k1_recover(&message_digest.0, signature, recorvery_id)
168    }
169
170    /// Verifies the ECDSA secp256r1 signature.
171    ///
172    /// The SEC-1-encoded public key is provided along with the message,
173    /// verifies the 64-byte signature.
174    pub fn secp256r1_verify(
175        &self,
176        public_key: &BytesN<65>,
177        message_digest: &Hash<32>,
178        signature: &BytesN<64>,
179    ) {
180        let env = self.env();
181        CryptoHazmat::new(env).secp256r1_verify(public_key, &message_digest.0, signature)
182    }
183
184    /// Get a [Bls12_381][bls12_381::Bls12_381] for accessing the bls12-381
185    /// functions.
186    pub fn bls12_381(&self) -> bls12_381::Bls12_381 {
187        bls12_381::Bls12_381::new(self.env())
188    }
189
190    /// Get a [Bn254][bn254::Bn254] for accessing the bn254
191    /// functions.
192    pub fn bn254(&self) -> bn254::Bn254 {
193        bn254::Bn254::new(self.env())
194    }
195}
196
197/// # ⚠️ Hazardous Materials
198///
199/// Cryptographic functions under [CryptoHazmat] are low-leveled which can be
200/// insecure if misused. They are not generally recommended. Using them
201/// incorrectly can introduce security vulnerabilities. Please use [Crypto] if
202/// possible.
203#[cfg_attr(any(test, feature = "hazmat-crypto"), visibility::make(pub))]
204#[cfg_attr(feature = "docs", doc(cfg(feature = "hazmat-crypto")))]
205pub(crate) struct CryptoHazmat {
206    env: Env,
207}
208
209impl CryptoHazmat {
210    pub(crate) fn new(env: &Env) -> CryptoHazmat {
211        CryptoHazmat { env: env.clone() }
212    }
213
214    pub fn env(&self) -> &Env {
215        &self.env
216    }
217
218    /// Recovers the ECDSA secp256k1 public key.
219    ///
220    /// The public key returned is the SEC-1-encoded ECDSA secp256k1 public key
221    /// that produced the 64-byte signature over a given 32-byte message digest,
222    /// for a given recovery_id byte.
223    ///
224    /// WARNING: The `message_digest` must be produced by a secure cryptographic
225    /// hash function on the message, otherwise the attacker can potentially
226    /// forge signatures.
227    pub fn secp256k1_recover(
228        &self,
229        message_digest: &BytesN<32>,
230        signature: &BytesN<64>,
231        recorvery_id: u32,
232    ) -> BytesN<65> {
233        let env = self.env();
234        let bytes = internal::Env::recover_key_ecdsa_secp256k1(
235            env,
236            message_digest.to_object(),
237            signature.to_object(),
238            recorvery_id.into(),
239        )
240        .unwrap_infallible();
241        unsafe { BytesN::unchecked_new(env.clone(), bytes) }
242    }
243
244    /// Verifies the ECDSA secp256r1 signature.
245    ///
246    /// The SEC-1-encoded public key is provided along with a 32-byte message
247    /// digest, verifies the 64-byte signature.
248    ///
249    /// WARNING: The `message_digest` must be produced by a secure cryptographic
250    /// hash function on the message, otherwise the attacker can potentially
251    /// forge signatures.
252    pub fn secp256r1_verify(
253        &self,
254        public_key: &BytesN<65>,
255        message_digest: &BytesN<32>,
256        signature: &BytesN<64>,
257    ) {
258        let env = self.env();
259        let _ = internal::Env::verify_sig_ecdsa_secp256r1(
260            env,
261            public_key.to_object(),
262            message_digest.to_object(),
263            signature.to_object(),
264        )
265        .unwrap_infallible();
266    }
267
268    /// Performs a Poseidon permutation on the input state vector.
269    ///
270    /// WARNING: This is a low-level permutation function. Most users should use
271    /// the higher-level `poseidon_hash` function instead.
272    pub fn poseidon_permutation(
273        &self,
274        input: &Vec<U256>,
275        field: Symbol,
276        t: u32,
277        d: u32,
278        rounds_f: u32,
279        rounds_p: u32,
280        mds: &Vec<Vec<U256>>,
281        round_constants: &Vec<Vec<U256>>,
282    ) -> Vec<U256> {
283        let env = self.env();
284        let result = internal::Env::poseidon_permutation(
285            env,
286            input.to_object(),
287            field.to_symbol_val(),
288            t.into(),
289            d.into(),
290            rounds_f.into(),
291            rounds_p.into(),
292            mds.to_object(),
293            round_constants.to_object(),
294        )
295        .unwrap_infallible();
296
297        result.try_into_val(env).unwrap_infallible()
298    }
299
300    /// Performs a Poseidon2 permutation on the input state vector.
301    ///
302    /// WARNING: This is a low-level permutation function. Most users should use
303    /// the higher-level `poseidon2_hash` function instead.
304    pub fn poseidon2_permutation(
305        &self,
306        input: &Vec<U256>,
307        field: Symbol,
308        t: u32,
309        d: u32,
310        rounds_f: u32,
311        rounds_p: u32,
312        mat_internal_diag_m_1: &Vec<U256>,
313        round_constants: &Vec<Vec<U256>>,
314    ) -> Vec<U256> {
315        let env = self.env();
316        let result = internal::Env::poseidon2_permutation(
317            env,
318            input.to_object(),
319            field.to_symbol_val(),
320            t.into(),
321            d.into(),
322            rounds_f.into(),
323            rounds_p.into(),
324            mat_internal_diag_m_1.to_object(),
325            round_constants.to_object(),
326        )
327        .unwrap_infallible();
328
329        result.try_into_val(env).unwrap_infallible()
330    }
331}