package chachax_test import ( "errors" "testing" "code.yun.ink/pkg/encryptx/chachax" ) // TestSentinelErrors 确认调用方可以用 errors.Is 区分失败类别, // 而不必解析错误文本——这决定了上层能否把「被篡改」与「参数写错」分开处理。 func TestSentinelErrors(t *testing.T) { key, err := chachax.GenerateKey() if err != nil { t.Fatalf("GenerateKey 失败: %v", err) } // 密钥长度错误 for _, badKey := range [][]byte{nil, {}, make([]byte, 16), make([]byte, 31), make([]byte, 33)} { if _, _, err := chachax.Encrypt(badKey, []byte("x"), nil); !errors.Is(err, chachax.ErrInvalidKeySize) { t.Errorf("密钥长度 %d: 期望 ErrInvalidKeySize, 得到 %v", len(badKey), err) } if _, _, err := chachax.EncryptX(badKey, []byte("x"), nil); !errors.Is(err, chachax.ErrInvalidKeySize) { t.Errorf("XChaCha 密钥长度 %d: 期望 ErrInvalidKeySize, 得到 %v", len(badKey), err) } } // nonce 长度错误 ciphertext, nonce, err := chachax.Encrypt(key, []byte("payload"), nil) if err != nil { t.Fatalf("Encrypt 失败: %v", err) } for _, badNonce := range [][]byte{nil, {}, make([]byte, 8), make([]byte, 24)} { if _, err := chachax.Decrypt(key, ciphertext, badNonce, nil); !errors.Is(err, chachax.ErrInvalidNonceSize) { t.Errorf("nonce 长度 %d: 期望 ErrInvalidNonceSize, 得到 %v", len(badNonce), err) } } // XChaCha 期望 24 字节,12 字节同样应被拒 xCiphertext, xNonce, err := chachax.EncryptX(key, []byte("payload"), nil) if err != nil { t.Fatalf("EncryptX 失败: %v", err) } if _, err := chachax.DecryptX(key, xCiphertext, nonce, nil); !errors.Is(err, chachax.ErrInvalidNonceSize) { t.Errorf("XChaCha 用 12 字节 nonce: 期望 ErrInvalidNonceSize, 得到 %v", err) } // 密文过短(不足认证标签) for n := 0; n < chachax.Overhead; n++ { if _, err := chachax.Decrypt(key, make([]byte, n), nonce, nil); !errors.Is(err, chachax.ErrCiphertextTooShort) { t.Errorf("密文长度 %d: 期望 ErrCiphertextTooShort, 得到 %v", n, err) } if _, err := chachax.DecryptX(key, make([]byte, n), xNonce, nil); !errors.Is(err, chachax.ErrCiphertextTooShort) { t.Errorf("XChaCha 密文长度 %d: 期望 ErrCiphertextTooShort, 得到 %v", n, err) } } // Seal 数据过短 for n := 0; n < chachax.NonceSize+chachax.Overhead; n++ { if _, err := chachax.Open(key, make([]byte, n), nil); !errors.Is(err, chachax.ErrCiphertextTooShort) { t.Errorf("Open 数据长度 %d: 期望 ErrCiphertextTooShort, 得到 %v", n, err) } } for n := 0; n < chachax.NonceSizeX+chachax.Overhead; n++ { if _, err := chachax.OpenX(key, make([]byte, n), nil); !errors.Is(err, chachax.ErrCiphertextTooShort) { t.Errorf("OpenX 数据长度 %d: 期望 ErrCiphertextTooShort, 得到 %v", n, err) } } // 认证失败:篡改密文、篡改 AAD、用错密钥 tampered := append([]byte(nil), ciphertext...) tampered[0] ^= 0x01 if _, err := chachax.Decrypt(key, tampered, nonce, nil); !errors.Is(err, chachax.ErrAuthenticationFailed) { t.Errorf("篡改密文: 期望 ErrAuthenticationFailed, 得到 %v", err) } if _, err := chachax.Decrypt(key, ciphertext, nonce, []byte("wrong aad")); !errors.Is(err, chachax.ErrAuthenticationFailed) { t.Errorf("错误 AAD: 期望 ErrAuthenticationFailed, 得到 %v", err) } otherKey, _ := chachax.GenerateKey() if _, err := chachax.Decrypt(otherKey, ciphertext, nonce, nil); !errors.Is(err, chachax.ErrAuthenticationFailed) { t.Errorf("错误密钥: 期望 ErrAuthenticationFailed, 得到 %v", err) } }