use crate::{FbError, FbObj};
use crypto_bigint::{ArrayEncoding, generic_array::GenericArray, Uint};
use std::sync::RwLock;
#[cfg(feature = "base64")]
#[cfg_attr(docsrs, doc(cfg(feature = "base64")))]
use base64::{prelude::BASE64_STANDARD, Engine};
pub trait Encode {
fn to_bytes(&self) -> (Vec<u8>, Vec<u8>);
fn from_bytes(cipher: &[u8], keybase: &[u8]) -> Result<Self, FbError>
where
Self: Sized;
#[cfg(feature = "base64")]
#[cfg_attr(docsrs, doc(cfg(feature = "base64")))]
fn to_base64(&self) -> (String, String);
#[cfg(feature = "base64")]
#[cfg_attr(docsrs, doc(cfg(feature = "base64")))]
fn from_base64(cipher: &str, keybase: &str) -> Result<Self, FbError>
where
Self: Sized;
}
impl<const LIMBS: usize> Encode for FbObj<Uint<LIMBS>>
where
Uint<LIMBS>: ArrayEncoding,
{
fn to_bytes(&self) -> (Vec<u8>, Vec<u8>) {
let c = self.c.read().unwrap()
.iter()
.flat_map(|bigint| bigint.to_le_byte_array())
.collect();
let r = self.r.iter()
.flat_map(|bigint| bigint.to_le_byte_array())
.collect();
(c, r)
}
fn from_bytes(cipher: &[u8], keybase: &[u8]) -> Result<Self, FbError> {
let to_uint = |chunk| Uint::<LIMBS>::from_le_byte_array(GenericArray::clone_from_slice(chunk));
let c_vec: Vec<_> = cipher.chunks_exact(Uint::<LIMBS>::BYTES)
.map(to_uint)
.collect();
let r: Vec<_> = keybase.chunks_exact(Uint::<LIMBS>::BYTES)
.map(to_uint)
.collect();
if r.len() > c_vec.len() || r.len() < 2 {
return Err(FbError::InvalidParams);
}
let c = RwLock::new(c_vec);
Ok(FbObj {c, r})
}
#[cfg(feature = "base64")]
#[cfg_attr(docsrs, doc(cfg(feature = "base64")))]
fn to_base64(&self) -> (String, String) {
let (c, r) = self.to_bytes();
(BASE64_STANDARD.encode(c), BASE64_STANDARD.encode(r))
}
#[cfg(feature = "base64")]
#[cfg_attr(docsrs, doc(cfg(feature = "base64")))]
fn from_base64(cipher: &str, keybase: &str) -> Result<Self, FbError> {
let c_bytes = BASE64_STANDARD.decode(cipher)
.map_err(|_| FbError::DecodeError)?;
let r_bytes = BASE64_STANDARD.decode(keybase)
.map_err(|_| FbError::DecodeError)?;
Self::from_bytes(&c_bytes, &r_bytes)
}
}