Skip to content

Restructure and adding 5-bit block scheme #51

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 18 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 9 additions & 20 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
[package]
name = "ore-rs"
version = "0.7.0"
authors = ["Dan Draper <[email protected]>"]
edition = "2018"
homepage = "https://cipherstash.com"
description = "Order-revealing encryption library used by the CipherStash searchable encryption platform"
license-file = "LICENCE"
[workspace]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dev-dependencies]
criterion = "0.3.5"
quickcheck = "1.0.3"
members = [
"core",
"formats",
"primitives",
"ore-rs-5bit",
]

[dependencies]
[workspace.dependencies]
aes = { version = "0.8.2", features = ["zeroize"]}
block-modes = "0.8.1"
byteorder = "1.4.3"
Expand All @@ -25,10 +20,4 @@ subtle-ng = "2.5.0"
zeroize = { version = "1.5.7", features = [ "zeroize_derive", "alloc" ] }
lazy_static = "1.4.0"
thiserror = "1.0.38"

[[bench]]
name = "oreaes128"
harness = false

[[example]]
name = "encrypt"
quickcheck = "1.0.3"
37 changes: 37 additions & 0 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[package]
name = "ore-rs"
version = "0.7.0"
authors = ["Dan Draper <[email protected]>"]
edition = "2018"
homepage = "https://cipherstash.com"
description = "Order-revealing encryption library used by the CipherStash searchable encryption platform"
license-file = "LICENCE"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dev-dependencies]
criterion = "0.3.5"
quickcheck = "1.0.3"

[dependencies]
primitives = { path = "../primitives" }
aes = { version = "0.8.2", features = ["zeroize"]}
block-modes = "0.8.1"
byteorder = "1.4.3"
hex-literal = "0.3.2"
rand = "0.8.5"
rand_chacha = "0.3.1"
num = "0.4.0"
hex = "0.4.3"
subtle-ng = "2.5.0"
zeroize = { version = "1.5.7", features = [ "zeroize_derive", "alloc" ] }
lazy_static = "1.4.0"
thiserror = "1.0.38"



[[bench]]
name = "oreaes128"
harness = false

[[example]]
name = "encrypt"
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion src/ciphertext.rs → core/src/ciphertext.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use thiserror::Error;

use crate::primitives::NONCE_SIZE;
use primitives::NONCE_SIZE;
pub use crate::OreCipher;

/// The trait of any encryption output (either Left, Right or combined).
Expand Down
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion src/lib.rs → core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@
mod ciphertext;
mod convert;
mod encrypt;
mod primitives;
//mod primitives;
pub mod scheme;
pub use crate::ciphertext::*;
pub use crate::encrypt::OreEncrypt;
Expand Down
File renamed without changes.
8 changes: 4 additions & 4 deletions src/scheme/bit2.rs → core/src/scheme/bit2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
* Block ORE Implemenation using a 2-bit indicator function
*/

use primitives::{
hash::Aes128Z2Hash, prf::Aes128Prf, prp::KnuthShufflePRP, AesBlock, Hash, HashKey, Prf,
Prp, NONCE_SIZE,
};
use crate::{
ciphertext::*,
primitives::{
hash::Aes128Z2Hash, prf::Aes128Prf, prp::KnuthShufflePRP, AesBlock, Hash, HashKey, Prf,
Prp, NONCE_SIZE,
},
OreCipher, OreError, PlainText,
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use zeroize::Zeroize;

use crate::ciphertext::{CipherTextBlock, ParseError};
use crate::primitives::AesBlock;
use primitives::AesBlock;

pub type LeftBlock16 = AesBlock;

Expand Down
9 changes: 9 additions & 0 deletions formats/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "formats"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
subtle-ng = { workspace = true }
145 changes: 145 additions & 0 deletions formats/src/ciphertext/combined.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
use std::{marker::PhantomData, cmp::Ordering};
use crate::{data_with_header::{CtType, DataWithHeader}, header::Header, ParseError, LeftBlockEq, LeftCipherTextBlock, OreBlockOrd};
use super::{CipherTextBlock, CipherText, left::LeftCiphertext, right::RightCiphertext, RightCipherTextBlock};

#[derive(Debug)]
pub struct CombinedBlock<'a, L: CipherTextBlock<'a>, R: CipherTextBlock<'a>> {
pub left: L,
pub right: R,
_phantom: PhantomData<&'a L>
}

/// A combined ciphertext block also implements Right Block
impl<'a, L, R> RightCipherTextBlock<'a> for CombinedBlock<'a, L, R>
where L: LeftCipherTextBlock<'a>,
R: RightCipherTextBlock<'a>
{}

impl <'a, L: CipherTextBlock<'a>, R: CipherTextBlock<'a>> From<&'a [u8]> for CombinedBlock<'a, L, R> {
fn from(value: &'a [u8]) -> Self {
let left = L::from(&value[..L::byte_size()]);
let right = R::from(&value[L::byte_size()..]);
Self { left, right, _phantom: PhantomData }
}
}

impl<'a, L: CipherTextBlock<'a>, R: CipherTextBlock<'a>> CipherTextBlock<'a> for CombinedBlock<'a, L, R> {
fn byte_size() -> usize {
L::byte_size() + R::byte_size()
}

fn extend_into(&self, out: &mut DataWithHeader) {
todo!()
}
}

pub struct CombinedCiphertext<'a, L: LeftCipherTextBlock<'a>, R: RightCipherTextBlock<'a>> {
data: DataWithHeader,
_phantom: (PhantomData<&'a L>, PhantomData<&'a R>),
}


impl<'a, L, R> CombinedCiphertext<'a, L, R>
where
L: LeftCipherTextBlock<'a>,
R: RightCipherTextBlock<'a>
{
pub fn new(num_blocks: usize, nonce: &[u8; 16]) -> Self {
let hdr = Header::new(CtType::Combined, num_blocks);
let mut data = DataWithHeader::new(
hdr,
RightCiphertext::<'a, R>::NONCE_SIZE + (num_blocks * <Self as CipherText>::Block::byte_size())
);
data.extend_from_slice(nonce);
Self { data, _phantom: (PhantomData, PhantomData) }
}

// TODO: We should probably pass the args as references (same for left and right impls)
pub fn add_block(&mut self, left: L, right: R) {
left.extend_into(&mut self.data);
right.extend_into(&mut self.data);
}

pub fn nonce(&self) -> &[u8] {
&self.data.body()[..RightCiphertext::<'a, R>::NONCE_SIZE]
}
}

impl<'a, L, R> CipherText<'a> for CombinedCiphertext<'a, L, R>
where
L: LeftCipherTextBlock<'a>,
R: RightCipherTextBlock<'a>
{
type Block = CombinedBlock<'a, L, R>;

fn len(&self) -> usize {
self.data.len()
}

fn header(&self) -> Header {
self.data.header()
}

// TODO: This can go into the trait if we add a body method
// Right is different though because we have the nonce!
fn blocks(&'a self) -> Box<dyn Iterator<Item=Self::Block> + 'a> {
Box::new(
self.data.body()[RightCiphertext::<'a, R>::NONCE_SIZE..]
.chunks(Self::Block::byte_size())
.map(|bytes| Self::Block::from(bytes))
)
}
}


impl<'a, L, R> TryFrom<&'a [u8]> for CombinedCiphertext<'a, L, R>
where
L: LeftCipherTextBlock<'a>,
R: RightCipherTextBlock<'a>
{
type Error = ParseError;

fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> {
let hdr = Header::from_slice(data);
if matches!(hdr.ct_type, CtType::Combined) {
Ok(Self { data: data.into(), _phantom: (PhantomData, PhantomData) })
} else {
Err(ParseError { })
}
}
}

impl<'a, L, R> AsRef<[u8]> for CombinedCiphertext<'a, L, R>
where
L: LeftCipherTextBlock<'a>,
R: RightCipherTextBlock<'a>
{
fn as_ref(&self) -> &[u8] {
self.data.as_ref()
}
}

/// Blanket implementation to compare a left block to the left of any combined block
impl<'a, L, R> LeftBlockEq<'a, CombinedBlock<'a, L, R>> for L
where
L: LeftCipherTextBlock<'a>,
R: RightCipherTextBlock<'a>
{
fn constant_eq(&self, other: &CombinedBlock<'a, L, R>) -> subtle_ng::Choice {
self.constant_eq(&other.left)
}
}

/// Blanket implementation for a left block to Ore compare to the right block
/// of a combined block.
impl<'a, L, R> OreBlockOrd<'a, CombinedBlock<'a, L, R>> for L
where
L: LeftCipherTextBlock<'a>,
R: RightCipherTextBlock<'a>,
L: OreBlockOrd<'a, R>
{
fn ore_compare(&self, nonce: &[u8], other: &CombinedBlock<'a, L, R>) -> i8 {
self.ore_compare(nonce, &other.right)
}
}

91 changes: 91 additions & 0 deletions formats/src/ciphertext/left.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use std::{marker::PhantomData, ops::{BitOr, BitAnd}, cmp::Ordering};
use subtle_ng::{Choice, CtOption, ConditionallySelectable};

use crate::{data_with_header::{CtType, DataWithHeader}, header::Header, ParseError, LeftCipherTextBlock, OreBlockOrd, RightCipherTextBlock};
use super::{CipherTextBlock, CipherText, LeftBlockEq};

pub struct LeftCiphertext<'a, B: LeftCipherTextBlock<'a>> {
pub(crate) data: DataWithHeader,
_phantom: PhantomData<&'a B>,
}

impl<'a, B: LeftCipherTextBlock<'a>> LeftCiphertext<'a, B> {
pub fn new(num_blocks: usize) -> Self {
let hdr = Header::new(CtType::Left, num_blocks);

Self {
data: DataWithHeader::new(hdr, num_blocks * <Self as CipherText>::Block::byte_size()),
_phantom: PhantomData
}
}

pub fn add_block(&mut self, block: B) {
block.extend_into(&mut self.data);
}

/// Compare all the blocks of self with all the blocks in the given iterator, up to the `n`
/// where `n` is the length of the shorter iterator.
/// The ordering mechanism is important here, too (i.e. Lexicographic or Numerical)
/// If its numerical then the shorter value is always less than the other.
pub fn compare_blocks<O>(&'a self, nonce: &[u8], other: Box<dyn Iterator<Item=O> + 'a>) -> i8
where
B: LeftBlockEq<'a, O> + OreBlockOrd<'a, O>,
O: RightCipherTextBlock<'a>
{
let mut ai = self.blocks();
let mut bi = other; // TODO: Don't pass an iterator to this func, pass an impl CipherText

// TODO: Perhaps the LeftBlock could define the whole comparison (rather than splitting Eq and Ord like this)
let mut result: CtOption<i8> = CtOption::new(0, Choice::from(0));

loop {
match (ai.next(), bi.next()) {
(None, None) => return result.unwrap_or(0),
(Some(_), None) => return result.unwrap_or(1),
(None, Some(_)) => return result.unwrap_or(-1),
(Some(x), Some(y)) => {
// Always do the ore compare (even though we don't need to) to keep timing constant.
// Then assign the comparison to result if result is None and the left values are not equal.
let comparison = CtOption::new(x.ore_compare(nonce, &y), Choice::from(1));
result.conditional_assign(&comparison, result.is_none().bitand(!x.constant_eq(&y)));
}
}
}
}
}

impl<'a, B: LeftCipherTextBlock<'a>> CipherText<'a> for LeftCiphertext<'a, B> {
type Block = B;

fn len(&self) -> usize {
self.data.len()
}

fn header(&self) -> Header {
self.data.header()
}

fn blocks(&'a self) -> Box<dyn Iterator<Item=Self::Block> + 'a> {
// TODO: Should we assert that length is a multiple of the block size?
Box::new(self.data.body().chunks(Self::Block::byte_size()).map(|bytes| B::from(bytes)))
}
}

impl<'a, B: LeftCipherTextBlock<'a>> TryFrom<&'a [u8]> for LeftCiphertext<'a, B> {
type Error = ParseError;

fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> {
let hdr = Header::from_slice(data);
if matches!(hdr.ct_type, CtType::Left) {
Ok(Self { data: data.into(), _phantom: PhantomData })
} else {
Err(ParseError { })
}
}
}

impl<'a, B: LeftCipherTextBlock<'a>> AsRef<[u8]> for LeftCiphertext<'a, B> {
fn as_ref(&self) -> &[u8] {
self.data.as_ref()
}
}
Loading