Skip to content

Commit

Permalink
feat: add XChacha key to ECDH AEAD Tink key
Browse files Browse the repository at this point in the history
  • Loading branch information
Baha Shaaban committed Jan 8, 2021
1 parent b423cb5 commit 48ebe02
Show file tree
Hide file tree
Showing 16 changed files with 987 additions and 67 deletions.
10 changes: 10 additions & 0 deletions pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,14 @@ func init() {
if err != nil {
panic(fmt.Sprintf("ecdh.init() failed: %v", err))
}

err = registry.RegisterKeyManager(newECDHXChachaPublicKeyManager())
if err != nil {
panic(fmt.Sprintf("ecdh.init() failed: %v", err))
}

err = registry.RegisterKeyManager(newECDHXChachaPrivateKeyManager())
if err != nil {
panic(fmt.Sprintf("ecdh.init() failed: %v", err))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func TestECDHESPrivateKeyManager_Primitive(t *testing.T) {
c = tt.curveType
}

pubKeyProto := &ecdhpb.EcdhAeadPrivateKey{
privKeyProto := &ecdhpb.EcdhAeadPrivateKey{
Version: v,
PublicKey: &ecdhpb.EcdhAeadPublicKey{
Version: v, // if v > 0 to force an error when calling km.Primitive()
Expand All @@ -149,10 +149,10 @@ func TestECDHESPrivateKeyManager_Primitive(t *testing.T) {
KeyValue: d,
}

sPubKey, err := proto.Marshal(pubKeyProto)
sPrivKey, err := proto.Marshal(privKeyProto)
require.NoError(t, err)

p, err := km.Primitive(sPubKey)
p, err := km.Primitive(sPrivKey)
if bytes.Equal(tt.encTmp.Value, badSerializedFormat) {
require.EqualError(t, err, errInvalidECDHAESPrivateKey.Error(),
"ECDHESPrivate primitive from serialized key with invalid serialized key")
Expand Down Expand Up @@ -259,12 +259,12 @@ func TestEcdhesPrivateKeyManager_NewKey(t *testing.T) {
privKeyProto := &ecdhpb.EcdhAeadKeyFormat{
Params: &ecdhpb.EcdhAeadParams{
KwParams: &ecdhpb.EcdhKwParams{
CurveType: c, // unknown curve type to force an error when calling km.Primitive()
CurveType: c, // unknown curve type to force an error when calling km.NewKey()
},
EncParams: &ecdhpb.EcdhAeadEncParams{
AeadEnc: encT, // invalid data enc key template to force an error when calling km.Primitive()
AeadEnc: encT, // invalid data enc key template to force an error when calling km.NewKey()
},
EcPointFormat: ptFmt, // unknown EC Point format type to force an error when calling km.Primitive()
EcPointFormat: ptFmt, // unknown EC Point format type to force an error when calling km.NewKey()
},
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func (km *ecdhPublicKeyManager) NewKeyData(serializedKeyFormat []byte) (*tinkpb.
func (km *ecdhPublicKeyManager) validateKey(key *ecdhpb.EcdhAeadPublicKey) (elliptic.Curve, error) {
err := keyset.ValidateKeyVersion(key.Version, ecdhAESPublicKeyVersion)
if err != nil {
return nil, fmt.Errorf("ecdh_aes_publie_key_manager: invalid key: %w", err)
return nil, fmt.Errorf("ecdh_aes_public_key_manager: invalid key: %w", err)
}

return validateKeyFormat(key.Params)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ func ecdhAEADPublicKey(t *testing.T, c commonpb.EllipticCurveType, ptfmt commonp
}
}

// eciesAEADESPrivateKey returns a EciesAeadHkdfPrivateKey with specified parameters.
func eciesAEADESPrivateKey(t *testing.T, p *ecdhpb.EcdhAeadPublicKey, d []byte) *ecdhpb.EcdhAeadPrivateKey {
// ecdhesAEADPrivateKey returns a EcdhAeadPrivateKey with specified parameters.
func ecdhesAEADPrivateKey(t *testing.T, p *ecdhpb.EcdhAeadPublicKey, d []byte) *ecdhpb.EcdhAeadPrivateKey {
t.Helper()

return &ecdhpb.EcdhAeadPrivateKey{
Expand All @@ -146,7 +146,7 @@ func generateECDHAEADPrivateKey(t *testing.T, c commonpb.EllipticCurveType, ptfm

pubKey := ecdhAEADPublicKey(t, c, ptfmt, encT, pvt.PublicKey.Point.X.Bytes(), pvt.PublicKey.Point.Y.Bytes(), cek)

return eciesAEADESPrivateKey(t, pubKey, pvt.D.Bytes())
return ecdhesAEADPrivateKey(t, pubKey, pvt.D.Bytes())
}

func TestECDHESFactoryWithBadKeysetType(t *testing.T) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,61 @@ func ECDH521KWAES256GCMKeyTemplate() *tinkpb.KeyTemplate {
return createKeyTemplate(commonpb.EllipticCurveType_NIST_P521, nil)
}

// XChachaECDHKeyTemplate is a KeyTemplate that generates a key that accepts a CEK for XChacha20Poly1305 encryption. CEK
// wrapping is done outside of this Tink key (in the tinkcrypto service). It is used to represent a key to execute the
// CompositeDecrypt primitive with the following parameters:
// - Content Encryption: XChaha20Poly1305
// Keys from this template represent a valid recipient public/private key pairs and can be stored in the kms.
func XChachaECDHKeyTemplate() *tinkpb.KeyTemplate {
return createXChachaKeyTemplate(nil)
}

// AES256GCMKeyTemplateWithCEK is similar to ECDHAES256GCMKeyTemplate but adding the cek to execute the
// CompositeEncrypt primitive for encrypting a message targeted to one ore more recipients.
// Keys from this template offer valid CompositeEncrypt primitive execution only and should not be stored in the KMS.
// The key created from this template has no recipient key info linked to it. It is use exclusively used for primitive
// The key created from this template has no recipient key info linked to it. It is exclusively used for primitive
// execution.
func AES256GCMKeyTemplateWithCEK(cek []byte) *tinkpb.KeyTemplate {
// the curve passed in the template below is ignored when executing the primitive, it's hardcoded to pass key
// key format validation only.
return createKeyTemplate(0, cek)
}

// TODO add chacha key templates as well https://github.com/hyperledger/aries-framework-go/issues/1637
// XChachaKeyTemplateWithCEK is similar to XChachaKeyTemplate but adding the cek to execute the
// CompositeEncrypt primitive for encrypting a message targeted to one ore more recipients.
// Keys from this template offer valid CompositeEncrypt primitive execution only and should not be stored in the KMS.
// The key created from this template has no recipient key info linked to it. It is exclusively used for primitive
// execution.
func XChachaKeyTemplateWithCEK(cek []byte) *tinkpb.KeyTemplate {
return createXChachaKeyTemplate(cek)
}

// createXChachaKeyTemplate creates a new ECDH-AEAD key template with the set cek for primitive execution.
func createXChachaKeyTemplate(cek []byte) *tinkpb.KeyTemplate {
format := &ecdhpb.EcdhAeadKeyFormat{
Params: &ecdhpb.EcdhAeadParams{
KwParams: &ecdhpb.EcdhKwParams{
KeyType: ecdhpb.KeyType_OKP,
},
EncParams: &ecdhpb.EcdhAeadEncParams{
AeadEnc: aead.XChaCha20Poly1305KeyTemplate(),
CEK: cek,
},
EcPointFormat: commonpb.EcPointFormat_UNCOMPRESSED,
},
}

serializedFormat, err := proto.Marshal(format)
if err != nil {
panic("failed to marshal EcdhAeadKeyFormat proto for XChacha key")
}

return &tinkpb.KeyTemplate{
TypeUrl: ecdhXChachaPrivateKeyTypeURL,
Value: serializedFormat,
OutputPrefixType: tinkpb.OutputPrefixType_RAW,
}
}

// createKeyTemplate creates a new ECDH-AEAD key template with the set cek for primitive execution.
func createKeyTemplate(c commonpb.EllipticCurveType, cek []byte) *tinkpb.KeyTemplate {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ SPDX-License-Identifier: Apache-2.0
package ecdh

import (
"strings"
"testing"

"github.com/google/tink/go/keyset"
Expand Down Expand Up @@ -36,6 +37,10 @@ func TestECDHESKeyTemplateSuccess(t *testing.T) {
curveType: "P-521",
tmplFunc: ECDH521KWAES256GCMKeyTemplate,
},
{
tcName: "creat ECDH XChacha key templates test",
tmplFunc: XChachaECDHKeyTemplate,
},
}

for _, tt := range flagTests {
Expand Down Expand Up @@ -63,7 +68,11 @@ func TestECDHESKeyTemplateSuccess(t *testing.T) {
require.Empty(t, ct)

// now try to create a new KH for primitive execution and try to encrypt
kt = AES256GCMKeyTemplateWithCEK(cek)
if strings.Contains(tc.tcName, "XChacha") {
kt = XChachaKeyTemplateWithCEK(cek)
} else {
kt = AES256GCMKeyTemplateWithCEK(cek)
}

kh, err = keyset.NewHandle(kt)
require.NoError(t, err)
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
/*
Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package ecdh

import (
"crypto/ed25519"
"crypto/rand"
"errors"
"fmt"

"github.com/golang/protobuf/proto"
"github.com/google/tink/go/core/registry"
"github.com/google/tink/go/keyset"
tinkpb "github.com/google/tink/go/proto/tink_go_proto"

"github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto/primitive/composite"
"github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto/primitive/composite/ecdh/subtle"
ecdhpb "github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto/primitive/proto/ecdh_aead_go_proto"
"github.com/hyperledger/aries-framework-go/pkg/internal/cryptoutil"
)

const (
ecdhXChachaPrivateKeyVersion = 0
ecdhXChachaPrivateKeyTypeURL = "type.hyperledger.org/hyperledger.aries.crypto.tink.EcdhXChachaAeadPrivateKey"
)

// common errors.
var (
errInvalidECDHXChachaPrivateKey = errors.New("ecdh_xchacha_private_key_manager: invalid key")
errInvalidECDHXChachaPrivateKeyFormat = errors.New("ecdh_xchacha_private_key_manager: invalid key format")
)

// ecdhXChachaPrivateKeyManager is an implementation of PrivateKeyManager interface.
// It generates new ECDHPrivateKey (XChacha) keys and produces new instances of ECDHAEADCompositeDecrypt subtle.
type ecdhXChachaPrivateKeyManager struct{}

// Assert that ecdhXChachaPrivateKeyManager implements the PrivateKeyManager interface.
var _ registry.PrivateKeyManager = (*ecdhXChachaPrivateKeyManager)(nil)

// newECDHXChachaPrivateKeyManager creates a new ecdhXChachaPrivateKeyManager.
func newECDHXChachaPrivateKeyManager() *ecdhXChachaPrivateKeyManager {
return new(ecdhXChachaPrivateKeyManager)
}

// Primitive creates an ECDHESPrivateKey subtle for the given serialized ECDHESPrivateKey proto.
func (km *ecdhXChachaPrivateKeyManager) Primitive(serializedKey []byte) (interface{}, error) {
if len(serializedKey) == 0 {
return nil, errInvalidECDHXChachaPrivateKey
}

key := new(ecdhpb.EcdhAeadPrivateKey)

err := proto.Unmarshal(serializedKey, key)
if err != nil {
return nil, errInvalidECDHXChachaPrivateKey
}

err = km.validateKey(key)
if err != nil {
return nil, errInvalidECDHXChachaPrivateKey
}

rEnc, err := composite.NewRegisterCompositeAEADEncHelper(key.PublicKey.Params.EncParams.AeadEnc)
if err != nil {
return nil, fmt.Errorf("ecdh_xchacha_private_key_manager: NewRegisterCompositeAEADEncHelper "+
"failed: %w", err)
}

return subtle.NewECDHAEADCompositeDecrypt(rEnc, key.PublicKey.Params.EncParams.CEK), nil
}

// NewKey creates a new key according to the specification of ECDHESPrivateKey format.
func (km *ecdhXChachaPrivateKeyManager) NewKey(serializedKeyFormat []byte) (proto.Message, error) {
if len(serializedKeyFormat) == 0 {
return nil, errInvalidECDHXChachaPrivateKeyFormat
}

keyFormat := new(ecdhpb.EcdhAeadKeyFormat)

err := proto.Unmarshal(serializedKeyFormat, keyFormat)
if err != nil {
return nil, errInvalidECDHXChachaPrivateKeyFormat
}

err = validateKeyXChachaFormat(keyFormat.Params)
if err != nil {
return nil, errInvalidECDHXChachaPrivateKeyFormat
}

pub, pvt, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
return nil, fmt.Errorf("ecdh_xchacha_private_key_manager: GenerateECDHKeyPair failed: %w", err)
}

x25519Pub, err := cryptoutil.PublicEd25519toCurve25519(pub)
if err != nil {
return nil, fmt.Errorf("ecdh_xchacha_private_key_manager: Convert to X25519 pub key failed: %w", err)
}

x25519Pvt, err := cryptoutil.SecretEd25519toCurve25519(pvt)
if err != nil {
return nil, fmt.Errorf("ecdh_xchacha_private_key_manager: Convert to X25519 priv key failed: %w", err)
}

return &ecdhpb.EcdhXChachaAeadPrivateKey{
Version: ecdhXChachaPrivateKeyVersion,
KeyValue: x25519Pvt,
PublicKey: &ecdhpb.EcdhXChachaAeadPublicKey{
Version: ecdhXChachaPrivateKeyVersion,
Params: keyFormat.Params,
KeyValue: x25519Pub,
},
}, nil
}

// NewKeyData creates a new KeyData according to the specification of ECDHESPrivateKey Format.
// It should be used solely by the key management API.
func (km *ecdhXChachaPrivateKeyManager) NewKeyData(serializedKeyFormat []byte) (*tinkpb.KeyData, error) {
key, err := km.NewKey(serializedKeyFormat)
if err != nil {
return nil, err
}

serializedKey, err := proto.Marshal(key)
if err != nil {
return nil, fmt.Errorf("ecdhes_xchacha_private_key_manager: Proto.Marshal failed: %w", err)
}

return &tinkpb.KeyData{
TypeUrl: ecdhXChachaPrivateKeyTypeURL,
Value: serializedKey,
KeyMaterialType: tinkpb.KeyData_ASYMMETRIC_PRIVATE,
}, nil
}

// PublicKeyData returns the enclosed public key data of serializedPrivKey.
func (km *ecdhXChachaPrivateKeyManager) PublicKeyData(serializedPrivKey []byte) (*tinkpb.KeyData, error) {
privKey := new(ecdhpb.EcdhAeadPrivateKey)

err := proto.Unmarshal(serializedPrivKey, privKey)
if err != nil {
return nil, errInvalidECDHXChachaPrivateKey
}

serializedPubKey, err := proto.Marshal(privKey.PublicKey)
if err != nil {
return nil, errInvalidECDHXChachaPrivateKey
}

return &tinkpb.KeyData{
TypeUrl: ecdhXChachaPublicKeyTypeURL,
Value: serializedPubKey,
KeyMaterialType: tinkpb.KeyData_ASYMMETRIC_PUBLIC,
}, nil
}

// DoesSupport indicates if this key manager supports the given key type.
func (km *ecdhXChachaPrivateKeyManager) DoesSupport(typeURL string) bool {
return typeURL == ecdhXChachaPrivateKeyTypeURL
}

// TypeURL returns the key type of keys managed by this key manager.
func (km *ecdhXChachaPrivateKeyManager) TypeURL() string {
return ecdhXChachaPrivateKeyTypeURL
}

// validateKey validates the given ECDHPrivateKey and returns the KW curve.
func (km *ecdhXChachaPrivateKeyManager) validateKey(key *ecdhpb.EcdhAeadPrivateKey) error {
err := keyset.ValidateKeyVersion(key.Version, ecdhXChachaPrivateKeyVersion)
if err != nil {
return fmt.Errorf("ecdhes_xchacha_private_key_manager: invalid key: %w", err)
}

return validateKeyXChachaFormat(key.PublicKey.Params)
}

// validateKeyFormat validates the given ECDHESKeyFormat and returns the KW Curve.
func validateKeyXChachaFormat(params *ecdhpb.EcdhAeadParams) error {
var err error

km, err := registry.GetKeyManager(params.EncParams.AeadEnc.TypeUrl)
if err != nil {
return fmt.Errorf("ecdhes_aes_private_key_manager: GetKeyManager error: %w", err)
}

_, err = km.NewKeyData(params.EncParams.AeadEnc.Value)
if err != nil {
return fmt.Errorf("ecdhes_aes_private_key_manager: NewKeyData error: %w", err)
}

if params.KwParams.KeyType.String() != ecdhpb.KeyType_OKP.String() {
return fmt.Errorf("ecdhes_aes_private_key_manager: invalid key type %v", params.KwParams.KeyType)
}

return nil
}
Loading

0 comments on commit 48ebe02

Please sign in to comment.