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)
|
|||
|
|
}
|