Skip to content

AES CFB can't decrypt when raw string's length more than 48 #29

@honmaple

Description

@honmaple

Hello,

I encountered an issue with the following code:

package main

import (
	"bytes"
	"crypto/aes"
	stdcipher "crypto/cipher"
	"encoding/base64"
	"fmt"
	"strings"

	"github.com/dromara/dongle"
	"github.com/dromara/dongle/crypto/cipher"
)

func main() {
	key := []byte("dongle1234567890")
	iv := []byte("1234567890123456")
	c := cipher.NewAesCipher(cipher.CFB)
	c.SetKey(key)
	c.SetIV(iv)
	c.SetPadding(cipher.PKCS7)

	text := strings.Repeat("1", 48)
	ciphertext := dongle.Encrypt.FromString(text).ByAes(c).ToBase64String()
	plaintext := dongle.Decrypt.FromBase64String(ciphertext).ByAes(c).ToString()
	fmt.Println(plaintext == text)

	text = strings.Repeat("1", 49)
	ciphertext = dongle.Encrypt.FromString(text).ByAes(c).ToBase64String()
	plaintext = dongle.Decrypt.FromBase64String(ciphertext).ByAes(c).ToString()
	fmt.Println(plaintext == text)

	fmt.Println("---")

	text = strings.Repeat("1", 48)
	ciphertext = stdEncrypt(key, iv, text)
	plaintext = stdDecrypt(key, iv, ciphertext)
	fmt.Println(plaintext == text)

	text = strings.Repeat("1", 49)
	ciphertext = stdEncrypt(key, iv, text)
	plaintext = stdDecrypt(key, iv, ciphertext)
	fmt.Println(plaintext == text)
}

func pkcs7Padding(src []byte, blockSize int) []byte {
	paddingSize := blockSize - len(src)%blockSize
	paddingText := bytes.Repeat([]byte{byte(paddingSize)}, paddingSize)
	return append(src, paddingText...)
}

func pkcs7UnPadding(src []byte) []byte {
	if len(src) == 0 {
		return []byte("")
	}
	n := len(src) - int(src[len(src)-1])
	return src[0:n]
}

func stdEncrypt(key []byte, iv []byte, text string) string {
	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	plaintext := pkcs7Padding([]byte(text), block.BlockSize())

	ciphertext := make([]byte, len(plaintext))

	stream := stdcipher.NewCFBEncrypter(block, iv[:])
	stream.XORKeyStream(ciphertext, plaintext)

	return base64.RawURLEncoding.EncodeToString(ciphertext)
}

func stdDecrypt(key []byte, iv []byte, text string) string {
	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	ciphertext, err := base64.RawURLEncoding.DecodeString(text)
	if err != nil {
		panic(err)
	}

	plaintext := make([]byte, len(ciphertext))

	stream := stdcipher.NewCFBDecrypter(block, iv[:])
	stream.XORKeyStream(plaintext, ciphertext)

	return string(pkcs7UnPadding(plaintext))
}

golang version: go1.24.5 darwin/arm64

dongle version: v1.1.4

I expected to get:

true
true
---
true
true

But I actually get:

true
false
---
true
true

The OFB is same, and by the way, different input would got different results, such as

text = strings.Repeat("0", 48)

The result is

false
false
---
true
true
text = strings.Repeat("2", 48)

The result is

true
true
---
true
true

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions