82 lines
2.7 KiB
Rust
82 lines
2.7 KiB
Rust
use anyhow::{anyhow, Result};
|
|
use secp256k1::{Message, PublicKey, ecdsa::Signature, Secp256k1, SecretKey};
|
|
use sha2::{Digest, Sha256};
|
|
|
|
/// Helper for authentication operations
|
|
pub struct AuthHelper {
|
|
secret_key: SecretKey,
|
|
public_key: PublicKey,
|
|
secp: Secp256k1<secp256k1::All>,
|
|
}
|
|
|
|
impl AuthHelper {
|
|
/// Create a new auth helper from a private key hex string
|
|
pub fn new(private_key_hex: &str) -> Result<Self> {
|
|
let secp = Secp256k1::new();
|
|
|
|
let secret_key_bytes = hex::decode(private_key_hex)
|
|
.map_err(|_| anyhow!("Invalid private key hex format"))?;
|
|
|
|
let secret_key = SecretKey::from_slice(&secret_key_bytes)
|
|
.map_err(|_| anyhow!("Invalid private key"))?;
|
|
|
|
let public_key = PublicKey::from_secret_key(&secp, &secret_key);
|
|
|
|
Ok(Self {
|
|
secret_key,
|
|
public_key,
|
|
secp,
|
|
})
|
|
}
|
|
|
|
/// Generate a new random private key
|
|
pub fn generate() -> Result<Self> {
|
|
let secp = Secp256k1::new();
|
|
let (secret_key, public_key) = secp.generate_keypair(&mut rand::thread_rng());
|
|
|
|
Ok(Self {
|
|
secret_key,
|
|
public_key,
|
|
secp,
|
|
})
|
|
}
|
|
|
|
/// Get the public key as a hex string
|
|
pub fn public_key_hex(&self) -> String {
|
|
hex::encode(self.public_key.serialize())
|
|
}
|
|
|
|
/// Get the private key as a hex string
|
|
pub fn private_key_hex(&self) -> String {
|
|
hex::encode(self.secret_key.secret_bytes())
|
|
}
|
|
|
|
/// Sign a message and return the signature as hex
|
|
pub fn sign_message(&self, message: &str) -> Result<String> {
|
|
let message_hash = Sha256::digest(message.as_bytes());
|
|
let message = Message::from_slice(&message_hash)
|
|
.map_err(|_| anyhow!("Failed to create message from hash"))?;
|
|
|
|
let signature = self.secp.sign_ecdsa(&message, &self.secret_key);
|
|
Ok(hex::encode(signature.serialize_compact()))
|
|
}
|
|
|
|
/// Verify a signature against a message
|
|
pub fn verify_signature(&self, message: &str, signature_hex: &str) -> Result<bool> {
|
|
let message_hash = Sha256::digest(message.as_bytes());
|
|
let message = Message::from_slice(&message_hash)
|
|
.map_err(|_| anyhow!("Failed to create message from hash"))?;
|
|
|
|
let signature_bytes = hex::decode(signature_hex)
|
|
.map_err(|_| anyhow!("Invalid signature hex format"))?;
|
|
|
|
let signature = Signature::from_compact(&signature_bytes)
|
|
.map_err(|_| anyhow!("Invalid signature format"))?;
|
|
|
|
match self.secp.verify_ecdsa(&message, &signature, &self.public_key) {
|
|
Ok(_) => Ok(true),
|
|
Err(_) => Ok(false),
|
|
}
|
|
}
|
|
}
|