碎碎念
最近越发觉得对常用加密算法的研究比较粗糙,借着完善社团文档的机会,再继续修改一下我的文章.
一些前置小知识
一、为什么需要加密模式(Modes of Operation)
像 RC6 这样的分组加密算法,是固定长度加密的:
- RC6-32/20/16 每次只能加密 128 bit(16 字节) 数据块
- 如果明文超过 16 字节,就必须把数据切成块,然后用一定的“模式(mode)”来处理多块数据
- 不同模式会影响安全性和加密结果的可预测
常见的几种模式:ECB, CBC, CFB, OFB, CTR ...
我们现在主要看 ECB 和 CBC
二、ECB 模式
工作原理
- 把明文分成一个个 16 字节的独立块
- 每个块单独用 RC6 加密(互不影响)
- 解密时也是每块独立解密
优点
✅ 实现简单
✅ 可以并行加密(每块独立不依赖前一块)
缺点(安全性问题)
❌ 如果两个明文块相同,密文块也会相同(模式可见性攻击)
❌ 不能隐藏数据模式,容易泄漏明文结构(比如图像加密后还能看出形状)
形象例子
- 用 ECB 加密 BMP 图片,虽然颜色被改变,但形状和轮廓还清晰可见
三、CBC 模式
工作原理
- 第一个明文块
P1
先与一个 初始化向量 IV(随机) 异或,再加密 - 每个后续块
Pi
在加密前先与前一个密文块 Ci-1 进行异或,然后再加密 - 解密时反过来:先解密,再异或前一块密文或 IV
公式
加密: C1 = E( P1 ⊕ IV )
Ci = E( Pi ⊕ Ci-1 )
解密: P1 = D( C1 ) ⊕ IV
Pi = D( Ci ) ⊕ Ci-1
优点
✅ 相同明文块在不同位置加密结果不同,消除了 ECB 的模式问题
✅ 使用随机 IV,每次加密结果都不同,即使明文相同
缺点
❌ 必须按顺序加密(不能并行加密)
✅ 但是解密可以并行(因为解密不依赖下一个块)
典型应用
绝大多数对称加密文件和通信协议(SSL/TLS、SSH)会用 CBC 或其他更安全的模式(如 GCM)
四、PKCS#7 自动补齐(padding)
为什么要补齐
- 分组加密算法需要固定块大小,比如 RC6 一次只能加密 16 字节
- 如果明文不是 16 字节的整数倍,就需要补齐到整块大小
- 即使刚好对齐,也要补一块,以防止解密时无法判断填充长度
PKCS#7 规则
- 假设要补 k 个字节,就用 k 作为填充字节的值
- 每个填充字节都相同
- 最少补 1 个字节,最多补 16 个字节
举例
块大小 16 字节,明文长度 13 字节:
明文: [41 42 43 44 45 46 47 48 49 4A 4B 4C 4D]
填充: [03 03 03] ←(3 个字节,值为 03)
明文: [41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 03 03 03]
总长度: 16 字节
主要的算法介绍
Tea/XTea/XXTea/IDEA/RC4/RC5/RC6/AES/DES/IDEA/MD5/SHA256/SHA1等加密算法
TEA系列算法
参数解析
- 分组大小: 64位 (8字节)
- 密钥大小: 128位 (16字节)
https://www.cnblogs.com/zpchcbd/p/15974293.html
可以参照这篇博客,写的比较详细
标准TEA
#include <stdio.h>
#include <stdint.h>
//加密函数
void encrypt (uint32_t* v, uint32_t* k) {
uint32_t v0=v[0], v1=v[1], sum=0, i; /* set up */
uint32_t delta=0x9e3779b9;
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
for (i=0; i < 32; i++) { /* basic cycle start */
sum += delta;
v0 += ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
v1 += ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
} /* end cycle */
v[0]=v0; v[1]=v1;
}
//解密函数
void decrypt (uint32_t* v, uint32_t* k) {
uint32_t v0=v[0], v1=v[1], sum=0xC6EF3720, i;
uint32_t delta=0x9e3779b9;
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];
for (i=0; i<32; i++) {
v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
sum -= delta;
}
v[0]=v0; v[1]=v1;
}
int main()
{
uint32_t v[2]={1,2},k[4]={2,2,3,4};
// v为要加密的数据是两个32位无符号整数(四个字节)
// k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
printf("加密前原始数据:%u %u\n",v[0],v[1]);
encrypt(v, k);
printf("加密后的数据:%u %u\n",v[0],v[1]);
decrypt(v, k)
printf("解密后的数据:%u %u\n",v[0],v[1]);
return 0;
}
XTEA算法
#include <stdio.h>
#include <stdint.h>
//加密函数
void encrypt (uint32_t* v, uint32_t* k) {
uint32_t v0=v[0], v1=v[1], sum=0, i;
uint32_t delta=0x9e3779b9;
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];
for (i=0; i < 32; i++) {
sum += delta;
v0 += ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
v1 += ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
}
v[0]=v0; v[1]=v1;
}
//解密函数
void decrypt (uint32_t* v, uint32_t* k) {
uint32_t v0=v[0], v1=v[1], sum=0xC6EF3720, i;
uint32_t delta=0x9e3779b9;
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];
for (i=0; i<32; i++) {
v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1)
sum -= delta;
}
v[0]=v0; v[1]=v1;
}
int main()
{
uint32_t v[2]={1,2},k[4]={2,2,3,4};
// v为要加密的数据是两个32位无符号整数(四个字节)
// k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
printf("加密前原始数据:%u %u\n",v[0],v[1]);
encrypt(v, k);
printf("加密后的数据:%u %u\n",v[0],v[1]);
decrypt(v, k);
printf("解密后的数据:%u %u\n",v[0],v[1]);
return 0;
}
XXTEA算法
#include <stdio.h>
#include <stdint.h>
#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
void btea(uint32_t *v, int n, uint32_t const key[4])
{
uint32_t y, z, sum;
unsigned p, rounds, e;
if (n > 1) /* Coding Part */
{
rounds = 6 + 52/n;
sum = 0;
z = v[n-1];
do
{
sum += DELTA;
e = (sum >> 2) & 3;
for (p=0; p<n-1; p++)
{
y = v[p+1];
z = v[p] += MX;
}
y = v[0];
z = v[n-1] += MX;
}
while (--rounds);
}
else if (n < -1) /* Decoding Part */
{
n = -n;
rounds = 6 + 52/n;
sum = rounds*DELTA;
y = v[0];
do
{
e = (sum >> 2) & 3;
for (p=n-1; p>0; p--)
{
z = v[p-1];
y = v[p] -= MX;
}
z = v[n-1];
y = v[0] -= MX;
sum -= DELTA;
}
while (--rounds);
}
}
int main()
{
uint32_t v[2]= {1,2};
uint32_t const k[4]= {2,2,3,4};
int n= 2;
// n的绝对值表示v的长度,取正表示加密,取负表示解密
// v为要加密的数据是两个32位无符号整数
// k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
printf("加密前原始数据:%u %u\n",v[0],v[1]);
btea(v, n, k);
printf("加密后的数据:%u %u\n",v[0],v[1]);
btea(v, -n, k);
printf("解密后的数据:%u %u\n",v[0],v[1]);
return 0;
}
RC4算法
RC4算法是一种对称加密算法
- 密钥大小: 可变,通过KSA拓展展得到一个256字节的状态数组
- PRGA: 用 S 生成一个伪随机字节流,全程与密钥有关.
- 明文和生成的密钥按位异或,得到密文,解密时再异或一次即可恢复原文
def KSA(key):
""" KSA 密钥拓展 """
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key[i % len(key)]) % 256
S[i], S[j] = S[j], S[i]
return S
def PRGA(S):
""" PRGA """
i, j = 0, 0
while True:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
K = S[(S[i] + S[j]) % 256]
yield K
def RC4(key, text):
""" RC4 加密流程 """
S = KSA(key)
keystream = PRGA(S)
res = []
for char in text:
res.append(char ^ next(keystream))
return bytes(res)
RC5算法
一种分组对称加密算法
参数解析
- RC5有三个主要参数:
RC5-W/R/B
- w:字长(word size),常用 32(位)、也可以是 16、64
- r:加密轮数(rounds),常用 12 或 20
- b:密钥长度(bytes),取值 0~255 字节,常用 16 字节(128 位)
- 分组大小
- 数据块大小 =
2 × w
位 - 如果 w = 32,则数据块大小为 64 位(8 字节)
- 如果 w = 64,则数据块大小为 128 位(16 字节)
- 数据块大小 =
- 密钥大小
- 密钥长度 b 可以从 0 到 255 字节可变
- 常用设置:
- 128 位密钥(16 字节)
- 192 位(24 字节)
- 256 位(32 字节)
算法实现
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define WORD uint32_t
#define w 32 // 字长
#define r 12 // 轮数
#define b 16 // 密钥字节数
#define c (b / 4) // 密钥字的个数
#define t (2 * (r + 1)) // 子密钥表的大小
#define P32 0xB7E15163
#define Q32 0x9E3779B9
WORD S[t]; // 子密钥表
// 左循环
WORD ROTL(WORD x, WORD y) {
return (x << (y & (w - 1))) | (x >> (w - (y & (w - 1))));
}
// 右循环
WORD ROTR(WORD x, WORD y) {
return (x >> (y & (w - 1))) | (x << (w - (y & (w - 1))));
}
// 密钥扩展
void RC5_KeySchedule(const uint8_t *key) {
WORD L[c];
memset(L, 0, sizeof(L));
// 将密钥转换为 L 数组
for (int i = b - 1; i >= 0; i--) {
L[i / 4] = (L[i / 4] << 8) + key[i];
}
// 初始化 S 表
S[0] = P32;
for (int i = 1; i < t; i++) {
S[i] = S[i - 1] + Q32;
}
// 混合 S 和 L
WORD A = 0, B = 0;
int i = 0, j = 0;
for (int k = 0; k < 3 * ((t > c) ? t : c); k++) {
A = S[i] = ROTL(S[i] + A + B, 3);
B = L[j] = ROTL(L[j] + A + B, A + B);
i = (i + 1) % t;
j = (j + 1) % c;
}
}
// 加密 64 位明文(两个 32 位字)
void RC5_Encrypt(WORD *pt, WORD *ct) {
WORD A = pt[0] + S[0];
WORD B = pt[1] + S[1];
for (int i = 1; i <= r; i++) {
A = ROTL(A ^ B, B) + S[2 * i];
B = ROTL(B ^ A, A) + S[2 * i + 1];
}
ct[0] = A;
ct[1] = B;
}
// 解密
void RC5_Decrypt(WORD *ct, WORD *pt) {
WORD B = ct[1];
WORD A = ct[0];
for (int i = r; i >= 1; i--) {
B = ROTR(B - S[2 * i + 1], A) ^ A;
A = ROTR(A - S[2 * i], B) ^ B;
}
pt[1] = B - S[1];
pt[0] = A - S[0];
}
// 十六进制打印
void print_hex(const char *label, WORD *data) {
printf("%s: %08X %08X\n", label, data[0], data[1]);
}
// 示例主函数
int main() {
// 16字节密钥
uint8_t key[b] = {
0x91, 0x5F, 0x46, 0x19, 0xBE, 0x41, 0xB2, 0x51,
0x63, 0x55, 0xA5, 0x01, 0x10, 0xA9, 0xCE, 0x91
};
WORD plaintext[2] = { 0x12345678, 0x9ABCDEF0 };
WORD ciphertext[2] = { 0, 0 }; // 加密缓冲区
WORD decrypted[2] = { 0, 0 }; // 解密缓冲区
RC5_KeySchedule(key); // 密钥拓展,生成子密钥数组
RC5_Encrypt(plaintext, ciphertext); // RC5加密
RC5_Decrypt(ciphertext, decrypted); // RC5解密
print_hex("Plaintext", plaintext);
print_hex("Ciphertext", ciphertext);
print_hex("Decrypted", decrypted);
return 0;
}
RC6算法
一种分组对称加密算法
是RC5的改进版
参数分析
- RC6用一下参数表示
RC6-w/r/b
- w:字长(word size),常用 32 位
- r:轮数(rounds),AES 竞赛版本使用 20 轮
- b:密钥长度(bytes),支持 0~255 字节
- AES 竞赛推荐参数:
RC6-32/20/16
- w = 32 位(每个数据字占 4 字节)
- 分组大小 = 4 × w = 128 位(16 字节)
- 轮数 r = 20
- 密钥长度 = 16 字节(128 位)
算法实现
#include <stdio.h>
#include <stdint.h>
#define w 32 // 字长
#define r 20 // 轮数
#define b 16 // 密钥长度
#define Pw 0xB7E15163 // Magic constants for key schedule (for w=32)
#define Qw 0x9E3779B9 // Magic constants for key schedule (for w=32)
// 轮位移实现
#define ROTL(x, y) (((x) << ((y) & (w - 1))) | ((x) >> (w - ((y) & (w - 1)))))
#define ROTR(x, y) (((x) >> ((y) & (w - 1))) | ((x) << (w - ((y) & (w - 1)))))
// 子密钥数组
uint32_t S[2 * r + 4];
//--------------------------------------
// 密钥扩展函数(b字节用户密钥 -> S[] 子密钥)
//--------------------------------------
void RC6_key_schedule(uint8_t *K) {
int i, j, s, v;
uint32_t L[(b + 3) / 4]; // 将字节密钥转换为字数组
uint32_t A, B;
// Step 1: 把用户密钥K[]装入L[],低位优先
for (i = b - 1, L[(b + 3) / 4 - 1] = 0; i != -1; i--)
L[i / 4] = (L[i / 4] << 8) + K[i];
// Step 2: 初始化子密钥S[]
S[0] = Pw;
for (i = 1; i < 2 * r + 4; i++)
S[i] = S[i - 1] + Qw;
// Step 3: 混合操作
A = B = i = j = 0;
v = 3 * ((b + 3) / 4 > 2 * r + 4 ? (b + 3) / 4 : 2 * r + 4);
for (s = 0; s < v; s++) {
A = S[i] = ROTL(S[i] + A + B, 3);
B = L[j] = ROTL(L[j] + A + B, (A + B));
i = (i + 1) % (2 * r + 4);
j = (j + 1) % ((b + 3) / 4);
}
}
//--------------------------------------
// 加密函数
//--------------------------------------
void RC6_encrypt(uint32_t *pt, uint32_t *ct) {
uint32_t A = pt[0], B = pt[1], C = pt[2], D = pt[3];
uint32_t t, u;
int i;
B += S[0];
D += S[1];
for (i = 1; i <= r; i++) {
t = ROTL(B * (2 * B + 1), 5);
u = ROTL(D * (2 * D + 1), 5);
A = ROTL(A ^ t, u) + S[2 * i];
C = ROTL(C ^ u, t) + S[2 * i + 1];
// Rotate registers
{
uint32_t tmp = A;
A = B; B = C; C = D; D = tmp;
}
}
A += S[2 * r + 2];
C += S[2 * r + 3];
ct[0] = A; ct[1] = B; ct[2] = C; ct[3] = D;
}
//--------------------------------------
// 解密函数
//--------------------------------------
void RC6_decrypt(uint32_t *ct, uint32_t *pt) {
uint32_t A = ct[0], B = ct[1], C = ct[2], D = ct[3];
uint32_t t, u;
int i;
C -= S[2 * r + 3];
A -= S[2 * r + 2];
for (i = r; i >= 1; i--) {
// Rotate registers backward
{
uint32_t tmp = D;
D = C; C = B; B = A; A = tmp;
}
t = ROTL(B * (2 * B + 1), 5);
u = ROTL(D * (2 * D + 1), 5);
C = ROTR(C - S[2 * i + 1], t) ^ u;
A = ROTR(A - S[2 * i], u) ^ t;
}
D -= S[1];
B -= S[0];
pt[0] = A; pt[1] = B; pt[2] = C; pt[3] = D;
}
//--------------------------------------
// 测试
//--------------------------------------
int main() {
uint8_t key[b] = {0x00,0x01,0x02,0x03,
0x04,0x05,0x06,0x07,
0x08,0x09,0x0A,0x0B,
0x0C,0x0D,0x0E,0x0F};
uint32_t plaintext[4] = {0x01234567, 0x89ABCDEF, 0xFEDCBA98, 0x76543210};
uint32_t ciphertext[4], decrypted[4];
RC6_key_schedule(key); // 密钥拓展
RC6_encrypt(plaintext, ciphertext);
RC6_decrypt(ciphertext, decrypted);
printf("Plaintext: %08X %08X %08X %08X\n", plaintext[0], plaintext[1], plaintext[2], plaintext[3]);
printf("Ciphertext: %08X %08X %08X %08X\n", ciphertext[0], ciphertext[1], ciphertext[2], ciphertext[3]);
printf("Decrypted: %08X %08X %08X %08X\n", decrypted[0], decrypted[1], decrypted[2], decrypted[3]);
return 0;
}
AES算法
一种对称分组加密算法
参数分析
- 密钥长度 (NK)
- 决定了加密强度,也直接影响轮数
- Nk 表示密钥包含的 32位字数(word)(一个 word = 4 字节)
Nk = 4 → AES-128 (4*32 = 128 位密钥) Nk = 6 → AES-192 (6*32 = 192 位密钥) Nk = 8 → AES-256 (8*32 = 256 位密钥)
- 密钥轮数 (Nr)
AES-128 → Nr = 10 轮 AES-192 → Nr = 12 轮 AES-256 → Nr = 14 轮
- 轮密钥数量 (Nb)
- Nb 固定为 4(表示 4 列,即 4×4=16 字节)
- 表示状态矩阵有 4 列
- 每轮需要 Nb × 4 字节的子密钥(16 字节)
比赛中的魔改
1. 修改SBOX
2. 修改RCON轮常量
3. 修改位移逻辑,应该不常见
代码实现
/*
* Minimal AES (Rijndael) encryption in C
* - AES-128/192/256 key schedule
* - ECB block encrypt
* - CBC + PKCS#7 padding encrypt
* No external dependencies.
*
* Build: gcc -O2 -std=c99 aes.c -o aes
*/
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
/*==================== 常量表:S-box & Rcon ====================*/
/* AES S-Box(FIPS-197,表4) */
/* ========用于密钥拓展======= */
static const uint8_t sbox[256] = {
0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16
};
/* 轮常量 Rcon(只需到 14 即可) */
/* ========用于密钥拓展======== */
static const uint8_t Rcon[15] = {
0x00, /* unused */
0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1B,0x36,
0x6C,0xD8,0xAB,0x4D
};
/*==================== 工具宏与上下文 ====================*/
typedef struct {
/* round_keys:按“轮 × 16 字节”顺序存储,总长最多 15*16=240 */
uint8_t round_keys[240];
int Nr; /* 轮数:AES-128=10, AES-192=12, AES-256=14 */
} aes_ctx;
/* GF(2^8) 上的 xtime:乘以 2 */
static inline uint8_t xtime(uint8_t x) {
return (uint8_t)((x << 1) ^ ((x & 0x80) ? 0x1B : 0x00));
}
/*==================== 核心变换:SubBytes / ShiftRows / MixColumns / AddRoundKey ====================*/
/* 对 state 的 16 字节逐字节过 S-box */
static inline void SubBytes(uint8_t st[16]) {
for (int i = 0; i < 16; ++i) st[i] = sbox[st[i]];
}
/* AES 的行移位(state 按列主序:索引 4*c + r) */
static inline void ShiftRows(uint8_t st[16]) {
uint8_t t;
/* 第1行:1字节循环左移:索引 1,5,9,13 */
t = st[1];
st[1] = st[5];
st[5] = st[9];
st[9] = st[13];
st[13] = t;
/* 第2行:2字节循环左移:索引 2,6,10,14 */
t = st[2];
st[2] = st[10];
st[10] = t;
t = st[6];
st[6] = st[14];
st[14] = t;
/* 第3行:3字节循环左移:索引 3,7,11,15 */
t = st[3];
st[3] = st[15];
st[15] = st[11];
st[11] = st[7];
st[7] = t;
}
/* 列混淆:对每一列做 GF(2^8) 线性变换 */
static inline void MixColumns(uint8_t st[16]) {
for (int c = 0; c < 4; ++c) {
uint8_t *col = &st[4*c];
uint8_t a0 = col[0], a1 = col[1], a2 = col[2], a3 = col[3];
uint8_t t = (uint8_t)(a0 ^ a1 ^ a2 ^ a3);
uint8_t u0 = a0;
col[0] ^= t ^ xtime((uint8_t)(a0 ^ a1));
col[1] ^= t ^ xtime((uint8_t)(a1 ^ a2));
col[2] ^= t ^ xtime((uint8_t)(a2 ^ a3));
col[3] ^= t ^ xtime((uint8_t)(a3 ^ u0));
}
}
/* 加轮密钥:st[i] ^= rk[i] */
static inline void AddRoundKey(uint8_t st[16], const uint8_t *rk) {
for (int i = 0; i < 16; ++i) st[i] ^= rk[i];
}
/*==================== Key Schedule(密钥扩展) ====================*/
/* 旋转 4 字节:a0 a1 a2 a3 -> a1 a2 a3 a0 */
static inline void RotWord(uint8_t w[4]) {
uint8_t t = w[0];
w[0] = w[1]; w[1] = w[2]; w[2] = w[3]; w[3] = t;
}
/* 4 字节逐字节 S-box */
static inline void SubWord(uint8_t w[4]) {
w[0] = sbox[w[0]];
w[1] = sbox[w[1]];
w[2] = sbox[w[2]];
w[3] = sbox[w[3]];
}
/* 生成 round_keys(按轮 × 16 字节顺序),支持 128/192/256 */
static int aes_init(aes_ctx *ctx, const uint8_t *key, int keybits) {
int Nk, Nr;
if (keybits == 128) { Nk = 4; Nr = 10; }
else if (keybits == 192) { Nk = 6; Nr = 12; }
else if (keybits == 256) { Nk = 8; Nr = 14; }
else return -1;
ctx->Nr = Nr;
/* W 一共 Nb*(Nr+1) 个 32bit 词;Nb=4 -> 4*(Nr+1) 个词 -> (Nr+1)*16 字节 */
const int Nb = 4;
const int total_words = Nb * (Nr + 1);
uint8_t *rk = ctx->round_keys;
/* 先拷入初始密钥(Nk*4 字节) */
for (int i = 0; i < Nk*4; ++i) rk[i] = key[i];
int iword = Nk; /* 已生成的词个数,从 Nk 开始 */
int rconi = 1; /* Rcon 索引,从 1 开始 */
uint8_t temp[4];
while (iword < total_words) {
/* temp = W[i-1] */
temp[0] = rk[4*(iword-1) + 0];
temp[1] = rk[4*(iword-1) + 1];
temp[2] = rk[4*(iword-1) + 2];
temp[3] = rk[4*(iword-1) + 3];
if (iword % Nk == 0) {
RotWord(temp);
SubWord(temp);
temp[0] ^= Rcon[rconi++];
} else if (Nk > 6 && (iword % Nk == 4)) {
/* AES-256 额外子字节替换 */
SubWord(temp);
}
/* W[i] = W[i-Nk] XOR temp */
rk[4*iword + 0] = (uint8_t)(rk[4*(iword-Nk) + 0] ^ temp[0]);
rk[4*iword + 1] = (uint8_t)(rk[4*(iword-Nk) + 1] ^ temp[1]);
rk[4*iword + 2] = (uint8_t)(rk[4*(iword-Nk) + 2] ^ temp[2]);
rk[4*iword + 3] = (uint8_t)(rk[4*(iword-Nk) + 3] ^ temp[3]);
++iword;
}
return 0;
}
/*==================== 单块加密(ECB 基元) ====================*/
static void aes_encrypt_block(const aes_ctx *ctx,
const uint8_t in[16],
uint8_t out[16]) {
uint8_t st[16];
memcpy(st, in, 16);
const uint8_t *rk = ctx->round_keys;
/* 轮 0:AddRoundKey */
AddRoundKey(st, rk);
rk += 16;
/* 轮 1..Nr-1 */
for (int round = 1; round < ctx->Nr; ++round) {
SubBytes(st);
ShiftRows(st);
MixColumns(st);
AddRoundKey(st, rk);
rk += 16;
}
/* 最后一轮:无 MixColumns */
SubBytes(st);
ShiftRows(st);
AddRoundKey(st, rk);
memcpy(out, st, 16);
}
/*==================== CBC + PKCS#7 填充加密 ====================*/
/*
* in_len: 明文长度
* iv_in : 16 字节初始向量
* out : 输出缓冲,需至少 in_len + (16 - in_len%16) 字节
* out_len: 返回实际输出长度
*/
static int aes_cbc_encrypt_pkcs7(const aes_ctx *ctx,
const uint8_t *in, size_t in_len,
const uint8_t iv_in[16],
uint8_t *out, size_t *out_len) {
uint8_t iv[16];
memcpy(iv, iv_in, 16);
size_t pad = 16 - (in_len % 16);
if (pad == 0) pad = 16; /* PKCS#7:整块也要补 16 个 0x10 */
size_t total = in_len + pad;
size_t nblocks = total / 16;
size_t i = 0, b = 0;
/* 处理整块部分 */
for (; b < in_len / 16; ++b) {
uint8_t buf[16];
/* XOR 明文与 IV/上一块密文 */
for (int j = 0; j < 16; ++j)
buf[j] = (uint8_t)(in[i + j] ^ iv[j]);
/* 加密 */
aes_encrypt_block(ctx, buf, &out[i]);
/* 更新 IV 为本块密文 */
memcpy(iv, &out[i], 16);
i += 16;
}
/* 处理尾块 + PKCS#7 填充 */
{
uint8_t last[16];
size_t rem = in_len - i; /* 剩余字节数(0..15) */
/* 拷入剩余字节 */
for (size_t j = 0; j < rem; ++j)
last[j] = in[i + j];
/* 填充 pad 值 */
for (size_t j = rem; j < 16; ++j)
last[j] = (uint8_t)pad;
/* CBC XOR */
for (int j = 0; j < 16; ++j)
last[j] ^= iv[j];
aes_encrypt_block(ctx, last, &out[i]);
i += 16;
}
*out_len = i;
(void)nblocks; /* 未直接使用,仅注释用 */
return 0;
}
/*==================== 可选:自测(FIPS-197 单块) ====================*/
#ifdef AES_TEST
static void print_hex(const uint8_t *p, size_t n) {
for (size_t i = 0; i < n; ++i) printf("%02x", p[i]);
printf("\n");
}
int main(void) {
/* FIPS-197 附录C:AES-128 单块测试 */
const uint8_t key128[16] = {
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f
};
const uint8_t pt[16] = {
0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,
0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff
};
const uint8_t expect_ct[16] = {
0x69,0xc4,0xe0,0xd8,0x6a,0x7b,0x04,0x30,
0xd8,0xcd,0xb7,0x80,0x70,0xb4,0xc5,0x5a
};
aes_ctx ctx;
if (aes_init(&ctx, key128, 128) != 0) {
printf("init failed\n");
return 1;
}
uint8_t ct[16];
aes_encrypt_block(&ctx, pt, ct);
printf("AES-128 block enc expect:\n");
print_hex(expect_ct, 16);
printf("result:\n");
print_hex(ct, 16);
/* 额外演示 CBC+PKCS#7 */
const uint8_t iv[16] = {
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f
};
const uint8_t msg[] = "Hello AES-CBC with PKCS7!";
uint8_t out[128]; size_t outlen = 0;
aes_cbc_encrypt_pkcs7(&ctx, msg, sizeof(msg)-1, iv, out, &outlen);
printf("CBC+PKCS7 ciphertext (%zu bytes):\n", outlen);
print_hex(out, outlen);
return 0;
}
#endif
SM4
参数分析
- 输入:128位密文(16字节),128位密钥(16字节)
- 输出:128位明文(16字节)
- 轮数:32轮
- 结构:非线性S盒 + 线性变换(扩散)+ 轮密钥
代码实现
#include <stdint.h>
#include <string.h>
#include <stdio.h>
static const uint8_t SBOX[256] = {
// 标准 S-Box 表,共256字节
0xD1, 0x90, 0xE9, 0xFE, 0xCC, 0xE1, 0x3D, 0xB7, 0x16, 0xB6, 0x14, 0xC2, 0x28, 0xFB, 0x2C, 0x05,
0x2B, 0x67, 0x9A, 0x76, 0x2A, 0xBE, 0x04, 0xC3, 0xAA, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
0x9C, 0x42, 0x50, 0xF4, 0x91, 0xEF, 0x98, 0x7A, 0x33, 0x54, 0x0B, 0x43, 0xED, 0xCF, 0xAC, 0x62,
0xE4, 0xB3, 0x17, 0xA9, 0x1C, 0x08, 0xE8, 0x95, 0x80, 0xDF, 0x94, 0xFA, 0x75, 0x8F, 0x3F, 0xA6,
0x47, 0x07, 0xA7, 0x4F, 0xF3, 0x73, 0x71, 0xBA, 0x83, 0x59, 0x3C, 0x19, 0xE6, 0x85, 0xD6, 0xA8,
0x68, 0x6B, 0x81, 0xB2, 0xFC, 0x64, 0xDA, 0x8B, 0xF8, 0xEB, 0x0F, 0x4B, 0x70, 0x56, 0x9D, 0x35,
0x1E, 0x24, 0x0E, 0x78, 0x63, 0x58, 0x9F, 0xA2, 0x25, 0x22, 0x7C, 0x3B, 0x01, 0x21, 0xC9, 0x87,
0xD4, 0x00, 0x46, 0x57, 0x5E, 0xD3, 0x27, 0x52, 0x4C, 0x36, 0x02, 0xE7, 0xA0, 0xC4, 0xC8, 0x9E,
0xEA, 0xBF, 0x8A, 0xD2, 0x40, 0xC7, 0x38, 0xB5, 0xA3, 0xF7, 0xF2, 0xCE, 0xF9, 0x61, 0x15, 0xA1,
0xE0, 0xAE, 0x5D, 0xA4, 0x9B, 0x34, 0x1A, 0x55, 0xAD, 0x93, 0x32, 0x30, 0xF5, 0x8C, 0xB1, 0xE3,
0x1D, 0xF6, 0xE2, 0x2E, 0x82, 0x66, 0xCA, 0x60, 0xC0, 0x29, 0x23, 0xAB, 0x0D, 0x53, 0x4E, 0x6F,
0xD5, 0xDB, 0x37, 0x45, 0xDE, 0xFD, 0x8E, 0x2F, 0x03, 0xFF, 0x6A, 0x72, 0x6D, 0x6C, 0x5B, 0x51,
0x8D, 0x1B, 0xAF, 0x92, 0xBB, 0xDD, 0xBC, 0x7F, 0x11, 0xD9, 0x5C, 0x41, 0x1F, 0x10, 0x5A, 0xD8,
0x0A, 0xC1, 0x31, 0x88, 0xA5, 0xCD, 0x7B, 0xBD, 0x2D, 0x74, 0xD0, 0x12, 0xB8, 0xE5, 0xB4, 0xB0,
0x89, 0x69, 0x97, 0x4A, 0x0C, 0x96, 0x77, 0x7E, 0x65, 0xB9, 0xF1, 0x09, 0xC5, 0x6E, 0xC6, 0x84,
0x18, 0xF0, 0x7D, 0xEC, 0x3A, 0xDC, 0x4D, 0x20, 0x79, 0xEE, 0x5F, 0x3E, 0xD7, 0xCB, 0x39, 0x48
};
static const uint32_t FK[4] = { 0xA3B1BAC6, 0x56AA3350, 0x677D9197, 0xB27022DC };
static const uint32_t CK[32] = {
0x00070E15, 0x1C232A31, 0x383F464D, 0x545B6269, 0x70777E85, 0x8C939AA1, 0xA8AFB6BD, 0xC4CBD2D9,
0xE0E7EEF5, 0xFC030A11, 0x181F262D, 0x343B4249, 0x50575E65, 0x6C737A81, 0x888F969D, 0xA4ABB2B9,
0xC0C7CED5, 0xDCE3EAF1, 0xF8FF060D, 0x141B2229, 0x30373E45, 0x4C535A61, 0x686F767D, 0x848B9299,
0xA0A7AEB5, 0xBCC3CAD1, 0xD8DFE6ED, 0xF4FB0209, 0x10171E25, 0x2C333A41, 0x484F565D, 0x646B7279
};
// 字节替代 + 线性变换
uint32_t sm4_t(uint32_t x)
{
uint8_t a[4];
a[0] = SBOX[(x >> 24) & 0xFF];
a[1] = SBOX[(x >> 16) & 0xFF];
a[2] = SBOX[(x >> 8) & 0xFF];
a[3] = SBOX[x & 0xFF];
uint32_t t = (a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3];
return t ^ (t << 2 | t >> 30) ^ (t << 10 | t >> 22) ^ (t << 18 | t >> 14) ^ (t << 24 | t >> 8);
}
// 用于密钥扩展的T函数
uint32_t sm4_key_t(uint32_t x)
{
uint8_t a[4];
a[0] = SBOX[(x >> 24) & 0xFF];
a[1] = SBOX[(x >> 16) & 0xFF];
a[2] = SBOX[(x >> 8) & 0xFF];
a[3] = SBOX[x & 0xFF];
uint32_t t = (a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3];
return t ^ (t << 13 | t >> 19) ^ (t << 23 | t >> 9);
}
// 密钥扩展,生成32轮密钥 rk[]
void sm4_key_schedule(const uint8_t key[16], uint32_t rk[32])
{
uint32_t K[36];
for (int i = 0; i < 4; ++i)
K[i] = ((uint32_t)key[4*i] << 24) | ((uint32_t)key[4*i+1] << 16) | ((uint32_t)key[4*i+2] << 8) | key[4*i+3];
for (int i = 0; i < 4; ++i)
K[i] ^= FK[i];
for (int i = 0; i < 32; ++i) {
K[i+4] = K[i] ^ sm4_key_t(K[i+1] ^ K[i+2] ^ K[i+3] ^ CK[i]);
rk[i] = K[i+4];
}
}
// 解密函数
void sm4_decrypt(const uint8_t input[16], const uint8_t key[16], uint8_t output[16])
{
uint32_t rk[32], X[36];
sm4_key_schedule(key, rk);
// 反转轮密钥顺序
for (int i = 0; i < 16; ++i) {
uint32_t tmp = rk[i];
rk[i] = rk[31 - i];
rk[31 - i] = tmp;
}
// 加载明文
for (int i = 0; i < 4; ++i)
X[i] = ((uint32_t)input[4*i] << 24) | ((uint32_t)input[4*i+1] << 16) | ((uint32_t)input[4*i+2] << 8) | input[4*i+3];
// 32轮加密
for (int i = 0; i < 32; ++i)
X[i+4] = X[i] ^ sm4_t(X[i+1] ^ X[i+2] ^ X[i+3] ^ rk[i]);
// 输出(倒序)
for (int i = 0; i < 4; ++i) {
output[4*i] = (X[35 - i] >> 24) & 0xFF;
output[4*i+1] = (X[35 - i] >> 16) & 0xFF;
output[4*i+2] = (X[35 - i] >> 8) & 0xFF;
output[4*i+3] = X[35 - i] & 0xFF;
}
}
int main()
{
uint8_t key[17] = "NCTF24nctfNCTF24";
uint8_t cipher[32] = {
0xFB, 0x97, 0x3C, 0x3B, 0xF1, 0x99, 0x12, 0xDF, 0x13, 0x30, 0xF7, 0xD8, 0x7F, 0xEB, 0xA0, 0x6C,
0x14, 0x5B, 0xA6, 0x2A, 0xA8, 0x05, 0xA5, 0xF3, 0x76, 0xBE, 0xC9, 0x01, 0xF9, 0x36, 0x7B, 0x46
};
uint8_t plain[32];
sm4_decrypt(cipher, key, plain);
sm4_decrypt(cipher+16, key, plain+16);
printf("Decrypted: ");
for (int i = 0; i < 32; ++i)
printf("%c", plain[i]);
printf("\n");
return 0;
}
BASE64编码
Base64 是一种 把二进制数据安全塞进文本 的编码方式,规则是 3字节变4字符,6位一组查表,=
补齐。
代码实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Base64字符表
static const char base64_chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
// 是否是合法Base64字符
static inline int is_base64(unsigned char c) {
return (c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') ||
(c == '+') || (c == '/');
}
// Base64编码
char *base64_encode(const unsigned char *data, size_t input_length, size_t *output_length) {
size_t i, j;
*output_length = 4 * ((input_length + 2) / 3);
char *encoded_data = (char *)malloc(*output_length + 1);
if (encoded_data == NULL) return NULL;
for (i = 0, j = 0; i < input_length;) {
uint32_t octet_a = i < input_length ? data[i++] : 0;
uint32_t octet_b = i < input_length ? data[i++] : 0;
uint32_t octet_c = i < input_length ? data[i++] : 0;
uint32_t triple = (octet_a << 16) | (octet_b << 8) | octet_c;
encoded_data[j++] = base64_chars[(triple >> 18) & 0x3F];
encoded_data[j++] = base64_chars[(triple >> 12) & 0x3F];
encoded_data[j++] = (i > input_length + 1) ? '=' : base64_chars[(triple >> 6) & 0x3F];
encoded_data[j++] = (i > input_length) ? '=' : base64_chars[triple & 0x3F];
}
encoded_data[*output_length] = '\0';
return encoded_data;
}
// Base64解码
unsigned char *base64_decode(const char *data, size_t input_length, size_t *output_length) {
if (input_length % 4 != 0) return NULL;
size_t alloc_len = input_length / 4 * 3;
if (data[input_length - 1] == '=') alloc_len--;
if (data[input_length - 2] == '=') alloc_len--;
unsigned char *decoded_data = (unsigned char *)malloc(alloc_len);
if (decoded_data == NULL) return NULL;
int decoding_table[256];
memset(decoding_table, -1, 256 * sizeof(int));
for (int i = 0; i < 64; i++) {
decoding_table[(unsigned char)base64_chars[i]] = i;
}
size_t i, j;
for (i = 0, j = 0; i < input_length;) {
uint32_t sextet_a = data[i] == '=' ? 0 & i++ : decoding_table[(unsigned char)data[i++]];
uint32_t sextet_b = data[i] == '=' ? 0 & i++ : decoding_table[(unsigned char)data[i++]];
uint32_t sextet_c = data[i] == '=' ? 0 & i++ : decoding_table[(unsigned char)data[i++]];
uint32_t sextet_d = data[i] == '=' ? 0 & i++ : decoding_table[(unsigned char)data[i++]];
uint32_t triple = (sextet_a << 18) + (sextet_b << 12) + (sextet_c << 6) + sextet_d;
if (j < alloc_len) decoded_data[j++] = (triple >> 16) & 0xFF;
if (j < alloc_len) decoded_data[j++] = (triple >> 8) & 0xFF;
if (j < alloc_len) decoded_data[j++] = triple & 0xFF;
}
*output_length = alloc_len;
return decoded_data;
}
// 测试
int main() {
const char *text = "Hello RC6 AES Base64!";
printf("原文: %s\n", text);
size_t enc_len;
char *encoded = base64_encode((const unsigned char *)text, strlen(text), &enc_len);
printf("Base64编码: %s\n", encoded);
size_t dec_len;
unsigned char *decoded = base64_decode(encoded, enc_len, &dec_len);
decoded[dec_len] = '\0'; // 确保当成字符串可以打印
printf("Base64解码: %s\n", decoded);
// 释放内存
free(encoded);
free(decoded);
return 0;
}
哈希算法
Hash,一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出
该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间
不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。
简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
常见哈希算法输出长度:
算法 | 输出长度 |
---|---|
MD5 | 128 位(16字节) |
SHA-1 | 160 位(20字节) |
SHA-256 | 256 位(32字节) |
SHA-512 | 512 位(64字节) |
参考链接:
https://ctf-wiki.org/reverse/identify-encode-encryption/introduction/#base64
https://blog.csdn.net/Pioo_/article/details/110878905
https://www.cnblogs.com/zpchcbd/p/15974293.html
Comments NOTHING