Sensitive Information Uncleared Before Debug/Power State Transition

Description

Sensitive Information Uncleared Before Debug/Power State Transition occurs when a product fails to clear sensitive information during power or debug state transitions, leaving data accessible when it should no longer be available due to changed access restrictions. Devices operate across multiple power states (normal, low power, hibernate, deep sleep) and debug conditions. When transitioning between states, information that was accessible in the previous state may remain in memory or storage. If this sensitive data isn't removed before the transition, it can leak to unauthorized actors in the new state.

Risk

Uncleared sensitive information has severe security implications. Cryptographic keys may leak across states. Authentication credentials may be exposed. Debug access may reveal secrets. Hidden device capabilities may be unlocked. Confidential data may be compromised. Security boundaries may be violated. Attackers can exploit state transitions. Information disclosure enables further attacks.

Solution

During state transitions, remove information not needed in the next state before completing the transition. Implement automatic scrubbing of sensitive registers and memory during power transitions. Clear cryptographic keys before entering debug mode. Design state machines that enforce data clearing. Verify clearing through testing. Apply this mitigation during both architecture/design and implementation phases.

Common Consequences

ImpactDetails
ConfidentialityScope: Confidentiality

Read Memory - Sensitive data remains accessible after state transition.
Access ControlScope: Access Control

Bypass Protection Mechanism - Attackers can access data through state transitions.
AuthenticationScope: Authentication

Gain Privileges - Credentials may be recovered from previous state.

Example Code

Vulnerable Code

// Vulnerable: State transition without clearing sensitive data

module vulnerable_power_controller (
    input wire clk,
    input wire reset_n,
    input wire [1:0] power_request,
    output reg [1:0] current_state,
    // Sensitive data
    input wire [127:0] encryption_key,
    output reg [127:0] key_register
);

    // Power states
    parameter ACTIVE = 2'b00;
    parameter IDLE = 2'b01;
    parameter SLEEP = 2'b10;
    parameter DEBUG = 2'b11;

    // VULNERABLE: Key remains in register across state transitions
    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            current_state <= ACTIVE;
            key_register <= 128'h0;
        end
        else begin
            case (current_state)
                ACTIVE: begin
                    // Load key in active state
                    key_register <= encryption_key;

                    if (power_request == SLEEP) begin
                        // VULNERABLE: Transition to sleep without clearing key
                        current_state <= SLEEP;
                    end
                    else if (power_request == DEBUG) begin
                        // VULNERABLE: Transition to debug without clearing key
                        current_state <= DEBUG;
                        // Key is still accessible in debug state!
                    end
                end

                SLEEP: begin
                    // VULNERABLE: Key still present in sleep state
                    // Attacker can probe memory while device sleeps

                    if (power_request == ACTIVE) begin
                        current_state <= ACTIVE;
                    end
                end

                DEBUG: begin
                    // VULNERABLE: Full key visible in debug state
                    // Debug interface can read key_register

                    if (power_request == ACTIVE) begin
                        current_state <= ACTIVE;
                    end
                end
            endcase
        end
    end

endmodule

// Vulnerable: Debug mode exposing sensitive information
module vulnerable_debug_access (
    input wire clk,
    input wire reset_n,
    input wire debug_enable,
    input wire [7:0] debug_addr,
    output reg [31:0] debug_data,
    // Internal sensitive state
    input wire [255:0] private_key,
    input wire [127:0] session_key,
    input wire [63:0] user_credentials
);

    // VULNERABLE: All internal state accessible in debug mode
    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            debug_data <= 32'h0;
        end
        else if (debug_enable) begin
            // VULNERABLE: No clearing of sensitive data before debug
            case (debug_addr)
                8'h00: debug_data <= private_key[31:0];
                8'h01: debug_data <= private_key[63:32];
                8'h02: debug_data <= private_key[95:64];
                // ... full private key accessible
                8'h10: debug_data <= session_key[31:0];
                8'h14: debug_data <= user_credentials[31:0];
            endcase
        end
    end

endmodule
// Vulnerable: Software state transition without clearing

#include <stdint.h>

typedef struct {
    uint8_t aes_key[32];
    uint8_t private_key[256];
    uint8_t user_password[64];
    uint8_t session_token[32];
} sensitive_data_t;

static sensitive_data_t secrets;

// VULNERABLE: Enter sleep without clearing secrets
void vulnerable_enter_sleep(void) {
    // Save some state for resume
    save_cpu_context();

    // VULNERABLE: Secrets remain in memory during sleep
    // RAM may be accessible to attacker

    // Enter sleep mode
    enter_low_power_mode();

    // On wake, secrets are still present
}

// VULNERABLE: Enter debug mode without clearing secrets
void vulnerable_enter_debug(void) {
    // VULNERABLE: No clearing before debug

    // Enable debug interface
    enable_jtag();
    enable_debug_uart();

    // Debugger can now access all memory including secrets!
}

// VULNERABLE: TPM specification violation
void vulnerable_tpm_power_transition(void) {
    // VULNERABLE: Don't set required flags per TPM spec
    // Failed authorization attempts not recorded after power loss

    // Enter low power mode
    tpm_enter_idle();

    // Power loss occurs...

    // On resume, authorization failure counter is reset
    // Attacker gets unlimited password attempts!
}

Fixed Code

// Fixed: State transition with sensitive data clearing

module secure_power_controller (
    input wire clk,
    input wire reset_n,
    input wire [1:0] power_request,
    output reg [1:0] current_state,
    input wire [127:0] encryption_key,
    output reg [127:0] key_register,
    output reg key_valid,
    output reg transition_complete
);

    // Power states
    parameter ACTIVE = 2'b00;
    parameter IDLE = 2'b01;
    parameter SLEEP = 2'b10;
    parameter DEBUG = 2'b11;

    // State transition phases
    reg [1:0] transition_phase;
    parameter PHASE_IDLE = 2'b00;
    parameter PHASE_CLEARING = 2'b01;
    parameter PHASE_TRANSITION = 2'b10;
    parameter PHASE_COMPLETE = 2'b11;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            current_state <= ACTIVE;
            key_register <= 128'h0;
            key_valid <= 1'b0;
            transition_phase <= PHASE_IDLE;
            transition_complete <= 1'b0;
        end
        else begin
            case (current_state)
                ACTIVE: begin
                    transition_complete <= 1'b0;

                    // Load key in active state
                    key_register <= encryption_key;
                    key_valid <= 1'b1;

                    if (power_request == SLEEP || power_request == DEBUG) begin
                        // FIXED: Enter clearing phase before transition
                        transition_phase <= PHASE_CLEARING;
                    end
                end
            endcase

            // FIXED: Clearing phase
            if (transition_phase == PHASE_CLEARING) begin
                // Clear all sensitive data
                key_register <= 128'h0;
                key_valid <= 1'b0;
                transition_phase <= PHASE_TRANSITION;
            end

            // Transition after clearing complete
            if (transition_phase == PHASE_TRANSITION) begin
                current_state <= power_request;
                transition_phase <= PHASE_COMPLETE;
                transition_complete <= 1'b1;
            end

            if (transition_phase == PHASE_COMPLETE) begin
                transition_phase <= PHASE_IDLE;
            end
        end
    end

endmodule

// Fixed: Debug mode with sensitive data protection
module secure_debug_access (
    input wire clk,
    input wire reset_n,
    input wire debug_request,
    input wire debug_authenticated,
    input wire [7:0] debug_addr,
    output reg [31:0] debug_data,
    output reg debug_enabled,
    // Internal sensitive state
    input wire [255:0] private_key,
    input wire [127:0] session_key,
    input wire [63:0] user_credentials
);

    // FIXED: Cleared copies for debug access
    reg [255:0] cleared_private_key;
    reg [127:0] cleared_session_key;
    reg [63:0] cleared_credentials;
    reg sensitive_cleared;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            debug_data <= 32'h0;
            debug_enabled <= 1'b0;
            sensitive_cleared <= 1'b0;
            cleared_private_key <= 256'h0;
            cleared_session_key <= 128'h0;
            cleared_credentials <= 64'h0;
        end
        else begin
            if (debug_request && debug_authenticated) begin
                // FIXED: Clear sensitive data BEFORE enabling debug
                if (!sensitive_cleared) begin
                    cleared_private_key <= 256'h0;
                    cleared_session_key <= 128'h0;
                    cleared_credentials <= 64'h0;
                    sensitive_cleared <= 1'b1;
                end
                else begin
                    debug_enabled <= 1'b1;
                end
            end

            if (debug_enabled) begin
                case (debug_addr)
                    // FIXED: Only provide cleared/sanitized data
                    8'h00: debug_data <= 32'hDEADBEEF;  // Indicator
                    8'h10: debug_data <= 32'hDEADBEEF;  // No real data
                    default: debug_data <= 32'h0;
                endcase
            end

            // Exit debug mode
            if (!debug_request) begin
                debug_enabled <= 1'b0;
                sensitive_cleared <= 1'b0;
            end
        end
    end

endmodule

// Fixed: Power state machine with mandatory clearing
module secure_power_fsm (
    input wire clk,
    input wire reset_n,
    input wire [1:0] requested_state,
    input wire transition_request,
    output reg [1:0] current_state,
    output reg clearing_in_progress,
    output reg transition_complete,
    // Sensitive data interface
    output reg clear_keys,
    output reg clear_session,
    output reg clear_cache,
    input wire keys_cleared,
    input wire session_cleared,
    input wire cache_cleared
);

    parameter ACTIVE = 2'b00;
    parameter SLEEP = 2'b01;
    parameter HIBERNATE = 2'b10;
    parameter DEBUG = 2'b11;

    reg [2:0] fsm_state;
    parameter FSM_IDLE = 3'd0;
    parameter FSM_START_CLEAR = 3'd1;
    parameter FSM_WAIT_CLEAR = 3'd2;
    parameter FSM_VERIFY_CLEAR = 3'd3;
    parameter FSM_TRANSITION = 3'd4;
    parameter FSM_COMPLETE = 3'd5;

    reg [1:0] pending_state;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            current_state <= ACTIVE;
            fsm_state <= FSM_IDLE;
            clearing_in_progress <= 1'b0;
            transition_complete <= 1'b0;
            clear_keys <= 1'b0;
            clear_session <= 1'b0;
            clear_cache <= 1'b0;
        end
        else begin
            case (fsm_state)
                FSM_IDLE: begin
                    transition_complete <= 1'b0;
                    if (transition_request && requested_state != current_state) begin
                        pending_state <= requested_state;
                        fsm_state <= FSM_START_CLEAR;
                    end
                end

                FSM_START_CLEAR: begin
                    // FIXED: Initiate clearing of all sensitive data
                    clearing_in_progress <= 1'b1;
                    clear_keys <= 1'b1;
                    clear_session <= 1'b1;
                    clear_cache <= 1'b1;
                    fsm_state <= FSM_WAIT_CLEAR;
                end

                FSM_WAIT_CLEAR: begin
                    // FIXED: Wait for all clearing to complete
                    if (keys_cleared && session_cleared && cache_cleared) begin
                        clear_keys <= 1'b0;
                        clear_session <= 1'b0;
                        clear_cache <= 1'b0;
                        fsm_state <= FSM_VERIFY_CLEAR;
                    end
                end

                FSM_VERIFY_CLEAR: begin
                    // FIXED: Verify clearing was successful
                    // Could add additional verification here
                    fsm_state <= FSM_TRANSITION;
                end

                FSM_TRANSITION: begin
                    // FIXED: Only transition after clearing verified
                    current_state <= pending_state;
                    clearing_in_progress <= 1'b0;
                    fsm_state <= FSM_COMPLETE;
                end

                FSM_COMPLETE: begin
                    transition_complete <= 1'b1;
                    fsm_state <= FSM_IDLE;
                end
            endcase
        end
    end

endmodule
// Fixed: Software state transition with proper clearing

#include <stdint.h>
#include <string.h>

typedef struct {
    uint8_t aes_key[32];
    uint8_t private_key[256];
    uint8_t user_password[64];
    uint8_t session_token[32];
} sensitive_data_t;

static sensitive_data_t secrets;

// FIXED: Secure memory clear that won't be optimized out
static void secure_clear(volatile void* ptr, size_t size) {
    volatile uint8_t* p = (volatile uint8_t*)ptr;
    while (size--) {
        *p++ = 0;
    }
    __asm__ volatile("" ::: "memory");
}

// FIXED: Enter sleep with proper clearing
void secure_enter_sleep(void) {
    // FIXED: Clear sensitive data before sleep
    secure_clear(&secrets.aes_key, sizeof(secrets.aes_key));
    secure_clear(&secrets.private_key, sizeof(secrets.private_key));
    secure_clear(&secrets.session_token, sizeof(secrets.session_token));
    // Note: May keep user_password if needed for resume authentication

    // FIXED: Clear hardware key registers
    clear_hardware_key_registers();

    // FIXED: Clear any caches
    flush_and_invalidate_cache();

    // Save minimal state for resume
    save_cpu_context();

    // Now safe to enter sleep
    enter_low_power_mode();
}

// FIXED: Enter debug mode with sensitive data protection
void secure_enter_debug(void) {
    // FIXED: Clear ALL sensitive data before enabling debug
    secure_clear(&secrets, sizeof(secrets));

    // FIXED: Clear hardware security state
    clear_hardware_key_registers();
    clear_secure_memory_regions();
    zeroize_crypto_state();

    // FIXED: Verify clearing
    if (!verify_sensitive_data_cleared()) {
        log_error("Failed to clear sensitive data");
        secure_halt();
        return;
    }

    // Now safe to enable debug
    enable_jtag();
    enable_debug_uart();
}

// FIXED: TPM compliant power transition
void secure_tpm_power_transition(void) {
    // FIXED: Set required flags per TPM specification
    tpm_set_state_save_flag();

    // FIXED: Save authorization failure counter
    save_auth_failure_counter();

    // Enter low power mode
    tpm_enter_idle();

    // On resume, counter will be restored
    // Brute force attacks still limited
}

// FIXED: Resume from sleep with verification
void secure_resume_from_sleep(void) {
    // Verify we're in expected state
    if (!verify_sleep_state()) {
        secure_halt();
        return;
    }

    // FIXED: Verify sensitive data was cleared
    if (!verify_secrets_zero(&secrets.aes_key, sizeof(secrets.aes_key))) {
        log_error("AES key not cleared during sleep");
        secure_halt();
        return;
    }

    // Restore CPU context
    restore_cpu_context();

    // Re-authenticate before restoring secrets
    if (!authenticate_user()) {
        secure_halt();
        return;
    }

    // Now safe to reload secrets from secure storage
    reload_secrets_from_secure_storage();
}

CVE Examples

  • CVE-2020-12926: Product software failed to set required flags per TPM specifications, preventing failed authorization attempts from being recorded after power loss, enabling brute force attacks.

  • CWE-226: Sensitive Information in Resource Not Removed Before Reuse (parent)
  • CWE-200: Exposure of Sensitive Information to an Unauthorized Actor (related)
  • CWE-1207: Debug and Test Problems (category)
  • CWE-1258: Exposure of Sensitive System Information Due to Uncleared Debug Information (related)

References

  1. MITRE Corporation. "CWE-1272: Sensitive Information Uncleared Before Debug/Power State Transition." https://cwe.mitre.org/data/definitions/1272.html
  2. TCG. "TPM 2.0 Library Specification"
  3. ARM. "TrustZone Power Management"