Comparison Logic is Vulnerable to Power Side-Channel Attacks
Description
Comparison Logic is Vulnerable to Power Side-Channel Attacks occurs when a device's power consumption during security token evaluation can be monitored in real-time to determine reference token values. Real-time power monitoring of devices enables attackers to observe variations in energy consumption during token validation. When comparison algorithms lack sufficient robustness and retry mechanisms are unlimited, power differences between correct and incorrect entries become exploitable, allowing attackers to incrementally determine reference values.
Risk
Power side-channel vulnerabilities have severe security implications. PINs and passwords can be recovered through power analysis. Cryptographic keys may be extracted. Authentication bypasses become possible. Secure boot keys may be compromised. Challenge-response systems can be defeated. Token values can be determined without brute force. Single Power Analysis (SPA) reveals operations directly. Differential Power Analysis (DPA) extracts keys statistically.
Solution
Decrement retry counter before token validation; design checks with uniform power consumption. Parallelize secret data shifting using wider buses. Inject random data into crypto operations as noise. Implement hardware filters for power lines. Avoid single secrets for extended periods through key rotation. Use masked implementations for cryptographic operations. Implement constant-power comparison circuits. Add noise generators to power supply.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality Unauthorized Access - Power analysis reveals secret values like PINs, passwords, and cryptographic keys. |
| Integrity | Scope: Integrity Bypass Protection Mechanism - Authentication and verification mechanisms can be defeated. |
| Access Control | Scope: Access Control Complete System Compromise - Recovery of authentication secrets grants full access. |
Example Code
Vulnerable Code
// Vulnerable: Power-leaking password comparison
#include <stdint.h>
#define NUM_PW_DIGITS 4
volatile uint8_t stored_password[NUM_PW_DIGITS] = {1, 2, 3, 4};
volatile int password_tries = 3;
uint8_t GetPasswordByte(void);
bool vulnerable_password_check(void) {
uint8_t password_ok = 0;
// VULNERABLE: Different operations based on match/mismatch
for (int i = 0; i < NUM_PW_DIGITS; i++) {
if (GetPasswordByte() == stored_password[i]) {
password_ok |= 1; // Different power signature here
}
else {
password_ok |= 0; // Than here (OR with 0 is different)
}
}
// VULNERABLE: Retry counter decremented AFTER check
// Attacker can power-cycle to avoid decrement
if (password_ok) {
return true;
}
else {
password_tries--;
return false;
}
}
// Vulnerable: Serial shifting leaks bits
void vulnerable_serial_shift(uint8_t* secret, int len) {
// VULNERABLE: Serial shifting reveals one bit at a time
for (int i = 0; i < len * 8; i++) {
uint8_t bit = (secret[i/8] >> (i%8)) & 1;
// Each bit shift has distinct power signature
shift_out_bit(bit);
}
}
// Vulnerable: Conditional operations based on secret
void vulnerable_crypto_operation(uint8_t* key, uint8_t* data, int len) {
for (int i = 0; i < len; i++) {
if (key[i] & 0x80) {
// VULNERABLE: Operation only if key bit is 1
data[i] = complex_transform(data[i]);
}
// Power difference reveals key bits
}
}
// Vulnerable: Power-leaking comparator
module vulnerable_pin_check (
input wire clk,
input wire reset_n,
input wire [3:0] digit_in,
input wire digit_valid,
output reg access_granted,
output reg check_complete
);
// Stored PIN
reg [3:0] stored_pin [0:3];
initial begin
stored_pin[0] = 4'd1;
stored_pin[1] = 4'd2;
stored_pin[2] = 4'd3;
stored_pin[3] = 4'd4;
end
reg [1:0] digit_index;
reg match_status;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
digit_index <= 2'b0;
match_status <= 1'b1;
access_granted <= 1'b0;
check_complete <= 1'b0;
end
else if (digit_valid) begin
// VULNERABLE: Conditional operation leaks power
if (digit_in == stored_pin[digit_index]) begin
// Match: certain power signature
match_status <= match_status & 1'b1;
end
else begin
// Mismatch: different power signature
match_status <= 1'b0;
end
if (digit_index == 2'd3) begin
access_granted <= match_status;
check_complete <= 1'b1;
end
else begin
digit_index <= digit_index + 1;
end
end
end
endmodule
// Vulnerable: Serial-In/Serial-Out shift register
module vulnerable_siso_shift (
input wire clk,
input wire reset_n,
input wire serial_in,
input wire shift_enable,
output wire serial_out
);
reg [7:0] shift_reg;
// VULNERABLE: Single bit shifts leak data through power
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
shift_reg <= 8'h0;
end
else if (shift_enable) begin
// Each shift has power signature based on bit values
shift_reg <= {shift_reg[6:0], serial_in};
end
end
assign serial_out = shift_reg[7];
endmodule
Fixed Code
// Fixed: Power-balanced password comparison
#include <stdint.h>
#define NUM_PW_DIGITS 4
volatile uint8_t stored_password[NUM_PW_DIGITS] = {1, 2, 3, 4};
volatile int password_tries = 3;
uint8_t GetPasswordByte(void);
bool secure_password_check(void) {
uint8_t password_ok = 0;
// FIXED: Decrement retry counter BEFORE check
// Prevents power-cycle attack to avoid decrement
password_tries--;
// FIXED: Uniform power operations regardless of match
for (int i = 0; i < NUM_PW_DIGITS; i++) {
uint8_t input = GetPasswordByte();
uint8_t stored = stored_password[i];
// FIXED: Same operation for match and mismatch
// Uses constant power regardless of result
if (input == stored) {
password_ok |= 0x10; // Same hamming weight
}
else {
password_ok |= 0x01; // Same hamming weight
}
}
// Check for all matches (password_ok should be 0x40 for 4 matches)
return (password_ok == 0x40);
}
// Fixed: Masked comparison with noise
bool secure_masked_compare(const uint8_t* input, const uint8_t* stored, int len) {
// FIXED: Generate random mask
uint8_t mask = get_random_byte();
uint8_t result = 0;
uint8_t dummy = 0;
for (int i = 0; i < len; i++) {
// FIXED: Apply mask to obscure power signature
uint8_t masked_input = input[i] ^ mask;
uint8_t masked_stored = stored[i] ^ mask;
// Comparison on masked values
uint8_t diff = masked_input ^ masked_stored;
// FIXED: Dummy operations to balance power
dummy ^= mask;
dummy ^= (diff | ~diff); // Always 0xFF
result |= diff;
}
// Use dummy to prevent optimization
volatile uint8_t anti_opt = dummy;
(void)anti_opt;
return result == 0;
}
// Fixed: Parallel shifting with wider bus
void secure_parallel_shift(uint8_t* data, int len) {
// FIXED: Shift multiple bits at once
// Power signature confounds multiple bits together
for (int i = 0; i < len; i += 4) {
// 32-bit parallel shift
uint32_t word = *(uint32_t*)&data[i];
shift_out_word(word); // All 32 bits at once
}
}
// Fixed: Constant-power crypto operation
void secure_crypto_operation(const uint8_t* key, uint8_t* data, int len) {
for (int i = 0; i < len; i++) {
// FIXED: Always perform both operations
uint8_t transform_result = complex_transform(data[i]);
uint8_t no_transform = data[i];
// FIXED: Use constant-time select based on key
// Both operations always execute
uint8_t key_bit = (key[i] >> 7) & 1;
data[i] = (transform_result & (0 - key_bit)) |
(no_transform & (0 - (1 - key_bit)));
}
}
// Fixed: Power-balanced PIN comparator
module secure_pin_check (
input wire clk,
input wire reset_n,
input wire [3:0] digit_in,
input wire digit_valid,
input wire [7:0] random_mask, // External random for masking
output reg access_granted,
output reg check_complete
);
// Stored PIN (would be in secure memory)
reg [3:0] stored_pin [0:3];
initial begin
stored_pin[0] = 4'd1;
stored_pin[1] = 4'd2;
stored_pin[2] = 4'd3;
stored_pin[3] = 4'd4;
end
reg [1:0] digit_index;
reg [3:0] match_accumulator;
reg [3:0] dummy_accumulator;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
digit_index <= 2'b0;
match_accumulator <= 4'hF;
dummy_accumulator <= 4'h0;
access_granted <= 1'b0;
check_complete <= 1'b0;
end
else if (digit_valid) begin
// FIXED: Masked comparison
wire [3:0] masked_input = digit_in ^ random_mask[3:0];
wire [3:0] masked_stored = stored_pin[digit_index] ^ random_mask[3:0];
// FIXED: Both operations always execute
wire match = (masked_input == masked_stored);
wire mismatch = (masked_input != masked_stored);
// FIXED: Uniform operations regardless of result
// Same number of bit flips for match and mismatch
if (match) begin
match_accumulator <= match_accumulator & 4'hF;
dummy_accumulator <= dummy_accumulator | 4'h1;
end
else begin
match_accumulator <= match_accumulator & 4'hE;
dummy_accumulator <= dummy_accumulator | 4'h0;
end
if (digit_index == 2'd3) begin
// FIXED: Single final comparison
access_granted <= (match_accumulator == 4'hF);
check_complete <= 1'b1;
end
else begin
digit_index <= digit_index + 1;
end
end
end
endmodule
// Fixed: Parallel-In/Parallel-Out shift register
module secure_pipo_shift (
input wire clk,
input wire reset_n,
input wire [31:0] parallel_in, // 32-bit parallel load
input wire load_enable,
input wire shift_enable,
output wire [31:0] parallel_out
);
reg [31:0] shift_reg;
// FIXED: Parallel operations confound individual bits
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
shift_reg <= 32'h0;
end
else if (load_enable) begin
// Load all 32 bits at once
shift_reg <= parallel_in;
end
else if (shift_enable) begin
// Shift all bits together - power signature
// confounds all 32 bits
shift_reg <= {shift_reg[30:0], 1'b0};
end
end
assign parallel_out = shift_reg;
endmodule
// Fixed: Dual-rail logic for constant power
module secure_dual_rail_compare (
input wire clk,
input wire reset_n,
input wire a_true, a_false, // Dual-rail input A
input wire b_true, b_false, // Dual-rail input B
output reg match_true, match_false
);
// FIXED: Dual-rail encoding ensures constant power
// Every comparison involves same number of transitions
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
match_true <= 1'b0;
match_false <= 1'b1;
end
else begin
// FIXED: Both rails always computed
match_true <= (a_true & b_true) | (a_false & b_false);
match_false <= (a_true & b_false) | (a_false & b_true);
// Total transitions constant regardless of values
end
end
endmodule
CVE Examples
Power side-channel vulnerabilities have been demonstrated against smart cards, secure microcontrollers, and cryptographic implementations where power analysis revealed PINs, passwords, and cryptographic keys.
Related CWEs
- CWE-1300: Improper Protection of Physical Side Channels (parent)
- CWE-1259: Improper Restriction of Security Token Assignment (related)
- CWE-1254: Incorrect Comparison Logic Granularity (related)
References
- MITRE Corporation. "CWE-1255: Comparison Logic is Vulnerable to Power Side-Channel Attacks." https://cwe.mitre.org/data/definitions/1255.html
- Kocher et al. "Differential Power Analysis"
- Mangard, Oswald, Popp. "Power Analysis Attacks: Revealing the Secrets of Smart Cards"