PROCESADOR PARA APLICACIONES CRIPTOGRÁFICAS J.-P. Deschamps, A. Guzmán Sacristán, J. I. Martínez Torre, B. Romero Universidad Rey Juan Carlos, Madrid, España {jpdescha, a.guzman, j.martinez, bromero}@escet.urjc.es RESUMEN En este articulo se propone un coprocesador capaz de ejecutar las operaciones atómicas empleadas en los algoritmos de criptografía. Estas operaciones modulares son la suma, resta, multiplicación y exponenciación donde los operandos son vectores binarios largos. Además de la especificación del coprocesador se presentan varias materializaciones con objeto de explorar el espacio de diseño y evaluar las prestaciones de área y el rendimiento así como el tiempo de desarrollo de la propuesta según distintas tendencias de diseño. Para materializar el coprocesador se han empleado técnicas clásicas de prototipado con VHDL y técnicas de prototipado rápido utilizando HandelC sobre una placa RC1000-PP. en el tercer apartado. Además de las materializaciones VHDL, se ha utilizado un lenguaje de prototipado rápido, denominado HandelC, con objeto de obtener otras materializaciones y comprobar la rapidez del proceso de diseño. Los resultados obtenidos para las distintas materializaciones se describen en el apartado cuarto. Finalmente se presentan las conclusiones del trabajo y la bibliografía de referencia. 2. OPERACIONES MODULARES A continuación, y para todas las operaciones artiméticas modulares básicas, se propone una especificación, se describen sus algoritmos, se hacen consideraciones de diseño y se plantean los esquemas de circuito a materializar con VHDL. 2.1. Suma y resta 1. INTRODUCCIÓN Las operaciones aritméticas modulares (módulo M) son funciones primitivas de numerosos algoritmos criptográficos. Es el caso del célebre algoritmo RSA [1] en el cual tanto el cifrado como el descifrado consisten en calcular una exponencial modulo M. Este mismo algoritmo, y variantes del mismo, se utilizan también para la firma de documentos y la identificación de personas [2]. Otro aspecto característico de los algoritmos criptográficos es el tamaño de los operandos. Para contrarrestar la capacidad de cálculo de los computadores, así como la habilidad y la tenacidad de los adversarios potenciales, se puede llegar a tener que procesar números con cientos de bits. Para responder a la necesidad de cálculos modulares, con operandos grandes y tiempos de procesamiento aceptables, es lógico pensar en el uso de circuitos específicos, es decir, coprocesadores especializados en este tipo de operaciones. En el segundo apartado de este artículo se presenta la especificación y los algoritmos de las operaciones modulares, poniendo especial énfasis en el desarrollo de modelos VHDL específicos capaces de materializarlas. El coprocesador que ejecuta todas las operaciones aritméticas modulares y su interfaz de usuario se describe Dados dos números enteros X, Y ? {0, 1, 2, ... , M-1}, el cálculo de Z = X + Y mod M puede llevarse a cabo como sigue: Z1 :=X + Y; Z2 := X + Y - M; if Z2 >= 0 then Z := Z2; else Z := Z1; end if; El cálculo de X = X – Y mod M, que es similar, podría expresarse así: Z1 :=X - Y; Z2 := X - Y + M; if Z1 >= 0 then Z := Z1; else Z := Z2; end if; Ambas operaciones se incluyen en el siguiente algoritmo: if sumar then Z1 :=X + Y; Z2 := X + Y - M; cond :=(Z2>=0); else Z1 :=X - Y; Z2 := X - Y + M; cond :=not(Z1>=0); end if; if cond then Z := Z2; else Z := Z1; end if; A este algoritmo le corresponde el diagrama de bloques de un sumador-restador (Figura 1). Este trabajo se ha realizado bajo el contrato programa TIC99-0947-C02-02 de la CICyT. x y sumar (0) / restar (1) coutsumador cin c1 z'1 coutsumador cin c2 c1 c2 M z'1 cond z'2 0 1 sumar/restar z Figura 1: Sumador–restador modulo M. 2.2. Multiplicación Del hecho de que X.Y mod M sea el resto de la división de X.Y por M se deduce un primer circuito compuesto de un multiplicador y un divisor. Tanto el multiplicador como el divisor contienen n2 celdas. El tiempo de calculo del multiplicador es proporcional a n (ver por ejemplo el apartado 7.4.2 de [3]), y el tiempo de calculo del divisor es proporcional a n2. En resumen, tanto el coste como el tiempo de calculo son proporcionales a n2. Otro circuito se deduce del algoritmo de multiplicación con desplazamientos y sumas: P := 0; for i in 1 .. n loop P := (P*2 + X(n-i)*Y) mod M ; end loop; El cuerpo de la iteración es equivalente al calculo del resto de la división de S = P.2 + X(n-i).Y por M: P.2 + X(n-i).Y = M.q + R. Dado que P e Y son menores que M, se ve que el cociente q pertenece al conjunto {0,1,2}, lo que sugiere el siguiente algoritmo de calculo de R: P1 := P*2 + X(n-i)*Y – M; if P1 < 0 then P2 := P1+M; R := P2; else P2 := P1-M; if P2 < 0 then R := P1; else R := P2; end if; end if; La ejecución del cuerpo de la iteración incluye dos sumas/restas, una con tres operandos (P1) y la otra con dos (P2). Se puede calcular M-Y una sola vez y calcular P1 de la forma siguiente: if X(n-i) = 1 then W = M - Y; else W = M; end if; P1 := P*2-W; El algoritmo de multiplicación es el siguiente: P := 0; K := M - Y; for i in 1 .. n loop if X(n-i) = 1 then W = K; else W = M; end if; P1 := P*2 - W; if P1 < 0 then P2 := P1 + M; R := P2; else p m k 0 1 x w c1_mas FA c1 m oper p1 c2_mas FA c2 p2 sel 0 1 r Figura 2: Circuito equivalente a una celda de multiplicador modulo M. P2 := P1 - M; if P2 < 0 then R := P1; else R := P2; end if; end if; end loop; El circuito correspondiente (multiplicador módulo M paralelo) consta de un restador que calcula M-Y y de n etapas que corresponden al cuerpo de la iteración. Cada etapa se descompone a su vez en n celdas idénticas funcionalmente equivalentes al circuito de la Figura 2. Unas transformaciones booleanas elementales permiten generar el siguiente modelo VHDL de la celda: if x = '0' then w := m; else w := k; end if; c := p xnor w; p1 := c xor c1; b := oper xor m; d := b xor c2; e := sel nand d; r <= p1 xnor e; c1_mas <= (p and not(w)) or (p and c1) or (not(w) and c1); c2_mas <= (p1 and b) or (p1 and c2) or (b and c2); La complejidad de la celda es del orden de 30 puertas. De dicha estimación se deduce que un multiplicador paralelo modulo M de n bit tiene una complejidad del orden de 30.n2 puertas. En la tabla siguiente se dan algunos valores: n numero de puertas 32 30.720 64 122.880 128 491.520 256 1.966.080 Como conclusión práctica se ve que a partir de (por ejemplo) 128 bits, se debe sustituir la arquitectura completamente paralela por otra parcialmente secuencializada: en lugar de n etapas de n celdas, se sintetizan solamente n/t de n celdas, se añade un registro de n bits para almacenar los resultados intermedios, n/t registros de desplazamiento de t bits para acceder a los bits de X, y se descompone el cálculo en t ciclos bajo el control de un contador adicional. 2.3. Exponenciación Para calcular Z = YX mod M se utiliza la representación del exponente en numeración binaria X = Xn-1.2n-1 + Xnn-2 + ... + X0.20; por tanto 2.2 ? ? .?Y ? YX ? Y2 n? 1 xn ? 1 2n? 2 xn ? 2 ? ? .? . Y 2 0 x0 es decir, Y X ? ?a( n ? 1) ? n ? 1 .?a( n ? 2) ? n ? 2 .? .?a( 0) ? 0 x x x donde a(0) = Y, a(1) = Y2 =(a(0))2, a(2) = Y4 = (a(1))2, etc. De esta última expresión se deduce un primer algoritmo de exponenciación: a(0) := Y; for i in 0 .. n-2 loop a(i+1) := (a(i)*a(i)) mod M; end loop; e(0) := 1; for i in 0 .. n-2 loop if X(i) = 1 then e(i+1) := (e(i)*a(i)) mod M; else e(i+1) := e(i); end if; end loop; if X(n-1) = 1 then Z := (e(n-1)*a(n-1)) mod M; else Z := e(n-1); end if; A este algoritmo le corresponde un circuito iterativo en el cual las dos iteraciones se ejecutan de forma concurrente. Dicho circuito incluye dos multiplicadores (uno para cada iteración) y dos registros que almacenan a(i) y e(i). Su tiempo de calculo es del orden de n multiplicado por el tiempo de respuesta de un multiplicador, es decir, proporcional a n3. De la siguiente igualdad: Y X ? ?? ? ? ??1 .Y 2 ? .Y xn ? 1 2 xn ? 2 ? 2 2 ? ?? .Y x0 ? se deduce un algoritmo alternativo: e := 1; for i in 0 .. n-1 loop e := (e*e) mod M; if X(n-1-i) = 1 then e := (e*Y) mod M; end if; end loop; Z := e; El circuito correspondiente incluye dos multiplicadores (e*e y e*Y) y un solo registro (e). Es más lento que el anterior dado que en cada iteración se calculan sucesivamente e*e y e*Y, pero en cambio tiene un registro menos. 3. COPROCESADOR Tal como se comento antes, el coprocesador VHDL consta de un núcleo que ejecuta las operaciones y de la interfaz con el bus del sistema (decodificador, registros y amplificadores de tres estados). Si el bus del sistema es de k bits, la interfaz contiene n/k registros de k bits para almacenar el primer operando (X), n/k registros para el segundo operando (Y), n/k registros para el modulo (M), más un registro de comando que contiene la operación a ejecutar (sumar, restar, multiplicar, exponenciar) así como una señal de inicio. La interfaz contiene también n/k conjuntos de k amplificadores de tres estados que permiten conectar el resultado del calculo (Z) al bus, y otro amplificador para conectar al bus una bandera de fin de cálculo. El núcleo del coprocesador, está siempre operativo, esperando hasta que le llegue la señal de inicio de operación (inicio). Una vez esta señal ha tomado su valor cierto, se indica que no se ha finalizado todavía la operación (fin toma su valor falso), se realiza la operación que se le ha indicado al procesador (operación) y se indica el fin del procesamiento (fin toma su valor cierto). El algoritmo de funcionamiento del coprocesador, materializando el segundo algoritmo de exponenciación, se muestra a continuación: loop loop if inicio then exit; end if; end loop; fin := 0; case operación is when sumar => Z := (X+Y) mod M; when restar => Z := (X-Y) mod M; when multiplicar => Z := (X*Y) mod M; when exponenciar => e := 1; for i in 0 .. n-1 loop e := (e*e) mod M; if X(n-1-i) = 1 then e := (e*Y) mod M; end if; end loop; Z := e; end case; fin := 1; end loop; En la Figura 3 se muestra el diagrama de bloques del núcleo. La ruta de datos incluye un sumador y un multiplicador. La suma, la resta y la multiplicación se ejecutan en un solo ciclo. Para la exponenciación se necesitan un registro de desplazamiento, para acceder de forma secuencial a los bits de X, y un contador para contar el numero de ejecuciones del cuerpo de la iteración (n veces). La unidad de control ejecuta un microprograma que se deduce directamente del programa anterior. 4. EXPLORACIÓN DEL ESPACIO DE DISEÑO Se han realizado materializaciones del coprocesador criptográfico con el diseño propuesto en VHDL y en HandelC. El objetivo era doble, por una parte explorar el espacio de diseño y por otra comparar el tiempo de desarrollo. 4.1. Materializaciones vhdl comando (inicio, operación) fin x +/- y M x sumador/restador y e M multiplicador load control e registro reset (e = 1) e load shift xn-1-i x z shift reg. reset (i = 0) count counter i=n Figura 3: Diagrama de bloques del núcleo. Se ha diseñado el núcleo del coprocesador criptográfico para operandos de 32 bits palabras sobre una fpga virtex XV1000-6-BG560. El área ocupada por el diseño es de 36.215 puertas equivalentes y el número de ciclos de reloj es de 130, es decir, por ejemplo, 130 microsegundos a 1 MHz. En la Figura 4 se muestra la simulación del coprocesador para dicha frecuencia de reloj de la exponenciación (operación=3) de tres números binarios de 32 bits (X=DA292E9A, Y=BA7FDFFF y M=FF800000). El resultado correcto (Z=47D34001) se genera 130 microsegundos después de la señal de inicio. Se diseñó también una versión reducida del núcleo del coprocesador que ejecuta solamente la operación de exponenciación módulo M con operandos de 32 bits. La multiplicación se ejecuta en serie con una primitiva de cálculo cuya complejidad (apartado 2.2) es del orden de 1000 puertas, a la cual hay que añadir un restador (cálculo de M-Y), varios registros (x, z) y una unidad de control. Para la exponenciación se necesitan además otro registro (e) y otra unidad de control. Se ha integrado en una FPGA XC4010E-1-PC84, utilizándose 206 CLBs (51%). El tiempo de cálculo medio es del orden de 1875 ciclos de reloj (por ejemplo, 150 microsegundos a 12,5 MHz). 4.2. Materializaciones HandelC Una vez terminado el desarrollo de los modelos VHDL, nos planteamos comprobar la rapidez y la versatilidad de esta herramienta, materializando directamente los algoritmos propuestos en el apartado 2 y comparar con VHDL. Como alternativa de materialización al diseño VHDL, se ha empleado un lenguaje de prototipado rápido denominado HandelC sobre una plataforma muy útil para codiseño hardware-software denominada RC1000-PP. En [4-5] se puede encontrar documentación sobre el lenguaje y un ejemplo de su aplicación al procesamiento de imágenes y en [6] sobre la plataforma. La principal característica de esta aproximación de diseño es que se orienta a diseñadores software y a expertos en algoritmos que desean conseguir una materialización hardware rápida sin tener que alejarse de su entorno habitual de desarrollo, principalmente C y C++. Esto es, sin tener que invertir tiempo en aprender a diseñar hardware de la Figura 4: formas de onda resultado de la simulación. forma tradicional o sin tener que recurrir a ingenieros hardware. Una vez terminado el desarrollo de los modelos VHDL, se decidió comparar y comprobar la rapidez y la versatilidad de HandelC, materializando directamente los algoritmos propuestos en el apartado 2. Además, con objeto de realizar comparaciones, se ha respetado el modo de operación del núcleo del coprocesador mostrado en el apartado 3. De este modo, por una parte se ha diseñado un programa host en C, parametrizable para cualquier ancho de bits, que se ejecuta sobre el procesador del PC, y por otra parte se ha realizado el diseño de los algoritmos de los operadores modulares en HandelC, también parametrizables para cualquier ancho de bits, para que los ejecute la fpga de la plataforma. 4.2.1. Código ejecutándose en el microprocesador del PC El programa host sirve de interfaz con el usuario y se encarga de la comunicación usuario-programa-fpgaprograma-usuario. En la Figura 5 se muestra un ejemplo de la interfaz con un usuario a través de teclado. En dicho ejemplo se le pasan a la fpga los mismos datos y devuelve el mismo resultado que en la simulación VHDL, sólo que cada operando aparece representado como cuatro enteros de ocho bits. El tiempo de ejecución está por debajo de la resolución de las funciones de medición de tiempo tradicionales de C que operan hasta milisegundos. Figura 5: interfaz de usuario. 4.2.2. Código ejecutándose en la fpga de la plataforma El coprocesador materializado en HandelC es directamente el mismo que el presentado en el apartado 3, como se muestra a continuación. if (Inicio= =1) { switch (Operacion) { case 0 : z=SumaC2(x,y,m); Inicio=0; Fin=1; break; case 1 : z=RestaC2(x,y,m); Inicio=0; Fin=1; break; case 2 : z=ProdC2(x,y,m); Inicio=0; Fin=1; break; case 3 : z=ExpoC2(x,y,m); Inicio=0; Fin=1; break; default : z=Cero; break; }} else z=Cero; Se puede comprobar directamente la facilidad de trasladar los algoritmos a HandelC. De esta forma la generación del control general del procesamiento está resuelta. Respecto a los algoritmos de los operadores modulares también se describen en HandelC de una manera prácticamente directa. A continuación se muestra la operación de suma para hacer algunos comentarios importantes sobre HandelC. Suma módulo M: z1 = (0@x)+(0@y); z2 = z1-(0@m); if(z2[WIDTH+1]==0) z= z2<-(WIDTH+1); else z= z1<-(WIDTH+1); Una de las principales características del lenguaje es la capacidad de operar con anchos de bits del tamaño que sea definidos por el usuario y de poder operar sobre bits individuales o conjuntos de bits, del mismo modo que se suele realizar en VHDL. Por ejemplo, para obtener z1 se amplían x e y con ceros (uno sólo en este caso) en sus posiciones más significativas para obtener el resultado de la suma y también el bit de acarreo. Se puede observar que la condición del if en el algoritmo original evalúa si el valor de z2 es positivo o no. HandelC permite hacer la misma comparación, pero al permitir también el acceso a bit, es más eficiente en cuanto a área consumida evaluar un solo bit que realizar la comparación de dos vectores binarios en hardware. Otro aspecto importante de HandelC es que el diseñador del código conoce el número de ciclos necesarios para la ejecución del algoritmo, ya que el compilador sólo se encarga de poner el hardware necesario para que cada instrucción se realice en un ciclo de reloj, y no de realizar ninguna planificación. Para el código de la suma, el tiempo de procesamiento va a ser de 3 ciclos de reloj, 1 para calcular z1, 1 para z2 y otro para realizar una o la otra rama de la condición. Por supuesto, el diseñador debe evaluar si el rendimiento de su algoritmo es suficiente o no, incluyendo donde sea menester tanto paralelismo explícito como le sea posible. Ésta es otra de las características fundamentales de HandelC. Se puede indicar al compilador que realice en paralelo (par) las sentencias que se deseen, que de otro modo realizaría en serie en el orden establecido por el diseñador, lógicamente aumentando el área consumida. Esto incluye la capacidad de segmentar las operaciones (pipelining). Otra característica importante es que permite que el diseñador se preocupe exclusivamente del algoritmo y no del diseño a medida de la unidad de control. Volviendo al coprocesador que nos ocupa, a continuación se muestran las operaciones de resta (casi idéntica a la suma), multiplicación y exponenciación; ambas materializadas de forma secuencial. Resta módulo M: z1 = (0@x)-(0@y); z2 = z1+(0@m); if(z1[WIDTH+1]==0) z= z1<-(WIDTH+1); else z= z2<-(WIDTH+1); Producto módulo M: while(i<=WIDTH) { p = Suma(p,p,m); if( x[WIDTH-i]= = 1) p = Suma(p,y,m); i++; } Exponente módulo M: while(i<=WIDTH) { p = Producto(p,p,m); if( x[WIDTH-i]= = 1) p = Producto(p,y,m); i++; } Con estas descripciones, unas pocas definiciones y con Número de puertas Puertas equivalentes en función del ancho de bits 40000 35000 30000 25000 20000 15000 10000 5000 0 36982 19308 10306 6184 4 8 12 16 20 24 28 32 36 40 44 48 52 56 60 64 68 Número de bits Figura 6: ocupación de área para coprocesadores de 8, 16, 32 y 64 bits. Microsegundos Coprocesador de 32 bits: rendimiento teórico para 12.5 MHz 1800 1600 1400 1200 1000 800 600 400 200 0 1660 18000 1000 11,5 23000 28000 33000 38000 Puertas equivalentes Figura 7: área consumida y tiempo de ejecución para tres versiones del coprocesador de 32 bits. el encapsulado de estos algoritmos en forma de funciones se completa el diseño del coprocesador. Sólo con estas breves indicaciones se puede tener la certeza de que el prototipado con HandelC es más rápido que con VHDL. La relación entre el tiempo de desarrollo del coprocesador en VHDL y en HandelC ha sido de 3 a 1, aproximadamente. En la Figura 6 se muestran los resultados de área ocupada, medidos en puertas equivalentes para el coprocesador completo para operandos x, y, m de 8, 16, 32 y 64 bits. La parametrización del ancho en bits de los operandos se hace mediante una simple definición de anchura (#define WIDTH). Basta con cambiar dicho valor y re-compilar para obtener un coprocesador del ancho que se desee, por ejemplo 52 bits, sin necesidad ni siquiera de pensar en el rediseño de la unidad de control. En la Figura 7 se presentan los resultados de área ocupada y la estimación teórica del número de ciclos de ejecución para el peor caso de tres versiones del coprocesador, a saber: la versión totalmente secuencial, una versión intermedia donde se sustituye el producto por el segundo de los algoritmos presentado en el apartado 2.2, y la versión directa del producto que calcula en un ciclo el valor (x*y)%m. Se pueden realizar muchas más versiones del coprocesador sin más que forzar al compilador a que materialice en paralelo o segmente operaciones. Como cabía esperar el precio pagado por el ahorro de área que produce el circuito más iterativo se ve compensado con la pérdida de prestaciones de velocidad, mientras que el caso opuesto se gana en velocidad pero se pierde con un mayor consumo de área. La síntesis de un coprocesador de 32 bits no es más que un primer experimento dentro de este proyecto. Para obtener un método de síntesis que permita desarrollar coprocesadores con capacidades de cálculo mucho mayores (256, 512, 1024 bits) se utilizarán dos estrategias (ademas de la serialización parcial de las operaciones): (1) por un lado se seguirán estudiando los algoritmos de multiplicación modular; de las características particulares del modulo M se pueden deducir nuevas ideas de diseño (ver por ejemplo el capitulo 14 de [3]); obsérvese que las FPGAs son circuitos programables, con lo cual pueden ser configuradas de forma óptima para cada módulo M particular; (2) por otro lado se dará la importancia debida a un aspecto no tenido en cuenta hasta ahora: la adecuación del diseño lógico a las características de las macrocelulas de la FPGA. Además, se han estudiado dos alternativas de especificación y diseño del comprocesador, siguiendo la metodología clásica de VHDL, orientada a diseñadores de hardware, y la metodología de prototipado rápido de HandelC, orientada a diseñadores de software. Con iguales condiciones de conocimiento de ambos sistemas, HandelC permite diseñar y explorar más fácilmente el espacio de diseño que VHDL, lo que no imposibilita que un diseñador experto en VHDL puede conseguir muy buenos o mejores resultados. Sin embargo, es probable que desde el punto de vista del tiempo empleado en el desarrollo del sistema completo HandelC sea la opción más rápida. 5. COMENTARIOS Y CONCLUSIONES [1] R. Rivest, A. Shamir and L. Adleman, “A Method for Obtaining Digital Signatures and Public-Key Cryptosystems”, 6. BIBLIOGRAFÍA Communications of the ACM, vol. 21, nº 2, pp. 120 – 126, Feb. 1978. [4] Celoxica, “Handel-C language reference manual”, UK, 2000. [2] A. Menezes, P. van Oorschot and S. Vanstone, "A Handbook of Applied Cryptography", CRC Press, 1996. [5] J-P. Deschamps, J.I. Martínez Torre, “Hardware Implementation of the Discrete Forward Wavelet Transform with Andel-C”, Proceedings of DCIS2001, Oporto, en prensa, 2001. [3] J.M.Rabaey, "Digital Integrated Circuits", Prentice Hall, 1996. [6] Celoxica, “RC1000-PP: Manual”, UK, 2000. Hardware Reference