use rsa::pkcs1::DecodeRsaPrivateKey;
use rsa::pkcs1v15::SigningKey;
use rsa::signature::SignatureEncoding;
use rsa::signature::hazmat::PrehashSigner;
use rsa::traits::PublicKeyParts;
use rsa::{Oaep, RsaPrivateKey};
use crate::error::CryptoError;
use crate::util::base64::Base64;
pub struct RsaKey {
key: RsaPrivateKey,
base64: Base64,
}
impl RsaKey {
pub fn from_pem(pem: &str) -> Result<Self, CryptoError> {
let key = RsaPrivateKey::from_pkcs1_pem(pem).map_err(|e| CryptoError::RsaKey(e.to_string()))?;
Ok(Self {
key,
base64: Base64::new(
b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
false,
false,
),
})
}
pub fn sign_challenge(&self, b64_challenge: &str, ip_addr: &[u8], hw_addr: &[u8]) -> Result<String, CryptoError> {
let challenge = self
.base64
.decode(b64_challenge)
.ok_or_else(|| CryptoError::RsaKey("invalid base64 challenge".into()))?;
let mut data = Vec::with_capacity(32);
data.extend_from_slice(&challenge);
data.extend_from_slice(ip_addr);
data.extend_from_slice(hw_addr);
if data.len() < 32 {
data.resize(32, 0);
}
let signing_key: SigningKey<sha1::Sha1> = SigningKey::new_unprefixed(self.key.clone());
let signature = signing_key
.sign_prehash(&data)
.map_err(|e| CryptoError::RsaKey(e.to_string()))?;
Ok(self.base64.encode(&signature.to_vec()))
}
pub fn decrypt(&self, b64_input: &str) -> Result<Vec<u8>, CryptoError> {
let ciphertext = self.base64.decode(b64_input).ok_or(CryptoError::RsaDecrypt)?;
let key_len = self.key.n().bits() / 8;
let mut padded = vec![0u8; key_len];
let offset = key_len.saturating_sub(ciphertext.len());
padded[offset..offset + ciphertext.len()].copy_from_slice(&ciphertext);
let padding = Oaep::new::<sha1::Sha1>();
self.key.decrypt(padding, &padded).map_err(|_| CryptoError::RsaDecrypt)
}
pub fn decode(&self, b64_input: &str) -> Result<Vec<u8>, CryptoError> {
self.base64
.decode(b64_input)
.ok_or_else(|| CryptoError::RsaKey("invalid base64 input".into()))
}
}