Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
E
ECP_for_McElieceCryptosystem
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Sophie Leuenberger
ECP_for_McElieceCryptosystem
Commits
dbc70e70
Commit
dbc70e70
authored
8 years ago
by
Sophie Leuenberger
Browse files
Options
Downloads
Patches
Plain Diff
Upload crypto_builder.sage
parent
1d50f9a3
No related branches found
No related tags found
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
crypto_builder.sage
+426
-0
426 additions, 0 deletions
crypto_builder.sage
with
426 additions
and
0 deletions
crypto_builder.sage
0 → 100644
+
426
−
0
View file @
dbc70e70
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: --- #
# Code length must be smaller or equal than number of field elements:
if n > extension_field.cardinality():
raise ValueError("Invalid parameters: n > |F|")
# Code length must be greater than subspace dimension:
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)
# --- Start time measurement: --- #
t0 = time.time()
# --- Construct random support and multiplier vectors: --- #
# Support: vector containing distinct elements of the extension field:
a = extension_field.gen()
support = [a^i for i in range(n-1)]+[0]
# Multiplier: vector containing non-zero elements of the extension field:
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)
# --- Compute max. number of error positions: --- #
t = floor(k/2)
# --- Build public key: --- #
G_public, P, S = hide_structure(Alt_k)
# --- Stop time measurement: --- #
t1 = time.time() - t0
print "Dimension of alternant code={0}. Time elapsed: {1} seconds.".format(Alt_k.dimension(), t1)
# --- Return public and private key: --- #
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
"""
# --- Compute generalized Reed-Solomon code over F with dimension k: --- #
# Build generator matrix:
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 its dual code:
GRS_k = LinearCode(G_GRS_k)
GRS_k_dual = GRS_k.dual_code()
# --- Build an alternant code over the base field F_q
# (subfield subcode of the GRS-code): --- #
# Parity check matrix:
H_ = GRS_k_dual.parity_check_matrix()
H = matrix(F.base_ring(), m*H_.nrows(), n, 0)
# Write every entry of H_ over the base field F_q:
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 a generator matrix G for the alternant code
# from the parity check matrix H (G*H^T=0):
ker = H.right_kernel()
G_alternant = ker.basis_matrix()
Alt_k = LinearCode(G_alternant)
# --- Check parameters: --- #
if Alt_k.dimension() == 0:
raise ValueError("Invalid parameters.")
# --- Return the alternant code --- #
return Alt_k
def generate_ecp(extension_field, n, k, supp, mult):
"""Generate an error-correcting pair (A,B) for the 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)
"""
# Compute the maximal errors which can be added to a codeword:
t = floor(k/2)
# --- Construct error-correcting pair: --- #
# Generator matrix for A:
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
# Build linear code A:
A = LinearCode(G_A)
# Generator matrix for B:
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)
# Build linear code B:
B = LinearCode(G_B)
# --- Return the linear codes A and B: --- #
return A, B
def hide_structure(code):
"""Hide structure of a code by modifying its generator matrix G
:param code: code whose structure should be hidden
:return: modified generator matrix, permutation matrix P, scrambler matrix S
"""
# --- Get information of the code: --- #
# Get the base field of the code:
K = code.base_field()
# Get the code length:
n = code.generator_matrix().ncols()
# Get the code dimension:
dim_code = code.generator_matrix().nrows()
# --- Multiply the generator matrix by a random permutation
# and a random scrambler matrix: --- #
# Compute random permutation matrix P:
indices = range(n)
random.shuffle(indices)
P = matrix(K, n, n, 0)
for i in xrange(n):
P[i, indices[i]] = K.one()
# Compute random scrambler matrix S:
S = random_matrix(K, dim_code, dim_code, algorithm='unimodular')
# --- Return the modified generator matrix G'=S*G*P --- #
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"""
# --- Start time measurement: --- #
t0 = time.time()
# --- Decryption process: --- #
# Get base field of the code:
base_field = Alt_k.base_field()
# transform the message to a vector over the base field:
word = vector(base_field, message)
# Start decryption process:
code = decrypt(P, S, Alt_k, A, B, word)
# if the decrypting process fails, return -1:
if code == -1:
return -1
# --- Stop time measurement: --- #
t1 = t1 = time.time() - t0
print "Time consumption for decryption process:{0} seconds.".format(t1)
# --- Return the decrypted word: --- #
return code
def decrypt(P, S, Alt_k, A, B, word):
"""Decrypt word with given private key arguments"""
# Multiply given word by P^{-1}:
y = word * P.inverse()
# Start decoding process:
code_word = decode_using_ECP(Alt_k, A, B, y)
# if the decoding process fails, return -1:
if code_word==-1:
return -1
# Compute the matrix S*G:
solve_mat = S*Alt_k.generator_matrix()
# Retrieve original message:
message = solve_mat.solve_left(code_word)
# --- Return the original message: --- #
return message
def decode_using_ECP(C, A, B, y):
"""Decode y = c + e where c is an element of C and e is an error vector"""
# --- Start time measurement: --- #
t0 = time.time()
# --- Initialization: --- #
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 kernel computation failed, return -1:
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: --- #
# Get parity check matrix:
H = C.parity_check_matrix()
# Compute complement of J:
J_comp = range(H.ncols())
for i in J:
J_comp.remove(i)
# Build matrix consisting of J-indexed columns of H:
H_J = H[:,J]
# Build matrix consisting of J_comp-indexed columns of H:
H_J_comp = H[:,J_comp]
# Build vector consisting of J-indexed entries of y:
y_J = vector(F,[y[i] for i in xrange(n) if i in J])
# Build vector consisting of J_comp-indexed entries of y:
y_J_comp = vector(F,[y[i] for i in xrange(n) if i not in J])
# Compute the solution space:
temp = H_J*y_J.column()+H_J_comp*(y_J_comp.column())
sol_ = H_J.solve_right(temp)
sol_e = vector(K, n)
# Compute the error vector:
index=0
for j in J:
sol_e[j] = sol_[index][0]
index+=1
# Compute codeword as y-error
sol_c = y-sol_e
# --- Stop time measurement: --- #
t1 = time.time() - t0
print "Time consumption for decoding process:{0} seconds.".format(t1)
# --- Return the decoded codeword: --- #
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"""
# --- Compute the tranformation matrix of the kernel map: --- #
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)
# --- Compute the kernel and check that it is non-empty: --- #
ker = kernel(T)
if ker.cardinality() == 0:
raise ValueError("Something went wrong! Empty kernel!")
# --- Compute a random element of K_y: --- #
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 an arbitrary element of K_y: --- #
return a_rand
def inner_star_prod(a,b,y):
"""Compute the dot product of a*b and y"""
# Get vector length:
n = len(y)
# Compute <a*b,y>:
res = 0
for i in range(n):
res += a[i]*b[i]*y[i]
# --- Return the reulting field element --- #
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: --- #
# Permutation matrix P:
p = load('P.sobj')
# Scrambler matrix S:
s = load('S.sobj')
# Alternant code alt_k:
alt_k = load('alt_k.sobj')
# Error-correcting pair (A,B):
A = load('A.sobj')
B = load('B.sobj')
# --- Get message which needs to be decrypted: --- #
message = eval(args.message)
# --- Start decryption process: --- #
word = run_decryption(p, s, alt_k, A, B, message[0])
if word == -1:
print "Decryption process failed."
# Print the decrypted message to the command line:
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: --- #
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()
# --- Get information about the scrambled generator matrix: --- #
gen_mat = load(args.gen_mat)
n = gen_mat.ncols()
l = gen_mat.nrows()
base_field = gen_mat.base_ring()
# --- Generate a random key: --- #
key = generate_random_key(l, base_field)
print "Randomly generated key is: {0}".format(key)
# --- Start time measurement: --- #
t0 = time.time()
# --- Encrytion process: --- #
# Encode:
encoded_key = vector(base_field, key)*gen_mat
t = args.t
encrypted = []
# Add errors to encoded key:
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)
# --- Stop time measurement: --- #
t1 = time.time() - t0
print "Time consumption for encryption process:{0} seconds.".format(t1)
# --- print encrypted key to command line: --- #
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 for command line input: --- #
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()
# --- Build McEliece cryptosystem: --- #
public, P, S, alt_k, A, B, t = build_McEliece_cryptosystem(args.q, args.n, args.m, args.k)
# --- Store private and public key at given path: --- #
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')
# --- Write encrypter and decrypter scripts: --- #
build_decrypter(path_string)
build_encrypter(path_string)
# --- Print information for user: --- #
print 'Generator Matrix is in "{0}.public.sobj". t is {1}'.format(path_string, t)
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment