每一个玩单片机的工程师或多或少都接触过加密算法,这些加密算法有的非常复杂,比如在物联网的系统开发中,我们通常用到的是 AES-128 等高强加密级别的算法。
有的也会非常简单,今天盘点一下能够运行在 51 单片机上的各类加密算法,防君子不防小人。
XOR
没错,异或操作其实也可以作为一个简易加密算法,不过通常我们使用来做串口通信的完整性校验。它的核心思想是,发送方和接收方对数据按相同的规则进行异或运算,通过比较结果来判断数据在传输过程中是否可能出错。
// 异或校验计算函数// 参数: data - 待校验的数据指针, len - 数据长度(字节数)// 返回值: 计算得到的异或校验字节unsigned char xor_checksum(const unsigned char *data, int len){unsigned char checksum = 0; // 初始化校验和为0for (int i = 0; i < len; i++){checksum ^= data[i]; // 将每个字节依次与checksum进行异或运算}return checksum;}
使用 XOR 来校验数据完整性最重要的特点是算法简单,速度快,它能检测出大部分的随机错误,但也只是大部分,因为它的可靠性太有限了。
如果数据块中两个不同的位同时发生错误(特别是偶数个错误位在相同位置),异或校验和可能不变,导致无法检测出错误。因此,它通常适用于对可靠性要求不特别高、数据量小或通信环境较好的场景,例如一些简单的串口通信、IC卡接口通讯等。
如果想进一步提高这个通信校验的可靠性,那就只能请出CRC 校验了。
开头说,我们盘点的是单片机上可用的加密算法,那么 XOR 如何应用于加密呢?
方法很简单,我们需要先设定一个字节的密钥key,然后将需要加密的数据段分别和密钥 key 异或即可。
void xor_encrypt(uint8_t* data, uint32_t len, uint8_t key){for (uint32_t i = 0; i < len; i++){data[i] ^= key;}}
代码量仅7行,速度极快,但安全性极低,仅能防止随意查看。
RC4流密码
在密码学中,RC4(来自Rivest Cipher4的缩写)是一种流加密算法,密钥长度可变。它加解密使用相同的密钥,因此也属于对称加密算法。
RC4被用于为网络浏览器和服务器间通信而制定的SSL/TLS(安全套接字协议/传输层安全协议)标准中,以及作为IEEE 801.11无线局域网标准一部分的WEP(WiredEquivalent Privacy)协议和新的WiFi受保护访问协议(WAP)中。从这些应用来看,RC4构成了当今网络通信的非常重要的部分,因此这个算法非常重要。
RC4的相关的名词
状态向量S:长度为256,S[0],S[1]…..S[255]。每个单元都是一个字节,算法运行的任何时候,S都包括0-255的8比特数的排列组合,只不过值的位置发生了变换;
临时向量T:长度也为256,每个单元也是一个字节。如果密钥的长度是256字节,就直接把密钥的值赋给T,否则,循环的将密钥的每个字节赋给T;
密钥K:长度为1-256字节,注意密钥的长度keylen与明文长度、密钥流的长度没有必然关系,通常密钥的长度16字节(128比特)。
密钥流:RC4算法的关键是根据明文和密钥生成相应的密钥流,密钥流的长度和明文的长度是对应的,也就是说明文的长度是500字节,那么密钥流也是500字节。当然,加密生成的密文也是500字节,因为密文第i字节=明文第i字节^密钥流第i字节;
RC4加密过程
RC4算法其中包括初始化算法(KSA)和伪随机子密码生成算法(PRGA)两大部分
S和T的初始状态(KSA调度算法)
首先是一个S数组,按照升序,给S数组中的每个字节赋值0,1,2,3,4,5,6…..,254,255,对应到图中如下:
再接着创建一个临时向量T,也就是一个数组,将密钥的值(你自己定义的密钥)循环复制到T向量中,就是下面程序中的key[i % keylen])。
算法初始化用程序来看:
// RC4初始化void rc4_init(uint8_t* s, uint8_t* key, int keylen){int j = 0;for (int i = 0; i < 256; i++) s[i] = i; //S 向量初始化for (int i = 0; i < 256; i++) //循环 256 次,打乱 S 向量{uint8_t t = s[i];j = (j + s[i] + key[i % keylen]) % 256;s[i] = s[j];s[j] = t;}}
设置一个变量 j 初始值为 0。然后通过一个循环来打乱 S 数组的顺序,此循环会执行 256 次。
在每次循环里,先通过 j = (j + S[i] + T[i]) % 256 来更新 j 的值,其中 i 是当前循环的索引。再将 S[i] 和 S[j] 的值进行交换。经过这一系列操作后,S 数组就完成了初始状态的设置,其中元素的顺序被打乱,不再是最初的升序排列。
伪随机子密码生成算法(PRGA)
伪随机子密码生成算法(PRGA)的主要作用是从经过 KSA 初始化后的状态向量 S 中生成密钥流。在这个阶段,我们会持续生成一个字节的密钥流,用于与明文进行异或操作来完成加密,或者与密文进行异或操作来完成解密。
首先,我们会初始化两个变量 i 和 j,它们的初始值都为 0。然后,进入一个循环,这个循环会根据明文的长度来执行相应的次数,因为我们要为明文中的每一个字节都生成对应的密钥流字节。
在每次循环中,我们会进行以下操作:
递增 i 的值,使其在 0 - 255 的范围内循环。
根据 i 的值更新 j 的值,计算公式为 j = (j + S[i]) % 256。
交换 S[i] 和 S[j] 的值,这一步会进一步打乱 S 数组的顺序。
计算 t 的值,t = (S[i] + S[j]) % 256。
从 S 数组中取出 S[t] 作为当前生成的密钥流字节。
以下是 PRGA 的代码实现:
// RC4伪随机子密码生成void rc4_prga(uint8_t* s, uint8_t* data, int len){int i = 0, j = 0;for (int k = 0; k < len; k++){i = (i + 1) % 256;j = (j + s[i]) % 256;uint8_t t = s[i];s[i] = s[j];s[j] = t;t = (s[i] + s[j]) % 256;data[k] ^= s[t];}}
在这个代码中,data数组既可以是明文,也可以是密文。当data是明文时,经过rc4_prga函数处理后,data数组就会变成密文;当data是密文时,经过同样的函数处理后,data数组就会恢复成明文。
RC4 算法在单片机上实现相对容易,并且速度较快,但是它也存在一些安全隐患。例如,在某些特定的密钥选择下,RC4 会产生弱密钥流,导致加密的安全性降低。因此,在使用RC4 算法时,需要谨慎选择密钥,并且要根据具体的应用场景来评估其安全性是否满足需求。
下图是整体的 RC4 密钥流生成的过程。
还有 DES 这种分组密码的方式,下次再聊。
386