use crate::utils::error::{Error, Result};
use base64::{engine::general_purpose, Engine as _};
use pqcrypto_mldsa::mldsa65;
use pqcrypto_traits::sign::{PublicKey, SecretKey, SignedMessage};
use sha2::{Digest, Sha256};
use std::fs;
use std::path::Path;
pub struct PqcSigner {
secret_key: mldsa65::SecretKey,
public_key: mldsa65::PublicKey,
}
impl PqcSigner {
pub fn new() -> Self {
let (public_key, secret_key) = mldsa65::keypair();
Self {
secret_key,
public_key,
}
}
pub fn from_files(secret_key_path: &Path, public_key_path: &Path) -> Result<Self> {
let sk_bytes = fs::read(secret_key_path)
.map_err(|e| Error::with_context("Failed to read secret key", &e.to_string()))?;
let pk_bytes = fs::read(public_key_path)
.map_err(|e| Error::with_context("Failed to read public key", &e.to_string()))?;
let secret_key = mldsa65::SecretKey::from_bytes(&sk_bytes)
.map_err(|_| Error::new("Invalid secret key format"))?;
let public_key = mldsa65::PublicKey::from_bytes(&pk_bytes)
.map_err(|_| Error::new("Invalid public key format"))?;
Ok(Self {
secret_key,
public_key,
})
}
pub fn save_to_files(&self, secret_key_path: &Path, public_key_path: &Path) -> Result<()> {
fs::write(secret_key_path, self.secret_key.as_bytes())
.map_err(|e| Error::with_context("Failed to write secret key", &e.to_string()))?;
fs::write(public_key_path, self.public_key.as_bytes())
.map_err(|e| Error::with_context("Failed to write public key", &e.to_string()))?;
Ok(())
}
pub fn sign(&self, message: &[u8]) -> Vec<u8> {
let signed = mldsa65::sign(message, &self.secret_key);
signed.as_bytes().to_vec()
}
pub fn sign_pack(&self, pack_id: &str, version: &str, sha256: &str) -> String {
let message = format!("{}:{}:{}", pack_id, version, sha256);
let signature = self.sign(message.as_bytes());
general_purpose::STANDARD.encode(&signature)
}
pub fn public_key_base64(&self) -> String {
general_purpose::STANDARD.encode(self.public_key.as_bytes())
}
pub fn secret_key_base64(&self) -> String {
general_purpose::STANDARD.encode(self.secret_key.as_bytes())
}
}
impl Default for PqcSigner {
fn default() -> Self {
Self::new()
}
}
pub struct PqcVerifier {
public_key: mldsa65::PublicKey,
}
impl PqcVerifier {
pub fn from_public_key(public_key_bytes: &[u8]) -> Result<Self> {
let public_key = mldsa65::PublicKey::from_bytes(public_key_bytes)
.map_err(|_| Error::new("Invalid public key format"))?;
Ok(Self { public_key })
}
pub fn from_base64(public_key_b64: &str) -> Result<Self> {
let public_key_bytes = general_purpose::STANDARD
.decode(public_key_b64)
.map_err(|e| Error::with_context("Failed to decode public key", &e.to_string()))?;
Self::from_public_key(&public_key_bytes)
}
pub fn verify(&self, message: &[u8], signature: &[u8]) -> Result<bool> {
let signed_message = SignedMessage::from_bytes(signature)
.map_err(|_| Error::new("Invalid signature format"))?;
match mldsa65::open(&signed_message, &self.public_key) {
Ok(verified_msg) => Ok(verified_msg == message),
Err(_) => Ok(false),
}
}
pub fn verify_pack(
&self, pack_id: &str, version: &str, sha256: &str, signature_b64: &str,
) -> Result<bool> {
let message = format!("{}:{}:{}", pack_id, version, sha256);
let signature = general_purpose::STANDARD
.decode(signature_b64)
.map_err(|e| Error::with_context("Failed to decode signature", &e.to_string()))?;
self.verify(message.as_bytes(), &signature)
}
pub fn public_key_base64(&self) -> String {
general_purpose::STANDARD.encode(self.public_key.as_bytes())
}
}
pub fn calculate_sha256_file(path: &Path) -> Result<String> {
let content =
fs::read(path).map_err(|e| Error::with_context("Failed to read file", &e.to_string()))?;
Ok(calculate_sha256(&content))
}
pub fn calculate_sha256(data: &[u8]) -> String {
let mut hasher = Sha256::new();
hasher.update(data);
format!("{:x}", hasher.finalize())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pqc_signer_creation() {
let signer = PqcSigner::new();
assert!(!signer.public_key_base64().is_empty());
assert!(!signer.secret_key_base64().is_empty());
}
#[test]
fn test_pqc_sign_and_verify() {
let signer = PqcSigner::new();
let message = b"test message";
let signature = signer.sign(message);
assert!(!signature.is_empty());
let verifier = PqcVerifier::from_public_key(signer.public_key.as_bytes()).unwrap();
let verified = verifier.verify(message, &signature).unwrap();
assert!(verified);
}
#[test]
fn test_pqc_pack_signature() {
let signer = PqcSigner::new();
let pack_id = "io.ggen.test";
let version = "1.0.0";
let sha256 = "abc123";
let signature = signer.sign_pack(pack_id, version, sha256);
assert!(!signature.is_empty());
let verifier = PqcVerifier::from_base64(&signer.public_key_base64()).unwrap();
let verified = verifier
.verify_pack(pack_id, version, sha256, &signature)
.unwrap();
assert!(verified);
}
#[test]
fn test_pqc_invalid_signature() {
let signer = PqcSigner::new();
let verifier = PqcVerifier::from_public_key(signer.public_key.as_bytes()).unwrap();
let message = b"test message";
let signature = signer.sign(message);
let tampered_message = b"tampered message";
let verified = verifier.verify(tampered_message, &signature).unwrap();
assert!(!verified);
}
#[test]
fn test_sha256_calculation() {
let data = b"test data";
let hash = calculate_sha256(data);
assert_eq!(
hash,
"916f0027a575074ce72a331777c3478d6513f786a591bd892da1a577bf2335f9"
);
}
}