Go AES CFB 兼容性

huangapple go评论76阅读模式
英文:

Go AES CFB compatibility

问题

我正在使用Go开发一个依赖于AES CFB的客户端应用程序,而服务器端是用C语言编写的。我的问题是,Go的AES CFB实现似乎与许多其他实现(包括OpenSSL)不同。我编写了以下代码来测试我的理论:

package main

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

func encrypt_aes_cfb(plain, key, iv []byte) (encrypted []byte) {
  block, err := aes.NewCipher(key)
  if err != nil {
    panic(err)
  }
  encrypted = make([]byte, len(plain))
  stream := cipher.NewCFBEncrypter(block, iv)
  stream.XORKeyStream(encrypted, plain)
  return
}

func decrypt_aes_cfb(encrypted, key, iv []byte) (plain []byte) {
  block, err := aes.NewCipher(key)
  if err != nil {
    panic(err)
  }
  plain = make([]byte, len(encrypted))
  stream := cipher.NewCFBDecrypter(block, iv)
  stream.XORKeyStream(plain, encrypted)
  return
}

func main() {
  plain := []byte("Hello world.....")
  key := []byte("01234567890123456789012345678901")
  iv := []byte("0123456789012345")
  enc := encrypt_aes_cfb(plain, key, iv)
  dec := decrypt_aes_cfb(enc, key, iv)
  fmt.Println("Key: ", hex.EncodeToString(key))
  fmt.Println("IV:  ", hex.EncodeToString(iv))
  fmt.Println("Enc: ", hex.EncodeToString(enc))
  fmt.Println("In:  ", hex.EncodeToString(plain))
  fmt.Println("Out: ", hex.EncodeToString(dec))
}

当运行这段代码时,它似乎完美地工作,但是,如果将加密后的字节粘贴到另一个AES实现中,并使用相同的密钥和IV进行解密,明文将会损坏(除了第一个字节)。http://aes.online-domain-tools.com/ 提供了一个简单的方法来测试这个问题。有什么建议可以解决这个问题吗?

谢谢
Steve

英文:

I am developing a client-side app in Go that relies on AES CFB. The server-side is written in C. My problem is that Go's AES CFB implementation appears to differ from many others (including OpenSSL). I wrote this to test my theory:-

package main

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

func encrypt_aes_cfb(plain, key, iv []byte) (encrypted []byte) {
  block, err := aes.NewCipher(key)
  if err != nil {
    panic(err)
  }
  encrypted = make([]byte, len(plain))
  stream := cipher.NewCFBEncrypter(block, iv)
  stream.XORKeyStream(encrypted, plain)
  return
}

func decrypt_aes_cfb(encrypted, key, iv []byte) (plain []byte) {
  block, err := aes.NewCipher(key)
  if err != nil {
    panic(err)
  }
  plain = make([]byte, len(encrypted))
  stream := cipher.NewCFBDecrypter(block, iv)
  stream.XORKeyStream(plain, encrypted)
  return
}

func main() {
  plain := []byte("Hello world.....")
  key := []byte("01234567890123456789012345678901")
  iv := []byte("0123456789012345")
  enc := encrypt_aes_cfb(plain, key, iv)
  dec := decrypt_aes_cfb(enc, key, iv)
  fmt.Println("Key: ", hex.EncodeToString(key))
  fmt.Println("IV:  ", hex.EncodeToString(iv))
  fmt.Println("Enc: ", hex.EncodeToString(enc))
  fmt.Println("In:  ", hex.EncodeToString(plain))
  fmt.Println("Out: ", hex.EncodeToString(dec))
}

When this is run, it appears to work perfectly, however, if the encrypted bytes are pasted into another AES implementation and decrypted using the same key and IV, the plaintext is corrupted (except for the first Byte). http://aes.online-domain-tools.com/ provides a simple means to test this. Any suggestions why this might be happening and how I can resolve it?

Thanks
Steve

答案1

得分: 4

首先,一个必要的警告:CFB模式是自行设计的加密算法的标志。除非你正在实现OpenPGP,否则应该使用像AES-GCM或NaCl的secretbox这样的AE模式。如果你被迫使用CFB模式,希望你至少使用HMAC对密文进行认证。

除此之外,Go语言中的CFB模式用于支持OpenPGP。(OpenPGP在不同的地方使用了一个名为OCFB的调整过的CFB模式和标准的CFB模式。)Go的OpenPGP代码似乎至少与其他实现可以互操作。

Nick正确指出Go的crypto包中缺少测试向量。测试是从OpenPGP代码中进行的,但是包应该是独立的,所以我将使用1中的F.3.13节的测试向量为crypto/cipher添加测试。

我对任何差异的来源的最佳猜测是CFB的参数是块大小的一部分。这通常是一个二的幂次数位数,最多是底层密码的块大小。如果未指定块大小,则通常将其视为密码块大小,这是Go代码所做的。参见1,第6.3节。[2]给出了一个更友好的解释。

在黑暗时代(90年代末)使用了较小的块大小,当时人们担心在丢失密文的情况下密码重新同步等问题。如果另一个实现使用CFB1或CFB8,那么它将与Go的CFB模式和其他许多实现非常不同。(Go的代码不支持较小的块大小。)

1 http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf

[2] http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29

英文:

(Firstly, an obligatory warning: CFB mode is a sign of homegrown crypto. Unless you're implementing OpenPGP you should be using an AE mode like AES-GCM or NaCl's secretbox. If you're forced to use CFB mode, I hope that you're authenticating ciphertexts with an HMAC at least.)

With that aside, CFB mode in Go is there for OpenPGP support. (OpenPGP uses both a tweaked CFB mode called OCFB, and standard CFB mode in different places.) The Go OpenPGP code appears to interoperate with other implementations at least.

Nick is correct that test vectors are missing in the Go crypto package. The testing was coming from the OpenPGP code, but packages should stand alone and so I'll add tests to crypto/cipher with the test vectors from section F.3.13 of 1.

My best guess for the source of any differences is that CFB is parameterised by a chunk size. This is generally a power of two number of bits up to the block size of the underlying cipher. If the chunk size isn't specified then it's generally taken to be the cipher block size, which is what the Go code does. See 1, section 6.3. A more friendly explanation is given by [2].

Small chunk sizes were used in the dark ages (the late 90s) when people worried about things like cipher resync in the face of ciphertext loss. If another implementation is using CFB1 or CFB8 then it'll be very different from Go's CFB mode and many others. (Go's code does not support smaller chunk sizes.)

1 http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf

[2] http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29

答案2

得分: 2

我使用以下输入进行了调查,因为我对输入和输出的位/字节顺序不确定:

密钥:00000000000000000000000000000000
IV:00000000000000000000000000000000
加密:66
输入:00
输出:00

http://play.golang.org/p/wl2y1EE6lK

这与您提供的工具匹配,然后是这个:

密钥:00000000000000000000000000000000
IV:00000000000000000000000000000000
加密:66e94b
输入:000000
输出:000000

http://play.golang.org/p/DNC42m2oU5

这与工具不匹配

6616f9

http://aes.online-domain-tools.com/link/63687gDNzymApefh/

第一个字节匹配,这表明可能存在反馈问题。

所以我检查了Go包的代码<strike>,我认为这里有一个错误1

func (x *cfb) XORKeyStream(dst, src []byte) {
for len(src) > 0 {
if x.outUsed == len(x.out) {
x.b.Encrypt(x.out, x.next)
x.outUsed = 0
}

    if x.decrypt {
        // We can precompute a larger segment of the
        // keystream on decryption. This will allow
        // larger batches for xor, and we should be
        // able to match CTR/OFB performance.
        copy(x.next[x.outUsed:], src)
    }
    n := xorBytes(dst, src, x.out[x.outUsed:])
    if !x.decrypt {
        copy(x.next[x.outUsed:], dst) // BUG? `dst` should be `src`
    }
    dst = dst[n:]
    src = src[n:]
    x.outUsed += n
}

}

</strike>

编辑

经过对CFB模式的第二次查看,似乎Go的代码是正确的,所以可能是其他实现有问题。

英文:

I investigated this with the following inputs because I was unsure of the bit/byte order for both inputs and outputs :

Key:  00000000000000000000000000000000
IV:   00000000000000000000000000000000
Enc:  66
In:   00
Out:  00

http://play.golang.org/p/wl2y1EE6lK

Which matches the tool you provided, and then this :

Key:  00000000000000000000000000000000
IV:   00000000000000000000000000000000
Enc:  66e94b
In:   000000
Out:  000000

http://play.golang.org/p/DNC42m2oU5

Which doesn't match the tool :

6616f9

http://aes.online-domain-tools.com/link/63687gDNzymApefh/

The first byte matches, which indicates there may be a feedback issue.

So I checked the Go package's code <strike>and I think there is a bug here :

func (x *cfb) XORKeyStream(dst, src []byte) {
	for len(src) &gt; 0 {
		if x.outUsed == len(x.out) {
			x.b.Encrypt(x.out, x.next)
			x.outUsed = 0
		}

		if x.decrypt {
			// We can precompute a larger segment of the
			// keystream on decryption. This will allow
			// larger batches for xor, and we should be
			// able to match CTR/OFB performance.
			copy(x.next[x.outUsed:], src)
		}
		n := xorBytes(dst, src, x.out[x.outUsed:])
		if !x.decrypt {
			copy(x.next[x.outUsed:], dst) // BUG? `dst` should be `src`
		}
		dst = dst[n:]
		src = src[n:]
		x.outUsed += n
	}
}

</strike>

EDIT

After a second look at CFB mode it seems that Go's code is fine, so yeah it may be the other implementations are wrong.

huangapple
  • 本文由 发表于 2014年8月23日 21:15:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/25462314.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定