Improper Protection of Physical Side Channels
Description
Improper Protection of Physical Side Channels occurs when a device lacks adequate protection mechanisms to prevent physical side channels from exposing sensitive information due to patterns in physically observable phenomena such as variations in power consumption, electromagnetic emissions (EME), or acoustic emissions. Adversaries with physical access or proximity can monitor and measure these physical phenomena to detect patterns and extract secrets. If the adversary can monitor hardware operation and correlate its data processing with power, EME, and acoustic measurements, they might be able to recover secret keys and data.
Risk
Physical side channels have severe security implications. Cryptographic keys extractable through power analysis. Electromagnetic emissions reveal processing patterns. Acoustic analysis can extract secrets. PIN codes recoverable from timing. Private keys compromised. Authentication bypassed. All secrets at risk from physical proximity. Particularly severe for cryptographic implementations and security features.
Solution
Apply blinding or masking techniques to cryptographic algorithm implementations during architecture and design phase. Add shielding or tamper-resistant protections during implementation to increase measurement difficulty. Use constant-time implementations. Add random delays and operation ordering. Implement power filtering and EMI shielding. Use physically unclonable functions (PUFs) for key storage.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality Secret keys and sensitive data extractable through physical observation. |
Example Code
Vulnerable Code
// Vulnerable: RSA with power side-channel leakage
module vulnerable_rsa_modexp (
input wire clk,
input wire rst_n,
input wire start,
input wire [2047:0] base, // Message to encrypt/decrypt
input wire [2047:0] exponent, // Private key (secret!)
input wire [2047:0] modulus,
output reg [2047:0] result,
output reg done
);
reg [2047:0] temp_result;
reg [2047:0] temp_base;
reg [2047:0] exponent_reg;
reg [11:0] bit_counter;
reg busy;
localparam IDLE = 2'b00;
localparam SQUARE = 2'b01;
localparam MULTIPLY = 2'b10;
localparam DONE_STATE = 2'b11;
reg [1:0] state;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
done <= 1'b0;
result <= 2048'b0;
end else begin
case (state)
IDLE: begin
if (start) begin
temp_result <= 2048'b1;
temp_base <= base;
exponent_reg <= exponent;
bit_counter <= 12'd2047;
state <= SQUARE;
end
end
SQUARE: begin
// Always square
temp_base <= mod_multiply(temp_base, temp_base, modulus);
state <= MULTIPLY;
end
MULTIPLY: begin
// VULNERABLE: Conditional multiply based on exponent bit
// This creates different power consumption patterns
// revealing the secret exponent bits!
if (exponent_reg[bit_counter]) begin
// Power spike when bit is 1
temp_result <= mod_multiply(temp_result, temp_base, modulus);
end
// No operation when bit is 0 - detectable!
if (bit_counter == 0) begin
state <= DONE_STATE;
end else begin
bit_counter <= bit_counter - 1;
state <= SQUARE;
end
end
DONE_STATE: begin
result <= temp_result;
done <= 1'b1;
state <= IDLE;
end
endcase
end
end
// Attack: Monitor power consumption
// - High power during MULTIPLY when exponent bit = 1
// - Low power (no multiply) when exponent bit = 0
// - Attacker can recover entire private exponent
endmodule
// Vulnerable: PIN comparison with timing side-channel
module vulnerable_pin_check (
input wire clk,
input wire rst_n,
input wire [31:0] entered_pin,
input wire check_enable,
output reg pin_correct,
output reg check_done
);
// Secret PIN stored in register
reg [31:0] stored_pin;
reg [2:0] byte_counter;
reg checking;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
pin_correct <= 1'b0;
check_done <= 1'b0;
byte_counter <= 3'b0;
checking <= 1'b0;
end else begin
if (check_enable && !checking) begin
checking <= 1'b1;
byte_counter <= 3'b0;
pin_correct <= 1'b1; // Assume correct until mismatch
end
if (checking) begin
// VULNERABLE: Early termination on mismatch
// Different number of cycles reveals correct digit count
case (byte_counter)
3'd0: begin
if (entered_pin[7:0] != stored_pin[7:0]) begin
pin_correct <= 1'b0;
check_done <= 1'b1; // VULNERABLE: Early exit
checking <= 1'b0;
end else begin
byte_counter <= 3'd1;
end
end
3'd1: begin
if (entered_pin[15:8] != stored_pin[15:8]) begin
pin_correct <= 1'b0;
check_done <= 1'b1; // VULNERABLE: Early exit
checking <= 1'b0;
end else begin
byte_counter <= 3'd2;
end
end
// ... continues for other bytes
3'd3: begin
if (entered_pin[31:24] != stored_pin[31:24]) begin
pin_correct <= 1'b0;
end
check_done <= 1'b1;
checking <= 1'b0;
end
endcase
end
end
end
// Attack: Count cycles until check_done
// - 1 cycle: first digit wrong
// - 2 cycles: second digit wrong (first correct!)
// - etc.
// Attacker can brute-force one digit at a time
endmodule
// Vulnerable: AES with cache timing side-channel
#include <stdint.h>
// VULNERABLE: Table-based AES with cache timing leakage
static const uint8_t sbox[256] = {
// S-box lookup table
0x63, 0x7c, 0x77, 0x7b, /* ... rest of S-box ... */
};
// VULNERABLE: Table lookups have variable timing
// Cache hits are faster than cache misses
// Attacker can determine which table entries were accessed
uint8_t vulnerable_sbox_lookup(uint8_t input) {
// VULNERABLE: Access time depends on cache state
return sbox[input];
}
void vulnerable_aes_encrypt(const uint8_t* plaintext,
const uint8_t* key,
uint8_t* ciphertext) {
uint8_t state[16];
// Copy plaintext to state
for (int i = 0; i < 16; i++) {
state[i] = plaintext[i] ^ key[i];
}
// VULNERABLE: S-box lookups leak cache timing
for (int round = 0; round < 10; round++) {
for (int i = 0; i < 16; i++) {
// Cache miss = slow, cache hit = fast
// Attacker can determine state[i] values
state[i] = vulnerable_sbox_lookup(state[i]);
}
// MixColumns, ShiftRows, AddRoundKey...
}
// Copy state to ciphertext
for (int i = 0; i < 16; i++) {
ciphertext[i] = state[i];
}
}
Fixed Code
// Fixed: RSA with constant-time operation (masked)
module secure_rsa_modexp (
input wire clk,
input wire rst_n,
input wire start,
input wire [2047:0] base,
input wire [2047:0] exponent,
input wire [2047:0] modulus,
output reg [2047:0] result,
output reg done
);
reg [2047:0] temp_result;
reg [2047:0] temp_base;
reg [2047:0] exponent_reg;
reg [11:0] bit_counter;
// FIXED: Masking register for dummy operations
reg [2047:0] mask_reg;
localparam IDLE = 2'b00;
localparam SQUARE = 2'b01;
localparam MULTIPLY = 2'b10;
localparam DONE_STATE = 2'b11;
reg [1:0] state;
// FIXED: Always perform multiply, discard result when not needed
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
done <= 1'b0;
result <= 2048'b0;
mask_reg <= 2048'b0;
end else begin
case (state)
IDLE: begin
if (start) begin
temp_result <= 2048'b1;
temp_base <= base;
exponent_reg <= exponent;
bit_counter <= 12'd2047;
state <= SQUARE;
end
end
SQUARE: begin
temp_base <= mod_multiply(temp_base, temp_base, modulus);
state <= MULTIPLY;
end
MULTIPLY: begin
// FIXED: Always perform multiply (constant power)
reg [2047:0] multiply_result;
multiply_result = mod_multiply(temp_result, temp_base, modulus);
// FIXED: Use masking to select result
// Both branches perform same operations
if (exponent_reg[bit_counter]) begin
temp_result <= multiply_result; // Keep result
mask_reg <= temp_result; // Dummy store
end else begin
temp_result <= temp_result; // Keep old value
mask_reg <= multiply_result; // Discard to mask
end
// FIXED: Same number of operations regardless of bit value
// Power consumption is constant
if (bit_counter == 0) begin
state <= DONE_STATE;
end else begin
bit_counter <= bit_counter - 1;
state <= SQUARE;
end
end
DONE_STATE: begin
result <= temp_result;
done <= 1'b1;
state <= IDLE;
end
endcase
end
end
endmodule
// Fixed: PIN comparison with constant-time operation
module secure_pin_check (
input wire clk,
input wire rst_n,
input wire [31:0] entered_pin,
input wire check_enable,
output reg pin_correct,
output reg check_done
);
reg [31:0] stored_pin;
reg [2:0] byte_counter;
reg checking;
reg [3:0] mismatch_flags; // FIXED: Accumulate all mismatches
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
pin_correct <= 1'b0;
check_done <= 1'b0;
byte_counter <= 3'b0;
checking <= 1'b0;
mismatch_flags <= 4'b0;
end else begin
if (check_enable && !checking) begin
checking <= 1'b1;
byte_counter <= 3'b0;
mismatch_flags <= 4'b0; // FIXED: Reset flags
end
if (checking) begin
// FIXED: Check ALL bytes, no early termination
case (byte_counter)
3'd0: begin
// FIXED: Just record mismatch, don't exit
mismatch_flags[0] <= (entered_pin[7:0] != stored_pin[7:0]);
byte_counter <= 3'd1;
end
3'd1: begin
mismatch_flags[1] <= (entered_pin[15:8] != stored_pin[15:8]);
byte_counter <= 3'd2;
end
3'd2: begin
mismatch_flags[2] <= (entered_pin[23:16] != stored_pin[23:16]);
byte_counter <= 3'd3;
end
3'd3: begin
mismatch_flags[3] <= (entered_pin[31:24] != stored_pin[31:24]);
byte_counter <= 3'd4;
end
3'd4: begin
// FIXED: Only report result after checking ALL bytes
// Same timing regardless of which byte is wrong
pin_correct <= (mismatch_flags == 4'b0);
check_done <= 1'b1;
checking <= 1'b0;
end
endcase
end
end
end
// FIXED: Always takes exactly 5 cycles to complete
// Attacker cannot determine which digit is wrong
endmodule
// Fixed: AES with side-channel countermeasures
#include <stdint.h>
#include <string.h>
// FIXED: Bit-sliced S-box implementation
// No table lookups, constant time
static void secure_sbox_bitslice(uint8_t* state) {
// FIXED: Compute S-box using boolean operations only
// No memory accesses that could leak through cache
// Implementation uses algebraic decomposition of AES S-box
for (int i = 0; i < 16; i++) {
uint8_t x = state[i];
// FIXED: Constant-time S-box computation
// Uses GF(2^8) inversion via boolean operations
uint8_t result = compute_sbox_algebraic(x);
state[i] = result;
}
}
// FIXED: Constant-time comparison
int secure_memcmp(const void* a, const void* b, size_t len) {
const volatile uint8_t* aa = a;
const volatile uint8_t* bb = b;
uint8_t diff = 0;
// FIXED: Always compare all bytes
// No early termination
for (size_t i = 0; i < len; i++) {
diff |= aa[i] ^ bb[i];
}
// FIXED: Return value doesn't reveal position of difference
return diff;
}
// FIXED: AES with masking
void secure_aes_encrypt(const uint8_t* plaintext,
const uint8_t* key,
uint8_t* ciphertext) {
uint8_t state[16];
uint8_t mask[16];
// FIXED: Generate random mask
get_random_bytes(mask, 16);
// FIXED: Apply mask to plaintext
for (int i = 0; i < 16; i++) {
state[i] = plaintext[i] ^ key[i] ^ mask[i];
}
// FIXED: Masked computation
// Operations performed on masked data
// Mask prevents correlation between power and actual values
for (int round = 0; round < 10; round++) {
secure_sbox_bitslice(state); // FIXED: No table lookups
// MixColumns, ShiftRows, AddRoundKey on masked state
}
// FIXED: Remove mask at end
for (int i = 0; i < 16; i++) {
ciphertext[i] = state[i] ^ final_mask[i];
}
// FIXED: Clear sensitive data from memory
secure_memzero(mask, 16);
secure_memzero(state, 16);
}
// FIXED: Secure memory clear (not optimized away)
void secure_memzero(void* ptr, size_t len) {
volatile uint8_t* p = ptr;
while (len--) {
*p++ = 0;
}
}
CVE Examples
- CVE-2022-35888: Processor power side-channels leak secrets.
- CVE-2021-3011: Google Titan security key vulnerable to electromagnetic side-channel.
- CVE-2019-14353: Crypto hardware wallet USB power consumption reveals PIN/password.
- CVE-2013-4576: Acoustic cryptanalysis extracts RSA keys.
- CVE-2020-28368: Guest AES key recovery via power monitoring interface.
Related CWEs
- CWE-203: Observable Discrepancy (parent)
- CWE-1255: Comparison Logic is Vulnerable to Power Side-Channel Attacks (child)
- CWE-1388: Physical Access Issues and Concerns (category)
- CWE-208: Observable Timing Discrepancy (related)
References
- MITRE Corporation. "CWE-1300: Improper Protection of Physical Side Channels." https://cwe.mitre.org/data/definitions/1300.html
- ISO 17825: Information technology — Security techniques — Testing methods for the mitigation of non-invasive attack classes against cryptographic modules
- Kocher, P., et al. "Differential Power Analysis"