Before realizing this, it's important to understand whathomomorphic encryption。
The following formula makes what we are trying to achievehomomorphic encryptionreleases
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⌋
The first type of encryption
symmetric encryption
If the key S is a unitary matrix, then c is nothing more than a reweighted, slightly noisy version of the input x.
When S is a unit matrix, it is equivalent to no encryption
When S is a random matrix, there are encrypted
Use the same key for encryption and decryption
import numpy as np
def generate_key(w,m,n):
S = (np.random.rand(m,n) * w / (2 ** 16))# It can be shown that max(S) < w
return S# key, symmetric encryption
def encrypt(x,S,m,n,w):
assert len(x) == len(S)
e = (np.random.rand(m))# It can be shown that 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)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
c = encrypt(x,S,m,n,w)
decrypt(c,S,w)
- 1
- 2
array([0, 1, 2, 5])
print(x+x)
print(x*10)
- 1
- 2
[ 0 2 4 10]
[ 0 10 20 50]
decrypt(c+c,S,w)
- 1
array([ 0, 2, 4, 10])
decrypt(c*10,S,w)
- 1
array([ 0, 10, 20, 50])
x*x
- 1
array([0, 1, 4])
decrypt(c*c,S,w)
- 1
array([15296055, 6267577, 8584289])
Second encryption
Instead of explicitly assigning a separate pair of "public key" and "private key", the authors of the paper propose a "key exchange" technique, where the private key S is replaced by S '. More specifically, this private key exchange technique involves generating a matrix that can perform this transformation, M. Since M has the ability to transform a message from an unencrypted state (a unit matrix key) to an encrypted state (a random and hard-to-guess key), this M matrix can be used as our public key!
Based on the opening two equations, if the key is a unitary matrix, then the message is unencrypted.
Based on the opening two equations, the message is encrypted if the key is a random matrix.
We construct a matrix M to convert one key to another private key.
When the matrix M converts the unit matrix into a random key, by definition it encrypts the message using one-way encryption.
Since M acts as a "one-way encryption", we call it a "public key" and can distribute it like a public key because it cannot be used for decryption.
import numpy as np
def generate_key(w,m,n):
S = (np.random.rand(m,n) * w / (2 ** 16)) # It can be shown that max(S) < w
return S
def encrypt(x,S,m,n,w):
assert len(x) == len(S)
e = (np.random.rand(m)) # It can be shown that 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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
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)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
[ 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)
- 1
- 2
- 3
- 4
- 5
- 6
[-115. -122. -21. -35. 23.]
[-177. -222. -87. -177. 45.]
print(decrypt(cx,S,w))
print(decrypt(cy,S,w))
- 1
- 2
[0 1 3 5]
[3 3 3 3]
decrypt(cx * 10,S,w)
- 1
array([ 0, 10, 30, 50])
decrypt(cx + cy,S,w)
- 1
array([3, 4, 6, 8])