SEAL.jl

SEAL.jl is a Julia package that wraps the Microsoft SEAL library for homomorphic encryption. It supports the Brakerski/Fan-Vercauteren (BFV) and Cheon-Kim-Kim-Song (CKKS, also known as HEAAN in literature) schemes and exposes the homomorphic encryption capabilitites of SEAL in a (mostly) intuitive and Julian way. SEAL.jl is published under the same permissive MIT license as the Microsoft SEAL library.

Currently, SEAL.jl supports all operations that are used in the examples of the SEAL library. This includes encoding and encryption, addition and multiplication, rotation, relinearization and modulus switching for the BFV and CKKS schemes.

Installation

To install SEAL.jl, start a Julia REPL, hit ] to enter Julia's Pkg mode, and then execute

(@v1.5) pkg> add SEAL

Alternatively, you can install SEAL.jl by using Pkg directly, i.e., by running

julia -e 'using Pkg; Pkg.add("SEAL")'

SEAL.jl depends on the binary distribution of the SEAL library, which is available as a Julia package SEAL_jll.jl and which is automatically installed as a dependency.

Note: Currently SEALjll.jl is not available on Windows, thus SEAL.jl will work only on Linux, MacOS and FreeBSD. Also, SEALjll.jl does not work on 32-bit systems.

Getting started

Usage

After installation, load SEAL.jl by running

using SEAL

in the REPL. A minimal working example for encrypting an array of integers using the BFV scheme, squaring it, and decrypting it, looks as follows:

julia> using SEAL
[ Info: Precompiling SEAL [bac81e26-86e4-4b48-8696-7d0406d5dbc1]

julia> parms = EncryptionParameters(SchemeType.bfv)
EncryptionParameters(Ptr{Nothing} @0x0000000002e1d3a0)

julia> poly_modulus_degree = 4096
4096

julia> set_poly_modulus_degree!(parms, poly_modulus_degree)
EncryptionParameters(Ptr{Nothing} @0x0000000002e1d3a0)

julia> set_coeff_modulus!(parms, coeff_modulus_bfv_default(poly_modulus_degree))
EncryptionParameters(Ptr{Nothing} @0x0000000002e1d3a0)

julia> set_plain_modulus!(parms, plain_modulus_batching(poly_modulus_degree, 20))
EncryptionParameters(Ptr{Nothing} @0x0000000002e1d3a0)

julia> context = SEALContext(parms)
SEALContext(Ptr{Nothing} @0x0000000004298440)

julia> keygen = KeyGenerator(context)
KeyGenerator(Ptr{Nothing} @0x00000000021ef540)

julia> public_key_ = PublicKey()
PublicKey(Ptr{Nothing} @0x0000000002272610)

julia> create_public_key!(public_key_, keygen)

julia> secret_key_ = secret_key(keygen)
SecretKey(Ptr{Nothing} @0x0000000001cec2a0)

julia> encryptor = Encryptor(context, public_key_)
Encryptor(Ptr{Nothing} @0x0000000001cd4480)

julia> evaluator = Evaluator(context)
Evaluator(Ptr{Nothing} @0x000000000428bdd0)

julia> decryptor = Decryptor(context, secret_key_)
Decryptor(Ptr{Nothing} @0x00000000037670d0)

julia> batch_encoder = BatchEncoder(context)
BatchEncoder(Ptr{Nothing} @0x0000000001fb4bd0, SEALContext(Ptr{Nothing} @0x0000000001b87780))

julia> pod_matrix = collect(UInt64, 1:slot_count(batch_encoder));

julia> Int.(vcat(pod_matrix[1:3], pod_matrix[end-3:end]))
7-element Array{Int64,1}:
    1
    2
    3
 4093
 4094
 4095
 4096

julia> plain_matrix = Plaintext()
Plaintext(Ptr{Nothing} @0x00000000042db6e0)

julia> encode!(plain_matrix, pod_matrix, batch_encoder)
Plaintext(Ptr{Nothing} @0x0000000002ce0370)

julia> encrypted_matrix = Ciphertext()
Ciphertext(Ptr{Nothing} @0x0000000002d91b80)

julia> encrypt!(encrypted_matrix, plain_matrix, encryptor)
Ciphertext(Ptr{Nothing} @0x0000000002d91b80)

julia> add_inplace!(encrypted_matrix, encrypted_matrix, evaluator)
Ciphertext(Ptr{Nothing} @0x0000000002ce1280)

julia> plain_result = Plaintext()
Plaintext(Ptr{Nothing} @0x0000000004591550)

julia> decrypt!(plain_result, encrypted_matrix, decryptor)
Plaintext(Ptr{Nothing} @0x0000000004591550)

julia> decode!(pod_matrix, plain_result, batch_encoder);

julia> Int.(vcat(pod_matrix[1:3], pod_matrix[end-3:end]))
7-element Array{Int64,1}:
    2
    4
    6
 8186
 8188
 8190
 8192

Examples

As you can see, using homomorphic encryption is quite involved: You need to pick a scheme, provide sensible encryption parameters, encode your raw data into plaintext, encrypt it to ciphertext, perform your arithmetic operations on it, and then decrypt and decode again. Therefore, before starting to use SEAL.jl for your own applications, it is highly recommended to have a look at the examples in the examples/ directory. Otherwise it will be very likely that you are using SEAL.jl (and SEAL) in a way that is either not secure, will produce unexpected results, or just crashes. The examples included in SEAL.jl follow almost line-by-line the examples provided by the SEAL library. For example, the snippet above is based on the example_batch_encoder() function in examples/2_encoders.jl. The full list of examples is as follows:

SEAL.jlSEAL (C++)Description
examples.jlexamples.cppThe example runner application
1_bfv_basics.jl1_bfv_basics.cppEncrypted modular arithmetic using the BFV scheme
2_encoders.jl2_encoders.cppEncoding more complex data into Microsoft SEAL plaintext objects
3_levels.jl3_levels.cppIntroduces the concept of levels; prerequisite for using the CKKS scheme
4_ckks_basics.jl4_ckks_basics.cppEncrypted real number arithmetic using the CKKS scheme
5_rotation.jl5_rotation.cppPerforming cyclic rotations on encrypted vectors in the BFV and CKKS schemes
6_serialization.jl6_serialization.cppSerializing objects in Microsoft SEAL
7_performance.jl7_performance.cppPerformance tests

To run the examples, first install SEAL.jl (as shown above) and clone this repository:

git clone https://github.com/JuliaCrypto/SEAL.jl.git

Then, run Julia and include examples/examples.jl before executing seal_examples():

julia --project=. -e 'include("SEAL.jl/examples/examples.jl"); seal_examples()'

You will be shown an interactive prompt that lets you run any of the available examples:

Microsoft SEAL version: 3.6.2
+---------------------------------------------------------+
| The following examples should be executed while reading |
| comments in associated files in examples/.              |
+---------------------------------------------------------+
| Examples                   | Source Files               |
+----------------------------+----------------------------+
| 1. BFV Basics              | 1_bfv_basics.jl            |
| 2. Encoders                | 2_encoders.jl              |
| 3. Levels                  | 3_levels.jl                |
| 4. CKKS Basics             | 4_ckks_basics.jl           |
| 5. Rotation                | 5_rotation.jl              |
| 6. Serialization           | 6_serialization.jl         |
| 7. Performance Test        | 7_performance.jl           |
+----------------------------+----------------------------+
[      0 MB] Total allocation from the memory pool

> Run example (1 ~ 7) or exit (0): 

Since the examples will not create or modify any files, feel free to run them from any directory.

Implementation strategy

SEAL.jl is work-in-progress, thus only a subset of the many capabilities of the SEAL library are so far supported (PRs are welcome!). In general, SEAL.jl makes use of the C bindings provided by SEAL, but tries to mimic SEAL's C++ API as close as possible. That is, file names, function/variable names, the order of arguments etc. are as close as possible to the SEAL C++ code as possible. The reason for this is that the SEAL library provides excellent inline code documentation, thus by reading (and understanding) the comments in the C++ files you should immediately be able to reproduce the same implementation with SEAL.jl.

However, some implementation details do not translate well from C++ to Julia. Also, the Julia community has a few strong conventions that if violated would make it unnecessarily difficult for experienced Julia users to use SEAL.jl correctly. Thus, when trying to recreate SEAL examples written in C++ with SEAL.jl in Julia, there are a few things to watch out for:

  • Functions that modify their input are suffixed by !.
  • Function arguments that are modified come first (but the rest remains in order) .
  • When translating C++ member function to Julia, the "owning" object is always passed as the last argument.
  • While x.size() in C++ returns a scalar, length-like value, size(x) in Julia is expected to return a tuple, which is also the case in SEAL.jl.

The next example shows the first three items in practice. The C++ code snippet

evaluator.multiply_plain(x1_encrypted, plain_coeff3, x1_encrypted_coeff3);

is translated to the following Julia code:

multiply_plain!(x1_encrypted_coeff3, x1_encrypted, plain_coeff3, evaluator)

Note the trailing !, the fact that x1_encrypted_coeff3 as the modified input variable is now the first argument, and evaluator being passed as the last argument.

Authors

SEAL.jl was initiated by Michael Schlottke-Lakemper (University of Cologne, Germany), who is also the principal developer of SEAL.jl.

License and contributing

SEAL.jl is licensed under the MIT license (see License). Since SEAL.jl is an open-source project, we are very happy to accept contributions from the community. Please refer to Contributing for more details.

Acknowledgements

This Julia package would have not been possible without the excellent work of the developers of the SEAL library. Their high-quality code documentation plus the fact that they provide C bindings for the entire functionality of the SEAL C++ library have made developing SEAL.jl a breeze.