Skip to content

Commit

Permalink
refactor(integer): reorganize concrete-integer
Browse files Browse the repository at this point in the history
This commit reorganize concrete-integer's source tree
as well as it does a few improvements of the API.

The improvements are:
 - Creation of a Crt and Radix ciphertext types for improved type safety
 - Creation of Wrapper RadixClientKey and CrtClientKey
 - add gen_keys_crt and gen_keys_radix and make them use the
   internal-keycache to speedup doctests runs.

BREAKING_CHANGE: Ciphertext type does not exists anymore as there are
		 now two 3 ciphertext types.
  • Loading branch information
tmontaigu committed Sep 23, 2022
1 parent ec6d786 commit fa5dcb9
Show file tree
Hide file tree
Showing 57 changed files with 3,030 additions and 2,821 deletions.
132 changes: 62 additions & 70 deletions concrete-integer/benches/bench.rs

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions concrete-integer/docs/getting_started/first_circuit.md
Expand Up @@ -35,13 +35,13 @@ by using **4** shortint blocks that store **2** bits of message each.


```rust
use concrete_integer::gen_keys;
use concrete_integer::gen_keys_radix;
use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;

fn main() {
// We generate a set of client/server keys, using the default parameters:
let num_block = 4;
let (client_key, server_key) = gen_keys(&PARAM_MESSAGE_2_CARRY_2, num_block);
let (client_key, server_key) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, num_block);
}
```

Expand All @@ -53,13 +53,13 @@ fn main() {
Once we have our keys we can encrypt values:

```rust
use concrete_integer::gen_keys;
use concrete_integer::gen_keys_radix;
use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;

fn main() {
// We generate a set of client/server keys, using the default parameters:
let num_block = 4;
let (client_key, server_key) = gen_keys(&PARAM_MESSAGE_2_CARRY_2, num_block);
let (client_key, server_key) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, num_block);

let msg1 = 128;
let msg2 = 13;
Expand All @@ -76,13 +76,13 @@ With our `server_key`, and encrypted values, we can now do an addition
and then decrypt the result.

```rust
use concrete_integer::gen_keys;
use concrete_integer::gen_keys_radix;
use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;

fn main() {
// We generate a set of client/server keys, using the default parameters:
let num_block = 4;
let (client_key, server_key) = gen_keys(&PARAM_MESSAGE_2_CARRY_2, num_block);
let (client_key, server_key) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, num_block);

let msg1 = 128;
let msg2 = 13;
Expand Down
4 changes: 2 additions & 2 deletions concrete-integer/docs/getting_started/operation_types.md
Expand Up @@ -19,13 +19,13 @@ the end, an Integer ciphertext is defined as a set of Shortint ciphertexts.
In practice, the definition of an Integer requires the basis and the number of blocks. This is
done at the key creation step.
```rust
use concrete_integer::gen_keys;
use concrete_integer::gen_keys_radix;
use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;

fn main() {
// We generate a set of client/server keys, using the default parameters:
let num_block = 4;
let (client_key, server_key) = gen_keys(&PARAM_MESSAGE_2_CARRY_2, num_block);
let (client_key, server_key) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, num_block);
}
```

Expand Down
4 changes: 2 additions & 2 deletions concrete-integer/docs/how_to/pbs.md
Expand Up @@ -13,14 +13,14 @@ The `tree pbs` is quite slow, therefore its use is currently restricted to two a
{% endhint %}

```rust
use concrete_integer::gen_keys;
use concrete_integer::gen_keys_radix;
use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
use concrete_integer::treepbs::TreepbsKey;

fn main() {
let num_block = 2;
// Generate the client key and the server key:
let (cks, sks) = gen_keys(&PARAM_MESSAGE_2_CARRY_2, num_block);
let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, num_block);

let msg: u64 = 27;
let ct = cks.encrypt(msg);
Expand Down
12 changes: 6 additions & 6 deletions concrete-integer/docs/tutorials/circuit_evaluation.md
Expand Up @@ -8,12 +8,12 @@ As an example, let's do a scalar multiplication, a subtraction and an addition.


```rust
use concrete_integer::gen_keys;
use concrete_integer::gen_keys_radix;
use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;

fn main() {
let num_block = 4;
let (client_key, server_key) = gen_keys(&PARAM_MESSAGE_2_CARRY_2, num_block);
let (client_key, server_key) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, num_block);

let msg1 = 12;
let msg2 = 11;
Expand Down Expand Up @@ -47,12 +47,12 @@ may be incorrect.
If we redo this same circuit but using the `checked` flavour, a panic will occur.

```rust
use concrete_integer::gen_keys;
use concrete_integer::gen_keys_radix;
use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;

fn main() {
let num_block = 2;
let (client_key, server_key) = gen_keys(&PARAM_MESSAGE_2_CARRY_2, num_block);
let (client_key, server_key) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, num_block);

let msg1 = 12;
let msg2 = 11;
Expand Down Expand Up @@ -87,12 +87,12 @@ Lastly, using the `smart` flavour will output the correct result all the time. H
as the carry buffer may be propagated during the computations.

```rust
use concrete_integer::gen_keys;
use concrete_integer::gen_keys_radix;
use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;

fn main() {
let num_block = 4;
let (client_key, server_key) = gen_keys(&PARAM_MESSAGE_2_CARRY_2, num_block);
let (client_key, server_key) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, num_block);

let msg1 = 12;
let msg2 = 11;
Expand Down
10 changes: 5 additions & 5 deletions concrete-integer/docs/tutorials/serialization.md
Expand Up @@ -25,14 +25,14 @@ bincode = "1.3.3"
use bincode;

use std::io::Cursor;
use concrete_integer::{gen_keys, ServerKey, Ciphertext};
use concrete_integer::{gen_keys_radix, ServerKey, RadixCiphertext};
use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;


fn main() -> Result<(), Box<dyn std::error::Error>> {
// We generate a set of client/server keys, using the default parameters:
let num_block = 4;
let (client_key, server_key) = gen_keys(&PARAM_MESSAGE_2_CARRY_2, num_block);
let (client_key, server_key) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, num_block);

let msg1 = 201;
let msg2 = 12;
Expand All @@ -51,7 +51,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// Simulate sending serialized data to a server and getting
// back the serialized result
let serialized_result = server_function(&serialized_data)?;
let result: Ciphertext = bincode::deserialize(&serialized_result)?;
let result: RadixCiphertext = bincode::deserialize(&serialized_result)?;

let output = client_key.decrypt(&result);
assert_eq!(output, (msg1 + msg2) % modulus);
Expand All @@ -62,8 +62,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
fn server_function(serialized_data: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let mut serialized_data = Cursor::new(serialized_data);
let server_key: ServerKey = bincode::deserialize_from(&mut serialized_data)?;
let ct_1: Ciphertext = bincode::deserialize_from(&mut serialized_data)?;
let ct_2: Ciphertext = bincode::deserialize_from(&mut serialized_data)?;
let ct_1: RadixCiphertext = bincode::deserialize_from(&mut serialized_data)?;
let ct_2: RadixCiphertext = bincode::deserialize_from(&mut serialized_data)?;

let result = server_key.unchecked_add(&ct_1, &ct_2);

Expand Down
40 changes: 28 additions & 12 deletions concrete-integer/src/ciphertext/mod.rs
@@ -1,26 +1,42 @@
//! This module implements the ciphertext structure containing an encryption of an integer message.
//! This module implements the ciphertext structures.
use concrete_shortint;
use serde::{Deserialize, Serialize};

/// Id to recognize the key used to encrypt a block.
#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)]
pub struct KeyId(pub usize);

/// A structure containing a ciphertext, meant to encrypt an (large) integer message.
/// It is used to evaluate a integer circuits homomorphically.
/// Structure containing a ciphertext in radix decomposition.
#[derive(Serialize, Clone, Deserialize)]
pub struct Ciphertext {
pub(crate) ct_vec: Vec<concrete_shortint::ciphertext::Ciphertext>,
pub(crate) message_modulus_vec: Vec<u64>,

// KeyId used to identify the encryption key of a bloc
// (mainly used in the CRT)
pub(crate) key_id_vec: Vec<KeyId>,
pub struct RadixCiphertext {
/// The blocks are stored from LSB to MSB
pub(crate) blocks: Vec<concrete_shortint::Ciphertext>,
}

impl Ciphertext {
impl RadixCiphertext {
/// Returns the slice of blocks that the ciphertext is composed of.
pub fn blocks(&self) -> &[concrete_shortint::Ciphertext] {
&self.ct_vec
&self.blocks
}
}

/// Structure containing a ciphertext in CRT decomposition.
///
/// For this CRT decomposition, each block is encrypted using
/// the same parameters.
#[derive(Serialize, Clone, Deserialize)]
pub struct CrtCiphertext {
pub(crate) blocks: Vec<concrete_shortint::Ciphertext>,
pub(crate) moduli: Vec<u64>,
}

/// Structure containing a ciphertext in CRT decomposition.
///
/// For this CRT decomposition, not all blocks
/// are encrypted using the same parameters
#[derive(Serialize, Clone, Deserialize)]
pub struct CrtMultiCiphertext {
pub(crate) blocks: Vec<concrete_shortint::Ciphertext>,
pub(crate) moduli: Vec<u64>,
pub(crate) key_ids: Vec<KeyId>,
}
67 changes: 67 additions & 0 deletions concrete-integer/src/client_key/crt.rs
@@ -0,0 +1,67 @@
use super::ClientKey;
use crate::CrtCiphertext;

use serde::{Deserialize, Serialize};

/// Client key "specialized" for CRT decomposition.
///
/// This key is a simple wrapper of the [ClientKey],
/// that only encrypt and decrypt in CRT decomposition.
///
/// # Example
///
/// ```rust
/// use concrete_integer::CrtClientKey;
/// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
///
/// let basis = vec![2, 3, 5];
/// let cks = CrtClientKey::new(PARAM_MESSAGE_2_CARRY_2, basis);
///
/// let msg = 13_u64;
///
/// // Encryption:
/// let ct = cks.encrypt(msg);
///
/// // Decryption:
/// let dec = cks.decrypt(&ct);
/// assert_eq!(msg, dec);
/// ```
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
pub struct CrtClientKey {
key: ClientKey,
moduli: Vec<u64>,
}

impl AsRef<ClientKey> for CrtClientKey {
fn as_ref(&self) -> &ClientKey {
&self.key
}
}

impl CrtClientKey {
pub fn new(parameters: concrete_shortint::Parameters, moduli: Vec<u64>) -> Self {
Self {
key: ClientKey::new(parameters),
moduli,
}
}

pub fn encrypt(&self, message: u64) -> CrtCiphertext {
self.key.encrypt_crt(message, self.moduli.clone())
}

pub fn decrypt(&self, ciphertext: &CrtCiphertext) -> u64 {
self.key.decrypt_crt(ciphertext)
}

/// Returns the parameters used by the client key.
pub fn parameters(&self) -> concrete_shortint::Parameters {
self.key.parameters()
}
}

impl From<(ClientKey, Vec<u64>)> for CrtClientKey {
fn from((key, moduli): (ClientKey, Vec<u64>)) -> Self {
Self { key, moduli }
}
}

0 comments on commit fa5dcb9

Please sign in to comment.