blob: 24d0dbfe0e52202c9cf72519825b16bbf15b3642 [file] [log] [blame]
// Copyright 2022 Google LLC
//
// 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 keyderivation
import (
"bytes"
"encoding/hex"
"fmt"
"testing"
"google.golang.org/protobuf/proto"
"github.com/google/tink/go/aead"
"github.com/google/tink/go/core/registry"
"github.com/google/tink/go/daead"
"github.com/google/tink/go/insecurecleartextkeyset"
"github.com/google/tink/go/mac"
"github.com/google/tink/go/prf"
"github.com/google/tink/go/signature"
"github.com/google/tink/go/streamingaead"
aesgcmpb "github.com/google/tink/go/proto/aes_gcm_go_proto"
commonpb "github.com/google/tink/go/proto/common_go_proto"
hkdfpb "github.com/google/tink/go/proto/hkdf_prf_go_proto"
tinkpb "github.com/google/tink/go/proto/tink_go_proto"
)
func TestPRFBasedDeriver(t *testing.T) {
prfs := []struct {
name string
template *tinkpb.KeyTemplate
}{
{
name: "HKDF_SHA256",
template: prf.HKDFSHA256PRFKeyTemplate(),
},
}
// Derivation names match KEY_TEMPLATE_NAMES in
// https://github.com/google/tink/blob/cd96c47ced3f72199832573cdccf18719dc7c73b/testing/cross_language/util/utilities.py.
derivations := []struct {
name string
template *tinkpb.KeyTemplate
}{
{
name: "AES128_GCM",
template: aead.AES128GCMKeyTemplate(),
},
{
name: "AES256_GCM",
template: aead.AES256GCMKeyTemplate(),
},
{
name: "AES256_GCM_RAW",
template: aead.AES256GCMNoPrefixKeyTemplate(),
},
{
name: "XCHACHA20_POLY1305",
template: aead.XChaCha20Poly1305KeyTemplate(),
},
{
name: "AES256_SIV",
template: daead.AESSIVKeyTemplate(),
},
{
name: "HMAC_SHA256_128BITTAG",
template: mac.HMACSHA256Tag128KeyTemplate(),
},
{
name: "HMAC_SHA256_256BITTAG",
template: mac.HMACSHA256Tag256KeyTemplate(),
},
{
name: "HMAC_SHA512_256BITTAG",
template: mac.HMACSHA512Tag256KeyTemplate(),
},
{
name: "HMAC_SHA512_512BITTAG",
template: mac.HMACSHA512Tag512KeyTemplate(),
},
{
name: "HKDF_SHA256",
template: prf.HKDFSHA256PRFKeyTemplate(),
},
{
name: "HMAC_SHA256_PRF",
template: prf.HMACSHA256PRFKeyTemplate(),
},
{
name: "HMAC_SHA512_PRF",
template: prf.HMACSHA512PRFKeyTemplate(),
},
{
name: "ED25519",
template: signature.ED25519KeyTemplate(),
},
{
name: "AES128_GCM_HKDF_4KB",
template: streamingaead.AES128GCMHKDF4KBKeyTemplate(),
},
{
name: "AES128_GCM_HKDF_1MB",
template: streamingaead.AES128GCMHKDF1MBKeyTemplate(),
},
{
name: "AES256_GCM_HKDF_4KB",
template: streamingaead.AES256GCMHKDF4KBKeyTemplate(),
},
{
name: "AES256_GCM_HKDF_1MB",
template: streamingaead.AES256GCMHKDF1MBKeyTemplate(),
},
}
salts := [][]byte{nil, []byte("salt")}
for _, prf := range prfs {
for _, der := range derivations {
for _, salt := range salts {
name := fmt.Sprintf("%s_%s", prf.name, der.name)
if salt != nil {
name += "_with_salt"
}
t.Run(name, func(t *testing.T) {
prfKeyData, err := registry.NewKeyData(prf.template)
if err != nil {
t.Fatalf("registry.NewKeyData() err = %v, want nil", err)
}
d, err := newPRFBasedDeriver(prfKeyData, der.template)
if err != nil {
t.Fatalf("newPRFBasedDeriver() err = %v, want nil", err)
}
if _, err := d.DeriveKeyset(salt); err != nil {
t.Errorf("DeriveKeyset() err = %v, want nil", err)
}
// We cannot test the derived keyset handle because, at this point, it
// is filled with placeholder values for the key ID, status, and
// output prefix type fields.
})
}
}
}
}
func TestPRFBasedDeriverWithHKDFRFCVectorForAESGCM(t *testing.T) {
// This is the only HKDF vector that uses an accepted hash function and has
// key size >= 32-bytes.
// https://www.rfc-editor.org/rfc/rfc5869#appendix-A.2
vec := struct {
hash commonpb.HashType
key string
salt string
info string
outLen int
okm string
}{
hash: commonpb.HashType_SHA256,
key: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f",
salt: "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf",
info: "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
outLen: 82,
okm: "b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c59045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71cc30c58179ec3e87c14c01d5c1f3434f1d87",
}
prfKeyValue, err := hex.DecodeString(vec.key)
if err != nil {
t.Fatalf("hex.DecodeString() err = %v, want nil", err)
}
prfSalt, err := hex.DecodeString(vec.salt)
if err != nil {
t.Fatalf("hex.DecodeString() err = %v, want nil", err)
}
derivationSalt, err := hex.DecodeString(vec.info)
if err != nil {
t.Fatalf("hex.DecodeString() err = %v, want nil", err)
}
wantKeyValue, err := hex.DecodeString(vec.okm)
if err != nil {
t.Fatalf("hex.DecodeString() err = %v, want nil", err)
}
prfKey := &hkdfpb.HkdfPrfKey{
Version: 0,
Params: &hkdfpb.HkdfPrfParams{
Hash: vec.hash,
Salt: prfSalt,
},
KeyValue: prfKeyValue,
}
serializedPRFKey, err := proto.Marshal(prfKey)
if err != nil {
t.Fatalf("proto.Marshal() err = %v, want nil", err)
}
prfKeyData := &tinkpb.KeyData{
TypeUrl: prf.HKDFSHA256PRFKeyTemplate().GetTypeUrl(),
Value: serializedPRFKey,
KeyMaterialType: tinkpb.KeyData_SYMMETRIC,
}
for _, test := range []struct {
name string
derivedKeyTemplate *tinkpb.KeyTemplate
}{
{
name: "AES128_GCM",
derivedKeyTemplate: aead.AES128GCMKeyTemplate(),
},
{
name: "AES256_GCM",
derivedKeyTemplate: aead.AES256GCMKeyTemplate(),
},
{
name: "AES256_GCM_RAW",
derivedKeyTemplate: aead.AES256GCMNoPrefixKeyTemplate(),
},
} {
t.Run(test.name, func(t *testing.T) {
// Derive keyset.
d, err := newPRFBasedDeriver(prfKeyData, test.derivedKeyTemplate)
if err != nil {
t.Fatalf("newPRFBasedDeriver() err = %v, want nil", err)
}
derivedHandle, err := d.DeriveKeyset(derivationSalt)
if err != nil {
t.Fatalf("DeriveKeyset() err = %v, want nil", err)
}
derivedKeyset := insecurecleartextkeyset.KeysetMaterial(derivedHandle)
// Verify keyset.
if len(derivedKeyset.GetKey()) != 1 {
t.Fatalf("len(keyset) = %d, want 1", len(derivedKeyset.GetKey()))
}
key := derivedKeyset.GetKey()[0]
if derivedKeyset.GetPrimaryKeyId() != key.GetKeyId() {
t.Fatal("keyset has no primary key")
}
// Verify key attributes set by prfBasedDeriver.
if got, want := key.GetStatus(), tinkpb.KeyStatusType_UNKNOWN_STATUS; got != want {
t.Errorf("derived key status = %s, want %s", got, want)
}
if got, want := key.GetOutputPrefixType(), tinkpb.OutputPrefixType_UNKNOWN_PREFIX; got != want {
t.Errorf("derived key output prefix type = %s, want %s", got, want)
}
// Verify key value.
derivedKeyFormat := &aesgcmpb.AesGcmKeyFormat{}
if err := proto.Unmarshal(test.derivedKeyTemplate.GetValue(), derivedKeyFormat); err != nil {
t.Fatalf("proto.Unmarshal() err = %v, want nil", err)
}
wantKeySize := int(derivedKeyFormat.GetKeySize())
aesGCMKey := &aesgcmpb.AesGcmKey{}
if err := proto.Unmarshal(key.GetKeyData().GetValue(), aesGCMKey); err != nil {
t.Fatalf("proto.Unmarshal() err = %v, want nil", err)
}
gotKeyValue := aesGCMKey.GetKeyValue()
if len(gotKeyValue) != wantKeySize {
t.Errorf("derived key value length = %d, want %d", len(gotKeyValue), wantKeySize)
}
if !bytes.Equal(gotKeyValue, wantKeyValue[:wantKeySize]) {
t.Errorf("derived key value = %q, want %q", gotKeyValue, wantKeyValue[:wantKeySize])
}
})
}
}
func TestNewPRFBasedDeriverRejectsInvalidInputs(t *testing.T) {
validPRFKeyData, err := registry.NewKeyData(prf.HKDFSHA256PRFKeyTemplate())
if err != nil {
t.Fatalf("registry.NewKeyData() err = %v, want nil", err)
}
validDerivedKeyTemplate := aead.AES128GCMKeyTemplate()
if _, err := newPRFBasedDeriver(validPRFKeyData, validDerivedKeyTemplate); err != nil {
t.Fatalf("newPRFBasedDeriver() err = %v, want nil", err)
}
invalidPRFKeyData, err := registry.NewKeyData(aead.AES128GCMKeyTemplate())
if err != nil {
t.Fatalf("registry.NewKeyData() err = %v, want nil", err)
}
// The derivation of KeysetDeriver keyset handles is not supported, i.e. a
// KeysetDeriver key template cannot be used as the derivedKeyTemplate
// argument in newPRFBasedDeriver().
invalidDerivedKeyTemplate, err := CreatePRFBasedKeyTemplate(prf.HKDFSHA256PRFKeyTemplate(), aead.AES128GCMKeyTemplate())
if err != nil {
t.Fatalf("CreatePRFBasedKeyTemplate() err = %v, want nil", err)
}
for _, test := range []struct {
name string
prfKeyData *tinkpb.KeyData
derivedKeyTemplate *tinkpb.KeyTemplate
}{
{
name: "nil inputs",
},
{
name: "nil PRF key data",
derivedKeyTemplate: validDerivedKeyTemplate,
},
{
name: "nil derived template",
prfKeyData: validPRFKeyData,
},
{
name: "invalid PRF key data",
prfKeyData: invalidPRFKeyData,
derivedKeyTemplate: validDerivedKeyTemplate,
},
{
name: "invalid derived template",
prfKeyData: validPRFKeyData,
derivedKeyTemplate: invalidDerivedKeyTemplate,
},
} {
t.Run(test.name, func(t *testing.T) {
if _, err := newPRFBasedDeriver(test.prfKeyData, test.derivedKeyTemplate); err == nil {
t.Errorf("newPRFBasedDeriver() err = nil, want non-nil")
}
})
}
}