blob: dc7ec0bedd3e2a69a04be249672a3d3011a76679 [file] [log] [blame]
// Copyright 2020 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 prf_test
import (
"bytes"
"encoding/hex"
"fmt"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/google/tink/go/insecurecleartextkeyset"
"github.com/google/tink/go/internal/internalregistry"
"github.com/google/tink/go/keyset"
"github.com/google/tink/go/mac"
"github.com/google/tink/go/monitoring"
"github.com/google/tink/go/prf"
"github.com/google/tink/go/testing/fakemonitoring"
"github.com/google/tink/go/testutil"
tinkpb "github.com/google/tink/go/proto/tink_go_proto"
)
const (
maxAutocorrelation = 100
)
func addKeyAndReturnID(m *keyset.Manager, template *tinkpb.KeyTemplate) (uint32, error) {
keyID, err := m.Add(template)
if err != nil {
return 0, fmt.Errorf("Could not add key from the given template: %v", err)
}
err = m.SetPrimary(keyID)
if err != nil {
return 0, fmt.Errorf("Could set key as primary: %v", err)
}
return keyID, nil
}
func TestFactoryBasic(t *testing.T) {
manager := keyset.NewManager()
aescmacID, err := addKeyAndReturnID(manager, prf.AESCMACPRFKeyTemplate())
if err != nil {
t.Errorf("Could not add AES CMAC PRF key: %v", err)
}
hmacsha256ID, err := addKeyAndReturnID(manager, prf.HMACSHA256PRFKeyTemplate())
if err != nil {
t.Errorf("Could not add HMAC SHA256 PRF key: %v", err)
}
hkdfsha256ID, err := addKeyAndReturnID(manager, prf.HKDFSHA256PRFKeyTemplate())
if err != nil {
t.Errorf("Could not add HKDF SHA256 PRF key: %v", err)
}
hmacsha512ID, err := addKeyAndReturnID(manager, prf.HMACSHA512PRFKeyTemplate())
if err != nil {
t.Errorf("Could not add HMAC SHA512 PRF key: %v", err)
}
handle, err := manager.Handle()
if err != nil {
t.Errorf("Could not obtain handle: %v", err)
}
prfSet, err := prf.NewPRFSet(handle)
if err != nil {
t.Errorf("Could not create prf.Set with standard key templates: %v", err)
}
primaryID := prfSet.PrimaryID
if primaryID != hmacsha512ID {
t.Errorf("Primary ID %d should be the ID %d, which was added last", primaryID, hmacsha512ID)
}
for _, length := range []uint32{1, 10, 16, 17, 32, 33, 64, 65, 100, 8160, 8161} {
results := [][]byte{}
for id, prf := range prfSet.PRFs {
ok := true
switch {
case length > 16 && id == aescmacID:
ok = false
case length > 32 && id == hmacsha256ID:
ok = false
case length > 64 && id == hmacsha512ID:
ok = false
case length > 8160 && id == hkdfsha256ID:
ok = false
}
result1, err := prf.ComputePRF([]byte("The input"), length)
switch {
case err != nil && !ok:
continue
case err != nil:
t.Errorf("Expected to be able to compute %d bytes of PRF output: %v", length, err)
continue
case !ok:
t.Errorf("Expected to be unable to compute %d bytes PRF output", length)
continue
}
result2, err := prf.ComputePRF([]byte("The different input"), length)
switch {
case err != nil && !ok:
continue
case err != nil:
t.Errorf("Expected to be able to compute %d bytes of PRF output: %v", length, err)
continue
case !ok:
t.Errorf("Expected to be unable to compute %d bytes PRF output", length)
continue
}
result3, err := prf.ComputePRF([]byte("The input"), length)
switch {
case err != nil && !ok:
continue
case err != nil:
t.Errorf("Expected to be able to compute %d bytes of PRF output: %v", length, err)
continue
case !ok:
t.Errorf("Expected to be unable to compute %d bytes PRF output", length)
continue
}
if id == primaryID {
primaryResult, err := prfSet.ComputePrimaryPRF([]byte("The input"), length)
switch {
case err != nil && !ok:
continue
case err != nil:
t.Errorf("Expected to be able to compute %d bytes of PRF output: %v", length, err)
continue
case !ok:
t.Errorf("Expected to be unable to compute %d bytes PRF output", length)
continue
}
if hex.EncodeToString(result1) != hex.EncodeToString(primaryResult) {
t.Errorf("Expected manual call of ComputePRF of primary PRF and ComputePrimaryPRF with the same input to produce the same output, but got %q and %q", result1, primaryResult)
}
}
if hex.EncodeToString(result1) != hex.EncodeToString(result3) {
t.Errorf("Expected different calls with the same input to produce the same output, but got %q and %q", result1, result3)
}
results = append(results, result1)
results = append(results, result2)
}
runZTests(results, t)
}
}
func TestNonRawKeys(t *testing.T) {
template := prf.AESCMACPRFKeyTemplate()
template.OutputPrefixType = tinkpb.OutputPrefixType_TINK
h, err := keyset.NewHandle(template)
if err != nil {
t.Errorf("Couldn't create keyset: %v", err)
}
_, err = prf.NewPRFSet(h)
if err == nil {
t.Errorf("Expected non RAW prefix to fail to create prf.Set")
}
m := keyset.NewManagerFromHandle(h)
_, err = addKeyAndReturnID(m, prf.HMACSHA256PRFKeyTemplate())
if err != nil {
t.Errorf("Expected to be able to add keys to the keyset: %v", err)
}
h, err = m.Handle()
if err != nil {
t.Errorf("Expected to be able to create keyset handle: %v", err)
}
_, err = prf.NewPRFSet(h)
if err == nil {
t.Errorf("Expected mixed prefix keyset to fail to create prf.Set")
}
}
func TestNonPRFPrimitives(t *testing.T) {
template := mac.AESCMACTag128KeyTemplate()
template.OutputPrefixType = tinkpb.OutputPrefixType_RAW
h, err := keyset.NewHandle(template)
if err != nil {
t.Errorf("Couldn't create keyset: %v", err)
}
_, err = prf.NewPRFSet(h)
if err == nil {
t.Errorf("Expected non PRF primitive to fail to create prf.Set")
}
m := keyset.NewManagerFromHandle(h)
_, err = addKeyAndReturnID(m, prf.HMACSHA256PRFKeyTemplate())
if err != nil {
t.Errorf("Expected to be able to add keys to the keyset: %v", err)
}
h, err = m.Handle()
if err != nil {
t.Errorf("Expected to be able to create keyset handle: %v", err)
}
_, err = prf.NewPRFSet(h)
if err == nil {
t.Errorf("Expected mixed primitive keyset to fail to create prf.Set")
}
}
func runZTests(results [][]byte, t *testing.T) {
for i, result1 := range results {
if err := testutil.ZTestUniformString(result1); err != nil {
t.Errorf("Expected PRF output to pass uniformity z test: %v", err)
}
if len(result1) <= maxAutocorrelation {
if err := testutil.ZTestAutocorrelationUniformString(result1); err != nil {
t.Errorf("Expected PRF output to pass autocorrelation test: %v", err)
}
}
for j := i + 1; j < len(results); j++ {
result2 := results[j]
if err := testutil.ZTestCrosscorrelationUniformStrings(result1, result2); err != nil {
t.Errorf("Expected different PRF outputs to be uncorrelated: %v", err)
}
}
}
}
func TestPrimitiveFactoryComputePRFWithoutAnnotationsDoesNothing(t *testing.T) {
defer internalregistry.ClearMonitoringClient()
client := fakemonitoring.NewClient("fake-client")
if err := internalregistry.RegisterMonitoringClient(client); err != nil {
t.Fatalf("internalregistry.RegisterMonitoringClient() err = %v, want nil", err)
}
kh, err := keyset.NewHandle(prf.HMACSHA256PRFKeyTemplate())
if err != nil {
t.Fatalf("keyset.NewHandle() err = %v, want nil", err)
}
prfSet, err := prf.NewPRFSet(kh)
if err != nil {
t.Fatalf("prf.NewPRFSet() err = %v, want nil", err)
}
if _, err := prfSet.ComputePrimaryPRF([]byte("input_data"), 32); err != nil {
t.Fatalf("prfSet.ComputePrimaryPRF() err = %v, want nil", err)
}
failures := len(client.Failures())
if failures != 0 {
t.Errorf("len(client.Failures()) = %d, want 0", failures)
}
got := client.Events()
if got != nil {
t.Errorf("client.Events() = %v, want nil", got)
}
}
func TestPrimitiveFactoryMonitoringWithAnnotationsComputePRFFailureIsLogged(t *testing.T) {
defer internalregistry.ClearMonitoringClient()
client := fakemonitoring.NewClient("fake-client")
if err := internalregistry.RegisterMonitoringClient(client); err != nil {
t.Fatalf("internalregistry.RegisterMonitoringClient() err = %v, want nil", err)
}
kh, err := keyset.NewHandle(prf.HMACSHA256PRFKeyTemplate())
if err != nil {
t.Fatalf("keyset.NewHandle() err = %v, want nil", err)
}
buff := &bytes.Buffer{}
if err := insecurecleartextkeyset.Write(kh, keyset.NewBinaryWriter(buff)); err != nil {
t.Fatalf("insecurecleartextkeyset.Write() err = %v, want nil", err)
}
annotations := map[string]string{"foo": "bar"}
mh, err := insecurecleartextkeyset.Read(keyset.NewBinaryReader(buff), keyset.WithAnnotations(annotations))
if err != nil {
t.Fatalf("insecurecleartextkeyset.Read() err = %v, want nil", err)
}
prfSet, err := prf.NewPRFSet(mh)
if err != nil {
t.Fatalf("prf.NewPRFSet() err = %v, want nil", err)
}
data := []byte("input_data")
if _, err := prfSet.ComputePrimaryPRF(data, 64); err == nil {
t.Fatalf("prfSet.ComputePrimaryPRF() err = nil, want non-nil errors")
}
got := client.Failures()
want := []*fakemonitoring.LogFailure{
{
Context: monitoring.NewContext(
"prf",
"compute",
&monitoring.KeysetInfo{
Annotations: annotations,
Entries: []*monitoring.Entry{
{
KeyID: kh.KeysetInfo().GetPrimaryKeyId(),
Status: monitoring.Enabled,
KeyType: "tink.HmacPrfKey",
KeyPrefix: "RAW",
},
},
PrimaryKeyID: kh.KeysetInfo().GetPrimaryKeyId(),
},
),
},
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("%v", diff)
}
}
func TestPrimitiveFactoryIndividualPrfWithAnnotatonsLogsCompute(t *testing.T) {
defer internalregistry.ClearMonitoringClient()
client := fakemonitoring.NewClient("fake-client")
if err := internalregistry.RegisterMonitoringClient(client); err != nil {
t.Fatalf("internalregistry.RegisterMonitoringClient() err = %v, want nil", err)
}
kh, err := keyset.NewHandle(prf.HMACSHA256PRFKeyTemplate())
if err != nil {
t.Fatalf("keyset.NewHandle() err = %v, want nil", err)
}
manager := keyset.NewManagerFromHandle(kh)
hmac512KeyID, err := manager.Add(prf.HMACSHA512PRFKeyTemplate())
if err != nil {
t.Fatalf("manager.Add() err = %v, want nil", err)
}
aesKeyID, err := manager.Add(prf.AESCMACPRFKeyTemplate())
if err != nil {
t.Fatalf("manager.Add() err = %v, want nil", err)
}
kh, err = manager.Handle()
if err != nil {
t.Fatalf("manager.Handle() err = %v, want nil", err)
}
buff := &bytes.Buffer{}
if err := insecurecleartextkeyset.Write(kh, keyset.NewBinaryWriter(buff)); err != nil {
t.Fatalf("insecurecleartextkeyset.Write() err = %v, want nil", err)
}
annotations := map[string]string{"foo": "bar"}
mh, err := insecurecleartextkeyset.Read(keyset.NewBinaryReader(buff), keyset.WithAnnotations(annotations))
if err != nil {
t.Fatalf("insecurecleartextkeyset.Read() err = %v, want nil", err)
}
prfSet, err := prf.NewPRFSet(mh)
if err != nil {
t.Fatalf("prf.NewPRFSet() err = %v, want nil", err)
}
for _, p := range prfSet.PRFs {
if _, err := p.ComputePRF([]byte("input_data"), 16); err != nil {
t.Fatalf("p.ComputePRF() err = %v, want nil", err)
}
}
got := client.Events()
wantKeysetInfo := &monitoring.KeysetInfo{
PrimaryKeyID: kh.KeysetInfo().GetPrimaryKeyId(),
Entries: []*monitoring.Entry{
{
KeyID: kh.KeysetInfo().GetPrimaryKeyId(),
Status: monitoring.Enabled,
KeyType: "tink.HmacPrfKey",
KeyPrefix: "RAW",
},
{
KeyID: hmac512KeyID,
Status: monitoring.Enabled,
KeyType: "tink.HmacPrfKey",
KeyPrefix: "RAW",
},
{
KeyID: aesKeyID,
Status: monitoring.Enabled,
KeyType: "tink.AesCmacPrfKey",
KeyPrefix: "RAW",
},
},
Annotations: annotations,
}
want := []*fakemonitoring.LogEvent{
{
Context: monitoring.NewContext("prf", "compute", wantKeysetInfo),
KeyID: kh.KeysetInfo().GetKeyInfo()[0].GetKeyId(),
NumBytes: len("input_data"),
},
{
Context: monitoring.NewContext("prf", "compute", wantKeysetInfo),
KeyID: kh.KeysetInfo().GetKeyInfo()[1].GetKeyId(),
NumBytes: len("input_data"),
},
{
Context: monitoring.NewContext("prf", "compute", wantKeysetInfo),
KeyID: kh.KeysetInfo().GetKeyInfo()[2].GetKeyId(),
NumBytes: len("input_data"),
},
}
eventCmp := func(a, b *fakemonitoring.LogEvent) bool {
return a.KeyID < b.KeyID
}
if !cmp.Equal(got, want, cmpopts.SortSlices(eventCmp)) {
t.Errorf("got = %v, want = %v, with diff: %v", got, want, cmp.Diff(got, want))
}
}
func TestPrimitiveFactoryWithMonitoringAnnotationsLogsComputePRF(t *testing.T) {
defer internalregistry.ClearMonitoringClient()
client := fakemonitoring.NewClient("fake-client")
if err := internalregistry.RegisterMonitoringClient(client); err != nil {
t.Fatalf("internalregistry.RegisterMonitoringClient() err = %v, want nil", err)
}
kh, err := keyset.NewHandle(prf.HMACSHA256PRFKeyTemplate())
if err != nil {
t.Fatalf("keyset.NewHandle() err = %v, want nil", err)
}
buff := &bytes.Buffer{}
if err := insecurecleartextkeyset.Write(kh, keyset.NewBinaryWriter(buff)); err != nil {
t.Fatalf("insecurecleartextkeyset.Write() err = %v, want nil", err)
}
annotations := map[string]string{"foo": "bar"}
mh, err := insecurecleartextkeyset.Read(keyset.NewBinaryReader(buff), keyset.WithAnnotations(annotations))
if err != nil {
t.Fatalf("insecurecleartextkeyset.Read() err = %v, want nil", err)
}
prfSet, err := prf.NewPRFSet(mh)
if err != nil {
t.Fatalf("prf.NewPRFSet() err = %v, want nil", err)
}
data := []byte("some_data")
if _, err := prfSet.ComputePrimaryPRF(data, 20); err != nil {
t.Fatalf("prfSet.ComputePrimaryPRF() err = %v, want nil", err)
}
got := client.Events()
wantKeysetInfo := &monitoring.KeysetInfo{
PrimaryKeyID: kh.KeysetInfo().GetPrimaryKeyId(),
Entries: []*monitoring.Entry{
{
KeyID: kh.KeysetInfo().GetPrimaryKeyId(),
Status: monitoring.Enabled,
KeyType: "tink.HmacPrfKey",
KeyPrefix: "RAW",
},
},
Annotations: annotations,
}
want := []*fakemonitoring.LogEvent{
{
Context: monitoring.NewContext("prf", "compute", wantKeysetInfo),
KeyID: kh.KeysetInfo().GetPrimaryKeyId(),
NumBytes: len(data),
},
}
if !cmp.Equal(got, want) {
t.Errorf("got = %v, want = %v, with diff: %v", got, want, cmp.Diff(got, want))
}
}