Files
encryptx/cipherx/suite.go
T
2026-09-30 15:49:12 +08:00

156 lines
4.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package cipherx
import (
"crypto/cipher"
"encoding/hex"
"errors"
"fmt"
"strings"
)
// ErrInvalidKeySize 表示密钥长度不被算法接受。
var ErrInvalidKeySize = errors.New("cipherx: invalid key size")
// Suite 把一个具体的分组密码算法(分组构造方式 + 允许的密钥长度)与通用模式
// 操作绑定在一起。
//
// 引入 Suite 的目的是消除各算法包之间重复的模式实现:aesx / desx / tdesx /
// blowfishx 只需声明「如何用密钥构造分组」,ECB/CBC/CFB/OFB/CTR 的加解密、
// 填充、IV 处理与长度校验全部由 Suite 统一提供。
type Suite struct {
name string
newBlock func(key []byte) (cipher.Block, error)
keySizes []int
blockSize int
}
// NewSuite 创建一个算法套件。
//
// name 用于错误信息(如 "AES");newBlock 通常是标准库的 aes.NewCipher /
// des.NewCipher / des.NewTripleDESCipher 或 x/crypto 的 blowfish.NewCipher;
// blockSize 是该算法的分组长度;keySizes 为允许的密钥长度(字节)。
// keySizes 留空表示密钥长度由 newBlock 自行校验(适用于 Blowfish 这类
// 密钥长度可变且非离散取值的算法)。
func NewSuite(name string, newBlock func(key []byte) (cipher.Block, error), blockSize int, keySizes ...int) *Suite {
sizes := make([]int, len(keySizes))
copy(sizes, keySizes)
return &Suite{
name: name,
newBlock: newBlock,
keySizes: sizes,
blockSize: blockSize,
}
}
// Name 返回算法名称。
func (s *Suite) Name() string { return s.name }
// BlockSize 返回分组长度(字节)。
func (s *Suite) BlockSize() int { return s.blockSize }
// KeySizes 返回允许的密钥长度(字节)。若为空,表示不限制为离散取值。
func (s *Suite) KeySizes() []int {
out := make([]int, len(s.keySizes))
copy(out, s.keySizes)
return out
}
// NewBlock 校验密钥长度后构造分组密码。
//
// 所有算法包都应通过本方法构造分组,以便统一错误语义:密钥长度非法时返回的
// 错误可以用 errors.Is(err, ErrInvalidKeySize) 判断。
func (s *Suite) NewBlock(key []byte) (cipher.Block, error) {
if len(s.keySizes) > 0 {
ok := false
for _, size := range s.keySizes {
if len(key) == size {
ok = true
break
}
}
if !ok {
return nil, fmt.Errorf("%s: 密钥长度 %d 非法, 允许 %v: %w",
s.name, len(key), s.keySizes, ErrInvalidKeySize)
}
}
block, err := s.newBlock(key)
if err != nil {
// 底层实现(如 blowfish)也会校验密钥长度,统一归类。
return nil, fmt.Errorf("%s: 构造分组密码失败: %w", s.name, errors.Join(ErrInvalidKeySize, err))
}
return block, nil
}
// Encrypt 用随机 IV 加密(IV 前置,格式为 iv||ciphertext;ECB 无 IV)。
func (s *Suite) Encrypt(mode Mode, plaintext, key []byte) ([]byte, error) {
block, err := s.NewBlock(key)
if err != nil {
return nil, err
}
return Encrypt(block, mode, plaintext)
}
// Decrypt 解密 Encrypt 产生的数据(IV 前置格式)。
func (s *Suite) Decrypt(mode Mode, ciphertext, key []byte) ([]byte, error) {
block, err := s.NewBlock(key)
if err != nil {
return nil, err
}
return Decrypt(block, mode, ciphertext)
}
// EncryptWithIV 使用调用方提供的 IV 加密,返回值不含 IV。
func (s *Suite) EncryptWithIV(mode Mode, plaintext, key, iv []byte) ([]byte, error) {
block, err := s.NewBlock(key)
if err != nil {
return nil, err
}
return EncryptWithIV(block, mode, plaintext, iv)
}
// DecryptWithIV 使用调用方提供的 IV 解密。
func (s *Suite) DecryptWithIV(mode Mode, ciphertext, key, iv []byte) ([]byte, error) {
block, err := s.NewBlock(key)
if err != nil {
return nil, err
}
return DecryptWithIV(block, mode, ciphertext, iv)
}
// EncryptHex 与 Encrypt 相同,返回大写十六进制字符串。
func (s *Suite) EncryptHex(mode Mode, plaintext, key []byte) (string, error) {
ciphertext, err := s.Encrypt(mode, plaintext, key)
if err != nil {
return "", err
}
return strings.ToUpper(hex.EncodeToString(ciphertext)), nil
}
// DecryptHex 解密十六进制密文,大小写均可。
func (s *Suite) DecryptHex(mode Mode, ciphertextHex string, key []byte) ([]byte, error) {
ciphertext, err := decodeHex(ciphertextHex)
if err != nil {
return nil, err
}
return s.Decrypt(mode, ciphertext, key)
}
// EncryptWithIVHex 与 EncryptWithIV 相同,返回大写十六进制字符串。
func (s *Suite) EncryptWithIVHex(mode Mode, plaintext, key, iv []byte) (string, error) {
ciphertext, err := s.EncryptWithIV(mode, plaintext, key, iv)
if err != nil {
return "", err
}
return strings.ToUpper(hex.EncodeToString(ciphertext)), nil
}
// DecryptWithIVHex 解密十六进制密文,使用调用方提供的 IV。
func (s *Suite) DecryptWithIVHex(mode Mode, ciphertextHex string, key, iv []byte) ([]byte, error) {
ciphertext, err := decodeHex(ciphertextHex)
if err != nil {
return nil, err
}
return s.DecryptWithIV(mode, ciphertext, key, iv)
}