Cryptographic Operations are run Before Supporting Units are Ready
Description
Cryptographic Operations are run Before Supporting Units are Ready occurs when performing cryptographic operations without ensuring that the supporting inputs are ready to supply valid data may compromise the cryptographic result. Cryptographic hardware units depend on other hardware components to function securely. Examples include random-number generators (RNG) providing entropy and fuse units supplying encryption keys. Operating crypto units before these dependencies are ready produces insecure results, such as using a predictable seed instead of true randomness.
Risk
Premature cryptographic operations have severe security implications. Random numbers may be predictable. Keys may not be properly loaded. Encryption may use weak values. Signatures may be forgeable. Authentication may be bypassable. Cryptographic security is undermined. Side-channel attacks become easier. Entire security model may collapse.
Solution
Use best practices in cryptographic system design. Continuously verify that cryptographic inputs supply valid information. Implement ready signals and handshaking between components. Verify RNG health before use. Ensure key material is fully loaded before operations. Use status registers to confirm unit readiness. Implement error handling for unready conditions.
Common Consequences
| Impact | Details |
|---|---|
| Access Control | Scope: Access Control Bypass Protection Mechanism - Weak crypto enables bypass. |
| Confidentiality | Scope: Confidentiality Read Memory - Predictable crypto can be broken. |
| Integrity | Scope: Integrity Modify Memory - Signatures may be forgeable. |
| Authentication | Scope: Authentication Bypass Authentication - Weak keys enable impersonation. |
Example Code
Vulnerable Code
// Vulnerable: Crypto operations before RNG is ready
module vulnerable_crypto_system (
input wire clk,
input wire reset_n,
input wire start_crypto,
input wire [127:0] plaintext,
output reg [127:0] ciphertext,
output reg crypto_done
);
// RNG signals
wire [127:0] rng_output;
wire rng_ready;
// Key signals
wire [127:0] key_from_fuse;
wire key_ready;
// RNG instance
rng_unit rng (
.clk(clk),
.reset_n(reset_n),
.random_data(rng_output),
.ready(rng_ready)
);
// Fuse unit instance
fuse_unit fuses (
.clk(clk),
.reset_n(reset_n),
.key_out(key_from_fuse),
.ready(key_ready)
);
// VULNERABLE: Crypto operation without checking readiness
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
ciphertext <= 128'h0;
crypto_done <= 1'b0;
end
else if (start_crypto) begin
// VULNERABLE: Uses RNG output even if RNG not ready
// rng_output may be all zeros or predictable
// VULNERABLE: Uses key even if fuse unit not ready
// key_from_fuse may be all zeros
ciphertext <= aes_encrypt(plaintext, key_from_fuse) ^ rng_output;
crypto_done <= 1'b1;
// Problem: If RNG not ready, IV/nonce is predictable
// Problem: If fuses not ready, key is zero/weak
end
end
endmodule
// Vulnerable: RNG with fallback to weak seed
module vulnerable_rng (
input wire clk,
input wire reset_n,
input wire request_random,
output reg [127:0] random_data,
output reg data_valid
);
wire entropy_ready;
wire [127:0] entropy_bits;
entropy_source entropy (
.clk(clk),
.reset_n(reset_n),
.entropy_out(entropy_bits),
.ready(entropy_ready)
);
reg self_test_passed;
// VULNERABLE: Fallback to weak seed if entropy not ready
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
random_data <= 128'h0;
data_valid <= 1'b0;
self_test_passed <= 1'b0;
end
else if (request_random) begin
if (entropy_ready && self_test_passed) begin
// Good: Use true entropy
random_data <= entropy_bits;
data_valid <= 1'b1;
end
else begin
// VULNERABLE: Fallback to hardcoded seed
random_data <= 128'hDEADBEEF_CAFEBABE_12345678_87654321;
data_valid <= 1'b1; // Claims valid but is predictable!
end
end
end
endmodule
// Vulnerable: Software crypto without checking RNG readiness
#include <stdint.h>
#define RNG_STATUS_REG 0x40001000
#define RNG_DATA_REG 0x40001004
#define RNG_READY_BIT 0x01
// VULNERABLE: Generate IV without checking RNG
void vulnerable_generate_iv(uint8_t* iv, size_t len) {
volatile uint32_t* rng_data = (volatile uint32_t*)RNG_DATA_REG;
// VULNERABLE: No check if RNG is ready
// RNG may still be collecting entropy after reset
for (size_t i = 0; i < len; i += 4) {
// RNG might return all zeros if not ready!
uint32_t random = *rng_data;
memcpy(iv + i, &random, 4);
}
// If called immediately after reset, IV may be predictable
// Encryption can be broken if attacker knows IV
}
// VULNERABLE: Use key before fuse unit ready
void vulnerable_load_key(void) {
volatile uint32_t* fuse_key = (volatile uint32_t*)FUSE_KEY_BASE;
// VULNERABLE: No check if fuse unit has completed initialization
// Key may be all zeros during power-up sequence
uint8_t key[16];
for (int i = 0; i < 4; i++) {
uint32_t word = fuse_key[i]; // May be zero!
memcpy(key + i*4, &word, 4);
}
// If key is all zeros, encryption is useless
aes_set_key(key);
}
// VULNERABLE: Encrypt with potentially weak parameters
void vulnerable_encrypt(const uint8_t* plaintext, size_t len,
uint8_t* ciphertext) {
uint8_t iv[16];
vulnerable_generate_iv(iv, 16); // May be predictable!
vulnerable_load_key(); // May be zero!
// Encryption with weak IV and key is breakable
aes_cbc_encrypt(plaintext, len, iv, ciphertext);
}
Fixed Code
// Fixed: Crypto operations only after supporting units ready
module secure_crypto_system (
input wire clk,
input wire reset_n,
input wire start_crypto,
input wire [127:0] plaintext,
output reg [127:0] ciphertext,
output reg crypto_done,
output reg crypto_error
);
// RNG signals
wire [127:0] rng_output;
wire rng_ready;
wire rng_health_ok;
// Key signals
wire [127:0] key_from_fuse;
wire key_ready;
wire key_valid;
// RNG instance with health check
secure_rng_unit rng (
.clk(clk),
.reset_n(reset_n),
.random_data(rng_output),
.ready(rng_ready),
.health_ok(rng_health_ok)
);
// Fuse unit instance
secure_fuse_unit fuses (
.clk(clk),
.reset_n(reset_n),
.key_out(key_from_fuse),
.ready(key_ready),
.valid(key_valid)
);
// State machine for crypto operation
reg [2:0] state;
parameter IDLE = 3'd0;
parameter WAIT_RNG = 3'd1;
parameter WAIT_KEY = 3'd2;
parameter ENCRYPT = 3'd3;
parameter DONE = 3'd4;
parameter ERROR = 3'd5;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
ciphertext <= 128'h0;
crypto_done <= 1'b0;
crypto_error <= 1'b0;
state <= IDLE;
end
else begin
case (state)
IDLE: begin
crypto_done <= 1'b0;
crypto_error <= 1'b0;
if (start_crypto) begin
state <= WAIT_RNG;
end
end
WAIT_RNG: begin
// FIXED: Wait for RNG to be ready and healthy
if (rng_ready && rng_health_ok) begin
state <= WAIT_KEY;
end
else if (!rng_health_ok) begin
// RNG health check failed
state <= ERROR;
end
// Timeout handling could be added here
end
WAIT_KEY: begin
// FIXED: Wait for key to be ready and valid
if (key_ready && key_valid) begin
state <= ENCRYPT;
end
else if (key_ready && !key_valid) begin
// Key validation failed
state <= ERROR;
end
end
ENCRYPT: begin
// FIXED: All dependencies satisfied, safe to encrypt
ciphertext <= aes_encrypt(plaintext, key_from_fuse) ^ rng_output;
state <= DONE;
end
DONE: begin
crypto_done <= 1'b1;
state <= IDLE;
end
ERROR: begin
crypto_error <= 1'b1;
state <= IDLE;
end
endcase
end
end
endmodule
// Fixed: RNG with proper health checking
module secure_rng_unit (
input wire clk,
input wire reset_n,
input wire request_random,
output reg [127:0] random_data,
output reg ready,
output reg health_ok
);
wire entropy_ready;
wire [127:0] entropy_bits;
entropy_source entropy (
.clk(clk),
.reset_n(reset_n),
.entropy_out(entropy_bits),
.ready(entropy_ready)
);
// FIXED: Health check state
reg [2:0] health_state;
reg [7:0] startup_count;
reg self_test_passed;
parameter HEALTH_INIT = 3'd0;
parameter HEALTH_STARTUP = 3'd1;
parameter HEALTH_CONTINUOUS = 3'd2;
parameter HEALTH_FAILED = 3'd3;
// Previous output for continuous testing
reg [127:0] previous_output;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
random_data <= 128'h0;
ready <= 1'b0;
health_ok <= 1'b0;
health_state <= HEALTH_INIT;
startup_count <= 8'h0;
self_test_passed <= 1'b0;
end
else begin
case (health_state)
HEALTH_INIT: begin
// FIXED: Wait for entropy source to stabilize
ready <= 1'b0;
health_ok <= 1'b0;
if (entropy_ready) begin
health_state <= HEALTH_STARTUP;
startup_count <= 8'h0;
end
end
HEALTH_STARTUP: begin
// FIXED: Run startup health tests
if (entropy_ready) begin
// Collect samples and run statistical tests
if (run_startup_tests(entropy_bits)) begin
startup_count <= startup_count + 1;
end
else begin
health_state <= HEALTH_FAILED;
end
// Need multiple passing samples
if (startup_count >= 8'd64) begin
self_test_passed <= 1'b1;
health_state <= HEALTH_CONTINUOUS;
ready <= 1'b1;
health_ok <= 1'b1;
end
end
end
HEALTH_CONTINUOUS: begin
// FIXED: Continuous health monitoring
ready <= 1'b1;
health_ok <= 1'b1;
if (request_random && entropy_ready) begin
// FIXED: Check for stuck output
if (entropy_bits == previous_output) begin
// Repetition detected - possible failure
health_state <= HEALTH_FAILED;
end
else begin
random_data <= entropy_bits;
previous_output <= entropy_bits;
end
end
end
HEALTH_FAILED: begin
// FIXED: RNG failed - do not provide output
ready <= 1'b0;
health_ok <= 1'b0;
random_data <= 128'h0;
// Require reset to recover
end
endcase
end
end
endmodule
// Fixed: Software crypto with proper readiness checks
#include <stdint.h>
#include <stdbool.h>
#define RNG_STATUS_REG 0x40001000
#define RNG_DATA_REG 0x40001004
#define RNG_READY_BIT 0x01
#define RNG_HEALTH_BIT 0x02
// FIXED: Check RNG status before use
static bool is_rng_ready(void) {
volatile uint32_t* rng_status = (volatile uint32_t*)RNG_STATUS_REG;
uint32_t status = *rng_status;
// Check both ready and health bits
return (status & RNG_READY_BIT) && (status & RNG_HEALTH_BIT);
}
// FIXED: Wait for RNG with timeout
static bool wait_for_rng(uint32_t timeout_ms) {
uint32_t start = get_tick_count();
while (!is_rng_ready()) {
if (get_tick_count() - start > timeout_ms) {
log_error("RNG timeout - not ready");
return false;
}
// Small delay to avoid hammering register
delay_us(100);
}
return true;
}
// FIXED: Generate IV only when RNG is ready
bool secure_generate_iv(uint8_t* iv, size_t len) {
volatile uint32_t* rng_data = (volatile uint32_t*)RNG_DATA_REG;
// FIXED: Wait for RNG to be ready
if (!wait_for_rng(1000)) {
return false;
}
for (size_t i = 0; i < len; i += 4) {
// FIXED: Verify RNG still healthy before each read
if (!is_rng_ready()) {
log_error("RNG became unhealthy during IV generation");
return false;
}
uint32_t random = *rng_data;
// FIXED: Sanity check - reject obvious bad values
static uint32_t previous = 0;
if (random == previous || random == 0 || random == 0xFFFFFFFF) {
log_error("RNG output suspicious");
return false;
}
previous = random;
memcpy(iv + i, &random, 4);
}
return true;
}
// FIXED: Load key only when fuse unit ready
bool secure_load_key(uint8_t* key_out) {
volatile uint32_t* fuse_status = (volatile uint32_t*)FUSE_STATUS_REG;
volatile uint32_t* fuse_key = (volatile uint32_t*)FUSE_KEY_BASE;
// FIXED: Wait for fuse unit to complete initialization
uint32_t start = get_tick_count();
while (!(*fuse_status & FUSE_READY_BIT)) {
if (get_tick_count() - start > 1000) {
log_error("Fuse unit not ready");
return false;
}
delay_us(100);
}
// FIXED: Check key validity
if (!(*fuse_status & FUSE_KEY_VALID_BIT)) {
log_error("Fuse key not valid");
return false;
}
// Read key
for (int i = 0; i < 4; i++) {
uint32_t word = fuse_key[i];
memcpy(key_out + i*4, &word, 4);
}
// FIXED: Verify key is not all zeros or all ones
if (is_key_weak(key_out, 16)) {
log_error("Weak key detected from fuses");
secure_memzero(key_out, 16);
return false;
}
return true;
}
// FIXED: Encrypt only with valid parameters
bool secure_encrypt(const uint8_t* plaintext, size_t len,
uint8_t* ciphertext) {
uint8_t iv[16];
uint8_t key[16];
// FIXED: Generate IV with verification
if (!secure_generate_iv(iv, 16)) {
log_error("Failed to generate IV");
return false;
}
// FIXED: Load key with verification
if (!secure_load_key(key)) {
log_error("Failed to load key");
secure_memzero(iv, 16);
return false;
}
// Now safe to encrypt
bool result = aes_cbc_encrypt(plaintext, len, key, iv, ciphertext);
// Clear sensitive data
secure_memzero(key, 16);
secure_memzero(iv, 16);
return result;
}
CVE Examples
Cryptographic timing vulnerabilities have been found in various systems where crypto operations proceeded before RNG initialization was complete, resulting in predictable keys or IVs that could be exploited.
Related CWEs
- CWE-665: Improper Initialization (parent)
- CWE-696: Incorrect Behavior Order (parent)
- CWE-1205: Security Primitives and Cryptography Issues (category)
- CWE-330: Use of Insufficiently Random Values (related)
- CAPEC-97: Cryptanalysis (attack pattern)
References
- MITRE Corporation. "CWE-1279: Cryptographic Operations are run Before Supporting Units are Ready." https://cwe.mitre.org/data/definitions/1279.html
- NIST SP 800-90B. "Recommendation for the Entropy Sources Used for Random Bit Generation"
- FIPS 140-3. "Security Requirements for Cryptographic Modules"