背景 同态加密在加密货币领域具有极强重要的作用, 在多方协同签名进行转账, 智能合约方面都有同态加密的身影。 看完本文, 你将有能力掌握同态加密的最经典算法 Paillier 同态加密, 并且使用Binance的 tss-lib 进行同态加密与解密。
什么是同态加密
同态加密其实是一种特殊的加密技术,它具有一种特殊的性质,即在密文上进行某种特定运算操作后,再解密得到的结果与在明文上进行同样的运算操作结果相同。
换句话说,同态加密允许在加密状态下进行计算操作,而无需解密。
比如 Paillier 同态加密就有下面这个性质:
(m1+m2)modn=D(E(m1,r1)⋅E(m2,r2)modn2)
这个性质就很有意思了,比如 m1 和 m2 是两个分片密钥被两个人把持, 这两个人想计算出共享密钥进行转账, 但谁都不想把自己的密钥泄露给另外一个人。这时候就可以找一个靠谱的第三方比如加密币交易所, 交易所提前分配给两个人一把公钥匙,交易所对两个人的密文进行计算然后解密即可得到共享密钥, 然后对转账进行签名, 整个过程是在内存中完成, 交易所本身并不会对分片密钥做任何保存,所以对于 m1 和 m2 密钥分片的持有者来说是安全的。 当然, 实际的交易所的密钥分发,保存,签名会比这个更加复杂和安全, 这里只是简单的举个例子。不过核心思想都是类似的,都是在想办法通过密文上的计算来间接计算明文或者验证明文。
前置知识
为了彻底理解同态加密,这里我列举了一些需要用到的数学定理,公式。
二次项定理/泰勒展开
(1+n)m=(0m)nm+(1m)nm−1+...+(m−2m)n2+(m−1m)n1+(0m)n0=(0m)nm+(1m)nm−1+...+(m−2m)n2+mn+1
若 上式 对 n的平方 进行取模, 可以看出 除了最后两项其他全部为零,也就是
(1+n)mmodn2=(mn+1)modn2
算术基本定理
任意正整数n可以写成多个质数次方的积:N=p1a1p2a2...pnan其中a1,a2...,an均为正整数
剩余类/同余类
设模为n,根据对n取模将所有正整数分为n类,记作[1,],[2],...[n−1]把所有与整数a模n同余的整数构成的集合叫做模n的一个剩余类,记作[a],a为[a]的代表元如n=5,a=1,[1]=1,6,11,26,21...这些数字对5取模都为1
费马小定理
若a为正整数,p为质数,则有:ap−1≡1modp
欧拉函数
ψ(n)为小于等于n的正整数中与n互质的数的数目如ψ(5)=4,因为有4个数与5互质分别是:1,2,3,4若为质数时很容易得到ψ(n)=n−1
欧拉函数性质
若p,q互质,n=pq,则ψ(n)=ψ(p)ψ(q)=(p−1)(q−1),
欧拉定理
若n与a互质,则aψ(n)≡1modn
Carmichael 函数
Carmichael函数λ(n)为满足aλ(n)≡1modn的最小值其中a为a模n的同余类的生成元λ(n)={ψ(n),n=1,2,3,4,5,6,7,9,10,11...n为奇质数的次方21ψ(n),n=8,16,32,64...n为2,4以外的2的次方如n=8,λ(8)=21ψ(8)=2a为[7]的同余类=7,15,23...可以验证:72≡1mod8,152≡1mod8,232≡1mod8
Carmichael 定理
λ(N)=λ(p1a1p2a2...pnan)=lcm[λ(p1a1)λ(p2a2)...λ(pnan)],其中lcm为lowestcommonvalue也就是最小公倍数
Carmichael 函数与欧拉定理
当n与a互质时,可以看出Carmichael函数就是欧拉函数,即:λ(n)=ψ(n)
根据欧拉函数性 与 Carmichael 定理, Carmichael 函数 也有如下性质:
若p,q互质,n=pq,则有λ(n)=λ(pq)=lcm[λ(p)λ(q)]=lcm[ψ(p)ψ(q)]=lcm[(p−1)(q−1)]
根据上式可以得出 p−1, q−1 与 λ(n) 之间的关系:
λ(n)为p−1的倍数λ(n)为q−1的倍数
Carmichael 函数重要推论
Carmichael 函数 有与Paillier 同态加密 算法紧密相关的两个重要推论:
对于任意正整数r,都有(1)rλ(n)≡1modn (2)rnλ(n)≡1modn2
若将 λ(n) 简写成 λ 则有:
对于任意正整数r,都有(1)rλ≡1modn (2)rnλ≡1modn2
下面来证明上面的 (1) 式 和 (2) 式
证明 (1)式
根据 Carmichael 函数与欧拉定理 章节部分有:
λ(n)=lcm[(p−1)(q−1)]λ(n)为p−1的倍数λ(n)为q−1的倍数
所以 λ(n) 可以写成如下形式:
{λ(n)=k1(p−1),一定存在一个正整数k1满足此式λ(n)=k2(q−1),一定存在一个正整数k2满足此式
将 rλ(n)=rk1(p−1) 重写:
rλ(n)=rk1(p−1)=(r(k1))p−1
根据 费马小定理 ap=1≡1modp 可得:
rλ(n)=rk1(p−1)=(r(k1))p−1≡1modp
即:
rλ(n)≡1modp
两边同时减去1 , 得:
rλ(n)−1≡0modp
也就是 rλ(n)−1为p 的倍数
同理: rλ(n)−1为q 的倍数
因为 rλ(n)−1为p和q 的公倍数, 而 n=pq, 所以有 rλ(n)−1为n 的公倍数, 用公式表达出来就是这样:
rλ(n)−1≡0modn,n=pqrλ(n)≡1modn
(1) 式得证
证明 (2)式
根据(1)式 :
rλ(n)≡1modn⇒rλ(n)−1≡0modn⇒rλ(n)−1为n的整数倍⇒一定存在一个整数k满足:rλ(n)−1=kn⇒一定存在一个整数k满足:rλ(n)=kn+1
两边同时取 n 次方得到:
rλ(n)n=(kn+1)n
两边同时对 n2 取模:
rλ(n)nmodn2=(kn+1)nmodn2
根据 二次项定理的推论,
rλ(n)nmodn2=(kn+1)nmodn2=(1+knn)modn2=(1+kn2)modn2=1⇒rλ(n)nmodn2=1⇒rλ(n)n≡1modn2
(2)式得证明
Paillier 同态加密
密钥生成
定义: p,q 为大素数, n =pq, g=n+1
定义: 公钥 pubKey=n, 私钥 priKey=(p,q)
安全性保障
可以看出 Paillier 加密的私钥安全性 依赖于 大整数的因数分解, 这点和RSA相同, 也就是同等密钥长度下, Paillier加密的安全性与RSA加密相当。
加密
密文 c=gm⋅rn⋅modn2 , 其中 m 为明文, r为随机数
解密
定义: λ=lcm((p−1),(q−1)),lcm为最小公倍数
定义: 函数 L(y)=ny−1
定义: μ=(L(gλ)modn2)−1modn
解密: m=L(cλmodn2)⋅μmodn2
正确性证明
下面来证明 m=L(cλmodn2)⋅μmodn2
1. 计算 cλmodn2
cλmodn2=(gm⋅rn)λmodn2=gλm⋅cλnmodn2
将上面的 Carmichael 函数重要推论的 (2)式 cλn≡1modn2 以及 g=1+n 带入上式得到:
cλmodn2=(gm⋅rn)λmodn2=gλm⋅cλnmodn2=gλm⋅1modn2=(1+n)λn⋅1modn2
根据上面 的 二项式展开公式可以看出 (n+1)λn⋅1modn2 只剩最后两项, 所以有:
cλmodn2=(gm⋅rn)λmodn2=gλm⋅cλnmodn2=gλm⋅1modn2=(1+n)λm⋅1modn2=(1+nmλ)modn2
2. 计算 L(cλmodn2)
L(cλmodn2)=ncλmodn2−1=n(1+nmλ)modn2−1=mλmodn2
3. 计算 gλmodn2
gλmodn2=(1+n)λmodn2
根据上面 的 二项式展开公式可以看出 (1+n)λmodn2 只剩最后两项, 所以有:
gλmodn2=(1+n)λmodn2=(1+nλ)modn2
4. 计算 L(gλmodn2)
L(gλmodn2)=n(1+nλ)modn2+1=λmodn2
5. 计算 μ=L(gλmodn2)−1modn
μ=L(gλmodn2)−1modn=λmodn2modn
6. 计算 L(cλmodn2)⋅μmodn
L(cλmodn2)⋅μmodn2=(mλmodn2)⋅λmodn2modn⋅modn=(mmodn)modn=mmodn,加密前确保m<n=m证明完毕!
同态性质
Paillier 加密满足两个同态性质, 同态加和同态乘
同态加:
(m1+m2)modn=D(E(m1,r1)⋅E(m2,r2)modn2)
同态乘
m1⋅m2modn=D(E(m1⋅r1)m2modn2)
这里就不详细证明了, 大家根据上面对于解密公式的证明顺着推导一下就能很容易得出这两个性质。
tss-lib 中的 同态加密
tss-lib/crypto/paillier/paillier_test.go
直接上单测函数, 代码比较简单, 我就不写注释了, 相信大家都可以看懂
package paillier
import (
"context"
"crypto/rand"
"math/big"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/bnb-chain/tss-lib/v2/common"
"github.com/bnb-chain/tss-lib/v2/crypto"
. "github.com/bnb-chain/tss-lib/v2/crypto/paillier"
"github.com/bnb-chain/tss-lib/v2/tss"
)
const (
testPaillierKeyLength = 2048
)
var (
privateKey *PrivateKey
publicKey *PublicKey
)
func setUp(t *testing.T) {
if privateKey != nil && publicKey != nil {
return
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
defer cancel()
var err error
privateKey, publicKey, err = GenerateKeyPair(ctx, rand.Reader, testPaillierKeyLength)
assert.NoError(t, err)
}
func TestGenerateKeyPair(t *testing.T) {
setUp(t)
assert.NotZero(t, publicKey)
assert.NotZero(t, privateKey)
t.Log(privateKey)
}
func TestEncrypt(t *testing.T) {
setUp(t)
cipher, err := publicKey.Encrypt(rand.Reader, big.NewInt(1))
assert.NoError(t, err, "must not error")
assert.NotZero(t, cipher)
t.Log(cipher)
}
func TestEncryptDecrypt(t *testing.T) {
setUp(t)
exp := big.NewInt(100)
cypher, err := privateKey.Encrypt(rand.Reader, exp)
if err != nil {
t.Error(err)
}
ret, err := privateKey.Decrypt(cypher)
assert.NoError(t, err)
assert.Equal(t, 0, exp.Cmp(ret),
"wrong decryption ", ret, " is not ", exp)
cypher = new(big.Int).Set(privateKey.N)
_, err = privateKey.Decrypt(cypher)
assert.Error(t, err)
}
func TestHomoMul(t *testing.T) {
setUp(t)
three, err := privateKey.Encrypt(rand.Reader, big.NewInt(3))
assert.NoError(t, err)
six := big.NewInt(6)
cm, err := privateKey.HomoMult(six, three)
assert.NoError(t, err)
multiple, err := privateKey.Decrypt(cm)
assert.NoError(t, err)
exp := int64(18)
assert.Equal(t, 0, multiple.Cmp(big.NewInt(exp)))
}
func TestHomoAdd(t *testing.T) {
setUp(t)
num1 := big.NewInt(10)
num2 := big.NewInt(32)
one, _ := publicKey.Encrypt(rand.Reader, num1)
two, _ := publicKey.Encrypt(rand.Reader, num2)
ciphered, _ := publicKey.HomoAdd(one, two)
plain, _ := privateKey.Decrypt(ciphered)
assert.Equal(t, new(big.Int).Add(num1, num2), plain)
}
巨人的肩膀
- github.com/bnb-chain/t…
- zh.wikipedia.org/zh-tw/%E4%B…
- zh.wikipedia.org/zh-tw/%E7%A…
- zh.wikipedia.org/zh-tw/%E6%9…
- zh.wikipedia.org/zh-tw/%E8%B…
- zh.wikipedia.org/zh-tw/%E6%A…
- zh.wikipedia.org/zh-tw/%E5%8…
- en.wikipedia.org/wiki/Pailli…
- www.bilibili.com/video/BV1bk…
- zh.wikipedia.org/zh-tw/RSA%E…
- case.ntu.edu.tw/blog/?p=330…