From dace834e5dfa89b0457a1e5a6f18113ed609754e Mon Sep 17 00:00:00 2001 From: Baha Shaaban Date: Mon, 25 Jan 2021 16:23:31 -0500 Subject: [PATCH] feat: add KMS key type for X25519 key This is the last change about X25519 keys for ECDH KW. It updates the old ECDH key types into the new type names and add the X25519 key type as well. Also part of this change is the removal of remnant code from legacyKMS which was removed from the framework last year. closes #2447, #1684, #815 also part of #857 closes #475, #596 Signed-off-by: Baha Shaaban --- pkg/didcomm/packager/package_test.go | 12 +- pkg/didcomm/packer/anoncrypt/pack_test.go | 10 +- pkg/didcomm/packer/authcrypt/pack_test.go | 12 +- pkg/doc/jose/encrypter_decrypter_test.go | 2 +- pkg/doc/util/jwkkid/kid_creator.go | 72 ++++++++++-- pkg/doc/util/jwkkid/kid_creator_test.go | 63 ++++++++++- pkg/internal/cryptoutil/utils.go | 131 +--------------------- pkg/internal/cryptoutil/utils_test.go | 61 +--------- pkg/kms/api.go | 28 +++-- pkg/kms/localkms/kid_creator_test.go | 23 +++- pkg/kms/localkms/localkms.go | 8 +- pkg/kms/localkms/localkms_test.go | 9 +- pkg/kms/localkms/pubkey_writer.go | 12 +- 13 files changed, 199 insertions(+), 244 deletions(-) diff --git a/pkg/didcomm/packager/package_test.go b/pkg/didcomm/packager/package_test.go index 77411958d9..e7e9a781cd 100644 --- a/pkg/didcomm/packager/package_test.go +++ b/pkg/didcomm/packager/package_test.go @@ -138,7 +138,7 @@ func TestBaseKMSInPackager_UnpackMessage(t *testing.T) { require.NoError(t, err) // fromKey is stored in the KMS - fromKID, fromKey, err := customKMS.CreateAndExportPubKeyBytes(kms.ECDH256KWAES256GCMType) + fromKID, fromKey, err := customKMS.CreateAndExportPubKeyBytes(kms.NISTP256ECDHKWType) require.NoError(t, err) // for authcrypt, sender key should be in third party store, must use base58 wrapped store to match kms store. @@ -149,7 +149,7 @@ func TestBaseKMSInPackager_UnpackMessage(t *testing.T) { require.NoError(t, err) // toVerKey is stored in the KMS as well - toKID, toKey, err := customKMS.CreateAndExportPubKeyBytes(kms.ECDH256KWAES256GCM) + toKID, toKey, err := customKMS.CreateAndExportPubKeyBytes(kms.NISTP256ECDHKW) require.NoError(t, err) // PackMessage should pass with both value from and to keys @@ -207,10 +207,10 @@ func TestBaseKMSInPackager_UnpackMessage(t *testing.T) { require.NoError(t, err) // use ECDH1PU type as we are using a sender key (ie: packer's FromKey is not empty aka authcrypt) - fromKID, _, err := customKMS.Create(kms.ECDH384KWAES256GCMType) + fromKID, _, err := customKMS.Create(kms.NISTP384ECDHKWType) require.NoError(t, err) - _, toKey, err := customKMS.CreateAndExportPubKeyBytes(kms.ECDH384KWAES256GCMType) + _, toKey, err := customKMS.CreateAndExportPubKeyBytes(kms.NISTP384ECDHKWType) require.NoError(t, err) // try pack with nil envelope - should fail @@ -282,10 +282,10 @@ func TestBaseKMSInPackager_UnpackMessage(t *testing.T) { packager, err := New(mockedProviders) require.NoError(t, err) - fromKID, fromKey, err := customKMS.CreateAndExportPubKeyBytes(kms.ECDH256KWAES256GCMType) + fromKID, fromKey, err := customKMS.CreateAndExportPubKeyBytes(kms.NISTP256ECDHKWType) require.NoError(t, err) - _, toKey, err := customKMS.CreateAndExportPubKeyBytes(kms.ECDH256KWAES256GCMType) + _, toKey, err := customKMS.CreateAndExportPubKeyBytes(kms.NISTP256ECDHKWType) require.NoError(t, err) // pack an non empty envelope - should pass diff --git a/pkg/didcomm/packer/anoncrypt/pack_test.go b/pkg/didcomm/packer/anoncrypt/pack_test.go index 614a089130..5c934e72f5 100644 --- a/pkg/didcomm/packer/anoncrypt/pack_test.go +++ b/pkg/didcomm/packer/anoncrypt/pack_test.go @@ -63,8 +63,8 @@ func TestAnoncryptPackerSuccess(t *testing.T) { func TestAnoncryptPackerSuccessWithDifferentCurvesSuccess(t *testing.T) { k := createKMS(t) _, recipientsKey1, keyHandles1 := createRecipients(t, k, 1) - _, recipientsKey2, _ := createRecipientsByKeyType(t, k, 1, kms.ECDH384KWAES256GCM) - _, recipientsKey3, _ := createRecipientsByKeyType(t, k, 1, kms.ECDH521KWAES256GCM) + _, recipientsKey2, _ := createRecipientsByKeyType(t, k, 1, kms.NISTP384ECDHKW) + _, recipientsKey3, _ := createRecipientsByKeyType(t, k, 1, kms.NISTP521ECDHKW) recipientsKeys := make([][]byte, 3) recipientsKeys[0] = make([]byte, len(recipientsKey1[0])) @@ -183,10 +183,10 @@ func TestAnoncryptPackerFail(t *testing.T) { require.NoError(t, err) // rotate keys to update keyID and force a failure - _, _, err = k.Rotate(kms.ECDH256KWAES256GCMType, kids[0]) + _, _, err = k.Rotate(kms.NISTP256ECDHKWType, kids[0]) require.NoError(t, err) - _, _, err = k.Rotate(kms.ECDH256KWAES256GCMType, kids[1]) + _, _, err = k.Rotate(kms.NISTP256ECDHKWType, kids[1]) require.NoError(t, err) _, err = validAnonPacker.Unpack(ct) @@ -196,7 +196,7 @@ func TestAnoncryptPackerFail(t *testing.T) { // createRecipients and return their public key and keyset.Handle. func createRecipients(t *testing.T, k *localkms.LocalKMS, recipientsCount int) ([]string, [][]byte, []*keyset.Handle) { - return createRecipientsByKeyType(t, k, recipientsCount, kms.ECDH256KWAES256GCM) + return createRecipientsByKeyType(t, k, recipientsCount, kms.NISTP256ECDHKW) } func createRecipientsByKeyType(t *testing.T, k *localkms.LocalKMS, recipientsCount int, diff --git a/pkg/didcomm/packer/authcrypt/pack_test.go b/pkg/didcomm/packer/authcrypt/pack_test.go index fd621375ed..91c567ddb2 100644 --- a/pkg/didcomm/packer/authcrypt/pack_test.go +++ b/pkg/didcomm/packer/authcrypt/pack_test.go @@ -79,8 +79,8 @@ func TestAuthryptPackerUsingKeysWithDifferentCurvesSuccess(t *testing.T) { _, recipientsKey1, keyHandles1 := createRecipients(t, k, 1) // since authcrypt does ECDH kw using the sender key, the recipient keys must be on the same curve as the sender's. // this is why recipient keys with different curves are not supported for authcrypt. - _, recipientsKey2, _ := createRecipients(t, k, 1) // can't create key with kms.ECDH384KWAES256GCM - _, recipientsKey3, _ := createRecipients(t, k, 1) // can't create key with kms.ECDH521KWAES256GCM + _, recipientsKey2, _ := createRecipients(t, k, 1) // can't create key with kms.NISTP384ECDHKW + _, recipientsKey3, _ := createRecipients(t, k, 1) // can't create key with kms.NISTP521ECDHKW recipientsKeys := make([][]byte, 3) recipientsKeys[0] = make([]byte, len(recipientsKey1[0])) @@ -251,10 +251,10 @@ func TestAuthcryptPackerFail(t *testing.T) { require.NoError(t, err) // rotate keys to update keyID and force a failure - _, _, err = k.Rotate(kms.ECDH256KWAES256GCMType, kids[0]) + _, _, err = k.Rotate(kms.NISTP256ECDHKWType, kids[0]) require.NoError(t, err) - _, _, err = k.Rotate(kms.ECDH256KWAES256GCMType, kids[1]) + _, _, err = k.Rotate(kms.NISTP256ECDHKWType, kids[1]) require.NoError(t, err) _, err = validAuthPacker.Unpack(ct) @@ -264,7 +264,7 @@ func TestAuthcryptPackerFail(t *testing.T) { // createRecipients and return their public key and keyset.Handle. func createRecipients(t *testing.T, k *localkms.LocalKMS, recipientsCount int) ([]string, [][]byte, []*keyset.Handle) { - return createRecipientsByKeyType(t, k, recipientsCount, kms.ECDH256KWAES256GCM) + return createRecipientsByKeyType(t, k, recipientsCount, kms.NISTP256ECDHKW) } func createRecipientsByKeyType(t *testing.T, k *localkms.LocalKMS, recipientsCount int, @@ -291,7 +291,7 @@ func createRecipientsByKeyType(t *testing.T, k *localkms.LocalKMS, recipientsCou // createAndMarshalKey creates a new recipient keyset.Handle, extracts public key, marshals it and returns // both marshalled public key and original recipient keyset.Handle. func createAndMarshalKey(t *testing.T, k *localkms.LocalKMS) (string, []byte, *keyset.Handle) { - return createAndMarshalKeyByKeyType(t, k, kms.ECDH256KWAES256GCMType) + return createAndMarshalKeyByKeyType(t, k, kms.NISTP256ECDHKWType) } func createAndMarshalKeyByKeyType(t *testing.T, k *localkms.LocalKMS, kt kms.KeyType) (string, []byte, *keyset.Handle) { diff --git a/pkg/doc/jose/encrypter_decrypter_test.go b/pkg/doc/jose/encrypter_decrypter_test.go index 1673deaeb9..6e01add32e 100644 --- a/pkg/doc/jose/encrypter_decrypter_test.go +++ b/pkg/doc/jose/encrypter_decrypter_test.go @@ -419,7 +419,7 @@ func createAndMarshalEntityKey(t *testing.T) ([]byte, *keyset.Handle, string) { err = pubKH.WriteWithNoSecrets(pubKeyWriter) require.NoError(t, err) - kid, err := jwkkid.CreateKID(buf.Bytes(), kms.ECDH256KWAES256GCMType) + kid, err := jwkkid.CreateKID(buf.Bytes(), kms.NISTP256ECDHKWType) require.NoError(t, err) return buf.Bytes(), kh, kid diff --git a/pkg/doc/util/jwkkid/kid_creator.go b/pkg/doc/util/jwkkid/kid_creator.go index 1781d42a5d..fd90faa707 100644 --- a/pkg/doc/util/jwkkid/kid_creator.go +++ b/pkg/doc/util/jwkkid/kid_creator.go @@ -22,6 +22,7 @@ import ( cryptoapi "github.com/hyperledger/aries-framework-go/pkg/crypto" "github.com/hyperledger/aries-framework-go/pkg/doc/jose" + "github.com/hyperledger/aries-framework-go/pkg/internal/cryptoutil" "github.com/hyperledger/aries-framework-go/pkg/kms" ) @@ -33,6 +34,16 @@ var errInvalidKeyType = errors.New("key type is not supported") // - base64 raw (no padding) URL encoded KID // - error in case of error func CreateKID(keyBytes []byte, kt kms.KeyType) (string, error) { + // X25519 JWK is not supported by go jose, manually build it and build its resulting KID. + if kt == kms.X25519ECDHKWType { + x25519KID, err := createX25519KID(keyBytes) + if err != nil { + return "", fmt.Errorf("createKID: %w", err) + } + + return x25519KID, nil + } + jwk, err := buildJWK(keyBytes, kt) if err != nil { return "", fmt.Errorf("createKID: failed to build jwk: %w", err) @@ -64,7 +75,7 @@ func buildJWK(keyBytes []byte, kt kms.KeyType) (*jose.JWK, error) { return nil, fmt.Errorf("buildJWK: failed to build JWK from ed25519 key: %w", err) } case kms.ECDSAP256TypeIEEEP1363, kms.ECDSAP384TypeIEEEP1363, kms.ECDSAP521TypeIEEEP1363: - c := getCurve(kt) + c := getCurveByKMSKeyType(kt) x, y := elliptic.Unmarshal(c, keyBytes) pubKey := &ecdsa.PublicKey{ @@ -77,7 +88,7 @@ func buildJWK(keyBytes []byte, kt kms.KeyType) (*jose.JWK, error) { if err != nil { return nil, fmt.Errorf("buildJWK: failed to build JWK from ecdsa key in IEEE1363 format: %w", err) } - case kms.ECDH256KWAES256GCMType, kms.ECDH384KWAES256GCMType, kms.ECDH521KWAES256GCMType: + case kms.NISTP256ECDHKWType, kms.NISTP384ECDHKWType, kms.NISTP521ECDHKWType: jwk, err = generateJWKFromECDH(keyBytes) if err != nil { return nil, fmt.Errorf("buildJWK: failed to build JWK from ecdh key: %w", err) @@ -99,11 +110,9 @@ func generateJWKFromDERECDSA(keyBytes []byte) (*jose.JWK, error) { } func generateJWKFromECDH(keyBytes []byte) (*jose.JWK, error) { - compositeKey := &cryptoapi.PublicKey{} - - err := json.Unmarshal(keyBytes, compositeKey) + compositeKey, err := unmarshalECDHKey(keyBytes) if err != nil { - return nil, fmt.Errorf("generateJWKFromECDH: failed to unmarshal ECDH key: %w", err) + return nil, fmt.Errorf("generateJWKFromECDH: %w", err) } c, err := hybrid.GetCurve(compositeKey.Curve) @@ -120,7 +129,7 @@ func generateJWKFromECDH(keyBytes []byte) (*jose.JWK, error) { return jose.JWKFromPublicKey(pubKey) } -func getCurve(kt kms.KeyType) elliptic.Curve { +func getCurveByKMSKeyType(kt kms.KeyType) elliptic.Curve { switch kt { case kms.ECDSAP256TypeIEEEP1363: return elliptic.P256() @@ -133,3 +142,52 @@ func getCurve(kt kms.KeyType) elliptic.Curve { // should never be called but added for linting return elliptic.P256() } + +func unmarshalECDHKey(keyBytes []byte) (*cryptoapi.PublicKey, error) { + compositeKey := &cryptoapi.PublicKey{} + + err := json.Unmarshal(keyBytes, compositeKey) + if err != nil { + return nil, fmt.Errorf("unmarshalECDHKey: failed to unmarshal ECDH key: %w", err) + } + + return compositeKey, nil +} + +func createX25519KID(marshalledKey []byte) (string, error) { + compositeKey, err := unmarshalECDHKey(marshalledKey) + if err != nil { + return "", fmt.Errorf("createX25519KID: %w", err) + } + + jwk, err := buildX25519JWK(compositeKey.X) + if err != nil { + return "", fmt.Errorf("createX25519KID: %w", err) + } + + thumbprint := thumbprintX25519(jwk) + + return base64.RawURLEncoding.EncodeToString(thumbprint), nil +} + +func buildX25519JWK(keyBytes []byte) (string, error) { + const x25519ThumbprintTemplate = `{"crv":"X25519","kty":"OKP",x":"%s"}` + + if len(keyBytes) > cryptoutil.Curve25519KeySize { + return "", errors.New("buildX25519JWK: invalid ECDH X25519 key") + } + + pad := make([]byte, cryptoutil.Curve25519KeySize-len(keyBytes)) + x25519RawKey := append(pad, keyBytes...) + + jwk := fmt.Sprintf(x25519ThumbprintTemplate, base64.RawURLEncoding.EncodeToString(x25519RawKey)) + + return jwk, nil +} + +func thumbprintX25519(jwk string) []byte { + h := crypto.SHA256.New() + _, _ = h.Write([]byte(jwk)) // nolint: errcheck // SHA256 digest returns empty error on Write() + + return h.Sum(nil) +} diff --git a/pkg/doc/util/jwkkid/kid_creator_test.go b/pkg/doc/util/jwkkid/kid_creator_test.go index 0688f82f7e..24653ee42a 100644 --- a/pkg/doc/util/jwkkid/kid_creator_test.go +++ b/pkg/doc/util/jwkkid/kid_creator_test.go @@ -11,10 +11,16 @@ import ( "crypto/ed25519" "crypto/elliptic" "crypto/rand" + "encoding/json" + "strings" "testing" + commonpb "github.com/google/tink/go/proto/common_go_proto" "github.com/stretchr/testify/require" + cryptoapi "github.com/hyperledger/aries-framework-go/pkg/crypto" + 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" "github.com/hyperledger/aries-framework-go/pkg/kms" ) @@ -30,19 +36,39 @@ func TestCreateKID(t *testing.T) { require.EqualError(t, err, "createKID: failed to build jwk: buildJWK: failed to build JWK from ed25519 "+ "key: create JWK: unable to read jose JWK, square/go-jose: unknown curve Ed25519'") + _, x, y, err := elliptic.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + + ecdhKey := &cryptoapi.PublicKey{ + Curve: elliptic.P256().Params().Name, + X: x.Bytes(), + Y: y.Bytes(), + } + + ecdhKeyMarshalled, err := json.Marshal(ecdhKey) + require.NoError(t, err) + + kid, err = CreateKID(ecdhKeyMarshalled, kms.NISTP256ECDHKWType) + require.NoError(t, err) + require.NotEmpty(t, kid) + _, err = CreateKID(pubKey, "badType") require.EqualError(t, err, "createKID: failed to build jwk: buildJWK: key type is not supported: 'badType'") badPubKey := ed25519.PublicKey{} - _, err = CreateKID(badPubKey, kms.ECDH256KWAES256GCMType) + _, err = CreateKID(badPubKey, kms.NISTP256ECDHKWType) require.EqualError(t, err, "createKID: failed to build jwk: buildJWK: failed to build JWK from ecdh "+ - "key: generateJWKFromECDH: failed to unmarshal ECDH key: unexpected end of JSON input") + "key: generateJWKFromECDH: unmarshalECDHKey: failed to unmarshal ECDH key: unexpected end of JSON input") _, err = CreateKID(badPubKey, kms.ECDSAP256TypeDER) require.EqualError(t, err, "createKID: failed to build jwk: buildJWK: failed to build JWK from ecdsa "+ "DER key: generateJWKFromDERECDSA: failed to parse ecdsa key in DER format: asn1: syntax error: sequence "+ "truncated") + _, err = CreateKID(badPubKey, kms.X25519ECDHKWType) + require.EqualError(t, err, "createKID: createX25519KID: unmarshalECDHKey: failed to unmarshal ECDH key: "+ + "unexpected end of JSON input") + ecKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) require.NoError(t, err) @@ -52,9 +78,38 @@ func TestCreateKID(t *testing.T) { } func TestGetCurve(t *testing.T) { - c := getCurve(kms.ECDSAP384TypeIEEEP1363) + c := getCurveByKMSKeyType(kms.ECDSAP384TypeIEEEP1363) require.Equal(t, c, elliptic.P384()) - c = getCurve(kms.AES128GCMType) // default P-256 if curve not found + c = getCurveByKMSKeyType(kms.AES128GCMType) // default P-256 if curve not found require.Equal(t, c, elliptic.P256()) } + +func TestGenerateJWKFromECDH(t *testing.T) { + keyBytesWithBadCurve := &cryptoapi.PublicKey{ + Curve: commonpb.EllipticCurveType_UNKNOWN_CURVE.String(), + X: []byte{}, + Y: []byte{}, + } + + badKeyMarshalled, err := json.Marshal(keyBytesWithBadCurve) + require.NoError(t, err) + + _, err = generateJWKFromECDH(badKeyMarshalled) + require.EqualError(t, err, "generateJWKFromECDH: failed to get Curve for ECDH key: unsupported curve") +} + +func TestCreateX25519KID_Failure(t *testing.T) { + key := &cryptoapi.PublicKey{ + Curve: "X25519", + X: []byte(strings.Repeat("a", cryptoutil.Curve25519KeySize+1)), // public key > X25519 key size + Y: []byte{}, + Type: ecdhpb.KeyType_OKP.String(), + } + + mKey, err := json.Marshal(key) + require.NoError(t, err) + + _, err = createX25519KID(mKey) + require.EqualError(t, err, "createX25519KID: buildX25519JWK: invalid ECDH X25519 key") +} diff --git a/pkg/internal/cryptoutil/utils.go b/pkg/internal/cryptoutil/utils.go index e913193745..c2c939e307 100644 --- a/pkg/internal/cryptoutil/utils.go +++ b/pkg/internal/cryptoutil/utils.go @@ -19,134 +19,11 @@ import ( "golang.org/x/crypto/curve25519" ) -// errEmptyRecipients is used when recipients list is empty. -var errEmptyRecipients = errors.New("empty recipients") - -// errInvalidKeypair is used when a keypair is invalid. -var errInvalidKeypair = errors.New("invalid keypair") - -// SignatureAlgorithm represents a signature algorithm. -type SignatureAlgorithm string - -// EncryptionAlgorithm represents a content encryption algorithm. -type EncryptionAlgorithm string - -const ( - // encryption key types. - - // Curve25519 encryption key type. - Curve25519 = EncryptionAlgorithm("Curve25519") - - // signing key types. - - // EdDSA signature key type. - EdDSA = SignatureAlgorithm("EdDSA") -) - -// VerifyKeys is a utility function that verifies if sender key pair and recipients keys are valid (not empty). -func VerifyKeys(sender KeyPair, recipients [][]byte) error { - if len(recipients) == 0 { - return errEmptyRecipients - } - - if !isKeyPairValid(sender) { - return errInvalidKeypair - } - - if !IsChachaKeyValid(sender.Priv) || !IsChachaKeyValid(sender.Pub) { - return ErrInvalidKey - } - - return nil -} - -// IsChachaKeyValid will return true if key size is the same as chacha20poly1305.keySize -// false otherwise. -func IsChachaKeyValid(key []byte) bool { - return len(key) == chacha.KeySize -} - -// KeyPair represents a private/public key pair. -type KeyPair struct { - // Priv is a private key - Priv []byte `json:"priv,omitempty"` - // Pub is a public key - Pub []byte `json:"pub,omitempty"` -} - -// EncKeyPair represents a private/public encryption key pair. -type EncKeyPair struct { - KeyPair `json:"keypair,omitempty"` - // Alg is the encryption algorithm of keys enclosed in this key pair - Alg EncryptionAlgorithm `json:"alg,omitempty"` -} - -// SigKeyPair represents a private/public signature (verification) key pair. -type SigKeyPair struct { - KeyPair `json:"keypair,omitempty"` - // Alg is the signature algorithm of keys enclosed in this key pair - Alg SignatureAlgorithm `json:"alg,omitempty"` -} - -// MessagingKeys represents a pair of key pairs, one for encryption and one for signature -// usually stored in a KMS, it helps prevent converting signing keys into encryption ones -// TODO refactor this structure and all KeyPair handling as per issue #596. -type MessagingKeys struct { - *EncKeyPair `json:"enckeypair,omitempty"` - *SigKeyPair `json:"sigkeypair,omitempty"` -} - -// isKeyPairValid is a utility function that validates a KeyPair. -func isKeyPairValid(kp KeyPair) bool { - if kp.Priv == nil || kp.Pub == nil { - return false - } - - return true -} - -// IsEncKeyPairValid is a utility function that validates an EncKeyPair. -func IsEncKeyPairValid(kp *EncKeyPair) bool { - if !isKeyPairValid(kp.KeyPair) { - return false - } - - switch kp.Alg { - case Curve25519: - return true - default: - return false - } -} - -// IsSigKeyPairValid is a utility function that validates an EncKeyPair. -func IsSigKeyPairValid(kp *SigKeyPair) bool { - if !isKeyPairValid(kp.KeyPair) { - return false - } - - switch kp.Alg { - case EdDSA: - return true - default: - return false - } -} - -// IsMessagingKeysValid is a utility function that validates a KeyPair. -func IsMessagingKeysValid(kpb *MessagingKeys) bool { - if !IsSigKeyPairValid(kpb.SigKeyPair) || !IsEncKeyPairValid(kpb.EncKeyPair) { - return false - } - - return true -} - // Derive25519KEK is a utility function that will derive an ephemeral // symmetric key (kek) using fromPrivKey and toPubKey. func Derive25519KEK(alg, apu, apv []byte, fromPrivKey, toPubKey *[chacha.KeySize]byte) ([]byte, error) { if fromPrivKey == nil || toPubKey == nil { - return nil, ErrInvalidKey + return nil, errors.New("invalid key") } const ( @@ -251,9 +128,3 @@ func SecretEd25519toCurve25519(priv []byte) ([]byte, error) { return sKOut[:], nil } - -// ErrKeyNotFound is returned when key not found. -var ErrKeyNotFound = errors.New("key not found") - -// ErrInvalidKey is used when a key is invalid. -var ErrInvalidKey = errors.New("invalid key") diff --git a/pkg/internal/cryptoutil/utils_test.go b/pkg/internal/cryptoutil/utils_test.go index 634d992841..73136e5fd4 100644 --- a/pkg/internal/cryptoutil/utils_test.go +++ b/pkg/internal/cryptoutil/utils_test.go @@ -15,66 +15,9 @@ import ( chacha "golang.org/x/crypto/chacha20poly1305" ) -func TestIsKeyPairValid(t *testing.T) { - require.False(t, isKeyPairValid(KeyPair{})) - - pubKey := []byte("testpublickey") - privKey := []byte("testprivatekey") - - validChachaKey, err := base64.RawURLEncoding.DecodeString("c8CSJr_27PN9xWCpzXNmepRndD6neQcnO9DS0YWjhNs") - require.NoError(t, err) - - require.False(t, isKeyPairValid(KeyPair{Priv: privKey, Pub: nil})) - require.False(t, isKeyPairValid(KeyPair{Priv: nil, Pub: pubKey})) - require.True(t, isKeyPairValid(KeyPair{Priv: privKey, Pub: pubKey})) - - require.EqualError(t, - VerifyKeys( - KeyPair{Priv: privKey, Pub: pubKey}, - [][]byte{[]byte("abc"), []byte("def")}), - ErrInvalidKey.Error()) - require.EqualError(t, - VerifyKeys( - KeyPair{Priv: privKey, Pub: pubKey}, - [][]byte{}), - errEmptyRecipients.Error()) - require.EqualError(t, VerifyKeys(KeyPair{}, [][]byte{[]byte("abc"), []byte("def")}), errInvalidKeypair.Error()) - require.NoError(t, VerifyKeys(KeyPair{Priv: validChachaKey, Pub: validChachaKey}, [][]byte{validChachaKey})) - - require.False(t, IsMessagingKeysValid(&MessagingKeys{ - &EncKeyPair{KeyPair{Priv: privKey, Pub: nil}, Curve25519}, - &SigKeyPair{KeyPair{Priv: nil, Pub: pubKey}, EdDSA}, - })) - - require.False(t, IsMessagingKeysValid(&MessagingKeys{ - &EncKeyPair{KeyPair{Priv: nil, Pub: pubKey}, Curve25519}, - &SigKeyPair{KeyPair{Priv: privKey, Pub: pubKey}, EdDSA}, - })) - - require.False(t, IsMessagingKeysValid(&MessagingKeys{ - &EncKeyPair{KeyPair{Priv: nil, Pub: pubKey}, Curve25519}, - &SigKeyPair{KeyPair{Priv: privKey, Pub: nil}, EdDSA}, - })) - - require.False(t, IsMessagingKeysValid(&MessagingKeys{ - &EncKeyPair{KeyPair{Priv: privKey, Pub: pubKey}, ""}, - &SigKeyPair{KeyPair{Priv: privKey, Pub: pubKey}, EdDSA}, - })) - - require.False(t, IsMessagingKeysValid(&MessagingKeys{ - &EncKeyPair{KeyPair{Priv: privKey, Pub: pubKey}, Curve25519}, - &SigKeyPair{KeyPair{Priv: privKey, Pub: pubKey}, ""}, - })) - - require.True(t, IsMessagingKeysValid(&MessagingKeys{ - &EncKeyPair{KeyPair{Priv: privKey, Pub: pubKey}, Curve25519}, - &SigKeyPair{KeyPair{Priv: privKey, Pub: pubKey}, EdDSA}, - })) -} - func TestDeriveKEK_Util(t *testing.T) { kek, err := Derive25519KEK(nil, nil, nil, nil, nil) - require.EqualError(t, err, ErrInvalidKey.Error()) + require.EqualError(t, err, "invalid key") require.Empty(t, kek) validChachaKey, err := base64.RawURLEncoding.DecodeString("c8CSJr_27PN9xWCpzXNmepRndD6neQcnO9DS0YWjhNs") @@ -83,7 +26,7 @@ func TestDeriveKEK_Util(t *testing.T) { chachaKey := new([chacha.KeySize]byte) copy(chachaKey[:], validChachaKey) kek, err = Derive25519KEK(nil, nil, nil, chachaKey, nil) - require.EqualError(t, err, ErrInvalidKey.Error()) + require.EqualError(t, err, "invalid key") require.Empty(t, kek) validChachaKey2, err := base64.RawURLEncoding.DecodeString("AAjrHjiFLw6kf6CZ5zqH1ooG3y2aQhuqxmUvqJnIvDI") diff --git a/pkg/kms/api.go b/pkg/kms/api.go index 2fdfa346a8..30a9640bed 100644 --- a/pkg/kms/api.go +++ b/pkg/kms/api.go @@ -110,12 +110,14 @@ const ( RSAPS256 = "RSAPS256" // HMACSHA256Tag256 key type value. HMACSHA256Tag256 = "HMACSHA256Tag256" - // ECDH256KWAES256GCM key type value. - ECDH256KWAES256GCM = "ECDH256KWAES256GCM" - // ECDH384KWAES256GCM key type value. - ECDH384KWAES256GCM = "ECDH384KWAES256GCM" - // ECDH521KWAES256GCM key type value. - ECDH521KWAES256GCM = "ECDH521KWAES256GCM" + // NISTP256ECDHKW key type value. + NISTP256ECDHKW = "NISTP256ECDHKW" + // NISTP384ECDHKW key type value. + NISTP384ECDHKW = "NISTP384ECDHKW" + // NISTP521ECDHKW key type value. + NISTP521ECDHKW = "NISTP521ECDHKW" + // X25519ECDHKW key type value. + X25519ECDHKW = "X25519ECDHKW" ) // KeyType represents a key type supported by the KMS. @@ -154,12 +156,14 @@ const ( RSAPS256Type = KeyType(RSAPS256) // HMACSHA256Tag256Type key type value. HMACSHA256Tag256Type = KeyType(HMACSHA256Tag256) - // ECDH256KWAES256GCMType key type value. - ECDH256KWAES256GCMType = KeyType(ECDH256KWAES256GCM) - // ECDH384KWAES256GCMType key type value. - ECDH384KWAES256GCMType = KeyType(ECDH384KWAES256GCM) - // ECDH521KWAES256GCMType key type value. - ECDH521KWAES256GCMType = KeyType(ECDH521KWAES256GCM) + // NISTP256ECDHKWType key type value. + NISTP256ECDHKWType = KeyType(NISTP256ECDHKW) + // NISTP384ECDHKWType key type value. + NISTP384ECDHKWType = KeyType(NISTP384ECDHKW) + // NISTP521ECDHKWType key type value. + NISTP521ECDHKWType = KeyType(NISTP521ECDHKW) + // X25519ECDHKWType key type value. + X25519ECDHKWType = KeyType(X25519ECDHKW) ) // CryptoBox is a libsodium crypto service used by legacy authcrypt packer. diff --git a/pkg/kms/localkms/kid_creator_test.go b/pkg/kms/localkms/kid_creator_test.go index 82e20af3a9..915777f82e 100644 --- a/pkg/kms/localkms/kid_creator_test.go +++ b/pkg/kms/localkms/kid_creator_test.go @@ -9,10 +9,13 @@ package localkms import ( "crypto/ed25519" "crypto/rand" + "encoding/json" "testing" "github.com/stretchr/testify/require" + cryptoapi "github.com/hyperledger/aries-framework-go/pkg/crypto" + ecdhpb "github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto/primitive/proto/ecdh_aead_go_proto" "github.com/hyperledger/aries-framework-go/pkg/kms" ) @@ -28,12 +31,28 @@ func TestCreateKID(t *testing.T) { require.EqualError(t, err, "createKID: failed to build jwk: buildJWK: key type is not supported: 'badType'") badPubKey := ed25519.PublicKey{} - _, err = CreateKID(badPubKey, kms.ECDH256KWAES256GCMType) + _, err = CreateKID(badPubKey, kms.NISTP256ECDHKWType) require.EqualError(t, err, "createKID: failed to build jwk: buildJWK: failed to build JWK from ecdh "+ - "key: generateJWKFromECDH: failed to unmarshal ECDH key: unexpected end of JSON input") + "key: generateJWKFromECDH: unmarshalECDHKey: failed to unmarshal ECDH key: unexpected end of JSON input") _, err = CreateKID(badPubKey, kms.ECDSAP256TypeDER) require.EqualError(t, err, "createKID: failed to build jwk: buildJWK: failed to build JWK from ecdsa "+ "DER key: generateJWKFromDERECDSA: failed to parse ecdsa key in DER format: asn1: syntax error: sequence "+ "truncated") + + randomKey := make([]byte, 32) + _, err = rand.Read(randomKey) + require.NoError(t, err) + + x25519Key := &cryptoapi.PublicKey{ + Curve: "X25519", + Type: ecdhpb.KeyType_OKP.String(), + X: randomKey, + } + mX25519Key, err := json.Marshal(x25519Key) + require.NoError(t, err) + + kid, err = CreateKID(mX25519Key, kms.X25519ECDHKWType) + require.NoError(t, err) + require.NotEmpty(t, kid) } diff --git a/pkg/kms/localkms/localkms.go b/pkg/kms/localkms/localkms.go index cb747985aa..e910fce33c 100644 --- a/pkg/kms/localkms/localkms.go +++ b/pkg/kms/localkms/localkms.go @@ -204,12 +204,14 @@ func getKeyTemplate(keyType kms.KeyType) (*tinkpb.KeyTemplate, error) { return signature.ED25519KeyWithoutPrefixTemplate(), nil case kms.HMACSHA256Tag256Type: return mac.HMACSHA256Tag256KeyTemplate(), nil - case kms.ECDH256KWAES256GCMType: + case kms.NISTP256ECDHKWType: return ecdh.NISTP256ECDHKWKeyTemplate(), nil - case kms.ECDH384KWAES256GCMType: + case kms.NISTP384ECDHKWType: return ecdh.NISTP384ECDHKWKeyTemplate(), nil - case kms.ECDH521KWAES256GCMType: + case kms.NISTP521ECDHKWType: return ecdh.NISTP521ECDHKWKeyTemplate(), nil + case kms.X25519ECDHKWType: + return ecdh.X25519ECDHKWKeyTemplate(), nil default: return nil, fmt.Errorf("getKeyTemplate: key type '%s' unrecognized", keyType) } diff --git a/pkg/kms/localkms/localkms_test.go b/pkg/kms/localkms/localkms_test.go index 2a78fd31f1..0f38da728e 100644 --- a/pkg/kms/localkms/localkms_test.go +++ b/pkg/kms/localkms/localkms_test.go @@ -260,15 +260,16 @@ func TestLocalKMS_Success(t *testing.T) { kms.ECDSAP384TypeIEEEP1363, kms.ECDSAP521TypeIEEEP1363, kms.ED25519Type, - kms.ECDH256KWAES256GCMType, - kms.ECDH384KWAES256GCMType, - kms.ECDH521KWAES256GCMType, + kms.NISTP256ECDHKWType, + kms.NISTP384ECDHKWType, + kms.NISTP521ECDHKWType, + kms.X25519ECDHKWType, } for _, v := range keyTemplates { // test Create() a new key keyID, newKeyHandle, e := kmsService.Create(v) - require.NoError(t, e) + require.NoError(t, e, "failed on template %v", v) require.NotEmpty(t, newKeyHandle) require.NotEmpty(t, keyID) diff --git a/pkg/kms/localkms/pubkey_writer.go b/pkg/kms/localkms/pubkey_writer.go index 8bf9a7b189..a83f92e9ff 100644 --- a/pkg/kms/localkms/pubkey_writer.go +++ b/pkg/kms/localkms/pubkey_writer.go @@ -25,9 +25,10 @@ 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.NistPEcdhKwPublicKey" + ecdsaVerifierTypeURL = "type.googleapis.com/google.crypto.tink.EcdsaPublicKey" + ed25519VerifierTypeURL = "type.googleapis.com/google.crypto.tink.Ed25519PublicKey" + nistPECDHKWPublicKeyTypeURL = "type.hyperledger.org/hyperledger.aries.crypto.tink.NistPEcdhKwPublicKey" + x25519ECDHKWPublicKeyTypeURL = "type.hyperledger.org/hyperledger.aries.crypto.tink.X25519EcdhKwPublicKey" ) // PubKeyWriter will write the raw bytes of a Tink KeySet's primary public key @@ -70,7 +71,7 @@ func write(w io.Writer, msg *tinkpb.Keyset) error { if err != nil { return err } - case ecdhAESPublicKeyTypeURL: + case nistPECDHKWPublicKeyTypeURL, x25519ECDHKWPublicKeyTypeURL: pkW := keyio.NewWriter(w) err = pkW.Write(msg) @@ -97,7 +98,8 @@ func write(w io.Writer, msg *tinkpb.Keyset) error { func writePubKey(w io.Writer, key *tinkpb.Keyset_Key) (bool, error) { var marshaledRawPubKey []byte - // TODO add other key types than the ones below and other than ecdhAESPublicKeyTypeURL + // TODO add other key types than the ones below and other than nistPECDHKWPublicKeyTypeURL and + // TODO x25519ECDHKWPublicKeyTypeURL(eg: BBS+, secp256k1 when introduced in KMS). switch key.KeyData.TypeUrl { case ecdsaVerifierTypeURL: pubKeyProto := new(ecdsapb.EcdsaPublicKey)