200 lines
7.1 KiB
Go
200 lines
7.1 KiB
Go
package cipherx_test
|
||||
|
|
|
|||
|
|
import (
|
|||
|
|
"bytes"
|
|||
|
|
"crypto/aes"
|
|||
|
|
"errors"
|
|||
|
|
"testing"
|
|||
|
|
|
|||
|
|
"code.yun.ink/pkg/encryptx/cipherx"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// testSuite 返回一个以 AES 为实现、覆盖两种密钥长度的套件。
|
|||
|
|
func testSuite() *cipherx.Suite {
|
|||
|
|
return cipherx.NewSuite("TEST", aes.NewCipher, 16, 16, 32)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestSuiteMetadata(t *testing.T) {
|
|||
|
|
suite := testSuite()
|
|||
|
|
|
|||
|
|
if got := suite.Name(); got != "TEST" {
|
|||
|
|
t.Errorf("Name() = %q, 期望 TEST", got)
|
|||
|
|
}
|
|||
|
|
if got := suite.BlockSize(); got != 16 {
|
|||
|
|
t.Errorf("BlockSize() = %d, 期望 16", got)
|
|||
|
|
}
|
|||
|
|
sizes := suite.KeySizes()
|
|||
|
|
if len(sizes) != 2 || sizes[0] != 16 || sizes[1] != 32 {
|
|||
|
|
t.Errorf("KeySizes() = %v, 期望 [16 32]", sizes)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// KeySizes 必须返回副本,改动它不应影响套件本身
|
|||
|
|
sizes[0] = 999
|
|||
|
|
if again := suite.KeySizes(); again[0] != 16 {
|
|||
|
|
t.Error("KeySizes 返回了内部切片,调用方可意外篡改套件配置")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestSuiteNewBlockValidatesKeyLength(t *testing.T) {
|
|||
|
|
suite := testSuite()
|
|||
|
|
|
|||
|
|
for _, size := range []int{16, 32} {
|
|||
|
|
key := bytes.Repeat([]byte{0x11}, size)
|
|||
|
|
if _, err := suite.NewBlock(key); err != nil {
|
|||
|
|
t.Errorf("NewBlock(密钥长度 %d) 失败: %v", size, err)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for _, size := range []int{0, 8, 15, 17, 24, 31, 64} {
|
|||
|
|
key := bytes.Repeat([]byte{0x11}, size)
|
|||
|
|
if _, err := suite.NewBlock(key); !errors.Is(err, cipherx.ErrInvalidKeySize) {
|
|||
|
|
t.Errorf("NewBlock(密钥长度 %d): 期望 ErrInvalidKeySize, 得到 %v", size, err)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// TestSuiteWithoutKeySizes 验证未声明密钥长度时由底层实现校验,
|
|||
|
|
// 且失败仍被统一归类为 ErrInvalidKeySize(Blowfish 走的就是这条路径)。
|
|||
|
|
func TestSuiteWithoutKeySizes(t *testing.T) {
|
|||
|
|
suite := cipherx.NewSuite("NOCHECK", aes.NewCipher, 16)
|
|||
|
|
|
|||
|
|
if _, err := suite.NewBlock(bytes.Repeat([]byte{0x11}, 16)); err != nil {
|
|||
|
|
t.Fatalf("合法的 16 字节密钥被拒绝: %v", err)
|
|||
|
|
}
|
|||
|
|
if _, err := suite.NewBlock(bytes.Repeat([]byte{0x11}, 17)); !errors.Is(err, cipherx.ErrInvalidKeySize) {
|
|||
|
|
t.Errorf("非法密钥长度: 期望 ErrInvalidKeySize, 得到 %v", err)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestSuiteEncryptDecryptAllModes(t *testing.T) {
|
|||
|
|
suite := testSuite()
|
|||
|
|
key := bytes.Repeat([]byte{0x42}, 32)
|
|||
|
|
plaintext := []byte("suite 往返测试 payload")
|
|||
|
|
|
|||
|
|
modes := []cipherx.Mode{cipherx.ECB, cipherx.CBC, cipherx.CFB, cipherx.OFB, cipherx.CTR}
|
|||
|
|
for _, mode := range modes {
|
|||
|
|
ciphertext, err := suite.Encrypt(mode, plaintext, key)
|
|||
|
|
if err != nil {
|
|||
|
|
t.Fatalf("%s: Encrypt 失败: %v", mode, err)
|
|||
|
|
}
|
|||
|
|
decrypted, err := suite.Decrypt(mode, ciphertext, key)
|
|||
|
|
if err != nil {
|
|||
|
|
t.Fatalf("%s: Decrypt 失败: %v", mode, err)
|
|||
|
|
}
|
|||
|
|
if !bytes.Equal(decrypted, plaintext) {
|
|||
|
|
t.Errorf("%s: 往返不一致", mode)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 密钥长度非法时必须在构造阶段就失败
|
|||
|
|
if _, err := suite.Encrypt(cipherx.CBC, plaintext, []byte("short")); !errors.Is(err, cipherx.ErrInvalidKeySize) {
|
|||
|
|
t.Errorf("Encrypt 非法密钥: 期望 ErrInvalidKeySize, 得到 %v", err)
|
|||
|
|
}
|
|||
|
|
if _, err := suite.Decrypt(cipherx.CBC, []byte("x"), []byte("short")); !errors.Is(err, cipherx.ErrInvalidKeySize) {
|
|||
|
|
t.Errorf("Decrypt 非法密钥: 期望 ErrInvalidKeySize, 得到 %v", err)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestSuiteWithIV(t *testing.T) {
|
|||
|
|
suite := testSuite()
|
|||
|
|
key := bytes.Repeat([]byte{0x42}, 16)
|
|||
|
|
iv := bytes.Repeat([]byte{0x7E}, 16)
|
|||
|
|
plaintext := []byte("explicit iv via suite")
|
|||
|
|
|
|||
|
|
for _, mode := range []cipherx.Mode{cipherx.CBC, cipherx.CFB, cipherx.OFB, cipherx.CTR} {
|
|||
|
|
ciphertext, err := suite.EncryptWithIV(mode, plaintext, key, iv)
|
|||
|
|
if err != nil {
|
|||
|
|
t.Fatalf("%s: EncryptWithIV 失败: %v", mode, err)
|
|||
|
|
}
|
|||
|
|
decrypted, err := suite.DecryptWithIV(mode, ciphertext, key, iv)
|
|||
|
|
if err != nil {
|
|||
|
|
t.Fatalf("%s: DecryptWithIV 失败: %v", mode, err)
|
|||
|
|
}
|
|||
|
|
if !bytes.Equal(decrypted, plaintext) {
|
|||
|
|
t.Errorf("%s: WithIV 往返不一致", mode)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if _, err := suite.EncryptWithIV(cipherx.CBC, plaintext, key, []byte("bad")); !errors.Is(err, cipherx.ErrInvalidIVSize) {
|
|||
|
|
t.Errorf("非法 IV 长度: 期望 ErrInvalidIVSize, 得到 %v", err)
|
|||
|
|
}
|
|||
|
|
if _, err := suite.EncryptWithIV(cipherx.ECB, plaintext, key, iv); !errors.Is(err, cipherx.ErrInvalidMode) {
|
|||
|
|
t.Errorf("ECB 传 IV: 期望 ErrInvalidMode, 得到 %v", err)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestSuiteHexHelpers(t *testing.T) {
|
|||
|
|
suite := testSuite()
|
|||
|
|
key := bytes.Repeat([]byte{0x42}, 16)
|
|||
|
|
iv := bytes.Repeat([]byte{0x7E}, 16)
|
|||
|
|
plaintext := []byte("hex 辅助函数")
|
|||
|
|
|
|||
|
|
for _, mode := range []cipherx.Mode{cipherx.ECB, cipherx.CBC, cipherx.CFB, cipherx.OFB, cipherx.CTR} {
|
|||
|
|
hexStr, err := suite.EncryptHex(mode, plaintext, key)
|
|||
|
|
if err != nil {
|
|||
|
|
t.Fatalf("%s: EncryptHex 失败: %v", mode, err)
|
|||
|
|
}
|
|||
|
|
decrypted, err := suite.DecryptHex(mode, hexStr, key)
|
|||
|
|
if err != nil {
|
|||
|
|
t.Fatalf("%s: DecryptHex 失败: %v", mode, err)
|
|||
|
|
}
|
|||
|
|
if !bytes.Equal(decrypted, plaintext) {
|
|||
|
|
t.Errorf("%s: Hex 往返不一致", mode)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 带显式 IV 的十六进制辅助函数
|
|||
|
|
for _, mode := range []cipherx.Mode{cipherx.CBC, cipherx.CFB, cipherx.OFB, cipherx.CTR} {
|
|||
|
|
hexStr, err := suite.EncryptWithIVHex(mode, plaintext, key, iv)
|
|||
|
|
if err != nil {
|
|||
|
|
t.Fatalf("%s: EncryptWithIVHex 失败: %v", mode, err)
|
|||
|
|
}
|
|||
|
|
decrypted, err := suite.DecryptWithIVHex(mode, hexStr, key, iv)
|
|||
|
|
if err != nil {
|
|||
|
|
t.Fatalf("%s: DecryptWithIVHex 失败: %v", mode, err)
|
|||
|
|
}
|
|||
|
|
if !bytes.Equal(decrypted, plaintext) {
|
|||
|
|
t.Errorf("%s: WithIVHex 往返不一致", mode)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 非法十六进制
|
|||
|
|
if _, err := suite.DecryptHex(cipherx.CBC, "zz", key); err == nil {
|
|||
|
|
t.Error("非法十六进制期望错误")
|
|||
|
|
}
|
|||
|
|
if _, err := suite.EncryptWithIVHex(cipherx.CBC, plaintext, []byte("short"), iv); !errors.Is(err, cipherx.ErrInvalidKeySize) {
|
|||
|
|
t.Errorf("非法密钥: 期望 ErrInvalidKeySize, 得到 %v", err)
|
|||
|
|
}
|
|||
|
|
if _, err := suite.DecryptWithIVHex(cipherx.CBC, "zz", key, iv); err == nil {
|
|||
|
|
t.Error("非法十六进制期望错误")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// TestSuiteMemberVariants 覆盖 Suite 各方法的失败分支,确保错误在
|
|||
|
|
// 构造阶段就被发现,而不是留到更难排查的位置。
|
|||
|
|
func TestSuiteMemberVariants(t *testing.T) {
|
|||
|
|
suite := testSuite()
|
|||
|
|
key := bytes.Repeat([]byte{0x42}, 16)
|
|||
|
|
iv := bytes.Repeat([]byte{0x7E}, 16)
|
|||
|
|
|
|||
|
|
if _, err := suite.Encrypt(cipherx.Mode(99), []byte("x"), key); !errors.Is(err, cipherx.ErrInvalidMode) {
|
|||
|
|
t.Errorf("Encrypt 非法模式: 期望 ErrInvalidMode, 得到 %v", err)
|
|||
|
|
}
|
|||
|
|
if _, err := suite.Decrypt(cipherx.Mode(99), []byte("x"), key); !errors.Is(err, cipherx.ErrInvalidMode) {
|
|||
|
|
t.Errorf("Decrypt 非法模式: 期望 ErrInvalidMode, 得到 %v", err)
|
|||
|
|
}
|
|||
|
|
if _, err := suite.EncryptWithIV(cipherx.Mode(99), []byte("x"), key, iv); !errors.Is(err, cipherx.ErrInvalidMode) {
|
|||
|
|
t.Errorf("EncryptWithIV 非法模式: 期望 ErrInvalidMode, 得到 %v", err)
|
|||
|
|
}
|
|||
|
|
if _, err := suite.DecryptWithIV(cipherx.Mode(99), []byte("x"), key, iv); !errors.Is(err, cipherx.ErrInvalidMode) {
|
|||
|
|
t.Errorf("DecryptWithIV 非法模式: 期望 ErrInvalidMode, 得到 %v", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if _, err := suite.EncryptHex(cipherx.CBC, []byte("x"), []byte("short")); !errors.Is(err, cipherx.ErrInvalidKeySize) {
|
|||
|
|
t.Errorf("EncryptHex 非法密钥: 期望 ErrInvalidKeySize, 得到 %v", err)
|
|||
|
|
}
|
|||
|
|
if _, err := suite.DecryptWithIV(cipherx.CBC, []byte("x"), key, []byte("bad")); !errors.Is(err, cipherx.ErrInvalidIVSize) {
|
|||
|
|
t.Errorf("DecryptWithIV 非法 IV: 期望 ErrInvalidIVSize, 得到 %v", err)
|
|||
|
|
}
|
|||
|
|
}
|