-
Notifications
You must be signed in to change notification settings - Fork 7
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
Comments
Hi @theory,
For 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?
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 |
Hi @morambro
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?
Not really; they're tightly linked in my app. It's fine that they're internally two keys; like I said, 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 That's essentially how it's implemented now, though it uses templates, so relies on 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. |
Apologies for the delay.
Not necessarily, I think one would need this only to implement a new primitive (i.e., they need a
const typeURL = "type.googleapis.com/google.crypto.tink.MyKey"
// ...
type keyManager struct{}
var _ registry.KeyManager = (*keyManager)(nil)
type mykeyPrimitive struct {
// ...
}
var _ tink.AEAD = (*mykeyPrimitive)(nil)
var _ tink.MAC = (*mykeyPrimitive)(nil)
func (p *mykeyPrimitive) Encrypt(plaintext, associatedData []byte) ([]byte, error) { ... }
func (p *mykeyPrimitive) Decrypt(ciphertext, associatedData []byte) ([]byte, error) { ... }
func (p *mykeyPrimitive) ComputeMAC(data []byte) ([]byte, error) { ... }
func (p *mykeyPrimitive) VerifyMAC(mac, data []byte) error { ... }
func (km *keyManager) Primitive(serializedKey []byte) (any, error) {
myKey := new(mykeypb.MyKey)
err := proto.Unmarshal(serializedKey, myKey)
if err != nil {
return nil, err
}
// Marshall AEAD and MAC keys, get the key material, etc.
ret := &mykeyPrimitive{
// pass appropriate key material...
}
return ret, nil
}
func (km *keyManager) NewKey(serializedKeyFormat []byte) (proto.Message, error) {
keyFormat := new(mykeypb.MyKeyFormat)
if err := proto.Unmarshal(serializedKeyFormat, keyFormat); err != nil {
// err handling...
}
// ...create AEAD and MAC keys, and serialize them...
k := &mykeypb.MyKey{}
k.SetAeadKeyData(&tinkpb.KeyData{
TypeUrl: macURL,
Value: aeadKeyBytes,
KeyMaterialType: tinkpb.KeyData_SYMMETRIC,
})
k.SetMacKeyData(&tinkpb.KeyData{
TypeUrl: aeadURL,
Value: macKeyBytes,
KeyMaterialType: tinkpb.KeyData_SYMMETRIC,
})
return k, nil
}
func (km *keyManager) NewKeyData(serializedKeyFormat []byte) (*tinkpb.KeyData, error) {
key, err := km.NewKey(serializedKeyFormat)
// err handling...
serializedKey, err := proto.Marshal(key)
// err handling...
return &tinkpb.KeyData{
TypeUrl: typeURL,
Value: serializedKey,
KeyMaterialType: km.KeyMaterialType(),
}, nil
}
func (km *keyManager) DoesSupport(typeURL string) bool { return typeURL == km.TypeURL() }
func (km *keyManager) TypeURL() string { return typeURL }
func (km *keyManager) KeyMaterialType() tinkpb.KeyData_KeyMaterialType {
return tinkpb.KeyData_SYMMETRIC
}
km := &keyManager{}
if err := registry.RegisterKeyManager(km); err != nil {
t.Fatalf("Failed to register key manager: %v", err)
}
kh, err := keyset.NewHandle(createMyKeyTemplate(..., tinkpb.OutputPrefixType_TINK))
if err != nil {
t.Fatalf("Failed to create keyset handle: %v", err)
}
-, err := aead.New(kh)
if err != nil {
t.Fatalf("Failed to create AEAD: %v", err)
}
_, err := mac.New(kh)
if err != nil {
t.Fatalf("Failed to create MAC: %v", err)
} The produced ciphertext and MAC will use the same output prefix. |
That just might work. I'm trying it now, but one thing I didn't mention is that I have a method that calculates MACs for all of the keys in a Mac keyset, not just the primary key. For that, I've been using |
Sorry, that just applies to the need for In the meantime, I think I've confirmed that your proposal will work by mimicking the new 2.3.0 methods like so: type mimicHandle struct {
*keyset.Handle
}
func (h *mimicHandle) Primary() (*primitiveset.Entry, error) {
ps, err := h.Primitives()
if err != nil {
return nil, err
}
return ps.Primary, nil
}
func (h *mimicHandle) Len() int {
ps, err := h.Primitives()
if err != nil {
return 0
}
return len(ps.Entries)
}
func (h *mimicHandle) Entry(i int) (*primitiveset.Entry, error) {
ps, err := h.Primitives()
if err != nil {
return nil, err
}
if i < 0 || i >= len(ps.EntriesInKeysetOrder) {
return nil, fmt.Errorf("index %d out of range", i)
}
return ps.EntriesInKeysetOrder[i], nil
} I think that approximates the new methods pretty well, and they work for my use case nicely. When I'm able to upgrade, I should be able to eliminate this wrapper struct and be good to go. Have I overlooked anything? Once there are |
That's assuming that |
I've started experimenting with this. Sadly, it looks like keyset.Entry lacks a method for the key URL. |
Sadly, it does not look like there is any way to get at the individual key to do the encryption or MAC generation. In the example above, one can get at the |
Thanks for the replies
Yes, one would have to use the type to distinguish the key type, e.g.: switch entry.Key().(type) {
case *aesgcm.Key:
...
case *aesgcmsiv.Key:
...
}
One way to get a primitive for each key (without casting) is to import the key into a new keyset with only that key, and get the primitive out, for example: macs := [][]byte{}
for i := 0; i < handle.Len(); i++ {
entry, err := handle.Entry(i)
// err handling
km := keyset.NewManager()
id, err := km.AddKey(entry.Key())
// err handling
err := km.SetPrimary(id)
// err handling
newHandle, err := km.Handle()
// err handling
m, err := mac.New(newHandle)
// err handling
res, err := m.ComputeMAC(...)
// err handling
macs := append(macs, res)
} |
I guess that's do-able, but it means I'd have to map URLs to Tink-internal types. It would be nice if keys had all the same info easily available via KeysetInfo_KeyInfo was also provided by keyset.Entry.
Oh, okay, that works, though it's a bit fussy. Is there a more succinct way to do it via type coercion or assertion? |
And I just realized an issue without access to the primitives: my code was using the Prefix for my key, not from the aead key. I can change that, but it really feels like there ought to be a way for key implementations outside the tink-go distribution to be able to get at their own primitives. |
One last thing: how do I implement and integrate a 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;
} |
I was able to get everything to work. However, to access the params in my key, I had to hack a public function into tink in theory/tink-go@e36b890. I could find no other way. I looked into implementing |
As I said this works, but I was adding some tests and found that, because MyKey uses TINK prefix, both encrypted and hashed values have prefixes. That's fine for AEAD, but I don't want prefixes on the HASH digests. If I could use the underlying MAC key, which is configured for a RAW prefix, then I could do away with the prefix. In the meantime I'm just going to snip it off. |
I managed to get everything cleaned up and working perfectly using the aforementioned theory/tink-go@e36b890. The upshot to all this, I think, is then need for key implementations without access to Tink internals to be able to access the primitives generated by their own managers. I'm not sure how to do this; maybe managers could be given an |
To access individual keys as |
Ah-ha, that is exactly what I needed, thank you! For some reason I thought that API was private, or I would have used it before. Would have saved me quite a lot of aggravation (here and previously in #14). All good now, thanks! |
From the v2.3.0 release notes:
I have an app that relies on
primitiveset.PrimitiveSet
andkeyset.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
UsageAs 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):primitiveset.PrimitiveSet
UsageMy 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):There's are also
Decrypt
method, naturally. Hopefully this makes it fairly clear what my use case is.The text was updated successfully, but these errors were encountered: