Skip to content

Commit

Permalink
Make subtle.AESGCM and alias of aesgcm.AEAD and deprecate it
Browse files Browse the repository at this point in the history
This requires returning `aesgcm.AEAD` from the key manager, to avoid circular dependencies.

PiperOrigin-RevId: 695348533
Change-Id: Ia19f8a676d8da340e3cc34040a85cf784e9a818f
  • Loading branch information
morambro authored and copybara-github committed Nov 11, 2024
1 parent 58f182b commit 3aae21b
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 59 deletions.
48 changes: 33 additions & 15 deletions aead/aesgcm/key_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ import (
"io"

"google.golang.org/protobuf/proto"
"github.com/tink-crypto/tink-go/v2/aead/subtle"
"github.com/tink-crypto/tink-go/v2/core/registry"
"github.com/tink-crypto/tink-go/v2/insecuresecretdataaccess"
"github.com/tink-crypto/tink-go/v2/internal/aead"
"github.com/tink-crypto/tink-go/v2/internal/internalapi"
"github.com/tink-crypto/tink-go/v2/keyset"
"github.com/tink-crypto/tink-go/v2/secretdata"
"github.com/tink-crypto/tink-go/v2/subtle/random"
gcmpb "github.com/tink-crypto/tink-go/v2/proto/aes_gcm_go_proto"
tinkpb "github.com/tink-crypto/tink-go/v2/proto/tink_go_proto"
Expand All @@ -49,18 +51,34 @@ func (km *keyManager) Primitive(serializedKey []byte) (any, error) {
if len(serializedKey) == 0 {
return nil, errInvalidKey
}
key := new(gcmpb.AesGcmKey)
if err := proto.Unmarshal(serializedKey, key); err != nil {
protoKey := new(gcmpb.AesGcmKey)
if err := proto.Unmarshal(serializedKey, protoKey); err != nil {
return nil, errInvalidKey
}
if err := km.validateKey(key); err != nil {
if err := km.validateKey(protoKey); err != nil {
return nil, err
}
ret, err := subtle.NewAESGCM(key.KeyValue)

keyBytes := secretdata.NewBytesFromData(protoKey.GetKeyValue(), insecuresecretdataaccess.Token{})
opts := ParametersOpts{
KeySizeInBytes: keyBytes.Len(),
IVSizeInBytes: ivSize,
TagSizeInBytes: tagSize,
Variant: VariantNoPrefix,
}
parameters, err := NewParameters(opts)
if err != nil {
return nil, fmt.Errorf("aes_gcm_key_manager: cannot create new parameters: %s", err)
}
key, err := NewKey(keyBytes, 0, parameters)
if err != nil {
return nil, fmt.Errorf("aes_gcm_key_manager: cannot create new key: %s", err)
}
primitive, err := NewAEAD(key)
if err != nil {
return nil, fmt.Errorf("aes_gcm_key_manager: cannot create new primitive: %s", err)
return nil, fmt.Errorf("aes_gcm_key_manager: cannot create new AEAD: %s", err)
}
return ret, nil
return primitive, nil
}

// NewKey creates a new key according to specification the given serialized AESGCMKeyFormat.
Expand All @@ -75,10 +93,10 @@ func (km *keyManager) NewKey(serializedKeyFormat []byte) (proto.Message, error)
if err := km.validateKeyFormat(keyFormat); err != nil {
return nil, fmt.Errorf("aes_gcm_key_manager: invalid key format: %s", err)
}
keyValue := random.GetRandomBytes(keyFormat.KeySize)
keyBytes := random.GetRandomBytes(keyFormat.KeySize)
return &gcmpb.AesGcmKey{
Version: keyVersion,
KeyValue: keyValue,
KeyValue: keyBytes,
}, nil
}

Expand Down Expand Up @@ -128,14 +146,14 @@ func (km *keyManager) DeriveKey(serializedKeyFormat []byte, pseudorandomness io.
return nil, fmt.Errorf("aes_gcm_key_manager: invalid key version: %s", err)
}

keyValue := make([]byte, keyFormat.GetKeySize())
if _, err := io.ReadFull(pseudorandomness, keyValue); err != nil {
keyBytes := make([]byte, keyFormat.GetKeySize())
if _, err := io.ReadFull(pseudorandomness, keyBytes); err != nil {
return nil, fmt.Errorf("aes_gcm_key_manager: not enough pseudorandomness given")
}

return &gcmpb.AesGcmKey{
Version: keyVersion,
KeyValue: keyValue,
KeyValue: keyBytes,
}, nil
}

Expand All @@ -144,16 +162,16 @@ func (km *keyManager) validateKey(key *gcmpb.AesGcmKey) error {
if err := keyset.ValidateKeyVersion(key.Version, keyVersion); err != nil {
return fmt.Errorf("aes_gcm_key_manager: %s", err)
}
keySize := uint32(len(key.KeyValue))
if err := subtle.ValidateAESKeySize(keySize); err != nil {
keySize := uint32(len(key.GetKeyValue()))
if err := aead.ValidateAESKeySize(keySize); err != nil {
return fmt.Errorf("aes_gcm_key_manager: %s", err)
}
return nil
}

// validateKeyFormat validates the given AESGCMKeyFormat.
func (km *keyManager) validateKeyFormat(format *gcmpb.AesGcmKeyFormat) error {
if err := subtle.ValidateAESKeySize(format.KeySize); err != nil {
if err := aead.ValidateAESKeySize(format.KeySize); err != nil {
return fmt.Errorf("aes_gcm_key_manager: %s", err)
}
return nil
Expand Down
25 changes: 20 additions & 5 deletions aead/aesgcm/key_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ import (
"github.com/google/go-cmp/cmp"
"google.golang.org/protobuf/proto"
"github.com/tink-crypto/tink-go/v2/aead/aesgcm"
"github.com/tink-crypto/tink-go/v2/aead/subtle"
"github.com/tink-crypto/tink-go/v2/core/registry"
"github.com/tink-crypto/tink-go/v2/insecuresecretdataaccess"
"github.com/tink-crypto/tink-go/v2/internal/internalapi"
"github.com/tink-crypto/tink-go/v2/internal/internalregistry"
"github.com/tink-crypto/tink-go/v2/secretdata"
"github.com/tink-crypto/tink-go/v2/subtle/random"
"github.com/tink-crypto/tink-go/v2/testutil"
gcmpb "github.com/tink-crypto/tink-go/v2/proto/aes_gcm_go_proto"
Expand Down Expand Up @@ -435,16 +436,30 @@ func validateAESGCMKey(key *gcmpb.AesGcmKey, format *gcmpb.AesGcmKeyFormat) erro
if key.Version != testutil.AESGCMKeyVersion {
return fmt.Errorf("incorrect key version")
}
// try to encrypt and decrypt
p, err := subtle.NewAESGCM(key.KeyValue)
keyValue := secretdata.NewBytesFromData(key.GetKeyValue(), insecuresecretdataaccess.Token{})
opts := aesgcm.ParametersOpts{
KeySizeInBytes: keyValue.Len(),
IVSizeInBytes: 12,
TagSizeInBytes: 16,
Variant: aesgcm.VariantNoPrefix,
}
params, err := aesgcm.NewParameters(opts)
if err != nil {
return fmt.Errorf("aesgcm.NewParameters(%v) err = %v, want nil", opts, err)
}
k, err := aesgcm.NewKey(keyValue, 0, params)
if err != nil {
return fmt.Errorf("aesgcm.NewKey() err = %v, want nil", err)
}
p, err := aesgcm.NewAEAD(k)
if err != nil {
return fmt.Errorf("invalid key")
return fmt.Errorf("aesgcm.NewAEAD() err = %v, want nil", err)
}
return validateAESGCMPrimitive(p, key)
}

func validateAESGCMPrimitive(p any, key *gcmpb.AesGcmKey) error {
cipher := p.(*subtle.AESGCM)
cipher := p.(*aesgcm.AEAD)
// try to encrypt and decrypt
pt := random.GetRandomBytes(32)
aad := random.GetRandomBytes(32)
Expand Down
62 changes: 23 additions & 39 deletions aead/subtle/aes_gcm.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,11 @@
package subtle

import (
"crypto/cipher"
"fmt"

internalaead "github.com/tink-crypto/tink-go/v2/internal/aead"
"github.com/tink-crypto/tink-go/v2/subtle/random"
"github.com/tink-crypto/tink-go/v2/tink"
"github.com/tink-crypto/tink-go/v2/aead/aesgcm"
"github.com/tink-crypto/tink-go/v2/insecuresecretdataaccess"
"github.com/tink-crypto/tink-go/v2/secretdata"
)

const (
Expand All @@ -32,44 +31,29 @@ const (
maxIntPlaintextSize = maxInt - AESGCMIVSize - AESGCMTagSize
)

// AESGCM is an implementation of AEAD interface.
type AESGCM struct {
cipher cipher.AEAD
}

// Assert that AESGCM implements the AEAD interface.
var _ tink.AEAD = (*AESGCM)(nil)
// AESGCM is an implementation of the [tink.AEAD] interface.
//
// This primitive adds no prefix to the ciphertext.
type AESGCM = aesgcm.AEAD

// NewAESGCM returns an AESGCM instance, where key is the AES key with length
// 16 bytes (AES-128) or 32 bytes (AES-256).
// NewAESGCM returns an [*AESGCM] value from the given key.
//
// The key must be of length 16 or 32 bytes. IV and TAG sizes are fixed to 12
// and 16 bytes respectively.
func NewAESGCM(key []byte) (*AESGCM, error) {
c, err := internalaead.NewAESGCMCipher(key)
if err != nil {
return nil, err
opts := aesgcm.ParametersOpts{
KeySizeInBytes: len(key),
IVSizeInBytes: AESGCMIVSize,
TagSizeInBytes: AESGCMTagSize,
Variant: aesgcm.VariantNoPrefix,
}
return &AESGCM{cipher: c}, nil
}

// Encrypt encrypts plaintext with associatedData. The returned ciphertext
// contains both the IV used for encryption and the actual ciphertext.
//
// Note: The crypto library's AES-GCM implementation always returns the
// ciphertext with an AESGCMTagSize (16-byte) tag.
func (a *AESGCM) Encrypt(plaintext, associatedData []byte) ([]byte, error) {
if err := internalaead.CheckPlaintextSize(uint64(len(plaintext))); err != nil {
return nil, err
params, err := aesgcm.NewParameters(opts)
if err != nil {
return nil, fmt.Errorf("subtle.NewAESGCM: %v", err)
}
iv := random.GetRandomBytes(AESGCMIVSize)
dst := make([]byte, 0, len(iv)+len(plaintext)+a.cipher.Overhead())
dst = append(dst, iv...)
return a.cipher.Seal(dst, iv, plaintext, associatedData), nil
}

// Decrypt decrypts ciphertext with associatedData.
func (a *AESGCM) Decrypt(ciphertext, associatedData []byte) ([]byte, error) {
if len(ciphertext) < AESGCMIVSize+AESGCMTagSize {
return nil, fmt.Errorf("ciphertext with size %d is too short", len(ciphertext))
k, err := aesgcm.NewKey(secretdata.NewBytesFromData(key, insecuresecretdataaccess.Token{}), 0, params)
if err != nil {
return nil, fmt.Errorf("subtle.NewAESGCM: %v", err)
}
iv := ciphertext[:AESGCMIVSize]
return a.cipher.Open(nil, iv, ciphertext[AESGCMIVSize:], associatedData)
return aesgcm.NewAEAD(k)
}

0 comments on commit 3aae21b

Please sign in to comment.