Repository: lestrrat-go/jwx
Pull request #672 (2022-03-29 22:33:46)

[v1] Backport #671 to v1

codecov-commenter: # [Codecov](https://codecov.io/gh/lestrrat-go/jwx/pull/672?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go) Report
> :exclamation: No coverage uploaded for pull request base (`develop/v1@941bb51`). [Click here to learn what that means](https://docs.codecov.io/docs/error-reference?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go#section-missing-base-commit).
> The diff coverage is `n/a`.

```diff
@@              Coverage Diff              @@
##             develop/v1     #672   +/-   ##
=============================================
  Coverage              ?   72.82%           
=============================================
  Files                 ?       84           
  Lines                 ?     9572           
  Branches              ?        0           
=============================================
  Hits                  ?     6971           
  Misses                ?     1785           
  Partials              ?      816           
```



------

[Continue to review full report at Codecov](https://codecov.io/gh/lestrrat-go/jwx/pull/672?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/lestrrat-go/jwx/pull/672?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go). Last update [941bb51...351b718](https://codecov.io/gh/lestrrat-go/jwx/pull/672?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lestrrat-go).


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 59967e63..2984df76 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,11 +1,9 @@
 name: CI
 on:
-  push:
-    branches:
-      - main
   pull_request:
     branches:
-      - main
+      - v*
+      - develop/*
 
 jobs:
   build:
diff --git a/jwk/internal/cmd/genheader/objects.yml b/jwk/internal/cmd/genheader/objects.yml
index 778f2412..f07ed175 100644
--- a/jwk/internal/cmd/genheader/objects.yml
+++ b/jwk/internal/cmd/genheader/objects.yml
@@ -67,10 +67,8 @@ key_types:
             required: true
           - name: p
             type: "[]byte"
-            required: true
           - name: q
             type: "[]byte"
-            required: true
           - name: dp
             getter: DP
             exported_name: DP
diff --git a/jwk/rsa.go b/jwk/rsa.go
index 0c80e737..f8a365ed 100644
--- a/jwk/rsa.go
+++ b/jwk/rsa.go
@@ -4,6 +4,7 @@ import (
 	"crypto"
 	"crypto/rsa"
 	"encoding/binary"
+	"fmt"
 	"math/big"
 
 	"github.com/lestrrat-go/blackmagic"
@@ -22,21 +23,27 @@ func (k *rsaPrivateKey) FromRaw(rawKey *rsa.PrivateKey) error {
 	}
 	k.d = d
 
-	if len(rawKey.Primes) < 2 {
-		return errors.Errorf(`invalid number of primes in rsa.PrivateKey: need 2, got %d`, len(rawKey.Primes))
+	l := len(rawKey.Primes)
+
+	if l < 0 /* I know, I'm being paranoid */ || l > 2 {
+		return fmt.Errorf(`invalid number of primes in rsa.PrivateKey: need 1 or 2, got %d`, len(rawKey.Primes))
 	}
 
-	p, err := bigIntToBytes(rawKey.Primes[0])
-	if err != nil {
-		return errors.Wrap(err, `invalid rsa.PrivateKey`)
+	if l > 0 {
+		p, err := bigIntToBytes(rawKey.Primes[0])
+		if err != nil {
+			return fmt.Errorf(`invalid rsa.PrivateKey: %w`, err)
+		}
+		k.p = p
 	}
-	k.p = p
 
-	q, err := bigIntToBytes(rawKey.Primes[1])
-	if err != nil {
-		return errors.Wrap(err, `invalid rsa.PrivateKey`)
+	if l > 1 {
+		q, err := bigIntToBytes(rawKey.Primes[1])
+		if err != nil {
+			return fmt.Errorf(`invalid rsa.PrivateKey: %w`, err)
+		}
+		k.q = q
 	}
-	k.q = q
 
 	// dp, dq, qi are optional values
 	if v, err := bigIntToBytes(rawKey.Precomputed.Dp); err == nil {
diff --git a/jwk/rsa_gen.go b/jwk/rsa_gen.go
index 21de3bf1..383cb34c 100644
--- a/jwk/rsa_gen.go
+++ b/jwk/rsa_gen.go
@@ -1180,12 +1180,6 @@ LOOP:
 	if h.n == nil {
 		return errors.Errorf(`required field n is missing`)
 	}
-	if h.p == nil {
-		return errors.Errorf(`required field p is missing`)
-	}
-	if h.q == nil {
-		return errors.Errorf(`required field q is missing`)
-	}
 	return nil
 }
 


Test patch:
diff --git a/jwk/jwk_test.go b/jwk/jwk_test.go
index df0e6886..911fe449 100644
--- a/jwk/jwk_test.go
+++ b/jwk/jwk_test.go
@@ -23,6 +23,7 @@ import (
 	"github.com/lestrrat-go/jwx/internal/jose"
 	"github.com/lestrrat-go/jwx/internal/json"
 	"github.com/lestrrat-go/jwx/internal/jwxtest"
+	"github.com/lestrrat-go/jwx/jws"
 	"github.com/pkg/errors"
 
 	"github.com/lestrrat-go/jwx/internal/base64"
@@ -2007,3 +2008,57 @@ func TestGH567(t *testing.T) {
 		}
 	})
 }
+
+func TestGH664(t *testing.T) {
+	privkey, err := jwxtest.GenerateRsaKey()
+	if !assert.NoError(t, err, `jwxtext.GenerateRsaKey() should succeed`) {
+		return
+	}
+
+	privkey.Primes = append(privkey.Primes, &big.Int{})
+	// first, test a stupid case where Primes > 2
+	_, err = jwk.New(privkey)
+	if !assert.Error(t, err, `jwk.New should fail`) {
+		return
+	}
+
+	privkey.Primes = privkey.Primes[:2]
+
+	// nuke p and q, dp, dq, qi
+	for i := 0; i < 3; i++ {
+		i := i
+		t.Run(fmt.Sprintf("Check what happens when primes are reduced to %d", i), func(t *testing.T) {
+			privkey.Primes = privkey.Primes[:i]
+			privkey.Precomputed.Dp = nil
+			privkey.Precomputed.Dq = nil
+			privkey.Precomputed.Qinv = nil
+			privkey.Precomputed.CRTValues = nil
+
+			jwkPrivkey, err := jwk.New(privkey)
+			if !assert.NoError(t, err, `jwk.FromRaw should succeed`) {
+				return
+			}
+
+			buf, _ := json.MarshalIndent(jwkPrivkey, "", "  ")
+			parsed, err := jwk.ParseKey(buf)
+			if !assert.NoError(t, err, `jwk.ParseKey should succeed`) {
+				return
+			}
+
+			payload := []byte(`hello , world!`)
+			signed, err := jws.Sign(payload, jwa.RS256, parsed)
+			if !assert.NoError(t, err, `jws.Sign should succeed`) {
+				return
+			}
+
+			verified, err := jws.Verify(signed, jwa.RS256, privkey.PublicKey)
+			if !assert.NoError(t, err, `jws.Verify should succeed`) {
+				return
+			}
+
+			if !assert.Equal(t, payload, verified, `verified content should match`) {
+				return
+			}
+		})
+	}
+}