Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

crypto/cipher: AES encrypted cipher text in CFB mode is incorrect #30201

Closed
lybox opened this issue Feb 13, 2019 · 9 comments
Closed

crypto/cipher: AES encrypted cipher text in CFB mode is incorrect #30201

lybox opened this issue Feb 13, 2019 · 9 comments
Labels
FrozenDueToAge NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. WaitingForInfo Issue is not actionable because of missing required information, which needs to be provided.

Comments

@lybox
Copy link

lybox commented Feb 13, 2019

go1.11
AES encrypted cipher text in CFB mode is incorrect
The ciphertext results are incorrect and cannot be decrypted using other tools.
package main

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"encoding/hex"
	"fmt"
)

func main() {
	var key = "1234567887654321"
	var vector = "8765432112345678"
	var info = "AES in CFB mode"

	Enc_str := EncryptAES_CFB(info, key, vector)
	fmt.Println(Enc_str)
	Dec_str := DecryptAES_CFB(Enc_str, key, vector)
	fmt.Println(Dec_str)
}

func EncryptAES_CFB(src, key, vector string) string {
	data := []byte(src)
	iv := []byte(vector)
	keyByte := []byte(key)
	block, err := aes.NewCipher(keyByte)
	if err != nil {
		panic(err)
	}
	data = PKCS5Padding(data, block.BlockSize())
	ciphertext := make([]byte, len(data))
	mode := cipher.NewCFBEncrypter(block, iv)
	mode.XORKeyStream(ciphertext, data)
	return fmt.Sprintf("%X", ciphertext)
}

func DecryptAES_CFB(src, key, vector string) string {
	keyByte := []byte(key)
	iv := []byte(vector)
	data, err := hex.DecodeString(src)
	if err != nil {
		panic(err)
	}
	block, err := aes.NewCipher(keyByte)
	if err != nil {
		panic(err)
	}
	plaintext := make([]byte, len(data))
	mode := cipher.NewCFBDecrypter(block, iv)
	mode.XORKeyStream(plaintext, data)
	plaintext = PKCS5UnPadding(plaintext)
	return string(plaintext)
}

func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, padtext...)
}
func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}
@agnivade
Copy link
Contributor

/cc @FiloSottile

@FiloSottile FiloSottile changed the title AES encrypted cipher text in CFB mode is incorrect crypto/cipher: AES encrypted cipher text in CFB mode is incorrect Feb 13, 2019
@FiloSottile
Copy link
Contributor

Can you provide some details on the other tools you compared against? An example showing a failure would be great.

@agnivade agnivade added NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. WaitingForInfo Issue is not actionable because of missing required information, which needs to be provided. labels Feb 13, 2019
@as
Copy link
Contributor

as commented Feb 13, 2019

CFB is a stream cipher, the error is almost certainly in your padding function, which is not supported for AES block sizes.

padding := blockSize - len(ciphertext)%blockSize

PKCS5 padding does not support arbitrary block sizes. It only supports size 8.

I have compared the CFB output to a clean re-implementation using a block diagram along with ecb mode: half of the code can be found here: https://play.golang.org/p/hn422wOs6NI The output of the stream is identical to what one would get with stdlib CFB.

See rfc2898 for more details.

@lybox
Copy link
Author

lybox commented Feb 13, 2019

@FiloSottile
I am trying this website
http://www.ssleye.com/

@lybox
Copy link
Author

lybox commented Feb 13, 2019

Because the result of running in CFB and OFB mode is the same, I think it is a bug.

package main

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"encoding/hex"
	"fmt"
)

func main() {
	var key = "1234567887654321"
	var vector = "8765432112345678"
	var info = "AES in CFB mode"

	Enc_str := EncryptAES_OFB(info, key, vector)
	fmt.Println(Enc_str)
	Dec_str := DecryptAES_OFB(Enc_str, key, vector)
	fmt.Println(Dec_str)
}
func EncryptAES_OFB(src, key, vector string) string {
	data := []byte(src)
	iv := []byte(vector)
	keyByte := []byte(key)
	block, err := aes.NewCipher(keyByte)
	if err != nil {
		panic(err)
	}
	data = PKCS5Padding(data, block.BlockSize())
	ciphertext := make([]byte, len(data))
	mode := cipher.NewOFB(block, iv)
	mode.XORKeyStream(ciphertext, data)
	return fmt.Sprintf("%X", ciphertext)
}
func DecryptAES_OFB(src, key, vector string) string {
	keyByte := []byte(key)
	iv := []byte(vector)
	data, err := hex.DecodeString(src)
	if err != nil {
		panic(err)
	}
	block, err := aes.NewCipher(keyByte)
	if err != nil {
		panic(err)
	}
	plaintext := make([]byte, len(data))
	mode := cipher.NewOFB(block, iv)
	mode.XORKeyStream(plaintext, data)
	plaintext = PKCS5UnPadding(plaintext)
	return string(plaintext)
}
func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, padtext...)
}
func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}

@as
Copy link
Contributor

as commented Feb 13, 2019

That's because it's based on the first block, and the operations on that block result in identical output from both modes. Try it with a bigger plaintext.

@lybox
Copy link
Author

lybox commented Feb 13, 2019

@as
The same reason is really as you said, but the results still can not be decoded on other tools.
http://www.txtwizard.net/crypto
http://www.ssleye.com/aes_cipher.html
http://tool.chacuo.net/cryptaes

@as
Copy link
Contributor

as commented Feb 13, 2019

OpenSSL agrees with Go: https://play.golang.org/p/q0R-wgj2BcQ

I think you are encountering encoding or implementation issues in your web tools. They do not pass FIPS test vectors and their input format is ambiguous (is it hex? Base64?).

Because openSSL outputs the same ciphertext as Go, I think that this is not a Go issue.

@lybox
Copy link
Author

lybox commented Feb 14, 2019

@as
Maybe you are right, thank you very much for your answers.

@lybox lybox closed this as completed Feb 14, 2019
@golang golang locked and limited conversation to collaborators Feb 14, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. WaitingForInfo Issue is not actionable because of missing required information, which needs to be provided.
Projects
None yet
Development

No branches or pull requests

5 participants