Side-Channel Attacks are cryptanalysis techniques that exploit physical information obtained from cryptographic implementations, such as execution time, power consumption, electromagnetic emissions, or sound.

What are Side-Channel Attacks?

Side-channel attacks do not directly attack cryptographic algorithms, but rather exploit physical information that leaks during the execution of cryptographic implementations in hardware or software.

Types of Side-Channel Attacks

Timing Attacks

  • Description: Exploit differences in execution time
  • Information: Time of cryptographic operations
  • Example: Attacks on RSA, AES
  • Countermeasures: Constant time

Power Analysis Attacks

  • Simple Power Analysis (SPA): Direct consumption analysis
  • Differential Power Analysis (DPA): Statistical analysis
  • Correlation Power Analysis (CPA): Correlation analysis
  • Template Attacks: Template-based attacks

Electromagnetic Attacks

  • EM Emissions: Electromagnetic emissions
  • Near Field: Near field
  • Far Field: Far field
  • Frequency Analysis: Frequency analysis

Acoustic Attacks

  • Sound Analysis: Sound analysis
  • Keyboard Eavesdropping: Keyboard eavesdropping
  • Printer Eavesdropping: Printer eavesdropping
  • CPU Sound: Processor sound

Timing Attacks

RSA Attack

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import time
import random
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding

class VulnerableRSA:
    def __init__(self, key_size=1024):
        self.private_key = rsa.generate_private_key(
            public_exponent=65537,
            key_size=key_size
        )
        self.public_key = self.private_key.public_key()
    
    def vulnerable_decrypt(self, ciphertext):
        """Implementation vulnerable to timing attack"""
        start_time = time.time()
        
        # Simulate operation that takes more time with bits set to 1
        result = 1
        for bit in bin(ciphertext)[2:]:
            if bit == '1':
                time.sleep(0.001)  # Simulate expensive operation
            result = (result * result) % self.private_key.private_numbers().private_exponent
        
        end_time = time.time()
        execution_time = end_time - start_time
        
        return result, execution_time

class TimingAttack:
    def __init__(self, target_rsa):
        self.target = target_rsa
    
    def attack(self, ciphertext, num_samples=1000):
        """Perform timing attack"""
        times = []
        
        for _ in range(num_samples):
            _, exec_time = self.target.vulnerable_decrypt(ciphertext)
            times.append(exec_time)
        
        # Statistical analysis
        avg_time = sum(times) / len(times)
        variance = sum((t - avg_time) ** 2 for t in times) / len(times)
        
        return {
            'average_time': avg_time,
            'variance': variance,
            'times': times
        }

# Usage example
vulnerable_rsa = VulnerableRSA()
attacker = TimingAttack(vulnerable_rsa)

ciphertext = 12345
attack_result = attacker.attack(ciphertext)
print(f"Average time: {attack_result['average_time']:.6f}s")
print(f"Variance: {attack_result['variance']:.6f}")

Countermeasures for Timing Attacks

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import time
import secrets

class SecureRSA:
    def __init__(self, key_size=1024):
        self.private_key = rsa.generate_private_key(
            public_exponent=65537,
            key_size=key_size
        )
        self.public_key = self.private_key.public_key()
    
    def constant_time_decrypt(self, ciphertext):
        """Constant-time implementation"""
        # Use constant-time operations
        result = 1
        base = ciphertext
        exponent = self.private_key.private_numbers().private_exponent
        
        # Constant-time modular exponentiation algorithm
        while exponent > 0:
            if exponent & 1:
                result = (result * base) % self.private_key.private_numbers().public_numbers.n
            base = (base * base) % self.private_key.private_numbers().public_numbers.n
            exponent >>= 1
        
        # Add random time to mask
        time.sleep(secrets.uniform(0.001, 0.002))
        
        return result

# Usage example
secure_rsa = SecureRSA()
result = secure_rsa.constant_time_decrypt(12345)
print(f"Secure result: {result}")

Power Analysis Attacks

Simple Power Analysis (SPA)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import numpy as np
import matplotlib.pyplot as plt

class SimplePowerAnalysis:
    def __init__(self):
        self.power_traces = []
        self.operations = []
    
    def simulate_power_consumption(self, operation, data):
        """Simulate power consumption"""
        power = []
        
        if operation == 'AES_encrypt':
            # Simulate power consumption during AES encryption
            for round_num in range(10):
                # Base consumption
                base_power = 100
                
                # Additional consumption for operations
                if round_num == 0:
                    base_power += 50  # AddRoundKey
                else:
                    base_power += 30  # SubBytes, ShiftRows, MixColumns
                
                # Random variation
                noise = np.random.normal(0, 5)
                power.append(base_power + noise)
        
        return power
    
    def analyze_trace(self, power_trace):
        """Analyze power trace"""
        # Find patterns in consumption
        peaks = []
        for i in range(1, len(power_trace) - 1):
            if power_trace[i] > power_trace[i-1] and power_trace[i] > power_trace[i+1]:
                peaks.append(i)
        
        return peaks
    
    def visualize_trace(self, power_trace, title="Power Trace"):
        """Visualize power trace"""
        plt.figure(figsize=(12, 6))
        plt.plot(power_trace)
        plt.title(title)
        plt.xlabel("Time")
        plt.ylabel("Power Consumption")
        plt.grid(True)
        plt.show()

# Usage example
spa = SimplePowerAnalysis()
power_trace = spa.simulate_power_consumption('AES_encrypt', 'test_data')
peaks = spa.analyze_trace(power_trace)
print(f"Detected peaks: {peaks}")

Differential Power Analysis (DPA)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
class DifferentialPowerAnalysis:
    def __init__(self):
        self.traces = []
        self.inputs = []
    
    def collect_traces(self, num_traces=1000):
        """Collect power traces"""
        for _ in range(num_traces):
            # Generate random input
            input_data = np.random.randint(0, 256, 16)
            self.inputs.append(input_data)
            
            # Simulate power trace
            trace = self.simulate_aes_trace(input_data)
            self.traces.append(trace)
    
    def simulate_aes_trace(self, input_data):
        """Simulate AES power trace"""
        trace = []
        
        # Simulate 10 rounds of AES
        for round_num in range(10):
            # Power consumption per byte
            for byte in input_data:
                # Base consumption
                power = 50
                
                # Additional consumption by byte value
                power += byte * 0.5
                
                # Noise
                noise = np.random.normal(0, 10)
                trace.append(power + noise)
        
        return trace
    
    def dpa_attack(self, target_byte=0):
        """Perform DPA attack"""
        if not self.traces:
            self.collect_traces()
        
        # Calculate hypotheses for each possible key
        best_key = 0
        best_correlation = 0
        
        for key_guess in range(256):
            # Calculate correlation
            correlation = self.calculate_correlation(key_guess, target_byte)
            
            if abs(correlation) > abs(best_correlation):
                best_correlation = correlation
                best_key = key_guess
        
        return best_key, best_correlation
    
    def calculate_correlation(self, key_guess, target_byte):
        """Calculate correlation for a key hypothesis"""
        # Simulate power consumption based on hypothesis
        predicted_consumption = []
        
        for i, input_data in enumerate(self.inputs):
            # Calculate intermediate value
            intermediate = input_data[target_byte] ^ key_guess
            
            # Simulate power consumption
            power = self.sbox_power_model(intermediate)
            predicted_consumption.append(power)
        
        # Calculate correlation with real traces
        correlations = []
        for trace in self.traces:
            correlation = np.corrcoef(predicted_consumption, trace)[0, 1]
            correlations.append(correlation)
        
        return np.mean(correlations)
    
    def sbox_power_model(self, value):
        """Power consumption model for S-box"""
        # Count number of bits set to 1 (Hamming weight)
        hamming_weight = bin(value).count('1')
        return hamming_weight * 10  # Scale factor

# Usage example
dpa = DifferentialPowerAnalysis()
dpa.collect_traces(1000)
best_key, correlation = dpa.dpa_attack()
print(f"Best key found: {best_key}")
print(f"Correlation: {correlation:.4f}")

Electromagnetic Attacks

EM Emission Analysis

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class ElectromagneticAnalysis:
    def __init__(self):
        self.em_traces = []
        self.frequencies = []
    
    def simulate_em_emission(self, operation, frequency_range=(1, 1000)):
        """Simulate electromagnetic emission"""
        # Simulate EM emission during cryptographic operation
        frequencies = np.linspace(frequency_range[0], frequency_range[1], 1000)
        amplitudes = []
        
        for freq in frequencies:
            # Base amplitude
            amplitude = 1.0 / (freq + 1)
            
            # Peaks at specific frequencies
            if operation == 'AES_encrypt':
                # Peaks at frequencies related to AES
                for harmonic in [100, 200, 300, 400]:
                    if abs(freq - harmonic) < 10:
                        amplitude += 0.5
            
            # Noise
            noise = np.random.normal(0, 0.1)
            amplitudes.append(amplitude + noise)
        
        return frequencies, amplitudes
    
    def analyze_em_trace(self, frequencies, amplitudes):
        """Analyze EM trace"""
        # Find peaks in spectrum
        peaks = []
        for i in range(1, len(amplitudes) - 1):
            if amplitudes[i] > amplitudes[i-1] and amplitudes[i] > amplitudes[i+1]:
                if amplitudes[i] > np.mean(amplitudes) + 2 * np.std(amplitudes):
                    peaks.append(frequencies[i])
        
        return peaks
    
    def visualize_em_spectrum(self, frequencies, amplitudes, title="EM Spectrum"):
        """Visualize EM spectrum"""
        plt.figure(figsize=(12, 6))
        plt.semilogy(frequencies, amplitudes)
        plt.title(title)
        plt.xlabel("Frequency (MHz)")
        plt.ylabel("Amplitude")
        plt.grid(True)
        plt.show()

# Usage example
em_analysis = ElectromagneticAnalysis()
frequencies, amplitudes = em_analysis.simulate_em_emission('AES_encrypt')
peaks = em_analysis.analyze_em_trace(frequencies, amplitudes)
print(f"Detected EM peaks: {peaks}")

Acoustic Attacks

Acoustic Analysis

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import numpy as np
from scipy import signal

class AcousticAnalysis:
    def __init__(self):
        self.audio_traces = []
        self.sample_rate = 44100
    
    def simulate_keyboard_sound(self, keystrokes):
        """Simulate keyboard sound"""
        audio = []
        
        for keystroke in keystrokes:
            # Generate key sound
            frequency = self.get_key_frequency(keystroke)
            duration = 0.1  # 100ms
            
            # Generate sinusoidal wave
            t = np.linspace(0, duration, int(self.sample_rate * duration))
            wave = np.sin(2 * np.pi * frequency * t)
            
            # Add envelope
            envelope = np.exp(-t * 10)
            wave *= envelope
            
            # Add noise
            noise = np.random.normal(0, 0.1, len(wave))
            wave += noise
            
            audio.extend(wave)
        
        return np.array(audio)
    
    def get_key_frequency(self, key):
        """Get key frequency"""
        # Simplified key to frequency mapping
        key_frequencies = {
            'a': 440, 'b': 466, 'c': 523, 'd': 587, 'e': 659,
            'f': 698, 'g': 784, 'h': 880, 'i': 988, 'j': 1047,
            'k': 1175, 'l': 1319, 'm': 1397, 'n': 1568, 'o': 1760,
            'p': 1976, 'q': 2093, 'r': 2349, 's': 2637, 't': 2794,
            'u': 3136, 'v': 3520, 'w': 3951, 'x': 4186, 'y': 4699,
            'z': 5274
        }
        
        return key_frequencies.get(key.lower(), 440)
    
    def analyze_audio(self, audio):
        """Analyze audio to extract information"""
        # Fourier transform
        fft = np.fft.fft(audio)
        frequencies = np.fft.fftfreq(len(audio), 1/self.sample_rate)
        
        # Find peaks
        peaks = []
        for i in range(1, len(fft) - 1):
            if abs(fft[i]) > abs(fft[i-1]) and abs(fft[i]) > abs(fft[i+1]):
                if abs(fft[i]) > np.mean(np.abs(fft)) + 2 * np.std(np.abs(fft)):
                    peaks.append(frequencies[i])
        
        return peaks
    
    def visualize_audio_spectrum(self, audio, title="Audio Spectrum"):
        """Visualize audio spectrum"""
        fft = np.fft.fft(audio)
        frequencies = np.fft.fftfreq(len(audio), 1/self.sample_rate)
        
        plt.figure(figsize=(12, 6))
        plt.plot(frequencies[:len(frequencies)//2], np.abs(fft[:len(fft)//2]))
        plt.title(title)
        plt.xlabel("Frequency (Hz)")
        plt.ylabel("Amplitude")
        plt.grid(True)
        plt.show()

# Usage example
acoustic_analysis = AcousticAnalysis()
keystrokes = ['p', 'a', 's', 's', 'w', 'o', 'r', 'd']
audio = acoustic_analysis.simulate_keyboard_sound(keystrokes)
peaks = acoustic_analysis.analyze_audio(audio)
print(f"Detected audio peaks: {peaks}")

Countermeasures

Protection against Timing Attacks

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class TimingAttackProtection:
    def __init__(self):
        self.dummy_operations = []
    
    def constant_time_compare(self, a, b):
        """Constant-time comparison"""
        if len(a) != len(b):
            return False
        
        result = 0
        for x, y in zip(a, b):
            result |= x ^ y
        
        return result == 0
    
    def constant_time_select(self, condition, true_value, false_value):
        """Constant-time selection"""
        mask = -1 if condition else 0
        return (true_value & mask) | (false_value & ~mask)
    
    def add_dummy_operations(self, operation):
        """Add dummy operations to mask time"""
        # Add operations that don't affect the result
        dummy = 0
        for i in range(1000):
            dummy += i * i
        return operation

# Usage example
protection = TimingAttackProtection()
result = protection.constant_time_compare(b"hello", b"hello")
print(f"Secure comparison: {result}")

Protection against Power Analysis

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class PowerAnalysisProtection:
    def __init__(self):
        self.masking_enabled = True
    
    def masked_aes_operation(self, data, mask):
        """Masked AES operation"""
        if not self.masking_enabled:
            return data
        
        # Apply mask
        masked_data = data ^ mask
        
        # Perform operation
        result = self.aes_operation(masked_data)
        
        # Remove mask
        return result ^ mask
    
    def aes_operation(self, data):
        """Simplified AES operation"""
        # Simplified implementation
        return data
    
    def randomize_execution_order(self, operations):
        """Randomize execution order"""
        import random
        return random.sample(operations, len(operations))

# Usage example
power_protection = PowerAnalysisProtection()
mask = 0xAA
data = 0x55
result = power_protection.masked_aes_operation(data, mask)
print(f"Masked operation: {result}")
  • Cryptanalysis - Discipline that includes side-channel attacks
  • AES - Algorithm vulnerable to side-channel attacks
  • RSA - Algorithm vulnerable to side-channel attacks
  • ECC - Algorithm vulnerable to side-channel attacks
  • HSM - Device that protects against side-channel attacks
  • CISO - Role that oversees side-channel attacks
  • General Cybersecurity - Discipline that includes side-channel attacks
  • Security Breaches - Incidents caused by side-channel attacks
  • Attack Vectors - Side-channel attacks as a vector
  • Incident Response - Process that includes side-channel attacks
  • SIEM - System that detects side-channel attacks
  • SOAR - Automation that responds to side-channel attacks
  • EDR - Tool that protects against side-channel attacks
  • Firewall - Device that complements protection against side-channel attacks
  • VPN - Connection that can be vulnerable to side-channel attacks
  • Dashboards - Visualization of side-channel attack metrics
  • Logs - Side-channel attack logs

References