# Differential Power Analysis

## 📑 **Índice**

1. [Fundamentos da Differential Power Analysis](#-fundamentos-da-differential-power-analysis)
2. [Arquitetura e Princípios Matemáticos](#-arquitetura-e-princípios-matemáticos)
3. [Tipos de Ataques de Potência](#-tipos-de-ataques-de-potência)
4. [Equipamentos e Configuração](#-equipamentos-e-configuração)
5. [Metodologia de Ataque](#-metodologia-de-ataque)
6. [Implementação em Python](#-implementação-em-python)
7. [Contramedidas e Proteções](#-contramedidas-e-proteções)
8. [Ferramentas e Hardware](#-ferramentas-e-hardware)

***

## 🔍 **Fundamentos da Differential Power Analysis**

### **O que é Differential Power Analysis (DPA)?**

A **Differential Power Analysis (DPA)** é uma técnica avançada de ataque de canal lateral (side-channel attack) que explora a correlação estatística entre o consumo de energia de um dispositivo criptográfico (como smart cards, HSMs, microcontroladores) e os dados que ele processa. Diferente da Simple Power Analysis (SPA), que observa padrões diretamente visíveis, a DPA utiliza métodos estatísticos para extrair chaves secretas mesmo quando o sinal de potência é ruidoso ou mascarado por contramedidas.

### **Contexto Histórico**

```yaml
Evolução da DPA:
  1996: Paul Kocher demonstra pela primeira vez ataques de timing
  1998: Kocher, Jaffe e Jun introduzem a Differential Power Analysis
  1999: Primeiros ataques DPA contra smart cards bancários
  2000: Introdução das primeiras contramedidas (masking, hiding)
  2005: DPA de ordem superior (HO-DPA) desenvolvida
  2010: Ataques DPA contra AES em dispositivos móveis
  2015: DPA em chips IoT e hardware embarcado
  2020: Machine Learning aplicado à DPA (DL-SCA)
  2024: Ataques DPA contra aceleradores criptográficos em CPUs modernas

Motivação:
  ✅ Dispositivos criptográficos são "blindados" contra ataques lógicos
  ✅ Ataques físicos podem contornar proteções de software
  ✅ Informação vaza através de canais laterais (potência, tempo, EM)
  ✅ DPA funciona mesmo com amostras ruidosas
```

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

```mermaid
graph TD
    subgraph "Dispositivo Alvo"
        A[Entrada de Dados] --> B[Operação Criptográfica]
        B --> C[Consumo de Energia]
    end
    
    subgraph "Medição"
        D[Osciloscópio] --> E[Traços de Potência]
    end
    
    subgraph "Análise"
        F[Hipóteses de Chave] --> G[Modelo de Potência]
        G --> H[Correlação Estatística]
        H --> I[Chave Encontrada]
    end
    
    C --> D
    E --> H
```

### **Comparação com Outros Ataques de Canal Lateral**

| Técnica                  | Informação Necessária        | Complexidade | Ruído | Contramedidas     |
| ------------------------ | ---------------------------- | ------------ | ----- | ----------------- |
| **Timing Attack**        | Tempo de execução            | Baixa        | Baixo | Const-time ops    |
| **SPA**                  | Traço de potência único      | Baixa        | Baixo | Hiding            |
| **DPA**                  | Múltiplos traços (100-10000) | Alta         | Alta  | Masking           |
| **CPA**                  | Múltiplos traços             | Média        | Média | Masking           |
| **Template Attack**      | Perfil do dispositivo        | Muito Alta   | Baixa | Rotação de chaves |
| **Machine Learning SCA** | Grande conjunto de treino    | Alta         | Média | Desconhecida      |

***

## 🏗️ **Arquitetura e Princípios Matemáticos**

### **Canal Lateral de Potência**

Quando um dispositivo CMOS processa dados, o consumo de energia varia com base nos bits sendo processados. A transição de 0→1 consome mais energia que a transição 1→0 ou manutenção do estado.

```yaml
Consumo de Potência em CMOS:
  Transição 0→1: Alta potência (carregamento do capacitor)
  Transição 1→0: Baixa potência (descarregamento)
  Manutenção: Mínima (apenas corrente de fuga)

Modelo de Hamming Weight:
  - Peso de Hamming: número de bits 1 em um valor
  - Consumo ∝ HW(valor)
  - Exemplo: HW(0x0F) = 4, HW(0xF0) = 4

Modelo de Hamming Distance:
  - Distância de Hamming: número de bits que mudam
  - Consumo ∝ HD(anterior, atual)
  - Usado para registradores e barramentos
```

### **Matemática da DPA**

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

import numpy as np
from scipy.stats import pearsonr, ttest_ind
import hashlib

class DPAMathematics:
    """
    Fundamentos matemáticos da Differential Power Analysis
    """
    
    @staticmethod
    def hamming_weight(value):
        """Calcular peso de Hamming"""
        return bin(value).count('1')
    
    @staticmethod
    def hamming_distance(val1, val2):
        """Calcular distância de Hamming"""
        return DPAMathematics.hamming_weight(val1 ^ val2)
    
    @staticmethod
    def correlation_coefficient(traces, predictions):
        """
        Coeficiente de correlação de Pearson
        ρ = Cov(X,Y) / (σX * σY)
        """
        traces = np.array(traces)
        predictions = np.array(predictions)
        
        # Média
        mean_traces = np.mean(traces, axis=0)
        mean_pred = np.mean(predictions)
        
        # Covariância
        cov = np.mean((traces - mean_traces) * (predictions - mean_pred), axis=0)
        
        # Desvios padrão
        std_traces = np.std(traces, axis=0)
        std_pred = np.std(predictions)
        
        # Correlação
        with np.errstate(divide='ignore', invalid='ignore'):
            correlation = cov / (std_traces * std_pred)
        
        return correlation
    
    @staticmethod
    def t_test(trace_a, trace_b):
        """
        Teste t de Student para diferença de médias
        t = (μA - μB) / sqrt(σA²/nA + σB²/nB)
        """
        from scipy import stats
        return stats.ttest_ind(trace_a, trace_b, axis=0)
    
    @staticmethod
    def mutual_information(traces, predictions):
        """
        Informação mútua entre traços e predições
        I(X;Y) = H(X) - H(X|Y)
        """
        from sklearn.feature_selection import mutual_info_regression
        return mutual_info_regression(traces.T, predictions)
    
    @staticmethod
    def sasebo_distinguishability(traces_a, traces_b):
        """
        Métrica de distinguibilidade de Sasebo
        d = (μA - μB)² / (σA² + σB²)
        """
        mean_a = np.mean(traces_a, axis=0)
        mean_b = np.mean(traces_b, axis=0)
        var_a = np.var(traces_a, axis=0)
        var_b = np.var(traces_b, axis=0)
        
        distinguishability = (mean_a - mean_b) ** 2 / (var_a + var_b)
        return distinguishability

# Exemplo
math = DPAMathematics()
print(f"Hamming Weight de 0x55: {math.hamming_weight(0x55)}")
print(f"Hamming Distance entre 0x00 e 0xFF: {math.hamming_distance(0x00, 0xFF)}")
```

### **Modelo de Potência para DPA**

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

import numpy as np

class PowerModel:
    """
    Modelos de consumo de potência para DPA
    """
    
    @staticmethod
    def hamming_weight_model(value):
        """Modelo baseado no peso de Hamming"""
        return bin(value).count('1')
    
    @staticmethod
    def hamming_distance_model(prev, current):
        """Modelo baseado na distância de Hamming"""
        return bin(prev ^ current).count('1')
    
    @staticmethod
    def zero_value_model(value):
        """Modelo baseado no valor zero (bit a bit)"""
        # Prediz 1 se o bit é 0, 0 caso contrário
        return [(~value >> i) & 1 for i in range(8)]
    
    @staticmethod
    def one_value_model(value):
        """Modelo baseado no valor um (bit a bit)"""
        # Prediz 1 se o bit é 1, 0 caso contrário
        return [(value >> i) & 1 for i in range(8)]
    
    @staticmethod
    def bit_model(value, bit_position):
        """Modelo baseado em um bit específico"""
        return (value >> bit_position) & 1
    
    @staticmethod
    def sbox_output_model(sbox_input, sbox_table, key_guess):
        """
        Modelo baseado na saída de uma S-box
        Usado para ataques DPA em AES, DES, etc.
        """
        sbox_output = sbox_table[sbox_input ^ key_guess]
        return PowerModel.hamming_weight_model(sbox_output)
    
    @staticmethod
    def register_model(prev_value, new_value):
        """Modelo para registradores (distância de Hamming)"""
        return PowerModel.hamming_distance_model(prev_value, new_value)

# Exemplo com S-box do AES
AES_SBOX = [
    0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
    0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
    # ... (restante da S-box)
]

model = PowerModel()
print(f"Modelo HW(0x55): {model.hamming_weight_model(0x55)}")
print(f"Modelo Bit 3 de 0x55: {model.bit_model(0x55, 3)}")
```

***

## 🎯 **Tipos de Ataques de Potência**

### **1. Simple Power Analysis (SPA)**

Observação direta de um único traço de potência para identificar operações criptográficas.

```yaml
Características:
  - Requer poucas amostras (1-10)
  - Baixa complexidade computacional
  - Vulnerável a contramedidas de hiding
  - Exemplos:
    - Identificação de multiplicação vs adição em RSA
    - Distinção entre rounds em AES
    - Identificação de operações de chave
```

### **2. Differential Power Analysis (DPA) Clássica**

```mermaid
graph LR
    subgraph "Fase 1: Coleta"
        A[Medir N traços] --> B[Traços de Potência]
    end
    
    subgraph "Fase 2: Hipóteses"
        C[Chave Guess K] --> D[Predição de Potência]
    end
    
    subgraph "Fase 3: Correlação"
        B --> E[Correlação]
        D --> E
        E --> F[Pico indica chave correta]
    end
```

### **3. Correlation Power Analysis (CPA)**

Utiliza correlação de Pearson em vez de diferença de médias.

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

import numpy as np
from scipy.stats import pearsonr

class CPAAttack:
    """
    Correlation Power Analysis - Implementação básica
    """
    
    def __init__(self, traces, plaintexts, sbox):
        self.traces = np.array(traces)
        self.plaintexts = np.array(plaintexts)
        self.sbox = sbox
        self.num_traces = len(traces)
        self.num_points = len(traces[0])
    
    def predict_power(self, key_guess, plaintext):
        """
        Predizer consumo de potência baseado na saída da S-box
        """
        sbox_input = plaintext ^ key_guess
        sbox_output = self.sbox[sbox_input]
        return bin(sbox_output).count('1')
    
    def correlate(self, key_guess):
        """
        Calcular correlação para um guess de chave
        """
        predictions = []
        for pt in self.plaintexts:
            pred = self.predict_power(key_guess, pt)
            predictions.append(pred)
        
        predictions = np.array(predictions)
        correlations = []
        
        for point in range(self.num_points):
            trace_points = self.traces[:, point]
            corr, _ = pearsonr(trace_points, predictions)
            correlations.append(corr)
        
        return np.array(correlations)
    
    def attack(self):
        """
        Executar ataque CPA para todos os guesses
        """
        best_correlations = []
        
        for guess in range(256):
            corr = self.correlate(guess)
            max_corr = np.max(np.abs(corr))
            best_correlations.append((guess, max_corr))
        
        # Encontrar melhor guess
        best_guess = max(best_correlations, key=lambda x: x[1])
        
        return best_guess[0], best_guess[1]

# Exemplo de uso (simulado)
# attack = CPAAttack(traces, plaintexts, AES_SBOX)
# key, confidence = attack.attack()
```

### **4. Higher-Order DPA (HO-DPA)**

Combina múltiplos pontos de amostragem para contornar contramedidas de masking.

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

import numpy as np
from itertools import combinations

class HigherOrderDPA:
    """
    Higher-Order Differential Power Analysis
    Contorna contramedidas de masking
    """
    
    def __init__(self, traces, plaintexts):
        self.traces = np.array(traces)
        self.plaintexts = np.array(plaintexts)
    
    def first_order_dpa(self, key_guess, point):
        """
        DPA de primeira ordem (clássica)
        """
        predictions = []
        for pt in self.plaintexts:
            sbox_input = pt ^ key_guess
            pred = bin(sbox_input).count('1')
            predictions.append(pred)
        
        # Separar grupos
        group0 = self.traces[np.array(predictions) == 0, point]
        group1 = self.traces[np.array(predictions) == 1, point]
        
        if len(group0) > 0 and len(group1) > 0:
            diff = np.mean(group0) - np.mean(group1)
            return abs(diff)
        return 0
    
    def second_order_dpa(self, key_guess, point1, point2):
        """
        DPA de segunda ordem - combina dois pontos
        Contorna masking de primeira ordem
        """
        predictions = []
        for pt in self.plaintexts:
            sbox_input = pt ^ key_guess
            pred = bin(sbox_input).count('1')
            predictions.append(pred)
        
        # Produto dos dois pontos (centrado)
        combined = (self.traces[:, point1] - np.mean(self.traces[:, point1])) * \
                   (self.traces[:, point2] - np.mean(self.traces[:, point2]))
        
        group0 = combined[np.array(predictions) == 0]
        group1 = combined[np.array(predictions) == 1]
        
        if len(group0) > 0 and len(group1) > 0:
            diff = np.mean(group0) - np.mean(group1)
            return abs(diff)
        return 0
    
    def third_order_dpa(self, key_guess, points):
        """
        DPA de terceira ordem - combina três pontos
        Contorna masking de segunda ordem
        """
        predictions = []
        for pt in self.plaintexts:
            sbox_input = pt ^ key_guess
            pred = bin(sbox_input).count('1')
            predictions.append(pred)
        
        # Produto centralizado dos três pontos
        centered = self.traces - np.mean(self.traces, axis=0)
        combined = np.prod(centered[:, points], axis=1)
        
        group0 = combined[np.array(predictions) == 0]
        group1 = combined[np.array(predictions) == 1]
        
        if len(group0) > 0 and len(group1) > 0:
            diff = np.mean(group0) - np.mean(group1)
            return abs(diff)
        return 0

# Exemplo
# ho_dpa = HigherOrderDPA(traces, plaintexts)
```

***

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

### **Hardware Necessário**

```yaml
Componentes Essenciais:
  
  Osciloscópio Digital:
    - Taxa de amostragem: ≥ 100 MS/s (megaamostras/segundo)
    - Resolução: ≥ 8 bits (12-14 bits recomendado)
    - Largura de banda: ≥ 100 MHz
    - Modelos: Rigol DS1054Z (~R$ 2.500), PicoScope 2204A
    
  Sonda de Corrente:
    - Alta sensibilidade (mA a μA)
    - Banda larga (≥ 100 MHz)
    - Modelos: TCP0030A (Tektronix), CT-1 (B&K Precision)
  
  Sonda de Tensão:
    - Medição diferencial recomendada
    - Baixa capacitância de entrada
    - Modelos: Tektronix P6245, LeCroy AP034
  
  Placa de Aquisição:
    - Sincronização com trigger
    - Interface USB/Ethernet
    - Modelos: ChipWhisperer, NewAE Luna
  
  Target Device:
    - Smart card, microcontrolador, FPGA
    - Interface de comunicação (UART, JTAG, SPI)
```

### **Configuração de Laboratório**

```mermaid
graph TD
    subgraph "Setup de Medição"
        A[PC com Software] --> B[Osciloscópio]
        B --> C[Sonda de Corrente]
        C --> D[Target Device]
        D --> E[Trigger Sync]
        E --> B
    end
    
    subgraph "Controle"
        A --> F[Controlador USB/GPIB]
        F --> B
    end
    
    subgraph "Target"
        D --> G[Power Supply]
        G --> D
    end
```

### **Custos Estimados (BRL)**

| Equipamento              | Modelo Recomendado   | Preço Médio (R$)    | Link Sugerido            |
| ------------------------ | -------------------- | ------------------- | ------------------------ |
| **Osciloscópio**         | Rigol DS1054Z        | \~R$ 2.500 - 3.500  | Mercado Livre, Techtools |
| **Sonda de Corrente**    | B\&K Precision CT-1  | \~R$ 800 - 1.500    | Importação (AliExpress)  |
| **Sonda Diferencial**    | Micsig DP10013       | \~R$ 600 - 1.000    | AliExpress, Shopee       |
| **ChipWhisperer**        | NewAE CW308          | \~R$ 3.000 - 5.000  | newae.com (importação)   |
| **Resistor Shunt (50Ω)** | -                    | \~R$ 10 - 50        | Mercado Livre            |
| **Cabo SMA**             | -                    | \~R$ 30 - 80        | Mercado Livre, Shopee    |
| **Total Básico**         | Osciloscópio + Sonda | \~R$ 3.500 - 5.000  | -                        |
| **Total Avançado**       | + ChipWhisperer      | \~R$ 7.000 - 10.000 | -                        |

***

## 🔬 **Metodologia de Ataque**

### **Passo a Passo da DPA**

```mermaid
flowchart TD
    A[1. Selecionar Alvo] --> B[2. Configurar Medição]
    B --> C[3. Coletar Traços de Potência]
    C --> D[4. Alinhamento de Traços]
    D --> E[5. Selecionar Ponto de Interesse]
    E --> F[6. Escolher Modelo de Potência]
    F --> G[7. Ataque de Hipótese de Chave]
    G --> H{Correlação Alta?}
    H -->|Não| I[Testar Próxima Hipótese]
    I --> G
    H -->|Sim| J[8. Verificar Chave]
    J --> K[9. Validar com Diferentes Traços]
```

### **Script de Coleta de Traços**

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

import numpy as np
import time
import serial
import struct

class TraceCollector:
    """
    Coleta de traços de potência via ChipWhisperer ou osciloscópio
    """
    
    def __init__(self, scope_type='simulated'):
        self.scope_type = scope_type
        self.traces = []
        self.plaintexts = []
        self.ciphertexts = []
        
        if scope_type == 'simulated':
            self._init_simulated()
        elif scope_type == 'chipwhisperer':
            self._init_chipwhisperer()
        elif scope_type == 'oscilloscope':
            self._init_oscilloscope()
    
    def _init_simulated(self):
        """Modo simulado para testes"""
        print("[*] Inicializando modo simulado")
        self.simulated = True
    
    def _init_chipwhisperer(self):
        """Inicializar ChipWhisperer"""
        try:
            import chipwhisperer as cw
            self.scope = cw.scope()
            self.target = cw.target(scope)
            self.simulated = False
            print("[+] ChipWhisperer inicializado")
        except ImportError:
            print("[-] ChipWhisperer não encontrado")
            self._init_simulated()
    
    def _init_oscilloscope(self):
        """Inicializar osciloscópio (exemplo com PyVISA)"""
        try:
            import pyvisa
            rm = pyvisa.ResourceManager()
            self.scope = rm.open_resource('USB0::0x0000::0x0000::INSTR')
            self.simulated = False
            print("[+] Osciloscópio inicializado")
        except:
            print("[-] Osciloscópio não encontrado")
            self._init_simulated()
    
    def capture_trace(self, plaintext, key=None):
        """
        Capturar um traço de potência para um plaintext específico
        """
        if self.simulated:
            # Simular traço com ruído
            trace = self._simulate_trace(plaintext, key)
            return trace
        
        # Implementação real
        self.target.load_plaintext(plaintext)
        self.scope.arm()
        self.target.go()
        trace = self.scope.capture()
        
        return trace
    
    def _simulate_trace(self, plaintext, key=None):
        """
        Simular traço de potência para testes
        """
        import random
        
        # Comprimento do traço (1000 pontos)
        trace_len = 1000
        
        if key:
            # Simular vazamento baseado no peso de Hamming da saída da S-box
            sbox_input = plaintext ^ key
            sbox_output = self._simulate_sbox(sbox_input)
            leakage = bin(sbox_output).count('1') / 8.0
        else:
            leakage = random.uniform(0.3, 0.7)
        
        # Criar traço com ruído gaussiano
        trace = np.zeros(trace_len)
        # Pico na posição 500
        trace[500] = leakage * 0.5 + 0.2
        
        # Adicionar ruído
        trace += np.random.normal(0, 0.05, trace_len)
        
        return trace
    
    def _simulate_sbox(self, value):
        """S-box simulada para testes"""
        return (value * 0x1B) & 0xFF
    
    def collect_traces(self, num_traces, key=None):
        """
        Coletar múltiplos traços
        """
        print(f"[*] Coletando {num_traces} traços...")
        
        for i in range(num_traces):
            plaintext = np.random.randint(0, 256)
            trace = self.capture_trace(plaintext, key)
            
            self.traces.append(trace)
            self.plaintexts.append(plaintext)
            
            if i % 100 == 0:
                print(f"  Coletados {i} traços")
        
        print(f"[+] Coleta concluída: {len(self.traces)} traços")
        return np.array(self.traces), np.array(self.plaintexts)
    
    def save_traces(self, filename):
        """Salvar traços em arquivo NPZ"""
        np.savez(filename, 
                 traces=self.traces, 
                 plaintexts=self.plaintexts,
                 ciphertexts=self.ciphertexts)
        print(f"[+] Traços salvos em {filename}")
    
    def load_traces(self, filename):
        """Carregar traços de arquivo"""
        data = np.load(filename)
        self.traces = data['traces']
        self.plaintexts = data['plaintexts']
        if 'ciphertexts' in data:
            self.ciphertexts = data['ciphertexts']
        print(f"[+] Traços carregados de {filename}")

# Exemplo
collector = TraceCollector('simulated')
traces, plaintexts = collector.collect_traces(1000, key=0x42)
collector.save_traces('traces.npz')
```

***

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

### **DPA Attack Completo**

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

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import pearsonr
import hashlib

class DPAAttack:
    """
    Implementação completa de Differential Power Analysis
    """
    
    # S-box do AES (para demonstração)
    AES_SBOX = [
        0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
        0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
        0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
        0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
        0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
        0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
        0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
        0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
        0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
        0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
        0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
        0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
        0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
        0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
        0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
        0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
    ]
    
    def __init__(self, traces, plaintexts):
        """
        Inicializar ataque DPA
        traces: matriz de traços de potência (N x L)
        plaintexts: vetor de plaintexts (N)
        """
        self.traces = np.array(traces)
        self.plaintexts = np.array(plaintexts)
        self.num_traces = len(traces)
        self.num_points = len(traces[0])
    
    def predict_power(self, key_guess, plaintext, model='hw'):
        """
        Predizer consumo de potência baseado na saída da S-box
        """
        sbox_input = plaintext ^ key_guess
        sbox_output = self.AES_SBOX[sbox_input]
        
        if model == 'hw':
            return bin(sbox_output).count('1')
        elif model == 'bit0':
            return (sbox_output >> 0) & 1
        elif model == 'bit7':
            return (sbox_output >> 7) & 1
        else:
            return sbox_output
    
    def differential_trace(self, key_guess, model='hw'):
        """
        Calcular traço diferencial para um guess de chave
        """
        # Dividir traços baseado na predição
        group0_indices = []
        group1_indices = []
        
        for i, pt in enumerate(self.plaintexts):
            pred = self.predict_power(key_guess, pt, model)
            if pred == 0:
                group0_indices.append(i)
            else:
                group1_indices.append(i)
        
        # Calcular médias
        group0_mean = np.mean(self.traces[group0_indices], axis=0)
        group1_mean = np.mean(self.traces[group1_indices], axis=0)
        
        # Traço diferencial
        diff_trace = group0_mean - group1_mean
        
        return diff_trace
    
    def attack_byte(self, start_guess=0, end_guess=256):
        """
        Atacar um byte da chave
        """
        best_guess = None
        best_peak = 0
        
        print(f"[*] Atacando byte...")
        
        for guess in range(start_guess, end_guess):
            diff_trace = self.differential_trace(guess)
            max_peak = np.max(np.abs(diff_trace))
            
            if max_peak > best_peak:
                best_peak = max_peak
                best_guess = guess
                print(f"  Novo melhor guess: {hex(guess)} (peak: {best_peak:.4f})")
        
        print(f"[+] Melhor guess: {hex(best_guess)} (peak: {best_peak:.4f})")
        return best_guess, best_peak
    
    def attack_all_bytes(self, num_bytes=16):
        """
        Atacar todos os bytes da chave AES
        """
        recovered_key = []
        
        for byte_pos in range(num_bytes):
            # Extrair plaintexts para este byte
            byte_plaintexts = (self.plaintexts >> (8 * byte_pos)) & 0xFF
            
            # Criar novo ataque para este byte
            attack = DPAAttack(self.traces, byte_plaintexts)
            key_byte, confidence = attack.attack_byte()
            recovered_key.append(key_byte)
            
            print(f"Byte {byte_pos}: {hex(key_byte)} (confiança: {confidence:.4f})")
        
        return bytes(recovered_key)
    
    def plot_results(self, key_guess):
        """
        Visualizar resultados do ataque
        """
        diff_trace = self.differential_trace(key_guess)
        
        fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8))
        
        # Traço diferencial
        ax1.plot(diff_trace)
        ax1.set_title(f'Traço Diferencial para Key Guess: {hex(key_guess)}')
        ax1.set_xlabel('Amostra')
        ax1.set_ylabel('Diferença de Potência')
        ax1.grid(True)
        
        # Pico máximo
        max_peak = np.max(np.abs(diff_trace))
        peak_pos = np.argmax(np.abs(diff_trace))
        ax1.axvline(x=peak_pos, color='r', linestyle='--', 
                    label=f'Pico máximo: {max_peak:.4f}')
        ax1.legend()
        
        # Correlação entre guesses
        correlations = []
        guesses = range(256)
        for guess in guesses:
            diff = self.differential_trace(guess)
            corr = np.max(np.abs(diff))
            correlations.append(corr)
        
        ax2.plot(guesses, correlations)
        ax2.set_title('Correlação por Key Guess')
        ax2.set_xlabel('Key Guess')
        ax2.set_ylabel('Máxima Correlação')
        ax2.grid(True)
        
        # Marcar guess correto
        correct_guess = max(correlations)
        correct_pos = np.argmax(correlations)
        ax2.axvline(x=correct_pos, color='g', linestyle='--', 
                    label=f'Guess correto: {hex(correct_pos)}')
        ax2.legend()
        
        plt.tight_layout()
        plt.show()
    
    def calculate_metrics(self, recovered_key, actual_key):
        """
        Calcular métricas de sucesso do ataque
        """
        correct_bytes = sum(r == a for r, a in zip(recovered_key, actual_key))
        success_rate = correct_bytes / len(recovered_key) * 100
        
        metrics = {
            'correct_bytes': correct_bytes,
            'total_bytes': len(recovered_key),
            'success_rate': success_rate,
            'recovered_key': recovered_key.hex(),
            'actual_key': actual_key.hex() if actual_key else 'Unknown'
        }
        
        return metrics

# Exemplo de uso com dados simulados
if __name__ == "__main__":
    # Gerar dados simulados
    np.random.seed(42)
    num_traces = 500
    trace_len = 1000
    actual_key = 0x42
    
    # Coletar traços
    collector = TraceCollector('simulated')
    traces, plaintexts = collector.collect_traces(num_traces, key=actual_key)
    
    # Executar ataque
    attack = DPAAttack(traces, plaintexts)
    recovered_key, confidence = attack.attack_byte()
    
    print(f"\n=== Resultados ===")
    print(f"Chave real: {hex(actual_key)}")
    print(f"Chave recuperada: {hex(recovered_key)}")
    print(f"Confiança: {confidence:.4f}")
    
    # Plotar resultados
    attack.plot_results(recovered_key)
```

***

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

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

```yaml
Proteções em Hardware:
  
  Hiding (Ocultação):
    - Clock jitter (variação do clock)
    - Ruído adicionado (hardware noise)
    - Execução paralela de operações
    - Capacitores de decoupling
  
  Masking (Mascaramento):
    - Divisão de dados em shares (Blakley, Shamir)
    - Máscaras aleatórias por operação
    - Boolean masking, arithmetic masking
    - Higher-order masking (até 3ª ordem)
  
  Shuffling (Embaralhamento):
    - Ordem aleatória de operações
    - Execução não determinística
  
  Dummy Operations:
    - Operações falsas para confundir análise
    - Caminhos de execução alternativos
```

### **Implementação de Masking em AES**

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

import random

class AESMasking:
    """
    Implementação de AES com proteção contra DPA
    usando Boolean Masking
    """
    
    AES_SBOX = [
        0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
        # ... (S-box completa)
    ]
    
    # S-box mascarada
    MASKED_SBOX = [0] * 256
    
    @classmethod
    def generate_masked_sbox(cls, mask_in, mask_out):
        """
        Gerar S-box mascarada
        S'(x ⊕ mask_in) = S(x) ⊕ mask_out
        """
        cls.MASKED_SBOX = [0] * 256
        for x in range(256):
            masked_input = x ^ mask_in
            sbox_output = cls.AES_SBOX[x]
            masked_output = sbox_output ^ mask_out
            cls.MASKED_SBOX[masked_input] = masked_output
        return cls.MASKED_SBOX
    
    @classmethod
    def masked_sub_bytes(cls, state, mask_in, mask_out):
        """
        SubBytes mascarado
        """
        for i in range(16):
            state[i] ^= mask_in
            state[i] = cls.MASKED_SBOX[state[i]]
            state[i] ^= mask_out
        return state
    
    @classmethod
    def refresh_masks(cls, state, mask):
        """
        Atualizar máscaras (refresh)
        """
        new_mask = random.randint(0, 255)
        for i in range(16):
            state[i] ^= mask
            state[i] ^= new_mask
        return state, new_mask
    
    @classmethod
    def secure_aes_encrypt(cls, plaintext, key, num_rounds=10):
        """
        AES com proteção DPA
        """
        # Gerar máscaras aleatórias
        mask_in = random.randint(0, 255)
        mask_out = random.randint(0, 255)
        mask_key = random.randint(0, 255)
        
        # Gerar S-box mascarada
        cls.generate_masked_sbox(mask_in, mask_out)
        
        # Mascarar estado inicial
        state = [p ^ mask_in for p in plaintext]
        round_key = [k ^ mask_key for k in key]
        
        # Executar rounds com masking
        for round in range(num_rounds):
            state = cls.masked_sub_bytes(state, mask_in, mask_out)
            # ShiftRows, MixColumns com masking...
            # Refresh masks
            state, mask_in = cls.refresh_masks(state, mask_in)
        
        # Remover máscara final
        result = [s ^ mask_in for s in state]
        
        return result
```

### **Verificação de Contramedidas**

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

class CountermeasureChecker:
    """
    Verificação de contramedidas DPA
    """
    
    @staticmethod
    def check_timing_variance(traces):
        """
        Verificar se há variação de timing (jitter)
        """
        # Detectar jitter nas bordas
        # Quanto maior a variação, mais proteção
        pass
    
    @staticmethod
    def check_power_distribution(traces):
        """
        Verificar distribuição de potência
        """
        # Distribuição uniforme indica masking
        hist, _ = np.histogram(traces.flatten(), bins=50)
        uniformity = np.std(hist) / np.mean(hist)
        
        if uniformity < 0.3:
            return "Masking detectado"
        else:
            return "Sem proteção aparente"
    
    @staticmethod
    def test_dpa_resistance(traces, plaintexts, num_tests=10):
        """
        Testar resistência a DPA
        """
        success_rates = []
        
        for test in range(num_tests):
            # Subamostragem dos traços
            indices = np.random.choice(len(traces), len(traces)//2, replace=False)
            sub_traces = traces[indices]
            sub_plaintexts = plaintexts[indices]
            
            # Tentar ataque
            attack = DPAAttack(sub_traces, sub_plaintexts)
            recovered, confidence = attack.attack_byte()
            
            # Verificar se chave foi recuperada
            success_rates.append(confidence)
        
        avg_success = np.mean(success_rates)
        
        if avg_success > 0.9:
            return "Vulnerável a DPA"
        elif avg_success > 0.5:
            return "Parcialmente protegido"
        else:
            return "Bem protegido contra DPA"
```

***

## 🔧 **Ferramentas e Hardware**

### **Ferramentas de Software**

```bash
# ChipWhisperer (Framework de SCA)
pip install chipwhisperer
cw_analyzer.py

# SCAPy (Side-Channel Analysis)
git clone https://github.com/IAIK/scapy
python3 scapy_analyzer.py

# Lascar (DPA em Python)
pip install lascar
# Exemplo: lascar_cpa -i traces.npy -p plaintexts.npy

# Jlsca (Java Side-Channel Analysis)
git clone https://github.com/jonof/jlsca

# Eta (DPA Framework)
git clone https://github.com/emsec/eta
```

### **Ferramentas de Hardware (Custos em BRL)**

| Ferramenta               | Descrição         | Preço (R$)           | Onde Encontrar           |
| ------------------------ | ----------------- | -------------------- | ------------------------ |
| **ChipWhisperer-Lite**   | Placa de SCA/AES  | \~R$ 2.500 - 3.500   | newae.com (importação)   |
| **ChipWhisperer-Pro**    | Profissional      | \~R$ 12.000 - 18.000 | newae.com                |
| **Rigol DS1054Z**        | Osciloscópio 4-ch | \~R$ 2.500 - 3.000   | Mercado Livre, Techtools |
| **PicoScope 2204A**      | USB Osciloscópio  | \~R$ 1.200 - 1.800   | Importação (Amazon US)   |
| **Sonda Corrente CT-1**  | 1 GHz probe       | \~R$ 800 - 1.200     | AliExpress               |
| **Resistor Shunt 50Ω**   | Para medição      | \~R$ 10 - 30         | Mercado Livre            |
| **Total Setup Básico**   | -                 | \~R$ 3.500 - 5.000   | -                        |
| **Total Setup Avançado** | -                 | \~R$ 15.000 - 25.000 | -                        |

***

## 📊 **Conclusão e Boas Práticas**

### **Resumo Técnico**

```yaml
Differential Power Analysis:
  ✅ Ataque de canal lateral poderoso e comprovado
  ✅ Funciona contra dispositivos criptográficos "blindados"
  ✅ Requer acesso físico ao dispositivo
  ✅ Pode ser realizado com equipamento de médio custo

Defesas:
  ❌ Proteção puramente lógica é insuficiente
  ✓ Combinar masking + hiding + shuffling
  ✓ Implementar verificações de integridade
  ✓ Limitar número de operações com mesma chave
```

### **Recomendações Finais**

1. **Para Desenvolvedores de Hardware**
   * Implementar masking de alta ordem (≥ 3ª ordem)
   * Adicionar jitter de clock aleatório
   * Usar fontes de ruído físico
   * Validar com testes de certificação (Common Criteria)
2. **Para Pesquisadores/Pentesters**
   * Começar com CPA (mais simples)
   * Praticar com dados simulados antes de hardware real
   * Utilizar ChipWhisperer para aprendizado
   * Documentar configurações de medição
3. **Para Profissionais de Segurança**
   * Avaliar riscos de canal lateral em produtos
   * Preferir chips com contramedidas certificadas
   * Limitar tempo de exposição de chaves
   * Implementar rotação frequente de chaves


---

# 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/differential-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.
