use crypto_box::SecretKey;
use zeroize::Zeroize;
use crate::CryptoError;
use crate::b64;
use crate::secretbox::{decrypt_secretbox_to_string, encrypt_secretbox_string};
const KEY_LEN: usize = 32;
const SALT_LEN: usize = 16;
#[derive(Debug, Clone)]
pub struct KeyPair {
pub public_key: String,
pub private_key: String,
}
#[inline]
fn random_bytes(buf: &mut [u8]) {
getrandom::getrandom(buf).expect("OS CSPRNG unavailable");
}
pub fn generate_key() -> String {
let mut key = [0u8; KEY_LEN];
random_bytes(&mut key);
let encoded = b64::encode(&key);
key.zeroize();
encoded
}
pub fn generate_keypair() -> KeyPair {
let mut sk_bytes = [0u8; KEY_LEN];
random_bytes(&mut sk_bytes);
let sk = SecretKey::from_slice(&sk_bytes).expect("valid 32-byte key");
let pk = sk.public_key();
let kp = KeyPair {
public_key: b64::encode(pk.as_bytes()),
private_key: b64::encode(&sk_bytes),
};
sk_bytes.zeroize();
kp
}
pub fn generate_salt() -> String {
let mut salt = [0u8; SALT_LEN];
random_bytes(&mut salt);
b64::encode(&salt)
}
pub fn encrypt_private_key(
private_key_b64: &str,
session_key_b64: &str,
) -> Result<String, CryptoError> {
encrypt_secretbox_string(private_key_b64, session_key_b64)
}
pub fn decrypt_private_key(ct_b64: &str, session_key_b64: &str) -> Result<String, CryptoError> {
decrypt_secretbox_to_string(ct_b64, session_key_b64)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn key_is_32_bytes() {
let key = generate_key();
assert_eq!(b64::decode(&key).unwrap().len(), 32);
}
#[test]
fn keys_are_unique() {
assert_ne!(generate_key(), generate_key());
}
#[test]
fn keypair_sizes() {
let kp = generate_keypair();
assert_eq!(b64::decode(&kp.public_key).unwrap().len(), 32);
assert_eq!(b64::decode(&kp.private_key).unwrap().len(), 32);
}
#[test]
fn salt_is_16_bytes() {
assert_eq!(b64::decode(&generate_salt()).unwrap().len(), 16);
}
#[test]
fn private_key_encrypt_decrypt() {
let kp = generate_keypair();
let session_key = generate_key();
let ct = encrypt_private_key(&kp.private_key, &session_key).unwrap();
let pt = decrypt_private_key(&ct, &session_key).unwrap();
assert_eq!(pt, kp.private_key);
}
#[test]
fn private_key_wrong_session_key() {
let kp = generate_keypair();
let k1 = generate_key();
let k2 = generate_key();
let ct = encrypt_private_key(&kp.private_key, &k1).unwrap();
assert!(decrypt_private_key(&ct, &k2).is_err());
}
}