if len(dst) < len(src) {
panic("crypto/cipher: output smaller than input")
}
+
+ iv := x.iv
+
for len(src) > 0 {
- xorBytes(x.iv, x.iv, src[:x.blockSize])
- x.b.Encrypt(x.iv, x.iv)
- copy(dst, x.iv)
+ // Write the xor to dst, then encrypt in place.
+ xorBytes(dst[:x.blockSize], src[:x.blockSize], iv)
+ x.b.Encrypt(dst[:x.blockSize], dst[:x.blockSize])
+
+ // Move to the next block with this block as the next iv.
+ iv = dst[:x.blockSize]
src = src[x.blockSize:]
dst = dst[x.blockSize:]
}
+
+ // Save the iv for the next CryptBlocks call.
+ copy(x.iv, iv)
}
func (x *cbcEncrypter) SetIV(iv []byte) {
if len(dst) < len(src) {
panic("crypto/cipher: output smaller than input")
}
- for len(src) > 0 {
- x.b.Decrypt(x.tmp, src[:x.blockSize])
- xorBytes(x.tmp, x.tmp, x.iv)
- copy(x.iv, src)
- copy(dst, x.tmp)
- src = src[x.blockSize:]
- dst = dst[x.blockSize:]
+ if len(src) == 0 {
+ return
}
+
+ // For each block, we need to xor the decrypted data with the previous block's ciphertext (the iv).
+ // To avoid making a copy each time, we loop over the blocks BACKWARDS.
+ end := len(src)
+ start := end - x.blockSize
+ prev := start - x.blockSize
+
+ // Copy the last block of ciphertext in preparation as the new iv.
+ copy(x.tmp, src[start:end])
+
+ // Loop over all but the first block.
+ for start > 0 {
+ x.b.Decrypt(dst[start:end], src[start:end])
+ xorBytes(dst[start:end], dst[start:end], src[prev:start])
+
+ end = start
+ start = prev
+ prev -= x.blockSize
+ }
+
+ // The first block is special because it uses the saved iv.
+ x.b.Decrypt(dst[start:end], src[start:end])
+ xorBytes(dst[start:end], dst[start:end], x.iv)
+
+ // Set the new iv to the first block we copied earlier.
+ x.iv, x.tmp = x.tmp, x.iv
}
func (x *cbcDecrypter) SetIV(iv []byte) {
},
}
-func TestCBC_AES(t *testing.T) {
- for _, tt := range cbcAESTests {
- test := tt.name
-
- c, err := aes.NewCipher(tt.key)
+func TestCBCEncrypterAES(t *testing.T) {
+ for _, test := range cbcAESTests {
+ c, err := aes.NewCipher(test.key)
if err != nil {
- t.Errorf("%s: NewCipher(%d bytes) = %s", test, len(tt.key), err)
+ t.Errorf("%s: NewCipher(%d bytes) = %s", test.name, len(test.key), err)
continue
}
- encrypter := cipher.NewCBCEncrypter(c, tt.iv)
- d := make([]byte, len(tt.in))
- encrypter.CryptBlocks(d, tt.in)
- if !bytes.Equal(tt.out, d) {
- t.Errorf("%s: CBCEncrypter\nhave %x\nwant %x", test, d, tt.out)
+ encrypter := cipher.NewCBCEncrypter(c, test.iv)
+
+ data := make([]byte, len(test.in))
+ copy(data, test.in)
+
+ encrypter.CryptBlocks(data, data)
+ if !bytes.Equal(test.out, data) {
+ t.Errorf("%s: CBCEncrypter\nhave %x\nwant %x", test.name, data, test.out)
}
+ }
+}
+
+func TestCBCDecrypterAES(t *testing.T) {
+ for _, test := range cbcAESTests {
+ c, err := aes.NewCipher(test.key)
+ if err != nil {
+ t.Errorf("%s: NewCipher(%d bytes) = %s", test.name, len(test.key), err)
+ continue
+ }
+
+ decrypter := cipher.NewCBCDecrypter(c, test.iv)
+
+ data := make([]byte, len(test.out))
+ copy(data, test.out)
- decrypter := cipher.NewCBCDecrypter(c, tt.iv)
- p := make([]byte, len(d))
- decrypter.CryptBlocks(p, d)
- if !bytes.Equal(tt.in, p) {
- t.Errorf("%s: CBCDecrypter\nhave %x\nwant %x", test, d, tt.in)
+ decrypter.CryptBlocks(data, data)
+ if !bytes.Equal(test.in, data) {
+ t.Errorf("%s: CBCDecrypter\nhave %x\nwant %x", test.name, data, test.in)
}
}
}