Lattigo入门学习3

Lattigo之浮点数加解密

先简单回顾,CKKS(Cheon-Kim-Kim-Song)加密方案是一种在同态加密领域中广泛应用的方案,特别是在处理实数和复数上的计算时表现出了优异的性能。CKKS方案允许对编码后的近似实数或复数进行加密,然后进行同态加密操作(如加法和乘法),最后能够解密出一个近似的结果。这种加密方案特别适合用于需要处理大量实数数据的机器学习和数据分析场景。

CKKS方案的核心步骤是通过一系列复杂的代数和数论操作来实现的。下面是这个方案的关键步骤和相关公式的一个概览。请注意,实际的CKKS实现涉及更复杂的数学,并且这里的描述只是为了提供一个概念框架。

CKKS方案

1. 参数设置和密钥生成

首先,选择一个安全参数 λ λ λ,然后根据 λ λ λ 选择多项式的度数 N N N(通常是 2 2 2的幂)以及一系列模数 ( q 1 q_1 q1, q 2 q_2 q2, ..., q L q_L qL ) 来构建模数链。接下来,生成密钥对:

  • 私钥 sk: 随机选择一个小项多项式 ( s ∈ R ) ( s \in R ) (s∈R)。
  • 公钥 pk: 计算 ( ( p 0 , p 1 ) ) ( (p_0, p_1) ) ((p0,p1)) 其中 ( p 0 , p 1 ∈ R q ) ( p_0, p_1 \in R_q ) (p0,p1∈Rq) 并且 ( p 0 = − a s + e + Δ m ) ( p_0 = -as + e + \Delta m ) (p0=−as+e+Δm) (其中 ( a ) ( a ) (a) 是随机选择的, ( e ) ( e ) (e) 是一个小项误差多项式,( Δ \Delta Δ ) 是用来调整比例的大整数,以便于编码实数)。
2. 加密过程

加密一个复数向量 ( m ⃗ ) ( \vec{m} ) (m ) 的步骤如下:

  • 编码:首先,将复数向量 $( \vec{m} ) $编码到一个多项式 ( m ( x ) ) ( m(x) ) (m(x)) 中。

  • 加密:然后计算密文 $c = (c_0, c_1) $ 其中:

    [ c 0 = p 0 ⋅ r + e 0 + Δ ⋅ m ( x ) m o d    q ] [ c_0 = p_0 \cdot r + e_0 + \Delta \cdot m(x) \mod q ] [c0=p0⋅r+e0+Δ⋅m(x)modq]
    [ c 1 = p 1 ⋅ r + e 1 m o d    q ] [ c_1 = p_1 \cdot r + e_1 \mod q ] [c1=p1⋅r+e1modq]

    这里的 ( r r r ) 是随机选择的,( e 0 e_0 e0 ) 和 ($ e_1$ ) 是小项误差多项式。

3. 同态运算

CKKS方案支持同态加法和乘法:

  • 同态加法:给定两个密文 ( $c^{(1)} = (c_0^{(1)}, c_1^{(1)}) $) 和 ( $c^{(2)} = (c_0^{(2)}, c_1^{(2)}) $),它们的和为:

    [ c ( 1 + 2 ) = ( c 0 ( 1 ) + c 0 ( 2 ) , c 1 ( 1 ) + c 1 ( 2 ) ) ] [ c^{(1+2)} = (c_0^{(1)} + c_0^{(2)}, c_1^{(1)} + c_1^{(2)}) ] [c(1+2)=(c0(1)+c0(2),c1(1)+c1(2))]

  • 同态乘法:对于乘法,通常需要执行relinearization和rescaling步骤来保持密文的可管理大小。这里省略具体步骤的描述。

4. 解密过程

解密一个密文 ( c = ( c 0 , c 1 ) c = (c_0, c_1) c=(c0,c1) ),使用私钥 ($ s $) 来计算:

[ m ( x ) = ( c 0 + c 1 ⋅ s ) m o d    q m o d    x N + 1 ] [ m(x) = \left( c_0 + c_1 \cdot s \right) \mod q \mod x^N + 1 ] [m(x)=(c0+c1⋅s)modqmodxN+1]

然后从多项式 ( m(x) ) 中解码出原始的复数向量。

5. 解码

解码过程涉及到从多项式 ( m ( x ) m(x) m(x) ) 中恢复出编码前的向量 ( m ⃗ \vec{m} m )。这通常包括舍去多项式系数的缩放因子 ( Δ \Delta Δ ) 以及将多项式的系数转换回复数。

CKKS方案的详细实现比上述描述要复杂得多,在这里我只做简要回顾,有兴趣的还是读原文Homomorphic Encryption for Arithmetic of Approximate Numbers (iacr.org)

CKKS加解密浮点数实例

接下来我们具体实现一个CKKS加密的例子,我们随机生成 X X X向量和 Y Y Y向量,我们对两个变量执行 ( X − Y ) 2 (X-Y)^2 (X−Y)2并打印输出最终结果。

  • 声明参数和钥匙

    go 复制代码
    package main
    
    import (
    	"fmt"
    	"math/rand"
    
    	"github.com/tuneinsight/lattigo/v5/core/rlwe"
    	"github.com/tuneinsight/lattigo/v5/he/hefloat"
    )
    
    func main() {
    	//paramter
    	var err error
    	var params hefloat.Parameters
    
    	if params, err = hefloat.NewParametersFromLiteral(
    		hefloat.ParametersLiteral{
    			LogN:            14,                                    // log2(ring degree)
    			LogQ:            []int{55, 45, 45, 45, 45, 45, 45, 45}, // log2(primes Q) (ciphertext modulus)
    			LogP:            []int{61},                             // log2(primes P) (auxiliary modulus)
    			LogDefaultScale: 45,                                    // log2(scale)
    		}); err != nil {
    		panic(err)
    	}
    
    	// Key Generator
    	keygen := rlwe.NewKeyGenerator(params)
    	sk := keygen.GenSecretKeyNew()
    	encoder := hefloat.NewEncoder(params)
    	encryptor := rlwe.NewEncryptor(params, sk)
    	decryptor := rlwe.NewDecryptor(params, sk)
    
    	rlk := keygen.GenRelinearizationKeyNew(sk)
    	evk := rlwe.NewMemEvaluationKeySet(rlk)
    	evaluator := hefloat.NewEvaluator(params, evk)
  • 构造数据并加密

    go 复制代码
    // generate data
    	x := make([]float64, params.MaxSlots())
    	y := make([]float64, params.MaxSlots())
    
    	r1 := rand.New(rand.NewSource(0))
    	for i := range x {
    		x[i] = r1.Float64()
    	}
    
    	r2 := rand.New(rand.NewSource(1))
    	for i := range y {
    		y[i] = r2.Float64()
    	}
    
    	// encoder and encryptor
    	plaintext_x := hefloat.NewPlaintext(params, params.MaxLevel())
    	plaintext_y := hefloat.NewPlaintext(params, params.MaxLevel())
    	var ct_x *rlwe.Ciphertext
    	var ct_y *rlwe.Ciphertext
    
    	if err = encoder.Encode(x, plaintext_x); err != nil {
    		panic(err)
    	}
    	if err = encoder.Encode(y, plaintext_y); err != nil {
    		panic(err)
    	}
    
    	if ct_x, err = encryptor.EncryptNew(plaintext_x); err != nil {
    		panic(err)
    	}
    	if ct_y, err = encryptor.EncryptNew(plaintext_y); err != nil {
    		panic(err)
    	}
  • 进行密文计算

    go 复制代码
    	//(x-y)
    	var ct_sub *rlwe.Ciphertext
    	if ct_sub, err = evaluator.SubNew(ct_x, ct_y); err != nil {
    		panic(err)
    	}
    
    	//(x-y)^2
    	var ct_square *rlwe.Ciphertext
    	if ct_square, err = evaluator.MulRelinNew(ct_sub, ct_sub); err != nil {
    		panic(err)
    	}
  • 显示输出

    go 复制代码
    	//print x and y data
    	fmt.Printf("X: ")
    	for i := 0; i < 4; i++ {
    		fmt.Printf("%f ", x[i])
    	}
    	fmt.Println()
    
    	fmt.Printf("Y: ")
    	for i := 0; i < 4; i++ {
    		fmt.Printf("%f ", y[i])
    	}
    	fmt.Println()
    
    	//print x-y
    	pt_sub := decryptor.DecryptNew(ct_sub)
    	have := make([]float64, params.MaxSlots())
    	if err = encoder.Decode(pt_sub, have); err != nil {
    		panic(err)
    	}
    	fmt.Printf("X-Y: ")
    	for i := 0; i < 4; i++ {
    		fmt.Printf("%f ", have[i])
    	}
    	fmt.Println()
    
    	//print (x-y)^2
    	pt_square := decryptor.DecryptNew(ct_square)
    	if err = encoder.Decode(pt_square, have); err != nil {
    		panic(err)
    	}
    	fmt.Printf("(X-Y)^2: ")
    	for i := 0; i < 4; i++ {
    		fmt.Printf("%f ", have[i])
    	}
    	fmt.Println()
    }

通过这个例子,我们展现了一些基本的用法,当然基础的用法还包括一些操作比如evaluator.Add(),但其操作都比较类似,就不赘述了。

相关推荐
XiaoLeisj2 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
dayouziei2 小时前
java的类加载机制的学习
java·学习
励志成为嵌入式工程师3 小时前
c语言简单编程练习9
c语言·开发语言·算法·vim
捕鲸叉3 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer3 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
Peter_chq3 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
记录成长java5 小时前
ServletContext,Cookie,HttpSession的使用
java·开发语言·servlet
前端青山5 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js
睡觉谁叫~~~5 小时前
一文解秘Rust如何与Java互操作
java·开发语言·后端·rust
音徽编程5 小时前
Rust异步运行时框架tokio保姆级教程
开发语言·网络·rust