tink icon indicating copy to clipboard operation
tink copied to clipboard

using aead subtle and getting invalid key length of 24 bytes.

Open natemccallum opened this issue 5 years ago • 2 comments

I generate an AES128_GCM keyset and get the following.

{
    "primaryKeyId": 1637900800,
    "key": [{
        "keyData": {
            "typeUrl": "type.googleapis.com/google.crypto.tink.AesGcmKey",
            "keyMaterialType": "SYMMETRIC",
            "value": "GhClmueYsj1Byzk2zlyh83JT"
        },
        "outputPrefixType": "TINK",
        "keyId": 1637900800,
        "status": "ENABLED"
    }]
}

I want to give the "value" to the client that will be encrypting.

	aesgcm, err := subtle.NewAESGCM([]byte("GhClmueYsj1Byzk2zlyh83JT"))

But here I get and error:

Failed to get AESGCM: aes_gcm: invalid AES key size; want 16 or 32, got 24

If I give the entire keyset then we can read in and use with

k, err :=insecurecleartextkeyset.Read(keyset.NewJSONReader(bytes.NewReader(keyset)))
...
a, err := aead.New(khPub)
...
a.Encrypt(...)

As I add keys to the keyset for additional customers I was hoping to just have to send them the "value" for their individual key. Otherwise I have to create each customer a keyset then copy paste their key into the "master" keyset file that the decrypt server is using. Am I doing this properly or am I all screwed up? :)

natemccallum avatar Nov 19 '20 15:11 natemccallum

What actually is stored in "value" is a base64 encoded protocol buffer of the key. If you want to extract this you can do the following:

import (                                                                                                                                                                                                                                                                                  
    "encoding/base64"                                                                                                                                                                                                                                                                 
    "github.com/golang/protobuf/proto"                                                                                                                                                                                                                                                
    gcmpb "github.com/google/tink/go/proto/aes_gcm_go_proto"                                                                                                                                                                                                                              
)                                                                                                                                                                                                                                                                                         
...                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
serializedKey, _ := base64.StdEncoding.DecodeString("GhClmueYsj1Byzk2zlyh83JT")                                                                                                                                                                                                   
key := new(gcmpb.AesGcmKey)                                                                                                                                                                                                                                                       
proto.Unmarshal(serializedKey, key)                                                                                                                                                                                                                                               
key_value := key.GetKeyValue()
...

This should give you the 16-byte key which is used for encrypting with AES-GCM. In general I think it would be better here to pass the "key" object to the client which can add it to the keyset, instead of trying to alter the values as this would preserve the other fields.

kste avatar Nov 20 '20 12:11 kste

Thank you kste! I was able to obtain the key and perform an Encrypt on the sender and Decrypt on the receiver.

The Decrypt fails if I try to decrypt with the full key from the keyset but if I do the exact same thing that you show with the key on the receiver then the decrypt works. I believe this is what you were referring to when you state 'better to pass the "key" object ... to preserve the other fields.'

The keyset allows me to take advantage of the status Enabled/Disabled field to "revoke" keys as well.

So I guess my options are 1) send the key object to each sender or 2) send just the key but then I have to manage the key and revoking on my own. I suppose another option is send the key and id to the sender and they simply build the key like so:

    id := 342771635
    key := "GhCk1JCnnNI5MLTFToZzQGEA"
    da := fmt.Sprintf(`
            {
                "primaryKeyId": %d,
                "key": [{
                    "keyData": {
                        "typeUrl": "type.googleapis.com/google.crypto.tink.AesGcmKey",
                        "keyMaterialType": "SYMMETRIC",
                        "value": "%s"
                    },
                    "outputPrefixType": "TINK",
                    "keyId": %d,
                    "status": "ENABLED"
                }]
            }`, id, key, id)

    khPub, err := insecurecleartextkeyset.Read(keyset.NewJSONReader(bytes.NewReader([]byte(da))))

Although, this means all keys better be AesGcm type. ;)

I do prefer the send entire key object, which leads to next question. When I add a key to a keyset I have to manually extract that key to give it to the sender. Is there a way to do that with a little less brute force? The program sending the encrypted messages is a standalone 3rd party app that will simply read a key file, there is no mechanism to programmatically send a key to the sender from the receiver.

natemccallum avatar Nov 20 '20 18:11 natemccallum

In Java (head) there is now an API for this using the normal API. In KeysetHandle you call getAt(i).getKey(), you call this to an AesGcmKey object, and you obtain the information from there.

tholenst avatar Jan 26 '23 15:01 tholenst

Sorry, in Go this is not yet available, but we are working on it. Feel free to reopen, though it's unlikely to change the priority as we have a lot of work to do.

tholenst avatar Jan 26 '23 15:01 tholenst