| Crates.io | goldilocks-crypto |
| lib.rs | goldilocks-crypto |
| version | 0.1.1 |
| created_at | 2025-11-05 09:53:58.832703+00 |
| updated_at | 2025-11-05 15:15:17.711055+00 |
| description | Rust port of ECgFp5 elliptic curve and Schnorr signatures over Goldilocks field, ported from lighter-go (Lighter Protocol) |
| homepage | |
| repository | https://github.com/bvvvp009/lighter-rust |
| max_upload_size | |
| id | 1917752 |
| size | 67,820 |
Rust port of ECgFp5 elliptic curve and Schnorr signatures over the Goldilocks field, ported from lighter-go (Lighter Protocol).
This library has NOT been audited and is provided as-is. Use with caution.
This crate provides elliptic curve cryptography primitives specifically designed for the Goldilocks field:
This crate depends on poseidon-hash for:
Add to your Cargo.toml:
[dependencies]
crypto = "0.1"
poseidon-hash = "0.1" # Required dependency
Or use the latest version from git (until published):
[dependencies]
crypto = { git = "https://github.com/bvvvp009/lighter-rust", path = "rust-signer/crypto" }
poseidon-hash = { git = "https://github.com/bvvvp009/lighter-rust", path = "rust-signer/poseidon-hash" }
use crypto::{ScalarField, Point};
// Generate a random private key
let private_key = ScalarField::sample_crypto();
// Derive public key
let public_key = Point::generator().mul(&private_key);
use crypto::{sign_with_nonce, verify_signature, Point, ScalarField};
// Generate keys
let private_key = ScalarField::sample_crypto();
let private_key_bytes = private_key.to_bytes_le();
// Derive public key
let public_key = Point::generator().mul(&private_key);
let public_key_bytes = public_key.encode().to_bytes_le();
// Sign a message (40 bytes)
let message = [0u8; 40];
let nonce = ScalarField::sample_crypto();
let nonce_bytes = nonce.to_bytes_le();
let signature = sign_with_nonce(&private_key_bytes, &message, &nonce_bytes).unwrap();
// Verify signature
let is_valid = verify_signature(&signature, &message, &public_key_bytes).unwrap();
assert!(is_valid);
use crypto::{Point, ScalarField};
// Create points
let generator = Point::generator();
let point1 = generator.mul(&ScalarField::from(2));
let point2 = generator.mul(&ScalarField::from(3));
// Point addition
let sum = point1.add(&point2);
// Point encoding/decoding
let encoded = sum.encode();
let decoded = Point::decode(&encoded).unwrap();
use crypto::ScalarField;
// Generate a new random private key (recommended)
let private_key = ScalarField::sample_crypto();
let private_key_bytes = private_key.to_bytes_le();
// Create from bytes (40 bytes, little-endian)
let private_key_bytes = [0u8; 40];
let private_key = ScalarField::from_bytes_le(&private_key_bytes).unwrap();
// Convert to bytes
let bytes = private_key.to_bytes_le(); // Returns [u8; 40]
// Create from hex string (80 hex characters = 40 bytes)
let hex_key = "0".repeat(80);
let private_key = ScalarField::from_hex(&hex_key).unwrap();
Here's a complete example of generating keys, signing, and verifying:
use crypto::{ScalarField, Point, sign_with_nonce, verify_signature};
// Step 1: Generate key pair
let private_key = ScalarField::sample_crypto();
let private_key_bytes = private_key.to_bytes_le();
// Step 2: Derive public key
let generator = Point::generator();
let public_key_point = generator.mul(&private_key);
let public_key_bytes = public_key_point.encode().to_bytes_le();
// Step 3: Prepare message (must be 40 bytes)
let message = b"Hello, World! This is a 40-byte message!!"; // 40 bytes
assert_eq!(message.len(), 40);
// Step 4: Generate nonce (CRITICAL: must be cryptographically secure and unique)
let nonce = ScalarField::sample_crypto();
let nonce_bytes = nonce.to_bytes_le();
// Step 5: Sign the message
let signature = sign_with_nonce(&private_key_bytes, message, &nonce_bytes)
.expect("Signing failed");
// Step 6: Verify the signature
let is_valid = verify_signature(&signature, message, &public_key_bytes)
.expect("Verification failed");
assert!(is_valid, "Signature should be valid");
use crypto::{ScalarField, Point};
fn derive_key_pair(seed: &[u8]) -> ([u8; 40], [u8; 40]) {
// Hash seed to get private key (simplified - use proper KDF in production)
use poseidon_hash::{hash_to_quintic_extension, Goldilocks};
let seed_elements: Vec<Goldilocks> = seed.chunks(8)
.map(|chunk| {
let mut arr = [0u8; 8];
arr[..chunk.len()].copy_from_slice(chunk);
Goldilocks::from_canonical_u64(u64::from_le_bytes(arr))
})
.collect();
let hash = hash_to_quintic_extension(&seed_elements);
let private_key_bytes = hash.to_bytes_le();
// Derive public key
let private_key = ScalarField::from_bytes_le(&private_key_bytes).unwrap();
let public_key = Point::generator().mul(&private_key);
let public_key_bytes = public_key.encode().to_bytes_le();
(private_key_bytes, public_key_bytes)
}
use crypto::verify_signature;
fn verify_batch(
signatures: &[&[u8]],
messages: &[&[u8]],
public_keys: &[&[u8]],
) -> Vec<bool> {
signatures
.iter()
.zip(messages.iter())
.zip(public_keys.iter())
.map(|((sig, msg), pk)| {
verify_signature(sig, msg, pk).unwrap_or(false)
})
.collect()
}
use crypto::{sign_with_nonce, CryptoError};
fn sign_message_safe(
private_key: &[u8],
message: &[u8],
nonce: &[u8],
) -> Result<Vec<u8>, CryptoError> {
// Validate inputs
if private_key.len() != 40 {
return Err(CryptoError::InvalidPrivateKeyLength(private_key.len()));
}
if message.len() != 40 {
return Err(CryptoError::InvalidMessageLength(message.len()));
}
// Sign
sign_with_nonce(private_key, message, nonce)
}
serde: Enable serialization/deserialization support[dependencies]
crypto = { version = "0.1", features = ["serde"] }
poseidon-hash = { version = "0.1", features = ["serde"] }
The implementation is optimized for:
⚠️ Important: This library has NOT been security audited. Use with caution in production systems.
ScalarField::sample_crypto())Full API documentation is available at docs.rs.
Licensed under either of:
at your option.
This library is ported from the Lighter-Go of the Lighter Protocol project. Feel free to contribute or request any changes.