// 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)) } }