Commit fc8fee09 authored by Sophie Leuenberger's avatar Sophie Leuenberger
Browse files

Upload crypto_builder_me.sage (message exchanging tool)

parent dbc70e70
import random
def build_McEliece_cryptosystem(q=7, n=20, m=3, k=5):
"""Build 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)
# Construct random support and multiplier vectors
a = extension_field.gen()
support = [a^i for i in range(n-1)]+[0]
l = list(extension_field)
multiplier = []
for i in range(n):
# 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', P, S, D=(A,B))
G_public, P, S = hide_structure(Alt_k)
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 alt_k:
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-sorrecting 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)
P = matrix(K, n, n, 0)
for i in xrange(n):
P[i, indices[i]] =
S = random_matrix(K, dim_code, dim_code, algorithm='unimodular')
return S*code.generator_matrix()*P, P, S
def build_decrypter(path, offset, space):
"""Build the decrypter script
:param path: name of the private folder
:param offset: offset to build into translator
:param space: boolean for space handling
out_str = '''def run_decryption(P, S, Alt_k, A, B, message_list):
"""Run decryption on message_list with given private key arguments"""
base_field = Alt_k.base_field()
words = [vector(base_field, message) for message in message_list]
codes = [decrypt(P, S, Alt_k, A, B, word) for word in words]
return codes
def decrypt(P, S, Alt_k, A, B, word):
"""Decrypt word with given private key arguments"""
# Decryption process:
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):
"""Do de actual decryption of y"""
# Decoder: Decodes y = c + e where c is an element of Alt_k
# and e is an error vector
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:
# Solve linear equation to get error vector:
H = Alt_k.parity_check_matrix()
J_comp = range(H.ncols())
for i in J:
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)
for j in J:
sol_e[j] = sol_[index][0]
sol_c = y-sol_e
return sol_c
def compute_kernel_element(A, B, y):
"""Find an element in the kernel"""
# Computes the kernel ker_y of y and returns 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 ==
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 inner product of the star product"""
# computes 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
def translate(words):
"""Translate from integers to string"""
# maps numbers to characters
out_message = ''
for word in words:
for char in word:
if char == 0 and {space}:
out_message += ' '
out_message += chr(ZZ(char) + {offset})
return out_message
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_list = eval(args.message)
words = run_decryption(p, s, alt_k, A, B, message_list)
out_message = translate(words)
print out_message.rstrip()
'''.format(space=space, offset=int(offset))
filehandle = open(path + '/decrypter.sage', 'w')
def build_encrypter(name, offset, space, range_message):
out_str = '''def translate(word):
"""Translate from string to list of integers"""
# maps characters to numbers
out_codes = []
for char in word:
if char == ' ' and {space}:
out_codes.append(ord(char) - {offset})
return out_codes
if __name__ == '__main__':
import random
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()
message = raw_input('Please type your message{range_message}:')
difference = l - len(message) % l
if difference != l:
message = message + ' ' * difference
words = [message[l*i:l*(i+1)] for i in xrange(len(message) // l)]
codes = [vector(base_field, translate(word))*gen_mat for word in words]
t = args.t
encrypteds = []
for code in codes:
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)
encrypteds.append(code + error)
out_str = str(encrypteds).replace(' ','')
print out_str
'''.format(space=space, offset=int(offset), range_message=range_message)
file_handle = open(name + '_encrypter.sage', 'w')
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',
args = parser.parse_args()
# modify depending on q
if args.q < 31:
raise UserWarning("q is too small, texts will possibly not be restored correctly.")
offset = 0
range_message = ''
space = False
elif 31 <= args.q <= 53:
offset = 96
space = True
range_message = '. Use lowercase letters and spaces only'
elif 53 < args.q <= 193:
offset = 64
space = True
range_message = '. Use letters and spaces only'
offset = 32
space = False
range_message = ''
public, P, S, alt_k, A, B, t = build_McEliece_cryptosystem(args.q, args.n, args.m, args.k)
path_string = './{0}'.format(
if not os.path.exists(path_string):
print "Path exists. Probably overwriting existing code. Your fault!" + '/alt_k') + '.public') + '/A') + '/B') + '/P') + '/S')
build_decrypter(path_string, offset, space)
build_encrypter(path_string, offset, space, range_message)
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