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 }