a329d4906b
- 方案文档: AVCC 体系建设、IPTV TCS 需求(0-req)/PRD(1-prd)/任务(2-task)/二三四期任务 - tcs-iptv: Go 后端(哈希SDK/MA码生成/可信数据空间mock/业务编排/HTTP API+HMAC鉴权) - web-console: React+AntD 监管大屏(角色工作台/全流程演示/监管片库) - 一剧一码+集级哈希, 集级下架/恢复, 全栈测试通过
79 lines
2.1 KiB
Go
79 lines
2.1 KiB
Go
package hash
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
)
|
|
|
|
// HashPackage 是哈希值包,对应需求1-AC5。
|
|
// 仅包含哈希与元数据,绝不包含原始内容(需求20-AC2)。
|
|
type HashPackage struct {
|
|
FileSHA256 string `json:"file_sha256"`
|
|
MerkleRoot string `json:"merkle_root"`
|
|
SegmentHashes []string `json:"segment_hashes"`
|
|
PerceptualHash string `json:"perceptual_hash,omitempty"`
|
|
FileSize int64 `json:"file_size"`
|
|
SegmentSize int `json:"segment_size"`
|
|
}
|
|
|
|
// Options 控制哈希计算行为。
|
|
type Options struct {
|
|
SegmentSize int // 分段大小;<=0 用默认
|
|
PerceptualHash string // 上层已抽帧并算好的感知哈希(可选)
|
|
}
|
|
|
|
// ComputeFile 对母版文件计算完整哈希值包(文件哈希 + 分段 Merkle)。
|
|
// 感知哈希需上层先抽帧,再通过 opts.PerceptualHash 传入或单独调用 AHash/DHash。
|
|
func ComputeFile(path string, opts Options) (*HashPackage, error) {
|
|
info, err := os.Stat(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("hash: stat file: %w", err)
|
|
}
|
|
if info.IsDir() {
|
|
return nil, fmt.Errorf("hash: path is a directory: %s", path)
|
|
}
|
|
if info.Size() == 0 {
|
|
return nil, ErrEmptyInput
|
|
}
|
|
|
|
segSize := opts.SegmentSize
|
|
if segSize <= 0 {
|
|
segSize = DefaultSegmentSize
|
|
}
|
|
|
|
fileHash, err := FileSHA256(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("hash: file sha256: %w", err)
|
|
}
|
|
|
|
segments, err := SegmentHashes(path, segSize)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("hash: segment hashes: %w", err)
|
|
}
|
|
|
|
tree := BuildMerkleTree(segments)
|
|
|
|
return &HashPackage{
|
|
FileSHA256: fileHash,
|
|
MerkleRoot: tree.Root(),
|
|
SegmentHashes: segments,
|
|
PerceptualHash: opts.PerceptualHash,
|
|
FileSize: info.Size(),
|
|
SegmentSize: segSize,
|
|
}, nil
|
|
}
|
|
|
|
// Validate 校验哈希值包的完整性(需求2-AC5:缺文件哈希/Merkle根则非法)。
|
|
func (p *HashPackage) Validate() error {
|
|
if p == nil {
|
|
return fmt.Errorf("hash: nil package")
|
|
}
|
|
if p.FileSHA256 == "" {
|
|
return fmt.Errorf("hash: missing file_sha256")
|
|
}
|
|
if p.MerkleRoot == "" {
|
|
return fmt.Errorf("hash: missing merkle_root")
|
|
}
|
|
return nil
|
|
}
|