impl HeContext {
pub fn new(security_level: SecurityLevel) -> Result<Self> {
let mut params = HeParameters::default_128bit();
params.security_level = security_level;
params.validate()?;
Ok(Self { params })
}
pub fn with_params(params: HeParameters) -> Result<Self> {
params.validate()?;
Ok(Self { params })
}
#[must_use]
pub const fn params(&self) -> &HeParameters {
&self.params
}
pub fn generate_keys(&self) -> Result<(HePublicKey, HeSecretKey)> {
let pk_size = match self.params.security_level {
SecurityLevel::Bit128 => 1_600_000, SecurityLevel::Bit192 => 3_200_000,
SecurityLevel::Bit256 => 6_400_000,
};
let public_key = HePublicKey {
data: vec![0u8; pk_size],
params: self.params.clone(),
};
let secret_key = HeSecretKey {
data: vec![0u8; 32], params: self.params.clone(),
};
Ok((public_key, secret_key))
}
pub fn generate_relin_keys(&self, _secret_key: &HeSecretKey) -> Result<HeRelinKeys> {
Ok(HeRelinKeys {
data: vec![0u8; 1024], })
}
pub fn generate_galois_keys(&self, _secret_key: &HeSecretKey) -> Result<HeGaloisKeys> {
Ok(HeGaloisKeys {
data: vec![0u8; 1024], })
}
pub fn encrypt_f64(&self, values: &[f64], public_key: &HePublicKey) -> Result<Ciphertext> {
self.validate_key_params(public_key)?;
let data: Vec<u8> = values.iter().flat_map(|v| v.to_le_bytes()).collect();
Ok(Ciphertext::new(data, HeScheme::Ckks))
}
pub fn encrypt_u64(&self, values: &[u64], public_key: &HePublicKey) -> Result<Ciphertext> {
self.validate_key_params(public_key)?;
let data: Vec<u8> = values.iter().flat_map(|v| v.to_le_bytes()).collect();
Ok(Ciphertext::new(data, HeScheme::Bfv))
}
pub fn decrypt_f64(
&self,
ciphertext: &Ciphertext,
secret_key: &HeSecretKey,
) -> Result<Vec<f64>> {
self.validate_key_params_secret(secret_key)?;
if ciphertext.scheme != HeScheme::Ckks {
return Err(AprenderError::FormatError {
message: format!(
"Cannot decrypt {:?} ciphertext as f64 (requires CKKS)",
ciphertext.scheme
),
});
}
let values: Vec<f64> = ciphertext
.data
.chunks_exact(8)
.map(|chunk| f64::from_le_bytes(chunk.try_into().unwrap_or([0; 8])))
.collect();
Ok(values)
}
pub fn decrypt_u64(
&self,
ciphertext: &Ciphertext,
secret_key: &HeSecretKey,
) -> Result<Vec<u64>> {
self.validate_key_params_secret(secret_key)?;
if ciphertext.scheme != HeScheme::Bfv {
return Err(AprenderError::FormatError {
message: format!(
"Cannot decrypt {:?} ciphertext as u64 (requires BFV)",
ciphertext.scheme
),
});
}
let values: Vec<u64> = ciphertext
.data
.chunks_exact(8)
.map(|chunk| u64::from_le_bytes(chunk.try_into().unwrap_or([0; 8])))
.collect();
Ok(values)
}
pub fn add(&self, a: &Ciphertext, b: &Ciphertext) -> Result<Ciphertext> {
if a.scheme != b.scheme {
return Err(AprenderError::FormatError {
message: format!(
"Cannot add ciphertexts with different schemes: {:?} vs {:?}",
a.scheme, b.scheme
),
});
}
let mut data = a.data.clone();
data.extend_from_slice(&b.data);
Ok(Ciphertext {
data,
scheme: a.scheme,
level: a.level.max(b.level),
})
}
pub fn multiply(
&self,
a: &Ciphertext,
b: &Ciphertext,
_relin_keys: &HeRelinKeys,
) -> Result<Ciphertext> {
if a.scheme != b.scheme {
return Err(AprenderError::FormatError {
message: format!(
"Cannot multiply ciphertexts with different schemes: {:?} vs {:?}",
a.scheme, b.scheme
),
});
}
let new_level = a.level.saturating_add(1).max(b.level.saturating_add(1));
let max_level = u8::try_from(self.params.coeff_modulus_bits.len()).unwrap_or(4);
if new_level >= max_level {
return Err(AprenderError::FormatError {
message: format!(
"Multiplication depth exceeded: level {new_level} >= max {max_level}"
),
});
}
Ok(Ciphertext {
data: a.data.clone(),
scheme: a.scheme,
level: new_level,
})
}
fn validate_key_params(&self, public_key: &HePublicKey) -> Result<()> {
if public_key.params.security_level != self.params.security_level {
return Err(AprenderError::FormatError {
message: "Public key security level doesn't match context".to_string(),
});
}
Ok(())
}
fn validate_key_params_secret(&self, secret_key: &HeSecretKey) -> Result<()> {
if secret_key.params.security_level != self.params.security_level {
return Err(AprenderError::FormatError {
message: "Secret key security level doesn't match context".to_string(),
});
}
Ok(())
}
}