现代密码学 第二章——流密码【下】

五、m序列

1、线性反馈移位寄存器的多项式表示

(1)在上一节中已经使用了递推公式描述LFSR,但这种形式不便于分析序列的周期、线性复杂度等性质,而多项式是数学里分析线性递推关系的"利器",它能把递推关系转化为多项式运算,直接关联到密码学关心的核心指标。

(2)延迟算子与LFSR一元多项式表示:

(3)生成函数的定义及定理:

①生成函数的定义:

②定理1:

③定理2:

④定理3:

2、m序列产生的条件

(1)不可约多项式与本原多项式:

①不可约多项式:

②本原多项式:

(2)m序列产生的必要条件:

(3)m序列产生的充要条件:

(4)m序列举例:

3、m序列的伪随机性

4、m序列的安全性

(1)已知m序列,推算相应的反馈多项式方法:

(2)解方程方法举例:

①例1:

②例2:

(3)线性反馈移位寄存器综合解:

①设计或求解LSFR时,主要考虑以下两个问题:

1如何利用级数尽可能短的LFSR产生周期大、随机性能良好的序列。这是从密钥生成角度考虑,用最小的代价产生尽可能好的、参与密码变换的序列。

2当已知一个长为N的序列时,如何构造一个级数尽可能小的LFSR来产生它。这是从密码分析角度来考虑,花费最小的代价,用线性方法重构密钥序列。

②线性综合解:

③LFSR综合问题:

④Berlekamp-Massey算法:

六、非线性序列

1、Geffe序列生成器

(1)Geffe序列生成器的结构:

Geffe序列生成器由3个LFSR组成,其中LFSR2作为控制生成器使用,如下图所示

当LFSR2输出1时,LFSR2与LFSR1相连接

当LFSR2输出0时,LFSR2与LFSR3相连接

(2)Geffe序列生成器的工作逻辑:

(3)Geffe序列生成器的C语言实现:

cpp 复制代码
#include <stdio.h>
#include <stdint.h>
#include <string.h>

// 定义LFSR结构体
typedef struct {
	uint32_t state;   // 寄存器状态(最高位为抽头位)
	uint32_t mask;    // 抽头掩码(反馈多项式中抽头位对应1)
	uint32_t poly;    // 反馈多项式(仅用于初始化掩码)
	int width;        // 寄存器位宽
} LFSR;

// 初始化LFSR:设置状态和抽头掩码(根据多项式)
void lfsr_init(LFSR *lfsr, uint32_t seed, uint32_t polynomial, int width) {
	lfsr->state = seed & ((1ULL << width) - 1); // 限制在width位内
	if (lfsr->state == 0) lfsr->state = 1;      // 避免全0状态
	lfsr->poly = polynomial;
	lfsr->width = width;

	// 构建掩码:多项式中所有非零项对应的位数(抽头位置)
	lfsr->mask = 0;
	for (int i = 0; i < width; i++) {
		if (polynomial & (1 << i)) {
			lfsr->mask |= (1 << i);
		}
	}
}

// 时钟一次:计算反馈位,移位,返回输出位(移出的最高位)
uint8_t lfsr_clock(LFSR *lfsr) {
	// 计算反馈位:所有抽头位的异或(计算掩码内的位)
	uint32_t feedback = 0;
	uint32_t masked = lfsr->state & lfsr->mask;
	// 快速奇偶校验(计算所有位的异或)
	feedback = masked ^ (masked >> 16);
	feedback ^= feedback >> 8;
	feedback ^= feedback >> 4;
	feedback ^= feedback >> 2;
	feedback ^= feedback >> 1;
	feedback &= 1;

	// 输出位为移出的最高位(第width-1位)
	uint8_t output = (lfsr->state >> (lfsr->width - 1)) & 1;

	// 左移一位,将反馈位放入最低位
	lfsr->state = ((lfsr->state << 1) & ((1ULL << lfsr->width) - 1)) | feedback;

	return output;
}

// 生成一个比特的Geffe输出
uint8_t geffe_bit(LFSR *lfsr1, LFSR *lfsr2, LFSR *lfsr3) {
	uint8_t b1 = lfsr_clock(lfsr1);
	uint8_t b2 = lfsr_clock(lfsr2);
	uint8_t b3 = lfsr_clock(lfsr3);
	// 若b3=1,输出b1;否则输出b2
	return b3 ? b1 : b2;
}

// 生成一个密钥字节(8个连续比特)
uint8_t geffe_byte(LFSR *lfsr1, LFSR *lfsr2, LFSR *lfsr3) {
	uint8_t byte = 0;
	for (int i = 0; i < 8; i++) {
		byte = (byte << 1) | geffe_bit(lfsr1, lfsr2, lfsr3);
	}
	return byte;
}

// 加密函数:数据与Geffe生成的密钥流异或(原地操作)
void geffe_encrypt(uint8_t *data, size_t len,
	uint32_t seed1, uint32_t seed2, uint32_t seed3) {
	LFSR lfsr1, lfsr2, lfsr3;
	// 使用三个不同本原多项式(长度分别为 13, 15, 17)
	// 多项式以二进制表示:x^13 + x^4 + x^3 + x + 1 -> 0x201B (bit13=1, bit4=1, bit3=1, bit1=1)
	lfsr_init(&lfsr1, seed1, (1 << 13) | (1 << 4) | (1 << 3) | (1 << 1) | 1, 13);
	lfsr_init(&lfsr2, seed2, (1 << 15) | (1 << 1) | 1, 15);              // x^15 + x + 1
	lfsr_init(&lfsr3, seed3, (1 << 17) | (1 << 3) | 1, 17);              // x^17 + x^3 + 1

	for (size_t i = 0; i < len; i++) {
		uint8_t keystream = geffe_byte(&lfsr1, &lfsr2, &lfsr3);
		data[i] ^= keystream;
	}
}

// 解密函数(与加密相同,因为异或对称)
void geffe_decrypt(uint8_t *data, size_t len,
	uint32_t seed1, uint32_t seed2, uint32_t seed3) {
	geffe_encrypt(data, len, seed1, seed2, seed3);
}

// 辅助函数:打印十六进制
void print_hex(const uint8_t *data, size_t len) {
	for (size_t i = 0; i < len; i++) {
		printf("%02X ", data[i]);
	}
	printf("\n");
}

int main() {
	// 明文示例
	uint8_t plaintext[] = "Geffe sequence generator example!";
	size_t len = strlen((char*)plaintext);

	// 三个种子(密钥),不能为0,且应小于各自LFSR的最大周期
	uint32_t seed1 = 0x1A2B;   // 13位,小于8192
	uint32_t seed2 = 0x3C4D;   // 15位,小于32768
	uint32_t seed3 = 0x5E6F;   // 17位,小于131072

	printf("原始明文: %s\n", plaintext);
	printf("明文(HEX): ");
	print_hex(plaintext, len);

	// 加密
	geffe_encrypt(plaintext, len, seed1, seed2, seed3);
	printf("密文(HEX): ");
	print_hex(plaintext, len);

	// 解密(使用相同种子)
	geffe_decrypt(plaintext, len, seed1, seed2, seed3);
	printf("解密后明文: %s\n", plaintext);

	return 0;
}

2、J-K触发器与Pless生成器

(1)J-K触发器的结构:

(2)利用1个J-K触发器构成的非线性序列生成器:

(3)利用1个J-K触发器构成的非线性序列生成器的弱点:

(4)Pless生成器的结构:

Pless生成器由8个LFSR、4个J-K触发器和1个循环计数器构成,由循环计数器进行选通控制,如图所示

(5)Pless生成器的C语言实现:

cpp 复制代码
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>

// ---------- 1. LFSR 结构体及实现 ----------
typedef struct {
	uint32_t state;
	uint32_t poly;
	uint8_t width;
} LFSR;

void lfsr_init(LFSR *lfsr, uint32_t seed, uint32_t poly, uint8_t width) {
	lfsr->state = seed & ((1UL << width) - 1);
	if (lfsr->state == 0 && width < 32) lfsr->state = 1;
	lfsr->poly = poly;
	lfsr->width = width;
}

uint8_t lfsr_clock(LFSR *lfsr) {
	uint8_t fb_bit = 0;
	for (uint8_t i = 0; i < lfsr->width; i++) {
		if ((lfsr->poly >> i) & 1) {
			fb_bit ^= (lfsr->state >> i) & 1;
		}
	}
	uint8_t output_bit = (lfsr->state >> (lfsr->width - 1)) & 1;
	lfsr->state = ((lfsr->state << 1) & ((1UL << lfsr->width) - 1)) | fb_bit;
	return output_bit;
}

// ---------- 2. JK 触发器结构体及实现 ----------
typedef struct {
	uint8_t q; // 当前输出状态
} JK_FlipFlop;

void jk_init(JK_FlipFlop *jk, uint8_t init_q) {
	jk->q = init_q; // 初始化为 0 或 1
}

uint8_t jk_clock(JK_FlipFlop *jk, uint8_t j, uint8_t k) {
	uint8_t next_q;
	if (j == 0 && k == 0) {
		next_q = jk->q; // 保持当前状态
	}
	else if (j == 0 && k == 1) {
		next_q = 0; // 复位 (Reset)
	}
	else if (j == 1 && k == 0) {
		next_q = 1; // 置位 (Set)
	}
	else { // (j == 1 && k == 1)
		next_q = 1 ^ jk->q; // 翻转 (Toggle)
	}
	jk->q = next_q;
	return jk->q;
}

// ---------- 3. Pless 生成器 ----------
#define NUM_LFSR_PAIRS 4

typedef struct {
	LFSR lfsr_j;
	LFSR lfsr_k;
	JK_FlipFlop jk;
} PlessUnit;

typedef struct {
	PlessUnit units[NUM_LFSR_PAIRS];
	uint8_t counter; // 循环计数器 (0 到 NUM_LFSR_PAIRS-1)
} PlessGenerator;

// 初始化Pless生成器
void pless_init(PlessGenerator *pg, const uint32_t seeds_J[4], const uint32_t seeds_K[4]) {
	// 8个LFSR的多项式和位宽(示例参数,可按需调整)
	const uint32_t polys_J[NUM_LFSR_PAIRS] = { 0x8003, 0x801B, 0x8021, 0x803D }; // x^15 + x + 1, 等等
	const uint32_t polys_K[NUM_LFSR_PAIRS] = { 0x8049, 0x8065, 0x808F, 0x8020 }; // 不同多项式
	const uint8_t width_J[NUM_LFSR_PAIRS] = { 15, 15, 15, 15 };
	const uint8_t width_K[NUM_LFSR_PAIRS] = { 15, 15, 15, 15 };

	for (int i = 0; i < NUM_LFSR_PAIRS; i++) {
		lfsr_init(&pg->units[i].lfsr_j, seeds_J[i], polys_J[i], width_J[i]);
		lfsr_init(&pg->units[i].lfsr_k, seeds_K[i], polys_K[i], width_K[i]);
		jk_init(&pg->units[i].jk, 0);
	}
	pg->counter = 0;
}

// 产生1位密钥流 -> Pless生成器的一个时钟
uint8_t pless_clock(PlessGenerator *pg) {
	// 1. 从循环计数器中选择当前的单元
	uint8_t idx = pg->counter;
	PlessUnit *unit = &pg->units[idx];

	// 2. 驱动选中的两个LFSR各前进1步,产生JK触发器的J和K输入
	uint8_t j_in = lfsr_clock(&unit->lfsr_j);
	uint8_t k_in = lfsr_clock(&unit->lfsr_k);

	// 3. JK触发器产生输出
	uint8_t out_bit = jk_clock(&unit->jk, j_in, k_in);

	// 4. 循环计数器前进到下一个单元
	pg->counter = (pg->counter + 1) % NUM_LFSR_PAIRS;

	return out_bit;
}

// ---------- 4. 加密/解密接口 ----------
void pless_encrypt(uint8_t *data, size_t len, const uint32_t seeds_J[4], const uint32_t seeds_K[4]) {
	PlessGenerator pg;
	pless_init(&pg, seeds_J, seeds_K);

	for (size_t i = 0; i < len; i++) {
		uint8_t keystream_byte = 0;
		for (int bit = 0; bit < 8; bit++) {
			keystream_byte = (keystream_byte << 1) | pless_clock(&pg);
		}
		data[i] ^= keystream_byte;
	}
}

// 异或对称性,解密函数与加密相同
void pless_decrypt(uint8_t *data, size_t len, const uint32_t seeds_J[4], const uint32_t seeds_K[4]) {
	pless_encrypt(data, len, seeds_J, seeds_K);
}

// ---------- 5. 主函数演示 ----------
void print_hex(uint8_t *data, size_t len) {
	for (size_t i = 0; i < len; i++) printf("%02X ", data[i]);
	printf("\n");
}

int main() {
	uint32_t seeds_J[4] = { 0x1234, 0x5678, 0x9ABC, 0xDEF0 };
	uint32_t seeds_K[4] = { 0x1357, 0x2468, 0x9BDF, 0xACE0 };

	char plaintext[] = "Pless generator example!";
	size_t len = strlen(plaintext);
	uint8_t *data = (uint8_t*)malloc(len);
	memcpy(data, plaintext, len);

	printf("原始明文: %s\n", plaintext);
	printf("明文(HEX): "); print_hex(data, len);

	pless_encrypt(data, len, seeds_J, seeds_K);
	printf("密文(HEX): "); print_hex(data, len);

	pless_decrypt(data, len, seeds_J, seeds_K);
	printf("解密后明文: %s\n", data);

	free(data);
	return 0;
}

3、钟控序列生成器

(1)钟控序列生成器的结构:

钟控序列最基本的模型是用一个LFSR控制另外一个LFSR的移位时钟脉冲,如下图所示,这是一个最简单的钟控序列生成器(停走生成器),下面也以此结构对钟控序列生成器进行介绍

(2)钟控序列的周期:

(3)钟控序列的线性复杂度:

(4)钟控序列生成器的C语言实现:

cpp 复制代码
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>

// ========== LFSR 结构体 ==========
typedef struct {
	uint32_t state;      // 当前状态(最高位为输出位)
	uint32_t mask;       // 抽头掩码(反馈多项式对应的位)
	int width;           // 寄存器宽度
} LFSR;

// 初始化LFSR:根据种子和抽头掩码(种子不能为0)
void lfsr_init(LFSR *lfsr, uint32_t seed, uint32_t tap_mask, int width) {
	lfsr->state = seed & ((1UL << width) - 1);
	if (lfsr->state == 0) lfsr->state = 1;   // 避免全0死锁
	lfsr->mask = tap_mask;
	lfsr->width = width;
}

// LFSR时钟一次,返回输出位(移出的最高位)
uint8_t lfsr_clock(LFSR *lfsr) {
	// 计算反馈位:抽头位的异或
	uint32_t fb = 0;
	uint32_t masked = lfsr->state & lfsr->mask;
	// 快速奇偶校验
	fb = masked ^ (masked >> 16);
	fb ^= fb >> 8;
	fb ^= fb >> 4;
	fb ^= fb >> 2;
	fb ^= fb >> 1;
	fb &= 1;

	uint8_t out = (lfsr->state >> (lfsr->width - 1)) & 1;
	lfsr->state = ((lfsr->state << 1) & ((1UL << lfsr->width) - 1)) | fb;
	return out;
}

// ========== 停走生成器结构体 ==========
typedef struct {
	LFSR control;   // 控制LFSR(决定目标时钟)
	LFSR target;    // 目标LFSR(输出序列)
} StopGoGen;

// 初始化停走生成器
void stopgo_init(StopGoGen *gen,
	uint32_t ctrl_seed, uint32_t ctrl_mask, int ctrl_width,
	uint32_t targ_seed, uint32_t targ_mask, int targ_width) {
	lfsr_init(&gen->control, ctrl_seed, ctrl_mask, ctrl_width);
	lfsr_init(&gen->target, targ_seed, targ_mask, targ_width);
}

// 产生一个输出比特(停走机制)
uint8_t stopgo_bit(StopGoGen *gen) {
	// 控制LFSR始终时钟
	uint8_t ctrl_out = lfsr_clock(&gen->control);

	// 如果控制输出为1,则目标LFSR时钟一次;否则目标不时钟
	if (ctrl_out == 1) {
		lfsr_clock(&gen->target);  // 注意:这里只时钟,丢弃其输出(也可以保存输出)
	}
	// 目标LFSR的当前输出作为最终输出(即每次调用都返回目标寄存器的当前最高位)
	// 注意:目标可能没时钟,所以输出与前一次相同。
	return (gen->target.state >> (gen->target.width - 1)) & 1;
}

// 产生一个密钥字节(收集8个输出比特)
uint8_t stopgo_byte(StopGoGen *gen) {
	uint8_t byte = 0;
	for (int i = 0; i < 8; i++) {
		byte = (byte << 1) | stopgo_bit(gen);
	}
	return byte;
}

// ========== 流密码加解密接口 ==========
void stopgo_encrypt(uint8_t *data, size_t len,
	uint32_t ctrl_seed, uint32_t ctrl_mask, int ctrl_width,
	uint32_t targ_seed, uint32_t targ_mask, int targ_width) {
	StopGoGen gen;
	stopgo_init(&gen, ctrl_seed, ctrl_mask, ctrl_width,
		targ_seed, targ_mask, targ_width);
	for (size_t i = 0; i < len; i++) {
		uint8_t ks = stopgo_byte(&gen);
		data[i] ^= ks;
	}
}

void stopgo_decrypt(uint8_t *data, size_t len,
	uint32_t ctrl_seed, uint32_t ctrl_mask, int ctrl_width,
	uint32_t targ_seed, uint32_t targ_mask, int targ_width) {
	// 流密码加解密相同
	stopgo_encrypt(data, len, ctrl_seed, ctrl_mask, ctrl_width,
		targ_seed, targ_mask, targ_width);
}

// ========== 辅助函数 ==========
void print_hex(const uint8_t *data, size_t len) {
	for (size_t i = 0; i < len; i++) {
		printf("%02X ", data[i]);
	}
	printf("\n");
}

// ========== 主函数演示 ==========
int main() {
	// 选择两个LFSR的参数(可使用本原多项式对应的抽头掩码)
	// 控制LFSR: 宽度13,抽头掩码 x^13 + x^4 + x^3 + x + 1 对应位: 13,4,3,1,0
	// 掩码 = (1<<12) | (1<<3) | (1<<2) | (1<<0) ? 注意:通常宽度13,最高位索引12
	// 为简单,使用常见15级LFSR: x^15 + x + 1 掩码 = (1<<14) | (1<<0)  实际抽头位14和0
	// 控制LFSR宽度15,掩码0x8001 (位14和位0)
	// 目标LFSR宽度17,掩码 x^17 + x^3 + 1 -> 位16和位2和位0 => 0x20005
	uint32_t ctrl_mask = (1 << 14) | 1;           // 15级,抽头14和0
	int ctrl_width = 15;
	uint32_t targ_mask = (1 << 16) | (1 << 2) | 1;  // 17级,抽头16,2,0
	int targ_width = 17;

	// 种子不能全0
	uint32_t ctrl_seed = 0x2A3F;   // 15位内
	uint32_t targ_seed = 0x1A5B7;  // 17位内

	// 测试明文
	char text[] = "Stop-and-Go clock-controlled generator!";
	size_t len = strlen(text);
	uint8_t *data = (uint8_t*)malloc(len);
	memcpy(data, text, len);

	printf("原始明文: %s\n", text);
	printf("明文(HEX): ");
	print_hex(data, len);

	// 加密
	stopgo_encrypt(data, len, ctrl_seed, ctrl_mask, ctrl_width,
		targ_seed, targ_mask, targ_width);
	printf("密文(HEX): ");
	print_hex(data, len);

	// 解密
	stopgo_decrypt(data, len, ctrl_seed, ctrl_mask, ctrl_width,
		targ_seed, targ_mask, targ_width);
	printf("解密后明文: %s\n", data);

	free(data);
	return 0;
}

七、A5流密码算法

1、A5流密码算法介绍

(1)A5/1是GSM移动通信中用于语音通话加密的流密码,它的核心是用三个线性反馈移位寄存器(LFSR),通过钟控停走的方式生成密钥流,再和明文/密文做逐位异或完成加解密。

需要说明的是,A5/1算法中,LFSR的移位方式是左移方式,各寄存器的编号从第0级编号到第n-1级

(2)基本密钥与会话密钥:

(3)明文处理与加密方式:

①A5/1算法将通话的明文数据按每帧228bit分为若干帧,然后进行逐帧加密,每帧的处理方式相同且独立。

②加密方式:

(4)算法初始化:

初始化就是利用一次通话的会话密钥k和帧序号设定三个线性移位寄存器的起点,也即初始状态

首先将三个LFSR的初始状态设置为全零,然后将64bit的会话密钥k从低位到高位依次移入三个寄存器,每一位密钥bit,同时与三个寄存器的反馈抽头输出异或,结果作为寄存器的输入,三个寄存器都移位64次,完成会话密钥的注入

然后将22bit的帧序号从低位到高位,依次移入三个寄存器,每一位帧序号bit,与三个寄存器的反馈抽头输出异或,作为输入,三个寄存器都移位22次,完成帧序号的注入(对不同的帧设置不同的帧序号,保证对每帧以不同的起点生成密钥流,尽可能地避免密钥重用)

此时,三个寄存器的状态已经混合了会话密钥和帧号,每帧的状态都不同

2、A5流密码算法的基本原理

(1)密钥流生成与加解密:

(2)加密过程:

三个LFSR以钟控方式连续动作100次,但不输出密钥流

然后三个LFSR以钟控方式连续动作114次,在每次动作后,三个LFSR都将最高位寄存器中的值输出,这三个bit的异或就是当前时刻输出的1bit密钥

然后连续动作114步,共输出114bit密钥流,用于对用户手机到基站传送的114bit数据的加密

(3)解密过程:

三个LFSR以钟控方式连续动作100次,但不输出密钥流

然后三个LFSR以钟控方式连续动作114次,在每次动作后,三个LFSR都将最高级寄存器中的值输出,这三个bit的模2和就是当前时刻输出的1bit密钥流

然后连续动作114步,共输出114bit密钥流,这114bit用于对基站到用户手机传送的114bit数据的解密

3、A5流密码算法的弱点

(1)移位寄存器,太短容易遭受穷举攻击。

A5/1算法把主密钥作为算法中三个寄存器的初始值,长度为64bit,如果利用已知明文攻击,只需要知道其中两个寄存器的初始值,就可以计算出另一个寄存器的初始值,这只需要240步就可以得出寄存器LFSR-1和LFSR-2的结构

(2)事实上,A5/1加密算法中存在严重的安全问题。

利用GSM通信加密中的两个安全漏洞,可以通过离线迭代计算生成一个彩虹表,它包含有密钥和其相对应的输出密码,这个彩虹表的大小为984GB,得到了彩虹表之后,安全专家就可以在短短的九秒内确定用于加密通信数据的密钥

相关推荐
wanzehongsheng10 分钟前
基于天文算法的双轴太阳能追踪系统:从原理到工程实现
算法
basketball61612 分钟前
Kadane算法 C++实现
java·c++·算法
handler0113 分钟前
【C++】二叉搜索树详解及其模拟实现(代码)
开发语言·c++·算法·c··二叉搜索树·搜索树
luj_176815 分钟前
残熵算法的稳健防灾逻辑
c语言·开发语言·c++·经验分享·算法
玖釉-16 分钟前
二叉树基础详解:TreeNode、buildTree、deleteTree 与 printTree 的实现原理(C++)
c++·windows·算法
Severus_black16 分钟前
【初阶数据结构与算法】八大排序之非比较排序(计数排序),一次性讲清!
数据结构·算法·排序算法
罗西的思考39 分钟前
【Agentic RL / 强化学习 / OPD】OpenClaw-RL 源码阅读笔记 --- (4)--- 系统架构
人工智能·算法·机器学习
QiLinkOS41 分钟前
从技术到资产的跃迁:企业专利布局的深层逻辑
c语言·数据结构·c++·单片机·嵌入式硬件·算法·开源
aini_lovee1 小时前
FMCW雷达测速测距系统(锯齿波 + CFAR检测)
算法
qq_297574671 小时前
设计模式系列文章(基础篇第 11 篇):模板方法模式——定义算法骨架,实现代码复用与流程统一
算法·设计模式·模板方法模式