156 lines
4.9 KiB
Go
156 lines
4.9 KiB
Go
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)
|
||
}
|