? DES-CBC加密详解以及C实现
前言
????本文章主要介绍加密算法DES-CBC的原理及C语言实现。DES概述部分是节选的《图解密码技术》一书中对DES算法的介绍。
1 DES概述
1.1 什么是DES
????DES(Data Encryption Standard)是1977年美国联邦信息处理标准(FIPS)中所采用的一种对称密码(FIPS 46-3)。DES 一直以来被美国以及其他国家的政府和银行等广泛使用。
1.2 加密和解密
????DES是一种将64 比特的明文加密成64 比特的密文的对称密码算法,它的密钥长度是56 比特。尽管从规格上来说,DES 的密钥长度是64 比特,但由于每隔7 比特会设置一个用于错误检查的比特,因此实质上其密钥长度是56 比特。 DES是以64 比特的明文(比特序列)为一个单位来进行加密的,这个64 比特的单位称为分组。一般来说,以分组为单位进行处理的密码算法称为分组密码(block cipher),DES 就是分组密码的一种。 ????DES 每次只能加密64 比特的数据,如果要加密的明文比较长,就需要对 DES加密进行迭代(反复),而迭代的具体方式就称为模式(mode)。 ????????????????????????????????????????????图1-1DES加密与解密
1.3 DES结构(Feistel网络)
????DES的基本结构是由Horst Feistel设计的,因此也称为Feistel 网络(Feistel network)、Feistel 结构(Feistel structure)或者Feistel密码(Feistel cipher)。这一结构不仅被用于DES,在其他很多密码算法中也有应用。 ????在Feistel 网络中,加密的各个步骤称为轮(round),整个加密过程就是进行若干次轮的循环。图3-3展现的是 Feistel网络中一轮的计算流程。DES是一种16轮循环的 Feistel 网络。 ????????????????????????????????????????图1-2 Feistel网络中的一轮 下面我们参照图1-2来讲解一下 Feistel 网络的具体结构。 ????上面的两个方框表示 Feistel 网络中一轮的输入(明文)。输入的数据被等分为左右两半分别进行处理,在图中,左半部分写作"左侧",右半部分写作"右侧"。 下面的两个方框表示本轮的输出(密文)。输出的左半部分写作"加密后的左侧",右半部分写作"右侧"。 ??中间的"子密钥"指的是本轮加密所使用的密钥。在 Feistel 网络中,每一轮都需要使用一个不同的子密钥。由于子密钥只在一轮中使用,它只是一个局部密钥,因此才称为子密钥(subkey )。 ????轮函数的作用是根据"右侧"和子密钥生成对"左侧"进行加密的比特序列,它是密码系统的核心。将轮函数的输出与"左侧"进行XOR 运算,其结果就是"加密后的左侧"。也就是说,我们用XOR将轮函数的输出与"左侧"进行了合并。而输入的"右侧"则会直接成为输出的"右侧"。 总结一下,一轮的具体计算步骤如下∶ (1)将输入的数据等分为左右两部分。 (2)将输入的右侧直接发送到输出的右侧。 (3)将输入的右侧发送到轮函数。 (4)轮函数根据右侧数据和子密钥,计算出一串看上去是随机的比特序列。 (5)将上一步得到的比特序列与左侧数据进行XOR 运算,并将结果作为加密后 的左侧。 ????但是,这样一来"右侧"根本就没有被加密,因此我们需要用不同的子密钥对一轮的处理重复若干次,并在每两轮处理之间将左侧和右侧的数据对调。 图 1-3展现了一个3轮的Feistel网络,3轮加密计算需要进行两次左右对调。对调只在两轮之间进行,最后一轮结束之后不需要对调。 ????Feistel 网络这个名字的由来,也许就是因为其结构图看起来酷似一张网吧。 ????那么,Feistel网络应该如何解密呢?例如,我们尝试一下将一轮加密的输出结果用相同的子密钥重新运行一次,这时Feistel 网络会怎么样呢?结果可能非常令人意外,无论轮函数的具体算法是什么,通过上述操作都能够将密文正确地还原为明文(图1-3)。关于这一点,大家可以从XOR的性质(两个相同的数进行XOR的结果一定为0)进行思考。 有多个轮的情况下也是一样的。也就是说,Feistel 网络的解密操作只要按照相反的顺序来使用子密钥就可以完成了,而 Feistel网络本身的结构,在加密和解密时都是完全相同的。(图1-5) ????????????????????????????????????????图1-3 Feistel网络的加密(3轮) ? ? ?
????????????????????????????????图1-4 用相同的子秘钥运行两次Feistel网络就能将数据还原
? ? ?
????????????????????????????????????????图1-5 Feistel网络的解密(3轮) ? ? ? ????下面我们来总结一下Feistel网络的性质。 ????最容易发现的一点就是,Feistel网络的轮数可以任意增加。无论运行多少轮加密计算都不会发生无法解密的情况。 ????其次,我们还可以发现,加密时无论使用任何函数作为轮函数都可以正确解密。也就是说,即便用轮函数的输出结果无法逆向计算出输入的值(即该函数不存在反函数)也没有问题。轮函数可以无需考虑解密的问题,可以被设计得任意复杂。 ??Feistel网络实际上就是从加密算法中抽取出"密码的本质部分"并将其封装成一个轮函数。只要使用Feistel网络,就能够保证一定可以解密。因此,设计密码算法的人只要努力设计出足够复杂的就可以了。 ????另外,加密和解密可以用完全相同的结构来实现,这也是 Feistel网络的一个特点。在Feistel 网络的一轮中,右半部分实际上没有进行任何处理,这在加密算法中看起来是一种浪费,但却保证了可解密性,因为完全没有进行任何处理的右半部分,是解密过程中所必需的信息。由于加密和解密可以用完全相同的结构来实现,因此用于实现 DES算法的硬件设备的设计也变得容易了。 综上所述,无论是任何轮数、任何轮函数,Feistel 网络都可以用相同的结构实现加密和解密,且加密的结果必定能够正确解密。 正是由于Feistel网络具备如此方便的特性,它才能够被许多分组密码算法使用。
2分组密码的模式
2.1分组密码和模式
?? ?? 分组密码(block cipher)是每次只能处理特定长度的一块数据的一类密码算法,这里的"一块"就称为分组(block)。此外,一个分组的比特数就称为分组长度(block length )。 ????例如,DES和三重 DES 的分组长度都是64 比特。这些密码算法一次只能加密64 比特的明文.并生成64比特的密文。 ????AES的分组长度可以从128 比特、192比特和 256比特中进行选择。当选择128 比特的分组长度时,AES 一次可加密128比特的明文,并生成128 比特的密文。 ????分组密码算法只能加密固定长度的分组,但是我们需要加密的明文长度可能会超过分组密码的分组长度,这时就需要对分组密码算法进行迭代,以便将一段很长的明文全部加密。而迭代的方法就称为分组密码的模式(mode )。 这里主要对比下两种模式: ● ECB模式∶Electronic CodeBook mode(电子密码本模式) ● CBC模式∶Cipher Block Chaining mode(密码分组链接模式)
2.2 ECB模式
????ECB 模式的全称是 Electronic CodeBook 模式。在ECB模式中,将明文分组加密之后的结果将直接成为密文分组。 ????????????????????????????????????????????图2-1 ECB模式
2.2 CBC模式
2.2.1 CBC概述
????CBC模式的全称是Cipher Block Chaining模式(密文分组链接模式),之所以叫这个名字,是因为密文分组是像链条一样相互连接在一起的。在CBC模式中,首先将明文分组与前一个密文分组进行XOR运算,然后再进行加密。 ????????????????????????????????????????????图2-2 CBC模式
2.2.2 初始化向量
????当加密第一个明文分组时,由于不存在"前一个密文分组",因此需要事先准备一个长度为一个分组的比特序列来代替"前一个密文分组",这个比特序列称为初始化向量(initialization vector),通常缩写为IV。一般来说,每次加密时都会随机产生一个不同的比特序列来作为初始化向量。
3 具体实现
3.1 DES算法步骤
????DES算法的步骤包括IP置换、密钥置换、扩展置换、S盒代替、P盒置换和末置换。 算法流程图: ????????????????????????????????????????图3-1 DES算法流程图
3.2 IP置换
????IP置换目的是将输入的64位数据块按位重新组合,并把输出分为L0、R0两部分,每部分各长32位。 ????IP置换表:
const uint8_t IPP[64] = {
57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3,
61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7,
56, 48, 40, 32, 24, 16, 8, 0, 58, 50, 42, 34, 26, 18, 10, 2,
60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6
};
????IP置换表如上定义,表中的数字代表重新组合后的数据块中,在此位置的数据在原数据块中的位置。 ????即原数据块的第57位放到新数据的第0位,第49位放到第2位,……依此类推,第6位放到第63位。 ????注:假设数据块存在数组dataBlock[8]中,那么dataBlock[0]的最高位为第0位,dataBlock[7]的最低位为第63位。
3.3 秘钥置换
秘钥置换的目的是得到56位的秘钥。 ????不考虑每个字节的第8位,DES的密钥由64位减至56位,每个字节的第8位作为奇偶校验位; 产生56位秘钥的置换表如下:
const uint8_t Choose56[56] = {
56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, 9, 1,
58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 62, 54, 46, 38,
30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 60, 52, 44, 36,
28, 20, 12, 4, 27, 19, 11, 3
};
????置换规则参照IP置换。
3.4 生成子秘钥
????子秘钥(subkey)为48位的秘钥,参与每次轮函数的计算。 ????生成子秘钥步骤: ????1、秘钥循环移位。将56位秘钥分成两部分,每部分28位,根据轮数,将这两部分分别循环左移1 or 2位(加密左移,解密右移); ????2、压缩置换。通过置换表(压缩置换),置换出48bit子秘钥; 综上,可以看出,在秘钥置换阶段,一共发生了两次置换,第一次得到56位秘钥,第二次得到48位的子秘钥。 ????分步讲解: 1、秘钥循环移位 移位表如下:
const uint8_t key_round[32] = {
1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1,
0, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
};
????表中前16位为,加密时,每轮左移的位数。后16位为,解密时,每轮右移的位数。 循环移位示例如下: ????????????????????????????????????????????图3-2 循环移位
2、压缩置换 ????从56位中选出48位。这个过程中,既置换了每位的顺序,又选择了子密钥,因此称为压缩置换。压缩置换表如下:
const uint8_t E[48] = {
13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, 22, 18, 11, 3,
25, 7, 15, 6, 26, 19, 12, 1, 40, 51, 30, 36, 46, 54, 29, 39,
50, 44, 32, 47, 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31
};
3.5 扩展置换
????扩展置换是将IP置换后获得的32位的右半部分R0,扩展为48位。 扩展的目的是,生成与子秘钥长度相同的数据,来进行异或运算。 扩展置换表如下:
const uint8_t Choose48[48] = {
31, 0, 1, 2, 3, 4, 3, 4, 5, 6, 7, 8, 7, 8, 9, 10,
11, 12, 11, 12, 13, 14, 15, 16, 15, 16, 17, 18, 19, 20, 19, 20,
21, 22, 23, 24, 23, 24, 25, 26, 27, 28, 27, 28, 29, 30, 31, 0
};
3.6 S盒替代
????把子密钥和扩展置换得到的48位右半部分R0进行异或运算,运算的结果作为S盒的输入。 S盒一共8个,把48位数据分为8组,每组6位。一个分组对应一个盒,对对应的S盒做替代操作。 ????????????????????????????????????????????图3-3 S-Box替代图 ????一个S盒就是一个4行14列的表,盒中的每一项为一个4bit的数。输入的6位数,取最低和最高bit组合成的数作为行H,中间4bit组合成的数作为列L。在S盒中找到H行L列的数,即为次S盒输出的4bit数据。如输入:000110,则取最高位和最低位为00,中间4位为0011,即0行3列。 ????S盒(S-BOX)定义如下:
const uint8_t S[8][64] = { {
14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13
},
{
15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9
},
{
10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12
},
{
7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14
},
{
2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3
},
{
12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13
},
{
4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12
},
{
13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
}
};
3.7 P盒置换
????P盒置换的输入为S盒替换的输出。 ????P盒置换的结果,与最初64位分组数据的左半部分L0异或,然后交换左右部分,接着开始下一轮的计算。 ????p盒定义如下:
const uint8_t PP[32] = {
15, 6, 19, 20, 28, 11, 27, 16, 0 , 14, 22, 25, 4, 17, 30, 9,
1, 7, 23, 13, 31, 26, 2, 8, 18, 12, 29, 5, 21, 10, 3, 24
};
????综上,DES算法一共要进行16轮的计算,参与每轮计算的步骤有:
????1、生成子秘钥 ????2、扩展置换 ????3、S盒替代 ????4、P盒置换 ????5、P盒置换的结果,与最初64位分组数据的左半部分L0异或,然后交换左右部分,接着开始下一轮的计算
3.8 IP-1末置换
????IP-1末置换是初始IP置换的逆过程,DES最后一轮后,左、右两半部分并未进行交换,而是两部分合并形成一个分组做为末置换的输入。 末置换定义如下:
const uint8_t IPN[64] = {
39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, 14, 54, 22, 62, 30,
37, 5, 45, 13, 53, 21, 61, 29, 36, 4, 44, 12, 52, 20, 60, 28,
35, 3, 43, 11, 51, 19, 59, 27, 34, 2, 42, 10, 50, 18, 58, 26,
33, 1, 41, 9, 49, 17, 57, 25, 32, 0, 40, 8, 48, 16, 56, 24
};
????末置换得到的结果即为,一次DES计算得到的密文输出(64bit)。
4 源代码
头文件,包含des加密和解密函数的声明。
#pragma once
#ifndef __DES_H__
#define __DES_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#define DES_BLOCK_LEN 8
#define DES_KEY_LEN_8 8
typedef enum _des_mode_e {
DES_MODE_ECB,
DES_MODE_CBC
} des_mode_e;
int32_t crypto_des_encrypt(const uint8_t *data, uint32_t data_len, uint8_t *out, const uint8_t *iv,
const uint8_t *key, uint32_t key_len, des_mode_e mode);
int32_t crypto_des_decrypt(const uint8_t *data, uint32_t data_len, uint8_t *out, const uint8_t *iv,
const uint8_t *key, uint32_t key_len, des_mode_e mode);
#ifdef __cplusplus
}
#endif
#endif
源文件,包含des加密和解密函数的实现。仅支持CBC模式。
#include "stdafx.h"
#include<stdlib.h>
#include <stdio.h>
#include <string.h>
#include "des_cbc.h"
#define DES_TRUE 0x01
#define DES_FALSE 0x00
#define DES_DO_INITIAL_PERMUTATION 0x10
#define DES_ENCRYPTION_MODE 0x20
#define DES_DECRYPTION_MODE 0
#define DES_DO_FINAL_PERMUTATION 0x40
#define DES_ENCRYPT_BLOCK (DES_DO_INITIAL_PERMUTATION|DES_ENCRYPTION_MODE|DES_DO_FINAL_PERMUTATION)
#define DES_DECRYPT_BLOCK (DES_DO_INITIAL_PERMUTATION|DES_DECRYPTION_MODE|DES_DO_FINAL_PERMUTATION)
const uint8_t IPP[64] = {
57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3,
61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7,
56, 48, 40, 32, 24, 16, 8, 0, 58, 50, 42, 34, 26, 18, 10, 2,
60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6
};
const uint8_t IPN[64] = {
39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, 14, 54, 22, 62, 30,
37, 5, 45, 13, 53, 21, 61, 29, 36, 4, 44, 12, 52, 20, 60, 28,
35, 3, 43, 11, 51, 19, 59, 27, 34, 2, 42, 10, 50, 18, 58, 26,
33, 1, 41, 9, 49, 17, 57, 25, 32, 0, 40, 8, 48, 16, 56, 24
};
const uint8_t Choose56[56] = {
56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, 9, 1,
58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 62, 54, 46, 38,
30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 60, 52, 44, 36,
28, 20, 12, 4, 27, 19, 11, 3
};
const uint8_t key_round[32] = {
1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1,
0, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
};
const uint8_t Choose48[48] = {
31, 0, 1, 2, 3, 4, 3, 4, 5, 6, 7, 8, 7, 8, 9, 10,
11, 12, 11, 12, 13, 14, 15, 16, 15, 16, 17, 18, 19, 20, 19, 20,
21, 22, 23, 24, 23, 24, 25, 26, 27, 28, 27, 28, 29, 30, 31, 0
};
const uint8_t E[48] = {
13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, 22, 18, 11, 3,
25, 7, 15, 6, 26, 19, 12, 1, 40, 51, 30, 36, 46, 54, 29, 39,
50, 44, 32, 47, 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31
};
const uint8_t PP[32] = {
15, 6, 19, 20, 28, 11, 27, 16, 0 , 14, 22, 25, 4, 17, 30, 9,
1, 7, 23, 13, 31, 26, 2, 8, 18, 12, 29, 5, 21, 10, 3, 24
};
const uint8_t S[8][64] = { {
14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13
},
{
15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9
},
{
10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12
},
{
7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14
},
{
2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3
},
{
12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13
},
{
4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12
},
{
13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
}
};
const uint8_t _bitposition[8] = {
0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
};
static void check_table(uint8_t line, uint8_t *text, uint8_t *lasttext, const uint8_t *IDD)
{
uint8_t i, j, k, temp, temp2;
for (j = 0, k = 0; j < line; j++, k += 8) {
lasttext[j] = 0;
for (i = 0; i < 8; i++) {
temp2 = IDD[k + i];
temp = text[temp2 / 8];
temp &= _bitposition[temp2 & 0x7];
if (temp) {
lasttext[j] |= _bitposition[i];
}
}
}
}
static void single_des_operation(uint8_t *plaintext, uint8_t *key, uint8_t mode)
{
static uint8_t prevtext[8];
uint8_t prevkey[8], Ltext[4], Rtext[4];
char temp, temp1;
int32_t i = 0;
int32_t Round = 0;
uint8_t j = 0;
if (mode & DES_DO_INITIAL_PERMUTATION) {
check_table(8, plaintext, prevtext, IPP);
}
for (i = 0; i < 4; i++) {
Ltext[i] = prevtext[i];
Rtext[i] = prevtext[i + 4];
}
check_table(7, key, prevkey, Choose56);
for (Round = 0; Round < 16; Round++) {
if (mode & DES_ENCRYPTION_MODE) {
for (j = 0; j < key_round[Round]; j++) {
temp = prevkey[3] & 0x08;
for (i = 7; i > 0; i--) {
temp1 = prevkey[i - 1] & 0x80;
prevkey[i - 1] <<= 1;
if (temp) {
prevkey[i - 1] |= 0x01;
}
temp = temp1;
}
if (temp) {
prevkey[3] |= 0x10;
}
else {
prevkey[3] &= 0xEF;
}
}
}
else {
for (j = 0; j < key_round[Round + 16]; j++) {
temp = prevkey[3] & 0x10;
for (i = 0; i < 7; i++) {
temp1 = prevkey[i] & 0x01;
prevkey[i] >>= 1;
if (temp) {
prevkey[i] |= 0x80;
}
temp = temp1;
}
if (temp) {
prevkey[3] |= 0x08;
}
else {
prevkey[3] &= 0xF7;
}
}
}
check_table(6, prevkey, plaintext, E);
check_table(6, Rtext, prevtext, Choose48);
prevtext[0] ^= plaintext[0];
prevtext[1] ^= plaintext[1];
prevtext[2] ^= plaintext[2];
prevtext[3] ^= plaintext[3];
prevtext[4] ^= plaintext[4];
prevtext[5] ^= plaintext[5];
for (j = 6, i = 8; j > 0; j -= 3, i -= 4) {
plaintext[i - 1] = prevtext[j - 1];
plaintext[i - 2] = ((prevtext[j - 1] >> 6) & 0x03) | (prevtext[j - 2] << 2);
plaintext[i - 3] = ((prevtext[j - 2] >> 4) & 0x0f) | (prevtext[j - 3] << 4);
plaintext[i - 4] = prevtext[j - 3] >> 2;
}
for (i = 0; i < 8; i++) {
temp = plaintext[i] & 0x21;
if (temp & 0x01) {
temp = (temp & 0x20) | 0x10;
}
plaintext[i] = temp | ((plaintext[i] >> 1) & 0x0F);
}
plaintext[0] = S[0][plaintext[0]];
plaintext[1] = S[1][plaintext[1]];
plaintext[2] = S[2][plaintext[2]];
plaintext[3] = S[3][plaintext[3]];
plaintext[4] = S[4][plaintext[4]];
plaintext[5] = S[5][plaintext[5]];
plaintext[6] = S[6][plaintext[6]];
plaintext[7] = S[7][plaintext[7]];
plaintext[0] = (plaintext[0] << 4) | plaintext[1];
plaintext[1] = (plaintext[2] << 4) | plaintext[3];
plaintext[2] = (plaintext[4] << 4) | plaintext[5];
plaintext[3] = (plaintext[6] << 4) | plaintext[7];
check_table(4, plaintext, prevtext, PP);
for (i = 0; i < 4; i++) {
prevtext[i] ^= Ltext[i];
Ltext[i] = Rtext[i];
Rtext[i] = prevtext[i];
}
}
for (i = 0; i < 4; i++) {
prevtext[i] = Rtext[i];
prevtext[i + 4] = Ltext[i];
}
if (mode & DES_DO_FINAL_PERMUTATION) {
check_table(8, prevtext, plaintext, IPN);
}
}
static int32_t xor_operation(uint8_t *out,
const uint8_t *data1,
const uint8_t *data2,
uint32_t dwLen)
{
int32_t ret = DES_TRUE;
uint32_t i = 0;
if ((dwLen != 0) && ((data1 == NULL) || (data2 == NULL) || (out == NULL))) {
ret = DES_FALSE;
}
else {
for (i = 0; i < dwLen; i++) {
out[i] = data1[i] ^ data2[i];
}
}
return ret;
}
int32_t crypto_des_encrypt(const uint8_t *data,
uint32_t data_len,
uint8_t *out,
const uint8_t *iv,
const uint8_t *key,
uint32_t key_len,
des_mode_e mode)
{
uint32_t i = 0;
uint32_t j = 0;
uint32_t k = 0;
if ((data_len % DES_BLOCK_LEN != 0) || \
(key_len != DES_KEY_LEN_8) ) {
return -1;
}
for (i = 0; i < data_len / DES_BLOCK_LEN; i++) {
if (mode == DES_MODE_CBC) {
if (i > 0) {
xor_operation(&out[i * DES_BLOCK_LEN], &data[i * DES_BLOCK_LEN], \
&out[(i - 1) * DES_BLOCK_LEN], DES_BLOCK_LEN);
}
else {
xor_operation(&out[i], &data[i], iv, DES_BLOCK_LEN);
}
}
if (key_len == DES_KEY_LEN_8) {
single_des_operation(&out[i * DES_BLOCK_LEN], (uint8_t *)&key[0], DES_ENCRYPT_BLOCK);
}
}
return 0;
}
int32_t crypto_des_decrypt(const uint8_t *data,
uint32_t data_len,
uint8_t *out,
const uint8_t *iv,
const uint8_t *key,
uint32_t key_len,
des_mode_e mode)
{
int32_t i = 0;
if ((data_len % DES_BLOCK_LEN != 0) || \
(key_len != DES_KEY_LEN_8)) {
return -1;
}
for (i = data_len / DES_BLOCK_LEN - 1; i >= 0; i--) {
memcpy(&out[i * DES_BLOCK_LEN], &data[i * DES_BLOCK_LEN], DES_BLOCK_LEN);
if (key_len == DES_KEY_LEN_8) {
single_des_operation(&out[i * DES_BLOCK_LEN], (uint8_t *)&key[0], DES_DECRYPT_BLOCK);
}
if (mode == DES_MODE_CBC) {
if (i > 0) {
xor_operation(&out[i * DES_BLOCK_LEN], &out[i * DES_BLOCK_LEN], \
&data[(i - 1) * DES_BLOCK_LEN], DES_BLOCK_LEN);
}
else {
xor_operation(&out[i], &out[i], iv, DES_BLOCK_LEN);
}
}
}
return 0;
}
测试用例:
#include "stdafx.h"
#include<stdlib.h>
#include <stdio.h>
#include <string.h>
#include "des_cbc.h"
typedef struct _des_test_data_t {
const char *data;
const char *key;
const char *iv;
_des_mode_e mode;
} des_test_data_t;
typedef struct _byte_t {
uint8_t low : 4;
uint8_t high : 4;
} byte_t;
static des_test_data_t g_des_test_data =
{
"12623132336261aa6162aa32f1626100",
"6777696e30383031",
"6777696e30383031",
DES_MODE_CBC
};
#define UTILS_ERR -1
#define UTILS_OK 0
int32_t utils_hex_string_2_bytes(const char *hextring, uint8_t *bytes, uint16_t *length)
{
const char *p = hextring;
byte_t tmp;
uint16_t cnt = 0;
if ((hextring == NULL) || (bytes == NULL) || (length == NULL)) {
return UTILS_ERR;
}
*length = 0;
if ((strlen(p) % 2) != 0) {
return UTILS_ERR;
}
while (*p != '\0') {
if ((*p >= '0') && (*p <= '9')) {
tmp.high = *p - '0';
}
else if ((*p >= 'A') && (*p <= 'F')) {
tmp.high = *p - 'A' + 10;
}
else if ((*p >= 'a') && (*p <= 'f')) {
tmp.high = *p - 'a' + 10;
}
else {
return UTILS_ERR;
}
p++;
if ((*p >= '0') && (*p <= '9')) {
tmp.low = *p - '0';
}
else if ((*p >= 'A') && (*p <= 'F')) {
tmp.low = *p - 'A' + 10;
}
else if ((*p >= 'a') && (*p <= 'f')) {
tmp.low = *p - 'a' + 10;
}
else {
return UTILS_ERR;
}
p++;
bytes[cnt++] = *(uint8_t *)& tmp;
}
*length = cnt;
return UTILS_OK;
}
static int des_test(des_test_data_t *data)
{
uint8_t data_hex[128];
uint16_t data_len;
uint8_t key[24];
uint16_t key_len;
uint8_t iv[24];
uint16_t iv_len;
uint8_t data_calc[128];
uint8_t data_calc2[128];
int32_t ret;
int32_t i;
utils_hex_string_2_bytes(data->key, key, &key_len);
utils_hex_string_2_bytes(data->iv, iv, &iv_len);
utils_hex_string_2_bytes(data->data, data_hex, &data_len);
if ((data_len % 8) != 0)
{
uint8_t fill_zero = (8 - (data_len % 8));
for (uint8_t i = data_len; i < data_len + fill_zero; i++)
data_hex[i] = 0;
data_len += fill_zero;
}
ret = crypto_des_encrypt(data_hex, data_len, data_calc, iv, key, key_len, data->mode);
printf("data_enc:");
for (i = 0; i < data_len; i++) {
printf("%02x ", data_calc[i]);
}
printf("\n");
ret = crypto_des_decrypt(data_calc, data_len, data_calc2, iv, key, key_len, data->mode);
printf("data_dec:");
for (i = 0; i < data_len; i++) {
printf("%02x ", data_calc2[i]);
}
printf("\n");
return ret;
}
int main(int argc, const char *argv[])
{
int ret;
ret = des_test(&g_des_test_data);
system("pause");
return 0;
}
输出结果演示,在Visual Studio 2015上编译运行结果:
参考文章: DES算法详解 【安全算法之DES】DES算法(支持ECB/CBC模式)的C语言源码实现
|