#McEliece cryptosystem implementation by vovuas2003
#v2. Power of Python OOP.
#Usage (check main function or class implementation):
#G = pubkey; S and p = privkeys; text = plaintext; msg = encrypted text.
#All these variables must be lists of integers from 0 to 255. Easy to use binary files (check console_v2).
#It is possible to rewrite my class McEliece_core and add changing the order of Galois Field (check galois lib docs) to M, so all these variables will be lists of integers from 0 to M-1.
#But it will be impossible to save keys and encrypted texts as binary files. Also you will need to add polynomial database for pyinstaller.
import numpy as np
import galois
import random
#import cryptosystem_core_v2 as ME_core
#core = ME_core.McEliece_core()
def main ( ) : #comment "return" for testing
return
core = McEliece_core ( )
n , k = core . get_config ( )
print ( n , k )
core . change_config ( 5 , 3 ) #check comments in class implementation to understand possible values
n , k = core . get_config ( )
print ( n , k )
print ( )
core . generate_keys ( ) #true random
print ( core . get_pubkey ( ) )
print ( core . get_privkey_S ( ) )
print ( core . get_privkey_p ( ) )
print ( )
core . generate_keys ( sum ( [ ord ( i ) for i in list ( " password " ) ] ) ) #very simple seed
G = core . get_pubkey ( )
S = core . get_privkey_S ( )
p = core . get_privkey_p ( )
print ( G )
print ( S )
print ( p )
print ( )
core . change_config ( 5 , 3 ) #unset all keys inside core
core . set_privkey_S ( S )
core . set_privkey_p ( p )
core . restore_pubkey ( )
print ( core . get_pubkey ( ) == G )
core . change_config ( 5 , 3 ) #unset all keys inside core
core . set_pubkey ( G )
core . set_privkey_p ( p )
core . restore_privkey_S ( )
print ( core . get_privkey_S ( ) == S )
core . change_config ( 5 , 3 ) #unset all keys inside core
core . set_pubkey ( G )
core . set_privkey_S ( S )
core . restore_privkey_p ( )
print ( core . get_privkey_p ( ) == p )
print ( )
for j in range ( 2 ) :
text = [ i + 1 for i in range ( 5 ) ]
msg = core . encrypt ( text )
print ( msg )
text = core . decrypt ( msg )
print ( text )
print ( )
print ( " All tests are finished! " )
class McEliece_core :
def __init__ ( self ) :
self . _order = 256 #p^m = 2**8; encryption of each byte
self . _n = 255 #(order - 1) mod n = 0 for Reed Solomon code; 255 = 3 * 5 * 17 = (order - 1)
self . _k = 210 #2 <= k <= n; randomly change t = (n - k) div 2 bytes during encryption, but add (n - k + 1) bytes to each chunk with len (k - 1); k != 1 for padding function; k almost equal to n is very bad because of small amount of randomly changed bytes (k == n -> privkey for decryption == numpy.linalg.inv(pubkey))
self . _t = ( self . _n - self . _k ) / / 2
self . _GF = galois . GF ( 2 , 8 , irreducible_poly = " x^8 + x^4 + x^3 + x^2 + 1 " , primitive_element = " x " , verify = False ) #hardcoded galois.GF(2**8).properties for pyinstaller
self . _rs = galois . ReedSolomon ( self . _n , self . _k , field = self . _GF )
self . _G = self . _GF . Zeros ( ( self . _k , self . _n ) ) #pubkey
self . _S = self . _GF . Zeros ( ( self . _k , self . _k ) ) #1st part of privkey
self . _S_inv = self . _GF . Zeros ( ( self . _k , self . _k ) ) #for decryption
self . _P = self . _GF . Zeros ( ( self . _n , self . _n ) ) #2nd part of privkey
self . _P_inv = self . _GF . Zeros ( ( self . _n , self . _n ) ) #for decryption
self . _p = [ 0 for i in range ( self . _n ) ] #compact format of P as a permutation array
def change_config ( self , n , k ) :
try :
if k < 2 :
raise Exception ( )
rs = galois . ReedSolomon ( n , k , field = self . _GF )
except :
raise
else :
self . _n = n
self . _k = k
self . _t = ( n - k ) / / 2
self . _rs = rs
#Also unset all keys!
self . _G = self . _GF . Zeros ( ( self . _k , self . _n ) )
self . _S = self . _GF . Zeros ( ( self . _k , self . _k ) )
self . _S_inv = self . _GF . Zeros ( ( self . _k , self . _k ) )
self . _P = self . _GF . Zeros ( ( self . _n , self . _n ) )
self . _P_inv = self . _GF . Zeros ( ( self . _n , self . _n ) )
self . _p = [ 0 for i in range ( self . _n ) ]
def get_config ( self ) :
return self . _n , self . _k
def generate_keys ( self , seed = None ) :
if seed == None :
self . _generate_S ( )
self . _generate_P ( )
elif type ( seed ) != int :
raise Exception ( )
else :
seed % = 2 * * 32
self . _unsafe_generate_S ( seed )
self . _unsafe_generate_P ( seed )
self . _G = self . _S @ self . _rs . G @ self . _P
def get_pubkey ( self ) :
return [ int ( i ) for j in self . _G for i in j ]
def get_privkey_S ( self ) :
return [ int ( i ) for j in self . _S for i in j ]
def get_privkey_p ( self ) :
return self . _p
def set_pubkey ( self , G ) :
try :
G = [ G [ i - self . _n : i ] for i in range ( self . _n , self . _n * self . _k + self . _n , self . _n ) ]
G = self . _GF ( G )
except :
raise
else :
self . _G = G
def set_privkey_S ( self , S ) :
try :
S = [ S [ i - self . _k : i ] for i in range ( self . _k , self . _k * self . _k + self . _k , self . _k ) ]
S = self . _GF ( S )
S_inv = np . linalg . inv ( S )
except :
raise
else :
self . _S = S
self . _S_inv = S_inv
def set_privkey_p ( self , p ) :
if sorted ( p ) != [ i for i in range ( self . _n ) ] :
raise Exception ( )
else :
self . _p = p
self . _P = self . _GF . Zeros ( ( self . _n , self . _n ) )
self . _P_inv = self . _GF . Zeros ( ( self . _n , self . _n ) )
for i in range ( self . _n ) :
self . _P [ i , p [ i ] ] = 1
self . _P_inv [ p [ i ] , i ] = 1
def restore_pubkey ( self ) :
self . _G = self . _S @ self . _rs . G @ self . _P
def restore_privkey_S ( self ) :
S = self . _G @ self . _P_inv
S = self . _GF ( S [ : , : self . _k ] )
try :
S_inv = np . linalg . inv ( S )
except :
raise
self . _S = S
self . _S_inv = S_inv
def restore_privkey_p ( self ) :
G = self . _rs . G
G = G . T
G = [ [ int ( i ) for i in j ] for j in G ]
GP = self . _S_inv @ self . _G
GP = GP . T
GP = [ [ int ( i ) for i in j ] for j in GP ]
p = [ 0 for i in range ( self . _n ) ]
f = False
for i in range ( self . _n ) :
f = False
for j in range ( self . _n ) :
if G [ i ] == GP [ j ] :
p [ i ] = j
f = True
break
if f :
continue
raise Exception ( )
if sorted ( p ) != [ i for i in range ( self . _n ) ] :
raise Exception ( )
self . _p = p
self . _P = self . _GF . Zeros ( ( self . _n , self . _n ) )
self . _P_inv = self . _GF . Zeros ( ( self . _n , self . _n ) )
for i in range ( self . _n ) :
self . _P [ i , p [ i ] ] = 1
self . _P_inv [ p [ i ] , i ] = 1
def encrypt ( self , text ) :
try :
out = [ ]
while len ( text ) > self . _k - 1 :
tmp = text [ : self . _k - 1 ]
text = text [ self . _k - 1 : ]
out + = self . _encrypt_one ( tmp )
out + = self . _encrypt_one ( text )
return out
except :
raise
def decrypt ( self , msg ) :
try :
msg = [ msg [ i - self . _n : i ] for i in range ( self . _n , len ( msg ) + self . _n , self . _n ) ]
msg = [ self . _decrypt_one ( self . _GF ( i ) ) for i in msg ]
return [ i for j in msg for i in j ]
except :
raise
#End of top-level functions, please do NOT use functions below without understanding!
def _generate_S ( self ) :
S = self . _GF . Random ( ( self . _k , self . _k ) )
while np . linalg . det ( S ) == 0 :
S = self . _GF . Random ( ( self . _k , self . _k ) )
self . _S = S
self . _S_inv = np . linalg . inv ( S )
def _generate_P ( self ) :
r = [ i for i in range ( self . _n ) ]
p = [ ]
for i in range ( self . _n ) :
p . append ( r . pop ( random . randint ( 0 , self . _n - 1 - i ) ) )
self . _p = p
self . _P = self . _GF . Zeros ( ( self . _n , self . _n ) )
self . _P_inv = self . _GF . Zeros ( ( self . _n , self . _n ) )
for i in range ( self . _n ) :
self . _P [ i , p [ i ] ] = 1
self . _P_inv [ p [ i ] , i ] = 1
def _unsafe_generate_S ( self , seed ) :
pseudo = np . random . RandomState ( seed )
S = self . _GF ( pseudo . randint ( 0 , self . _order , ( self . _k , self . _k ) ) )
while np . linalg . det ( S ) == 0 :
S = self . _GF ( pseudo . randint ( 0 , self . _order , ( self . _k , self . _k ) ) )
self . _S = S
self . _S_inv = np . linalg . inv ( S )
def _unsafe_generate_P ( self , seed ) :
pseudo = np . random . RandomState ( seed )
p = [ i for i in range ( self . _n ) ]
pseudo . shuffle ( p )
self . _p = p
self . _P = self . _GF . Zeros ( ( self . _n , self . _n ) )
self . _P_inv = self . _GF . Zeros ( ( self . _n , self . _n ) )
for i in range ( self . _n ) :
self . _P [ i , p [ i ] ] = 1
self . _P_inv [ p [ i ] , i ] = 1
def _encrypt_one ( self , text ) :
msg = self . _pad_message ( text )
m = self . _GF ( msg )
c = m . T @ self . _G
z = np . zeros ( self . _n , dtype = int )
p = [ i for i in range ( self . _n ) ]
for i in range ( self . _t ) :
ind = p . pop ( random . randint ( 0 , self . _n - 1 - i ) )
z [ ind ] = random . randint ( 1 , self . _order - 1 )
c = c + self . _GF ( z )
return [ int ( i ) for i in c ]
def _decrypt_one ( self , msg ) :
msg = msg @ self . _P_inv
msg , e = self . _rs . decode ( msg , errors = True )
if e == - 1 :
raise Exception ( )
msg = msg @ self . _S_inv
msg = [ int ( i ) for i in msg ]
try :
msg = self . _unpad_message ( msg )
except :
raise
return msg
def _pad_message ( self , msg ) :
last_value = self . _k - ( len ( msg ) % self . _k )
return msg + [ last_value ] * last_value
def _unpad_message ( self , msg ) :
last_value = msg [ - 1 ]
if last_value > = self . _k or last_value < = 0 :
raise Exception ( )
for i in range ( 1 , last_value + 1 ) :
if msg [ - i ] != last_value :
raise Exception ( )
return msg [ : - last_value ]
if __name__ == " __main__ " :
main ( )