PALISADE:CKKS的使用

如果不知道CKKS原理的话,不妨移步本专栏下的CKKS介绍文章。

本文通过PALISADE库的使用样例来进一步了解CKKS。

/*
 * @file  simple-real-numbers.cpp - Simple examples for CKKS.
 * @author  TPOC: [email protected]
 */

#define PROFILE

#include "palisade.h"

using namespace lbcrypto;

int main()
{
	// 和TenSEAL一样,先建立加密的上下文,可以理解为一个包含了公私钥的大模块。

	// 主要的一些参数
	/* A1) Multiplicative depth:
	 * The CKKS scheme we setup here will work for any computation
	 * that has a multiplicative depth equal to 'multDepth'.
	 * This is the maximum possible depth of a given multiplication,
	 * but not the total number of multiplications supported by the
	 * scheme.
	 *
	 * For example, computation f(x, y) = x^2 + x*y + y^2 + x + y has
	 * a multiplicative depth of 1, but requires a total of 3 multiplications.
	 * On the other hand, computation g(x_i) = x1*x2*x3*x4 can be implemented
	 * either as a computation of multiplicative depth 3 as
	 * g(x_i) = ((x1*x2)*x3)*x4, or as a computation of multiplicative depth 2
	 * as g(x_i) = (x1*x2)*(x3*x4).
	 *
	 * For performance reasons, it's generally preferable to perform operations
	 * in the shorted multiplicative depth possible.
	 */
	 // 这是(指定的)乘法深度。具体地,指一个具体加密方案允许的最大乘法次数。
	uint32_t multDepth = 1;

	/* A2) Bit-length of scaling factor.
	 * CKKS works for real numbers, but these numbers are encoded as integers.
	 * For instance, real number m=0.01 is encoded as m'=round(m*D), where D is
	 * a scheme parameter called scaling factor. Suppose D=1000, then m' is 10 (an
	 * integer). Say the result of a computation based on m' is 130, then at
	 * decryption, the scaling factor is removed so the user is presented with
	 * the real number result of 0.13.
	 *
	 * Parameter 'scaleFactorBits' determines the bit-length of the scaling
	 * factor D, but not the scaling factor itself. The latter is implementation
	 * specific, and it may also vary between ciphertexts in certain versions of
	 * CKKS (e.g., in EXACTRESCALE).
	 *
	 * Choosing 'scaleFactorBits' depends on the desired accuracy of the
	 * computation, as well as the remaining parameters like multDepth or security
	 * standard. This is because the remaining parameters determine how much noise
	 * will be incurred during the computation (remember CKKS is an approximate
	 * scheme that incurs small amounts of noise with every operation). The scaling
	 * factor should be large enough to both accommodate this noise and support results
	 * that match the desired accuracy.
	 */
	 // 这是放大因子的比特数,即二进制位数。
	 // 当然也可以指定具体大小。比如用EXACTSCALE。
	 // 它取决于期望的精度、乘法深度以及拟定的安全标准。
	 // 这个参数需要足够大,以掩盖噪声和保证足够的精度。
	uint32_t scaleFactorBits = 50;

	/* A3) Number of plaintext slots used in the ciphertext.
	 * CKKS packs multiple plaintext values in each ciphertext.
	 * The maximum number of slots depends on a security parameter called ring
	 * dimension. In this instance, we don't specify the ring dimension directly,
	 * but let the library choose it for us, based on the security level we choose,
	 * the multiplicative depth we want to support, and the scaling factor size.
	 *
	 * Please use method GetRingDimension() to find out the exact ring dimension
	 * being used for these parameters. Give ring dimension N, the maximum batch
	 * size is N/2, because of the way CKKS works.
	 */
	 // 这是向量的大小。CKKS对整个明文向量进行加密,但是这个向量的大小需要我们提前指定。
	 // 这个向量的大小需要为2的幂。比如,加密一个长度为8的向量:
	uint32_t batchSize = 8;

	/* A4) Desired security level based on FHE standards.
	 * This parameter can take four values. Three of the possible values correspond
	 * to 128-bit, 192-bit, and 256-bit security, and the fourth value corresponds
	 * to "NotSet", which means that the user is responsible for choosing security
	 * parameters. Naturally, "NotSet" should be used only in non-production
	 * environments, or by experts who understand the security implications of their
	 * choices.
	 *
	 * If a given security level is selected, the library will consult the current
	 * security parameter tables defined by the FHE standards consortium
	 * (https://homomorphicencryption.org/introduction/) to automatically
	 * select the security parameters. Please see "TABLES of RECOMMENDED PARAMETERS"
	 * in  the following reference for more details:
	 * http://homomorphicencryption.org/wp-content/uploads/2018/11/HomomorphicEncryptionStandardv1.1.pdf
	 */
	 // 这里支持128bit, 192bit和256bit的安全性。也可以让用户自己指定。
	 // 这里所谓的k-bit安全性是指,目前已知所有的对于CKKS方案的攻击都需要经过O(2^k)次比特运算才能奏效。
	 // 这个问题来自于RLWE问题的困难性。
	 // 安全性和一个大模数Q成反比关系,乘法深度约为大模数Q的比特数除以放大因子数的商。
	 // 一般的规律是,安全性数字越高,能做的乘法乘法深度越少。
	SecurityLevel securityLevel = HEStd_128_classic;

	// The following call creates a CKKS crypto context based on the
	// arguments defined above.
	CryptoContext<DCRTPoly> cc =
			CryptoContextFactory<DCRTPoly>::genCryptoContextCKKS(
			   multDepth,
			   scaleFactorBits,
			   batchSize,
			   securityLevel);

	cout << "CKKS scheme is using ring dimension " << cc->GetRingDimension() << endl << endl;

	// Enable the features that you wish to use
	cc->Enable(ENCRYPTION);
	cc->Enable(SHE);

	// B. Step 2 � Key Generation
	/* B1) Generate encryption keys.
	 * These are used for encryption/decryption, as well as in generating different
	 * kinds of keys.
	 */
	 // 这里生成加密和解密的密钥。
	 // auto关键字,用于自动推断变量的类型。
	auto keys = cc->KeyGen();

	/* B2) Generate the relinearization key
	 * In CKKS, whenever someone multiplies two ciphertexts encrypted with key s,
	 * we get a result with some components that are valid under key s, and
	 * with an additional component that's valid under key s^2.
	 *
	 * In most cases, we want to perform relinearization of the multiplicaiton result,
	 * i.e., we want to transform the s^2 component of the ciphertext so it becomes valid
	 * under original key s. To do so, we need to create what we call a relinearization
	 * key with the following line.
	 */
	 // 这里需要基于密钥生成重线性化钥。
	cc->EvalMultKeyGen(keys.secretKey);

	/* B3) Generate the rotation keys
	 * CKKS supports rotating the contents of a packed ciphertext, but to do so, we
	 * need to create what we call a rotation key. This is done with the following call,
	 * which takes as input a vector with indices that correspond to the rotation offset
	 * we want to support. Negative indices correspond to right shift and positive to left
	 * shift. Look at the output of this demo for an illustration of this.
	 *
	 * Keep in mind that rotations work on the entire ring dimension, not the specified
	 * batch size. This means that, if ring dimension is 8 and batch size is 4, then an
	 * input (1,2,3,4,0,0,0,0) rotated by 2 will become (3,4,0,0,0,0,1,2) and not
	 * (3,4,1,2,0,0,0,0). Also, as someone can observe in the output of this demo, since
	 * CKKS is approximate, zeros are not exact - they're just very small numbers.
	 */
	 // 这里生成旋转钥。根据指定的旋转方向和步数生成。
	 // 需要注意,这个旋转是相对于大的batchsize来说的。
	 // 比如说,[1,2,3,4,0,0,0,0]旋转是这个向量整体在做旋转,而不是仅限于前面四个值1,2,3,4之间的旋转。
	cc->EvalAtIndexKeyGen(keys.secretKey, { 1, -2 });


	// Step 3 � Encoding and encryption of inputs

	// Inputs
	// 可以看到,对应前面的batchsize = 8,我们就加密长度为8的向量。
	vector<complex<double>> x1 = { 0.25, 0.5, 0.75, 1.0, 2.0, 3.0, 4.0, 5.0 };
	vector<complex<double>> x2 = { 5.0, 4.0, 3.0, 2.0, 1.0, 0.75, 0.5, 0.25 };

	// Encoding as plaintexts
	// 编码成多项式。
	Plaintext ptxt1 = cc->MakeCKKSPackedPlaintext(x1);
	Plaintext ptxt2 = cc->MakeCKKSPackedPlaintext(x2);

	cout << "Input x1: " << ptxt1 << endl;
	cout << "Input x2: " << ptxt2 << endl;

	// Encrypt the encoded vectors
	// 加密。
	auto c1 = cc->Encrypt(keys.publicKey, ptxt1);
	auto c2 = cc->Encrypt(keys.publicKey, ptxt2);

	// Step 4 � Evaluation
	// 加减乘除
	// Homomorphic addition
	auto cAdd = cc->EvalAdd(c1, c2);

	// Homomorphic subtraction
	auto cSub = cc->EvalSub(c1, c2);

	// Homomorphic scalar multiplication
	auto cScalar = cc->EvalMult(c1, 4.0);

	// Homomorphic multiplication
	auto cMul = cc->EvalMult(c1, c2);

	// Homomorphic rotations
	auto cRot1 = cc->EvalAtIndex(c1, 1);
	auto cRot2 = cc->EvalAtIndex(c1, -2);

	// Step 5 � Decryption and output
	Plaintext result;
	// We set the cout precision to 8 decimal digits for a nicer output.
	// If you want to see the error/noise introduced by CKKS, bump it up
	// to 15 and it should become visible.
	// 这里似乎是设置输出格式为8位小数。
	cout.precision(8);
	cout << endl << "Results of homomorphic computations: " << endl;

	// Decrypt the result of addition
	cc->Decrypt(keys.secretKey, cAdd, &result);
	result->SetLength(batchSize);
	cout << "x1 + x2 = " << result << endl;

	// Decrypt the result of subtraction
	cc->Decrypt(keys.secretKey, cSub, &result);
	result->SetLength(batchSize);
	cout << "x1 - x2 = " << result << endl;

	// Decrypt the result of scalar multiplication
	cc->Decrypt(keys.secretKey, cScalar, &result);
	result->SetLength(batchSize);
	cout << "4 * x1 = " << result << endl;

	// Decrypt the result of multiplication
	cc->Decrypt(keys.secretKey, cMul, &result);
	result->SetLength(batchSize);
	cout << "x1 * x2 = " << result << endl;

	// Decrypt the result of rotations
	cc->Decrypt(keys.secretKey, cRot1, &result);
	result->SetLength(batchSize);
	cout << endl << "In rotations, very small outputs (~10^-10 here) correspond to 0's:" << endl;
	cout << "x1 rotate by 1 = " << result << endl;

	cc->Decrypt(keys.secretKey, cRot2, &result);
	result->SetLength(batchSize);
	cout << "x1 rotate by -2 = " << result << endl;

	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43466027/article/details/123506960
今日推荐