| Title: | Fully Homomorphic Encryption |
|---|---|
| Description: | A reference implementation of homomorphic encryption schemes. These are encryption schemes which allow limited operations (such as addition and multiplication) to be performed on the cipher text directly without decrypting first. In other words, if you encrypt the values 2 and 3 and "add" their cipher texts then decrypting renders 5. At present, the scheme of Fan and Vercauteren (2012) <https://eprint.iacr.org/2012/144> is provided. This package was originally named {HomomorphicEncryption} and available in source form. It is the package described in Aslett et al. (2015) <arXiv:1508.06574>, but due to a name collision created by a later package we have renamed to {fhe}. |
| Authors: | Louis Aslett [aut, cre, cph] (ORCID: <https://orcid.org/0000-0003-2211-233X>) |
| Maintainer: | Louis Aslett <[email protected]> |
| License: | GPL-2 |
| Version: | 0.7.0 |
| Built: | 2026-07-19 14:13:44 UTC |
| Source: | https://github.com/louisaslett/fhe |
This package provides easy to use implementations of some homomorphic encryption schemes. An encryption scheme is said to be homomorphic when certain functions can be applied directly to the cipher text in such a way that decrypting the result renders the same answer as if the function had been applied to the unencrypted data.
| Package: | fhe |
| Type: | Package |
| Version: | 0.6.0 |
| Date: | 2024-03-01 |
| License: | GPL-2 |
Louis Aslett
Maintainer: Louis Aslett <[email protected]>
Aslett, L. J. M., Esperança, P. M. and Holmes, C. C. (2015), A review of homomorphic encryption and software tools for encrypted statistical machine learning. Technical report, University of Oxford. https://arxiv.org/abs/1508.06574
# Generate cryptographic parameters p <- pars("FandV") # Create public/private keypair keys <- keygen(p) # Encrypt the values 2 and 3 ct1 <- enc(keys$pk, 2) ct2 <- enc(keys$pk, 3) # Homomorphically add the cipertexts together ct <- ct1 + ct2 # Decrypt to 5, the result of applying + to plain messages dec(keys$sk, ct)# Generate cryptographic parameters p <- pars("FandV") # Create public/private keypair keys <- keygen(p) # Encrypt the values 2 and 3 ct1 <- enc(keys$pk, 2) ct2 <- enc(keys$pk, 3) # Homomorphically add the cipertexts together ct <- ct1 + ct2 # Decrypt to 5, the result of applying + to plain messages dec(keys$sk, ct)
These special operations overload the standard behaviour of the arithmetic operations to work instead homomorphically on ciphertexts.
Some arithmetic operations have been overloaded allowing use of the standard
operators on ciphertexts. Therefore, if ct1 and ct2 are two
ciphertexts then the following will be handled automatically:
ct1 + ct2 ct1 - ct2 ct1 * ct2
Note that not all homomorphic encryption schemes will support all operations. Also, it is important to note that typically homomorphic operations cause an increase in the noise within a ciphertext. Once a certain number of operations have taken place the cipertext may no longer correctly decrypt. If a scheme is *fully* homomorphic, then it may be possible to apply a bootstrapping procedure which reduces the noise.
A new ciphertext with the encrypted result of applying the operation to the messages held by the two original cipertexts.
p <- pars("FandV") keys <- keygen(p) ct1 <- enc(keys$pk, 2) ct2 <- enc(keys$pk, 3) ctAdd <- ct1 + ct2 ctSub <- ct1 - ct2 ctMul <- ct1 * ct2 # Decrypt to 5, -1 and 6: the result of applying +, - and * to plain messages dec(keys$sk, ctAdd) dec(keys$sk, ctSub) dec(keys$sk, ctMul)p <- pars("FandV") keys <- keygen(p) ct1 <- enc(keys$pk, 2) ct2 <- enc(keys$pk, 3) ctAdd <- ct1 + ct2 ctSub <- ct1 - ct2 ctMul <- ct1 * ct2 # Decrypt to 5, -1 and 6: the result of applying +, - and * to plain messages dec(keys$sk, ctAdd) dec(keys$sk, ctSub) dec(keys$sk, ctMul)
This decrypts an integer message which has been encrypted under one of the homomorphic schemes supported by this package.
dec(sk, ct)dec(sk, ct)
sk |
a private/secret key for any scheme as generated by the |
ct |
a ciphertext as produced from a call to |
Note that the scheme specified by the private key, sk, and the ciphertext,
ct, must match.
If a symmetric key scheme is being used, then the secret key should be provided
for the sk argument.
The decrypted integer message. If the value is in the range of a standard
integer in R (-2147483647 to 2147483647) then an integer will be returned,
otherwise a bigz big integer object from the gmp package
will be returned.
Louis Aslett
enc to encrypt messages to ciphertexts which this function decrypts.
p <- pars("FandV") keys <- keygen(p) ct <- enc(keys$pk, 1) dec(keys$sk, ct)p <- pars("FandV") keys <- keygen(p) ct <- enc(keys$pk, 1) dec(keys$sk, ct)
This encrypts an integer message under one of the homomorphic encryption schemes supported by this package.
enc(pk, m)enc(pk, m)
pk |
a public key for any scheme as generated by the |
m |
an integer to be encrypted. Note that the permissable range of values
for |
The scheme to use is determined by the public key which contains the parameter values for that scheme as part of it.
If a symmetric key scheme is being used, then the secret key should be provided
for the pk argument.
A ciphertext under the encryption scheme, encrypted using the public key provided.
Louis Aslett
keygen to create public/private keypairs;
dec to decrypt the ciphertext generated by this function.
p <- pars("FandV") keys <- keygen(p) ct <- enc(keys$pk, 1) dec(keys$sk, ct)p <- pars("FandV") keys <- keygen(p) ct <- enc(keys$pk, 1) dec(keys$sk, ct)
The Fan and Vercauteren scheme is implemented in this package.
Description of the scheme.
# Benchmark the performance of the scheme library(microbenchmark) p <- pars("FandV") microbenchmark({ keys <- keygen(p) }, unit="ms") microbenchmark({ ct1 <- enc(keys$pk, 2) }, unit="ms") ct2 <- enc(keys$pk, 3) microbenchmark({ ct1 + ct2 }, unit="ms") microbenchmark({ ct1 * ct2 }, unit="ms") microbenchmark({ dec(keys$sk, ct1) }, unit="ms")# Benchmark the performance of the scheme library(microbenchmark) p <- pars("FandV") microbenchmark({ keys <- keygen(p) }, unit="ms") microbenchmark({ ct1 <- enc(keys$pk, 2) }, unit="ms") ct2 <- enc(keys$pk, 3) microbenchmark({ ct1 + ct2 }, unit="ms") microbenchmark({ ct1 * ct2 }, unit="ms") microbenchmark({ dec(keys$sk, ct1) }, unit="ms")
This function will generate cryptographic keys to enable encryption, decryption and possibly other operations (such as relinearisation or bootstrapping) for the homomorphic encryption schemes supported in this package.
keygen(p)keygen(p)
p |
a parameters object as produced by the |
The scheme to be used is determined by the type of the parameters object,
p.
A list object containing the keys will be returned
Depending on the scheme specified by the parameters object the exact structure
will vary. For example, a symmetric key scheme will only return a private key,
whereas a public key scheme will return a public and private keypair.
Certain schemes may include additional keys for operations such as
relinearisation or bootstrapping. The keys will be named pk (public),
sk (private), rlk (relinearisation), bk (bootstrapping)
in the list object if they are part of the scheme.
Louis Aslett
pars for generating the parameters for a scheme; enc
for encrypting messages using the keys generated by this function.
p <- pars("FandV") keys <- keygen(p) # Look at public key and encrypt keys$pk ct <- enc(keys$pk, 1) # Look at private key and decrypt keys$sk dec(keys$sk, ct) # Obviously a different private key won't be able to decrypt keys2 <- keygen(p) keys2$sk dec(keys2$sk, ct)p <- pars("FandV") keys <- keygen(p) # Look at public key and encrypt keys$pk ct <- enc(keys$pk, 1) # Look at private key and decrypt keys$sk dec(keys$sk, ct) # Obviously a different private key won't be able to decrypt keys2 <- keygen(p) keys2$sk dec(keys2$sk, ct)
Use this function to create an encryption scheme parameters object.
pars(scheme, ...)pars(scheme, ...)
scheme |
the scheme for which to create a parameter object. Currently
only Fan and Vercauteren's scheme is supported by specifying |
... |
pass the specific options for the chosen scheme as named arguments to override any default values. See the details section for options for encryption schemes currently implemented. |
Currently only the scheme of Fan and Vercauteren ("FandV") is implemented.
For "FandV" you may specify:
dpower of the cyclotomic polynomial ring (default 4096);
sigmathe standard deviation of the discrete Gaussian used to induce a distribution on the cyclotomic polynomial ring (default 16.0);
qpowthe power of 2 to use for the coefficient modulus (default 128);
tthe value to use for the message space modulus (default 32768).
This function simply sets up the parameters which must be specified to use a particular encryption scheme. Using the scheme then requires generating cryptographic keys.
Louis Aslett
Fan, J., & Vercauteren, F. (2012). Somewhat Practical Fully Homomorphic Encryption. IACR ePrint. Retrieved from https://eprint.iacr.org/2012/144
parsHelp for automatic assistance selecting parameters which
achieve a certain security level and multiplicative depth.
keygen to generate encryption keys using these parameters.
# Simplest example p <- pars("FandV") keys <- keygen(p) ct <- enc(keys$pk, 1) dec(keys$sk, ct) # Change degree of cyclotomic polynomial used for ciphertext p <- pars("FandV", d=2048) keys <- keygen(p) ct <- enc(keys$pk, 1) dec(keys$sk, ct)# Simplest example p <- pars("FandV") keys <- keygen(p) ct <- enc(keys$pk, 1) dec(keys$sk, ct) # Change degree of cyclotomic polynomial used for ciphertext p <- pars("FandV", d=2048) keys <- keygen(p) ct <- enc(keys$pk, 1) dec(keys$sk, ct)
Use this function to help choose encryption parameters for your scheme.
parsHelp(scheme, ...)parsHelp(scheme, ...)
scheme |
the scheme for which to get parameter help. Currently
only Fan and Vercauteren's scheme is supported by specifying |
... |
pass the specific options for the chosen scheme as named arguments. See the details section for options for encryption schemes currently implemented. |
This function provides assistance with selecting parameter values for the encryption scheme you wish to use.
Currently only the scheme of Fan and Vercauteren ("FandV") is implemented.
For "FandV" you may specify:
lambdathe security level required (bits), default is 80;
maxthe largest absolute value you will need to store encrypted, default is 1000;
Lthe deepest multiplication depth you need to be able to evaluate encrypted, default is 4.
The security level in bits relates to the approximate number of operations
which would be required to break the cipher text, i.e.
operations.
Ensuring the depth of multiplication operations you require is based on an overwhelming probability of being able to correctly decrypt after so many operations by roughly bounding the possible noise growth. In most situations it is very conservative and additional multiplications are possible, so this should be considered only a lower bound on the number of multiplies required. If computational performance is a concern this should be treated only as a good starting point from which the relevant parameters can be eased off, testing more thoroughly by simulation.
For the scheme of Fan and Vercauteren ("FandV"): the security level is
based on the analysis of Lindner and Peikert (2011) which is known to be quite
conservative; and the multiplicative depth bound is based on the analysis of
Lepoint and Naehrig (2014). If the multiplicative depth lower bound is too
conservative, then for computational performance reduce the qpow parameter from
this recommended starting point.
Louis Aslett
Fan, J., & Vercauteren, F. (2012). Somewhat Practical Fully Homomorphic Encryption. IACR ePrint. Retrieved from https://eprint.iacr.org/2012/144
Lepoint, T., & Naehrig, M. (2014). A comparison of the homomorphic encryption schemes FV and YASHE. In Progress in Cryptology–AFRICACRYPT 2014 (pp. 318-335).
Lindner, R., & Peikert, C. (2011). Better key sizes (and attacks) for LWE-based encryption. In Topics in Cryptology–CT-RSA 2011 (pp. 319-339).
pars to manually choose parameters.
keygen to generate encryption keys using these parameters.
# Want 128-bit security with 6 multiplies deep # to evaluate 7 factorial p <- parsHelp("FandV", lambda=128, L=6) keys <- keygen(p) ct <- enc(keys$pk, 1:7) dec(keys$sk, prod(ct)) factorial(7) # ... but notice this is quite conservative # The following will usually give the right answer too ct <- enc(keys$pk, 1:8) dec(keys$sk, prod(ct)) factorial(8) # ... however, another multiply will usually fail: ct <- enc(keys$pk, 1:9) dec(keys$sk, prod(ct)) factorial(9)# Want 128-bit security with 6 multiplies deep # to evaluate 7 factorial p <- parsHelp("FandV", lambda=128, L=6) keys <- keygen(p) ct <- enc(keys$pk, 1:7) dec(keys$sk, prod(ct)) factorial(7) # ... but notice this is quite conservative # The following will usually give the right answer too ct <- enc(keys$pk, 1:8) dec(keys$sk, prod(ct)) factorial(8) # ... however, another multiply will usually fail: ct <- enc(keys$pk, 1:9) dec(keys$sk, prod(ct)) factorial(9)
Use these functions rather than the base R functions for saving and loading
of ciphertexts and keys. Note that this is due to a limitation in Rcpp,
so the save, save.image and saveRDS base R functions are
overriden to warn if they are used incorrectly on ciper text objects.
saveFHE(object, file) loadFHE(file)saveFHE(object, file) loadFHE(file)
object |
the ciphertext or keys to be saved |
file |
the filename to save (load) the object to (from) |
These functions provide a way to save ciphertexts and keys generated by this package to a file in such a way that they can later be restored to another R session (possibly on a different machine).
At present no compression is performed so for long-term storage or transmission over a network it would be advisable to gzip the files after creation.
p <- pars("FandV") keys <- keygen(p) ct1 <- enc(keys$pk, 2) ## Not run: saveFHE(ct1, "~/myCipherText.fhe") # Start a new session or transfer to another machine ## Not run: ct1 <- loadFHE("~/myCipherText.fhe")p <- pars("FandV") keys <- keygen(p) ct1 <- enc(keys$pk, 2) ## Not run: saveFHE(ct1, "~/myCipherText.fhe") # Start a new session or transfer to another machine ## Not run: ct1 <- loadFHE("~/myCipherText.fhe")
Vectors of cipher texts can be seemlessly created and manipulated in the same way one normally works with vectors in R.
Vectors of cipher texts can be seemlessly created either at encryption time
by passing a vector message argument to enc, or after encryption
using the standard concatenation operator c:
enc(keys$pk, c(m1, m2, ...)) c(ct1, ct2)
Standard operations which can be used on regular vectors can also be used on vectors of cipher texts, where the scheme supports it:
ct1 + ct2 ct1 - ct2 ct1 * ct2 ct1 %*% ct2 sum(ct1) prod(ct1)
As with regular scalar operations, note that not all homomorphic encryption schemes will support all vector operations. Also, it is important to note that typically homomorphic operations cause an increase in the noise within a ciphertext. Once a certain number of operations have taken place the cipertext may no longer correctly decrypt. If a scheme is *fully* homomorphic, then it may be possible to apply a bootstrapping procedure which reduces the noise.
p <- pars("FandV") keys <- keygen(p) ct1 <- enc(keys$pk, c(2,3)) ct2 <- enc(keys$pk, c(4,5)) ctConcat <- c(ct1, ct2) ctAdd <- ct1 + ct2 ctSub <- ct1 - ct2 ctMul <- ct1 * ct2 ctIP <- ct1 %*% ct2 # Decrypt to the equivalent of the above operations applied to the vectors # c(2,3) and c(4,5) dec(keys$sk, ctConcat) dec(keys$sk, ctAdd) dec(keys$sk, ctSub) dec(keys$sk, ctMul) dec(keys$sk, ctIP)p <- pars("FandV") keys <- keygen(p) ct1 <- enc(keys$pk, c(2,3)) ct2 <- enc(keys$pk, c(4,5)) ctConcat <- c(ct1, ct2) ctAdd <- ct1 + ct2 ctSub <- ct1 - ct2 ctMul <- ct1 * ct2 ctIP <- ct1 %*% ct2 # Decrypt to the equivalent of the above operations applied to the vectors # c(2,3) and c(4,5) dec(keys$sk, ctConcat) dec(keys$sk, ctAdd) dec(keys$sk, ctSub) dec(keys$sk, ctMul) dec(keys$sk, ctIP)