CVE-2023-50658: Insufficient Information (NVD-CWE-noinfo)
value: The jose2go component before 1.6.0 for Go allows attackers to cause a denial of service (CPU consumption) via a large p2c (aka PBES2 Count) value.
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
Published: 2024-02-29T01:42Z
Repository: https://github.com/dvsekhvalnov/jose2go
Language: Go
Files changed: jose_test.go, pbse2_hmac_aeskw.go
Commit date: 2023-11-23 20:26:26 +0300
Commit message: Adding min/max iteration count guards to pbes2-hmac-aeskw algorithm

Diff: diff --git a/jose_test.go b/jose_test.go
index 62cb6c6..2b67fd6 100644
--- a/jose_test.go
+++ b/jose_test.go
@@ -1632,10 +1632,44 @@ func (s *TestSuite) TestDecrypt_PBSE2_HS512_A256KW_A256CBC_HS512(c *C) {
 	//then
 	c.Assert(err, IsNil)
 	c.Assert(test, Equals, `{"exp":1392553211,"sub":"alice","nbf":1392552611,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"586dd129-a29f-49c8-9de7-454af1155e27","iat":1392552611}`)
 }
 
+func (s *TestSuite) TestDecrypt_PBSE2_HS512_A256KW_A256CBC_HS512_MinIterationViolation(c *C) {
+	//given
+	pbes2Hs512 := DeregisterJwa(PBES2_HS512_A256KW)
+	RegisterJwa(NewPbse2HmacAesKWAlg(256, 300000, 10000))
+	token := "eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwicDJjIjo4MTkyLCJwMnMiOiJCUlkxQ1M3VXNpaTZJNzhkIn0.ovjAL7yRnB_XdJbK8lAaUDRZ-CyVeio8f4pnqOt1FPj1PoQAdEX3S5x6DlzR8aqN_WR5LUwdqDSyUDYhSurnmq8VLfzd3AEe.YAjH6g_zekXJIlPN4Ooo5Q.tutaltxpeVyayXZ9pQovGXTWTf_GWWvtu25Jeg9jgoH0sUX9KCnL00A69e4GJR6EMxalmWsa45AItffbwjUBmwdyklC4ZbTgaovVRs-UwqsZFBO2fpEb7qLajjwra7o4OegzgXDD0jhrKrUusvRWGBvenvumb5euibUxmIfBUcVF1JbdfYxx7ztFeS-QKJpDkE00zyEkViq-QxfrMVl5p7LGmTz8hMrFL3LXLokypZSDgFBfsUzChJf3mlYzxiGaGUqhs7NksQJDoUYf6prPow.XwRVfVTTPogO74RnxZD_9Mse26fTSehna1pbWy4VHfY"
+
+	//when
+	test, headers, err := Decode(token, "top secret")
+	fmt.Printf("\np2c min iteration err= %v\n", err)
+
+	//then
+	RegisterJwa(pbes2Hs512)
+	c.Assert(err, NotNil)
+	c.Assert(test, Equals, "")
+	c.Assert(headers, IsNil)
+}
+
+func (s *TestSuite) TestDecrypt_PBSE2_HS512_A256KW_A256CBC_HS512_MaxIterationViolation(c *C) {
+	//given
+	pbes2Hs512 := DeregisterJwa(PBES2_HS512_A256KW)
+	RegisterJwa(NewPbse2HmacAesKWAlg(256, 8000, 0))
+	token := "eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwicDJjIjo4MTkyLCJwMnMiOiJCUlkxQ1M3VXNpaTZJNzhkIn0.ovjAL7yRnB_XdJbK8lAaUDRZ-CyVeio8f4pnqOt1FPj1PoQAdEX3S5x6DlzR8aqN_WR5LUwdqDSyUDYhSurnmq8VLfzd3AEe.YAjH6g_zekXJIlPN4Ooo5Q.tutaltxpeVyayXZ9pQovGXTWTf_GWWvtu25Jeg9jgoH0sUX9KCnL00A69e4GJR6EMxalmWsa45AItffbwjUBmwdyklC4ZbTgaovVRs-UwqsZFBO2fpEb7qLajjwra7o4OegzgXDD0jhrKrUusvRWGBvenvumb5euibUxmIfBUcVF1JbdfYxx7ztFeS-QKJpDkE00zyEkViq-QxfrMVl5p7LGmTz8hMrFL3LXLokypZSDgFBfsUzChJf3mlYzxiGaGUqhs7NksQJDoUYf6prPow.XwRVfVTTPogO74RnxZD_9Mse26fTSehna1pbWy4VHfY"
+
+	//when
+	test, headers, err := Decode(token, "top secret")
+	fmt.Printf("\np2c max iteration err= %v\n", err)
+
+	//then
+	RegisterJwa(pbes2Hs512)
+	c.Assert(err, NotNil)
+	c.Assert(test, Equals, "")
+	c.Assert(headers, IsNil)
+}
+
 func (s *TestSuite) TestEncrypt_PBSE2_HS256_A128KW_A128GCM(c *C) {
 	//given
 	payload := `{"hello": "world"}`
 
 	//when
diff --git a/pbse2_hmac_aeskw.go b/pbse2_hmac_aeskw.go
index baeaf9c..154c0f3 100644
--- a/pbse2_hmac_aeskw.go
+++ b/pbse2_hmac_aeskw.go
@@ -2,27 +2,41 @@ package jose
 
 import (
 	"crypto/sha256"
 	"crypto/sha512"
 	"errors"
+	"fmt"
 	"hash"
 
 	"github.com/dvsekhvalnov/jose2go/arrays"
 	"github.com/dvsekhvalnov/jose2go/base64url"
 	"github.com/dvsekhvalnov/jose2go/kdf"
 )
 
 func init() {
-	RegisterJwa(&Pbse2HmacAesKW{keySizeBits: 128, aesKW: &AesKW{keySizeBits: 128}})
-	RegisterJwa(&Pbse2HmacAesKW{keySizeBits: 192, aesKW: &AesKW{keySizeBits: 192}})
-	RegisterJwa(&Pbse2HmacAesKW{keySizeBits: 256, aesKW: &AesKW{keySizeBits: 256}})
+	RegisterJwa(NewPbse2HmacAesKWAlg(128, 310000, 0))
+	RegisterJwa(NewPbse2HmacAesKWAlg(192, 250000, 0))
+	RegisterJwa(NewPbse2HmacAesKWAlg(256, 120000, 0))
 }
 
 // PBSE2 with HMAC key management algorithm implementation
 type Pbse2HmacAesKW struct {
-	keySizeBits int
-	aesKW       JwaAlgorithm
+	keySizeBits   int
+	aesKW         JwaAlgorithm
+	maxIterations int64
+	minIterations int64
+}
+
+func NewPbse2HmacAesKWAlg(keySize int, maxIters int64, minIters int64) JwaAlgorithm {
+	switch keySize {
+	case 128:
+		return &Pbse2HmacAesKW{keySizeBits: 128, maxIterations: maxIters, minIterations: minIters, aesKW: &AesKW{keySizeBits: 128}}
+	case 192:
+		return &Pbse2HmacAesKW{keySizeBits: 192, maxIterations: maxIters, minIterations: minIters, aesKW: &AesKW{keySizeBits: 192}}
+	default:
+		return &Pbse2HmacAesKW{keySizeBits: 256, maxIterations: maxIters, minIterations: minIters, aesKW: &AesKW{keySizeBits: 256}}
+	}
 }
 
 func (alg *Pbse2HmacAesKW) Name() string {
 	switch alg.keySizeBits {
 	case 128:
@@ -44,10 +58,25 @@ func (alg *Pbse2HmacAesKW) WrapNewKey(cekSizeBits int, key interface{}, header m
 
 		if saltInput, err = arrays.Random(12); err != nil {
 			return nil, nil, err
 		}
 
+		// use user provided iteration counts if any
+		if p2c, ok := header["p2c"].(int); ok {
+			iterationCount = p2c
+		}
+
+		if int64(iterationCount) > alg.maxIterations {
+			return nil, nil, errors.New(
+				fmt.Sprintf("Pbse2HmacAesKW.Unwrap(): expected 'p2c' to be less than %v but got %v", alg.maxIterations, iterationCount))
+		}
+
+		if int64(iterationCount) < alg.minIterations {
+			return nil, nil, errors.New(
+				fmt.Sprintf("Pbse2HmacAesKW.Unwrap(): expected 'p2c' to be higher than %v but got %v", alg.minIterations, iterationCount))
+		}
+
 		header["p2c"] = iterationCount
 		header["p2s"] = base64url.Encode(saltInput)
 
 		salt := arrays.Concat(algId, []byte{0}, saltInput)
 
@@ -67,12 +96,22 @@ func (alg *Pbse2HmacAesKW) Unwrap(encryptedCek []byte, key interface{}, cekSizeB
 
 		if p2c, ok = header["p2c"].(float64); !ok {
 			return nil, errors.New("Pbse2HmacAesKW.Unwrap(): expected 'p2c' param in JWT header, but was not found.")
 		}
 
+		if int64(p2c) > alg.maxIterations {
+			return nil, errors.New(
+				fmt.Sprintf("Pbse2HmacAesKW.Unwrap(): expected 'p2c' to be less than %v but got %v", alg.maxIterations, p2c))
+		}
+
+		if int64(p2c) < alg.minIterations {
+			return nil, errors.New(
+				fmt.Sprintf("Pbse2HmacAesKW.Unwrap(): expected 'p2c' to be higher than %v but got %v", alg.minIterations, p2c))
+		}
+
 		if p2s, ok = header["p2s"].(string); !ok {
-			return nil, errors.New("Pbse2HmacAesKW.Unwrap(): expected 'p2s' param in JWT header, but was not found.")
+			return nil, errors.New("Pbse2HmacAesKW.Unwrap(): expected 'p2s' param in JWT header, but was not found")
 		}
 
 		var saltInput []byte
 
 		algId := []byte(header["alg"].(string))

Vulnerable code: 	RegisterJwa(&Pbse2HmacAesKW{keySizeBits: 128, aesKW: &AesKW{keySizeBits: 128}})
	RegisterJwa(&Pbse2HmacAesKW{keySizeBits: 192, aesKW: &AesKW{keySizeBits: 192}})
	RegisterJwa(&Pbse2HmacAesKW{keySizeBits: 256, aesKW: &AesKW{keySizeBits: 256}})
	keySizeBits int
	aesKW       JwaAlgorithm
			return nil, errors.New("Pbse2HmacAesKW.Unwrap(): expected 'p2s' param in JWT header, but was not found.")
Fixed code: func (s *TestSuite) TestDecrypt_PBSE2_HS512_A256KW_A256CBC_HS512_MinIterationViolation(c *C) {
	//given
	pbes2Hs512 := DeregisterJwa(PBES2_HS512_A256KW)
	RegisterJwa(NewPbse2HmacAesKWAlg(256, 300000, 10000))
	token := "eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwicDJjIjo4MTkyLCJwMnMiOiJCUlkxQ1M3VXNpaTZJNzhkIn0.ovjAL7yRnB_XdJbK8lAaUDRZ-CyVeio8f4pnqOt1FPj1PoQAdEX3S5x6DlzR8aqN_WR5LUwdqDSyUDYhSurnmq8VLfzd3AEe.YAjH6g_zekXJIlPN4Ooo5Q.tutaltxpeVyayXZ9pQovGXTWTf_GWWvtu25Jeg9jgoH0sUX9KCnL00A69e4GJR6EMxalmWsa45AItffbwjUBmwdyklC4ZbTgaovVRs-UwqsZFBO2fpEb7qLajjwra7o4OegzgXDD0jhrKrUusvRWGBvenvumb5euibUxmIfBUcVF1JbdfYxx7ztFeS-QKJpDkE00zyEkViq-QxfrMVl5p7LGmTz8hMrFL3LXLokypZSDgFBfsUzChJf3mlYzxiGaGUqhs7NksQJDoUYf6prPow.XwRVfVTTPogO74RnxZD_9Mse26fTSehna1pbWy4VHfY"

	//when
	test, headers, err := Decode(token, "top secret")
	fmt.Printf("\np2c min iteration err= %v\n", err)

	//then
	RegisterJwa(pbes2Hs512)
	c.Assert(err, NotNil)
	c.Assert(test, Equals, "")
	c.Assert(headers, IsNil)
}

func (s *TestSuite) TestDecrypt_PBSE2_HS512_A256KW_A256CBC_HS512_MaxIterationViolation(c *C) {
	//given
	pbes2Hs512 := DeregisterJwa(PBES2_HS512_A256KW)
	RegisterJwa(NewPbse2HmacAesKWAlg(256, 8000, 0))
	token := "eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwicDJjIjo4MTkyLCJwMnMiOiJCUlkxQ1M3VXNpaTZJNzhkIn0.ovjAL7yRnB_XdJbK8lAaUDRZ-CyVeio8f4pnqOt1FPj1PoQAdEX3S5x6DlzR8aqN_WR5LUwdqDSyUDYhSurnmq8VLfzd3AEe.YAjH6g_zekXJIlPN4Ooo5Q.tutaltxpeVyayXZ9pQovGXTWTf_GWWvtu25Jeg9jgoH0sUX9KCnL00A69e4GJR6EMxalmWsa45AItffbwjUBmwdyklC4ZbTgaovVRs-UwqsZFBO2fpEb7qLajjwra7o4OegzgXDD0jhrKrUusvRWGBvenvumb5euibUxmIfBUcVF1JbdfYxx7ztFeS-QKJpDkE00zyEkViq-QxfrMVl5p7LGmTz8hMrFL3LXLokypZSDgFBfsUzChJf3mlYzxiGaGUqhs7NksQJDoUYf6prPow.XwRVfVTTPogO74RnxZD_9Mse26fTSehna1pbWy4VHfY"

	//when
	test, headers, err := Decode(token, "top secret")
	fmt.Printf("\np2c max iteration err= %v\n", err)

	//then
	RegisterJwa(pbes2Hs512)
	c.Assert(err, NotNil)
	c.Assert(test, Equals, "")
	c.Assert(headers, IsNil)
}

	"fmt"
	RegisterJwa(NewPbse2HmacAesKWAlg(128, 310000, 0))
	RegisterJwa(NewPbse2HmacAesKWAlg(192, 250000, 0))
	RegisterJwa(NewPbse2HmacAesKWAlg(256, 120000, 0))
	keySizeBits   int
	aesKW         JwaAlgorithm
	maxIterations int64
	minIterations int64
}

func NewPbse2HmacAesKWAlg(keySize int, maxIters int64, minIters int64) JwaAlgorithm {
	switch keySize {
	case 128:
		return &Pbse2HmacAesKW{keySizeBits: 128, maxIterations: maxIters, minIterations: minIters, aesKW: &AesKW{keySizeBits: 128}}
	case 192:
		return &Pbse2HmacAesKW{keySizeBits: 192, maxIterations: maxIters, minIterations: minIters, aesKW: &AesKW{keySizeBits: 192}}
	default:
		return &Pbse2HmacAesKW{keySizeBits: 256, maxIterations: maxIters, minIterations: minIters, aesKW: &AesKW{keySizeBits: 256}}
	}
		// use user provided iteration counts if any
		if p2c, ok := header["p2c"].(int); ok {
			iterationCount = p2c
		}

		if int64(iterationCount) > alg.maxIterations {
			return nil, nil, errors.New(
				fmt.Sprintf("Pbse2HmacAesKW.Unwrap(): expected 'p2c' to be less than %v but got %v", alg.maxIterations, iterationCount))
		}

		if int64(iterationCount) < alg.minIterations {
			return nil, nil, errors.New(
				fmt.Sprintf("Pbse2HmacAesKW.Unwrap(): expected 'p2c' to be higher than %v but got %v", alg.minIterations, iterationCount))
		}

		if int64(p2c) > alg.maxIterations {
			return nil, errors.New(
				fmt.Sprintf("Pbse2HmacAesKW.Unwrap(): expected 'p2c' to be less than %v but got %v", alg.maxIterations, p2c))
		}

		if int64(p2c) < alg.minIterations {
			return nil, errors.New(
				fmt.Sprintf("Pbse2HmacAesKW.Unwrap(): expected 'p2c' to be higher than %v but got %v", alg.minIterations, p2c))
		}

			return nil, errors.New("Pbse2HmacAesKW.Unwrap(): expected 'p2s' param in JWT header, but was not found")