Improvisando un cifrado de flujo/generador de números aleatorios

A veces hay que hacer un pequeño sistema de cifrado para la ocasión que no tiene porque ser criptográficamente seguro, una chapuzilla vamos, veremos como improvisar uno.

La idea de un cifrado de flujo es hacer un generador de números pseudo- aleatorios, que funcione en base a una semilla (la contraseña), y utilizar los números generados para cifrar/descifrar el mensaje, en este caso utilizaremos la operación XOR con cada byte generado y cada byte del mensaje, lo que se llama un cifrado_Vernam, de una forma similar a como hace ARC4

Una vez decidido esto, ya podemos comenzar con el código class sample_cipher:

def cipherStream(self, stream):

   s = ""  # Se parte de un flujo vacio

   for i in stream:  # A cada byte del flujo original

       s += chr(ord(i) ^ self.nextByte())  # Se le hace XOR con el

aleatorio generado

       # Y se anhade al flujo cifrado

   return s  # El flujo cifrado se devuelve

def nextByte(self):

   if (len(self.buffer) < 1 ):  # Si ya no hay elementos en el buffer

       self.fillBuffer()  # Se rellena

   return self.buffer.pop(0)  # Se extrae y devuelve el primer elemento

Como se puede ver, el cifrado en si es simple, pero hay que añadirle una fuente de aleatoriedad controlable que llenará el buffer, yo por ejemplo he utilizado sha256y sha512. Importante: Al parecer las funciones hash (como SHA, el utilizado) no son una buena fuente de aleatoriedad, ya que su funcion es comprimir información, no expandirla, avisados estáis. Para utilizar fácilmente las funciones sha256 y sha512 las importaremos así: from hashlib import sha256, sha512

Aviso: antes de empezar con lo realmente escabroso repito que no soy criptografo y se más bien poco de eso, y que esto es solo un ejemplo de un sistema que se supone inseguro desde un principio.

Esta es una forma de manejar el buffer, si encuentras otra que te guste más, pues mejor :). Lo primero sería tener en cuenta el buffer al inicializar el objeto, tomando una clave de 64 bytes, 512 bits, (o haciendole un sha512 posterior a la clave) y dividir la contraseña en dos partes y volver a hashearla (con sha256, ya que serían de 32 bytes, 256 bits cada parte), después se llenará el buffer:
def init(self, key): # Inicializacion del objeto

   self.h1 = sha256(key[ : 32 ]).digest() # Se hace sha256 de los 256

primeros bits

   self.h2 = sha256(key[ 32 : ]).digest() # Se hace lo mismo con los

ultimos bits

   self.fillBuffer() # Y se rellena el buffer

Para rellenar el buffer se juntan los dos hash que se obtuvieron a partir de la contraseña y se pasan por un sha512. A los primeros y a los segundos 32 bytes se les pasa por un sha256 por separado y se almacenan en los hashes, substituyendo a los que se obtuvieron de la contraseña, por último se limpia el buffer y se rellena haciendo XOR de cada byte de los hashes:
def fillBuffer(self):

   key = sha512(self.h1 + self.h2).digest() # Se hashean las dos cadenas

juntas

   self.h1 = sha256(key[ : 32 ]).digest() # Se hace sha256 de los primeros

bytes

   self.h2 = sha256(key[ 32 : ]).digest() # Se tambien con los ultimos

   self.buffer = [] # Se limpia el buffer

   for i in xrange(32): # Se rellena con el XOR de las dos cadenas de hash

       self.buffer.append(ord(self.h1[i]) ^ ord(self.h2[i]))

Y ya está, aquí [sample_cipher.py] completo, si se lanza sin argumentos mostrará las instruciones, la generación de números aleatorios usa el time como semilla.

===============================================================================

Uso: ./sample_cipher.py | randtest Si se utiliza randtest solo generará los números pseudo-aleatorios Clave de 512 bits (64 caracteres)

===============================================================================

[Referencias] https://secure.wikimedia.org/wikipedia/es/wiki/Cifrado_XOR https://secure.wikimedia.org/wikipedia/es/wiki/Cifrado_Vernam https://secure.wikimedia.org/wikipedia/en/wiki/SHA2

untagged

Música con el WiiMote y python » « Mostrar el voltaje actual de la batería en gambas