简介DA PUF库Defense/Attack PUF Library是一套面向硬件安全与物理不可克隆函数研究者的开源代码集合由Durga Prasad Sahoo等学者发布针对仲裁器PUF、异或仲裁器PUF及Interpose PUF的模拟与攻击场景。资源共765个文件压缩包约30.74MB内容涵盖MATLAB仿真与攻击脚本含Logistic回归、基于可靠性的CMA-ES攻击及k-junta测试、C#并行计算代码、Python深度神经网络攻击代码以及XOR Arbiter PUF的FPGA实现工程。其中MATLAB脚本完整实现了多种攻击流程C#工程支持并行加速Python部分可直接训练深度网络FPGA源码和位流文件便于硬件验证。目录按语言与功能模块组织包含m、mat、cs、py、vhd、bit等类型文件。这些代码可复现CHES 2019论文《The Interpose PUF》中的实验适合学习PUF建模攻击、安全性评估及硬件实现。已有403人学习下载适合密码学、硬件安全方向的中高级研究者或工程师作为算法对照与实验基线。该库特别适用于复现PUF安全性分析实验也可作为课程设计与科研入门的参考资料。1. 逻辑回归遇上PUF先弄懂这份DAPUF库解决什么问题做硬件安全相关的实验时最头疼的不是算法本身而是“拿什么数据来验证”。我去年评估一款仲裁器PUF的可预测性手头只有芯片的挑战-响应对CRP想跑一个逻辑回归基线结果从特征构造到Matlab训练脚本都得自己拼。DA_PUF_LibraryDA即Defense/Attack防护/攻击正是针对这个场景开源的PUF攻击评估库核心是逻辑回归训练Matlab代码围着“挑战-响应”数据做二分类建模用来快速判断一个PUF的响应位能否被机器学习预测。它适合两类人一类是刚接触PUF建模、想用现成代码跑通课程设计验证的另一类是评估PUF安全性、需要一份可复现基线数据的从业者。与其说它是一个库不如说它把“CRP数据准备-特征构造-逻辑回归训练-准确率评估”这条链路给你串好了。2. PUF建模攻击的原理为什么逻辑回归能当攻击武器2.1 从CRP讲起挑战-响应对才是建模的基础PUF物理不可克隆函数的核心特征在于每个芯片因为制造工艺偏差即使设计完全相同也会表现出独特的输入输出行为。它接受一个“挑战”Challenge作为输入产出一个“响应”Response这个挑战和响应对就构成CRP。理想情况下PUF的CRP映射是不可克隆的攻击者即使抓到了大量CRP样本也无法预测未知挑战的响应。但现实并不是那么理想。以仲裁器PUF为例挑战是一串比特经过一组级联的延时单元最终由仲裁器比较两条路径的延时快慢来决定响应位。这个过程的物理本质可以近似为一个带符号的累加模型每一级挑战比特决定该级路径的延时贡献方向仲裁结果取决于总的延时差。换句话说响应位并不对应一个复杂的非线性函数而更像“一系列延时系数加权求和后的符号”。攻击者不需要知道具体的物理延时只需要通过大量CRP学习到这个权重向量就能对新的挑战做出预测。这就是PUF建模攻击的逻辑起点。因此广义上的“攻击PUF”并不是物理剖开芯片而是通过机器学习构造一个替身模型让替身模型的CRP映射与原PUF高度重合。当拟合出的模型准确率达到90%甚至95%以上时认证系统里的PUF就已经可以被仿真替代了。比如某实验室的A同学做开题验证时正是先用这类建模方法证明了某类改进型PUF的响应可预测性不足才把设计方向转向XOR-PUF。对新手来说先理解“CRP是建模的数据资产”这一点后面的特征构造和训练才是顺理成章的。2.2 逻辑回归分类器把响应位看成二分类问题一个PUF可以同时出多位响应但建模时通常按响应位拆开处理每一位响应就是一个二分类标签。给定挑战向量x要预测响应位y是0还是1。逻辑回归模型的输出是P(y1|x) sigmoid(w^T x) 1 / (1 exp(-w^T x))训练过程用交叉熵损失配合梯度下降或拟牛顿法求解权重w。这一步在Matlab里并不复杂glmfit和fitclinear都可以做。关键是“特征向量x”怎么构造。原始挑战本身是0/1比特串如果直接把0/1丢给模型权重只能给出每一级“是否存在”的影响丢失了符号信息。仲裁器PUF中0和1表示延时的两种拼接方向应该映射成1和-1这样w的每个分量才对应那一级延时差的贡献方向w^T x就重建了总的延时差。我一般会在构建特征时做两件事一是把0/1映射到±1二是加上不同阶数的组合特征。为什么要加组合特征因为并非所有PUF都严格满足仲裁器那样的线性累加关系。像XOR-PUF或者带反馈的PUF单级挑战的影响之间存在交互低阶线性模型拟合不够。二阶特征比如任意两个挑战位的乘积可以在一定程度上捕捉这种交互关系。只是特征维度会从n上升到n(n1)/2存数据之前要先算好内存。2.3 为什么基线选逻辑回归而不是神经网络很多人一上来就用多层感知机做PUF建模准确率确实好看但代价是代价太高、可解释性太差。深层网络容易在有限CRP上过拟合而且那一堆权重很难看出PUF结构上的弱点。从安全评估的角度看基线模型应当是“最朴素但表现稳定的模型”。逻辑回归恰好满足参数规模小、训练时间短、Matlab原生支持好。仲裁器PUF的延时累加模型和逻辑回归的线性决策边界天然匹配。换句话说如果逻辑回归就能达到高准确率说明这个PUF响应与挑战之间存在近乎线性的映射那它在物理上就是欠安全的。反过来如果逻辑回归只能达到50%左右也不代表PUF绝对安全只是说简单线性模型学不到规律需要换非线性模型继续试探。在工程实操里逻辑回归还方便做“增量式验证”。我可以在同一个特征集上先跑一阶特征再跑二阶特征对比准确率上升幅度就能快速判断PUF的复杂度边界在哪。这套做法也会延续到第6章要说的自定义PUF型别评估中。记住一个判据先拿逻辑回归垫底如果它都打到了90%以上就没有必要再上深度学习去证明“可被攻击”了。3. DA_PUF_Library 运行准备从目录结构到第一批CRP数据3.1 库目录结构与核心文件拿到DA_PUF_Library之后我建议先不看算法先看目录。一个能落地的Matlab库目录结构基本决定了你用起来顺不顺手。常见组织方式是这样的目录/文件作用src/核心Matlab函数包括PUF仿真、特征构造、训练评估data/预生成的CRP数据样本通常是.mat文件scripts/一键运行入口脚本直接跑通全流程docs/说明文档和参数表格实际打开库时重点找三个脚本一个是CRP生成脚本负责产出挑战和响应一个是特征构造函数把原始挑战变成特征矩阵一个是训练评估脚本用逻辑回归出准确率和混淆矩阵。许多开源库的问题是“代码能跑但不知道改哪里”这里的应对办法是先从scripts跑通再回src改参数。3.2 生成CRP仿真测量与原始格式第一步是准备数据。如果你手头有真实PUF实体可以通过测量采集CRP如果没有大多数库里会内置PUF仿真器用模型产生挑战并计算响应。以64位挑战、单响应位为例生成一批CRP的Matlab代码大致如下% generate_crp.m rng(2024); % 固定随机种子保证可复现 n_crp 20000; % 采样20000条CRP bit_len 64; % 挑战长度64位 challenges randi([0 1], n_crp, bit_len); % 随机挑战矩阵 % 调用PUF仿真函数返回响应列向量 % puf_arbiter_sim是库中内置的仲裁器PUF仿真函数 responses puf_arbiter_sim(challenges); % 保存为.mat后面训练直接用 save(data/crp_dataset.mat, challenges, responses);这段代码的重点是仿真器的接口约定挑战用0/1矩阵表示每一行是一条CRP列表示挑战的每一位响应返回的是0/1列向量长度等于CRP数量。仿真时的稳定性和参数控制靠puf_arbiter_sim内部的延时系数不同PUF型别这里会不同。如果你用的是真实芯片采集就不能一次性生成这么多雷同数据。实际采集中同一个挑战需要多次测量取多数投票因为PUF响应存在噪声位。我见过有人在仿真数据上跑出99%准确率换到实测数据直接滑到80%原因就是仿真器没建模噪声。用库之前先在docs里看好仿真器有没有噪声参数。3.3 特征构造与标签对齐原始挑战不能直接喂给逻辑回归。以仲裁器PUF为例我要构造符合延时累加模型的特征。最简单的做法是% build_features.m function [X, y] build_features(challenges, responses) % challenges: N x L 的0/1矩阵 % responses: N x 1 的0/1矩阵 % X: N x L 的特征矩阵取值1/-1 % y: 标签列向量把0/1原样保留给逻辑回归 N size(challenges, 1); X challenges * 2 - 1; % 0 - -11 - 1 y responses(:); % 保持0/1标签不变 % 可选加入二阶交互特征内存开销大 % L size(challenges, 2); % X [X, X(:,1:end-1) .* X(:,2:end)]; % 相邻位乘积 end这里有个很容易踩的坑标签对齐。逻辑回归要求y是0/1二值而有些库里的响应仿真器返回的是±1直接混用会导致训练脚本不报错但结果全反。我在第二次跑这个库的时候就翻过车——准确率显示只有50%查了半天发现是仿真器内部把响应返回成了-1/1而我的训练脚本只认0/1。从那以后我每次读完数据都先做一次unique值检查确认标签取值再做特征构造。特征维度要提前算。一阶特征维度等于挑战长度64加二阶交互后变成6420162080。对20000条CRP来说维度还能接受但如果你把挑战长度加到128二阶特征就接近8000维Matlab矩阵存储会明显变慢。如果库附带的训练脚本跑得很慢优先检查是不是特征阶数开高了。4. 逻辑回归训练与评估Matlab代码里的关键参数4.1 训练脚本glmfit与交叉验证训练这一步我习惯分成三段划分数据、训练模型、评估泛化能力。Matlab里最直接的是glmfit适合普通大小的特征矩阵。代码骨架如下% train_lr.m load(data/crp_dataset.mat); [X, y] build_features(challenges, responses); % 划分训练集和测试集保持类别比例 cv cvpartition(y, HoldOut, 0.3, Stratify, true); idx_train cv.training; idx_test cv.test; % 训练逻辑回归二项分布logit连接函数 [b, dev, stats] glmfit(X(idx_train,:), y(idx_train), binomial, link, logit); % 预测测试集概率与标签 prob_test glmval(b, X(idx_test,:), logit); pred_test double(prob_test 0.5); % 计算准确率和混淆矩阵 acc mean(pred_test y(idx_test)); C confusionmat(y(idx_test), pred_test); fprintf(Test accuracy: %.2f%%\n, acc * 100);这里有几个参数要说明。cvpartition里HoldOut0.3意味着留30%做测试Stratify保证训练和测试集的正负样本比例一致。glmfit里的binomial指定了二分类模型linklogit指定sigmoid连接函数。stats结构体里包含系数的显著性信息可以顺带看一下哪些挑战位的权重在统计上不显著。不过glmfit不支持正则化。特征维度高、CRP数量少时我建议换fitclinear它是专门为线性分类器设计的支持L2正则化和更快的求解器。用一个简单的代码分支就可以切换% 使用fitclinear适合高维特征 lr_model fitclinear(X(idx_train,:), y(idx_train), ... Learner, logistic, Regularization, ridge, ... Lambda, 0.01, Solver, lbfgs); pred_test predict(lr_model, X(idx_test,:));两种方式的预测结果在低维特征上差别不大但fitclinear在维度上千时的训练速度明显更快。具体用哪个看库的docs里推荐配置默认不强制。4.2 参数调整从正则化到迭代上限逻辑回归不是“放进去就能跑出理想结果”关键参数决定了模型是欠拟合还是过拟合。下面是我在这类PUF建模任务里的常用参数表参数典型设置调整方向挑战长度 bit_len64/128越长越难拟合模型效果更接近真实场景CRP数量10000~50000太少过拟合太多训练慢特征阶数1或2阶数越高越容易过拟合正则化系数 Lambda0.001~0.1特征维度高时适当增大交叉验证折数5折或留出30%样本少用5折样本多用留出法我在跑一个128位挑战的实验时CRP只有8000条特征用了二阶结果训练准确率98%测试只有76%。把Lambda从0.0001调到0.05后测试准确率回升到85%。这种“训练高、测试低”的差距是调参时最先要处理的信号。如果你加的CRP数量上不去优先加正则化而不是继续加特征阶数。4.3 评估指标与结果判读准确率是最直观的指标但它往往是“第一层滤镜”。PUF响应的正负样本可能出现偏差比如某类响应位天然更偏向0模型全预测0也能拿到65%准确率。只看准确率你会高估模型的攻击能力。所以我每次训练完都会打混淆矩阵看正类召回率和负类召回率。一个典型的评估输出示例指标一阶特征二阶特征准确率71.3%84.7%正类召回率70.2%83.9%负类召回率72.1%85.5%训练时间8秒47秒如果正类召回率和负类召回率相差超过10个百分点说明模型被某类样本带偏了需要在cvpartition里用Stratify保证比例一致或者对少数类样本加权。不要小看这一步很多PUF因为仲裁器固有的偏置默认响应就不是均匀的。5. 实战避坑CRP数量、特征阶数与响应不稳定5.1 响应位抖动训练好、测试崩的罪魁祸首现象在仿真数据上逻辑回归准确率98%换成实际测量数据后只有81%且多次实验波动很大。原因真实PUF响应有噪声某些挑战对应的响应位是非确定的。仿真器如果默认没开噪声模型训练时模型学会的是一套“理想映射”而实测时这些噪声位成了不可能学会的样本。解决采集CRP时对同一挑战多次测量比如重复10次取多数投票作为标签同时在训练前统计每个挑战的响应稳定性将稳定度低于70%的样本滤掉。DAPUF库的data目录下如果提供了实测数据优先用实测数据验证流程不要只依赖仿真。5.2 特征顺序错位挑战位反了准确率直接掉到50%现象代码看起来没毛病但训练出的模型预测结果接近随机猜测。原因特征构造时挑战位的顺序与PUF仿真器的内部顺序不一致。比如仿真器把第一列当作最高位而特征函数把它当作最低位权重系数虽然学出来了但物理对应关系错位模型完全没有泛化能力。解决在build_features中固定“列位即挑战位序号”的约定并用一个哑变量检查——把挑战矩阵第一列取反预测结果应显著变化。我写过一个10行的自测脚本随机翻转一位挑战看预测概率变化方向是否符合该位置权重符号能快速定位顺序问题。5.3 正负样本失衡模型学成“全预测大多数类”现象训练集准确率看起来不低但查看混淆矩阵发现模型把几乎所有样本都预测成0类。原因PUF响应本身有偏置。仲裁器PUF如果上下路径的初始延时差较大响应位为0的比例会明显高于1。逻辑回归在没有类别权重时会偏向多数类。解决训练前检查sum(y)/length(y)如果比例不在0.3~0.7之间就应该做类别加权。glmfit不直接支持权重但fitclinear的Prior参数可以设置类别先验。更简单的办法是训练数据里做平衡采样把少数类复制或重采样到相近数量。5.4 高维特征过拟合训练100%测试崩到60%现象一阶特征测试准确率70%加上二阶特征后训练准确率接近100%测试准确率反而降到62%。原因二阶特征维度随挑战长度急剧增长而CRP数量没跟上模型把训练数据里的噪声当成了规律。这在PUF数据里很常见因为挑战之间不是完全独立高阶组合特征大量冗余。解决控制特征维度与样本量比例一般要求样本量至少是特征维度的5~10倍。若CRP实在有限降低特征阶数或加大Lambda。另一个办法是先跑特征选择比如Matlab的relieff或简单的相关分析保留与响应强相关的特征子集后再训练。5.5 随机划分泄漏前后相邻CRP时间相关现象随机划分训练集/测试集时准确率虚高按时间顺序划分后准确率大跌。原因CRP采集过程中温度和电压漂移是缓慢的相邻采样的响应噪声处于相似状态。随机划分会把时间临近的样本同时放到训练和测试集模型实质上记住了噪声状态而不是映射规律。解决用时间顺序划分代替随机划分取前70%作为训练集后30%作为测试集并明确写进实验记录。如果库自带的数据集有采集时间戳字段建议按时间排序后再划分。6. 进阶用法换PUF型别与自定义基线对比6.1 从仲裁器PUF扩展到异或PUF评估完仲裁器PUF后下一步往往是试试异或PUF。异或PUF由k条仲裁器PUF链并联响应由这k条链的仲裁结果做异或得到。它的核心变化在于单条链的特征空间不再线性决定最终输出。但逻辑回归仍然可以作为第一层评估工具。常见做法是构造每条子链的一阶特征然后拼接成一个总特征向量在向量层面拟合出“不完全线性”的决策边界。% xor_puf_feature.m % 假设challenge_blks是k条子链的挑战分块每块64位 function X_or build_xor_features(challenge_blks) k length(challenge_blks); X_or []; for i 1:k X challenge_blks{i} * 2 - 1; X_or [X_or, X]; end % 可加一阶特征间的交互项模拟XOR非线性 % X_or [X_or, X_or(:,1:end-1) .* X_or(:,2:end)]; end这里的关键是不要一口吃成胖子。先跑“拼接特征逻辑回归”如果准确率接近50%说明XOR层确实打乱了线性关系再考虑加交互特征或换非线性模型。把这一步作为自定义PUF的评估习惯能避免在错误的方向上浪费时间。6.2 快速对比不同分类器的代码骨架拿到一份新PUF库的CRP后除了逻辑回归我一般会同时跑一个线性SVM和朴素贝叶斯作为基线对比。Matlab里三者共用同一个特征矩阵代码骨架非常短% compare_baselines.m svm_model fitcsvm(X_train, y_train, KernelFunction, linear); nb_model fitcnb(X_train, y_train); acc_svm 1 - loss(svm_model, X_test, y_test); acc_nb 1 - loss(nb_model, X_test, y_test);对比时关注两个维度准确率和训练时长。如果逻辑回归和线性SVM准确率接近说明数据线性可分性强如果线性SVM明显更好说明数据中存在一些逻辑回归难以利用的分布特征。这个信息在写安全评估报告时比单独一个准确率有用得多。从那以后我每次拿到新的PUF库都强制自己先做完三件事检查响应位稳定性、验证特征顺序对齐、按时间顺序划分数据集然后才敢跑逻辑回归出准确率。这套流程救过我很多次也让我避开了数据泄漏带来的假高准确率。希望帮到你。本文还有配套的精品资源点击获取