Skip to content

Commit

Permalink
Add secp256r1 sig verify, split crypto interface into "safe" (using `…
Browse files Browse the repository at this point in the history
…Hash` type) and "hazmat"
  • Loading branch information
jayz22 committed Apr 16, 2024
1 parent 09c7023 commit a31d848
Show file tree
Hide file tree
Showing 8 changed files with 210 additions and 9 deletions.
77 changes: 77 additions & 0 deletions soroban-sdk/src/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,59 @@ impl Crypto {
/// The public key returned is the SEC-1-encoded ECDSA secp256k1 public key
/// that produced the 64-byte signature over a given 32-byte message digest,
/// for a given recovery_id byte.
pub fn secp256k1_recover(
&self,
message_digest: &Hash<32>,
signature: &BytesN<64>,
recorvery_id: u32,
) -> BytesN<65> {
let env = self.env();
CryptoHazmat::new(env).secp256k1_recover(&message_digest.0, signature, recorvery_id)
}

/// Verifies the ECDSA secp256r1 signature.
///
/// The SEC-1-encoded public key is provided along with the message,
/// verifies the 64-byte signature.
pub fn secp256r1_verify(
&self,
public_key: &BytesN<65>,
message_digest: &Hash<32>,
signature: &BytesN<64>,
) {
let env = self.env();
CryptoHazmat::new(env).secp256r1_verify(public_key, &message_digest.0, signature)
}
}

/// Hazardous Materials
///
/// Cryptographic functions under [CryptoHazmat] are low-leveled which can be
/// insecure if misused. They are not generally recommended. Using them
/// incorrectly can introduce security vulnerabilities. Please use [Crypto] if
/// possible.
pub struct CryptoHazmat {
env: Env,
}

impl CryptoHazmat {
pub(crate) fn new(env: &Env) -> CryptoHazmat {
CryptoHazmat { env: env.clone() }
}

pub fn env(&self) -> &Env {
&self.env
}

/// Recovers the ECDSA secp256k1 public key.
///
/// The public key returned is the SEC-1-encoded ECDSA secp256k1 public key
/// that produced the 64-byte signature over a given 32-byte message digest,
/// for a given recovery_id byte.
///
/// WARNING: The `message_digest` must be produced by a secure cryptographic
/// hash function on the message, otherwise the attacker can potentially
/// forge signatures.
pub fn secp256k1_recover(
&self,
message_digest: &BytesN<32>,
Expand All @@ -117,4 +170,28 @@ impl Crypto {
.unwrap_infallible();
unsafe { BytesN::unchecked_new(env.clone(), bytes) }
}

/// Verifies the ECDSA secp256r1 signature.
///
/// The SEC-1-encoded public key is provided along with a 32-byte message
/// digest, verifies the 64-byte signature.
///
/// WARNING: The `message_digest` must be produced by a secure cryptographic
/// hash function on the message, otherwise the attacker can potentially
/// forge signatures.
pub fn secp256r1_verify(
&self,
public_key: &BytesN<65>,
message_digest: &BytesN<32>,
signature: &BytesN<64>,
) {
let env = self.env();
let _ = internal::Env::verify_sig_ecdsa_secp256r1(
env,
public_key.to_object(),
message_digest.to_object(),
signature.to_object(),
)
.unwrap_infallible();
}
}
20 changes: 18 additions & 2 deletions soroban-sdk/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,14 @@ use crate::unwrap::UnwrapInfallible;
use crate::unwrap::UnwrapOptimized;
use crate::InvokeError;
use crate::{
crypto::Crypto, deploy::Deployer, events::Events, ledger::Ledger, logs::Logs, prng::Prng,
storage::Storage, Address, Vec,
crypto::{Crypto, CryptoHazmat},
deploy::Deployer,
events::Events,
ledger::Ledger,
logs::Logs,
prng::Prng,
storage::Storage,
Address, Vec,
};
use internal::{
AddressObject, Bool, BytesObject, DurationObject, I128Object, I256Object, I256Val, I64Object,
Expand Down Expand Up @@ -313,6 +319,16 @@ impl Env {
Crypto::new(self)
}

/// Hazardous Materials
///
/// Get a [CryptoHazmat] for accessing the cryptographic functions that are
/// not generally recommended. Using them incorrectly can introduce security
/// vulnerabilities. Please use [Crypto] if possible.
#[inline(always)]
pub fn crypto_hazmat(&self) -> CryptoHazmat {
CryptoHazmat::new(self)
}

/// Get a [Prng] for accessing the current functions which provide pseudo-randomness.
///
/// # Warning
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 @@ -24,6 +24,7 @@ mod contractimport_with_error;
mod crypto_ed25519;
mod crypto_keccak256;
mod crypto_secp256k1;
mod crypto_secp256r1;
mod crypto_sha256;
mod env;
mod max_ttl;
Expand Down
5 changes: 3 additions & 2 deletions soroban-sdk/src/tests/crypto_keccak256.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{bytesn, Env, IntoVal};
use crate::{bytesn, BytesN, Env, IntoVal};

#[test]
fn test_keccak256() {
Expand All @@ -9,5 +9,6 @@ fn test_keccak256() {
&env,
0x352fe2eaddf44eb02eb3eab1f8d6ff4ba426df4f1734b1e3f210d621ee8853d9
);
assert_eq!(env.crypto().keccak256(&bytes), expect);
let hash: BytesN<32> = env.crypto().keccak256(&bytes).into();
assert_eq!(hash, expect);
}
6 changes: 3 additions & 3 deletions soroban-sdk/src/tests/crypto_secp256k1.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use crate::{bytesn, Env};
use crate::{bytesn, crypto::Hash, Env};

#[test]
fn test_recover_key_ecdsa_secp256k1() {
let env = Env::default();

// From: https://github.com/ethereum/go-ethereum/blob/90d5bd85bcf2919ac2735a47fde675213348a0a6/crypto/secp256k1/secp256_test.go#L204-L217
let message_digest = bytesn!(
let message_digest = Hash::from_bytes(bytesn!(
&env,
0xce0677bb30baa8cf067c88db9811f4333d131bf8bcf12fe7065d211dce971008
);
));
let signature = bytesn!(
&env,
0x90f27b8b488db00b00606796d2987f6a5f59ae62ea05effe84fef5b8b0e549984a691139ad57a3f0b906637673aa2f63d1f55cb1a69199d4009eea23ceaddc93
Expand Down
25 changes: 25 additions & 0 deletions soroban-sdk/src/tests/crypto_secp256r1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use crate::{bytesn, crypto::Hash, Env};

#[test]
fn test_verify_sig_ecdsa_secp256r1() {
let env = Env::default();

// Test vector copied and adapted from
// https://csrc.nist.gov/groups/STM/cavp/documents/dss/186-3ecdsatestvectors.zip
// `SigVer.rsp` section [P-256,SHA-256]
let message_digest = Hash::from_bytes(bytesn!(
&env,
0xd1b8ef21eb4182ee270638061063a3f3c16c114e33937f69fb232cc833965a94
));
let signature = bytesn!(
&env,
0xbf96b99aa49c705c910be33142017c642ff540c76349b9dab72f981fd9347f4f17c55095819089c2e03b9cd415abdf12444e323075d98f31920b9e0f57ec871c
);
let public_key = bytesn!(
&env,
0x04e424dc61d4bb3cb7ef4344a7f8957a0c5134e16f7a67c074f82e6e12f49abf3c970eed7aa2bc48651545949de1dddaf0127e5965ac85d1243d6f60e7dfaee927
);

env.crypto()
.secp256r1_verify(&public_key, &message_digest, &signature)
}
5 changes: 3 additions & 2 deletions soroban-sdk/src/tests/crypto_sha256.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{bytes, bytesn, Env};
use crate::{bytes, bytesn, BytesN, Env};

#[test]
fn test_sha256() {
Expand All @@ -9,5 +9,6 @@ fn test_sha256() {
&env,
0x4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a
);
assert_eq!(env.crypto().sha256(&input), expect);
let hash: BytesN<32> = env.crypto().sha256(&input).into();
assert_eq!(hash, expect);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
{
"generators": {
"address": 0,
"nonce": 0
},
"auth": [],
"ledger": {
"protocol_version": 21,
"sequence_number": 0,
"timestamp": 0,
"network_id": "0000000000000000000000000000000000000000000000000000000000000000",
"base_reserve": 0,
"min_persistent_entry_ttl": 4096,
"min_temp_entry_ttl": 16,
"max_entry_ttl": 6312000,
"ledger_entries": []
},
"events": [
{
"event": {
"ext": "v0",
"contract_id": null,
"type_": "diagnostic",
"body": {
"v0": {
"topics": [
{
"symbol": "error"
},
{
"error": {
"context": "index_bounds"
}
}
],
"data": {
"vec": [
{
"string": "ledger protocol {} is less than specified lower bound {}"
},
{
"u32": 20
},
{
"u32": 21
}
]
}
}
}
},
"failed_call": false
},
{
"event": {
"ext": "v0",
"contract_id": null,
"type_": "diagnostic",
"body": {
"v0": {
"topics": [
{
"symbol": "error"
},
{
"error": {
"context": "index_bounds"
}
}
],
"data": {
"string": "escalating error to panic"
}
}
}
},
"failed_call": false
}
]
}

0 comments on commit a31d848

Please sign in to comment.