diff --git a/starknet-accounts/src/single_owner.rs b/starknet-accounts/src/single_owner.rs index 4fc44ede..c615faf2 100644 --- a/starknet-accounts/src/single_owner.rs +++ b/starknet-accounts/src/single_owner.rs @@ -47,13 +47,13 @@ where { /// Create a new account controlled by a single signer. /// - /// ### Arguments + /// ### Parameters /// - /// * `provider`: A `Provider` implementation that provides access to the Starknet network. - /// * `signer`: A `Signer` implementation that can generate valid signatures for this account. - /// * `address`: Account contract address. - /// * `chain_id`: Network chain ID. - /// * `encoding`: How `__execute__` calldata should be encoded. + /// - `provider`: A `Provider` implementation that provides access to the Starknet network. + /// - `signer`: A `Signer` implementation that can generate valid signatures for this account. + /// - `address`: Account contract address. + /// - `chain_id`: Network chain ID. + /// - `encoding`: How `__execute__` calldata should be encoded. pub const fn new( provider: P, signer: S, diff --git a/starknet-crypto/README.md b/starknet-crypto/README.md index c4790d62..3af6bc0e 100644 --- a/starknet-crypto/README.md +++ b/starknet-crypto/README.md @@ -1,6 +1,14 @@ # Low-level cryptography utilities for Starknet -`starknet-crypto` contains utilities for performing **low-level** cryptographic operations in Starknet. +`starknet-crypto` contains utilities for performing **low-level** cryptographic operations in Starknet: + +- ECDSA operations + - Signing hashes + - Verifying signatures + - Recovering public keys from signatures +- Pedersen hash +- Poseidon hash +- RFC-6979 > _You're advised to use high-level crypto utilities implemented by the `starknet-core` crate (or use it through the `starknet::core` re-export) if you're not familiar with cryptographic primitives. Using these low-level functions incorrectly could result in leaking your private key, for example._ diff --git a/starknet-crypto/src/ecdsa.rs b/starknet-crypto/src/ecdsa.rs index ae15e181..978f0cdd 100644 --- a/starknet-crypto/src/ecdsa.rs +++ b/starknet-crypto/src/ecdsa.rs @@ -7,6 +7,17 @@ use crate::{ use starknet_types_core::curve::{AffinePoint, ProjectivePoint}; use starknet_types_core::felt::Felt; +/// The (exclusive) upper bound on many ECDSA-related elements based on the original C++ +/// implementation from [`crypto-cpp`](https://github.com/starkware-libs/crypto-cpp). +/// +/// The C++ implementation [imposes](https://github.com/starkware-libs/crypto-cpp/blob/78e3ed8dc7a0901fe6d62f4e99becc6e7936adfd/src/starkware/crypto/ecdsa.cc#L23) +/// an upper bound of `0x0800000000000000000000000000000000000000000000000000000000000000`. +/// +/// The limit is imposed to ensure all elements that would form part of a Merkle tree remain in the +/// range of `[0, 2^251)`, allowing certain optimizations to be made. +/// +/// When a compuated value is greater than or equal to this bound, the modulus is taken to ensure +/// the resulting value falls under the bound. const ELEMENT_UPPER_BOUND: Felt = Felt::from_raw([ 576459263475450960, 18446744073709255680, @@ -14,7 +25,7 @@ const ELEMENT_UPPER_BOUND: Felt = Felt::from_raw([ 18446743986131435553, ]); -/// Stark ECDSA signature +/// Stark ECDSA signature. #[derive(Debug)] pub struct Signature { /// The `r` value of a signature @@ -23,7 +34,7 @@ pub struct Signature { pub s: Felt, } -/// Stark ECDSA signature with `v` +/// Stark ECDSA signature with `v`, useful for recovering the public key. #[derive(Debug)] pub struct ExtendedSignature { /// The `r` value of a signature @@ -70,9 +81,9 @@ impl core::fmt::Display for ExtendedSignature { /// Computes the public key given a Stark private key. /// -/// ### Arguments +/// ### Parameters /// -/// * `private_key`: The private key +/// - `private_key`: The private key. pub fn get_public_key(private_key: &Felt) -> Felt { mul_by_bits(&GENERATOR, private_key) .to_affine() @@ -82,11 +93,11 @@ pub fn get_public_key(private_key: &Felt) -> Felt { /// Computes ECDSA signature given a Stark private key and message hash. /// -/// ### Arguments +/// ### Parameters /// -/// * `private_key`: The private key -/// * `message`: The message hash -/// * `k`: A random `k` value. You **MUST NOT** use the same `k` on different signatures +/// - `private_key`: The private key. +/// - `message`: The message hash. +/// - `k`: A random `k` value. You **MUST NOT** use the same `k` on different signatures. pub fn sign(private_key: &Felt, message: &Felt, k: &Felt) -> Result { if message >= &ELEMENT_UPPER_BOUND { return Err(SignError::InvalidMessageHash); @@ -120,12 +131,12 @@ pub fn sign(private_key: &Felt, message: &Felt, k: &Felt) -> Result Result { if message >= &ELEMENT_UPPER_BOUND { return Err(VerifyError::InvalidMessageHash); @@ -162,12 +173,12 @@ pub fn verify(public_key: &Felt, message: &Felt, r: &Felt, s: &Felt) -> Result Result { if message >= &ELEMENT_UPPER_BOUND { return Err(RecoverError::InvalidMessageHash); diff --git a/starknet-crypto/src/error.rs b/starknet-crypto/src/error.rs index 6f42fe15..b21e7f2d 100644 --- a/starknet-crypto/src/error.rs +++ b/starknet-crypto/src/error.rs @@ -1,8 +1,11 @@ mod sign_error { - /// Errors when performing ECDSA [`sign`](fn.sign) operations + /// Errors when performing ECDSA [`sign`](fn.sign) operations. #[derive(Debug)] pub enum SignError { + /// The message hash is not in the range of `[0, 2^251)`. InvalidMessageHash, + /// The random `k` value results in an invalid signature. A different `k` value should be + /// used instead, typically by using a new seed per RFC-6979. InvalidK, } @@ -21,12 +24,16 @@ mod sign_error { pub use sign_error::SignError; mod verify_error { - /// Errors when performing ECDSA [`verify`](fn.verify) operations + /// Errors when performing ECDSA [`verify`](fn.verify) operations. #[derive(Debug)] pub enum VerifyError { + /// The public key is not a valid point on the STARK curve. InvalidPublicKey, + /// The message hash is not in the range of `[0, 2^251)`. InvalidMessageHash, + /// The `r` value is not in the range of `[0, 2^251)`. InvalidR, + /// The `s` value is not in the range of `[0, 2^251)`. InvalidS, } @@ -47,12 +54,17 @@ mod verify_error { pub use verify_error::VerifyError; mod recover_error { - /// Errors when performing ECDSA [`recover`](fn.recover) operations + /// Errors when performing ECDSA [`recover`](fn.recover) operations. #[derive(Debug)] pub enum RecoverError { + /// The message hash is not in the range of `[0, 2^251)`. InvalidMessageHash, + /// The `r` value is not in the range of `[0, 2^251)`. InvalidR, + /// The `s` value is not in the range of `[0, + /// 0x0800000000000010ffffffffffffffffb781126dcae7b2321e66a241adc64d2f)`. InvalidS, + /// The `v` value is neither `0` nor `1`. InvalidV, } diff --git a/starknet-crypto/src/lib.rs b/starknet-crypto/src/lib.rs index 551141e9..321f4e5f 100644 --- a/starknet-crypto/src/lib.rs +++ b/starknet-crypto/src/lib.rs @@ -1,5 +1,21 @@ +//! Low-level cryptography utilities for Starknet. Features include: +//! +//! - ECDSA operations +//! - [Signing hashes](fn.sign) +//! - [Verifying signatures](fn.verify) +//! - [Recovering public keys from signatures](fn.recover) +//! - [Pedersen hash](fn.pedersen_hash) +//! - Poseidon hash +//! - [RFC-6979](fn.rfc6979_generate_k) +//! +//! # Warning +//! +//! You're advised to use high-level crypto utilities implemented by the `starknet-core` crate if +//! you're not familiar with cryptographic primitives. Using these low-level functions incorrectly +//! could result in catastrophic consequences like leaking your private key. + +#![deny(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] -#![doc = include_str!("../README.md")] #[allow(unused_extern_crates)] #[cfg(all(not(feature = "std"), any(test, feature = "alloc")))] diff --git a/starknet-crypto/src/pedersen_hash.rs b/starknet-crypto/src/pedersen_hash.rs index 5080a826..275b5d81 100644 --- a/starknet-crypto/src/pedersen_hash.rs +++ b/starknet-crypto/src/pedersen_hash.rs @@ -5,10 +5,10 @@ use starknet_types_core::{ /// Computes the Starkware version of the Pedersen hash of x and y. All inputs are little-endian. /// -/// ### Arguments +/// ### Parameters /// -/// * `x`: The x coordinate -/// * `y`: The y coordinate +/// - `x`: The x coordinate. +/// - `y`: The y coordinate. pub fn pedersen_hash(x: &Felt, y: &Felt) -> Felt { Pedersen::hash(x, y) } diff --git a/starknet-crypto/src/poseidon_hash.rs b/starknet-crypto/src/poseidon_hash.rs index 18a4d4e9..4155f056 100644 --- a/starknet-crypto/src/poseidon_hash.rs +++ b/starknet-crypto/src/poseidon_hash.rs @@ -3,7 +3,7 @@ use starknet_types_core::{felt::Felt, hash::Poseidon}; -/// A hasher for Starknet Poseidon hash. +/// A stateful hasher for Starknet Poseidon hash. /// /// Using this hasher is the same as calling [`poseidon_hash_many`]. #[derive(Debug, Default)] diff --git a/starknet-crypto/src/rfc6979.rs b/starknet-crypto/src/rfc6979.rs index cd7ea7da..550040df 100644 --- a/starknet-crypto/src/rfc6979.rs +++ b/starknet-crypto/src/rfc6979.rs @@ -9,11 +9,11 @@ const EC_ORDER: U256 = /// Deterministically generate ephemeral scalar `k` based on RFC 6979. /// -/// ### Arguments +/// ### Parameters /// -/// * `message_hash`: message hash -/// * `private_key`: private key -/// * `seed`: extra seed for additional entropy +/// - `message_hash`: Message hash. +/// - `private_key`: Private key. +/// - `seed`: Extra seed for additional entropy. pub fn generate_k(message_hash: &Felt, private_key: &Felt, seed: Option<&Felt>) -> Felt { // The message hash padding as implemented in `cairo-lang` is not needed here. The hash is // padded in `cairo-lang` only to make sure the lowest 4 bits won't get truncated, but here it's