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
This is a first change to add XChacha20Poly1305 keys to ECDH Tink keys to support XChacha AEAD encryption and key wrapping.

It includes adding new Tink key managers in the Tink keys package to support keys for:
1. NIST P curves KW - AES/XChacha20Poly1305 content encryption
2. X25519 KW - AES/XChacha20Poly1305 content encryption

This change also includes new key templates to support the creation of these ECDH keys.

Future changes will include supporting Key exports of these ECDH keys, adding new KMS key types, add XChacha Key wrapping and finally testing the new key with the JWE packers.

part of hyperledger-archives#1637, hyperledger-archives#1806, hyperledger-archives#1684, hyperledger-archives#815

Signed-off-by: Baha Shaaban <[email protected]>
  • Loading branch information
Baha Shaaban authored and sudeshrshetty committed Jan 22, 2022
1 parent 7fc59cc commit 97667c2
Show file tree
Hide file tree
Showing 29 changed files with 2,768 additions and 261 deletions.
2 changes: 1 addition & 1 deletion pkg/crypto/tinkcrypto/key_wrapper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ func Test_ksToPrivateECDSAKey_Failure(t *testing.T) {
_, err = ksToPrivateECDSAKey(recipientKeyPub)
require.EqualError(t, err, "ksToPrivateECDSAKey: failed to extract sender key: extractPrivKey: "+
"can't extract unsupported private key 'type.hyperledger.org/hyperledger.aries.crypto.tink"+
".EcdhAesAeadPublicKey'")
".EcdhNistPKwAesAeadPublicKey'")
}

func Test_ksToPublicECDSAKey_Failure(t *testing.T) {
Expand Down
34 changes: 32 additions & 2 deletions pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,42 @@ import (
// nolint: gochecknoinits
func init() {
// TODO - avoid the tink registry singleton.
err := registry.RegisterKeyManager(newECDHPrivateKeyManager())
err := registry.RegisterKeyManager(newECDHNISTPAESPrivateKeyManager())
if err != nil {
panic(fmt.Sprintf("ecdh.init() failed: %v", err))
}

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

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

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

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

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

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

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

This file was deleted.

106 changes: 91 additions & 15 deletions pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_factory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ SPDX-License-Identifier: Apache-2.0
package ecdh

import (
"crypto/ed25519"
"crypto/rand"
"encoding/base64"
"encoding/json"
"fmt"
Expand All @@ -27,6 +29,7 @@ import (

"github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto/primitive/composite"
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"
)

func TestECDHESFactory(t *testing.T) {
Expand All @@ -35,40 +38,65 @@ func TestECDHESFactory(t *testing.T) {
rawPtFmt := commonpb.EcPointFormat_COMPRESSED
primaryEncT := aead.AES128GCMKeyTemplate()
rawEncT := aead.AES256GCMKeyTemplate()
kt := ecdhpb.KeyType_EC
cek := random.GetRandomBytes(32)

primaryPrivProto := generateECDHAEADPrivateKey(t, c, primaryPtFmt, primaryEncT, cek)
primaryPrivProto := generateECDHAEADPrivateKey(t, c, primaryPtFmt, kt, primaryEncT, cek)

sPrimaryPriv, err := proto.Marshal(primaryPrivProto)
require.NoError(t, err)

primaryPrivKey := testutil.NewKey(
testutil.NewKeyData(ecdhAESPrivateKeyTypeURL, sPrimaryPriv, tinkpb.KeyData_ASYMMETRIC_PRIVATE),
testutil.NewKeyData(ecdhNISTPAESPrivateKeyTypeURL, sPrimaryPriv, tinkpb.KeyData_ASYMMETRIC_PRIVATE),
tinkpb.KeyStatusType_ENABLED, 8, tinkpb.OutputPrefixType_RAW)

rawPrivProto := generateECDHAEADPrivateKey(t, c, rawPtFmt, rawEncT, cek)
rawPrivProto := generateECDHAEADPrivateKey(t, c, rawPtFmt, kt, rawEncT, cek)

sRawPriv, err := proto.Marshal(rawPrivProto)
require.NoError(t, err)

rawPrivKey := testutil.NewKey(
testutil.NewKeyData(ecdhAESPrivateKeyTypeURL, sRawPriv, tinkpb.KeyData_ASYMMETRIC_PRIVATE),
testutil.NewKeyData(ecdhNISTPAESPrivateKeyTypeURL, sRawPriv, tinkpb.KeyData_ASYMMETRIC_PRIVATE),
tinkpb.KeyStatusType_ENABLED, 11, tinkpb.OutputPrefixType_RAW)

privKeys := []*tinkpb.Keyset_Key{primaryPrivKey, rawPrivKey}
x25519XChachaPrivProto := generateECDHAEADPrivateKey(t, commonpb.EllipticCurveType_CURVE25519, primaryPtFmt,
ecdhpb.KeyType_OKP, aead.XChaCha20Poly1305KeyTemplate(), cek)

sX25519XChachaPriv, err := proto.Marshal(x25519XChachaPrivProto)
require.NoError(t, err)

x25519XChachaPrivKey := testutil.NewKey(
testutil.NewKeyData(ecdhX25519XChachaPrivateKeyTypeURL, sX25519XChachaPriv, tinkpb.KeyData_ASYMMETRIC_PRIVATE),
tinkpb.KeyStatusType_ENABLED, 15, tinkpb.OutputPrefixType_RAW)

privKeys := []*tinkpb.Keyset_Key{primaryPrivKey, rawPrivKey, x25519XChachaPrivKey}
privKeyset := testutil.NewKeyset(privKeys[0].KeyId, privKeys)
khPriv, err := testkeyset.NewHandle(privKeyset)
require.NoError(t, err)

xPrivKeys := []*tinkpb.Keyset_Key{x25519XChachaPrivKey, rawPrivKey, primaryPrivKey}
xPrivKeyset := testutil.NewKeyset(xPrivKeys[0].KeyId, xPrivKeys)
xKHPriv, err := testkeyset.NewHandle(xPrivKeyset)
require.NoError(t, err)

khPub, err := khPriv.Public()
require.NoError(t, err)

xKHPub, err := xKHPriv.Public()
require.NoError(t, err)

e, err := NewECDHEncrypt(khPub)
require.NoError(t, err)

d, err := NewECDHDecrypt(khPriv)
require.NoError(t, err)

xe, err := NewECDHEncrypt(xKHPub)
require.NoError(t, err)

xd, err := NewECDHDecrypt(xKHPriv)
require.NoError(t, err)

for i := 0; i < 1000; i++ {
pt := random.GetRandomBytes(20)
aadRndNb := random.GetRandomBytes(10)
Expand All @@ -88,6 +116,9 @@ func TestECDHESFactory(t *testing.T) {
ct, err := e.Encrypt(pt, aad)
require.NoError(t, err)

xct, err := xe.Encrypt(pt, aad)
require.NoError(t, err)

// encrypt for single recipient will generate new AAD for recipient, extract it from ct
encData := &composite.EncryptedData{}
err = json.Unmarshal(ct, encData)
Expand All @@ -96,12 +127,16 @@ func TestECDHESFactory(t *testing.T) {
gotpt, err := d.Decrypt(ct, aad)
require.NoError(t, err)

xgotpt, err := xd.Decrypt(xct, aad)
require.NoError(t, err)

require.EqualValues(t, pt, gotpt)
require.EqualValues(t, pt, xgotpt)
}
}

// ecdhAEADPublicKey returns a EcdhAeadPublicKey with specified parameters.
func ecdhAEADPublicKey(t *testing.T, c commonpb.EllipticCurveType, ptfmt commonpb.EcPointFormat,
func ecdhAEADPublicKey(t *testing.T, c commonpb.EllipticCurveType, ptfmt commonpb.EcPointFormat, kt ecdhpb.KeyType,
encT *tinkpb.KeyTemplate, x, y, cek []byte) *ecdhpb.EcdhAeadPublicKey {
t.Helper()

Expand All @@ -110,6 +145,7 @@ func ecdhAEADPublicKey(t *testing.T, c commonpb.EllipticCurveType, ptfmt commonp
Params: &ecdhpb.EcdhAeadParams{
KwParams: &ecdhpb.EcdhKwParams{
CurveType: c,
KeyType: kt,
},
EncParams: &ecdhpb.EcdhAeadEncParams{
AeadEnc: encT,
Expand All @@ -122,8 +158,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 @@ -135,18 +171,57 @@ func eciesAEADESPrivateKey(t *testing.T, p *ecdhpb.EcdhAeadPublicKey, d []byte)

// generateECDHAEADPrivateKey generates a new EC key pair and returns the private key proto.
func generateECDHAEADPrivateKey(t *testing.T, c commonpb.EllipticCurveType, ptfmt commonpb.EcPointFormat,
encT *tinkpb.KeyTemplate, cek []byte) *ecdhpb.EcdhAeadPrivateKey {
kt ecdhpb.KeyType, encT *tinkpb.KeyTemplate, cek []byte) *ecdhpb.EcdhAeadPrivateKey {
t.Helper()

if ecdhpb.KeyType_OKP.String() == kt.String() {
return buildXChachaKey(t, ptfmt, encT, c, cek)
}

curve, err := hybrid.GetCurve(c.String())
require.NoError(t, err)

pvt, err := hybrid.GenerateECDHKeyPair(curve)
require.NoError(t, err)

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

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

func buildXChachaKey(t *testing.T, ptfmt commonpb.EcPointFormat, encT *tinkpb.KeyTemplate, c commonpb.EllipticCurveType,
cek []byte) *ecdhpb.EcdhAeadPrivateKey {
pub, pvt, err := ed25519.GenerateKey(rand.Reader)
require.NoError(t, err)

x25519Pub, err := cryptoutil.PublicEd25519toCurve25519(pub)
require.NoError(t, err)

x25519Pvt, err := cryptoutil.SecretEd25519toCurve25519(pvt)
require.NoError(t, err)

params := &ecdhpb.EcdhAeadParams{
KwParams: &ecdhpb.EcdhKwParams{
KeyType: ecdhpb.KeyType_OKP,
CurveType: c,
},
EncParams: &ecdhpb.EcdhAeadEncParams{
AeadEnc: encT,
CEK: cek,
},
EcPointFormat: ptfmt,
}

return eciesAEADESPrivateKey(t, pubKey, pvt.D.Bytes())
return &ecdhpb.EcdhAeadPrivateKey{
Version: 0,
KeyValue: x25519Pvt,
PublicKey: &ecdhpb.EcdhAeadPublicKey{
Version: 0,
Params: params,
X: x25519Pub,
},
}
}

func TestECDHESFactoryWithBadKeysetType(t *testing.T) {
Expand All @@ -155,24 +230,25 @@ func TestECDHESFactoryWithBadKeysetType(t *testing.T) {
rawPtFmt := commonpb.EcPointFormat_COMPRESSED
primaryEncT := aead.AES128GCMKeyTemplate()
rawEncT := aead.AES256GCMKeyTemplate()
kt := ecdhpb.KeyType_EC
cek := random.GetRandomBytes(32)

primaryPrivProto := generateECDHAEADPrivateKey(t, c, primaryPtFmt, primaryEncT, cek)
primaryPrivProto := generateECDHAEADPrivateKey(t, c, primaryPtFmt, kt, primaryEncT, cek)

sPrimaryPriv, err := proto.Marshal(primaryPrivProto)
require.NoError(t, err)

primaryPrivKey := testutil.NewKey(
testutil.NewKeyData(ecdhAESPrivateKeyTypeURL, sPrimaryPriv, tinkpb.KeyData_ASYMMETRIC_PRIVATE),
testutil.NewKeyData(ecdhNISTPAESPrivateKeyTypeURL, sPrimaryPriv, tinkpb.KeyData_ASYMMETRIC_PRIVATE),
tinkpb.KeyStatusType_ENABLED, 8, tinkpb.OutputPrefixType_RAW)

rawPrivProto := generateECDHAEADPrivateKey(t, c, rawPtFmt, rawEncT, cek)
rawPrivProto := generateECDHAEADPrivateKey(t, c, rawPtFmt, kt, rawEncT, cek)

sRawPriv, err := proto.Marshal(rawPrivProto)
require.NoError(t, err)

rawPrivKey := testutil.NewKey(
testutil.NewKeyData(ecdhAESPrivateKeyTypeURL, sRawPriv, tinkpb.KeyData_ASYMMETRIC_PRIVATE),
testutil.NewKeyData(ecdhNISTPAESPrivateKeyTypeURL, sRawPriv, tinkpb.KeyData_ASYMMETRIC_PRIVATE),
tinkpb.KeyStatusType_ENABLED, 11, tinkpb.OutputPrefixType_RAW)

badPrivKeyProto, err := testutil.GenerateECIESAEADHKDFPrivateKey(c, commonpb.HashType_SHA256, primaryPtFmt,
Expand Down
Loading

0 comments on commit 97667c2

Please sign in to comment.