Vergleichslogik anfällig für Power-Seitenkanal-Angriffe

Beschreibung

Vergleichslogik anfällig für Power-Seitenkanal-Angriffe tritt auf, wenn der Stromverbrauch eines Geräts während der Auswertung von Sicherheitstoken in Echtzeit überwacht werden kann, um Referenz-Token-Werte zu bestimmen. Echtzeit-Stromüberwachung von Geräten ermöglicht es Angreifern, Schwankungen im Energieverbrauch während der Token-Validierung zu beobachten. Wenn Vergleichsalgorithmen nicht ausreichend robust sind und Wiederholungsmechanismen unbegrenzt sind, werden Stromunterschiede zwischen korrekten und falschen Eingaben ausnutzbar, wodurch Angreifer Referenzwerte inkrementell bestimmen können.

Risiko

Power-Seitenkanal-Schwachstellen haben schwerwiegende Sicherheitsauswirkungen. PINs und Passwörter können durch Stromanalyse wiederhergestellt werden. Kryptografische Schlüssel können extrahiert werden. Authentifizierungsumgehungen werden möglich. Secure-Boot-Schlüssel können kompromittiert werden. Challenge-Response-Systeme können besiegt werden. Token-Werte können ohne Brute Force bestimmt werden. Single Power Analysis (SPA) offenbart Operationen direkt. Differential Power Analysis (DPA) extrahiert Schlüssel statistisch.

Lösung

Dekrementieren Sie den Wiederholungszähler vor der Token-Validierung; entwerfen Sie Prüfungen mit einheitlichem Stromverbrauch. Parallelisieren Sie das Verschieben geheimer Daten unter Verwendung breiterer Busse. Injizieren Sie Zufallsdaten in Krypto-Operationen als Rauschen. Implementieren Sie Hardware-Filter für Stromleitungen. Vermeiden Sie einzelne Geheimnisse über längere Zeiträume durch Schlüsselrotation. Verwenden Sie maskierte Implementierungen für kryptografische Operationen. Implementieren Sie Konstant-Leistung-Vergleichsschaltungen. Fügen Sie Rauschgeneratoren zur Stromversorgung hinzu.

Häufige Auswirkungen

AuswirkungDetails
VertraulichkeitUmfang: Vertraulichkeit

Unbefugter Zugriff - Stromanalyse offenbart geheime Werte wie PINs, Passwörter und kryptografische Schlüssel.
IntegritätUmfang: Integrität

Schutzmechanismus umgehen - Authentifizierungs- und Verifizierungsmechanismen können besiegt werden.
ZugriffskontrolleUmfang: Zugriffskontrolle

Vollständige Systemkompromittierung - Wiederherstellung von Authentifizierungsgeheimnissen gewährt vollen Zugriff.

Beispielcode und Lösung

Verwundbarer Code

// VERWUNDBAR: Strom-leakender Passwortvergleich

#include <stdint.h>

#define NUM_PW_DIGITS 4

volatile uint8_t stored_password[NUM_PW_DIGITS] = {1, 2, 3, 4};
volatile int password_tries = 3;

uint8_t GetPasswordByte(void);

bool vulnerable_password_check(void) {
    uint8_t password_ok = 0;

    // VERWUNDBAR: Unterschiedliche Operationen basierend auf Übereinstimmung/Nichtübereinstimmung
    for (int i = 0; i < NUM_PW_DIGITS; i++) {
        if (GetPasswordByte() == stored_password[i]) {
            password_ok |= 1;  // Unterschiedliche Strom-Signatur hier
        }
        else {
            password_ok |= 0;  // Als hier (ODER mit 0 ist anders)
        }
    }

    // VERWUNDBAR: Wiederholungszähler NACH Prüfung dekrementiert
    // Angreifer kann Stromzyklus durchführen, um Dekrement zu vermeiden
    if (password_ok) {
        return true;
    }
    else {
        password_tries--;
        return false;
    }
}

// VERWUNDBAR: Serielles Schieben leakt Bits
void vulnerable_serial_shift(uint8_t* secret, int len) {
    // VERWUNDBAR: Serielles Schieben offenbart ein Bit zur Zeit
    for (int i = 0; i < len * 8; i++) {
        uint8_t bit = (secret[i/8] >> (i%8)) & 1;
        // Jeder Bit-Shift hat eindeutige Strom-Signatur
        shift_out_bit(bit);
    }
}

// VERWUNDBAR: Bedingte Operationen basierend auf Geheimnis
void vulnerable_crypto_operation(uint8_t* key, uint8_t* data, int len) {
    for (int i = 0; i < len; i++) {
        if (key[i] & 0x80) {
            // VERWUNDBAR: Operation nur wenn Schlüssel-Bit 1 ist
            data[i] = complex_transform(data[i]);
        }
        // Stromunterschiede offenbaren Schlüssel-Bits
    }
}
// VERWUNDBAR: Strom-leakender Komparator

module vulnerable_pin_check (
    input wire clk,
    input wire reset_n,
    input wire [3:0] digit_in,
    input wire digit_valid,
    output reg access_granted,
    output reg check_complete
);

    // Gespeicherte PIN
    reg [3:0] stored_pin [0:3];
    initial begin
        stored_pin[0] = 4'd1;
        stored_pin[1] = 4'd2;
        stored_pin[2] = 4'd3;
        stored_pin[3] = 4'd4;
    end

    reg [1:0] digit_index;
    reg match_status;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            digit_index <= 2'b0;
            match_status <= 1'b1;
            access_granted <= 1'b0;
            check_complete <= 1'b0;
        end
        else if (digit_valid) begin
            // VERWUNDBAR: Bedingte Operation leakt Strom
            if (digit_in == stored_pin[digit_index]) begin
                // Übereinstimmung: bestimmte Strom-Signatur
                match_status <= match_status & 1'b1;
            end
            else begin
                // Nichtübereinstimmung: andere Strom-Signatur
                match_status <= 1'b0;
            end

            if (digit_index == 2'd3) begin
                access_granted <= match_status;
                check_complete <= 1'b1;
            end
            else begin
                digit_index <= digit_index + 1;
            end
        end
    end

endmodule

// VERWUNDBAR: Seriell-Ein/Seriell-Aus Schieberegister
module vulnerable_siso_shift (
    input wire clk,
    input wire reset_n,
    input wire serial_in,
    input wire shift_enable,
    output wire serial_out
);

    reg [7:0] shift_reg;

    // VERWUNDBAR: Einzelne Bit-Verschiebungen leaken Daten über Strom
    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            shift_reg <= 8'h0;
        end
        else if (shift_enable) begin
            // Jede Verschiebung hat Strom-Signatur basierend auf Bit-Werten
            shift_reg <= {shift_reg[6:0], serial_in};
        end
    end

    assign serial_out = shift_reg[7];

endmodule

Sichere Lösung

// SICHER: Strom-balancierter Passwortvergleich

#include <stdint.h>

#define NUM_PW_DIGITS 4

volatile uint8_t stored_password[NUM_PW_DIGITS] = {1, 2, 3, 4};
volatile int password_tries = 3;

uint8_t GetPasswordByte(void);

bool secure_password_check(void) {
    uint8_t password_ok = 0;

    // SICHER: Wiederholungszähler VOR Prüfung dekrementieren
    // Verhindert Stromzyklus-Angriff zur Vermeidung des Dekrements
    password_tries--;

    // SICHER: Einheitliche Strom-Operationen unabhängig von Übereinstimmung
    for (int i = 0; i < NUM_PW_DIGITS; i++) {
        uint8_t input = GetPasswordByte();
        uint8_t stored = stored_password[i];

        // SICHER: Gleiche Operation für Übereinstimmung und Nichtübereinstimmung
        // Verwendet konstanten Strom unabhängig vom Ergebnis
        if (input == stored) {
            password_ok |= 0x10;  // Gleiches Hamming-Gewicht
        }
        else {
            password_ok |= 0x01;  // Gleiches Hamming-Gewicht
        }
    }

    // Auf alle Übereinstimmungen prüfen (password_ok sollte 0x40 für 4 Übereinstimmungen sein)
    return (password_ok == 0x40);
}

// SICHER: Maskierter Vergleich mit Rauschen
bool secure_masked_compare(const uint8_t* input, const uint8_t* stored, int len) {
    // SICHER: Zufällige Maske generieren
    uint8_t mask = get_random_byte();

    uint8_t result = 0;
    uint8_t dummy = 0;

    for (int i = 0; i < len; i++) {
        // SICHER: Maske anwenden, um Strom-Signatur zu verschleiern
        uint8_t masked_input = input[i] ^ mask;
        uint8_t masked_stored = stored[i] ^ mask;

        // Vergleich auf maskierten Werten
        uint8_t diff = masked_input ^ masked_stored;

        // SICHER: Dummy-Operationen zum Strom-Ausgleich
        dummy ^= mask;
        dummy ^= (diff | ~diff);  // Immer 0xFF

        result |= diff;
    }

    // Dummy verwenden um Optimierung zu verhindern
    volatile uint8_t anti_opt = dummy;
    (void)anti_opt;

    return result == 0;
}

// SICHER: Paralleles Schieben mit breiterem Bus
void secure_parallel_shift(uint8_t* data, int len) {
    // SICHER: Mehrere Bits gleichzeitig schieben
    // Strom-Signatur vermischt mehrere Bits
    for (int i = 0; i < len; i += 4) {
        // 32-Bit paralleles Schieben
        uint32_t word = *(uint32_t*)&data[i];
        shift_out_word(word);  // Alle 32 Bits gleichzeitig
    }
}

// SICHER: Konstant-Leistung Krypto-Operation
void secure_crypto_operation(const uint8_t* key, uint8_t* data, int len) {
    for (int i = 0; i < len; i++) {
        // SICHER: Beide Operationen immer ausführen
        uint8_t transform_result = complex_transform(data[i]);
        uint8_t no_transform = data[i];

        // SICHER: Konstant-Zeit-Auswahl basierend auf Schlüssel verwenden
        // Beide Operationen werden immer ausgeführt
        uint8_t key_bit = (key[i] >> 7) & 1;
        data[i] = (transform_result & (0 - key_bit)) |
                  (no_transform & (0 - (1 - key_bit)));
    }
}
// SICHER: Strom-balancierter PIN-Komparator

module secure_pin_check (
    input wire clk,
    input wire reset_n,
    input wire [3:0] digit_in,
    input wire digit_valid,
    input wire [7:0] random_mask,  // Externer Zufall für Maskierung
    output reg access_granted,
    output reg check_complete
);

    // Gespeicherte PIN (wäre in sicherem Speicher)
    reg [3:0] stored_pin [0:3];
    initial begin
        stored_pin[0] = 4'd1;
        stored_pin[1] = 4'd2;
        stored_pin[2] = 4'd3;
        stored_pin[3] = 4'd4;
    end

    reg [1:0] digit_index;
    reg [3:0] match_accumulator;
    reg [3:0] dummy_accumulator;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            digit_index <= 2'b0;
            match_accumulator <= 4'hF;
            dummy_accumulator <= 4'h0;
            access_granted <= 1'b0;
            check_complete <= 1'b0;
        end
        else if (digit_valid) begin
            // SICHER: Maskierter Vergleich
            wire [3:0] masked_input = digit_in ^ random_mask[3:0];
            wire [3:0] masked_stored = stored_pin[digit_index] ^ random_mask[3:0];

            // SICHER: Beide Operationen werden immer ausgeführt
            wire match = (masked_input == masked_stored);
            wire mismatch = (masked_input != masked_stored);

            // SICHER: Einheitliche Operationen unabhängig vom Ergebnis
            // Gleiche Anzahl von Bit-Umschaltungen für Übereinstimmung und Nichtübereinstimmung
            if (match) begin
                match_accumulator <= match_accumulator & 4'hF;
                dummy_accumulator <= dummy_accumulator | 4'h1;
            end
            else begin
                match_accumulator <= match_accumulator & 4'hE;
                dummy_accumulator <= dummy_accumulator | 4'h0;
            end

            if (digit_index == 2'd3) begin
                // SICHER: Einzelner abschließender Vergleich
                access_granted <= (match_accumulator == 4'hF);
                check_complete <= 1'b1;
            end
            else begin
                digit_index <= digit_index + 1;
            end
        end
    end

endmodule

// SICHER: Parallel-Ein/Parallel-Aus Schieberegister
module secure_pipo_shift (
    input wire clk,
    input wire reset_n,
    input wire [31:0] parallel_in,  // 32-Bit paralleles Laden
    input wire load_enable,
    input wire shift_enable,
    output wire [31:0] parallel_out
);

    reg [31:0] shift_reg;

    // SICHER: Parallele Operationen vermischen einzelne Bits
    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            shift_reg <= 32'h0;
        end
        else if (load_enable) begin
            // Alle 32 Bits gleichzeitig laden
            shift_reg <= parallel_in;
        end
        else if (shift_enable) begin
            // Alle Bits zusammen schieben - Strom-Signatur
            // vermischt alle 32 Bits
            shift_reg <= {shift_reg[30:0], 1'b0};
        end
    end

    assign parallel_out = shift_reg;

endmodule

// SICHER: Dual-Rail-Logik für konstanten Strom
module secure_dual_rail_compare (
    input wire clk,
    input wire reset_n,
    input wire a_true, a_false,  // Dual-Rail-Eingang A
    input wire b_true, b_false,  // Dual-Rail-Eingang B
    output reg match_true, match_false
);

    // SICHER: Dual-Rail-Kodierung gewährleistet konstanten Strom
    // Jeder Vergleich beinhaltet gleiche Anzahl von Übergängen

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            match_true <= 1'b0;
            match_false <= 1'b1;
        end
        else begin
            // SICHER: Beide Rails werden immer berechnet
            match_true <= (a_true & b_true) | (a_false & b_false);
            match_false <= (a_true & b_false) | (a_false & b_true);
            // Gesamtübergänge konstant unabhängig von Werten
        end
    end

endmodule

CVE-Beispiele

Power-Seitenkanal-Schwachstellen wurden bei Smartcards, sicheren Mikrocontrollern und kryptografischen Implementierungen demonstriert, bei denen Stromanalyse PINs, Passwörter und kryptografische Schlüssel offenbarte.


Verwandte CWEs

  • CWE-1300: Unsachgemäßer Schutz physischer Seitenkanale (übergeordnet)
  • CWE-1259: Unsachgemäße Beschränkung der Sicherheitstoken-Zuweisung (verwandt)
  • CWE-1254: Fehlerhafte Granularität der Vergleichslogik (verwandt)

Referenzen

  1. MITRE Corporation. "CWE-1255: Comparison Logic is Vulnerable to Power Side-Channel Attacks." https://cwe.mitre.org/data/definitions/1255.html
  2. Kocher et al. "Differential Power Analysis"
  3. Mangard, Oswald, Popp. "Power Analysis Attacks: Revealing the Secrets of Smart Cards"