Python: Cifrado por sustitución simple monoalfabética ó Cifrado César

El cifrado de César es un tipo de cifrado simple que se basa en el uso de un sólo alfabeto, por lo regular, ordenado. Se emplea una llave para realizar la sustitución. Por ejemplo, si la llave vale 1 y el mensaje es ‘A’, entonces, el mensaje cifrado será ‘B’, y así sucesivamente.

A diferencia de otros algoritmos, como el de Vigenére, se emplea una sola llave para cifrar todo el mensaje.

Para saber más sobre el método de César, este es un libro excelente Cryptography Decrypted

Implementación
Vamos a crear cuatro funciones para el algoritmo:

  • get_alphabet()
  • get_index()
  • crypt()
  • decrypt()

La función get_alphabet() devolverá una lista con todos los elementos del alfabeto y otros, como números lo que se desee agregar.

def get_alphabet():
	"""Devuelve una lista con mayusculas, minuscula, numeros y un espacio"""
	alphabet = []

	# mayusculas:65-90
	for i in range(65,91):
		alphabet.append(chr(i))

	# minusculas:97-122
	for i in range(97,123):
		alphabet.append(chr(i))

	# numeros
	for i in range(10):
		alphabet.append(str(i))

	# espacio
	#alphabet.append(" ")

	return alphabet

La función get_index() se usa para validar la llave. En nuestro caso el alfabeto consta de 62 elementos (length), por lo que nuestro índice máximo (max_index) dentro de la lista es de 61 y el índice mínimo (min_index) es 0. Para que un índice sea válido, debe estar en este rango.

En teoría, el algoritmo sólo sirve para un cierto valor de llaves. En un alfabeto de 25 elementos, habrá un máximo de 25 llaves distintas, ya que después se repetirán los valores, es decir, si la llave vale 26, producirá el mismo cifrado que la llave con valor de 1.

def get_index(index = 0, length = 0):
	"""Devuelve un indice valido dentro de un rango, evitando desbordamiento"""
	
	min_index = 0			# indice minimo
	max_index = length - 1		# indice maximo = longitud - 1

	while True:
		
		# index excede el limite superior
		if index > max_index:
			index -= length

		# index es menor al limite inferior
		elif index < min_index:
			index += length
			
		# index es valido
		else:
			break

	return index

La función crypt() recibe dos parámetros, el mensaje a cifrar y la llave. Inicia con la declaración de variables. Enseguida, recorre cada letra del mensaje. Si la letra se encuentra dentro de alphabet, se devolverá su índice dentro de la lista. Sino es así, letter no existe dentro de alphabet, provocando una excepción y continuando con la siguiente letra.

La sustitución se realiza de esta forma. Se incrementa index el valor de key y se guarda en move. Si move no fuese un índice válido se evaluará con get_index(). Por ejemplo, si move vale 63 excede el valor de max_index, por lo que se devolverá 1.

Por último se agrega el elemento de alphabet en la posición move al crypted.

def crypt(message = "", key = 0):
	"""Cifra un mensaje usando sustitucion"""
	
	crypted = ""			# mensaje cifrado

	alphabet = get_alphabet()	# alfabeto
	length = len(alphabet) 		# longitud del alfabeto
	index = 0			# indice de la letra en el alfabeto

	for letter in message:

		# se obtiene el indice de letter dentro de alphabet, si existe
		try:		
			index = alphabet.index(letter)	# indice dentro del alfabeto
		except:
			print "No existe %s en el alfabeto" % (letter,)
			continue


		# se obtiene la nueva posicion, evitando desbordamiento
		move = index + key		# nueva posicion
		move = get_index(move,length)	
	
		# debug
		if debug:
			print "move %d"%(move,)

		crypted += alphabet[move]	# agrega la letra al mensaje cifrado

		#debug
		if debug:
			print "%s = %s" % (alphabet[index],alphabet[move])

	return crypted

La función decrypt() es similar a la anterior, pero en lugar de incrementar el valor de key, se decrementa.

def decrypt(message = "", key = 0):
	"""Decifra un mensaje usando sustitucion"""
	
	decrypted = ""			# mensaje decifrado

	alphabet = get_alphabet()	# alfabeto
	length = len(alphabet)		# longitud del alfabeto
	index = 0

	for letter in message:

		try:
			index = alphabet.index(letter)	# indice dentro del alfabeto
		except:
			print "No existe %s en el alfabeto" % (letter,)
			continue		

		move = index - key		# nueva posicion
		move = get_index(move,length)

		# debug
		if debug:
			print "move %d"%(move,)


		decrypted += alphabet[move]	# agrega al mensaje decifrado
		
		#debug
		if debug:
			print "%s = %s" % (alphabet[index],alphabet[move])

	return decrypted

Por último, este es todo el código. Al final, se manda llamar la función crypted() y se imprime el mensaje cifrado.

#!/usr/bin/python

debug = False

# Cifrado por sustitucion

def get_alphabet():
	"""Devuelve una lista con mayusculas, minuscula, numeros y un espacio"""
	alphabet = []

	# mayusculas:65-90
	for i in range(65,91):
		alphabet.append(chr(i))

	# minusculas:97-122
	for i in range(97,123):
		alphabet.append(chr(i))

	# numeros
	for i in range(10):
		alphabet.append(str(i))

	# espacio
	#alphabet.append(" ")

	return alphabet
	

def get_index(index = 0, length = 0):
	"""Devuelve un indice valido dentro de un rango, evitando desbordamiento"""
	
	min_index = 0			# indice minimo
	max_index = length - 1		# indice maximo = longitud - 1

	while True:
		
		# index excede el limite superior
		if index > max_index:
			index -= length

		# index es menor al limite inferior
		elif index < min_index:
			index += length
			
		# index es valido
		else:
			break

	return index	




def crypt(message = "", key = 0):
	"""Cifra un mensaje usando sustitucion"""
	
	crypted = ""			# mensaje cifrado

	alphabet = get_alphabet()	# alfabeto
	length = len(alphabet) 		# longitud del alfabeto
	index = 0			# indice de la letra en el alfabeto

	for letter in message:

		# se obtiene el indice de letter dentro de alphabet, si existe
		try:		
			index = alphabet.index(letter)	# indice dentro del alfabeto
		except:
			print "No existe %s en el alfabeto" % (letter,)
			continue


		# se obtiene la nueva posicion, evitando desbordamiento
		move = index + key		# nueva posicion
		move = get_index(move,length)	
	
		# debug
		if debug:
			print "move %d"%(move,)

		crypted += alphabet[move]	# agrega la letra al mensaje cifrado

		#debug
		if debug:
			print "%s = %s" % (alphabet[index],alphabet[move])

	return crypted


def decrypt(message = "", key = 0):
	"""Decifra un mensaje usando sustitucion"""
	
	decrypted = ""			# mensaje decifrado

	alphabet = get_alphabet()	# alfabeto
	length = len(alphabet)		# longitud del alfabeto
	index = 0

	for letter in message:

		try:
			index = alphabet.index(letter)	# indice dentro del alfabeto
		except:
			print "No existe %s en el alfabeto" % (letter,)
			continue		

		move = index - key		# nueva posicion
		move = get_index(move,length)

		# debug
		if debug:
			print "move %d"%(move,)


		decrypted += alphabet[move]	# agrega al mensaje decifrado
		
		#debug
		if debug:
			print "%s = %s" % (alphabet[index],alphabet[move])

	return decrypted


# inicio
print "Cifrado por sustitucion"
print "Mensaje:"
message = raw_input()

print "Llave:"
key = int(raw_input())

# mensaje cifrado
crypted = crypt(message,key)
print crypted
	

Ejemplos

# key = 1
auraham@machine:~/projects/python$ python -i cifrar_sustitucion.py 
Cifrado por sustitucion
Mensaje:
hola
Llave:
1
ipmb
# key = 62, tiene el mismo efecto de key = 0, es decir, no cifra
auraham@machine:~/projects/python$ python -i cifrar_sustitucion.py 
Cifrado por sustitucion
Mensaje:
hola
Llave:
62
hola

# key = 63, tiene el mismo efecto que key = 1
auraham@machine:~/projects/python$ python -i cifrar_sustitucion.py 
Cifrado por sustitucion
Mensaje:
hola
Llave:
63
ipmb

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s