Skip to content

Commit

Permalink
Expose PRNG functions
Browse files Browse the repository at this point in the history
Other Changes:

* Add tests for `sha256` and `verify_sig_ed25519`
  • Loading branch information
masonforest committed Jun 27, 2023
1 parent c20b456 commit cc1ecc7
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 1 deletion.
24 changes: 23 additions & 1 deletion soroban-sdk/src/crypto.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! Crypto contains functions for cryptographic functions.
use crate::{env::internal, unwrap::UnwrapInfallible, Bytes, BytesN, Env};
use crate::{env::internal, unwrap::UnwrapInfallible, Bytes, BytesN, Env, VecObject};

/// Crypto provides access to cryptographic functions.
pub struct Crypto {
Expand All @@ -22,6 +22,28 @@ impl Crypto {
unsafe { BytesN::unchecked_new(env.clone(), bin) }
}

// Reseeds the pseudorandom number generator (PRNG) with the provided `seed` value.
pub fn prng_reseed(&self, seed: &Bytes) {
let env = self.env();
internal::Env::prng_reseed(env, seed.into()).unwrap_infallible();
}

// Returns a random u64 in the range between `lower` and `upper` inclusive.
pub fn u64_in_inclusive_range(&self, lower: u64, upper: u64) -> u64 {
let env = self.env();
internal::Env::prng_u64_in_inclusive_range(env, lower.into(), upper.into())
.unwrap_infallible()
.into()
}

// Shuffles a given vector v using the Fisher-Yates algorithm.
pub fn vec_shuffle(&self, v: VecObject) -> VecObject {
let env = self.env();
internal::Env::prng_vec_shuffle(env, v.into())
.unwrap_infallible()
.into()
}

/// Verifies an ed25519 signature.
///
/// The signature is verified as a valid signature of the message by the
Expand Down
1 change: 1 addition & 0 deletions soroban-sdk/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ mod contractfile_with_sha256;
mod contractimport;
mod contractimport_with_error;
mod contractimport_with_sha256;
mod crypto;
mod env;
mod proptest_scval_cmp;
mod proptest_val_cmp;
Expand Down
131 changes: 131 additions & 0 deletions soroban-sdk/src/tests/crypto.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
use crate::{self as soroban_sdk};
use crate::{bytes, bytesn, vec, Bytes, BytesN, Env, IntoVal, Vec};
use soroban_sdk::{contract, contractimpl};

#[contract]
pub struct TestCryptoContract;

#[contractimpl]
impl TestCryptoContract {
pub fn sha256(env: Env, bytes: Bytes) -> BytesN<32> {
env.crypto().sha256(&bytes)
}

pub fn prng_reseed(env: Env, bytes: Bytes) {
env.crypto().prng_reseed(&bytes);
}

pub fn u64_in_inclusive_range(env: Env, min: u64, max: u64) -> u64 {
env.crypto().u64_in_inclusive_range(min, max)
}

pub fn vec_shuffle(env: Env, vec: Vec<u32>) -> Vec<u32> {
env.crypto().vec_shuffle(vec.into()).into_val(&env)
}

pub fn verify_sig_ed25519(
env: Env,
public_key: BytesN<32>,
message: Bytes,
signature: BytesN<64>,
) {
env.crypto()
.ed25519_verify(&public_key, &message, &signature);
}
}

#[test]
fn test_prng_reseed() {
let env = Env::default();
let contract_id = env.register_contract(None, TestCryptoContract);
env.host().set_base_prng_seed([0; 32]);
let client = TestCryptoContractClient::new(&env, &contract_id);

let seed = bytes!(
&env,
0x0000000000000000000000000000000000000000000000000000000000000001
);
assert_eq!(client.u64_in_inclusive_range(&0, &9), 6);

client.prng_reseed(&seed);

assert_eq!(client.u64_in_inclusive_range(&0, &9), 8);
}

#[test]
fn test_sha256() {
let env = Env::default();
let contract_id = env.register_contract(None, TestCryptoContract);
let client = TestCryptoContractClient::new(&env, &contract_id);

let bytes = bytes!(&env, 0x01);

assert_eq!(client.sha256(&bytes), bytesn!(&env, 0x4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a));
}

#[test]
fn test_vec_shuffle() {
let env = Env::default();
env.host().set_base_prng_seed([0; 32]);
let contract_id = env.register_contract(None, TestCryptoContract);
let client = TestCryptoContractClient::new(&env, &contract_id);

let vec = vec![&env, 1, 2, 3];

assert_eq!(client.vec_shuffle(&vec), vec![&env, 2, 3, 1]);
}

#[test]
fn test_u64_in_inclusive_range() {
let env = Env::default();
env.host().set_base_prng_seed([0; 32]);
let contract_id = env.register_contract(None, TestCryptoContract);
let client = TestCryptoContractClient::new(&env, &contract_id);

assert_eq!(client.u64_in_inclusive_range(&0, &9), 6);
}

#[test]
fn test_verify_sig_ed25519() {
let env = Env::default();
env.host().set_base_prng_seed([0; 32]);
let contract_id = env.register_contract(None, TestCryptoContract);
let client = TestCryptoContractClient::new(&env, &contract_id);
// From https://datatracker.ietf.org/doc/html/rfc8032#section-7.1
let public_key: BytesN<32> = bytes!(
&env,
0x3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c
)
.try_into()
.unwrap();
let signature = bytesn!(
&env,
0x92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00
);
let message = bytes!(&env, 0x72);

assert_eq!(client.verify_sig_ed25519(&public_key, &message, &signature), ());
}

#[test]
#[should_panic]
fn test_verify_sig_ed25519_invalid_sig() {
let env = Env::default();
env.host().set_base_prng_seed([0; 32]);
let contract_id = env.register_contract(None, TestCryptoContract);
let client = TestCryptoContractClient::new(&env, &contract_id);
// From https://datatracker.ietf.org/doc/html/rfc8032#section-7.1
let public_key = bytesn!(
&env,
0x3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c
)
.try_into()
.unwrap();
let signature = bytesn!(
&env,
0x92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00
);
let message = bytes!(&env, 0x73);

client.verify_sig_ed25519(&public_key, &message, &signature);
}

0 comments on commit cc1ecc7

Please sign in to comment.