Coder Social home page Coder Social logo

micahparks / jwkset Goto Github PK

View Code? Open in Web Editor NEW
22.0 2.0 6.0 785 KB

A JWK and JWK Set implementation. A JWK Set HTTP client is provided. Generate, validate, and inspect JWKs. Self-host this project's website: https://jwkset.com

Home Page: https://jwkset.com

License: Apache License 2.0

Go 90.67% Dockerfile 0.13% Shell 0.57% CSS 0.03% JavaScript 8.60%
jwk jwks jwks-endpoint jwks-rsa jwkset go golang rfc7517

jwkset's Introduction

Find me on the internet

My personal website: link

  • This has a contact form, if you'd like to report a security sensitive bug or send a kind message.

StackOverflow: link

If you appreciate my work, consider supporting me via a one-time or monthly financial contribution via GitHub Sponsors.

jwkset's People

Contributors

aarnaud avatar eherde avatar micahparks avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

jwkset's Issues

Correct behavior for RSA multi-prime unmarshaling

In this snippet of code there is a TODO that needs to be addressed.

jwkset/marshal.go

Lines 305 to 331 in f4551a1

var oth []rsa.CRTValue
if len(jwk.OTH) > 0 {
// TODO Does each extra multi-prime need to be added to the slice of primes on the private key?
oth = make([]rsa.CRTValue, len(jwk.OTH))
for i, otherPrimes := range jwk.OTH {
if otherPrimes.R == "" || otherPrimes.D == "" || otherPrimes.T == "" {
return KeyWithMeta{}, fmt.Errorf(`%w: %s requires parameters "r", "d", and "t" for each "oth"`, ErrKeyUnmarshalParameter, KeyTypeRSA)
}
othD, err := base64urlTrailingPadding(otherPrimes.D)
if err != nil {
return KeyWithMeta{}, fmt.Errorf(`failed to decode %s key parameter "d": %w`, KeyTypeRSA, err)
}
othT, err := base64urlTrailingPadding(otherPrimes.T)
if err != nil {
return KeyWithMeta{}, fmt.Errorf(`failed to decode %s key parameter "t": %w`, KeyTypeRSA, err)
}
othR, err := base64urlTrailingPadding(otherPrimes.R)
if err != nil {
return KeyWithMeta{}, fmt.Errorf(`failed to decode %s key parameter "r": %w`, KeyTypeRSA, err)
}
oth[i] = rsa.CRTValue{
Exp: new(big.Int).SetBytes(othD),
Coeff: new(big.Int).SetBytes(othT),
R: new(big.Int).SetBytes(othR),
}
}
}

When I generate a multi-prime RSA key with the below function call:

private, err := rsa.GenerateMultiPrimeKey(rand.Reader, 5, 2048)

I can see that the private.Primes field is a slice with a length of 5 and private.Precomputed.CRTValues field is a slice with a length of 3.

However, the current project does not unmarshal back into this private.Primes field correctly.

stdout and stderr

Hi,
Just to track this behaviour.

I'm not sure about this, but it's seem the println here write on the stderr.

This is problematic to redirect output to a file and keep the log on stderr.

Anthony,

Handle extra padding for RSA "n" JWK parameter

RFC 7518 Section 6.3.1.1 notes that:

Note that implementers have found that some cryptographic libraries prefix an extra zero-valued octet to the modulus representations they return, for instance, returning 257 octets for a 2048-bit key, rather than 256. Implementations using such libraries will need to take care to omit the extra octet from the base64url-encoded representation.

This note is not implemented in this project. Only the first half of the section defining it as a Base64urlUInt-encoded was regarded, but this will be remedied.

This affects Firebase JWK Sets. Please see the user report here:
#20 (comment)

Issue with JWK Validation: Leading Zero in Coordinates

We have encountered a problem where loading JSON Web Keys (JWKs) results in the following error message:

"failed to validate JSON Web Key: failed to validate JWK: marshaled JWK does not match original JWK"

The JWK is set as follows:

                "kty": "EC",
                "crv": "P-256",
                "alg": "ES256"

Upon investigation, we found that the problem lies in one of the key coordinates starting with a leading zero. For example:
The x coordinate starts with "ALTu..." After the coordinate is changed with the following function

func base64urlTrailingPadding(s string) ([]byte, error) {
the result looks like:
[0 180 238...]

However, after calling Set.Bytes() on this value, the leading zero disappears..

X: new(big.Int).SetBytes(x),

When converting the result back to bytes we see the following result:
[180 238...]

This discrepancy leads to the original error message because the deepEqual check here no longer validates correctly.

jwkset/jwk.go

Line 311 in b0b8e8b

ok := reflect.DeepEqual(j.marshal, marshalled)

We recommend addressing this issue by ensuring consistent handling of leading zeros in key coordinates during JWK validation.

We hope to hear soon from you!

Kind regards,
Hauke

JWK validation fails when `x5ts256` value is missing from original JWK

When building a JWK from NewJWKFromMarshal, validation fails when the unmarshalled key (key from a remote host) does not contain an x5ts246 value and the x5c is present. This appears to be due to building the x5ts256 in the keyMarshal logic, resulting in a mismatch when DeepEqual the structs:

jwkset/marshal.go

Lines 195 to 206 in b7c3a1f

haveX5C := len(options.X509.X5C) > 0
if haveX5C {
for i, cert := range options.X509.X5C {
m.X5C = append(m.X5C, base64.StdEncoding.EncodeToString(cert.Raw))
if i == 0 {
h1 := sha1.Sum(cert.Raw)
m.X5T = base64.RawURLEncoding.EncodeToString(h1[:])
h256 := sha256.Sum256(cert.Raw)
m.X5TS256 = base64.RawURLEncoding.EncodeToString(h256[:])
}
}
}

This is a behavioral difference between [email protected] and [email protected]. I'm curious if this would be expected behavior in the upgrade?

Question about the JWKS Refresh Rate Limiting

The current implementation, from my understanding,
uses a rate limiter (RefreshUnknownKID) to control the frequency of JWKS refreshes for unknown KIDs.
My concern is that this may lead to redundant fetches if multiple requests with unknown KIDs arrive within the rate limiting window (e.g. 10 seconds).
Additionally, the last requests to arrive may experience long delays as they will have to wait for the rate limiting delay for each preceding request (if burst is set to 1 for example).

I wonder if we could optimize this, by adding a lock for the fetching process so that only the first request to arrive will initiate the JWKS refresh. Subsequent requests with unknown KIDs would wait for the refresh to complete. After the refresh is complete, the waiting requests will check the local storage for the KID and if the KID is still not found in the local storage, return a "not found" error instead of triggering another refresh, since we just refreshed the JWKS.

I think that this approach would reduce redundant fetches, improve performance, avoid unnecessary refreshes for KIDs that are not present in the JWKS after a fresh refresh, and prevent long delays for the last requests to arrive.

Using expired token from long ago blocks at parsing the token (hits JWKS rate limit since KID is unknown)

Having an expired old JWT that even has the KID removed from the JWKS (rotated cert) .
The first request goes through but the rest are rate limited .
Stack trace with 999 goroutines waiting on rate limiting:

997 @ 0x44b57c 0x45d927 0x967318 0x966bdc 0x966afc 0x970005 0x99fa74 0x992f23 0x992930 0x994ac5 0xe8f74b 0xe8ec94 0x8eda53 0xc721d3 0x8eda53 0xc70348 0xc6e3d1 0x8eda53 0x8f0e1e 0x8f2f77 0x8ec735 0x4839e1
#	0x967317	golang.org/x/time/rate.(*Limiter).wait+0x6d7							/home/ovilinux/go/pkg/mod/golang.org/x/[email protected]/rate/rate.go:285
#	0x966bdb	golang.org/x/time/rate.(*Limiter).WaitN+0x9b							/home/ovilinux/go/pkg/mod/golang.org/x/[email protected]/rate/rate.go:248
#	0x966afb	golang.org/x/time/rate.(*Limiter).Wait+0x3b							/home/ovilinux/go/pkg/mod/golang.org/x/[email protected]/rate/rate.go:233
#	0x970004	github.com/MicahParks/jwkset.httpClient.KeyRead+0xbe4						/home/ovilinux/go/pkg/mod/github.com/!micah!parks/[email protected]/http.go:172
#	0x99fa73	github.com/MicahParks/keyfunc/v3.keyfunc.Keyfunc+0x3d3						/home/ovilinux/go/pkg/mod/github.com/!micah!parks/keyfunc/[email protected]/keyfunc.go:135
#	0x992f22	github.com/golang-jwt/jwt/v5.(*Parser).ParseWithClaims+0x562					/home/ovilinux/go/pkg/mod/github.com/golang-jwt/jwt/[email protected]/parser.go:90
#	0x99292f	github.com/golang-jwt/jwt/v5.(*Parser).Parse+0x8f						/home/ovilinux/go/pkg/mod/github.com/golang-jwt/jwt/[email protected]/parser.go:45
#	0x994ac4	github.com/golang-jwt/jwt/v5.Parse+0xa4								/home/ovilinux/go/pkg/mod/github.com/golang-jwt/jwt/[email protected]/parser.go:226

All these concurrent requests wait for at least several minutes . (even 30 mins)

If this parse is called at at every request so we should return from our main logic in a few seconds .If it hangs at jwt parsing due to rate limiting , what can be done ? (we also don't want to spam our IDP jwks but we can allow more requests towards JWKS than 1 request every few minutes)

Change how JWK validation works

Currently, validation in this project uses the result of marshaling the cryptographic key and reflect.DeepEqual() to ensure the jwkset.JWK is valid.

jwkset/jwk.go

Lines 311 to 314 in a0de971

ok := reflect.DeepEqual(j.marshal, marshalled)
if !ok {
return fmt.Errorf("%w: marshaled JWK does not match original JWK", ErrJWKValidation)
}

Rework validation to compare JWK parameters in a less strict way. This will allow the project to be compatible with other JWK projects that are not RFC compliant.

One case to note in particular is ECDSA key parameters with leading padding. The integers should be compared, not the base64 encoded strings. See #18 for additional context.

Investigate if this project is in full RFC compliance for JWK parameters using `Base64urlUInt` vs `Base64url Encoding`

Investigate if this project is in full RFC compliance for JWK parameters using Base64urlUInt vs Base64url Encoding.

There is a difference in how JWK parameters described by these types. I am concerned that this project is using Golang's .Bytes and .SetBytes incorrectly for Baseu64url Encoding parameters. If these methods are being used on Base64url Encoding JWK parameters as signed integers, that is likely a bug.

#18 (comment)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.