signer-core 0.3.2

Signer core package.
Documentation
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)
}