Skip to content
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

ScrambleDB WASM demo fix / ElGamal based Rerandomizable encryption #80

Draft
wants to merge 3 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
3 changes: 2 additions & 1 deletion atlas-spec/scrambledb/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ rand = { version = "0.8.5", optional = true }
getrandom = { version = "0.2.10", features = ["js"], optional = true }
hex = { version = "0.4.3", optional = true }

libcrux = { git = "https://github.com/cryspen/libcrux.git" }
libcrux = { git = "https://github.com/cryspen/libcrux.git", optional = true }

gloo-utils = { version = "0.1", features = ["serde"] }
serde_json.workspace = true
Expand All @@ -42,6 +42,7 @@ features = [

[features]
wasm = ["wasm-bindgen", "getrandom", "web-sys", "rand", "hex"]
double-hpke = ["dep:libcrux"]

[dev-dependencies]
scrambledb = { path = ".", features = ["rand"] }
67 changes: 11 additions & 56 deletions atlas-spec/scrambledb/src/data_transformations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::{
setup::{StoreContext, StoreEncryptionKey},
};

pub(crate) mod double_hpke;
pub(crate) mod data_encryption;

/// CoPRF context string for domain separation of intial pseudonymization.
const PSEUDONYMIZATION_CONTEXT: &[u8] = b"CoPRF-Context-Pseudonymization";
Expand Down Expand Up @@ -52,32 +52,15 @@ pub fn blind_identifiable_datum(
)?);

// Encrypt data value towards receiver.
let encrypted_data_value = encrypt_data_value(&datum.data_value, ek, randomness)?;
let encrypted_data_value =
data_encryption::encrypt_data_value(&datum.data_value, ek, randomness)?;

Ok(BlindedIdentifiableData {
blinded_handle,
encrypted_data_value,
})
}

/// Encrypt a data value towards a data store.
///
/// Inputs:
/// - `data`: The data value to encrypt.
/// - `ek`: The receiver's public encryption key.
/// - `randomness`: Random bytes
///
/// Output:
/// A new [EncryptedDataValue], the encryption of `data`.
fn encrypt_data_value(
data: &DataValue,
ek: &StoreEncryptionKey,
randomness: &mut Randomness,
) -> Result<EncryptedDataValue, Error> {
let encrypted_data_value = double_hpke::hpke_seal_level_1(data, &ek.0, randomness)?;
Ok(encrypted_data_value)
}

/// Blind a pseudonymous datum as a first step in pseudonym
/// conversion.
///
Expand Down Expand Up @@ -108,7 +91,8 @@ pub fn blind_pseudonymized_datum(
)?);

// Encrypt data value towards receiver.
let encrypted_data_value = encrypt_data_value(&datum.data_value, ek, randomness)?;
let encrypted_data_value =
data_encryption::encrypt_data_value(&datum.data_value, ek, randomness)?;

Ok(BlindedPseudonymizedData {
blinded_handle,
Expand Down Expand Up @@ -150,31 +134,15 @@ pub fn pseudonymize_blinded_datum(
)?);

// Rerandomize encrypted data value towards receiver.
let encrypted_data_value = rerandomize_encryption(&datum.encrypted_data_value, ek, randomness)?;
let encrypted_data_value =
data_encryption::rerandomize_encryption(&datum.encrypted_data_value, ek, randomness)?;

Ok(BlindedPseudonymizedData {
blinded_handle,
encrypted_data_value,
})
}

/// Rerandomize the encryption of an encrypted data value.
///
/// Inputs:
/// - `data`: The encrypted data value.
/// - `ek`: The receiver's public encryption key.
/// - `randomness`: Random bytes
///
/// Output:
/// A new, rerandomized [EncryptedDataValue].
fn rerandomize_encryption(
data: &EncryptedDataValue,
ek: &StoreEncryptionKey,
randomness: &mut Randomness,
) -> Result<EncryptedDataValue, Error> {
double_hpke::hpke_seal_level_2(data, &ek.0, randomness)
}

/// Obliviously convert a blinded pseudonymous datum to a given target pseudonym key.
///
/// Inputs:
Expand Down Expand Up @@ -215,7 +183,8 @@ pub fn convert_blinded_datum(
)?);

// Rerandomize encrypted data value towards receiver.
let encrypted_data_value = rerandomize_encryption(&datum.encrypted_data_value, ek, randomness)?;
let encrypted_data_value =
data_encryption::rerandomize_encryption(&datum.encrypted_data_value, ek, randomness)?;

Ok(BlindedPseudonymizedData {
blinded_handle,
Expand Down Expand Up @@ -244,22 +213,8 @@ pub fn finalize_blinded_datum(
let handle = store_context.finalize_pseudonym(datum.blinded_handle)?;

// Decrypt data value for storage.
let data_value = decrypt_data_value(&datum.encrypted_data_value, store_context)?;
let data_value =
data_encryption::decrypt_data_value(&datum.encrypted_data_value, store_context)?;

Ok(PseudonymizedData { handle, data_value })
}

/// Decrypt an encrypted data value.
///
/// Inputs:
/// - `data`: The value to decrypt.
/// - `store_context`: The data store's long term private state, including in particular its decryption key.
///
/// Output:
/// The decrypted [DataValue] or an [Error] on decryption failure.
fn decrypt_data_value(
data: &EncryptedDataValue,
store_context: &StoreContext,
) -> Result<DataValue, Error> {
double_hpke::hpke_open_level_2(data, &store_context.dk.0)
}
116 changes: 116 additions & 0 deletions atlas-spec/scrambledb/src/data_transformations/data_encryption.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#[cfg(feature = "double-hpke")]
pub(crate) mod double_hpke;

#[cfg(not(feature = "double-hpke"))]
pub(crate) mod elgamal;

use crate::data_types::{DataValue, EncryptedDataValue};
use crate::error::Error;
use crate::setup::{StoreContext, StoreEncryptionKey};
use hacspec_lib::Randomness;

/// Encrypt a data value towards a data store.
///
/// Inputs:
/// - `data`: The data value to encrypt.
/// - `ek`: The receiver's public encryption key.
/// - `randomness`: Random bytes
///
/// Output:
/// A new [EncryptedDataValue], the encryption of `data`.
#[cfg(feature = "double-hpke")]
pub(crate) fn encrypt_data_value(
data: &DataValue,
ek: &StoreEncryptionKey,
randomness: &mut Randomness,
) -> Result<EncryptedDataValue, Error> {
let encrypted_data_value = double_hpke::hpke_seal_level_1(data, &ek.0, randomness)?;
Ok(encrypted_data_value)
}

/// Encrypt a data value towards a data store.
///
/// Inputs:
/// - `data`: The data value to encrypt.
/// - `ek`: The receiver's public encryption key.
/// - `randomness`: Random bytes
///
/// Output:
/// A new [EncryptedDataValue], the encryption of `data`.
#[cfg(not(feature = "double-hpke"))]
pub(crate) fn encrypt_data_value(
data: &DataValue,
ek: &StoreEncryptionKey,
randomness: &mut Randomness,
) -> Result<EncryptedDataValue, Error> {
let encrypted_data_value = elgamal::encrypt(data, ek, randomness)?;
Ok(encrypted_data_value)
}

/// Rerandomize the encryption of an encrypted data value.
///
/// Inputs:
/// - `data`: The encrypted data value.
/// - `ek`: The receiver's public encryption key.
/// - `randomness`: Random bytes
///
/// Output:
/// A new, rerandomized [EncryptedDataValue].
#[cfg(feature = "double-hpke")]
pub(crate) fn rerandomize_encryption(
data: &EncryptedDataValue,
ek: &StoreEncryptionKey,
randomness: &mut Randomness,
) -> Result<EncryptedDataValue, Error> {
double_hpke::hpke_seal_level_2(data, &ek.0, randomness)
}

/// Rerandomize the encryption of an encrypted data value.
///
/// Inputs:
/// - `data`: The encrypted data value.
/// - `ek`: The receiver's public encryption key.
/// - `randomness`: Random bytes
///
/// Output:
/// A new, rerandomized [EncryptedDataValue].
#[cfg(not(feature = "double-hpke"))]
pub(crate) fn rerandomize_encryption(
data: &EncryptedDataValue,
ek: &StoreEncryptionKey,
randomness: &mut Randomness,
) -> Result<EncryptedDataValue, Error> {
elgamal::rerandomize(data, ek, randomness)
}

/// Decrypt an encrypted data value.
///
/// Inputs:
/// - `data`: The value to decrypt.
/// - `store_context`: The data store's long term private state, including in particular its decryption key.
///
/// Output:
/// The decrypted [DataValue] or an [Error] on decryption failure.
#[cfg(feature = "double-hpke")]
pub(crate) fn decrypt_data_value(
data: &EncryptedDataValue,
store_context: &StoreContext,
) -> Result<DataValue, Error> {
double_hpke::hpke_open_level_2(data, &store_context.dk.0)
}

/// Decrypt an encrypted data value.
///
/// Inputs:
/// - `data`: The value to decrypt.
/// - `store_context`: The data store's long term private state, including in particular its decryption key.
///
/// Output:
/// The decrypted [DataValue] or an [Error] on decryption failure.
#[cfg(not(feature = "double-hpke"))]
pub(crate) fn decrypt_data_value(
data: &EncryptedDataValue,
store_context: &StoreContext,
) -> Result<DataValue, Error> {
elgamal::decrypt(data, &store_context.dk)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use hacspec_lib::hacspec_helper::NatMod;
use hacspec_lib::Randomness;
use p256::{P256Point, P256Scalar};

use crate::data_types::{DataValue, EncryptedDataValue};
use crate::error::Error;
use crate::setup::{StoreDecryptionKey, StoreEncryptionKey};

fn encode_byte(byte: &u8) -> P256Point {
let mut scalar_encoding = P256Scalar::one();
for _i in 0..*byte {
scalar_encoding = scalar_encoding.fadd(P256Scalar::one());
}
p256::p256_point_mul_base(scalar_encoding).unwrap()
}

fn decode_point(point: P256Point) -> u8 {
let mut byte_candidate = 0u8;
let mut candidate_encoding = P256Scalar::one();
for _i in 0..256 {
let candidate_point = p256::p256_point_mul_base(candidate_encoding).unwrap();
if candidate_point == point {
break;
}
candidate_encoding = candidate_encoding.fadd(P256Scalar::one());
byte_candidate += 1;
}
byte_candidate
}

pub(crate) fn encrypt(
data: &DataValue,
ek: &StoreEncryptionKey,
randomness: &mut Randomness,
) -> Result<EncryptedDataValue, Error> {
let encoded_data = data.value.iter().map(encode_byte);
let encrypted_data: Vec<(P256Point, P256Point)> = encoded_data
.map(|p| elgamal::encrypt(ek.0, p, randomness).unwrap())
.collect();
let encryted_data_value = EncryptedDataValue {
attribute_name: data.attribute_name.clone(),
value: encrypted_data,
};
Ok(encryted_data_value)
}

pub(crate) fn decrypt(
data: &EncryptedDataValue,
dk: &StoreDecryptionKey,
) -> Result<DataValue, Error> {
let decrypted_data = data
.value
.iter()
.map(|c| elgamal::decrypt(dk.0, *c).unwrap());
let decoded_data = decrypted_data.map(decode_point).collect();
Ok(DataValue {
attribute_name: data.attribute_name.clone(),
value: decoded_data,
})
}

pub(crate) fn rerandomize(
data: &EncryptedDataValue,
ek: &StoreEncryptionKey,
randomness: &mut Randomness,
) -> Result<EncryptedDataValue, Error> {
let rerandomized_data = data
.value
.iter()
.map(|c| elgamal::rerandomize(ek.0, *c, randomness).unwrap())
.collect();
Ok(EncryptedDataValue {
attribute_name: data.attribute_name.clone(),
value: rerandomized_data,
})
}

#[test]
fn encode_decode() {
assert_eq!(1u8, decode_point(encode_byte(&1u8)));
assert_eq!(17u8, decode_point(encode_byte(&17u8)));
assert_eq!(0u8, decode_point(encode_byte(&0u8)));
assert_eq!(u8::MAX, decode_point(encode_byte(&u8::MAX)));
}
14 changes: 14 additions & 0 deletions atlas-spec/scrambledb/src/data_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
//! name of the attribute they belong to in plain text.

use oprf::coprf::coprf_online::{BlindInput, BlindOutput};
#[cfg(not(feature = "double-hpke"))]
use p256::P256Point;

/// A type for finalized pseudonyms, i.e. those which have been hardened for
/// storage by applying a PRP.
Expand All @@ -28,7 +30,9 @@ pub struct DataValue {
/// The name of the attribute the value belongs to.
pub(crate) attribute_name: String,
}

/// An encrypted data value.
#[cfg(feature = "double-hpke")]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct EncryptedDataValue {
/// A byte string encoding the encrypted data value.
Expand All @@ -39,6 +43,16 @@ pub struct EncryptedDataValue {
pub(crate) encryption_level: u8,
}

/// An encrypted data value.
#[cfg(not(feature = "double-hpke"))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct EncryptedDataValue {
/// A vector of ElGamal ciphertexts encoding the encrypted data value.
pub(crate) value: Vec<(P256Point, P256Point)>,
/// The name of the attribute the value belongs to.
pub(crate) attribute_name: String,
}

/// An identifiable piece of data.
///
/// `PartialOrd` derive:
Expand Down
1 change: 1 addition & 0 deletions atlas-spec/scrambledb/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ impl From<oprf::Error> for Error {
}
}

#[cfg(feature = "double-hpke")]
impl From<libcrux::hpke::errors::HpkeError> for Error {
fn from(_value: libcrux::hpke::errors::HpkeError) -> Self {
Self::CorruptedData
Expand Down
Loading
Loading