花了几天时间我写了一个自用的压缩工具,可以加密成任意后缀的格式文件,且无法用其他软件打开。

架构设计

下面说一下架构设计

+-----------------+
|     主菜单       |
+--------+--------+
         |
         v
+--------+--------+
| 用户输入路径/密码 |
+--------+--------+
         |
         v
+--------+--------+
| 文件遍历与信息收集|
+--------+--------+
         |
         v
+--------+--------+     +----------------+
| 任务分发协程    | --> | 压缩&加密协程   |
+--------+--------+     +----------------+
         |                         |
         |       +----------------+
         v       v
    +--------------------+
    | 数据写入与偏移管理 |
    +--------------------+
         |
         v
+--------+--------+     +----------------+
| 结果收集协程    | <-- |   条目打包写头部 |
+-----------------+     +----------------+

解压方向:结构对称 + 验证 HMAC + 解密 + 解压 + CRC 校验

流程

输入1

1.用户输入文件或目录路径

2.用户输入压缩密码(可为空,空则不加密)

3.用户选择压缩算法

  • 1:LZ4(速度优先)
  • 2:Zstd(压缩率和速度平衡)
  • 其他或空:自动选择

4.遍历输入路径,收集所有文件的路径、相对路径和大小信息

5.如果用户未指定压缩算法,根据文件总大小自动选择:

  • 小于10MB -> LZ4
  • 大于等于10MB -> Zstd

6.生成32字节随机盐(salt)

7.根据用户输入密码和盐,通过PBKDF2-HMAC-SHA256算法派生32字节加密密钥

8.创建输出文件,预留空间写入文件头和文件条目(FileEntry)信息

9.启动一个工作协程进行流式文件压缩与加密处理

  • 该协程读取文件内容
  • 使用选定的压缩算法(LZ4或Zstd)流式压缩
  • 将压缩后的数据分块(1MB为单位)
  • 为每块生成随机nonce
  • 使用ChaCha20-Poly1305 AEAD算法加密每块数据
  • 将加密块写入输出文件的指定偏移
  • 更新当前写入偏移,记录加密块数量和最后一块大小
  • 计算并记录每个文件的CRC32校验值

10. 启动结果收集协程,接收压缩协程返回的文件条目结果,保存至内存结构中

11. 主协程顺序发送所有文件任务到压缩协程

12. 所有文件压缩加密完成后,主协程计算整个文件头及文件条目的HMAC值进行完整性保护

13. 将计算好的HMAC写入文件头部

14. 将完整的文件头和文件条目写回输出文件头部指定位置,完成文件封装

15. 输出压缩完成统计信息(文件数、压缩率、耗时、速度、输出路径等)

16. 结束压缩流程,等待用户下一步操作


Mermaid流程图

输入2

解压流程(输入 2)

1. 用户交互阶段

  • 提示用户输入 .xs 压缩包路径
  • 提示用户输入解压密码

2. 读取文件头

  • 使用 binary.Read 解析 FileHeader 结构
  • 验证 MagicNumber 是否为 “xiaosheng”
  • 派生解密密钥(PBKDF2)

3. 校验文件头完整性

  • 读取所有 FileEntry
  • 构造 header + entries 的 binary 数据
  • 用 HMAC-SHA256 校验是否被篡改(密码正确性)

4. 创建输出目录

  • 基于输入路径创建一个 xxx_output 文件夹

5. 初始化进度条

6. 遍历每个文件条目进行解压

对于每个 FileEntry:
– 创建输出子目录和目标文件
– 打开源文件定位到加密数据 Offset
– 分块读取每个加密块:
– nonce + ciphertext 解密(chacha20poly1305)
– 解密后写入临时文件(压缩数据)
– 解压缩(LZ4/Zstd)临时文件到目标文件
– 校验 CRC32 和解压后大小

7. 完成

  • 打印成功信息(文件数、耗时、输出目录)

Mermaid流程图

核心设计理念

1. 安全性优先

  • 密钥派生:使用PBKDF2(100,000次迭代)将用户密码强化为256位密钥
  • 加密算法:ChaCha20-Poly1305提供AEAD(认证加密)保护
  • 完整性验证:双重保护 – HMAC验证头部,CRC32验证数据
  • 随机性:每次压缩生成新的32字节盐值,每个数据块使用独立nonce

2. 性能优化

  • 流式处理:避免一次性加载整个文件到内存
  • 并发设计:多文件并行处理,充分利用多核CPU
  • 缓冲区管理:64KB I/O缓冲区平衡性能和内存使用
  • 算法选择:根据文件大小自动选择最优压缩算法

3. 可扩展性

  • 版本控制:主/次版本号支持格式升级
  • 算法抽象:压缩和加密算法类型字段,便于添加新算法
  • 模块化设计:压缩、加密、I/O操作相互独立

关键设计决策

为什么选择ChaCha20-Poly1305?

  1. 性能:在没有AES硬件加速的设备上比AES-GCM快3-5倍
  2. 安全性:由Daniel J. Bernstein设计,经过广泛审查
  3. 简单性:实现简单,不易出错
  4. 移动友好:在ARM处理器上表现优异

为什么使用分块加密?

  1. 内存控制:1MB块大小避免大文件导致内存溢出
  2. 并行潜力:块可以独立解密,支持未来并行解密
  3. 错误隔离:单个块损坏不影响其他块
  4. 流式处理:支持边读边处理,无需等待完整文件

为什么采用临时文件?

// 压缩流程:源文件 -> 临时文件(压缩) -> 最终文件(加密)
tempFile, err := os.CreateTemp("", "xs_compress_*.tmp")
  1. 解耦操作:压缩和加密分离,代码更清晰
  2. 错误恢复:失败时不会损坏输出文件
  3. 内存友好:避免在内存中存储整个压缩数据

并发模型

Producer-Consumer模式

文件收集 -> 任务队列 -> Worker池 -> 结果收集
   |           |            |           |
顺序生成    缓冲通道    并发处理    顺序组装

关键同步点

  1. 文件偏移量:互斥锁保护,确保连续性
  2. 任务分发:通道实现自然的背压控制
  3. 结果收集:按索引存储,保持原始顺序

数据完整性保证

三层保护

  1. 文件级CRC32:检测数据传输错误
  2. 块级AEAD:每个加密块包含认证标签
  3. 头部HMAC:防止元数据篡改

验证流程

解压时验证顺序:
1. 魔数验证 -> 确认文件格式
2. HMAC验证 -> 确认密码正确且头部完整
3. AEAD验证 -> 每个块的完整性
4. CRC32验证 -> 最终数据正确性

用户体验考虑

1. 智能默认值

  • 自动选择压缩算法
  • 空密码时仍可压缩(不推荐)
  • 输出文件名自动生成

2. 进度反馈

  • 实时进度条显示
  • 包含速度、剩余时间估算
  • 处理完成后的详细统计

3. 错误处理

  • 明确的错误信息
  • 部分失败时继续处理其他文件
  • 验证失败时给出具体原因

性能特征

理论性能

  • 压缩速度:主要受限于磁盘I/O和压缩算法
  • LZ4模式:接近磁盘读取速度(500+ MB/s)
  • Zstd模式:200-300 MB/s(取决于CPU)
  • 加密开销:ChaCha20约10-20%额外开销

实测优化点

  1. 缓冲区大小:64KB在大多数系统上最优
  2. 块大小:1MB平衡了内存使用和加密开销
  3. 并发度:默认使用所有CPU核心

安全性分析

威胁模型

  • 被动攻击者:无法读取加密内容
  • 主动攻击者:HMAC和AEAD防止篡改
  • 暴力破解:PBKDF2显著增加破解成本

密码强度建议

  • 最小长度:12个字符
  • 包含:大小写字母、数字、特殊字符
  • 避免:字典词汇、个人信息

未来改进方向

  1. 性能提升
    • 并行解密支持
    • SIMD加速压缩
    • 零拷贝优化
  2. 功能增强
    • 增量备份支持
    • 密钥文件选项
    • 云存储集成
  3. 算法支持
    • Brotli压缩
    • AES-GCM加密选项
    • 后量子密码算法预备

    源码部分

    文件头

type FileHeader struct {
    MagicNumber     [9]byte  // "xiaosheng" - 文件格式标识
    VersionMajor    byte     // 主版本号
    VersionMinor    byte     // 次版本号
    CompressionType byte     // 压缩算法类型
    EncryptionType  byte     // 加密算法类型
    Timestamp       int64    // 创建时间戳
    FileCount       uint32   // 文件数量
    HeaderSize      uint32   // 头部总大小
    DataOffset      uint64   // 数据开始偏移
    Salt            [32]byte // 密钥派生盐值
    HeaderHMAC      [32]byte // 头部完整性校验
}

设计理由

1. 魔数 (9字节)

  • 作用:快速识别文件格式
  • “xiaosheng”:意为“小生”
  • 9字节而非8字节:避免对齐问题,确保唯一性

2. 版本信息 (2字节)

  • 主版本号:不兼容的格式变更
  • 次版本号:向后兼容的功能添加
  • 便于未来升级和兼容性处理

3. 算法标识 (2字节)

  • CompressionType:支持多种压缩算法
  • EncryptionType:支持多种加密算法
  • 预留扩展空间,便于添加新算法

4. 元数据 (20字节)

  • Timestamp:记录创建时间
  • FileCount:快速获知文件数量
  • HeaderSize:便于跳过头部读取数据
  • DataOffset:直接定位数据区域

5. 安全相关 (64字节)

  • Salt (32字节):
    • 每次压缩生成随机盐值
    • 防止彩虹表攻击
    • 确保相同密码产生不同密钥
  • HeaderHMAC (32字节):
    • 保护头部完整性
    • 验证密码正确性
    • 防止头部被篡改

文件布局

┌─────────────────┐
│   FileHeader    │ 64 bytes
├─────────────────┤
│   FileEntry 1   │ variable
├─────────────────┤
│   FileEntry 2   │ variable
├─────────────────┤
│      ...        │
├─────────────────┤
│   FileEntry N   │ variable
├─────────────────┤
│ Compressed Data │ 
│   (Encrypted)   │
└─────────────────┘

优势

  1. 固定大小头部:便于读取和解析
  2. 完整性保护:HMAC防止篡改
  3. 扩展性:版本号支持未来升级
  4. 安全性:盐值+HMAC双重保护
  5. 高效定位:DataOffset直接定位数据

密钥派生(PBKDF2)机制分析

实现代码

func deriveKey(password string, salt []byte) []byte {
    iterations := 100000  // 迭代次数
    keyLen := 32         // 256位密钥
    dk := pbkdf2([]byte(password), salt, iterations, keyLen)
    return dk
}

func pbkdf2(password, salt []byte, iterations, keyLen int) []byte {
    h := hmac.New(sha256.New, password)
    h.Write(salt)
    h.Write([]byte{0, 0, 0, 1})  // 块索引

    result := h.Sum(nil)
    prev := result

    // 迭代计算
    for i := 1; i < iterations; i++ {
        h.Reset()
        h.Write(prev)
        prev = h.Sum(nil)
        for j := range result {
            result[j] ^= prev[j]  // XOR累积
        }
    }

    return result[:keyLen]
}

作用和意义

1. 密码强化

  • 将弱密码转换为强密钥
  • 100,000次迭代大幅增加破解成本
  • 每次迭代约需0.1ms,总计10秒破解一个密码

2. 防彩虹表攻击

// 每次压缩生成随机盐值
salt := make([]byte, 32)
if _, err := rand.Read(salt); err != nil {
    return err
}
  • 32字节随机盐值
  • 相同密码产生不同密钥
  • 彩虹表无法预计算

3. 密钥长度标准化

  • 输入:任意长度密码
  • 输出:固定256位密钥
  • 适配ChaCha20-Poly1305要求

4. 计算成本控制

迭代次数 单次耗时 安全性 用户体验
1,000 0.1秒 极好
10,000 1秒
100,000 10秒 可接受
1,000,000 100秒 极高

5. HMAC-SHA256优势

  • 抗长度扩展攻击
  • 经过密码学验证
  • 硬件加速支持

安全性分析

攻击成本计算

假设攻击者有1000个GPU,每个GPU可以进行10^9次哈希/秒:
– 单个密码尝试:100,000次迭代
– 每秒可尝试密码:10^9 / 100,000 = 10,000个
– 1000 GPU每秒:10^7个密码
– 破解8位字母数字密码:62^8 / 10^7 ≈ 6年

与直接使用密码对比

方面 直接使用密码 PBKDF2派生
密钥强度 依赖密码强度 始终256位
抗暴力破解 强(10万倍成本)
抗彩虹表 有(随机盐值)
密钥分发 简单 简单
分类: go

0 条评论

发表回复

Avatar placeholder

您的邮箱地址不会被公开。 必填项已用 * 标注

站点统计

  • 文章总数:322 篇
  • 分类总数:20 个
  • 标签总数:193 个
  • 运行天数:1354 天
  • 访问总数:495777 人次

浙公网安备33011302000604

辽ICP备20003309号