Skip to content

Commit

Permalink
Introduce basic Configuration API. It's an internal change.
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 681516730
Change-Id: I7553c34c38dee2d0d30e19058dfa50e6289134af
  • Loading branch information
LizaTretyakova authored and copybara-github committed Oct 2, 2024
1 parent 09b081f commit 3a8e130
Show file tree
Hide file tree
Showing 3 changed files with 250 additions and 0 deletions.
28 changes: 28 additions & 0 deletions internal/configuration/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "configuration",
srcs = ["configuration.go"],
importpath = "github.com/tink-crypto/tink-go/v2/internal/configuration",
visibility = ["//:__subpackages__"],
deps = [
"//internal/internalapi",
"//key",
],
)

alias(
name = "go_default_library",
actual = ":configuration",
visibility = ["//:__subpackages__"],
)

go_test(
name = "configuration_test",
srcs = ["configuration_test.go"],
deps = [
":configuration",
"//internal/internalapi",
"//key",
],
)
69 changes: 69 additions & 0 deletions internal/configuration/configuration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright 2024 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 configuration provides internal implementation of Configurations.
package configuration

import (
"fmt"
"reflect"

"github.com/tink-crypto/tink-go/v2/internal/internalapi"
"github.com/tink-crypto/tink-go/v2/key"
)

// Configuration keeps a collection of functions that create a primitive from
// [key.Key].
//
// This is an internal API.
type Configuration struct {
primitiveCreators map[reflect.Type]primitiveConstructor
}

type primitiveConstructor func(key key.Key) (any, error)

// PrimitiveFromKey creates a primitive from the given [key.Key]. Returns an
// error if there is no primitiveConstructor registered for the given key.
//
// This is an internal API.
func (c *Configuration) PrimitiveFromKey(k key.Key, _ internalapi.Token) (any, error) {
keyType := reflect.TypeOf(k)
creator, ok := c.primitiveCreators[keyType]
if !ok {
return nil, fmt.Errorf("PrimitiveFromKey: no primitive creator from key %v registered", keyType)
}
return creator(k)
}

// RegisterPrimitiveConstructor registers a primitiveConstructor for the keyType.
// Not thread-safe.
//
// Returns an error if a primitiveConstructor for the keyType already
// registered (no matter whether it's the same object or different, since
// constructors are of type [Func] and they are never considered equal in Go
// unless they are nil).
//
// This is an internal API.
func (c *Configuration) RegisterPrimitiveConstructor(keyType reflect.Type, constructor primitiveConstructor, _ internalapi.Token) error {
if _, ok := c.primitiveCreators[keyType]; ok {
return fmt.Errorf("RegisterPrimitiveConstructor: attempt to register a different primitive constructor for the same key type %v", keyType)
}
c.primitiveCreators[keyType] = constructor
return nil
}

// New creates an empty Configuration.
func New() (*Configuration, error) {
return &Configuration{map[reflect.Type]primitiveConstructor{}}, nil
}
153 changes: 153 additions & 0 deletions internal/configuration/configuration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// Copyright 2024 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 configuration_test

import (
"reflect"
"testing"

"github.com/tink-crypto/tink-go/v2/internal/configuration"
"github.com/tink-crypto/tink-go/v2/internal/internalapi"
"github.com/tink-crypto/tink-go/v2/key"
)

type testParameters0 struct{}

func (tp testParameters0) HasIDRequirement() bool { return false }
func (tp testParameters0) Equals(other key.Parameters) bool {
return false
}

type testKey0 struct{}

func (tk testKey0) Parameters() key.Parameters { return new(testParameters0) }
func (tk testKey0) IDRequirement() (id uint32, required bool) { return 0, false }
func (tk testKey0) Equals(other key.Key) bool { return false }

type testPrimitive0 struct{}

type testParameters1 struct{}

func (tp testParameters1) HasIDRequirement() bool { return false }
func (tp testParameters1) Equals(other key.Parameters) bool {
return false
}

type testKey1 struct{}

func (tk testKey1) Parameters() key.Parameters { return new(testParameters1) }
func (tk testKey1) IDRequirement() (id uint32, required bool) { return 0, false }
func (tk testKey1) Equals(other key.Key) bool { return false }

type testPrimitive1 struct{}

type testKeyUnregistered struct{}

func (tk testKeyUnregistered) Parameters() key.Parameters { return new(testParameters1) }
func (tk testKeyUnregistered) IDRequirement() (id uint32, required bool) { return 0, false }
func (tk testKeyUnregistered) Equals(other key.Key) bool { return false }

func TestConfigurationWorks(t *testing.T) {
testConfiguration, err := configuration.New()
if err != nil {
t.Fatalf("Configuration.New() err = %v, want nil", err)
}
token := internalapi.Token{}

err = testConfiguration.RegisterPrimitiveConstructor(reflect.TypeFor[testKey0](), func(k key.Key) (any, error) { return testPrimitive0{}, nil }, token)
if err != nil {
t.Fatalf("testConfiguration.RegisterPrimitiveConstructor() err = %v, want nil", err)
}

p0, err := testConfiguration.PrimitiveFromKey(testKey0{}, token)
if err != nil {
t.Fatalf("testConfiguration.PrimitiveFromKey() err = %v, want nil", err)
}
if reflect.TypeOf(p0) != reflect.TypeFor[testPrimitive0]() {
t.Errorf("Wrong primitive returned: got %T, want testPrimitive0", p0)
}
}

func TestMultiplePrimitiveConstructors(t *testing.T) {
testConfiguration, err := configuration.New()
if err != nil {
t.Fatalf("configuration.New() err = %v, want nil", err)
}
token := internalapi.Token{}

err = testConfiguration.RegisterPrimitiveConstructor(reflect.TypeFor[testKey0](), func(k key.Key) (any, error) { return testPrimitive0{}, nil }, token)
if err != nil {
t.Fatalf("testConfiguration.RegisterPrimitiveConstructor() err = %v, want nil", err)
}
err = testConfiguration.RegisterPrimitiveConstructor(reflect.TypeFor[testKey1](), func(k key.Key) (any, error) { return testPrimitive1{}, nil }, token)
if err != nil {
t.Fatalf("testConfiguration.RegisterPrimitiveConstructor() err = %v, want nil", err)
}

p0, err := testConfiguration.PrimitiveFromKey(testKey0{}, token)
if err != nil {
t.Fatalf("testConfiguration.RegisterPrimitiveConstructor() err = %v, want nil", err)
}
if reflect.TypeOf(p0) != reflect.TypeFor[testPrimitive0]() {
t.Errorf("Wrong primitive returned: got %T, want testPrimitive0", p0)
}
p1, err := testConfiguration.PrimitiveFromKey(testKey1{}, token)
if err != nil {
t.Fatalf("testConfiguration.RegisterPrimitiveConstructor() err = %v, want nil", err)
}
if reflect.TypeOf(p1) != reflect.TypeFor[testPrimitive1]() {
t.Errorf("Wrong primitive returned: got %T, want testPrimitive0", p1)
}
}

func TestRegisterDifferentPrimitiveConstructor(t *testing.T) {
testConfiguration, err := configuration.New()
if err != nil {
t.Fatalf("configuration.New() err = %v, want nil", err)
}
token := internalapi.Token{}

err = testConfiguration.RegisterPrimitiveConstructor(reflect.TypeFor[testKey1](), func(k key.Key) (any, error) { return testPrimitive1{}, nil }, token)
if err != nil {
t.Fatalf("testConfiguration.RegisterPrimitiveConstructor() err = %v, want nil", err)
}

// Register another primitiveCreator for the same key type fails.
err = testConfiguration.RegisterPrimitiveConstructor(reflect.TypeFor[testKey1](), func(k key.Key) (any, error) { return testPrimitive0{}, nil }, token)
if err == nil {
t.Errorf("testConfiguration.RegisterPrimitiveConstructor() err = nil, want error")
}
}

func TestUnregisteredPrimitive(t *testing.T) {
testConfiguration, err := configuration.New()
if err != nil {
t.Fatalf("configuration.New() err = %v, want nil", err)
}
token := internalapi.Token{}

err = testConfiguration.RegisterPrimitiveConstructor(reflect.TypeFor[testKey0](), func(k key.Key) (any, error) { return testPrimitive0{}, nil }, token)
if err != nil {
t.Fatalf("testConfiguration.RegisterPrimitiveConstructor() err = %v, want nil", err)
}

res, err := testConfiguration.PrimitiveFromKey(testKeyUnregistered{}, token)
if res != nil {
t.Errorf("testConfiguration.PrimitiveFromKey() return value = %v, want nil", res)
}
if err == nil {
t.Errorf("testConfiguration.PrimitiveFromKey() err = nil, want error")
}
}

0 comments on commit 3a8e130

Please sign in to comment.