Repository: lestrrat-go/jwx
Pull request #993 (2023-10-06 07:49:26)

Bump golang.org/x/crypto from 0.13.0 to 0.14.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.13.0 to 0.14.0.
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/golang/crypto/commit/e3cc52e598e302f8c613a645bb7231264d8ec995"><code>e3cc52e</code></a> go.mod: update golang.org/x dependencies</li>
<li><a href="https://github.com/golang/crypto/commit/833695f0a57b3037385dc9c0073bc88773cae6f3"><code>833695f</code></a> ssh: add server side support for <a href="mailto:ping@openssh.com">ping@openssh.com</a> protocol extension</li>
<li><a href="https://github.com/golang/crypto/commit/ec07f4e35b9ef2fcfd790dd9437ef46bd64b3a4b"><code>ec07f4e</code></a> chacha20: drop Go 1.10 compatibility for arm64</li>
<li><a href="https://github.com/golang/crypto/commit/b665ba6f6f9abc2ab124f8ec2cb44de0e44ce077"><code>b665ba6</code></a> all: use crypto/ed25519 instead of golang.org/x/crypto/ed25519</li>
<li><a href="https://github.com/golang/crypto/commit/a1aeb9b34eb6b8f469bbd66b9cd1c9d905cb3714"><code>a1aeb9b</code></a> ssh: add test cases for compatibility with old (buggy) clients</li>
<li><a href="https://github.com/golang/crypto/commit/28c53ff63c09fc7df7793600caa30989bc69e194"><code>28c53ff</code></a> ssh: add MultiAlgorithmSigner</li>
<li><a href="https://github.com/golang/crypto/commit/3f0842a46434ea6f56bf6e684c2b83d90e9cff07"><code>3f0842a</code></a> sha3: have ShakeHash extend hash.Hash</li>
<li><a href="https://github.com/golang/crypto/commit/e90f1e17ee2ffe351a8295e8ae8b66afda2969c6"><code>e90f1e1</code></a> cryptobyte: add uint48 methods</li>
<li><a href="https://github.com/golang/crypto/commit/d359caa4a39d59a440003b37a6cc7ace3871fd4a"><code>d359caa</code></a> ssh: support for marshaling keys using the OpenSSH format</li>
<li><a href="https://github.com/golang/crypto/commit/c5370d2cc696bb18a6ddc151cee09673f06e8497"><code>c5370d2</code></a> ssh: check the declared public key algo against decoded one</li>
<li>See full diff in <a href="https://github.com/golang/crypto/compare/v0.13.0...v0.14.0">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=golang.org/x/crypto&package-manager=go_modules&previous-version=0.13.0&new-version=0.14.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/lestrrat-go/jwx/pull/993?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go) Report
All modified lines are covered by tests :white_check_mark:
> Comparison is base [(`de6bd31`)](https://app.codecov.io/gh/lestrrat-go/jwx/commit/de6bd314bf7ed2525a216fee56f0284d90a436fb?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go) 71.90% compared to head [(`ebc7361`)](https://app.codecov.io/gh/lestrrat-go/jwx/pull/993?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go) 71.73%.

<details><summary>Additional details and impacted files</summary>


```diff
@@              Coverage Diff               @@
##           develop/v2     #993      +/-   ##
==============================================
- Coverage       71.90%   71.73%   -0.18%     
==============================================
  Files              93       89       -4     
  Lines           13597    13573      -24     
==============================================
- Hits             9777     9736      -41     
- Misses           3003     3020      +17     
  Partials          817      817              
```


[see 6 files with indirect coverage changes](https://app.codecov.io/gh/lestrrat-go/jwx/pull/993/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go)


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/lestrrat-go/jwx/pull/993?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go).


Patch:
diff --git a/deps.bzl b/deps.bzl
index c9edbc01..b33ddb72 100644
--- a/deps.bzl
+++ b/deps.bzl
@@ -122,8 +122,8 @@ def go_dependencies():
         name = "org_golang_x_crypto",
         build_file_proto_mode = "disable_global",
         importpath = "golang.org/x/crypto",
-        sum = "h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=",
-        version = "v0.13.0",
+        sum = "h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=",
+        version = "v0.14.0",
     )
     go_repository(
         name = "org_golang_x_mod",
@@ -152,15 +152,15 @@ def go_dependencies():
         name = "org_golang_x_sys",
         build_file_proto_mode = "disable_global",
         importpath = "golang.org/x/sys",
-        sum = "h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=",
-        version = "v0.12.0",
+        sum = "h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=",
+        version = "v0.13.0",
     )
     go_repository(
         name = "org_golang_x_term",
         build_file_proto_mode = "disable_global",
         importpath = "golang.org/x/term",
-        sum = "h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU=",
-        version = "v0.12.0",
+        sum = "h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=",
+        version = "v0.13.0",
     )
 
     go_repository(
diff --git a/examples/go.sum b/examples/go.sum
index 66519f3c..015a0662 100644
--- a/examples/go.sum
+++ b/examples/go.sum
@@ -36,8 +36,8 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
-golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
-golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
+golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
+golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -59,14 +59,14 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
-golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
+golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
-golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
+golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
diff --git a/go.mod b/go.mod
index bfc8b4eb..3ec01f0b 100644
--- a/go.mod
+++ b/go.mod
@@ -11,5 +11,5 @@ require (
 	github.com/lestrrat-go/option v1.0.1
 	github.com/segmentio/asm v1.2.0
 	github.com/stretchr/testify v1.8.4
-	golang.org/x/crypto v0.13.0
+	golang.org/x/crypto v0.14.0
 )
diff --git a/go.sum b/go.sum
index e3e3d5e7..de5d6be7 100644
--- a/go.sum
+++ b/go.sum
@@ -32,8 +32,8 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
-golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
+golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
+golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -52,13 +52,13 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
-golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
+golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
-golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
+golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
diff --git a/jwk/ecdsa_gen.go b/jwk/ecdsa_gen.go
index 95a29951..537ee24c 100644
--- a/jwk/ecdsa_gen.go
+++ b/jwk/ecdsa_gen.go
@@ -65,6 +65,10 @@ func (h ecdsaPublicKey) KeyType() jwa.KeyType {
 	return jwa.EC
 }
 
+func (h ecdsaPublicKey) IsPrivate() bool {
+	return false
+}
+
 func (h *ecdsaPublicKey) Algorithm() jwa.KeyAlgorithm {
 	if h.algorithm != nil {
 		return *(h.algorithm)
@@ -629,6 +633,10 @@ func (h ecdsaPrivateKey) KeyType() jwa.KeyType {
 	return jwa.EC
 }
 
+func (h ecdsaPrivateKey) IsPrivate() bool {
+	return true
+}
+
 func (h *ecdsaPrivateKey) Algorithm() jwa.KeyAlgorithm {
 	if h.algorithm != nil {
 		return *(h.algorithm)
diff --git a/jwk/interface_gen.go b/jwk/interface_gen.go
index 6e4e79a0..4a4053f3 100644
--- a/jwk/interface_gen.go
+++ b/jwk/interface_gen.go
@@ -22,6 +22,12 @@ const (
 	X509CertThumbprintS256Key = "x5t#S256"
 )
 
+// AsymmetricKey is able to indicate if it's a public
+// or private key.
+type AsymmetricKey interface {
+	IsPrivate() bool
+}
+
 // Key defines the minimal interface for each of the
 // key types. Their use and implementation differ significantly
 // between each key types, so you should use type assertions
diff --git a/jwk/jwk.go b/jwk/jwk.go
index d46e3e04..0a431809 100644
--- a/jwk/jwk.go
+++ b/jwk/jwk.go
@@ -744,3 +744,13 @@ func Equal(k1, k2 Key) bool {
 
 	return bytes.Equal(tp1, tp2)
 }
+
+// IsPrivateKey tells whether the supplied key is a private or public key.  An
+// error is raised if the supplied key is not an AsymmetricKey.
+func IsPrivateKey(k Key) (bool, error) {
+	asymmetric, ok := k.(AsymmetricKey)
+	if ok {
+		return asymmetric.IsPrivate(), nil
+	}
+	return false, fmt.Errorf("%T is not an asymmetric key", k)
+}
diff --git a/jwk/okp_gen.go b/jwk/okp_gen.go
index 832dc912..72cdf57a 100644
--- a/jwk/okp_gen.go
+++ b/jwk/okp_gen.go
@@ -61,6 +61,10 @@ func (h okpPublicKey) KeyType() jwa.KeyType {
 	return jwa.OKP
 }
 
+func (h okpPublicKey) IsPrivate() bool {
+	return false
+}
+
 func (h *okpPublicKey) Algorithm() jwa.KeyAlgorithm {
 	if h.algorithm != nil {
 		return *(h.algorithm)
@@ -595,6 +599,10 @@ func (h okpPrivateKey) KeyType() jwa.KeyType {
 	return jwa.OKP
 }
 
+func (h okpPrivateKey) IsPrivate() bool {
+	return true
+}
+
 func (h *okpPrivateKey) Algorithm() jwa.KeyAlgorithm {
 	if h.algorithm != nil {
 		return *(h.algorithm)
diff --git a/jwk/rsa_gen.go b/jwk/rsa_gen.go
index 65962429..633b3010 100644
--- a/jwk/rsa_gen.go
+++ b/jwk/rsa_gen.go
@@ -67,6 +67,10 @@ func (h rsaPublicKey) KeyType() jwa.KeyType {
 	return jwa.RSA
 }
 
+func (h rsaPublicKey) IsPrivate() bool {
+	return false
+}
+
 func (h *rsaPublicKey) Algorithm() jwa.KeyAlgorithm {
 	if h.algorithm != nil {
 		return *(h.algorithm)
@@ -606,6 +610,10 @@ func (h rsaPrivateKey) KeyType() jwa.KeyType {
 	return jwa.RSA
 }
 
+func (h rsaPrivateKey) IsPrivate() bool {
+	return true
+}
+
 func (h *rsaPrivateKey) Algorithm() jwa.KeyAlgorithm {
 	if h.algorithm != nil {
 		return *(h.algorithm)
diff --git a/tools/cmd/genjwk/main.go b/tools/cmd/genjwk/main.go
index ab066f9c..c92e4cc0 100644
--- a/tools/cmd/genjwk/main.go
+++ b/tools/cmd/genjwk/main.go
@@ -168,7 +168,8 @@ func generateObject(o *codegen.Output, kt *KeyType, obj *codegen.Object) error {
 	if v := obj.String(`interface`); v != "" {
 		ifName = v
 	}
-	structName := strings.ToLower(kt.Prefix) + obj.Name(true)
+	objName := obj.Name(true)
+	structName := strings.ToLower(kt.Prefix) + objName
 	if v := obj.String(`struct_name`); v != "" {
 		structName = v
 	}
@@ -210,6 +211,12 @@ func generateObject(o *codegen.Output, kt *KeyType, obj *codegen.Object) error {
 	o.L("return %s", kt.KeyType)
 	o.L("}")
 
+	if objName == "PublicKey" || objName == "PrivateKey" {
+		o.LL("func (h %s) IsPrivate() bool {", structName)
+		o.L("return %s", fmt.Sprint(objName == "PrivateKey"))
+		o.L("}")
+	}
+
 	for _, f := range obj.Fields() {
 		o.LL("func (h *%s) %s() ", structName, f.GetterMethod(true))
 		if v := f.String(`getter_return_value`); v != "" {
@@ -619,6 +626,12 @@ func generateGenericHeaders(fields codegen.FieldList) error {
 	}
 	o.L(")") // end const
 
+	o.LL("// AsymmetricKey is able to indicate if it's a public ")
+	o.L("// or private key.")
+	o.L("type AsymmetricKey interface {")
+	o.L("IsPrivate() bool")
+	o.L("}")
+
 	o.LL("// Key defines the minimal interface for each of the")
 	o.L("// key types. Their use and implementation differ significantly")
 	o.L("// between each key types, so you should use type assertions")
diff --git a/tools/cmd/genjwk/objects.yml b/tools/cmd/genjwk/objects.yml
index 765d0b06..dfeb8047 100644
--- a/tools/cmd/genjwk/objects.yml
+++ b/tools/cmd/genjwk/objects.yml
@@ -89,7 +89,7 @@ key_types:
   - filename: ecdsa_gen.go
     prefix: ECDSA
     key_type: jwa.EC
-    objects: 
+    objects:
       - name: publicKey
         raw_key_type: "*ecdsa.PublicKey"
         fields:


Test patch:
diff --git a/jwk/jwk_test.go b/jwk/jwk_test.go
index 896b29f0..4cca0603 100644
--- a/jwk/jwk_test.go
+++ b/jwk/jwk_test.go
@@ -292,6 +292,18 @@ func VerifyKey(t *testing.T, def map[string]keyDef) {
 			return
 		}
 	})
+	t.Run("IsPrivate", func(t *testing.T) {
+		_, err := jwk.IsPrivateKey(key)
+		if _, ok := key.(jwk.SymmetricKey); ok {
+			if !assert.Error(t, err, `jwk.IsPrivateKey should fail`) {
+				return
+			}
+		} else {
+			if !assert.NoError(t, err, `jwk.IsPrivateKey should succeed`) {
+				return
+			}
+		}
+	})
 	t.Run("Set/Remove", func(t *testing.T) {
 		ctx := context.TODO()
 
@@ -395,27 +407,44 @@ func TestParse(t *testing.T) {
 					return
 				}
 
+				isPrivate, err := jwk.IsPrivateKey(key)
+				if !assert.NoError(t, err, "jwk.IsPrivateKey(%T) sould succeed", key) {
+					return
+				}
+
 				var crawkey interface{}
 				switch k := key.(type) {
 				case jwk.RSAPrivateKey:
+					if !assert.True(t, isPrivate, `jwk.IsPrivateKey(&rsa.PrivateKey) should be true`) {
+						return
+					}
 					var rawkey rsa.PrivateKey
 					if !assert.NoError(t, key.Raw(&rawkey), `key.Raw(&rsa.PrivateKey) should succeed`) {
 						return
 					}
 					crawkey = &rawkey
 				case jwk.RSAPublicKey:
+					if !assert.False(t, isPrivate, `jwk.IsPrivateKey(&rsa.PublicKey) should be false`) {
+						return
+					}
 					var rawkey rsa.PublicKey
 					if !assert.NoError(t, key.Raw(&rawkey), `key.Raw(&rsa.PublicKey) should succeed`) {
 						return
 					}
 					crawkey = &rawkey
 				case jwk.ECDSAPrivateKey:
+					if !assert.True(t, isPrivate, `jwk.IsPrivateKey(&ecdsa.PrivateKey) should be true`) {
+						return
+					}
 					var rawkey ecdsa.PrivateKey
 					if !assert.NoError(t, key.Raw(&rawkey), `key.Raw(&ecdsa.PrivateKey) should succeed`) {
 						return
 					}
 					crawkey = &rawkey
 				case jwk.OKPPrivateKey:
+					if !assert.True(t, isPrivate, `jwk.IsPrivateKey(&ed25519.PrivateKey) should be true`) {
+						return
+					}
 					switch k.Crv() {
 					case jwa.Ed25519:
 						var rawkey ed25519.PrivateKey
@@ -436,6 +465,9 @@ func TestParse(t *testing.T) {
 				// key, since it's a subset of the
 				// private key variant.
 				case jwk.OKPPublicKey:
+					if !assert.False(t, isPrivate, `jwk.IsPrivateKey(&ed25519.PublicKey) should be false`) {
+						return
+					}
 					switch k.Crv() {
 					case jwa.Ed25519:
 						var rawkey ed25519.PublicKey