Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v2.3.0 Removed APIs Break my App #26

Open
theory opened this issue Jan 15, 2025 · 2 comments
Open

v2.3.0 Removed APIs Break my App #26

theory opened this issue Jan 15, 2025 · 2 comments
Assignees

Comments

@theory
Copy link

theory commented Jan 15, 2025

From the v2.3.0 release notes:

Removed primitiveset.PrimitiveSet, keyset.Primitives and
keyset.PrimitivesWithKeyManager from the public API. This API was never
really intended to be used outside Tink. We are making changes to Tink
internals and we prefer to break users at compile time. If this affects you,
please file an issue.

I have an app that relies on primitiveset.PrimitiveSet and keyset.PrimitivesWithKeyManager. I suspect many implementing their own keys might have a challenge, but because my app creates a key that simply encapsulates an AEAD key and a MAC key they're fairly essential.

keyset.PrimitivesWithKeyManager Usage

As described in #14 and #16, my app generates custom key info on output. It depends on keyset.PrimitivesWithKeyManager to get at the key info it needs, similar to this (error handling omitted for clarity):

ps, err := handle.PrimitivesWithKeyManager(&infoManager{})
keyInfo := make([]*mypb.KeyInfo, len(ps.EntriesInKeysetOrder))

for i, e := range ps.EntriesInKeysetOrder {
	params, ok := (e.Primitive).(*mypb.MyParams)

	keyInfo[i] = &mypb.KeyInfo{
		TypeUri:     e.TypeURL,
		KeyId:       e.KeyID,
		AeadScheme:  params.GetAeadScheme(),
		MacScheme:   params.GetMacScheme(),
	}
}

primitiveset.PrimitiveSet Usage

My app's custom keys are a fairly straightforward bundling of an AEAD and MAC key as a single notional "key". It provides an interface that includes the functionality of both AEAD and MAC keys. To do so, the implementation simply wraps the underlying primitiveset.PrimitiveSet to get at the key material required for its functionality, more or less like so (error handling omitted for clarity):

type wrappedMyKey struct {
	ps *primitiveset.PrimitiveSet
}

func New(handle *keyset.Handle) (MyKey, error) {
	ps, err := handle.Primitives()
	// elided code that uses ps.Primary.Primitive to verify that all keys in the handle are MyKeys
	return &wrappedMyKey{ps: ps}, nil
}

func (wh *wrappedMyKey) Encrypt(plaintext, associatedData []byte) ([]byte, error) {
	primary := wh.ps.Primary
	p, ok := (primary.Primitive).(MyKey)

	ct, err := p.Encrypt(plaintext, associatedData)
	output := make([]byte, 0, len(primary.Prefix)+len(ct))
	output = append(output, primary.Prefix...)
	output = append(output, ct...)

	return output, nil
}

func (wh *wrappedMyKey) ComputeMAC(data []byte) ([]byte, error) {
	primary := wh.ps.Primary
	primitive, ok := (primary.Primitive).(MyKey)
	mac, err := primitive.ComputeMAC(data)
	return mac, nil
}

There's are also Decrypt method, naturally. Hopefully this makes it fairly clear what my use case is.

@morambro
Copy link
Contributor

morambro commented Jan 15, 2025

Hi @theory,

keyset.PrimitivesWithKeyManager Usage

For tink.AEAD primitives, it is possible to get individual keyset.Entrys for existing AEAD primitives as follows (omitting error handling):

ks := // some keyset
for i := 0; i < ks.Len(); i++ {
  e, err := ks.Entry(i);
  status := e.KeyStatus()
  keyID := e.KeyID()
  switch actualKey := k.(type) {
    case *aesgcm.Key:
      params := actualKey.Parameters().(*aesgcm.Parameters)
      // Can have access to the paramters, such as key size, IV size and TAG size.
    case *aesgcmsiv.Key: ...
  }
}

We are planning to add similar types for other primitives as well. Would that help?

primitiveset.PrimitiveSet Usage

Can these be two different (existing) keys or do they have to be one key? An option could be registering a key manager that creates MyKey using registry.RegisterKeyManager(...) and use it as an AEAD or MAC when aead.New(kh) or mac.New(kh) are needed. Would that work?

@morambro morambro self-assigned this Jan 15, 2025
@theory
Copy link
Author

theory commented Jan 15, 2025

Hi @morambro

We are planning to add similar types for other primitives as well. Would that help?

Yes, I believe so, as long as it iterates over all keys, not just active keys. I assume it's a pretty straightforward interface to implement, yes?

Can these be two different (existing) keys or do they have to be one key?

Not really; they're tightly linked in my app. It's fine that they're internally two keys; like I said, MyKey is really just a wrapper around the other two keys. Makes thing nice and tidy for users.

The photo is pretty simple:

message MyKey {
  uint32 version = 1;
  // Tracks the AEAD and Mac schemes used by this key.
  HarkParams params = 2;
  // Stores the AEAD key material and configuration.
  google.crypto.tink.KeyData aead_key_data = 3;
  // Stores the MAC key material and configuration.
  google.crypto.tink.KeyData mac_key_data = 4;
}

An option could be registering a key manager that creates MyKey using registry.RegisterKeyManager(...) and use it as an AEAD or MAC when aead.New(kh) or mac.New(kh) are needed. Would that work?

That's essentially how it's implemented now, though it uses templates, so relies on registry.GetKeyManager(template.GetTypeUrl()) to get the manager for each key (AEAD or Mac), then mgr.NewKeyData(template.GetValue()) to create each key.

But because I implemented MyKey using all of Tinks' patterns, including protobuf, the implementation of the methods that actually do encryption, decryption, and hashing needs to get at the underlying primitive set. I assume that anyone implementing their own key would need to so, as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants