diff --git a/pkg/crypto/tinkcrypto/key_wrapper_test.go b/pkg/crypto/tinkcrypto/key_wrapper_test.go index a7283840bf..38a5e744f6 100644 --- a/pkg/crypto/tinkcrypto/key_wrapper_test.go +++ b/pkg/crypto/tinkcrypto/key_wrapper_test.go @@ -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) { diff --git a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh.go b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh.go index 0273d1ba5c..b77043278c 100644 --- a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh.go +++ b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh.go @@ -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)) } diff --git a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_aes_aead_public_key_manager.go b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_aes_aead_public_key_manager.go deleted file mode 100644 index 439d4c0548..0000000000 --- a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_aes_aead_public_key_manager.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package ecdh - -import ( - "crypto/elliptic" - "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" -) - -const ( - ecdhAESPublicKeyVersion = 0 - ecdhAESPublicKeyTypeURL = "type.hyperledger.org/hyperledger.aries.crypto.tink.EcdhAesAeadPublicKey" -) - -// common errors. -var errInvalidECDHAESPublicKey = errors.New("ecdh_aes_public_key_manager: invalid key") - -// ecdhPublicKeyManager is an implementation of KeyManager interface. -// It generates new ECDHPublicKey (AES) keys and produces new instances of ECDHAEADCompositeEncrypt subtle. -type ecdhPublicKeyManager struct{} - -// Assert that ecdhPublicKeyManager implements the KeyManager interface. -var _ registry.KeyManager = (*ecdhPublicKeyManager)(nil) - -// newECDHPublicKeyManager creates a new ecdhPublicKeyManager. -func newECDHPublicKeyManager() *ecdhPublicKeyManager { - return new(ecdhPublicKeyManager) -} - -// Primitive creates an ECDHESPublicKey subtle for the given serialized ECDHESPublicKey proto. -func (km *ecdhPublicKeyManager) Primitive(serializedKey []byte) (interface{}, error) { - if len(serializedKey) == 0 { - return nil, errInvalidECDHAESPublicKey - } - - ecdhPubKey := new(ecdhpb.EcdhAeadPublicKey) - - err := proto.Unmarshal(serializedKey, ecdhPubKey) - if err != nil { - return nil, errInvalidECDHAESPublicKey - } - - _, err = km.validateKey(ecdhPubKey) - if err != nil { - return nil, errInvalidECDHAESPublicKey - } - - rEnc, err := composite.NewRegisterCompositeAEADEncHelper(ecdhPubKey.Params.EncParams.AeadEnc) - if err != nil { - return nil, fmt.Errorf("ecdh_aes_public_key_manager: NewRegisterCompositeAEADEncHelper "+ - "failed: %w", err) - } - - return subtle.NewECDHAEADCompositeEncrypt(rEnc, ecdhPubKey.Params.EncParams.CEK), nil -} - -// DoesSupport indicates if this key manager supports the given key type. -func (km *ecdhPublicKeyManager) DoesSupport(typeURL string) bool { - return typeURL == ecdhAESPublicKeyTypeURL -} - -// TypeURL returns the key type of keys managed by this key manager. -func (km *ecdhPublicKeyManager) TypeURL() string { - return ecdhAESPublicKeyTypeURL -} - -// NewKey is not implemented for public key manager. -func (km *ecdhPublicKeyManager) NewKey(serializedKeyFormat []byte) (proto.Message, error) { - return nil, errors.New("ecdh_aes_public_key_manager: NewKey not implemented") -} - -// NewKeyData is not implemented for public key manager. -func (km *ecdhPublicKeyManager) NewKeyData(serializedKeyFormat []byte) (*tinkpb.KeyData, error) { - return nil, errors.New("ecdh_aes_public_key_manager: NewKeyData not implemented") -} - -// validateKey validates the given ECDHESPublicKey. -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 validateKeyFormat(key.Params) -} diff --git a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_factory_test.go b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_factory_test.go index 416e82828a..a2069d6652 100644 --- a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_factory_test.go +++ b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_factory_test.go @@ -7,6 +7,8 @@ SPDX-License-Identifier: Apache-2.0 package ecdh import ( + "crypto/ed25519" + "crypto/rand" "encoding/base64" "encoding/json" "fmt" @@ -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) { @@ -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) @@ -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) @@ -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() @@ -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, @@ -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{ @@ -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) { @@ -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, diff --git a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_key_template.go b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_key_template.go index 1eb6af08b4..12b16ecedc 100644 --- a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_key_template.go +++ b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_key_template.go @@ -15,59 +15,137 @@ import ( ecdhpb "github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto/primitive/proto/ecdh_aead_go_proto" ) -// ECDH256KWAES256GCMKeyTemplate is a KeyTemplate that generates a key that accepts a CEK for AES256-GCM 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: +// ECDH256KWAES256GCMKeyTemplate is a KeyTemplate that generates a key that accepts a CEK for AES256-GCM content +// 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: AES256-GCM // Keys from this template represent a valid recipient public/private key pairs and can be stored in the KMS. The -// recipient key represented in this key template uses NIST curve P-256. +// recipient key represented in this key template uses the following key wrapping curve: +// - NIST curve P-256. func ECDH256KWAES256GCMKeyTemplate() *tinkpb.KeyTemplate { - return createKeyTemplate(commonpb.EllipticCurveType_NIST_P256, nil) + return createKeyTemplate(true, true, commonpb.EllipticCurveType_NIST_P256, nil) } -// ECDH384KWAES256GCMKeyTemplate is a KeyTemplate that generates a key that accepts a CEK for AES256-GCM 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: +// ECDH384KWAES256GCMKeyTemplate is a KeyTemplate that generates a key that accepts a CEK for AES256-GCM content +// 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: AES256-GCM // Keys from this template represent a valid recipient public/private key pairs and can be stored in the KMS. The -// recipient key represented in this key template uses NIST curve P-384. +// recipient key represented in this key template uses the following key wrapping curve: +// - NIST curve P-384 func ECDH384KWAES256GCMKeyTemplate() *tinkpb.KeyTemplate { - return createKeyTemplate(commonpb.EllipticCurveType_NIST_P384, nil) + return createKeyTemplate(true, true, commonpb.EllipticCurveType_NIST_P384, nil) } -// ECDH521KWAES256GCMKeyTemplate is a KeyTemplate that generates a key that accepts a CEK for AES256-GCM 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: +// ECDH521KWAES256GCMKeyTemplate is a KeyTemplate that generates a key that accepts a CEK for AES256-GCM content +// 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: AES256-GCM // Keys from this template represent a valid recipient public/private key pairs and can be stored in the KMS. The -// recipient key represented in this key template uses NIST curve P-521. +// recipient key represented in this key template uses the following key wrapping curve: +// - NIST curve P-521 func ECDH521KWAES256GCMKeyTemplate() *tinkpb.KeyTemplate { - return createKeyTemplate(commonpb.EllipticCurveType_NIST_P521, nil) + return createKeyTemplate(true, true, commonpb.EllipticCurveType_NIST_P521, 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. +// ECDH256KWXChachaKeyTemplate is a KeyTemplate that generates a key that accepts a CEK for XChacha20Poly1305 content +// 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: XChacha20Poly1305 +// Keys from this template represent a valid recipient public/private key pairs and can be stored in the KMS. The +// recipient key represented in this key template uses the following key wrapping curve: +// - NIST curve P-256. +func ECDH256KWXChachaKeyTemplate() *tinkpb.KeyTemplate { + return createKeyTemplate(true, false, commonpb.EllipticCurveType_NIST_P256, nil) +} + +// ECDH384KWXChachaKeyTemplate is a KeyTemplate that generates a key that accepts a CEK for XChacha20Poly1305 content +// 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: XChacha20Poly1305 +// Keys from this template represent a valid recipient public/private key pairs and can be stored in the KMS. The +// recipient key represented in this key template uses the following key wrapping curve: +// - NIST curve P-384 +func ECDH384KWXChachaKeyTemplate() *tinkpb.KeyTemplate { + return createKeyTemplate(true, false, commonpb.EllipticCurveType_NIST_P384, nil) +} + +// ECDH521KWXChachaKeyTemplate is a KeyTemplate that generates a key that accepts a CEK for XChacha20Poly1305 content +// 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: XChacha20Poly1305 +// Keys from this template represent a valid recipient public/private key pairs and can be stored in the KMS. The +// recipient key represented in this key template uses the following key wrapping curve: +// - NIST curve P-521 +func ECDH521KWXChachaKeyTemplate() *tinkpb.KeyTemplate { + return createKeyTemplate(true, false, commonpb.EllipticCurveType_NIST_P521, nil) +} + +// X25519XChachaECDHKeyTemplate is a KeyTemplate that generates a key that accepts a CEK for XChacha20Poly1305 content +// 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.The +// recipient key represented in this key template uses the following key wrapping curve: +// - Curve25519 +func X25519XChachaECDHKeyTemplate() *tinkpb.KeyTemplate { + return createKeyTemplate(false, false, commonpb.EllipticCurveType_CURVE25519, nil) +} + +// X25519AES256GCMECDHKeyTemplate is a KeyTemplate that generates a key that accepts a CEK for AES256-GCM content +// 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: AES256-GCM +// Keys from this template represent a valid recipient public/private key pairs and can be stored in the KMS.The +// recipient key represented in this key template uses the following key wrapping curve: +// - Curve25519 +func X25519AES256GCMECDHKeyTemplate() *tinkpb.KeyTemplate { + return createKeyTemplate(false, true, commonpb.EllipticCurveType_CURVE25519, nil) +} + +// AES256GCMKeyTemplateWithCEK is similar to ECDH256KWAES256GCMKeyTemplate but adding the cek to execute the +// CompositeEncrypt primitive for encrypting a message targeted to one ore more recipients. KW is not executed by this +// template, so it is ignored and set to NIST P Curved key by default. // 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) + return createKeyTemplate(true, true, 0, cek) } -// TODO add chacha key templates as well https://github.com/hyperledger/aries-framework-go/issues/1637 +// XChachaKeyTemplateWithCEK is similar to X25519XChachaECDHKeyTemplate 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 createKeyTemplate(false, false, 0, cek) +} + +// createKeyTemplate creates a new ECDH-AEAD key template with the set cek for primitive execution. Boolean flags used: +// - nistpKW flag to state if kw is either NIST P curves (true) or Curve25519 (false) +// - aesEnc flag to state if content encryption is either AES256-GCM (true) or XChacha20Poly1305 (false) +func createKeyTemplate(nistpKW, aesEnc bool, c commonpb.EllipticCurveType, cek []byte) *tinkpb.KeyTemplate { + var encTemplate *tinkpb.KeyTemplate + + typeURL, keyType := getTypeParams(nistpKW, aesEnc) + + if aesEnc { + encTemplate = aead.AES256GCMKeyTemplate() + } else { + encTemplate = aead.XChaCha20Poly1305KeyTemplate() + } -// createKeyTemplate creates a new ECDH-AEAD key template with the set cek for primitive execution. -func createKeyTemplate(c commonpb.EllipticCurveType, cek []byte) *tinkpb.KeyTemplate { format := &ecdhpb.EcdhAeadKeyFormat{ Params: &ecdhpb.EcdhAeadParams{ KwParams: &ecdhpb.EcdhKwParams{ CurveType: c, - KeyType: ecdhpb.KeyType_EC, + KeyType: keyType, }, EncParams: &ecdhpb.EcdhAeadEncParams{ - AeadEnc: aead.AES256GCMKeyTemplate(), + AeadEnc: encTemplate, CEK: cek, }, EcPointFormat: commonpb.EcPointFormat_UNCOMPRESSED, @@ -80,8 +158,24 @@ func createKeyTemplate(c commonpb.EllipticCurveType, cek []byte) *tinkpb.KeyTemp } return &tinkpb.KeyTemplate{ - TypeUrl: ecdhAESPrivateKeyTypeURL, + TypeUrl: typeURL, Value: serializedFormat, OutputPrefixType: tinkpb.OutputPrefixType_RAW, } } + +func getTypeParams(nispKW, aesEnc bool) (string, ecdhpb.KeyType) { + if nispKW { + if aesEnc { + return ecdhNISTPAESPrivateKeyTypeURL, ecdhpb.KeyType_EC + } + + return ecdhNISTPXChachaPrivateKeyTypeURL, ecdhpb.KeyType_EC + } + + if aesEnc { + return ecdhX25519AESPrivateKeyTypeURL, ecdhpb.KeyType_OKP + } + + return ecdhX25519XChachaPrivateKeyTypeURL, ecdhpb.KeyType_OKP +} diff --git a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_key_template_test.go b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_key_template_test.go index 0782007124..6b6a95fcbd 100644 --- a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_key_template_test.go +++ b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_key_template_test.go @@ -7,6 +7,7 @@ SPDX-License-Identifier: Apache-2.0 package ecdh import ( + "strings" "testing" "github.com/google/tink/go/keyset" @@ -17,24 +18,40 @@ import ( func TestECDHESKeyTemplateSuccess(t *testing.T) { flagTests := []struct { - tcName string - curveType string - tmplFunc func() *tinkpb.KeyTemplate + tcName string + tmplFunc func() *tinkpb.KeyTemplate }{ { - tcName: "create ECDH AES-GCM 256 key templates test", - curveType: "P-256", - tmplFunc: ECDH256KWAES256GCMKeyTemplate, + tcName: "create ECDH NIST P-256 KW with AES256-GCM key templates test", + tmplFunc: ECDH256KWAES256GCMKeyTemplate, }, { - tcName: "create ECDH AES-GCM 384 key templates test", - curveType: "P-384", - tmplFunc: ECDH384KWAES256GCMKeyTemplate, + tcName: "create ECDH NIST P-384 KW AES256-GCM key templates test", + tmplFunc: ECDH384KWAES256GCMKeyTemplate, }, { - tcName: "create ECDH AES-GCM 521 key templates test", - curveType: "P-521", - tmplFunc: ECDH521KWAES256GCMKeyTemplate, + tcName: "create ECDH NIST P-521 KW AES256-GCM key templates test", + tmplFunc: ECDH521KWAES256GCMKeyTemplate, + }, + { + tcName: "creat ECDH X25519 KW with XChacha20Poly1305 key templates test", + tmplFunc: X25519XChachaECDHKeyTemplate, + }, + { + tcName: "create ECDH NIST P-256 KW with XChacha20Poly1305 key templates test", + tmplFunc: ECDH256KWXChachaKeyTemplate, + }, + { + tcName: "create ECDH NIST P-384 KW XChacha20Poly1305 key templates test", + tmplFunc: ECDH384KWXChachaKeyTemplate, + }, + { + tcName: "create ECDH NIST P-521 KW key XChacha20Poly1305 templates test", + tmplFunc: ECDH521KWXChachaKeyTemplate, + }, + { + tcName: "creat ECDH X25519 KW with AES256-GCM key templates test", + tmplFunc: X25519AES256GCMECDHKeyTemplate, }, } @@ -63,7 +80,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) diff --git a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_aes_aead_private_key_manager.go b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_nistpkw_aesaead_private_key_manager.go similarity index 52% rename from pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_aes_aead_private_key_manager.go rename to pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_nistpkw_aesaead_private_key_manager.go index f7f984765c..f9f5e4595e 100644 --- a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_aes_aead_private_key_manager.go +++ b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_nistpkw_aesaead_private_key_manager.go @@ -23,49 +23,50 @@ import ( ) const ( - ecdhAESPrivateKeyVersion = 0 - ecdhAESPrivateKeyTypeURL = "type.hyperledger.org/hyperledger.aries.crypto.tink.EcdhAesAeadPrivateKey" + ecdhNISTPAESPrivateKeyVersion = 0 + ecdhNISTPAESPrivateKeyTypeURL = "type.hyperledger.org/hyperledger.aries.crypto.tink.EcdhNistPKwAesAeadPrivateKey" ) // common errors. var ( - errInvalidECDHAESPrivateKey = errors.New("ecdh_aes_private_key_manager: invalid key") - errInvalidECDHAESPrivateKeyFormat = errors.New("ecdh_aes_private_key_manager: invalid key format") + errInvalidECDHNISTPAESPrivateKey = errors.New("ecdh_nistpkw_aesaead_private_key_manager: invalid key") + errInvalidECDHNISTPAESPrivateKeyFormat = errors.New("ecdh_nistpkw_aesaead_private_key_manager: invalid key format") // nolint:lll ) -// ecdhAESPrivateKeyManager is an implementation of PrivateKeyManager interface. -// It generates new ECDHPrivateKey (AES) keys and produces new instances of ECDHAEADCompositeDecrypt subtle. -type ecdhAESPrivateKeyManager struct{} +// ecdhNISTPAESPrivateKeyManager is an implementation of PrivateKeyManager interface for NIST P curved key wrapping and +// AES-GCM content encryption. +// It generates new ECDHPrivateKey (NIST P KW) keys and produces new instances of ECDHAEADCompositeDecrypt subtle. +type ecdhNISTPAESPrivateKeyManager struct{} -// Assert that ecdhAESPrivateKeyManager implements the PrivateKeyManager interface. -var _ registry.PrivateKeyManager = (*ecdhAESPrivateKeyManager)(nil) +// Assert that ecdhNISTPAESPrivateKeyManager implements the PrivateKeyManager interface. +var _ registry.PrivateKeyManager = (*ecdhNISTPAESPrivateKeyManager)(nil) -// newECDHPrivateKeyManager creates a new ecdhAESPrivateKeyManager. -func newECDHPrivateKeyManager() *ecdhAESPrivateKeyManager { - return new(ecdhAESPrivateKeyManager) +// newECDHNISTPAESPrivateKeyManager creates a new ecdhNISTPAESPrivateKeyManager. +func newECDHNISTPAESPrivateKeyManager() *ecdhNISTPAESPrivateKeyManager { + return new(ecdhNISTPAESPrivateKeyManager) } // Primitive creates an ECDHESPrivateKey subtle for the given serialized ECDHESPrivateKey proto. -func (km *ecdhAESPrivateKeyManager) Primitive(serializedKey []byte) (interface{}, error) { +func (km *ecdhNISTPAESPrivateKeyManager) Primitive(serializedKey []byte) (interface{}, error) { if len(serializedKey) == 0 { - return nil, errInvalidECDHAESPrivateKey + return nil, errInvalidECDHNISTPAESPrivateKey } key := new(ecdhpb.EcdhAeadPrivateKey) err := proto.Unmarshal(serializedKey, key) if err != nil { - return nil, errInvalidECDHAESPrivateKey + return nil, errInvalidECDHNISTPAESPrivateKey } _, err = km.validateKey(key) if err != nil { - return nil, errInvalidECDHAESPrivateKey + return nil, errInvalidECDHNISTPAESPrivateKey } rEnc, err := composite.NewRegisterCompositeAEADEncHelper(key.PublicKey.Params.EncParams.AeadEnc) if err != nil { - return nil, fmt.Errorf("ecdh_aes_private_key_manager: NewRegisterCompositeAEADEncHelper "+ + return nil, fmt.Errorf("ecdh_nistpkw_aesaead_private_key_manager: NewRegisterCompositeAEADEncHelper "+ "failed: %w", err) } @@ -73,33 +74,33 @@ func (km *ecdhAESPrivateKeyManager) Primitive(serializedKey []byte) (interface{} } // NewKey creates a new key according to the specification of ECDHESPrivateKey format. -func (km *ecdhAESPrivateKeyManager) NewKey(serializedKeyFormat []byte) (proto.Message, error) { +func (km *ecdhNISTPAESPrivateKeyManager) NewKey(serializedKeyFormat []byte) (proto.Message, error) { if len(serializedKeyFormat) == 0 { - return nil, errInvalidECDHAESPrivateKeyFormat + return nil, errInvalidECDHNISTPAESPrivateKeyFormat } keyFormat := new(ecdhpb.EcdhAeadKeyFormat) err := proto.Unmarshal(serializedKeyFormat, keyFormat) if err != nil { - return nil, errInvalidECDHAESPrivateKeyFormat + return nil, errInvalidECDHNISTPAESPrivateKeyFormat } curve, err := validateKeyFormat(keyFormat.Params) if err != nil { - return nil, errInvalidECDHAESPrivateKeyFormat + return nil, errInvalidECDHNISTPAESPrivateKeyFormat } pvt, err := hybrid.GenerateECDHKeyPair(curve) if err != nil { - return nil, fmt.Errorf("ecdh_aes_private_key_manager: GenerateECDHKeyPair failed: %w", err) + return nil, fmt.Errorf("ecdh_nistpkw_aesaead_private_key_manager: GenerateECDHKeyPair failed: %w", err) } return &ecdhpb.EcdhAeadPrivateKey{ - Version: ecdhAESPrivateKeyVersion, + Version: ecdhNISTPAESPrivateKeyVersion, KeyValue: pvt.D.Bytes(), PublicKey: &ecdhpb.EcdhAeadPublicKey{ - Version: ecdhAESPrivateKeyVersion, + Version: ecdhNISTPAESPrivateKeyVersion, Params: keyFormat.Params, X: pvt.PublicKey.Point.X.Bytes(), Y: pvt.PublicKey.Point.Y.Bytes(), @@ -109,7 +110,7 @@ func (km *ecdhAESPrivateKeyManager) NewKey(serializedKeyFormat []byte) (proto.Me // NewKeyData creates a new KeyData according to the specification of ECDHESPrivateKey Format. // It should be used solely by the key management API. -func (km *ecdhAESPrivateKeyManager) NewKeyData(serializedKeyFormat []byte) (*tinkpb.KeyData, error) { +func (km *ecdhNISTPAESPrivateKeyManager) NewKeyData(serializedKeyFormat []byte) (*tinkpb.KeyData, error) { key, err := km.NewKey(serializedKeyFormat) if err != nil { return nil, err @@ -117,52 +118,52 @@ func (km *ecdhAESPrivateKeyManager) NewKeyData(serializedKeyFormat []byte) (*tin serializedKey, err := proto.Marshal(key) if err != nil { - return nil, fmt.Errorf("ecdhes_aes_private_key_manager: Proto.Marshal failed: %w", err) + return nil, fmt.Errorf("ecdh_nistpkw_aesaead_private_key_manager: Proto.Marshal failed: %w", err) } return &tinkpb.KeyData{ - TypeUrl: ecdhAESPrivateKeyTypeURL, + TypeUrl: ecdhNISTPAESPrivateKeyTypeURL, Value: serializedKey, KeyMaterialType: tinkpb.KeyData_ASYMMETRIC_PRIVATE, }, nil } // PublicKeyData returns the enclosed public key data of serializedPrivKey. -func (km *ecdhAESPrivateKeyManager) PublicKeyData(serializedPrivKey []byte) (*tinkpb.KeyData, error) { +func (km *ecdhNISTPAESPrivateKeyManager) PublicKeyData(serializedPrivKey []byte) (*tinkpb.KeyData, error) { privKey := new(ecdhpb.EcdhAeadPrivateKey) err := proto.Unmarshal(serializedPrivKey, privKey) if err != nil { - return nil, errInvalidECDHAESPrivateKey + return nil, errInvalidECDHNISTPAESPrivateKey } serializedPubKey, err := proto.Marshal(privKey.PublicKey) if err != nil { - return nil, errInvalidECDHAESPrivateKey + return nil, errInvalidECDHNISTPAESPrivateKey } return &tinkpb.KeyData{ - TypeUrl: ecdhAESPublicKeyTypeURL, + TypeUrl: ecdhNISTPAESPublicKeyTypeURL, Value: serializedPubKey, KeyMaterialType: tinkpb.KeyData_ASYMMETRIC_PUBLIC, }, nil } // DoesSupport indicates if this key manager supports the given key type. -func (km *ecdhAESPrivateKeyManager) DoesSupport(typeURL string) bool { - return typeURL == ecdhAESPrivateKeyTypeURL +func (km *ecdhNISTPAESPrivateKeyManager) DoesSupport(typeURL string) bool { + return typeURL == ecdhNISTPAESPrivateKeyTypeURL } // TypeURL returns the key type of keys managed by this key manager. -func (km *ecdhAESPrivateKeyManager) TypeURL() string { - return ecdhAESPrivateKeyTypeURL +func (km *ecdhNISTPAESPrivateKeyManager) TypeURL() string { + return ecdhNISTPAESPrivateKeyTypeURL } // validateKey validates the given ECDHPrivateKey and returns the KW curve. -func (km *ecdhAESPrivateKeyManager) validateKey(key *ecdhpb.EcdhAeadPrivateKey) (elliptic.Curve, error) { - err := keyset.ValidateKeyVersion(key.Version, ecdhAESPrivateKeyVersion) +func (km *ecdhNISTPAESPrivateKeyManager) validateKey(key *ecdhpb.EcdhAeadPrivateKey) (elliptic.Curve, error) { + err := keyset.ValidateKeyVersion(key.Version, ecdhNISTPAESPrivateKeyVersion) if err != nil { - return nil, fmt.Errorf("ecdhes_aes_private_key_manager: invalid key: %w", err) + return nil, fmt.Errorf("ecdh_nistpkw_aesaead_private_key_manager: invalid key: %w", err) } return validateKeyFormat(key.PublicKey.Params) @@ -180,7 +181,7 @@ func validateKeyFormat(params *ecdhpb.EcdhAeadParams) (elliptic.Curve, error) { if params.EncParams.CEK == nil { c, err = hybrid.GetCurve(params.KwParams.CurveType.String()) if err != nil { - return nil, fmt.Errorf("ecdhes_aes_private_key_manager: invalid key: %w", err) + return nil, fmt.Errorf("ecdh_nistpkw_aesaead_private_key_manager: invalid key: %w", err) } } else { c = elliptic.P384() @@ -188,12 +189,17 @@ func validateKeyFormat(params *ecdhpb.EcdhAeadParams) (elliptic.Curve, error) { km, err := registry.GetKeyManager(params.EncParams.AeadEnc.TypeUrl) if err != nil { - return nil, fmt.Errorf("ecdhes_aes_private_key_manager: GetKeyManager error: %w", err) + return nil, fmt.Errorf("ecdh_nistpkw_aesaead_private_key_manager: GetKeyManager error: %w", err) } _, err = km.NewKeyData(params.EncParams.AeadEnc.Value) if err != nil { - return nil, fmt.Errorf("ecdhes_aes_private_key_manager: NewKeyData error: %w", err) + return nil, fmt.Errorf("ecdh_nistpkw_aesaead_private_key_manager: NewKeyData error: %w", err) + } + + if params.KwParams.KeyType.String() != ecdhpb.KeyType_EC.String() { + return nil, fmt.Errorf("ecdh_nistpkw_aesaead_private_key_manager: invalid key type %v", + params.KwParams.KeyType) } return c, nil diff --git a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_aes_aead_private_key_manager_test.go b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_nistpkw_aesaead_private_key_manager_test.go similarity index 74% rename from pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_aes_aead_private_key_manager_test.go rename to pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_nistpkw_aesaead_private_key_manager_test.go index e927d4782b..ddf6740b60 100644 --- a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_aes_aead_private_key_manager_test.go +++ b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_nistpkw_aesaead_private_key_manager_test.go @@ -25,20 +25,20 @@ import ( ecdhpb "github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto/primitive/proto/ecdh_aead_go_proto" ) -func TestECDHESPrivateKeyManager_Primitive(t *testing.T) { - km := newECDHPrivateKeyManager() +func TestECDHNISTPAESPrivateKeyManager_Primitive(t *testing.T) { + km := newECDHNISTPAESPrivateKeyManager() t.Run("Test private key manager Primitive() with empty serialized key", func(t *testing.T) { p, err := km.Primitive([]byte("")) - require.EqualError(t, err, errInvalidECDHAESPrivateKey.Error(), - "ECDHESPrivate primitive from empty serialized key must fail") + require.EqualError(t, err, errInvalidECDHNISTPAESPrivateKey.Error(), + "ecdhNISTPAESPrivateKeyManager primitive from empty serialized key must fail") require.Empty(t, p) }) t.Run("Test private key manager Primitive() with bad serialize key", func(t *testing.T) { p, err := km.Primitive([]byte("bad.data")) - require.EqualError(t, err, errInvalidECDHAESPrivateKey.Error(), - "ECDHESPrivate primitive from bad serialized key must fail") + require.EqualError(t, err, errInvalidECDHNISTPAESPrivateKey.Error(), + "ecdhNISTPAESPrivateKeyManager primitive from bad serialized key must fail") require.Empty(t, p) }) @@ -59,6 +59,7 @@ func TestECDHESPrivateKeyManager_Primitive(t *testing.T) { tcName string version uint32 curveType commonpb.EllipticCurveType + keyType ecdhpb.KeyType ecPtFmt commonpb.EcPointFormat encTmp *tinkpb.KeyTemplate }{ @@ -66,6 +67,7 @@ func TestECDHESPrivateKeyManager_Primitive(t *testing.T) { tcName: "private key manager Primitive() using key with bad version", version: 9999, curveType: commonpb.EllipticCurveType_NIST_P256, + keyType: ecdhpb.KeyType_EC, ecPtFmt: commonpb.EcPointFormat_COMPRESSED, encTmp: aead.AES128GCMKeyTemplate(), }, @@ -73,6 +75,15 @@ func TestECDHESPrivateKeyManager_Primitive(t *testing.T) { tcName: "private key manager Primitive() using key with bad curve", version: 0, curveType: commonpb.EllipticCurveType_UNKNOWN_CURVE, + keyType: ecdhpb.KeyType_EC, + ecPtFmt: commonpb.EcPointFormat_COMPRESSED, + encTmp: aead.AES128GCMKeyTemplate(), + }, + { + tcName: "private key manager Primitive() using key with bad key type", + version: 0, + curveType: commonpb.EllipticCurveType_NIST_P256, + keyType: ecdhpb.KeyType_UNKNOWN_KEY_TYPE, ecPtFmt: commonpb.EcPointFormat_COMPRESSED, encTmp: aead.AES128GCMKeyTemplate(), }, @@ -80,6 +91,7 @@ func TestECDHESPrivateKeyManager_Primitive(t *testing.T) { tcName: "success private key manager Primitive()", version: 0, curveType: commonpb.EllipticCurveType_NIST_P256, + keyType: ecdhpb.KeyType_EC, ecPtFmt: commonpb.EcPointFormat_UNCOMPRESSED, encTmp: aead.AES128GCMKeyTemplate(), }, @@ -87,6 +99,7 @@ func TestECDHESPrivateKeyManager_Primitive(t *testing.T) { tcName: "private key manager Primitive() using key with bad key template URL", version: 0, curveType: commonpb.EllipticCurveType_NIST_P256, + keyType: ecdhpb.KeyType_EC, ecPtFmt: commonpb.EcPointFormat_COMPRESSED, encTmp: &tinkpb.KeyTemplate{ TypeUrl: "bad.type/url/value", @@ -98,6 +111,7 @@ func TestECDHESPrivateKeyManager_Primitive(t *testing.T) { tcName: "private key manager Primitive() using key with bad dem key size", version: 0, curveType: commonpb.EllipticCurveType_NIST_P256, + keyType: ecdhpb.KeyType_EC, ecPtFmt: commonpb.EcPointFormat_COMPRESSED, encTmp: &tinkpb.KeyTemplate{ TypeUrl: composite.AESGCMTypeURL, @@ -130,13 +144,14 @@ 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() 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.Primitive() + KeyType: tt.keyType, // invalid key type to force error when calling km.Primitive() }, EncParams: &ecdhpb.EcdhAeadEncParams{ AeadEnc: encT, // invalid data enc key template to get an error when calling km.Primitive() @@ -149,13 +164,13 @@ 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") + require.EqualError(t, err, errInvalidECDHNISTPAESPrivateKey.Error(), + "ecdhNISTPAESPrivateKeyManager primitive from serialized key with invalid serialized key") require.Empty(t, p) return @@ -173,24 +188,24 @@ func TestECDHESPrivateKeyManager_Primitive(t *testing.T) { } } -func TestEcdhesPrivateKeyManager_DoesSupport(t *testing.T) { - km := newECDHPrivateKeyManager() +func TestEcdhNISTPAESPrivateKeyManager_DoesSupport(t *testing.T) { + km := newECDHNISTPAESPrivateKeyManager() require.False(t, km.DoesSupport("bad/url")) - require.True(t, km.DoesSupport(ecdhAESPrivateKeyTypeURL)) + require.True(t, km.DoesSupport(ecdhNISTPAESPrivateKeyTypeURL)) } -func TestEcdhesPrivateKeyManager_NewKey(t *testing.T) { - km := newECDHPrivateKeyManager() +func TestEcdhNISTPAESPrivateKeyManager_NewKey(t *testing.T) { + km := newECDHNISTPAESPrivateKeyManager() t.Run("Test private key manager NewKey() with nil key", func(t *testing.T) { k, err := km.NewKey(nil) - require.EqualError(t, err, errInvalidECDHAESPrivateKeyFormat.Error()) + require.EqualError(t, err, errInvalidECDHNISTPAESPrivateKeyFormat.Error()) require.Empty(t, k) }) t.Run("Test private key manager NewKey() with bad serialize key", func(t *testing.T) { p, err := km.NewKey([]byte("bad.data")) - require.EqualError(t, err, errInvalidECDHAESPrivateKeyFormat.Error(), + require.EqualError(t, err, errInvalidECDHNISTPAESPrivateKeyFormat.Error(), "ECDHESPrivate NewKey() from bad serialized key must fail") require.Empty(t, p) }) @@ -212,24 +227,35 @@ func TestEcdhesPrivateKeyManager_NewKey(t *testing.T) { flagTests := []struct { tcName string curveType commonpb.EllipticCurveType + keyType ecdhpb.KeyType ecPtFmt commonpb.EcPointFormat encTmp *tinkpb.KeyTemplate }{ { tcName: "success private key manager NewKey() and NewKeyData()", curveType: commonpb.EllipticCurveType_NIST_P256, + keyType: ecdhpb.KeyType_EC, ecPtFmt: commonpb.EcPointFormat_COMPRESSED, encTmp: aead.AES128GCMKeyTemplate(), }, { tcName: "private key manager NewKey() and NewKeyData() using key with bad curve", curveType: commonpb.EllipticCurveType_UNKNOWN_CURVE, + keyType: ecdhpb.KeyType_EC, ecPtFmt: commonpb.EcPointFormat_COMPRESSED, encTmp: aead.AES128GCMKeyTemplate(), }, + { + tcName: "private key manager NewKey() and NewKeyData() using key with bad key type", + curveType: commonpb.EllipticCurveType_NIST_P256, + keyType: ecdhpb.KeyType_UNKNOWN_KEY_TYPE, + ecPtFmt: commonpb.EcPointFormat_COMPRESSED, + encTmp: aead.AES256GCMKeyTemplate(), + }, { tcName: "private key manager NewKey() and NewKeyData() using key with bad key template URL", curveType: commonpb.EllipticCurveType_NIST_P256, + keyType: ecdhpb.KeyType_EC, ecPtFmt: commonpb.EcPointFormat_COMPRESSED, encTmp: &tinkpb.KeyTemplate{ TypeUrl: "bad.type/url/value", @@ -240,6 +266,7 @@ func TestEcdhesPrivateKeyManager_NewKey(t *testing.T) { { tcName: "private key manager NewKey() and NewKeyData() using key with bad dem key size", curveType: commonpb.EllipticCurveType_NIST_P256, + keyType: ecdhpb.KeyType_EC, ecPtFmt: commonpb.EcPointFormat_COMPRESSED, encTmp: &tinkpb.KeyTemplate{ TypeUrl: composite.AESGCMTypeURL, @@ -252,19 +279,19 @@ func TestEcdhesPrivateKeyManager_NewKey(t *testing.T) { for _, tc := range flagTests { tt := tc t.Run("Test "+tt.tcName, func(t *testing.T) { - c := tt.curveType encT := tt.encTmp ptFmt := tt.ecPtFmt privKeyProto := &ecdhpb.EcdhAeadKeyFormat{ Params: &ecdhpb.EcdhAeadParams{ KwParams: &ecdhpb.EcdhKwParams{ - CurveType: c, // unknown curve type to force an error when calling km.Primitive() + CurveType: tt.curveType, // unknown curve type to force an error when calling km.NewKey() + KeyType: tt.keyType, // 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() }, } @@ -295,14 +322,14 @@ func TestEcdhesPrivateKeyManager_NewKey(t *testing.T) { if strings.Contains(tt.tcName, "success") { require.NoError(t, err) require.NotEmpty(t, kd) - require.Equal(t, kd.TypeUrl, ecdhAESPrivateKeyTypeURL) + require.Equal(t, kd.TypeUrl, ecdhNISTPAESPrivateKeyTypeURL) require.Equal(t, kd.KeyMaterialType, tinkpb.KeyData_ASYMMETRIC_PRIVATE) return } if bytes.Equal(tt.encTmp.Value, badSerializedFormat) { - require.EqualError(t, err, errInvalidECDHAESPrivateKeyFormat.Error(), - "ECDHESPrivate NewKey from serialized key with invalid serialized key") + require.EqualError(t, err, errInvalidECDHNISTPAESPrivateKeyFormat.Error(), + "ecdhNISTPAESPrivateKeyManager NewKey from serialized key with invalid serialized key") require.Empty(t, p) return diff --git a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_nistpkw_aesaead_public_key_manager.go b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_nistpkw_aesaead_public_key_manager.go new file mode 100644 index 0000000000..6e664fcd8a --- /dev/null +++ b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_nistpkw_aesaead_public_key_manager.go @@ -0,0 +1,100 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package ecdh + +import ( + "crypto/elliptic" + "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" +) + +const ( + ecdhNISTPAESPublicKeyVersion = 0 + ecdhNISTPAESPublicKeyTypeURL = "type.hyperledger.org/hyperledger.aries.crypto.tink.EcdhNistPKwAesAeadPublicKey" +) + +// common errors. +var errInvalidECDHNISTPAESPublicKey = errors.New("ecdh_nistpkw_aesaead_public_key_manager: invalid key") + +// ecdhNISTPAESPublicKeyManager is an implementation of KeyManager interface for NIST P curved key wrapping and +// AES-GCM content encryption. +// It generates new ECDHPublicKey (AES) keys and produces new instances of ECDHAEADCompositeEncrypt subtle. +type ecdhNISTPAESPublicKeyManager struct{} + +// Assert that ecdhNISTPAESPublicKeyManager implements the KeyManager interface. +var _ registry.KeyManager = (*ecdhNISTPAESPublicKeyManager)(nil) + +// newECDHNISTPAESPublicKeyManager creates a new ecdhNISTPAESPublicKeyManager. +func newECDHNISTPAESPublicKeyManager() *ecdhNISTPAESPublicKeyManager { + return new(ecdhNISTPAESPublicKeyManager) +} + +// Primitive creates an ECDHESPublicKey subtle for the given serialized ECDHESPublicKey proto. +func (km *ecdhNISTPAESPublicKeyManager) Primitive(serializedKey []byte) (interface{}, error) { + if len(serializedKey) == 0 { + return nil, errInvalidECDHNISTPAESPublicKey + } + + ecdhPubKey := new(ecdhpb.EcdhAeadPublicKey) + + err := proto.Unmarshal(serializedKey, ecdhPubKey) + if err != nil { + return nil, errInvalidECDHNISTPAESPublicKey + } + + _, err = km.validateKey(ecdhPubKey) + if err != nil { + return nil, errInvalidECDHNISTPAESPublicKey + } + + rEnc, err := composite.NewRegisterCompositeAEADEncHelper(ecdhPubKey.Params.EncParams.AeadEnc) + if err != nil { + return nil, fmt.Errorf("ecdh_nistpkw_aesaead_public_key_manager: NewRegisterCompositeAEADEncHelper "+ + "failed: %w", err) + } + + return subtle.NewECDHAEADCompositeEncrypt(rEnc, ecdhPubKey.Params.EncParams.CEK), nil +} + +// DoesSupport indicates if this key manager supports the given key type. +func (km *ecdhNISTPAESPublicKeyManager) DoesSupport(typeURL string) bool { + return typeURL == ecdhNISTPAESPublicKeyTypeURL +} + +// TypeURL returns the key type of keys managed by this key manager. +func (km *ecdhNISTPAESPublicKeyManager) TypeURL() string { + return ecdhNISTPAESPublicKeyTypeURL +} + +// NewKey is not implemented for public key manager. +func (km *ecdhNISTPAESPublicKeyManager) NewKey(serializedKeyFormat []byte) (proto.Message, error) { + return nil, errors.New("ecdh_nistpkw_aesaead_public_key_manager: NewKey not implemented") +} + +// NewKeyData is not implemented for public key manager. +func (km *ecdhNISTPAESPublicKeyManager) NewKeyData(serializedKeyFormat []byte) (*tinkpb.KeyData, error) { + return nil, errors.New("ecdh_nistpkw_aesaead_public_key_manager: NewKeyData not implemented") +} + +// validateKey validates the given EcdhAeadPublicKey. +func (km *ecdhNISTPAESPublicKeyManager) validateKey(key *ecdhpb.EcdhAeadPublicKey) (elliptic.Curve, error) { + err := keyset.ValidateKeyVersion(key.Version, ecdhNISTPAESPublicKeyVersion) + if err != nil { + return nil, fmt.Errorf("ecdh_nistpkw_aesaead_public_key_manager: invalid key: %w", err) + } + + return validateKeyFormat(key.Params) +} diff --git a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_aes_aead_public_key_manager_test.go b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_nistpkw_aesaead_public_key_manager_test.go similarity index 82% rename from pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_aes_aead_public_key_manager_test.go rename to pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_nistpkw_aesaead_public_key_manager_test.go index 7bdc0c50d3..1fc6927ad0 100644 --- a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_aes_aead_public_key_manager_test.go +++ b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_nistpkw_aesaead_public_key_manager_test.go @@ -24,20 +24,20 @@ import ( ecdhpb "github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto/primitive/proto/ecdh_aead_go_proto" ) -func TestECDHPublicKeyManager_Primitive(t *testing.T) { - km := newECDHPublicKeyManager() +func TestECDHNISTPAESPublicKeyManager_Primitive(t *testing.T) { + km := newECDHNISTPAESPublicKeyManager() t.Run("Test public key manager Primitive() with empty serialized key", func(t *testing.T) { p, err := km.Primitive([]byte("")) - require.EqualError(t, err, errInvalidECDHAESPublicKey.Error(), - "ECDHPublic primitive from empty serialized key must fail") + require.EqualError(t, err, errInvalidECDHNISTPAESPublicKey.Error(), + "newECDHNISTPAESPublicKeyManager primitive from empty serialized key must fail") require.Empty(t, p) }) t.Run("Test public key manager Primitive() with bad serialize key", func(t *testing.T) { p, err := km.Primitive([]byte("bad.data")) - require.EqualError(t, err, errInvalidECDHAESPublicKey.Error(), - "ECDHPublic primitive from bad serialized key must fail") + require.EqualError(t, err, errInvalidECDHNISTPAESPublicKey.Error(), + "newECDHNISTPAESPublicKeyManager primitive from bad serialized key must fail") require.Empty(t, p) }) @@ -150,8 +150,8 @@ func TestECDHPublicKeyManager_Primitive(t *testing.T) { p, err := km.Primitive(sPubKey) if strings.Contains(tt.tcName, "with bad content encryption key size") { - require.EqualError(t, err, errInvalidECDHAESPublicKey.Error(), - "ECDHPublic primitive from serialized key with invalid serialized key") + require.EqualError(t, err, errInvalidECDHNISTPAESPublicKey.Error(), + "newECDHNISTPAESPublicKeyManager primitive from serialized key with invalid serialized key") require.Empty(t, p) return @@ -169,24 +169,24 @@ func TestECDHPublicKeyManager_Primitive(t *testing.T) { } } -func TestEcdhPublicKeyManager_DoesSupport(t *testing.T) { - km := newECDHPublicKeyManager() +func TestEcdhNISTPAESPublicKeyManager_DoesSupport(t *testing.T) { + km := newECDHNISTPAESPublicKeyManager() require.False(t, km.DoesSupport("bad/url")) - require.True(t, km.DoesSupport(ecdhAESPublicKeyTypeURL)) + require.True(t, km.DoesSupport(ecdhNISTPAESPublicKeyTypeURL)) } -func TestEcdhPublicKeyManager_NewKeyAndNewKeyData(t *testing.T) { - km := newECDHPublicKeyManager() +func TestEcdhNISTPAESPublicKeyManager_NewKeyAndNewKeyData(t *testing.T) { + km := newECDHNISTPAESPublicKeyManager() t.Run("Test public key manager NewKey()", func(t *testing.T) { k, err := km.NewKey(nil) - require.EqualError(t, err, "ecdh_aes_public_key_manager: NewKey not implemented") + require.EqualError(t, err, "ecdh_nistpkw_aesaead_public_key_manager: NewKey not implemented") require.Empty(t, k) }) t.Run("Test private key manager NewKeyData()", func(t *testing.T) { p, err := km.NewKeyData(nil) - require.EqualError(t, err, "ecdh_aes_public_key_manager: NewKeyData not implemented") + require.EqualError(t, err, "ecdh_nistpkw_aesaead_public_key_manager: NewKeyData not implemented") require.Empty(t, p) }) } diff --git a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_nistpkw_xchachaaead_private_key_manager.go b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_nistpkw_xchachaaead_private_key_manager.go new file mode 100644 index 0000000000..cbf0327295 --- /dev/null +++ b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_nistpkw_xchachaaead_private_key_manager.go @@ -0,0 +1,170 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package ecdh + +import ( + "crypto/elliptic" + "errors" + "fmt" + + "github.com/golang/protobuf/proto" + "github.com/google/tink/go/core/registry" + hybrid "github.com/google/tink/go/hybrid/subtle" + "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" +) + +const ( + ecdhNISTPXChachaPrivateKeyVersion = 0 + ecdhNISTPXChachaPrivateKeyTypeURL = "type.hyperledger.org/hyperledger.aries.crypto.tink.EcdhNistPKwXChachaAeadPrivateKey" // nolint:lll +) + +// common errors. +var ( + errInvalidECDHNISTPXChachaPrivateKey = errors.New("ecdh_nistpkw_xchachaaead_private_key_manager: invalid key") // nolint:lll + errInvalidECDHNISTPXChachaPrivateKeyFormat = errors.New("ecdh_nistpkw_xchachaaead_private_key_manager: invalid key format") // nolint:lll +) + +// ecdhNISTPXChachaPrivateKeyManager is an implementation of PrivateKeyManager interface for NIST P curved key wrapping +// and XChacha20Poly1305 content encryption. +// It generates new ECDHPrivateKey (NIST P KW) keys and produces new instances of ECDHAEADCompositeDecrypt subtle. +type ecdhNISTPXChachaPrivateKeyManager struct{} + +// Assert that ecdhNISTPXChachaPrivateKeyManager implements the PrivateKeyManager interface. +var _ registry.PrivateKeyManager = (*ecdhNISTPXChachaPrivateKeyManager)(nil) + +// newECDHNISTPXChachaPrivateKeyManager creates a new ecdhNISTPXChachaPrivateKeyManager. +func newECDHNISTPXChachaPrivateKeyManager() *ecdhNISTPXChachaPrivateKeyManager { + return new(ecdhNISTPXChachaPrivateKeyManager) +} + +// Primitive creates an ECDHESPrivateKey subtle for the given serialized ECDHESPrivateKey proto. +func (km *ecdhNISTPXChachaPrivateKeyManager) Primitive(serializedKey []byte) (interface{}, error) { + if len(serializedKey) == 0 { + return nil, errInvalidECDHNISTPXChachaPrivateKey + } + + key := new(ecdhpb.EcdhAeadPrivateKey) + + err := proto.Unmarshal(serializedKey, key) + if err != nil { + return nil, errInvalidECDHNISTPXChachaPrivateKey + } + + _, err = km.validateKey(key) + if err != nil { + return nil, errInvalidECDHNISTPXChachaPrivateKey + } + + rEnc, err := composite.NewRegisterCompositeAEADEncHelper(key.PublicKey.Params.EncParams.AeadEnc) + if err != nil { + return nil, fmt.Errorf("ecdh_nistpkw_xchachaaead_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 *ecdhNISTPXChachaPrivateKeyManager) NewKey(serializedKeyFormat []byte) (proto.Message, error) { + if len(serializedKeyFormat) == 0 { + return nil, errInvalidECDHNISTPXChachaPrivateKeyFormat + } + + keyFormat := new(ecdhpb.EcdhAeadKeyFormat) + + err := proto.Unmarshal(serializedKeyFormat, keyFormat) + if err != nil { + return nil, errInvalidECDHNISTPXChachaPrivateKeyFormat + } + + curve, err := validateKeyFormat(keyFormat.Params) + if err != nil { + return nil, errInvalidECDHNISTPXChachaPrivateKeyFormat + } + + pvt, err := hybrid.GenerateECDHKeyPair(curve) + if err != nil { + return nil, fmt.Errorf("ecdh_nistpkw_xchachaaead_private_key_manager: GenerateECDHKeyPair failed: %w", err) + } + + return &ecdhpb.EcdhAeadPrivateKey{ + Version: ecdhNISTPXChachaPrivateKeyVersion, + KeyValue: pvt.D.Bytes(), + PublicKey: &ecdhpb.EcdhAeadPublicKey{ + Version: ecdhNISTPXChachaPrivateKeyVersion, + Params: keyFormat.Params, + X: pvt.PublicKey.Point.X.Bytes(), + Y: pvt.PublicKey.Point.Y.Bytes(), + }, + }, 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 *ecdhNISTPXChachaPrivateKeyManager) 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("ecdh_nistpkw_xchachaaead_private_key_manager: Proto.Marshal failed: %w", err) + } + + return &tinkpb.KeyData{ + TypeUrl: ecdhNISTPXChachaPrivateKeyTypeURL, + Value: serializedKey, + KeyMaterialType: tinkpb.KeyData_ASYMMETRIC_PRIVATE, + }, nil +} + +// PublicKeyData returns the enclosed public key data of serializedPrivKey. +func (km *ecdhNISTPXChachaPrivateKeyManager) PublicKeyData(serializedPrivKey []byte) (*tinkpb.KeyData, error) { + privKey := new(ecdhpb.EcdhAeadPrivateKey) + + err := proto.Unmarshal(serializedPrivKey, privKey) + if err != nil { + return nil, errInvalidECDHNISTPXChachaPrivateKey + } + + serializedPubKey, err := proto.Marshal(privKey.PublicKey) + if err != nil { + return nil, errInvalidECDHNISTPXChachaPrivateKey + } + + return &tinkpb.KeyData{ + TypeUrl: ecdhNISTPXChachaPublicKeyTypeURL, + Value: serializedPubKey, + KeyMaterialType: tinkpb.KeyData_ASYMMETRIC_PUBLIC, + }, nil +} + +// DoesSupport indicates if this key manager supports the given key type. +func (km *ecdhNISTPXChachaPrivateKeyManager) DoesSupport(typeURL string) bool { + return typeURL == ecdhNISTPXChachaPrivateKeyTypeURL +} + +// TypeURL returns the key type of keys managed by this key manager. +func (km *ecdhNISTPXChachaPrivateKeyManager) TypeURL() string { + return ecdhNISTPXChachaPrivateKeyTypeURL +} + +// validateKey validates the given ECDHPrivateKey and returns the KW curve. +func (km *ecdhNISTPXChachaPrivateKeyManager) validateKey(key *ecdhpb.EcdhAeadPrivateKey) (elliptic.Curve, error) { + err := keyset.ValidateKeyVersion(key.Version, ecdhNISTPXChachaPrivateKeyVersion) + if err != nil { + return nil, fmt.Errorf("ecdh_nistpkw_xchachaaead_private_key_manager: invalid key: %w", err) + } + + return validateKeyFormat(key.PublicKey.Params) +} diff --git a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_nistpkw_xchachaaead_private_key_manager_test.go b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_nistpkw_xchachaaead_private_key_manager_test.go new file mode 100644 index 0000000000..5f2654dc8f --- /dev/null +++ b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_nistpkw_xchachaaead_private_key_manager_test.go @@ -0,0 +1,271 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package ecdh + +import ( + "crypto/elliptic" + "crypto/rand" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/google/tink/go/aead" + hybrid "github.com/google/tink/go/hybrid/subtle" + commonpb "github.com/google/tink/go/proto/common_go_proto" + tinkpb "github.com/google/tink/go/proto/tink_go_proto" + "github.com/stretchr/testify/require" + + ecdhpb "github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto/primitive/proto/ecdh_aead_go_proto" +) + +func TestECDHNISTPXChachaPrivateKeyManager_Primitive(t *testing.T) { + km := newECDHNISTPXChachaPrivateKeyManager() + + t.Run("Test private key manager Primitive() with empty serialized key", func(t *testing.T) { + p, err := km.Primitive([]byte("")) + require.EqualError(t, err, errInvalidECDHNISTPXChachaPrivateKey.Error(), + "ecdhNISTPXChachaPrivateKeyManager primitive from empty serialized key must fail") + require.Empty(t, p) + }) + + t.Run("Test private key manager Primitive() with bad serialize key", func(t *testing.T) { + p, err := km.Primitive([]byte("bad.data")) + require.EqualError(t, err, errInvalidECDHNISTPXChachaPrivateKey.Error(), + "ecdhNISTPXChachaPrivateKeyManager primitive from bad serialized key must fail") + require.Empty(t, p) + }) + + flagTests := []struct { + tcName string + version uint32 + curveType commonpb.EllipticCurveType + keyType ecdhpb.KeyType + ecPtFmt commonpb.EcPointFormat + encTmp *tinkpb.KeyTemplate + }{ + { + tcName: "private key manager Primitive() using key with bad version", + version: 9999, + curveType: commonpb.EllipticCurveType_NIST_P256, + keyType: ecdhpb.KeyType_EC, + ecPtFmt: commonpb.EcPointFormat_COMPRESSED, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "private key manager Primitive() using key with bad curve", + version: 0, + curveType: commonpb.EllipticCurveType_UNKNOWN_CURVE, + keyType: ecdhpb.KeyType_EC, + ecPtFmt: commonpb.EcPointFormat_COMPRESSED, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "private key manager Primitive() using key with bad key type", + version: 0, + curveType: commonpb.EllipticCurveType_NIST_P256, + keyType: ecdhpb.KeyType_UNKNOWN_KEY_TYPE, + ecPtFmt: commonpb.EcPointFormat_COMPRESSED, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "success private key manager Primitive()", + version: 0, + curveType: commonpb.EllipticCurveType_NIST_P256, + keyType: ecdhpb.KeyType_EC, + ecPtFmt: commonpb.EcPointFormat_UNCOMPRESSED, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "private key manager Primitive() using key with bad key template URL", + version: 0, + curveType: commonpb.EllipticCurveType_NIST_P256, + keyType: ecdhpb.KeyType_EC, + ecPtFmt: commonpb.EcPointFormat_COMPRESSED, + encTmp: &tinkpb.KeyTemplate{ + TypeUrl: "bad.type/url/value", + OutputPrefixType: tinkpb.OutputPrefixType_RAW, + }, + }, + } + + for _, tc := range flagTests { + tt := tc + t.Run("Test "+tt.tcName, func(t *testing.T) { + c := tt.curveType + encT := tt.encTmp + ptFmt := tt.ecPtFmt + v := tt.version + + // temporarily reset curvType if its unknown type so subtle.GetCurve() below doesn't fail + if tt.curveType.String() == commonpb.EllipticCurveType_UNKNOWN_CURVE.String() { + c = commonpb.EllipticCurveType_NIST_P256 + } + + crv, err := hybrid.GetCurve(c.String()) + require.NoError(t, err) + d, x, y, err := elliptic.GenerateKey(crv, rand.Reader) + require.NoError(t, err) + + // set back curvType if it was unknown to proceed with the test + if tt.curveType.String() == commonpb.EllipticCurveType_UNKNOWN_CURVE.String() { + c = tt.curveType + } + + privKeyProto := &ecdhpb.EcdhAeadPrivateKey{ + Version: v, + PublicKey: &ecdhpb.EcdhAeadPublicKey{ + Version: v, // if v > 0 to force an error when calling km.Primitive() + Params: &ecdhpb.EcdhAeadParams{ + KwParams: &ecdhpb.EcdhKwParams{ + CurveType: c, // unknown curve type to force an error when calling km.Primitive() + KeyType: tt.keyType, // invalid key type to force error when calling km.Primitive() + }, + EncParams: &ecdhpb.EcdhAeadEncParams{ + AeadEnc: encT, // invalid data enc key template to get an error when calling km.Primitive() + }, + EcPointFormat: ptFmt, + }, + X: x.Bytes(), + Y: y.Bytes(), + }, + KeyValue: d, + } + + sPrivKey, err := proto.Marshal(privKeyProto) + require.NoError(t, err) + + p, err := km.Primitive(sPrivKey) + if strings.Contains(tt.tcName, "success") { + require.NoError(t, err) + require.NotEmpty(t, p) + return + } + + require.Errorf(t, err, tt.tcName) + require.Empty(t, p) + }) + } +} + +func TestEcdhNISTPXChachaPrivateKeyManager_DoesSupport(t *testing.T) { + km := newECDHNISTPXChachaPrivateKeyManager() + require.False(t, km.DoesSupport("bad/url")) + require.True(t, km.DoesSupport(ecdhNISTPXChachaPrivateKeyTypeURL)) +} + +func TestEcdhNISTPXChachaPrivateKeyManager_NewKey(t *testing.T) { + km := newECDHNISTPXChachaPrivateKeyManager() + + t.Run("Test private key manager NewKey() with nil key", func(t *testing.T) { + k, err := km.NewKey(nil) + require.EqualError(t, err, errInvalidECDHNISTPXChachaPrivateKeyFormat.Error()) + require.Empty(t, k) + }) + + t.Run("Test private key manager NewKey() with bad serialize key", func(t *testing.T) { + p, err := km.NewKey([]byte("bad.data")) + require.EqualError(t, err, errInvalidECDHNISTPXChachaPrivateKeyFormat.Error(), + "ecdhNISTPXChachaPrivateKeyManager NewKey() from bad serialized key must fail") + require.Empty(t, p) + }) + + flagTests := []struct { + tcName string + curveType commonpb.EllipticCurveType + keyType ecdhpb.KeyType + ecPtFmt commonpb.EcPointFormat + encTmp *tinkpb.KeyTemplate + }{ + { + tcName: "success private key manager NewKey() and NewKeyData()", + curveType: commonpb.EllipticCurveType_NIST_P256, + keyType: ecdhpb.KeyType_EC, + ecPtFmt: commonpb.EcPointFormat_COMPRESSED, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "private key manager NewKey() and NewKeyData() using key with bad curve", + curveType: commonpb.EllipticCurveType_UNKNOWN_CURVE, + keyType: ecdhpb.KeyType_EC, + ecPtFmt: commonpb.EcPointFormat_COMPRESSED, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "private key manager NewKey() and NewKeyData() using key with bad key type", + curveType: commonpb.EllipticCurveType_NIST_P256, + keyType: ecdhpb.KeyType_UNKNOWN_KEY_TYPE, + ecPtFmt: commonpb.EcPointFormat_COMPRESSED, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "private key manager NewKey() and NewKeyData() using key with bad key template URL", + curveType: commonpb.EllipticCurveType_NIST_P256, + keyType: ecdhpb.KeyType_EC, + ecPtFmt: commonpb.EcPointFormat_COMPRESSED, + encTmp: &tinkpb.KeyTemplate{ + TypeUrl: "bad.type/url/value", + OutputPrefixType: tinkpb.OutputPrefixType_RAW, + }, + }, + } + + for _, tc := range flagTests { + tt := tc + t.Run("Test "+tt.tcName, func(t *testing.T) { + encT := tt.encTmp + ptFmt := tt.ecPtFmt + + privKeyProto := &ecdhpb.EcdhAeadKeyFormat{ + Params: &ecdhpb.EcdhAeadParams{ + KwParams: &ecdhpb.EcdhKwParams{ + CurveType: tt.curveType, // unknown curve type to force an error when calling km.NewKey() + KeyType: tt.keyType, // 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.NewKey() + }, + EcPointFormat: ptFmt, // unknown EC Point format type to force an error when calling km.NewKey() + }, + } + + sPrivKey, err := proto.Marshal(privKeyProto) + require.NoError(t, err) + + p, err := km.NewKey(sPrivKey) + if strings.Contains(tt.tcName, "success") { + require.NoError(t, err) + require.NotEmpty(t, p) + + sp, e := proto.Marshal(p) + require.NoError(t, e) + require.NotEmpty(t, sp) + + // try PublicKeyData() with bad serialized private key + pubK, e := km.PublicKeyData([]byte("bad serialized private key")) + require.Error(t, e) + require.Empty(t, pubK) + + // try PublicKeyData() with valid serialized private key + pubK, e = km.PublicKeyData(sp) + require.NoError(t, e) + require.NotEmpty(t, pubK) + } + + kd, err := km.NewKeyData(sPrivKey) + if strings.Contains(tt.tcName, "success") { + require.NoError(t, err) + require.NotEmpty(t, kd) + require.Equal(t, kd.TypeUrl, ecdhNISTPXChachaPrivateKeyTypeURL) + require.Equal(t, kd.KeyMaterialType, tinkpb.KeyData_ASYMMETRIC_PRIVATE) + return + } + + require.Errorf(t, err, tt.tcName) + require.Empty(t, p) + }) + } +} diff --git a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_nistpkw_xchachaaead_public_key_manager.go b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_nistpkw_xchachaaead_public_key_manager.go new file mode 100644 index 0000000000..5eabe0b68e --- /dev/null +++ b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_nistpkw_xchachaaead_public_key_manager.go @@ -0,0 +1,100 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package ecdh + +import ( + "crypto/elliptic" + "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" +) + +const ( + ecdhNISTPXChachaPublicKeyVersion = 0 + ecdhNISTPXChachaPublicKeyTypeURL = "type.hyperledger.org/hyperledger.aries.crypto.tink.EcdhNistPKwXChachaAeadPublicKey" // nolint:lll +) + +// common errors. +var errInvalidECDHNISTPXChachaPublicKey = errors.New("ecdh_nistpkw_xchachaaead_public_key_manager: invalid key") + +// ecdhNISTPXChachaPublicKeyManager is an implementation of KeyManager interface for NIST P curved key wrapping and +// XChacha20Poly1305 content encryption. +// It generates new ECDHPublicKey (X25519) keys and produces new instances of ECDHAEADCompositeEncrypt subtle. +type ecdhNISTPXChachaPublicKeyManager struct{} + +// Assert that ecdhNISTPXChachaPublicKeyManager implements the KeyManager interface. +var _ registry.KeyManager = (*ecdhNISTPXChachaPublicKeyManager)(nil) + +// newECDHNISTPXChachaPublicKeyManager creates a new ecdhNISTPXChachaPublicKeyManager. +func newECDHNISTPXChachaPublicKeyManager() *ecdhNISTPXChachaPublicKeyManager { + return new(ecdhNISTPXChachaPublicKeyManager) +} + +// Primitive creates an ECDHESPublicKey subtle for the given serialized ECDHESPublicKey proto. +func (km *ecdhNISTPXChachaPublicKeyManager) Primitive(serializedKey []byte) (interface{}, error) { + if len(serializedKey) == 0 { + return nil, errInvalidECDHNISTPXChachaPublicKey + } + + ecdhPubKey := new(ecdhpb.EcdhAeadPublicKey) + + err := proto.Unmarshal(serializedKey, ecdhPubKey) + if err != nil { + return nil, errInvalidECDHNISTPXChachaPublicKey + } + + _, err = km.validateKey(ecdhPubKey) + if err != nil { + return nil, errInvalidECDHNISTPXChachaPublicKey + } + + rEnc, err := composite.NewRegisterCompositeAEADEncHelper(ecdhPubKey.Params.EncParams.AeadEnc) + if err != nil { + return nil, fmt.Errorf("ecdh_nistpkw_xchachaaead_public_key_manager: NewRegisterCompositeAEADEncHelper "+ + "failed: %w", err) + } + + return subtle.NewECDHAEADCompositeEncrypt(rEnc, ecdhPubKey.Params.EncParams.CEK), nil +} + +// DoesSupport indicates if this key manager supports the given key type. +func (km *ecdhNISTPXChachaPublicKeyManager) DoesSupport(typeURL string) bool { + return typeURL == ecdhNISTPXChachaPublicKeyTypeURL +} + +// TypeURL returns the key type of keys managed by this key manager. +func (km *ecdhNISTPXChachaPublicKeyManager) TypeURL() string { + return ecdhNISTPXChachaPublicKeyTypeURL +} + +// NewKey is not implemented for public key manager. +func (km *ecdhNISTPXChachaPublicKeyManager) NewKey(serializedKeyFormat []byte) (proto.Message, error) { + return nil, errors.New("ecdh_nistpkw_xchachaaead_public_key_manager: NewKey not implemented") +} + +// NewKeyData is not implemented for public key manager. +func (km *ecdhNISTPXChachaPublicKeyManager) NewKeyData(serializedKeyFormat []byte) (*tinkpb.KeyData, error) { + return nil, errors.New("ecdh_nistpkw_xchachaaead_public_key_manager: NewKeyData not implemented") +} + +// validateKey validates the given EcdhAeadPublicKey. +func (km *ecdhNISTPXChachaPublicKeyManager) validateKey(key *ecdhpb.EcdhAeadPublicKey) (elliptic.Curve, error) { + err := keyset.ValidateKeyVersion(key.Version, ecdhNISTPXChachaPublicKeyVersion) + if err != nil { + return nil, fmt.Errorf("ecdh_nistpkw_xchachaaead_public_key_manager: invalid key: %w", err) + } + + return validateKeyFormat(key.Params) +} diff --git a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_nistpkw_xchachaaead_public_key_manager_test.go b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_nistpkw_xchachaaead_public_key_manager_test.go new file mode 100644 index 0000000000..9f11c07822 --- /dev/null +++ b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_nistpkw_xchachaaead_public_key_manager_test.go @@ -0,0 +1,165 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package ecdh + +import ( + "crypto/elliptic" + "crypto/rand" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/google/tink/go/aead" + hybrid "github.com/google/tink/go/hybrid/subtle" + commonpb "github.com/google/tink/go/proto/common_go_proto" + tinkpb "github.com/google/tink/go/proto/tink_go_proto" + "github.com/stretchr/testify/require" + + ecdhpb "github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto/primitive/proto/ecdh_aead_go_proto" +) + +func TestECDHNISTPXChachaPublicKeyManager_Primitive(t *testing.T) { + km := newECDHNISTPXChachaPublicKeyManager() + + t.Run("Test public key manager Primitive() with empty serialized key", func(t *testing.T) { + p, err := km.Primitive([]byte("")) + require.EqualError(t, err, errInvalidECDHNISTPXChachaPublicKey.Error(), + "ecdhNISTPXChachaPublicKeyManager primitive from empty serialized key must fail") + require.Empty(t, p) + }) + + t.Run("Test public key manager Primitive() with bad serialize key", func(t *testing.T) { + p, err := km.Primitive([]byte("bad.data")) + require.EqualError(t, err, errInvalidECDHNISTPXChachaPublicKey.Error(), + "ecdhNISTPXChachaPublicKeyManager primitive from bad serialized key must fail") + require.Empty(t, p) + }) + + flagTests := []struct { + tcName string + version uint32 + curveType commonpb.EllipticCurveType + ecPtFmt commonpb.EcPointFormat + encTmp *tinkpb.KeyTemplate + }{ + { + tcName: "public key manager Primitive() using key with bad version", + version: 9999, + curveType: commonpb.EllipticCurveType_NIST_P256, + ecPtFmt: commonpb.EcPointFormat_COMPRESSED, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "public key manager Primitive() using key with bad curve", + version: 0, + curveType: commonpb.EllipticCurveType_UNKNOWN_CURVE, + ecPtFmt: commonpb.EcPointFormat_COMPRESSED, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "success public key manager Primitive()", + version: 0, + curveType: commonpb.EllipticCurveType_NIST_P256, + ecPtFmt: commonpb.EcPointFormat_UNCOMPRESSED, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "public key manager Primitive() using key with bad key template URL", + version: 0, + curveType: commonpb.EllipticCurveType_NIST_P256, + ecPtFmt: commonpb.EcPointFormat_COMPRESSED, + encTmp: &tinkpb.KeyTemplate{ + TypeUrl: "bad.type/url/value", + OutputPrefixType: tinkpb.OutputPrefixType_RAW, + }, + }, + } + + for _, tc := range flagTests { + tt := tc + t.Run("Test "+tt.tcName, func(t *testing.T) { + c := tt.curveType + encT := tt.encTmp + ptFmt := tt.ecPtFmt + v := tt.version + + // temporarily reset curvType if its unknown type so subtle.GetCurve() below doesn't fail + if tt.curveType.String() == commonpb.EllipticCurveType_UNKNOWN_CURVE.String() { + c = commonpb.EllipticCurveType_NIST_P256 + } + + crv, err := hybrid.GetCurve(c.String()) + require.NoError(t, err) + _, x, y, err := elliptic.GenerateKey(crv, rand.Reader) + require.NoError(t, err) + + // set back curvType if it was unknown to proceed with the test + if tt.curveType.String() == commonpb.EllipticCurveType_UNKNOWN_CURVE.String() { + c = tt.curveType + } + + pubKeyProto := &ecdhpb.EcdhAeadPublicKey{ + Version: v, // if v > 0 to force an error when calling km.Primitive() + Params: &ecdhpb.EcdhAeadParams{ + KwParams: &ecdhpb.EcdhKwParams{ + CurveType: c, // unknown curve type to force an error when calling km.Primitive() + KeyType: ecdhpb.KeyType_EC, + }, + EncParams: &ecdhpb.EcdhAeadEncParams{ + AeadEnc: encT, // invalid data enc key template to force an error when calling km.Primitive() + }, + EcPointFormat: ptFmt, // unknown EC Point format type to force an error when calling km.Primitive() + }, + X: x.Bytes(), + Y: y.Bytes(), + } + + sPubKey, err := proto.Marshal(pubKeyProto) + require.NoError(t, err) + + p, err := km.Primitive(sPubKey) + if strings.Contains(tt.tcName, "with bad content encryption key size") { + require.EqualError(t, err, errInvalidECDHNISTPXChachaPublicKey.Error(), + "ecdhNISTPXChachaPublicKeyManager primitive from serialized key with invalid serialized key") + require.Empty(t, p) + + return + } + + if strings.Contains(tt.tcName, "success") { + require.NoError(t, err) + require.NotEmpty(t, p) + return + } + + require.Errorf(t, err, tt.tcName) + require.Empty(t, p) + }) + } +} + +func TestEcdhNISTPXChachaPublicKeyManager_DoesSupport(t *testing.T) { + km := newECDHNISTPXChachaPublicKeyManager() + require.False(t, km.DoesSupport("bad/url")) + require.True(t, km.DoesSupport(ecdhNISTPXChachaPublicKeyTypeURL)) +} + +func TestEcdhNISTPXChachaPublicKeyManager_NewKeyAndNewKeyData(t *testing.T) { + km := newECDHNISTPXChachaPublicKeyManager() + + t.Run("Test public key manager NewKey()", func(t *testing.T) { + k, err := km.NewKey(nil) + require.EqualError(t, err, "ecdh_nistpkw_xchachaaead_public_key_manager: NewKey not implemented") + require.Empty(t, k) + }) + + t.Run("Test private key manager NewKeyData()", func(t *testing.T) { + p, err := km.NewKeyData(nil) + require.EqualError(t, err, "ecdh_nistpkw_xchachaaead_public_key_manager: NewKeyData not implemented") + require.Empty(t, p) + }) +} diff --git a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519_private_key_manager.go b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519_private_key_manager.go deleted file mode 100644 index beb29793d7..0000000000 --- a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519_private_key_manager.go +++ /dev/null @@ -1,9 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package ecdh - -// TODO implement diff --git a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519kw_aesaead_private_key_manager.go b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519kw_aesaead_private_key_manager.go new file mode 100644 index 0000000000..484c1b87ef --- /dev/null +++ b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519kw_aesaead_private_key_manager.go @@ -0,0 +1,180 @@ +/* +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 ( + ecdhX25519AESPrivateKeyVersion = 0 + ecdhX25519AESPrivateKeyTypeURL = "type.hyperledger.org/hyperledger.aries.crypto.tink.EcdhX25519KwAesAeadPrivateKey" +) + +// common errors. +var ( + errInvalidECDHX25519AESPrivateKey = errors.New("ecdh_x25519kw_aesaead_private_key_manager: invalid key") + errInvalidECDHX25519AESPrivateKeyFormat = errors.New("ecdh_x25519kw_aesaead_private_key_manager: invalid key format") // nolint:lll +) + +// ecdhX25519AESPrivateKeyManager is an implementation of PrivateKeyManager interface for X25519 key wrapping and +// AES-GCM content encryption. +// It generates new ECDHPrivateKey (X25519 KW) keys and produces new instances of ECDHAEADCompositeDecrypt subtle. +type ecdhX25519AESPrivateKeyManager struct{} + +// Assert that ecdhX25519AESPrivateKeyManager implements the PrivateKeyManager interface. +var _ registry.PrivateKeyManager = (*ecdhX25519AESPrivateKeyManager)(nil) + +// newECDHX25519AESPrivateKeyManager creates a new ecdhX25519AESPrivateKeyManager. +func newECDHX25519AESPrivateKeyManager() *ecdhX25519AESPrivateKeyManager { + return new(ecdhX25519AESPrivateKeyManager) +} + +// Primitive creates an ECDHESPrivateKey subtle for the given serialized ECDHESPrivateKey proto. +func (km *ecdhX25519AESPrivateKeyManager) Primitive(serializedKey []byte) (interface{}, error) { + if len(serializedKey) == 0 { + return nil, errInvalidECDHX25519AESPrivateKey + } + + key := new(ecdhpb.EcdhAeadPrivateKey) + + err := proto.Unmarshal(serializedKey, key) + if err != nil { + return nil, errInvalidECDHX25519AESPrivateKey + } + + err = km.validateKey(key) + if err != nil { + return nil, errInvalidECDHX25519AESPrivateKey + } + + rEnc, err := composite.NewRegisterCompositeAEADEncHelper(key.PublicKey.Params.EncParams.AeadEnc) + if err != nil { + return nil, fmt.Errorf("ecdh_x25519kw_aesaead_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 *ecdhX25519AESPrivateKeyManager) NewKey(serializedKeyFormat []byte) (proto.Message, error) { + if len(serializedKeyFormat) == 0 { + return nil, errInvalidECDHX25519AESPrivateKeyFormat + } + + keyFormat := new(ecdhpb.EcdhAeadKeyFormat) + + err := proto.Unmarshal(serializedKeyFormat, keyFormat) + if err != nil { + return nil, errInvalidECDHX25519AESPrivateKeyFormat + } + + err = validateKeyXChachaFormat(keyFormat.Params) + if err != nil { + return nil, errInvalidECDHX25519AESPrivateKeyFormat + } + + pub, pvt, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + return nil, fmt.Errorf("ecdh_x25519kw_aesaead_private_key_manager: GenerateECDHKeyPair failed: %w", err) + } + + x25519Pub, err := cryptoutil.PublicEd25519toCurve25519(pub) + if err != nil { + return nil, fmt.Errorf("ecdh_x25519kw_aesaead_private_key_manager: Convert to X25519 pub key failed: %w", err) + } + + x25519Pvt, err := cryptoutil.SecretEd25519toCurve25519(pvt) + if err != nil { + return nil, fmt.Errorf("ecdh_x25519kw_aesaead_private_key_manager: Convert to X25519 priv key failed: %w", err) + } + + return &ecdhpb.EcdhAeadPrivateKey{ + Version: ecdhX25519AESPrivateKeyVersion, + KeyValue: x25519Pvt, + PublicKey: &ecdhpb.EcdhAeadPublicKey{ + Version: ecdhX25519AESPrivateKeyVersion, + Params: keyFormat.Params, + X: 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 *ecdhX25519AESPrivateKeyManager) 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("ecdh_x25519kw_aesaead_private_key_manager: Proto.Marshal failed: %w", err) + } + + return &tinkpb.KeyData{ + TypeUrl: ecdhX25519AESPrivateKeyTypeURL, + Value: serializedKey, + KeyMaterialType: tinkpb.KeyData_ASYMMETRIC_PRIVATE, + }, nil +} + +// PublicKeyData returns the enclosed public key data of serializedPrivKey. +func (km *ecdhX25519AESPrivateKeyManager) PublicKeyData(serializedPrivKey []byte) (*tinkpb.KeyData, error) { + privKey := new(ecdhpb.EcdhAeadPrivateKey) + + err := proto.Unmarshal(serializedPrivKey, privKey) + if err != nil { + return nil, errInvalidECDHX25519AESPrivateKey + } + + serializedPubKey, err := proto.Marshal(privKey.PublicKey) + if err != nil { + return nil, errInvalidECDHX25519AESPrivateKey + } + + return &tinkpb.KeyData{ + TypeUrl: ecdhX25519AESPublicKeyTypeURL, + Value: serializedPubKey, + KeyMaterialType: tinkpb.KeyData_ASYMMETRIC_PUBLIC, + }, nil +} + +// DoesSupport indicates if this key manager supports the given key type. +func (km *ecdhX25519AESPrivateKeyManager) DoesSupport(typeURL string) bool { + return typeURL == ecdhX25519AESPrivateKeyTypeURL +} + +// TypeURL returns the key type of keys managed by this key manager. +func (km *ecdhX25519AESPrivateKeyManager) TypeURL() string { + return ecdhX25519AESPrivateKeyTypeURL +} + +// validateKey validates the given ECDHPrivateKey and returns the KW curve. +func (km *ecdhX25519AESPrivateKeyManager) validateKey(key *ecdhpb.EcdhAeadPrivateKey) error { + err := keyset.ValidateKeyVersion(key.Version, ecdhX25519AESPrivateKeyVersion) + if err != nil { + return fmt.Errorf("ecdh_x25519kw_aesaead_private_key_manager: invalid key: %w", err) + } + + return validateKeyXChachaFormat(key.PublicKey.Params) +} diff --git a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519kw_aesaead_private_key_manager_test.go b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519kw_aesaead_private_key_manager_test.go new file mode 100644 index 0000000000..bddbf7644d --- /dev/null +++ b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519kw_aesaead_private_key_manager_test.go @@ -0,0 +1,315 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package ecdh + +import ( + "bytes" + "crypto/ed25519" + "crypto/rand" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/google/tink/go/aead" + gcmpb "github.com/google/tink/go/proto/aes_gcm_go_proto" + commonpb "github.com/google/tink/go/proto/common_go_proto" + tinkpb "github.com/google/tink/go/proto/tink_go_proto" + "github.com/stretchr/testify/require" + + "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 TestECDHX25519AESPrivateKeyManager_Primitive(t *testing.T) { + km := newECDHX25519AESPrivateKeyManager() + + t.Run("Test private key manager Primitive() with empty serialized key", func(t *testing.T) { + p, err := km.Primitive([]byte("")) + require.EqualError(t, err, errInvalidECDHX25519AESPrivateKey.Error(), + "ecdhX25519AESPrivateKeyManager primitive from empty serialized key must fail") + require.Empty(t, p) + }) + + t.Run("Test private key manager Primitive() with bad serialize key", func(t *testing.T) { + p, err := km.Primitive([]byte("bad.data")) + require.EqualError(t, err, errInvalidECDHX25519AESPrivateKey.Error(), + "ecdhX25519AESPrivateKeyManager primitive from bad serialized key must fail") + require.Empty(t, p) + }) + + format := &gcmpb.AesGcmKeyFormat{ + KeySize: 32, + } + serializedFormat, err := proto.Marshal(format) + require.NoError(t, err) + + format = &gcmpb.AesGcmKeyFormat{ + KeySize: 99, // bad AES128GCM size + } + + badSerializedFormat, err := proto.Marshal(format) + require.NoError(t, err) + + flagTests := []struct { + tcName string + version uint32 + curveType commonpb.EllipticCurveType + keyType ecdhpb.KeyType + encTmp *tinkpb.KeyTemplate + }{ + { + tcName: "private key manager Primitive() using key with bad version", + version: 9999, + curveType: commonpb.EllipticCurveType_CURVE25519, + keyType: ecdhpb.KeyType_OKP, + encTmp: aead.AES256GCMKeyTemplate(), + }, + { + tcName: "private key manager Primitive() using key with bad curve", + version: 0, + curveType: commonpb.EllipticCurveType_UNKNOWN_CURVE, + keyType: ecdhpb.KeyType_OKP, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "private key manager Primitive() using key with bad key type", + version: 0, + curveType: commonpb.EllipticCurveType_CURVE25519, + keyType: ecdhpb.KeyType_UNKNOWN_KEY_TYPE, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "success private key manager Primitive()", + version: 0, + curveType: commonpb.EllipticCurveType_CURVE25519, + keyType: ecdhpb.KeyType_OKP, + encTmp: aead.AES256GCMKeyTemplate(), + }, + { + tcName: "private key manager Primitive() using key with bad key template URL", + version: 0, + curveType: commonpb.EllipticCurveType_CURVE25519, + keyType: ecdhpb.KeyType_OKP, + encTmp: &tinkpb.KeyTemplate{ + TypeUrl: "bad.type/url/value", + Value: serializedFormat, + OutputPrefixType: tinkpb.OutputPrefixType_RAW, + }, + }, + { + tcName: "private key manager Primitive() using key with bad dem key size", + version: 0, + curveType: commonpb.EllipticCurveType_CURVE25519, + keyType: ecdhpb.KeyType_OKP, + encTmp: &tinkpb.KeyTemplate{ + TypeUrl: composite.AESGCMTypeURL, + Value: badSerializedFormat, + OutputPrefixType: tinkpb.OutputPrefixType_RAW, + }, + }, + } + + for _, tc := range flagTests { + tt := tc + t.Run("Test "+tt.tcName, func(t *testing.T) { + 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{ + CurveType: tt.curveType, // unknown curve to force an error when calling km.NewKey() + KeyType: tt.keyType, // invalid key type to force error when calling km.Primitive() + }, + EncParams: &ecdhpb.EcdhAeadEncParams{ + AeadEnc: tt.encTmp, + CEK: []byte{}, + }, + EcPointFormat: commonpb.EcPointFormat_UNCOMPRESSED, + } + + privKeyProto := &ecdhpb.EcdhAeadPrivateKey{ + Version: tt.version, + KeyValue: x25519Pvt, + PublicKey: &ecdhpb.EcdhAeadPublicKey{ + Version: ecdhX25519AESPrivateKeyVersion, + Params: params, + X: x25519Pub, + }, + } + + sPrivKey, err := proto.Marshal(privKeyProto) + require.NoError(t, err) + + p, err := km.Primitive(sPrivKey) + if bytes.Equal(tt.encTmp.Value, badSerializedFormat) { + require.EqualError(t, err, errInvalidECDHX25519AESPrivateKey.Error(), + "ecdhX25519AESPrivateKeyManager primitive from serialized key with invalid serialized key") + require.Empty(t, p) + + return + } + + if strings.Contains(tt.tcName, "success") { + require.NoError(t, err) + require.NotEmpty(t, p) + return + } + + require.Errorf(t, err, tt.tcName) + require.Empty(t, p) + }) + } +} + +func TestECDHX25519AESPrivateKeyManager_DoesSupport(t *testing.T) { + km := newECDHX25519AESPrivateKeyManager() + require.False(t, km.DoesSupport("bad/url")) + require.True(t, km.DoesSupport(ecdhX25519AESPrivateKeyTypeURL)) +} + +func TestECDHX25519AESPrivateKeyManager_NewKey(t *testing.T) { + km := newECDHX25519AESPrivateKeyManager() + + t.Run("Test private key manager NewKey() with nil key", func(t *testing.T) { + k, err := km.NewKey(nil) + require.EqualError(t, err, errInvalidECDHX25519AESPrivateKeyFormat.Error()) + require.Empty(t, k) + }) + + t.Run("Test private key manager NewKey() with bad serialize key", func(t *testing.T) { + p, err := km.NewKey([]byte("bad.data")) + require.EqualError(t, err, errInvalidECDHX25519AESPrivateKeyFormat.Error(), + "ecdhX25519AESPrivateKeyManager NewKey() from bad serialized key must fail") + require.Empty(t, p) + }) + + format := &gcmpb.AesGcmKeyFormat{ + KeySize: 32, + } + + serializedFormat, err := proto.Marshal(format) + require.NoError(t, err) + + format = &gcmpb.AesGcmKeyFormat{ + KeySize: 99, // bad AES128GCM size + } + + badSerializedFormat, err := proto.Marshal(format) + require.NoError(t, err) + + flagTests := []struct { + tcName string + curveType commonpb.EllipticCurveType + keyType ecdhpb.KeyType + encTmp *tinkpb.KeyTemplate + }{ + { + tcName: "success private key manager NewKey() and NewKeyData()", + curveType: commonpb.EllipticCurveType_CURVE25519, + keyType: ecdhpb.KeyType_OKP, + encTmp: aead.AES256GCMKeyTemplate(), + }, + { + tcName: "private key manager NewKey() and NewKeyData() using key with bad curve", + curveType: commonpb.EllipticCurveType_UNKNOWN_CURVE, + keyType: ecdhpb.KeyType_OKP, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "private key manager NewKey() and NewKeyData() using key with bad key type", + curveType: commonpb.EllipticCurveType_CURVE25519, + keyType: ecdhpb.KeyType_UNKNOWN_KEY_TYPE, + encTmp: aead.AES256GCMKeyTemplate(), + }, + { + tcName: "private key manager NewKey() and NewKeyData() using key with bad key template URL", + curveType: commonpb.EllipticCurveType_CURVE25519, + keyType: ecdhpb.KeyType_OKP, + encTmp: &tinkpb.KeyTemplate{ + TypeUrl: "bad.type/url/value", + Value: serializedFormat, + OutputPrefixType: tinkpb.OutputPrefixType_RAW, + }, + }, + { + tcName: "private key manager NewKey() and NewKeyData() using key with bad dem key size", + encTmp: &tinkpb.KeyTemplate{ + TypeUrl: composite.AESGCMTypeURL, + Value: badSerializedFormat, + OutputPrefixType: tinkpb.OutputPrefixType_RAW, + }, + }, + } + + for _, tc := range flagTests { + tt := tc + t.Run("Test "+tt.tcName, func(t *testing.T) { + privKeyProto := &ecdhpb.EcdhAeadKeyFormat{ + Params: &ecdhpb.EcdhAeadParams{ + KwParams: &ecdhpb.EcdhKwParams{ + CurveType: tt.curveType, // unknown curve to force an error when calling km.NewKey() + KeyType: tt.keyType, // unknown curve type to force an error when calling km.NewKey() + }, + EncParams: &ecdhpb.EcdhAeadEncParams{ + AeadEnc: tt.encTmp, // invalid data enc key template to force an error when calling km.NewKey() + }, + EcPointFormat: commonpb.EcPointFormat_UNCOMPRESSED, + }, + } + + sPrivKey, err := proto.Marshal(privKeyProto) + require.NoError(t, err) + + p, err := km.NewKey(sPrivKey) + if strings.Contains(tt.tcName, "success") { + require.NoError(t, err) + require.NotEmpty(t, p) + + sp, e := proto.Marshal(p) + require.NoError(t, e) + require.NotEmpty(t, sp) + + // try PublicKeyData() with bad serialized private key + pubK, e := km.PublicKeyData([]byte("bad serialized private key")) + require.Error(t, e) + require.Empty(t, pubK) + + // try PublicKeyData() with valid serialized private key + pubK, e = km.PublicKeyData(sp) + require.NoError(t, e) + require.NotEmpty(t, pubK) + } + + kd, err := km.NewKeyData(sPrivKey) + if strings.Contains(tt.tcName, "success") { + require.NoError(t, err) + require.NotEmpty(t, kd) + require.Equal(t, kd.TypeUrl, ecdhX25519AESPrivateKeyTypeURL) + require.Equal(t, kd.KeyMaterialType, tinkpb.KeyData_ASYMMETRIC_PRIVATE) + return + } + + if bytes.Equal(tt.encTmp.Value, badSerializedFormat) { + require.EqualError(t, err, errInvalidECDHX25519AESPrivateKeyFormat.Error(), + "ecdhX25519AESPrivateKeyManager NewKey from serialized key with invalid serialized key") + require.Empty(t, p) + + return + } + + require.Errorf(t, err, tt.tcName) + require.Empty(t, p) + }) + } +} diff --git a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519kw_aesaead_public_key_manager.go b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519kw_aesaead_public_key_manager.go new file mode 100644 index 0000000000..7dfb3110e1 --- /dev/null +++ b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519kw_aesaead_public_key_manager.go @@ -0,0 +1,99 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package ecdh + +import ( + "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" +) + +const ( + ecdhX25519AESPublicKeyVersion = 0 + ecdhX25519AESPublicKeyTypeURL = "type.hyperledger.org/hyperledger.aries.crypto.tink.EcdhX25519KwAesAeadPublicKey" +) + +// common errors. +var errInvalidECDHX25519AESPublicKey = errors.New("ecdh_x25519kw_aesaead_public_key_manager: invalid key") + +// ecdhX25519AESPublicKeyManager is an implementation of KeyManager interface for X25519 key wrapping and +// AES20Poly1305 content encryption. +// It generates new ECDHPublicKey (X25519) keys and produces new instances of ECDHAEADCompositeEncrypt subtle. +type ecdhX25519AESPublicKeyManager struct{} + +// Assert that ecdhX25519AESPublicKeyManager implements the KeyManager interface. +var _ registry.KeyManager = (*ecdhX25519AESPublicKeyManager)(nil) + +// newECDHX25519AESPublicKeyManager creates a new ecdhX25519AESPublicKeyManager. +func newECDHX25519AESPublicKeyManager() *ecdhX25519AESPublicKeyManager { + return new(ecdhX25519AESPublicKeyManager) +} + +// Primitive creates an ECDHESAESPublicKey subtle for the given serialized ECDHESAESPublicKey proto. +func (km *ecdhX25519AESPublicKeyManager) Primitive(serializedKey []byte) (interface{}, error) { + if len(serializedKey) == 0 { + return nil, errInvalidECDHX25519AESPublicKey + } + + ecdhPubKey := new(ecdhpb.EcdhAeadPublicKey) + + err := proto.Unmarshal(serializedKey, ecdhPubKey) + if err != nil { + return nil, errInvalidECDHX25519AESPublicKey + } + + err = km.validateKey(ecdhPubKey) + if err != nil { + return nil, errInvalidECDHX25519AESPublicKey + } + + rEnc, err := composite.NewRegisterCompositeAEADEncHelper(ecdhPubKey.Params.EncParams.AeadEnc) + if err != nil { + return nil, fmt.Errorf("ecdh_x25519kw_aesaead_public_key_manager: NewRegisterCompositeAEADEncHelper "+ + "failed: %w", err) + } + + return subtle.NewECDHAEADCompositeEncrypt(rEnc, ecdhPubKey.Params.EncParams.CEK), nil +} + +// DoesSupport indicates if this key manager supports the given key type. +func (km *ecdhX25519AESPublicKeyManager) DoesSupport(typeURL string) bool { + return typeURL == ecdhX25519AESPublicKeyTypeURL +} + +// TypeURL returns the key type of keys managed by this key manager. +func (km *ecdhX25519AESPublicKeyManager) TypeURL() string { + return ecdhX25519AESPublicKeyTypeURL +} + +// NewKey is not implemented for public key manager. +func (km *ecdhX25519AESPublicKeyManager) NewKey(serializedKeyFormat []byte) (proto.Message, error) { + return nil, errors.New("ecdh_x25519kw_aesaead_public_key_manager: NewKey not implemented") +} + +// NewKeyData is not implemented for public key manager. +func (km *ecdhX25519AESPublicKeyManager) NewKeyData(serializedKeyFormat []byte) (*tinkpb.KeyData, error) { + return nil, errors.New("ecdh_x25519kw_aesaead_public_key_manager: NewKeyData not implemented") +} + +// validateKey validates the given EcdhAeadPublicKey. +func (km *ecdhX25519AESPublicKeyManager) validateKey(key *ecdhpb.EcdhAeadPublicKey) error { + err := keyset.ValidateKeyVersion(key.Version, ecdhX25519AESPublicKeyVersion) + if err != nil { + return fmt.Errorf("ecdh_x25519kw_aesaead_public_key_manager: invalid key: %w", err) + } + + return validateKeyXChachaFormat(key.Params) +} diff --git a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519kw_aesaead_public_key_manager_test.go b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519kw_aesaead_public_key_manager_test.go new file mode 100644 index 0000000000..3be1f0642c --- /dev/null +++ b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519kw_aesaead_public_key_manager_test.go @@ -0,0 +1,185 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package ecdh + +import ( + "crypto/ed25519" + "crypto/rand" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/google/tink/go/aead" + gcmpb "github.com/google/tink/go/proto/aes_gcm_go_proto" + commonpb "github.com/google/tink/go/proto/common_go_proto" + tinkpb "github.com/google/tink/go/proto/tink_go_proto" + "github.com/stretchr/testify/require" + + "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 TestECDHX25519AESPublicKeyManager_Primitive(t *testing.T) { + km := newECDHX25519AESPublicKeyManager() + + t.Run("Test public key manager Primitive() with empty serialized key", func(t *testing.T) { + p, err := km.Primitive([]byte("")) + require.EqualError(t, err, errInvalidECDHX25519AESPublicKey.Error(), + "ecdhX25519AESPublicKeyManager primitive from empty serialized key must fail") + require.Empty(t, p) + }) + + t.Run("Test public key manager Primitive() with bad serialize key", func(t *testing.T) { + p, err := km.Primitive([]byte("bad.data")) + require.EqualError(t, err, errInvalidECDHX25519AESPublicKey.Error(), + "ecdhX25519AESPublicKeyManager primitive from bad serialized key must fail") + require.Empty(t, p) + }) + + format := &gcmpb.AesGcmKeyFormat{ + KeySize: 32, + } + + serializedFormat, err := proto.Marshal(format) + require.NoError(t, err) + + format = &gcmpb.AesGcmKeyFormat{ + KeySize: 99, // bad AES128GCM size + } + + badSerializedFormat, err := proto.Marshal(format) + require.NoError(t, err) + + flagTests := []struct { + tcName string + version uint32 + curveType commonpb.EllipticCurveType + keyType ecdhpb.KeyType + encTmp *tinkpb.KeyTemplate + }{ + { + tcName: "public key manager Primitive() using key with bad version", + version: 9999, + curveType: commonpb.EllipticCurveType_CURVE25519, + keyType: ecdhpb.KeyType_OKP, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "private key manager NewKey() and NewKeyData() using key with bad curve", + version: 0, + curveType: commonpb.EllipticCurveType_UNKNOWN_CURVE, + keyType: ecdhpb.KeyType_OKP, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "public key manager Primitive() using key with bad key type", + version: 0, + curveType: commonpb.EllipticCurveType_CURVE25519, + keyType: ecdhpb.KeyType_EC, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "success public key manager Primitive()", + version: 0, + curveType: commonpb.EllipticCurveType_CURVE25519, + keyType: ecdhpb.KeyType_OKP, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "public key manager Primitive() using key with bad key template URL", + version: 0, + curveType: commonpb.EllipticCurveType_CURVE25519, + keyType: ecdhpb.KeyType_OKP, + encTmp: &tinkpb.KeyTemplate{ + TypeUrl: "bad.type/url/value", + Value: serializedFormat, + OutputPrefixType: tinkpb.OutputPrefixType_RAW, + }, + }, + { + tcName: "public key manager Primitive() using key with bad content encryption key size", + version: 0, + curveType: commonpb.EllipticCurveType_CURVE25519, + encTmp: &tinkpb.KeyTemplate{ + TypeUrl: composite.AESGCMTypeURL, + Value: badSerializedFormat, + OutputPrefixType: tinkpb.OutputPrefixType_RAW, + }, + }, + } + + for _, tc := range flagTests { + tt := tc + t.Run("Test "+tt.tcName, func(t *testing.T) { + pub, _, err := ed25519.GenerateKey(rand.Reader) + require.NoError(t, err) + + x25519Pub, err := cryptoutil.PublicEd25519toCurve25519(pub) + require.NoError(t, err) + + pubKeyProto := &ecdhpb.EcdhAeadPublicKey{ + Version: tt.version, // if version > 0 to force an error when calling km.Primitive() + Params: &ecdhpb.EcdhAeadParams{ + KwParams: &ecdhpb.EcdhKwParams{ + CurveType: tt.curveType, // unknown curve to force an error when calling km.NewKey() + KeyType: tt.keyType, // invalid key type to force error when calling km.Primitive() + }, + EncParams: &ecdhpb.EcdhAeadEncParams{ + AeadEnc: tt.encTmp, // invalid data enc key template to force error when calling km.Primitive() + CEK: []byte{}, + }, + EcPointFormat: commonpb.EcPointFormat_UNCOMPRESSED, + }, + X: x25519Pub, + } + + sPubKey, err := proto.Marshal(pubKeyProto) + require.NoError(t, err) + + p, err := km.Primitive(sPubKey) + if strings.Contains(tt.tcName, "with bad content encryption key size") { + require.EqualError(t, err, errInvalidECDHX25519AESPublicKey.Error(), + "ecdhX25519AESPublicKeyManager primitive from serialized key with invalid serialized key") + require.Empty(t, p) + + return + } + + if strings.Contains(tt.tcName, "success") { + require.NoError(t, err) + require.NotEmpty(t, p) + return + } + + require.Errorf(t, err, tt.tcName) + require.Empty(t, p) + }) + } +} + +func TestEcdhX25519AESPublicKeyManager_DoesSupport(t *testing.T) { + km := newECDHX25519AESPublicKeyManager() + require.False(t, km.DoesSupport("bad/url")) + require.True(t, km.DoesSupport(ecdhX25519AESPublicKeyTypeURL)) +} + +func TestEcdhX25519AESPublicKeyManager_NewKeyAndNewKeyData(t *testing.T) { + km := newECDHX25519AESPublicKeyManager() + + t.Run("Test public key manager NewKey()", func(t *testing.T) { + k, err := km.NewKey(nil) + require.EqualError(t, err, "ecdh_x25519kw_aesaead_public_key_manager: NewKey not implemented") + require.Empty(t, k) + }) + + t.Run("Test private key manager NewKeyData()", func(t *testing.T) { + p, err := km.NewKeyData(nil) + require.EqualError(t, err, "ecdh_x25519kw_aesaead_public_key_manager: NewKeyData not implemented") + require.Empty(t, p) + }) +} diff --git a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519kw_xchachaaead_private_key_manager.go b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519kw_xchachaaead_private_key_manager.go new file mode 100644 index 0000000000..3c901ec7a2 --- /dev/null +++ b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519kw_xchachaaead_private_key_manager.go @@ -0,0 +1,211 @@ +/* +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" + commonpb "github.com/google/tink/go/proto/common_go_proto" + 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 ( + ecdhX25519XChachaPrivateKeyVersion = 0 + ecdhX25519XChachaPrivateKeyTypeURL = "type.hyperledger.org/hyperledger.aries.crypto.tink.EcdhX25519KwXChachaAeadPrivateKey" // nolint:lll +) + +// common errors. +var ( + errInvalidECDHX25519XChachaPrivateKey = errors.New("ecdh_x25519kw_xchachaaead_private_key_manager: invalid key") // nolint:lll + errInvalidECDHX25519XChachaPrivateKeyFormat = errors.New("ecdh_x25519kw_xchachaaead_private_key_manager: invalid key format") // nolint:lll +) + +// ecdhX25519XChachaPrivateKeyManager is an implementation of PrivateKeyManager interface for X25519 key wrapping and +// XChacha20Poly1305 content encryption. +// It generates new ECDHPrivateKey (X25519 KW) keys and produces new instances of ECDHAEADCompositeDecrypt subtle. +type ecdhX25519XChachaPrivateKeyManager struct{} + +// Assert that ecdhX25519XChachaPrivateKeyManager implements the PrivateKeyManager interface. +var _ registry.PrivateKeyManager = (*ecdhX25519XChachaPrivateKeyManager)(nil) + +// newECDHX25519XChachaPrivateKeyManager creates a new ecdhX25519XChachaPrivateKeyManager. +func newECDHX25519XChachaPrivateKeyManager() *ecdhX25519XChachaPrivateKeyManager { + return new(ecdhX25519XChachaPrivateKeyManager) +} + +// Primitive creates an ECDHESPrivateKey subtle for the given serialized ECDHESPrivateKey proto. +func (km *ecdhX25519XChachaPrivateKeyManager) Primitive(serializedKey []byte) (interface{}, error) { + if len(serializedKey) == 0 { + return nil, errInvalidECDHX25519XChachaPrivateKey + } + + key := new(ecdhpb.EcdhAeadPrivateKey) + + err := proto.Unmarshal(serializedKey, key) + if err != nil { + return nil, errInvalidECDHX25519XChachaPrivateKey + } + + err = km.validateKey(key) + if err != nil { + return nil, errInvalidECDHX25519XChachaPrivateKey + } + + rEnc, err := composite.NewRegisterCompositeAEADEncHelper(key.PublicKey.Params.EncParams.AeadEnc) + if err != nil { + return nil, fmt.Errorf("ecdh_x25519kw_xchachaaead_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 *ecdhX25519XChachaPrivateKeyManager) NewKey(serializedKeyFormat []byte) (proto.Message, error) { + if len(serializedKeyFormat) == 0 { + return nil, errInvalidECDHX25519XChachaPrivateKeyFormat + } + + keyFormat := new(ecdhpb.EcdhAeadKeyFormat) + + err := proto.Unmarshal(serializedKeyFormat, keyFormat) + if err != nil { + return nil, errInvalidECDHX25519XChachaPrivateKeyFormat + } + + err = validateKeyXChachaFormat(keyFormat.Params) + if err != nil { + return nil, errInvalidECDHX25519XChachaPrivateKeyFormat + } + + pub, pvt, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + return nil, fmt.Errorf("ecdh_x25519kw_xchachaaead_private_key_manager: GenerateECDHKeyPair failed: %w", err) + } + + x25519Pub, err := cryptoutil.PublicEd25519toCurve25519(pub) + if err != nil { + return nil, fmt.Errorf("ecdh_x25519kw_xchachaaead_private_key_manager: Convert to X25519 pub key failed: %w", err) + } + + x25519Pvt, err := cryptoutil.SecretEd25519toCurve25519(pvt) + if err != nil { + return nil, fmt.Errorf("ecdh_x25519kw_xchachaaead_private_key_manager: Convert to X25519 priv key failed: %w", err) + } + + return &ecdhpb.EcdhAeadPrivateKey{ + Version: ecdhX25519XChachaPrivateKeyVersion, + KeyValue: x25519Pvt, + PublicKey: &ecdhpb.EcdhAeadPublicKey{ + Version: ecdhX25519XChachaPrivateKeyVersion, + Params: keyFormat.Params, + X: 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 *ecdhX25519XChachaPrivateKeyManager) 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("ecdh_x25519kw_xchachaaead_private_key_manager: Proto.Marshal failed: %w", err) + } + + return &tinkpb.KeyData{ + TypeUrl: ecdhX25519XChachaPrivateKeyTypeURL, + Value: serializedKey, + KeyMaterialType: tinkpb.KeyData_ASYMMETRIC_PRIVATE, + }, nil +} + +// PublicKeyData returns the enclosed public key data of serializedPrivKey. +func (km *ecdhX25519XChachaPrivateKeyManager) PublicKeyData(serializedPrivKey []byte) (*tinkpb.KeyData, error) { + privKey := new(ecdhpb.EcdhAeadPrivateKey) + + err := proto.Unmarshal(serializedPrivKey, privKey) + if err != nil { + return nil, errInvalidECDHX25519XChachaPrivateKey + } + + serializedPubKey, err := proto.Marshal(privKey.PublicKey) + if err != nil { + return nil, errInvalidECDHX25519XChachaPrivateKey + } + + return &tinkpb.KeyData{ + TypeUrl: ecdhX25519XChachaPublicKeyTypeURL, + Value: serializedPubKey, + KeyMaterialType: tinkpb.KeyData_ASYMMETRIC_PUBLIC, + }, nil +} + +// DoesSupport indicates if this key manager supports the given key type. +func (km *ecdhX25519XChachaPrivateKeyManager) DoesSupport(typeURL string) bool { + return typeURL == ecdhX25519XChachaPrivateKeyTypeURL +} + +// TypeURL returns the key type of keys managed by this key manager. +func (km *ecdhX25519XChachaPrivateKeyManager) TypeURL() string { + return ecdhX25519XChachaPrivateKeyTypeURL +} + +// validateKey validates the given ECDHPrivateKey and returns the KW curve. +func (km *ecdhX25519XChachaPrivateKeyManager) validateKey(key *ecdhpb.EcdhAeadPrivateKey) error { + err := keyset.ValidateKeyVersion(key.Version, ecdhX25519XChachaPrivateKeyVersion) + if err != nil { + return fmt.Errorf("ecdh_x25519kw_xchachaaead_private_key_manager: invalid key: %w", err) + } + + return validateKeyXChachaFormat(key.PublicKey.Params) +} + +// validateKeyXChachaFormat 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("ecdh_x25519kw_xchachaaead_private_key_manager: GetKeyManager error: %w", err) + } + + _, err = km.NewKeyData(params.EncParams.AeadEnc.Value) + if err != nil { + return fmt.Errorf("ecdh_x25519kw_xchachaaead_private_key_manager: NewKeyData error: %w", err) + } + + if params.KwParams.KeyType.String() != ecdhpb.KeyType_OKP.String() { + return fmt.Errorf("ecdh_x25519kw_xchachaaead_private_key_manager: invalid key type %v", + params.KwParams.KeyType) + } + + // if CEK is not set, this is a KW key for storage, it must have the curve. + // if it is set, then this is a primitive execution key, the curve is not needed since we do content encryption. + if params.EncParams.CEK == nil && + params.KwParams.CurveType.String() != commonpb.EllipticCurveType_CURVE25519.String() { + return fmt.Errorf("ecdh_x25519kw_xchachaaead_private_key_manager: invalid curve %v", + params.KwParams.CurveType) + } + + return nil +} diff --git a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519kw_xchachaaead_private_key_manager_test.go b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519kw_xchachaaead_private_key_manager_test.go new file mode 100644 index 0000000000..bd379e2ef7 --- /dev/null +++ b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519kw_xchachaaead_private_key_manager_test.go @@ -0,0 +1,248 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package ecdh + +import ( + "crypto/ed25519" + "crypto/rand" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/google/tink/go/aead" + commonpb "github.com/google/tink/go/proto/common_go_proto" + tinkpb "github.com/google/tink/go/proto/tink_go_proto" + "github.com/stretchr/testify/require" + + 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 TestECDHX25519XChachaPrivateKeyManager_Primitive(t *testing.T) { + km := newECDHX25519XChachaPrivateKeyManager() + + t.Run("Test private key manager Primitive() with empty serialized key", func(t *testing.T) { + p, err := km.Primitive([]byte("")) + require.EqualError(t, err, errInvalidECDHX25519XChachaPrivateKey.Error(), + "ecdhX25519XChachaPrivateKeyManager primitive from empty serialized key must fail") + require.Empty(t, p) + }) + + t.Run("Test private key manager Primitive() with bad serialize key", func(t *testing.T) { + p, err := km.Primitive([]byte("bad.data")) + require.EqualError(t, err, errInvalidECDHX25519XChachaPrivateKey.Error(), + "ecdhX25519XChachaPrivateKeyManager primitive from bad serialized key must fail") + require.Empty(t, p) + }) + + flagTests := []struct { + tcName string + version uint32 + curveType commonpb.EllipticCurveType + keyType ecdhpb.KeyType + encTmp *tinkpb.KeyTemplate + }{ + { + tcName: "private key manager Primitive() using key with bad version", + version: 9999, + curveType: commonpb.EllipticCurveType_CURVE25519, + keyType: ecdhpb.KeyType_OKP, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "private key manager Primitive() using key with bad curve", + version: 0, + curveType: commonpb.EllipticCurveType_UNKNOWN_CURVE, + keyType: ecdhpb.KeyType_OKP, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "private key manager Primitive() using key with bad key type", + version: 0, + curveType: commonpb.EllipticCurveType_CURVE25519, + keyType: ecdhpb.KeyType_UNKNOWN_KEY_TYPE, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "success private key manager Primitive()", + version: 0, + curveType: commonpb.EllipticCurveType_CURVE25519, + keyType: ecdhpb.KeyType_OKP, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "private key manager Primitive() using key with bad key template URL", + version: 0, + curveType: commonpb.EllipticCurveType_CURVE25519, + keyType: ecdhpb.KeyType_OKP, + encTmp: &tinkpb.KeyTemplate{ + TypeUrl: "bad.type/url/value", + OutputPrefixType: tinkpb.OutputPrefixType_RAW, + }, + }, + } + + for _, tc := range flagTests { + tt := tc + t.Run("Test "+tt.tcName, func(t *testing.T) { + 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: tt.keyType, + CurveType: tt.curveType, + }, + EncParams: &ecdhpb.EcdhAeadEncParams{ + AeadEnc: tt.encTmp, + CEK: []byte{}, + }, + EcPointFormat: commonpb.EcPointFormat_UNCOMPRESSED, + } + + privKeyProto := &ecdhpb.EcdhAeadPrivateKey{ + Version: tt.version, + KeyValue: x25519Pvt, + PublicKey: &ecdhpb.EcdhAeadPublicKey{ + Version: ecdhX25519XChachaPrivateKeyVersion, + Params: params, + X: x25519Pub, + }, + } + + sPrivKey, err := proto.Marshal(privKeyProto) + require.NoError(t, err) + + p, err := km.Primitive(sPrivKey) + if strings.Contains(tt.tcName, "success") { + require.NoError(t, err) + require.NotEmpty(t, p) + return + } + + require.Errorf(t, err, tt.tcName) + require.Empty(t, p) + }) + } +} + +func TestECDHX25519XChachaPrivateKeyManager_DoesSupport(t *testing.T) { + km := newECDHX25519XChachaPrivateKeyManager() + require.False(t, km.DoesSupport("bad/url")) + require.True(t, km.DoesSupport(ecdhX25519XChachaPrivateKeyTypeURL)) +} + +func TestECDHX25519XChachaPrivateKeyManager_NewKey(t *testing.T) { + km := newECDHX25519XChachaPrivateKeyManager() + + t.Run("Test private key manager NewKey() with nil key", func(t *testing.T) { + k, err := km.NewKey(nil) + require.EqualError(t, err, errInvalidECDHX25519XChachaPrivateKeyFormat.Error()) + require.Empty(t, k) + }) + + t.Run("Test private key manager NewKey() with bad serialize key", func(t *testing.T) { + p, err := km.NewKey([]byte("bad.data")) + require.EqualError(t, err, errInvalidECDHX25519XChachaPrivateKeyFormat.Error(), + "ecdhX25519XChachaPrivateKeyManager NewKey() from bad serialized key must fail") + require.Empty(t, p) + }) + + flagTests := []struct { + tcName string + curveType commonpb.EllipticCurveType + keyType ecdhpb.KeyType + encTmp *tinkpb.KeyTemplate + }{ + { + tcName: "success private key manager NewKey() and NewKeyData()", + curveType: commonpb.EllipticCurveType_CURVE25519, + keyType: ecdhpb.KeyType_OKP, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "private key manager NewKey() and NewKeyData() using key with bad curve", + curveType: commonpb.EllipticCurveType_UNKNOWN_CURVE, + keyType: ecdhpb.KeyType_OKP, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "private key manager NewKey() and NewKeyData() using key with bad key type", + curveType: commonpb.EllipticCurveType_CURVE25519, + keyType: ecdhpb.KeyType_EC, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "private key manager NewKey() and NewKeyData() using key with bad key template URL", + curveType: commonpb.EllipticCurveType_CURVE25519, + keyType: ecdhpb.KeyType_OKP, + encTmp: &tinkpb.KeyTemplate{ + TypeUrl: "bad.type/url/value", + OutputPrefixType: tinkpb.OutputPrefixType_RAW, + }, + }, + } + + for _, tc := range flagTests { + tt := tc + t.Run("Test "+tt.tcName, func(t *testing.T) { + privKeyProto := &ecdhpb.EcdhAeadKeyFormat{ + Params: &ecdhpb.EcdhAeadParams{ + KwParams: &ecdhpb.EcdhKwParams{ + CurveType: tt.curveType, // unknown curve to force an error when calling km.NewKey() + KeyType: tt.keyType, // unknown key type to force an error when calling km.NewKey() + }, + EncParams: &ecdhpb.EcdhAeadEncParams{ + AeadEnc: tt.encTmp, // invalid data enc key template to force an error when calling km.NewKey() + }, + EcPointFormat: commonpb.EcPointFormat_UNCOMPRESSED, + }, + } + + sPrivKey, err := proto.Marshal(privKeyProto) + require.NoError(t, err) + + p, err := km.NewKey(sPrivKey) + if strings.Contains(tt.tcName, "success") { + require.NoError(t, err) + require.NotEmpty(t, p) + + sp, e := proto.Marshal(p) + require.NoError(t, e) + require.NotEmpty(t, sp) + + // try PublicKeyData() with bad serialized private key + pubK, e := km.PublicKeyData([]byte("bad serialized private key")) + require.Error(t, e) + require.Empty(t, pubK) + + // try PublicKeyData() with valid serialized private key + pubK, e = km.PublicKeyData(sp) + require.NoError(t, e) + require.NotEmpty(t, pubK) + } + + kd, err := km.NewKeyData(sPrivKey) + if strings.Contains(tt.tcName, "success") { + require.NoError(t, err) + require.NotEmpty(t, kd) + require.Equal(t, kd.TypeUrl, ecdhX25519XChachaPrivateKeyTypeURL) + require.Equal(t, kd.KeyMaterialType, tinkpb.KeyData_ASYMMETRIC_PRIVATE) + return + } + + require.Errorf(t, err, tt.tcName) + require.Empty(t, p) + }) + } +} diff --git a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519kw_xchachaaead_public_key_manager.go b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519kw_xchachaaead_public_key_manager.go new file mode 100644 index 0000000000..c3ca07966c --- /dev/null +++ b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519kw_xchachaaead_public_key_manager.go @@ -0,0 +1,99 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package ecdh + +import ( + "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" +) + +const ( + ecdhX25519XChachaPublicKeyVersion = 0 + ecdhX25519XChachaPublicKeyTypeURL = "type.hyperledger.org/hyperledger.aries.crypto.tink.EcdhX25519KwXChachaAeadPublicKey" // nolint:lll +) + +// common errors. +var errInvalidECDHX25519XChachaPublicKey = errors.New("ecdh_x25519kw_xchachaaead_public_key_manager: invalid key") + +// ecdhX25519XChachaPublicKeyManager is an implementation of KeyManager interface for X25519 key wrapping and +// XChacha20Poly1305 content encryption. +// It generates new ECDHPublicKey (X25519) keys and produces new instances of ECDHAEADCompositeEncrypt subtle. +type ecdhX25519XChachaPublicKeyManager struct{} + +// Assert that ecdhX25519XChachaPublicKeyManager implements the KeyManager interface. +var _ registry.KeyManager = (*ecdhX25519XChachaPublicKeyManager)(nil) + +// newECDHX25519XChachaPublicKeyManager creates a new ecdhX25519XChachaPublicKeyManager. +func newECDHX25519XChachaPublicKeyManager() *ecdhX25519XChachaPublicKeyManager { + return new(ecdhX25519XChachaPublicKeyManager) +} + +// Primitive creates an ECDHESXChachaPublicKey subtle for the given serialized ECDHESXChachaPublicKey proto. +func (km *ecdhX25519XChachaPublicKeyManager) Primitive(serializedKey []byte) (interface{}, error) { + if len(serializedKey) == 0 { + return nil, errInvalidECDHX25519XChachaPublicKey + } + + ecdhPubKey := new(ecdhpb.EcdhAeadPublicKey) + + err := proto.Unmarshal(serializedKey, ecdhPubKey) + if err != nil { + return nil, errInvalidECDHX25519XChachaPublicKey + } + + err = km.validateKey(ecdhPubKey) + if err != nil { + return nil, errInvalidECDHX25519XChachaPublicKey + } + + rEnc, err := composite.NewRegisterCompositeAEADEncHelper(ecdhPubKey.Params.EncParams.AeadEnc) + if err != nil { + return nil, fmt.Errorf("ecdh_x25519kw_xchachaaead_public_key_manager: NewRegisterCompositeAEADEncHelper "+ + "failed: %w", err) + } + + return subtle.NewECDHAEADCompositeEncrypt(rEnc, ecdhPubKey.Params.EncParams.CEK), nil +} + +// DoesSupport indicates if this key manager supports the given key type. +func (km *ecdhX25519XChachaPublicKeyManager) DoesSupport(typeURL string) bool { + return typeURL == ecdhX25519XChachaPublicKeyTypeURL +} + +// TypeURL returns the key type of keys managed by this key manager. +func (km *ecdhX25519XChachaPublicKeyManager) TypeURL() string { + return ecdhX25519XChachaPublicKeyTypeURL +} + +// NewKey is not implemented for public key manager. +func (km *ecdhX25519XChachaPublicKeyManager) NewKey(serializedKeyFormat []byte) (proto.Message, error) { + return nil, errors.New("ecdh_x25519kw_xchachaaead_public_key_manager: NewKey not implemented") +} + +// NewKeyData is not implemented for public key manager. +func (km *ecdhX25519XChachaPublicKeyManager) NewKeyData(serializedKeyFormat []byte) (*tinkpb.KeyData, error) { + return nil, errors.New("ecdh_x25519kw_xchachaaead_public_key_manager: NewKeyData not implemented") +} + +// validateKey validates the given EcdhAeadPublicKey. +func (km *ecdhX25519XChachaPublicKeyManager) validateKey(key *ecdhpb.EcdhAeadPublicKey) error { + err := keyset.ValidateKeyVersion(key.Version, ecdhX25519XChachaPublicKeyVersion) + if err != nil { + return fmt.Errorf("ecdh_x25519kw_xchachaaead_public_key_manager: invalid key: %w", err) + } + + return validateKeyXChachaFormat(key.Params) +} diff --git a/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519kw_xchachaaead_public_key_manager_test.go b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519kw_xchachaaead_public_key_manager_test.go new file mode 100644 index 0000000000..a83d467bf1 --- /dev/null +++ b/pkg/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_x25519kw_xchachaaead_public_key_manager_test.go @@ -0,0 +1,158 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package ecdh + +import ( + "crypto/ed25519" + "crypto/rand" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/google/tink/go/aead" + commonpb "github.com/google/tink/go/proto/common_go_proto" + tinkpb "github.com/google/tink/go/proto/tink_go_proto" + "github.com/stretchr/testify/require" + + 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 TestECDHX25519XChachaPublicKeyManager_Primitive(t *testing.T) { + km := newECDHX25519XChachaPublicKeyManager() + + t.Run("Test public key manager Primitive() with empty serialized key", func(t *testing.T) { + p, err := km.Primitive([]byte("")) + require.EqualError(t, err, errInvalidECDHX25519XChachaPublicKey.Error(), + "ecdhX25519XChachaPublicKeyManager primitive from empty serialized key must fail") + require.Empty(t, p) + }) + + t.Run("Test public key manager Primitive() with bad serialize key", func(t *testing.T) { + p, err := km.Primitive([]byte("bad.data")) + require.EqualError(t, err, errInvalidECDHX25519XChachaPublicKey.Error(), + "ecdhX25519XChachaPublicKeyManager primitive from bad serialized key must fail") + require.Empty(t, p) + }) + + flagTests := []struct { + tcName string + version uint32 + curveType commonpb.EllipticCurveType + keyType ecdhpb.KeyType + encTmp *tinkpb.KeyTemplate + }{ + { + tcName: "public key manager Primitive() using key with bad version", + version: 9999, + curveType: commonpb.EllipticCurveType_CURVE25519, + keyType: ecdhpb.KeyType_OKP, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "private key manager NewKey() and NewKeyData() using key with bad curve", + version: 0, + curveType: commonpb.EllipticCurveType_UNKNOWN_CURVE, + keyType: ecdhpb.KeyType_OKP, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "public key manager Primitive() using key with bad key type", + version: 0, + curveType: commonpb.EllipticCurveType_CURVE25519, + keyType: ecdhpb.KeyType_EC, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "success public key manager Primitive()", + version: 0, + curveType: commonpb.EllipticCurveType_CURVE25519, + keyType: ecdhpb.KeyType_OKP, + encTmp: aead.XChaCha20Poly1305KeyTemplate(), + }, + { + tcName: "public key manager Primitive() using key with bad key template URL", + version: 0, + curveType: commonpb.EllipticCurveType_CURVE25519, + keyType: ecdhpb.KeyType_OKP, + encTmp: &tinkpb.KeyTemplate{ + TypeUrl: "bad.type/url/value", + OutputPrefixType: tinkpb.OutputPrefixType_RAW, + }, + }, + } + + for _, tc := range flagTests { + tt := tc + t.Run("Test "+tt.tcName, func(t *testing.T) { + pub, _, err := ed25519.GenerateKey(rand.Reader) + require.NoError(t, err) + + x25519Pub, err := cryptoutil.PublicEd25519toCurve25519(pub) + require.NoError(t, err) + + pubKeyProto := &ecdhpb.EcdhAeadPublicKey{ + Version: tt.version, // if version > 0 to force an error when calling km.Primitive() + Params: &ecdhpb.EcdhAeadParams{ + KwParams: &ecdhpb.EcdhKwParams{ + CurveType: tt.curveType, // unknown curve to force an error when calling km.NewKey() + KeyType: tt.keyType, // invalid key type to force error when calling km.Primitive() + }, + EncParams: &ecdhpb.EcdhAeadEncParams{ + AeadEnc: tt.encTmp, // invalid data enc key template to force error when calling km.Primitive() + CEK: []byte{}, + }, + EcPointFormat: commonpb.EcPointFormat_UNCOMPRESSED, + }, + X: x25519Pub, + } + + sPubKey, err := proto.Marshal(pubKeyProto) + require.NoError(t, err) + + p, err := km.Primitive(sPubKey) + if strings.Contains(tt.tcName, "with bad content encryption key size") { + require.EqualError(t, err, errInvalidECDHX25519XChachaPublicKey.Error(), + "ecdhX25519XChachaPublicKeyManager primitive from serialized key with invalid serialized key") + require.Empty(t, p) + + return + } + + if strings.Contains(tt.tcName, "success") { + require.NoError(t, err) + require.NotEmpty(t, p) + return + } + + require.Errorf(t, err, tt.tcName) + require.Empty(t, p) + }) + } +} + +func TestEcdhX25519XChachaPublicKeyManager_DoesSupport(t *testing.T) { + km := newECDHX25519XChachaPublicKeyManager() + require.False(t, km.DoesSupport("bad/url")) + require.True(t, km.DoesSupport(ecdhX25519XChachaPublicKeyTypeURL)) +} + +func TestEcdhX25519XChachaPublicKeyManager_NewKeyAndNewKeyData(t *testing.T) { + km := newECDHX25519XChachaPublicKeyManager() + + t.Run("Test public key manager NewKey()", func(t *testing.T) { + k, err := km.NewKey(nil) + require.EqualError(t, err, "ecdh_x25519kw_xchachaaead_public_key_manager: NewKey not implemented") + require.Empty(t, k) + }) + + t.Run("Test private key manager NewKeyData()", func(t *testing.T) { + p, err := km.NewKeyData(nil) + require.EqualError(t, err, "ecdh_x25519kw_xchachaaead_public_key_manager: NewKeyData not implemented") + require.Empty(t, p) + }) +} diff --git a/pkg/crypto/tinkcrypto/primitive/composite/keyio/composite_key_export.go b/pkg/crypto/tinkcrypto/primitive/composite/keyio/composite_key_export.go index 5cf63c3832..56f396f3c7 100644 --- a/pkg/crypto/tinkcrypto/primitive/composite/keyio/composite_key_export.go +++ b/pkg/crypto/tinkcrypto/primitive/composite/keyio/composite_key_export.go @@ -30,12 +30,14 @@ import ( // key (aka PublicKeyToHandle to be used as a valid Tink key) const ( - ecdhAESPublicKeyTypeURL = "type.hyperledger.org/hyperledger.aries.crypto.tink.EcdhAesAeadPublicKey" + ecdhAESPublicKeyTypeURL = "type.hyperledger.org/hyperledger.aries.crypto.tink.EcdhNistPKwAesAeadPublicKey" + ecdhXChachaPublicKeyTypeURL = "type.hyperledger.org/hyperledger.aries.crypto.tink.EcdhX25519KwAesAeadPublicKey" // nolint:lll ) // PubKeyWriter will write the raw bytes of a Tink KeySet's primary public key. The raw bytes are a marshaled // composite.VerificationMethod type. -// The keyset must have a keyURL value equal to `ecdhAESPublicKeyTypeURL` constant of ecdh package. +// The keyset must have a keyURL value equal to `ecdhAESPublicKeyTypeURL` or `ecdhXChachaPublicKeyTypeURL` constant of +// ecdh package. // Note: This writer should be used only for ECDH public key exports. Other export of public keys should be // called via localkms package. type PubKeyWriter struct { @@ -115,6 +117,11 @@ func protoToCompositeKey(keyData *tinkpb.KeyData) (*cryptoapi.PublicKey, error) if err != nil { return nil, err } + case ecdhXChachaPublicKeyTypeURL: + cKey, err = newECDHXChachaKey(keyData.Value) + if err != nil { + return nil, err + } default: return nil, fmt.Errorf("can't export key with keyURL:%s", keyData.TypeUrl) } @@ -123,6 +130,7 @@ func protoToCompositeKey(keyData *tinkpb.KeyData) (*cryptoapi.PublicKey, error) } func buildKey(c compositeKeyGetter) (*cryptoapi.PublicKey, error) { + // TODO build XChacha key differently curveName := c.curveName() keyTypeName := c.keyType() @@ -246,7 +254,7 @@ func PublicKeyToKeysetHandle(pubKey *cryptoapi.PublicKey) (*keyset.Handle, error Params: &ecdhpb.EcdhAeadParams{ KwParams: &ecdhpb.EcdhKwParams{ CurveType: cp, - KeyType: ecdhpb.KeyType_EC, // for now, TODO create getTypeProto(pubKey.Type) function + KeyType: ecdhpb.KeyType_EC, }, EncParams: &ecdhpb.EcdhAeadEncParams{ AeadEnc: aead.AES256GCMKeyTemplate(), @@ -308,3 +316,8 @@ func newKeySet(tURL string, marshalledKey []byte, keyMaterialType tinkpb.KeyData PrimaryKeyId: 1, } } + +func newECDHXChachaKey(mKey []byte) (compositeKeyGetter, error) { + // TODO implement + return nil, nil +} diff --git a/pkg/crypto/tinkcrypto/primitive/composite/keyio/composite_key_export_import_test.go b/pkg/crypto/tinkcrypto/primitive/composite/keyio/composite_key_export_import_test.go index d54a61ce13..b286de0f59 100644 --- a/pkg/crypto/tinkcrypto/primitive/composite/keyio/composite_key_export_import_test.go +++ b/pkg/crypto/tinkcrypto/primitive/composite/keyio/composite_key_export_import_test.go @@ -30,7 +30,7 @@ func TestPubKeyExport(t *testing.T) { keyTemplate *tinkpb.KeyTemplate }{ { - tcName: "export then read AES256GCM with ECDH P-256 public recipient key", + tcName: "export then read AES256GCM with ECDHES P-256 public recipient key", keyTemplate: ecdh.ECDH256KWAES256GCMKeyTemplate(), }, { diff --git a/pkg/crypto/tinkcrypto/primitive/composite/register_ecdh_aead_enc_helper.go b/pkg/crypto/tinkcrypto/primitive/composite/register_ecdh_aead_enc_helper.go index 0d1b3b6a35..a08c63024b 100644 --- a/pkg/crypto/tinkcrypto/primitive/composite/register_ecdh_aead_enc_helper.go +++ b/pkg/crypto/tinkcrypto/primitive/composite/register_ecdh_aead_enc_helper.go @@ -66,25 +66,71 @@ func NewRegisterCompositeAEADEncHelper(k *tinkpb.KeyTemplate) (*RegisterComposit skf, err = proto.Marshal(gcmKeyFormat) if err != nil { - return nil, fmt.Errorf("compositeAEADEncHelper: failed to serialize key format, error: %w", err) + return nil, fmt.Errorf("compositeAEADEncHelper: failed to serialize gcm key format, error: %w", err) } case ChaCha20Poly1305TypeURL: tagSize = poly1305.TagSize ivSize = chacha20poly1305.NonceSize + + skf, err = buildChachaSKF(k) + if err != nil { + return nil, err + } case XChaCha20Poly1305TypeURL: tagSize = poly1305.TagSize ivSize = chacha20poly1305.NonceSizeX + + skf, err = buildXChachaSKF(k) + if err != nil { + return nil, err + } default: return nil, fmt.Errorf("compositeAEADEncHelper: unsupported AEAD content encryption key type: %s", k.TypeUrl) } + return buildRegisterCompositeAEADEncHelper(k, skf, tagSize, ivSize) +} + +func buildChachaSKF(k *tinkpb.KeyTemplate) ([]byte, error) { + chachaKeyFormat := new(chachapb.ChaCha20Poly1305KeyFormat) + + err := proto.Unmarshal(k.Value, chachaKeyFormat) + if err != nil { + return nil, fmt.Errorf("compositeAEADEncHelper: failed to unmarshal chachaKeyFormat: %w", err) + } + + skf, err := proto.Marshal(chachaKeyFormat) + if err != nil { + return nil, fmt.Errorf("compositeAEADEncHelper: failed to serialize chacha key format, error: %w", err) + } + + return skf, nil +} + +func buildXChachaSKF(k *tinkpb.KeyTemplate) ([]byte, error) { + xChachaKeyFormat := new(xchachapb.XChaCha20Poly1305KeyFormat) + + err := proto.Unmarshal(k.Value, xChachaKeyFormat) + if err != nil { + return nil, fmt.Errorf("compositeAEADEncHelper: failed to unmarshal xChachaKeyFormat: %w", err) + } + + skf, err := proto.Marshal(xChachaKeyFormat) + if err != nil { + return nil, fmt.Errorf("compositeAEADEncHelper: failed to serialize xChacha key format, error: %w", err) + } + + return skf, nil +} + +func buildRegisterCompositeAEADEncHelper(k *tinkpb.KeyTemplate, skf []byte, + tagSize, ivSize int) (*RegisterCompositeAEADEncHelper, error) { km, err := registry.GetKeyManager(k.TypeUrl) if err != nil { return nil, fmt.Errorf("compositeAEADEncHelper: failed to fetch KeyManager, error: %w", err) } - // skf is nil for (X)Chahcha20Poly1305 km key, err := km.NewKey(skf) if err != nil { return nil, fmt.Errorf("compositeAEADEncHelper: failed to fetch key, error: %w", err) diff --git a/pkg/crypto/tinkcrypto/unwrap_support.go b/pkg/crypto/tinkcrypto/unwrap_support.go index aa8357d0a9..d6046f86b5 100644 --- a/pkg/crypto/tinkcrypto/unwrap_support.go +++ b/pkg/crypto/tinkcrypto/unwrap_support.go @@ -42,7 +42,7 @@ func extractPrivKey(kh *keyset.Handle) (*hybrid.ECPrivateKey, error) { return nil, errors.New("extractPrivKey: invalid private key") } - ecdhAESPrivateKeyTypeURL := "type.hyperledger.org/hyperledger.aries.crypto.tink.EcdhAesAeadPrivateKey" + ecdhAESPrivateKeyTypeURL := "type.hyperledger.org/hyperledger.aries.crypto.tink.EcdhNistPKwAesAeadPrivateKey" primaryKey := ks.Key[0] if primaryKey.KeyData.TypeUrl != ecdhAESPrivateKeyTypeURL { diff --git a/pkg/kms/localkms/pubkey_writer.go b/pkg/kms/localkms/pubkey_writer.go index ff8eaa60ef..01349cdde7 100644 --- a/pkg/kms/localkms/pubkey_writer.go +++ b/pkg/kms/localkms/pubkey_writer.go @@ -27,7 +27,7 @@ import ( const ( ecdsaVerifierTypeURL = "type.googleapis.com/google.crypto.tink.EcdsaPublicKey" ed25519VerifierTypeURL = "type.googleapis.com/google.crypto.tink.Ed25519PublicKey" - ecdhAESPublicKeyTypeURL = "type.hyperledger.org/hyperledger.aries.crypto.tink.EcdhAesAeadPublicKey" + ecdhAESPublicKeyTypeURL = "type.hyperledger.org/hyperledger.aries.crypto.tink.EcdhNistPKwAesAeadPublicKey" ) // PubKeyWriter will write the raw bytes of a Tink KeySet's primary public key diff --git a/proto/tink/ecdh_aead.proto b/proto/tink/ecdh_aead.proto index 8cc99f9032..65881bef88 100644 --- a/proto/tink/ecdh_aead.proto +++ b/proto/tink/ecdh_aead.proto @@ -31,19 +31,19 @@ enum KeyType { // Parameters of KW (Key Wrapping) for storage only as KW is done outside the Tink key primitive. message EcdhKwParams { // Required. - EllipticCurveType curve_type = 1; + EllipticCurveType curve_type = 1; // required for NIST P Curved keys, not needed or X25519 keys. // Required. - KeyType key_type = 2; + KeyType key_type = 2; // EC for NIST P Curved keys, OKP for X25519 keys } // Parameters of AEAD Content encryption. message EcdhAeadEncParams { // Required. - KeyTemplate aead_enc = 1; // Contains e.g. AesGcmKeyFormat. + KeyTemplate aead_enc = 1; // Contains e.g. AesGcmKeyFormat or XChaCha20Poly1305KeyFormat. // Required for primitive execution, not storage. - bytes CEK = 2; // Contains cek used for AES GCM encryption + bytes CEK = 2; // Contains cek used for AES GCM/XChacha20Poly1305 encryption. } message EcdhAeadParams { @@ -61,7 +61,7 @@ message EcdhAeadParams { } // EcdhAeadPublicKey represents ECDHEncrypt primitive. -// key_type: type.hyperledger.org/hyperledger.aries.crypto.tink.EcdhAeadPublicKey +// key_type: type.hyperledger.org/hyperledger.aries.crypto.tink.Ecdh[...]AeadPublicKey message EcdhAeadPublicKey { // Required. uint32 version = 1; @@ -73,7 +73,8 @@ message EcdhAeadPublicKey { string KID = 3; // Affine coordinates of the public key in bigendian representation. - // The public key is a point (x, y) on the curve defined by params.kw_params.curve. + // The public key is a point (x, y) on the curve defined by params.kw_params.curve for NIST P Curved keys. + // For X25519 keys, x is the raw key value (y is not used). // Required. bytes x = 4; @@ -82,7 +83,7 @@ message EcdhAeadPublicKey { } // EcdhAeadPrivateKey represents ECDHDecrypt primitive. -// key_type: type.hyperledger.org/hyperledger.aries.crypto.tink.EcdhAeadPrivateKey +// key_type: type.hyperledger.org/hyperledger.aries.crypto.tink.Ecdh[...]AeadPrivateKey message EcdhAeadPrivateKey { // Required. uint32 version = 1;