Remove encoders WIP

This commit is contained in:
Rick Weber
2021-12-08 17:15:58 -08:00
parent f87bc9a473
commit ebc7fdb880
12 changed files with 476 additions and 82 deletions

View File

@@ -1,5 +1,10 @@
{
"files.associations": {
"iostream": "cpp"
"iostream": "cpp",
"__locale": "cpp",
"__string": "cpp",
"regex": "cpp",
"string": "cpp",
"string_view": "cpp"
}
}

17
Cargo.lock generated
View File

@@ -13,9 +13,9 @@ dependencies = [
[[package]]
name = "ansi_term"
version = "0.11.0"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
@@ -112,9 +112,9 @@ dependencies = [
[[package]]
name = "clap"
version = "2.33.3"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"ansi_term",
"atty",
@@ -465,6 +465,12 @@ dependencies = [
"serde_json",
]
[[package]]
name = "semver"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012"
[[package]]
name = "serde"
version = "1.0.130"
@@ -555,6 +561,7 @@ dependencies = [
name = "sunscreen_frontend_macros"
version = "0.1.0"
dependencies = [
"clap",
"proc-macro2",
"quote",
"serde_json",
@@ -571,7 +578,9 @@ dependencies = [
"log",
"petgraph",
"seal",
"semver",
"serde",
"serde_json",
"sunscreen_backend",
"sunscreen_circuit",
"sunscreen_runtime",

View File

@@ -8,7 +8,7 @@ use crate::bindgen::{
/**
* A type representing all errors that can occur in SEAL.
*/
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub enum Error {
/// No error
Ok,

View File

@@ -1,9 +1,13 @@
use std::ffi::{c_void, CString};
use std::ptr::null_mut;
use crate::bindgen;
use crate::error::*;
use crate::{bindgen, serialization::CompressionType, Context};
use serde::ser::Error;
use serde::{Serialize, Serializer};
#[derive(Debug)]
/**
* Class to store a plaintext element. The data for the plaintext is
* a polynomial with coefficients modulo the plaintext modulus. The degree
@@ -35,7 +39,75 @@ pub struct Plaintext {
unsafe impl Sync for Plaintext {}
unsafe impl Send for Plaintext {}
impl Clone for Plaintext {
fn clone(&self) -> Self {
let mut copy = null_mut();
convert_seal_error(unsafe { bindgen::Plaintext_Create5(self.handle, &mut copy) })
.expect("Internal error: Failed to copy plaintext.");
Self { handle: copy }
}
}
impl Serialize for Plaintext {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut num_bytes: i64 = 0;
convert_seal_error(unsafe {
bindgen::Plaintext_SaveSize(self.handle, CompressionType::ZStd as u8, &mut num_bytes)
})
.map_err(|e| {
S::Error::custom(format!("Failed to get secret key serialized size: {}", e))
})?;
let mut data: Vec<u8> = Vec::with_capacity(num_bytes as usize);
let mut bytes_written: i64 = 0;
convert_seal_error(unsafe {
let data_ptr = data.as_mut_ptr();
bindgen::Plaintext_Save(
self.handle,
data_ptr,
num_bytes as u64,
CompressionType::ZStd as u8,
&mut bytes_written,
)
})
.map_err(|e| S::Error::custom(format!("Failed to get secret key bytes: {}", e)))?;
unsafe { data.set_len(bytes_written as usize) };
serializer.serialize_bytes(&data)
}
}
impl Plaintext {
/**
* Deserializes a byte stream into a plaintext. This requires a context, which is why
* Plaintext doesn't `impl Deserialize`.
*/
pub fn from_bytes(&mut self, context: Context, data: &[u8]) -> Result<()> {
let mut bytes_read = 0;
convert_seal_error(unsafe {
// While the interface marks data as mut, SEAL doesn't actually modify it, so we're okay.
bindgen::Plaintext_Load(
self.handle,
context.get_handle(),
data.as_ptr() as *mut u8,
data.len() as u64,
&mut bytes_read,
)
})?;
Ok(())
}
/**
* Returns the handle to the underlying SEAL object.
*/
@@ -92,6 +164,68 @@ impl Plaintext {
Ok(Self { handle })
}
/**
* Gets the coefficient at the given location. Coefficients are ordered
* from lowest to highest degree, with the first value being the constant
* coefficient.
*
* # Panics
* Panics if index is greater than len().
*/
pub fn get_coefficient(&self, index: usize) -> u64 {
let mut coeff: u64 = 0;
if index > self.len() {
panic!("Index {} out of bounds {}", index, self.len());
}
convert_seal_error(unsafe {
bindgen::Plaintext_CoeffAt(self.handle, index as u64, &mut coeff)
})
.expect("Fatal error in Plaintext::index().");
coeff
}
/**
* Sets the coefficient at the given location. Coefficients are ordered
* from lowest to highest degree, with the first value being the constant
* coefficient.
*
* # Panics
* Panics if index is greater than len().
*/
pub fn set_coefficient(&mut self, index: usize, value: u64) {
if index > self.len() {
panic!("Index {} out of bounds {}", index, self.len());
}
convert_seal_error(unsafe {
bindgen::Plaintext_SetCoeffAt(self.handle, index as u64, value)
})
.expect("Fatal error in Plaintext::index().");
}
/**
* Sets the number of coefficients this plaintext can hold.
*/
pub fn resize(&mut self, count: usize) {
convert_seal_error(unsafe { bindgen::Plaintext_Reserve(self.handle, count as u64) })
.expect("Fatal error in Plaintext::index().");
}
/**
* Returns the number of coefficients this plaintext can hold.
*/
pub fn len(&self) -> usize {
let mut size: u64 = 0;
convert_seal_error(unsafe { bindgen::Plaintext_Capacity(self.handle, &mut size) })
.expect("Fatal error in Plaintext::index().");
size as usize
}
}
impl Drop for Plaintext {
@@ -186,4 +320,13 @@ mod tests {
std::mem::drop(plaintext);
}
#[test]
fn plaintext_coefficients_in_increasing_order() {
let plaintext = Plaintext::from_hex_string("1234x^2 + 4321").unwrap();
assert_eq!(plaintext.get_coefficient(0), 0x4321);
assert_eq!(plaintext.get_coefficient(1), 0);
assert_eq!(plaintext.get_coefficient(2), 0x1234);
}
}

View File

@@ -9,6 +9,7 @@ proc-macro = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clap = "2.34.0"
proc-macro2 = "1.0.32"
quote = "1.0.10"
syn = { version = "1.0.81", features = ["derive", "full", "fold"] }

View File

@@ -12,4 +12,8 @@ sunscreen_backend = { path = "../sunscreen_backend" }
sunscreen_circuit = { path = "../sunscreen_circuit" }
sunscreen_runtime = { path = "../sunscreen_runtime" }
seal = { path = "../seal" }
serde = { version = "1.0.130", features = ["derive"] }
serde = { version = "1.0.130", features = ["derive"] }
semver = "1.0.4"
[dev-dependencies]
serde_json = "1.0.72"

View File

@@ -18,6 +18,17 @@ pub enum Error {
* Attempted to compile the given circuit with the wrong scheme.
*/
IncorrectScheme,
/**
* An internal error occurred in the SEAL library.
*/
SealError(seal::Error),
}
impl From<seal::Error> for Error {
fn from(err: seal::Error) -> Self {
Self::SealError(err)
}
}
/**

View File

@@ -171,6 +171,24 @@ thread_local! {
pub static CURRENT_CTX: RefCell<Option<&'static mut Context>> = RefCell::new(None);
}
/**
* Runs the specified closure, injecting the current circuit context.
*/
pub fn with_ctx<F, R>(f: F) -> R
where
F: FnOnce(&mut Context) -> R,
{
CURRENT_CTX.with(|ctx| {
let mut option = ctx.borrow_mut();
let ctx = option
.as_mut()
.expect("Called Ciphertext::new() outside of a context.");
f(ctx)
})
}
impl Context {
/**
* Creates a new empty frontend intermediate representation context with the given scheme.

View File

@@ -1,31 +1,9 @@
use std::ops::{Add, Mul, Shl, Shr};
use petgraph::stable_graph::NodeIndex;
use serde::{Deserialize, Serialize};
use seal::Plaintext as SealPlaintext;
use crate::{Context, Literal, CURRENT_CTX};
#[derive(Clone, Copy, Serialize, Deserialize)]
struct U64LiteralRef {}
impl FheType for U64LiteralRef {}
impl BfvType for U64LiteralRef {}
impl U64LiteralRef {
pub fn new(val: u64) -> NodeIndex {
with_ctx(|ctx| ctx.add_literal(Literal::U64(val)))
}
}
/**
* Denotes the given rust type is an encoding in an FHE scheme
*/
pub trait FheType {}
/**
* Denotes the given type is valid under the [SchemeType::BFV](crate::SchemeType::Bfv).
*/
pub trait BfvType: FheType {}
use crate::{Context, CURRENT_CTX, Params, Result, types::{CircuitNode, FheType, BfvType, U64LiteralRef, TryIntoPlaintext}};
use sunscreen_runtime::{InnerPlaintext, Plaintext};
impl CircuitNode<Unsigned> {
/**
@@ -36,55 +14,13 @@ impl CircuitNode<Unsigned> {
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
/**
* A type that wraps an FheType during graph construction
*/
pub struct CircuitNode<T: FheType> {
/**
* The node's index
*/
pub id: NodeIndex,
_phantom: std::marker::PhantomData<T>,
}
impl<T: FheType> CircuitNode<T> {
/**
* Creates a new circuit node with the given node index.
*/
pub fn new(id: NodeIndex) -> Self {
Self {
id,
_phantom: std::marker::PhantomData,
}
}
/**
* Creates a new CircuitNode denoted as an input to a circuit graph.
*/
pub fn input() -> Self {
with_ctx(|ctx| Self::new(ctx.add_input()))
}
/**
* Denote this node as an output by appending an output circuit node.
*/
pub fn output(&self) -> Self {
with_ctx(|ctx| Self::new(ctx.add_output(self.id)))
}
}
#[derive(Clone, Copy)]
/**
* Represents a single unsigned integer encrypted as a ciphertext. Suitable for use
* as an input or output for a Sunscreen circuit.
*/
pub struct Unsigned {
/**
* The internal graph node id of this input or output.
*/
pub id: NodeIndex,
val: u64,
}
impl FheType for Unsigned {}
@@ -141,3 +77,38 @@ where
f(ctx)
})
}
impl TryIntoPlaintext for Unsigned {
fn try_into_plaintext(&self, params: &Params) -> Result<Plaintext> {
let mut seal_plaintext = SealPlaintext::new()?;
let bits = std::mem::size_of::<u64>() * 8;
seal_plaintext.resize(bits);
for i in 0..bits {
let bit_value = (self.val & 0x1 << i) >> i;
seal_plaintext.set_coefficient(i, bit_value);
}
Ok(Plaintext::new(InnerPlaintext::Seal(seal_plaintext), params.clone()))
}
}
impl From<u64> for Unsigned {
fn from(val: u64) -> Self {
Self { val }
}
}
#[cfg(test)]
mod tests
{
use super::*;
#[test]
fn can_convert_u64_to_unsigned() {
let foo: Unsigned = 64u64.into();
assert_eq!(foo.val, 64);
}
}

View File

@@ -0,0 +1,176 @@
mod integer;
use crate::{Literal, with_ctx, Params, Result};
use petgraph::stable_graph::NodeIndex;
use sunscreen_runtime::Plaintext;
use semver::Version;
use serde::{Deserialize, de::{self, Visitor}, Deserializer, Serialize, Serializer};
pub use integer::Unsigned;
/**
* Denotes the given rust type is an encoding in an FHE scheme
*/
pub trait FheType {}
/**
* Denotes the given type is valid under the [SchemeType::BFV](crate::SchemeType::Bfv).
*/
pub trait BfvType: FheType {}
#[derive(Clone, Copy, Serialize, Deserialize)]
/**
* A reference to a u64 literal in a circuit graph.
*/
pub struct U64LiteralRef {}
impl FheType for U64LiteralRef {}
impl BfvType for U64LiteralRef {}
impl U64LiteralRef {
/**
* Creates a reference to the given literal. If the given literal already exists in the current
* graph, a reference to the existing literal is returned.
*/
pub fn new(val: u64) -> NodeIndex {
with_ctx(|ctx| ctx.add_literal(Literal::U64(val)))
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
/**
* A type that wraps an FheType during graph construction
*/
pub struct CircuitNode<T: FheType> {
/**
* The node's index
*/
pub id: NodeIndex,
_phantom: std::marker::PhantomData<T>,
}
impl<T: FheType> CircuitNode<T> {
/**
* Creates a new circuit node with the given node index.
*/
pub fn new(id: NodeIndex) -> Self {
Self {
id,
_phantom: std::marker::PhantomData,
}
}
/**
* Creates a new CircuitNode denoted as an input to a circuit graph.
*/
pub fn input() -> Self {
with_ctx(|ctx| Self::new(ctx.add_input()))
}
/**
* Denote this node as an output by appending an output circuit node.
*/
pub fn output(&self) -> Self {
with_ctx(|ctx| Self::new(ctx.add_output(self.id)))
}
}
/**
* This trait denotes one may attempt to turn this type into a plaintext.
*/
pub trait TryIntoPlaintext {
/**
* Attempts to turn this type into a [`Plaintext`].
*/
fn try_into_plaintext(&self, params: &Params) -> Result<Plaintext>;
}
/**
* A type which represents the fully qualified name and version of a datatype.
*/
#[derive(Debug, Clone)]
pub struct TypeName {
/**
* The fully qualified name of the type (including crate name)
*/
pub name: String,
/**
* The semantic version of this type.
*/
pub version: Version,
}
impl Serialize for TypeName {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
let type_string = format!("{},{}", self.name, self.version);
serializer.serialize_str(&type_string)
}
}
struct TypeNameVisitor;
impl<'de> Visitor<'de> for TypeNameVisitor {
type Value = String;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "A string of the form foo::bar::Baz,1.2.3")
}
fn visit_str<E>(self, s: &str) -> std::result::Result<Self::Value, E>
where
E: de::Error,
{
if s.split(",").count() != 2 {
Err(de::Error::invalid_value(de::Unexpected::Str(s), &self))
} else {
Ok(s.to_owned())
}
}
}
impl <'de> Deserialize<'de> for TypeName {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let type_string = deserializer.deserialize_string(TypeNameVisitor)?;
let mut splits = type_string.split(",");
let typename = splits.next().unwrap();
let version = Version::parse(splits.next().unwrap()).map_err(|e| {
de::Error::custom(format!("Failed to parse version: {}", e))
})?;
Ok(Self {
name: typename.to_owned(),
version
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn can_serialize_deserialize_typename() {
let typename = TypeName {
name: "foo::Bar".to_owned(),
version: Version::new(42, 24, 6),
};
let serialized = serde_json::to_string(&typename).unwrap();
let deserialized: TypeName = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized.name, typename.name);
assert_eq!(deserialized.version, typename.version);
}
}

View File

@@ -27,6 +27,11 @@ pub enum Error {
* When attempting to run a circuit, the wrong number of ciphertexts were provided.
*/
IncorrectCiphertextCount,
/**
* An argument is incompatible with the parameters in the runtime.
*/
ParameterMismatch,
}
impl From<sunscreen_circuit::Error> for Error {

View File

@@ -19,7 +19,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
use seal::{
BFVEvaluator, BfvEncryptionParametersBuilder, Ciphertext, Context as SealContext, Decryptor,
Encryptor, Evaluator, GaloisKeys, KeyGenerator, Modulus, Plaintext, PublicKey,
Encryptor, Evaluator, GaloisKeys, KeyGenerator, Modulus, Plaintext as SealPlaintext, PublicKey,
RelinearizationKeys, SecretKey, SecurityLevel,
};
@@ -62,6 +62,11 @@ pub enum Runtime {
* This runtime is for the BFV scheme.
*/
Bfv {
/**
* The parameters used to construct the scheme used in this runtime.
*/
params: Params,
/**
* The context associated with the BFV scheme. Created by [`RuntimeBuilder`].
*/
@@ -154,13 +159,21 @@ impl Runtime {
/**
* Encrypts the given plaintext using the given public key.
*
* Returns [`Error::ParameterMismatch`] if the plaintext is incompatible with this runtime's
* scheme.
*/
pub fn encrypt(&self, p: &Plaintext, public_key: &PublicKey) -> Result<Ciphertext> {
let ciphertext = match self {
Self::Bfv { context, .. } => {
let encryptor = Encryptor::with_public_key(&context, public_key)?;
match &p.inner {
InnerPlaintext::Seal(p) => {
let encryptor = Encryptor::with_public_key(&context, public_key)?;
encryptor.encrypt(&p)?
encryptor.encrypt(&p)?
},
}
}
};
@@ -172,10 +185,15 @@ impl Runtime {
*/
pub fn decrypt(&self, c: &Ciphertext, secret_key: &SecretKey) -> Result<Plaintext> {
let plaintext = match self {
Self::Bfv { context, .. } => {
Self::Bfv { context, params } => {
let decryptor = Decryptor::new(&context, secret_key)?;
decryptor.decrypt(&c)?
let plaintext = decryptor.decrypt(&c)?;
Plaintext {
params: params.clone(),
inner: InnerPlaintext::Seal(plaintext)
}
}
};
@@ -183,6 +201,39 @@ impl Runtime {
}
}
#[derive(Debug, Serialize)]
/**
* The underlying backend implementation of a plaintext (e.g. SEAL's [`Plaintext`](seal::Plaintext)).
*/
pub enum InnerPlaintext {
/**
* This plaintext wraps a SEAL [`Plaintext`](seal::Plaintext).
*/
Seal(SealPlaintext),
}
#[derive(Debug, Serialize)]
/**
* Represents an encoded plaintext suitable for use in the underlying scheme.
*/
pub struct Plaintext {
params: Params,
inner: InnerPlaintext,
}
impl Plaintext {
/**
* Creates a new plaintext. Moves the given [`InnerPlaintext`] and [`Params`].
*/
pub fn new(inner: InnerPlaintext, params: Params) -> Self {
Self {
inner,
params
}
}
}
/**
* Used to construct a runtime.
*/
@@ -220,7 +271,7 @@ impl RuntimeBuilder {
let context = SealContext::new(&bfv_params, true, self.params.security_level)?;
Ok(Runtime::Bfv { context })
Ok(Runtime::Bfv { context, params: self.params })
}
_ => unimplemented!(),
}