go-openssl/ciphers_test.go
paul cannon d0177cb6a7 Fix init'ing non-authenticated decryption contexts
As pointed out by ishbir in issue 24, the applyKeyAndIV function was
incorrectly always using EVP_EncryptInit_ex(), even when the underlying
context being initialized was for a decryption.

This change causes it to select the right initializer based on the
"encrypt" field in the EVP_CIPHER_CTX. A test is also added which
verifies a fix.

Closes #24.
2014-12-19 02:32:36 -06:00

308 lines
9.0 KiB
Go

// Copyright (C) 2014 Space Monkey, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build !darwin
package openssl
import (
"bytes"
"fmt"
"strings"
"testing"
)
func expectError(t *testing.T, err error, msg string) {
if err == nil {
t.Fatalf("Expected error containing %#v, but got none", msg)
}
if !strings.Contains(err.Error(), msg) {
t.Fatalf("Expected error containing %#v, but got %s", msg, err)
}
}
func TestBadInputs(t *testing.T) {
_, err := NewGCMEncryptionCipherCtx(256, nil,
[]byte("abcdefghijklmnopqrstuvwxyz"), nil)
expectError(t, err, "bad key size")
_, err = NewGCMEncryptionCipherCtx(128, nil,
[]byte("abcdefghijklmnopqrstuvwxyz"), nil)
expectError(t, err, "bad key size")
_, err = NewGCMEncryptionCipherCtx(200, nil,
[]byte("abcdefghijklmnopqrstuvwxy"), nil)
expectError(t, err, "unknown block size")
c, err := GetCipherByName("AES-128-CBC")
if err != nil {
t.Fatal("Could not look up AES-128-CBC")
}
_, err = NewEncryptionCipherCtx(c, nil, []byte("abcdefghijklmnop"),
[]byte("abc"))
expectError(t, err, "bad IV size")
}
func doEncryption(key, iv, aad, plaintext []byte, blocksize, bufsize int) (
ciphertext, tag []byte, err error) {
ectx, err := NewGCMEncryptionCipherCtx(blocksize, nil, key, iv)
if err != nil {
return nil, nil, fmt.Errorf("Failed making GCM encryption ctx: %s", err)
}
err = ectx.ExtraData(aad)
if err != nil {
return nil, nil, fmt.Errorf("Failed to add authenticated data: %s",
err)
}
plainb := bytes.NewBuffer(plaintext)
cipherb := new(bytes.Buffer)
for plainb.Len() > 0 {
moar, err := ectx.EncryptUpdate(plainb.Next(bufsize))
if err != nil {
return nil, nil, fmt.Errorf("Failed to perform an encryption: %s",
err)
}
cipherb.Write(moar)
}
moar, err := ectx.EncryptFinal()
if err != nil {
return nil, nil, fmt.Errorf("Failed to finalize encryption: %s", err)
}
cipherb.Write(moar)
tag, err = ectx.GetTag()
if err != nil {
return nil, nil, fmt.Errorf("Failed to get GCM tag: %s", err)
}
return cipherb.Bytes(), tag, nil
}
func doDecryption(key, iv, aad, ciphertext, tag []byte, blocksize,
bufsize int) (plaintext []byte, err error) {
dctx, err := NewGCMDecryptionCipherCtx(blocksize, nil, key, iv)
if err != nil {
return nil, fmt.Errorf("Failed making GCM decryption ctx: %s", err)
}
aadbuf := bytes.NewBuffer(aad)
for aadbuf.Len() > 0 {
err = dctx.ExtraData(aadbuf.Next(bufsize))
if err != nil {
return nil, fmt.Errorf("Failed to add authenticated data: %s", err)
}
}
plainb := new(bytes.Buffer)
cipherb := bytes.NewBuffer(ciphertext)
for cipherb.Len() > 0 {
moar, err := dctx.DecryptUpdate(cipherb.Next(bufsize))
if err != nil {
return nil, fmt.Errorf("Failed to perform a decryption: %s", err)
}
plainb.Write(moar)
}
err = dctx.SetTag(tag)
if err != nil {
return nil, fmt.Errorf("Failed to set expected GCM tag: %s", err)
}
moar, err := dctx.DecryptFinal()
if err != nil {
return nil, fmt.Errorf("Failed to finalize decryption: %s", err)
}
plainb.Write(moar)
return plainb.Bytes(), nil
}
func checkEqual(t *testing.T, output []byte, original string) {
output_s := string(output)
if output_s != original {
t.Fatalf("output != original! %#v != %#v", output_s, original)
}
}
func TestGCM(t *testing.T) {
aad := []byte("foo bar baz")
key := []byte("nobody can guess this i'm sure..") // len=32
iv := []byte("just a bunch of bytes")
plaintext := "Long long ago, in a land far away..."
blocksizes_to_test := []int{256, 192, 128}
// best for this to have no common factors with blocksize, so that the
// buffering layer inside the CIPHER_CTX gets exercised
bufsize := 33
if len(plaintext)%8 == 0 {
plaintext += "!" // make sure padding is exercised
}
for _, bsize := range blocksizes_to_test {
subkey := key[:bsize/8]
ciphertext, tag, err := doEncryption(subkey, iv, aad, []byte(plaintext),
bsize, bufsize)
if err != nil {
t.Fatalf("Encryption with b=%d: %s", bsize, err)
}
plaintext_out, err := doDecryption(subkey, iv, aad, ciphertext, tag,
bsize, bufsize)
if err != nil {
t.Fatalf("Decryption with b=%d: %s", bsize, err)
}
checkEqual(t, plaintext_out, plaintext)
}
}
func TestGCMWithNoAAD(t *testing.T) {
key := []byte("0000111122223333")
iv := []byte("9999")
plaintext := "ABORT ABORT ABORT DANGAR"
ciphertext, tag, err := doEncryption(key, iv, nil, []byte(plaintext),
128, 32)
if err != nil {
t.Fatal("Encryption failure:", err)
}
plaintext_out, err := doDecryption(key, iv, nil, ciphertext, tag, 128, 129)
if err != nil {
t.Fatal("Decryption failure:", err)
}
checkEqual(t, plaintext_out, plaintext)
}
func TestBadTag(t *testing.T) {
key := []byte("abcdefghijklmnop")
iv := []byte("v7239qjfv3qr793fuaj")
plaintext := "The red rooster has flown the coop I REPEAT" +
"the red rooster has flown the coop!!1!"
ciphertext, tag, err := doEncryption(key, iv, nil, []byte(plaintext),
128, 32)
if err != nil {
t.Fatal("Encryption failure:", err)
}
// flip the last bit
tag[len(tag)-1] ^= 1
plaintext_out, err := doDecryption(key, iv, nil, ciphertext, tag, 128, 129)
if err == nil {
t.Fatal("Expected error for bad tag, but got none")
}
// flip it back, try again just to make sure
tag[len(tag)-1] ^= 1
plaintext_out, err = doDecryption(key, iv, nil, ciphertext, tag, 128, 129)
if err != nil {
t.Fatal("Decryption failure:", err)
}
checkEqual(t, plaintext_out, plaintext)
}
func TestBadCiphertext(t *testing.T) {
key := []byte("hard boiled eggs & bacon")
iv := []byte("x") // it's not a very /good/ IV, is it
aad := []byte("mu")
plaintext := "Roger roger bingo charlie, we have a niner fourteen tango"
ciphertext, tag, err := doEncryption(key, iv, aad, []byte(plaintext),
192, 1)
if err != nil {
t.Fatal("Encryption failure:", err)
}
// flip the last bit
ciphertext[len(ciphertext)-1] ^= 1
plaintext_out, err := doDecryption(key, iv, aad, ciphertext, tag, 192, 192)
if err == nil {
t.Fatal("Expected error for bad ciphertext, but got none")
}
// flip it back, try again just to make sure
ciphertext[len(ciphertext)-1] ^= 1
plaintext_out, err = doDecryption(key, iv, aad, ciphertext, tag, 192, 192)
if err != nil {
t.Fatal("Decryption failure:", err)
}
checkEqual(t, plaintext_out, plaintext)
}
func TestBadAAD(t *testing.T) {
key := []byte("Ive got a lovely buncha coconuts")
iv := []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab")
aad := []byte("Hi i am a plain")
plaintext := "Whatever."
ciphertext, tag, err := doEncryption(key, iv, aad, []byte(plaintext),
256, 256)
if err != nil {
t.Fatal("Encryption failure:", err)
}
// flip the last bit
aad[len(aad)-1] ^= 1
plaintext_out, err := doDecryption(key, iv, aad, ciphertext, tag, 256, 256)
if err == nil {
t.Fatal("Expected error for bad AAD, but got none")
}
// flip it back, try again just to make sure
aad[len(aad)-1] ^= 1
plaintext_out, err = doDecryption(key, iv, aad, ciphertext, tag, 256, 256)
if err != nil {
t.Fatal("Decryption failure:", err)
}
checkEqual(t, plaintext_out, plaintext)
}
func TestNonAuthenticatedEncryption(t *testing.T) {
key := []byte("never gonna give you up, never g")
iv := []byte("onna let you dow")
plaintext1 := "n, never gonna run around"
plaintext2 := " and desert you"
cipher, err := GetCipherByName("aes-256-cbc")
if err != nil {
t.Fatal("Could not get cipher: ", err)
}
eCtx, err := NewEncryptionCipherCtx(cipher, nil, key, iv)
if err != nil {
t.Fatal("Could not create encryption context: ", err)
}
cipherbytes, err := eCtx.EncryptUpdate([]byte(plaintext1))
if err != nil {
t.Fatal("EncryptUpdate(plaintext1) failure: ", err)
}
ciphertext := string(cipherbytes)
cipherbytes, err = eCtx.EncryptUpdate([]byte(plaintext2))
if err != nil {
t.Fatal("EncryptUpdate(plaintext2) failure: ", err)
}
ciphertext += string(cipherbytes)
cipherbytes, err = eCtx.EncryptFinal()
if err != nil {
t.Fatal("EncryptFinal() failure: ", err)
}
ciphertext += string(cipherbytes)
dCtx, err := NewDecryptionCipherCtx(cipher, nil, key, iv)
if err != nil {
t.Fatal("Could not create decryption context: ", err)
}
plainbytes, err := dCtx.DecryptUpdate([]byte(ciphertext[:15]))
if err != nil {
t.Fatal("DecryptUpdate(ciphertext part 1) failure: ", err)
}
plainOutput := string(plainbytes)
plainbytes, err = dCtx.DecryptUpdate([]byte(ciphertext[15:]))
if err != nil {
t.Fatal("DecryptUpdate(ciphertext part 2) failure: ", err)
}
plainOutput += string(plainbytes)
plainbytes, err = dCtx.DecryptFinal()
if err != nil {
t.Fatal("DecryptFinal() failure: ", err)
}
plainOutput += string(plainbytes)
checkEqual(t, []byte(plainOutput), plaintext1+plaintext2)
}