I’m trying to understand how encryption using the CTR mode works, so I created these functions to test it:
import (
"crypto/cipher"
"crypto/rand"
)
// generateIV generates an initialization vector (IV) suitable for encryption.
//
// http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Initialization_vector_.28IV.29
func generateIV(bytes int) []byte {
b := make([]byte, bytes)
rand.Read(b)
return b
}
func encrypt(block cipher.Block, value []byte) []byte {
iv := generateIV(block.BlockSize())
encrypted := make([]byte, len(value) + block.BlockSize())
encrypted = append(encrypted, iv...)
stream := cipher.NewCTR(block, iv)
stream.XORKeyStream(encrypted, value)
return encrypted
}
func decrypt(block cipher.Block, encrypted []byte) []byte {
iv := encrypted[:block.BlockSize()]
ciphertext := encrypted[block.BlockSize():]
stream := cipher.NewCTR(block, iv)
plain := make([]byte, len(ciphertext))
// XORKeyStream is used to decrypt too?
stream.XORKeyStream(plain, ciphertext)
return plain
}
Encryption seems to work fine, but well I don’t know really because I don’t understand the output of decryption. Should I use stream.XORKeyStream to decrypt too? The test looks like this:
import (
"crypto/aes"
"fmt"
"testing"
)
func TestEncryptCTR(t *testing.T) {
block, err := aes.NewCipher([]byte("1234567890123456"))
if err != nil {
panic(err)
}
value := "foobarbaz"
encrypted := encrypt(block, []byte(value))
decrypted := decrypt(block, encrypted)
fmt.Printf("--- %s ---", string(decrypted))
}
But I’m definitely not getting “foobarbaz” back. Can you spot what I’m doing wrong?
The problem was me trying to do too much before testing the basics. I wanted to prepend the IV to the generated ciphertext, but somewhat I broke everything when I did that. This simple version, with no prepended IV, works:
And the corresponding test:
I get “— foobarbaz —“, as expected.
Now back to make the prepending IV work. 🙂
Edit And this is it, with auto-generated and prepended IV: