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