diff --git a/crypto/jwx/jwk.go b/crypto/jwx/jwk.go index 949458f3..17301f18 100644 --- a/crypto/jwx/jwk.go +++ b/crypto/jwx/jwk.go @@ -4,6 +4,7 @@ import ( gocrypto "crypto" "crypto/ecdsa" "crypto/ed25519" + "crypto/elliptic" "crypto/rsa" "encoding/base64" "fmt" @@ -83,7 +84,7 @@ func (k *PrivateKeyJWK) ToPrivateKey() (gocrypto.PrivateKey, error) { } k.ALG = alg } - if IsSupportedJWXSigningVerificationAlgorithm(k.ALG) { + if IsSupportedJWXSigningVerificationAlgorithm(k.ALG) || IsSupportedKeyAgreementType(k.ALG) { return k.toSupportedPrivateKey() } if IsExperimentalJWXSigningVerificationAlgorithm(k.ALG) { @@ -303,6 +304,9 @@ func PrivateKeyToPrivateKeyJWK(keyID string, key gocrypto.PrivateKey) (*PublicKe case secp256k1.PrivateKey: pubKeyJWK, privKeyJWK, err = jwkFromSECP256k1PrivateKey(k) case ecdsa.PrivateKey: + if k.Curve == elliptic.P224() { + return nil, nil, fmt.Errorf("unsupported curve: %s", k.Curve.Params().Name) + } pubKeyJWK, privKeyJWK, err = jwkFromECDSAPrivateKey(k) case mode2.PrivateKey: privKey := dilithium.Mode2.PrivateKeyFromBytes(k.Bytes()) diff --git a/crypto/jwx/jwk_test.go b/crypto/jwx/jwk_test.go index 0b8ddda2..22cce2cd 100644 --- a/crypto/jwx/jwk_test.go +++ b/crypto/jwx/jwk_test.go @@ -1,485 +1,80 @@ package jwx import ( + "crypto/ecdsa" "testing" "github.com/TBD54566975/ssi-sdk/crypto" - "github.com/cloudflare/circl/sign/dilithium" "github.com/stretchr/testify/assert" ) -func TestJWKToPrivateKeyJWK(t *testing.T) { +func TestKeyToJWK(t *testing.T) { testKID := "test-kid" - t.Run("Ed25519", func(tt *testing.T) { - // known private key - _, privateKey, err := crypto.GenerateEd25519Key() - assert.NoError(tt, err) - assert.NotEmpty(tt, privateKey) - - // to our representation of a jwk - _, privKeyJWK, err := PrivateKeyToPrivateKeyJWK(testKID, privateKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, privKeyJWK) - - assert.Equal(tt, "OKP", privKeyJWK.KTY) - assert.Equal(tt, "Ed25519", privKeyJWK.CRV) - - // convert back - gotPrivKey, err := privKeyJWK.ToPrivateKey() - assert.NoError(tt, err) - assert.NotEmpty(tt, gotPrivKey) - assert.Equal(tt, privateKey, gotPrivKey) - }) - - t.Run("Dilithium 2", func(tt *testing.T) { - // known private key - _, privateKey, err := crypto.GenerateDilithiumKeyPair(dilithium.Mode2) - assert.NoError(tt, err) - assert.NotEmpty(tt, privateKey) - - // to our representation of a jwk - _, privKeyJWK, err := PrivateKeyToPrivateKeyJWK(testKID, privateKey) - assert.NoError(tt, err) - - assert.Equal(tt, DilithiumKTY, privKeyJWK.KTY) - assert.EqualValues(tt, DilithiumMode2Alg, privKeyJWK.ALG) - - // convert back - gotPrivKey, err := privKeyJWK.ToPrivateKey() - assert.NoError(tt, err) - assert.NotEmpty(tt, gotPrivKey) - assert.Equal(tt, privateKey, gotPrivKey) - }) - - t.Run("Dilithium 3", func(tt *testing.T) { - // known private key - _, privateKey, err := crypto.GenerateDilithiumKeyPair(dilithium.Mode3) - assert.NoError(tt, err) - assert.NotEmpty(tt, privateKey) - - // to our representation of a jwk - _, privKeyJWK, err := PrivateKeyToPrivateKeyJWK(testKID, privateKey) - assert.NoError(tt, err) - - assert.Equal(tt, DilithiumKTY, privKeyJWK.KTY) - assert.EqualValues(tt, DilithiumMode3Alg, privKeyJWK.ALG) - - // convert back - gotPrivKey, err := privKeyJWK.ToPrivateKey() - assert.NoError(tt, err) - assert.NotEmpty(tt, gotPrivKey) - assert.Equal(tt, privateKey, gotPrivKey) - }) - - t.Run("Dilithium 5", func(tt *testing.T) { - // known private key - _, privateKey, err := crypto.GenerateDilithiumKeyPair(dilithium.Mode5) - assert.NoError(tt, err) - assert.NotEmpty(tt, privateKey) - - // to our representation of a jwk - _, privKeyJWK, err := PrivateKeyToPrivateKeyJWK(testKID, privateKey) - assert.NoError(tt, err) - - assert.Equal(tt, DilithiumKTY, privKeyJWK.KTY) - assert.EqualValues(tt, DilithiumMode5Alg, privKeyJWK.ALG) - - // convert back - gotPrivKey, err := privKeyJWK.ToPrivateKey() - assert.NoError(tt, err) - assert.NotEmpty(tt, gotPrivKey) - assert.Equal(tt, privateKey, gotPrivKey) - }) -} - -func TestJWKToPublicKeyJWK(t *testing.T) { - testKID := "test-kid" - t.Run("Ed25519", func(tt *testing.T) { - // known public key - publicKey, _, err := crypto.GenerateEd25519Key() - assert.NoError(tt, err) - assert.NotEmpty(tt, publicKey) - - // to our representation of a jwk - pubKeyJWK, err := PublicKeyToPublicKeyJWK(testKID, publicKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, pubKeyJWK) - - assert.Equal(tt, "OKP", pubKeyJWK.KTY) - assert.Equal(tt, "Ed25519", pubKeyJWK.CRV) - - // convert back - gotPubKey, err := pubKeyJWK.ToPublicKey() - assert.NoError(tt, err) - assert.NotEmpty(tt, gotPubKey) - assert.Equal(tt, publicKey, gotPubKey) - }) - - t.Run("X25519", func(tt *testing.T) { - // known public key - publicKey, _, err := crypto.GenerateX25519Key() - assert.NoError(tt, err) - assert.NotEmpty(tt, publicKey) - - // to our representation of a jwk - pubKeyJWK, err := PublicKeyToPublicKeyJWK(testKID, publicKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, pubKeyJWK) - - assert.Equal(tt, "OKP", pubKeyJWK.KTY) - assert.Equal(tt, "X25519", pubKeyJWK.CRV) - - // convert back - gotPubKey, err := pubKeyJWK.ToPublicKey() - assert.NoError(tt, err) - assert.NotEmpty(tt, gotPubKey) - assert.Equal(tt, publicKey, gotPubKey) - }) - - t.Run("Dilithium 2", func(tt *testing.T) { - // known private key - publicKey, _, err := crypto.GenerateDilithiumKeyPair(dilithium.Mode2) - assert.NoError(tt, err) - assert.NotEmpty(tt, publicKey) - - // to our representation of a jwk - pubKeyJWK, err := PublicKeyToPublicKeyJWK(testKID, publicKey) - assert.NoError(tt, err) - - assert.Equal(tt, DilithiumKTY, pubKeyJWK.KTY) - assert.EqualValues(tt, DilithiumMode2Alg, pubKeyJWK.ALG) - - // convert back - gotPubKey, err := pubKeyJWK.ToPublicKey() - assert.NoError(tt, err) - assert.NotEmpty(tt, gotPubKey) - assert.Equal(tt, publicKey, gotPubKey) - }) - - t.Run("Dilithium 3", func(tt *testing.T) { - // known private key - publicKey, _, err := crypto.GenerateDilithiumKeyPair(dilithium.Mode3) - assert.NoError(tt, err) - assert.NotEmpty(tt, publicKey) - - // to our representation of a jwk - pubKeyJWK, err := PublicKeyToPublicKeyJWK(testKID, publicKey) - assert.NoError(tt, err) - - assert.Equal(tt, DilithiumKTY, pubKeyJWK.KTY) - assert.EqualValues(tt, DilithiumMode3Alg, pubKeyJWK.ALG) - - // convert back - gotPubKey, err := pubKeyJWK.ToPublicKey() - assert.NoError(tt, err) - assert.NotEmpty(tt, gotPubKey) - assert.Equal(tt, publicKey, gotPubKey) - }) - - t.Run("Dilithium 5", func(tt *testing.T) { - // known private key - publicKey, _, err := crypto.GenerateDilithiumKeyPair(dilithium.Mode5) - assert.NoError(tt, err) - assert.NotEmpty(tt, publicKey) - - // to our representation of a jwk - pubKeyJWK, err := PublicKeyToPublicKeyJWK(testKID, publicKey) - assert.NoError(tt, err) - - assert.Equal(tt, DilithiumKTY, pubKeyJWK.KTY) - assert.EqualValues(tt, DilithiumMode5Alg, pubKeyJWK.ALG) - - // convert back - gotPubKey, err := pubKeyJWK.ToPublicKey() - assert.NoError(tt, err) - assert.NotEmpty(tt, gotPubKey) - assert.Equal(tt, publicKey, gotPubKey) - }) -} - -func TestPublicKeyToPublicKeyJWK(t *testing.T) { - testKID := "key-id" - t.Run("RSA", func(tt *testing.T) { - pubKey, _, err := crypto.GenerateRSA2048Key() - assert.NoError(t, err) - - jwk, err := PublicKeyToPublicKeyJWK(testKID, pubKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk) - assert.Equal(tt, "RSA", jwk.KTY) - - jwk2, err := PublicKeyToPublicKeyJWK(testKID, &pubKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk2) - assert.Equal(tt, "RSA", jwk2.KTY) - }) - - t.Run("Ed25519", func(tt *testing.T) { - pubKey, _, err := crypto.GenerateEd25519Key() - assert.NoError(t, err) - - jwk, err := PublicKeyToPublicKeyJWK(testKID, pubKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk) - assert.Equal(tt, "OKP", jwk.KTY) - assert.Equal(tt, "Ed25519", jwk.CRV) - - jwk2, err := PublicKeyToPublicKeyJWK(testKID, &pubKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk2) - assert.Equal(tt, "OKP", jwk2.KTY) - assert.Equal(tt, "Ed25519", jwk2.CRV) - }) - - t.Run("X25519", func(tt *testing.T) { - pubKey, _, err := crypto.GenerateX25519Key() - assert.NoError(t, err) - - jwk, err := PublicKeyToPublicKeyJWK(testKID, pubKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk) - assert.Equal(tt, "OKP", jwk.KTY) - assert.Equal(tt, "X25519", jwk.CRV) - - jwk2, err := PublicKeyToPublicKeyJWK(testKID, &pubKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk2) - assert.Equal(tt, "OKP", jwk2.KTY) - assert.Equal(tt, "X25519", jwk2.CRV) - }) - - t.Run("secp256k1", func(tt *testing.T) { - pubKey, _, err := crypto.GenerateSECP256k1Key() - assert.NoError(t, err) - - jwk, err := PublicKeyToPublicKeyJWK(testKID, pubKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk) - assert.Equal(tt, "EC", jwk.KTY) - assert.Equal(tt, "secp256k1", jwk.CRV) - - jwk2, err := PublicKeyToPublicKeyJWK(testKID, &pubKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk2) - assert.Equal(tt, "EC", jwk2.KTY) - assert.Equal(tt, "secp256k1", jwk2.CRV) - }) - - t.Run("ecdsa P-256", func(tt *testing.T) { - pubKey, _, err := crypto.GenerateP256Key() - assert.NoError(t, err) - - jwk, err := PublicKeyToPublicKeyJWK(testKID, pubKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk) - assert.Equal(tt, "EC", jwk.KTY) - assert.Equal(tt, "P-256", jwk.CRV) - - jwk2, err := PublicKeyToPublicKeyJWK(testKID, &pubKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk2) - assert.Equal(tt, "EC", jwk2.KTY) - assert.Equal(tt, "P-256", jwk2.CRV) - }) - - t.Run("ecdsa P-384", func(tt *testing.T) { - pubKey, _, err := crypto.GenerateP384Key() - assert.NoError(t, err) - - jwk, err := PublicKeyToPublicKeyJWK(testKID, pubKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk) - assert.Equal(tt, "EC", jwk.KTY) - assert.Equal(tt, "P-384", jwk.CRV) - - jwk2, err := PublicKeyToPublicKeyJWK(testKID, &pubKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk2) - assert.Equal(tt, "EC", jwk2.KTY) - assert.Equal(tt, "P-384", jwk2.CRV) - }) - - t.Run("Dilithium 2", func(tt *testing.T) { - pubKey, _, err := crypto.GenerateDilithiumKeyPair(dilithium.Mode2) - assert.NoError(t, err) - - jwk, err := PublicKeyToPublicKeyJWK(testKID, pubKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk) - assert.Equal(tt, DilithiumKTY, jwk.KTY) - assert.EqualValues(tt, DilithiumMode2Alg, jwk.ALG) - - jwk2, err := PublicKeyToPublicKeyJWK(testKID, &pubKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk2) - assert.Equal(tt, DilithiumKTY, jwk2.KTY) - assert.EqualValues(tt, DilithiumMode2Alg, jwk2.ALG) - }) - - t.Run("Dilithium 3", func(tt *testing.T) { - pubKey, _, err := crypto.GenerateDilithiumKeyPair(dilithium.Mode3) - assert.NoError(t, err) - - jwk, err := PublicKeyToPublicKeyJWK(testKID, pubKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk) - assert.Equal(tt, DilithiumKTY, jwk.KTY) - assert.EqualValues(tt, DilithiumMode3Alg, jwk.ALG) - - jwk2, err := PublicKeyToPublicKeyJWK(testKID, &pubKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk2) - assert.Equal(tt, DilithiumKTY, jwk2.KTY) - assert.EqualValues(tt, DilithiumMode3Alg, jwk2.ALG) - }) - - t.Run("Dilithium 5", func(tt *testing.T) { - pubKey, _, err := crypto.GenerateDilithiumKeyPair(dilithium.Mode5) - assert.NoError(t, err) - - jwk, err := PublicKeyToPublicKeyJWK(testKID, pubKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk) - assert.Equal(tt, DilithiumKTY, jwk.KTY) - assert.EqualValues(tt, DilithiumMode5Alg, jwk.ALG) - - jwk2, err := PublicKeyToPublicKeyJWK(testKID, &pubKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk2) - assert.Equal(tt, DilithiumKTY, jwk2.KTY) - assert.EqualValues(tt, DilithiumMode5Alg, jwk2.ALG) - }) - - t.Run("unsupported", func(tt *testing.T) { - jwk, err := PublicKeyToPublicKeyJWK(testKID, nil) - assert.Error(tt, err) - assert.Empty(tt, jwk) - }) -} - -func TestPrivateKeyToPrivateKeyJWK(t *testing.T) { - testKID := "test-kid" - t.Run("RSA", func(tt *testing.T) { - _, privKey, err := crypto.GenerateRSA2048Key() - assert.NoError(t, err) - - _, jwk, err := PrivateKeyToPrivateKeyJWK(testKID, privKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk) - assert.Equal(tt, "RSA", jwk.KTY) - }) - - t.Run("Ed25519", func(tt *testing.T) { - _, privKey, err := crypto.GenerateEd25519Key() - assert.NoError(t, err) - _, jwk, err := PrivateKeyToPrivateKeyJWK(testKID, privKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk) - assert.Equal(tt, "OKP", jwk.KTY) - assert.Equal(tt, "Ed25519", jwk.CRV) - }) - - t.Run("X25519", func(tt *testing.T) { - _, privKey, err := crypto.GenerateX25519Key() - assert.NoError(t, err) - - _, jwk, err := PrivateKeyToPrivateKeyJWK(testKID, privKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk) - assert.Equal(tt, "OKP", jwk.KTY) - assert.Equal(tt, "X25519", jwk.CRV) - }) - - t.Run("secp256k1", func(tt *testing.T) { - _, privKey, err := crypto.GenerateSECP256k1Key() - assert.NoError(t, err) - - _, jwk, err := PrivateKeyToPrivateKeyJWK(testKID, privKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk) - assert.Equal(tt, "EC", jwk.KTY) - assert.Equal(tt, "secp256k1", jwk.CRV) - }) - - t.Run("ecdsa P-256", func(tt *testing.T) { - _, privKey, err := crypto.GenerateP256Key() - assert.NoError(t, err) - - _, jwk, err := PrivateKeyToPrivateKeyJWK(testKID, privKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk) - assert.Equal(tt, "EC", jwk.KTY) - assert.Equal(tt, "P-256", jwk.CRV) - }) - - t.Run("ecdsa P-384", func(tt *testing.T) { - _, privKey, err := crypto.GenerateP384Key() - assert.NoError(t, err) - - _, jwk, err := PrivateKeyToPrivateKeyJWK(testKID, privKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk) - assert.Equal(tt, "EC", jwk.KTY) - assert.Equal(tt, "P-384", jwk.CRV) - }) - - t.Run("Dilithium 2", func(tt *testing.T) { - _, privKey, err := crypto.GenerateDilithiumKeyPair(dilithium.Mode2) - assert.NoError(t, err) - - _, jwk, err := PrivateKeyToPrivateKeyJWK(testKID, privKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk) - assert.Equal(tt, DilithiumKTY, jwk.KTY) - assert.EqualValues(tt, DilithiumMode2Alg, jwk.ALG) - - _, jwk2, err := PrivateKeyToPrivateKeyJWK(testKID, &privKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk2) - assert.Equal(tt, DilithiumKTY, jwk2.KTY) - assert.EqualValues(tt, DilithiumMode2Alg, jwk2.ALG) - }) - - t.Run("Dilithium 3", func(tt *testing.T) { - _, privKey, err := crypto.GenerateDilithiumKeyPair(dilithium.Mode3) - assert.NoError(t, err) - - _, jwk, err := PrivateKeyToPrivateKeyJWK(testKID, privKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk) - assert.Equal(tt, DilithiumKTY, jwk.KTY) - assert.EqualValues(tt, DilithiumMode3Alg, jwk.ALG) - - _, jwk2, err := PrivateKeyToPrivateKeyJWK(testKID, &privKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk2) - assert.Equal(tt, DilithiumKTY, jwk2.KTY) - assert.EqualValues(tt, DilithiumMode3Alg, jwk2.ALG) - }) - - t.Run("Dilithium 5", func(tt *testing.T) { - _, privKey, err := crypto.GenerateDilithiumKeyPair(dilithium.Mode5) - assert.NoError(t, err) - - _, jwk, err := PrivateKeyToPrivateKeyJWK(testKID, privKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk) - assert.Equal(tt, DilithiumKTY, jwk.KTY) - assert.EqualValues(tt, DilithiumMode5Alg, jwk.ALG) - - _, jwk2, err := PrivateKeyToPrivateKeyJWK(testKID, &privKey) - assert.NoError(tt, err) - assert.NotEmpty(tt, jwk2) - assert.Equal(tt, DilithiumKTY, jwk2.KTY) - assert.EqualValues(tt, DilithiumMode5Alg, jwk2.ALG) - }) - - t.Run("unsupported", func(tt *testing.T) { - _, jwk, err := PrivateKeyToPrivateKeyJWK(testKID, nil) - assert.Error(tt, err) - assert.Empty(tt, jwk) - }) + for _, keyType := range crypto.GetSupportedJWKKeyTypes() { + t.Run(string(keyType), func(tt *testing.T) { + pub, priv, err := crypto.GenerateKeyByKeyType(keyType) + assert.NoError(tt, err) + assert.NotEmpty(tt, pub) + assert.NotEmpty(tt, priv) + + pubKeyJWK, privKeyJWK, err := PrivateKeyToPrivateKeyJWK(testKID, priv) + assert.NoError(tt, err) + assert.NotEmpty(tt, pubKeyJWK) + assert.NotEmpty(tt, privKeyJWK) + + otherPubKeyJWK, err := PublicKeyToPublicKeyJWK(testKID, pub) + assert.NoError(tt, err) + assert.NotEmpty(tt, otherPubKeyJWK) + assert.Equal(tt, pubKeyJWK, otherPubKeyJWK) + + privKey, err := privKeyJWK.ToPrivateKey() + assert.NoError(tt, err) + assert.NotEmpty(tt, privKey) + + pubKey, err := pubKeyJWK.ToPublicKey() + assert.NoError(tt, err) + assert.NotEmpty(tt, pubKey) + + if keyType == crypto.SECP256k1 { + pubKey = crypto.SECP256k1ECDSAPubKeyToSECP256k1(pubKey.(ecdsa.PublicKey)) + privKey = crypto.SECP256k1ECDSASPrivKeyToSECP256k1(privKey.(ecdsa.PrivateKey)) + } + + assert.Equal(tt, priv, privKey) + assert.Equal(tt, pub, pubKey) + }) + } + + for _, keyType := range crypto.GetExperimentalKeyTypes() { + t.Run(string(keyType), func(tt *testing.T) { + pub, priv, err := crypto.GenerateKeyByKeyType(keyType) + + assert.NoError(tt, err) + assert.NotEmpty(tt, pub) + assert.NotEmpty(tt, priv) + + pubKeyJWK, privKeyJWK, err := PrivateKeyToPrivateKeyJWK(testKID, priv) + assert.NoError(tt, err) + assert.NotEmpty(tt, pubKeyJWK) + assert.NotEmpty(tt, privKeyJWK) + + otherPubKeyJWK, err := PublicKeyToPublicKeyJWK(testKID, pub) + assert.NoError(tt, err) + assert.NotEmpty(tt, otherPubKeyJWK) + assert.Equal(tt, pubKeyJWK, otherPubKeyJWK) + + privKey, err := privKeyJWK.ToPrivateKey() + assert.NoError(tt, err) + assert.NotEmpty(tt, privKey) + assert.EqualValues(tt, priv, privKey) + + pubKey, err := pubKeyJWK.ToPublicKey() + assert.NoError(tt, err) + assert.NotEmpty(tt, pubKey) + assert.EqualValues(tt, pub, pubKey) + }) + } } // https://www.ietf.org/archive/id/draft-ietf-cose-dilithium-00.html#section-6.1.1 diff --git a/crypto/keys.go b/crypto/keys.go index 41c83c2a..6334b8f7 100644 --- a/crypto/keys.go +++ b/crypto/keys.go @@ -307,6 +307,22 @@ func BytesToPrivKey(keyBytes []byte, kt KeyType) (crypto.PrivateKey, error) { } } +// SECP256k1ECDSAPubKeyToSECP256k1 converts an ecdsa.PublicKey to a secp.PublicKey +func SECP256k1ECDSAPubKeyToSECP256k1(key ecdsa.PublicKey) secp.PublicKey { + x := new(btcec.FieldVal) + x.SetByteSlice(key.X.Bytes()) + y := new(btcec.FieldVal) + y.SetByteSlice(key.Y.Bytes()) + return *btcec.NewPublicKey(x, y) +} + +// SECP256k1ECDSASPrivKeyToSECP256k1 converts an ecdsa.PrivateKey to a secp.PrivateKey +func SECP256k1ECDSASPrivKeyToSECP256k1(key ecdsa.PrivateKey) secp.PrivateKey { + scalar := new(btcec.ModNScalar) + scalar.SetByteSlice(key.D.Bytes()) + return *secp.NewPrivateKey(scalar) +} + func GenerateEd25519Key() (ed25519.PublicKey, ed25519.PrivateKey, error) { return ed25519.GenerateKey(rand.Reader) } diff --git a/crypto/keys_test.go b/crypto/keys_test.go index f6806b5b..0b19b5e9 100644 --- a/crypto/keys_test.go +++ b/crypto/keys_test.go @@ -218,3 +218,17 @@ func TestDilithiumKeys(t *testing.T) { } }) } + +func TestSECP256k1Conversions(t *testing.T) { + pk, sk, err := GenerateSECP256k1Key() + assert.NoError(t, err) + + ecdsaPK := pk.ToECDSA() + ecdsaSK := sk.ToECDSA() + + gotPK := SECP256k1ECDSAPubKeyToSECP256k1(*ecdsaPK) + gotSK := SECP256k1ECDSASPrivKeyToSECP256k1(*ecdsaSK) + + assert.Equal(t, pk, gotPK) + assert.Equal(t, sk, gotSK) +} diff --git a/crypto/models.go b/crypto/models.go index 6b026ac2..a6eecbd4 100644 --- a/crypto/models.go +++ b/crypto/models.go @@ -64,6 +64,12 @@ func IsSupportedKeyType(kt KeyType) bool { return false } +// GetSupportedJWKKeyTypes returns a list of supported JWK key types +// RSA, secp256k1, and P-224 are not supported by the lib we use for JWK +func GetSupportedJWKKeyTypes() []KeyType { + return []KeyType{Ed25519, X25519, SECP256k1, SECP256k1ECDSA, P256, P384, P521} +} + // GetSupportedKeyTypes returns a list of supported key types func GetSupportedKeyTypes() []KeyType { return []KeyType{Ed25519, X25519, SECP256k1, SECP256k1ECDSA, P224, P256, P384, P521, RSA} diff --git a/did/ion/did.go b/did/ion/did.go index 03b50181..6796a18c 100644 --- a/did/ion/did.go +++ b/did/ion/did.go @@ -205,15 +205,15 @@ func addPublicKeysPatch(doc did.Document, patch AddPublicKeysAction) (*did.Docum }) for _, purpose := range currKey.Purposes { switch purpose { - case Authentication: + case did.Authentication: doc.Authentication = append(doc.Authentication, currKey.ID) - case AssertionMethod: + case did.AssertionMethod: doc.AssertionMethod = append(doc.AssertionMethod, currKey.ID) - case KeyAgreement: + case did.KeyAgreement: doc.KeyAgreement = append(doc.KeyAgreement, currKey.ID) - case CapabilityInvocation: + case did.CapabilityInvocation: doc.CapabilityInvocation = append(doc.CapabilityInvocation, currKey.ID) - case CapabilityDelegation: + case did.CapabilityDelegation: doc.CapabilityDelegation = append(doc.CapabilityDelegation, currKey.ID) default: return nil, fmt.Errorf("unknown key purpose: %s:%s", currKey.ID, purpose) diff --git a/did/ion/did_test.go b/did/ion/did_test.go index 57bece32..7620a9fa 100644 --- a/did/ion/did_test.go +++ b/did/ion/did_test.go @@ -189,7 +189,7 @@ func TestPatchesToDIDDocument(t *testing.T) { Action: AddPublicKeys, PublicKeys: []PublicKey{{ ID: "did:ion:test#key1", - Purposes: []PublicKeyPurpose{Authentication, AssertionMethod}, + Purposes: []did.PublicKeyPurpose{did.Authentication, did.AssertionMethod}, }}, }}) assert.NoError(tt, err) @@ -210,7 +210,7 @@ func TestPatchesToDIDDocument(t *testing.T) { Action: AddPublicKeys, PublicKeys: []PublicKey{{ ID: "did:ion:test#key1", - Purposes: []PublicKeyPurpose{Authentication, AssertionMethod}, + Purposes: []did.PublicKeyPurpose{did.Authentication, did.AssertionMethod}, }}, } removeKeys := RemovePublicKeysAction{ @@ -229,11 +229,11 @@ func TestPatchesToDIDDocument(t *testing.T) { PublicKeys: []PublicKey{ { ID: "did:ion:test#key1", - Purposes: []PublicKeyPurpose{Authentication, AssertionMethod}, + Purposes: []did.PublicKeyPurpose{did.Authentication, did.AssertionMethod}, }, { ID: "did:ion:test#key2", - Purposes: []PublicKeyPurpose{Authentication, AssertionMethod}, + Purposes: []did.PublicKeyPurpose{did.Authentication, did.AssertionMethod}, }, }, } @@ -295,7 +295,7 @@ func TestPatchesToDIDDocument(t *testing.T) { PublicKeys: []PublicKey{ { ID: "did:ion:test#key1", - Purposes: []PublicKeyPurpose{Authentication, AssertionMethod}, + Purposes: []did.PublicKeyPurpose{did.Authentication, did.AssertionMethod}, }, }, Services: []did.Service{ diff --git a/did/ion/enum.go b/did/ion/enum.go index 9d88eba0..1d2d06e0 100644 --- a/did/ion/enum.go +++ b/did/ion/enum.go @@ -7,16 +7,6 @@ const ( Testnet Network = "testnet" ) -type PublicKeyPurpose string - -const ( - Authentication PublicKeyPurpose = "authentication" - AssertionMethod PublicKeyPurpose = "assertionMethod" - CapabilityInvocation PublicKeyPurpose = "capabilityInvocation" - CapabilityDelegation PublicKeyPurpose = "capabilityDelegation" - KeyAgreement PublicKeyPurpose = "keyAgreement" -) - type OperationKeyType string const ( diff --git a/did/ion/model.go b/did/ion/model.go index fd30c79c..94edc2eb 100644 --- a/did/ion/model.go +++ b/did/ion/model.go @@ -21,10 +21,10 @@ func (d Document) IsEmpty() bool { } type PublicKey struct { - ID string `json:"id,omitempty"` - Type string `json:"type,omitempty"` - PublicKeyJWK jwx.PublicKeyJWK `json:"publicKeyJwk,omitempty"` - Purposes []PublicKeyPurpose `json:"purposes,omitempty"` + ID string `json:"id,omitempty"` + Type string `json:"type,omitempty"` + PublicKeyJWK jwx.PublicKeyJWK `json:"publicKeyJwk,omitempty"` + Purposes []did.PublicKeyPurpose `json:"purposes,omitempty"` } // action models diff --git a/did/model.go b/did/model.go index b70b5e05..4f39d913 100644 --- a/did/model.go +++ b/did/model.go @@ -105,3 +105,13 @@ func KeyTypeToMultikeyLDType(kt crypto.KeyType) (cryptosuite.LDKeyType, error) { return "", fmt.Errorf("keyType %+v failed to convert to multikey LDKeyType", kt) } } + +type PublicKeyPurpose string + +const ( + Authentication PublicKeyPurpose = "authentication" + AssertionMethod PublicKeyPurpose = "assertionMethod" + CapabilityInvocation PublicKeyPurpose = "capabilityInvocation" + CapabilityDelegation PublicKeyPurpose = "capabilityDelegation" + KeyAgreement PublicKeyPurpose = "keyAgreement" +)