Visión general
Su objetivo es implementar el cifrado RC4. El cifrado RC4, inventado por Ron Rivest (de la fama RSA), fue diseñado para ser seguro, pero lo suficientemente simple como para ser implementado desde la memoria por soldados militares en el campo de batalla. Hoy en día, hay varios ataques contra RC4, pero todavía se usa en muchos lugares hoy.
Su programa debe aceptar una sola cadena con una clave y algunos datos. Se presentará en este formato.
\x0Dthis is a keythis is some data to encrypt
El primer byte representa la longitud de la clave. Se puede suponer que la clave no tendrá más de 255 bytes y no menos de 1 byte. Los datos pueden ser arbitrariamente largos.
Su programa debe procesar la clave y luego devolver los datos cifrados. El cifrado y descifrado RC4 son idénticos, por lo que usar la misma clave para "cifrar" el texto cifrado debería devolver el texto sin formato original.
Cómo funciona RC4
Inicialización
La inicialización de RC4 es bastante simple. Una matriz de estado de 256 bytes se inicializa en todos los bytes de 0 a 255.
S = [0, 1, 2, 3, ..., 253, 254, 255]
Procesamiento de claves
Los valores en el estado se intercambian según la clave.
j = 0
for i from 0 to 255
j = (j + S[i] + key[i mod keylength]) mod 256
swap S[i] and S[j]
Cifrado
El cifrado se logra utilizando el estado para generar bytes pseudoaleatorios, que luego son XOR'd a los datos. Los valores en el estado se intercambian constantemente.
i = j = 0
for each byte B in data
i = (i + 1) mod 256
j = (j + S[i]) mod 256
swap S[i] and S[j]
K = S[(S[i] + S[j]) mod 256]
output K XOR B
Entradas y salidas esperadas
Los caracteres no imprimibles se mostrarán en \xAB
formato.
Entrada: \x01\x00\x00\x00\x00\x00\x00
Salida: \xde\x18\x89A\xa3
Salida (hexadecimal):de188941a3
Entrada: \x0Dthis is a keythis is some data to encrypt
Salida: \xb5\xdb?i\x1f\x92\x96\x96e!\xf3\xae(!\xf3\xeaC\xd4\x9fS\xbd?d\x82\x84{\xcdN
Salida (hexadecimal):b5db3f691f9296966521f3ae2821f3ea43d49f53bd3f6482847bcd4e
Entrada: \x0dthis is a key\xb5\xdb?i\x1f\x92\x96\x96e!\xf3\xae(!\xf3\xeaC\xd4\x9fS\xbd?d\x82\x84{\xcdN
Entrada (hexadecimal): 0d746869732069732061206b6579b5db3f691f9296966521f3ae2821f3ea43d49f53bd3f6482847bcd4e
Salida:this is some data to encrypt
Entrada: Sthis is a rather long key because the value of S is 83 so the key length must matchand this is the data to be encrypted
Salida: \x96\x1f,\x8f\xa3%\x9b\xa3f[mk\xdf\xbc\xac\x8b\x8e\xfa\xfe\x96B=!\xfc;\x13`c\x16q\x04\x11\xd8\x86\xee\x07
Salida (hexadecimal):961f2c8fa3259ba3665b6d6bdfbcac8b8efafe96423d21fc3b13606316710411d886ee07
fuente
\xde
, entonces debe tener 1 byte de largo, y convertirlo a un número (a través de Pythonord()
o JavaScript.charCodeAt(0)
) debería devolver 222 (0xDE).Respuestas:
JavaScript (ES6),
169168bytesToma la entrada como una matriz de bytes. Devuelve otra matriz de bytes.
¿Cómo?
Esto es esencialmente una implementación literal de la especificación.
Primero dividimos la matriz de entrada en l (longitud de la clave) ys (datos de carga útil: clave + mensaje). Luego, en orden de ejecución:
Inicializamos la matriz de estado S y definimos m = 255 que se usa repetidamente más tarde como una máscara de bits.
Barajamos la matriz de estado. Los índices I y J que se inicializan aquí se usan realmente en el siguiente paso.
Aplicamos el cifrado.
Casos de prueba
Mostrar fragmento de código
fuente
138 bytes, código de máquina (16 bits x86)
Ejecutando: guardar en codegolf.com, dosbox:
codegolf.com < input.bin
No sé si esto contará como una entrada, pero he decidido hacerlo manualmente usando editores hexadecimales. No se usaron compiladores para hacer esto.
ht editor en realidad tiene ensamblador, pero honestamente no supe sobre esto hasta que terminé ¯ \ _ (ツ) _ / ¯
Por que y como
Por qué: principalmente porque quería comprobar si soy capaz de hacer esto.
Cómo: comencé a crear un byte lleno de
NOP
sy seguí con una parte simple: tratando de escribir el primer ciclo que llenaS
tate con 0..255 valores. Cambié a Python y rápidamente escribí la versión de Python, solo para tener algo contra lo que probar. A continuación, estaba simplificando el código de Python en pseudocódigo / pseudo ensamblaje. Entonces yo estaba tratando de escribir piezas pequeñas. Decidí que sería más fácil leer desde stdin, así que comencé con algo pequeño que leería un solo byte, luego agregué lectura de contraseña e inicialización de clave. Averiguar qué registros elegir me llevó algo de tiempo.Pensé que agregar el ciclo de / cifrado será fácil, pero primero obtuve una decodificación de un solo byte y luego agregué el ciclo completo.
El último paso fue deshacerme de lo adicional
nops
que dejé entre las instrucciones al escribirlo (ofc que también requería corregir saltos).Puedes ver una pequeña galería que intenté hacer mientras progresaba aquí .
Disección
El programa se basa en algunos valores iniciales después del inicio (ver recursos a continuación).
rellenar estado (a 0x200)
leer longitud, leer contraseña, almacenar contraseña en ds: 0x300
Inicializar el estado con una tecla (
BP
se usa para atravesar la tecla,SI
se usa para atravesar el estado)generar un valor pseudoaleatorio (en
DL
,DH
es 0 thx a xor en 0x140)SI
- las entradas no lo tocaránBX
)DX
)PD: Esto probablemente podría ser aún más corto, pero esto tomó 4 noches, así que no estoy seguro de si quiero pasar otra ...
Herramientas y recursos
fuente
C (gcc) ,
193 188 182 178 171172 bytesPruébalo en línea!
Editar: ahora funciona con claves de más de 127 bytes.
Edit2: Se agregó un caso de prueba con clave de 129 bytes al enlace TIO.
Versión ligeramente menos golfizada
fuente
Conjunto de instrucciones de CPU x86, 133 bytes
A7D-9F8 = 85h = 133 bytes, pero no sé si el cálculo está bien porque el número de bytes de la misma función resulta 130 bytes ... El primer argumento de la función que denomino "cript" es la cadena, El segundo argumento es la longitud de la cadena (primer byte + longitud de la tecla + longitud del mensaje). Debajo está el archivo de lenguaje ensamblador para obtener las rutinas de cript:
debajo del archivo C para ver los resultados:
Los resultados:
fuente
JavaScript (ES6), 262 bytes
Pensé en usar solo funciones encadenadas, pero opté por golfificar el algoritmo dado aquí: https://gist.github.com/farhadi/2185197 .
Menos golf
Mostrar fragmento de código
fuente
Python 2 , 203 bytes
Pruébalo en línea!
f
es un generador (iterable) de cadenas.Sin golf:
fuente
Rubí, 234 bytes
No probado
fuente
C, 181 bytes
gracias a ceilingcat por algunos bytes menos:
f (a, n) en "a" habría una matriz de caracteres 1Byte len + tecla + mensaje; en n existe el tamaño de toda la matriz de "a" sin contar el último '\ 0'. el código para la prueba y el resultado sería el utilizado para la función de ensamblaje.
fuente
APL (NARS), 329 caracteres, 658 bytes
como siempre, la verificación de error se exigiría a otra persona ... Esto parece estar bien en entrada y salida, prueba:
Sí, todo se puede reducir ... pero, por ejemplo, hacer más poco la función xor podría significar que sea menos general ...
fuente
Rust 348
Esto es bastante grande, espero que alguien pueda dar algunas sugerencias.
sin golf: en play.rust-lang.org playground
fuente
k
lugar dekey
y enm
lugar demsg
y enfoo&255
lugar de(foo)%256