Incorrect Decoding of Security Identifiers

Description

Incorrect Decoding of Security Identifiers occurs when a product implements a decoding mechanism to decode bus-transaction signals to security identifiers incorrectly. In System-On-Chip (SoC) environments, hardware transactions typically include source and destination identities alongside security identifiers that determine which agents receive access to assets and what actions they can perform. When the decoder incorrectly maps an untrusted agent's security identifier to a trusted one, it inadvertently grants unauthorized access to protected resources.

Risk

Incorrect security identifier decoding has severe implications. Untrusted agents gain unauthorized access. Memory modification possible. Unauthorized reads enabled. Denial of service through resource consumption. Execution of unauthorized code. Privilege escalation possible. Identity assumption attacks enabled. Complete bypass of access controls. High likelihood of exploitation once the flaw exists.

Solution

Security identifier decoders require review for design consistency and common weaknesses identification during the architecture and design phase. Ensure complete bit-field checking rather than partial checks. Access and programming flows must be tested in both pre-silicon and post-silicon testing phases. Implement formal verification of decoder logic. Use comprehensive test vectors covering all possible security identifier values. Review decoder truth tables for completeness.

Common Consequences

ImpactDetails
ConfidentialityScope: Confidentiality

Unauthorized read access to protected memory and assets.
IntegrityScope: Integrity

Memory modification by untrusted agents.
AvailabilityScope: Availability

Denial of service through resource consumption.
Access ControlScope: Access Control

Privilege escalation, identity assumption, unauthorized code execution.

Example Code

Vulnerable Code

// Vulnerable: Incomplete security identifier decoding

module vulnerable_security_decoder (
    input  wire [31:0] bus_transaction,
    output reg  [1:0]  security_identifier,
    output reg         access_granted
);

    // Security identifier is in bits [15:14]
    // 00 = Untrusted (Master_0)
    // 01 = Limited trust (Master_1)
    // 10 = Trusted (Master_2)
    // 11 = Highly trusted (Master_3)

    // VULNERABLE: Only checking bit [14], ignoring bit [15]
    always @(*) begin
        if (bus_transaction[14] == 1'b1) begin
            // VULNERABLE: This incorrectly grants trusted status
            // Master_1 (01) is treated same as Master_3 (11)
            security_identifier = 2'b01;  // Assumes limited trust
        end else begin
            security_identifier = 2'b00;  // Untrusted
        end
    end

    // VULNERABLE: Simplified access check
    always @(*) begin
        // Grant access if any trust level
        access_granted = (security_identifier != 2'b00);
    end

    // Attack scenario:
    // Master_0 (untrusted, bits[15:14] = 00) - correctly denied
    // Master_1 (limited, bits[15:14] = 01) - granted (bit 14 = 1)
    // Master_2 (trusted, bits[15:14] = 10) - INCORRECTLY denied (bit 14 = 0)
    // Master_3 (high trust, bits[15:14] = 11) - granted (bit 14 = 1)
    //
    // This allows Master_1 to have same access as Master_3!

endmodule

// VULNERABLE: AES key register access with flawed decoder
module vulnerable_aes_key_access (
    input  wire        clk,
    input  wire        rst_n,
    input  wire [31:0] bus_transaction,
    input  wire [7:0]  address,
    input  wire        read_enable,
    output reg  [127:0] aes_key_out,
    output reg         access_fault
);

    reg [127:0] aes_key;
    reg [1:0]   decoded_security_id;

    // AES key register at address 0x80
    localparam AES_KEY_ADDR = 8'h80;

    // VULNERABLE: Incomplete decoder
    always @(*) begin
        // Only checking one bit!
        decoded_security_id = bus_transaction[14] ? 2'b01 : 2'b00;
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            aes_key_out <= 128'b0;
            access_fault <= 1'b0;
        end else if (read_enable && (address == AES_KEY_ADDR)) begin
            // VULNERABLE: Flawed security check
            if (decoded_security_id != 2'b00) begin
                aes_key_out <= aes_key;
                access_fault <= 1'b0;
            end else begin
                aes_key_out <= 128'b0;
                access_fault <= 1'b1;
            end
        end
    end

endmodule
// Vulnerable: Software security identifier decoding

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

#define SECURITY_UNTRUSTED     0
#define SECURITY_LIMITED       1
#define SECURITY_TRUSTED       2
#define SECURITY_HIGH_TRUST    3

// VULNERABLE: Incomplete bit extraction
uint8_t vulnerable_decode_security_id(uint32_t transaction) {
    // VULNERABLE: Only checking bit 14, ignoring bit 15
    if (transaction & (1 << 14)) {
        return SECURITY_LIMITED;  // Wrong!
    }
    return SECURITY_UNTRUSTED;
}

// VULNERABLE: Access control using flawed decoder
bool vulnerable_check_access(uint32_t transaction, uint8_t resource_level) {
    uint8_t security_id = vulnerable_decode_security_id(transaction);

    // VULNERABLE: Allows bypass due to incorrect decoding
    return security_id >= resource_level;
}

// VULNERABLE: Mask is incomplete
#define SECURITY_ID_MASK_VULNERABLE  (1 << 14)  // Only one bit!

uint8_t vulnerable_extract_security_id(uint32_t transaction) {
    // VULNERABLE: Extracting only one bit of two-bit field
    return (transaction & SECURITY_ID_MASK_VULNERABLE) >> 14;
}

Fixed Code

// Fixed: Complete security identifier decoding

module secure_security_decoder (
    input  wire [31:0] bus_transaction,
    output reg  [1:0]  security_identifier,
    output reg         access_granted,
    output reg  [3:0]  access_level
);

    // Security identifier is in bits [15:14]
    // 00 = Untrusted (Master_0)
    // 01 = Limited trust (Master_1)
    // 10 = Trusted (Master_2)
    // 11 = Highly trusted (Master_3)

    // FIXED: Check complete 2-bit field
    always @(*) begin
        case (bus_transaction[15:14])
            2'b00: begin
                security_identifier = 2'b00;
                access_level = 4'b0001;  // Level 0: Public resources only
            end
            2'b01: begin
                security_identifier = 2'b01;
                access_level = 4'b0011;  // Level 1: Limited access
            end
            2'b10: begin
                security_identifier = 2'b10;
                access_level = 4'b0111;  // Level 2: Trusted access
            end
            2'b11: begin
                security_identifier = 2'b11;
                access_level = 4'b1111;  // Level 3: Full access
            end
            default: begin
                // FIXED: Safe default for undefined states
                security_identifier = 2'b00;
                access_level = 4'b0000;  // Deny all
            end
        endcase
    end

    // FIXED: Explicit access decision per level
    always @(*) begin
        access_granted = (security_identifier != 2'b00);
    end

endmodule

// FIXED: AES key register access with correct decoder
module secure_aes_key_access (
    input  wire        clk,
    input  wire        rst_n,
    input  wire [31:0] bus_transaction,
    input  wire [7:0]  address,
    input  wire        read_enable,
    output reg  [127:0] aes_key_out,
    output reg         access_fault
);

    reg [127:0] aes_key;
    reg [1:0]   decoded_security_id;

    // AES key register at address 0x80
    localparam AES_KEY_ADDR = 8'h80;

    // Required security level for AES key access
    localparam REQUIRED_LEVEL = 2'b10;  // Trusted or higher

    // FIXED: Complete decoder
    always @(*) begin
        decoded_security_id = bus_transaction[15:14];  // Full 2-bit field
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            aes_key_out <= 128'b0;
            access_fault <= 1'b0;
        end else if (read_enable && (address == AES_KEY_ADDR)) begin
            // FIXED: Proper security level check
            if (decoded_security_id >= REQUIRED_LEVEL) begin
                aes_key_out <= aes_key;
                access_fault <= 1'b0;
            end else begin
                aes_key_out <= 128'b0;
                access_fault <= 1'b1;
            end
        end
    end

endmodule

// FIXED: Parameterized decoder with validation
module secure_parameterized_decoder #(
    parameter SECURITY_BITS = 2,
    parameter FIELD_MSB = 15,
    parameter FIELD_LSB = 14
) (
    input  wire [31:0] bus_transaction,
    input  wire [SECURITY_BITS-1:0] required_level,
    output wire [SECURITY_BITS-1:0] security_id,
    output wire access_granted
);

    // FIXED: Extract complete field
    assign security_id = bus_transaction[FIELD_MSB:FIELD_LSB];

    // FIXED: Compare against required level
    assign access_granted = (security_id >= required_level);

    // Assertions for verification
    // synthesis translate_off
    initial begin
        // Verify field width matches parameter
        if ((FIELD_MSB - FIELD_LSB + 1) != SECURITY_BITS) begin
            $error("Field width mismatch!");
        end
    end
    // synthesis translate_on

endmodule
// Fixed: Complete software security identifier decoding

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

#define SECURITY_UNTRUSTED     0
#define SECURITY_LIMITED       1
#define SECURITY_TRUSTED       2
#define SECURITY_HIGH_TRUST    3

// FIXED: Complete bit mask for 2-bit field
#define SECURITY_ID_MASK       (0x3 << 14)  // Bits 15:14
#define SECURITY_ID_SHIFT      14

// FIXED: Extract complete security identifier
uint8_t secure_decode_security_id(uint32_t transaction) {
    // FIXED: Extract both bits of the security field
    return (transaction & SECURITY_ID_MASK) >> SECURITY_ID_SHIFT;
}

// FIXED: Access control with proper decoding
bool secure_check_access(uint32_t transaction, uint8_t required_level) {
    // FIXED: Validate required_level
    if (required_level > SECURITY_HIGH_TRUST) {
        return false;  // Invalid level, deny access
    }

    uint8_t security_id = secure_decode_security_id(transaction);

    // FIXED: Proper comparison
    return security_id >= required_level;
}

// FIXED: Decoder with validation
typedef struct {
    uint8_t security_level;
    bool valid;
    const char* description;
} decoded_security_t;

decoded_security_t secure_decode_with_validation(uint32_t transaction) {
    decoded_security_t result;

    // FIXED: Extract complete field
    uint8_t raw_id = (transaction & SECURITY_ID_MASK) >> SECURITY_ID_SHIFT;

    switch (raw_id) {
        case SECURITY_UNTRUSTED:
            result.security_level = SECURITY_UNTRUSTED;
            result.description = "Untrusted";
            result.valid = true;
            break;
        case SECURITY_LIMITED:
            result.security_level = SECURITY_LIMITED;
            result.description = "Limited Trust";
            result.valid = true;
            break;
        case SECURITY_TRUSTED:
            result.security_level = SECURITY_TRUSTED;
            result.description = "Trusted";
            result.valid = true;
            break;
        case SECURITY_HIGH_TRUST:
            result.security_level = SECURITY_HIGH_TRUST;
            result.description = "High Trust";
            result.valid = true;
            break;
        default:
            result.security_level = SECURITY_UNTRUSTED;
            result.description = "Unknown - Defaulting to Untrusted";
            result.valid = false;
            break;
    }

    return result;
}

// FIXED: Resource access with complete validation
typedef struct {
    uint32_t address;
    uint8_t required_level;
    const char* name;
} protected_resource_t;

bool secure_resource_access(uint32_t transaction, const protected_resource_t* resource) {
    if (resource == NULL) {
        return false;
    }

    decoded_security_t decoded = secure_decode_with_validation(transaction);

    // FIXED: Log access attempts for auditing
    log_access_attempt(resource->name, decoded.description,
                       decoded.security_level >= resource->required_level);

    // FIXED: Validate decoding was successful
    if (!decoded.valid) {
        return false;  // Deny on invalid security identifier
    }

    return decoded.security_level >= resource->required_level;
}

CVE Examples

  • CVE-2021-33101: Decoder in certain Intel processors incorrectly interpreted security identifiers, potentially allowing privilege escalation.

  • CWE-284: Improper Access Control (parent)
  • CWE-1294: Insecure Security Identifier Mechanism (parent)
  • CWE-1292: Incorrect Conversion of Security Identifiers (related)
  • CWE-863: Incorrect Authorization (related)

References

  1. MITRE Corporation. "CWE-1290: Incorrect Decoding of Security Identifiers." https://cwe.mitre.org/data/definitions/1290.html
  2. ARM. "AMBA Bus Protocol Specifications"
  3. CERT. "Hardware Security Guidelines"