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(),但其操作都比较类似,就不赘述了。

相关推荐
凡人的AI工具箱10 分钟前
每天40分玩转Django:Django管理界面
开发语言·数据库·后端·python·django
每天写点bug25 分钟前
【go每日一题】:并发任务调度器
开发语言·后端·golang
一个不秃头的 程序员26 分钟前
代码加入SFTP Go ---(小白篇5)
开发语言·后端·golang
初学者7.29 分钟前
Webpack学习笔记(3)
笔记·学习·webpack
数据小爬虫@36 分钟前
Python爬虫抓取数据,有哪些常见的问题?
开发语言·爬虫·python
逊嘘1 小时前
【Java数据结构】ArrayList相关的算法
java·开发语言
基哥的奋斗历程1 小时前
初识Go语言
开发语言·后端·golang
煤泥做不到的!1 小时前
挑战一个月基本掌握C++(第六天)了解函数,数字,数组,字符串
开发语言·c++
智能与优化1 小时前
C++打造局域网聊天室第十一课: 程序关闭及线程的结束
开发语言·c++
璞~2 小时前
MQTT 课程概览 (学习笔记)02
笔记·学习