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

No hay comentarios:

Publicar un comentario