Device Unlock Credential Sharing

Description

Device Unlock Credential Sharing occurs when the credentials necessary for unlocking a device are shared across multiple parties and may expose sensitive information. Device unlocking typically activates debug and manufacturer-specific capabilities using sensitive credentials needed for troubleshooting. The risk of credential compromise increases significantly in multi-company supply chains where chip designers, manufacturers, and testers work for different organizations. Each party needs access to unlock credentials, creating greater exposure risks than vertically integrated companies face.

Risk

Shared unlock credentials have severe security implications. Credentials may be leaked to unauthorized parties. Debug interfaces may be accessible to attackers. Protected functionalities may be exposed. Privilege escalation becomes possible. Memory and files may be accessed without authorization. Protection mechanisms may be bypassed. Supply chain attacks become feasible. Intellectual property may be exposed.

Solution

Limit credential sharing to the minimum necessary parties. Maintain utmost secrecy protocols for all shared credentials. Implement part-specific or batch-specific credentials where feasible. Apply strict access control and need-to-know principles. Use cryptographic methods for credential derivation. Implement credential rotation policies. Audit credential usage and access. Consider hardware security modules for credential storage.

Common Consequences

ImpactDetails
ConfidentialityScope: Confidentiality

Read Memory - Compromised credentials allow unauthorized memory access.
IntegrityScope: Integrity

Modify Files - Attackers can modify device configuration and firmware.
Access ControlScope: Access Control

Bypass Protection Mechanism - Debug access bypasses security controls.
AuthorizationScope: Authorization

Gain Privileges - Device unlock enables privileged operations.

Example Code

Vulnerable Code

// Vulnerable: Single global unlock credential

module vulnerable_device_unlock (
    input wire clk,
    input wire reset_n,
    input wire [127:0] unlock_credential,
    input wire unlock_request,
    output reg device_unlocked,
    output reg debug_enabled
);

    // VULNERABLE: Single global credential for all devices
    // Same credential used across entire supply chain
    parameter GLOBAL_UNLOCK_KEY = 128'h0123456789ABCDEF0123456789ABCDEF;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            device_unlocked <= 1'b0;
            debug_enabled <= 1'b0;
        end
        else if (unlock_request) begin
            // VULNERABLE: Check against global key
            if (unlock_credential == GLOBAL_UNLOCK_KEY) begin
                device_unlocked <= 1'b1;
                debug_enabled <= 1'b1;
            end

            // Problems:
            // 1. Key shared with chip designer
            // 2. Key shared with foundry
            // 3. Key shared with test house
            // 4. Key shared with OEM
            // 5. Any leak compromises ALL devices
        end
    end

endmodule

// Vulnerable: Hardcoded unlock credentials
module vulnerable_hardcoded_unlock (
    input wire clk,
    input wire reset_n,
    input wire [63:0] password,
    input wire unlock_request,
    output reg unlocked
);

    // VULNERABLE: Hardcoded passwords shared across organization
    parameter PASSWORD_ENGINEERING = 64'h456E67696E656572;  // "Engineer"
    parameter PASSWORD_PRODUCTION = 64'h50726F64756374;     // "Product"
    parameter PASSWORD_DEBUG = 64'h4465627567313233;        // "Debug123"

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            unlocked <= 1'b0;
        end
        else if (unlock_request) begin
            // VULNERABLE: Any of the shared passwords unlocks device
            if (password == PASSWORD_ENGINEERING ||
                password == PASSWORD_PRODUCTION ||
                password == PASSWORD_DEBUG) begin
                unlocked <= 1'b1;
            end

            // Anyone who knows any password can unlock any device
        end
    end

endmodule
// Vulnerable: Software with shared unlock credentials

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

// VULNERABLE: Global unlock key compiled into firmware
// Same key for all devices from this manufacturer
static const uint8_t global_unlock_key[16] = {
    0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
    0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF
};

// VULNERABLE: Shared across supply chain
static const char* shared_passwords[] = {
    "DesignerAccess2024",   // Chip designer
    "FoundryDebug",         // Foundry
    "TestHouseUnlock",      // Test house
    "OEMService",           // OEM
    "FieldDebug123"         // Field service
};

bool vulnerable_check_unlock(const uint8_t* credential, size_t len) {
    // VULNERABLE: Check against global key
    if (len == 16 && memcmp(credential, global_unlock_key, 16) == 0) {
        return true;
    }

    // VULNERABLE: Check against all shared passwords
    for (int i = 0; i < sizeof(shared_passwords)/sizeof(shared_passwords[0]); i++) {
        if (strcmp((const char*)credential, shared_passwords[i]) == 0) {
            return true;
        }
    }

    return false;
}

void vulnerable_unlock_device(const uint8_t* credential, size_t len) {
    if (vulnerable_check_unlock(credential, len)) {
        // VULNERABLE: Full unlock for any valid credential
        enable_jtag();
        enable_debug_uart();
        disable_secure_boot();
        enable_memory_dump();

        // Any party with any credential gets full access
    }
}

// VULNERABLE: Credential sharing in supply chain
typedef struct {
    char company_name[64];
    uint8_t unlock_credential[16];
    uint32_t access_level;
} supply_chain_credential_t;

supply_chain_credential_t vulnerable_supply_chain[] = {
    {"Chip Designer Co", {0x01, 0x23, ...}, ACCESS_FULL},
    {"Global Foundry",   {0x01, 0x23, ...}, ACCESS_FULL},  // Same key!
    {"Test Systems Inc", {0x01, 0x23, ...}, ACCESS_FULL},  // Same key!
    {"Acme OEM",         {0x01, 0x23, ...}, ACCESS_FULL},  // Same key!
};
// All parties share the same credential
// Leak from any party compromises all

Fixed Code

// Fixed: Device-specific unlock credentials

module secure_device_unlock (
    input wire clk,
    input wire reset_n,
    input wire [127:0] unlock_credential,
    input wire [127:0] challenge,
    input wire unlock_request,
    input wire [63:0] device_id,
    output reg device_unlocked,
    output reg debug_enabled,
    output reg unlock_failed
);

    // FIXED: Device-specific key derived from master + device ID
    // Master key stored in HSM, never shared directly

    wire [127:0] device_specific_key;
    reg [127:0] expected_response;

    // Key derivation (simplified - use proper KDF in practice)
    // In real implementation: HKDF(master_key, device_id)
    key_derivation_unit kdf (
        .device_id(device_id),
        .derived_key(device_specific_key)
    );

    // Challenge-response authentication
    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            device_unlocked <= 1'b0;
            debug_enabled <= 1'b0;
            unlock_failed <= 1'b0;
        end
        else if (unlock_request) begin
            // FIXED: Verify challenge-response with device-specific key
            expected_response <= compute_hmac(device_specific_key, challenge);

            if (unlock_credential == expected_response) begin
                device_unlocked <= 1'b1;
                debug_enabled <= 1'b1;
                unlock_failed <= 1'b0;
            end
            else begin
                unlock_failed <= 1'b1;
            end
        end
    end

endmodule

// Fixed: Tiered unlock with different access levels
module secure_tiered_unlock (
    input wire clk,
    input wire reset_n,
    input wire [127:0] credential,
    input wire [1:0] requested_level,
    input wire unlock_request,
    output reg [1:0] current_access_level,
    output reg unlock_granted
);

    // FIXED: Different credentials for different access levels
    // Each level has unique, non-shared credentials

    parameter LEVEL_NONE = 2'b00;
    parameter LEVEL_BASIC = 2'b01;      // Basic diagnostics
    parameter LEVEL_ADVANCED = 2'b10;   // Advanced debug
    parameter LEVEL_FULL = 2'b11;       // Full unlock (restricted)

    // Per-device credentials stored in OTP
    reg [127:0] basic_credential;       // From fuse
    reg [127:0] advanced_credential;    // From fuse
    reg [127:0] full_credential;        // From secure fuse

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            current_access_level <= LEVEL_NONE;
            unlock_granted <= 1'b0;
        end
        else if (unlock_request) begin
            unlock_granted <= 1'b0;

            case (requested_level)
                LEVEL_BASIC: begin
                    // FIXED: Basic level - limited exposure
                    if (credential == basic_credential) begin
                        current_access_level <= LEVEL_BASIC;
                        unlock_granted <= 1'b1;
                    end
                end

                LEVEL_ADVANCED: begin
                    // FIXED: Advanced level - more restricted sharing
                    if (credential == advanced_credential) begin
                        current_access_level <= LEVEL_ADVANCED;
                        unlock_granted <= 1'b1;
                    end
                end

                LEVEL_FULL: begin
                    // FIXED: Full level - never shared externally
                    if (credential == full_credential) begin
                        current_access_level <= LEVEL_FULL;
                        unlock_granted <= 1'b1;
                    end
                end
            endcase
        end
    end

endmodule

// Fixed: One-time unlock tokens
module secure_one_time_unlock (
    input wire clk,
    input wire reset_n,
    input wire [255:0] unlock_token,
    input wire unlock_request,
    output reg device_unlocked,
    output reg token_consumed
);

    // FIXED: One-time tokens - cannot be reused
    reg [255:0] token_hash_storage [0:15];
    reg [3:0] token_index;
    reg token_valid;

    // Check if token matches any unused token
    integer i;
    always @(*) begin
        token_valid = 1'b0;
        for (i = 0; i < 16; i = i + 1) begin
            if (token_hash_storage[i] == sha256(unlock_token) &&
                token_hash_storage[i] != 256'h0) begin
                token_valid = 1'b1;
                token_index = i;
            end
        end
    end

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            device_unlocked <= 1'b0;
            token_consumed <= 1'b0;
        end
        else if (unlock_request && token_valid) begin
            // FIXED: Consume token after use
            token_hash_storage[token_index] <= 256'h0;
            device_unlocked <= 1'b1;
            token_consumed <= 1'b1;
        end
    end

endmodule
// Fixed: Software with secure credential management

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

// FIXED: No hardcoded credentials
// All credentials derived from device-specific secrets

typedef struct {
    uint8_t device_id[16];
    uint8_t derived_key[32];
} device_credentials_t;

// FIXED: Derive device-specific credentials from master + device ID
static bool derive_device_credential(const uint8_t* device_id,
                                     uint8_t* credential_out) {
    // In practice: HSM derives key, device only stores derived value
    // Key derivation: HKDF(master_key, device_id || "unlock")

    uint8_t context[32];
    memcpy(context, device_id, 16);
    memcpy(context + 16, "unlock_context", 16);

    // Derive from device-specific root key
    return hkdf_derive(get_device_root_key(), context, 32, credential_out);
}

// FIXED: Challenge-response authentication
typedef struct {
    uint8_t challenge[32];
    uint8_t response[32];
    uint32_t timestamp;
    uint32_t nonce;
} unlock_request_t;

bool secure_verify_unlock(const unlock_request_t* request) {
    uint8_t device_credential[32];
    uint8_t expected_response[32];

    // Get device-specific credential
    if (!derive_device_credential(get_device_id(), device_credential)) {
        return false;
    }

    // FIXED: Verify timestamp is recent (prevent replay)
    if (!verify_timestamp(request->timestamp)) {
        log_security_event("Unlock timestamp invalid");
        return false;
    }

    // FIXED: Verify nonce hasn't been used (prevent replay)
    if (is_nonce_used(request->nonce)) {
        log_security_event("Unlock nonce replay detected");
        return false;
    }

    // Compute expected response
    compute_hmac_sha256(device_credential, request->challenge, 32,
                        expected_response);

    // FIXED: Constant-time comparison
    if (!secure_compare(request->response, expected_response, 32)) {
        log_security_event("Unlock credential mismatch");
        return false;
    }

    // Mark nonce as used
    mark_nonce_used(request->nonce);

    return true;
}

// FIXED: Tiered access levels with separate credentials
typedef enum {
    ACCESS_NONE = 0,
    ACCESS_BASIC = 1,      // Basic diagnostics - shared with field service
    ACCESS_ADVANCED = 2,   // Advanced debug - limited sharing
    ACCESS_FULL = 3        // Full access - never shared externally
} access_level_t;

typedef struct {
    access_level_t level;
    uint8_t credential[32];
    uint32_t permissions;
} access_tier_t;

static access_tier_t access_tiers[4];

void secure_unlock_device(access_level_t level, const uint8_t* credential) {
    // FIXED: Verify credential for specific level only
    uint8_t expected[32];
    derive_level_credential(level, expected);

    if (!secure_compare(credential, expected, 32)) {
        log_security_event("Invalid credential for level %d", level);
        increment_failure_counter();
        return;
    }

    // FIXED: Grant only permissions for this level
    switch (level) {
        case ACCESS_BASIC:
            // Limited access for field service
            enable_basic_diagnostics();
            break;

        case ACCESS_ADVANCED:
            // More access for authorized technicians
            enable_advanced_debug();
            break;

        case ACCESS_FULL:
            // Full access - internal use only
            enable_jtag();
            enable_memory_dump();
            break;

        default:
            break;
    }
}

// FIXED: Audit credential usage
void log_credential_usage(access_level_t level, const char* requester) {
    audit_entry_t entry = {
        .timestamp = get_secure_time(),
        .access_level = level,
        .device_id = get_device_id(),
        .requester_hash = hash_string(requester),
        .success = true
    };

    write_audit_log(&entry);
}

CVE Examples

Credential sharing vulnerabilities have been found in various supply chain scenarios where unlock credentials shared across multiple parties were leaked, allowing unauthorized access to device debug interfaces across entire product lines.


  • CWE-200: Exposure of Sensitive Information to an Unauthorized Actor (parent)
  • CWE-1195: Manufacturing and Life Cycle Management Concerns (category)
  • CWE-798: Use of Hard-coded Credentials (related)
  • CAPEC-560: Use of Known Domain Credentials (attack pattern)

References

  1. MITRE Corporation. "CWE-1273: Device Unlock Credential Sharing." https://cwe.mitre.org/data/definitions/1273.html
  2. NIST. "Supply Chain Risk Management Practices"
  3. GlobalPlatform. "Device Trust Architecture"