use std::cmp::Ordering;
use crate::encryption::plaintext::Plaintext;
use chrono::Datelike;
use ore_rs::{CipherText, OreCipher, OreEncrypt, OreError};
use zeroize::{Zeroize, ZeroizeOnDrop};
pub trait IntoOrePlaintext<T: PartialEq + PartialOrd + Zeroize> {
fn to_ore(&self) -> OrePlaintext<T>;
}
#[derive(Debug, PartialEq, Zeroize, ZeroizeOnDrop)]
pub struct OrePlaintext<T: Zeroize + PartialEq + PartialOrd>(T);
impl<T> OrePlaintext<T>
where
T: Zeroize + PartialEq + PartialOrd,
{
pub(crate) fn inner(&self) -> T
where
T: Copy,
{
self.0
}
}
impl<T> Eq for OrePlaintext<T> where T: PartialEq + PartialOrd + Zeroize + Eq {}
impl<T> Ord for OrePlaintext<T>
where
T: PartialEq + PartialOrd + Eq + Zeroize + ZeroizeOnDrop,
{
fn cmp(&self, other: &Self) -> Ordering {
if self.0 == other.0 {
Ordering::Equal
} else if self.0 > other.0 {
Ordering::Greater
} else {
Ordering::Less
}
}
}
impl<T> PartialOrd for OrePlaintext<T>
where
T: PartialOrd + Zeroize + PartialEq + PartialOrd,
{
#[inline]
fn partial_cmp(&self, other: &OrePlaintext<T>) -> Option<Ordering> {
PartialOrd::partial_cmp(&self.0, &other.0)
}
}
impl OrePlaintext<u64> {
pub fn encrypt<T: OreCipher>(&self, cipher: &T) -> Result<CipherText<T, 8>, OreError> {
self.0.encrypt(cipher)
}
}
impl IntoOrePlaintext<u64> for i16 {
fn to_ore(&self) -> OrePlaintext<u64> {
let r = *self as u16 ^ (u16::MAX / 2 + 1);
OrePlaintext(r.into())
}
}
impl IntoOrePlaintext<u64> for i32 {
fn to_ore(&self) -> OrePlaintext<u64> {
let r = *self as u32 ^ (u32::MAX / 2 + 1);
OrePlaintext(r.into())
}
}
impl IntoOrePlaintext<u64> for i64 {
fn to_ore(&self) -> OrePlaintext<u64> {
OrePlaintext(*self as u64 ^ (u64::MAX / 2 + 1))
}
}
impl IntoOrePlaintext<u64> for f64 {
fn to_ore(&self) -> OrePlaintext<u64> {
let mut value = *self;
if value == -0.0f64 {
value = 0.0f64;
}
let num: u64 = value.to_bits();
let signed: i64 = -u64::cast_signed(num >> 63);
let mut mask: u64 = i64::cast_unsigned(signed);
mask |= 0x8000000000000000;
OrePlaintext(num ^ mask)
}
}
impl IntoOrePlaintext<u64> for &Plaintext {
fn to_ore(&self) -> OrePlaintext<u64> {
match self {
Plaintext::BigInt(Some(x)) => x.to_ore(),
Plaintext::Boolean(Some(x)) => OrePlaintext(*x as u64),
Plaintext::Decimal(_) => unimplemented!("Decimals not yet supported by ORE"),
Plaintext::Float(Some(x)) => x.to_ore(),
Plaintext::Int(Some(x)) => x.to_ore(),
Plaintext::NaiveDate(Some(x)) => x.num_days_from_ce().to_ore(),
Plaintext::SmallInt(Some(x)) => x.to_ore(),
Plaintext::Timestamp(_) => unimplemented!("Timestamp not yet supported by ORE"),
Plaintext::Utf8Str(_) => unimplemented!(),
_ => unimplemented!("Null values not supported by ORE"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::encryption::plaintext::Plaintext;
use chrono::NaiveDate;
#[test]
fn test_i64_preserve_ordering() {
assert!((-1i64).to_ore() < 100i64.to_ore());
assert!((10i64).to_ore() < 100i64.to_ore());
assert!((i64::MIN).to_ore() < i64::MAX.to_ore());
assert_eq!((0i64).to_ore(), 0i64.to_ore());
}
#[test]
fn test_naive_date_preserves_ordering() {
assert!(
(&Plaintext::from(NaiveDate::from_ymd_opt(2023, 2, 3).unwrap())).to_ore()
< (&Plaintext::from(NaiveDate::from_ymd_opt(2023, 2, 4).unwrap())).to_ore()
);
assert!(
(&Plaintext::from(NaiveDate::from_ymd_opt(2024, 2, 3).unwrap())).to_ore()
> (&Plaintext::from(NaiveDate::from_ymd_opt(2023, 2, 4).unwrap())).to_ore()
);
assert_eq!(
(&Plaintext::from(NaiveDate::from_ymd_opt(2024, 5, 5).unwrap())).to_ore(),
(&Plaintext::from(NaiveDate::from_ymd_opt(2024, 5, 5).unwrap())).to_ore()
);
}
}