use p256::ecdsa::{
signature::{Signer as P256Signer, Verifier},
Signature, SigningKey, VerifyingKey,
};
use p256::elliptic_curve::rand_core::OsRng;
use std::sync::Arc;
use super::error::{Result, SignError};
use super::signer::Signer;
use super::types::Platform;
#[derive(Clone)]
pub struct LocalES256Signer {
ios_signing_key: Arc<SigningKey>,
android_signing_key: Arc<SigningKey>,
ios_public_key_bytes: Vec<u8>,
android_public_key_bytes: Vec<u8>,
}
impl LocalES256Signer {
pub fn new(
ios_key_path: Option<&std::path::Path>,
android_key_path: Option<&std::path::Path>,
) -> Result<Self> {
let ios_signing_key = match ios_key_path {
Some(path) if path.exists() => Self::load_key_from_pem(path)?,
_ => SigningKey::random(&mut OsRng),
};
let android_signing_key = match android_key_path {
Some(path) if path.exists() => Self::load_key_from_pem(path)?,
_ => SigningKey::random(&mut OsRng),
};
let ios_public_key_bytes = ios_signing_key
.verifying_key()
.to_encoded_point(false)
.as_bytes()
.to_vec();
let android_public_key_bytes = android_signing_key
.verifying_key()
.to_encoded_point(false)
.as_bytes()
.to_vec();
Ok(Self {
ios_signing_key: Arc::new(ios_signing_key),
android_signing_key: Arc::new(android_signing_key),
ios_public_key_bytes,
android_public_key_bytes,
})
}
pub fn random() -> Self {
let ios_signing_key = SigningKey::random(&mut OsRng);
let android_signing_key = SigningKey::random(&mut OsRng);
let ios_public_key_bytes = ios_signing_key
.verifying_key()
.to_encoded_point(false)
.as_bytes()
.to_vec();
let android_public_key_bytes = android_signing_key
.verifying_key()
.to_encoded_point(false)
.as_bytes()
.to_vec();
Self {
ios_signing_key: Arc::new(ios_signing_key),
android_signing_key: Arc::new(android_signing_key),
ios_public_key_bytes,
android_public_key_bytes,
}
}
pub fn from_pem(ios_pem: &str, android_pem: &str) -> Result<Self> {
let ios_signing_key = Self::parse_pem_key(ios_pem)?;
let android_signing_key = Self::parse_pem_key(android_pem)?;
let ios_public_key_bytes = ios_signing_key
.verifying_key()
.to_encoded_point(false)
.as_bytes()
.to_vec();
let android_public_key_bytes = android_signing_key
.verifying_key()
.to_encoded_point(false)
.as_bytes()
.to_vec();
Ok(Self {
ios_signing_key: Arc::new(ios_signing_key),
android_signing_key: Arc::new(android_signing_key),
ios_public_key_bytes,
android_public_key_bytes,
})
}
fn load_key_from_pem(path: &std::path::Path) -> Result<SigningKey> {
let pem = std::fs::read_to_string(path).map_err(|e| {
SignError::InvalidKey(format!("Failed to read {}: {}", path.display(), e))
})?;
Self::parse_pem_key(&pem)
}
fn parse_pem_key(pem: &str) -> Result<SigningKey> {
use p256::pkcs8::DecodePrivateKey;
use p256::SecretKey;
if let Ok(key) = SigningKey::from_pkcs8_pem(pem) {
return Ok(key);
}
let secret_key = SecretKey::from_sec1_pem(pem)
.map_err(|e| SignError::InvalidKey(format!("Failed to parse PEM key: {}", e)))?;
Ok(SigningKey::from(secret_key))
}
fn key_for_platform(&self, platform: &Platform) -> &SigningKey {
match platform {
Platform::Ios => &self.ios_signing_key,
Platform::Android => &self.android_signing_key,
}
}
}
impl Signer for LocalES256Signer {
fn sign(&self, platform: &Platform, data: &[u8]) -> Result<Vec<u8>> {
let key = self.key_for_platform(platform);
let signature: Signature = key.sign(data);
Ok(signature.to_bytes().to_vec())
}
fn public_key(&self, platform: &Platform) -> Result<Vec<u8>> {
Ok(match platform {
Platform::Ios => self.ios_public_key_bytes.clone(),
Platform::Android => self.android_public_key_bytes.clone(),
})
}
fn verify(&self, platform: &Platform, data: &[u8], signature: &[u8]) -> Result<()> {
let public_key_bytes = match platform {
Platform::Ios => &self.ios_public_key_bytes,
Platform::Android => &self.android_public_key_bytes,
};
let verifying_key = VerifyingKey::from_sec1_bytes(public_key_bytes)
.map_err(|e| SignError::InvalidKey(format!("Failed to parse public key: {}", e)))?;
let sig = if signature.len() == 64 {
Signature::from_slice(signature).map_err(|e| {
SignError::InvalidSignature(format!("Invalid signature format: {}", e))
})?
} else {
Signature::from_der(signature).map_err(|e| {
SignError::InvalidSignature(format!("Invalid signature format: {}", e))
})?
};
verifying_key.verify(data, &sig).map_err(|e| {
SignError::VerificationFailed(format!("Signature verification failed: {}", e))
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_random_signer_sign_and_verify() {
let signer = LocalES256Signer::random();
let data = b"hello world";
for platform in &[Platform::Ios, Platform::Android] {
let signature = signer.sign(platform, data).unwrap();
assert!(!signature.is_empty());
assert_eq!(signature.len(), 64);
signer.verify(platform, data, &signature).unwrap();
}
}
#[test]
fn test_public_key_retrieval() {
let signer = LocalES256Signer::random();
let ios_key = signer.public_key(&Platform::Ios).unwrap();
let android_key = signer.public_key(&Platform::Android).unwrap();
assert!(!ios_key.is_empty());
assert!(!android_key.is_empty());
assert_eq!(ios_key.len(), 65);
assert_eq!(android_key.len(), 65);
assert_ne!(ios_key, android_key);
}
#[test]
fn test_verify_wrong_data_fails() {
let signer = LocalES256Signer::random();
let data = b"hello world";
let wrong_data = b"wrong data";
let signature = signer.sign(&Platform::Ios, data).unwrap();
let result = signer.verify(&Platform::Ios, wrong_data, &signature);
assert!(result.is_err());
}
#[test]
fn test_verify_wrong_platform_fails() {
let signer = LocalES256Signer::random();
let data = b"hello world";
let signature = signer.sign(&Platform::Ios, data).unwrap();
let result = signer.verify(&Platform::Android, data, &signature);
assert!(result.is_err());
}
}