加密货币安全基石: 详解 Paillier 同态加密并使用币安tss-lib库

576 阅读6分钟

背景 同态加密在加密货币领域具有极强重要的作用, 在多方协同签名进行转账, 智能合约方面都有同态加密的身影。 看完本文, 你将有能力掌握同态加密的最经典算法 Paillier 同态加密, 并且使用Binance的 tss-lib 进行同态加密与解密。

什么是同态加密

同态加密其实是一种特殊的加密技术,它具有一种特殊的性质,即在密文上进行某种特定运算操作后,再解密得到的结果与在明文上进行同样的运算操作结果相同。

换句话说,同态加密允许在加密状态下进行计算操作,而无需解密。

比如 Paillier 同态加密就有下面这个性质:

(m1+m2)modn=D(E(m1,r1)E(m2,r2)modn2)(m1+m2) \enspace mod \enspace n =D(E(m1,r1)\cdot E(m2,r2) \enspace mod \enspace n^2)

这个性质就很有意思了,比如 m1 和 m2 是两个分片密钥被两个人把持, 这两个人想计算出共享密钥进行转账, 但谁都不想把自己的密钥泄露给另外一个人。这时候就可以找一个靠谱的第三方比如加密币交易所, 交易所提前分配给两个人一把公钥匙,交易所对两个人的密文进行计算然后解密即可得到共享密钥, 然后对转账进行签名, 整个过程是在内存中完成, 交易所本身并不会对分片密钥做任何保存,所以对于 m1 和 m2 密钥分片的持有者来说是安全的。 当然, 实际的交易所的密钥分发,保存,签名会比这个更加复杂和安全, 这里只是简单的举个例子。不过核心思想都是类似的,都是在想办法通过密文上的计算来间接计算明文或者验证明文。

前置知识

为了彻底理解同态加密,这里我列举了一些需要用到的数学定理,公式。

二次项定理/泰勒展开

1+nm=(m0)nm+(m1)nm1+...+(mm2)n2+(mm1)n1+(m0)n0=(m0)nm+(m1)nm1+...+(mm2)n2+mn+1(1+n)^m = \binom{m}{0}n^m + \binom{m}{1}n^{m-1} + ... + \binom{m}{m-2}n^2+ \binom{m}{m-1}n^1+\binom{m}{0}n^0 \\ = \binom{m}{0}n^m + \binom{m}{1}n^{m-1} +... + \binom{m}{m-2}n^2 + mn+1

若 上式 对 n的平方 进行取模, 可以看出 除了最后两项其他全部为零,也就是

1+nmmodn2=(mn+1)modn2(1+n)^m \enspace mod \enspace n^2 = (mn +1)\enspace mod \enspace n^2 \\

算术基本定理

任意正整数n可以写成多个质数次方的积:N=p1a1p2a2...pnan其中a1,a2...,an均为正整数 任意正整数 n 可以写成 多个质数次方的积: N = p_1^{a1} p_2^{a2} ... p_n^{an} \\ 其中 a1, a2..., an 均为正整数

剩余类/同余类

设模为n,根据对n取模将所有正整数分为n,记作[1,],[2],...[n1]把所有与整数an同余的整数构成的集合叫做模n的一个剩余类,记作[a],a[a]的代表元n=5,a=1,[1]=1,6,11,26,21...这些数字对5取模都为1设模为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为质数,则有:ap11modp若 a 为正整数, p 为质数, 则有 : \\ a^{p-1} \equiv 1 \enspace mod \enspace p

欧拉函数

ψ(n)为小于等于n的正整数中与n互质的数的数目ψ(5)=4,因为有4个数与5互质分别是:1,2,3,4为质数时很容易得到ψ(n)=n1\psi(n) 为 小于等于n的正整数中与n互质的数的数目 \\ 如 \enspace \psi(5) = 4,\enspace 因为有4个数与5互质 分别是 : 1,2,3,4 \\ 若 \enspace 为质数时 很容易得到 \psi(n) = n-1

欧拉函数性质

p,q互质,n=pq,ψ(n)=ψ(p)ψ(q)=(p1)(q1),若 p,q 互质, n =pq, 则 \psi(n) = \psi(p) \psi(q) = (p-1)(q-1),

欧拉定理

na互质,aψ(n)1modn若 \enspace n \enspace 与 \enspace a \enspace 互质, 则 \enspace a^{\psi(n)} \equiv 1 \enspace mod \enspace n \enspace

Carmichael 函数

Carmichael函数λ(n)为满足aλ(n)1modn的最小值其中aan的同余类的生成元λ(n)={ψ(n),n=1,2,3,4,5,6,7,9,10,11...n为奇质数的次方12ψ(n),n=8,16,32,64...n2,4以外的2的次方n=8,λ(8)=12ψ(8)=2a[7]的同余类=7,15,23...可以验证:721mod8,1521mod8,2321mod8Carmichael \enspace 函数 \enspace \lambda(n) \enspace 为满足 \enspace a^{\lambda(n)} \equiv 1 \enspace mod \enspace n \enspace 的最小值 \\ 其中 \enspace a \enspace 为 \enspace a \enspace 模 \enspace n \enspace 的同余类的生成元 \\ \lambda(n) = \begin{cases} \psi(n), n =1,2,3,4,5,6,7,9,10,11... n 为奇质数的次方 \\ \frac{1}{2} \psi(n), n =8,16,32,64 ... n 为2,4以外的2的次方 \end{cases} \\ 如 n=8, \lambda(8) = \frac{1}{2}\psi(8) =2 \\ a为 [7]的同余类={7,15,23... } \\ 可以验证: 7^2 \equiv \enspace 1 \enspace mod \enspace 8 \enspace , 15^2 \equiv \enspace 1 \enspace mod \enspace 8 \enspace ,23^2 \equiv \enspace 1 \enspace mod \enspace 8 \enspace

Carmichael 定理

λ(N)=λ(p1a1p2a2...pnan)=lcm[λ(p1a1)λ(p2a2)...λ(pnan)],其中lcmlowestcommonvalue也就是最小公倍数 \lambda(N) = \lambda(p_1^{a1} p_2^{a2} ... p_n^{an} ) = l_{cm}[\lambda(p_1^{a1})\lambda(p_2^{a2}) ... \lambda(p_n^{an}) ], \\ 其中 l_{cm} 为 \enspace lowest \enspace common \enspace value \enspace也就是最小公倍数

Carmichael 函数与欧拉定理

na互质时,可以看出Carmichael函数就是欧拉函数,:λ(n)=ψn当 n 与 a 互质时, 可以看出 \enspace Carmichael \enspace 函数 就是 欧拉函数, 即: \enspace \lambda(n) = \psi(n)

根据欧拉函数性 与 Carmichael 定理, Carmichael 函数 也有如下性质:

p,q互质,n=pq,则有λ(n)=λ(pq)=lcm[λ(p)λ(q)]=lcm[ψ(p)ψ(q)]=lcm[(p1)(q1)] 若 p, q 互质, n = pq, \\ 则有 \enspace \lambda(n) = \lambda(pq) =l_{cm}[\lambda(p)\lambda(q)] = l_{cm}[\psi(p)\psi(q)] = l_{cm}[(p-1)(q-1)]

根据上式可以得出 p1p-1, q1q-1λ(n)\lambda(n) 之间的关系:

λ(n)p1的倍数λ(n)q1的倍数\lambda(n) 为 p-1 \enspace 的 倍数 \\ \lambda(n) 为 q-1 \enspace 的 倍数

Carmichael 函数重要推论

Carmichael 函数 有与Paillier 同态加密 算法紧密相关的两个重要推论:

对于任意正整数r,都有(1)rλ(n)1modn (2)rnλ(n)1modn2对于任意正整数 r, 都有 \\ (1) \enspace r^{\lambda(n)} \equiv 1 \enspace mod \enspace n \\\ (2) \enspace r^{n\lambda(n)} \equiv 1 \enspace mod \enspace n^2

若将 λ(n)\lambda(n) 简写成 λ\lambda 则有:

对于任意正整数r,都有(1)rλ1modn (2)rnλ1modn2对于任意正整数 r, 都有 \\ (1) \enspace r^{\lambda} \equiv 1 \enspace mod \enspace n \\\ (2) \enspace r^{n\lambda} \equiv 1 \enspace mod \enspace n^2

下面来证明上面的 (1) 式 和 (2) 式

证明 (1)式

根据 Carmichael 函数与欧拉定理 章节部分有:

λ(n)=lcm[(p1)(q1)]λ(n)p1的倍数λ(n)q1的倍数\lambda(n) = l_{cm}[(p-1)(q-1)] \\ \lambda(n) 为 p-1 \enspace 的 倍数 \\ \lambda(n) 为 q-1 \enspace 的 倍数

所以 λ(n)\lambda(n) 可以写成如下形式:

{λ(n)=k1(p1),一定存在一个正整数k1满足此式λ(n)=k2(q1),一定存在一个正整数k2满足此式\begin{cases} \lambda(n) = k1(p-1), 一定存在一个正整数k1满足此式 \\ \lambda(n) = k2(q-1), 一定存在一个正整数k2满足此式 \end{cases}

rλ(n)=rk1(p1)r^{\lambda(n)} = r^{k1(p-1)} 重写:

rλ(n)=rk1(p1)=(r(k1))p1r^{\lambda(n)} = r^{k1(p-1)} = (r^{(k1)})^{p-1}

根据 费马小定理 ap=11modpa^{p=1} \equiv 1 mod p 可得:

rλ(n)=rk1(p1)=(r(k1))p11modpr^{\lambda(n)} = r^{k1(p-1)} = (r^{(k1)})^{p-1}\equiv 1 mod p

即:

rλ(n)1modpr^{\lambda(n)} \equiv 1 mod p

两边同时减去1 , 得:

rλ(n)10modpr^{\lambda(n)} - 1 \equiv 0 mod p

也就是 rλ(n)1p r^{\lambda(n)} - 1 为 p 的倍数

同理: rλ(n)1q r^{\lambda(n)} - 1 为 q 的倍数

因为 rλ(n)1pq r^{\lambda(n)} - 1 为 p 和 q 的公倍数, 而 n=pq n=pq, 所以有 rλ(n)1n r^{\lambda(n)} - 1 为 n 的公倍数, 用公式表达出来就是这样:

rλ(n)10modn,n=pqrλ(n)1modnr^{\lambda(n)} - 1 \equiv 0 \enspace mod \enspace n, \enspace n = pq \\ r^{\lambda(n)} \equiv 1 \enspace mod \enspace n

(1) 式得证

证明 (2)式

根据(1)式 :

rλ(n)1modnrλ(n)10modnrλ(n)1n的整数倍一定存在一个整数k满足:rλ(n)1=kn一定存在一个整数k满足:rλ(n)=kn+1r^{\lambda(n)} \equiv 1 \enspace mod \enspace n \\ \Rightarrow r^{\lambda(n)} -1 \equiv 0\enspace mod \enspace n \\ \Rightarrow r^{\lambda(n)} -1 为 \enspace n \enspace 的整数倍 \\ \Rightarrow 一定存在一个整数 k 满足: r^{\lambda(n)} -1 = kn \\ \Rightarrow 一定存在一个整数 k 满足: r^{\lambda(n)} = kn+1

两边同时取 n 次方得到:

rλ(n)n=(kn+1)nr^{\lambda(n)n} = (kn+1)^n

两边同时对 n2n^2 取模:

rλ(n)nmodn2=(kn+1)nmodn2r^{\lambda(n)n} \enspace mod \enspace n^2= (kn+1) ^n \enspace mod \enspace n^2

根据 二次项定理的推论,

rλ(n)nmodn2=(kn+1)nmodn2=(1+knn)modn2=(1+kn2)modn2=1rλ(n)nmodn2=1rλ(n)n1modn2r^{\lambda(n)n} \enspace mod \enspace n^2 = (kn+1) ^n \enspace mod \enspace n^2 = (1+knn) \enspace mod \enspace n^2 = (1+kn^2) \enspace mod \enspace n^2 =1 \\ \Rightarrow r^{\lambda(n)n} \enspace mod \enspace n^2 =1 \\ \Rightarrow r^{\lambda(n)n} \equiv 1 \enspace mod \enspace n^2

(2)式得证明

Paillier 同态加密

密钥生成

定义: p,q 为大素数, n =pq, g=n+1

定义: 公钥 pubKey=npubKey = n, 私钥 priKey=p,qpriKey = (p,q)

安全性保障

可以看出 Paillier 加密的私钥安全性 依赖于 大整数的因数分解, 这点和RSA相同, 也就是同等密钥长度下, Paillier加密的安全性与RSA加密相当。

加密

密文 c=gmrnmodn2c \enspace = \enspace g^m \cdot r^n \cdot mod \enspace n^2 , 其中 mm 为明文, r为随机数r 为随机数

解密

定义: λ=lcm((p1),(q1)),lcm为最小公倍数\lambda = l_{cm}((p-1),(q-1)), \enspace l_{cm} 为最小公倍数

定义: 函数 L(y)=y1nL(y)=\frac{y-1}{n}

定义: μ=(L(gλ)modn2)1modn\mu = (L(g^{\lambda})\enspace mod \enspace n^2)^{-1} \enspace mod \enspace n

解密: m=L(cλmodn2)μmodn2m= L(c^{\lambda} \enspace mod \enspace n^2) \cdot \mu \enspace mod \enspace n^2

正确性证明

下面来证明 m=L(cλmodn2)μmodn2m= L(c^{\lambda} \enspace mod \enspace n^2) \cdot \mu \enspace mod \enspace n^2

1. 计算 cλmodn2c^{\lambda} \enspace mod \enspace n^2

cλmodn2=(gmrn)λmodn2=gλmcλnmodn2c^{\lambda} \enspace mod \enspace n^2 = (g^m \cdot r^n)^{\lambda} \enspace mod \enspace n^2 \\ = g^{\lambda m} \cdot c^{\lambda n} \enspace mod \enspace n^2

将上面的 Carmichael 函数重要推论的 (2)式 cλn1modn2c^{\lambda n} \equiv 1 \enspace mod \enspace n^2 以及 g=1+ng=1+n 带入上式得到:

cλmodn2=(gmrn)λmodn2=gλmcλnmodn2=gλm1modn2=1+nλn1modn2c^{\lambda} \enspace mod \enspace n^2 = (g^m \cdot r^n)^{\lambda} \enspace mod \enspace n^2 \\ = g^{\lambda m} \cdot c^{\lambda n} \enspace mod \enspace n^2 \\ = g^{\lambda m} \cdot 1 mod \enspace n^2 \\ = (1+n)^{\lambda n} \cdot 1 mod \enspace n^2

根据上面 的 二项式展开公式可以看出 n+1λn1modn2(n+1)^{\lambda n} \cdot 1 mod \enspace n^2 只剩最后两项, 所以有:

cλmodn2=(gmrn)λmodn2=gλmcλnmodn2=gλm1modn2=1+nλm1modn2=(1+nmλmodn2c^{\lambda} \enspace mod \enspace n^2 = (g^m \cdot r^n)^{\lambda} \enspace mod \enspace n^2 \\ = g^{\lambda m} \cdot c^{\lambda n} \enspace mod \enspace n^2 \\ = g^{\lambda m} \cdot 1 mod \enspace n^2 \\ = (1+n)^{\lambda m} \cdot 1 mod \enspace n^2 \\ = (1+ nm\lambda) mod \enspace n^2

2. 计算 L(cλmodn2)L(c^{\lambda} \enspace mod \enspace n^2)

L(cλmodn2)=cλmodn21n=(1+nmλmodn21n=mλmodn2L(c^{\lambda} \enspace mod \enspace n^2) = \frac{c^{\lambda} \enspace mod \enspace n^2-1}{n} \\ = \frac{(1+ nm\lambda) mod \enspace n^2 -1}{n} \\ = m\lambda \enspace mod \enspace n^2

3. 计算 gλmodn2g^{\lambda } \enspace mod \enspace n^2

gλmodn2=(1+n)λmodn2g^{\lambda } \enspace mod \enspace n^2 = (1+n)^{\lambda} \enspace mod \enspace n^2

根据上面 的 二项式展开公式可以看出 (1+n)λmodn2(1+n)^{\lambda} \enspace mod \enspace n^2 只剩最后两项, 所以有:

gλmodn2=(1+n)λmodn2=(1+nλ)modn2g^{\lambda } \enspace mod \enspace n^2 = (1+n)^{\lambda} \enspace mod \enspace n^2 \\ = (1+n\lambda) \enspace mod \enspace n^2

4. 计算 L(gλmodn2) L( g^{\lambda } \enspace mod \enspace n^2 )

L(gλmodn2)=(1+nλ)modn2+1n=λmodn2 L( g^{\lambda } \enspace mod \enspace n^2 ) = \frac{(1+n\lambda) \enspace mod \enspace n^2 +1}{n} \\ = \lambda \enspace mod \enspace n^2

5. 计算 μ=L(gλmodn2)1modn\mu = L( g^{\lambda } \enspace mod \enspace n^2 )^{-1} \enspace mod \enspace n

μ=L(gλmodn2)1modn=modnλmodn2\mu = L( g^{\lambda } \enspace mod \enspace n^2 )^{-1} \enspace mod \enspace n = \frac{mod \enspace n}{\lambda \enspace mod \enspace n^2}

6. 计算 L(cλmodn2)μmodnL(c^{\lambda} \enspace mod \enspace n^2) \cdot \mu \enspace mod \enspace n

L(cλmodn2)μmodn2=(mλmodn2)modnλmodn2modn=(mmodn)modn=mmodn,加密前确保m<n=m证明完毕!L(c^{\lambda} \enspace mod \enspace n^2) \cdot \mu \enspace mod \enspace n^2 = (m\lambda \enspace mod \enspace n^2) \cdot \frac{mod \enspace n}{\lambda \enspace mod \enspace n^2} \cdot mod \enspace n \\ = (m \enspace mod \enspace n ) mod \enspace n \\ = m \enspace mod \enspace n , 加密前确保 m<n \\ = m \\ 证明完毕!

同态性质

Paillier 加密满足两个同态性质, 同态加和同态乘

同态加:

(m1+m2)modn=D(E(m1,r1)E(m2,r2)modn2) (m1+m2) \enspace mod \enspace n =D(E(m1,r1)\cdot E(m2,r2) \enspace mod \enspace n^2) \\ \\

同态乘

m1m2modn=D(E(m1r1)m2modn2) m1 \cdot m2 \enspace mod \enspace n = D(E(m1 \cdot r1 )^{m2} \enspace mod \enspace n^2)

这里就不详细证明了, 大家根据上面对于解密公式的证明顺着推导一下就能很容易得出这两个性质。

tss-lib 中的 同态加密

tss-lib/crypto/paillier/paillier_test.go

直接上单测函数, 代码比较简单, 我就不写注释了, 相信大家都可以看懂

// Copyright © 2019 Binance
//
// This file is part of Binance. The full Binance copyright notice, including
// terms governing use, modification, and redistribution, is contained in the
// file LICENSE at the root of the source code distribution tree.

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"
)

// Using a modulus length of 2048 is recommended in the GG18 spec
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)

	// for HomoMul, the first argument `m` is not ciphered
	six := big.NewInt(6)

	cm, err := privateKey.HomoMult(six, three)
	assert.NoError(t, err)
	multiple, err := privateKey.Decrypt(cm)
	assert.NoError(t, err)

	// 3 * 6 = 18
	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)
}

巨人的肩膀

  1. github.com/bnb-chain/t…
  2. zh.wikipedia.org/zh-tw/%E4%B…
  3. zh.wikipedia.org/zh-tw/%E7%A…
  4. zh.wikipedia.org/zh-tw/%E6%9…
  5. zh.wikipedia.org/zh-tw/%E8%B…
  6. zh.wikipedia.org/zh-tw/%E6%A…
  7. zh.wikipedia.org/zh-tw/%E5%8…
  8. en.wikipedia.org/wiki/Pailli…
  9. www.bilibili.com/video/BV1bk…
  10. zh.wikipedia.org/zh-tw/RSA%E…
  11. case.ntu.edu.tw/blog/?p=330…