Reed solomon and mceliece in python

179 views Asked by At

I am using this library to try and make a mceliece encrypter with reed solomon. My problem is when I try to decrypt, after applying P_inv the decoder does not recognize the coded data and gives me an error. I cannot apply S_inv before using the decoder as the matrix sizes do not match. I know the library is not focusded in cryptography but in error correction, anyway I am trying to use it correction capability to get mceliece to work as secure as possible and faster than with the goppa codes. My code is:

from ecc import galois as g
from ecc import reed_solomon as rs
from ecc import polynomial as p
from Crypto.Random import get_random_bytes
import numpy as np
import random
import os

def generate_permutation_matrix(n):
    # Crear una matriz identidad
    P = np.eye(n, dtype=int)

    # Generar una permutación aleatoria
    perm = np.random.permutation(n)

    # Reorganizar las filas de la matriz identidad de acuerdo con la permutación
    P = P[perm]

    return P

def generate_keys(key_length, priv_key_name, pub_key_name):
    # Crear el campo GF 2^8
    field = g.Field(reversed([1, 0, 0, 0, 1, 1, 1, 0, 1]), p=2)

    # La matriz G es la matriz generadora del código Reed-Solomon G
    k = key_length  
    n = 254  
    gen = rs.generator(field, n)

    msg = [0] * k
    rev = msg[:]
    msg_encoded = msg[:] * k

    # Crear la matriz generadora
    G = np.zeros((k, n), dtype=int)
    for i in range(k):
        G[i, i] = 1  # Matriz de identidad
        for j in range(k):  #Matriz parities
            msg[j] = G[i, j]
        for j in range(k):  #reverse
            rev[k-1-j] = msg[j]
        rev_encoded = rs.encode(rev, gen)
        for j in range(n):  #un-reverse
            msg_encoded[n-1-j] = rev_encoded[j]
        for j in range(n-k):
            G[i, k+j] = msg_encoded[k+j]  

    # Generar una matriz S invertible de tamaño k × k
    S = np.random.randint(0, 2, size=(k, k))
    while np.linalg.det(S) == 0:  # asegurarse de que la matriz es invertible ya que su determinante debe ser distinto de zero para que lo sea
        S = np.random.randint(0, 2, size=(k, k))
    # Generar una matriz de permutación P de tamaño n × n
    P = generate_permutation_matrix(n)

    # print("Matriz S:")
    # print(S)

    # print("Matriz P:")
    # print(P)

    # print("Matriz G:")
    #print(G)

    # Paso 3: Generar las claves
    # La clave pública es la matriz G1 = SGP
    if not os.path.exists(priv_key_name):
        with open(pub_key_name, 'wb') as f:
                pass
        
        with open(priv_key_name, 'wb') as f:
                pass

    pub_key = np.dot(S, np.dot(G, P))
    np.savez_compressed(pub_key_name, pub_key=pub_key)
    # La clave privada es la terna (S, gen, P)
    np.savez_compressed(priv_key_name, S=S, gen_coeffs=gen.coeffs, P=P)

def encrypt(pub_key_name, msg):
    pub_key_data = np.load(str(pub_key_name), allow_pickle=True)
    # print(pub_key_name)

    pub_key = pub_key_data['pub_key']
    
    # msg_encoded = rs.encode(msg, gen)
    print(np.shape(pub_key))
    print(len(msg))
    msg_encrypted = np.dot(msg, pub_key)

    for _ in range(111):
        # Elige una posición aleatoria en el rango de los datos codificados
        pos = random.randint(0, len(msg_encrypted) - 1)
        # Cambia el byte en esa posición a un valor aleatorio en el rango del campo Galois
        msg_encrypted[pos] = random.randint(0, 1)
    return msg_encrypted

def decrypt(priv_key_name, msg_encrypted, password):
    private_key_data = np.load(str(priv_key_name), allow_pickle=True)
    S = private_key_data['S']
    gen_coeffs = private_key_data['gen_coeffs']
    P = private_key_data['P']
    field = g.Field(reversed([1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1]), p=2)
    gen = p.Polynomial(field, gen_coeffs)
    print(gen.field)
    S_inv = np.linalg.inv(S)
    P_inv = np.linalg.inv(P)
    # Aplicar la inversa de la matriz de permutación a la palabra de código
    msg_encrypted = np.dot(msg_encrypted, P_inv)

    # Decodificar la palabra de código utilizando la matriz generadora
    msg_encrypted = rs.decode(msg_encrypted, gen)

    msg = np.dot(msg_encrypted, S_inv)
    # Aplicar la inversa de la matriz de dispersión al mensaje

    return msg

def main():
    key = get_random_bytes(32)
    password =  ''
    generate_keys(32, 'priv_key.npz', 'pub_key.npz')
    key_enc = encrypt('pub_key.npz', list(key))
    key_dec = decrypt('priv_key.npz', key_enc, password)

    assert key==key_dec

if __name__ == "__main__":
    main()

I have tried changing the library, the way I generate the keys but i cannot get it to work, the error I am currently given is:

Traceback (most recent call last):
  File "test.py", line 25, in <module>
    main()
  File "\test.py", line 17, in main
    decrypted_key = decrypt(priv_key_name, encrypted_key, password)
  File "\mceliece.py", line 106, in decrypt
    msg_encrypted = rs.decode(msg_encrypted, gen)
  File "C:\Users\x\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\ecc\reed_solomon.py", line 27, in decode
    synd = syndrome(msg, gen)
  File "C:\Users\x\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\ecc\reed_solomon.py", line 48, in syndrome
    return [p.eval(field.exp[i]) for i in range(degree)]
  File "C:\Users\x\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\ecc\reed_solomon.py", line 48, in <listcomp>
    return [p.eval(field.exp[i]) for i in range(degree)]
  File "C:\Users\x\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\ecc\polynomial.py", line 43, in eval
    result = self.field.add(result, c)
  File "C:\Users\x\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\ecc\galois.py", line 52, in add
    pairs = zip(self.to_poly[x], self.to_poly[y])
KeyError: 31869579.0
1

There are 1 answers

0
rcgldr On

Looking at the RS library you were using, the ordering of data is not standard, making it awkward to generate G compatible with rs.encode(). From your comments, I see you have switched to another RS library.


I modified one of my ECC programs written in C to do McEliece encryption. Here is a simple example using GF(2^8), K = 9, N = 15

00 01 02 03 04 05 06 07 08                      msg
b1 22 e2 c3 96 c7 dd 1d 15                      msg · S
b1 22 e2 c3 96 c7 dd 1d 15 51 c4 2c 6b 43 a7    msg · S · G
c4 51 6b c3 dd 43 96 15 1d c7 b1 a7 22 e2 2c    msg · S · G · P  (permute)

                                                public key = S · G · P, t = 3

                                                encypt
00 01 02 03 04 05 06 07 08                      msg
c4 51 6b c3 dd 43 96 15 1d c7 b1 a7 22 e2 2c    msg · public key = msg · S · G · P
00 11 00 00 44 00 00 00 00 00 00 00 00 dd 00    xor with  t=3 error values
c4 40 6b c3 99 43 96 15 1d c7 b1 a7 22 3f 2c    enc = encrypted message

                                                decrypt
b1 22 3f c3 96 c7 99 1d 15 40 c4 2c 6b 43 a7    enc · P_inv      (unpermute)
00 00 dd 00 00 00 44 00 00 11 00 00 00 00 00    xor with rs error correction values
b1 22 e2 c3 96 c7 dd 1d 15 51 c4 2c 6b 43 a7    msg · S · G
b1 22 e2 c3 96 c7 dd 1d 15                      msg · S
00 01 02 03 04 05 06 07 08                      msg