Improper Zeroization of Hardware Register
Description
Improper Zeroization of Hardware Register occurs when a hardware product does not properly clear sensitive information from built-in registers when the user of the hardware block changes. Hardware IPs store data in local registers for I/O buffering and intermediate computations. Sensitive data like encryption keys or passwords can persist in these registers. When hardware access changes hands, new users may extract previous sensitive information. Standards like FIPS-140-2 emphasize "zeroization"—the clearing of register contents in cryptographic hardware.
Risk
Improper register zeroization has severe security implications. Cryptographic keys may be exposed to unauthorized users. Passwords may be extracted from registers. Intermediate computation values may leak secrets. Previous user data may be accessible to new users. Security boundaries between users may be violated. Cryptographic hardware may fail security certification. Data remanence may persist even after clearing. Side-channel attacks may exploit residual data.
Solution
Define clear policies for register clearing procedures during architecture and design phases. Specify responsibility (hardware or software) for initiating zeroization. Clear sensitive registers immediately after use. Implement automatic zeroization on context switches. Use secure clearing patterns that overwrite multiple times if needed. Verify zeroization through testing. Consider data remanence characteristics of memory technology. Comply with FIPS-140-2/3 zeroization requirements for cryptographic modules.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality Read Application Data - Information disclosure severity depends on what sensitive data is exposed through retained register values. Cryptographic keys and passwords are high-value targets. |
Example Code
Vulnerable Code
// Vulnerable: SHA256 wrapper leaves data registers uncleared
module vulnerable_sha256_wrapper (
input wire clk,
input wire reset_n,
input wire [255:0] data_in,
input wire data_valid,
input wire [31:0] axi_addr,
input wire axi_read,
output reg [255:0] hash_out,
output reg hash_valid,
output reg [31:0] axi_rdata
);
reg [255:0] input_data_reg;
reg [255:0] intermediate_hash;
reg hash_complete;
// SHA256 computation
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
input_data_reg <= 256'h0;
intermediate_hash <= 256'h0;
hash_out <= 256'h0;
hash_valid <= 1'b0;
end
else if (data_valid) begin
// Store input data for processing
input_data_reg <= data_in;
// Compute hash (simplified)
intermediate_hash <= compute_sha256(data_in);
hash_out <= intermediate_hash;
hash_valid <= 1'b1;
end
// VULNERABLE: Data remains in registers after hash completion
// No clearing of input_data_reg or intermediate_hash
end
// AXI interface for reading results
always @(posedge clk) begin
if (axi_read) begin
case (axi_addr)
32'h0000: axi_rdata <= hash_out[31:0];
32'h0004: axi_rdata <= hash_out[63:32];
// ...
// VULNERABLE: Can read back input data!
32'h0020: axi_rdata <= input_data_reg[31:0];
32'h0024: axi_rdata <= input_data_reg[63:32];
// Previous user's sensitive data exposed!
endcase
end
end
endmodule
// Vulnerable: Crypto key stored in register not cleared
module vulnerable_aes_key_register (
input wire clk,
input wire reset_n,
input wire [127:0] key_in,
input wire key_load,
input wire [127:0] plaintext,
input wire encrypt_start,
output reg [127:0] ciphertext,
output reg encrypt_done
);
reg [127:0] key_register;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
key_register <= 128'h0;
ciphertext <= 128'h0;
encrypt_done <= 1'b0;
end
else begin
if (key_load) begin
key_register <= key_in;
end
if (encrypt_start) begin
ciphertext <= aes_encrypt(plaintext, key_register);
encrypt_done <= 1'b1;
end
// VULNERABLE: Key persists in register
// New user of crypto block can potentially extract key
end
end
endmodule
// Vulnerable: Software doesn't zeroize hardware registers
void vulnerable_crypto_operation(uint8_t* key, uint8_t* data, size_t len) {
// Load key into hardware crypto accelerator
crypto_hw_load_key(key);
// Perform encryption
crypto_hw_encrypt(data, len);
// VULNERABLE: Key left in hardware registers
// Next user of crypto hardware could extract it
}
void vulnerable_password_hash(const char* password) {
// Load password into hash accelerator
hash_hw_load_data(password, strlen(password));
// Compute hash
hash_hw_compute();
// VULNERABLE: Password data left in hash accelerator registers
}
Fixed Code
// Fixed: SHA256 wrapper with proper zeroization
module secure_sha256_wrapper (
input wire clk,
input wire reset_n,
input wire [255:0] data_in,
input wire data_valid,
input wire clear_registers, // Explicit clear signal
input wire [31:0] axi_addr,
input wire axi_read,
output reg [255:0] hash_out,
output reg hash_valid,
output reg [31:0] axi_rdata,
output reg registers_cleared
);
reg [255:0] input_data_reg;
reg [255:0] intermediate_hash;
reg hash_complete;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
input_data_reg <= 256'h0;
intermediate_hash <= 256'h0;
hash_out <= 256'h0;
hash_valid <= 1'b0;
registers_cleared <= 1'b1;
end
else if (clear_registers) begin
// FIXED: Explicit zeroization of all sensitive registers
input_data_reg <= 256'h0;
intermediate_hash <= 256'h0;
hash_out <= 256'h0;
hash_valid <= 1'b0;
registers_cleared <= 1'b1;
end
else if (data_valid) begin
input_data_reg <= data_in;
intermediate_hash <= compute_sha256(data_in);
hash_out <= intermediate_hash;
hash_valid <= 1'b1;
registers_cleared <= 1'b0;
end
end
// FIXED: Auto-clear input data after hash completion
always @(posedge clk) begin
if (hash_valid && !clear_registers) begin
// Automatically clear input data after hash is ready
input_data_reg <= 256'h0;
end
end
// AXI interface - input data not exposed
always @(posedge clk) begin
if (axi_read) begin
case (axi_addr)
32'h0000: axi_rdata <= hash_out[31:0];
32'h0004: axi_rdata <= hash_out[63:32];
// Input data registers NOT readable via AXI
default: axi_rdata <= 32'h0;
endcase
end
end
endmodule
// Fixed: Crypto key register with automatic zeroization
module secure_aes_key_register (
input wire clk,
input wire reset_n,
input wire [127:0] key_in,
input wire key_load,
input wire key_clear,
input wire [127:0] plaintext,
input wire encrypt_start,
input wire context_switch, // User/context change signal
output reg [127:0] ciphertext,
output reg encrypt_done,
output reg key_cleared
);
reg [127:0] key_register;
reg key_valid;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
key_register <= 128'h0;
ciphertext <= 128'h0;
encrypt_done <= 1'b0;
key_valid <= 1'b0;
key_cleared <= 1'b1;
end
else if (key_clear || context_switch) begin
// FIXED: Clear key on explicit request or context switch
key_register <= 128'h0;
key_valid <= 1'b0;
key_cleared <= 1'b1;
end
else begin
if (key_load) begin
key_register <= key_in;
key_valid <= 1'b1;
key_cleared <= 1'b0;
end
if (encrypt_start && key_valid) begin
ciphertext <= aes_encrypt(plaintext, key_register);
encrypt_done <= 1'b1;
end
end
end
endmodule
// Fixed: Comprehensive zeroization controller
module zeroization_controller (
input wire clk,
input wire reset_n,
input wire zeroize_request,
input wire context_change,
input wire security_violation,
output reg zeroize_active,
output reg [31:0] zeroize_address,
output reg zeroize_write,
output reg [255:0] zeroize_data,
output reg zeroization_complete
);
// State machine for zeroization
localparam IDLE = 2'b00;
localparam CLEARING = 2'b01;
localparam VERIFY = 2'b10;
localparam DONE = 2'b11;
reg [1:0] state;
reg [31:0] clear_count;
parameter TOTAL_REGISTERS = 32;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
state <= IDLE;
zeroize_active <= 1'b0;
zeroization_complete <= 1'b0;
end
else begin
case (state)
IDLE: begin
if (zeroize_request || context_change || security_violation) begin
state <= CLEARING;
zeroize_active <= 1'b1;
clear_count <= 0;
zeroization_complete <= 1'b0;
end
end
CLEARING: begin
// Write zeros to all sensitive registers
zeroize_address <= clear_count * 4;
zeroize_data <= 256'h0;
zeroize_write <= 1'b1;
if (clear_count >= TOTAL_REGISTERS - 1) begin
state <= VERIFY;
clear_count <= 0;
end else begin
clear_count <= clear_count + 1;
end
end
VERIFY: begin
// Verify registers are cleared
zeroize_write <= 1'b0;
// Verification logic
if (clear_count >= TOTAL_REGISTERS - 1) begin
state <= DONE;
end else begin
clear_count <= clear_count + 1;
end
end
DONE: begin
zeroize_active <= 1'b0;
zeroization_complete <= 1'b1;
state <= IDLE;
end
endcase
end
end
endmodule
// Fixed: Software with proper hardware zeroization
void secure_crypto_operation(uint8_t* key, uint8_t* data, size_t len) {
// Load key into hardware crypto accelerator
crypto_hw_load_key(key);
// Perform encryption
crypto_hw_encrypt(data, len);
// FIXED: Clear key from hardware after use
crypto_hw_zeroize_key();
// Verify key was cleared
if (!crypto_hw_verify_key_cleared()) {
panic("Key zeroization failed!");
}
}
void secure_password_hash(const char* password, uint8_t* hash_out) {
// Load password into hash accelerator
hash_hw_load_data(password, strlen(password));
// Compute hash
hash_hw_compute();
// Read result
hash_hw_read_result(hash_out);
// FIXED: Clear all input data from hardware
hash_hw_zeroize_input();
// Verify
if (!hash_hw_verify_cleared()) {
panic("Hash input zeroization failed!");
}
}
// Context switch handler
void context_switch_handler(void) {
// Before switching to new user/process, clear all crypto state
crypto_hw_zeroize_all();
hash_hw_zeroize_all();
rng_hw_clear_state();
// Verify all cleared
if (!verify_all_crypto_hw_cleared()) {
// Security violation - halt
security_violation_handler();
}
}
// FIPS-140-2 compliant zeroization
void fips_zeroize_crypto_module(void) {
// Multiple overwrite passes for data remanence protection
for (int pass = 0; pass < 3; pass++) {
// Pass 1: All zeros
crypto_hw_overwrite_all(0x00);
// Pass 2: All ones
crypto_hw_overwrite_all(0xFF);
// Pass 3: Random pattern
crypto_hw_overwrite_all(get_random_pattern());
}
// Final zeros
crypto_hw_overwrite_all(0x00);
// Verify
if (!verify_zeroization_complete()) {
log_security_event("Zeroization verification failed");
}
}
CVE Examples
Register zeroization failures have been found in cryptographic hardware accelerators where sensitive keys or data remained accessible after operations completed.
Related CWEs
- CWE-226: Sensitive Information in Resource Not Removed Before Reuse (parent)
- CWE-212: Improper Removal of Sensitive Information Before Storage or Transfer (related)
- CWE-1258: Exposure of Sensitive System Information Due to Uncleared Debug Information (related)
References
- MITRE Corporation. "CWE-1239: Improper Zeroization of Hardware Register." https://cwe.mitre.org/data/definitions/1239.html
- FIPS PUB 140-2: Security Requirements for Cryptographic Modules
- Gutmann, P. "Data Remanence in Semiconductor Devices" (USENIX Security 2001)
- HACK@DAC'21 OpenPiton SoC Examples