Commit 4ad1d97a authored by Sophie Leuenberger's avatar Sophie Leuenberger
Browse files

Initial commit.

parents
import random
import time
def build_McEliece_cryptosystem(q=7, n=20, m=3, k=5):
"""Set up a McEliece cryptosystem with
:param q: prime base
:param n: length of linear code
:param m: extension degree
:param k: dimension of underlying GRS code
:return: public and private key of McEliece cryptosystem
"""
base_field = GF(q)
extension_field = GF(q**m, 'z')
# Check parameters:
if n > extension_field.cardinality():
raise ValueError("Invalid parameters: n > |F|")
if k > n:
raise ValueError("Invalid parameters: k>n")
print "Generating Code with n={0}, k={1}, m={2} and q={3}".format(n, k, m, q)
t0 = time.time()
# Construct random support and multiplier vectors
a = extension_field.gen()
support = [a^i for i in range(n-1)]+[0]
l = list(extension_field)
l.remove(extension_field.zero())
multiplier = []
for i in range(n):
multiplier.append(random.choice(l))
# Build alternant code:
Alt_k = generate_alternant_code(extension_field, n, m, k, support, multiplier)
# Build error-correcting pair
A, B = generate_ecp(extension_field, n, k, support, multiplier)
# Max. number of error positions:
t = floor(k/2)
# Build public key
G_public, P, S = hide_structure(Alt_k)
t1 = time.time() - t0
print "Dimension of alternant code={0}. Time elapsed: {1} seconds.".format(Alt_k.dimension(), t1)
return G_public, P, S, Alt_k, A, B, t
def generate_alternant_code(F, n, m, k, supp, mult):
"""Generate an alternant code with parameters
:param F: extension field
:param n: code length
:param m: extension degree
:param k: dimension of underlying GRS code
:param supp: support vector
:param mult: multiplier vector
:return: Alternant code of lenght n and degree k
"""
# Build GRS_k(supp, mult):
G_GRS_k = matrix(F, k, n, 0)
for i in range(k):
for j in range(n):
G_GRS_k[i,j] = mult[j]*supp[j]**i
# Build dual code:
GRS_k = LinearCode(G_GRS_k)
GRS_k_dual = GRS_k.dual_code()
# Get parity check matrix of alt_k over fq
H_ = GRS_k_dual.parity_check_matrix()
H = matrix(F.base_ring(), m*H_.nrows(), n, 0)
FF = F.vector_space()
for i in range(H_.nrows()):
for j in range(n):
elem = H_[i,j]
coerced_elem = FF(elem)
for l in range(m):
H[l+i*m, j] = coerced_elem[l]
# Construct generator matrix for alternant code:
ker = H.right_kernel()
G_alternant = ker.basis_matrix()
Alt_k = LinearCode(G_alternant)
if Alt_k.dimension() == 0:
raise ValueError("Invalid parameters.")
return Alt_k
def generate_ecp(extension_field, n, k, supp, mult):
"""Generate an error-correcting pair for alternant code
:param extension_field: base_field of underlying GRS code
:param n: code length
:param k: degree of alternant code
:param supp: support vector of alternant code
:param mult: multiplier vector of alternant code
:return: error-correcting pair (A, B)
"""
t = floor(k/2)
# Construct error-correcting pair
G_A = matrix(extension_field, t+1, n, 0)
for i in range(G_A.nrows()):
for j in range(G_A.ncols()):
G_A[i, j] = supp[j]**i
A = LinearCode(G_A)
G_B = matrix(extension_field, t, n, 0)
for i in range(G_B.nrows()):
for j in range(G_B.ncols()):
G_B[i,j] = mult[j]*(supp[j]**i)
B = LinearCode(G_B)
return A, B
def hide_structure(code):
"""Hide structure of alternant code by modifying generator matrix
:param code: code whose structure should be hidden
:return: modified generator matrix, permutation matrix P, scrambler matrix S
"""
K = code.base_field()
n = code.generator_matrix().ncols()
dim_code = code.generator_matrix().nrows()
indices = range(n)
random.shuffle(indices)
P = matrix(K, n, n, 0)
for i in xrange(n):
P[i, indices[i]] = K.one()
S = random_matrix(K, dim_code, dim_code, algorithm='unimodular')
return S*code.generator_matrix()*P, P, S
def build_decrypter(path):
"""Build the decrypter script
:param path: name of the private folder
"""
out_str = '''import time
def run_decryption(P, S, Alt_k, A, B, message):
"""Run decryption on message with given private key arguments"""
t0 = time.time()
base_field = Alt_k.base_field()
word = vector(base_field, message)
code = decrypt(P, S, Alt_k, A, B, word)
t1 = t1 = time.time() - t0
print "Time consumption for decryption process:{0} seconds.".format(t1)
return code
def decrypt(P, S, Alt_k, A, B, word):
"""Decrypt word with given private key arguments"""
y = word * P.inverse()
code_word = decode_alternant(Alt_k, A, B, y)
solve_mat = S*Alt_k.generator_matrix()
message = solve_mat.solve_left(code_word)
return message
def decode_alternant(Alt_k, A, B, y):
"""Decode y = c + e where c is an element of Alt_k and e is an error vector"""
t0 = time.time()
F = A.base_field()
n = len(y)
ky_element = 0
K = F.base_ring()
# Compute the kernel of received word y:
ky_element = compute_kernel_element(A, B, y)
if ky_element == 0:
return -1
J = []
for i in range(n):
if ky_element[i] == 0:
J.append(i)
# Solve linear equation to get error vector:
H = Alt_k.parity_check_matrix()
J_comp = range(H.ncols())
for i in J:
J_comp.remove(i)
H_J = H[:,J]
H_J_comp = H[:,J_comp]
y_J = vector(F,[y[i] for i in xrange(n) if i in J])
y_J_comp = vector(F,[y[i] for i in xrange(n) if i not in J])
temp = H_J*y_J.column()+H_J_comp*(y_J_comp.column())
sol_ = H_J.solve_right(temp)
sol_e = vector(K, n)
index=0
for j in J:
sol_e[j] = sol_[index][0]
index+=1
sol_c = y-sol_e
t1 = time.time() - t0
print "Time consumption for decoding process:{0} seconds.".format(t1)
return sol_c
def compute_kernel_element(A, B, y):
"""Compute the kernel ker_y of y and return an arbitrary element of ker_y"""
T = matrix(A.base_ring(), A.dimension(), B.dimension(), 0)
for i in range(A.dimension()):
for j in range(B.dimension()):
T[i,j] = inner_star_prod(A.basis()[i], B.basis()[j], y)
ker = kernel(T)
if ker.cardinality() == 0:
raise ValueError("something went wrong! empty kernel!")
lambdas = ker.random_element()
while lambdas == ker.zero():
lambdas = ker.random_element()
A_base_mat = matrix(A.base_ring(), A.basis())
a_rand = lambdas * A_base_mat
return a_rand
def inner_star_prod(a,b,y):
"""Compute the dot product of a*b and y"""
n = len(y)
res = 0
for i in range(n):
res += a[i]*b[i]*y[i]
return res
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('message', help='encoded message you want to decode (put " around it)', type=str)
args = parser.parse_args()
# Load the private key from path
p = load('P.sobj')
s = load('S.sobj')
alt_k = load('alt_k.sobj')
A = load('A.sobj')
B = load('B.sobj')
message = eval(args.message)
word = run_decryption(p, s, alt_k, A, B, message[0])
print word
'''
filehandle = open(path + '/decrypter.sage', 'w')
filehandle.write(out_str)
filehandle.close()
def build_encrypter(name):
out_str = '''import time
def generate_random_key(keylength, base_field):
"""Generate a random key to be encrypted"""
key = []
for i in range(keylength):
key.append(base_field.random_element())
return key
if __name__ == '__main__':
import random
import time
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('gen_mat', help='File with generator matrix (part of public key)', type=str)
parser.add_argument('t', help='t (part of public key)', type=int)
args = parser.parse_args()
gen_mat = load(args.gen_mat)
n = gen_mat.ncols()
l = gen_mat.nrows()
base_field = gen_mat.base_ring()
key = generate_random_key(l, base_field)
print "Randomly generated key is: {0}".format(key)
t0 = time.time()
encoded_key = vector(base_field, key)*gen_mat
t = args.t
encrypted = []
check_list = random.sample(range(n), t)
error_list = [0 if x not in check_list else random.randint(1, base_field.characteristic()-1)
for x in xrange(n)]
error = vector(base_field, error_list)
encrypted.append(encoded_key + error)
t1 = time.time() - t0
print "Time consumption for encryption process:{0} seconds.".format(t1)
out_str = str(encrypted).replace(' ','')
print out_str
'''
file_handle = open(name + '_encrypter.sage', 'w')
file_handle.write(out_str)
file_handle.close()
if __name__ == '__main__':
import argparse
import os
parser = argparse.ArgumentParser()
parser.add_argument('q', help='prime base for the algorithm, at least 31, best >= 163', type=int, default=271)
parser.add_argument('m', help='extension degree', type=int, default=3)
parser.add_argument('n', help='length of code words. Smaller or equal than q^m', type=int, default=20)
parser.add_argument('k', help='dimension of the code', type=int, default=5)
parser.add_argument('name', help='give your code a name. Will create a folder of same name in cwd with decoder',
type=str)
args = parser.parse_args()
public, P, S, alt_k, A, B, t = build_McEliece_cryptosystem(args.q, args.n, args.m, args.k)
path_string = './{0}'.format(args.name)
if not os.path.exists(path_string):
os.makedirs(path_string)
else:
print "Path already exists. Probably overwriting existing code."
alt_k.save(path_string + '/alt_k')
public.save(path_string + '.public')
t.save(path_string + '.errorCorrectionCapability')
A.save(path_string + '/A')
B.save(path_string + '/B')
P.save(path_string + '/P')
S.save(path_string + '/S')
build_decrypter(path_string)
build_encrypter(path_string)
print 'Generator Matrix is in "{0}.public.sobj". t is {1}'.format(path_string, t)
import random
import time
def build_McEliece_cryptosystem(q=7, n=20, m=3, k=5):
"""Set up a McEliece cryptosystem with
:param q: prime base
:param n: length of linear code
:param m: extension degree
:param k: dimension of underlying GRS code
:return: public and private key of McEliece cryptosystem
"""
base_field = GF(q)
extension_field = GF(q**m, 'z')
# Check parameters:
if n > extension_field.cardinality():
raise ValueError("Invalid parameters: n > |F|")
if k > n:
raise ValueError("Invalid parameters: k>n")
print "Generating Code with n={0}, k={1}, m={2} and q={3}".format(n, k, m, q)
t0 = time.time()
# Construct random support and multiplier vectors
a = extension_field.gen()
support = [a^i for i in range(n-1)]+[0]
l = list(extension_field)
l.remove(extension_field.zero())
multiplier = []
for i in range(n):
multiplier.append(random.choice(l))
# Build alternant code:
Alt_k = generate_alternant_code(extension_field, n, m, k, support, multiplier)
# Build error-correcting pair
A, B = generate_ecp(extension_field, n, k, support, multiplier)
# Max. number of error positions:
t = floor(k/2)
# Build public key
G_public, P, S = hide_structure(Alt_k)
t1 = time.time() - t0
print "Dimension of alternant code={0}. Time elapsed: {1} seconds.".format(Alt_k.dimension(), t1)
return G_public, P, S, Alt_k, A, B, t
def generate_alternant_code(F, n, m, k, supp, mult):
"""Generate an alternant code with parameters
:param F: extension field
:param n: code length
:param m: extension degree
:param k: dimension of underlying GRS code
:param supp: support vector
:param mult: multiplier vector
:return: Alternant code of lenght n and degree k
"""
# Build GRS_k(supp, mult):
G_GRS_k = matrix(F, k, n, 0)
for i in range(k):
for j in range(n):
G_GRS_k[i,j] = mult[j]*supp[j]**i
# Build dual code:
GRS_k = LinearCode(G_GRS_k)
GRS_k_dual = GRS_k.dual_code()
# Get parity check matrix of alt_k over fq
H_ = GRS_k_dual.parity_check_matrix()
H = matrix(F.base_ring(), m*H_.nrows(), n, 0)
FF = F.vector_space()
for i in range(H_.nrows()):
for j in range(n):
elem = H_[i,j]
coerced_elem = FF(elem)
for l in range(m):
H[l+i*m, j] = coerced_elem[l]
# Construct generator matrix for alternant code:
ker = H.right_kernel()
G_alternant = ker.basis_matrix()
Alt_k = LinearCode(G_alternant)
if Alt_k.dimension() == 0:
raise ValueError("Invalid parameters.")
return Alt_k
def generate_ecp(extension_field, n, k, supp, mult):
"""Generate an error-correcting pair for alternant code
:param extension_field: base_field of underlying GRS code
:param n: code length
:param k: degree of alternant code
:param supp: support vector of alternant code
:param mult: multiplier vector of alternant code
:return: error-correcting pair (A, B)
"""
t = floor(k/2)
# Construct error-correcting pair
G_A = matrix(extension_field, t+1, n, 0)
for i in range(G_A.nrows()):
for j in range(G_A.ncols()):
G_A[i, j] = supp[j]**i
A = LinearCode(G_A)
G_B = matrix(extension_field, t, n, 0)
for i in range(G_B.nrows()):
for j in range(G_B.ncols()):
G_B[i,j] = mult[j]*(supp[j]**i)
B = LinearCode(G_B)
return A, B
def hide_structure(code):
"""Hide structure of alternant code by modifying generator matrix
:param code: code whose structure should be hidden
:return: modified generator matrix, permutation matrix P, scrambler matrix S
"""
K = code.base_field()
n = code.generator_matrix().ncols()
dim_code = code.generator_matrix().nrows()
indices = range(n)
random.shuffle(indices)
P = matrix(K, n, n, 0)
for i in xrange(n):
P[i, indices[i]] = K.one()
S = random_matrix(K, dim_code, dim_code, algorithm='unimodular')
return S*code.generator_matrix()*P, P, S
def build_decrypter(path):
"""Build the decrypter script
:param path: name of the private folder
"""
out_str = '''import time
def run_decryption(P, S, Alt_k, A, B, message):
"""Run decryption on message with given private key arguments"""
t0 = time.time()
base_field = Alt_k.base_field()
word = vector(base_field, message)
code = decrypt(P, S, Alt_k, A, B, word)
t1 = t1 = time.time() - t0
print "Time consumption for decryption process:{0} seconds.".format(t1)
return code
def decrypt(P, S, Alt_k, A, B, word):
"""Decrypt word with given private key arguments"""
y = word * P.inverse()
code_word = decode_alternant(Alt_k, A, B, y)
solve_mat = S*Alt_k.generator_matrix()
message = solve_mat.solve_left(code_word)
return message
def decode_alternant(Alt_k, A, B, y):
"""Decode y = c + e where c is an element of Alt_k and e is an error vector"""
t0 = time.time()
F = A.base_field()
n = len(y)
ky_element = 0
K = F.base_ring()
# Compute the kernel of received word y:
ky_element = compute_kernel_element(A, B, y)
if ky_element == 0:
return -1
J = []
for i in range(n):
if ky_element[i] == 0:
J.append(i)
# Solve linear equation to get error vector:
H = Alt_k.parity_check_matrix()
J_comp = range(H.ncols())
for i in J:
J_comp.remove(i)
H_J = H[:,J]
H_J_comp = H[:,J_comp]
y_J = vector(F,[y[i] for i in xrange(n) if i in J])
y_J_comp = vector(F,[y[i] for i in xrange(n) if i not in J])
temp = H_J*y_J.column()+H_J_comp*(y_J_comp.column())
sol_ = H_J.solve_right(temp)
sol_e = vector(K, n)
index=0
for j in J:
sol_e[j] = sol_[index][0]
index+=1
sol_c = y-sol_e
t1 = time.time() - t0
print "Time consumption for decoding process:{0} seconds.".format(t1)
return sol_c
def compute_kernel_element(A, B, y):
"""Compute the kernel ker_y of y and return an arbitrary element of ker_y"""
T = matrix(A.base_ring(), A.dimension(), B.dimension(), 0)
for i in range(A.dimension()):
for j in range(B.dimension()):
T[i,j] = inner_star_prod(A.basis()[i], B.basis()[j], y)
ker = kernel(T)
if ker.cardinality() == 0:
raise ValueError("something went wrong! empty kernel!")
lambdas = ker.random_element()
while lambdas == ker.zero():
lambdas = ker.random_element()
A_base_mat = matrix(A.base_ring(), A.basis())
a_rand = lambdas * A_base_mat
return a_rand
def inner_star_prod(a,b,y):
"""Compute the dot product of a*b and y"""
n = len(y)
res = 0
for i in range(n):
res += a[i]*b[i]*y[i]
return res
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('message', help='encoded message you want to decode (put " around it)', type=str)
args = parser.parse_args()
# Load the private key from path
p = load('P.sobj')
s = load('S.sobj')
alt_k = load('alt_k.sobj')
A = load('A.sobj')
B = load('B.sobj')
message = eval(args.message)
word = run_decryption(p, s, alt_k, A, B, message[0])
print word
'''
filehandle = open(path + '/decrypter.sage', 'w')
filehandle.write(out_str)
filehandle.close()
def build_encrypter(name):
out_str = '''import time
def generate_random_key(keylength, base_field):
"""Generate a random key to be encrypted"""
key = []
for i in range(keylength):
key.append(base_field.random_element())
return key
if __name__ == '__main__':
import random
import time
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('gen_mat', help='File with generator matrix (part of public key)', type=str)
parser.add_argument('t', help='t (part of public key)', type=int)
args = parser.parse_args()
gen_mat = load(args.gen_mat)
n = gen_mat.ncols()
l = gen_mat.nrows()
base_field = gen_mat.base_ring()
key = generate_random_key(l, base_field)
print "Randomly generated key is: {0}".format(key)
t0 = time.time()
encoded_key = vector(base_field, key)*gen_mat
t = args.t
encrypted = []
check_list = random.sample(range(n), t)
error_list = [0 if x not in check_list else random.randint(1, base_field.characteristic()-1)
for x in xrange(n)]
error = vector(base_field, error_list)
encrypted.append(encoded_key + error)
t1 = time.time() - t0
print "Time consumption for encryption process:{0} seconds.".format(t1)
out_str = str(encrypted).replace(' ','')
print out_str
'''
file_handle = open(name + '_encrypter.sage', 'w')
file_handle.write(out_str)
file_handle.close()
if __name__ == '__main__':
import argparse
import os
parser = argparse.ArgumentParser()
parser.add_argument('q', help='prime base for the algorithm, at least 31, best >= 163', type=int, default=271)
parser.add_argument('m', help='extension degree', type=int, default=3)
parser.add_argument('n', help='length of code words. Smaller or equal than q^m', type=int, default=20)
parser.add_argument('k', help='dimension of the code', type=int, default=5)
parser.add_argument('name', help='give your code a name. Will create a folder of same name in cwd with decoder',
type=str)
args = parser.parse_args()
public, P, S, alt_k, A, B, t = build_McEliece_cryptosystem(args.q, args.n, args.m, args.k)
path_string = './{0}'.format(args.name)
if not os.path.exists(path_string):
os.makedirs(path_string)
else:
print "Path already exists. Probably overwriting existing code."
alt_k.save(path_string + '/alt_k')
public.save(path_string + '.public')
t.save(path_string + '.errorCorrectionCapability')
A.save(path_string + '/A')
B.save(path_string + '/B')
P.save(path_string + '/P')
S.save(path_string + '/S')
build_decrypter(path_string)
build_encrypter(path_string)
print 'Generator Matrix is in "{0}.public.sobj". t is {1}'.format(path_string, t)
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment