Unzureichender Schutz physischer Seitenkanale
Beschreibung
Unzureichender Schutz physischer Seitenkanale tritt auf, wenn ein Gerät keine angemessenen Schutzmechanismen besitzt, um zu verhindern, dass physische Seitenkanale sensible Informationen durch Muster in physisch beobachtbaren Phänomenen wie Variationen im Stromverbrauch, elektromagnetischen Emissionen (EME) oder akustischen Emissionen preisgeben. Angreifer mit physischem Zugang oder in der Nähe können diese physischen Phänomene überwachen und messen, um Muster zu erkennen und Geheimnisse zu extrahieren. Wenn der Angreifer den Hardware-Betrieb überwachen und dessen Datenverarbeitung mit Strom-, EME- und Akustikmessungen korrelieren kann, könnte er in der Lage sein, geheime Schlüssel und Daten wiederherzustellen.
Risiko
Physische Seitenkanale haben schwerwiegende Sicherheitsauswirkungen. Kryptographische Schlüssel durch Stromanalyse extrahierbar. Elektromagnetische Emissionen enthüllen Verarbeitungsmuster. Akustische Analyse kann Geheimnisse extrahieren. PIN-Codes über Timing wiederherstellbar. Private Schlüssel kompromittiert. Authentifizierung umgangen. Alle Geheimnisse bei physischer Nähe gefährdet. Besonders schwerwiegend für kryptographische Implementierungen und Sicherheitsfunktionen.
Lösung
Wenden Sie Blinding- oder Masking-Techniken auf kryptographische Algorithmus-Implementierungen während der Architektur- und Designphase an. Fügen Sie Abschirmung oder manipulationssichere Schutzmaßnahmen während der Implementierung hinzu, um die Messung zu erschweren. Verwenden Sie Constant-Time-Implementierungen. Fügen Sie zufällige Verzögerungen und Operationsreihenfolgen hinzu. Implementieren Sie Stromfilterung und EMI-Abschirmung. Verwenden Sie Physically Unclonable Functions (PUFs) für Schlüsselspeicherung.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Vertraulichkeit | Scope: Vertraulichkeit Geheime Schlüssel und sensible Daten durch physische Beobachtung extrahierbar. |
Beispielcode und Lösung
Verwundbarer Code
// VERWUNDBAR: RSA mit Strom-Seitenkanal-Leckage
module vulnerable_rsa_modexp (
input wire clk,
input wire rst_n,
input wire start,
input wire [2047:0] base, // Zu verschlüsselnde/entschlüsselnde Nachricht
input wire [2047:0] exponent, // Privater Schlüssel (geheim!)
input wire [2047:0] modulus,
output reg [2047:0] result,
output reg done
);
reg [2047:0] temp_result;
reg [2047:0] temp_base;
reg [2047:0] exponent_reg;
reg [11:0] bit_counter;
reg busy;
localparam IDLE = 2'b00;
localparam SQUARE = 2'b01;
localparam MULTIPLY = 2'b10;
localparam DONE_STATE = 2'b11;
reg [1:0] state;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
done <= 1'b0;
result <= 2048'b0;
end else begin
case (state)
IDLE: begin
if (start) begin
temp_result <= 2048'b1;
temp_base <= base;
exponent_reg <= exponent;
bit_counter <= 12'd2047;
state <= SQUARE;
end
end
SQUARE: begin
// Immer quadrieren
temp_base <= mod_multiply(temp_base, temp_base, modulus);
state <= MULTIPLY;
end
MULTIPLY: begin
// VERWUNDBAR: Bedingte Multiplikation basierend auf Exponent-Bit
// Dies erzeugt unterschiedliche Stromverbrauchsmuster
// die die geheimen Exponent-Bits enthüllen!
if (exponent_reg[bit_counter]) begin
// Stromspitze wenn Bit 1 ist
temp_result <= mod_multiply(temp_result, temp_base, modulus);
end
// Keine Operation wenn Bit 0 ist - erkennbar!
if (bit_counter == 0) begin
state <= DONE_STATE;
end else begin
bit_counter <= bit_counter - 1;
state <= SQUARE;
end
end
DONE_STATE: begin
result <= temp_result;
done <= 1'b1;
state <= IDLE;
end
endcase
end
end
// Angriff: Stromverbrauch überwachen
// - Hoher Strom während MULTIPLY wenn Exponent-Bit = 1
// - Niedriger Strom (keine Multiplikation) wenn Exponent-Bit = 0
// - Angreifer kann gesamten privaten Exponenten wiederherstellen
endmodule
// VERWUNDBAR: PIN-Vergleich mit Timing-Seitenkanal
module vulnerable_pin_check (
input wire clk,
input wire rst_n,
input wire [31:0] entered_pin,
input wire check_enable,
output reg pin_correct,
output reg check_done
);
// Geheime PIN in Register gespeichert
reg [31:0] stored_pin;
reg [2:0] byte_counter;
reg checking;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
pin_correct <= 1'b0;
check_done <= 1'b0;
byte_counter <= 3'b0;
checking <= 1'b0;
end else begin
if (check_enable && !checking) begin
checking <= 1'b1;
byte_counter <= 3'b0;
pin_correct <= 1'b1; // Als korrekt annehmen bis Nichtübereinstimmung
end
if (checking) begin
// VERWUNDBAR: Frühes Beenden bei Nichtübereinstimmung
// Unterschiedliche Zyklusanzahl enthüllt korrekte Ziffernanzahl
case (byte_counter)
3'd0: begin
if (entered_pin[7:0] != stored_pin[7:0]) begin
pin_correct <= 1'b0;
check_done <= 1'b1; // VERWUNDBAR: Frühes Beenden
checking <= 1'b0;
end else begin
byte_counter <= 3'd1;
end
end
3'd1: begin
if (entered_pin[15:8] != stored_pin[15:8]) begin
pin_correct <= 1'b0;
check_done <= 1'b1; // VERWUNDBAR: Frühes Beenden
checking <= 1'b0;
end else begin
byte_counter <= 3'd2;
end
end
// ... weiter für andere Bytes
3'd3: begin
if (entered_pin[31:24] != stored_pin[31:24]) begin
pin_correct <= 1'b0;
end
check_done <= 1'b1;
checking <= 1'b0;
end
endcase
end
end
end
// Angriff: Zyklen bis check_done zählen
// - 1 Zyklus: erste Ziffer falsch
// - 2 Zyklen: zweite Ziffer falsch (erste korrekt!)
// - usw.
// Angreifer kann eine Ziffer nach der anderen brute-forcen
endmodule
// VERWUNDBAR: AES mit Cache-Timing-Seitenkanal
#include <stdint.h>
// VERWUNDBAR: Tabellenbasiertes AES mit Cache-Timing-Leckage
static const uint8_t sbox[256] = {
// S-Box Lookup-Tabelle
0x63, 0x7c, 0x77, 0x7b, /* ... Rest der S-Box ... */
};
// VERWUNDBAR: Tabellen-Lookups haben variables Timing
// Cache-Hits sind schneller als Cache-Misses
// Angreifer kann bestimmen, auf welche Tabelleneinträge zugegriffen wurde
uint8_t vulnerable_sbox_lookup(uint8_t input) {
// VERWUNDBAR: Zugriffszeit hängt vom Cache-Zustand ab
return sbox[input];
}
void vulnerable_aes_encrypt(const uint8_t* plaintext,
const uint8_t* key,
uint8_t* ciphertext) {
uint8_t state[16];
// Klartext in State kopieren
for (int i = 0; i < 16; i++) {
state[i] = plaintext[i] ^ key[i];
}
// VERWUNDBAR: S-Box-Lookups lecken Cache-Timing
for (int round = 0; round < 10; round++) {
for (int i = 0; i < 16; i++) {
// Cache-Miss = langsam, Cache-Hit = schnell
// Angreifer kann state[i]-Werte bestimmen
state[i] = vulnerable_sbox_lookup(state[i]);
}
// MixColumns, ShiftRows, AddRoundKey...
}
// State in Ciphertext kopieren
for (int i = 0; i < 16; i++) {
ciphertext[i] = state[i];
}
}
Sichere Lösung
// SICHER: RSA mit Constant-Time-Operation (maskiert)
module secure_rsa_modexp (
input wire clk,
input wire rst_n,
input wire start,
input wire [2047:0] base,
input wire [2047:0] exponent,
input wire [2047:0] modulus,
output reg [2047:0] result,
output reg done
);
reg [2047:0] temp_result;
reg [2047:0] temp_base;
reg [2047:0] exponent_reg;
reg [11:0] bit_counter;
// SICHER: Masking-Register für Dummy-Operationen
reg [2047:0] mask_reg;
localparam IDLE = 2'b00;
localparam SQUARE = 2'b01;
localparam MULTIPLY = 2'b10;
localparam DONE_STATE = 2'b11;
reg [1:0] state;
// SICHER: Immer Multiplikation durchführen, Ergebnis verwerfen wenn nicht benötigt
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
done <= 1'b0;
result <= 2048'b0;
mask_reg <= 2048'b0;
end else begin
case (state)
IDLE: begin
if (start) begin
temp_result <= 2048'b1;
temp_base <= base;
exponent_reg <= exponent;
bit_counter <= 12'd2047;
state <= SQUARE;
end
end
SQUARE: begin
temp_base <= mod_multiply(temp_base, temp_base, modulus);
state <= MULTIPLY;
end
MULTIPLY: begin
// SICHER: Immer Multiplikation durchführen (konstanter Strom)
reg [2047:0] multiply_result;
multiply_result = mod_multiply(temp_result, temp_base, modulus);
// SICHER: Masking verwenden, um Ergebnis zu wählen
// Beide Zweige führen gleiche Operationen durch
if (exponent_reg[bit_counter]) begin
temp_result <= multiply_result; // Ergebnis behalten
mask_reg <= temp_result; // Dummy-Speicherung
end else begin
temp_result <= temp_result; // Alten Wert behalten
mask_reg <= multiply_result; // Zur Maske verwerfen
end
// SICHER: Gleiche Anzahl Operationen unabhängig vom Bit-Wert
// Stromverbrauch ist konstant
if (bit_counter == 0) begin
state <= DONE_STATE;
end else begin
bit_counter <= bit_counter - 1;
state <= SQUARE;
end
end
DONE_STATE: begin
result <= temp_result;
done <= 1'b1;
state <= IDLE;
end
endcase
end
end
endmodule
// SICHER: PIN-Vergleich mit Constant-Time-Operation
module secure_pin_check (
input wire clk,
input wire rst_n,
input wire [31:0] entered_pin,
input wire check_enable,
output reg pin_correct,
output reg check_done
);
reg [31:0] stored_pin;
reg [2:0] byte_counter;
reg checking;
reg [3:0] mismatch_flags; // SICHER: Alle Nichtübereinstimmungen sammeln
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
pin_correct <= 1'b0;
check_done <= 1'b0;
byte_counter <= 3'b0;
checking <= 1'b0;
mismatch_flags <= 4'b0;
end else begin
if (check_enable && !checking) begin
checking <= 1'b1;
byte_counter <= 3'b0;
mismatch_flags <= 4'b0; // SICHER: Flags zurücksetzen
end
if (checking) begin
// SICHER: ALLE Bytes prüfen, kein frühes Beenden
case (byte_counter)
3'd0: begin
// SICHER: Nur Nichtübereinstimmung aufzeichnen, nicht beenden
mismatch_flags[0] <= (entered_pin[7:0] != stored_pin[7:0]);
byte_counter <= 3'd1;
end
3'd1: begin
mismatch_flags[1] <= (entered_pin[15:8] != stored_pin[15:8]);
byte_counter <= 3'd2;
end
3'd2: begin
mismatch_flags[2] <= (entered_pin[23:16] != stored_pin[23:16]);
byte_counter <= 3'd3;
end
3'd3: begin
mismatch_flags[3] <= (entered_pin[31:24] != stored_pin[31:24]);
byte_counter <= 3'd4;
end
3'd4: begin
// SICHER: Ergebnis erst nach Prüfung ALLER Bytes melden
// Gleiches Timing unabhängig davon, welches Byte falsch ist
pin_correct <= (mismatch_flags == 4'b0);
check_done <= 1'b1;
checking <= 1'b0;
end
endcase
end
end
end
// SICHER: Dauert immer genau 5 Zyklen
// Angreifer kann nicht bestimmen, welche Ziffer falsch ist
endmodule
// SICHER: AES mit Seitenkanal-Gegenmaßnahmen
#include <stdint.h>
#include <string.h>
// SICHER: Bit-Sliced S-Box-Implementierung
// Keine Tabellen-Lookups, konstante Zeit
static void secure_sbox_bitslice(uint8_t* state) {
// SICHER: S-Box nur mit booleschen Operationen berechnen
// Keine Speicherzugriffe, die durch Cache lecken könnten
// Implementierung verwendet algebraische Zerlegung der AES-S-Box
for (int i = 0; i < 16; i++) {
uint8_t x = state[i];
// SICHER: Constant-Time S-Box-Berechnung
// Verwendet GF(2^8)-Inversion über boolesche Operationen
uint8_t result = compute_sbox_algebraic(x);
state[i] = result;
}
}
// SICHER: Constant-Time-Vergleich
int secure_memcmp(const void* a, const void* b, size_t len) {
const volatile uint8_t* aa = a;
const volatile uint8_t* bb = b;
uint8_t diff = 0;
// SICHER: Immer alle Bytes vergleichen
// Kein frühes Beenden
for (size_t i = 0; i < len; i++) {
diff |= aa[i] ^ bb[i];
}
// SICHER: Rückgabewert enthüllt nicht Position des Unterschieds
return diff;
}
// SICHER: AES mit Masking
void secure_aes_encrypt(const uint8_t* plaintext,
const uint8_t* key,
uint8_t* ciphertext) {
uint8_t state[16];
uint8_t mask[16];
// SICHER: Zufällige Maske generieren
get_random_bytes(mask, 16);
// SICHER: Maske auf Klartext anwenden
for (int i = 0; i < 16; i++) {
state[i] = plaintext[i] ^ key[i] ^ mask[i];
}
// SICHER: Maskierte Berechnung
// Operationen auf maskierten Daten durchgeführt
// Maske verhindert Korrelation zwischen Strom und tatsächlichen Werten
for (int round = 0; round < 10; round++) {
secure_sbox_bitslice(state); // SICHER: Keine Tabellen-Lookups
// MixColumns, ShiftRows, AddRoundKey auf maskiertem State
}
// SICHER: Maske am Ende entfernen
for (int i = 0; i < 16; i++) {
ciphertext[i] = state[i] ^ final_mask[i];
}
// SICHER: Sensible Daten aus Speicher löschen
secure_memzero(mask, 16);
secure_memzero(state, 16);
}
// SICHER: Sicheres Speicherlöschen (wird nicht wegoptimiert)
void secure_memzero(void* ptr, size_t len) {
volatile uint8_t* p = ptr;
while (len--) {
*p++ = 0;
}
}
CVE-Beispiele
- CVE-2022-35888: Prozessor-Strom-Seitenkanale lecken Geheimnisse.
- CVE-2021-3011: Google Titan Security Key anfällig für elektromagnetischen Seitenkanal.
- CVE-2019-14353: Krypto-Hardware-Wallet USB-Stromverbrauch enthüllt PIN/Passwort.
- CVE-2013-4576: Akustische Kryptoanalyse extrahiert RSA-Schlüssel.
- CVE-2020-28368: Guest-AES-Schlüssel-Wiederherstellung über Stromüberwachungs-Interface.
Verwandte CWEs
- CWE-203: Observable Discrepancy (übergeordnet)
- CWE-1255: Comparison Logic is Vulnerable to Power Side-Channel Attacks (untergeordnet)
- CWE-1388: Physical Access Issues and Concerns (Kategorie)
- CWE-208: Observable Timing Discrepancy (verwandt)
Referenzen
- MITRE Corporation. "CWE-1300: Improper Protection of Physical Side Channels." https://cwe.mitre.org/data/definitions/1300.html
- ISO 17825: Information technology — Security techniques — Testing methods for the mitigation of non-invasive attack classes against cryptographic modules
- Kocher, P., et al. "Differential Power Analysis"