miércoles, 26 de junio de 2013

Tarea 1 Libreta de un sólo uso

En ésta actividad, nos encontramos con una implementación de un tipo de algoritmo de cifrado, llamado "libreta de un sólo uso", conocido en inglés como "One-time pad", el cual consiste en la creación de varias claves generadas aleatoriamente que son utilizadas para modificar un mensaje antes de ser enviado, de manera que al momento de que otra persona reciba el mensaje, ésta tenga una copia de la "libreta" y para descifrarla, utilice la misma clave pera obtener el mensaje original.

La ventaja principal de utilizar éste algoritmo, es que hace que aunque nuestro mensaje cifrado sea visible para personas distintas del emisor y receptor, éstas no puedan realizar ataques estadísticos, ni algún tipo de intuición para acercarse al mensaje original.

Al final se elimina la clave o claves utilizadas, para darle aún más seguridad a nuestro envío y recepción de mensajes.

Existen tres programas con sus respectivas funciones dentro del algoritmo.
El primero, se encarga de crear las claves y almacenarlas en la libreta y su copia.
from random import shuffle #importamos la funcion shuffle desde random
def stringear(claves): #Funcion que transforma las claves en un string
cadena=""
for k in claves: #for que toma los elementos de la lista claves
cadena += str(claves[k]) + " " #se agrega cada elemento a la cadena y los separa con un espacio
cadena +="\n" #se agrega un salto de linea
return cadena #devuelve cadena
def crearclaves (n, cant): #funcion que crea las claves
f_nuevo= open("pruebas/libreta.txt", "w") #creamos el archivo de las claves
f_nuevo2= open("pruebas/copialibreta.txt", "w") #creamos la copia
claves= range(cant) #obtenemos todos los elementos desde 0 hasta cant en una lista
#for e in claves:
# claves[e]= claves[e]+1
for i in range(n): #ciclo for desde 0 hasta el numero claves o keys
shuffle(claves) #revuelve los elementos presentes en la lista claves
f_nuevo.write(stringear(claves)) #se escribe cada mensaje convertido en string en el archivo
f_nuevo2.write(stringear(claves)) #se hace lo mismo para la copia
f_nuevo.close() #cierre del archivo
f_nuevo2.close()
return
def main():
n= abs(int(raw_input("Cantidad de hojas en libreta: "))) #se pide el numero de claves a generar
cant= int(raw_input("Tamano de las claves en cada hoja: ")) #se pide el tamano de las claves
crearclaves(n, cant) #se crean las claves
main()
view raw libreta.py hosted with ❤ by GitHub

El segundo, es el que pide el mensaje, lo encripta y lo envía.
def noespacios(clave): #Funcion que quita los espacios vacios en los keys
for t in range(len(clave)): #for que se repite desde 0 hasta el largo de la clave
if " "in clave: # si hay un " " en la clave
clave.remove(" ") # lo elimina
return clave
def encriptar(mensaje, cifras, alfabeto): #funcion que encripta el mensaje
resultado=""
f_nuevo= open("pruebas/libreta.txt", "r") #se abre el archivo en modo solo lectura
clavel= f_nuevo.readline() #se obtiene la primer linea del archivo en un string
f_nuevo.close() #cierre de archivo
limit=len(alfabeto)
alfabetolist = list(alfabeto) #alfabeto se hace una lista
clavelist=list(clavel) #se transforma la clave en una lista
clave = noespacios(clavelist) #se quitan los espacios de la clave obtenida del archivo
for j in range(cifras): #ciclo for empezando en 0 y terminando en el numero de cifras
pos= alfabetolist.index(mensaje[j]) #se almacena la posicion de cada letra del mensaje en el alfabeto
num=int(clave[j]) #se transforma cada numero de la clave en entero
if (pos+num)<=limit:
newpos = pos + num #se suma la posicion original mas el numero correspondiente en la clave
if (pos+num)>limit:
newpos = pos - num
resultado += str(alfabetolist[newpos]) #se obtiene el mensaje encriptado obteniendo cada letra de su nueva posicion
print resultado #se imprime el mensaje encriptado
return resultado #devuelve el resultado
def main():
alfabeto = "abcdefghijklmnopqrstuvwxyz ," #caracteres aceptados en mensajes
resultado=""
mensaje= raw_input("Ingrese el mensaje: ") #se pide al usuario que ingrese el mensaje
cifras = int(len(mensaje)) #se obtiene el largo del mensaje
resultado =encriptar(mensaje, cifras, alfabeto) #se encripta el mensaje
#eliminarlinea() #se elimina la linea que contenia la clave ya utilizada
message = open("pruebas/mensajes.txt", "w") #se escribe el mensaje en el "sobre"
message.write(resultado) #se escribe el mensaje encriptado
message.close() #se cierra el archivo
main()
view raw enviar.py hosted with ❤ by GitHub

El tercero, es el que obtiene el mensaje original, nuestro programa desencriptador.
def noespacios(clave): #funcion para quitar espacios
for t in range(len(clave)): #recorremos con un for
if " "in clave: #si, hay un espacio
clave.remove(" ") #lo removemos
return clave #devuelve clave
def desencriptar(resultado, cifras, alfabeto): #funcion inversa a la de encriptacion
mensaje=""
f_nuevo2 = open("pruebas/copialibreta.txt", "r") #se abre la copia de la libreta
clavel= f_nuevo2.readline() #se obtiene la clave a utilizar
f_nuevo2.close() #se cierra el archivo
limit=0 #se establece el limite como 0
alfabetolist = list(alfabeto) #se transforma el string alfabeto en lista
clavelist=list(clavel) #se transforma la clave en una lista
clave=noespacios(clavelist) #se le quitan espacios a la clave
for j in range(cifras): #se recorre con un for desde 0 hasta el numero de cifras del mensaje
pos = alfabetolist.index(resultado[j]) #se obtiene la posicion de cada letra en la lista de alfabeto
num= int(clave[j]) #se almacenan los numeros de la clave
if (pos-num)>=limit: #checa si la resta no usa una posicion negativa
newpos = pos - num #se resta el valor de la posicion
if (pos-num)<limit: #se checa que sea mayor a 0
newpos = pos + num #se suma la posicion original mas el numero correspondiente en la clave
mensaje += str(alfabetolist[newpos]) #se obtiene el mensaje original
return mensaje #devuelve mensaje
def main():
alfabeto = "abcdefghijklmnopqrstuvwxyz ," #se establece el alfabeto
arch = open("pruebas/mensajes.txt", "r") #se abre el archivo en modo lectura
resultado= arch.readline() #se guarda en resultado el mensaje encriptado
cifras = int(len(resultado)) #se obtienen el largo del mensaje
mensaje =desencriptar(resultado, cifras, alfabeto) #funcion que descencripta el mensaje
#eliminarlinea() #elimina la linea que contiene la clave utilizada
print mensaje #se imprime el mensaje
main()
view raw recibido.py hosted with ❤ by GitHub

En la implementación de éste algoritmo no se logró:
El desecho de claves o keys ya utilizados debido a un error de identación el cual no encontré explicación alguna.

Aquí muestro la función que tenía dentro de mi programa para eliminar la clave ya utilizada.
def eliminarlinea(): #funcion que elimina la primer linea utilizada esta desactivada
newkeys=list() #convertimos new keys en lista
cont=0 #inicializamos un contador
with open("pruebas/copialibreta.txt") as archivo: #abrimos el archivo de libretas
for line in archivo: #lo recorremos con un for
newkeys[cont]=line
if cont==0:
newkeys[cont]=line #sobrescribimos la primera línea con la segunda
cont+=1 #aumentamos el contador dentro del for
f2=open("pruebas/copialibreta.txt", "w") #abrimos archivo para escritura
for li in range(cont): #ciclo for desde 0 hasta el numero maximo de lineas
f2.write(newkeys[li]) #escribimos sobre el archivo
f2.close() #cerramos el archivo
return
view raw borrarlinea.py hosted with ❤ by GitHub

Después de correr varias veces el programa me dí cuenta que no siempre la encriptación y desencriptación arroja perfectos resultados, por lo tanto una solución sería que en lugar de que la clave tuviera valores desde 0, fuera desde 1, para que sí afecte a la hora de sumar o restar posiciones en los métodos de encriptación o su inversa.

El programa no se encuentra completamente validado, cuando existe la entrada de un número y se pide una cadena, es necesario volverlo a correr y viceversa cuando existe la entrada de una cadena y se pide un número, ésto se soluciona volviendo a pedir una entrada del usuario y no dejar avanzar al programa hasta que no se ingrese el tipo de dato que se necesita.

Aquí se muestra cómo se almacenan las claves en el texto:

Y aquí se muestra en qué orden se corren los programas y los resultados que arrojan.

[ramsmunoz@RamsMunoz Escritorio]$ python libreta.py
Cantidad de hojas en libreta: 10
Tamano de las claves en cada hoja: 12
[ramsmunoz@RamsMunoz Escritorio]$ python enviar.py
Ingrese el mensaje: hernandez
ifrqbodkq
[ramsmunoz@RamsMunoz Escritorio]$ python recibido.py
hernandeh
[ramsmunoz@RamsMunoz Escritorio]$ python enviar.py
Ingrese el mensaje: benito
cfnlup
[ramsmunoz@RamsMunoz Escritorio]$ python recibido.py
benito


Referencias:
Raúl González Duque "Python para todos" Fecha: -- URL: https://launchpadlibrarian.net/18980633/Python%20para%20todos.pdf

Autor: Banyut Título: "Python: Las listas" Fecha: sábado, 02 de agosto del 2008 URL: http://banyut.obolog.com/python-listas-115312

Autor: Javier Montero Título: "Python: Aprendiendo a leer" Fecha: 15/10/2012 URL: http://elclubdelautodidacta.es/wp/2012/10/python-aprendiendo-a-leer/