You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

96 lines
3.6 KiB

package comm
import (
"strings"
"testing"
)
// 指纹的唯一用途是「两端比对」:实际 AES 密钥相同 → 指纹必相同;不同 → 指纹必不同。
func TestKeyFingerprint_同密钥同指纹_异密钥异指纹(t *testing.T) {
a := KeyFingerprint("SzwdcXrtrmJMRZkZtQuzhAETOIJnFzMH")
if a != KeyFingerprint("SzwdcXrtrmJMRZkZtQuzhAETOIJnFzMH") {
t.Fatal("同一把 key 两次指纹不一致")
}
if a == KeyFingerprint("") {
t.Fatal("真实 key 与空 key 指纹相同")
}
// 不得泄露密钥本身。
if strings.Contains(a, "Szwdc") {
t.Fatalf("指纹泄露了密钥内容: %s", a)
}
}
// normAESKey 截断到 32 字节:前 32 字节相同的两把 key 实为同一把 AES 密钥,指纹须相同,
// 否则两端会因"指纹不同"去排查一个根本不存在的问题。
func TestKeyFingerprint_超长截断后等价(t *testing.T) {
k32 := "SzwdcXrtrmJMRZkZtQuzhAETOIJnFzMH" // 恰好 32 字节
long := k32 + "这些字节根本不参与加密"
if KeyFingerprint(k32) == KeyFingerprint(long) {
// 指纹的哈希部分应相同(实际 AES key 相同),但备注不同,故比较哈希前缀。
t.Fatal("期望备注不同")
}
h1, h2 := strings.Fields(KeyFingerprint(k32))[0], strings.Fields(KeyFingerprint(long))[0]
if h1 != h2 {
t.Fatalf("前 32 字节相同的 key 实际 AES 密钥相同,指纹哈希应一致: %s vs %s", h1, h2)
}
if !strings.Contains(KeyFingerprint(long), "被丢弃") {
t.Fatal("超长 key 应提示截断")
}
}
func TestKeyFingerprint_标注常见事故(t *testing.T) {
if !strings.Contains(KeyFingerprint(""), "未配置") {
t.Error("空 key 应提示未配置")
}
if !strings.Contains(KeyFingerprint("short-key"), "补零") {
t.Error("不足 32 字节应提示补零")
}
// .env 存成 CRLF 时,值尾部会多一个 \r,肉眼与正确值毫无差别。
if !strings.Contains(KeyFingerprint("SzwdcXrtrmJMRZkZtQuzhAETOIJnFzM\r"), "CR") {
t.Error("尾部 CR 应被标注")
}
}
// 事故复现:FIELD_ENCRYPT_KEY 进 env 模板之前,业务服务用空串(→全零密钥)把配置 seed 进了库。
// 后来 env 配上了真 key,解密失败——诊断必须指出"这是旧 key 加密的存量数据",而不是让人去查 env。
func TestDiagnoseDecryptFailure_识别空key加密的存量数据(t *testing.T) {
const realKey = "SzwdcXrtrmJMRZkZtQuzhAETOIJnFzMH"
legacyCT, err := Encrypt("", "wx-appsecret-plaintext") // 旧进程用空 key 加密
if err != nil {
t.Fatal(err)
}
if _, err = Decrypt(realKey, legacyCT); err == nil {
t.Fatal("前提不成立:真 key 不该能解开空 key 的密文")
}
d := DiagnoseDecryptFailure(realKey, legacyCT)
if !strings.Contains(d, "全零密钥") {
t.Errorf("应指认出空 key,实际: %s", d)
}
if strings.Contains(d, "wx-appsecret-plaintext") {
t.Fatalf("诊断泄露了明文: %s", d)
}
}
// .env 存成 CRLF:值尾部混入 \r,两端"看起来"配的是同一把 key,实际差一个字节。
func TestDiagnoseDecryptFailure_识别尾部CR(t *testing.T) {
const clean = "SzwdcXrtrmJMRZkZtQuzhAETOIJnFzMH"
ct, err := Encrypt(clean, "some-secret")
if err != nil {
t.Fatal(err)
}
d := DiagnoseDecryptFailure(clean+"\r", ct) // 本服务读到的 key 尾部多了 \r
if !strings.Contains(d, "多余字符") {
t.Errorf("应指认出尾部混入字符,实际: %s", d)
}
}
func TestDiagnoseDecryptFailure_未知key不瞎猜(t *testing.T) {
ct, err := Encrypt("some-unknown-key-nobody-has-seen", "x")
if err != nil {
t.Fatal(err)
}
if d := DiagnoseDecryptFailure("SzwdcXrtrmJMRZkZtQuzhAETOIJnFzMH", ct); !strings.Contains(d, "未知的 key") {
t.Errorf("无线索时应如实说明,实际: %s", d)
}
}