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
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality Read Memory - Obsolete encoding may allow unauthorized data access. |
| Integrity | Scope: Integrity Modify Memory - Outdated policies may permit unauthorized writes. |
| Access Control | Scope: 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.
Related CWEs
- CWE-284: Improper Access Control (parent)
- CWE-1259: Improper Restriction of Security Token Assignment (related)
- CWE-1270: Generation of Incorrect Security Tokens (related)
References
- MITRE Corporation. "CWE-1267: Policy Uses Obsolete Encoding." https://cwe.mitre.org/data/definitions/1267.html
- ARM. "AMBA Security Extensions"
- RISC-V. "Physical Memory Protection (PMP)"