Cryptographic Operations are run Before Supporting Units are Ready

Description

Cryptographic Operations are run Before Supporting Units are Ready occurs when performing cryptographic operations without ensuring that the supporting inputs are ready to supply valid data may compromise the cryptographic result. Cryptographic hardware units depend on other hardware components to function securely. Examples include random-number generators (RNG) providing entropy and fuse units supplying encryption keys. Operating crypto units before these dependencies are ready produces insecure results, such as using a predictable seed instead of true randomness.

Risk

Premature cryptographic operations have severe security implications. Random numbers may be predictable. Keys may not be properly loaded. Encryption may use weak values. Signatures may be forgeable. Authentication may be bypassable. Cryptographic security is undermined. Side-channel attacks become easier. Entire security model may collapse.

Solution

Use best practices in cryptographic system design. Continuously verify that cryptographic inputs supply valid information. Implement ready signals and handshaking between components. Verify RNG health before use. Ensure key material is fully loaded before operations. Use status registers to confirm unit readiness. Implement error handling for unready conditions.

Common Consequences

ImpactDetails
Access ControlScope: Access Control

Bypass Protection Mechanism - Weak crypto enables bypass.
ConfidentialityScope: Confidentiality

Read Memory - Predictable crypto can be broken.
IntegrityScope: Integrity

Modify Memory - Signatures may be forgeable.
AuthenticationScope: Authentication

Bypass Authentication - Weak keys enable impersonation.

Example Code

Vulnerable Code

// Vulnerable: Crypto operations before RNG is ready

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 signals
    wire [127:0] rng_output;
    wire rng_ready;

    // Key signals
    wire [127:0] key_from_fuse;
    wire key_ready;

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

    // Fuse unit instance
    fuse_unit fuses (
        .clk(clk),
        .reset_n(reset_n),
        .key_out(key_from_fuse),
        .ready(key_ready)
    );

    // VULNERABLE: Crypto operation without checking readiness
    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
            // VULNERABLE: Uses RNG output even if RNG not ready
            // rng_output may be all zeros or predictable

            // VULNERABLE: Uses key even if fuse unit not ready
            // key_from_fuse may be all zeros

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

            // Problem: If RNG not ready, IV/nonce is predictable
            // Problem: If fuses not ready, key is zero/weak
        end
    end

endmodule

// Vulnerable: RNG with fallback to weak 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;

    // VULNERABLE: Fallback to weak seed if entropy not ready
    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
                // Good: Use true entropy
                random_data <= entropy_bits;
                data_valid <= 1'b1;
            end
            else begin
                // VULNERABLE: Fallback to hardcoded seed
                random_data <= 128'hDEADBEEF_CAFEBABE_12345678_87654321;
                data_valid <= 1'b1;  // Claims valid but is predictable!
            end
        end
    end

endmodule
// Vulnerable: Software crypto without checking RNG readiness

#include <stdint.h>

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

// VULNERABLE: Generate IV without checking RNG
void vulnerable_generate_iv(uint8_t* iv, size_t len) {
    volatile uint32_t* rng_data = (volatile uint32_t*)RNG_DATA_REG;

    // VULNERABLE: No check if RNG is ready
    // RNG may still be collecting entropy after reset

    for (size_t i = 0; i < len; i += 4) {
        // RNG might return all zeros if not ready!
        uint32_t random = *rng_data;
        memcpy(iv + i, &random, 4);
    }

    // If called immediately after reset, IV may be predictable
    // Encryption can be broken if attacker knows IV
}

// VULNERABLE: Use key before fuse unit ready
void vulnerable_load_key(void) {
    volatile uint32_t* fuse_key = (volatile uint32_t*)FUSE_KEY_BASE;

    // VULNERABLE: No check if fuse unit has completed initialization
    // Key may be all zeros during power-up sequence

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

    // If key is all zeros, encryption is useless
    aes_set_key(key);
}

// VULNERABLE: Encrypt with potentially weak parameters
void vulnerable_encrypt(const uint8_t* plaintext, size_t len,
                        uint8_t* ciphertext) {
    uint8_t iv[16];
    vulnerable_generate_iv(iv, 16);  // May be predictable!
    vulnerable_load_key();           // May be zero!

    // Encryption with weak IV and key is breakable
    aes_cbc_encrypt(plaintext, len, iv, ciphertext);
}

Fixed Code

// Fixed: Crypto operations only after supporting units ready

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 signals
    wire [127:0] rng_output;
    wire rng_ready;
    wire rng_health_ok;

    // Key signals
    wire [127:0] key_from_fuse;
    wire key_ready;
    wire key_valid;

    // RNG instance with health check
    secure_rng_unit rng (
        .clk(clk),
        .reset_n(reset_n),
        .random_data(rng_output),
        .ready(rng_ready),
        .health_ok(rng_health_ok)
    );

    // Fuse unit instance
    secure_fuse_unit fuses (
        .clk(clk),
        .reset_n(reset_n),
        .key_out(key_from_fuse),
        .ready(key_ready),
        .valid(key_valid)
    );

    // State machine for crypto 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
                    // FIXED: Wait for RNG to be ready and healthy
                    if (rng_ready && rng_health_ok) begin
                        state <= WAIT_KEY;
                    end
                    else if (!rng_health_ok) begin
                        // RNG health check failed
                        state <= ERROR;
                    end
                    // Timeout handling could be added here
                end

                WAIT_KEY: begin
                    // FIXED: Wait for key to be ready and valid
                    if (key_ready && key_valid) begin
                        state <= ENCRYPT;
                    end
                    else if (key_ready && !key_valid) begin
                        // Key validation failed
                        state <= ERROR;
                    end
                end

                ENCRYPT: begin
                    // FIXED: All dependencies satisfied, safe to encrypt
                    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

// Fixed: RNG with proper health checking
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)
    );

    // FIXED: Health check state
    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;

    // Previous output for continuous testing
    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
                    // FIXED: Wait for entropy source to stabilize
                    ready <= 1'b0;
                    health_ok <= 1'b0;
                    if (entropy_ready) begin
                        health_state <= HEALTH_STARTUP;
                        startup_count <= 8'h0;
                    end
                end

                HEALTH_STARTUP: begin
                    // FIXED: Run startup health tests
                    if (entropy_ready) begin
                        // Collect samples and run statistical tests
                        if (run_startup_tests(entropy_bits)) begin
                            startup_count <= startup_count + 1;
                        end
                        else begin
                            health_state <= HEALTH_FAILED;
                        end

                        // Need multiple passing samples
                        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
                    // FIXED: Continuous health monitoring
                    ready <= 1'b1;
                    health_ok <= 1'b1;

                    if (request_random && entropy_ready) begin
                        // FIXED: Check for stuck output
                        if (entropy_bits == previous_output) begin
                            // Repetition detected - possible failure
                            health_state <= HEALTH_FAILED;
                        end
                        else begin
                            random_data <= entropy_bits;
                            previous_output <= entropy_bits;
                        end
                    end
                end

                HEALTH_FAILED: begin
                    // FIXED: RNG failed - do not provide output
                    ready <= 1'b0;
                    health_ok <= 1'b0;
                    random_data <= 128'h0;
                    // Require reset to recover
                end
            endcase
        end
    end

endmodule
// Fixed: Software crypto with proper readiness checks

#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

// FIXED: Check RNG status before use
static bool is_rng_ready(void) {
    volatile uint32_t* rng_status = (volatile uint32_t*)RNG_STATUS_REG;
    uint32_t status = *rng_status;

    // Check both ready and health bits
    return (status & RNG_READY_BIT) && (status & RNG_HEALTH_BIT);
}

// FIXED: Wait for RNG with timeout
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 - not ready");
            return false;
        }
        // Small delay to avoid hammering register
        delay_us(100);
    }

    return true;
}

// FIXED: Generate IV only when RNG is ready
bool secure_generate_iv(uint8_t* iv, size_t len) {
    volatile uint32_t* rng_data = (volatile uint32_t*)RNG_DATA_REG;

    // FIXED: Wait for RNG to be ready
    if (!wait_for_rng(1000)) {
        return false;
    }

    for (size_t i = 0; i < len; i += 4) {
        // FIXED: Verify RNG still healthy before each read
        if (!is_rng_ready()) {
            log_error("RNG became unhealthy during IV generation");
            return false;
        }

        uint32_t random = *rng_data;

        // FIXED: Sanity check - reject obvious bad values
        static uint32_t previous = 0;
        if (random == previous || random == 0 || random == 0xFFFFFFFF) {
            log_error("RNG output suspicious");
            return false;
        }
        previous = random;

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

    return true;
}

// FIXED: Load key only when fuse unit ready
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;

    // FIXED: Wait for fuse unit to complete initialization
    uint32_t start = get_tick_count();
    while (!(*fuse_status & FUSE_READY_BIT)) {
        if (get_tick_count() - start > 1000) {
            log_error("Fuse unit not ready");
            return false;
        }
        delay_us(100);
    }

    // FIXED: Check key validity
    if (!(*fuse_status & FUSE_KEY_VALID_BIT)) {
        log_error("Fuse key not valid");
        return false;
    }

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

    // FIXED: Verify key is not all zeros or all ones
    if (is_key_weak(key_out, 16)) {
        log_error("Weak key detected from fuses");
        secure_memzero(key_out, 16);
        return false;
    }

    return true;
}

// FIXED: Encrypt only with valid parameters
bool secure_encrypt(const uint8_t* plaintext, size_t len,
                    uint8_t* ciphertext) {
    uint8_t iv[16];
    uint8_t key[16];

    // FIXED: Generate IV with verification
    if (!secure_generate_iv(iv, 16)) {
        log_error("Failed to generate IV");
        return false;
    }

    // FIXED: Load key with verification
    if (!secure_load_key(key)) {
        log_error("Failed to load key");
        secure_memzero(iv, 16);
        return false;
    }

    // Now safe to encrypt
    bool result = aes_cbc_encrypt(plaintext, len, key, iv, ciphertext);

    // Clear sensitive data
    secure_memzero(key, 16);
    secure_memzero(iv, 16);

    return result;
}

CVE Examples

Cryptographic timing vulnerabilities have been found in various systems where crypto operations proceeded before RNG initialization was complete, resulting in predictable keys or IVs that could be exploited.


  • CWE-665: Improper Initialization (parent)
  • CWE-696: Incorrect Behavior Order (parent)
  • CWE-1205: Security Primitives and Cryptography Issues (category)
  • CWE-330: Use of Insufficiently Random Values (related)
  • CAPEC-97: Cryptanalysis (attack pattern)

References

  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"