use crate::{CllwOreDecrypt, Error, Key};
use hex::{FromHex, FromHexError};
#[cfg(feature = "postgres-types")]
use postgres_types::ToSql;
use std::cmp::Ordering;
use subtle::ConstantTimeEq;
const BITS_PER_BYTE: usize = 8;
#[derive(Debug, Clone)]
#[cfg_attr(feature = "postgres-types", derive(ToSql))]
#[cfg_attr(feature = "postgres-types", postgres(name = "ore_cllw_8_variable_v1"))]
pub struct OreCllw8VariableV1 {
bytes: Vec<u8>,
}
impl AsRef<[u8]> for OreCllw8VariableV1 {
fn as_ref(&self) -> &[u8] {
&self.bytes
}
}
impl FromHex for OreCllw8VariableV1 {
type Error = FromHexError;
fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
Ok(Self {
bytes: hex::decode(hex)?,
})
}
}
impl From<Vec<u8>> for OreCllw8VariableV1 {
fn from(bytes: Vec<u8>) -> Self {
Self { bytes }
}
}
impl OreCllw8VariableV1 {
pub fn decrypt_to_bytes(&self, key: &Key, salt: Option<&[u8]>) -> Result<Vec<u8>, Error> {
if !self.bytes.len().is_multiple_of(BITS_PER_BYTE) {
return Err(Error);
}
let mut hasher = blake3::Hasher::new_keyed(&key.0);
if let Some(salt) = salt {
let _ = hasher.update(salt);
}
let mut result_bytes = Vec::with_capacity(self.bytes.len() / BITS_PER_BYTE);
let mut current_byte: u8 = 0;
for (i, &ciphertext_byte) in self.bytes.iter().enumerate() {
let mut buf: [u8; 16] = [0; 16];
hasher.finalize_xof().fill(&mut buf);
let prf_block = u128::from_be_bytes(buf) & 0xFF;
let bit = ciphertext_byte.wrapping_sub(prf_block as u8) & 0x01;
let _ = hasher.update(&[bit]);
let bit_position = i % BITS_PER_BYTE;
current_byte |= bit << (7 - bit_position);
if bit_position == 7 {
result_bytes.push(current_byte);
current_byte = 0;
}
}
Ok(result_bytes)
}
}
impl CllwOreDecrypt for OreCllw8VariableV1 {
type Output = String;
fn decrypt_with_salt(self, key: &Key, salt: Option<&[u8]>) -> Result<Self::Output, Error> {
let result_bytes = self.decrypt_to_bytes(key, salt)?;
String::from_utf8(result_bytes).map_err(|_| Error)
}
}
impl Ord for OreCllw8VariableV1 {
fn cmp(&self, other: &Self) -> Ordering {
compare_lex(self, other)
}
}
impl PartialOrd for OreCllw8VariableV1 {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for OreCllw8VariableV1 {
fn eq(&self, other: &Self) -> bool {
self.bytes.ct_eq(&other.bytes).into()
}
}
impl Eq for OreCllw8VariableV1 {}
fn compare_lex(a: &OreCllw8VariableV1, b: &OreCllw8VariableV1) -> Ordering {
let a_len = a.bytes.len();
let b_len = b.bytes.len();
let slice_len = a_len.min(b_len);
if slice_len == 0 {
return a_len.cmp(&b_len);
}
let prefix_ordering = super::compare_slice(&a.bytes[..slice_len], &b.bytes[..slice_len]);
match prefix_ordering {
Ordering::Equal => a_len.cmp(&b_len),
other => other,
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "postgres-types", derive(ToSql))]
#[cfg_attr(
feature = "postgres-types",
postgres(name = "ore_cllw_8_ope_variable_v1")
)]
pub struct OpeCllw8VariableV1 {
bytes: Vec<u8>,
}
impl OpeCllw8VariableV1 {
pub(crate) fn from_bytes(bytes: Vec<u8>) -> Self {
Self { bytes }
}
}
impl AsRef<[u8]> for OpeCllw8VariableV1 {
fn as_ref(&self) -> &[u8] {
&self.bytes
}
}
impl FromHex for OpeCllw8VariableV1 {
type Error = FromHexError;
fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
Ok(Self {
bytes: hex::decode(hex)?,
})
}
}