This crate exposes an experimental C FFI to the concrete-core
primitives. Using this FFI, any language can benefit from the FHE scheme proposed in concrete
.
This FFI is currently experimental and therefore unstable in terms of naming and exposed structures/entry points.
You can build concrete-core-ffi
yourself on a Unix x86_64 machine using the following command:
RUSTFLAGS="-Ctarget-cpu=native" cargo build --all-features --release -p concrete-core-ffi
All features in the FFI crate are opt-in, but for simplicity here, we enable all of them.
You can then find the concrete-core-ffi.h
header as well as the static (.a) and dynamic (.so) libconcrete_core_ffi
binaries in "${REPO_ROOT}/target/release/"
Whether you build concrete-core-ffi yourself or downloaded a pre-built version you will need to set-up you build system so that your C or C++ program links against concrete-core-ffi
.
Here is a minimal CMakeLists.txt allowing to do just that:
project(my-project)
cmake_minimum_required(VERSION 3.16)
set(CONCRETE_CORE_FFI_RELEASE "/path/to/concrete-core-ffi/binaries/and/header")
include_directories(${CONCRETE_CORE_FFI_RELEASE})
add_library(Concrete STATIC IMPORTED)
set_target_properties(Concrete PROPERTIES IMPORTED_LOCATION ${CONCRETE_CORE_FFI_RELEASE}/libconcrete_core_ffi.a)
set(EXECUTABLE_NAME my-executable)
add_executable(${EXECUTABLE_NAME} main.c)
target_include_directories(${EXECUTABLE_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(${EXECUTABLE_NAME} LINK_PUBLIC Concrete m pthread dl)
target_compile_options(${EXECUTABLE_NAME} PRIVATE -Werror)
DISCLAIMER: the parameters in the example below are insecure and for example purposes only.
Here is a small-ish example of how to call concrete-core
from C through concrete-core-ffi
to compute the homomorphic addition of two ciphertexts. This needs to be linked against libconcrete_core_ffi
.
If you use the following code block content for the main.c file used in the CMakeLists.txt above you will be able to compile and run the homomorphic addition between 1 and 2.
#include "concrete-core-ffi.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <tgmath.h>
// Precision related constants
const int MESSAGE_BITS = 4;
const int SHIFT = 64 - (MESSAGE_BITS + 1);
int main(void) {
// DefaultEngine requires a seeder to seed random number generators for key generation and
// encryption.
SeederBuilder* builder = NULL;
bool unix_seeder_available = false;
int unix_seeder_available_ok = unix_seeder_is_available(&unix_seeder_available);
// Here we use asserts to hard crash if something goes wrong, you will want to have a different
// behavior in your production code for better error handling
assert(unix_seeder_available_ok == 0);
if (unix_seeder_available) {
// DANGER DANGER DANGER DANGER DANGER DANGER
// HIGHLY UNSAFE ONLY FOR TESTING PURPOSES
uint64_t secret_high_64 = 0;
uint64_t secret_low_64 = 0;
int get_builder_ok = get_unix_seeder_builder(secret_high_64, secret_low_64, &builder);
assert(get_builder_ok == 0);
}
else {
printf("UNIX seeder unavailable on this system.\n");
return EXIT_FAILURE;
}
// Pointer for the engine we will instantiate and later use
DefaultEngine *engine = NULL;
int default_engine_ok = new_default_engine(builder, &engine);
assert(default_engine_ok == 0);
double variance = 0.000000001;
// We generate the secret key
size_t lwe_dimension = 10;
LweSecretKey64 *sk = NULL;
int sk_ok = default_engine_generate_new_lwe_secret_key_u64(engine, lwe_dimension, &sk);
assert(sk_ok == 0);
// We allocate the ciphertext buffer
uint64_t *input_ct_1_buffer =
aligned_alloc(U64_ALIGNMENT, sizeof(uint64_t) * (lwe_dimension + 1));
uint64_t *input_ct_2_buffer =
aligned_alloc(U64_ALIGNMENT, sizeof(uint64_t) * (lwe_dimension + 1));
uint64_t *output_ct_buffer = aligned_alloc(U64_ALIGNMENT, sizeof(uint64_t) * (lwe_dimension + 1));
uint64_t plaintext_1 = {((uint64_t)1) << SHIFT};
uint64_t plaintext_2 = {((uint64_t)2) << SHIFT};
// We encrypt the plaintext
int enc_ct_1_ok = default_engine_discard_encrypt_lwe_ciphertext_u64_raw_ptr_buffers(
engine, sk, input_ct_1_buffer, plaintext_1, variance);
assert(enc_ct_1_ok == 0);
int enc_ct_2_ok = default_engine_discard_encrypt_lwe_ciphertext_u64_raw_ptr_buffers(
engine, sk, input_ct_2_buffer, plaintext_2, variance);
assert(enc_ct_2_ok == 0);
// Perform the addition
int add_ok = default_engine_discard_add_lwe_ciphertext_u64_raw_ptr_buffers(
engine, output_ct_buffer, input_ct_1_buffer, input_ct_2_buffer, lwe_dimension);
assert(add_ok == 0);
// We decrypt the plaintext
uint64_t output = -1;
int decrypt_ok = default_engine_decrypt_lwe_ciphertext_u64_raw_ptr_buffers(
engine, sk, output_ct_buffer, &output);
assert(decrypt_ok == 0);
// We check that the output are the same
double expected = ((double)plaintext_2 + (double)plaintext_1) / pow(2, SHIFT);
double obtained = (double)output / pow(2, SHIFT);
printf("Comparing output. Expected %f, Obtained %f\n", expected, obtained);
double abs_diff = abs(obtained - expected);
double rel_error = abs_diff / fmax(expected, obtained);
assert(rel_error < 0.002);
// We deallocate the objects
default_engine_destroy_lwe_secret_key_u64(engine, sk);
destroy_default_engine(engine);
destroy_seeder_builder(builder);
free(input_ct_1_buffer);
free(input_ct_2_buffer);
free(output_ct_buffer);
return EXIT_SUCCESS;
}
- TFHE
- Concrete-core user documentation
- Concrete-core V1.0.0-alpha release
- Concrete-core V1.0.0-beta release
- Concrete-core V1.0.0-gamma release
This software is distributed under the BSD-3-Clause-Clear license. If you have any questions,
please contact us at [email protected]
.