Files
xgcl/utils/blockmode/blockmode.go
T
2026-05-27 23:03:00 +08:00

140 lines
4.5 KiB
Go

// Package blockmodes 使用BlockEncrypter接口封装GCM、CCM等加密模式。
// 适用于将密码机、ukey的ECB加密模式封装为GCM、CCM等加密模式。
package blockmode
import (
"crypto/cipher"
)
// var errorCodeBase = gcl.ErrorCodeBaseMap[PACKAGE_NAME]
// EcbBlockMode 是对多组数据进行加密/解密运算。可以对密码机等的ECB加密进行封装后得到。
// GCM模式、CTR等模式将基于EcbBlockMode进行计算。
// 注:由于Cipher.NewGCM的入参是cipher.Block, 针对单组数据进行的运算,不适合密码机等,
// 否则每个分组都要调用密码机或ukey,调用消耗巨大。所以这里使用多组加密接口进行GCM、CTR、CCM
// 等封装。
type EcbBlockMode interface {
// 每个分组长度
BlockSize() int
// CryptBlocks 加密多组数据。传入src保证是BlockSize()的整数倍,len(dst) >= len(src)
// 加密完成后dst[:len(src)]为(ECB)密文。
// 如果src非常大,BlockEncrypter负责进行分组调用密码机, 可能因为网络等原因会返回错误。
// 本函数为阻塞函数
EcbEncCryptBlocks(dst, src []byte) error
EcbDecCryptBlocks(dst, src []byte) error
}
// CbcBlockMode CBC计算。BlockEncrypter对象也可以做CBC,但是,如果每次分组都去调密码卡或ukey,会非常慢。
type CbcBlockMode interface {
// 分组长度
BlockSize() int
// CBCEncrypt CBC加密多组数据。传入src保证是BlockSize()的整数倍,len(dst) >= len(src)
// iv是BlockSize()大小。 dst和src要么完全重合,要么完全不相交
// 返回时:如果正常则填充dst[:len(src)]为密文。iv返回时不作要求,可以保持不变,也可以为最后一个分组内容。
// 如果src非常大,CBCEncrypter负责进行分组调用密码机, 可能因为网络等原因会返回错误。
// 本函数为阻塞函数
CbcEncCryptBlocks(dst, iv, src []byte) error
CbcDecCryptBlocks(dst, iv, src []byte) error
}
type EcbCbcBlockMode interface {
EcbBlockMode
CbcBlockMode
}
// 三段式加解密接口
type TernaryCrypter interface {
// 三段式加密
// Init an BlockUpdater. Set nonce as iv and additional data.
EncryptInit(iv []byte) error
// Encrypt 单组加密,等价于调用EncryptUpdate后EncryptFinal
Encrypt(dst []byte, in []byte) ([]byte, error)
// Update appends the encrypted/decrypted result to dst and return it
EncryptUpdate(dst []byte, in []byte) ([]byte, error)
// Final 将剩余密文和tag append到out上并返回。返回结果的最后16字节为tag.
EncryptFinal(dst []byte) ([]byte, error)
// 三段式解密
DecryptInit(iv []byte) error
Decrypt(dst []byte, in []byte) ([]byte, error)
DecryptUpdate(dst []byte, in []byte) ([]byte, error)
DecryptFinal(dst []byte) ([]byte, error)
}
// TernaryGCM 标准库cipher.AEAD和SDF/SKF的三段式用法。
// 在三段式调用中,若某次返回错误(比如某个大文件已经EncryptUpdate多次了,但如果某次网络原因
// EncryptUpdate返回错误),则保证内部状态不变, 可以重复该次返错的调用。
type TernaryGCM interface {
// 保留标准库用法,AEAD.Seal和AEAD.Open前无需调用EncryptInit/DecryptInit
cipher.AEAD
// SpecifyADD 在三段式调用中必须在调用EncryptInit后调用
ADDSpecifier
TernaryCrypter
}
type ADDSpecifier interface {
SpecifyADD(additionalData []byte)
}
type ADDAndDataLenSpecifier interface {
SpecifyADDAndDataLen(ad []byte, dataLen int)
}
// Stream for ctr mode
type TernaryStream interface {
cipher.Stream
TernaryCrypter
}
// Wrapper 将一个cipher.Block接口包装为EcbCbcEncBlockMode接口
type wrapper struct {
cipher.Block
}
func Wrap(b cipher.Block) EcbCbcBlockMode {
// If b is also an EcbCbcBlockMode interface.
if bm, ok := b.(EcbCbcBlockMode); ok {
return bm
}
return wrapper{b}
}
func (w wrapper) BlockSize() int {
return w.Block.BlockSize()
}
func (w wrapper) EcbEncCryptBlocks(dst, src []byte) error {
for len(src) > 0 {
w.Encrypt(dst, src)
dst = dst[w.BlockSize():]
src = src[w.BlockSize():]
}
return nil
}
func (w wrapper) CbcEncCryptBlocks(dst, iv, src []byte) error {
cbc := cipher.NewCBCEncrypter(w.Block, iv)
cbc.CryptBlocks(dst, src)
return nil
}
func (w wrapper) EcbDecCryptBlocks(dst, src []byte) error {
for len(src) > 0 {
w.Decrypt(dst, src)
dst = dst[w.BlockSize():]
src = src[w.BlockSize():]
}
return nil
}
func (w wrapper) CbcDecCryptBlocks(dst, iv, src []byte) error {
cbc := cipher.NewCBCDecrypter(w.Block, iv)
cbc.CryptBlocks(dst, src)
return nil
}