在实现之前,先了解一下什么是同态加密。
以下这个公式使我们要实现的同态加密版本
S c = w x + e x = ⌈ S c w ⌋ S \mathbf{c}=w \mathbf{x}+\mathbf{e} \quad \mathbf{x}=\left\lceil\frac{S \mathbf{c}}{w}\right\rfloor Sc=wx+ex=⌈wSc⌋
第一种加密
对称加密
如果密钥S是一个单位矩阵,那么c不过是输入x的一个重加权的、略带噪声的版本。
当S为单位矩阵时,相当于没有加密
当S为随机矩阵时,有加密
加解密使用同一key
import numpy as np
def generate_key(w,m,n):
S = (np.random.rand(m,n) * w / (2 ** 16))# 可证明 max(S) < w
return S# key,对称加密
def encrypt(x,S,m,n,w):
assert len(x) == len(S)
e = (np.random.rand(m))# 可证明 max(e) < w / 2
c = np.linalg.inv(S).dot((w * x) + e)
return c
def decrypt(c,S,w):
return (S.dot(c) / w).astype('int')
x = np.array([0,1,2,5])
m = len(x)
n = m
w = 16
S = generate_key(w,m,n)
c = encrypt(x,S,m,n,w)
decrypt(c,S,w)
array([0, 1, 2, 5])
print(x+x)
print(x*10)
[ 0 2 4 10]
[ 0 10 20 50]
decrypt(c+c,S,w)
array([ 0, 2, 4, 10])
decrypt(c*10,S,w)
array([ 0, 10, 20, 50])
x*x
array([0, 1, 4])
decrypt(c*c,S,w)
array([15296055, 6267577, 8584289])
第二种加密
论文作者没有显式地分配一对独立的“公钥”和“私钥”,相反,提出了一种“钥交换”技术,将私钥S替换为S’。更具体地,这一私钥交换技术涉及生成一个可以进行该变换的矩阵M。由于M具备将消息从未加密状态(单位矩阵密钥)转换为加密状态(随机而难以猜测的密钥),这个M矩阵正好可以用作我们的公钥!
基于开篇两个公式,如果密钥是一个单位矩阵,那么消息是未加密的。
基于开篇两个公式,如果密钥是一个随机矩阵,那么消息是加密的。
我们构造一个矩阵M将一个密钥转换为另一个私钥。
当矩阵M将单位矩阵转换为一个随机密钥时,根据定义,它使用单向加密方式加密了消息。
由于M充当了“单向加密”的角色,我们称它为“公钥”,并且可以像公钥一样分发它,因为它无法用于解密。
import numpy as np
def generate_key(w,m,n):
S = (np.random.rand(m,n) * w / (2 ** 16)) # 可证明 max(S) < w
return S
def encrypt(x,S,m,n,w):
assert len(x) == len(S)
e = (np.random.rand(m)) # 可证明 max(e) < w / 2
c = np.linalg.inv(S).dot((w * x) + e)
return c
def decrypt(c,S,w):
return (S.dot(c) / w).astype('int')
def get_c_star(c,m,l):
c_star = np.zeros(l * m,dtype='int')
for i in range(m):
b = np.array(list(np.binary_repr(np.abs(c[i]))),dtype='int')
if(c[i] < 0):
b *= -1
c_star[(i * l) + (l-len(b)): (i+1) * l] += b
return c_star
def switch_key(c,S,m,n,T):
l = int(np.ceil(np.log2(np.max(np.abs(c)))))
c_star = get_c_star(c,m,l)
S_star = get_S_star(S,m,n,l)
n_prime = n + 1
S_prime = np.concatenate((np.eye(m),T.T),0).T
A = (np.random.rand(n_prime - m, n*l) * 10).astype('int')
E = (1 * np.random.rand(S_star.shape[0],S_star.shape[1])).astype('int')
M = np.concatenate(((S_star - T.dot(A) + E),A),0)
c_prime = M.dot(c_star)
return c_prime,S_prime
def get_S_star(S,m,n,l):
S_star = list()
for i in range(l):
S_star.append(S*2**(l-i-1))
S_star = np.array(S_star).transpose(1,2,0).reshape(m,n*l)
return S_star
def get_T(n):
n_prime = n + 1
T = (10 * np.random.rand(n,n_prime - n)).astype('int')
return T
def encrypt_via_switch(x,w,m,n,T):
c,S = switch_key(x*w,np.eye(m),m,n,T)
return c,S
x = np.array([0,1,3,5])
m = len(x)
n = m
w = 16
S = generate_key(w,m,n)
y = np.array([3,3,3,3])
m = len(y)
n = m
w = 16
S = generate_key(w,m,n)
print(x * 10)
print(x + y)
[ 0 10 30 50]
[3 4 6 8]
T = get_T(n)
cx,S = encrypt_via_switch(x,w,m,n,T)
print(cx)
#T = get_T(n)
cy,S = encrypt_via_switch(y,w,m,n,T)
print(cy)
[-115. -122. -21. -35. 23.]
[-177. -222. -87. -177. 45.]
print(decrypt(cx,S,w))
print(decrypt(cy,S,w))
[0 1 3 5]
[3 3 3 3]
decrypt(cx * 10,S,w)
array([ 0, 10, 30, 50])
decrypt(cx + cy,S,w)
array([3, 4, 6, 8])