2. 升级包完整性校验

任务说明

OTA升级包在下载过程中可能因网络异常、存储介质故障或恶意篡改导致数据不完整。本案例实现一个升级包完整性校验工具,在升级前对升级包文件计算SHA-256哈希值,并与官方发布的基准哈希值比对,一致则允许升级,否则终止升级流程。

环境依赖

  • Python 3.8+(标准库,无第三方依赖)
  • 操作系统:Linux/Windows/macOS均可

关键完整代码

import hashlib
import sys
import os
import json

def calculate_sha256(file_path, chunk_size=8192):
    """计算文件的SHA-256哈希值,分块读取避免内存占用过高"""
    sha256_hash = hashlib.sha256()
    try:
        with open(file_path, 'rb') as f:
            while chunk := f.read(chunk_size):
                sha256_hash.update(chunk)
        return sha256_hash.hexdigest()
    except FileNotFoundError:
        return None
    except PermissionError:
        return None

def verify_package(file_path, expected_hash):
    """
    校验升级包完整性
    返回: (是否通过, 实际哈希值, 错误信息)
    """
    if not os.path.exists(file_path):
        return False, None, f"文件不存在: {file_path}"
    
    actual_hash = calculate_sha256(file_path)
    if actual_hash is None:
        return False, None, "文件读取失败,请检查文件权限"
    
    if actual_hash.lower() == expected_hash.lower():
        return True, actual_hash, "校验通过"
    else:
        return False, actual_hash, "哈希不匹配,文件可能已损坏或被篡改"

def load_expected_hash(manifest_path):
    """从manifest.json中加载期望哈希值"""
    try:
        with open(manifest_path, 'r', encoding='utf-8') as f:
            manifest = json.load(f)
        return manifest.get('sha256')
    except (FileNotFoundError, json.JSONDecodeError):
        return None

def main():
    if len(sys.argv) != 3:
        print("用法: python verify_ota.py <升级包路径> <manifest.json路径>")
        sys.exit(2)
    
    package_path = sys.argv[1]
    manifest_path = sys.argv[2]
    
    expected_hash = load_expected_hash(manifest_path)
    if expected_hash is None:
        print("错误: 无法从manifest.json读取sha256字段")
        sys.exit(1)
    
    passed, actual_hash, message = verify_package(package_path, expected_hash)
    
    print(f"期望SHA-256: {expected_hash}")
    if actual_hash:
        print(f"实际SHA-256: {actual_hash}")
    print(f"校验结果: {message}")
    
    if passed:
        print("升级包校验通过,允许执行升级流程")
        sys.exit(0)
    else:
        print("升级包校验失败,终止升级流程")
        sys.exit(1)

if __name__ == "__main__":
    main()

运行与验证

准备测试文件:

# 创建测试升级包和manifest
echo "OTA升级包测试内容 v1.0" > upgrade_package.bin
sha256sum upgrade_package.bin   # Linux/Mac
# Windows: certutil -hashfile upgrade_package.bin SHA256

# 将输出的哈希值写入manifest.json
cat > manifest.json << EOF
{"sha256": "上一步计算出的哈希值"}
EOF

运行校验:

python verify_ota.py upgrade_package.bin manifest.json

通过条件:

  • 输出“校验通过”,退出码为0
  • 篡改测试:修改upgrade_package.bin内容后再次运行,应输出“哈希不匹配”,退出码为1

关键说明

  • SHA-256为当前OTA升级校验的主流算法,碰撞概率极低,满足完整性校验需求
  • manifest.json由升级服务端在发布时生成,与升级包一同下发或预置于车机安全存储区
  • 分块读取(8KB)确保大升级包(数GB)校验时内存占用恒定
  • 实际车机集成时,将verify_package()的返回值作为升级前置条件,校验失败时直接中止升级流程并上报错误码