滑动攻击
1. 滑动攻击简介
滑动攻击也是一种概率攻击,但它的方法跟差分或线性攻击中的概率偏移性检测是不同的。对差分或线性来说,增加轮数可防止被攻击。但对滑动攻击来说,对于很破的轮函数,仅轮数的增加,是很难抵挡滑动攻击的。
本文对http://www.theamazingking.com/crypto-slide.php中的文章进行了学习,记录笔记如下。
代码中描述了使用30个明、密文对的滑动攻击及与穷举攻击的比较。
2. TOY100密码算法结构
TOY100密码算法100圈,数据块为8位。密钥位为16位。结构图如下:
图1 轮函数+整体密码
这里的子密钥实际就K0和K1,太弱了,也是滑动攻击成功的主要原因。
3. 滑动攻击
3.1 滑动攻击成功的前题
密码算法有很多重复的子结构;每个子结构中的密钥最好全相同,或起码要有足够的相关性;每个重复的子结构在已知明文攻击下必须很简单;显然TOY100密码算法满足上述需求。其可看成图2中的结构:
图2 FEAL轮函数结构
而对一个Doubleround来说,是不难被攻破的,参见图3:
图3 Doubleround的破解
其过程如下:把密文经逆SBOX后,作备用。再把明文经猜测的K0加密再经SBOX处理后与备用数据XOR,得到K1,但此时得到的是256种K0和K1的组合。
3.2 滑动对的寻找
在众多的明、密文对中要找到两对相关的明、密文对。即P0,P1,C0,C1,称为滑动对。
由上图可看出,P0与P1,C0与C1的关系。这样由C0与C1,可以求出最后一轮的256种K0和K1的组合。实际上C0与C1是随机组对,然后求出256种K0和K1的组合,再通过P0与P1去判断是否真是滑动对。一旦P0经过猜测的K0和K1处理后等于P1,则猜测正确。所得的K即为初始密钥。
3.3 与穷举搜索的对比
穷举搜索穷举2^16个密钥,且每个100轮。而滑动攻击仅C(n,2)*256个密钥,每个密钥跑4轮。这里n是明、密文对数。由生日攻击原理可知,C0与C1发生碰撞的概率是很大的。
4. 试验结果
做1000次的滑动与穷举对比。穷举每次必然成功;滑动未必,看是否能找到滑动对。其成功率跟使用的对数有关。gcc -O3 slide.c -o slide
5. 附录代码
- //Slide Attack
- //theamazingking.com
- #include <stdio.h>
- #include <stdlib.h>
- #include <time.h>
- unsigned int sbox[16] = {2, 6, 1, 13, 0, 5, 8, 7, 14, 10, 15, 11, 4, 9, 3, 12};
- unsigned int revSbox[16] = {4, 2, 0, 14, 12, 5, 1, 7, 6, 13, 9, 11, 15, 3, 8, 10};
- unsigned int realK0, realK1;
- unsigned int plain[200];
- unsigned int cipher[200];
- unsigned int fullSbox(unsigned int a)
- {
- unsigned int left = (a >> 4) & 0xf;
- unsigned int right = a & 0xf;
- left = sbox[left];
- right = sbox[right];
- return left << 4 | right;
- }
- unsigned int fullRevSbox(unsigned int a)
- {
- unsigned int left = (a >> 4) & 0xf;
- unsigned int right = a & 0xf;
- left = revSbox[left];
- right = revSbox[right];
- return left << 4 | right;
- }
- unsigned int oneRound(unsigned int a, unsigned int k)
- {
- return fullSbox(a ^ k);
- }
- unsigned int encrypt(unsigned int plain, unsigned int k0, unsigned int k1)
- {
- int c;
- for(c = 0; c < 100; c++)
- {
- if ((c % 2) == 0) //EVEN ROUNDS
- plain = oneRound(plain, k0);
- else
- plain = oneRound(plain, k1);
- }
- return plain;
- }
- unsigned int slidePair(unsigned int plain1, unsigned int cipher1, unsigned int plain2, unsigned int cipher2)
- {
- int k0;
- for(k0 = 0; k0 <= 0xff; k0++)
- {
- unsigned int k1 = fullSbox(cipher1 ^ k0) ^ fullRevSbox(cipher2);
- unsigned int testPlain2 = fullSbox(fullSbox(plain1 ^ k0) ^ k1);
- if (testPlain2 == plain2)
- {
- //VERIFY
- int crapped = 0;
- int c;
- for(c = 0; c < 10; c++)
- {
- if (encrypt(plain[c], k0, k1) != cipher[c])
- {
- crapped = 1;
- break;
- }
- }
- if (crapped) continue;
- // printf("REAL K0 = %x K1 = %x\n", realK0, realK1);
- // printf(" FOUND K0 = %x K1 = %x\n", k0, k1);
- // printf("\n");
- return 1;
- }
- }
- return 0;
- }
- int slideIt()
- {
- int c, d;
- for(c = 0; c < 30; c++)
- {
- for(d = 0; d < 30; d++)
- if (c != d)
- if (slidePair(plain[c], cipher[c], plain[d], cipher[d]))
- return 1;
- }
- return 0;
- }
- int bruteIt()
- {
- int c, d;
- for(c = 0; c <= 0xFF; c++)
- for(d = 0; d <= 0xFF; d++)
- {
- int crapped = 0;
- int e;
- for(e = 0; e < 10; e++)
- {
- if (encrypt(plain[e], c, d) != cipher[e])
- {
- crapped = 1;
- break;
- }
- }
- if (crapped) continue;
- return 1;
- }
- return 0;
- }
- int main()
- {
- srand(time(NULL));
- printf("Simple Slide Attack\n");
- int goodOnes = 0;
- int f;
- //重复1000次
- for(f = 0; f < 1000; f++)
- {
- realK0 = rand() & 0xFF;
- realK1 = rand() & 0xFF;
- //每次产生200个明、密文对,只用30对
- int c;
- for(c = 0; c < 200; c++)
- {
- plain[c] = rand() & 0xFF;
- cipher[c] = encrypt(plain[c], realK0, realK1);
- }
- //不是每次30对都能找到密钥的,即1000次中有几次可能不行
- if (slideIt())
- goodOnes++;
- }
- printf("PERCENTAGE FOUND = %i\n", goodOnes * 100 / f);
- return 0;
- }