Incorrect Conversion of Security Identifiers

Description

Incorrect Conversion of Security Identifiers occurs when a product's bus-transaction signal to security identifier mapping contains conversion errors, potentially allowing untrusted agents unauthorized asset access. In System-on-Chip (SoC) environments, transactions between integrated circuits carry source and destination identities along with security identifiers that enable destination agents to enforce access controls. Protocol bridges connecting incompatible bus types (e.g., AHB-to-OCP, APB-to-AXI) must correctly translate security identifier information between protocols. When these bridges incorrectly convert security identifiers, untrusted agents may bypass access restrictions.

Risk

Incorrect security identifier conversion has severe implications. Untrusted agents bypass access controls. Memory modification possible. Unauthorized reads enabled. Denial of service attacks. Execution of unauthorized code. Privilege escalation. Identity assumption. Complete bypass of access control policies. High likelihood of exploitation when protocol bridges have conversion flaws.

Solution

Security identifier converters/bridges require design review for inconsistency and common weaknesses during architecture and design phase. Ensure protocol mappings preserve security semantics. Access and programming flows must be tested in pre-silicon and post-silicon testing. Create comprehensive test vectors covering all security identifier values across protocol boundaries. Use formal verification to prove conversion correctness. Document security identifier mappings explicitly.

Common Consequences

ImpactDetails
ConfidentialityScope: Confidentiality

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

Memory modification by untrusted agents through protocol bridge.
AvailabilityScope: Availability

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

Privilege escalation and identity assumption via incorrect mapping.

Example Code

Vulnerable Code

// Vulnerable: AHB to OCP bridge with incorrect security ID conversion

module vulnerable_ahb_to_ocp_bridge (
    input  wire        clk,
    input  wire        rst_n,

    // AHB signals
    input  wire [31:0] ahb_haddr,
    input  wire [2:0]  ahb_hburst,
    input  wire [3:0]  ahb_hprot,      // AHB protection signals
    input  wire        ahb_hwrite,
    input  wire [31:0] ahb_hwdata,
    input  wire [1:0]  ahb_htrans,

    // OCP signals
    output reg  [31:0] ocp_maddr,
    output reg  [2:0]  ocp_mburstlength,
    output reg  [1:0]  ocp_mcmd,
    output reg  [31:0] ocp_mdata,
    output reg  [7:0]  ocp_mtagid,     // OCP security tag
    output reg         ocp_mreqinfo    // OCP security info
);

    // AHB HPROT bits:
    // [0] = Data/Opcode (1=data, 0=opcode)
    // [1] = Privileged (1=privileged, 0=user)
    // [2] = Bufferable
    // [3] = Cacheable

    // OCP security model (example):
    // mtagid[7:6] = security level (00=untrusted, 01=user, 10=priv, 11=secure)
    // mtagid[5:0] = master ID

    // VULNERABLE: Incorrect conversion of security identifiers
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            ocp_maddr <= 32'b0;
            ocp_mtagid <= 8'b0;
        end else begin
            ocp_maddr <= ahb_haddr;

            // VULNERABLE: Only checking bit [1] of hprot
            // Missing bit [3] which indicates secure access in some implementations
            if (ahb_hprot[1]) begin
                ocp_mtagid[7:6] <= 2'b10;  // Privileged
            end else begin
                ocp_mtagid[7:6] <= 2'b01;  // User
            end

            // VULNERABLE: Master ID not properly converted
            // ahb doesn't have explicit master ID, but OCP requires it
            ocp_mtagid[5:0] <= 6'b000000;  // All masters mapped to ID 0!
        end
    end

    // VULNERABLE: Security info bit not properly set
    always @(*) begin
        // Should indicate secure/non-secure based on bus master
        ocp_mreqinfo = 1'b0;  // Always indicates non-secure!
    end

endmodule

// Vulnerable: APB to AXI bridge with security mapping issues
module vulnerable_apb_to_axi_bridge (
    input  wire        clk,
    input  wire        rst_n,

    // APB signals
    input  wire [31:0] paddr,
    input  wire        pwrite,
    input  wire [31:0] pwdata,
    input  wire        psel,
    input  wire        penable,
    input  wire [2:0]  pprot,          // APB protection

    // AXI signals
    output reg  [31:0] axi_awaddr,
    output reg  [2:0]  axi_awprot,     // AXI protection
    output reg  [3:0]  axi_awcache,
    output reg  [31:0] axi_wdata,
    output reg         axi_awvalid
);

    // APB PPROT:
    // [0] = Normal/Privileged
    // [1] = Secure/Non-secure
    // [2] = Data/Instruction

    // AXI AWPROT:
    // [0] = Privileged
    // [1] = Non-secure
    // [2] = Instruction

    // VULNERABLE: Incorrect bit mapping between protocols
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            axi_awprot <= 3'b0;
        end else begin
            // VULNERABLE: Bits are in different positions!
            // APB pprot[0] is privilege, but just copying directly
            axi_awprot <= pprot;  // WRONG! Bit positions don't match!

            // Correct mapping should be:
            // axi_awprot[0] = pprot[0]  (privilege matches)
            // axi_awprot[1] = ~pprot[1] (APB secure=0, AXI non-secure=0, INVERTED!)
            // axi_awprot[2] = pprot[2]  (data/instruction matches)
        end
    end

endmodule
// Vulnerable: Software bridge with security ID conversion issues

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

// Protocol A security levels
#define PROTO_A_UNTRUSTED  0x00
#define PROTO_A_USER       0x01
#define PROTO_A_SUPERVISOR 0x02
#define PROTO_A_SECURE     0x03

// Protocol B security levels (different encoding)
#define PROTO_B_PUBLIC     0x00
#define PROTO_B_NORMAL     0x04
#define PROTO_B_PRIVILEGED 0x08
#define PROTO_B_HYPERVISOR 0x0C

// VULNERABLE: Incorrect mapping table
uint8_t vulnerable_convert_security_id(uint8_t proto_a_id) {
    // VULNERABLE: Direct value copy - encodings don't match!
    return proto_a_id;  // Values mean different things!

    // PROTO_A_USER (0x01) becomes PROTO_B_PUBLIC (0x00 + something)
    // Security semantics are lost!
}

// VULNERABLE: Bridge request structure
typedef struct {
    uint32_t address;
    uint32_t data;
    uint8_t  security_id;
    bool     is_write;
} bridge_request_t;

void vulnerable_forward_request(bridge_request_t* req) {
    // VULNERABLE: Security ID not properly converted
    uint8_t proto_b_security = vulnerable_convert_security_id(req->security_id);

    // Forward to protocol B endpoint
    send_proto_b_request(req->address, req->data, proto_b_security, req->is_write);
}

Fixed Code

// Fixed: AHB to OCP bridge with correct security ID conversion

module secure_ahb_to_ocp_bridge (
    input  wire        clk,
    input  wire        rst_n,

    // AHB signals
    input  wire [31:0] ahb_haddr,
    input  wire [2:0]  ahb_hburst,
    input  wire [3:0]  ahb_hprot,
    input  wire        ahb_hwrite,
    input  wire [31:0] ahb_hwdata,
    input  wire [1:0]  ahb_htrans,
    input  wire [3:0]  ahb_hmaster,    // AHB master ID (implementation specific)
    input  wire        ahb_hnonsec,    // AHB non-secure indicator

    // OCP signals
    output reg  [31:0] ocp_maddr,
    output reg  [2:0]  ocp_mburstlength,
    output reg  [1:0]  ocp_mcmd,
    output reg  [31:0] ocp_mdata,
    output reg  [7:0]  ocp_mtagid,
    output reg         ocp_mreqinfo
);

    // FIXED: Complete security mapping with all relevant signals
    reg [1:0] security_level;

    always @(*) begin
        // FIXED: Comprehensive security level determination
        if (!ahb_hnonsec) begin
            // Secure access
            if (ahb_hprot[1]) begin
                security_level = 2'b11;  // Secure privileged
            end else begin
                security_level = 2'b10;  // Secure user
            end
        end else begin
            // Non-secure access
            if (ahb_hprot[1]) begin
                security_level = 2'b01;  // Non-secure privileged
            end else begin
                security_level = 2'b00;  // Non-secure user (untrusted)
            end
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            ocp_maddr <= 32'b0;
            ocp_mtagid <= 8'b0;
            ocp_mreqinfo <= 1'b0;
        end else begin
            ocp_maddr <= ahb_haddr;

            // FIXED: Proper security level conversion
            ocp_mtagid[7:6] <= security_level;

            // FIXED: Master ID properly mapped
            ocp_mtagid[5:2] <= ahb_hmaster;
            ocp_mtagid[1:0] <= 2'b00;  // Reserved

            // FIXED: Security info properly indicates secure/non-secure
            ocp_mreqinfo <= ahb_hnonsec;
        end
    end

    // FIXED: Assertions to verify conversion
    // synthesis translate_off
    always @(posedge clk) begin
        if (!rst_n) begin
            // Reset
        end else begin
            // Verify secure access maps correctly
            if (!ahb_hnonsec && ahb_hprot[1]) begin
                assert(ocp_mtagid[7:6] == 2'b11)
                    else $error("Secure privileged mapping failed");
            end
        end
    end
    // synthesis translate_on

endmodule

// Fixed: APB to AXI bridge with correct protection bit mapping
module secure_apb_to_axi_bridge (
    input  wire        clk,
    input  wire        rst_n,

    // APB signals
    input  wire [31:0] paddr,
    input  wire        pwrite,
    input  wire [31:0] pwdata,
    input  wire        psel,
    input  wire        penable,
    input  wire [2:0]  pprot,

    // AXI signals
    output reg  [31:0] axi_awaddr,
    output reg  [2:0]  axi_awprot,
    output reg  [3:0]  axi_awcache,
    output reg  [31:0] axi_wdata,
    output reg         axi_awvalid
);

    // APB PPROT:
    // [0] = Normal(0)/Privileged(1)
    // [1] = Secure(0)/Non-secure(1)
    // [2] = Data(0)/Instruction(1)

    // AXI AWPROT:
    // [0] = Unprivileged(0)/Privileged(1)
    // [1] = Secure(0)/Non-secure(1)
    // [2] = Data(0)/Instruction(1)

    // FIXED: Explicit bit-by-bit mapping with documentation
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            axi_awprot <= 3'b000;  // Safe default: unprivileged, secure, data
        end else begin
            // FIXED: Correct mapping - bits happen to align in this case
            // but we explicitly map each one for clarity and maintainability

            // Privilege bit: both protocols use same encoding
            axi_awprot[0] <= pprot[0];

            // Security bit: both protocols use same encoding (1=non-secure)
            axi_awprot[1] <= pprot[1];

            // Data/Instruction bit: both protocols use same encoding
            axi_awprot[2] <= pprot[2];
        end
    end

    // FIXED: Validation logic
    // synthesis translate_off
    always @(posedge clk) begin
        // Verify privileged access preserved
        if (pprot[0]) begin
            assert(axi_awprot[0]) else $error("Privilege not preserved");
        end

        // Verify secure/non-secure preserved
        assert(axi_awprot[1] == pprot[1]) else $error("Security not preserved");
    end
    // synthesis translate_on

endmodule
// Fixed: Software bridge with correct security ID conversion

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

// Protocol A security levels
typedef enum {
    PROTO_A_UNTRUSTED  = 0x00,
    PROTO_A_USER       = 0x01,
    PROTO_A_SUPERVISOR = 0x02,
    PROTO_A_SECURE     = 0x03
} proto_a_security_t;

// Protocol B security levels (different encoding)
typedef enum {
    PROTO_B_PUBLIC     = 0x00,
    PROTO_B_NORMAL     = 0x04,
    PROTO_B_PRIVILEGED = 0x08,
    PROTO_B_HYPERVISOR = 0x0C
} proto_b_security_t;

// FIXED: Explicit conversion table
static const proto_b_security_t security_conversion_table[] = {
    [PROTO_A_UNTRUSTED]  = PROTO_B_PUBLIC,      // Lowest maps to lowest
    [PROTO_A_USER]       = PROTO_B_NORMAL,      // User maps to normal
    [PROTO_A_SUPERVISOR] = PROTO_B_PRIVILEGED,  // Supervisor maps to privileged
    [PROTO_A_SECURE]     = PROTO_B_HYPERVISOR   // Highest maps to highest
};

// FIXED: Validated conversion function
proto_b_security_t secure_convert_security_id(proto_a_security_t proto_a_id) {
    // FIXED: Validate input
    if (proto_a_id > PROTO_A_SECURE) {
        // Invalid input - return most restrictive level
        log_security_error("Invalid Protocol A security ID: %d", proto_a_id);
        return PROTO_B_PUBLIC;  // Fail safe
    }

    // FIXED: Use explicit mapping table
    return security_conversion_table[proto_a_id];
}

// FIXED: Bridge with validated conversion
typedef struct {
    uint32_t address;
    uint32_t data;
    proto_a_security_t security_id;
    bool is_write;
} bridge_request_t;

bool secure_forward_request(const bridge_request_t* req) {
    if (req == NULL) {
        return false;
    }

    // FIXED: Convert security ID with validation
    proto_b_security_t proto_b_security = secure_convert_security_id(req->security_id);

    // FIXED: Log conversion for audit trail
    audit_log("Security conversion: ProtoA=%d -> ProtoB=%d",
              req->security_id, proto_b_security);

    // Forward to protocol B endpoint
    return send_proto_b_request(req->address, req->data,
                                proto_b_security, req->is_write);
}

// FIXED: Reverse conversion for responses
proto_a_security_t secure_convert_security_id_reverse(proto_b_security_t proto_b_id) {
    switch (proto_b_id) {
        case PROTO_B_PUBLIC:     return PROTO_A_UNTRUSTED;
        case PROTO_B_NORMAL:     return PROTO_A_USER;
        case PROTO_B_PRIVILEGED: return PROTO_A_SUPERVISOR;
        case PROTO_B_HYPERVISOR: return PROTO_A_SECURE;
        default:
            log_security_error("Invalid Protocol B security ID: %d", proto_b_id);
            return PROTO_A_UNTRUSTED;  // Fail safe
    }
}

// FIXED: Bidirectional bridge verification
void verify_security_mapping(void) {
    // Verify round-trip conversion preserves security level
    for (int i = PROTO_A_UNTRUSTED; i <= PROTO_A_SECURE; i++) {
        proto_b_security_t b = secure_convert_security_id(i);
        proto_a_security_t a_back = secure_convert_security_id_reverse(b);
        assert(a_back == i && "Security mapping is not bijective!");
    }
}

CVE Examples

  • CVE-2020-24512: Protocol bridge in certain Intel processors incorrectly mapped security attributes during bus transactions.
  • CVE-2019-11091: Security identifier conversion error in microarchitectural data sampling.

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

References

  1. MITRE Corporation. "CWE-1292: Incorrect Conversion of Security Identifiers." https://cwe.mitre.org/data/definitions/1292.html
  2. ARM. "AMBA Protocol Specifications"
  3. OCP-IP. "Open Core Protocol Specification"