PRÁCTICA 1: Concurrencia y Criptografía Básica
Limitaciones de recv y accept (Sockets Bloqueantes)
- La función
recvbloquea la ejecución del programa hasta que el otro extremo del socket envía datos o cierra la conexión. - La función
acceptbloquea la ejecución del programa hasta que se conecta un cliente. - Esto implica que solo se pueda atender a un cliente a la vez (esquema cliente: enviar–recibir–enviar / servidor: recibir–enviar–recibir).
select: Solución al Bloqueo de Sockets
(SOLUCIONA EL PROBLEMA DE BLOQUEO DE RECV Y ACCEPT)
- Permite manejar múltiples conexiones simultáneas monitorizando varios sockets.
- Evita bloqueos indefinidos de
recv/accepty permite salir mediante temporizador. - Permite gestionar varios clientes sin crear un hilo por conexión (menor consumo de recursos).
- Puede esperar lectura, escritura o condiciones excepcionales (como mensajes urgentes).
Hilos (Threads) para Concurrencia
- En un chat multiusuario sencillo se suelen usar dos hilos: uno para la gestión del socket y otro para la entrada por teclado.
- Se utiliza el módulo
threadingy la claseThread. - Ejemplo de creación de hilo:
nuevo_hilo = threading.Thread(target=función, args=(param,))
Cifrado Afín
La inversa modular a⁻¹ se calcula como ainv = pow(a, −1, m). Este concepto no se utiliza directamente para el cifrado y descifrado AES.
Cifrador OTP (One-Time Pad)
- Basado en la operación
XOR. - Generación de flujo de claves (keystream) con el módulo
secrets:
import secrets
keystream = ''
for i in range(key_length):
keystream += secrets.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ')PRÁCTICA 2: Cifrado Simétrico Avanzado (AES)
Cifrado Simétrico AES en Modo ECB
- Se utiliza la biblioteca
cryptography. - Generación de clave:
key = os.random(16)genera una clave de 16 bytes (128 bits). - Inicialización del cifrador:
aesCipher = Cipher(algorithms.AES(key), modes.ECB()) - Creación de objetos de cifrado/descifrado:
aesEncryptor = aesCipher.encryptor()aesDecryptor = aesCipher.decryptor()
- La función
update()solo devuelve salida cuando acumula 16 bytes (un bloque completo).
Ejemplos de Comportamiento de update() (Bloque de 16 bytes)
b'Alice'(5 bytes) → nadab'Alice Alice'(10 bytes) → nadab'Alice Alice Alice'(15 bytes) → nadab'Alice Alice Alice A'(20 bytes) → Devuelve 16 bytes cifrados (AliceAliceAliceA), y los 4 bytes restantes (“lice”) se guardan internamente.
Funciones de Conversión de Datos
bytes.fromhex(): Convierte una cadena hexadecimal a bytes.hex(): Convierte bytes a una cadena hexadecimal.
Conversión entre Enteros y Bytes
def int_to_bytes(i):
return i.to_bytes(16, byteorder="big") (Convierte de entero a bytes, usando 16 bytes y orden big-endian)
def bytes_to_int(b):
return int.frombytes(b, byteorder="big") (Convierte de bytes a entero, usando orden big-endian)
Cifrado de Imágenes .BMP
- Apertura del archivo:
img = open('imagen.bmp','rb') - Lectura de datos:
data = img.read() - Importante: No cifrar los primeros 54 bytes (que corresponden a la cabecera de la imagen).
- Cifrar solo los datos de los píxeles.
Cifrado AES en Modo CBC y Padding
- El modo CBC (Cipher Block Chaining) requiere que los datos se procesen en bloques completos. Si el mensaje no es múltiplo del tamaño del bloque (16 bytes), es necesario usar padding (relleno).
- El padding más sencillo es añadir ceros (y quitarlos al descifrar), aunque se recomienda PKCS7.
Añadir Relleno (Padding PKCS7)
data = b'mensaje'
padder = padding.PKCS7(128).padder()
padded_data = padder.update(data) + padder.finalize()Eliminar Relleno (Unpadding PKCS7)
unpadder = padding.PKCS7(128).unpadder()
data = unpadder.update(padded_data) + unpadder.finalize()Vector de Inicialización (IV) en AES
- El IV es necesario en CBC para aportar aleatoriedad al cifrado.
- Se combina mediante XOR con el primer bloque de texto plano, y el resultado del cifrado se usa como IV para el siguiente bloque.
- Debe ser único para cada mensaje, no necesita ser secreto, y se transmite junto con el mensaje cifrado.
Concepto de Padding en AES
- AES utiliza bloques de 128 bits (16 bytes).
- Si el mensaje no es un múltiplo exacto de 16 bytes, debe agregarse relleno (padding) para completar el último bloque.
PRÁCTICA 3: Esteganografía LSB
Biblioteca PIL (Pillow)
- Biblioteca fundamental para la manipulación de imágenes, utilizada comúnmente para implementar marcas de agua y esteganografía.
Esteganografía LSB (Least Significant Bit)
- Oculta información en el bit menos significativo de cada componente de color (RGB).
- Los cambios resultantes son imperceptibles al ojo humano.
- No aumenta el tamaño del archivo de imagen.
Cálculo del Máximo de Bits Ocultables
max_bits = imagen.size[0] * imagen.size[1] * canales * n
canales: 3 para RGB, 4 para RGBA.n: Número de bits LSB utilizados por canal (por defecto,n=1si no se especifica).
Ocultación de Mensajes: Conversión a Binario
Para ocultar un mensaje, primero se convierte cada carácter en su representación de 8 bits:
mensaje_bin = ''.join([format(ord(x), '08b') for x in mens])
Esto genera una cadena de bits del mensaje, donde cada carácter ocupa 8 bits (formato binario 00000000–11111111).
Modificación del Bit Menos Significativo
Acceder y modificar el bit menos significativo de un componente de color (r): r = r[:-1] + bit_a_ocultar
CONCEPTOS ADICIONALES Y CIFRADO CLÁSICO
Conversión de Bases y Manipulación de Cadenas
int(num_binario, 2): Convierte una cadena binaria a su representación decimal (entero).text = text.upper(): Convierte todo el texto a mayúsculas.
Cifrado César: Implementación y Ataque
Fórmula de Cifrado César (para mayúsculas)
chr((ord(char) − 65 + s) % 26 + 65)
ord(char): Obtiene el código ASCII del carácter.ord(char) - 65: Ajusta el rango para que ‘A’ sea 0, ‘B’ sea 1, etc.(ord(char) - 65 + s) % 26: Aplica el desplazamiento (s) y asegura que el resultado se mantenga dentro de las 26 letras del alfabeto (módulo 26).+ 65: Vuelve al rango ASCII de las letras mayúsculas.chr(...): Convierte el número resultante de nuevo en un carácter.
Funcionalidad: El código toma un carácter (almacenado en char), lo desplaza en el alfabeto por la cantidad definida por la variable s utilizando el cifrado de César, y devuelve el carácter cifrado.
Ataque por Fuerza Bruta al Cifrado César
Se prueban todos los posibles desplazamientos (claves) de 1 a 25:
for s in range(1, 26):
result = ""
# probar todos los resultadosIntercambio de Mensajes Cifrados con RSA (Alice y Bob)
Alice envía a Bob
- Alice necesita la clave pública de Bob (
n_Bob,e_Bob). - Alice cifra el mensaje con la clave pública de Bob.
- Bob descifra el mensaje con su clave privada (
n_Bob,d_Bob).
Bob envía a Alice
- Bob necesita la clave pública de Alice (
n_Alice,e_Alice). - Bob cifra el mensaje con la clave pública de Alice.
- Alice descifra el mensaje con su clave privada (
n_Alice,d_Alice).
Resumen de Conceptos Clave
- En un servidor TCP sin hilos,
recvse bloquea esperando datos yacceptesperando conexiones, impidiendo atender a varios clientes a la vez.selectmonitoriza múltiples sockets y avisa cuáles tienen actividad, de modo que el servidor solo usarecvoacceptcuando realmente hay algo que atender, evitando bloqueos y permitiendo gestionar varios clientes sin necesidad de hilos. - Ejemplo de manipulación de bits:
int(a[:-4], 2)- 1. Se toman todos los números de la cadena
aexcepto los 4 últimos. - 2. La subcadena resultante se convierte a entero (decimal) asumiendo que está en base 2 (binario). Ejemplo: si la subcadena es
0011, el resultado es 3 (1*1 + 1*2 + 0*4 + 0*8 = 3).
- 1. Se toman todos los números de la cadena
