blob: 05e4d934f1eff6907c80d13fc0da10565ebb96e8 [file] [log] [blame]
// 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.
//
////////////////////////////////////////////////////////////////////////////////
package aead_test
import (
"bytes"
"encoding/hex"
"encoding/json"
"os"
"testing"
"github.com/google/tink/go/subtle/aead"
"github.com/google/tink/go/subtle/random"
)
var keySizes = []int{16, 32}
// Since the tag size depends on the Seal() function of crypto library,
// this test checks that the tag size is always 128 bit.
func TestAESGCMTagLength(t *testing.T) {
for _, keySize := range keySizes {
key := random.GetRandomBytes(uint32(keySize))
a, _ := aead.NewAESGCM(key)
ad := random.GetRandomBytes(32)
pt := random.GetRandomBytes(32)
ct, _ := a.Encrypt(pt, ad)
actualTagSize := len(ct) - aead.AESGCMIVSize - len(pt)
if actualTagSize != aead.AESGCMTagSize {
t.Errorf("tag size is not 128 bit, it is %d bit", actualTagSize*8)
}
}
}
func TestAESGCMKeySize(t *testing.T) {
for _, keySize := range keySizes {
if _, err := aead.NewAESGCM(make([]byte, keySize)); err != nil {
t.Errorf("unexpected error when key size is %d btyes", keySize)
}
if _, err := aead.NewAESGCM(make([]byte, keySize+1)); err == nil {
t.Errorf("expect an error when key size is not supported %d", keySize)
}
}
}
func TestAESGCMEncryptDecrypt(t *testing.T) {
for _, keySize := range keySizes {
key := random.GetRandomBytes(uint32(keySize))
a, err := aead.NewAESGCM(key)
if err != nil {
t.Errorf("unexpected error when creating new cipher: %s", err)
}
ad := random.GetRandomBytes(5)
for ptSize := 0; ptSize < 75; ptSize++ {
pt := random.GetRandomBytes(uint32(ptSize))
ct, err := a.Encrypt(pt, ad)
if err != nil {
t.Errorf("unexpected error in encryption: keySize %v, ptSize %v", keySize, ptSize)
}
decrypted, err := a.Decrypt(ct, ad)
if err != nil {
t.Errorf("unexpected error in decryption: keySize %v, ptSize %v", keySize, ptSize)
}
if !bytes.Equal(pt, decrypted) {
t.Errorf("decrypted text and plaintext don't match: keySize %v, ptSize %v", keySize, ptSize)
}
}
}
}
func TestAESGCMLongMessages(t *testing.T) {
ptSize := 16
for ptSize <= 1<<24 {
pt := random.GetRandomBytes(uint32(ptSize))
ad := random.GetRandomBytes(uint32(ptSize / 3))
for _, keySize := range keySizes {
key := random.GetRandomBytes(uint32(keySize))
a, _ := aead.NewAESGCM(key)
ct, _ := a.Encrypt(pt, ad)
decrypted, _ := a.Decrypt(ct, ad)
if !bytes.Equal(pt, decrypted) {
t.Errorf("decrypted text and plaintext don't match: keySize %v, ptSize %v", keySize, ptSize)
}
}
ptSize += 5 * ptSize / 11
}
}
func TestAESGCMModifyCiphertext(t *testing.T) {
ad := random.GetRandomBytes(33)
key := random.GetRandomBytes(16)
pt := random.GetRandomBytes(32)
a, _ := aead.NewAESGCM(key)
ct, _ := a.Encrypt(pt, ad)
// flipping bits
for i := 0; i < len(ct); i++ {
tmp := ct[i]
for j := 0; j < 8; j++ {
ct[i] ^= 1 << uint8(j)
if _, err := a.Decrypt(ct, ad); err == nil {
t.Errorf("expect an error when flipping bit of ciphertext: byte %d, bit %d", i, j)
}
ct[i] = tmp
}
}
// truncated ciphertext
for i := 1; i < len(ct); i++ {
if _, err := a.Decrypt(ct[:i], ad); err == nil {
t.Errorf("expect an error ciphertext is truncated until byte %d", i)
}
}
// modify additinal authenticated data
for i := 0; i < len(ad); i++ {
tmp := ad[i]
for j := 0; j < 8; j++ {
ad[i] ^= 1 << uint8(j)
if _, err := a.Decrypt(ct, ad); err == nil {
t.Errorf("expect an error when flipping bit of ad: byte %d, bit %d", i, j)
}
ad[i] = tmp
}
}
}
/**
* This is a very simple test for the randomness of the nonce.
* The test simply checks that the multiple ciphertexts of the same
* message are distinct.
*/
func TestAESGCMRandomNonce(t *testing.T) {
nSample := 1 << 17
key := random.GetRandomBytes(16)
pt := []byte{}
ad := []byte{}
a, _ := aead.NewAESGCM(key)
ctSet := make(map[string]bool)
for i := 0; i < nSample; i++ {
ct, _ := a.Encrypt(pt, ad)
ctHex := hex.EncodeToString(ct)
_, existed := ctSet[ctHex]
if existed {
t.Errorf("nonce is repeated after %d samples", i)
}
ctSet[ctHex] = true
}
}
type testdata struct {
Algorithm string
GeneratorVersion string
NumberOfTests uint32
TestGroups []*testgroup
}
type testgroup struct {
IvSize uint32
KeySize uint32
TagSize uint32
Type string
Tests []*testcase
}
type testcase struct {
Aad string
Comment string
Ct string
Iv string
Key string
Msg string
Result string
Tag string
TcID uint32
}
func TestVectors(t *testing.T) {
f, err := os.Open("../../../third_party/wycheproof/testvectors/aes_gcm_test.json")
if err != nil {
t.Fatalf("cannot open file: %s, make sure that github.com/google/wycheproof is in your gopath", err)
}
parser := json.NewDecoder(f)
data := new(testdata)
if err := parser.Decode(data); err != nil {
t.Fatalf("cannot decode test data: %s", err)
}
for _, g := range data.TestGroups {
if err := aead.ValidateAESKeySize(g.KeySize / 8); err != nil {
continue
}
if g.IvSize != aead.AESGCMIVSize*8 {
continue
}
for _, tc := range g.Tests {
key, err := hex.DecodeString(tc.Key)
if err != nil {
t.Errorf("cannot decode key in test case %d: %s", tc.TcID, err)
}
aad, err := hex.DecodeString(tc.Aad)
if err != nil {
t.Errorf("cannot decode aad in test case %d: %s", tc.TcID, err)
}
msg, err := hex.DecodeString(tc.Msg)
if err != nil {
t.Errorf("cannot decode msg in test case %d: %s", tc.TcID, err)
}
ct, err := hex.DecodeString(tc.Ct)
if err != nil {
t.Errorf("cannot decode ct in test case %d: %s", tc.TcID, err)
}
iv, err := hex.DecodeString(tc.Iv)
if err != nil {
t.Errorf("cannot decode iv in test case %d: %s", tc.TcID, err)
}
tag, err := hex.DecodeString(tc.Tag)
if err != nil {
t.Errorf("cannot decode tag in test case %d: %s", tc.TcID, err)
}
var combinedCt []byte
combinedCt = append(combinedCt, iv...)
combinedCt = append(combinedCt, ct...)
combinedCt = append(combinedCt, tag...)
// create cipher and do encryption
cipher, err := aead.NewAESGCM(key)
if err != nil {
t.Errorf("cannot create new instance of AESGCM in test case %d: %s", tc.TcID, err)
continue
}
decrypted, err := cipher.Decrypt(combinedCt, aad)
if err != nil {
if tc.Result == "valid" {
t.Errorf("unexpected error in test case %d: %s", tc.TcID, err)
}
} else {
if tc.Result == "invalid" {
t.Errorf("decrypted invalid test case %d", tc.TcID)
}
if !bytes.Equal(decrypted, msg) {
t.Errorf("incorrect decryption in test case %d", tc.TcID)
}
}
}
}
}