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

ImpactDetails
ConfidentialityScope: Confidentiality

Unauthorized Access - Power analysis reveals secret values like PINs, passwords, and cryptographic keys.
IntegrityScope: Integrity

Bypass Protection Mechanism - Authentication and verification mechanisms can be defeated.
Access ControlScope: 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.


  • 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

  1. MITRE Corporation. "CWE-1255: Comparison Logic is Vulnerable to Power Side-Channel Attacks." https://cwe.mitre.org/data/definitions/1255.html
  2. Kocher et al. "Differential Power Analysis"
  3. Mangard, Oswald, Popp. "Power Analysis Attacks: Revealing the Secrets of Smart Cards"