Interface
This section walks through the CKKS interface in the order one would use it: choosing parameters, generating keys, encrypting, computing, and decrypting. Throughout, we assume the standard parameter set from Section 2.3.
Parameter selection
The parameters (ring dimension, number of levels, precision, and so on) are fixed once, ahead of time; Section 2.3 covers how to choose them. The rest of this page assumes the standard parameter set.
Key generation
Key generation comes in two forms:
- Single-party. Key generation happens once, on the client that owns the data: the client samples a secret key and derives the matching public and evaluation keys.
- Multiparty. No single party may hold the whole secret key, so the keys are produced by a three-round ceremony among the $n$ parties (two rounds if the computation never bootstraps; see below).
One part of key generation deserves special attention: the rotation keys. Every distinct rotation needs its own rotation key, and these keys must be generated ahead of time: a rotation whose key was never generated simply cannot be performed. The catch is that each key is roughly $126$ MB apiece at the standard parameters, so generating a key for every conceivable rotation is not an option. Users should generate only the rotation keys the computation actually uses.
Encryption
A CKKS plaintext vector is a vector in $\mathbb{C}^{32768}$. Each coordinate is called a slot. Encryption turns such a plaintext vector into a ciphertext. At the standard parameters, the ciphertext stores each slot to about $40$ bits of precision after the binary point, and the entries should have magnitude at most about $2^{12}$.
Most computations don't have exactly $32768$ values to encrypt, so users will have to decide how to lay the data out across the slots. Three common strategies are:
- Pad with zeros. Place the data in the first few slots and fill the rest with zeros. This is the simplest option when there are fewer than $32768$ values.
- Space evenly. Spread the values evenly around the $32768$ slots, with zeros in between. This is convenient when later operations (such as rotations) need the data arranged at regular strides.
- Duplicate. Repeat the values to fill the slots. Replicating data is often useful for arranging the inputs of a matrix multiplication or a batched computation.
Attached to every ciphertext is a nonnegative integer called its level. A fresh encryption is produced at level $17$. The level is a budget: it starts high and can only go down as the computation proceeds. The only way to raise it again is bootstrapping, an expensive operation that turns a low-level ciphertext back into a high-level one. The next section describes exactly how each operation spends this budget.
Computation
CKKS exposes a small set of homomorphic operations. Each one produces a new ciphertext and has a well-defined effect on the level. The table below summarizes those effects; $\ell$, $\ell_1$, and $\ell_2$ denote the levels of the inputs.
| Operation | Resulting level |
|---|---|
| Add (levels $\ell_1, \ell_2$) | $\min(\ell_1, \ell_2)$ |
| Subtract (levels $\ell_1, \ell_2$) | $\min(\ell_1, \ell_2)$ |
| Multiply by plaintext (level $\ell$) | $\ell - 1$ in general; $\ell$ if the plaintext is a scalar integer |
| Multiply by ciphertext (levels $\ell_1, \ell_2$) | $\min(\ell_1, \ell_2) - 1$ |
| Rotation (level $\ell$) | $\ell$ |
| Conjugation (level $\ell$) | $\ell$ |
| Polynomial evaluation, degree $d$ (level $\ell$) | $\ell - \lceil \log_2(d + 1) \rceil$ |
| Matrix multiplication (level $\ell$) | $\ell - 1$ |
| Bootstrapping (level $0$) | $17$ |
Addition and subtraction
Addition and subtraction are the cheapest operations: they combine two ciphertexts pointwise and leave the result at the minimum of the two input levels.
Multiplication
Multiplication comes in two flavors, both pointwise (Hadamard):
- By a plaintext vector. Multiplies each slot by the corresponding plaintext entry and drops the level by one, unless the plaintext is a constant integer scalar, in which case no level is consumed.
- By another ciphertext. Multiplies the two encrypted vectors pointwise and drops the level to one below the minimum of the two inputs.
Rotation
Rotation cyclically shifts the slots: rotating by $i$ sends $$(v_0, v_1, \ldots, v_{32767}) \mapsto (v_i, v_{i+1}, \ldots, v_{i-1}),$$ with indices taken modulo $32768$. It is the only way to move data between slots, and it leaves the level unchanged. Each rotation amount needs its own rotation key, generated ahead of time; a rotation whose key was not generated cannot be performed.
Conjugation
Conjugation replaces each slot by its complex conjugate, sending $$(v_0, v_1, \ldots, v_{32767}) \mapsto (\bar v_0, \bar v_1, \ldots, \bar v_{32767}).$$ It is pointwise and leaves the level unchanged.
Polynomial evaluation
Polynomial evaluation can be built out of the operations above, but is exposed directly for convenience. It applies a polynomial slotwise to an encrypted vector, and a different polynomial can be applied to each slot if desired. Evaluation costs $\lceil \log_2(d + 1) \rceil$ levels, where $d$ is the largest degree among the per-slot polynomials. One caveat for high-degree polynomials: they can only be evaluated accurately when the encrypted values lie in the range $[-2, 2]$, so inputs needs to be scaled into that range first.
Matrix multiplication
Matrix multiplication can also be built out of the operations above, but is exposed directly for convenience. It multiplies an encrypted vector by a plaintext matrix and consumes a single level, regardless of the size of the matrix.
Bootstrapping
Bootstrapping takes a level-$0$ ciphertext and returns a level-$17$ ciphertext encrypting the same vector, preserving about $13$ bits of precision after the binary point. It assumes its input lies within the $2^{12}$ magnitude range, so intermediate values must be kept in this range before a bootstrap.
Decryption
Decryption always happens client-side, and comes in two forms:
- Single-party. The client that generated the key takes the final ciphertext and decrypts it directly to recover the result vector.
- Multiparty. No single party holds the whole secret key, so at least $t$ of the $n$ parties must each compute a partial decryption, called a decryption share, and exchange those shares with one another; combining any $t$ of them recovers the plaintext.
At this stage, a small amount of noise flooding is added if needed. If noise flooding is not performed, decryption results must be kept private and never shared with the computation server. Otherwise, the server, which has seen the corresponding ciphertexts, can use the leaked information to recover the secret key.