Policy Uses Obsolete Encoding

Description

Policy Uses Obsolete Encoding occurs when a product implements access controls using an encoding mechanism that is no longer trusted or appropriate for current security requirements. In System-On-a-Chip (SoC) designs, hardware transactions typically include source identity, destination identity, and security tokens. A policy encoder maps these transactions to security tokens used for access control. The weakness occurs when an outdated encoding scheme is retained that fails to properly protect assets in updated systems where new, untrusted agents may exist.

Risk

Obsolete policy encoding has severe security implications. New agents may bypass access control. Protection mechanisms may be ineffective. Memory may be modified without authorization. Files may be tampered with. Denial of service may be possible. Unauthorized code may execute. Privilege escalation may occur. Legacy encoding becomes a backdoor.

Solution

Review security token decoders for design inconsistencies. Test access flows in pre-silicon and post-silicon testing phases. Update encoding schemes when system security requirements change. Remove support for obsolete encodings. Implement version checking for policy encodings. Audit all policy decoders during security reviews.

Common Consequences

ImpactDetails
ConfidentialityScope: Confidentiality

Read Memory - Obsolete encoding may allow unauthorized data access.
IntegrityScope: Integrity

Modify Memory - Outdated policies may permit unauthorized writes.
Access ControlScope: Access Control

Bypass Protection Mechanism - Legacy encoding may be exploited to bypass current protections.

Example Code

Vulnerable Code

// Vulnerable: Policy decoder with obsolete encoding

module vulnerable_policy_decoder (
    input wire clk,
    input wire reset_n,
    input wire [3:0] source_id,
    input wire [3:0] security_token,
    input wire [31:0] target_addr,
    input wire access_request,
    output reg access_granted
);

    // AES key storage address range
    parameter AES_KEY_START = 32'h1000_0000;
    parameter AES_KEY_END = 32'h1000_00FF;

    // VULNERABLE: Old "odd token" policy
    // This was designed when only trusted agents had odd tokens
    // But new untrusted agents have been added with odd tokens

    wire is_aes_key_access = (target_addr >= AES_KEY_START) &&
                             (target_addr <= AES_KEY_END);

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            access_granted <= 1'b0;
        end
        else if (access_request) begin
            if (is_aes_key_access) begin
                // VULNERABLE: Odd token = access granted
                // This encoding is obsolete and insecure
                access_granted <= security_token[0];  // Check if odd

                // Original design had:
                // Token 1 (odd): Trusted Crypto Engine
                // Token 3 (odd): Secure Boot
                //
                // New design added:
                // Token 5 (odd): Untrusted Debug Agent
                // Token 7 (odd): Untrusted Test Agent
                //
                // These untrusted agents now have AES key access!
            end
            else begin
                access_granted <= 1'b1;  // Non-key access allowed
            end
        end
    end

endmodule

// Vulnerable: Legacy policy with magic numbers
module vulnerable_legacy_policy (
    input wire clk,
    input wire reset_n,
    input wire [7:0] agent_id,
    input wire [31:0] resource_addr,
    input wire read_request,
    input wire write_request,
    output reg access_allowed
);

    // VULNERABLE: Hardcoded agent IDs from old design
    // These were secure when designed, but system has changed

    parameter SECURE_AGENT = 8'h01;  // Original secure agent
    parameter CRYPTO_AGENT = 8'h02;  // Original crypto engine
    // ... later agents added without updating this list

    // Protected resource
    parameter SECRET_STORAGE = 32'h2000_0000;

    always @(*) begin
        access_allowed = 1'b0;

        if (resource_addr == SECRET_STORAGE) begin
            // VULNERABLE: Only checks against old agent IDs
            // New secure agents (0x10, 0x11) are denied
            // But old test agent (0x01) still has access
            if (agent_id == SECURE_AGENT || agent_id == CRYPTO_AGENT) begin
                access_allowed = 1'b1;
            end
        end
        else begin
            access_allowed = 1'b1;
        end

        // Problem: SECURE_AGENT (0x01) is now assigned to untrusted debug
        // Original secure functionality moved to agent 0x10
        // Policy still grants 0x01 access!
    end

endmodule
// Vulnerable: Software with obsolete permission encoding

#include <stdint.h>

#define PERMISSION_READ  0x01
#define PERMISSION_WRITE 0x02
#define PERMISSION_EXEC  0x04

// VULNERABLE: Old encoding where high bit meant "trusted"
// This is no longer reliable in current system

typedef struct {
    uint8_t agent_id;
    uint8_t permissions;
} access_entry_t;

// Old policy table
access_entry_t legacy_policy[] = {
    {0x01, PERMISSION_READ | PERMISSION_WRITE},  // Was secure boot
    {0x02, PERMISSION_READ},                      // Was user agent
    {0x81, PERMISSION_READ | PERMISSION_WRITE | PERMISSION_EXEC},  // Trusted (high bit)
    {0x82, PERMISSION_READ | PERMISSION_WRITE | PERMISSION_EXEC},  // Trusted (high bit)
};

bool vulnerable_check_access(uint8_t agent_id, uint8_t required_perm) {
    // VULNERABLE: Trust high bit encoding
    if (agent_id & 0x80) {
        // High bit set = trusted, grant all access
        return true;
    }

    // Check policy table
    for (int i = 0; i < sizeof(legacy_policy)/sizeof(legacy_policy[0]); i++) {
        if (legacy_policy[i].agent_id == agent_id) {
            return (legacy_policy[i].permissions & required_perm) == required_perm;
        }
    }

    return false;

    // Problem: Attacker can set high bit on their agent ID
    // to bypass all policy checks
}

Fixed Code

// Fixed: Policy decoder with explicit access control

module secure_policy_decoder (
    input wire clk,
    input wire reset_n,
    input wire [3:0] source_id,
    input wire [3:0] security_token,
    input wire [31:0] target_addr,
    input wire access_request,
    output reg access_granted,
    output reg policy_violation
);

    // AES key storage address range
    parameter AES_KEY_START = 32'h1000_0000;
    parameter AES_KEY_END = 32'h1000_00FF;

    // FIXED: Explicit policy register instead of encoding tricks
    reg [15:0] aes_key_access_policy;  // Bit per token: 1=allowed

    // Initialize with explicit allowed tokens
    initial begin
        aes_key_access_policy = 16'b0000_0000_0000_0110;
        // Token 1 (Crypto Engine): allowed
        // Token 2 (Secure Boot): allowed
        // All other tokens: denied
    end

    wire is_aes_key_access = (target_addr >= AES_KEY_START) &&
                             (target_addr <= AES_KEY_END);

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            access_granted <= 1'b0;
            policy_violation <= 1'b0;
        end
        else if (access_request) begin
            policy_violation <= 1'b0;

            if (is_aes_key_access) begin
                // FIXED: Check explicit policy bit for this token
                if (aes_key_access_policy[security_token]) begin
                    access_granted <= 1'b1;
                end
                else begin
                    access_granted <= 1'b0;
                    policy_violation <= 1'b1;
                end
            end
            else begin
                access_granted <= 1'b1;
            end
        end
    end

    // FIXED: Secure policy update (requires secure privilege)
    input wire policy_update_enable,
    input wire secure_master,
    input wire [15:0] new_policy

    always @(posedge clk) begin
        if (policy_update_enable && secure_master) begin
            aes_key_access_policy <= new_policy;
        end
    end

endmodule

// Fixed: Modern policy with named agents
module secure_named_policy (
    input wire clk,
    input wire reset_n,
    input wire [7:0] agent_id,
    input wire [31:0] resource_addr,
    input wire read_request,
    input wire write_request,
    output reg access_allowed,
    output reg access_denied_log
);

    // FIXED: Explicit agent definitions with current assignments
    parameter AGENT_SECURE_BOOT = 8'h10;      // Current secure boot
    parameter AGENT_CRYPTO_ENGINE = 8'h11;    // Current crypto
    parameter AGENT_DEBUG = 8'h01;            // Debug (untrusted)
    parameter AGENT_TEST = 8'h02;             // Test (untrusted)

    // Protected resource
    parameter SECRET_STORAGE_START = 32'h2000_0000;
    parameter SECRET_STORAGE_END = 32'h2000_0FFF;

    // FIXED: Access control list
    reg [7:0] allowed_agents [0:7];
    reg [2:0] num_allowed_agents;

    initial begin
        // Explicitly list allowed agents
        allowed_agents[0] = AGENT_SECURE_BOOT;
        allowed_agents[1] = AGENT_CRYPTO_ENGINE;
        num_allowed_agents = 2;
    end

    // Check if agent is in allowed list
    function automatic is_agent_allowed;
        input [7:0] agent;
        integer i;
        begin
            is_agent_allowed = 1'b0;
            for (i = 0; i < num_allowed_agents; i = i + 1) begin
                if (allowed_agents[i] == agent) begin
                    is_agent_allowed = 1'b1;
                end
            end
        end
    endfunction

    wire is_secret_access = (resource_addr >= SECRET_STORAGE_START) &&
                            (resource_addr <= SECRET_STORAGE_END);

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            access_allowed <= 1'b0;
            access_denied_log <= 1'b0;
        end
        else begin
            access_denied_log <= 1'b0;

            if (is_secret_access) begin
                // FIXED: Check against explicit allowed list
                if (is_agent_allowed(agent_id)) begin
                    access_allowed <= 1'b1;
                end
                else begin
                    access_allowed <= 1'b0;
                    access_denied_log <= 1'b1;  // Log for audit
                end
            end
            else begin
                access_allowed <= 1'b1;
            end
        end
    end

endmodule
// Fixed: Software with modern permission system

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

// FIXED: Explicit permission definitions
typedef struct {
    uint8_t agent_id;
    uint8_t permissions;
    bool is_trusted;  // Explicit trust flag
    const char* agent_name;  // For audit logging
} secure_access_entry_t;

// FIXED: Modern policy table with explicit trust
static const secure_access_entry_t secure_policy[] = {
    {0x10, PERMISSION_READ | PERMISSION_WRITE | PERMISSION_EXEC, true, "Secure Boot"},
    {0x11, PERMISSION_READ | PERMISSION_WRITE | PERMISSION_EXEC, true, "Crypto Engine"},
    {0x20, PERMISSION_READ, false, "User Application"},
    {0x01, 0, false, "Debug Agent"},  // FIXED: Explicitly no permissions
    {0x02, 0, false, "Test Agent"},   // FIXED: Explicitly no permissions
};

bool secure_check_access(uint8_t agent_id, uint8_t required_perm) {
    // FIXED: No encoding tricks - check explicit policy table

    for (size_t i = 0; i < sizeof(secure_policy)/sizeof(secure_policy[0]); i++) {
        if (secure_policy[i].agent_id == agent_id) {
            // Found agent in policy

            // Check if required permissions are granted
            if ((secure_policy[i].permissions & required_perm) == required_perm) {
                log_access_granted(secure_policy[i].agent_name, required_perm);
                return true;
            }
            else {
                log_access_denied(secure_policy[i].agent_name, required_perm);
                return false;
            }
        }
    }

    // FIXED: Unknown agent = deny
    log_unknown_agent(agent_id, required_perm);
    return false;
}

// FIXED: Separate function for trust level check
bool is_agent_trusted(uint8_t agent_id) {
    for (size_t i = 0; i < sizeof(secure_policy)/sizeof(secure_policy[0]); i++) {
        if (secure_policy[i].agent_id == agent_id) {
            return secure_policy[i].is_trusted;
        }
    }
    return false;  // Unknown = untrusted
}

// FIXED: Policy version checking
#define POLICY_VERSION 2

bool validate_policy_version(void) {
    uint32_t hw_policy_version = read_hw_policy_version();

    if (hw_policy_version < POLICY_VERSION) {
        log_error("Hardware policy version %d is obsolete (need %d)",
                  hw_policy_version, POLICY_VERSION);
        return false;
    }

    return true;
}

CVE Examples

Obsolete encoding vulnerabilities have been found in various SoC designs where legacy access control mechanisms were exploited by newer untrusted agents that inadvertently matched old trusted encoding patterns.


  • CWE-284: Improper Access Control (parent)
  • CWE-1259: Improper Restriction of Security Token Assignment (related)
  • CWE-1270: Generation of Incorrect Security Tokens (related)

References

  1. MITRE Corporation. "CWE-1267: Policy Uses Obsolete Encoding." https://cwe.mitre.org/data/definitions/1267.html
  2. ARM. "AMBA Security Extensions"
  3. RISC-V. "Physical Memory Protection (PMP)"