use std::{fmt, convert::{TryFrom, TryInto}};
use serde::{Deserialize, Serialize};
use subtle::ConstantTimeEq;
use crate::{
self as fadroma,
prelude::*,
crypto::{Prng, sha_256},
impl_canonize_default
};
pub mod auth;
const VIEWING_KEY_PREFIX: &str = "api_key_";
#[derive(Serialize, Deserialize, FadromaSerialize, FadromaDeserialize, JsonSchema, Clone, Default, Debug)]
pub struct ViewingKey(pub String);
#[derive(Serialize, Deserialize, FadromaSerialize, FadromaDeserialize, JsonSchema, Clone, Copy, Default, Debug)]
pub struct ViewingKeyHashed([u8; Self::SIZE]);
impl_canonize_default!(ViewingKey);
impl_canonize_default!(ViewingKeyHashed);
impl ViewingKey {
pub fn new(env: &Env, info: &MessageInfo, seed: &[u8], entropy: &[u8]) -> Self {
let entropy_len = 16 + info.sender.as_str().len() + entropy.len();
let mut rng_entropy = Vec::with_capacity(entropy_len);
rng_entropy.extend_from_slice(&env.block.height.to_be_bytes());
rng_entropy.extend_from_slice(&env.block.time.seconds().to_be_bytes());
rng_entropy.extend_from_slice(&info.sender.as_bytes());
rng_entropy.extend_from_slice(entropy);
let mut rng = Prng::new(seed, &rng_entropy);
let rand_slice = rng.rand_bytes();
let key = sha_256(&rand_slice);
Self(VIEWING_KEY_PREFIX.to_string() + &Binary::from(&key).to_base64())
}
#[inline]
pub fn to_hashed(&self) -> ViewingKeyHashed {
ViewingKeyHashed(sha_256(self.as_bytes()))
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
self.0.as_bytes()
}
#[inline]
pub fn check(&self, other: &ViewingKey) -> bool {
let this = self.to_hashed();
let other = other.to_hashed();
this.check(&other)
}
#[inline]
pub fn check_hashed(&self, hashed: &ViewingKeyHashed) -> bool {
let this = self.to_hashed();
this.check(&hashed)
}
}
impl ViewingKeyHashed {
pub const SIZE: usize = 32;
#[inline]
pub fn check(&self, other: &Self) -> bool {
bool::from(self.as_slice().ct_eq(other.as_slice()))
}
#[inline]
pub fn as_slice(&self) -> &[u8] {
self.0.as_slice()
}
}
impl AsRef<str> for ViewingKey {
#[inline]
fn as_ref(&self) -> &str {
&self.0
}
}
impl AsRef<[u8]> for ViewingKey {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl fmt::Display for ViewingKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl<T: Into<String>> From<T> for ViewingKey {
#[inline]
fn from (vk: T) -> Self {
ViewingKey(vk.into())
}
}
impl From<&[u8; Self::SIZE]> for ViewingKeyHashed {
#[inline]
fn from(array: &[u8; Self::SIZE]) -> Self {
Self(*array)
}
}
impl From<[u8; Self::SIZE]> for ViewingKeyHashed {
#[inline]
fn from(array: [u8; Self::SIZE]) -> Self {
Self(array)
}
}
impl TryFrom<&[u8]> for ViewingKeyHashed {
type Error = StdError;
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
let array = slice.try_into().map_err(|_|
StdError::InvalidDataSize {
expected: Self::SIZE as u64,
actual: slice.len() as u64
}
)?;
Ok(Self(array))
}
}
impl AsRef<[u8]> for ViewingKeyHashed {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_slice()
}
}