DES-CBC加密详解以及C实现


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-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
}; //Choosing the 56bit key from 64bit

    置换规则参照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
}; //shift key left //shift key right

    表中前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
}; //Compression and permutation for key

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
}; //expands the right half text of 32 bits to 48 bits

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
}; //P-box permutation

    综上,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
}; //Inverse permutation

    末置换得到的结果即为,一次DES计算得到的密文输出(64bit)。

4 源代码

头文件,包含des加密和解密函数的声明。


#pragma once
#ifndef __DES_H__
#define __DES_H__

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

#include <stdint.h>

#define DES_BLOCK_LEN 		8       // 8bytes

#define DES_KEY_LEN_8		8		// key-length: 8bytes  -> single DES

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 /* __cplusplus */

#endif /*__DES_H__*/

源文件,包含des加密和解密函数的实现。仅支持CBC模式。

// des_cbc.cpp : 定义控制台应用程序的入口点。
//

#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
}; //Inverse permutation

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
}; //Choosing the 56bit key from 64bit

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
}; //shift key left //shift key right

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
}; //expands the right half text of 32 bits to 48 bits

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
}; //Compression and permutation for key

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
}; //P-box permutation

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]; // get the byte
			temp &= _bitposition[temp2 & 0x7]; // get the bit
			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); //Initial permutation
	}
	//将置换后的数据,分为“左侧”和“右侧”
	for (i = 0; i < 4; i++) {
		Ltext[i] = prevtext[i];
		Rtext[i] = prevtext[i + 4];
	}

	/*
	产生子秘钥过程:
	1、通过置换产生56位秘钥
	2、将56位秘钥分成两部分,每部分28位
	3、根据轮数,将这两部分分别循环左移1 or 2位
	4、通过置换表(压缩置换),置换出48bit子秘钥
	*/
	check_table(7, key, prevkey, Choose56);

	for (Round = 0; Round < 16; Round++) {
		//rotate both 28bits block to left(encrypt) or right(decrypt)
		if (mode & DES_ENCRYPTION_MODE) { // encrypt

			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 { // decrypt			   
			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); //Compression permutation
		check_table(6, Rtext, prevtext, Choose48); //Expansion permutation
												   //Expanded right half xor_operation with the subkey
		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) { //S-Box Substitution
			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++) { //Get the S-Box location
			temp = plaintext[i] & 0x21;
			if (temp & 0x01) {
				temp = (temp & 0x20) | 0x10;
			}
			plaintext[i] = temp | ((plaintext[i] >> 1) & 0x0F);
		}

		//Get S-Box output
		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]];



		//Combine 4-bit block to form 32-bit block
		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); //Final permutation
	}
}

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
			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            
			single_des_operation(&out[i * DES_BLOCK_LEN], (uint8_t *)&key[0], DES_DECRYPT_BLOCK);
		}

		/*
		解密模式下,是把密文按照des的逆运算后,把得到的输出,与相邻的,编号较小的一个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语言源码实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值