a rust cryptographic library implementing ECDSA from scratch
A comprehensive, educational implementation of ECDSA (Elliptic Curve Digital Signature Algorithm) from scratch in Rust. This project demonstrates the mathematical foundations of elliptic curve cryptography and digital signatures.
- Overview
- Mathematical Background
- Features
- Installation
- Usage
- Project Structure
- How It Works
- Security Considerations
- Testing
- Educational Resources
- Contributing
- License
This project implements ECDSA, a cryptographic algorithm used for digital signatures based on elliptic curve mathematics. ECDSA is widely used in:
- Bitcoin & Cryptocurrencies: Signing transactions
- TLS/SSL: Securing web communications
- SSH: Authenticating connections
- Code Signing: Verifying software authenticity
- Smaller Keys: 256-bit ECDSA provides similar security to 3072-bit RSA
- Faster Operations: More efficient than RSA for signing
- Modern Standard: Widely adopted in contemporary cryptographic systems
A finite field
-
Addition:
$(a + b) \bmod p$ -
Multiplication:
$(a \times b) \bmod p$ -
Division:
$a \times b^{-1} \bmod p$ where$b^{-1} = b^{p-2} \bmod p$ (Fermat's Little Theorem)
An elliptic curve over
where
For points
Case 1: Different Points (
Case 2: Point Doubling (
Computing
Result = O (identity)
For each bit in k (from right to left):
If bit is 1: Result = Result + P
P = 2P (double)
Time complexity:
- Choose random private key:
$d \in [1, n-1]$ where$n$ is the curve order - Compute public key:
$Q = d \cdot G$ where$G$ is the generator point
To sign message
- Compute hash:
$z = \text{SHA256}(m) \bmod n$ - Generate random
$k \in [1, n-1]$ - Compute point:
$(x, y) = k \cdot G$ - Compute:
$r = x \bmod n$ - Compute:
$s = k^{-1}(z + rd) \bmod n$ - Signature is
$(r, s)$
To verify signature
- Check:
$r, s \in [1, n-1]$ - Compute hash:
$z = \text{SHA256}(m) \bmod n$ - Compute:
$w = s^{-1} \bmod n$ - Compute:
$u_1 = zw \bmod n$ ,$u_2 = rw \bmod n$ - Compute point:
$(x, y) = u_1 \cdot G + u_2 \cdot Q$ - Verify:
$r = x \bmod n$
-
Finite Field Arithmetic: Addition, subtraction, multiplication, division over
$\mathbb{F}_p$ - Elliptic Curve Operations: Point addition, doubling, scalar multiplication
- ECDSA Implementation: Complete key generation, signing, and verification
- Comprehensive Testing: 19 unit tests covering all major functionality
- Educational Code: Well-commented with clear mathematical foundations
- Type Safety: Leverages Rust's type system for correctness
- Rust 1.70 or higher
- Cargo (comes with Rust)
# Clone the repository
git clone https://github.com/yourusername/elliptic-curve.git
cd elliptic-curve
# Build the project
cargo build
# Run tests
cargo test
# Run the demo
cargo run[dependencies]
num-bigint = { version = "0.4", features = ["rand"] }
sha2 = "0.10"
rand = "0.8"use elliptic_curve::{EllipticCurve, Point, ecdsa::ECDSA};
use num_bigint::BigUint;
fn main() {
// Define an elliptic curve: yΒ² = xΒ³ + 2x + 2 (mod 17)
let curve = EllipticCurve {
a: BigUint::from(2u32),
b: BigUint::from(2u32),
p: BigUint::from(17u32),
};
// Generator point
let generator = Point::Coordinate(
BigUint::from(5u32),
BigUint::from(1u32),
);
// Create ECDSA instance
let order = BigUint::from(19u32);
let ecdsa = ECDSA::new(curve, generator, order);
// Generate keypair
let keypair = ecdsa.generate_keypair();
println!("Private key: {}", keypair.private_key);
println!("Public key: {:?}", keypair.public_key);
// Sign a message
let message = b"Hello, ECDSA!";
let signature = ecdsa.sign(message, &keypair.private_key).unwrap();
println!("Signature: (r={}, s={})", signature.r, signature.s);
// Verify the signature
let is_valid = ecdsa.verify(message, &signature, &keypair.public_key);
println!("Signature valid: {}", is_valid);
}use elliptic_curve::{EllipticCurve, Point};
use num_bigint::BigUint;
let curve = EllipticCurve {
a: BigUint::from(2u32),
b: BigUint::from(3u32),
p: BigUint::from(11u32),
};
let point = Point::Coordinate(BigUint::from(0u32), BigUint::from(5u32));
// Point doubling
let doubled = curve.double(&point);
// Scalar multiplication
let result = curve.scalar_mult(&point, &BigUint::from(5u32));
// Point addition
let sum = curve.add(&point, &doubled);elliptic-curve/
βββ src/
β βββ lib.rs # Core elliptic curve implementation
β βββ ecdsa.rs # ECDSA signing and verification
β βββ main.rs # Demo application
βββ Cargo.toml # Project dependencies
βββ README.md # This file
-
FiniteField: Arithmetic operations in$\mathbb{F}_p$ -
Point: Enum representing curve points (coordinates or identity) -
EllipticCurve: Core elliptic curve operations-
is_on_curve(): Verify point validity -
add(): Point addition -
double(): Point doubling -
scalar_mult(): Scalar multiplication
-
-
ECDSA: Main ECDSA implementation-
generate_keypair(): Create public/private key pair -
sign(): Generate digital signature -
verify(): Verify signature authenticity
-
-
ECDSAKeyPair: Container for key pairs -
ECDSASignature: Container for signatures$(r, s)$
The FiniteField struct implements modular arithmetic:
pub struct FiniteField {
pub p: BigUint, // Prime modulus
}
impl FiniteField {
// Division using Fermat's Little Theorem
// a/b = a Γ b^(-1) = a Γ b^(p-2) mod p
pub fn div(&self, x: &BigUint, y: &BigUint) -> BigUint {
let p_minus_2 = &self.p - BigUint::from(2u32);
let y_inverse = y.modpow(&p_minus_2, &self.p);
self.mul(x, &y_inverse)
}
}Key Insight: Fermat's Little Theorem states that for prime
Therefore:
Points on the curve can be:
-
Coordinate points:
$(x, y)$ satisfying the curve equation -
Identity (point at infinity): The additive identity element
$\mathcal{O}$
The Point enum handles both cases:
pub enum Point {
Coordinate(BigUint, BigUint),
Identity,
}The double-and-add algorithm efficiently computes
pub fn scalar_mult(&self, point: &Point, k: &BigUint) -> Point {
let mut result = Point::Identity;
let mut addend = point.clone();
let mut scalar = k.clone();
while scalar > BigUint::from(0u32) {
if &scalar % BigUint::from(2u32) == BigUint::from(1u32) {
result = self.add(&result, &addend);
}
addend = self.double(&addend);
scalar /= BigUint::from(2u32);
}
result
}Example: Computing
13 = 1Γ8 + 1Γ4 + 0Γ2 + 1Γ1
13Β·P = 8P + 4P + P
Only 3 additions instead of 12!
The signing process ensures:
-
Uniqueness: Random
$k$ makes each signature unique - Non-repudiation: Only the private key holder can sign
- Integrity: Any message modification invalidates the signature
pub fn sign(&self, message: &[u8], private_key: &BigUint)
-> Result<ECDSASignature, &'static str> {
let z = self.hash_message(message);
loop {
let k = generate_random();
let point = self.curve.scalar_mult(&self.generator, &k);
let r = extract_x_coordinate(point) % &self.order;
if r == 0 { continue; }
let s = k_inv * (z + r * private_key) % &self.order;
if s == 0 { continue; }
return Ok(ECDSASignature { r, s });
}
}Verification works because:
Where:
$u_1 = zw \bmod n$ $u_2 = rw \bmod n$ $w = s^{-1} \bmod n$
Substituting the signature equation
Therefore:
This implementation is for educational purposes and should NOT be used in production systems.
-
Small Test Curves: Uses small primes (e.g.,
$p = 17$ ) for demonstration- Real systems use 256-bit curves (e.g., secp256k1, P-256)
- Small curves are vulnerable to brute-force attacks
-
Timing Attacks: Not resistant to side-channel attacks
- No constant-time operations
- Vulnerable to timing analysis
-
Random Number Generation: Uses standard RNG
- Production systems need cryptographically secure RNG
- Weak randomness can leak private keys
-
Hash Collisions: With small order curves (
$n = 19$ ), hash collisions are possible- Real curves use 256-bit orders where collisions are computationally infeasible
-
No Nonce Reuse Protection: Reusing
$k$ reveals the private key- Production implementations use deterministic
$k$ (RFC 6979)
- Production implementations use deterministic
For real applications, use battle-tested libraries:
- Rust:
secp256k1,ring,ed25519-dalek - Bitcoin/Ethereum: Use official client libraries
- General Purpose: OpenSSL, libsodium
If you sign two different messages with the same
An attacker can compute:
Then recover the private key:
This happened in real life: The PlayStation 3 signing key was compromised due to Sony reusing
cargo testThe project includes 19 comprehensive tests:
-
test_add: Addition modulo$p$ -
test_sub: Subtraction modulo$p$ -
test_mul: Multiplication modulo$p$ -
test_div: Division using modular inverse -
test_multiplicative_identity:$x \times x^{-1} \equiv 1$ -
test_additive_identity:$x + (-x) \equiv 0$
-
test_point_on_curve: Point validation -
test_ec_point_addition: Addition of distinct points -
test_ec_point_doubling: Point doubling ($P + P$ ) -
test_y_zero_special_case: Points with$y = 0$ have order 2 -
test_scalar_multiplication:$k \cdot P$ verification -
test_group_properties: Associativity and commutativity -
test_secp256k1_like_curve: Testing$y^2 = x^3 + 7$
test_keygen: Key pair generationtest_sign_verify: Basic signing and verificationtest_sign_verify_same_message_twice: Different signatures for same messagetest_invalid_signature: Reject invalid signaturestest_signature_with_wrong_public_key: Reject wrong public keys
running 19 tests
test tests::test_add ... ok
test tests::test_additive_identity ... ok
test ecdsa::tests::test_keygen ... ok
...
test result: ok. 19 passed; 0 failed; 0 ignored
- "Understanding Cryptography" by Christof Paar: Excellent introduction to elliptic curves
- "Guide to Elliptic Curve Cryptography" by Hankerson et al.: Comprehensive technical reference
- "Serious Cryptography" by Jean-Philippe Aumasson: Modern cryptography overview
- A (Relatively Easy To Understand) Primer on Elliptic Curve Cryptography
- Elliptic Curve Cryptography: a gentle introduction
- Bitcoin's Use of ECDSA
- RFC 6979: Deterministic ECDSA
- NIST Curves
To fully understand this implementation, you should be familiar with:
- Modular Arithmetic: Operations modulo a prime
- Group Theory: Groups, cyclic groups, generators
- Abstract Algebra: Fields, especially finite fields
- Number Theory: Fermat's Little Theorem, discrete logarithm problem
- Start Here: Understand finite field arithmetic (
FiniteFieldstruct) - Geometric Intuition: Study elliptic curve point addition visually
- Scalar Multiplication: Understand the double-and-add algorithm
- ECDSA Theory: Learn why verification works mathematically
- Implementation: Study the code with the mathematical background
ECDSA security relies on the computational hardness of:
Given:
Find:
This is called the Elliptic Curve Discrete Logarithm Problem (ECDLP) and is believed to be computationally infeasible for large curves.
- Smaller Keys: 256-bit EC β 3072-bit RSA security
- Efficient: Faster operations than RSA
- Mobile-Friendly: Lower power consumption
- Future-Proof: Better resistance to quantum attacks than RSA (though still vulnerable)
-
secp256k1: Used by Bitcoin, Ethereum
-
$y^2 = x^3 + 7$ over a 256-bit field
-
-
P-256 (secp256r1): NIST standard
- Used in TLS, government applications
-
Curve25519: Modern alternative
- Used in Signal, SSH, TLS 1.3
Contributions are welcome! This is an educational project, so clarity and documentation are priorities.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Add tests for new functionality
- Ensure all tests pass (
cargo test) - Update documentation
- Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Add support for standard curves (secp256k1, P-256)
- Implement deterministic
$k$ generation (RFC 6979) - Add more comprehensive examples
- Improve documentation
- Add visualization tools
- Performance optimizations
- Constant-time operations (for educational purposes)
This project is licensed under the MIT License - see the LICENSE file for details.
- The Rust community for excellent cryptographic libraries that inspired this implementation
- Cloudflare's blog posts on elliptic curve cryptography
- Andrea Corbellini's excellent ECC tutorial series
- The secp256k1 specification authors
| Operation | Formula |
|---|---|
| Field Addition | |
| Field Multiplication | |
| Field Division | |
| Point Addition (different) |
|
| Point Doubling |
|
| ECDSA Sign | |
| ECDSA Verify | Check if |
# Build
cargo build
# Run demo
cargo run
# Run tests
cargo test
# Run specific test
cargo test test_sign_verify
# Build documentation
cargo doc --open
# Check code
cargo clippyMade with β€οΈ for learning cryptography
"In mathematics, you don't understand things. You just get used to them." - John von Neumann
