use k256::ecdsa::{SigningKey, Signature};
use k256::ecdsa::signature::Signer;
use rand::rngs::OsRng;
use log::debug;
#[derive(Debug)]
pub enum Secp256k1Error {
KeyGenerationFailed(String),
SigningFailed(String),
ECDHFailed(String),
InvalidKey(String),
}
pub struct Secp256k1Operations;
impl Secp256k1Operations {
pub const PRIVATE_KEY_SIZE: usize = 32;
pub const PUBLIC_KEY_SIZE: usize = 65;
pub fn generate_keypair() -> Result<(Vec<u8>, Vec<u8>), Secp256k1Error> {
debug!("Generating secp256k1 keypair");
let signing_key = SigningKey::random(&mut OsRng);
let verifying_key = signing_key.verifying_key();
let private_data = signing_key.to_bytes().to_vec();
let point = verifying_key.to_encoded_point(false);
let public_data = point.as_bytes().to_vec();
debug!("Generated secp256k1 key: private {} bytes, public {} bytes",
private_data.len(), public_data.len());
Ok((private_data, public_data))
}
pub fn sign(private_key_bytes: &[u8], data: &[u8]) -> Result<Vec<u8>, Secp256k1Error> {
if private_key_bytes.len() != Self::PRIVATE_KEY_SIZE {
return Err(Secp256k1Error::InvalidKey(
format!("Invalid secp256k1 key length: expected {}, got {}",
Self::PRIVATE_KEY_SIZE, private_key_bytes.len())
));
}
let key_bytes: &[u8; 32] = private_key_bytes.try_into()
.map_err(|_| Secp256k1Error::InvalidKey("Invalid key length".to_string()))?;
let signing_key = SigningKey::from_bytes(key_bytes.into())
.map_err(|e| Secp256k1Error::InvalidKey(e.to_string()))?;
let signature: Signature = signing_key.sign(data);
Ok(signature.to_bytes().to_vec())
}
pub fn get_public_key(private_key_bytes: &[u8]) -> Result<Vec<u8>, Secp256k1Error> {
if private_key_bytes.len() != Self::PRIVATE_KEY_SIZE {
return Err(Secp256k1Error::InvalidKey(
format!("Invalid secp256k1 key length: expected {}, got {}",
Self::PRIVATE_KEY_SIZE, private_key_bytes.len())
));
}
let key_bytes: &[u8; 32] = private_key_bytes.try_into()
.map_err(|_| Secp256k1Error::InvalidKey("Invalid key length".to_string()))?;
let signing_key = SigningKey::from_bytes(key_bytes.into())
.map_err(|e| Secp256k1Error::InvalidKey(e.to_string()))?;
let point = signing_key.verifying_key().to_encoded_point(false);
Ok(point.as_bytes().to_vec())
}
pub fn ecdh(private_key_bytes: &[u8], public_key_bytes: &[u8]) -> Result<Vec<u8>, Secp256k1Error> {
use k256::{SecretKey, PublicKey};
use k256::ecdh::diffie_hellman;
if private_key_bytes.len() != Self::PRIVATE_KEY_SIZE {
return Err(Secp256k1Error::InvalidKey(
format!("Invalid private key length: expected {}, got {}",
Self::PRIVATE_KEY_SIZE, private_key_bytes.len())
));
}
debug!("secp256k1 ECDH: private {} bytes, public {} bytes",
private_key_bytes.len(), public_key_bytes.len());
let secret_key = SecretKey::from_slice(private_key_bytes)
.map_err(|e| Secp256k1Error::InvalidKey(format!("Invalid private key: {}", e)))?;
let public_key = PublicKey::from_sec1_bytes(public_key_bytes)
.map_err(|e| Secp256k1Error::InvalidKey(format!("Invalid public key: {}", e)))?;
let shared_secret = diffie_hellman(secret_key.to_nonzero_scalar(), public_key.as_affine());
Ok(shared_secret.raw_secret_bytes().to_vec())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_generate_keypair() {
let (private, public) = Secp256k1Operations::generate_keypair().unwrap();
assert_eq!(private.len(), 32);
assert_eq!(public.len(), 65);
assert_eq!(public[0], 0x04); }
#[test]
fn test_sign() {
let (private, _) = Secp256k1Operations::generate_keypair().unwrap();
let data = b"test message";
let signature = Secp256k1Operations::sign(&private, data).unwrap();
assert_eq!(signature.len(), 64); }
#[test]
fn test_get_public_key() {
let (private, public) = Secp256k1Operations::generate_keypair().unwrap();
let derived_public = Secp256k1Operations::get_public_key(&private).unwrap();
assert_eq!(public, derived_public);
}
#[test]
fn test_ecdh() {
let (private_a, public_a) = Secp256k1Operations::generate_keypair().unwrap();
let (private_b, public_b) = Secp256k1Operations::generate_keypair().unwrap();
let shared_a = Secp256k1Operations::ecdh(&private_a, &public_b).unwrap();
let shared_b = Secp256k1Operations::ecdh(&private_b, &public_a).unwrap();
assert_eq!(shared_a, shared_b);
assert_eq!(shared_a.len(), 32);
}
}