适用单片机的btea加密代码剖析

  以前做单片机bootloader时候,尝试过搞些简单的加密来保护app的bin文件,后面在网上抄了份tea代码

 1 #define MX                    ((z>>5)^(y<<2))+(((y>>3)^(z<<4))^(sum^y))+(k[p&(3^e)]^z)
 2 #define DELTA                0x9e3779b9
 3 #define S_LOOPTIME            1
 4 
 5 /*
 6  *key  must be 128bit = 16 Bytes.
 7  */
 8 void btea_encrypt(uint8_t* buf,uint8_t* key,uint32_t block_size)
 9 {
10     uint8_t* v = (uint8_t*)buf;
11     uint8_t* k = (uint8_t*)key;
12     uint8_t z = v[block_size - 1],y = v[0],sum = 0,e;
13     uint32_t p,q ;
14     // Coding Part
15 
16     q = S_LOOPTIME + 52 / block_size ;
17     while ( q-- > 0 )
18     {
19         sum += DELTA ;
20         e = sum >> 2 & 3 ;
21         for (p = 0 ; p < block_size - 1 ; p++)
22         {
23             y = v[p + 1],
24             v[p] += MX;
25             z = v[p];
26         }
27 
28         y = v[0] ;
29         z = v[block_size - 1] += MX;
30     }
31 }
32 
33 /*
34  *key  must be 128bit = 16 Bytes.
35  *inbuf == outbuf == buf
36  */
37 void btea_decrpyt(uint8_t* buf,uint8_t* key,uint32_t block_size)
38 {
39     uint8_t* v=(uint8_t*)buf;
40     uint8_t* k=(uint8_t*)key;
41     uint8_t z = v[block_size - 1],y = v[0],sum = 0,e ;
42     uint32_t  p,q ;
43 
44     //Decoding Part...
45     q = S_LOOPTIME + 52 / block_size ;
46     sum = q * DELTA ;
47     while (sum != 0)
48     {
49         e = sum >> 2 & 3 ;
50         for (p = block_size - 1 ; p > 0 ; p--)
51         {
52             z = v[p - 1],
53             v[p] -= MX;
54             y = v[p];
55         }
56 
57         z = v[block_size - 1] ;
58         y = v[0] -= MX;
59         sum -= DELTA ;
60     }
61 }
人家送的鱼

 

  既然有人送鱼了,能吃饱,就直接用了,看着如此复杂的计算,就没有关心过实现原理。

  最近有其他需求用到加密,趁手的只有btea了,就从冷库里把这条鱼拿了出来。好家伙,so smelly!!!直接发现4个问题

  1.密钥第5-16字节怎么改,加密解密结果都一样

  2.传入的大小block_size = 1的情况下,无法解密出结果

  3.S_LOOPTIME >= 256,无法解密出结果

  4.S_LOOPTIME < 256的情况下,变更DELTA后,概率性无法解密出结果

  

 

  网上查,都是uint32_t形式的,对byte处理的看着就是八仙过海,可惜没有能讲清的。

  躲不掉了,还是得理解才行,一个个问题查吧。

  1.密钥5-16位无效问题

  跟密钥有关的就 e = sum >> 2 & 3 ; 和 k[p&(3^e)] 两句,一眼看到&和3,就有点像学生看到30°、45°和60°那种感觉了。查下运算符优先级(这种不加括号的写法真的恶心),右移>>运算高于按位与&运算,所以不需要在意sum,e最后的范围就是[0,3],进而(3 ^ e)取值也是[0,3],再&p后,p & (3 ^ e)的结果也在[0,3],这意味着,密钥下标就会用前4个字节。R U kidding???深度思考下,为啥能出这种幺蛾子,肯定就是上面提到的,原本uint32_t改成uint8_t没改到精髓,以前22个uint32_t,mask自然就是22 - 1,即为3,现在同样用128bit的密钥,那可是24个uint8_t,那mask不得改成么24 - 1 = 15才行吗。

 

  2.单字节数据无法解密问题

  这就得先大体上了解下tea的玩法了,加密过程就是(多次)遍历数组,每个元素加上MX这样一个表达式,姑且理解为一个变化的val吧,解密就是(多次)遍历数组减回对应的val。那下一步就是要搞清楚,MX依赖啥东西了,z、y、sum、k、p、e,这种没有技术文档的代码还毫无可读性,白送的鱼,没办法,哎。看眼缘一个个分析吧

  1)k,这玩意就是密钥,加解密都已知

  2)sum,sum在加密过程就是DELTA在轮数上的累加,那对应的解密过程就是DELTA和总轮数确认结果然后递减,而总轮数就是代码里的q,由配置S_LOOPTIME和block_size决定,二者均已知,最终sum在加解密过程中均为已知项

  3)e直接就是sum的固定方式位运算,sum确定e确定,已知;

  4)p,遍历数组时的索引,表示当前在处理的元素,加密时递增,解密时递减,已知

  5)z,把buf看成环形,z代表前一个元素,详见下表,表格内数字x代表z = buf[x]

  6)y,把buf看成环形,y代表后一个元素,详见下表,表格内数字x代表y = buf[x]

p 0 1 2 ... block_size - 3 block_size - 2 block_size - 1
z block_size - 1 0 1 ... block_size - 4 block_size - 3 block_size - 2
y 1 2 3 ... block_size - 2 block_size - 1 0

  经过上述分析,就剩z,y需要确定了,在加密过程,处理p数据时依赖于其前后的数据;解密时,处理p数据时依赖与加密时用到的前后数据。根据block_size,需要分4类讨论:

  a)block_size >= 3,加密最后的一个数据所依赖的y、z均不会再变化,解密直接用,make sense

  b)2 == block_size,加密最后的一个数据所依赖的y、z均为另一个元素,不变化,也可直接用于解密

  c)1 == block_size,加密最后的一个数据所依赖的y、z均为自身,加密计算后变化,无法用于解密

  d)0 == block_size,嘥鸠气

  综上,因为依赖的关系,这个代码只能用于2bytes以上的文本加密

 

  3.循环轮数超过0xFF后失效

  这个问题下意识就是轮数数据类型出问题了,一查 ,uint32_t的q,居然不是。再查下q和什么鬼挂钩了,sum!!!加密轮数就很干净地按q算,解密却是按sum是否为0作为结束的指示,而这个sum还是个uint8_t,那问题显而易见了,当总轮数超过255后,计为round,在解密过程中round执行了(round & 0xFF)次后,一字节的sum就变0了,毕竟((round & 0xFFFFFF00)  *  DELTA)对低8位毫无贡献,最后解密执行次数不足,能解出来原文就怪了。

 

  4.循环轮数 < 256的情况下,变更DELTA概率失效

  这个跟3原理通的,解密过程如果DELTA * 次数正好整除256,那就会提前结束。

 

  综合下来,这条鱼更多像是别人拿着32位的ltea鱼解剖出来的8位btea鱼。上面排问题过程可以理解为见鱼思渔了。下面,上修复了4个问题的渔具

  1 #define TEA_CFG_MX              ((prev >> 5) ^ (next << 2)) + (((next >> 3) ^ (prev << 4)) ^ (sum ^ next)) + (key[idx & (TEA_KEY_LEN_MASK ^ keyMask)] ^ prev)
  2 #define TEA_CFG_DELTA           0x9e3779b9
  3 #define TEA_CFG_LOOP_TIMES      6
  4 #define TEA_CFG_KEY_LEN_POWER   4
  5 
  6 #define TEA_MX                  TEA_CFG_MX
  7 #define TEA_DELTA               TEA_CFG_DELTA
  8 #define TEA_LOOP_TIMES          TEA_CFG_LOOP_TIMES
  9 #define TEA_KEY_LEN_POWER       TEA_CFG_KEY_LEN_POWER
 10 #define TEA_KEY_LEN             (1U << TEA_KEY_LEN_POWER)
 11 #define TEA_KEY_LEN_MASK        (TEA_KEY_LEN - 1)
 12 
 13 void btea_encrypt(uint8_t* buf,uint8_t* key,uint32_t block_size)
 14 {
 15     uint8_t prev;
 16     uint8_t next;
 17     uint8_t keyMask;
 18     uint32_t idx;
 19     uint32_t restRound;
 20     uint32_t sum;
 21     
 22     if((0 == block_size) || (NULL == buf) || (NULL == key))
 23     {
 24         return;
 25     }
 26 
 27     if(1 == block_size)
 28     {
 29         for(uint32_t i = 0;i < TEA_KEY_LEN;i++)
 30         {
 31             buf[0] ^= key[i];
 32         }
 33         return;
 34     }
 35 
 36     sum = 0;
 37     prev = buf[block_size - 1];
 38     next = buf[0];
 39     restRound = TEA_LOOP_TIMES + 52 / block_size ;
 40 
 41     while (restRound--)
 42     {
 43         sum += TEA_DELTA ;
 44         keyMask = (sum >> 2) & TEA_KEY_LEN_MASK ;
 45         for (idx = 0;idx < block_size - 1;idx++)
 46         {
 47             next = buf[idx + 1];
 48             buf[idx] += TEA_MX;
 49             prev = buf[idx];
 50         }
 51 
 52         next = buf[0];
 53         buf[block_size - 1] += TEA_MX;
 54         prev = buf[block_size - 1];
 55     }
 56 }
 57 
 58 void btea_decrypt(uint8_t* buf,uint8_t* key,uint32_t block_size)
 59 {
 60     uint8_t prev;
 61     uint8_t next;
 62     uint8_t keyMask;
 63     uint32_t idx;
 64     uint32_t restRound;
 65     uint32_t sum;
 66     
 67     if((0 == block_size) || (NULL == buf) || (NULL == key))
 68     {
 69         return;
 70     }
 71 
 72     if(1 == block_size)
 73     {
 74         for(uint32_t i = 0;i < TEA_KEY_LEN;i++)
 75         {
 76             buf[0] ^= key[i];
 77         }
 78         return;
 79     }
 80 
 81     sum = 0;
 82     prev = buf[block_size - 1];
 83     next = buf[0];
 84     restRound = TEA_LOOP_TIMES + 52 / block_size ;
 85 
 86 
 87     sum = restRound * TEA_DELTA ;
 88     while (restRound--)
 89     {
 90         keyMask = (sum >> 2) & TEA_KEY_LEN_MASK ;
 91         for (idx = block_size - 1;idx > 0;idx--)
 92         {
 93             prev = buf[idx - 1];
 94             buf[idx] -= TEA_MX;
 95             next = buf[idx];
 96         }
 97 
 98         prev = buf[block_size - 1];
 99         buf[0] -= TEA_MX;
100         next = buf[0];
101         sum -= TEA_DELTA ;
102     }
103 }

 

posted @ 2025-05-16 12:07  蓝bleu  阅读(61)  评论(0)    收藏  举报