ChaCha20 加密算法的实现与逆向分析
selph 发表于 北京 二进制安全 1033浏览 · 2025-06-24 09:00

简介

ChaCha20是一种流密码算法,用于加密和解密数据流。它由Daniel J. Bernstein于2008年设计,是Salsa20的改进版,被广泛应用于网络通信、加密文件和存储介质等领域。

ChaCha20算法使用256位的密钥(32字节) 和一个12字节的随机数(称为nonce) 作为输入,生成一个可变长度的伪随机比特流,然后与明文进行异或运算得到密文。ChaCha20算法的特点是快速、安全、易于实现和内存友好,适用于高速网络通信和移动设备。

ChaCha20 的解密过程与加密完全相同,因为异或操作是可逆的,类似的算法还有RC4

与RC4的区别

Chacha20和RC4都是流密码算法,但它们的加密原理有以下几个区别:

1 密钥长度:Chacha20的密钥长度为256位(32字节) ,而RC4的密钥长度为1到256字节不等。因此,Chacha20可以提供更强的密钥安全性,也更适合用于现代加密应用。

2 S盒:Chacha20没有S盒,而RC4使用一个256字节的S盒来生成密钥流。Chacha20是基于置换的加密算法它使用固定的置换来生成密钥流

3 加密过程:在Chacha20中,通过对输入的明文数据块进行置换和加密操作,生成密文输出。每次加密需要根据密钥和计数器生成一个新的置换和密钥流。而RC4是在密钥生成完毕后,直接用密钥流对输入的明文进行异或操作得到密文输出。

4安全性:由于RC4存在一些安全性问题,它已经不被推荐使用。而Chacha20已经被广泛应用于TLS、SSH、IPsec等加密通信协议中,并且其安全性得到了广泛认可。

chacha20 算法流程介绍

1初始化:

构建一个固定的 4x4 矩阵(64字节)。

这个矩阵由四部分填充:固定的128位魔数(16字节)256位密钥(32字节)96位Nonce(12字节) 和一个 32位计数器(4字节)

1密钥流生成:

将初始状态矩阵复制一份。

对副本进行20轮高度混淆的轮函数操作(核心是Quarter Round,即ARX)。

将混淆后的矩阵与原始矩阵相加,得到一个64字节的密钥流块。

将计数器加1,重复以上过程,生成下一个密钥流块。

1对明文使用密钥流块进行逐字节异或,得到这一块的密文(每块加密64字节)

具体过程看接下来的源码分析就好理解了

chacha20 算法代码实现分析

本节算法源码基于开源项目 Ginurx/chacha20-c: ChaCha20 stream cipher implemented in C 进行详细分析:

代码主要分为三个部分:

chacha20_context

chacha20 算法维护一个结构体:

结构体中保存加密过程需要的信息,包括counter,nonce,key,position,密钥流,初始状态state

counter:8字节,计算加密块数,每一次加密64字节后就会+1,默认初始是0

nonce:12字节,会放入初始状态state,state中和计数器有4字节是重合的(感觉像是用来凑数的

key:32字节,密钥

position:记录当前加密用的索引,0-63,到64了说明该计算新的密钥流了

密钥流keystream:64字节,每个块加密都会生成一个,用于和明文异或进行加密

初始状态state:64字节,初始状态,包括16字节魔数,32字节密钥,4字节计数器,12字节nonce

这里最核心的部分就是初始状态state,每轮运算都是基于初始状态进行的

chacha20_init_context

初始化过程中,初始化state和设置计数器counter和位置position的值:

这里使用pack4函数进行打包:

就是将密钥字符串按照小端序进行组装

关于状态state,通过16字节魔数,32字节密钥,4字节计数器,12字节nonce的顺序拼接到64字节的state中,计数器的设置在另一个函数里

计数器的设置,其中低4字节保存在state的规定位置中

高4字节和nonce的前4字节进行加法运算,重叠在一起

最后就是将position设置为64,以便第一次加密的时候直接初始化密钥流keystream

chacha20_xor

这里是生成密钥流和加密的过程,是chacha20算法的核心

这里初始情况下,keystream还是0,没有赋值,初始化的时候将position设置为了64,所以这里会直接进入chacha20_block_next函数去生成密钥流keystream

当完成生成之后,keystream是64字节的数组,逐字节和明文进行异或

最核心的计算在chacha20_block_next中:

第一步,将初始状态state的64字节,复制到keystream中,按照4字节一个,组成4*4的矩阵

第二步,10次双轮变换(20轮变换)

第三步,将新得到的4*4矩阵和初始矩阵相加,计数器+=1

这里的核心是10次双轮变换这里,矩阵的成员标号如下:

第一轮对列进行处理,也就是:

第二轮对对角线进行处理:

具体是哪几个成员,根据函数参数和上面的标号去匹配就行

处理函数chacha20_quarterround:

这里会有特征:会有大量的加法-异或-右移位组合(具体内容看代码注释吧)

在逆向中,因为编译器优化的问题,可能会导致这些函数都被内联了,10次双轮变换,将8次四分之一轮变化放在一起了,那就会出现8*4=32次右移特征

到此,chacha20的加密过程分析完毕,接下来看看逆向静态看见的chacha20

chacha20 逆向识别 & 示例题目求解

示例题目:HackTheBox Challenge reverse - CryptOfTheUndead

chacha20的逆向识别

先回到今天的主题,chacha20算法的逆向分析,使用该题目先进行chacha20部分的分析,后面再介绍题目和求解

程序中使用了一个函数encrypt_buf,这里使用了chacha20进行加密操作(这里是我重命名后的反编译结果)

首先是初始化缓冲区,然后进入第一个函数,init:

看到大量的变量名+偏移的赋值,说明此处可能是个结构体

然后可以看到,这里初始化了结构体内存之后,将一个16字节的固定数据保存到了ctx+128开始的地方

然后是将key赋值在ctx+144开始的地方

然后紧接着是counter和nonce,以及counter和nonce重叠部分相加运算

设置一个值为64,就是之前分析的position

逆向看这个有点不好看,但是重命名变量之后,看到这个排列方式(16字节魔数,32字节密钥,4字节计数器,12字节nonce),就是chacha20的初始化特征

然后看下一个函数,太长了分段看:

整体的框架如上,首先是一个判断63,小于等于63,就异或一个值(buf_ptr)和明文(ctx_5),然后position++

else的部分太长了,继续分段看:

首先是一系列赋值,分析可以看到,是从ctx_1赋值16个4字节给16个变量

这里的ctx_1的值来自v1,v1来自ctx+128,也就是之前初始化阶段分析得到的state的偏移

接下来是一个10次的循环:

这里就是编译器优化的结果了,将每一次四分之一轮运算都内联到一起了,明显的特征,32个加-异或-右移位的组合,进行10轮循环

接着看:

然后就是将10次2轮计算的结果保存到ctx_2,这里的异常处理是防止counter溢出

然后和原本的ctx_3进行加法,设置position=0

和之前正向代码的特征匹配,逆向看着就是更复杂了

示例题目分析&求解

题目是一个elf文件加密器和一个加密的文件,让我们求得原文

main函数:

不难猜出,flag是被加密了的,需要进行解密,这里加密使用了chacha20算法

那么根据异或可逆,chacha20相同key生成相同的keystream

那么只需要对加密的文件再次加密,就可以得到明文:

总结:chacha20 逆向题目解法

和RC4一样,都是通过key生成一个东西(rc4是s-box,chacha20是keystream),然后基于这个东西对明文进行异或

对于程序使用相同的key,那么对密文加密就可以得到明文

参考资料

[0] chacha20加密算法笔记 - 知乎

[1] Ginurx/chacha20-c: ChaCha20 stream cipher implemented in C

0 条评论
某人
表情
可输入 255