use std::{fmt, str::FromStr};
#[cfg(feature = "bindgen")]
use aead::rand_core::OsRng;
use base64::prelude::*;
#[cfg(feature = "bindgen")]
use x25519_dalek::StaticSecret;
const KEY_SIZE: usize = 32;
pub struct KeyBytes(pub(crate) [u8; KEY_SIZE]);
#[derive(Debug)]
#[cfg_attr(feature = "bindgen", derive(uniffi::Enum))]
pub enum KeyBytesError {
IllegalCharacter,
IllegalSize,
}
impl fmt::Display for KeyBytesError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::IllegalCharacter => "Illegal character in key",
Self::IllegalSize => "Illegal key size",
}
)
}
}
impl FromStr for KeyBytes {
type Err = KeyBytesError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut internal = [0u8; KEY_SIZE];
match s.len() {
64 => {
for i in 0..KEY_SIZE {
internal[i] = u8::from_str_radix(&s[i * 2..=i * 2 + 1], 16)
.map_err(|_| KeyBytesError::IllegalCharacter)?;
}
}
43 | 44 => {
if let Ok(decoded_key) = BASE64_STANDARD.decode(s) {
if decoded_key.len() == internal.len() {
internal[..].copy_from_slice(&decoded_key);
} else {
return Err(KeyBytesError::IllegalCharacter);
}
}
}
_ => return Err(KeyBytesError::IllegalSize),
}
Ok(KeyBytes(internal))
}
}
#[cfg(feature = "bindgen")]
impl KeyBytes {
pub fn secret() -> Self {
let key = StaticSecret::random_from_rng(OsRng).to_bytes();
Self(key)
}
pub fn raw_bytes(&self) -> Vec<u8> {
self.0.into()
}
pub fn from_string(s: &str) -> Result<Self, KeyBytesError> {
Self::from_str(s)
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, KeyBytesError> {
let internal = bytes.try_into().map_err(|_| KeyBytesError::IllegalSize)?;
Ok(Self(internal))
}
#[must_use]
pub fn to_base64(&self) -> String {
BASE64_STANDARD.encode(self.0)
}
#[must_use]
pub fn to_lower_hex(&self) -> String {
let mut hex = String::with_capacity(64);
let to_char = |nibble: u8| -> char {
(match nibble {
0..=9 => b'0' + nibble,
_ => nibble + b'a' - 10,
}) as char
};
self.0.iter().for_each(|byte| {
hex.push(to_char(*byte >> 4));
hex.push(to_char(*byte & 0xf));
});
hex
}
}