Skip to content

Commit

Permalink
Add proto serialization for X-AES-GCM keys and parameters
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 707797361
Change-Id: I5cf9cbcd637dd41d4b12d74be718e5ed601b8d65
  • Loading branch information
morambro authored and copybara-github committed Dec 19, 2024
1 parent a94ff67 commit e565fde
Show file tree
Hide file tree
Showing 4 changed files with 511 additions and 5 deletions.
5 changes: 0 additions & 5 deletions aead/xaesgcm/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// Package xaesgcm provides a Tink key type for X-AES-GCM, which is specified
// in https://c2sp.org/XAES-256-GCM.
//
// This implementation uses AES-CMAC as the KDF to derive the per-message key,
// as per NIST SP 800-108 Rev. 1.
package xaesgcm

import (
Expand Down
162 changes: 162 additions & 0 deletions aead/xaesgcm/protoserialization.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// 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 xaesgcm

import (
"fmt"

"google.golang.org/protobuf/proto"
"github.com/tink-crypto/tink-go/v2/insecuresecretdataaccess"
"github.com/tink-crypto/tink-go/v2/internal/protoserialization"
"github.com/tink-crypto/tink-go/v2/key"
"github.com/tink-crypto/tink-go/v2/secretdata"
tinkpb "github.com/tink-crypto/tink-go/v2/proto/tink_go_proto"
xaesgcmpb "github.com/tink-crypto/tink-go/v2/proto/x_aes_gcm_go_proto"
)

const (
// protoVersion is the accepted [xaesgcmpb.XAesGcmKey] proto version.
//
// Currently, only version 0 is supported; other versions are rejected.
protoVersion = 0
typeURL = "type.googleapis.com/google.crypto.tink.XAesGcmKey"
)

type keySerializer struct{}

var _ protoserialization.KeySerializer = (*keySerializer)(nil)

func protoOutputPrefixTypeFromVariant(variant Variant) (tinkpb.OutputPrefixType, error) {
switch variant {
case VariantTink:
return tinkpb.OutputPrefixType_TINK, nil
case VariantNoPrefix:
return tinkpb.OutputPrefixType_RAW, nil
default:
return tinkpb.OutputPrefixType_UNKNOWN_PREFIX, fmt.Errorf("unknown output prefix variant: %v", variant)
}
}

func (s *keySerializer) SerializeKey(key key.Key) (*protoserialization.KeySerialization, error) {
that, ok := key.(*Key)
if !ok {
return nil, fmt.Errorf("invalid key type: got %T, want *xaesgcm.Key", key)
}
thatParameters := that.Parameters().(*Parameters)
outputPrefixType, err := protoOutputPrefixTypeFromVariant(thatParameters.Variant())
if err != nil {
return nil, err
}
keyBytes := that.KeyBytes()
protoKey := &xaesgcmpb.XAesGcmKey{
KeyValue: keyBytes.Data(insecuresecretdataaccess.Token{}),
Params: &xaesgcmpb.XAesGcmParams{
SaltSize: uint32(thatParameters.SaltSizeInBytes()),
},
Version: protoVersion,
}
serializedKey, err := proto.Marshal(protoKey)
if err != nil {
return nil, err
}
// idRequirement is zero if the key doesn't have a key requirement.
idRequirement, _ := that.IDRequirement()
keyData := &tinkpb.KeyData{
TypeUrl: typeURL,
Value: serializedKey,
KeyMaterialType: tinkpb.KeyData_SYMMETRIC,
}
return protoserialization.NewKeySerialization(keyData, outputPrefixType, idRequirement)
}

type keyParser struct{}

var _ protoserialization.KeyParser = (*keyParser)(nil)

func variantFromProto(prefixType tinkpb.OutputPrefixType) (Variant, error) {
switch prefixType {
case tinkpb.OutputPrefixType_TINK:
return VariantTink, nil
case tinkpb.OutputPrefixType_RAW:
return VariantNoPrefix, nil
default:
return VariantUnknown, fmt.Errorf("unsupported output prefix type: %v", prefixType)
}
}

func (s *keyParser) ParseKey(keySerialization *protoserialization.KeySerialization) (key.Key, error) {
if keySerialization == nil {
return nil, fmt.Errorf("key serialization is nil")
}
keyData := keySerialization.KeyData()
if keyData.GetTypeUrl() != typeURL {
return nil, fmt.Errorf("invalid type URL: got %s, want %s", keyData.GetTypeUrl(), typeURL)
}
if keyData.GetKeyMaterialType() != tinkpb.KeyData_SYMMETRIC {
return nil, fmt.Errorf("invalid key material type: got %v, want %v", keyData.GetKeyMaterialType(), tinkpb.KeyData_SYMMETRIC)
}
protoKey := new(xaesgcmpb.XAesGcmKey)
if err := proto.Unmarshal(keyData.GetValue(), protoKey); err != nil {
return nil, err
}
if protoKey.GetVersion() != protoVersion {
return nil, fmt.Errorf("unsupported key version: got %d, want %d", protoKey.GetVersion(), protoVersion)
}
variant, err := variantFromProto(keySerialization.OutputPrefixType())
if err != nil {
return nil, err
}

saltSize := protoKey.GetParams().GetSaltSize()
params, err := NewParameters(variant, int(saltSize))
if err != nil {
return nil, err
}
keyMaterial := secretdata.NewBytesFromData(protoKey.GetKeyValue(), insecuresecretdataaccess.Token{})
// keySerialization.IDRequirement() returns zero if the key doesn't have a
// key requirement.
keyID, _ := keySerialization.IDRequirement()
return NewKey(keyMaterial, keyID, params)
}

type parametersSerializer struct{}

var _ protoserialization.ParametersSerializer = (*parametersSerializer)(nil)

func (s *parametersSerializer) Serialize(parameters key.Parameters) (*tinkpb.KeyTemplate, error) {
thatParameters, ok := parameters.(*Parameters)
if !ok {
return nil, fmt.Errorf("invalid parameters type: got %T, want *xaesgcm.Parameters", parameters)
}
outputPrefixType, err := protoOutputPrefixTypeFromVariant(thatParameters.Variant())
if err != nil {
return nil, err
}
format := &xaesgcmpb.XAesGcmKeyFormat{
Params: &xaesgcmpb.XAesGcmParams{
SaltSize: uint32(thatParameters.SaltSizeInBytes()),
},
Version: protoVersion,
}
serializedFormat, err := proto.Marshal(format)
if err != nil {
return nil, err
}
return &tinkpb.KeyTemplate{
TypeUrl: typeURL,
OutputPrefixType: outputPrefixType,
Value: serializedFormat,
}, nil
}
Loading

0 comments on commit e565fde

Please sign in to comment.