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)