Commit fc8fee09 by Sophie Leuenberger

### 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) 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', 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) 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, 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: 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 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 == 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 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 += ' ' else: 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') filehandle.write(out_str) filehandle.close() 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(0) else: 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') 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() # 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' else: 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(args.name) if not os.path.exists(path_string): os.makedirs(path_string) else: print "Path exists. Probably overwriting existing code. Your fault!" alt_k.save(path_string + '/alt_k') public.save(path_string + '.public') A.save(path_string + '/A') B.save(path_string + '/B') P.save(path_string + '/P') S.save(path_string + '/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