Kryptographische Operationen werden ausgeführt, bevor unterstützende Einheiten bereit sind

Beschreibung

Kryptographische Operationen werden ausgeführt, bevor unterstützende Einheiten bereit sind, tritt auf, wenn kryptographische Operationen durchgeführt werden, ohne sicherzustellen, dass die unterstützenden Eingaben bereit sind, gültige Daten zu liefern, was das kryptographische Ergebnis kompromittieren kann. Kryptographische Hardware-Einheiten hängen von anderen Hardware-Komponenten ab, um sicher zu funktionieren. Beispiele umfassen Zufallszahlengeneratoren (RNG), die Entropie liefern, und Fuse-Einheiten, die Verschlüsselungsschlüssel bereitstellen. Die Ausführung von Krypto-Einheiten, bevor diese Abhängigkeiten bereit sind, produziert unsichere Ergebnisse, wie die Verwendung eines vorhersagbaren Seeds anstelle echter Zufälligkeit.

Risiko

Vorzeitige kryptographische Operationen haben schwerwiegende Sicherheitsauswirkungen. Zufallszahlen können vorhersagbar sein. Schlüssel sind möglicherweise nicht ordnungsgemäß geladen. Verschlüsselung kann schwache Werte verwenden. Signaturen können fälschbar sein. Authentifizierung kann umgangen werden. Kryptographische Sicherheit wird untergraben. Seitenkanalangriffe werden einfacher. Das gesamte Sicherheitsmodell kann zusammenbrechen.

Lösung

Verwenden Sie Best Practices beim Design kryptographischer Systeme. Verifizieren Sie kontinuierlich, dass kryptographische Eingaben gültige Informationen liefern. Implementieren Sie Ready-Signale und Handshaking zwischen Komponenten. Verifizieren Sie die RNG-Gesundheit vor der Verwendung. Stellen Sie sicher, dass Schlüsselmaterial vollständig geladen ist, bevor Operationen beginnen. Verwenden Sie Statusregister, um die Bereitschaft der Einheiten zu bestätigen. Implementieren Sie Fehlerbehandlung für nicht bereite Zustände.

Häufige Auswirkungen

AuswirkungDetails
ZugriffskontrolleBereich: Zugriffskontrolle

Schutzmechanismus umgehen - Schwache Kryptographie ermöglicht Bypass.
VertraulichkeitBereich: Vertraulichkeit

Speicher lesen - Vorhersagbare Kryptographie kann gebrochen werden.
IntegritätBereich: Integrität

Speicher modifizieren - Signaturen können fälschbar sein.
AuthentifizierungBereich: Authentifizierung

Authentifizierung umgehen - Schwache Schlüssel ermöglichen Identitätsfälschung.

Beispielcode und Lösung

Verwundbarer Code

// VERWUNDBAR: Krypto-Operationen bevor RNG bereit ist

module vulnerable_crypto_system (
    input wire clk,
    input wire reset_n,
    input wire start_crypto,
    input wire [127:0] plaintext,
    output reg [127:0] ciphertext,
    output reg crypto_done
);

    // RNG-Signale
    wire [127:0] rng_output;
    wire rng_ready;

    // Schlüsselsignale
    wire [127:0] key_from_fuse;
    wire key_ready;

    // RNG-Instanz
    rng_unit rng (
        .clk(clk),
        .reset_n(reset_n),
        .random_data(rng_output),
        .ready(rng_ready)
    );

    // Fuse-Einheit-Instanz
    fuse_unit fuses (
        .clk(clk),
        .reset_n(reset_n),
        .key_out(key_from_fuse),
        .ready(key_ready)
    );

    // VERWUNDBAR: Krypto-Operation ohne Bereitschaftsprüfung
    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            ciphertext <= 128'h0;
            crypto_done <= 1'b0;
        end
        else if (start_crypto) begin
            // VERWUNDBAR: Verwendet RNG-Ausgabe, auch wenn RNG nicht bereit ist
            // rng_output kann nur Nullen oder vorhersagbar sein

            // VERWUNDBAR: Verwendet Schlüssel, auch wenn Fuse-Einheit nicht bereit ist
            // key_from_fuse kann nur Nullen sein

            ciphertext <= aes_encrypt(plaintext, key_from_fuse) ^ rng_output;
            crypto_done <= 1'b1;

            // Problem: Wenn RNG nicht bereit, ist IV/Nonce vorhersagbar
            // Problem: Wenn Fuses nicht bereit, ist Schlüssel null/schwach
        end
    end

endmodule

// VERWUNDBAR: RNG mit Fallback auf schwachen Seed
module vulnerable_rng (
    input wire clk,
    input wire reset_n,
    input wire request_random,
    output reg [127:0] random_data,
    output reg data_valid
);

    wire entropy_ready;
    wire [127:0] entropy_bits;

    entropy_source entropy (
        .clk(clk),
        .reset_n(reset_n),
        .entropy_out(entropy_bits),
        .ready(entropy_ready)
    );

    reg self_test_passed;

    // VERWUNDBAR: Fallback auf schwachen Seed, wenn Entropie nicht bereit
    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            random_data <= 128'h0;
            data_valid <= 1'b0;
            self_test_passed <= 1'b0;
        end
        else if (request_random) begin
            if (entropy_ready && self_test_passed) begin
                // Gut: Echte Entropie verwenden
                random_data <= entropy_bits;
                data_valid <= 1'b1;
            end
            else begin
                // VERWUNDBAR: Fallback auf hartcodierten Seed
                random_data <= 128'hDEADBEEF_CAFEBABE_12345678_87654321;
                data_valid <= 1'b1;  // Behauptet gültig, ist aber vorhersagbar!
            end
        end
    end

endmodule
// VERWUNDBAR: Software-Krypto ohne Prüfung der RNG-Bereitschaft

#include <stdint.h>

#define RNG_STATUS_REG 0x40001000
#define RNG_DATA_REG   0x40001004
#define RNG_READY_BIT  0x01

// VERWUNDBAR: IV generieren ohne RNG-Prüfung
void vulnerable_generate_iv(uint8_t* iv, size_t len) {
    volatile uint32_t* rng_data = (volatile uint32_t*)RNG_DATA_REG;

    // VERWUNDBAR: Keine Prüfung, ob RNG bereit ist
    // RNG sammelt möglicherweise noch Entropie nach Reset

    for (size_t i = 0; i < len; i += 4) {
        // RNG könnte nur Nullen zurückgeben, wenn nicht bereit!
        uint32_t random = *rng_data;
        memcpy(iv + i, &random, 4);
    }

    // Wenn direkt nach Reset aufgerufen, kann IV vorhersagbar sein
    // Verschlüsselung kann gebrochen werden, wenn Angreifer IV kennt
}

// VERWUNDBAR: Schlüssel verwenden, bevor Fuse-Einheit bereit ist
void vulnerable_load_key(void) {
    volatile uint32_t* fuse_key = (volatile uint32_t*)FUSE_KEY_BASE;

    // VERWUNDBAR: Keine Prüfung, ob Fuse-Einheit Initialisierung abgeschlossen hat
    // Schlüssel kann während Power-Up-Sequenz nur Nullen sein

    uint8_t key[16];
    for (int i = 0; i < 4; i++) {
        uint32_t word = fuse_key[i];  // Kann null sein!
        memcpy(key + i*4, &word, 4);
    }

    // Wenn Schlüssel nur Nullen ist, ist Verschlüsselung nutzlos
    aes_set_key(key);
}

// VERWUNDBAR: Verschlüsseln mit potenziell schwachen Parametern
void vulnerable_encrypt(const uint8_t* plaintext, size_t len,
                        uint8_t* ciphertext) {
    uint8_t iv[16];
    vulnerable_generate_iv(iv, 16);  // Kann vorhersagbar sein!
    vulnerable_load_key();           // Kann null sein!

    // Verschlüsselung mit schwachem IV und Schlüssel ist brechbar
    aes_cbc_encrypt(plaintext, len, iv, ciphertext);
}

Sichere Lösung

// SICHER: Krypto-Operationen erst nach Bereitschaft der unterstützenden Einheiten

module secure_crypto_system (
    input wire clk,
    input wire reset_n,
    input wire start_crypto,
    input wire [127:0] plaintext,
    output reg [127:0] ciphertext,
    output reg crypto_done,
    output reg crypto_error
);

    // RNG-Signale
    wire [127:0] rng_output;
    wire rng_ready;
    wire rng_health_ok;

    // Schlüsselsignale
    wire [127:0] key_from_fuse;
    wire key_ready;
    wire key_valid;

    // RNG-Instanz mit Gesundheitsprüfung
    secure_rng_unit rng (
        .clk(clk),
        .reset_n(reset_n),
        .random_data(rng_output),
        .ready(rng_ready),
        .health_ok(rng_health_ok)
    );

    // Fuse-Einheit-Instanz
    secure_fuse_unit fuses (
        .clk(clk),
        .reset_n(reset_n),
        .key_out(key_from_fuse),
        .ready(key_ready),
        .valid(key_valid)
    );

    // Zustandsmaschine für Krypto-Operation
    reg [2:0] state;
    parameter IDLE = 3'd0;
    parameter WAIT_RNG = 3'd1;
    parameter WAIT_KEY = 3'd2;
    parameter ENCRYPT = 3'd3;
    parameter DONE = 3'd4;
    parameter ERROR = 3'd5;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            ciphertext <= 128'h0;
            crypto_done <= 1'b0;
            crypto_error <= 1'b0;
            state <= IDLE;
        end
        else begin
            case (state)
                IDLE: begin
                    crypto_done <= 1'b0;
                    crypto_error <= 1'b0;

                    if (start_crypto) begin
                        state <= WAIT_RNG;
                    end
                end

                WAIT_RNG: begin
                    // SICHER: Warten bis RNG bereit und gesund ist
                    if (rng_ready && rng_health_ok) begin
                        state <= WAIT_KEY;
                    end
                    else if (!rng_health_ok) begin
                        // RNG-Gesundheitsprüfung fehlgeschlagen
                        state <= ERROR;
                    end
                    // Timeout-Behandlung könnte hier hinzugefügt werden
                end

                WAIT_KEY: begin
                    // SICHER: Warten bis Schlüssel bereit und gültig ist
                    if (key_ready && key_valid) begin
                        state <= ENCRYPT;
                    end
                    else if (key_ready && !key_valid) begin
                        // Schlüsselvalidierung fehlgeschlagen
                        state <= ERROR;
                    end
                end

                ENCRYPT: begin
                    // SICHER: Alle Abhängigkeiten erfüllt, sicher zu verschlüsseln
                    ciphertext <= aes_encrypt(plaintext, key_from_fuse) ^ rng_output;
                    state <= DONE;
                end

                DONE: begin
                    crypto_done <= 1'b1;
                    state <= IDLE;
                end

                ERROR: begin
                    crypto_error <= 1'b1;
                    state <= IDLE;
                end
            endcase
        end
    end

endmodule

// SICHER: RNG mit ordnungsgemäßer Gesundheitsprüfung
module secure_rng_unit (
    input wire clk,
    input wire reset_n,
    input wire request_random,
    output reg [127:0] random_data,
    output reg ready,
    output reg health_ok
);

    wire entropy_ready;
    wire [127:0] entropy_bits;

    entropy_source entropy (
        .clk(clk),
        .reset_n(reset_n),
        .entropy_out(entropy_bits),
        .ready(entropy_ready)
    );

    // SICHER: Gesundheitsprüfungszustand
    reg [2:0] health_state;
    reg [7:0] startup_count;
    reg self_test_passed;

    parameter HEALTH_INIT = 3'd0;
    parameter HEALTH_STARTUP = 3'd1;
    parameter HEALTH_CONTINUOUS = 3'd2;
    parameter HEALTH_FAILED = 3'd3;

    // Vorherige Ausgabe für kontinuierliche Tests
    reg [127:0] previous_output;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            random_data <= 128'h0;
            ready <= 1'b0;
            health_ok <= 1'b0;
            health_state <= HEALTH_INIT;
            startup_count <= 8'h0;
            self_test_passed <= 1'b0;
        end
        else begin
            case (health_state)
                HEALTH_INIT: begin
                    // SICHER: Warten bis Entropiequelle stabilisiert ist
                    ready <= 1'b0;
                    health_ok <= 1'b0;
                    if (entropy_ready) begin
                        health_state <= HEALTH_STARTUP;
                        startup_count <= 8'h0;
                    end
                end

                HEALTH_STARTUP: begin
                    // SICHER: Start-Gesundheitstests ausführen
                    if (entropy_ready) begin
                        // Proben sammeln und statistische Tests ausführen
                        if (run_startup_tests(entropy_bits)) begin
                            startup_count <= startup_count + 1;
                        end
                        else begin
                            health_state <= HEALTH_FAILED;
                        end

                        // Mehrere bestandene Proben erforderlich
                        if (startup_count >= 8'd64) begin
                            self_test_passed <= 1'b1;
                            health_state <= HEALTH_CONTINUOUS;
                            ready <= 1'b1;
                            health_ok <= 1'b1;
                        end
                    end
                end

                HEALTH_CONTINUOUS: begin
                    // SICHER: Kontinuierliche Gesundheitsüberwachung
                    ready <= 1'b1;
                    health_ok <= 1'b1;

                    if (request_random && entropy_ready) begin
                        // SICHER: Auf festgefahrene Ausgabe prüfen
                        if (entropy_bits == previous_output) begin
                            // Wiederholung erkannt - möglicher Fehler
                            health_state <= HEALTH_FAILED;
                        end
                        else begin
                            random_data <= entropy_bits;
                            previous_output <= entropy_bits;
                        end
                    end
                end

                HEALTH_FAILED: begin
                    // SICHER: RNG fehlgeschlagen - keine Ausgabe liefern
                    ready <= 1'b0;
                    health_ok <= 1'b0;
                    random_data <= 128'h0;
                    // Reset erforderlich zur Wiederherstellung
                end
            endcase
        end
    end

endmodule
// SICHER: Software-Krypto mit ordnungsgemäßen Bereitschaftsprüfungen

#include <stdint.h>
#include <stdbool.h>

#define RNG_STATUS_REG 0x40001000
#define RNG_DATA_REG   0x40001004
#define RNG_READY_BIT  0x01
#define RNG_HEALTH_BIT 0x02

// SICHER: RNG-Status vor Verwendung prüfen
static bool is_rng_ready(void) {
    volatile uint32_t* rng_status = (volatile uint32_t*)RNG_STATUS_REG;
    uint32_t status = *rng_status;

    // Sowohl Ready- als auch Health-Bits prüfen
    return (status & RNG_READY_BIT) && (status & RNG_HEALTH_BIT);
}

// SICHER: Auf RNG mit Timeout warten
static bool wait_for_rng(uint32_t timeout_ms) {
    uint32_t start = get_tick_count();

    while (!is_rng_ready()) {
        if (get_tick_count() - start > timeout_ms) {
            log_error("RNG-Timeout - nicht bereit");
            return false;
        }
        // Kleine Verzögerung, um Register nicht zu überhämmern
        delay_us(100);
    }

    return true;
}

// SICHER: IV nur generieren, wenn RNG bereit ist
bool secure_generate_iv(uint8_t* iv, size_t len) {
    volatile uint32_t* rng_data = (volatile uint32_t*)RNG_DATA_REG;

    // SICHER: Warten bis RNG bereit ist
    if (!wait_for_rng(1000)) {
        return false;
    }

    for (size_t i = 0; i < len; i += 4) {
        // SICHER: Verifizieren, dass RNG vor jedem Lesevorgang noch gesund ist
        if (!is_rng_ready()) {
            log_error("RNG wurde während IV-Generierung ungesund");
            return false;
        }

        uint32_t random = *rng_data;

        // SICHER: Plausibilitätsprüfung - offensichtlich schlechte Werte ablehnen
        static uint32_t previous = 0;
        if (random == previous || random == 0 || random == 0xFFFFFFFF) {
            log_error("RNG-Ausgabe verdächtig");
            return false;
        }
        previous = random;

        memcpy(iv + i, &random, 4);
    }

    return true;
}

// SICHER: Schlüssel nur laden, wenn Fuse-Einheit bereit ist
bool secure_load_key(uint8_t* key_out) {
    volatile uint32_t* fuse_status = (volatile uint32_t*)FUSE_STATUS_REG;
    volatile uint32_t* fuse_key = (volatile uint32_t*)FUSE_KEY_BASE;

    // SICHER: Warten bis Fuse-Einheit Initialisierung abgeschlossen hat
    uint32_t start = get_tick_count();
    while (!(*fuse_status & FUSE_READY_BIT)) {
        if (get_tick_count() - start > 1000) {
            log_error("Fuse-Einheit nicht bereit");
            return false;
        }
        delay_us(100);
    }

    // SICHER: Schlüsselgültigkeit prüfen
    if (!(*fuse_status & FUSE_KEY_VALID_BIT)) {
        log_error("Fuse-Schlüssel nicht gültig");
        return false;
    }

    // Schlüssel lesen
    for (int i = 0; i < 4; i++) {
        uint32_t word = fuse_key[i];
        memcpy(key_out + i*4, &word, 4);
    }

    // SICHER: Verifizieren, dass Schlüssel nicht nur Nullen oder Einsen ist
    if (is_key_weak(key_out, 16)) {
        log_error("Schwacher Schlüssel aus Fuses erkannt");
        secure_memzero(key_out, 16);
        return false;
    }

    return true;
}

// SICHER: Nur mit gültigen Parametern verschlüsseln
bool secure_encrypt(const uint8_t* plaintext, size_t len,
                    uint8_t* ciphertext) {
    uint8_t iv[16];
    uint8_t key[16];

    // SICHER: IV mit Verifizierung generieren
    if (!secure_generate_iv(iv, 16)) {
        log_error("IV-Generierung fehlgeschlagen");
        return false;
    }

    // SICHER: Schlüssel mit Verifizierung laden
    if (!secure_load_key(key)) {
        log_error("Schlüssel-Laden fehlgeschlagen");
        secure_memzero(iv, 16);
        return false;
    }

    // Jetzt sicher zu verschlüsseln
    bool result = aes_cbc_encrypt(plaintext, len, key, iv, ciphertext);

    // Sensible Daten löschen
    secure_memzero(key, 16);
    secure_memzero(iv, 16);

    return result;
}

CVE-Beispiele

Kryptographische Timing-Schwachstellen wurden in verschiedenen Systemen gefunden, bei denen Krypto-Operationen fortführen, bevor die RNG-Initialisierung abgeschlossen war, was zu vorhersagbaren Schlüsseln oder IVs führte, die ausgenutzt werden könnten.


Verwandte CWEs

  • CWE-665: Unsachgemäße Initialisierung (Eltern)
  • CWE-696: Falsche Verhaltensreihenfolge (Eltern)
  • CWE-1205: Sicherheitsprimitive und Kryptographie-Probleme (Kategorie)
  • CWE-330: Verwendung unzureichend zufälliger Werte (verwandt)
  • CAPEC-97: Kryptoanalyse (Angriffsmuster)

Referenzen

  1. MITRE Corporation. "CWE-1279: Cryptographic Operations are run Before Supporting Units are Ready." https://cwe.mitre.org/data/definitions/1279.html
  2. NIST SP 800-90B. "Recommendation for the Entropy Sources Used for Random Bit Generation"
  3. FIPS 140-3. "Security Requirements for Cryptographic Modules"