Skip to content

feat: Implement WinZip AES encryption support in pzip#397

Merged
lzwind merged 3 commits intolinuxdeepin:masterfrom
dengzhongyuan365-dev:pwd-dev
Apr 28, 2026
Merged

feat: Implement WinZip AES encryption support in pzip#397
lzwind merged 3 commits intolinuxdeepin:masterfrom
dengzhongyuan365-dev:pwd-dev

Conversation

@dengzhongyuan365-dev
Copy link
Copy Markdown
Contributor

feat: Implement WinZip AES encryption support in pzip

- Added AES encryption options (AES128, AES192, AES256) to the CliPzipPlugin for secure archiving.
- Introduced password handling and temporary file management for encryption.
- Enhanced the Archiver to support encrypted file writing with WinZip AES format.
- Updated command-line interface to include password and encryption method options.
- Implemented AES encryption and decryption logic in the AESEncryptor class.
- Added WinZip AES extra field handling for ZIP file headers.

This feature improves data security during compression and aligns with modern encryption standards.

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @dengzhongyuan365-dev, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

- Added AES encryption options (AES128, AES192, AES256) to the CliPzipPlugin for secure archiving.
- Introduced password handling and temporary file management for encryption.
- Enhanced the Archiver to support encrypted file writing with WinZip AES format.
- Updated command-line interface to include password and encryption method options.
- Implemented AES encryption and decryption logic in the AESEncryptor class.
- Added WinZip AES extra field handling for ZIP file headers.

This feature improves data security during compression and aligns with modern encryption standards.

TASK: https://pms.uniontech.com/task-view-388835.html
…hive handling

- Introduced a new method `emitProgressIfArchiveGrew` to improve progress reporting during archiving.
- Added support for temporary archive creation when dealing with MTP mounted directories.
- Updated the command-line interface to include `--ui-events` for better integration with GUI, allowing real-time updates of current file names and progress.
- Enhanced the Archiver to handle entry start and byte read events for UI updates.
- Improved progress calculation logic to ensure accurate reporting and prevent UI stalls.

These changes enhance user experience by providing more responsive feedback during the archiving process.

TASK: https://pms.uniontech.com/task-view-388835.html
- Modified the regular expression in Cli7zPlugin to match both "p7zip Version" and "7-Zip" version strings.
- This change ensures compatibility with different version outputs, enhancing the plugin's robustness.

Improves version detection for better integration with various archive formats.

TASK: https://pms.uniontech.com/task-view-388835.html
@deepin-ci-robot
Copy link
Copy Markdown

deepin pr auto review

代码审查报告

1. 概述

本次代码修改主要实现了以下功能:

  1. 为 pzip 压缩工具添加了 WinZip AES 加密支持
  2. 改进了进度报告机制,支持更精确的进度更新
  3. 修复了 MTP 设备上的压缩问题
  4. 优化了 ZIP 文件压缩时的密码处理逻辑

2. 语法与逻辑审查

2.1 正确部分

  • 加密相关代码结构清晰,使用了 OpenSSL 库实现 AES 加密
  • 使用了 RAII 模式管理资源(如 std::unique_ptr<QTemporaryFile>
  • 使用了现代 C++ 特性(如 std::move, std::unique_ptr

2.2 潜在问题

2.2.1 加密实现中的潜在问题

src/pzip/src/crypto/aes_encryptor.cpp 中:

bool AESEncryptor::generateSalt() {
    return RAND_bytes(salt_.data(), static_cast<int>(salt_.size())) == 1;
}

问题:没有检查 RAND_bytes 是否失败,虽然返回值已检查,但错误信息不够详细。

建议

bool AESEncryptor::generateSalt() {
    if (RAND_bytes(salt_.data(), static_cast<int>(salt_.size())) != 1) {
        lastError_ = "CSPRNG failed: RAND_bytes could not generate salt";
        return false;
    }
    return true;
}

2.2.2 进度报告中的潜在问题

src/source/page/progresspage.cpp 中:

void ProgressPage::calSpeedAndRemainingTime(double &dSpeed, qint64 &qRemainingTime)
{
    // ...
    if (!m_timer.isValid()) {
        m_timer.start();
        m_qConsumeTime = 0;
    }
    
    const qint64 delta = m_timer.elapsed();
    if (delta > 0) {
        m_qConsumeTime += delta;
    }

问题:虽然增加了 isValid() 检查,但 m_timer.restart() 后没有重置 m_qConsumeTime,可能导致累积时间计算错误。

建议:确保在 restartTimer() 中重置 m_qConsumeTime(代码中已实现,但需确认调用时机)。

2.2.3 密码处理逻辑中的潜在问题

3rdparty/clipzipplugin/clipzipplugin.cpp 中:

static QByteArray passwordBytesLikeLibzipForZip(const QString &strPassword, const QString &archiveName)
{
    if (!archiveName.endsWith(QLatin1String(".zip"), Qt::CaseInsensitive)) {
        return strPassword.toUtf8();
    }
    // ...
}

问题:密码处理逻辑依赖于文件扩展名判断,如果文件没有 .zip 扩展名但实际是 ZIP 格式,会导致密码处理不一致。

建议:考虑使用 MIME 类型检测而非仅依赖扩展名。

3. 代码质量审查

3.1 优点

  • 代码结构清晰,模块划分合理
  • 使用了命名空间避免全局污染
  • 使用了现代 C++ 特性(如 std::unique_ptr, std::move
  • 添加了详细的注释说明加密算法和格式

3.2 改进建议

3.2.1 魔法数字

src/pzip/include/pzip/common.h 中:

constexpr uint16_t WINZIP_AES_VERSION_1 = 0x0001;   // AE-1 
constexpr uint16_t WINZIP_AES_VERSION_2 = 0x0002;   // AE-2 
constexpr size_t WINZIP_AES_AUTH_CODE_SIZE = 10;    // HMAC-SHA1 认证码大小
constexpr size_t WINZIP_AES_PV_SIZE = 2;            // 密码验证值大小
constexpr uint32_t PBKDF2_ITERATIONS = 1000;        // PBKDF2 迭代次数

建议:这些常量定义良好,但建议添加更多注释说明这些值的来源和标准参考。

3.2.2 错误处理

src/pzip/src/crypto/aes_encryptor.cpp 中:

AESEncryptor::~AESEncryptor() {
    if (aesCtx_) {
        EVP_CIPHER_CTX_free(aesCtx_);
        aesCtx_ = nullptr;
    }
    if (hmacCtx_) {
        HMAC_CTX_free(hmacCtx_);
        hmacCtx_ = nullptr;
    }
    std::fill(aesKey_.begin(), aesKey_.end(), 0);
    std::fill(hmacKey_.begin(), hmacKey_.end(), 0);
    std::fill(counterBlock_.begin(), counterBlock_.end(), 0);
    std::fill(pad_.begin(), pad_.end(), 0);
}

优点:析构函数中正确清理了敏感数据(密钥等)。

建议:考虑使用 std::memset_s 或类似函数确保内存清除不被优化掉。

4. 代码性能审查

4.1 优点

  • 使用了缓冲区减少 I/O 操作
  • 使用了线程池并行处理文件
  • 限制了进度报告频率(REPORT_GRANULARITY

4.2 改进建议

4.2.1 加密性能

src/pzip/src/crypto/aes_encryptor.cpp 中:

bool AESEncryptor::aesCrypt(uint8_t* data, size_t length) {
    for (size_t i = 0; i < length; i++) {
        if (padOffset_ == AES_BLOCK_SIZE) {
            incrementCounter();
            
            int outLen = 0;
            if (EVP_EncryptUpdate(aesCtx_, pad_.data(), &outLen,
                                  counterBlock_.data(), AES_BLOCK_SIZE) != 1) {
                lastError_ = "AES encryption failed";
                return false;
            }
            padOffset_ = 0;
        }
        data[i] ^= pad_[padOffset_++];
    }
    return true;
}

问题:逐字节处理效率较低。

建议:考虑批量处理数据块,减少循环次数。

4.2.2 进度报告频率

src/pzip/src/archiver.cpp 中:

uint64_t reportedBytesRead = 0;
constexpr uint64_t REPORT_GRANULARITY = 512 * 1024; // 限制回调频率,避免拖慢压缩线程

优点:限制了进度报告频率,避免频繁回调影响性能。

建议:考虑将 REPORT_GRANULARITY 设为可配置参数,以便在不同场景下调整。

5. 代码安全审查

5.1 优点

  • 使用了安全的随机数生成器(RAND_bytes
  • 密钥派生使用了 PBKDF2-HMAC-SHA1
  • 敏感数据(密钥等)在析构时被清除
  • 使用了 AE-2 格式(比 AE-1 更安全)

5.2 改进建议

5.2.1 密码处理

3rdparty/clipzipplugin/clipzipplugin.cpp 中:

static QByteArray passwordBytesLikeLibzipForZip(const QString &strPassword, const QString &archiveName)
{
    // ...
    const QString strUnicode = utf8->toUnicode(strPassword.toUtf8().constData());
    return dst->fromUnicode(strUnicode);
}

问题:密码处理逻辑复杂,可能引入安全漏洞。

建议

  1. 考虑使用专门的密码处理库(如 libsodium)
  2. 确保密码在内存中的停留时间尽可能短
  3. 考虑使用安全字符串类(如 std::string 的安全变体)

5.2.2 加密强度

src/pzip/include/pzip/common.h 中:

constexpr uint32_t PBKDF2_ITERATIONS = 1000;        // PBKDF2 迭代次数

问题:1000 次迭代对于现代硬件可能不够安全。

建议

  1. 增加迭代次数(至少 100,000 次)
  2. 考虑使用 Argon2 或 scrypt 等更现代的密钥派生函数
  3. 将迭代次数设为可配置参数

5.2.3 临时文件处理

3rdparty/clipzipplugin/clipzipplugin.cpp 中:

m_passwordFile = std::make_unique<QTemporaryFile>();
if (!m_passwordFile->open()) {
    qWarning() << "Failed to create temporary file for password";
    m_eErrorType = ET_PluginError;
    return PFT_Error;
}
const QByteArray pwBytes = passwordBytesLikeLibzipForZip(options.strPassword, m_strArchiveName);
if (m_passwordFile->write(pwBytes) != pwBytes.size()) {
    qWarning() << "Failed to write password bytes";
    m_eErrorType = ET_PluginError;
    return PFT_Error;
}

问题:密码被写入临时文件,可能被其他进程读取。

建议

  1. 考虑使用内存传递密码而非文件
  2. 如果必须使用文件,确保文件权限设置为仅当前用户可读
  3. 确保临时文件在使用后被安全删除

6. 总结

6.1 整体评价

代码整体质量较高,实现了 WinZip AES 加密功能,并改进了进度报告机制。使用了现代 C++ 特性和安全编程实践。

6.2 主要改进点

  1. 增强加密强度(增加 PBKDF2 迭代次数)
  2. 改进密码处理逻辑,减少安全风险
  3. 优化加密性能,减少逐字节处理
  4. 增强错误处理和日志记录

6.3 优先级建议

  1. 高优先级:增加 PBKDF2 迭代次数,改进密码处理逻辑
  2. 中优先级:优化加密性能,改进错误处理
  3. 低优先级:增加代码注释,改进可维护性

7. 具体修改建议

7.1 加密强度改进

// 在 src/pzip/include/pzip/common.h 中
constexpr uint32_t PBKDF2_ITERATIONS = 100000;  // 增加到 100,000 次

7.2 密码处理改进

// 在 3rdparty/clipzipplugin/clipzipplugin.cpp 中
static QByteArray passwordBytesLikeLibzipForZip(const QString &strPassword, const QString &archiveName)
{
    QByteArray passwordBytes;
    
    // 检查是否为 ZIP 文件
    if (!archiveName.endsWith(QLatin1String(".zip"), Qt::CaseInsensitive)) {
        passwordBytes = strPassword.toUtf8();
    } else {
        // 处理 ZIP 文件密码
        QTextCodec *utf8 = QTextCodec::codecForName("UTF-8");
        if (!utf8) {
            passwordBytes = strPassword.toUtf8();
        } else {
            passwordBytes = utf8->fromUnicode(strPassword);
        }
    }
    
    // 清除敏感数据
    QString::const_iterator it = strPassword.begin();
    for (; it != strPassword.end(); ++it) {
        // 清除原始密码(Qt 字符串可能无法完全清除)
    }
    
    return passwordBytes;
}

7.3 加密性能优化

// 在 src/pzip/src/crypto/aes_encryptor.cpp 中
bool AESEncryptor::aesCrypt(uint8_t* data, size_t length) {
    // 批量处理数据块
    while (length > 0) {
        if (padOffset_ == AES_BLOCK_SIZE) {
            incrementCounter();
            
            int outLen = 0;
            if (EVP_EncryptUpdate(aesCtx_, pad_.data(), &outLen,
                                  counterBlock_.data(), AES_BLOCK_SIZE) != 1) {
                lastError_ = "AES encryption failed";
                return false;
            }
            padOffset_ = 0;
        }
        
        // 计算当前块大小
        size_t blockSize = std::min(AES_BLOCK_SIZE - padOffset_, length);
        
        // 批量 XOR
        for (size_t i = 0; i < blockSize; i++) {
            data[i] ^= pad_[padOffset_++];
        }
        
        data += blockSize;
        length -= blockSize;
    }
    return true;
}

这些改进将提高代码的安全性、性能和可维护性,同时保持原有功能的完整性。

@deepin-ci-robot
Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: dengzhongyuan365-dev, lzwind

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@dengzhongyuan365-dev
Copy link
Copy Markdown
Contributor Author

/forcemerge

@lzwind lzwind merged commit 0b3ec3f into linuxdeepin:master Apr 28, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants