针对基于深度学习的侧信道分析(DLSCA)进行超参数的贝叶斯优化
一、随机搜索和贝叶斯优化简介
在深度学习的侧信道分析(DLSCA)中,超参数选择直接影响模型攻击效果。传统网格搜索需要遍历所有可能组合,计算成本极高。随机搜索通过随机采样参数空间,虽然效率高于网格搜索,但仍需大量尝试才能找到较优解。
贝叶斯优化通过构建目标函数的概率模型,实现高效参数搜索。它使用高斯过程代理模型估计目标函数分布,根据采集函数选择最可能提升的区域进行评估。这种方法特别适合计算成本高的目标函数评估场景。
在DLSCA中,每次模型训练和评估都需要消耗大量计算资源。贝叶斯优化通过智能探索-利用平衡,能在较少尝试次数内找到更优参数组合。本文使用scikit-optimize库实现贝叶斯优化,核心是gp_minimize函数。它定义参数空间(包括网络层数、神经元数量、学习率等),通过高斯过程建模目标函数,逐步收敛到最优解。
二、猜测熵作为优化指标
在传统监督学习中,验证集损失作为优化目标很直观。但在DLSCA中,最终目标是恢复密钥而非最小化预测误差,因此需要直接优化侧信道分析的核心指标:猜测熵和成功率。猜测熵和成功率的定义可
猜测熵可以量化密钥被正确识别的难度,它表示真实密钥在攻击者排序列表中的平均位置,猜测熵越低,表示密钥越容易被识别。当猜测熵降为0时,表示真实密钥被完全识别。
本文创新性地将猜测熵作为贝叶斯优化的目标函数。在优化过程中,对每个超参数组合,训练模型后计算其在验证集上的猜测熵。该指标直接反映模型恢复密钥的能力,与侧信道分析的最终目标高度一致。
具体实现中,我们采用增量式计算策略:随着训练曲线增加,持续跟踪猜测熵变化。当连续三次迭代猜测熵不再降低时提前停止训练,平衡计算效率和模型性能。
三、Python代码实现
3.1 数据加载与预处理函数
load_trs_to_dict函数处理侧信道分析特有的.trs格式数据文件。该格式包含能量曲线数据和加密参数(如明文)。函数使用trsfile库解析文件头信息、能量曲线和加密参数,返回结构化字典。
get_intermediate_values函数计算AES加密的S盒输出中间值。基于目标字节位置和真实密钥字节,生成真实中间值向量和假设中间值矩阵(256种密钥假设)。这是侧信道攻击的关键步骤,将物理泄露与密钥信息关联。
数据预处理流程:首先加载.trs文件,提取能量曲线和明文。然后选择目标字节位置,生成中间值标签。最后分割数据集为训练集和验证集,为模型训练做好准备。
3.2 评估指标函数
metric_guess_entropy函数实现增量式猜测熵计算。核心思想是:随着攻击曲线增加,跟踪密钥排名变化。使用对数似然估计累积密钥假设概率,排序后确定真实密钥位置。
函数采用分块处理策略,避免内存溢出。关键技巧:设置概率下限(1e-8)防止数值下溢。当真实密钥排名连续达到最优时提前终止计算,显著提升效率。
该指标直接反映模型恢复密钥的能力。猜测熵越低表示攻击效果越好,0表示完全恢复密钥。这使其成为比验证损失更直接的优化目标。
3.3 贝叶斯优化核心函数
tune函数实现基于猜测熵的贝叶斯优化框架。定义五维参数空间:网络层数(2-6)、神经元数量(50-200)、学习率(1e-5至1e-2)、批大小(64-256)和激活函数。
objective内部函数是优化目标:对每组参数,构建MLP模型并训练。采用动态早停策略——当猜测熵连续三次未提升时停止训练。gp_minimize函数管理优化过程,执行50次评估(含10次随机初始点)。
优化流程:随机初始化参数→训练模型→计算猜测熵→更新高斯过程模型→选择下一个评估点。最终输出最佳参数组合,包括各层神经元数、学习率等关键超参数。
3.4 实验执行函数
test函数整合完整实验流程。设置目标字节位置(0)和训练曲线数量(50000)。加载.trs文件后,提取能量曲线和明文数据。
关键步骤:通过get_intermediate_values生成中间值标签。分割数据集后调用tune函数启动优化。日志记录所有评估结果和最终最优参数。
具体代码如下:
"""
针对基于深度学习的侧信道分析(DLSCA)进行超参数的贝叶斯优化
泄漏模型:S盒输出值
运行前先激活Python环境,执行pip install numpy trsfile scikit-optimize命令安装所需的库
另外参考另一篇文章搭建深度学习环境: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 logging
import numpy as np
from keras import optimizers
from keras.models import Sequential
from keras.layers import Input, Dense
from skopt import gp_minimize # 贝叶斯优化(高斯过程)
from skopt.space import Real, Integer, Categorical # 参数空间定义
from skopt.utils import use_named_args # 参数命名空间装饰器
from trsfile import trs_open # 用于读取.trs格式的侧信道数据文件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):"""计算AES的中间值(第一轮轮函数的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, hypodef metric_guess_entropy(hypo, real_key, predict, index):"""计算猜测熵(Guess Entropy)评估模型性能参数:hypo: 验证集的假设中间值矩阵real_key: 真实密钥字节predict: 模型预测的概率分布 (N x 256)index: 评估点列表(按trace数量递增)返回:key_rank_mean: 平均密钥排名(越小越好)"""key_rank_list = []guess_key_likelihood = np.zeros((256)) # 累积密钥似然值key_rank_zero = False # 标记真实密钥是否曾排名第一# 分段评估猜测熵for i in range(len(index) - 1):# 获取当前评估区间的数据块hypo_chunk = hypo[index[i]:index[i + 1]]predict_chunk = predict[index[i]:index[i + 1]]# 获取对应中间值的预测概率(高级索引)likelihood_matrix = predict_chunk[np.arange(predict_chunk.shape[0])[:, None], hypo_chunk]# 处理数值稳定性:替换过小值为阈值min_nzero = min(min(likelihood_matrix[likelihood_matrix >= 1e-8]), 1e-4)likelihood_matrix = np.where(likelihood_matrix < 1e-8, min_nzero, likelihood_matrix)# 计算对数似然并累积likelihood = np.sum(np.log(likelihood_matrix), axis=0)guess_key_likelihood = guess_key_likelihood + likelihood# 获取当前密钥排名sorted_index = np.argsort(guess_key_likelihood)[::-1]key_rank = np.where(sorted_index == real_key)[0][0]key_rank_list.append(key_rank)# 提前终止条件:连续两次排名第一if key_rank == 0 and key_rank_zero:breakkey_rank_zero = key_rank == 0key_rank_mean = np.mean(key_rank_list)return key_rank_meandef tune(traces_train, traces_val, label_train, hypo_val, real_key):"""执行贝叶斯超参数优化参数:traces_train: 训练集能量曲线traces_val: 验证集能量曲线label_train: 训练集标签(真实中间值)hypo_val: 验证集假设中间值矩阵real_key: 真实密钥字节"""epochs_top = 300 # 最大训练轮数# 猜测熵评估点(不能超过验证集trace数量)guess_entropy_index = [0, 2, 5, 10, 20, 30, 40, 50, 60, 80, 100]# 定义超参数搜索空间dimensions = [Integer(2, 6, name='layer_n'), # 网络层数Integer(50, 200, name='units'), # 每层神经元数Real(10 ** -5, 10 ** -2, prior='log-uniform', name='lr'), # 学习率(对数均匀分布)Integer(64, 256, name='batch_size'), # 批大小Categorical(['relu', 'elu', 'tanh'], name='activation') # 激活函数]# 目标函数(使用命名参数)@use_named_args(dimensions=dimensions)def objective(layer_n, units, lr, batch_size, activation):"""贝叶斯优化的目标函数:构建并评估模型"""# 构建全连接神经网络model = Sequential()model.add(Input(shape=(traces_train.shape[1],)))for _ in range(layer_n):model.add(Dense(units, activation=activation))model.add(Dense(256, activation='softmax'))model.compile(optimizer=optimizers.Adam(learning_rate=lr),loss='sparse_categorical_crossentropy' # 稀疏分类交叉熵)# 训练过程控制变量max_epochs, max_metric = 0, 0 # 记录最佳epoch和指标epochs_accum = 0 # 累计训练轮数decline_n = 0 # 指标连续下降次数# 分段训练策略(30+10*N轮)while epochs_accum < epochs_top:epochs = 30 if epochs_accum == 0 else 10 # 初始30轮,后续每次10轮# 训练模型(静默模式)model.fit(traces_train, label_train, epochs=epochs, batch_size=batch_size, verbose=0)# 验证集预测predict = model.predict(traces_val, verbose=0)# 计算猜测熵指标(平均密钥排名)metric = metric_guess_entropy(hypo_val, real_key, predict, guess_entropy_index)epochs_accum += epochs# 更新最佳指标if metric >= max_metric:max_metric = metricmax_epochs = epochs_accumdecline_n = 0else:decline_n += 1 # 指标下降# 早停条件:连续3次下降if decline_n == 3:break# 记录当前参数组合结果print(f"metric:{max_metric:.04f}\tlayer_n:{layer_n}\tunits:{units}\tLR:{lr:.06f}\tepochs:{max_epochs}\tbatch_size:{batch_size}\tactivation: {activation}")logging.info(f"metric:{max_metric:.04f}\tlayer_n:{layer_n}\tunits:{units}\tLR:{lr:.06f}\tepochs:{max_epochs}\tbatch_size:{batch_size}\tactivation: {activation}")return max_metric# 执行贝叶斯优化result = gp_minimize(func=objective,dimensions=dimensions,n_calls=50, # 总评估次数n_random_starts=10, # 随机初始点数量random_state=42, # 随机种子verbose=True # 显示优化过程)# 输出最佳参数组合result_x = f"最佳参数组合:layer_n={result.x[0]} units={result.x[1]} learning_rate={result.x[2]} batch_size={result.x[3]} activation={result.x[4]}"print(result_x)logging.info(result_x)def test():"""执行端到端的超参数优化流程"""target_byte = 0 # 攻击目标字节位置(0-15)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_train, traces_val = traces[:traces_train_n], traces[traces_train_n:]label_train, hypo_val = actual[:traces_train_n], hypo[traces_train_n:]# 执行超参数优化tune(traces_train, traces_val, label_train, hypo_val, mkey[target_byte])if __name__ == '__main__':# 配置日志记录logging.basicConfig(filename='tune_results.txt',level=logging.INFO,format='[%(asctime)s]\t%(message)s')test()
四、实验结果
结过50次评估(含10次随机初始点),得到的最优参数如下,我们可以利用优化后的参数进行DLSCA,参考另一篇文章:基于深度学习的侧信道分析(DLSCA)Python实现(带测试)。
layer_n(隐藏层数):2
units(单元数):185
LR(学习率):0.000886
epochs(迭代次数):210
batch_size(批量大小):70
activation(激活函数): elu
最终的实验结果如下,可以看到,经过贝叶斯优化,我们得到了一组攻击效果非常好的参数。
五、总结
本文提出了一种基于猜测熵的贝叶斯优化方法,用于深度学习的侧信道分析(DLSCA)超参数调优。不同于传统以验证损失为目标的优化,该方法直接优化侧信道分析的核心指标——猜测熵,能更有效地恢复密钥。通过定义五维参数空间(网络层数、神经元数量、学习率、批大小和激活函数),采用高斯过程建模目标函数,实现了高效的参数搜索。实验使用STM32F芯片的AES功耗数据,结果显示优化后的模型仅需6条曲线即可完全恢复密钥。