use anyhow::anyhow;
use base64::{prelude::BASE64_URL_SAFE, Engine};
use chacha20poly1305::{
aead::{Aead, OsRng},
AeadCore, KeyInit,
};
use schnorrkel::context::SigningContext;
use serde::{Deserialize, Serialize};
use sha3::digest::generic_array::GenericArray;
use crate::signer_user::SignerUser;
#[derive(Debug, Serialize, Deserialize)]
pub struct SignerMessage {
pub sender: String,
pub cipher_text: String,
pub nonce: String,
}
impl SignerMessage {
pub fn create(
sender: &SignerUser,
receiver: &str,
message: &[u8],
) -> anyhow::Result<Self> {
let key = key_agreement(sender, &receiver)?;
let cipher =
chacha20poly1305::ChaCha20Poly1305::new(GenericArray::from_slice(&key));
let nonce = chacha20poly1305::ChaCha20Poly1305::generate_nonce(&mut OsRng);
let cipher_text = cipher
.encrypt(&nonce, message)
.map_err(|e| anyhow!("encrypt signer message failed: {}", e))?;
Ok(Self {
sender: sender.public.pub_key.clone(),
cipher_text: BASE64_URL_SAFE.encode(cipher_text),
nonce: BASE64_URL_SAFE.encode(nonce),
})
}
pub fn decrypt(&self, receiver: &SignerUser) -> anyhow::Result<Vec<u8>> {
let key = key_agreement(receiver, &self.sender)?;
let cipher =
chacha20poly1305::ChaCha20Poly1305::new(GenericArray::from_slice(&key));
let nonce = BASE64_URL_SAFE.decode(&self.nonce)?;
let plain_text = cipher
.decrypt(
GenericArray::from_slice(&nonce),
&*BASE64_URL_SAFE.decode(&self.cipher_text)?,
)
.map_err(|e| anyhow!("decrypt signer message failed: {}", e))?;
Ok(plain_text)
}
}
fn key_agreement(
sender: &SignerUser,
receiver: &str,
) -> anyhow::Result<[u8; 32]> {
let keypair = sender.to_keypair()?;
let public =
schnorrkel::PublicKey::from_bytes(&BASE64_URL_SAFE.decode(&receiver)?)
.map_err(|e| anyhow!("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)
}