Repository: lestrrat-go/jwx
Pull request #336 (2021-02-17 22:32:31)

Add ES256K signature / verification
Fixes issue #334 

Let me know if I should try to make this do optional build for including the decred repo.


Patch:
diff --git a/examples/go.sum b/examples/go.sum
index 6d3ea0b2..0ef04679 100644
--- a/examples/go.sum
+++ b/examples/go.sum
@@ -2,6 +2,10 @@ github.com/cloudflare/circl v1.0.1-0.20210104183656-96a0695de3c3 h1:tpTW2GMi0DOd
 github.com/cloudflare/circl v1.0.1-0.20210104183656-96a0695de3c3/go.mod h1:l2CvGr3DNS9Egif8pwQqJ45Ci9Y/PPs0XJHTcRKbGBQ=
 github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/decred/dcrd/chaincfg/chainhash v1.0.2/go.mod h1:BpbrGgrPTr3YJYRN3Bm+D9NuaFd+zGyNeIKgrhCXK60=
+github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
+github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 h1:sgNeV1VRMDzs6rzyPpxyM0jp317hnwiq58Filgag2xw=
+github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0/go.mod h1:J70FGZSbzsjecRTiTzER+3f1KZLNaXkuv+yeFTKoxM8=
 github.com/goccy/go-json v0.4.6 h1:zpWhZVrIu+WfuJ1waElFa1KOnKj2p0H9wpJn/TTY0zQ=
 github.com/goccy/go-json v0.4.6/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
 github.com/lestrrat-go/backoff/v2 v2.0.7 h1:i2SeK33aOFJlUNJZzf2IpXRBvqBBnaGXfY5Xaop/GsE=
diff --git a/go.mod b/go.mod
index 96feaa96..d818033e 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,7 @@ module github.com/lestrrat-go/jwx
 go 1.13
 
 require (
+	github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0
 	github.com/goccy/go-json v0.4.6
 	github.com/lestrrat-go/backoff/v2 v2.0.7
 	github.com/lestrrat-go/codegen v1.0.0
diff --git a/go.sum b/go.sum
index c92181a6..d25f0187 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,12 @@
 github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/decred/dcrd v1.3.0 h1:EEXm7BdiROfazDtuFsOu9mfotnyy00bgCuVwUqaszFo=
+github.com/decred/dcrd/chaincfg/chainhash v1.0.2/go.mod h1:BpbrGgrPTr3YJYRN3Bm+D9NuaFd+zGyNeIKgrhCXK60=
+github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
+github.com/decred/dcrd/dcrec v1.0.0 h1:W+z6Es+Rai3MXYVoPAxYr5U1DGis0Co33scJ6uH2J6o=
+github.com/decred/dcrd/dcrec/secp256k1 v1.0.3 h1:u4XpHqlscRolxPxt2YHrFBDVZYY1AK+KMV02H1r+HmU=
+github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 h1:sgNeV1VRMDzs6rzyPpxyM0jp317hnwiq58Filgag2xw=
+github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0/go.mod h1:J70FGZSbzsjecRTiTzER+3f1KZLNaXkuv+yeFTKoxM8=
 github.com/goccy/go-json v0.4.6 h1:zpWhZVrIu+WfuJ1waElFa1KOnKj2p0H9wpJn/TTY0zQ=
 github.com/goccy/go-json v0.4.6/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
 github.com/lestrrat-go/backoff/v2 v2.0.7 h1:i2SeK33aOFJlUNJZzf2IpXRBvqBBnaGXfY5Xaop/GsE=
diff --git a/jwa/elliptic_gen.go b/jwa/elliptic_gen.go
index acfecb95..5ef8a87f 100644
--- a/jwa/elliptic_gen.go
+++ b/jwa/elliptic_gen.go
@@ -19,6 +19,7 @@ const (
 	P256                 EllipticCurveAlgorithm = "P-256"
 	P384                 EllipticCurveAlgorithm = "P-384"
 	P521                 EllipticCurveAlgorithm = "P-521"
+	Secp256k1            EllipticCurveAlgorithm = "secp256k1"
 	X25519               EllipticCurveAlgorithm = "X25519"
 	X448                 EllipticCurveAlgorithm = "X448"
 )
@@ -29,6 +30,7 @@ var allEllipticCurveAlgorithms = []EllipticCurveAlgorithm{
 	P256,
 	P384,
 	P521,
+	Secp256k1,
 	X25519,
 	X448,
 }
@@ -57,7 +59,7 @@ func (v *EllipticCurveAlgorithm) Accept(value interface{}) error {
 		tmp = EllipticCurveAlgorithm(s)
 	}
 	switch tmp {
-	case Ed25519, Ed448, P256, P384, P521, X25519, X448:
+	case Ed25519, Ed448, P256, P384, P521, Secp256k1, X25519, X448:
 	default:
 		return errors.Errorf(`invalid jwa.EllipticCurveAlgorithm value`)
 	}
diff --git a/jwa/internal/cmd/gentypes/main.go b/jwa/internal/cmd/gentypes/main.go
index c915b3d8..616bd798 100644
--- a/jwa/internal/cmd/gentypes/main.go
+++ b/jwa/internal/cmd/gentypes/main.go
@@ -131,6 +131,10 @@ func _main() error {
 					name:  `P521`,
 					value: `P-521`,
 				},
+				{
+					name:  `Secp256k1`,
+					value: `secp256k1`,
+				},
 				{
 					name:  `Ed25519`,
 					value: `Ed25519`,
@@ -203,6 +207,11 @@ func _main() error {
 					value:   "ES512",
 					comment: `ECDSA using P-521 and SHA-512`,
 				},
+				{
+					name:    `ES256K`,
+					value:   "ES256K",
+					comment: `ECDSA using secp256k1 and SHA-256`,
+				},
 				{
 					name:    `EdDSA`,
 					value:   `EdDSA`,
diff --git a/jwa/signature_gen.go b/jwa/signature_gen.go
index e1e10cb2..5d293918 100644
--- a/jwa/signature_gen.go
+++ b/jwa/signature_gen.go
@@ -13,13 +13,14 @@ type SignatureAlgorithm string
 
 // Supported values for SignatureAlgorithm
 const (
-	ES256       SignatureAlgorithm = "ES256" // ECDSA using P-256 and SHA-256
-	ES384       SignatureAlgorithm = "ES384" // ECDSA using P-384 and SHA-384
-	ES512       SignatureAlgorithm = "ES512" // ECDSA using P-521 and SHA-512
-	EdDSA       SignatureAlgorithm = "EdDSA" // EdDSA signature algorithms
-	HS256       SignatureAlgorithm = "HS256" // HMAC using SHA-256
-	HS384       SignatureAlgorithm = "HS384" // HMAC using SHA-384
-	HS512       SignatureAlgorithm = "HS512" // HMAC using SHA-512
+	ES256       SignatureAlgorithm = "ES256"  // ECDSA using P-256 and SHA-256
+	ES256K      SignatureAlgorithm = "ES256K" // ECDSA using secp256k1 and SHA-256
+	ES384       SignatureAlgorithm = "ES384"  // ECDSA using P-384 and SHA-384
+	ES512       SignatureAlgorithm = "ES512"  // ECDSA using P-521 and SHA-512
+	EdDSA       SignatureAlgorithm = "EdDSA"  // EdDSA signature algorithms
+	HS256       SignatureAlgorithm = "HS256"  // HMAC using SHA-256
+	HS384       SignatureAlgorithm = "HS384"  // HMAC using SHA-384
+	HS512       SignatureAlgorithm = "HS512"  // HMAC using SHA-512
 	NoSignature SignatureAlgorithm = "none"
 	PS256       SignatureAlgorithm = "PS256" // RSASSA-PSS using SHA256 and MGF1-SHA256
 	PS384       SignatureAlgorithm = "PS384" // RSASSA-PSS using SHA384 and MGF1-SHA384
@@ -31,6 +32,7 @@ const (
 
 var allSignatureAlgorithms = []SignatureAlgorithm{
 	ES256,
+	ES256K,
 	ES384,
 	ES512,
 	EdDSA,
@@ -70,7 +72,7 @@ func (v *SignatureAlgorithm) Accept(value interface{}) error {
 		tmp = SignatureAlgorithm(s)
 	}
 	switch tmp {
-	case ES256, ES384, ES512, EdDSA, HS256, HS384, HS512, NoSignature, PS256, PS384, PS512, RS256, RS384, RS512:
+	case ES256, ES256K, ES384, ES512, EdDSA, HS256, HS384, HS512, NoSignature, PS256, PS384, PS512, RS256, RS384, RS512:
 	default:
 		return errors.Errorf(`invalid jwa.SignatureAlgorithm value`)
 	}
diff --git a/jwk/README.md b/jwk/README.md
index 49b34039..de0b2763 100644
--- a/jwk/README.md
+++ b/jwk/README.md
@@ -16,7 +16,7 @@ Supported key types:
 | kty | Curve                   | Go Key Type                                   |
 |:----|:------------------------|:----------------------------------------------|
 | RSA | N/A                     | rsa.PrivateKey / rsa.PublicKey (2)            |
-| EC  | P-256<br>P-384<br>P-521 | ecdsa.PrivateKey / ecdsa.PublicKey (2)        |
+| EC  | P-256<br>P-384<br>P-521<br>secp256k1 (1) | ecdsa.PrivateKey / ecdsa.PublicKey (2)        |
 | oct | N/A                     | []byte                                        |
 | OKP | Ed25519 (1)             | ed25519.PrivateKey / ed25519.PublicKey (2)    |
 |     | X25519 (1)              | (jwx/)x25519.PrivateKey / x25519.PublicKey (2)|
diff --git a/jwk/ecdsa.go b/jwk/ecdsa.go
index 2d6779e8..4ab6359b 100644
--- a/jwk/ecdsa.go
+++ b/jwk/ecdsa.go
@@ -8,6 +8,7 @@ import (
 	"fmt"
 	"math/big"
 
+	"github.com/decred/dcrd/dcrec/secp256k1/v3"
 	"github.com/lestrrat-go/jwx/internal/base64"
 	"github.com/lestrrat-go/jwx/internal/blackmagic"
 	"github.com/lestrrat-go/jwx/internal/ecutil"
@@ -37,6 +38,8 @@ func (k *ecdsaPublicKey) FromRaw(rawKey *ecdsa.PublicKey) error {
 		crv = jwa.P384
 	case elliptic.P521():
 		crv = jwa.P521
+	case secp256k1.S256():
+		crv = jwa.Secp256k1
 	default:
 		return errors.Errorf(`invalid elliptic curve %s`, rawKey.Curve)
 	}
@@ -71,6 +74,8 @@ func (k *ecdsaPrivateKey) FromRaw(rawKey *ecdsa.PrivateKey) error {
 		crv = jwa.P384
 	case elliptic.P521():
 		crv = jwa.P521
+	case secp256k1.S256():
+		crv = jwa.Secp256k1
 	default:
 		return errors.Errorf(`invalid elliptic curve %s`, rawKey.Curve)
 	}
@@ -88,6 +93,8 @@ func buildECDSAPublicKey(alg jwa.EllipticCurveAlgorithm, xbuf, ybuf []byte) (*ec
 		curve = elliptic.P384()
 	case jwa.P521:
 		curve = elliptic.P521()
+	case jwa.Secp256k1:
+		curve = secp256k1.S256()
 	default:
 		return nil, errors.Errorf(`invalid curve algorithm %s`, alg)
 	}
diff --git a/jws/README.md b/jws/README.md
index 0eb720b1..4845f2f5 100644
--- a/jws/README.md
+++ b/jws/README.md
@@ -23,6 +23,7 @@ Supported signature algorithms:
 | ECDSA using P-256 and SHA-256           | YES        | jwa.ES256                |
 | ECDSA using P-384 and SHA-384           | YES        | jwa.ES384                |
 | ECDSA using P-521 and SHA-512           | YES        | jwa.ES512                |
+| ECDSA using secp256k1 and SHA-256 (1)   | YES        | jwa.ES256K               |
 | RSASSA-PSS using SHA256 and MGF1-SHA256 | YES        | jwa.PS256                |
 | RSASSA-PSS using SHA384 and MGF1-SHA384 | YES        | jwa.PS384                |
 | RSASSA-PSS using SHA512 and MGF1-SHA512 | YES        | jwa.PS512                |
diff --git a/jws/ecdsa.go b/jws/ecdsa.go
index 1e5d1067..d0b33049 100644
--- a/jws/ecdsa.go
+++ b/jws/ecdsa.go
@@ -16,9 +16,10 @@ var ecdsaVerifyFuncs = map[jwa.SignatureAlgorithm]ecdsaVerifyFunc{}
 
 func init() {
 	algs := map[jwa.SignatureAlgorithm]crypto.Hash{
-		jwa.ES256: crypto.SHA256,
-		jwa.ES384: crypto.SHA384,
-		jwa.ES512: crypto.SHA512,
+		jwa.ES256:  crypto.SHA256,
+		jwa.ES384:  crypto.SHA384,
+		jwa.ES512:  crypto.SHA512,
+		jwa.ES256K: crypto.SHA256,
 	}
 
 	for alg, h := range algs {
diff --git a/jws/signer.go b/jws/signer.go
index 29ab1cb4..6065817f 100644
--- a/jws/signer.go
+++ b/jws/signer.go
@@ -37,7 +37,7 @@ func init() {
 		}(alg))
 	}
 
-	for _, alg := range []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512} {
+	for _, alg := range []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512, jwa.ES256K} {
 		RegisterSigner(alg, func(alg jwa.SignatureAlgorithm) SignerFactory {
 			return SignerFactoryFn(func() (Signer, error) {
 				return newECDSASigner(alg), nil
diff --git a/jws/verifier.go b/jws/verifier.go
index 79a2be56..3a4173b9 100644
--- a/jws/verifier.go
+++ b/jws/verifier.go
@@ -37,7 +37,7 @@ func init() {
 		}(alg))
 	}
 
-	for _, alg := range []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512} {
+	for _, alg := range []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512, jwa.ES256K} {
 		RegisterVerifier(alg, func(alg jwa.SignatureAlgorithm) VerifierFactory {
 			return VerifierFactoryFn(func() (Verifier, error) {
 				return newECDSAVerifier(alg), nil


Test patch:
diff --git a/internal/jwxtest/jwxtest.go b/internal/jwxtest/jwxtest.go
index cf503e83..a0efdb26 100644
--- a/internal/jwxtest/jwxtest.go
+++ b/internal/jwxtest/jwxtest.go
@@ -15,6 +15,7 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/decred/dcrd/dcrec/secp256k1/v3"
 	"github.com/lestrrat-go/jwx/jwa"
 	"github.com/lestrrat-go/jwx/jwe"
 	"github.com/lestrrat-go/jwx/jwk"
@@ -51,12 +52,26 @@ func GenerateRsaPublicJwk() (jwk.Key, error) {
 	return jwk.PublicKeyOf(key)
 }
 
-func GenerateEcdsaKey() (*ecdsa.PrivateKey, error) {
-	return ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
+func GenerateEcdsaKey(alg jwa.EllipticCurveAlgorithm) (*ecdsa.PrivateKey, error) {
+	var curve elliptic.Curve
+	switch alg {
+	case jwa.P256:
+		curve = elliptic.P256()
+	case jwa.P384:
+		curve = elliptic.P384()
+	case jwa.P521:
+		curve = elliptic.P521()
+	case jwa.Secp256k1:
+		curve = secp256k1.S256()
+	default:
+		return nil, errors.Errorf(`invalid curve algorithm %s`, alg)
+	}
+
+	return ecdsa.GenerateKey(curve, rand.Reader)
 }
 
 func GenerateEcdsaJwk() (jwk.Key, error) {
-	key, err := GenerateEcdsaKey()
+	key, err := GenerateEcdsaKey(jwa.P521)
 	if err != nil {
 		return nil, errors.Wrap(err, `failed to generate ECDSA private key`)
 	}
diff --git a/internal/keyconv/keyconv_test.go b/internal/keyconv/keyconv_test.go
index 524c254e..84bac4cd 100644
--- a/internal/keyconv/keyconv_test.go
+++ b/internal/keyconv/keyconv_test.go
@@ -7,6 +7,7 @@ import (
 
 	"github.com/lestrrat-go/jwx/internal/jwxtest"
 	"github.com/lestrrat-go/jwx/internal/keyconv"
+	"github.com/lestrrat-go/jwx/jwa"
 	"github.com/lestrrat-go/jwx/jwk"
 	"github.com/stretchr/testify/assert"
 )
@@ -124,7 +125,7 @@ func TestKeyconv(t *testing.T) {
 		})
 	})
 	t.Run("ECDSA", func(t *testing.T) {
-		key, err := jwxtest.GenerateEcdsaKey()
+		key, err := jwxtest.GenerateEcdsaKey(jwa.P521)
 		if !assert.NoError(t, err, `ecdsa.GenerateKey should succeed`) {
 			return
 		}
diff --git a/jwa/elliptic_gen_test.go b/jwa/elliptic_gen_test.go
index 923f2910..05734da2 100644
--- a/jwa/elliptic_gen_test.go
+++ b/jwa/elliptic_gen_test.go
@@ -191,6 +191,42 @@ func TestEllipticCurveAlgorithm(t *testing.T) {
 			return
 		}
 	})
+	t.Run(`accept jwa constant Secp256k1`, func(t *testing.T) {
+		t.Parallel()
+		var dst jwa.EllipticCurveAlgorithm
+		if !assert.NoError(t, dst.Accept(jwa.Secp256k1), `accept is successful`) {
+			return
+		}
+		if !assert.Equal(t, jwa.Secp256k1, dst, `accepted value should be equal to constant`) {
+			return
+		}
+	})
+	t.Run(`accept the string secp256k1`, func(t *testing.T) {
+		t.Parallel()
+		var dst jwa.EllipticCurveAlgorithm
+		if !assert.NoError(t, dst.Accept("secp256k1"), `accept is successful`) {
+			return
+		}
+		if !assert.Equal(t, jwa.Secp256k1, dst, `accepted value should be equal to constant`) {
+			return
+		}
+	})
+	t.Run(`accept fmt.Stringer for secp256k1`, func(t *testing.T) {
+		t.Parallel()
+		var dst jwa.EllipticCurveAlgorithm
+		if !assert.NoError(t, dst.Accept(stringer{src: "secp256k1"}), `accept is successful`) {
+			return
+		}
+		if !assert.Equal(t, jwa.Secp256k1, dst, `accepted value should be equal to constant`) {
+			return
+		}
+	})
+	t.Run(`stringification for secp256k1`, func(t *testing.T) {
+		t.Parallel()
+		if !assert.Equal(t, "secp256k1", jwa.Secp256k1.String(), `stringified value matches`) {
+			return
+		}
+	})
 	t.Run(`accept jwa constant X25519`, func(t *testing.T) {
 		t.Parallel()
 		var dst jwa.EllipticCurveAlgorithm
diff --git a/jwa/signature_gen_test.go b/jwa/signature_gen_test.go
index 9de6b78d..ed4f0cad 100644
--- a/jwa/signature_gen_test.go
+++ b/jwa/signature_gen_test.go
@@ -47,6 +47,42 @@ func TestSignatureAlgorithm(t *testing.T) {
 			return
 		}
 	})
+	t.Run(`accept jwa constant ES256K`, func(t *testing.T) {
+		t.Parallel()
+		var dst jwa.SignatureAlgorithm
+		if !assert.NoError(t, dst.Accept(jwa.ES256K), `accept is successful`) {
+			return
+		}
+		if !assert.Equal(t, jwa.ES256K, dst, `accepted value should be equal to constant`) {
+			return
+		}
+	})
+	t.Run(`accept the string ES256K`, func(t *testing.T) {
+		t.Parallel()
+		var dst jwa.SignatureAlgorithm
+		if !assert.NoError(t, dst.Accept("ES256K"), `accept is successful`) {
+			return
+		}
+		if !assert.Equal(t, jwa.ES256K, dst, `accepted value should be equal to constant`) {
+			return
+		}
+	})
+	t.Run(`accept fmt.Stringer for ES256K`, func(t *testing.T) {
+		t.Parallel()
+		var dst jwa.SignatureAlgorithm
+		if !assert.NoError(t, dst.Accept(stringer{src: "ES256K"}), `accept is successful`) {
+			return
+		}
+		if !assert.Equal(t, jwa.ES256K, dst, `accepted value should be equal to constant`) {
+			return
+		}
+	})
+	t.Run(`stringification for ES256K`, func(t *testing.T) {
+		t.Parallel()
+		if !assert.Equal(t, "ES256K", jwa.ES256K.String(), `stringified value matches`) {
+			return
+		}
+	})
 	t.Run(`accept jwa constant ES384`, func(t *testing.T) {
 		t.Parallel()
 		var dst jwa.SignatureAlgorithm
diff --git a/jwe/headers_test.go b/jwe/headers_test.go
index 4f4c90b9..e66da846 100644
--- a/jwe/headers_test.go
+++ b/jwe/headers_test.go
@@ -21,7 +21,7 @@ func TestHeaders(t *testing.T) {
 		"MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE5MDYyNjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vYdA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QSv4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9vUJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTuIYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwCW/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd",
 	}
 
-	rawKey, err := jwxtest.GenerateEcdsaKey()
+	rawKey, err := jwxtest.GenerateEcdsaKey(jwa.P521)
 	if !assert.NoError(t, err, `jwxtest.GenerateEcdsaKey should succeed`) {
 		return
 	}
diff --git a/jwk/jwk_test.go b/jwk/jwk_test.go
index 1e028dd2..b721c391 100644
--- a/jwk/jwk_test.go
+++ b/jwk/jwk_test.go
@@ -5,7 +5,6 @@ import (
 	"crypto"
 	"crypto/ecdsa"
 	"crypto/ed25519"
-	"crypto/elliptic"
 	"crypto/rsa"
 	"fmt"
 	"reflect"
@@ -782,7 +781,7 @@ func TestPublicKeyOf(t *testing.T) {
 		return
 	}
 
-	ecdsakey, err := jwxtest.GenerateEcdsaKey()
+	ecdsakey, err := jwxtest.GenerateEcdsaKey(jwa.P521)
 	if !assert.NoError(t, err, `generating raw ECDSA key should succeed`) {
 		return
 	}
@@ -1142,12 +1141,12 @@ func TestECDSA(t *testing.T) {
 		})
 	})
 	t.Run("Curve types", func(t *testing.T) {
-		crvs := []elliptic.Curve{elliptic.P256(), elliptic.P384(), elliptic.P521()}
+		crvs := []jwa.EllipticCurveAlgorithm{jwa.P256, jwa.P384, jwa.P521, jwa.Secp256k1}
 
 		for _, crv := range crvs {
 			crv := crv
-			t.Run(crv.Params().Name, func(t *testing.T) {
-				key, err := jwxtest.GenerateEcdsaKey()
+			t.Run(crv.String(), func(t *testing.T) {
+				key, err := jwxtest.GenerateEcdsaKey(crv)
 				if !assert.NoError(t, err, `jwxtest.GenerateEcdsaKey should succeed`) {
 					return
 				}
diff --git a/jws/jws_test.go b/jws/jws_test.go
index 3555b771..4b16e8d6 100644
--- a/jws/jws_test.go
+++ b/jws/jws_test.go
@@ -191,7 +191,7 @@ func TestRoundtrip(t *testing.T) {
 	})
 	t.Run("ECDSA", func(t *testing.T) {
 		t.Parallel()
-		key, err := jwxtest.GenerateEcdsaKey()
+		key, err := jwxtest.GenerateEcdsaKey(jwa.P521)
 		if !assert.NoError(t, err, "ECDSA key generated") {
 			return
 		}
@@ -209,6 +209,20 @@ func TestRoundtrip(t *testing.T) {
 			})
 		}
 	})
+	t.Run("ECDSA-256K", func(t *testing.T) {
+		t.Parallel()
+		key, err := jwxtest.GenerateEcdsaKey(jwa.Secp256k1)
+		if !assert.NoError(t, err, "ECDSA key generated") {
+			return
+		}
+		jwkKey, _ := jwk.New(key.PublicKey)
+		keys := map[string]interface{}{
+			"Verify(ecdsa.PublicKey)":  key.PublicKey,
+			"Verify(*ecdsa.PublicKey)": &key.PublicKey,
+			"Verify(jwk.Key)":          jwkKey,
+		}
+		testRoundtrip(t, payload, jwa.ES256K, key, keys)
+	})
 	t.Run("RSA", func(t *testing.T) {
 		t.Parallel()
 		key, err := jwxtest.GenerateRsaKey()
diff --git a/jws/signer_test.go b/jws/signer_test.go
index 72db41fd..61eb8cf1 100644
--- a/jws/signer_test.go
+++ b/jws/signer_test.go
@@ -69,7 +69,7 @@ func TestSignMulti(t *testing.T) {
 		return
 	}
 
-	dsakey, err := jwxtest.GenerateEcdsaKey()
+	dsakey, err := jwxtest.GenerateEcdsaKey(jwa.P521)
 	if !assert.NoError(t, err, "ECDSA key generated") {
 		return
 	}
diff --git a/jwt/jwt_test.go b/jwt/jwt_test.go
index ebac02b6..b10337c2 100644
--- a/jwt/jwt_test.go
+++ b/jwt/jwt_test.go
@@ -361,7 +361,7 @@ func TestGH52(t *testing.T) {
 	}
 
 	t.Parallel()
-	priv, err := jwxtest.GenerateEcdsaKey()
+	priv, err := jwxtest.GenerateEcdsaKey(jwa.P521)
 	if !assert.NoError(t, err) {
 		return
 	}
@@ -414,7 +414,7 @@ func TestUnmarshalJSON(t *testing.T) {
 
 func TestSignErrors(t *testing.T) {
 	t.Parallel()
-	priv, err := jwxtest.GenerateEcdsaKey()
+	priv, err := jwxtest.GenerateEcdsaKey(jwa.P521)
 	if !assert.NoError(t, err, `jwxtest.GenerateEcdsaKey should succeed`) {
 		return
 	}