<jupyter_start><jupyter_text>Ejercicio 5 Criptosistema Massey-Omura **<NAME>, <NAME>, <NAME> y <NAME>** En primer lugar definimos la función que nos servirá para generar las claves del criptosistema.<jupyter_code>import random

def generate_keys(q):
    """
    Generacion de claves para el criptosistema.
    q: int primo sobre el que se construye el cuerpo 
    """
    
    K = GF(q, 'a')
    invertible_element = None
    
    # Nos aseguramos de encontrar un elemento que sea invertible
    while not invertible_element:
        e = Integer(K.random_element())
        if gcd(e, q-1) == 1:
            #print(gcd(e, q-1))
            invertible_element = e
    
    # power_mod utiliza el algoritmo de euclides
    d = power_mod(Integer(invertible_element), -1, q-1)
    
    return Integer(invertible_element), Integer(d)<jupyter_output><empty_output><jupyter_text>Ahora generamos un par de claves para usar en el ejemplo<jupyter_code>q = 31472159917268862941
eA, dA = generate_keys(q)
eB, dB = generate_keys(q)<jupyter_output><empty_output><jupyter_text>Las funciones siguientes las usamos para trabajar con caracteres en utf-8<jupyter_code>def numfy(s):
    """
    Función que dado una cadena devuelve un número que representa a 
    dicha cadena. Esta funcion permite codificar mensajes en utf-8
    """
    number = 0
    for e in [ord(c) for c in s]:
        number = (number * 0x110000) + e
    return number

def denumfy(number):
    """
    Obtiene una cadena de texto utf-8 a partir de su representación numérica
    """
    l = []
    while(number != 0):
        l.append(chr(number % 0x110000))
        number = number // 0x110000
    return ''.join(reversed(l))

def chunkyfy(m, chunk_size ):
    """
    m: str que representa la cadena de texto a partir
    chunk_size: int tamanio de los bloques
    """
    # Aniadimos 0 al principio de la cadena para que todos los bloques
    # tenga la misma longitud
    m = str(m)
    m_encoded = (chunk_size - (len(m)% chunk_size )) % chunk_size * '0' + m
    
    # Construimos los bloques
    f = len(m_encoded)//chunk_size
    return [ int(m_encoded[i*chunk_size:(i+1)*chunk_size]) for i in range(f) ]<jupyter_output><empty_output><jupyter_text>A continuación presentamos las funciones propias del criptosistema. Supondremos que Alice es la que quiere enviar el mensaje y Bob es el que lo recibe.<jupyter_code>def MO_step(m, q, e):
    """
    m: int mensaje codificado como entero
    q: int primo orden del cuerpo
    e: int exponente (0, q-1) 
    """
    # https://github.com/sagemath/sage/blob/222059565bc2166f29c50a6d85db7992589098c2/src/sage/arith/misc.py#L2201
    return power_mod(m, e, q)


def MO_step1(m, q, eA, chunk_size=None):
    """
    Este paso lo realizaria Alice, el emisor. 
    La funcion se encarga de codificar y aplicar el primer
    paso del algoritmo
    """

    chunk_size = int(log(q,10))
    digits_n = chunk_size + 1
    
    mc = numfy(m)
    chunks = chunkyfy(mc, chunk_size)
    f = "{:0" +  str(digits_n) + "d}"
    
    encrypted = [f.format(MO_step(mi,q,eA)) for mi in chunks]    
    
    return "".join(encrypted)


def MO_step2(m, q, eB, chunk_size=None):
    """
    Este paso lo hace Bob, el receptor
    """
    
    chunk_size = int(log(q,10))
    digits_n = chunk_size + 1
    
    chunks = chunkyfy(int(m), digits_n)
    f = "{:0" +  str(digits_n) + "d}"
    
    encrypted = [f.format(MO_step(mi,q,eB)) for mi in chunks]    
    
    return "".join(encrypted)

def MO_step3(m, q, dA, chunk_size=None):
    """
    Este paso lo realizaria Alice, el emisor
    """
    chunk_size = int(log(q,10))
    digits_n = chunk_size + 1
    
    chunks = chunkyfy(int(m), digits_n)
    f = "{:0" +  str(digits_n) + "d}"
    
    encrypted = [f.format(MO_step(mi,q,dA)) for mi in chunks]    
    
    return "".join(encrypted)

def MO_step4(m, q, dB, chunk_size=None):
    """
    Este paso lo realizaria Alice, el emisor.
    Esta funcion aplica el ultimo paso del algoritmo y decodifica
    """
    chunk_size = int(log(q,10))
    digits_n = chunk_size + 1
    
    chunks = chunkyfy(int(m), digits_n)
    f = "{:0" +  str(chunk_size) + "d}"
    
    decrypted = [f.format(MO_step(mi,q,dB)) for mi in chunks]    
    
    return denumfy(int("".join(decrypted)))

s1 = MO_step1("❤️ spsi", q, eA); print(s1) # Alice
s2 = MO_step2(s1, q, eB); print(s2) # Bob
s3 = MO_step3(s2, q, dA); print(s3) # Alice
s4 = MO_step4(s3, q, dB); print(s4) # Bob<jupyter_output>304408955723561356651713557030211687066114355167029497079531
266870710687855982682941464514951388792219882908690420103208
085364945083314131572947249147572896532903691555981448744337
❤️ spsi
{'fraction_non_alphanumeric': 0.11378791137879114, 'fraction_numerical': 0.06603570660357067, 'mean_word_length': 3.1778975741239894, 'pattern_counts': {'":': 0, '<': 20, '<?xml version=': 0, '>': 20, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '176568', 'license': 'MIT', 'n_tokens_mistral': 1978, 'n_tokens_neox': 1815, 'n_words': 458, 'path': 'examen/ejercicio5/massey_omura.ipynb', 'repo_name': 'dcabezas98/SPSI', 'size': '4715'}