当前位置: 首页 > news >正文

基于深度学习的侧信道分析(DLSCA)Python实现(带测试)

一、DLSCA原理介绍

基于深度学习的侧信道分析(DLSCA)是一种结合深度神经网络与侧信道分析技术的密码分析方法。该方法利用深度学习模型从能量消耗、电磁辐射等侧信道信息中提取与密钥相关的特征模式。相比传统分析方法,DLSCA能够自动学习复杂的特征关系,无需人工设计区分函数,具有更强的特征提取能力和更高的攻击效率。

DLSCA的核心思想是将侧信道分析建模为分类问题。在本代码中,攻击目标是通过分析AES加密过程中泄露的功耗信息恢复密钥。具体采用S盒输出值作为标签,因为微处理器处理不同数据时消耗的功率与S盒输出的具体值密切相关。神经网络通过学习功耗曲线与S盒输出之间的映射关系,可以预测不同密钥假设下的中间值概率分布。

攻击过程分为训练和测试两个阶段。训练阶段使用已知密钥的功耗曲线训练神经网络模型,使其能够准确预测S盒输出值。测试阶段则利用训练好的模型评估不同密钥假设的可能性,通过统计方法计算各密钥假设的似然分数,最终确定最可能的密钥值。这种方法能够有效处理噪声较大的侧信道数据,且对时间对齐的要求相对较低。

二、代码结构与功能解析

1. 数据加载与预处理函数

load_trs_to_dict函数负责读取.trs格式的侧信道数据文件,这种格式专门用于存储能量曲线数据。该函数将文件头信息、能量曲线样本和相关参数解析为结构化字典,便于后续处理。函数支持指定读取的能量曲线范围,可灵活控制数据量大小,这对大规模数据集的处理尤为重要。

get_intermediate_values函数根据AES算法特性生成中间值矩阵。它接收明文数据、目标字节位置和真实密钥字节作为输入,计算真实中间值向量和假设中间值矩阵。至于为什么不再使用汉明重量模型,是因为神经网络具有强大的特征提取能力,可以直接找到能量曲线与S盒输出之间的非线性关系,使用汉明重量模型会反而导致数据分布不平衡。

2. 模型训练与评估函数

train_model函数构建并训练一个多层感知器(MLP)神经网络模型。模型采用ReLU激活函数的隐藏层和softmax输出层,适合处理多分类问题。训练使用Adam优化器和稀疏分类交叉熵损失函数,能够有效学习功耗曲线与S盒输出值的复杂非线性关系。模型结构经过精心设计,在表达能力和训练效率之间取得了良好平衡。

ProbCalculator类实现密钥假设的似然概率计算。它维护一个256维的向量记录各密钥假设的累积对数似然值。update方法将模型预测概率与假设中间值结合,更新密钥似然值。这种增量式计算方式使得算法能够高效处理大规模数据集,同时保持数值稳定性。

3. 攻击执行与结果分析函数

single_attack函数执行单次密钥恢复攻击。它分批处理能量曲线数据,利用概率计算器累积各密钥假设的证据,并跟踪真实密钥的排名变化。这种分批次处理策略不仅节省内存,还能观察攻击效果随曲线数量增加的变化趋势,为评估攻击效率提供详细数据。

guess_entropyshow_result函数共同完成结果分析与可视化。前者计算猜测熵和成功率两个关键指标,评估不同数量能量曲线下的攻击效果;后者将结果绘制成直观的曲线图,便于观察攻击性能随曲线数量增加的变化规律。这些分析工具为评估侧信道防护措施的有效性提供了量化依据。

具体代码如下:

"""
对AES算法的软件实现进行基于深度学习的侧信道分析(DLSCA)的Python代码
泄漏模型:S盒输出值
运行前先激活Python环境,执行pip install numpy tqdm trsfile matplotlib命令安装所需的库
另外参考另一篇文章搭建深度学习环境:https://blog.csdn.net/weixin_43261410/article/details/148606456?spm=1011.2415.3001.5331
'AES_POWER_STM32F_NO-PROTECT_60000.trs'是功耗曲线文件,请点击https://download.csdn.net/download/weixin_43261410/91057055免费下载
查看.trs格式文件介绍请浏览https://blog.csdn.net/weixin_43261410/article/details/148737286?fromshare=blogdetail&sharetype=blogdetail&sharerId=148737286&sharerefer=PC&sharesource=weixin_43261410&sharefrom=from_link
"""import os
import numpy as np
from keras import optimizers
from keras.models import Sequential
from keras.layers import Input, Dense
from tqdm import tqdm  # 用于显示进度条
from trsfile import trs_open  # 用于读取.trs格式的侧信道数据文件
import matplotlib.pyplot as plt  # 用于画图AES_SBOX = np.array([0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
], dtype=np.uint8)def load_trs_to_dict(trs_file_path: str, start_trace: int = None, end_trace: int = None):"""加载.trs文件到字典结构中参数:trs_file_path: .trs文件路径start_trace: 起始能量曲线索引(可选)end_trace: 结束能量曲线索引(可选)返回:包含头信息、能量曲线数据和额外参数的字典"""header, traces, param = {}, [], {}with trs_open(trs_file_path, 'r') as trs_file:# 读取文件头信息for key, value in trs_file.get_headers().items():header[key.name] = value# 处理能量曲线范围number_traces = header['NUMBER_TRACES']start_trace = 0 if start_trace is None else min(start_trace, number_traces - 1)end_trace = number_traces if end_trace is None else min(end_trace, number_traces)header['NUMBER_TRACES'] = end_trace - start_trace# 初始化能量曲线数据数组traces = np.zeros((header['NUMBER_TRACES'], header['NUMBER_SAMPLES']), dtype=np.float32)# 初始化参数数组param = {}for key in trs_file[0].parameters:param_len = len(trs_file[0].parameters[key].value)param[key] = np.zeros((header['NUMBER_TRACES'], param_len), dtype=np.uint8)# 读取能量曲线数据和参数for i, trace in enumerate(trs_file[start_trace:end_trace]):for key in trace.parameters:param[key][i] = trace.parameters[key].valuetraces[i] = np.array(trace.samples)# 返回结构化数据trs_dict = {"HEADER": header,"TRACES": traces,"PARAM": param}return trs_dictdef get_intermediate_values(plaintext, target_byte, real_key):"""生成中间值(S盒输出值)矩阵参数:plaintext: 明文数组 (N x 16)target_byte: 目标字节位置(0-15)real_key: 真实密钥字节返回:actual: 真实中间值向量(长度等于能量曲线数量)hypo: 假设中间值矩阵(能量曲线数量 × 256种密钥假设)"""# 计算真实的S盒输入actual_sbox_in = plaintext[:, target_byte] ^ real_key# 计算S盒输出actual = AES_SBOX[actual_sbox_in]# 生成256种可能的密钥假设hypo_key = np.array(range(256), np.uint8)# 计算S盒输入hypo_sbox_in = np.array([pt ^ hypo_key for pt in plaintext[:, target_byte]], np.uint8)# 计算S盒输出hypo = AES_SBOX[hypo_sbox_in]return actual, hypoclass ProbCalculator():"""计算密钥假设的似然概率"""def __init__(self):# 初始化密钥假设的似然累加器,256个可能的密钥假设self.guess_key_likelihood = np.zeros((256))def update(self, predict, hypo):"""更新似然值参数:predict: 当前批次的概率值hypo: 当前批次的假设中间值"""# 根据假设中间值获取对应的概率值likelihood_matrix = predict[np.arange(predict.shape[0])[:, None], hypo]# 处理零概率值,避免对数运算出错min_nzero = min(min(likelihood_matrix[likelihood_matrix != 0]), 0.01)likelihood_matrix = np.where(likelihood_matrix == 0, min_nzero ** 2, likelihood_matrix)# 计算对数似然并累加guess_key_likelihood = np.sum(np.log(likelihood_matrix), axis=0)self.guess_key_likelihood = self.guess_key_likelihood + guess_key_likelihooddef __call__(self):"""返回当前似然值向量"""return self.guess_key_likelihooddef train_model(traces, actual, learning_rate=0.0001, epochs=200, batch_size=200):"""训练MLP模型参数:traces: 用于训练MLP模型的能量曲线actual: 每条曲线对应的真实汉明重量learning_rate: 学习率epochs: 迭代次数batch_size: 批处理大小返回:model: MLP模型"""# 创建多层感知机模型model = Sequential()model.add(Input(shape=(traces.shape[1],)))model.add(Dense(200, activation='relu'))  # 5个隐藏层,每层200个神经元model.add(Dense(200, activation='relu'))model.add(Dense(200, activation='relu'))model.add(Dense(200, activation='relu'))model.add(Dense(200, activation='relu'))model.add(Dense(256, activation='softmax'))  # 输出层256个神经元,对应256种汉明重量可能性# 使用Adam优化器和分类交叉熵损失函数optimizer = optimizers.Adam(learning_rate)model.compile(optimizer=optimizer, loss='sparse_categorical_crossentropy', metrics=['accuracy'])# 训练模型model.fit(traces, actual, epochs=epochs, batch_size=batch_size)return modeldef single_attack(predict_matrix, hypo, real_key, index):"""执行单次攻击参数:predict_matrix: 模型预测的概率值数据hypo: 假设中间值数据real_key: 真实密钥字节(用于评估攻击效果)index: 分批处理的索引列表返回:key_rank_list: 每次更新后真实密钥的排名列表"""# 初始化概率计算器probability = ProbCalculator()key_rank_list = []# 分批处理能量曲线数据for i in range(len(index) - 1):# 获取当前批次的概率和中间值start_idx = index[i]end_idx = index[i + 1]current_predict = predict_matrix[start_idx:end_idx]current_hypo = hypo[start_idx:end_idx]# 更新密钥似然值probability.update(current_predict, current_hypo)# 获取当前所有密钥假设的似然值prob = probability()# 计算密钥排名(似然值越小排名越高)prob_sort = np.argsort(np.argsort(-prob))# 记录真实密钥的当前排名key_rank = prob_sort[real_key]key_rank_list.append(key_rank)return key_rank_listdef guess_entropy(traces, hypo, model, real_key, result_path='result.txt', repeat_n=100, sample=500, header='', step=1):"""计算猜测熵和成功率参数:traces: 能量曲线数据hypo: 假设中间值数据model: MLP模型real_key: 真实密钥字节result_path: 结果保存路径repeat_n: 重复实验次数sample: 每次实验使用的能量曲线数量header: 结果文件头部信息step: 能量曲线数量增加的步长"""def get_value_n(arr, value, traces_number, less):"""辅助函数: 获取达到特定值所需的最少能量曲线数量"""if less:mask = arr <= valueelse:mask = arr >= valuevalid_index = np.cumprod(mask[::-1])[::-1]valid_index = np.where(valid_index)[0]return traces_number[valid_index[0]] if len(valid_index) > 0 else None# 设置分批索引列表index = [i for i in range(0, sample, step)]index.append(sample)# 准备随机排列key_rank_arr = []permutations = np.zeros((repeat_n, sample), dtype=int)for i in range(repeat_n):permutations[i] = np.random.permutation(traces.shape[0])[:sample]# 获取模型对所有能量曲线的预测结果predict_matrix = model.predict(traces, verbose=0)# 多次重复实验for i in tqdm(range(repeat_n)):hypo_random = hypo[permutations[i]]predict_random = predict_matrix[permutations[i]]key_rank_list = single_attack(predict_random, hypo_random, real_key, index)key_rank_arr.append(key_rank_list)key_rank_arr = np.array(key_rank_arr)traces_number = np.array(index[1:])# 计算猜测熵guess_entropy = np.mean(key_rank_arr, axis=0)guess_entropy_10 = get_value_n(guess_entropy, 10, traces_number, True)guess_entropy_1 = get_value_n(guess_entropy, 1, traces_number, True)guess_entropy_0 = get_value_n(guess_entropy, 0, traces_number, True)# 计算成功率success_rate = np.mean(key_rank_arr < 10, axis=0)  # 排名小于10被认为攻击成功success_rate_p9 = get_value_n(success_rate, 0.9, traces_number, False)success_rate_p99 = get_value_n(success_rate, 0.99, traces_number, False)success_rate_1 = get_value_n(success_rate, 1, traces_number, False)# 保存结果到文件mode = 'a' if os.path.exists(result_path) else 'w'with open(result_path, mode, encoding='utf-8') as f:f.write(header)trs_n = ' '.join(f"{int(x)}" for x in traces_number)f.write('traces number: ' + trs_n + '\n')success_rate = ' '.join(f"{x:.2f}" for x in success_rate)f.write('success rate: ' + success_rate + '\n')guess_entropy = ' '.join(f"{x:.2f}" for x in guess_entropy)f.write('guess entropy: ' + guess_entropy + '\n')f.write(f'tips: When guess entropy equals 10, 1, or 0, the corresponding traces number are {guess_entropy_10}, {guess_entropy_1}, and {guess_entropy_0}.\n')f.write(f'tips: When success rate equals 0.9, 0.99, or 1, the corresponding traces number are {success_rate_p9}, {success_rate_p99}, and {success_rate_1}.\n\n')def show_result():"""可视化攻击结果从result.txt文件中读取数据并绘制成功率与猜测熵曲线"""result_path = 'result.txt'# 读取结果文件with open(result_path, 'r', encoding='utf-8') as f:lines = f.readlines()# 初始化数据列表traces = []success_rate = []guess_entropy = []# 解析结果文件for line in lines:if line.startswith('traces number:'):traces = list(map(int, line.split(':')[1].strip().split()))elif line.startswith('success rate:'):success_rate = list(map(float, line.split(':')[1].strip().split()))elif line.startswith('guess entropy:'):guess_entropy = list(map(float, line.split(':')[1].strip().split()))# 检查是否成功读取数据if not traces or not success_rate or not guess_entropy:print("Error: Could not find valid data in result file.")return# 创建图形和双轴fig, ax1 = plt.subplots(figsize=(12, 6))# 绘制成功率曲线(左轴,蓝色)color = 'tab:blue'ax1.set_xlabel('Number of Traces', fontsize=12)ax1.set_ylabel('Success Rate', color=color, fontsize=12)ax1.plot(traces, success_rate, color=color, label='Success Rate', linewidth=2)ax1.tick_params(axis='y', labelcolor=color)ax1.grid(True, linestyle='--', alpha=0.7)ax1.set_ylim(0, 1.05)  # 设置成功率y轴范围# 创建第二个y轴用于猜测熵ax2 = ax1.twinx()color = 'tab:red'ax2.set_ylabel('Guess Entropy', color=color, fontsize=12)ax2.plot(traces, guess_entropy, color=color, label='Guess Entropy', linewidth=2)ax2.tick_params(axis='y', labelcolor=color)ax2.set_ylim(0, 256)  # 固定猜测熵y轴范围为0-256# 添加图例lines1, labels1 = ax1.get_legend_handles_labels()lines2, labels2 = ax2.get_legend_handles_labels()ax1.legend(lines1 + lines2, labels1 + labels2, loc='upper right', fontsize=10)# 添加标题plt.title('DLSCA (MLP) Attack Performance: Success Rate and Guess Entropy', fontsize=14, pad=20)# 调整布局并保存图像(同时保存svg格式和png格式)fig.tight_layout()plt.savefig('result.svg', format='svg', bbox_inches='tight')plt.savefig('result.png', dpi=600, bbox_inches='tight')plt.show()def test():"""测试函数:执行完整的DLSCA流程"""target_byte = 0  # 攻击的字节位置(0表示第一个字节)traces_train_n = 50000  # 用于训练模型的曲线数量# 主密钥(16字节)mkey = [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10]# 加载能量曲线数据trs_path = 'AES_POWER_STM32F_NO-PROTECT_60000.trs'trs_dict = load_trs_to_dict(trs_path)traces, plaintext = trs_dict['TRACES'], trs_dict['PARAM']['plaintext']# 生成真实中间值向量和假设中间值矩阵actual, hypo = get_intermediate_values(plaintext, target_byte, mkey[target_byte])# 分割数据集traces_build, traces_match = traces[:traces_train_n], traces[traces_train_n:]actual_build, hypo_match = actual[:traces_train_n], hypo[traces_train_n:]# 训练MLP模型learning_rate, epochs,  batch_size = 0.0001, 200, 200model = train_model(traces_build, actual_build, learning_rate, epochs,  batch_size)# 设置实验参数repeat_n = 100  # 重复次数sample = 100  # 每次实验使用的能量曲线数量step = 1  # 能量曲线数量增加的步长# 执行猜测熵分析guess_entropy(traces_match, hypo_match, model, mkey[target_byte], repeat_n=repeat_n, sample=sample, step=step)# 可视化结果show_result()if __name__ == '__main__':test()

三、实验结果

实验使用STM32F系列微控制器采集的AES加密功耗数据进行测试。数据集包含60,000条能量曲线,其中50,000条用于训练神经网络模型,剩余10,000条用于评估攻击效果。结果显示,经过训练的MLP模型能够有效学习功耗与S盒输出之间的潜在关系,在足够数量的能量曲线下成功恢复密钥字节。关键性能指标显示,当使用约4条能量曲线时,攻击成功率可达90%以上;随着曲线数量增加,猜测熵迅速下降并最终归零,表明密钥被确定性地恢复。实验结果验证了DLSCA方法的高效性,同时也揭示了神经网络在侧信道分析中的强大特征提取能力。

四、总结

基于深度学习的侧信道分析(DLSCA)是一种结合深度神经网络与侧信道分析技术的密码分析方法。该方法利用深度学习模型从能量消耗、电磁辐射等侧信道信息中提取与密钥相关的特征模式。相比传统分析方法,DLSCA能够自动学习复杂的特征关系,无需人工设计区分函数,具有更强的特征提取能力和更高的攻击效率。DLSCA的核心思想是将侧信道分析建模为分类问题。在本代码中,攻击目标是通过分析AES加密过程中泄露的功耗信息恢复密钥。具体采用S盒输出值作为标签,因为微处理器处理不同数据时消耗的功率与S盒输出的具体值密切相关。代码实现了数据加载、中间值生成、模型训练和攻击评估等完整流程,采用MLP模型分析功耗数据,通过猜测熵和成功率量化攻击效果。实验结果表明,该方法能高效恢复密钥,仅需约4条功耗曲线即可达到90%以上的成功率,展现了深度学习在侧信道分析中的强大能力。

相关文章:

  • Vscode 编写Markdown支持 plantuml书写
  • 「Linux文件及目录管理」输入输出重定向与管道
  • 2025年渗透测试面试题总结-2025年HW(护网面试) 04(题目+回答)
  • Node.js 实训专栏规划目录
  • 中文文本相似度分析指标:BERTScore
  • XCUITest + Objective-C 详细示例
  • Python 的内置函数 hash
  • “组学”的数据结构与概念
  • 【STM32笔记】F1F4 STM32初识、MDK调试、HAL简介
  • 计算机网络第九章——数据链路层《流量控制和可靠传输》
  • 《信号与系统》第 10 章 z变换
  • 腾讯云IM即时通讯:开启实时通信新时代
  • LLM-201: OpenHands与LLM交互链路分析
  • 一,python语法教程.内置API
  • 软件工程概述知识点总结
  • 软件工程期末试卷简答题版带答案(共21道)
  • 每天一个前端小知识 Day 8 - 网络通信与前端性能优化
  • Linux--迷宫探秘:从路径解析到存储哲学
  • FastGPT:开启大模型应用新时代(4/6)
  • 分享两个可以一键生成sql server数据库 html格式巡检报告的脚本
  • 无锡微网站制作/营销方案策划
  • 网站建设与维护流程图/全网营销推广是什么
  • 做高仿包的网站有哪些/短视频平台推广
  • 做网站页面报价/常州seo外包公司
  • 备案需要网站吗/推广代理平台登录
  • 福州市建设厅网站/北京百度seo公司