Skip to main content
The Soroban SDK provides comprehensive cryptographic primitives through the Crypto module, including support for elliptic curve operations on BLS12-381 and BN254 curves.

Accessing Crypto Functions

Access cryptographic functions through the environment:
use soroban_sdk::{Env, Bytes};

let env = Env::default();
let crypto = env.crypto();

Hash Functions

SHA-256

Compute SHA-256 hash of data:
let data = Bytes::from_slice(&env, b"hello world");
let hash = crypto.sha256(&data);
// Returns Hash<32> - a cryptographically secure hash

Keccak-256

Compute Keccak-256 hash (used in Ethereum):
let data = Bytes::from_slice(&env, b"hello world");
let hash = crypto.keccak256(&data);
// Returns Hash<32>

Digital Signatures

ED25519

Verify ED25519 signatures:
use soroban_sdk::BytesN;

let public_key: BytesN<32> = /* ... */;
let message = Bytes::from_slice(&env, b"message");
let signature: BytesN<64> = /* ... */;

crypto.ed25519_verify(&public_key, &message, &signature);
// Panics if verification fails

ECDSA secp256k1

Recover public key from signature:
let message_digest = crypto.sha256(&message);
let signature: BytesN<64> = /* ... */;
let recovery_id: u32 = 0;

let public_key = crypto.secp256k1_recover(&message_digest, &signature, recovery_id);
// Returns BytesN<65> - SEC-1-encoded public key

ECDSA secp256r1

Verify secp256r1 signatures:
let public_key: BytesN<65> = /* ... */;
let message_digest = crypto.sha256(&message);
let signature: BytesN<64> = /* ... */;

crypto.secp256r1_verify(&public_key, &message_digest, &signature);

BLS12-381 Curve Operations

The BLS12-381 curve is used for pairing-based cryptography and BLS signatures.

Accessing BLS12-381

use soroban_sdk::crypto::bls12_381::{Bls12_381, G1Affine, G2Affine, Fr};

let bls = env.crypto().bls12_381();

Field Elements

use soroban_sdk::{bytesn, crypto::bls12_381::Fp};

// Create a field element
let fp = Fp::from_array(&env, &[0u8; 48]);

// Field arithmetic
let neg_fp = -fp;

G1 Point Operations

use soroban_sdk::bytesn;

// Generator point in G1
let g1_generator = G1Affine::from_bytes(bytesn!(&env, 
    0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1
));

// Point addition
let result = bls.g1_add(&point1, &point2);

// Scalar multiplication
let scalar = Fr::from_u256(U256::from_u32(&env, 5));
let result = bls.g1_mul(&point1, &scalar);

// Check if point is in correct subgroup
let is_valid = bls.g1_is_in_subgroup(&point1);

// Multi-scalar multiplication (MSM)
let points = vec![&env, point1, point2];
let scalars = vec![&env, scalar1, scalar2];
let result = bls.g1_msm(points, scalars);

G2 Point Operations

// G2 operations are similar to G1
let result = bls.g2_add(&p1, &p2);
let result = bls.g2_mul(&p1, &scalar);
let is_valid = bls.g2_is_in_subgroup(&p1);

Hash to Curve

Map arbitrary data to curve points:
// Hash to G1
let msg = Bytes::from_slice(&env, b"message");
let dst = Bytes::from_slice(&env, b"DOMAIN_SEPARATION_TAG");
let point = bls.hash_to_g1(&msg, &dst);

// Hash to G2
let point = bls.hash_to_g2(&msg, &dst);

Pairing Operations

Perform pairing checks for signature verification:
use soroban_sdk::Vec;

// Pairing check: e(P1, Q1) * e(P2, Q2) == 1
let g1_points = vec![&env, p1, p2];
let g2_points = vec![&env, q1, q2];

let is_valid = bls.pairing_check(g1_points, g2_points);

Scalar Field Arithmetic

// Fr represents elements in the scalar field
let a = Fr::from_u256(U256::from_u32(&env, 10));
let b = Fr::from_u256(U256::from_u32(&env, 5));

// Arithmetic operations
let sum = bls.fr_add(&a, &b);
let diff = bls.fr_sub(&a, &b);
let product = bls.fr_mul(&a, &b);
let power = bls.fr_pow(&a, 3);
let inverse = bls.fr_inv(&a);

BN254 Curve Operations

BN254 (alt_bn128) is Ethereum-compatible and commonly used in zkSNARKs.

Accessing BN254

use soroban_sdk::crypto::bn254::{Bn254, Bn254G1Affine, Bn254G2Affine};

let bn254 = env.crypto().bn254();

G1 Operations

// Point addition
let result = bn254.g1_add(&point1, &point2);

// Scalar multiplication
let scalar = bn254::Fr::from_u256(U256::from_u32(&env, 5));
let result = bn254.g1_mul(&point1, &scalar);

Pairing Check (Ethereum-compatible)

use soroban_sdk::Vec;

// BN254 pairing for zkSNARK verification
let g1_points = vec![&env, p1, p2];
let g2_points = vec![&env, q1, q2];

let is_valid = bn254.pairing_check(g1_points, g2_points);

Best Practices

Use Checked Operations for Subgroup Safety

When aggregating multiple points, use checked operations on the final result:
// Use unchecked operations for intermediate additions
let temp1 = bls.g1_add(&p0, &p1);
let temp2 = bls.g1_add(&temp1, &p2);

// Use checked operation for final result to ensure correctness
let final_result = bls.g1_checked_add(&temp2, &p3);
match final_result {
    Some(point) => {
        // Point is in correct subgroup
    },
    None => {
        // Point is not in correct subgroup
    }
}

Hash Type Safety

The Hash<N> type ensures values come from cryptographic hash functions:
use soroban_sdk::crypto::Hash;

// Hash<32> can only be created from secure hash functions
let hash: Hash<32> = crypto.sha256(&data);

// Convert to bytes when needed
let bytes = hash.to_bytes();
let array = hash.to_array();

Hazmat Crypto

Low-level operations are available but should be used with caution:
// ⚠️ Warning: These operations can be insecure if misused
// Only use if you understand the security implications

Performance Considerations

  • MSM operations are optimized for multiple scalar multiplications
  • Subgroup checks are expensive; minimize their use in loops
  • Pairing operations are the most expensive; batch verifications when possible
  • BLS12-381 operations are generally slower but more secure than BN254
  • BN254 operations are faster and Ethereum-compatible