
Prova de conceito demonstrando um ataque de canal lateral por análise de potência contra uma implementação RSA vulnerável em Arduino (Atmega328P), com configuração de hardware detalhada e metodologia de medição.
Recentemente, observei pessoas implementando criptografia para Arduino por conta própria, como mencionado neste tópico no Stackoverflow:
https://stackoverflow.com/questions/39189065/rsa-encryption-decryption-functions-for-arduino
Várias delas podem ser encontradas na internet, algumas, a propósito, estão em bibliotecas bem conhecidas.
Decidi fazer este pequeno PoC (Prova de conceito) para mostrar por que é importante não inventar seu próprio algoritmo de criptografia, mas também usar uma implementação robusta de tais algoritmos.
Este PoC realiza um ataque de canal lateral (ataque de análise de potência) contra uma má implementação de uma rotina auxiliar usada na implementação RSA (exponenciação rápida).
Um ataque de canal lateral é um método de comprometer um sistema criptográfico explorando vazamento indireto de informações, em vez de atacar diretamente o algoritmo ou protocolo criptográfico em si.
Esse tipo de vazamento pode se originar de várias fontes, como informações de temporização, consumo de energia, emissões eletromagnéticas ou até mesmo som.
Tais ataques podem ser altamente eficazes para comprometer sistemas criptográficos como o RSA, sem exigir que o atacante resolva os problemas matemáticos subjacentes que garantem a segurança do esquema criptográfico (Kocher, Jaffe, & Jun, 1999).
Este artigo realizará um ataque de análise de potência sobre uma vulnerabilidade bem conhecida em uma implementação do algoritmo RSA em um firmware para Arduino (Atmega328P).
Nota: Fiz isso com pressa, por favor, perdoe-me pelos erros de ortografia/gramática que você eventualmente encontrará.
Ataques de análise de potência envolvem medir o consumo de energia de um dispositivo durante operações criptográficas.
A Análise Diferencial de Potência (DPA) envolve análise estatística de padrões de consumo de energia em múltiplas operações criptográficas para extrair segredos, tornando-se mais sofisticada que a Análise Simples de Potência (SPA), que correlaciona diretamente flutuações de potência com operações criptográficas específicas para deduzir segredos.
A Análise Diferencial de Potência (DPA) e a Análise Simples de Potência (SPA) podem ser usadas para extrair chaves privadas analisando padrões no consumo de energia durante computações RSA.
Esses ataques podem revelar a chave privada identificando padrões distintos de uso de energia associados a diferentes bits da chave (Kocher, Jaffe, & Jun, 1999).
RSA (Rivest-Shamir-Adleman) é um algoritmo de criptografia de chave pública amplamente utilizado, nomeado após seus inventores: Ron Rivest, Adi Shamir e Leonard Adleman, que o introduziram em 1977 (Paar & Pelzl, 2010).
Ele continua sendo um dos métodos mais seguros para transmitir dados de forma segura pela internet.
Um dos fundamentos da segurança do RSA reside na dificuldade de fatorar grandes números compostos em seus fatores primos (Menezes, van Oorschot, & Vanstone, 1996).
Este problema, conhecido como problema da fatoração, envolve encontrar os números primos que se multiplicam para formar um determinado número grande.
A criptografia RSA depende da suposição de que este problema de fatoração é computacionalmente difícil o suficiente para tornar impraticável quebrar a criptografia fatorando o módulo em seus fatores primos (Menezes, van Oorschot, & Vanstone, 1996).
A Figura 1 ilustra o processo de criptografia e descriptografia RSA usando um exemplo simples.

Figura 1 - Exemplo RSA.
Observe que neste caso, 3 e 33 são públicos. O número 7 no exemplo é a chave privada.
A função Phi (N), função totiente de Euler, calcula todos os números coprimos no intervalo de 1 a 33.
O valor 33 é obtido da multiplicação de P e Q; neste caso, 11 multiplicado por 3.
O resultado da função Phi é obtido multiplicando (P - 1) por (Q - 1); neste caso, 10 multiplicado por 2.
A operação e<sup>-1</sup> mod 20 denota a operação de inverso modular (Menezes, van Oorschot, & Vanstone, 1996).
Nota: Se desejar se aprofundar no RSA, o que não é necessário para entender este artigo. Há uma introdução rápida a essas operações básicas de teoria dos números neste repositório, no arquivo number_theory.md. A explicação pode aprimorar seu entendimento das operações RSA.
O experimento utilizou um osciloscópio DS1102 (apresentado na Figura 2) fabricado pela Rigol.
O manual do osciloscópio pode ser encontrado nas referências (RIGOL Technologies, Inc., 2017).
Uma fonte de alimentação genérica também foi usada (apresentada na Figura 3).

Figura 2 - Osciloscópio usado no experimento.

Figura 3 - Fonte de alimentação usada no experimento.
A Lei de Ohm é um princípio fundamental no campo da engenharia elétrica e da física.
Ela afirma que a corrente que flui através de um condutor entre dois pontos é diretamente proporcional à tensão entre os dois pontos e inversamente proporcional à resistência entre eles (Boylestad, 2015).
A Figura 4 mostra um circuito e a Lei de Ohm.

Figura 4 - Ilustração da Lei de Ohm.
A Lei de Ohm implica que, se você aumentar a tensão através de um condutor, a corrente também aumentará, desde que a resistência permaneça constante (Johnson & Hilburn, 2013). A Figura 5 apresenta um exemplo de aplicação da Lei de Ohm cujo objetivo é encontrar a corrente no circuito.

Figura 5 - Exemplo da Lei de Ohm.
A Lei de Tensão de Kirchhoff (KVL) é um princípio fundamental na engenharia elétrica e na física (Boylestad, 2015).
Ela afirma que a soma de todas as diferenças de potencial elétrico (tensões) em qualquer rede ou malha fechada é zero (Boylestad, 2015).
A Figura 6 ilustra a Lei de Tensão de Kirchhoff.

Figura 6 - Ilustração da Lei de Kirchhoff.
Há um exemplo da aplicação da Lei de Kirchhoff na Figura 7 para encontrar a corrente sobre os resistores R1 e R2.

Figura 7 - Exemplo da Lei de Kirchhoff.
O divisor de tensão é uma consequência da Lei de Tensão de Kirchhoff (KVL) e estabelece uma maneira de calcular a Vout, que é a tensão entre o resistor R1 e R2.
A fórmula do divisor de tensão é apresentada na Figura 8.

Figura 8 - Ilustração do divisor de tensão.
Um exemplo de aplicação do divisor de tensão é apresentado na Figura 9.

Figura 9 - Exemplo de divisor de tensão.
Um resistor shunt é um resistor de baixo valor colocado em série com a fonte de alimentação do circuito para medir a corrente que flui através do circuito.
Usar um shunt para medir corrente é uma das técnicas utilizadas em multímetros modernos. (Boylestad, 2015).
Medir a queda de tensão no resistor shunt e conhecer sua resistência fornece informações suficientes para calcular a corrente usando a Lei de Ohm.
Para medir o consumo de corrente de um dispositivo Arduino, é necessário colocar um resistor shunt em série com o VCC (positivo).
A forma como será conectado é apresentada na Figura 10.
Nota 1: Observe que no PoC, em vez de usar a placa Arduino Uno, o alvo (microcontrolador atmega328p) é transferido para uma placa de prototipagem separada, conforme apresentado na Figura 11. Isso permite manipular mais facilmente a pinagem do microcontrolador sem exigir solda.
Nota 2: Se você não sabe como fazer isso, meu artigo anterior mostrando como funciona o glitching ensina como fazer e pode ser encontrado em https://github.com/lord-feistel/hardware_hacking_lab)

Figura 10 - Shunt com Arduino.

Figura 11 - Circuito do shunt na placa de prototipagem.
Para demonstrar a medição do consumo de corrente usando um resistor shunt, um LED (Sedra & Smith, 2014) será conectado ao GPIO do microcontrolador (Figura 12) e o osciloscópio será usado para observar como isso afeta o consumo de energia sobre o resistor shunt nas situações em que o LED está ligado ou desligado.
Observe que para extrair a chave ou observar o efeito da carga sobre o consumo de energia, não é necessário usar a Lei de Ohm para obter a corrente, apenas a queda de tensão já é suficiente (Johnson & Hilburn, 2013).

Figura 12 - Medição de potência.
Para melhor observar, por favor, veja o Vídeo 1 que mostra como a tensão cai quando o LED está ligado.
Vídeo 1 - Queda de tensão devido ao consumo do LED.
O seguinte código foi utilizado para piscar o LED. Ele também pode ser encontrado neste repositório.```C const int PIN_CHARGE = 9 ; void setup() { pinMode(PIN_CHARGE, OUTPUT);
}
void loop() {
digitalWrite(PIN_CHARGE, HIGH);
delay(10);
digitalWrite(PIN_CHARGE, LOW);
delay(10);
}
É importante destacar que esta queda de tensão também ocorre quando um cálculo complexo é realizado (Kocher, Jaffe, & Jun, 1999).
O código a seguir causa a queda de tensão apresentada na **Figura 13**
``````C
void setup() {
}
void loop() {
volatile unsigned long i = 0;
i = ((i + 1) * (i - 1) + (i * i) - (i / 2) * (i % 3) + (i * i * i * i)) * ((i + 2) * (i - 2) + (i * i) - (i / 3) * (i % 5) + (i * i * i * i));
delayMicroseconds(100);
}
Se a queda de tensão reflete o cálculo, então pode ser usada para determinar os dados que estão sendo processados.

Figura 13 - Consumo de energia no cálculo pesado.
A forma convencional de realizar a operação de potência é multiplicando a base n vezes.
Suponha 23 resultará em 2*2*2, já que 2 é a base e 3 é n.
Funciona muito bem, mas não é eficiente o suficiente para tornar o RSA viável.
Para alcançar tal implementação, utiliza-se o algoritmo de exponenciação rápida.
A exponenciação rápida, também conhecida como exponenciação por quadrados, é um método eficiente para elevar um número a uma potência.
A seguir, pode ser encontrado o pseudo-código para a exponenciação rápida.```C
function fast_exponentiation(a, b): result = 1 base = a exponent = b
while exponent > 0:
if (exponent % 2 == 1): // If exponent is odd
result = result * base
base = base * base // Square the base
exponent = exponent // 2 // Divide exponent by 2
return result
.
.
.
Os passos da exponenciação rápida de 2<sup>4</sup> podem ser encontrados na **Tabela 1**
| Iteração | Valor base | Expoente em binário | Operação | Resultado |
|----------|------------|---------------------|----------|-----------|
| Inicial | 2 | 100 | Início | 1 |
| 1 | 4 | 010 | Quadrado | 1 |
| 2 | 16 | 001 | Quadrado | 1 |
| 3 | 256 | 000 | Multiplicar | 16 |
| Final | - | - | Fim | 16 |
**Tabela 1** - Iterações da exponenciação rápida de 2<sup>4</sup>.
Seguindo o processo é explicado :```
- **Initialization:**
Start with base = 2 , exponent = 4 ( binary 100) , result = 1.
- **Iteration 1:** exponent = 4 (binary 100, even)
Square base to get 4 .
result remains 1.
- **Iteration 2:** exponent = 2 (binary 010, even)
Square base to get 16 .
result remains 1.
- **Iteration 3:** exponent = 1 (binary: 001, odd)
Multiply result by a = 16 to get 16.
result becomes 16.
- **Final:** n = 0 (binary: 000)
The loop ends with result = 16.
Note que para números menores nada muda ou fica pior, no entanto para números grandes obtém-se uma conquista significativa na eficiência.
Reduz o número de operações multiplicativas em comparação com a abordagem ingênua, o que é particularmente útil para expoentes grandes.
Tabela 2 mostra uma comparação das iterações para tal número usando a exponenciação ingênua e a exponenciação rápida.
A exponenciação rápida é crítica no RSA tanto para os processos de criptografia quanto de descriptografia, pois esses processos envolvem elevar números grandes a grandes potências módulo algum outro número grande (Paar & Pelzl, 2010).
Conforme apresentado na seção Exemplo RSA, a chave é o expoente e geralmente será um número muito grande.
Tabela 2 - Comparação de eficiência entre exponenciação convencional e exponenciação rápida.
Uma exponenciação rápida foi implementada no Arduino e carregada no ATmega328P. Como é uma PoC, implementamos da maneira mais fácil para ser visualizada.
Por exemplo, normalmente é utilizada a operação de deslocamento (shift) sobre uma variável inteira, mas implementamos o expoente como um array para ser melhor compreendido.
Note que o expoente que representa a chave é o array {0, 1, 0, 1, 0, 1, 0, 1} que criará um padrão na medição adquirida pelo osciloscópio como prova de que funciona.```C
#include <Arduino.h>
volatile long long dumb_vulnerableExponentiation(volatile long long base, const volatile int* exponentArray, volatile int arrayLength, volatile long long modulo) { volatile long long result = 1; base %= modulo;
for (volatile int i = 0; i < arrayLength; ++i) {
result = (result * result) % modulo;
if (exponentArray[i] == 1) {
result = (result * base) % modulo;
}
}
return result;
}
void setup() { }
void loop() { volatile long long base = 3; volatile long long modulo = 1000000007; const volatile int exponentArray[] = {0, 1, 0, 1, 0, 1, 0, 1}; volatile int arrayLength = sizeof(exponentArray) / sizeof(exponentArray[0]); delay(2); volatile long long result = dumb_vulnerableExponentiation(base, exponentArray, arrayLength, modulo); }
### Results
Conforme apresentado no início, para as operações de criptografia e descriptografia, a chave é o expoente; portanto, descobrindo o expoente, a chave RSA fica exposta.
Usando o hardware mencionado anteriormente, é possível ver o espectro do consumo de energia no osciloscópio, conforme apresentado na **Figure* 14* e **Figure 15**
Os períodos em que a tensão cai por um longo tempo significam que o bit `1` da chave está sendo processado; caso contrário, é o bit `0`.
Observe que, quando o expoente é par, há uma multiplicação extra, o que faz com que a queda de energia demore mais, expondo informações da chave.
**Video 2** mostra a captura da chave. Para entender como regular o período e a amplitude, consulte o manual do osciloscópio.

**Figure 14** - Captura da chave

**Figure 15** - Expondo 0 e 1 da chave usando osciloscópio
[](https://youtu.be/MBZ1abtTN_k)
**Video 2** - Capturando a chave com osciloscópio.
Tal ataque pode ser usado em um cenário em que o microcontrolador está usando uma biblioteca bem conhecida, mas o firmware está bloqueado, não permitindo que o atacante obtenha a chave diretamente da memória.
Esse tipo de ataque também pode ser usado contra hardware.
### Conclusion
Implementar seu próprio sistema de criptografia RSA (Rivest-Shamir-Adleman) é altamente desencorajado devido a várias razões críticas, em particular a vulnerabilidade a ataques sofisticados como ataques de análise de energia.
A criptografia RSA, embora matematicamente robusta quando implementada corretamente, requer atenção meticulosa aos detalhes em sua implementação para garantir segurança.
Mesmo pequenas falhas ou descuidos de implementação podem inadvertidamente vazar informações sobre a chave privada, comprometendo toda a segurança do sistema.
Além disso, as bibliotecas e frameworks criptográficos estabelecidos passam por rigoroso escrutínio e testes pela comunidade de segurança, garantindo que sejam resilientes contra ataques e vulnerabilidades conhecidos. Usar essas bibliotecas verificadas não apenas economiza tempo e esforço, mas também reduz significativamente o risco de introduzir vulnerabilidades inadvertidamente no sistema.
### References
1. Understanding Cryptography - Paar, C., & Pelzl, J. (2010). **Understanding Cryptography**. Springer.
2. Handbook of Applied Cryptography - Menezes, A. J., van Oorschot, P. C., & Vanstone, S. A. (1996). **Handbook of Applied Cryptography**. CRC Press.
3. Differential Power Analysis - Kocher, P., Jaffe, J., & Jun, B. (1999). **Differential Power Analysis**. Proceedings of CRYPTO '99, Lecture Notes in Computer Science, vol 1666. Springer, Berlin, Heidelberg. DOI: 10.1007/3-540-48405-1_25.
4. DS1102 Oscilloscope Datasheet - RIGOL Technologies, Inc. (2017). **DS1000E, DS1000D Series Digital Oscilloscope Datasheet**. Recuperado de [RIGOL Datasheet](https://beyondmeasure.rigoltech.com/acton/attachment/1579/f-03b8/1/-/-/-/-/DS1000E_DS1000D_DataSheet_EN.pdf)
5. Introductory Circuit Analysis - Boylestad, R. L. (2015). **Introductory Circuit Analysis** (13th ed.). Pearson.
6. Fundamentals of Electrical Circuits - Johnson, D., & Hilburn, J. L. (2013). **Fundamentals of Electrical Circuits**. McGraw-Hill Education.
7. Microelectronic Circuits - Sedra, A. S., & Smith, K. C. (2014). **Microelectronic Circuits** (7th ed.). Oxford University Press.
| Expoente (b) | Binário (b) | Operações de Exponenciação Convencional | Operações de Exponenciação Rápida |
|---|
| 1 | 1 | 1 | 1 |
| 2 | 10 | 1 | 1 |
| 4 | 100 | 3 | 2 |
| 8 | 1000 | 7 | 3 |
| 16 | 10000 | 15 | 4 |
| 32 | 100000 | 31 | 5 |
| 64 | 1000000 | 63 | 6 |
| 128 | 10000000 | 127 | 7 |
| 256 | 100000000 | 255 | 8 |
| 512 | 1000000000 | 511 | 9 |
| 1024 | 10000000000 | 1023 | 10 |