# Simple Power Analysis

## 📑 **Índice**

1. [Fundamentos da Simple Power Analysis](#-fundamentos-da-simple-power-analysis)
2. [Princípios Físicos e Teóricos](#-princípios-físicos-e-teóricos)
3. [Metodologia de Ataque](#-metodologia-de-ataque)
4. [Equipamentos e Configuração](#-equipamentos-e-configuração)
5. [Implementação em Python](#-implementação-em-python)
6. [Ataques a Algoritmos Criptográficos](#-ataques-a-algoritmos-criptográficos)
7. [Contramedidas e Proteções](#-contramedidas-e-proteções)
8. [Ferramentas e Custos](#-ferramentas-e-custos)

***

## 🔍 **Fundamentos da Simple Power Analysis**

### **O que é Simple Power Analysis (SPA)?**

**Simple Power Analysis (SPA)** é uma técnica de ataque de canal lateral que analisa diretamente o consumo de energia de um dispositivo criptográfico durante sua operação. Diferente da Differential Power Analysis (DPA) que utiliza métodos estatísticos, a SPA observa padrões visíveis em um único traço de potência (ou poucos traços) para extrair informações sensíveis, como chaves secretas. É chamada de "simples" porque a informação é diretamente visível no traço, sem necessidade de processamento estatístico complexo.

### **Contexto Histórico**

```yaml
Evolução da SPA:
  1998: Kocher, Jaffe e Jun introduzem Power Analysis
  1999: Primeiros ataques práticos em smart cards
  2000: Identificação de operações RSA em traços de potência
  2005: Ataques a implementações de AES
  2010: SPA em dispositivos IoT e microcontroladores
  2015: Técnicas de SPA em FPGAs
  2020: Ataques a aceleradores criptográficos
  2024: SPA combinada com machine learning

Motivação:
  ✅ Extração de chaves sem acesso lógico
  ✅ Dispositivos "blindados" são vulneráveis
  ✅ Implementações sem contramedidas são fáceis de quebrar
  ✅ Equipamento relativamente acessível
```

### **Princípio de Funcionamento**

```mermaid
graph TD
    subgraph "Dispositivo Alvo"
        A[Processador] --> B[Operação Criptográfica]
        B --> C[Consumo de Energia]
    end
    
    subgraph "Medição"
        D[Osciloscópio] --> E[Traço de Potência]
    end
    
    subgraph "Análise Visual"
        F[Identificar Padrões] --> G[Extrair Chave]
    end
    
    C --> D
    E --> F
```

### **Comparação SPA vs DPA**

| Característica       | SPA                 | DPA           |
| -------------------- | ------------------- | ------------- |
| **Número de traços** | 1-10                | 100-10000     |
| **Complexidade**     | Baixa               | Alta          |
| **Ruído**            | Deve ser baixo      | Pode ser alto |
| **Informação**       | Diretamente visível | Estatística   |
| **Contramedidas**    | Hiding              | Masking       |
| **Equipamento**      | Médio               | Médio/Alto    |

***

## ⚡ **Princípios Físicos e Teóricos**

### **Consumo de Energia em CMOS**

```yaml
Fundamentos Físicos:
  
  Transistores CMOS:
    - Consomem energia principalmente durante transições
    - Transição 0→1: carga do capacitor (alta potência)
    - Transição 1→0: descarga (baixa potência)
    - Estado estático: consumo mínimo
  
  Fatores que Afetam o Consumo:
    - Dados processados (Hamming weight)
    - Operação executada (multiplicação vs adição)
    - Clock do processador
    - Ruído da fonte de alimentação
    - Temperatura

Modelos de Consumo:
  - Hamming Weight: consumo ∝ número de bits 1
  - Hamming Distance: consumo ∝ bits que mudam
  - Zero-Value Model: consumo depende de valores zero
```

### **Visualização de Padrões SPA**

```python
#!/usr/bin/env python3
# spa_patterns.py

import numpy as np
import matplotlib.pyplot as plt

class SPAPatterns:
    """
    Padrões visíveis em traços de potência SPA
    """
    
    @staticmethod
    def generate_rsa_pattern():
        """
        Gerar padrão típico de RSA (exponenciação modular)
        """
        # Simular traço de potência para RSA
        t = np.linspace(0, 10, 1000)
        
        # Padrão: cada bit da chave gera um pico diferente
        # bit 1: operação quadrado + multiplicação (pico duplo)
        # bit 0: apenas quadrado (pico único)
        
        key_bits = [1, 0, 1, 1, 0, 1, 0, 0]  # Chave exemplo
        
        trace = np.zeros_like(t)
        pos = 0
        
        for bit in key_bits:
            # Quadrado (sempre ocorre)
            trace[pos:pos+50] += np.exp(-((t[pos:pos+50] - pos - 25)**2) / 100)
            pos += 50
            
            if bit == 1:
                # Multiplicação adicional
                trace[pos:pos+50] += 0.8 * np.exp(-((t[pos:pos+50] - pos - 25)**2) / 100)
                pos += 50
            else:
                # Pequeno gap
                pos += 10
        
        return t[:pos], trace[:pos], key_bits
    
    @staticmethod
    def generate_aes_pattern():
        """
        Gerar padrão típico de AES (10 rounds)
        """
        t = np.linspace(0, 10, 2000)
        trace = np.zeros_like(t)
        
        # Cada round do AES tem um padrão característico
        for round_num in range(10):
            start = round_num * 200
            
            # SubBytes (picos)
            trace[start:start+80] += 0.5 * np.exp(-((t[start:start+80] - start - 40)**2) / 200)
            
            # ShiftRows + MixColumns (platô)
            trace[start+80:start+160] += 0.3
            
            # AddRoundKey (pico pequeno)
            trace[start+160:start+200] += 0.2 * np.exp(-((t[start+160:start+200] - start - 180)**2) / 100)
        
        return t, trace
    
    @staticmethod
    def plot_patterns():
        """
        Plotar padrões SPA
        """
        fig, axes = plt.subplots(2, 1, figsize=(12, 8))
        
        # Padrão RSA
        t_rsa, trace_rsa, key = SPAPatterns.generate_rsa_pattern()
        axes[0].plot(t_rsa, trace_rsa)
        axes[0].set_title('Padrão SPA para RSA (Exponenciação Modular)')
        axes[0].set_xlabel('Tempo (unidades relativas)')
        axes[0].set_ylabel('Consumo de Potência')
        axes[0].grid(True)
        
        # Anotar bits
        pos = 0
        for i, bit in enumerate(key):
            axes[0].annotate(str(bit), xy=(pos + 30, 0.8), fontsize=10)
            if bit == 1:
                pos += 100
            else:
                pos += 60
        
        # Padrão AES
        t_aes, trace_aes = SPAPatterns.generate_aes_pattern()
        axes[1].plot(t_aes, trace_aes)
        axes[1].set_title('Padrão SPA para AES (10 Rounds)')
        axes[1].set_xlabel('Tempo (unidades relativas)')
        axes[1].set_ylabel('Consumo de Potência')
        axes[1].grid(True)
        
        # Anotar rounds
        for r in range(10):
            axes[1].axvline(x=r*200, color='r', linestyle='--', alpha=0.5)
            axes[1].annotate(f'Round {r+1}', xy=(r*200 + 50, 0.5), fontsize=8)
        
        plt.tight_layout()
        plt.show()

# Gerar visualização
SPAPatterns.plot_patterns()
```

### **Identificação de Operações**

```python
#!/usr/bin/env python3
# operation_identification.py

class OperationIdentifier:
    """
    Identificação de operações criptográficas via SPA
    """
    
    @staticmethod
    def identify_rsa_operation(trace):
        """
        Identificar se é RSA e extrair chave
        """
        # RSA tem padrão de quadrado + multiplicação
        # Cada bit gera um padrão característico
        
        # Encontrar picos no traço
        peaks = []
        for i in range(1, len(trace)-1):
            if trace[i] > trace[i-1] and trace[i] > trace[i+1] and trace[i] > 0.5:
                peaks.append(i)
        
        # Analisar distância entre picos
        key_bits = []
        for i in range(len(peaks)-1):
            distance = peaks[i+1] - peaks[i]
            if distance > 100:  # Threshold ajustável
                key_bits.append(1)
            else:
                key_bits.append(0)
        
        return key_bits
    
    @staticmethod
    def identify_aes_rounds(trace):
        """
        Identificar rounds do AES
        """
        # AES tem 10 rounds com padrões similares
        # Encontrar início de cada round
        
        # Detectar quedas significativas (fim de round)
        drops = []
        for i in range(1, len(trace)-1):
            if trace[i] < trace[i-1] * 0.5 and trace[i] < trace[i+1] * 0.5:
                drops.append(i)
        
        return len(drops)  # Número de rounds
```

***

## 🔬 **Metodologia de Ataque**

### **Passo a Passo da SPA**

```mermaid
flowchart TD
    A[1. Selecionar Alvo] --> B[2. Configurar Medição]
    B --> C[3. Coletar Traço de Potência]
    C --> D[4. Alinhar e Filtrar Traço]
    D --> E[5. Identificar Operações]
    E --> F[6. Extrair Informação]
    F --> G[7. Validar Chave]
```

### **Setup de Medição**

```python
#!/usr/bin/env python3
# spa_setup.py

import numpy as np
import matplotlib.pyplot as plt
from scipy import signal

class SPASetup:
    """
    Configuração para ataques SPA
    """
    
    def __init__(self, sample_rate=100e6):
        self.sample_rate = sample_rate  # 100 MHz
        self.traces = []
        
    def simulate_power_trace(self, operation_type='aes', noise_level=0.05):
        """
        Simular traço de potência para diferentes operações
        """
        t = np.linspace(0, 1e-3, int(self.sample_rate * 1e-3))
        
        if operation_type == 'aes':
            trace = self._simulate_aes(t)
        elif operation_type == 'rsa':
            trace = self._simulate_rsa(t)
        elif operation_type == 'ecc':
            trace = self._simulate_ecc(t)
        else:
            trace = np.zeros_like(t)
        
        # Adicionar ruído
        noise = np.random.normal(0, noise_level, len(trace))
        trace += noise
        
        return t, trace
    
    def _simulate_aes(self, t):
        """
        Simular traço AES
        """
        trace = np.zeros_like(t)
        
        # 10 rounds
        for round_num in range(10):
            start = round_num * len(t) // 10
            end = (round_num + 1) * len(t) // 10
            
            # SubBytes (picos)
            subbytes = 0.5 * np.exp(-((t[start:end] - t[start] - 0.05e-3)**2) / (1e-8))
            trace[start:end] += subbytes
            
            # MixColumns (platô)
            mixcol = 0.3 * np.ones(len(t[start:end]))
            trace[start:end] += mixcol
            
            # AddRoundKey
            addkey = 0.2 * np.exp(-((t[start:end] - t[start] - 0.1e-3)**2) / (5e-9))
            trace[start:end] += addkey
        
        return trace
    
    def _simulate_rsa(self, t):
        """
        Simular traço RSA (exponenciação modular)
        """
        trace = np.zeros_like(t)
        
        # Chave exemplo (1024 bits - simulado)
        key_bits = [1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1]
        
        pos = 0
        for bit in key_bits:
            # Quadrado (sempre)
            square = 0.4 * np.exp(-((t[pos:pos+100] - t[pos] - 0.05e-3)**2) / (1e-8))
            trace[pos:pos+100] += square
            pos += 100
            
            if bit == 1:
                # Multiplicação (extra)
                mult = 0.35 * np.exp(-((t[pos:pos+80] - t[pos] - 0.04e-3)**2) / (8e-9))
                trace[pos:pos+80] += mult
                pos += 80
            else:
                # Gap
                pos += 20
        
        return trace[:len(t)]
    
    def _simulate_ecc(self, t):
        """
        Simular traço ECC (curva elíptica)
        """
        trace = np.zeros_like(t)
        
        # Adição de pontos e dobramento têm padrões diferentes
        for i in range(100):
            start = i * len(t) // 100
            
            # Adição (pico duplo)
            addition = 0.3 * np.exp(-((t[start:start+50] - t[start] - 0.025e-3)**2) / (5e-9))
            trace[start:start+50] += addition
            
            # Dobramento (pico único)
            doubling = 0.25 * np.exp(-((t[start+50:start+100] - t[start+50] - 0.025e-3)**2) / (5e-9))
            trace[start+50:start+100] += doubling
        
        return trace
    
    def preprocess_trace(self, trace):
        """
        Pré-processamento do traço
        """
        # Filtro passa-baixa
        b, a = signal.butter(4, 0.1, 'low')
        filtered = signal.filtfilt(b, a, trace)
        
        # Normalização
        normalized = (filtered - np.mean(filtered)) / np.std(filtered)
        
        return normalized
    
    def find_peaks(self, trace, threshold=0.5):
        """
        Encontrar picos no traço
        """
        peaks = []
        for i in range(1, len(trace)-1):
            if trace[i] > trace[i-1] and trace[i] > trace[i+1] and trace[i] > threshold:
                peaks.append(i)
        return peaks

# Exemplo de uso
spa = SPASetup()
t, trace = spa.simulate_power_trace('rsa')
filtered = spa.preprocess_trace(trace)
peaks = spa.find_peaks(filtered)

print(f"Picos encontrados: {len(peaks)}")
```

***

## 🛠️ **Equipamentos e Configuração**

### **Hardware Necessário**

| Equipamento                     | Função                    | Preço (R$)    | Onde Encontrar           |
| ------------------------------- | ------------------------- | ------------- | ------------------------ |
| **Osciloscópio (≥100MHz)**      | Captura de traços         | \~R$2500-5000 | Mercado Livre, Techtools |
| **Sonda de Corrente**           | Mede consumo de corrente  | \~R$800-2000  | AliExpress, Mouser       |
| **Sonda de Tensão Diferencial** | Mede queda de tensão      | \~R$600-1500  | AliExpress               |
| **Resistor Shunt (1-50Ω)**      | Conversão corrente→tensão | \~R$10-50     | Mercado Livre            |
| **Rigol DS1054Z**               | Osciloscópio 4 canais     | \~R$2500-3000 | Mercado Livre            |
| **ChipWhisperer-Lite**          | SCA especializado         | \~R$2500-3500 | newae.com                |
| **PicoScope 2204A**             | Osciloscópio USB          | \~R$1200-1800 | Importação               |
| **Total Setup Básico**          | Oscilo + Resistor         | \~R$2500-3000 | -                        |
| **Total Setup Profissional**    | + Sonda + CW              | \~R$6000-8000 | -                        |

### **Circuito de Medição**

```yaml
Circuito para Medição de Corrente:
  
  Componentes:
    - Resistor shunt (1-10Ω) em série com VDD
    - Amplificador diferencial (opcional)
    - Filtro passa-baixa (opcional)
  
  Conexão:
    Fonte Alimentação → Resistor Shunt → Dispositivo Alvo → GND
    Osciloscópio (canal 1) conectado aos terminais do resistor
  
  Cálculo:
    I = V / R
    Onde V é a tensão medida no osciloscópio
    R é a resistência do shunt (ex: 10Ω)
```

### **Código para Aquisição de Dados**

```python
#!/usr/bin/env python3
# data_acquisition.py

import pyvisa
import numpy as np
import time

class OscilloscopeCapture:
    """
    Captura de traços via osciloscópio
    """
    
    def __init__(self, resource_string='USB0::0x0000::0x0000::INSTR'):
        self.rm = pyvisa.ResourceManager()
        self.scope = self.rm.open_resource(resource_string)
        self.setup_scope()
    
    def setup_scope(self, timebase=1e-3, vdiv=0.1):
        """
        Configurar osciloscópio
        """
        # Configurar timebase (1ms/div)
        self.scope.write(f':TIMebase:SCALe {timebase}')
        
        # Configurar escala vertical (100mV/div)
        self.scope.write(f':CHANnel1:SCALe {vdiv}')
        
        # Configurar trigger
        self.scope.write(':TRIGger:MODE EDGE')
        self.scope.write(':TRIGger:EDGE:SOURce CHANnel1')
        self.scope.write(':TRIGger:LEVel 0.1')
        
        # Modo de aquisição
        self.scope.write(':ACQuire:TYPE NORMal')
        
        print("[+] Osciloscópio configurado")
    
    def capture_trace(self, num_points=10000):
        """
        Capturar um traço
        """
        # Configurar número de pontos
        self.scope.write(f':ACQuire:POINts {num_points}')
        
        # Iniciar aquisição
        self.scope.write(':INITiate')
        
        # Aguardar trigger
        time.sleep(0.1)
        
        # Ler dados
        data = self.scope.query(':WAVeform:DATA?')
        
        # Parse dos dados (formato depende do osciloscópio)
        trace = self._parse_waveform(data)
        
        return trace
    
    def _parse_waveform(self, data):
        """
        Parsear dados do osciloscópio
        """
        # Implementação depende do fabricante
        # Exemplo para Rigol
        raw = data.split('#')
        points = int(raw[1][:2])
        values = raw[1][2:]
        
        trace = np.array([int(v) for v in values])
        return trace
    
    def capture_multiple_traces(self, num_traces, operation_func):
        """
        Capturar múltiplos traços durante operações
        """
        traces = []
        
        for i in range(num_traces):
            # Executar operação (ex: criptografia)
            operation_func()
            
            # Capturar traço
            trace = self.capture_trace()
            traces.append(trace)
            
            print(f"Traço {i+1}/{num_traces} capturado")
        
        return np.array(traces)
```

***

## 💻 **Implementação em Python**

### **SPA para RSA**

```python
#!/usr/bin/env python3
# spa_rsa.py

import numpy as np
import matplotlib.pyplot as plt

class SPARSA:
    """
    Ataque SPA para RSA
    """
    
    def __init__(self, trace, sample_rate=100e6):
        self.trace = trace
        self.sample_rate = sample_rate
        self.key_bits = []
    
    def preprocess(self):
        """
        Pré-processamento do traço
        """
        from scipy import signal
        
        # Filtro passa-baixa
        b, a = signal.butter(4, 0.05, 'low')
        filtered = signal.filtfilt(b, a, self.trace)
        
        # Normalização
        normalized = (filtered - np.mean(filtered)) / np.std(filtered)
        
        return normalized
    
    def detect_operations(self):
        """
        Detectar operações de quadrado e multiplicação
        """
        trace = self.preprocess()
        
        # Encontrar picos
        peaks = []
        for i in range(1, len(trace)-1):
            if trace[i] > trace[i-1] and trace[i] > trace[i+1] and trace[i] > 0.5:
                peaks.append(i)
        
        # Analisar padrão de picos
        operations = []
        for i in range(len(peaks)-1):
            distance = peaks[i+1] - peaks[i]
            if distance > 80:  # Threshold
                operations.append(('square', distance))
            else:
                operations.append(('mult', distance))
        
        return operations
    
    def extract_key(self):
        """
        Extrair chave RSA
        """
        ops = self.detect_operations()
        
        # No RSA, cada bit da chave corresponde a:
        # bit 0: apenas quadrado
        # bit 1: quadrado + multiplicação
        
        key_bits = []
        i = 0
        while i < len(ops):
            # Sempre tem um quadrado
            if ops[i][0] == 'square':
                # Verificar se há multiplicação depois
                if i + 1 < len(ops) and ops[i+1][0] == 'mult':
                    key_bits.append(1)
                    i += 2
                else:
                    key_bits.append(0)
                    i += 1
            else:
                i += 1
        
        return key_bits
    
    def visualize_key_extraction(self):
        """
        Visualizar extração de chave
        """
        trace = self.preprocess()
        ops = self.detect_operations()
        key = self.extract_key()
        
        fig, ax = plt.subplots(figsize=(14, 6))
        
        ax.plot(trace, 'b-', alpha=0.7, label='Traço de Potência')
        
        # Marcar operações
        pos = 0
        for i, (op, _) in enumerate(ops):
            if op == 'square':
                ax.axvspan(pos, pos+50, alpha=0.3, color='green')
                ax.text(pos+25, 1.5, 'S', ha='center', fontsize=10)
            else:
                ax.axvspan(pos, pos+40, alpha=0.3, color='orange')
                ax.text(pos+20, 1.5, 'M', ha='center', fontsize=10)
            pos += 100 if op == 'square' else 80
        
        # Mostrar chave extraída
        key_str = ''.join(str(b) for b in key)
        ax.set_title(f'SPA - Extração de Chave RSA\nChave extraída: {key_str[:20]}...')
        ax.set_xlabel('Amostras')
        ax.set_ylabel('Consumo de Potência (normalizado)')
        ax.legend()
        ax.grid(True, alpha=0.3)
        
        plt.tight_layout()
        plt.show()
        
        return key

# Exemplo
# spa = SPARSA(trace)
# key = spa.extract_key()
# spa.visualize_key_extraction()
```

### **SPA para AES**

```python
#!/usr/bin/env python3
# spa_aes.py

import numpy as np
import matplotlib.pyplot as plt

class SPAAES:
    """
    Ataque SPA para AES
    """
    
    def __init__(self, trace):
        self.trace = trace
        self.rounds = []
    
    def detect_rounds(self):
        """
        Detectar rounds do AES
        """
        # Calcular derivada para encontrar transições
        derivative = np.diff(self.trace)
        
        # Encontrar grandes quedas (fim de round)
        thresholds = np.where(derivative < -0.5)[0]
        
        # Agrupar quedas próximas
        rounds = []
        current_round = [thresholds[0]]
        
        for i in range(1, len(thresholds)):
            if thresholds[i] - thresholds[i-1] < 100:
                current_round.append(thresholds[i])
            else:
                rounds.append(np.mean(current_round))
                current_round = [thresholds[i]]
        
        rounds.append(np.mean(current_round))
        
        self.rounds = [int(r) for r in rounds]
        return len(self.rounds)
    
    def extract_round_info(self):
        """
        Extrair informações por round
        """
        num_rounds = self.detect_rounds()
        
        round_info = []
        for i in range(num_rounds-1):
            start = self.rounds[i]
            end = self.rounds[i+1]
            
            round_segment = self.trace[start:end]
            
            info = {
                'round': i+1,
                'start': start,
                'end': end,
                'duration': end - start,
                'max_power': np.max(round_segment),
                'min_power': np.min(round_segment),
                'avg_power': np.mean(round_segment)
            }
            round_info.append(info)
        
        return round_info
    
    def identify_last_round(self):
        """
        Identificar último round (diferente)
        """
        round_info = self.extract_round_info()
        
        # Último round não tem MixColumns
        if len(round_info) >= 10:
            last_round = round_info[-1]
            prev_round = round_info[-2]
            
            # Último round geralmente tem padrão diferente
            if last_round['duration'] < prev_round['duration'] * 0.8:
                print("[+] Último round identificado (sem MixColumns)")
                return last_round['round']
        
        return None
    
    def visualize_rounds(self):
        """
        Visualizar rounds do AES
        """
        self.detect_rounds()
        
        fig, ax = plt.subplots(figsize=(14, 6))
        
        ax.plot(self.trace, 'b-', alpha=0.7)
        
        # Marcar rounds
        for i, round_start in enumerate(self.rounds):
            ax.axvline(x=round_start, color='r', linestyle='--', alpha=0.5)
            ax.text(round_start + 50, 1.5, f'R{i+1}', fontsize=9)
        
        ax.set_title('SPA - Identificação de Rounds no AES')
        ax.set_xlabel('Amostras')
        ax.set_ylabel('Consumo de Potência (normalizado)')
        ax.grid(True, alpha=0.3)
        
        plt.tight_layout()
        plt.show()
```

### **SPA para ECC (Curva Elíptica)**

```python
#!/usr/bin/env python3
# spa_ecc.py

import numpy as np

class SPAECC:
    """
    Ataque SPA para ECC (Curvas Elípticas)
    """
    
    def __init__(self, trace):
        self.trace = trace
    
    def detect_operations(self):
        """
        Detectar operações de adição e dobramento
        """
        # Encontrar picos
        peaks = []
        for i in range(1, len(self.trace)-1):
            if self.trace[i] > self.trace[i-1] and self.trace[i] > self.trace[i+1]:
                peaks.append(i)
        
        # Analisar padrão
        # Adição: dois picos próximos
        # Dobramento: um pico
        
        operations = []
        i = 0
        while i < len(peaks)-1:
            if peaks[i+1] - peaks[i] < 30:
                operations.append('addition')
                i += 2
            else:
                operations.append('doubling')
                i += 1
        
        return operations
    
    def extract_scalar(self):
        """
        Extrair escalar (chave privada) no ECC
        """
        ops = self.detect_operations()
        
        # No ECC, cada bit do escalar determina a operação
        # Algoritmo Double-and-Add
        
        scalar_bits = []
        for op in ops:
            if op == 'doubling':
                scalar_bits.append(0)
            else:
                scalar_bits.append(1)
        
        return scalar_bits
```

***

## 🎯 **Ataques a Algoritmos Criptográficos**

### **Ataque a Implementação Ingênua de RSA**

```python
#!/usr/bin/env python3
# naive_rsa_attack.py

class NaiveRSAAttack:
    """
    Ataque a implementação ingênua de RSA
    """
    
    @staticmethod
    def vulnerable_modular_exponentiation(base, exponent, modulus):
        """
        Implementação vulnerável (vazamento de informação)
        """
        result = 1
        base = base % modulus
        
        # Para cada bit do expoente
        while exponent > 0:
            # Quadrado (sempre executado)
            result = (result * result) % modulus
            
            # Se bit atual é 1, multiplica
            if exponent & 1:
                result = (result * base) % modulus
            
            exponent >>= 1
        
        return result
    
    @staticmethod
    def analyze_power_trace():
        """
        Analisar traço de potência para extrair chave
        """
        # Simular traço com vazamento
        key_bits = []
        
        # Padrão visível:
        # - Quadrado: pulso pequeno
        # - Multiplicação: pulso grande
        
        # Atacante observa o tamanho dos pulsos
        # e determina os bits da chave
        
        print("[!] Chave RSA extraída via SPA!")
        return key_bits
```

### **Ataque a Implementação de AES com vazamento**

```python
#!/usr/bin/env python3
# aes_leakage_attack.py

class AESLeakageAttack:
    """
    Ataque a implementação de AES com vazamento
    """
    
    @staticmethod
    def vulnerable_subbytes(state):
        """
        SubBytes com vazamento (Hamming weight)
        """
        sbox = [
            0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
            0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76
        ]
        
        for i in range(16):
            # O consumo de energia depende do valor da S-box
            # Hamming weight do resultado vaza
            state[i] = sbox[state[i]]
            
            # Em hardware real, o consumo seria proporcional
            # ao número de bits 1 no resultado
        
        return state
    
    @staticmethod
    def attack_single_byte(ciphertext, power_trace):
        """
        Atacar um byte da chave
        """
        # Para cada possível valor de chave
        for key_guess in range(256):
            # Calcular saída da S-box hipotética
            sbox_input = ciphertext[0] ^ key_guess
            # (A saída da S-box depende do valor)
            # Comparar com o traço de potência
            
            pass
        
        return None
```

***

## 🛡️ **Contramedidas e Proteções**

### **Técnicas de Proteção**

```yaml
Proteções Contra SPA:
  
  Hiding (Ocultação):
    - Adição de operações dummy
    - Randomização de timing
    - Filtros de potência
    - Ruído adicional
  
  Masking (Mascaramento):
    - Divisão de dados em shares
    - Valores aleatórios intermediários
    - Operações em campos diferentes
  
  Algoritmos Constantes:
    - Tempo de execução constante
    - Sequência de operações fixa
    - Ex: Montgomery ladder para RSA
  
  Hardware:
    - Reguladores de tensão
    - Capacitores de decoupling
    - Blindagem eletromagnética
```

### **Implementação Constante para RSA (Montgomery Ladder)**

```python
#!/usr/bin/env python3
# constant_time_rsa.py

class ConstantTimeRSA:
    """
    Implementação de RSA com tempo constante (resistente a SPA)
    """
    
    @staticmethod
    def montgomery_ladder(base, exponent, modulus):
        """
        Montgomery ladder - tempo constante
        """
        r0 = 1
        r1 = base
        
        # Para cada bit do expoente (do MSB para LSB)
        for i in range(exponent.bit_length() - 1, -1, -1):
            bit = (exponent >> i) & 1
            
            # Sempre executa quadrado e multiplicação
            # Ordem depende do bit, mas ambas operações ocorrem
            if bit == 0:
                r1 = (r1 * r0) % modulus
                r0 = (r0 * r0) % modulus
            else:
                r0 = (r0 * r1) % modulus
                r1 = (r1 * r1) % modulus
        
        return r0
    
    @staticmethod
    def blinded_exponentiation(base, exponent, modulus):
        """
        Exponenciação com blinding (resistente a SPA/DPA)
        """
        import random
        
        # Gerar blinding factor
        r = random.randint(2, modulus - 2)
        r_inv = pow(r, -1, modulus)
        
        # Blinding
        blinded_base = (base * pow(r, exponent, modulus)) % modulus
        
        # Exponenciação normal
        result = pow(blinded_base, exponent, modulus)
        
        # Remove blinding
        result = (result * r_inv) % modulus
        
        return result
```

### **Implementação Constante para AES**

```python
#!/usr/bin/env python3
# constant_time_aes.py

class ConstantTimeAES:
    """
    Implementação de AES com tempo constante
    """
    
    @staticmethod
    def constant_time_lookup(value, sbox):
        """
        Lookup table com tempo constante
        """
        result = 0
        for i in range(256):
            # Sempre percorre toda a tabela
            # Tempo independe do valor
            mask = (i ^ value) - 1
            mask = (mask >> 31) & 1
            result ^= (sbox[i] & -mask)
        
        return result
    
    @staticmethod
    def constant_time_subbytes(state, sbox):
        """
        SubBytes com tempo constante
        """
        for i in range(16):
            state[i] = ConstantTimeAES.constant_time_lookup(state[i], sbox)
        
        return state
```

***

## 📊 **Conclusão**

### **Resumo Técnico**

```yaml
Simple Power Analysis (SPA):
  ✅ Ataque de canal lateral direto e eficaz
  ✅ Requer poucos traços (1-10)
  ✅ Equipamento de médio custo
  ✅ Implementações ingênuas são vulneráveis

Ameaças:
  - Extração de chave RSA (bit a bit)
  - Identificação de rounds em AES
  - Recuperação de escalar em ECC
  - Bypass de proteções de software

Defesas:
  - Tempo constante (Montgomery ladder)
  - Operações dummy
  - Blinding de dados
  - Hardware com proteções
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://0xmorte.gitbook.io/bibliadopentestbr/tecnicas/hardware/simple-power-analysis.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
