refactor: split into subpackages and add AEAD/options/stream APIs with comprehensive tests
This commit is contained in:
+331
@@ -0,0 +1,331 @@
|
||||
package symm
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"b612.me/starcrypto/paddingx"
|
||||
)
|
||||
|
||||
const (
|
||||
PKCS5PADDING = paddingx.PKCS5
|
||||
PKCS7PADDING = paddingx.PKCS7
|
||||
ZEROPADDING = paddingx.ZERO
|
||||
ANSIX923PADDING = paddingx.ANSIX923
|
||||
)
|
||||
|
||||
var ErrInvalidGCMNonceLength = errors.New("gcm nonce length must be 12 bytes")
|
||||
|
||||
func EncryptAes(data, key, iv []byte, mode, paddingType string) ([]byte, error) {
|
||||
normalizedMode := normalizeCipherMode(mode)
|
||||
if normalizedMode == MODEGCM {
|
||||
return EncryptAesGCM(data, key, iv, nil)
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return encryptWithBlockMode(block, data, iv, normalizedMode, paddingType, PKCS7PADDING)
|
||||
}
|
||||
|
||||
func DecryptAes(src, key, iv []byte, mode, paddingType string) ([]byte, error) {
|
||||
normalizedMode := normalizeCipherMode(mode)
|
||||
if normalizedMode == MODEGCM {
|
||||
return DecryptAesGCM(src, key, iv, nil)
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decryptWithBlockMode(block, src, iv, normalizedMode, paddingType, PKCS7PADDING)
|
||||
}
|
||||
|
||||
func EncryptAesStream(dst io.Writer, src io.Reader, key, iv []byte, mode, paddingType string) error {
|
||||
normalizedMode := normalizeCipherMode(mode)
|
||||
if normalizedMode == MODEGCM {
|
||||
return EncryptAesGCMStream(dst, src, key, iv, nil)
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return encryptWithBlockModeStream(block, dst, src, iv, normalizedMode, paddingType, PKCS7PADDING)
|
||||
}
|
||||
|
||||
func DecryptAesStream(dst io.Writer, src io.Reader, key, iv []byte, mode, paddingType string) error {
|
||||
normalizedMode := normalizeCipherMode(mode)
|
||||
if normalizedMode == MODEGCM {
|
||||
return DecryptAesGCMStream(dst, src, key, iv, nil)
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return decryptWithBlockModeStream(block, dst, src, iv, normalizedMode, paddingType, PKCS7PADDING)
|
||||
}
|
||||
|
||||
func EncryptAesWithOptions(data, key []byte, opts *CipherOptions) ([]byte, error) {
|
||||
cfg := normalizeCipherOptions(opts)
|
||||
mode := normalizeCipherMode(cfg.Mode)
|
||||
if mode == "" {
|
||||
mode = MODEGCM
|
||||
}
|
||||
if mode == MODEGCM {
|
||||
return EncryptAesGCM(data, key, nonceFromOptions(cfg), cfg.AAD)
|
||||
}
|
||||
return EncryptAes(data, key, cfg.IV, mode, cfg.Padding)
|
||||
}
|
||||
|
||||
func DecryptAesWithOptions(src, key []byte, opts *CipherOptions) ([]byte, error) {
|
||||
cfg := normalizeCipherOptions(opts)
|
||||
mode := normalizeCipherMode(cfg.Mode)
|
||||
if mode == "" {
|
||||
mode = MODEGCM
|
||||
}
|
||||
if mode == MODEGCM {
|
||||
return DecryptAesGCM(src, key, nonceFromOptions(cfg), cfg.AAD)
|
||||
}
|
||||
return DecryptAes(src, key, cfg.IV, mode, cfg.Padding)
|
||||
}
|
||||
|
||||
func EncryptAesStreamWithOptions(dst io.Writer, src io.Reader, key []byte, opts *CipherOptions) error {
|
||||
cfg := normalizeCipherOptions(opts)
|
||||
mode := normalizeCipherMode(cfg.Mode)
|
||||
if mode == "" {
|
||||
mode = MODEGCM
|
||||
}
|
||||
if mode == MODEGCM {
|
||||
return EncryptAesGCMStream(dst, src, key, nonceFromOptions(cfg), cfg.AAD)
|
||||
}
|
||||
return EncryptAesStream(dst, src, key, cfg.IV, mode, cfg.Padding)
|
||||
}
|
||||
|
||||
func DecryptAesStreamWithOptions(dst io.Writer, src io.Reader, key []byte, opts *CipherOptions) error {
|
||||
cfg := normalizeCipherOptions(opts)
|
||||
mode := normalizeCipherMode(cfg.Mode)
|
||||
if mode == "" {
|
||||
mode = MODEGCM
|
||||
}
|
||||
if mode == MODEGCM {
|
||||
return DecryptAesGCMStream(dst, src, key, nonceFromOptions(cfg), cfg.AAD)
|
||||
}
|
||||
return DecryptAesStream(dst, src, key, cfg.IV, mode, cfg.Padding)
|
||||
}
|
||||
|
||||
func EncryptAesGCM(plain, key, nonce, aad []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(nonce) != gcm.NonceSize() {
|
||||
return nil, ErrInvalidGCMNonceLength
|
||||
}
|
||||
return gcm.Seal(nil, nonce, plain, aad), nil
|
||||
}
|
||||
|
||||
func DecryptAesGCM(ciphertext, key, nonce, aad []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(nonce) != gcm.NonceSize() {
|
||||
return nil, ErrInvalidGCMNonceLength
|
||||
}
|
||||
return gcm.Open(nil, nonce, ciphertext, aad)
|
||||
}
|
||||
|
||||
func EncryptAesGCMStream(dst io.Writer, src io.Reader, key, nonce, aad []byte) error {
|
||||
plain, err := io.ReadAll(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out, err := EncryptAesGCM(plain, key, nonce, aad)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = dst.Write(out)
|
||||
return err
|
||||
}
|
||||
|
||||
func DecryptAesGCMStream(dst io.Writer, src io.Reader, key, nonce, aad []byte) error {
|
||||
enc, err := io.ReadAll(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out, err := DecryptAesGCM(enc, key, nonce, aad)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = dst.Write(out)
|
||||
return err
|
||||
}
|
||||
|
||||
func EncryptAesECB(data, key []byte, paddingType string) ([]byte, error) {
|
||||
return EncryptAes(data, key, nil, MODEECB, paddingType)
|
||||
}
|
||||
|
||||
func DecryptAesECB(src, key []byte, paddingType string) ([]byte, error) {
|
||||
return DecryptAes(src, key, nil, MODEECB, paddingType)
|
||||
}
|
||||
|
||||
func EncryptAesCBC(data, key, iv []byte, paddingType string) ([]byte, error) {
|
||||
return EncryptAes(data, key, iv, MODECBC, paddingType)
|
||||
}
|
||||
|
||||
func DecryptAesCBC(src, key, iv []byte, paddingType string) ([]byte, error) {
|
||||
return DecryptAes(src, key, iv, MODECBC, paddingType)
|
||||
}
|
||||
|
||||
func EncryptAesCFB(data, key, iv []byte) ([]byte, error) {
|
||||
return EncryptAes(data, key, iv, MODECFB, "")
|
||||
}
|
||||
|
||||
func DecryptAesCFB(src, key, iv []byte) ([]byte, error) {
|
||||
return DecryptAes(src, key, iv, MODECFB, "")
|
||||
}
|
||||
|
||||
func EncryptAesOFB(data, key, iv []byte) ([]byte, error) {
|
||||
return EncryptAes(data, key, iv, MODEOFB, "")
|
||||
}
|
||||
|
||||
func DecryptAesOFB(src, key, iv []byte) ([]byte, error) {
|
||||
return DecryptAes(src, key, iv, MODEOFB, "")
|
||||
}
|
||||
|
||||
func EncryptAesCTR(data, key, iv []byte) ([]byte, error) {
|
||||
return EncryptAes(data, key, iv, MODECTR, "")
|
||||
}
|
||||
|
||||
func DecryptAesCTR(src, key, iv []byte) ([]byte, error) {
|
||||
return DecryptAes(src, key, iv, MODECTR, "")
|
||||
}
|
||||
|
||||
func EncryptAesECBStream(dst io.Writer, src io.Reader, key []byte, paddingType string) error {
|
||||
return EncryptAesStream(dst, src, key, nil, MODEECB, paddingType)
|
||||
}
|
||||
|
||||
func DecryptAesECBStream(dst io.Writer, src io.Reader, key []byte, paddingType string) error {
|
||||
return DecryptAesStream(dst, src, key, nil, MODEECB, paddingType)
|
||||
}
|
||||
|
||||
func EncryptAesCBCStream(dst io.Writer, src io.Reader, key, iv []byte, paddingType string) error {
|
||||
return EncryptAesStream(dst, src, key, iv, MODECBC, paddingType)
|
||||
}
|
||||
|
||||
func DecryptAesCBCStream(dst io.Writer, src io.Reader, key, iv []byte, paddingType string) error {
|
||||
return DecryptAesStream(dst, src, key, iv, MODECBC, paddingType)
|
||||
}
|
||||
|
||||
func EncryptAesCFBStream(dst io.Writer, src io.Reader, key, iv []byte) error {
|
||||
return EncryptAesStream(dst, src, key, iv, MODECFB, "")
|
||||
}
|
||||
|
||||
func DecryptAesCFBStream(dst io.Writer, src io.Reader, key, iv []byte) error {
|
||||
return DecryptAesStream(dst, src, key, iv, MODECFB, "")
|
||||
}
|
||||
|
||||
func EncryptAesOFBStream(dst io.Writer, src io.Reader, key, iv []byte) error {
|
||||
return EncryptAesStream(dst, src, key, iv, MODEOFB, "")
|
||||
}
|
||||
|
||||
func DecryptAesOFBStream(dst io.Writer, src io.Reader, key, iv []byte) error {
|
||||
return DecryptAesStream(dst, src, key, iv, MODEOFB, "")
|
||||
}
|
||||
|
||||
func EncryptAesCTRStream(dst io.Writer, src io.Reader, key, iv []byte) error {
|
||||
return EncryptAesStream(dst, src, key, iv, MODECTR, "")
|
||||
}
|
||||
|
||||
func DecryptAesCTRStream(dst io.Writer, src io.Reader, key, iv []byte) error {
|
||||
return DecryptAesStream(dst, src, key, iv, MODECTR, "")
|
||||
}
|
||||
|
||||
func CustomEncryptAesCFB(origData, key []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
encrypted := make([]byte, aes.BlockSize+len(origData))
|
||||
iv := encrypted[:block.BlockSize()]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := EncryptAesCFB(origData, key, iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(encrypted[block.BlockSize():], body)
|
||||
return encrypted, nil
|
||||
}
|
||||
|
||||
func CustomDecryptAesCFB(encrypted, key []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(encrypted) < block.BlockSize() {
|
||||
return nil, errors.New("ciphertext too short")
|
||||
}
|
||||
|
||||
iv := encrypted[:block.BlockSize()]
|
||||
return DecryptAesCFB(encrypted[block.BlockSize():], key, iv)
|
||||
}
|
||||
|
||||
func CustomEncryptAesCFBNoBlock(origData, key, iv []byte) ([]byte, error) {
|
||||
return EncryptAesCFB(origData, key, iv)
|
||||
}
|
||||
|
||||
func CustomDecryptAesCFBNoBlock(encrypted, key, iv []byte) ([]byte, error) {
|
||||
return DecryptAesCFB(encrypted, key, iv)
|
||||
}
|
||||
|
||||
func PKCS5Padding(cipherText []byte, blockSize int) []byte {
|
||||
out, _ := paddingx.Pad(cipherText, blockSize, PKCS5PADDING)
|
||||
return out
|
||||
}
|
||||
|
||||
func PKCS5Trimming(encrypted []byte) []byte {
|
||||
if len(encrypted) == 0 {
|
||||
return nil
|
||||
}
|
||||
padding := int(encrypted[len(encrypted)-1])
|
||||
if padding <= 0 || padding > len(encrypted) {
|
||||
return nil
|
||||
}
|
||||
for i := len(encrypted) - padding; i < len(encrypted); i++ {
|
||||
if int(encrypted[i]) != padding {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return encrypted[:len(encrypted)-padding]
|
||||
}
|
||||
|
||||
func PKCS7Padding(cipherText []byte, blockSize int) []byte {
|
||||
out, _ := paddingx.Pad(cipherText, blockSize, PKCS7PADDING)
|
||||
return out
|
||||
}
|
||||
|
||||
func PKCS7Trimming(encrypted []byte, blockSize int) []byte {
|
||||
out, err := paddingx.Unpad(encrypted, blockSize, PKCS7PADDING)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package symm
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"golang.org/x/crypto/chacha20"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
)
|
||||
|
||||
var ErrInvalidChaCha20NonceLength = errors.New("chacha20 nonce length must be 12 or 24 bytes")
|
||||
|
||||
func EncryptChaCha20(data, key, nonce []byte) ([]byte, error) {
|
||||
stream, err := chacha20.NewUnauthenticatedCipher(key, nonce)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]byte, len(data))
|
||||
stream.XORKeyStream(out, data)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func DecryptChaCha20(src, key, nonce []byte) ([]byte, error) {
|
||||
return EncryptChaCha20(src, key, nonce)
|
||||
}
|
||||
|
||||
func EncryptChaCha20Stream(dst io.Writer, src io.Reader, key, nonce []byte) error {
|
||||
stream, err := chacha20.NewUnauthenticatedCipher(key, nonce)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return xorStreamCopy(dst, src, stream)
|
||||
}
|
||||
|
||||
func DecryptChaCha20Stream(dst io.Writer, src io.Reader, key, nonce []byte) error {
|
||||
return EncryptChaCha20Stream(dst, src, key, nonce)
|
||||
}
|
||||
|
||||
func EncryptChaCha20Poly1305(plain, key, nonce, aad []byte) ([]byte, error) {
|
||||
aead, err := newChaCha20AEAD(key, nonce)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return aead.Seal(nil, nonce, plain, aad), nil
|
||||
}
|
||||
|
||||
func DecryptChaCha20Poly1305(ciphertext, key, nonce, aad []byte) ([]byte, error) {
|
||||
aead, err := newChaCha20AEAD(key, nonce)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return aead.Open(nil, nonce, ciphertext, aad)
|
||||
}
|
||||
|
||||
func newChaCha20AEAD(key, nonce []byte) (cipher.AEAD, error) {
|
||||
switch len(nonce) {
|
||||
case chacha20poly1305.NonceSize:
|
||||
return chacha20poly1305.New(key)
|
||||
case chacha20poly1305.NonceSizeX:
|
||||
return chacha20poly1305.NewX(key)
|
||||
default:
|
||||
return nil, ErrInvalidChaCha20NonceLength
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
package symm
|
||||
|
||||
import (
|
||||
"crypto/des"
|
||||
"io"
|
||||
)
|
||||
|
||||
func EncryptDESCBC(data, key, iv []byte, paddingType string) ([]byte, error) {
|
||||
block, err := des.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return encryptWithBlockMode(block, data, iv, MODECBC, paddingType, PKCS5PADDING)
|
||||
}
|
||||
|
||||
func DecryptDESCBC(src, key, iv []byte, paddingType string) ([]byte, error) {
|
||||
block, err := des.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decryptWithBlockMode(block, src, iv, MODECBC, paddingType, PKCS5PADDING)
|
||||
}
|
||||
|
||||
func EncryptDESCBCStream(dst io.Writer, src io.Reader, key, iv []byte, paddingType string) error {
|
||||
block, err := des.NewCipher(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return encryptWithBlockModeStream(block, dst, src, iv, MODECBC, paddingType, PKCS5PADDING)
|
||||
}
|
||||
|
||||
func DecryptDESCBCStream(dst io.Writer, src io.Reader, key, iv []byte, paddingType string) error {
|
||||
block, err := des.NewCipher(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return decryptWithBlockModeStream(block, dst, src, iv, MODECBC, paddingType, PKCS5PADDING)
|
||||
}
|
||||
|
||||
func Encrypt3DESCBC(data, key, iv []byte, paddingType string) ([]byte, error) {
|
||||
block, err := des.NewTripleDESCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return encryptWithBlockMode(block, data, iv, MODECBC, paddingType, PKCS5PADDING)
|
||||
}
|
||||
|
||||
func Decrypt3DESCBC(src, key, iv []byte, paddingType string) ([]byte, error) {
|
||||
block, err := des.NewTripleDESCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decryptWithBlockMode(block, src, iv, MODECBC, paddingType, PKCS5PADDING)
|
||||
}
|
||||
|
||||
func Encrypt3DESCBCStream(dst io.Writer, src io.Reader, key, iv []byte, paddingType string) error {
|
||||
block, err := des.NewTripleDESCipher(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return encryptWithBlockModeStream(block, dst, src, iv, MODECBC, paddingType, PKCS5PADDING)
|
||||
}
|
||||
|
||||
func Decrypt3DESCBCStream(dst io.Writer, src io.Reader, key, iv []byte, paddingType string) error {
|
||||
block, err := des.NewTripleDESCipher(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return decryptWithBlockModeStream(block, dst, src, iv, MODECBC, paddingType, PKCS5PADDING)
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package symm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func FuzzAesCBCRoundTrip(f *testing.F) {
|
||||
f.Add([]byte("fuzz-aes-cbc"))
|
||||
f.Add([]byte{})
|
||||
|
||||
key := []byte("0123456789abcdef")
|
||||
iv := []byte("abcdef9876543210")
|
||||
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
enc, err := EncryptAesCBC(data, key, iv, "")
|
||||
if err != nil {
|
||||
t.Fatalf("EncryptAesCBC failed: %v", err)
|
||||
}
|
||||
dec, err := DecryptAesCBC(enc, key, iv, "")
|
||||
if err != nil {
|
||||
t.Fatalf("DecryptAesCBC failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(dec, data) {
|
||||
t.Fatalf("aes cbc fuzz roundtrip mismatch")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func FuzzChaCha20RoundTrip(f *testing.F) {
|
||||
f.Add([]byte("fuzz-chacha20"))
|
||||
f.Add([]byte{})
|
||||
|
||||
key := []byte("0123456789abcdef0123456789abcdef")
|
||||
nonce := []byte("123456789012")
|
||||
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
enc, err := EncryptChaCha20(data, key, nonce)
|
||||
if err != nil {
|
||||
t.Fatalf("EncryptChaCha20 failed: %v", err)
|
||||
}
|
||||
dec, err := DecryptChaCha20(enc, key, nonce)
|
||||
if err != nil {
|
||||
t.Fatalf("DecryptChaCha20 failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(dec, data) {
|
||||
t.Fatalf("chacha20 fuzz roundtrip mismatch")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func FuzzAesCBCStreamRoundTrip(f *testing.F) {
|
||||
f.Add([]byte("fuzz-aes-stream"))
|
||||
f.Add([]byte{})
|
||||
|
||||
key := []byte("0123456789abcdef")
|
||||
iv := []byte("abcdef9876543210")
|
||||
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
enc := &bytes.Buffer{}
|
||||
dec := &bytes.Buffer{}
|
||||
if err := EncryptAesCBCStream(enc, bytes.NewReader(data), key, iv, ""); err != nil {
|
||||
t.Fatalf("EncryptAesCBCStream failed: %v", err)
|
||||
}
|
||||
if err := DecryptAesCBCStream(dec, bytes.NewReader(enc.Bytes()), key, iv, ""); err != nil {
|
||||
t.Fatalf("DecryptAesCBCStream failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(dec.Bytes(), data) {
|
||||
t.Fatalf("aes cbc stream fuzz roundtrip mismatch")
|
||||
}
|
||||
})
|
||||
}
|
||||
+325
@@ -0,0 +1,325 @@
|
||||
package symm
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"b612.me/starcrypto/paddingx"
|
||||
)
|
||||
|
||||
const (
|
||||
MODEECB = "ECB"
|
||||
MODECBC = "CBC"
|
||||
MODECFB = "CFB"
|
||||
MODEOFB = "OFB"
|
||||
MODECTR = "CTR"
|
||||
MODEGCM = "GCM"
|
||||
)
|
||||
|
||||
var ErrUnsupportedCipherMode = errors.New("cipher mode not supported")
|
||||
|
||||
func normalizeCipherMode(mode string) string {
|
||||
return strings.ToUpper(strings.TrimSpace(mode))
|
||||
}
|
||||
|
||||
func encryptWithBlockMode(block cipher.Block, data, iv []byte, mode, paddingType, defaultPadding string) ([]byte, error) {
|
||||
mode = normalizeCipherMode(mode)
|
||||
if mode == "" {
|
||||
mode = MODECBC
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case MODEECB:
|
||||
if paddingType == "" {
|
||||
paddingType = defaultPadding
|
||||
}
|
||||
content, err := paddingx.Pad(data, block.BlockSize(), paddingType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]byte, len(content))
|
||||
ecbEncryptBlocks(block, out, content)
|
||||
return out, nil
|
||||
case MODECBC:
|
||||
if len(iv) != block.BlockSize() {
|
||||
return nil, errors.New("iv length must match block size")
|
||||
}
|
||||
if paddingType == "" {
|
||||
paddingType = defaultPadding
|
||||
}
|
||||
content, err := paddingx.Pad(data, block.BlockSize(), paddingType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]byte, len(content))
|
||||
cipher.NewCBCEncrypter(block, iv).CryptBlocks(out, content)
|
||||
return out, nil
|
||||
case MODECFB, MODEOFB, MODECTR:
|
||||
if len(iv) != block.BlockSize() {
|
||||
return nil, errors.New("iv length must match block size")
|
||||
}
|
||||
stream, err := newCipherStream(block, iv, mode, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]byte, len(data))
|
||||
stream.XORKeyStream(out, data)
|
||||
return out, nil
|
||||
default:
|
||||
return nil, ErrUnsupportedCipherMode
|
||||
}
|
||||
}
|
||||
|
||||
func decryptWithBlockMode(block cipher.Block, src, iv []byte, mode, paddingType, defaultPadding string) ([]byte, error) {
|
||||
mode = normalizeCipherMode(mode)
|
||||
if mode == "" {
|
||||
mode = MODECBC
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case MODEECB:
|
||||
if len(src) == 0 || len(src)%block.BlockSize() != 0 {
|
||||
return nil, errors.New("ciphertext is not a full block size")
|
||||
}
|
||||
if paddingType == "" {
|
||||
paddingType = defaultPadding
|
||||
}
|
||||
decrypted := make([]byte, len(src))
|
||||
ecbDecryptBlocks(block, decrypted, src)
|
||||
return paddingx.Unpad(decrypted, block.BlockSize(), paddingType)
|
||||
case MODECBC:
|
||||
if len(iv) != block.BlockSize() {
|
||||
return nil, errors.New("iv length must match block size")
|
||||
}
|
||||
if len(src) == 0 || len(src)%block.BlockSize() != 0 {
|
||||
return nil, errors.New("ciphertext is not a full block size")
|
||||
}
|
||||
if paddingType == "" {
|
||||
paddingType = defaultPadding
|
||||
}
|
||||
decrypted := make([]byte, len(src))
|
||||
cipher.NewCBCDecrypter(block, iv).CryptBlocks(decrypted, src)
|
||||
return paddingx.Unpad(decrypted, block.BlockSize(), paddingType)
|
||||
case MODECFB, MODEOFB, MODECTR:
|
||||
if len(iv) != block.BlockSize() {
|
||||
return nil, errors.New("iv length must match block size")
|
||||
}
|
||||
stream, err := newCipherStream(block, iv, mode, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]byte, len(src))
|
||||
stream.XORKeyStream(out, src)
|
||||
return out, nil
|
||||
default:
|
||||
return nil, ErrUnsupportedCipherMode
|
||||
}
|
||||
}
|
||||
|
||||
func encryptWithBlockModeStream(block cipher.Block, dst io.Writer, src io.Reader, iv []byte, mode, paddingType, defaultPadding string) error {
|
||||
mode = normalizeCipherMode(mode)
|
||||
if mode == "" {
|
||||
mode = MODECBC
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case MODEECB:
|
||||
if paddingType == "" {
|
||||
paddingType = defaultPadding
|
||||
}
|
||||
return encryptPaddedBlockStream(dst, src, block.BlockSize(), paddingType, func(out, in []byte) {
|
||||
ecbEncryptBlocks(block, out, in)
|
||||
})
|
||||
case MODECBC:
|
||||
if len(iv) != block.BlockSize() {
|
||||
return errors.New("iv length must match block size")
|
||||
}
|
||||
if paddingType == "" {
|
||||
paddingType = defaultPadding
|
||||
}
|
||||
modeEnc := cipher.NewCBCEncrypter(block, iv)
|
||||
return encryptPaddedBlockStream(dst, src, block.BlockSize(), paddingType, modeEnc.CryptBlocks)
|
||||
case MODECFB, MODEOFB, MODECTR:
|
||||
if len(iv) != block.BlockSize() {
|
||||
return errors.New("iv length must match block size")
|
||||
}
|
||||
stream, err := newCipherStream(block, iv, mode, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return xorStreamCopy(dst, src, stream)
|
||||
default:
|
||||
return ErrUnsupportedCipherMode
|
||||
}
|
||||
}
|
||||
|
||||
func decryptWithBlockModeStream(block cipher.Block, dst io.Writer, src io.Reader, iv []byte, mode, paddingType, defaultPadding string) error {
|
||||
mode = normalizeCipherMode(mode)
|
||||
if mode == "" {
|
||||
mode = MODECBC
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case MODEECB:
|
||||
if paddingType == "" {
|
||||
paddingType = defaultPadding
|
||||
}
|
||||
return decryptPaddedBlockStream(dst, src, block.BlockSize(), paddingType, func(out, in []byte) {
|
||||
ecbDecryptBlocks(block, out, in)
|
||||
})
|
||||
case MODECBC:
|
||||
if len(iv) != block.BlockSize() {
|
||||
return errors.New("iv length must match block size")
|
||||
}
|
||||
if paddingType == "" {
|
||||
paddingType = defaultPadding
|
||||
}
|
||||
modeDec := cipher.NewCBCDecrypter(block, iv)
|
||||
return decryptPaddedBlockStream(dst, src, block.BlockSize(), paddingType, modeDec.CryptBlocks)
|
||||
case MODECFB, MODEOFB, MODECTR:
|
||||
if len(iv) != block.BlockSize() {
|
||||
return errors.New("iv length must match block size")
|
||||
}
|
||||
stream, err := newCipherStream(block, iv, mode, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return xorStreamCopy(dst, src, stream)
|
||||
default:
|
||||
return ErrUnsupportedCipherMode
|
||||
}
|
||||
}
|
||||
|
||||
func newCipherStream(block cipher.Block, iv []byte, mode string, decrypt bool) (cipher.Stream, error) {
|
||||
switch mode {
|
||||
case MODECFB:
|
||||
if decrypt {
|
||||
return cipher.NewCFBDecrypter(block, iv), nil
|
||||
}
|
||||
return cipher.NewCFBEncrypter(block, iv), nil
|
||||
case MODEOFB:
|
||||
return cipher.NewOFB(block, iv), nil
|
||||
case MODECTR:
|
||||
return cipher.NewCTR(block, iv), nil
|
||||
default:
|
||||
return nil, ErrUnsupportedCipherMode
|
||||
}
|
||||
}
|
||||
|
||||
func xorStreamCopy(dst io.Writer, src io.Reader, stream cipher.Stream) error {
|
||||
buf := make([]byte, 32*1024)
|
||||
out := make([]byte, 32*1024)
|
||||
for {
|
||||
n, err := src.Read(buf)
|
||||
if n > 0 {
|
||||
stream.XORKeyStream(out[:n], buf[:n])
|
||||
if _, werr := dst.Write(out[:n]); werr != nil {
|
||||
return werr
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func encryptPaddedBlockStream(dst io.Writer, src io.Reader, blockSize int, paddingType string, cryptBlocks func(dst, src []byte)) error {
|
||||
pending := make([]byte, 0, blockSize*2)
|
||||
buf := make([]byte, 32*1024)
|
||||
|
||||
for {
|
||||
n, err := src.Read(buf)
|
||||
if n > 0 {
|
||||
pending = append(pending, buf[:n]...)
|
||||
processLen := len(pending) - blockSize
|
||||
if processLen > 0 {
|
||||
processLen -= processLen % blockSize
|
||||
if processLen > 0 {
|
||||
out := make([]byte, processLen)
|
||||
cryptBlocks(out, pending[:processLen])
|
||||
if _, werr := dst.Write(out); werr != nil {
|
||||
return werr
|
||||
}
|
||||
pending = append([]byte(nil), pending[processLen:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
content, err := paddingx.Pad(pending, blockSize, paddingType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out := make([]byte, len(content))
|
||||
cryptBlocks(out, content)
|
||||
_, err = dst.Write(out)
|
||||
return err
|
||||
}
|
||||
|
||||
func decryptPaddedBlockStream(dst io.Writer, src io.Reader, blockSize int, paddingType string, cryptBlocks func(dst, src []byte)) error {
|
||||
pending := make([]byte, 0, blockSize*2)
|
||||
buf := make([]byte, 32*1024)
|
||||
|
||||
for {
|
||||
n, err := src.Read(buf)
|
||||
if n > 0 {
|
||||
pending = append(pending, buf[:n]...)
|
||||
processLen := len(pending) - blockSize
|
||||
if processLen > 0 {
|
||||
processLen -= processLen % blockSize
|
||||
if processLen > 0 {
|
||||
out := make([]byte, processLen)
|
||||
cryptBlocks(out, pending[:processLen])
|
||||
if _, werr := dst.Write(out); werr != nil {
|
||||
return werr
|
||||
}
|
||||
pending = append([]byte(nil), pending[processLen:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(pending) == 0 || len(pending)%blockSize != 0 {
|
||||
return errors.New("ciphertext is not a full block size")
|
||||
}
|
||||
|
||||
decrypted := make([]byte, len(pending))
|
||||
cryptBlocks(decrypted, pending)
|
||||
out, err := paddingx.Unpad(decrypted, blockSize, paddingType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = dst.Write(out)
|
||||
return err
|
||||
}
|
||||
|
||||
func ecbEncryptBlocks(block cipher.Block, dst, src []byte) {
|
||||
blockSize := block.BlockSize()
|
||||
for i := 0; i < len(src); i += blockSize {
|
||||
block.Encrypt(dst[i:i+blockSize], src[i:i+blockSize])
|
||||
}
|
||||
}
|
||||
|
||||
func ecbDecryptBlocks(block cipher.Block, dst, src []byte) {
|
||||
blockSize := block.BlockSize()
|
||||
for i := 0; i < len(src); i += blockSize {
|
||||
block.Decrypt(dst[i:i+blockSize], src[i:i+blockSize])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package symm
|
||||
|
||||
// CipherOptions provides a unified configuration for symmetric APIs.
|
||||
// For GCM mode, Nonce is used; if Nonce is empty, IV is used as fallback.
|
||||
type CipherOptions struct {
|
||||
Mode string
|
||||
Padding string
|
||||
IV []byte
|
||||
Nonce []byte
|
||||
AAD []byte
|
||||
}
|
||||
|
||||
func normalizeCipherOptions(opts *CipherOptions) CipherOptions {
|
||||
if opts == nil {
|
||||
return CipherOptions{}
|
||||
}
|
||||
return *opts
|
||||
}
|
||||
|
||||
func nonceFromOptions(opts CipherOptions) []byte {
|
||||
if len(opts.Nonce) > 0 {
|
||||
return opts.Nonce
|
||||
}
|
||||
return opts.IV
|
||||
}
|
||||
+276
@@ -0,0 +1,276 @@
|
||||
package symm
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/emmansun/gmsm/sm4"
|
||||
)
|
||||
|
||||
func EncryptSM4(data, key, iv []byte, mode, paddingType string) ([]byte, error) {
|
||||
normalizedMode := normalizeCipherMode(mode)
|
||||
if normalizedMode == MODEGCM {
|
||||
return EncryptSM4GCM(data, key, iv, nil)
|
||||
}
|
||||
|
||||
block, err := sm4.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return encryptWithBlockMode(block, data, iv, normalizedMode, paddingType, PKCS7PADDING)
|
||||
}
|
||||
|
||||
func DecryptSM4(src, key, iv []byte, mode, paddingType string) ([]byte, error) {
|
||||
normalizedMode := normalizeCipherMode(mode)
|
||||
if normalizedMode == MODEGCM {
|
||||
return DecryptSM4GCM(src, key, iv, nil)
|
||||
}
|
||||
|
||||
block, err := sm4.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decryptWithBlockMode(block, src, iv, normalizedMode, paddingType, PKCS7PADDING)
|
||||
}
|
||||
|
||||
func EncryptSM4Stream(dst io.Writer, src io.Reader, key, iv []byte, mode, paddingType string) error {
|
||||
normalizedMode := normalizeCipherMode(mode)
|
||||
if normalizedMode == MODEGCM {
|
||||
return EncryptSM4GCMStream(dst, src, key, iv, nil)
|
||||
}
|
||||
|
||||
block, err := sm4.NewCipher(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return encryptWithBlockModeStream(block, dst, src, iv, normalizedMode, paddingType, PKCS7PADDING)
|
||||
}
|
||||
|
||||
func DecryptSM4Stream(dst io.Writer, src io.Reader, key, iv []byte, mode, paddingType string) error {
|
||||
normalizedMode := normalizeCipherMode(mode)
|
||||
if normalizedMode == MODEGCM {
|
||||
return DecryptSM4GCMStream(dst, src, key, iv, nil)
|
||||
}
|
||||
|
||||
block, err := sm4.NewCipher(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return decryptWithBlockModeStream(block, dst, src, iv, normalizedMode, paddingType, PKCS7PADDING)
|
||||
}
|
||||
|
||||
func EncryptSM4WithOptions(data, key []byte, opts *CipherOptions) ([]byte, error) {
|
||||
cfg := normalizeCipherOptions(opts)
|
||||
mode := normalizeCipherMode(cfg.Mode)
|
||||
if mode == "" {
|
||||
mode = MODEGCM
|
||||
}
|
||||
if mode == MODEGCM {
|
||||
return EncryptSM4GCM(data, key, nonceFromOptions(cfg), cfg.AAD)
|
||||
}
|
||||
return EncryptSM4(data, key, cfg.IV, mode, cfg.Padding)
|
||||
}
|
||||
|
||||
func DecryptSM4WithOptions(src, key []byte, opts *CipherOptions) ([]byte, error) {
|
||||
cfg := normalizeCipherOptions(opts)
|
||||
mode := normalizeCipherMode(cfg.Mode)
|
||||
if mode == "" {
|
||||
mode = MODEGCM
|
||||
}
|
||||
if mode == MODEGCM {
|
||||
return DecryptSM4GCM(src, key, nonceFromOptions(cfg), cfg.AAD)
|
||||
}
|
||||
return DecryptSM4(src, key, cfg.IV, mode, cfg.Padding)
|
||||
}
|
||||
|
||||
func EncryptSM4StreamWithOptions(dst io.Writer, src io.Reader, key []byte, opts *CipherOptions) error {
|
||||
cfg := normalizeCipherOptions(opts)
|
||||
mode := normalizeCipherMode(cfg.Mode)
|
||||
if mode == "" {
|
||||
mode = MODEGCM
|
||||
}
|
||||
if mode == MODEGCM {
|
||||
return EncryptSM4GCMStream(dst, src, key, nonceFromOptions(cfg), cfg.AAD)
|
||||
}
|
||||
return EncryptSM4Stream(dst, src, key, cfg.IV, mode, cfg.Padding)
|
||||
}
|
||||
|
||||
func DecryptSM4StreamWithOptions(dst io.Writer, src io.Reader, key []byte, opts *CipherOptions) error {
|
||||
cfg := normalizeCipherOptions(opts)
|
||||
mode := normalizeCipherMode(cfg.Mode)
|
||||
if mode == "" {
|
||||
mode = MODEGCM
|
||||
}
|
||||
if mode == MODEGCM {
|
||||
return DecryptSM4GCMStream(dst, src, key, nonceFromOptions(cfg), cfg.AAD)
|
||||
}
|
||||
return DecryptSM4Stream(dst, src, key, cfg.IV, mode, cfg.Padding)
|
||||
}
|
||||
|
||||
func EncryptSM4GCM(plain, key, nonce, aad []byte) ([]byte, error) {
|
||||
block, err := sm4.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(nonce) != gcm.NonceSize() {
|
||||
return nil, ErrInvalidGCMNonceLength
|
||||
}
|
||||
return gcm.Seal(nil, nonce, plain, aad), nil
|
||||
}
|
||||
|
||||
func DecryptSM4GCM(ciphertext, key, nonce, aad []byte) ([]byte, error) {
|
||||
block, err := sm4.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(nonce) != gcm.NonceSize() {
|
||||
return nil, ErrInvalidGCMNonceLength
|
||||
}
|
||||
return gcm.Open(nil, nonce, ciphertext, aad)
|
||||
}
|
||||
|
||||
func EncryptSM4GCMStream(dst io.Writer, src io.Reader, key, nonce, aad []byte) error {
|
||||
plain, err := io.ReadAll(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out, err := EncryptSM4GCM(plain, key, nonce, aad)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = dst.Write(out)
|
||||
return err
|
||||
}
|
||||
|
||||
func DecryptSM4GCMStream(dst io.Writer, src io.Reader, key, nonce, aad []byte) error {
|
||||
enc, err := io.ReadAll(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out, err := DecryptSM4GCM(enc, key, nonce, aad)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = dst.Write(out)
|
||||
return err
|
||||
}
|
||||
|
||||
func EncryptSM4CFB(origData, key []byte) ([]byte, error) {
|
||||
block, err := sm4.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]byte, block.BlockSize()+len(origData))
|
||||
iv := out[:block.BlockSize()]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body, err := EncryptSM4CFBNoBlock(origData, key, iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(out[block.BlockSize():], body)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func DecryptSM4CFB(encrypted, key []byte) ([]byte, error) {
|
||||
block, err := sm4.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(encrypted) < block.BlockSize() {
|
||||
return nil, errors.New("ciphertext too short")
|
||||
}
|
||||
iv := encrypted[:block.BlockSize()]
|
||||
return DecryptSM4CFBNoBlock(encrypted[block.BlockSize():], key, iv)
|
||||
}
|
||||
|
||||
func EncryptSM4CFBNoBlock(origData, key, iv []byte) ([]byte, error) {
|
||||
return EncryptSM4(origData, key, iv, MODECFB, "")
|
||||
}
|
||||
|
||||
func DecryptSM4CFBNoBlock(encrypted, key, iv []byte) ([]byte, error) {
|
||||
return DecryptSM4(encrypted, key, iv, MODECFB, "")
|
||||
}
|
||||
|
||||
func EncryptSM4ECB(data, key []byte, paddingType string) ([]byte, error) {
|
||||
return EncryptSM4(data, key, nil, MODEECB, paddingType)
|
||||
}
|
||||
|
||||
func DecryptSM4ECB(src, key []byte, paddingType string) ([]byte, error) {
|
||||
return DecryptSM4(src, key, nil, MODEECB, paddingType)
|
||||
}
|
||||
|
||||
func EncryptSM4CBC(data, key, iv []byte, paddingType string) ([]byte, error) {
|
||||
return EncryptSM4(data, key, iv, MODECBC, paddingType)
|
||||
}
|
||||
|
||||
func DecryptSM4CBC(src, key, iv []byte, paddingType string) ([]byte, error) {
|
||||
return DecryptSM4(src, key, iv, MODECBC, paddingType)
|
||||
}
|
||||
|
||||
func EncryptSM4OFB(data, key, iv []byte) ([]byte, error) {
|
||||
return EncryptSM4(data, key, iv, MODEOFB, "")
|
||||
}
|
||||
|
||||
func DecryptSM4OFB(src, key, iv []byte) ([]byte, error) {
|
||||
return DecryptSM4(src, key, iv, MODEOFB, "")
|
||||
}
|
||||
|
||||
func EncryptSM4CTR(data, key, iv []byte) ([]byte, error) {
|
||||
return EncryptSM4(data, key, iv, MODECTR, "")
|
||||
}
|
||||
|
||||
func DecryptSM4CTR(src, key, iv []byte) ([]byte, error) {
|
||||
return DecryptSM4(src, key, iv, MODECTR, "")
|
||||
}
|
||||
|
||||
func EncryptSM4ECBStream(dst io.Writer, src io.Reader, key []byte, paddingType string) error {
|
||||
return EncryptSM4Stream(dst, src, key, nil, MODEECB, paddingType)
|
||||
}
|
||||
|
||||
func DecryptSM4ECBStream(dst io.Writer, src io.Reader, key []byte, paddingType string) error {
|
||||
return DecryptSM4Stream(dst, src, key, nil, MODEECB, paddingType)
|
||||
}
|
||||
|
||||
func EncryptSM4CBCStream(dst io.Writer, src io.Reader, key, iv []byte, paddingType string) error {
|
||||
return EncryptSM4Stream(dst, src, key, iv, MODECBC, paddingType)
|
||||
}
|
||||
|
||||
func DecryptSM4CBCStream(dst io.Writer, src io.Reader, key, iv []byte, paddingType string) error {
|
||||
return DecryptSM4Stream(dst, src, key, iv, MODECBC, paddingType)
|
||||
}
|
||||
|
||||
func EncryptSM4CFBStream(dst io.Writer, src io.Reader, key, iv []byte) error {
|
||||
return EncryptSM4Stream(dst, src, key, iv, MODECFB, "")
|
||||
}
|
||||
|
||||
func DecryptSM4CFBStream(dst io.Writer, src io.Reader, key, iv []byte) error {
|
||||
return DecryptSM4Stream(dst, src, key, iv, MODECFB, "")
|
||||
}
|
||||
|
||||
func EncryptSM4OFBStream(dst io.Writer, src io.Reader, key, iv []byte) error {
|
||||
return EncryptSM4Stream(dst, src, key, iv, MODEOFB, "")
|
||||
}
|
||||
|
||||
func DecryptSM4OFBStream(dst io.Writer, src io.Reader, key, iv []byte) error {
|
||||
return DecryptSM4Stream(dst, src, key, iv, MODEOFB, "")
|
||||
}
|
||||
|
||||
func EncryptSM4CTRStream(dst io.Writer, src io.Reader, key, iv []byte) error {
|
||||
return EncryptSM4Stream(dst, src, key, iv, MODECTR, "")
|
||||
}
|
||||
|
||||
func DecryptSM4CTRStream(dst io.Writer, src io.Reader, key, iv []byte) error {
|
||||
return DecryptSM4Stream(dst, src, key, iv, MODECTR, "")
|
||||
}
|
||||
@@ -0,0 +1,608 @@
|
||||
package symm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEncryptAesDefaultModeCBC(t *testing.T) {
|
||||
key := []byte("0123456789abcdef")
|
||||
iv := []byte("abcdef9876543210")
|
||||
plain := []byte("aes-default-mode-cbc")
|
||||
|
||||
encDefault, err := EncryptAes(plain, key, iv, "", "")
|
||||
if err != nil {
|
||||
t.Fatalf("EncryptAes default failed: %v", err)
|
||||
}
|
||||
encCBC, err := EncryptAesCBC(plain, key, iv, "")
|
||||
if err != nil {
|
||||
t.Fatalf("EncryptAesCBC failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(encDefault, encCBC) {
|
||||
t.Fatalf("default mode should match CBC mode")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAESCBCRoundTripDefaultPKCS7(t *testing.T) {
|
||||
key := []byte("0123456789abcdef")
|
||||
iv := []byte("abcdef9876543210")
|
||||
plain := []byte("aes-cbc-with-default-padding")
|
||||
|
||||
enc, err := EncryptAesCBC(plain, key, iv, "")
|
||||
if err != nil {
|
||||
t.Fatalf("EncryptAesCBC failed: %v", err)
|
||||
}
|
||||
dec, err := DecryptAesCBC(enc, key, iv, "")
|
||||
if err != nil {
|
||||
t.Fatalf("DecryptAesCBC failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(dec, plain) {
|
||||
t.Fatalf("aes cbc mismatch, got %q want %q", dec, plain)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAESCFBRoundTrip(t *testing.T) {
|
||||
key := []byte("0123456789abcdef")
|
||||
plain := []byte("aes-cfb-roundtrip")
|
||||
enc, err := CustomEncryptAesCFB(plain, key)
|
||||
if err != nil {
|
||||
t.Fatalf("CustomEncryptAesCFB failed: %v", err)
|
||||
}
|
||||
dec, err := CustomDecryptAesCFB(enc, key)
|
||||
if err != nil {
|
||||
t.Fatalf("CustomDecryptAesCFB failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(dec, plain) {
|
||||
t.Fatalf("aes cfb mismatch, got %q want %q", dec, plain)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAesGenericModesRoundTrip(t *testing.T) {
|
||||
key := []byte("0123456789abcdef")
|
||||
iv := []byte("abcdef9876543210")
|
||||
plain := []byte("generic-aes-mode-roundtrip")
|
||||
|
||||
modes := []string{MODEECB, MODECBC, MODECFB, MODEOFB, MODECTR}
|
||||
for _, mode := range modes {
|
||||
t.Run(mode, func(t *testing.T) {
|
||||
useIV := iv
|
||||
if mode == MODEECB {
|
||||
useIV = nil
|
||||
}
|
||||
enc, err := EncryptAes(plain, key, useIV, mode, "")
|
||||
if err != nil {
|
||||
t.Fatalf("EncryptAes(%s) failed: %v", mode, err)
|
||||
}
|
||||
dec, err := DecryptAes(enc, key, useIV, mode, "")
|
||||
if err != nil {
|
||||
t.Fatalf("DecryptAes(%s) failed: %v", mode, err)
|
||||
}
|
||||
if !bytes.Equal(dec, plain) {
|
||||
t.Fatalf("aes %s mismatch", mode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAesDerivedFunctionsRoundTrip(t *testing.T) {
|
||||
key := []byte("0123456789abcdef")
|
||||
iv := []byte("abcdef9876543210")
|
||||
plain := []byte("aes-derived-func-roundtrip")
|
||||
|
||||
ecbEnc, err := EncryptAesECB(plain, key, "")
|
||||
if err != nil {
|
||||
t.Fatalf("EncryptAesECB failed: %v", err)
|
||||
}
|
||||
ecbDec, err := DecryptAesECB(ecbEnc, key, "")
|
||||
if err != nil {
|
||||
t.Fatalf("DecryptAesECB failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(ecbDec, plain) {
|
||||
t.Fatalf("aes ecb mismatch")
|
||||
}
|
||||
|
||||
cfbEnc, err := EncryptAesCFB(plain, key, iv)
|
||||
if err != nil {
|
||||
t.Fatalf("EncryptAesCFB failed: %v", err)
|
||||
}
|
||||
cfbDec, err := DecryptAesCFB(cfbEnc, key, iv)
|
||||
if err != nil {
|
||||
t.Fatalf("DecryptAesCFB failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(cfbDec, plain) {
|
||||
t.Fatalf("aes cfb mismatch")
|
||||
}
|
||||
|
||||
ofbEnc, err := EncryptAesOFB(plain, key, iv)
|
||||
if err != nil {
|
||||
t.Fatalf("EncryptAesOFB failed: %v", err)
|
||||
}
|
||||
ofbDec, err := DecryptAesOFB(ofbEnc, key, iv)
|
||||
if err != nil {
|
||||
t.Fatalf("DecryptAesOFB failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(ofbDec, plain) {
|
||||
t.Fatalf("aes ofb mismatch")
|
||||
}
|
||||
|
||||
ctrEnc, err := EncryptAesCTR(plain, key, iv)
|
||||
if err != nil {
|
||||
t.Fatalf("EncryptAesCTR failed: %v", err)
|
||||
}
|
||||
ctrDec, err := DecryptAesCTR(ctrEnc, key, iv)
|
||||
if err != nil {
|
||||
t.Fatalf("DecryptAesCTR failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(ctrDec, plain) {
|
||||
t.Fatalf("aes ctr mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAesStreamRoundTrip(t *testing.T) {
|
||||
key := []byte("0123456789abcdef")
|
||||
iv := []byte("abcdef9876543210")
|
||||
plain := []byte("streaming-aes-mode-roundtrip-content")
|
||||
|
||||
modes := []string{MODEECB, MODECBC, MODECFB, MODEOFB, MODECTR}
|
||||
for _, mode := range modes {
|
||||
t.Run(mode, func(t *testing.T) {
|
||||
encBuf := &bytes.Buffer{}
|
||||
decBuf := &bytes.Buffer{}
|
||||
useIV := iv
|
||||
if mode == MODEECB {
|
||||
useIV = nil
|
||||
}
|
||||
if err := EncryptAesStream(encBuf, bytes.NewReader(plain), key, useIV, mode, ""); err != nil {
|
||||
t.Fatalf("EncryptAesStream(%s) failed: %v", mode, err)
|
||||
}
|
||||
if err := DecryptAesStream(decBuf, bytes.NewReader(encBuf.Bytes()), key, useIV, mode, ""); err != nil {
|
||||
t.Fatalf("DecryptAesStream(%s) failed: %v", mode, err)
|
||||
}
|
||||
if !bytes.Equal(decBuf.Bytes(), plain) {
|
||||
t.Fatalf("aes stream %s mismatch", mode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAesStreamInvalidMode(t *testing.T) {
|
||||
key := []byte("0123456789abcdef")
|
||||
iv := []byte("abcdef9876543210")
|
||||
err := EncryptAesStream(&bytes.Buffer{}, bytes.NewReader([]byte("x")), key, iv, "BAD", "")
|
||||
if err == nil {
|
||||
t.Fatalf("expected invalid mode error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSM4CBCRoundTripDefaultPKCS7(t *testing.T) {
|
||||
key := []byte("0123456789abcdef")
|
||||
iv := []byte("abcdef9876543210")
|
||||
plain := []byte("sm4-cbc-with-default-padding")
|
||||
|
||||
enc, err := EncryptSM4CBC(plain, key, iv, "")
|
||||
if err != nil {
|
||||
t.Fatalf("EncryptSM4CBC failed: %v", err)
|
||||
}
|
||||
dec, err := DecryptSM4CBC(enc, key, iv, "")
|
||||
if err != nil {
|
||||
t.Fatalf("DecryptSM4CBC failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(dec, plain) {
|
||||
t.Fatalf("sm4 cbc mismatch, got %q want %q", dec, plain)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSM4CFBRoundTrip(t *testing.T) {
|
||||
key := []byte("0123456789abcdef")
|
||||
plain := []byte("sm4-cfb-roundtrip")
|
||||
enc, err := EncryptSM4CFB(plain, key)
|
||||
if err != nil {
|
||||
t.Fatalf("EncryptSM4CFB failed: %v", err)
|
||||
}
|
||||
dec, err := DecryptSM4CFB(enc, key)
|
||||
if err != nil {
|
||||
t.Fatalf("DecryptSM4CFB failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(dec, plain) {
|
||||
t.Fatalf("sm4 cfb mismatch, got %q want %q", dec, plain)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSM4StreamRoundTrip(t *testing.T) {
|
||||
key := []byte("0123456789abcdef")
|
||||
iv := []byte("abcdef9876543210")
|
||||
plain := []byte("sm4-stream-roundtrip-data")
|
||||
|
||||
encCBC := &bytes.Buffer{}
|
||||
if err := EncryptSM4CBCStream(encCBC, bytes.NewReader(plain), key, iv, ""); err != nil {
|
||||
t.Fatalf("EncryptSM4CBCStream failed: %v", err)
|
||||
}
|
||||
decCBC := &bytes.Buffer{}
|
||||
if err := DecryptSM4CBCStream(decCBC, bytes.NewReader(encCBC.Bytes()), key, iv, ""); err != nil {
|
||||
t.Fatalf("DecryptSM4CBCStream failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(decCBC.Bytes(), plain) {
|
||||
t.Fatalf("sm4 cbc stream mismatch")
|
||||
}
|
||||
|
||||
encCFB := &bytes.Buffer{}
|
||||
if err := EncryptSM4CFBStream(encCFB, bytes.NewReader(plain), key, iv); err != nil {
|
||||
t.Fatalf("EncryptSM4CFBStream failed: %v", err)
|
||||
}
|
||||
decCFB := &bytes.Buffer{}
|
||||
if err := DecryptSM4CFBStream(decCFB, bytes.NewReader(encCFB.Bytes()), key, iv); err != nil {
|
||||
t.Fatalf("DecryptSM4CFBStream failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(decCFB.Bytes(), plain) {
|
||||
t.Fatalf("sm4 cfb stream mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDESCBCRoundTripDefaultPKCS5(t *testing.T) {
|
||||
key := []byte("12345678")
|
||||
iv := []byte("abcdefgh")
|
||||
plain := []byte("des-cbc")
|
||||
|
||||
enc, err := EncryptDESCBC(plain, key, iv, "")
|
||||
if err != nil {
|
||||
t.Fatalf("EncryptDESCBC failed: %v", err)
|
||||
}
|
||||
dec, err := DecryptDESCBC(enc, key, iv, "")
|
||||
if err != nil {
|
||||
t.Fatalf("DecryptDESCBC failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(dec, plain) {
|
||||
t.Fatalf("des cbc mismatch, got %q want %q", dec, plain)
|
||||
}
|
||||
}
|
||||
|
||||
func Test3DESCBCRoundTripDefaultPKCS5(t *testing.T) {
|
||||
key := []byte("12345678abcdefgh87654321")
|
||||
iv := []byte("12345678")
|
||||
plain := []byte("3des-cbc-default-padding")
|
||||
|
||||
enc, err := Encrypt3DESCBC(plain, key, iv, "")
|
||||
if err != nil {
|
||||
t.Fatalf("Encrypt3DESCBC failed: %v", err)
|
||||
}
|
||||
dec, err := Decrypt3DESCBC(enc, key, iv, "")
|
||||
if err != nil {
|
||||
t.Fatalf("Decrypt3DESCBC failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(dec, plain) {
|
||||
t.Fatalf("3des cbc mismatch, got %q want %q", dec, plain)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDESStreamRoundTrip(t *testing.T) {
|
||||
desKey := []byte("12345678")
|
||||
desIV := []byte("abcdefgh")
|
||||
desPlain := []byte("des-stream-roundtrip")
|
||||
|
||||
desEnc := &bytes.Buffer{}
|
||||
if err := EncryptDESCBCStream(desEnc, bytes.NewReader(desPlain), desKey, desIV, ""); err != nil {
|
||||
t.Fatalf("EncryptDESCBCStream failed: %v", err)
|
||||
}
|
||||
desDec := &bytes.Buffer{}
|
||||
if err := DecryptDESCBCStream(desDec, bytes.NewReader(desEnc.Bytes()), desKey, desIV, ""); err != nil {
|
||||
t.Fatalf("DecryptDESCBCStream failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(desDec.Bytes(), desPlain) {
|
||||
t.Fatalf("des cbc stream mismatch")
|
||||
}
|
||||
|
||||
key3des := []byte("12345678abcdefgh87654321")
|
||||
iv3des := []byte("12345678")
|
||||
plain3des := []byte("3des-stream-roundtrip")
|
||||
|
||||
enc3des := &bytes.Buffer{}
|
||||
if err := Encrypt3DESCBCStream(enc3des, bytes.NewReader(plain3des), key3des, iv3des, ""); err != nil {
|
||||
t.Fatalf("Encrypt3DESCBCStream failed: %v", err)
|
||||
}
|
||||
dec3des := &bytes.Buffer{}
|
||||
if err := Decrypt3DESCBCStream(dec3des, bytes.NewReader(enc3des.Bytes()), key3des, iv3des, ""); err != nil {
|
||||
t.Fatalf("Decrypt3DESCBCStream failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(dec3des.Bytes(), plain3des) {
|
||||
t.Fatalf("3des cbc stream mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCBCInvalidIVLength(t *testing.T) {
|
||||
_, err := EncryptAesCBC([]byte("a"), []byte("0123456789abcdef"), []byte("short"), PKCS7PADDING)
|
||||
if err == nil {
|
||||
t.Fatalf("expected invalid IV length error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCBCInvalidCiphertextLength(t *testing.T) {
|
||||
_, err := DecryptSM4CBC([]byte("short"), []byte("0123456789abcdef"), []byte("abcdef9876543210"), PKCS7PADDING)
|
||||
if err == nil {
|
||||
t.Fatalf("expected invalid ciphertext length error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCBCStreamInvalidCiphertextLength(t *testing.T) {
|
||||
err := DecryptAesCBCStream(&bytes.Buffer{}, bytes.NewReader([]byte("short")), []byte("0123456789abcdef"), []byte("abcdef9876543210"), PKCS7PADDING)
|
||||
if err == nil {
|
||||
t.Fatalf("expected invalid ciphertext length error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSM4DerivedModesRoundTrip(t *testing.T) {
|
||||
key := []byte("0123456789abcdef")
|
||||
iv := []byte("abcdef9876543210")
|
||||
plain := []byte("sm4-derived-mode-roundtrip")
|
||||
|
||||
ecbEnc, err := EncryptSM4ECB(plain, key, "")
|
||||
if err != nil {
|
||||
t.Fatalf("EncryptSM4ECB failed: %v", err)
|
||||
}
|
||||
ecbDec, err := DecryptSM4ECB(ecbEnc, key, "")
|
||||
if err != nil {
|
||||
t.Fatalf("DecryptSM4ECB failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(ecbDec, plain) {
|
||||
t.Fatalf("sm4 ecb mismatch")
|
||||
}
|
||||
|
||||
ofbEnc, err := EncryptSM4OFB(plain, key, iv)
|
||||
if err != nil {
|
||||
t.Fatalf("EncryptSM4OFB failed: %v", err)
|
||||
}
|
||||
ofbDec, err := DecryptSM4OFB(ofbEnc, key, iv)
|
||||
if err != nil {
|
||||
t.Fatalf("DecryptSM4OFB failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(ofbDec, plain) {
|
||||
t.Fatalf("sm4 ofb mismatch")
|
||||
}
|
||||
|
||||
ctrEnc, err := EncryptSM4CTR(plain, key, iv)
|
||||
if err != nil {
|
||||
t.Fatalf("EncryptSM4CTR failed: %v", err)
|
||||
}
|
||||
ctrDec, err := DecryptSM4CTR(ctrEnc, key, iv)
|
||||
if err != nil {
|
||||
t.Fatalf("DecryptSM4CTR failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(ctrDec, plain) {
|
||||
t.Fatalf("sm4 ctr mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSM4DerivedStreamRoundTrip(t *testing.T) {
|
||||
key := []byte("0123456789abcdef")
|
||||
iv := []byte("abcdef9876543210")
|
||||
plain := []byte("sm4-derived-stream-roundtrip")
|
||||
|
||||
ecbEnc := &bytes.Buffer{}
|
||||
if err := EncryptSM4ECBStream(ecbEnc, bytes.NewReader(plain), key, ""); err != nil {
|
||||
t.Fatalf("EncryptSM4ECBStream failed: %v", err)
|
||||
}
|
||||
ecbDec := &bytes.Buffer{}
|
||||
if err := DecryptSM4ECBStream(ecbDec, bytes.NewReader(ecbEnc.Bytes()), key, ""); err != nil {
|
||||
t.Fatalf("DecryptSM4ECBStream failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(ecbDec.Bytes(), plain) {
|
||||
t.Fatalf("sm4 ecb stream mismatch")
|
||||
}
|
||||
|
||||
ofbEnc := &bytes.Buffer{}
|
||||
if err := EncryptSM4OFBStream(ofbEnc, bytes.NewReader(plain), key, iv); err != nil {
|
||||
t.Fatalf("EncryptSM4OFBStream failed: %v", err)
|
||||
}
|
||||
ofbDec := &bytes.Buffer{}
|
||||
if err := DecryptSM4OFBStream(ofbDec, bytes.NewReader(ofbEnc.Bytes()), key, iv); err != nil {
|
||||
t.Fatalf("DecryptSM4OFBStream failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(ofbDec.Bytes(), plain) {
|
||||
t.Fatalf("sm4 ofb stream mismatch")
|
||||
}
|
||||
|
||||
ctrEnc := &bytes.Buffer{}
|
||||
if err := EncryptSM4CTRStream(ctrEnc, bytes.NewReader(plain), key, iv); err != nil {
|
||||
t.Fatalf("EncryptSM4CTRStream failed: %v", err)
|
||||
}
|
||||
ctrDec := &bytes.Buffer{}
|
||||
if err := DecryptSM4CTRStream(ctrDec, bytes.NewReader(ctrEnc.Bytes()), key, iv); err != nil {
|
||||
t.Fatalf("DecryptSM4CTRStream failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(ctrDec.Bytes(), plain) {
|
||||
t.Fatalf("sm4 ctr stream mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChaCha20RoundTrip(t *testing.T) {
|
||||
key := []byte("0123456789abcdef0123456789abcdef")
|
||||
nonce := []byte("123456789012")
|
||||
plain := []byte("chacha20-roundtrip")
|
||||
|
||||
enc, err := EncryptChaCha20(plain, key, nonce)
|
||||
if err != nil {
|
||||
t.Fatalf("EncryptChaCha20 failed: %v", err)
|
||||
}
|
||||
dec, err := DecryptChaCha20(enc, key, nonce)
|
||||
if err != nil {
|
||||
t.Fatalf("DecryptChaCha20 failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(dec, plain) {
|
||||
t.Fatalf("chacha20 mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChaCha20StreamRoundTrip(t *testing.T) {
|
||||
key := []byte("0123456789abcdef0123456789abcdef")
|
||||
nonce := []byte("123456789012")
|
||||
plain := []byte("chacha20-stream-roundtrip")
|
||||
|
||||
enc := &bytes.Buffer{}
|
||||
if err := EncryptChaCha20Stream(enc, bytes.NewReader(plain), key, nonce); err != nil {
|
||||
t.Fatalf("EncryptChaCha20Stream failed: %v", err)
|
||||
}
|
||||
dec := &bytes.Buffer{}
|
||||
if err := DecryptChaCha20Stream(dec, bytes.NewReader(enc.Bytes()), key, nonce); err != nil {
|
||||
t.Fatalf("DecryptChaCha20Stream failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(dec.Bytes(), plain) {
|
||||
t.Fatalf("chacha20 stream mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChaCha20Poly1305RoundTrip(t *testing.T) {
|
||||
key := []byte("0123456789abcdef0123456789abcdef")
|
||||
nonce := []byte("123456789012")
|
||||
aad := []byte("aad")
|
||||
plain := []byte("chacha20-poly1305-roundtrip")
|
||||
|
||||
enc, err := EncryptChaCha20Poly1305(plain, key, nonce, aad)
|
||||
if err != nil {
|
||||
t.Fatalf("EncryptChaCha20Poly1305 failed: %v", err)
|
||||
}
|
||||
dec, err := DecryptChaCha20Poly1305(enc, key, nonce, aad)
|
||||
if err != nil {
|
||||
t.Fatalf("DecryptChaCha20Poly1305 failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(dec, plain) {
|
||||
t.Fatalf("chacha20-poly1305 mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChaCha20Poly1305InvalidNonce(t *testing.T) {
|
||||
key := []byte("0123456789abcdef0123456789abcdef")
|
||||
_, err := EncryptChaCha20Poly1305([]byte("x"), key, []byte("short"), nil)
|
||||
if err == nil {
|
||||
t.Fatalf("expected invalid nonce error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAESGCMNISTVectorEmpty(t *testing.T) {
|
||||
key := mustHex(t, "00000000000000000000000000000000")
|
||||
nonce := mustHex(t, "000000000000000000000000")
|
||||
enc, err := EncryptAesGCM(nil, key, nonce, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("EncryptAesGCM failed: %v", err)
|
||||
}
|
||||
want := mustHex(t, "58e2fccefa7e3061367f1d57a4e7455a")
|
||||
if !bytes.Equal(enc, want) {
|
||||
t.Fatalf("AES-GCM empty vector mismatch: got %x want %x", enc, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAESGCMNISTVectorOneBlock(t *testing.T) {
|
||||
key := mustHex(t, "00000000000000000000000000000000")
|
||||
nonce := mustHex(t, "000000000000000000000000")
|
||||
plain := mustHex(t, "00000000000000000000000000000000")
|
||||
enc, err := EncryptAesGCM(plain, key, nonce, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("EncryptAesGCM failed: %v", err)
|
||||
}
|
||||
want := mustHex(t, "0388dace60b6a392f328c2b971b2fe78ab6e47d42cec13bdf53a67b21257bddf")
|
||||
if !bytes.Equal(enc, want) {
|
||||
t.Fatalf("AES-GCM one-block vector mismatch: got %x want %x", enc, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSM4ECBStandardVector(t *testing.T) {
|
||||
key := mustHex(t, "0123456789abcdeffedcba9876543210")
|
||||
plain := mustHex(t, "0123456789abcdeffedcba9876543210")
|
||||
enc, err := EncryptSM4ECB(plain, key, ZEROPADDING)
|
||||
if err != nil {
|
||||
t.Fatalf("EncryptSM4ECB failed: %v", err)
|
||||
}
|
||||
want := mustHex(t, "681edf34d206965e86b3e94f536e4246")
|
||||
if !bytes.Equal(enc, want) {
|
||||
t.Fatalf("SM4 ECB vector mismatch: got %x want %x", enc, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChaCha20Poly1305RFCVector(t *testing.T) {
|
||||
key := mustHex(t, "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f")
|
||||
nonce := mustHex(t, "070000004041424344454647")
|
||||
enc, err := EncryptChaCha20Poly1305(nil, key, nonce, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("EncryptChaCha20Poly1305 failed: %v", err)
|
||||
}
|
||||
want := mustHex(t, "a0784d7a4716f3feb4f64e7f4b39bf04")
|
||||
if !bytes.Equal(enc, want) {
|
||||
t.Fatalf("ChaCha20-Poly1305 vector mismatch: got %x want %x", enc, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAesOptionsDefaultToGCM(t *testing.T) {
|
||||
key := []byte("0123456789abcdef")
|
||||
nonce := []byte("123456789012")
|
||||
plain := []byte("aes-options-default-gcm")
|
||||
|
||||
enc, err := EncryptAesWithOptions(plain, key, &CipherOptions{Nonce: nonce})
|
||||
if err != nil {
|
||||
t.Fatalf("EncryptAesWithOptions failed: %v", err)
|
||||
}
|
||||
dec, err := DecryptAesWithOptions(enc, key, &CipherOptions{Nonce: nonce})
|
||||
if err != nil {
|
||||
t.Fatalf("DecryptAesWithOptions failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(dec, plain) {
|
||||
t.Fatalf("aes options default gcm mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSM4OptionsDefaultToGCM(t *testing.T) {
|
||||
key := []byte("0123456789abcdef")
|
||||
nonce := []byte("123456789012")
|
||||
plain := []byte("sm4-options-default-gcm")
|
||||
|
||||
enc, err := EncryptSM4WithOptions(plain, key, &CipherOptions{Nonce: nonce})
|
||||
if err != nil {
|
||||
t.Fatalf("EncryptSM4WithOptions failed: %v", err)
|
||||
}
|
||||
dec, err := DecryptSM4WithOptions(enc, key, &CipherOptions{Nonce: nonce})
|
||||
if err != nil {
|
||||
t.Fatalf("DecryptSM4WithOptions failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(dec, plain) {
|
||||
t.Fatalf("sm4 options default gcm mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLargeStreamRoundTrip(t *testing.T) {
|
||||
large := bytes.Repeat([]byte("starcrypto-large-stream-data-0123456789"), 180000)
|
||||
|
||||
aesKey := []byte("0123456789abcdef")
|
||||
aesIV := []byte("abcdef9876543210")
|
||||
aesEnc := &bytes.Buffer{}
|
||||
if err := EncryptAesCBCStream(aesEnc, bytes.NewReader(large), aesKey, aesIV, ""); err != nil {
|
||||
t.Fatalf("EncryptAesCBCStream large failed: %v", err)
|
||||
}
|
||||
aesDec := &bytes.Buffer{}
|
||||
if err := DecryptAesCBCStream(aesDec, bytes.NewReader(aesEnc.Bytes()), aesKey, aesIV, ""); err != nil {
|
||||
t.Fatalf("DecryptAesCBCStream large failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(aesDec.Bytes(), large) {
|
||||
t.Fatalf("aes large stream mismatch")
|
||||
}
|
||||
|
||||
chachaKey := []byte("0123456789abcdef0123456789abcdef")
|
||||
chachaNonce := []byte("123456789012")
|
||||
chachaEnc := &bytes.Buffer{}
|
||||
if err := EncryptChaCha20Stream(chachaEnc, bytes.NewReader(large), chachaKey, chachaNonce); err != nil {
|
||||
t.Fatalf("EncryptChaCha20Stream large failed: %v", err)
|
||||
}
|
||||
chachaDec := &bytes.Buffer{}
|
||||
if err := DecryptChaCha20Stream(chachaDec, bytes.NewReader(chachaEnc.Bytes()), chachaKey, chachaNonce); err != nil {
|
||||
t.Fatalf("DecryptChaCha20Stream large failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(chachaDec.Bytes(), large) {
|
||||
t.Fatalf("chacha20 large stream mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func mustHex(t *testing.T, s string) []byte {
|
||||
t.Helper()
|
||||
b, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
t.Fatalf("DecodeString failed: %v", err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
Reference in New Issue
Block a user