From c5d6a57d4843d9cc91403187d16eea0a70f64db4 Mon Sep 17 00:00:00 2001 From: DV Date: Mon, 20 Nov 2023 16:34:44 +0300 Subject: [PATCH 01/12] Adding set of 'Deregister' methods to remove specific algs runtime --- jose.go | 38 ++++++++++++++++++++++++++++- jose_test.go | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 103 insertions(+), 3 deletions(-) diff --git a/jose.go b/jose.go index 1f1c19e..9bc6482 100644 --- a/jose.go +++ b/jose.go @@ -1,4 +1,4 @@ -//Package jose provides high level functions for producing (signing, encrypting and +// Package jose provides high level functions for producing (signing, encrypting and // compressing) or consuming (decoding) Json Web Tokens using Java Object Signing and Encryption spec package jose @@ -79,6 +79,42 @@ func RegisterJwc(alg JwcAlgorithm) { jwcCompressors[alg.Name()] = alg } +// DeregisterJwa deregister existing key management algorithm +func DeregisterJwa(alg string) JwaAlgorithm { + jwa := jwaAlgorithms[alg] + + delete(jwaAlgorithms, alg) + + return jwa +} + +// DeregisterJws deregister existing signing algorithm +func DeregisterJws(alg string) JwsAlgorithm { + jws := jwsHashers[alg] + + delete(jwsHashers, alg) + + return jws +} + +// DeregisterJws deregister existing encryption algorithm +func DeregisterJwe(alg string) JweEncryption { + jwe := jweEncryptors[alg] + + delete(jweEncryptors, alg) + + return jwe +} + +// DeregisterJwc deregister existing compression algorithm +func DeregisterJwc(alg string) JwcAlgorithm { + jwc := jwcCompressors[alg] + + delete(jwcCompressors, alg) + + return jwc +} + // JweEncryption is a contract for implementing encryption algorithm type JweEncryption interface { Encrypt(aad, plainText, cek []byte) (iv, cipherText, authTag []byte, err error) diff --git a/jose_test.go b/jose_test.go index 42b2054..62cb6c6 100644 --- a/jose_test.go +++ b/jose_test.go @@ -9,7 +9,7 @@ import ( "testing" "github.com/dvsekhvalnov/jose2go/keys/ecc" - "github.com/dvsekhvalnov/jose2go/keys/rsa" + Rsa "github.com/dvsekhvalnov/jose2go/keys/rsa" . "gopkg.in/check.v1" ) @@ -2435,7 +2435,71 @@ func (s *TestSuite) TestEncryptBytes_RSA_OAEP_256_A128GCM(c *C) { c.Assert(t, DeepEquals, payload) } -//test utils +func (s *TestSuite) TestDeregisterJwa(c *C) { + //given + alg := DeregisterJwa(PBES2_HS256_A128KW) + token := "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwicDJjIjo4MTkyLCJwMnMiOiJiMFlFVmxMemtaNW9UUjBMIn0.dhPAhJ9kmaEbP-02VtEoPOF2QSEYM5085V6zYt1U1qIlVNRcHTGDgQ.4QAAq0dVQT41dQKDG7dhRA.H9MgJmesbU1ow6GCa0lEMwv8A_sHvgaWKkaMcdoj_z6O8LaMSgquxA-G85R_5hEILnHUnFllNJ48oJY7VmAJw0BQW73dMnn58u161S6Ftq7Mjxxq7bcksWvFTVtG5RsqqYSol5BZz5xm8Fcj-y5BMYMvrsCyQhYdeGEHkAvwzRdvZ8pGMsU2XPzl6GqxGjjuRh2vApAeNrj6MwKuD-k6AR0MH46EiNkVCmMkd2w8CNAXjJe9z97zky93xbxlOLozaC3NBRO2Q4bmdGdRg5y4Ew.xNqRi0ouQd7uo5UrPraedg" + + //when + test, _, err := Decode(token, shaKey) + + fmt.Printf("\nunknown 'alg' header err= %v\n", err) + + //then + RegisterJwa(alg) + c.Assert(err, NotNil) + c.Assert(test, Equals, "") +} + +func (s *TestSuite) TestDeregisterJwe(c *C) { + //given + alg := DeregisterJwe(A128CBC_HS256) + token := "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..3lClLoerWhxIc811QXDLbg.iFd5MNk2eWDlW3hbq7vTFLPJlC0Od_MSyWGakEn5kfYbbPk7BM_SxUMptwcvDnZ5uBKwwPAYOsHIm5IjZ79LKZul9ZnOtJONRvxWLeS9WZiX4CghOLZL7dLypKn-mB22xsmSUbtizMuNSdgJwUCxEmms7vYOpL0Che-0_YrOu3NmBCLBiZzdWVtSSvYw6Ltzbch4OAaX2ye_IIemJoU1VnrdW0y-AjPgnAUA-GY7CAKJ70leS1LyjTW8H_ecB4sDCkLpxNOUsWZs3DN0vxxSQw.bxrZkcOeBgFAo3t0585ZdQ" + + //when + test, _, err := Decode(token, shaKey) + + fmt.Printf("\nunknown 'enc' header err= %v\n", err) + + //then + RegisterJwe(alg) + c.Assert(err, NotNil) + c.Assert(test, Equals, "") +} + +func (s *TestSuite) TestDeregisterJws(c *C) { + //given + alg := DeregisterJws(HS256) + token := "eyJhbGciOiJIUzI1NiIsImN0eSI6InRleHRcL3BsYWluIn0.eyJoZWxsbyI6ICJ3b3JsZCJ9.chIoYWrQMA8XL5nFz6oLDJyvgHk2KA4BrFGrKymjC8E" + + //when + test, _, err := Decode(token, shaKey) + + fmt.Printf("\nunknown 'alg' header err= %v\n", err) + + //then + RegisterJws(alg) + c.Assert(err, NotNil) + c.Assert(test, Equals, "") +} + +func (s *TestSuite) TestDeregisterJwc(c *C) { + //given + alg := DeregisterJwc(DEF) + token := "eyJhbGciOiJSU0EtT0FFUCIsInppcCI6IkRFRiIsImVuYyI6IkExMjhDQkMtSFMyNTYifQ.nXSS9jDwE0dXkcGI7UquZBhn2nsB2P8u-YSWEuTAgEeuV54qNU4SlE76bToI1z4LUuABHmZOv9S24xkF45b7Mrap_Fu4JXH8euXrQgKQb9o_HL5FvE8m4zk5Ow13MKGPvHvWKOaNEBFriwYIfPi6QBYrpuqn0BaANc_aMyInV0Fn7e8EAgVmvoagmy7Hxic2sPUeLEIlRCDSGa82mpiGusjo7VMJxymkhnMdKufpGPh4wod7pvgb-jDWasUHpsUkHqSKZxlrDQxcy1-Pu1G37TAnImlWPa9NU7500IXc-W07IJccXhR3qhA5QaIyBbmHY0j1Dn3808oSFOYSF85A9w.uwbZhK-8iNzcjvKRb1a2Ig.jxj1GfH9Ndu1y0b7NRz_yfmjrvX2rXQczyK9ZJGWTWfeNPGR_PZdJmddiam15Qtz7R-pzIeyR4_qQoMzOISkq6fDEvEWVZdHnnTUHQzCoGX1dZoG9jXEwfAk2G1vXYT2vynEQZ72xk0V_OBtKhpIAUEFsXwCUeLAAgjFNY4OGWZl_Kmv9RTGhnePZfVbrbwg.WuV64jlV03OZm99qHMP9wQ" + + //when + test, _, err := Decode(token, PrivKey()) + + fmt.Printf("\nunknown 'zip' header err= %v\n", err) + + //then + RegisterJwc(alg) + c.Assert(err, NotNil) + c.Assert(test, Equals, "") +} + +// test utils func PubKey() *rsa.PublicKey { key, _ := Rsa.ReadPublic([]byte(pubKey)) return key From a4584e9dd7128608fedbc67892eba9697f0d5317 Mon Sep 17 00:00:00 2001 From: DV Date: Thu, 23 Nov 2023 20:26:26 +0300 Subject: [PATCH 02/12] Adding min/max iteration count guards to pbes2-hmac-aeskw algorithm --- jose_test.go | 34 ++++++++++++++++++++++++++++++ pbse2_hmac_aeskw.go | 51 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 79 insertions(+), 6 deletions(-) diff --git a/jose_test.go b/jose_test.go index 62cb6c6..2b67fd6 100644 --- a/jose_test.go +++ b/jose_test.go @@ -1634,6 +1634,40 @@ func (s *TestSuite) TestDecrypt_PBSE2_HS512_A256KW_A256CBC_HS512(c *C) { 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"}` 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 @@ -4,6 +4,7 @@ import ( "crypto/sha256" "crypto/sha512" "errors" + "fmt" "hash" "github.com/dvsekhvalnov/jose2go/arrays" @@ -12,15 +13,28 @@ import ( ) 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 { @@ -46,6 +60,21 @@ func (alg *Pbse2HmacAesKW) WrapNewKey(cekSizeBits int, key interface{}, header m 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) @@ -69,8 +98,18 @@ func (alg *Pbse2HmacAesKW) Unwrap(encryptedCek []byte, key interface{}, cekSizeB 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 From 7a76bc49d9e87b97207b72682cdf9881756956ac Mon Sep 17 00:00:00 2001 From: DV Date: Wed, 29 Nov 2023 08:38:27 +0300 Subject: [PATCH 03/12] Unit tests for custom 'p2c' headers --- jose_test.go | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/jose_test.go b/jose_test.go index 2b67fd6..92a7d1d 100644 --- a/jose_test.go +++ b/jose_test.go @@ -8,6 +8,7 @@ import ( "strings" "testing" + "github.com/dvsekhvalnov/jose2go/base64url" "github.com/dvsekhvalnov/jose2go/keys/ecc" Rsa "github.com/dvsekhvalnov/jose2go/keys/rsa" @@ -1772,6 +1773,36 @@ func (s *TestSuite) TestEncrypt_PBSE2_HS256_A128KW_A256CBC_HS512(c *C) { c.Assert(t, Equals, payload) } +func (s *TestSuite) TestEncrypt_PBES2_HS512_A256KW_A256CBC_HS512_Custom_p2c(c *C) { + // given + payload := `{"exp":1389189552,"sub":"alice","nbf":1389188952,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"e543edf6-edf0-4348-8940-c4e28614d463","iat":1389188952}"` + + // when + test, err := Encrypt(payload, PBES2_HS256_A128KW, A256CBC_HS512, "top secret", Header("p2c", 10000)) + + fmt.Printf("\nPBES2-HS256+A128KW A256CBC_HS512 (custom p2c=10000)= %v\n", test) + + //then + c.Assert(err, IsNil) + + parts := strings.Split(test, ".") + + c.Assert(len(parts), Equals, 5) + c.Assert(len(parts[0]), Equals, 116) + c.Assert(len(parts[1]), Equals, 96) + c.Assert(len(parts[2]), Equals, 22) + c.Assert(len(parts[3]), Equals, 278) + c.Assert(len(parts[4]), Equals, 43) + + // ensure custom 'p2c' header have been included + v, _ := base64url.Decode(parts[0]) + c.Assert(string(v), Matches, `.*"p2c":10000.*`) + + //make sure we consistent with ourselfs + t, _, _ := Decode(test, "top secret") + c.Assert(t, Equals, payload) +} + func (s *TestSuite) TestDecrypt_ECDH_ES_A128CBC_HS256(c *C) { //given token := "eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOENCQy1IUzI1NiIsImVwayI6eyJrdHkiOiJFQyIsIngiOiItVk1LTG5NeW9IVHRGUlpGNnFXNndkRm5BN21KQkdiNzk4V3FVMFV3QVhZIiwieSI6ImhQQWNReTgzVS01Qjl1U21xbnNXcFZzbHVoZGJSZE1nbnZ0cGdmNVhXTjgiLCJjcnYiOiJQLTI1NiJ9fQ..UA3N2j-TbYKKD361AxlXUA.XxFur_nY1GauVp5W_KO2DEHfof5s7kUwvOgghiNNNmnB4Vxj5j8VRS8vMOb51nYy2wqmBb2gBf1IHDcKZdACkCOMqMIcpBvhyqbuKiZPLHiilwSgVV6ubIV88X0vK0C8ZPe5lEyRudbgFjdlTnf8TmsvuAsdtPn9dXwDjUR23bD2ocp8UGAV0lKqKzpAw528vTfD0gwMG8gt_op8yZAxqqLLljMuZdTnjofAfsW2Rq3Z6GyLUlxR51DAUlQKi6UpsKMJoXTrm1Jw8sXBHpsRqA.UHCYOtnqk4SfhAknCnymaQ" From ed5dd96763d198ce95dc10b9252cb96854522114 Mon Sep 17 00:00:00 2001 From: DV Date: Thu, 30 Nov 2023 21:48:49 +0300 Subject: [PATCH 04/12] Unit tests for custom 'p2c' headers min/max limits --- jose_test.go | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/jose_test.go b/jose_test.go index 92a7d1d..eff9e95 100644 --- a/jose_test.go +++ b/jose_test.go @@ -1803,6 +1803,42 @@ func (s *TestSuite) TestEncrypt_PBES2_HS512_A256KW_A256CBC_HS512_Custom_p2c(c *C c.Assert(t, Equals, payload) } +func (s *TestSuite) TestEncrypt_PBES2_HS512_A256KW_A256CBC_HS512_MaxIterationViolation(c *C) { + // given + pbes2Hs512 := DeregisterJwa(PBES2_HS512_A256KW) + RegisterJwa(NewPbse2HmacAesKWAlg(256, 8000, 0)) + + payload := `{"hello": "world"}` + + // when + test, err := Encrypt(payload, PBES2_HS512_A256KW, A256CBC_HS512, "top secret", Header("p2c", 10000)) + + fmt.Printf("\nTestEncrypt_PBES2_HS512_A256KW_A256CBC_HS512_MaxIterationViolation, err = %v\n", err) + + //then + RegisterJwa(pbes2Hs512) + c.Assert(err, NotNil) + c.Assert(test, Equals, "") +} + +func (s *TestSuite) TestEncrypt_PBES2_HS512_A256KW_A256CBC_HS512_MinIterationViolation(c *C) { + // given + pbes2Hs512 := DeregisterJwa(PBES2_HS512_A256KW) + RegisterJwa(NewPbse2HmacAesKWAlg(256, 800000, 300000)) + + payload := `{"hello": "world"}` + + // when + test, err := Encrypt(payload, PBES2_HS512_A256KW, A256CBC_HS512, "top secret", Header("p2c", 10000)) + + fmt.Printf("\nTestEncrypt_PBES2_HS512_A256KW_A256CBC_HS512_MinIterationViolation, err = %v\n", err) + + //then + RegisterJwa(pbes2Hs512) + c.Assert(err, NotNil) + c.Assert(test, Equals, "") +} + func (s *TestSuite) TestDecrypt_ECDH_ES_A128CBC_HS256(c *C) { //given token := "eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOENCQy1IUzI1NiIsImVwayI6eyJrdHkiOiJFQyIsIngiOiItVk1LTG5NeW9IVHRGUlpGNnFXNndkRm5BN21KQkdiNzk4V3FVMFV3QVhZIiwieSI6ImhQQWNReTgzVS01Qjl1U21xbnNXcFZzbHVoZGJSZE1nbnZ0cGdmNVhXTjgiLCJjcnYiOiJQLTI1NiJ9fQ..UA3N2j-TbYKKD361AxlXUA.XxFur_nY1GauVp5W_KO2DEHfof5s7kUwvOgghiNNNmnB4Vxj5j8VRS8vMOb51nYy2wqmBb2gBf1IHDcKZdACkCOMqMIcpBvhyqbuKiZPLHiilwSgVV6ubIV88X0vK0C8ZPe5lEyRudbgFjdlTnf8TmsvuAsdtPn9dXwDjUR23bD2ocp8UGAV0lKqKzpAw528vTfD0gwMG8gt_op8yZAxqqLLljMuZdTnjofAfsW2Rq3Z6GyLUlxR51DAUlQKi6UpsKMJoXTrm1Jw8sXBHpsRqA.UHCYOtnqk4SfhAknCnymaQ" From 8e9e0d1c6b39ac448a6042ed1275efa70a81c7b7 Mon Sep 17 00:00:00 2001 From: DV Date: Tue, 5 Dec 2023 21:19:31 +0300 Subject: [PATCH 05/12] updated p2c limits with new OWASP numbers, docs --- README.md | 63 +++++++++++++++++++++++++++++++++++++++++---- pbse2_hmac_aeskw.go | 6 ++--- 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 9df801c..4d6b771 100644 --- a/README.md +++ b/README.md @@ -250,7 +250,7 @@ func main() { //go use token fmt.Printf("\ntoken = %v\n",token) } -} +} ``` #### AES Key Wrap key management family of algorithms @@ -330,7 +330,7 @@ func main() { //go use token fmt.Printf("\ntoken = %v\n",token) } -} +} ``` #### PBES2 using HMAC SHA with AES Key Wrap key management family of algorithms @@ -482,7 +482,7 @@ func main() { //and/or use headers fmt.Printf("\nheaders = %v\n",headers) } -} +} ``` **RSA-OAEP-256**, **RSA-OAEP** and **RSA1_5** key management algorithms expecting `*rsa.PrivateKey` private key of corresponding length: @@ -522,7 +522,7 @@ func main() { //and/or use headers fmt.Printf("\nheaders = %v\n",headers) } -} +} ``` **PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW** key management algorithms expects `string` passpharase as a key @@ -776,7 +776,7 @@ func main() { //go use token fmt.Printf("\ntoken = %v\n",token) } -} +} ``` ### Dealing with keys **jose2go** provides several helper methods to simplify loading & importing of elliptic and rsa keys. Import `jose2go/keys/rsa` or `jose2go/keys/ecc` respectively: @@ -925,7 +925,60 @@ func main() { ### More examples Checkout `jose_test.go` for more examples. +## Customizing library for security +In response to ever increasing attacks on various JWT implementations, `jose2go` as of version v1.3 introduced number of additional security controls to limit potential attack surface on services and projects using the library. + +### Deregister algorithm implementations +One can use following methods to deregister any signing, encryption, key management or compression algorithms from runtime suite, that is considered unsafe or simply not expected by service. + +- `func DeregisterJwa(alg string) JwaAlgorithm` +- `func DeregisterJwe(alg string) JweEncryption` +- `func DeregisterJws(alg string) JwsAlgorithm` +- `func DeregisterJwc(alg string) JwcAlgorithm` + +All of them expecting alg name matching `jose` constants and returns implementation that have been deregistered. + +### Customizing PBKDF2 +As it quite easy to abuse PBES2 family of algorithms via forging header with extra large p2c values, jose-jwt library introduced iteration count limits in v1.3 to reduce runtime exposure. + +By default, maxIterations is set according to [OWASP PBKDF2](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2) Recomendations: + +``` +PBES2-HS256+A128KW: 1300000 +PBES2-HS384+A192KW: 950000 +PBES2-HS512+A256KW: 600000 +``` + +, while minIterations kept at 0 for backward compatibility. + +If it is desired to implement different limits, register new implementation with new parameters: + +```Go + jose.RegisterJwa(NewPbse2HmacAesKWAlg(128, 1300000, 1300000)) + jose.RegisterJwa(NewPbse2HmacAesKWAlg(192, 950000, 950000)) + jose.RegisterJwa(NewPbse2HmacAesKWAlg(256, 600000, 600000)) +``` + +In case you can't upgrade to latest version, but would like to have protections against PBES2 abuse, it is recommended to stick with [Two-phase validation](#two-phase-validation) precheck before decoding: + +```Go +test, headers, err := Decode(token, func(headers map[string]interface{}, payload string) interface{} { + alg := headers["alg"].(string) + p2c := headers["p2c"].(float64) + + if strings.HasPrefix(alg, "PBES2-") && int64(p2c) > 100 { + return errors.New("Too many p2c interation count, aborting") + } + + return "top secret" +}) +``` + ## Changelog +### 1.3 +- ability to deregister specific algorithms +- configurable min/max restrictions for PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW + ### 1.2 - interface to access token headers after decoding - interface to provide extra headers for token encoding diff --git a/pbse2_hmac_aeskw.go b/pbse2_hmac_aeskw.go index 154c0f3..2915ae6 100644 --- a/pbse2_hmac_aeskw.go +++ b/pbse2_hmac_aeskw.go @@ -13,9 +13,9 @@ import ( ) func init() { - RegisterJwa(NewPbse2HmacAesKWAlg(128, 310000, 0)) - RegisterJwa(NewPbse2HmacAesKWAlg(192, 250000, 0)) - RegisterJwa(NewPbse2HmacAesKWAlg(256, 120000, 0)) + RegisterJwa(NewPbse2HmacAesKWAlg(128, 1300000, 0)) + RegisterJwa(NewPbse2HmacAesKWAlg(192, 950000, 0)) + RegisterJwa(NewPbse2HmacAesKWAlg(256, 600000, 0)) } // PBSE2 with HMAC key management algorithm implementation From 675bb14fb3216d48f571b7e5d4274faf4aceb069 Mon Sep 17 00:00:00 2001 From: DV Date: Tue, 5 Dec 2023 21:23:31 +0300 Subject: [PATCH 06/12] docs --- README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 4d6b771..0f605a2 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,9 @@ Extensively unit tested and cross tested (100+ tests) for compatibility with [jo ## Status -Used in production. GA ready. Current version is 1.5. +Used in production. GA ready. Current version is 1.6. ## Important -v1.5 bug fix release - v1.4 changes default behavior of inserting `typ=JWT` header if not overriden. As of 1.4 no extra headers added by library automatically. To mimic pre 1.4 behaviour use: ```Go @@ -926,7 +924,7 @@ func main() { Checkout `jose_test.go` for more examples. ## Customizing library for security -In response to ever increasing attacks on various JWT implementations, `jose2go` as of version v1.3 introduced number of additional security controls to limit potential attack surface on services and projects using the library. +In response to ever increasing attacks on various JWT implementations, `jose2go` as of version v1.6 introduced number of additional security controls to limit potential attack surface on services and projects using the library. ### Deregister algorithm implementations One can use following methods to deregister any signing, encryption, key management or compression algorithms from runtime suite, that is considered unsafe or simply not expected by service. @@ -939,7 +937,7 @@ One can use following methods to deregister any signing, encryption, key managem All of them expecting alg name matching `jose` constants and returns implementation that have been deregistered. ### Customizing PBKDF2 -As it quite easy to abuse PBES2 family of algorithms via forging header with extra large p2c values, jose-jwt library introduced iteration count limits in v1.3 to reduce runtime exposure. +As it quite easy to abuse PBES2 family of algorithms via forging header with extra large p2c values, jose-jwt library introduced iteration count limits in v1.6 to reduce runtime exposure. By default, maxIterations is set according to [OWASP PBKDF2](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2) Recomendations: @@ -975,7 +973,7 @@ test, headers, err := Decode(token, func(headers map[string]interface{}, payload ``` ## Changelog -### 1.3 +### 1.6 - ability to deregister specific algorithms - configurable min/max restrictions for PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW From 9a18aff07b6c5574e02d74439ad7d7ae88510b6f Mon Sep 17 00:00:00 2001 From: DV Date: Tue, 5 Dec 2023 21:25:22 +0300 Subject: [PATCH 07/12] docs --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 0f605a2..d583914 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ Extensively unit tested and cross tested (100+ tests) for compatibility with [jo Used in production. GA ready. Current version is 1.6. ## Important +v1.6 security tuning options +v1.5 bug fix release v1.4 changes default behavior of inserting `typ=JWT` header if not overriden. As of 1.4 no extra headers added by library automatically. To mimic pre 1.4 behaviour use: ```Go From 299576231d2311655289f451218678044d6ddbc9 Mon Sep 17 00:00:00 2001 From: DV Date: Tue, 5 Dec 2023 21:26:08 +0300 Subject: [PATCH 08/12] docs --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index d583914..dcf84cf 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,9 @@ Used in production. GA ready. Current version is 1.6. ## Important v1.6 security tuning options + v1.5 bug fix release + v1.4 changes default behavior of inserting `typ=JWT` header if not overriden. As of 1.4 no extra headers added by library automatically. To mimic pre 1.4 behaviour use: ```Go From cf0a53b05fc1faf3e655a30bd7d523cb3a2c6dbd Mon Sep 17 00:00:00 2001 From: DV Date: Tue, 5 Dec 2023 21:28:22 +0300 Subject: [PATCH 09/12] docs --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index dcf84cf..deba2c0 100644 --- a/README.md +++ b/README.md @@ -981,6 +981,15 @@ test, headers, err := Decode(token, func(headers map[string]interface{}, payload - ability to deregister specific algorithms - configurable min/max restrictions for PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW +### 1.5 +- security and bug fixes + +### 1.4 +- removed extra headers to be inserted by library + +### 1.3 +- security fixes: Invalid Curve Attack on NIST curves + ### 1.2 - interface to access token headers after decoding - interface to provide extra headers for token encoding From 0f6c7c346282f2d264aef1d7dec8be71f9190b08 Mon Sep 17 00:00:00 2001 From: DV Date: Tue, 5 Dec 2023 21:45:40 +0300 Subject: [PATCH 10/12] MatchAlg helper --- jose.go | 11 +++++++++++ jose_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/jose.go b/jose.go index 9bc6482..6942903 100644 --- a/jose.go +++ b/jose.go @@ -458,3 +458,14 @@ func retrieveActualKey(headers map[string]interface{}, payload string, key inter return key, nil } + +func MatchAlg(expected string, key interface{}) func(headers map[string]interface{}, payload string) interface{} { + return func(headers map[string]interface{}, payload string) interface{} { + alg := headers["alg"].(string) + if expected == alg { + return key + } + + return errors.New("Expected alg to be '" + expected + "' but got '" + alg + "'") + } +} diff --git a/jose_test.go b/jose_test.go index eff9e95..dffd4dc 100644 --- a/jose_test.go +++ b/jose_test.go @@ -2600,6 +2600,33 @@ func (s *TestSuite) TestDeregisterJwc(c *C) { c.Assert(test, Equals, "") } +func (s *TestSuite) TestDecode_TwoPhased_MatchAlg(c *C) { + //given + token := "eyJhbGciOiJFUzI1NiIsImN0eSI6InRleHRcL3BsYWluIn0.eyJoZWxsbyI6ICJ3b3JsZCJ9.EVnmDMlz-oi05AQzts-R3aqWvaBlwVZddWkmaaHyMx5Phb2NSLgyI0kccpgjjAyo1S5KCB3LIMPfmxCX_obMKA" + + //when + test, _, err := Decode(token, MatchAlg("ES256", Ecc256Public())) + + //then + c.Assert(err, IsNil) + c.Assert(test, Equals, `{"hello": "world"}`) +} + +func (s *TestSuite) TestDecode_TwoPhased_MatchAlg_Invalid(c *C) { + //given + token := "eyJhbGciOiJFUzI1NiIsImN0eSI6InRleHRcL3BsYWluIn0.eyJoZWxsbyI6ICJ3b3JsZCJ9.EVnmDMlz-oi05AQzts-R3aqWvaBlwVZddWkmaaHyMx5Phb2NSLgyI0kccpgjjAyo1S5KCB3LIMPfmxCX_obMKA" + + //when + test, headers, err := Decode(token, MatchAlg("RS256", Ecc256Public())) + + fmt.Printf("\nalg doesn't match err=%v\n", err) + + //then + c.Assert(headers, IsNil) + c.Assert(err, NotNil) + c.Assert(test, Equals, "") +} + // test utils func PubKey() *rsa.PublicKey { key, _ := Rsa.ReadPublic([]byte(pubKey)) From e0264a274aa4c14e22f197c5325599224c1dd412 Mon Sep 17 00:00:00 2001 From: DV Date: Wed, 6 Dec 2023 21:19:45 +0300 Subject: [PATCH 11/12] added helper matchers: Alg and Eng --- jose.go | 20 +++++++++++++++++--- jose_test.go | 44 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/jose.go b/jose.go index 6942903..3549a91 100644 --- a/jose.go +++ b/jose.go @@ -459,13 +459,27 @@ func retrieveActualKey(headers map[string]interface{}, payload string, key inter return key, nil } -func MatchAlg(expected string, key interface{}) func(headers map[string]interface{}, payload string) interface{} { +func Alg(key interface{}, jws string) func(headers map[string]interface{}, payload string) interface{} { return func(headers map[string]interface{}, payload string) interface{} { alg := headers["alg"].(string) - if expected == alg { + + if jws == alg { + return key + } + + return errors.New("Expected alg to be '" + jws + "' but got '" + alg + "'") + } +} + +func Enc(key interface{}, jwa string, jwe string) func(headers map[string]interface{}, payload string) interface{} { + return func(headers map[string]interface{}, payload string) interface{} { + alg := headers["alg"].(string) + enc := headers["enc"].(string) + + if jwa == alg && jwe == enc { return key } - return errors.New("Expected alg to be '" + expected + "' but got '" + alg + "'") + return errors.New("Expected alg to be '" + jwa + "' and enc to be '" + jwe + "' but got '" + alg + "' and '" + enc + "'") } } diff --git a/jose_test.go b/jose_test.go index dffd4dc..bcd166f 100644 --- a/jose_test.go +++ b/jose_test.go @@ -2605,7 +2605,7 @@ func (s *TestSuite) TestDecode_TwoPhased_MatchAlg(c *C) { token := "eyJhbGciOiJFUzI1NiIsImN0eSI6InRleHRcL3BsYWluIn0.eyJoZWxsbyI6ICJ3b3JsZCJ9.EVnmDMlz-oi05AQzts-R3aqWvaBlwVZddWkmaaHyMx5Phb2NSLgyI0kccpgjjAyo1S5KCB3LIMPfmxCX_obMKA" //when - test, _, err := Decode(token, MatchAlg("ES256", Ecc256Public())) + test, _, err := Decode(token, Alg(Ecc256Public(), "ES256")) //then c.Assert(err, IsNil) @@ -2617,7 +2617,7 @@ func (s *TestSuite) TestDecode_TwoPhased_MatchAlg_Invalid(c *C) { token := "eyJhbGciOiJFUzI1NiIsImN0eSI6InRleHRcL3BsYWluIn0.eyJoZWxsbyI6ICJ3b3JsZCJ9.EVnmDMlz-oi05AQzts-R3aqWvaBlwVZddWkmaaHyMx5Phb2NSLgyI0kccpgjjAyo1S5KCB3LIMPfmxCX_obMKA" //when - test, headers, err := Decode(token, MatchAlg("RS256", Ecc256Public())) + test, headers, err := Decode(token, Alg(Ecc256Public(), "RS256")) fmt.Printf("\nalg doesn't match err=%v\n", err) @@ -2627,6 +2627,46 @@ func (s *TestSuite) TestDecode_TwoPhased_MatchAlg_Invalid(c *C) { c.Assert(test, Equals, "") } +func (s *TestSuite) TestDecode_TwoPhased_MatchEnc(c *C) { + //given + token := "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0In0.COuKvozBVi2vkEPpFdx0HTMpU9tmpP1lLngbmGn8RVphY-vjhVaduv8D_Ay_1j8LuMz4tgP98xWtbJkTyhxY1kBwXe0CgqFUOSJ1mTEPRkKSXpdFR7rT1Pv68qug2yKaXT_qcviyBerIcUVFbXBmtiYAosYO4kaPSOE1IvLadFOrMkxdZv6QiiCROzWgJNCCMgNQZGRoPhqLe3wrcxi86DhNO7Bpqq_yeNVyHdU_qObMuMVZIWWEQIDhiU4nE8WGJLG_NtKElc_nQwbmclL_YYgTiHsIAKWZCdj0nwfLe5mwJQN4r7pjakiUVzCbNNgI1-iBH1vJD5VCPxgWldzfYA.7cDs4wzbNDt1Kq40Q5ae4w.u1bR6ChVd90QkFIp3H6IkOCIMwf5aIKsQOvqgFangRLrDjctl5qO5jTHr1o1GwBQvAkRmaGSE7fRIwWB_l-Ayx2c2WDFOkVXFSR_D23GrWaLMLbugPItQd2Mny6H4QOzO3O0EK_Qm7frqwKQI3og72SB8DUqzEaKsrz7HR2z_qMa2CEEApxai_R6NIlAdMUbYvOfZx262MWFGrITBDmma-Mnqiz9WJUv2wexfwjROaaS4wXfkGy5B6ltESifpZZk5NerExR3GA6yX7cFqJc4pQ.FKcbLyB9eP1UXmxyliTu1_GQrnS-JtAB" + + //when + test, _, err := Decode(token, Enc(PrivKey(), "RSA-OAEP-256", "A192CBC-HS384")) + + //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) TestDecode_TwoPhased_MatchEnc_InvalidAlg(c *C) { + //given + token := "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0In0.COuKvozBVi2vkEPpFdx0HTMpU9tmpP1lLngbmGn8RVphY-vjhVaduv8D_Ay_1j8LuMz4tgP98xWtbJkTyhxY1kBwXe0CgqFUOSJ1mTEPRkKSXpdFR7rT1Pv68qug2yKaXT_qcviyBerIcUVFbXBmtiYAosYO4kaPSOE1IvLadFOrMkxdZv6QiiCROzWgJNCCMgNQZGRoPhqLe3wrcxi86DhNO7Bpqq_yeNVyHdU_qObMuMVZIWWEQIDhiU4nE8WGJLG_NtKElc_nQwbmclL_YYgTiHsIAKWZCdj0nwfLe5mwJQN4r7pjakiUVzCbNNgI1-iBH1vJD5VCPxgWldzfYA.7cDs4wzbNDt1Kq40Q5ae4w.u1bR6ChVd90QkFIp3H6IkOCIMwf5aIKsQOvqgFangRLrDjctl5qO5jTHr1o1GwBQvAkRmaGSE7fRIwWB_l-Ayx2c2WDFOkVXFSR_D23GrWaLMLbugPItQd2Mny6H4QOzO3O0EK_Qm7frqwKQI3og72SB8DUqzEaKsrz7HR2z_qMa2CEEApxai_R6NIlAdMUbYvOfZx262MWFGrITBDmma-Mnqiz9WJUv2wexfwjROaaS4wXfkGy5B6ltESifpZZk5NerExR3GA6yX7cFqJc4pQ.FKcbLyB9eP1UXmxyliTu1_GQrnS-JtAB" + + //when + test, _, err := Decode(token, Enc(PrivKey(), "RSA-OAEP-256", "A256CBC-HS512")) + + fmt.Printf("\nalg/enc doesn't match err=%v\n", err) + + //then + c.Assert(err, NotNil) + c.Assert(test, Equals, "") +} + +func (s *TestSuite) TestDecode_TwoPhased_MatchEnc_InvalidEnc(c *C) { + //given + token := "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0In0.COuKvozBVi2vkEPpFdx0HTMpU9tmpP1lLngbmGn8RVphY-vjhVaduv8D_Ay_1j8LuMz4tgP98xWtbJkTyhxY1kBwXe0CgqFUOSJ1mTEPRkKSXpdFR7rT1Pv68qug2yKaXT_qcviyBerIcUVFbXBmtiYAosYO4kaPSOE1IvLadFOrMkxdZv6QiiCROzWgJNCCMgNQZGRoPhqLe3wrcxi86DhNO7Bpqq_yeNVyHdU_qObMuMVZIWWEQIDhiU4nE8WGJLG_NtKElc_nQwbmclL_YYgTiHsIAKWZCdj0nwfLe5mwJQN4r7pjakiUVzCbNNgI1-iBH1vJD5VCPxgWldzfYA.7cDs4wzbNDt1Kq40Q5ae4w.u1bR6ChVd90QkFIp3H6IkOCIMwf5aIKsQOvqgFangRLrDjctl5qO5jTHr1o1GwBQvAkRmaGSE7fRIwWB_l-Ayx2c2WDFOkVXFSR_D23GrWaLMLbugPItQd2Mny6H4QOzO3O0EK_Qm7frqwKQI3og72SB8DUqzEaKsrz7HR2z_qMa2CEEApxai_R6NIlAdMUbYvOfZx262MWFGrITBDmma-Mnqiz9WJUv2wexfwjROaaS4wXfkGy5B6ltESifpZZk5NerExR3GA6yX7cFqJc4pQ.FKcbLyB9eP1UXmxyliTu1_GQrnS-JtAB" + + //when + test, _, err := Decode(token, Enc(PrivKey(), "RSA-OAEP", "A192CBC-HS384")) + + fmt.Printf("\nalg/enc doesn't match err=%v\n", err) + + //then + c.Assert(err, NotNil) + c.Assert(test, Equals, "") +} + // test utils func PubKey() *rsa.PublicKey { key, _ := Rsa.ReadPublic([]byte(pubKey)) From 05eb00788125e3996824d2005e850ed8b37d5aa4 Mon Sep 17 00:00:00 2001 From: DV Date: Wed, 6 Dec 2023 21:44:06 +0300 Subject: [PATCH 12/12] docs --- README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.md b/README.md index deba2c0..bbf0ef7 100644 --- a/README.md +++ b/README.md @@ -681,6 +681,8 @@ func main() { } ``` +Two phase validation can be used for implementing additional things like strict `alg` or `enc` validation, see [Customizing library for security](#customizing-library-for-security) for more information. + ### Working with binary payload In addition to work with string payloads (typical use-case) `jose2go` supports encoding and decoding of raw binary data. `jose.DecodeBytes`, `jose.SignBytes` @@ -940,6 +942,25 @@ One can use following methods to deregister any signing, encryption, key managem All of them expecting alg name matching `jose` constants and returns implementation that have been deregistered. +### Strict validation +Sometimes it is desirable to verify that `alg` or `enc` values are matching expected before attempting to decode actual payload. +`jose2go` provides helper matchers to be used within [Two-phase validation](#two-phase-validation) precheck: + +- `jose.Alg(key, alg)` - to match alg header +- `jose.Enc(key, alg)` - to match alg and enc headers + +```Go + token := "eyJhbGciOiJSUzI1NiIsImN0eSI6InRleHRcL3BsYWluIn0.eyJoZWxsbyI6ICJ3b3JsZCJ9.NL_dfVpZkhNn4bZpCyMq5TmnXbT4yiyecuB6Kax_lV8Yq2dG8wLfea-T4UKnrjLOwxlbwLwuKzffWcnWv3LVAWfeBxhGTa0c4_0TX_wzLnsgLuU6s9M2GBkAIuSMHY6UTFumJlEeRBeiqZNrlqvmAzQ9ppJHfWWkW4stcgLCLMAZbTqvRSppC1SMxnvPXnZSWn_Fk_q3oGKWw6Nf0-j-aOhK0S0Lcr0PV69ZE4xBYM9PUS1MpMe2zF5J3Tqlc1VBcJ94fjDj1F7y8twmMT3H1PI9RozO-21R0SiXZ_a93fxhE_l_dj5drgOek7jUN9uBDjkXUwJPAyp9YPehrjyLdw" + + key := Rsa.ReadPublic(....) + + // we expecting 'RS256' alg here and if matching continue to decode with a key + payload, header, err := jose.Decode(token, Alg(key, "RS256")) + + // or match both alg and enc for decrypting scenarios + payload, header, err := jose.Decode(token, Enc(key, "RSA-OAEP-256", "A192CBC-HS384")) +``` + ### Customizing PBKDF2 As it quite easy to abuse PBES2 family of algorithms via forging header with extra large p2c values, jose-jwt library introduced iteration count limits in v1.6 to reduce runtime exposure.