visit
A form of encryption that allows computation on ciphertexts generating an encrypted result that, when decrypted, matches the result of the operations as if they had been performed on plaintext. — Massimo Bertaccini ()
Use a special privacy homomorphism to encrypt its data so that the time-share computer can operate on the data without the necessity of decrypting it first.
To put it another way, I want to delete the processing of my data without giving away access to it.
To put it another yet another way, function(A, B) = function (A, B
) where A and B
are encrypted values of A and B, respectively. This enables third parties to compute encrypted data and provide the results without having any knowledge of the original value. With RSA, two values encrypted with the same key can be multiplied, and the product can be decrypted yielding the product of the encrypted values.
Library | Schemes | Language | License |
---|---|---|---|
BFV, BGV, CKKS | C++/C# Wrapper | MIT | |
Open FHE | BFV, BGV, CKKS, DM, CGGI | C++ | BSD 2-Clause License |
CKKS | C++ | Creative Commons 3.0 | |
TFHE (Torus) | Rust | BSD-3-Clause-Clear | |
TFHE | Rust | BSD-3-Clause-Clear | |
BFV, BGV, CKKS | Go | Apache 2.0 | |
N/A – Uses SEAL, PALISADE, HLib | Python,Docker | Apache 2.0 |
To get started, a public key is created by the client and shared with the server. It is used to encrypt data and also to perform arithmetic operations on encrypted data. Both client and server use an Encryptor. The client generates and retains a private key used for decryption used by the aptly named Decryptor.
using Microsoft.Research.SEAL;
protected SEALContext _context;
private KeyGenerator _keyGenerator;
private Encryptor _encryptor;
private Decryptor _decryptor;
protected IFitnessTrackerApiClient _apiClient;
. . .
_context = SEALUtils.GetContext(_config.Value.PolyModulusDegree, this.SchemeType);
_keyGenerator = new KeyGenerator(_context);
_keyGenerator.CreatePublicKey(out PublicKey publicKey);
_keyGenerator.CreateRelinKeys(out RelinKeys relinKeys);
_encryptor = new Encryptor(_context, _publicKey);
_decryptor = new Decryptor(_context, _keyGenerator.SecretKey);
. . .
PublicKeyModelCKKS keyModelCKKS = new(
SEALUtils.KeyToBase64String(_publicKey),
SEALUtils.KeyToBase64String(relinKeys));
await _apiClient.SendPublicKeyCKKSAsync(keyModelCKKS);
Relinearization keys are passed from client to server. Per Microsoft SEAL, they have no semantic meaning other than to reduce the size of Ciphertext following a multiplication operation.
At the command line, the user enters time and distance.
Each value is encoded to PlainText, then encrypted to Ciphertext, and finally encoded to base64 prior to sending it to the server.
using Microsoft.Research.SEAL;
var plaintext = new Plaintext();
_encoder.Encode(value, _scale, plaintext);
var ciphertext = new Ciphertext();
_encryptor.Encrypt(value, ciphertext);
using (var ms = new MemoryStream())
{
ciphertext.Save(ms);
return Convert.ToBase64String(ms.ToArray());
}
RunItemCKKS metricsRequest = new(
EncryptBase64(runItem.Distance),
EncryptBase64(runItem.Time),
EncryptBase64(1 / runItem.Time));
await _apiClient.AddNewRunningDistanceCKKSAsync(metricsRequest);
The server performs a similar bootstrapping function creating a SEALContext using only the public key and re-linearization keys, then processes the request:
var distance = SEALUtils.BuildCiphertextFromBase64String(request.Distance, _sealContext);
var time = SEALUtils.BuildCiphertextFromBase64String(request.Time, _sealContext);
var timeReciprocal = SEALUtils.BuildCiphertextFromBase64String(request.TimeReciprocal, _sealContext);
Ciphertext speed = new();
_evaluator.Multiply(distance, timeReciprocal, speed);
_evaluator.RelinearizeInplace(speed, _relinKeys);
_runListCKKS.Add(new EncryptedRunInfoCKKS(distance, time, speed));
A reciprocal of the time is used to calculate the speed for the submitted run and the RelinearizeInplace method reduces the size of the resulting Ciphertext. Distance, time, and speed are saved to an in-memory List.
The GetMetrics method aggregates the list and returns:
public SummaryItemCKKS GetMetrics()
{
int count = _runListCKKS.Count;
var totalDistanceCKKS = SumEncryptedValues(_runListCKKS.Select(m => m.Distance));
var totalTimeCKKS = SumEncryptedValues(_runListCKKS.Select(m => m.Time));
var totalSpeed = SumEncryptedValues(_runListCKKS.Select(m => m.Speed));
. . .
protected Ciphertext SumEncryptedValues(IEnumerable<Ciphertext> encryptedData)
{
. . .
Ciphertext encTotal = new();
_evaluator.AddMany(encryptedData, encTotal);
return encTotal;
. . .
}
Since the server keeps track of the number of run submissions, the total number of runs is not an encrypted value. That unencrypted value can be converted to Plaintext and used in an operation performed on Ciphertext. While this is a simple example, the implications are worth noting. The server can provide additional values used on encrypted data enabling third-party providers to apply proprietary data to computations against customer-provided encrypted data.
In the example below, the total count is converted to a reciprocal and added to a List<double>. CKKS operations expect encrypted list values, even if the list only contains a single value. So, the List<double> is encoded to a PlainText. The MultiplyPlainInplace multiplies totalSpeed by (1/number of runs) resulting in the average speed. To conserve space, the result is applied to the totalSpeed Ciphertext and the re-linearization keys reduce the size of the output.
Plaintext encodedCountReciprocal = new();
List<double> averagePaceList = new();
double runCountReciprocal = 1 / (double)count;
averagePaceList.Add(runCountReciprocal);
_encoder.Encode(averagePaceList, _scale, encodedCountReciprocal);
_evaluator.MultiplyPlainInplace(totalSpeed, encodedCountReciprocal);
_evaluator.RelinearizeInplace(totalSpeed, _relinKeys);
The values are base64 encoded, returned, and decrypted on the client. The client takes the additional step of converting decrypted Plaintext to List<double>.
var payload = Convert.FromBase64String(encryptedDistanceText);
using var ms = new MemoryStream(payload);
var ciphertext = new Ciphertext();
ciphertext.Load(_context, ms);
var decryptedText = new Plaintext();
_decryptor.Decrypt(cypherText, decryptedText);
List<double> distanceList = new();
_encoder.Decode(decryptedText, distanceList);
Miles | Time |
---|---|
2.5 | 0:35:32.643 |
2.2 | 0:32:48.826 |
2.8 | 0:34:52.036 |
Microsoft SEAL is a Microsoft Research project and is not ready for a production application. All classes implement IDisposable and used unmanaged resources. Production code would need to protect against a memory leak. The SEALContext class does not follow the single responsibility principle. It includes methods for both BGV/BVF and CKKS. Further, CKKS supports vector operations, but not vector addition. A single List<double> can be encrypted to a single CipherText but there is no means of summing the values in a single CipherText. It can be done with , but that is a suboptimal solution.