use crate::error::LicenseError;
use base64::engine::Engine;
use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
use rand::RngCore;
use rand::rngs::OsRng;
use serde::{Deserialize, Serialize};
use std::convert::TryInto;
use tracing::{debug, error, info};
use zeroize::{Zeroize, ZeroizeOnDrop};
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct PrivateKey(#[zeroize(skip)] SigningKey);
impl PrivateKey {
pub fn new() -> Self {
let mut csprng = OsRng;
let mut bytes = [0u8; 32];
csprng.fill_bytes(&mut bytes);
let signing_key = SigningKey::from_bytes(&bytes);
Self(signing_key)
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, LicenseError> {
if bytes.len() != 32 {
error!("Invalid private key length: {}", bytes.len());
return Err(LicenseError::CryptoError(
"Invalid private key length. Must be 32 bytes".to_string(),
));
}
let signing_key = SigningKey::from_bytes(bytes.try_into().map_err(|_| {
LicenseError::CryptoError("Failed to convert bytes to array".to_string())
})?);
Ok(Self(signing_key))
}
pub fn sign(&self, message: &[u8]) -> Result<Signature, LicenseError> {
let signature = self.0.sign(message);
debug!("Signed {} bytes message", message.len());
Ok(signature)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PublicKey(#[serde(with = "serde_bytes")] [u8; 32]);
impl PublicKey {
pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), LicenseError> {
let verifying_key = VerifyingKey::from_bytes(&self.0).map_err(|e| {
error!("Invalid public key format: {}", e);
LicenseError::CryptoError(format!("Invalid public key: {}", e))
})?;
verifying_key.verify(message, signature).map_err(|e| {
error!("Signature verification failed: {}", e);
LicenseError::SignatureValidationFailed
})?;
debug!("Signature verified successfully");
Ok(())
}
pub fn to_base64(&self) -> String {
base64::engine::general_purpose::STANDARD.encode(self.0)
}
pub fn from_base64(s: &str) -> Result<Self, LicenseError> {
let bytes = base64::engine::general_purpose::STANDARD
.decode(s)
.map_err(|e| {
error!("Failed to decode base64 public key: {}", e);
LicenseError::CryptoError(format!("Base64 decode error: {}", e))
})?;
if bytes.len() != 32 {
error!(
"Invalid public key length after base64 decode: {}",
bytes.len()
);
return Err(LicenseError::CryptoError(
"Invalid public key length".to_string(),
));
}
let mut key = [0u8; 32];
key.copy_from_slice(&bytes);
Ok(Self(key))
}
pub fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
pub fn to_bytes(&self) -> [u8; 32] {
self.0
}
}
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct KeyPair {
#[zeroize(skip)]
pub public: PublicKey,
pub private: PrivateKey,
}
impl KeyPair {
pub fn generate() -> Self {
info!("Generating new Ed25519 key pair");
let mut csprng = OsRng;
let mut bytes = [0u8; 32];
csprng.fill_bytes(&mut bytes);
let signing_key = SigningKey::from_bytes(&bytes);
let verifying_key = signing_key.verifying_key();
Self {
private: PrivateKey(signing_key),
public: PublicKey(verifying_key.to_bytes()),
}
}
pub fn from_seed(seed: &[u8]) -> Result<Self, LicenseError> {
debug!("Generating key pair from seed");
if seed.len() != 32 {
return Err(LicenseError::CryptoError(
"Seed must be 32 bytes".to_string(),
));
}
let seed_bytes: [u8; 32] = seed.try_into().map_err(|_| {
LicenseError::CryptoError("Failed to convert seed to array".to_string())
})?;
let signing_key = SigningKey::from_bytes(&seed_bytes);
let verifying_key = signing_key.verifying_key();
Ok(Self {
private: PrivateKey(signing_key),
public: PublicKey(verifying_key.to_bytes()),
})
}
pub fn save_to_file(&self, path: &std::path::Path) -> Result<(), LicenseError> {
use std::fs;
use std::io::Write;
debug!("Saving key pair to: {:?}", path);
let data = serde_json::json!({
"public_key": self.public.to_base64(),
"algorithm": "ed25519",
"created_at": chrono::Utc::now().to_rfc3339(),
});
let json = serde_json::to_string_pretty(&data)
.map_err(|e| LicenseError::SerializationError(e.to_string()))?;
let mut file = fs::OpenOptions::new()
.write(true)
.create_new(true)
.open(path)?;
file.write_all(json.as_bytes())?;
file.sync_all()?;
info!("Key pair saved to {:?}", path);
Ok(())
}
pub fn load_public_from_file(path: &std::path::Path) -> Result<PublicKey, LicenseError> {
debug!("Loading public key from: {:?}", path);
let data = std::fs::read_to_string(path)?;
let json: serde_json::Value = serde_json::from_str(&data)
.map_err(|e| LicenseError::SerializationError(e.to_string()))?;
let pub_key_b64 = json["public_key"].as_str().ok_or_else(|| {
error!("Invalid key file format: missing public_key field");
LicenseError::CryptoError("Invalid key file format".to_string())
})?;
PublicKey::from_base64(pub_key_b64)
}
}