use crate::{Result, SignerError};
use base64::{prelude::BASE64_URL_SAFE, Engine};
use chacha20poly1305::{
aead::{Aead, OsRng},
AeadCore, KeyInit,
};
use schnorrkel::context::SigningContext;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use sha3::digest::generic_array::GenericArray;
use crate::signer_user::SignerUser;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SignerCrypted<T> {
pub sender_pub_key: String,
pub receiver_pub_key: String,
pub cipher_text: String,
pub nonce: String,
#[serde(skip)]
pub _marker: std::marker::PhantomData<T>,
}
impl<T> SignerCrypted<T>
where
T: Serialize + DeserializeOwned,
{
pub fn create(sender: &SignerUser, receiver_pub_key: &str, value: T) -> Result<Self> {
let message = serde_json::to_string(&value)?;
let key = key_agreement(sender, &receiver_pub_key)?;
let cipher = chacha20poly1305::ChaCha20Poly1305::new(GenericArray::from_slice(&key));
let nonce = chacha20poly1305::ChaCha20Poly1305::generate_nonce(&mut OsRng);
let cipher_text = cipher
.encrypt(&nonce, message.as_bytes())
.map_err(|e| SignerError::Msg(format!("encrypt signer message failed: {}", e)))?;
Ok(Self {
sender_pub_key: sender.public.pub_key.clone(),
receiver_pub_key: receiver_pub_key.to_string(),
cipher_text: BASE64_URL_SAFE.encode(cipher_text),
nonce: BASE64_URL_SAFE.encode(nonce.to_vec()),
_marker: std::marker::PhantomData,
})
}
pub fn decrypt(&self, decrypter: &SignerUser) -> Result<T> {
let key = {
if decrypter.public.pub_key == self.sender_pub_key {
key_agreement(decrypter, &self.receiver_pub_key)?
} else if decrypter.public.pub_key == self.receiver_pub_key {
key_agreement(decrypter, &self.sender_pub_key)?
} else {
return Err(SignerError::Msg(
"decrypter is not a part of message peer".to_string(),
));
}
};
let cipher = chacha20poly1305::ChaCha20Poly1305::new(GenericArray::from_slice(&key));
let nonce = BASE64_URL_SAFE.decode(&self.nonce)?;
let cipher_text = BASE64_URL_SAFE.decode(&self.cipher_text)?;
let plain_text = cipher
.decrypt(GenericArray::from_slice(&nonce), cipher_text.as_slice())
.map_err(|e| SignerError::Msg(format!("decrypt signer message failed: {}", e)))?;
Ok(serde_json::from_str(&String::from_utf8(plain_text)?)?)
}
}
fn key_agreement(sender: &SignerUser, receiver: &str) -> Result<[u8; 32]> {
let keypair = sender.to_keypair()?;
let public = schnorrkel::PublicKey::from_bytes(&BASE64_URL_SAFE.decode(&receiver)?)
.map_err(|e| SignerError::Msg(format!("parse receiver public key failed: {}", e)))?;
let mut trans = SigningContext::new(b"").bytes(b"");
keypair.commit_key_exchange(&mut trans, b"", &public);
let mut key: [u8; 32] = [0; 32];
trans.challenge_bytes(b"", key.as_mut_slice());
Ok(key)
}