:这里指代码实现层面(非数学优化层面),使用寄存器优化,即主密钥/轮密钥、敏感数据比如中间/临时变量必须存于寄存器,明文/密文放在内存(若有够用的寄存器则放寄存器),主密钥用特权寄存器(为支持长期存储,比如调试寄存器、MSR寄存器),轮密钥和敏感数据用通用寄存器。那么怎么做?稳妥快捷的方法是用汇编或内联汇编,手工编排寄存器即构建密钥与敏感数据到寄存器集合的映射,若用普通的汇编指令,则寄存器的映射比较自由;若用专用的加密指令,则映射相对受限。如果用高级语言比如c/c++开发,问题在于
强制某些变量必须分配(特定的)寄存器,为通用性要从编程语言语法属性到目标机器代码生成都改动支持,这个方法实现成本有点大。下面是摘自LLVM X86RegisterInfo.td的部分寄存器
1 // 32-bit registers
2 let SubRegIndices = [sub_16bit, sub_16bit_hi], CoveredBySubRegs = 1
in {
3 def EAX : X86Reg<"eax", 0, [AX, HAX]>, DwarfRegNum<[-2, 0, 0]>;
4 def EDX : X86Reg<"edx", 2, [DX, HDX]>, DwarfRegNum<[-2, 2, 2]>;
5 def ECX : X86Reg<"ecx", 1, [CX, HCX]>, DwarfRegNum<[-2, 1, 1]>;
6 def EBX : X86Reg<"ebx", 3, [BX, HBX]>, DwarfRegNum<[-2, 3, 3]>;
7 def ESI : X86Reg<"esi", 6, [SI, HSI]>, DwarfRegNum<[-2, 6, 6]>;
8 def EDI : X86Reg<"edi", 7, [DI, HDI]>, DwarfRegNum<[-2, 7, 7]>;
9 def EBP : X86Reg<"ebp", 5, [BP, HBP]>, DwarfRegNum<[-2, 4, 5]>;
10 def ESP : X86Reg<"esp", 4, [SP, HSP]>, DwarfRegNum<[-2, 5, 4]>;
11 def EIP : X86Reg<"eip", 0, [IP, HIP]>, DwarfRegNum<[-2, 8, 8]>;
12 }
13
14 // X86-64 only, requires REX
15 let SubRegIndices = [sub_16bit, sub_16bit_hi], CoveredBySubRegs = 1
in {
16 def R8D : X86Reg<"r8d", 8, [R8W,R8WH]>;
17 def R9D : X86Reg<"r9d", 9, [R9W,R9WH]>;
18 def R10D : X86Reg<"r10d", 10, [R10W,R10WH]>;
19 def R11D : X86Reg<"r11d", 11, [R11W,R11WH]>;
20 def R12D : X86Reg<"r12d", 12, [R12W,R12WH]>;
21 def R13D : X86Reg<"r13d", 13, [R13W,R13WH]>;
22 def R14D : X86Reg<"r14d", 14, [R14W,R14WH]>;
23 def R15D : X86Reg<"r15d", 15, [R15W,R15WH]>;
24 }
25
26 // 64-bit registers, X86-64 only
27 let SubRegIndices = [sub_32bit]
in {
28 def RAX : X86Reg<"rax", 0, [EAX]>, DwarfRegNum<[0, -2, -2]>;
29 def RDX : X86Reg<"rdx", 2, [EDX]>, DwarfRegNum<[1, -2, -2]>;
30 def RCX : X86Reg<"rcx", 1, [ECX]>, DwarfRegNum<[2, -2, -2]>;
31 def RBX : X86Reg<"rbx", 3, [EBX]>, DwarfRegNum<[3, -2, -2]>;
32 def RSI : X86Reg<"rsi", 6, [ESI]>, DwarfRegNum<[4, -2, -2]>;
33 def RDI : X86Reg<"rdi", 7, [EDI]>, DwarfRegNum<[5, -2, -2]>;
34 def RBP : X86Reg<"rbp", 5, [EBP]>, DwarfRegNum<[6, -2, -2]>;
35 def RSP : X86Reg<"rsp", 4, [ESP]>, DwarfRegNum<[7, -2, -2]>;
36
37 // These also require REX.
38 def R8 : X86Reg<"r8", 8, [R8D]>, DwarfRegNum<[ 8, -2, -2]>;
39 def R9 : X86Reg<"r9", 9, [R9D]>, DwarfRegNum<[ 9, -2, -2]>;
40 def R10 : X86Reg<"r10", 10, [R10D]>, DwarfRegNum<[10, -2, -2]>;
41 def R11 : X86Reg<"r11", 11, [R11D]>, DwarfRegNum<[11, -2, -2]>;
42 def R12 : X86Reg<"r12", 12, [R12D]>, DwarfRegNum<[12, -2, -2]>;
43 def R13 : X86Reg<"r13", 13, [R13D]>, DwarfRegNum<[13, -2, -2]>;
44 def R14 : X86Reg<"r14", 14, [R14D]>, DwarfRegNum<[14, -2, -2]>;
45 def R15 : X86Reg<"r15", 15, [R15D]>, DwarfRegNum<[15, -2, -2]>;
46 def RIP : X86Reg<"rip", 0, [EIP]>, DwarfRegNum<[16, -2, -2]>;
47 }
48 
49 // XMM Registers, used by the various SSE instruction set extensions.
50 def XMM0: X86Reg<"xmm0", 0>, DwarfRegNum<[17, 21, 21]>;
51 def XMM1: X86Reg<"xmm1", 1>, DwarfRegNum<[18, 22, 22]>;
52 def XMM2: X86Reg<"xmm2", 2>, DwarfRegNum<[19, 23, 23]>;
53 def XMM3: X86Reg<"xmm3", 3>, DwarfRegNum<[20, 24, 24]>;
54 def XMM4: X86Reg<"xmm4", 4>, DwarfRegNum<[21, 25, 25]>;
55 def XMM5: X86Reg<"xmm5", 5>, DwarfRegNum<[22, 26, 26]>;
56 def XMM6: X86Reg<"xmm6", 6>, DwarfRegNum<[23, 27, 27]>;
57 def XMM7: X86Reg<"xmm7", 7>, DwarfRegNum<[24, 28, 28]>;
58
59 // X86-64 only
60 def XMM8: X86Reg<"xmm8", 8>, DwarfRegNum<[25, -2, -2]>;
61 def XMM9: X86Reg<"xmm9", 9>, DwarfRegNum<[26, -2, -2]>;
62 def XMM10: X86Reg<"xmm10", 10>, DwarfRegNum<[27, -2, -2]>;
63 def XMM11: X86Reg<"xmm11", 11>, DwarfRegNum<[28, -2, -2]>;
64 def XMM12: X86Reg<"xmm12", 12>, DwarfRegNum<[29, -2, -2]>;
65 def XMM13: X86Reg<"xmm13", 13>, DwarfRegNum<[30, -2, -2]>;
66 def XMM14: X86Reg<"xmm14", 14>, DwarfRegNum<[31, -2, -2]>;
67 def XMM15: X86Reg<"xmm15", 15>, DwarfRegNum<[32, -2, -2]>;
68
69 def XMM16: X86Reg<"xmm16", 16>, DwarfRegNum<[67, -2, -2]>;
70 def XMM17: X86Reg<"xmm17", 17>, DwarfRegNum<[68, -2, -2]>;
71 def XMM18: X86Reg<"xmm18", 18>, DwarfRegNum<[69, -2, -2]>;
72 def XMM19: X86Reg<"xmm19", 19>, DwarfRegNum<[70, -2, -2]>;
73 def XMM20: X86Reg<"xmm20", 20>, DwarfRegNum<[71, -2, -2]>;
74 def XMM21: X86Reg<"xmm21", 21>, DwarfRegNum<[72, -2, -2]>;
75 def XMM22: X86Reg<"xmm22", 22>, DwarfRegNum<[73, -2, -2]>;
76 def XMM23: X86Reg<"xmm23", 23>, DwarfRegNum<[74, -2, -2]>;
77 def XMM24: X86Reg<"xmm24", 24>, DwarfRegNum<[75, -2, -2]>;
78 def XMM25: X86Reg<"xmm25", 25>, DwarfRegNum<[76, -2, -2]>;
79 def XMM26: X86Reg<"xmm26", 26>, DwarfRegNum<[77, -2, -2]>;
80 def XMM27: X86Reg<"xmm27", 27>, DwarfRegNum<[78, -2, -2]>;
81 def XMM28: X86Reg<"xmm28", 28>, DwarfRegNum<[79, -2, -2]>;
82 def XMM29: X86Reg<"xmm29", 29>, DwarfRegNum<[80, -2, -2]>;
83 def XMM30: X86Reg<"xmm30", 30>, DwarfRegNum<[81, -2, -2]>;
84 def XMM31: X86Reg<"xmm31", 31>, DwarfRegNum<[82, -2, -2]>;
85
86 // YMM0-15 registers, used by AVX instructions and
87 // YMM16-31 registers, used by AVX-512 instructions.
88 let SubRegIndices = [sub_xmm]
in {
89 foreach Index = 0-31
in {
90 def YMM#Index : X86Reg<"ymm"#Index, Index, [!cast
("XMM"#Index)]>,
91 DwarfRegAlias("XMM"#Index)>;
92 }
93 }
94
95 // ZMM Registers, used by AVX-512 instructions.
96 let SubRegIndices = [sub_ymm] in {
97 foreach Index = 0-31 in {
98 def ZMM#Index : X86Reg<"zmm"#Index, Index, [!cast("YMM"#Index)]>,
99 DwarfRegAlias("XMM"#Index)>;
100 }
101 }
102 

103 // Debug registers
104 def DR0 : X86Reg<"dr0", 0>;
105 def DR1 : X86Reg<"dr1", 1>;
106 def DR2 : X86Reg<"dr2", 2>;
107 def DR3 : X86Reg<"dr3", 3>;
108 def DR4 : X86Reg<"dr4", 4>;
109 def DR5 : X86Reg<"dr5", 5>;
110 def DR6 : X86Reg<"dr6", 6>;
111 def DR7 : X86Reg<"dr7", 7>;
112 def DR8 : X86Reg<"dr8", 8>;
113 def DR9 : X86Reg<"dr9", 9>;
114 def DR10 : X86Reg<"dr10", 10>;
115 def DR11 : X86Reg<"dr11", 11>;
116 def DR12 : X86Reg<"dr12", 12>;
117 def DR13 : X86Reg<"dr13", 13>;
118 def DR14 : X86Reg<"dr14", 14>;
119 def DR15 : X86Reg<"dr15", 15>;
120 

121 def GR32 : RegisterClass<"X86", [i32], 32,
122 (add EAX, ECX, EDX, ESI, EDI, EBX, EBP, ESP,
123 R8D, R9D, R10D, R11D, R14D, R15D, R12D, R13D)>;
124
125 // GR64 - 64-bit GPRs. This oddly includes RIP, which isn't accurate, since
126 // RIP isn't really a register and it can't be used anywhere except in an
127 // address, but it doesn't cause trouble.
128 // FIXME: it *does* cause trouble - CheckBaseRegAndIndexReg() has extra
129 // tests because of the inclusion of RIP in this register class.
130 def GR64 : RegisterClass<"X86", [i64], 64,
131 (add RAX, RCX, RDX, RSI, RDI, R8, R9, R10, R11,
132 RBX, R14, R15, R12, R13, RBP, RSP, RIP)>;
:为保障安全就复杂了,由于密钥及敏感数据存于寄存器,首先要防止寄存器交换/拷贝到内存(为避免读取内存的冷启动攻击、基于cache的侧信道攻击)的一切可能因素,比如进程调度、由信号或异步中断引起的处理器模式切换、系统休眠,如果在用户态实现加解密,就避免不了被调度或切换,因为单核上不可能只运行加解密进程,所以得实现在内核态。这样一来就要在加解密中禁止抢占与中断,考虑到系统响应,禁止的粒度不能过大最小为一个分组,分组加解密前禁止抢占与中断(比如调用linux内核接口
)前必须清零寄存器。在系统休眠时,禁止寄存器复制到内存,休眠恢复时在所有用户态进程恢复前执行密钥初始化,同理系统启动时的密钥初始化也得在用户态进程运行前执行。其次要防止其它用户态进程/内核线程/中断服务程序读写寄存器尤其特权寄存器(为避免用户态或内核态rootkit),所以要修改内核,过滤相关系统调用比如linux的
。对于不可屏蔽的中断靠禁止是无效的,只能修改中断处理程序避免寄存器中的密钥数据被扩散到内存,比如在中断处理函数入口处清零相关寄存器。综上基于已知代码修改的防御不能防御恶意加载/修改代码之类的攻击,比如动态安装的内核模块/驱动,但可有效防御冷启动攻击、只读DMA攻击、基于cache的侧信道攻击、用户态权限的软件攻击、内核态的仅运行已有代码的软件攻击
1.
,给定大整数n分解的一对素因子p和q,p或q是否素数决定不了安全性,但决定算法的正确性,也就是说p或q不能为合数,而安全性取决于n的位数及p、q的距离,n越大则难于素因子分解(因为素数测试是一个P问题,而因子分解是一个NP问题,其耗时是关于n的指数),|p - q|要大是为抵抗一种
2.
,通常选择阶为素数的有限循环(子)群,这时素数决定了安全性。因素数不能再因子分解,故避免了针对阶为合数的质因子分解且利用中国剩余定理求离散对数的(已知最好)攻击。具体讲就是为了防
3.
,安全性要求有三点:第一是单向性,由于压缩函数理论上存在碰撞,因此单向性是指计算不可行,为什么要单向性?因为若不单向,则可从结果比如签名逆出原文消息;第二是抗弱冲突性即
4. 周知
基于多项式的拉格朗日插值公式,普遍的设计采用GF(q)域上的多项式,秘密s为f(0),q是一个大于n的大素数(n是s被分成的部分数)。正常来讲,参与者个数必须至少是设计时的k,才能恢复出正确的s。如果个数少于k比如k-1,则只能猜测s0=f(0)以构建第k个方程,那么恢复得到的多项式g(x)等同设计时的多项式f(x)的概率是1/q。因为g(x)的项系数可以看作关于s0的同余式即h(s0)=(a+b*s0)mod q的形式,因q为素数,故依模剩余系遍历定理,当s0取GF(q)一值时,则h(s0)唯一对应另一值。所以h(s0)等于f(0)的概率为1/q。由此可见,当q取80位以上,敌手攻击概率不大于1/2
5.
是密码学经典应用,体现在首先支持保密与认证业务的正交,即独立或组合,且组合时按认证、压缩、加密的顺序,这个顺序是经考究有优势的;其次会话密钥是一次性的,由安全伪随机数生成器生成,且按公钥加密;最后使用自研的密钥环与信任网解决公钥管理问题。理论本质上,PGP提供的是一种保密认证业务的通用框架,因为具体的对称加密算法、随机数生成、公钥算法,都可依需要灵活选配扩展。PGP有两个问题跟组合与概率相关,一个是算密钥环N个公钥中,密钥ID(64位)至少有两个重复的概率?设所求概率为p,先算任意两个不重复的概率q,令m=2
),则p=1-q,不难看出,N越小则q越大则p越小,因实际应用N<<m,故p非常小可忽略,即PGP取公钥中最低64有效位作密钥ID,是可行的。另一个是签名摘要暴露了前16位明文,对哈希函数安全的影响有多大?这问题意思应该是敌手拿到消息后但没发送方的私钥作签名,只能穷举变换原消息并求哈希值,使之与消息摘要剩余位组相等。这本质是求
综上不难看出,如果一个基本块很大比如有1000条指令,平均每指令花2个周期,则RTD需要2000个条目,若一条目即矩阵每行占用32字节(256种资源数),则总量约64k。当然这对于现代内存体量来说不算什么,但可以有更好的节省内存的做法:RTD尺寸其实可以相对固定,其上限为基本块中耗费周期最多指令的周期的一个大于1常数因子倍(为兼顾指令并行性),这样一来就要增加当指令完成时(当前指令发射周期大于前一条的终止周期时复位前一条指令的RTD)从发射周期处复位RTD即作一个矩阵反运算的操作,其它步骤对应的矩阵与、矩阵或运算的操作保留不变。另由于RTD固定了尺寸,因此发射周期递增后要取模
【备注】以上是我针对简单机器模型(每种资源数量仅一个,比如整数运算单元1个,内存访问单元1个,浮点运算单元1个)用布尔矩阵作的优化。如果是复杂的超标量机器即每种资源数有多个,那么只需修改如下:布尔矩阵换成整数矩阵;新增一个机器资源可用总数整数矩阵RDA(单列资源数同值),布尔矩阵与运算换成加法并与RDA比较,若大于RDA则递增tmax;布尔矩阵或运算换成加法;布尔矩阵反运算换成减法,RTD减RT存于RTD
1. kretprobe_trampoline_holder该跳转函数无参是必须的或说最好的通用设计,因为替换返回地址是非正常程序流程,即被探测函数的调用者无感知,不存在为跳转函数准备入参。若要设计传参且只读,则不会破坏被探测函数调用者的上下文,但跳转函数内部流程怎么用参数是个问题,这需要一种约定
2. 跳转函数为调用trampoline_handler准备入参,即在栈上构造一个(不完整的)pt_regs,再把它地址即栈顶赋给rdi,rdi是x86_64上传入第一参数使用的寄存器,同时预留一个栈单元存放原返回地址(为什么要预留?因为被探测函数返回时,其调用者存放返回地址的栈空间被释放了,所以得在跳转函数内造一个)。由于trampoline_handler内调到用户自定义handler而传入pt_regs,因此自定义handler内要注意最好别改动pt_regs,否则会破坏被探测函数调用者的上下文