feature: Bump go-jose and require signing algorithms in auth
This bumps go-jose to the latest available version: v4.0.3. This slightly breaks the backwards compatibility with the existing registry deployments but brings more security with it. We now require the users to specify the list of token signing algorithms in the configuration. We do strive to maintain the b/w compat by providing a list of supported algorithms, though, this isn't something we recommend due to security issues, see: * https://github.com/go-jose/go-jose/issues/64 * https://github.com/go-jose/go-jose/pull/69 As part of this change we now return to the original flow of the token signature validation: 1. X2C (tls) headers 2. JWKS 3. KeyID Signed-off-by: Milos Gajdos <milosthegajdos@gmail.com>
This commit is contained in:
parent
56a020f7f1
commit
52d68216c0
46 changed files with 628 additions and 319 deletions
|
@ -168,6 +168,9 @@ auth:
|
||||||
service: token-service
|
service: token-service
|
||||||
issuer: registry-token-issuer
|
issuer: registry-token-issuer
|
||||||
rootcertbundle: /root/certs/bundle
|
rootcertbundle: /root/certs/bundle
|
||||||
|
signingalgorithms:
|
||||||
|
- EdDSA
|
||||||
|
- HS256
|
||||||
htpasswd:
|
htpasswd:
|
||||||
realm: basic-realm
|
realm: basic-realm
|
||||||
path: /path/to/htpasswd
|
path: /path/to/htpasswd
|
||||||
|
@ -572,6 +575,10 @@ auth:
|
||||||
service: token-service
|
service: token-service
|
||||||
issuer: registry-token-issuer
|
issuer: registry-token-issuer
|
||||||
rootcertbundle: /root/certs/bundle
|
rootcertbundle: /root/certs/bundle
|
||||||
|
signingalgorithms:
|
||||||
|
- EdDSA
|
||||||
|
- HS256
|
||||||
|
- ES512
|
||||||
htpasswd:
|
htpasswd:
|
||||||
realm: basic-realm
|
realm: basic-realm
|
||||||
path: /path/to/htpasswd
|
path: /path/to/htpasswd
|
||||||
|
@ -615,7 +622,37 @@ security.
|
||||||
| `rootcertbundle` | yes | The absolute path to the root certificate bundle. This bundle contains the public part of the certificates used to sign authentication tokens. |
|
| `rootcertbundle` | yes | The absolute path to the root certificate bundle. This bundle contains the public part of the certificates used to sign authentication tokens. |
|
||||||
| `autoredirect` | no | When set to `true`, `realm` will automatically be set using the Host header of the request as the domain and a path of `/auth/token/`(or specified by `autoredirectpath`), the `realm` URL Scheme will use `X-Forwarded-Proto` header if set, otherwise it will be set to `https`. |
|
| `autoredirect` | no | When set to `true`, `realm` will automatically be set using the Host header of the request as the domain and a path of `/auth/token/`(or specified by `autoredirectpath`), the `realm` URL Scheme will use `X-Forwarded-Proto` header if set, otherwise it will be set to `https`. |
|
||||||
| `autoredirectpath` | no | The path to redirect to if `autoredirect` is set to `true`, default: `/auth/token/`. |
|
| `autoredirectpath` | no | The path to redirect to if `autoredirect` is set to `true`, default: `/auth/token/`. |
|
||||||
|
| `signingalgorithms` | no | A list of token signing algorithms to use for verifying token signatures. If left empty the default list of signing algorithms is used. Please see below for allowed values and default. |
|
||||||
|
|
||||||
|
Available `signingalgorithms`:
|
||||||
|
- EdDSA
|
||||||
|
- HS256
|
||||||
|
- HS384
|
||||||
|
- HS512
|
||||||
|
- RS256
|
||||||
|
- RS384
|
||||||
|
- RS512
|
||||||
|
- ES256
|
||||||
|
- ES384
|
||||||
|
- ES512
|
||||||
|
- PS256
|
||||||
|
- PS384
|
||||||
|
- PS512
|
||||||
|
|
||||||
|
Default `signingalgorithms`:
|
||||||
|
- EdDSA
|
||||||
|
- HS256
|
||||||
|
- HS384
|
||||||
|
- HS512
|
||||||
|
- RS256
|
||||||
|
- RS384
|
||||||
|
- RS512
|
||||||
|
- ES256
|
||||||
|
- ES384
|
||||||
|
- ES512
|
||||||
|
- PS256
|
||||||
|
- PS384
|
||||||
|
- PS512
|
||||||
|
|
||||||
For more information about Token based authentication configuration, see the
|
For more information about Token based authentication configuration, see the
|
||||||
[specification](../spec/auth/token.md).
|
[specification](../spec/auth/token.md).
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -14,7 +14,7 @@ require (
|
||||||
github.com/distribution/reference v0.6.0
|
github.com/distribution/reference v0.6.0
|
||||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c
|
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c
|
||||||
github.com/docker/go-metrics v0.0.1
|
github.com/docker/go-metrics v0.0.1
|
||||||
github.com/go-jose/go-jose/v3 v3.0.3
|
github.com/go-jose/go-jose/v4 v4.0.2
|
||||||
github.com/google/uuid v1.3.1
|
github.com/google/uuid v1.3.1
|
||||||
github.com/gorilla/handlers v1.5.2
|
github.com/gorilla/handlers v1.5.2
|
||||||
github.com/gorilla/mux v1.8.1
|
github.com/gorilla/mux v1.8.1
|
||||||
|
|
19
go.sum
19
go.sum
|
@ -81,8 +81,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
|
||||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
|
github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk=
|
||||||
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
|
github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
|
@ -126,7 +126,6 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
@ -287,7 +286,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
|
||||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
@ -295,7 +293,6 @@ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTk
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
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-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
@ -311,8 +308,6 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
|
||||||
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
|
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
|
||||||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
@ -324,7 +319,6 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
@ -344,24 +338,16 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
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.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
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.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.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
|
||||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
@ -371,7 +357,6 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/distribution/distribution/v3/registry/auth"
|
"github.com/distribution/distribution/v3/registry/auth"
|
||||||
"github.com/go-jose/go-jose/v3"
|
"github.com/go-jose/go-jose/v4"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -151,13 +151,14 @@ func (ac authChallenge) SetHeaders(r *http.Request, w http.ResponseWriter) {
|
||||||
|
|
||||||
// accessController implements the auth.AccessController interface.
|
// accessController implements the auth.AccessController interface.
|
||||||
type accessController struct {
|
type accessController struct {
|
||||||
realm string
|
realm string
|
||||||
autoRedirect bool
|
autoRedirect bool
|
||||||
autoRedirectPath string
|
autoRedirectPath string
|
||||||
issuer string
|
issuer string
|
||||||
service string
|
service string
|
||||||
rootCerts *x509.CertPool
|
rootCerts *x509.CertPool
|
||||||
trustedKeys map[string]crypto.PublicKey
|
trustedKeys map[string]crypto.PublicKey
|
||||||
|
signingAlgorithms []jose.SignatureAlgorithm
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -167,13 +168,14 @@ const (
|
||||||
// tokenAccessOptions is a convenience type for handling
|
// tokenAccessOptions is a convenience type for handling
|
||||||
// options to the constructor of an accessController.
|
// options to the constructor of an accessController.
|
||||||
type tokenAccessOptions struct {
|
type tokenAccessOptions struct {
|
||||||
realm string
|
realm string
|
||||||
autoRedirect bool
|
autoRedirect bool
|
||||||
autoRedirectPath string
|
autoRedirectPath string
|
||||||
issuer string
|
issuer string
|
||||||
service string
|
service string
|
||||||
rootCertBundle string
|
rootCertBundle string
|
||||||
jwks string
|
jwks string
|
||||||
|
signingAlgorithms []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkOptions gathers the necessary options
|
// checkOptions gathers the necessary options
|
||||||
|
@ -206,7 +208,7 @@ func checkOptions(options map[string]interface{}) (tokenAccessOptions, error) {
|
||||||
if ok {
|
if ok {
|
||||||
autoRedirect, ok := autoRedirectVal.(bool)
|
autoRedirect, ok := autoRedirectVal.(bool)
|
||||||
if !ok {
|
if !ok {
|
||||||
return opts, fmt.Errorf("token auth requires a valid option bool: autoredirect")
|
return opts, errors.New("token auth requires a valid option bool: autoredirect")
|
||||||
}
|
}
|
||||||
opts.autoRedirect = autoRedirect
|
opts.autoRedirect = autoRedirect
|
||||||
}
|
}
|
||||||
|
@ -215,7 +217,7 @@ func checkOptions(options map[string]interface{}) (tokenAccessOptions, error) {
|
||||||
if ok {
|
if ok {
|
||||||
autoRedirectPath, ok := autoRedirectPathVal.(string)
|
autoRedirectPath, ok := autoRedirectPathVal.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return opts, fmt.Errorf("token auth requires a valid option string: autoredirectpath")
|
return opts, errors.New("token auth requires a valid option string: autoredirectpath")
|
||||||
}
|
}
|
||||||
opts.autoRedirectPath = autoRedirectPath
|
opts.autoRedirectPath = autoRedirectPath
|
||||||
}
|
}
|
||||||
|
@ -224,6 +226,15 @@ func checkOptions(options map[string]interface{}) (tokenAccessOptions, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signingAlgos, ok := options["signingalgorithms"]
|
||||||
|
if ok {
|
||||||
|
signingAlgorithmsVals, ok := signingAlgos.([]string)
|
||||||
|
if !ok {
|
||||||
|
return opts, errors.New("signingalgorithms must be a list of signing algorithms")
|
||||||
|
}
|
||||||
|
opts.signingAlgorithms = signingAlgorithmsVals
|
||||||
|
}
|
||||||
|
|
||||||
return opts, nil
|
return opts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,6 +290,18 @@ func getJwks(path string) (*jose.JSONWebKeySet, error) {
|
||||||
return &jwks, nil
|
return &jwks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getSigningAlgorithms(algos []string) ([]jose.SignatureAlgorithm, error) {
|
||||||
|
signAlgVals := make([]jose.SignatureAlgorithm, 0, len(algos))
|
||||||
|
for _, alg := range algos {
|
||||||
|
alg, ok := signingAlgorithms[alg]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unsupported signing algorithm: %s", alg)
|
||||||
|
}
|
||||||
|
signAlgVals = append(signAlgVals, alg)
|
||||||
|
}
|
||||||
|
return signAlgVals, nil
|
||||||
|
}
|
||||||
|
|
||||||
// newAccessController creates an accessController using the given options.
|
// newAccessController creates an accessController using the given options.
|
||||||
func newAccessController(options map[string]interface{}) (auth.AccessController, error) {
|
func newAccessController(options map[string]interface{}) (auth.AccessController, error) {
|
||||||
config, err := checkOptions(options)
|
config, err := checkOptions(options)
|
||||||
|
@ -289,6 +312,7 @@ func newAccessController(options map[string]interface{}) (auth.AccessController,
|
||||||
var (
|
var (
|
||||||
rootCerts []*x509.Certificate
|
rootCerts []*x509.Certificate
|
||||||
jwks *jose.JSONWebKeySet
|
jwks *jose.JSONWebKeySet
|
||||||
|
signAlgos []jose.SignatureAlgorithm
|
||||||
)
|
)
|
||||||
|
|
||||||
if config.rootCertBundle != "" {
|
if config.rootCertBundle != "" {
|
||||||
|
@ -322,14 +346,25 @@ func newAccessController(options map[string]interface{}) (auth.AccessController,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signAlgos, err = getSigningAlgorithms(config.signingAlgorithms)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(signAlgos) == 0 {
|
||||||
|
// NOTE: this is to maintain backwards compat
|
||||||
|
// with existing registry deployments
|
||||||
|
signAlgos = defaultSigningAlgorithms
|
||||||
|
}
|
||||||
|
|
||||||
return &accessController{
|
return &accessController{
|
||||||
realm: config.realm,
|
realm: config.realm,
|
||||||
autoRedirect: config.autoRedirect,
|
autoRedirect: config.autoRedirect,
|
||||||
autoRedirectPath: config.autoRedirectPath,
|
autoRedirectPath: config.autoRedirectPath,
|
||||||
issuer: config.issuer,
|
issuer: config.issuer,
|
||||||
service: config.service,
|
service: config.service,
|
||||||
rootCerts: rootPool,
|
rootCerts: rootPool,
|
||||||
trustedKeys: trustedKeys,
|
trustedKeys: trustedKeys,
|
||||||
|
signingAlgorithms: signAlgos,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,7 +385,7 @@ func (ac *accessController) Authorized(req *http.Request, accessItems ...auth.Ac
|
||||||
return nil, challenge
|
return nil, challenge
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := NewToken(rawToken)
|
token, err := NewToken(rawToken, ac.signingAlgorithms)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
challenge.err = err
|
challenge.err = err
|
||||||
return nil, challenge
|
return nil, challenge
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
fuzz "github.com/AdaLogics/go-fuzz-headers"
|
fuzz "github.com/AdaLogics/go-fuzz-headers"
|
||||||
|
"github.com/go-jose/go-jose/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
func FuzzToken1(f *testing.F) {
|
func FuzzToken1(f *testing.F) {
|
||||||
|
@ -18,7 +19,7 @@ func FuzzToken1(f *testing.F) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
token, err := NewToken(rawToken)
|
token, err := NewToken(rawToken, []jose.SignatureAlgorithm{jose.EdDSA, jose.RS384})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-jose/go-jose/v3"
|
"github.com/go-jose/go-jose/v4"
|
||||||
"github.com/go-jose/go-jose/v3/jwt"
|
"github.com/go-jose/go-jose/v4/jwt"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/distribution/distribution/v3/registry/auth"
|
"github.com/distribution/distribution/v3/registry/auth"
|
||||||
|
@ -23,6 +23,38 @@ const (
|
||||||
Leeway = 60 * time.Second
|
Leeway = 60 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var signingAlgorithms = map[string]jose.SignatureAlgorithm{
|
||||||
|
"EdDSA": jose.EdDSA,
|
||||||
|
"HS256": jose.HS256,
|
||||||
|
"HS384": jose.HS384,
|
||||||
|
"HS512": jose.HS512,
|
||||||
|
"RS256": jose.RS256,
|
||||||
|
"RS384": jose.RS384,
|
||||||
|
"RS512": jose.RS512,
|
||||||
|
"ES256": jose.ES256,
|
||||||
|
"ES384": jose.ES384,
|
||||||
|
"ES512": jose.ES512,
|
||||||
|
"PS256": jose.PS256,
|
||||||
|
"PS384": jose.PS384,
|
||||||
|
"PS512": jose.PS512,
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultSigningAlgorithms = []jose.SignatureAlgorithm{
|
||||||
|
jose.EdDSA,
|
||||||
|
jose.HS256,
|
||||||
|
jose.HS384,
|
||||||
|
jose.HS512,
|
||||||
|
jose.RS256,
|
||||||
|
jose.RS384,
|
||||||
|
jose.RS512,
|
||||||
|
jose.ES256,
|
||||||
|
jose.ES384,
|
||||||
|
jose.ES512,
|
||||||
|
jose.PS256,
|
||||||
|
jose.PS384,
|
||||||
|
jose.PS512,
|
||||||
|
}
|
||||||
|
|
||||||
// Errors used by token parsing and verification.
|
// Errors used by token parsing and verification.
|
||||||
var (
|
var (
|
||||||
ErrMalformedToken = errors.New("malformed token")
|
ErrMalformedToken = errors.New("malformed token")
|
||||||
|
@ -69,8 +101,8 @@ type VerifyOptions struct {
|
||||||
|
|
||||||
// NewToken parses the given raw token string
|
// NewToken parses the given raw token string
|
||||||
// and constructs an unverified JSON Web Token.
|
// and constructs an unverified JSON Web Token.
|
||||||
func NewToken(rawToken string) (*Token, error) {
|
func NewToken(rawToken string, signingAlgs []jose.SignatureAlgorithm) (*Token, error) {
|
||||||
token, err := jwt.ParseSigned(rawToken)
|
token, err := jwt.ParseSigned(rawToken, signingAlgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ErrMalformedToken
|
return nil, ErrMalformedToken
|
||||||
}
|
}
|
||||||
|
@ -140,6 +172,13 @@ func (t *Token) VerifySigningKey(verifyOpts VerifyOptions) (signingKey crypto.Pu
|
||||||
// verifying the first one in the list only at the moment.
|
// verifying the first one in the list only at the moment.
|
||||||
header := t.JWT.Headers[0]
|
header := t.JWT.Headers[0]
|
||||||
|
|
||||||
|
signingKey, err = verifyCertChain(header, verifyOpts.Roots)
|
||||||
|
// NOTE(milosgajdos): if the x5c header is missing
|
||||||
|
// the token may have been signed by a JWKS.
|
||||||
|
if err != nil && err != jose.ErrMissingX5cHeader {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case header.JSONWebKey != nil:
|
case header.JSONWebKey != nil:
|
||||||
signingKey, err = verifyJWK(header, verifyOpts)
|
signingKey, err = verifyJWK(header, verifyOpts)
|
||||||
|
@ -149,7 +188,7 @@ func (t *Token) VerifySigningKey(verifyOpts VerifyOptions) (signingKey crypto.Pu
|
||||||
err = fmt.Errorf("token signed by untrusted key with ID: %q", header.KeyID)
|
err = fmt.Errorf("token signed by untrusted key with ID: %q", header.KeyID)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
signingKey, err = verifyCertChain(header, verifyOpts.Roots)
|
err = ErrInvalidToken
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
|
@ -19,8 +19,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/distribution/distribution/v3/registry/auth"
|
"github.com/distribution/distribution/v3/registry/auth"
|
||||||
"github.com/go-jose/go-jose/v3"
|
"github.com/go-jose/go-jose/v4"
|
||||||
"github.com/go-jose/go-jose/v3/jwt"
|
"github.com/go-jose/go-jose/v4/jwt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func makeRootKeys(numKeys int) ([]*ecdsa.PrivateKey, error) {
|
func makeRootKeys(numKeys int) ([]*ecdsa.PrivateKey, error) {
|
||||||
|
@ -123,12 +123,12 @@ func makeTestToken(jwk *jose.JSONWebKey, issuer, audience string, access []*Reso
|
||||||
Access: access,
|
Access: access,
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenString, err := jwt.Signed(signer).Claims(claimSet).CompactSerialize()
|
tokenString, err := jwt.Signed(signer).Claims(claimSet).Serialize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to build token string: %v", err)
|
return nil, fmt.Errorf("unable to build token string: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewToken(tokenString)
|
return NewToken(tokenString, []jose.SignatureAlgorithm{signingKey.Algorithm})
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE(milosgajdos): certTemplateInfo type as well
|
// NOTE(milosgajdos): certTemplateInfo type as well
|
||||||
|
|
133
vendor/github.com/go-jose/go-jose/v3/jwt/jwt.go
generated
vendored
133
vendor/github.com/go-jose/go-jose/v3/jwt/jwt.go
generated
vendored
|
@ -1,133 +0,0 @@
|
||||||
/*-
|
|
||||||
* Copyright 2016 Zbigniew Mandziejewicz
|
|
||||||
* Copyright 2016 Square, Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package jwt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
jose "github.com/go-jose/go-jose/v3"
|
|
||||||
"github.com/go-jose/go-jose/v3/json"
|
|
||||||
)
|
|
||||||
|
|
||||||
// JSONWebToken represents a JSON Web Token (as specified in RFC7519).
|
|
||||||
type JSONWebToken struct {
|
|
||||||
payload func(k interface{}) ([]byte, error)
|
|
||||||
unverifiedPayload func() []byte
|
|
||||||
Headers []jose.Header
|
|
||||||
}
|
|
||||||
|
|
||||||
type NestedJSONWebToken struct {
|
|
||||||
enc *jose.JSONWebEncryption
|
|
||||||
Headers []jose.Header
|
|
||||||
}
|
|
||||||
|
|
||||||
// Claims deserializes a JSONWebToken into dest using the provided key.
|
|
||||||
func (t *JSONWebToken) Claims(key interface{}, dest ...interface{}) error {
|
|
||||||
b, err := t.payload(key)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, d := range dest {
|
|
||||||
if err := json.Unmarshal(b, d); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnsafeClaimsWithoutVerification deserializes the claims of a
|
|
||||||
// JSONWebToken into the dests. For signed JWTs, the claims are not
|
|
||||||
// verified. This function won't work for encrypted JWTs.
|
|
||||||
func (t *JSONWebToken) UnsafeClaimsWithoutVerification(dest ...interface{}) error {
|
|
||||||
if t.unverifiedPayload == nil {
|
|
||||||
return fmt.Errorf("go-jose/go-jose: Cannot get unverified claims")
|
|
||||||
}
|
|
||||||
claims := t.unverifiedPayload()
|
|
||||||
for _, d := range dest {
|
|
||||||
if err := json.Unmarshal(claims, d); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *NestedJSONWebToken) Decrypt(decryptionKey interface{}) (*JSONWebToken, error) {
|
|
||||||
b, err := t.enc.Decrypt(decryptionKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
sig, err := ParseSigned(string(b))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sig, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseSigned parses token from JWS form.
|
|
||||||
func ParseSigned(s string) (*JSONWebToken, error) {
|
|
||||||
sig, err := jose.ParseSigned(s)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
headers := make([]jose.Header, len(sig.Signatures))
|
|
||||||
for i, signature := range sig.Signatures {
|
|
||||||
headers[i] = signature.Header
|
|
||||||
}
|
|
||||||
|
|
||||||
return &JSONWebToken{
|
|
||||||
payload: sig.Verify,
|
|
||||||
unverifiedPayload: sig.UnsafePayloadWithoutVerification,
|
|
||||||
Headers: headers,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseEncrypted parses token from JWE form.
|
|
||||||
func ParseEncrypted(s string) (*JSONWebToken, error) {
|
|
||||||
enc, err := jose.ParseEncrypted(s)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &JSONWebToken{
|
|
||||||
payload: enc.Decrypt,
|
|
||||||
Headers: []jose.Header{enc.Header},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseSignedAndEncrypted parses signed-then-encrypted token from JWE form.
|
|
||||||
func ParseSignedAndEncrypted(s string) (*NestedJSONWebToken, error) {
|
|
||||||
enc, err := jose.ParseEncrypted(s)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
contentType, _ := enc.Header.ExtraHeaders[jose.HeaderContentType].(string)
|
|
||||||
if strings.ToUpper(contentType) != "JWT" {
|
|
||||||
return nil, ErrInvalidContentType
|
|
||||||
}
|
|
||||||
|
|
||||||
return &NestedJSONWebToken{
|
|
||||||
enc: enc,
|
|
||||||
Headers: []jose.Header{enc.Header},
|
|
||||||
}, nil
|
|
||||||
}
|
|
|
@ -45,12 +45,6 @@ token".
|
||||||
|
|
||||||
[1]: https://i.blackhat.com/BH-US-23/Presentations/US-23-Tervoort-Three-New-Attacks-Against-JSON-Web-Tokens.pdf
|
[1]: https://i.blackhat.com/BH-US-23/Presentations/US-23-Tervoort-Three-New-Attacks-Against-JSON-Web-Tokens.pdf
|
||||||
|
|
||||||
# v3.0.3
|
|
||||||
|
|
||||||
## Fixed
|
|
||||||
|
|
||||||
- Limit decompression output size to prevent a DoS. Backport from v4.0.1.
|
|
||||||
|
|
||||||
# v3.0.2
|
# v3.0.2
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
|
@ -1,17 +1,9 @@
|
||||||
# Go JOSE
|
# Go JOSE
|
||||||
|
|
||||||
### Versions
|
[![godoc](https://pkg.go.dev/badge/github.com/go-jose/go-jose/v4.svg)](https://pkg.go.dev/github.com/go-jose/go-jose/v4)
|
||||||
|
[![godoc](https://pkg.go.dev/badge/github.com/go-jose/go-jose/v4/jwt.svg)](https://pkg.go.dev/github.com/go-jose/go-jose/v4/jwt)
|
||||||
[Version 4](https://github.com/go-jose/go-jose)
|
[![license](https://img.shields.io/badge/license-apache_2.0-blue.svg?style=flat)](https://raw.githubusercontent.com/go-jose/go-jose/master/LICENSE)
|
||||||
([branch](https://github.com/go-jose/go-jose/),
|
[![test](https://img.shields.io/github/checks-status/go-jose/go-jose/v4)](https://github.com/go-jose/go-jose/actions)
|
||||||
[doc](https://pkg.go.dev/github.com/go-jose/go-jose/v4), [releases](https://github.com/go-jose/go-jose/releases)) is the current stable version:
|
|
||||||
|
|
||||||
import "github.com/go-jose/go-jose/v4"
|
|
||||||
|
|
||||||
The old [square/go-jose](https://github.com/square/go-jose) repo contains the prior v1 and v2 versions, which
|
|
||||||
are deprecated.
|
|
||||||
|
|
||||||
### Summary
|
|
||||||
|
|
||||||
Package jose aims to provide an implementation of the Javascript Object Signing
|
Package jose aims to provide an implementation of the Javascript Object Signing
|
||||||
and Encryption set of standards. This includes support for JSON Web Encryption,
|
and Encryption set of standards. This includes support for JSON Web Encryption,
|
||||||
|
@ -43,6 +35,20 @@ of [case-insensitive matching](https://www.ietf.org/mail-archive/web/json/curren
|
||||||
This is to avoid differences in interpretation of messages between go-jose and
|
This is to avoid differences in interpretation of messages between go-jose and
|
||||||
libraries in other languages.
|
libraries in other languages.
|
||||||
|
|
||||||
|
### Versions
|
||||||
|
|
||||||
|
[Version 4](https://github.com/go-jose/go-jose)
|
||||||
|
([branch](https://github.com/go-jose/go-jose/tree/main),
|
||||||
|
[doc](https://pkg.go.dev/github.com/go-jose/go-jose/v4), [releases](https://github.com/go-jose/go-jose/releases)) is the current stable version:
|
||||||
|
|
||||||
|
import "github.com/go-jose/go-jose/v4"
|
||||||
|
|
||||||
|
The old [square/go-jose](https://github.com/square/go-jose) repo contains the prior v1 and v2 versions, which
|
||||||
|
are still useable but not actively developed anymore.
|
||||||
|
|
||||||
|
Version 3, in this repo, is still receiving security fixes but not functionality
|
||||||
|
updates.
|
||||||
|
|
||||||
### Supported algorithms
|
### Supported algorithms
|
||||||
|
|
||||||
See below for a table of supported algorithms. Algorithm identifiers match
|
See below for a table of supported algorithms. Algorithm identifiers match
|
||||||
|
@ -98,11 +104,11 @@ allows attaching a key id.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
[![godoc](https://pkg.go.dev/badge/github.com/go-jose/go-jose/v3.svg)](https://pkg.go.dev/github.com/go-jose/go-jose/v3)
|
[![godoc](https://pkg.go.dev/badge/github.com/go-jose/go-jose/v4.svg)](https://pkg.go.dev/github.com/go-jose/go-jose/v4)
|
||||||
[![godoc](https://pkg.go.dev/badge/github.com/go-jose/go-jose/v3/jwt.svg)](https://pkg.go.dev/github.com/go-jose/go-jose/v3/jwt)
|
[![godoc](https://pkg.go.dev/badge/github.com/go-jose/go-jose/v4/jwt.svg)](https://pkg.go.dev/github.com/go-jose/go-jose/v4/jwt)
|
||||||
|
|
||||||
Examples can be found in the Godoc
|
Examples can be found in the Godoc
|
||||||
reference for this package. The
|
reference for this package. The
|
||||||
[`jose-util`](https://github.com/go-jose/go-jose/tree/v3/jose-util)
|
[`jose-util`](https://github.com/go-jose/go-jose/tree/v4/jose-util)
|
||||||
subdirectory also contains a small command-line utility which might be useful
|
subdirectory also contains a small command-line utility which might be useful
|
||||||
as an example as well.
|
as an example as well.
|
|
@ -29,8 +29,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
josecipher "github.com/go-jose/go-jose/v3/cipher"
|
josecipher "github.com/go-jose/go-jose/v4/cipher"
|
||||||
"github.com/go-jose/go-jose/v3/json"
|
"github.com/go-jose/go-jose/v4/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A generic RSA-based encrypter/verifier
|
// A generic RSA-based encrypter/verifier
|
|
@ -22,7 +22,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/go-jose/go-jose/v3/json"
|
"github.com/go-jose/go-jose/v4/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Encrypter represents an encrypter which produces an encrypted JWE object.
|
// Encrypter represents an encrypter which produces an encrypted JWE object.
|
|
@ -27,7 +27,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"github.com/go-jose/go-jose/v3/json"
|
"github.com/go-jose/go-jose/v4/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Helper function to serialize known-good objects.
|
// Helper function to serialize known-good objects.
|
||||||
|
@ -106,10 +106,7 @@ func inflate(input []byte) ([]byte, error) {
|
||||||
output := new(bytes.Buffer)
|
output := new(bytes.Buffer)
|
||||||
reader := flate.NewReader(bytes.NewBuffer(input))
|
reader := flate.NewReader(bytes.NewBuffer(input))
|
||||||
|
|
||||||
maxCompressedSize := 10 * int64(len(input))
|
maxCompressedSize := max(250_000, 10*int64(len(input)))
|
||||||
if maxCompressedSize < 250000 {
|
|
||||||
maxCompressedSize = 250000
|
|
||||||
}
|
|
||||||
|
|
||||||
limit := maxCompressedSize + 1
|
limit := maxCompressedSize + 1
|
||||||
n, err := io.CopyN(output, reader, limit)
|
n, err := io.CopyN(output, reader, limit)
|
||||||
|
@ -167,7 +164,7 @@ func (b *byteBuffer) UnmarshalJSON(data []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
decoded, err := base64URLDecode(encoded)
|
decoded, err := base64.RawURLEncoding.DecodeString(encoded)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -197,12 +194,6 @@ func (b byteBuffer) toInt() int {
|
||||||
return int(b.bigInt().Int64())
|
return int(b.bigInt().Int64())
|
||||||
}
|
}
|
||||||
|
|
||||||
// base64URLDecode is implemented as defined in https://www.rfc-editor.org/rfc/rfc7515.html#appendix-C
|
|
||||||
func base64URLDecode(value string) ([]byte, error) {
|
|
||||||
value = strings.TrimRight(value, "=")
|
|
||||||
return base64.RawURLEncoding.DecodeString(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func base64EncodeLen(sl []byte) int {
|
func base64EncodeLen(sl []byte) int {
|
||||||
return base64.RawURLEncoding.EncodedLen(len(sl))
|
return base64.RawURLEncoding.EncodedLen(len(sl))
|
||||||
}
|
}
|
134
vendor/github.com/go-jose/go-jose/v3/jwe.go → vendor/github.com/go-jose/go-jose/v4/jwe.go
generated
vendored
134
vendor/github.com/go-jose/go-jose/v3/jwe.go → vendor/github.com/go-jose/go-jose/v4/jwe.go
generated
vendored
|
@ -18,10 +18,11 @@ package jose
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-jose/go-jose/v3/json"
|
"github.com/go-jose/go-jose/v4/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
// rawJSONWebEncryption represents a raw JWE JSON object. Used for parsing/serializing.
|
// rawJSONWebEncryption represents a raw JWE JSON object. Used for parsing/serializing.
|
||||||
|
@ -104,29 +105,75 @@ func (obj JSONWebEncryption) computeAuthData() []byte {
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseEncrypted parses an encrypted message in compact or JWE JSON Serialization format.
|
func containsKeyAlgorithm(haystack []KeyAlgorithm, needle KeyAlgorithm) bool {
|
||||||
func ParseEncrypted(input string) (*JSONWebEncryption, error) {
|
for _, algorithm := range haystack {
|
||||||
input = stripWhitespace(input)
|
if algorithm == needle {
|
||||||
if strings.HasPrefix(input, "{") {
|
return true
|
||||||
return parseEncryptedFull(input)
|
}
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
return parseEncryptedCompact(input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseEncryptedFull parses a message in compact format.
|
func containsContentEncryption(haystack []ContentEncryption, needle ContentEncryption) bool {
|
||||||
func parseEncryptedFull(input string) (*JSONWebEncryption, error) {
|
for _, algorithm := range haystack {
|
||||||
|
if algorithm == needle {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseEncrypted parses an encrypted message in JWE Compact or JWE JSON Serialization.
|
||||||
|
//
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc7516#section-3.1
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc7516#section-3.2
|
||||||
|
//
|
||||||
|
// The keyAlgorithms and contentEncryption parameters are used to validate the "alg" and "enc"
|
||||||
|
// header parameters respectively. They must be nonempty, and each "alg" or "enc" header in
|
||||||
|
// parsed data must contain a value that is present in the corresponding parameter. That
|
||||||
|
// includes the protected and unprotected headers as well as all recipients. To accept
|
||||||
|
// multiple algorithms, pass a slice of all the algorithms you want to accept.
|
||||||
|
func ParseEncrypted(input string,
|
||||||
|
keyEncryptionAlgorithms []KeyAlgorithm,
|
||||||
|
contentEncryption []ContentEncryption,
|
||||||
|
) (*JSONWebEncryption, error) {
|
||||||
|
input = stripWhitespace(input)
|
||||||
|
if strings.HasPrefix(input, "{") {
|
||||||
|
return ParseEncryptedJSON(input, keyEncryptionAlgorithms, contentEncryption)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ParseEncryptedCompact(input, keyEncryptionAlgorithms, contentEncryption)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseEncryptedJSON parses a message in JWE JSON Serialization.
|
||||||
|
//
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc7516#section-3.2
|
||||||
|
func ParseEncryptedJSON(
|
||||||
|
input string,
|
||||||
|
keyEncryptionAlgorithms []KeyAlgorithm,
|
||||||
|
contentEncryption []ContentEncryption,
|
||||||
|
) (*JSONWebEncryption, error) {
|
||||||
var parsed rawJSONWebEncryption
|
var parsed rawJSONWebEncryption
|
||||||
err := json.Unmarshal([]byte(input), &parsed)
|
err := json.Unmarshal([]byte(input), &parsed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return parsed.sanitized()
|
return parsed.sanitized(keyEncryptionAlgorithms, contentEncryption)
|
||||||
}
|
}
|
||||||
|
|
||||||
// sanitized produces a cleaned-up JWE object from the raw JSON.
|
// sanitized produces a cleaned-up JWE object from the raw JSON.
|
||||||
func (parsed *rawJSONWebEncryption) sanitized() (*JSONWebEncryption, error) {
|
func (parsed *rawJSONWebEncryption) sanitized(
|
||||||
|
keyEncryptionAlgorithms []KeyAlgorithm,
|
||||||
|
contentEncryption []ContentEncryption,
|
||||||
|
) (*JSONWebEncryption, error) {
|
||||||
|
if len(keyEncryptionAlgorithms) == 0 {
|
||||||
|
return nil, errors.New("go-jose/go-jose: no key algorithms provided")
|
||||||
|
}
|
||||||
|
if len(contentEncryption) == 0 {
|
||||||
|
return nil, errors.New("go-jose/go-jose: no content encryption algorithms provided")
|
||||||
|
}
|
||||||
|
|
||||||
obj := &JSONWebEncryption{
|
obj := &JSONWebEncryption{
|
||||||
original: parsed,
|
original: parsed,
|
||||||
unprotected: parsed.Unprotected,
|
unprotected: parsed.Unprotected,
|
||||||
|
@ -170,7 +217,7 @@ func (parsed *rawJSONWebEncryption) sanitized() (*JSONWebEncryption, error) {
|
||||||
} else {
|
} else {
|
||||||
obj.recipients = make([]recipientInfo, len(parsed.Recipients))
|
obj.recipients = make([]recipientInfo, len(parsed.Recipients))
|
||||||
for r := range parsed.Recipients {
|
for r := range parsed.Recipients {
|
||||||
encryptedKey, err := base64URLDecode(parsed.Recipients[r].EncryptedKey)
|
encryptedKey, err := base64.RawURLEncoding.DecodeString(parsed.Recipients[r].EncryptedKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -185,10 +232,31 @@ func (parsed *rawJSONWebEncryption) sanitized() (*JSONWebEncryption, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, recipient := range obj.recipients {
|
for i, recipient := range obj.recipients {
|
||||||
headers := obj.mergedHeaders(&recipient)
|
headers := obj.mergedHeaders(&recipient)
|
||||||
if headers.getAlgorithm() == "" || headers.getEncryption() == "" {
|
if headers.getAlgorithm() == "" {
|
||||||
return nil, fmt.Errorf("go-jose/go-jose: message is missing alg/enc headers")
|
return nil, fmt.Errorf(`go-jose/go-jose: recipient %d: missing header "alg"`, i)
|
||||||
|
}
|
||||||
|
if headers.getEncryption() == "" {
|
||||||
|
return nil, fmt.Errorf(`go-jose/go-jose: recipient %d: missing header "enc"`, i)
|
||||||
|
}
|
||||||
|
err := validateAlgEnc(headers, keyEncryptionAlgorithms, contentEncryption)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("go-jose/go-jose: recipient %d: %s", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if obj.protected != nil {
|
||||||
|
err := validateAlgEnc(*obj.protected, keyEncryptionAlgorithms, contentEncryption)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("go-jose/go-jose: protected header: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if obj.unprotected != nil {
|
||||||
|
err := validateAlgEnc(*obj.unprotected, keyEncryptionAlgorithms, contentEncryption)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("go-jose/go-jose: unprotected header: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,34 +268,52 @@ func (parsed *rawJSONWebEncryption) sanitized() (*JSONWebEncryption, error) {
|
||||||
return obj, nil
|
return obj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseEncryptedCompact parses a message in compact format.
|
func validateAlgEnc(headers rawHeader, keyAlgorithms []KeyAlgorithm, contentEncryption []ContentEncryption) error {
|
||||||
func parseEncryptedCompact(input string) (*JSONWebEncryption, error) {
|
alg := headers.getAlgorithm()
|
||||||
|
enc := headers.getEncryption()
|
||||||
|
if alg != "" && !containsKeyAlgorithm(keyAlgorithms, alg) {
|
||||||
|
return fmt.Errorf("unexpected key algorithm %q; expected %q", alg, keyAlgorithms)
|
||||||
|
}
|
||||||
|
if alg != "" && !containsContentEncryption(contentEncryption, enc) {
|
||||||
|
return fmt.Errorf("unexpected content encryption algorithm %q; expected %q", enc, contentEncryption)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseEncryptedCompact parses a message in JWE Compact Serialization.
|
||||||
|
//
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc7516#section-3.1
|
||||||
|
func ParseEncryptedCompact(
|
||||||
|
input string,
|
||||||
|
keyAlgorithms []KeyAlgorithm,
|
||||||
|
contentEncryption []ContentEncryption,
|
||||||
|
) (*JSONWebEncryption, error) {
|
||||||
parts := strings.Split(input, ".")
|
parts := strings.Split(input, ".")
|
||||||
if len(parts) != 5 {
|
if len(parts) != 5 {
|
||||||
return nil, fmt.Errorf("go-jose/go-jose: compact JWE format must have five parts")
|
return nil, fmt.Errorf("go-jose/go-jose: compact JWE format must have five parts")
|
||||||
}
|
}
|
||||||
|
|
||||||
rawProtected, err := base64URLDecode(parts[0])
|
rawProtected, err := base64.RawURLEncoding.DecodeString(parts[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
encryptedKey, err := base64URLDecode(parts[1])
|
encryptedKey, err := base64.RawURLEncoding.DecodeString(parts[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
iv, err := base64URLDecode(parts[2])
|
iv, err := base64.RawURLEncoding.DecodeString(parts[2])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ciphertext, err := base64URLDecode(parts[3])
|
ciphertext, err := base64.RawURLEncoding.DecodeString(parts[3])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tag, err := base64URLDecode(parts[4])
|
tag, err := base64.RawURLEncoding.DecodeString(parts[4])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -240,7 +326,7 @@ func parseEncryptedCompact(input string) (*JSONWebEncryption, error) {
|
||||||
Tag: newBuffer(tag),
|
Tag: newBuffer(tag),
|
||||||
}
|
}
|
||||||
|
|
||||||
return raw.sanitized()
|
return raw.sanitized(keyAlgorithms, contentEncryption)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompactSerialize serializes an object using the compact serialization format.
|
// CompactSerialize serializes an object using the compact serialization format.
|
|
@ -35,7 +35,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-jose/go-jose/v3/json"
|
"github.com/go-jose/go-jose/v4/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
// rawJSONWebKey represents a public or private key in JWK format, used for parsing/serializing.
|
// rawJSONWebKey represents a public or private key in JWK format, used for parsing/serializing.
|
||||||
|
@ -266,7 +266,7 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) {
|
||||||
|
|
||||||
// x5t parameters are base64url-encoded SHA thumbprints
|
// x5t parameters are base64url-encoded SHA thumbprints
|
||||||
// See RFC 7517, Section 4.8, https://tools.ietf.org/html/rfc7517#section-4.8
|
// See RFC 7517, Section 4.8, https://tools.ietf.org/html/rfc7517#section-4.8
|
||||||
x5tSHA1bytes, err := base64URLDecode(raw.X5tSHA1)
|
x5tSHA1bytes, err := base64.RawURLEncoding.DecodeString(raw.X5tSHA1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("go-jose/go-jose: invalid JWK, x5t header has invalid encoding")
|
return errors.New("go-jose/go-jose: invalid JWK, x5t header has invalid encoding")
|
||||||
}
|
}
|
||||||
|
@ -286,7 +286,7 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) {
|
||||||
|
|
||||||
k.CertificateThumbprintSHA1 = x5tSHA1bytes
|
k.CertificateThumbprintSHA1 = x5tSHA1bytes
|
||||||
|
|
||||||
x5tSHA256bytes, err := base64URLDecode(raw.X5tSHA256)
|
x5tSHA256bytes, err := base64.RawURLEncoding.DecodeString(raw.X5tSHA256)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("go-jose/go-jose: invalid JWK, x5t#S256 header has invalid encoding")
|
return errors.New("go-jose/go-jose: invalid JWK, x5t#S256 header has invalid encoding")
|
||||||
}
|
}
|
|
@ -23,7 +23,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-jose/go-jose/v3/json"
|
"github.com/go-jose/go-jose/v4/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
// rawJSONWebSignature represents a raw JWS JSON object. Used for parsing/serializing.
|
// rawJSONWebSignature represents a raw JWS JSON object. Used for parsing/serializing.
|
||||||
|
@ -75,22 +75,41 @@ type Signature struct {
|
||||||
original *rawSignatureInfo
|
original *rawSignatureInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseSigned parses a signed message in compact or JWS JSON Serialization format.
|
// ParseSigned parses a signed message in JWS Compact or JWS JSON Serialization.
|
||||||
func ParseSigned(signature string) (*JSONWebSignature, error) {
|
//
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc7515#section-7
|
||||||
|
func ParseSigned(
|
||||||
|
signature string,
|
||||||
|
signatureAlgorithms []SignatureAlgorithm,
|
||||||
|
) (*JSONWebSignature, error) {
|
||||||
signature = stripWhitespace(signature)
|
signature = stripWhitespace(signature)
|
||||||
if strings.HasPrefix(signature, "{") {
|
if strings.HasPrefix(signature, "{") {
|
||||||
return parseSignedFull(signature)
|
return ParseSignedJSON(signature, signatureAlgorithms)
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseSignedCompact(signature, nil)
|
return parseSignedCompact(signature, nil, signatureAlgorithms)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseSignedCompact parses a message in JWS Compact Serialization.
|
||||||
|
//
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc7515#section-7.1
|
||||||
|
func ParseSignedCompact(
|
||||||
|
signature string,
|
||||||
|
signatureAlgorithms []SignatureAlgorithm,
|
||||||
|
) (*JSONWebSignature, error) {
|
||||||
|
return parseSignedCompact(signature, nil, signatureAlgorithms)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseDetached parses a signed message in compact serialization format with detached payload.
|
// ParseDetached parses a signed message in compact serialization format with detached payload.
|
||||||
func ParseDetached(signature string, payload []byte) (*JSONWebSignature, error) {
|
func ParseDetached(
|
||||||
|
signature string,
|
||||||
|
payload []byte,
|
||||||
|
signatureAlgorithms []SignatureAlgorithm,
|
||||||
|
) (*JSONWebSignature, error) {
|
||||||
if payload == nil {
|
if payload == nil {
|
||||||
return nil, errors.New("go-jose/go-jose: nil payload")
|
return nil, errors.New("go-jose/go-jose: nil payload")
|
||||||
}
|
}
|
||||||
return parseSignedCompact(stripWhitespace(signature), payload)
|
return parseSignedCompact(stripWhitespace(signature), payload, signatureAlgorithms)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a header value
|
// Get a header value
|
||||||
|
@ -137,19 +156,36 @@ func (obj JSONWebSignature) computeAuthData(payload []byte, signature *Signature
|
||||||
return authData.Bytes(), nil
|
return authData.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseSignedFull parses a message in full format.
|
// ParseSignedJSON parses a message in JWS JSON Serialization.
|
||||||
func parseSignedFull(input string) (*JSONWebSignature, error) {
|
//
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc7515#section-7.2
|
||||||
|
func ParseSignedJSON(
|
||||||
|
input string,
|
||||||
|
signatureAlgorithms []SignatureAlgorithm,
|
||||||
|
) (*JSONWebSignature, error) {
|
||||||
var parsed rawJSONWebSignature
|
var parsed rawJSONWebSignature
|
||||||
err := json.Unmarshal([]byte(input), &parsed)
|
err := json.Unmarshal([]byte(input), &parsed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return parsed.sanitized()
|
return parsed.sanitized(signatureAlgorithms)
|
||||||
|
}
|
||||||
|
|
||||||
|
func containsSignatureAlgorithm(haystack []SignatureAlgorithm, needle SignatureAlgorithm) bool {
|
||||||
|
for _, algorithm := range haystack {
|
||||||
|
if algorithm == needle {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// sanitized produces a cleaned-up JWS object from the raw JSON.
|
// sanitized produces a cleaned-up JWS object from the raw JSON.
|
||||||
func (parsed *rawJSONWebSignature) sanitized() (*JSONWebSignature, error) {
|
func (parsed *rawJSONWebSignature) sanitized(signatureAlgorithms []SignatureAlgorithm) (*JSONWebSignature, error) {
|
||||||
|
if len(signatureAlgorithms) == 0 {
|
||||||
|
return nil, errors.New("go-jose/go-jose: no signature algorithms specified")
|
||||||
|
}
|
||||||
if parsed.Payload == nil {
|
if parsed.Payload == nil {
|
||||||
return nil, fmt.Errorf("go-jose/go-jose: missing payload in JWS message")
|
return nil, fmt.Errorf("go-jose/go-jose: missing payload in JWS message")
|
||||||
}
|
}
|
||||||
|
@ -198,6 +234,12 @@ func (parsed *rawJSONWebSignature) sanitized() (*JSONWebSignature, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
alg := SignatureAlgorithm(signature.Header.Algorithm)
|
||||||
|
if !containsSignatureAlgorithm(signatureAlgorithms, alg) {
|
||||||
|
return nil, fmt.Errorf("go-jose/go-jose: unexpected signature algorithm %q; expected %q",
|
||||||
|
alg, signatureAlgorithms)
|
||||||
|
}
|
||||||
|
|
||||||
if signature.header != nil {
|
if signature.header != nil {
|
||||||
signature.Unprotected, err = signature.header.sanitized()
|
signature.Unprotected, err = signature.header.sanitized()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -241,6 +283,12 @@ func (parsed *rawJSONWebSignature) sanitized() (*JSONWebSignature, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
alg := SignatureAlgorithm(obj.Signatures[i].Header.Algorithm)
|
||||||
|
if !containsSignatureAlgorithm(signatureAlgorithms, alg) {
|
||||||
|
return nil, fmt.Errorf("go-jose/go-jose: unexpected signature algorithm %q; expected %q",
|
||||||
|
alg, signatureAlgorithms)
|
||||||
|
}
|
||||||
|
|
||||||
if obj.Signatures[i].header != nil {
|
if obj.Signatures[i].header != nil {
|
||||||
obj.Signatures[i].Unprotected, err = obj.Signatures[i].header.sanitized()
|
obj.Signatures[i].Unprotected, err = obj.Signatures[i].header.sanitized()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -274,7 +322,11 @@ func (parsed *rawJSONWebSignature) sanitized() (*JSONWebSignature, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseSignedCompact parses a message in compact format.
|
// parseSignedCompact parses a message in compact format.
|
||||||
func parseSignedCompact(input string, payload []byte) (*JSONWebSignature, error) {
|
func parseSignedCompact(
|
||||||
|
input string,
|
||||||
|
payload []byte,
|
||||||
|
signatureAlgorithms []SignatureAlgorithm,
|
||||||
|
) (*JSONWebSignature, error) {
|
||||||
parts := strings.Split(input, ".")
|
parts := strings.Split(input, ".")
|
||||||
if len(parts) != 3 {
|
if len(parts) != 3 {
|
||||||
return nil, fmt.Errorf("go-jose/go-jose: compact JWS format must have three parts")
|
return nil, fmt.Errorf("go-jose/go-jose: compact JWS format must have three parts")
|
||||||
|
@ -284,19 +336,19 @@ func parseSignedCompact(input string, payload []byte) (*JSONWebSignature, error)
|
||||||
return nil, fmt.Errorf("go-jose/go-jose: payload is not detached")
|
return nil, fmt.Errorf("go-jose/go-jose: payload is not detached")
|
||||||
}
|
}
|
||||||
|
|
||||||
rawProtected, err := base64URLDecode(parts[0])
|
rawProtected, err := base64.RawURLEncoding.DecodeString(parts[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if payload == nil {
|
if payload == nil {
|
||||||
payload, err = base64URLDecode(parts[1])
|
payload, err = base64.RawURLEncoding.DecodeString(parts[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
signature, err := base64URLDecode(parts[2])
|
signature, err := base64.RawURLEncoding.DecodeString(parts[2])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -306,7 +358,7 @@ func parseSignedCompact(input string, payload []byte) (*JSONWebSignature, error)
|
||||||
Protected: newBuffer(rawProtected),
|
Protected: newBuffer(rawProtected),
|
||||||
Signature: newBuffer(signature),
|
Signature: newBuffer(signature),
|
||||||
}
|
}
|
||||||
return raw.sanitized()
|
return raw.sanitized(signatureAlgorithms)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (obj JSONWebSignature) compactSerialize(detached bool) (string, error) {
|
func (obj JSONWebSignature) compactSerialize(detached bool) (string, error) {
|
|
@ -21,13 +21,13 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/go-jose/go-jose/v3/json"
|
"github.com/go-jose/go-jose/v4/json"
|
||||||
|
|
||||||
"github.com/go-jose/go-jose/v3"
|
"github.com/go-jose/go-jose/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Builder is a utility for making JSON Web Tokens. Calls can be chained, and
|
// Builder is a utility for making JSON Web Tokens. Calls can be chained, and
|
||||||
// errors are accumulated until the final call to CompactSerialize/FullSerialize.
|
// errors are accumulated until the final call to Serialize.
|
||||||
type Builder interface {
|
type Builder interface {
|
||||||
// Claims encodes claims into JWE/JWS form. Multiple calls will merge claims
|
// Claims encodes claims into JWE/JWS form. Multiple calls will merge claims
|
||||||
// into single JSON object. If you are passing private claims, make sure to set
|
// into single JSON object. If you are passing private claims, make sure to set
|
||||||
|
@ -36,15 +36,13 @@ type Builder interface {
|
||||||
Claims(i interface{}) Builder
|
Claims(i interface{}) Builder
|
||||||
// Token builds a JSONWebToken from provided data.
|
// Token builds a JSONWebToken from provided data.
|
||||||
Token() (*JSONWebToken, error)
|
Token() (*JSONWebToken, error)
|
||||||
// FullSerialize serializes a token using the JWS/JWE JSON Serialization format.
|
// Serialize serializes a token.
|
||||||
FullSerialize() (string, error)
|
Serialize() (string, error)
|
||||||
// CompactSerialize serializes a token using the compact serialization format.
|
|
||||||
CompactSerialize() (string, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NestedBuilder is a utility for making Signed-Then-Encrypted JSON Web Tokens.
|
// NestedBuilder is a utility for making Signed-Then-Encrypted JSON Web Tokens.
|
||||||
// Calls can be chained, and errors are accumulated until final call to
|
// Calls can be chained, and errors are accumulated until final call to
|
||||||
// CompactSerialize/FullSerialize.
|
// Serialize.
|
||||||
type NestedBuilder interface {
|
type NestedBuilder interface {
|
||||||
// Claims encodes claims into JWE/JWS form. Multiple calls will merge claims
|
// Claims encodes claims into JWE/JWS form. Multiple calls will merge claims
|
||||||
// into single JSON object. If you are passing private claims, make sure to set
|
// into single JSON object. If you are passing private claims, make sure to set
|
||||||
|
@ -53,10 +51,8 @@ type NestedBuilder interface {
|
||||||
Claims(i interface{}) NestedBuilder
|
Claims(i interface{}) NestedBuilder
|
||||||
// Token builds a NestedJSONWebToken from provided data.
|
// Token builds a NestedJSONWebToken from provided data.
|
||||||
Token() (*NestedJSONWebToken, error)
|
Token() (*NestedJSONWebToken, error)
|
||||||
// FullSerialize serializes a token using the JSON Serialization format.
|
// Serialize serializes a token.
|
||||||
FullSerialize() (string, error)
|
Serialize() (string, error)
|
||||||
// CompactSerialize serializes a token using the compact serialization format.
|
|
||||||
CompactSerialize() (string, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type builder struct {
|
type builder struct {
|
||||||
|
@ -194,7 +190,7 @@ func (b *signedBuilder) Token() (*JSONWebToken, error) {
|
||||||
return b.builder.token(sig.Verify, h)
|
return b.builder.token(sig.Verify, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *signedBuilder) CompactSerialize() (string, error) {
|
func (b *signedBuilder) Serialize() (string, error) {
|
||||||
sig, err := b.sign()
|
sig, err := b.sign()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -203,15 +199,6 @@ func (b *signedBuilder) CompactSerialize() (string, error) {
|
||||||
return sig.CompactSerialize()
|
return sig.CompactSerialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *signedBuilder) FullSerialize() (string, error) {
|
|
||||||
sig, err := b.sign()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sig.FullSerialize(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *signedBuilder) sign() (*jose.JSONWebSignature, error) {
|
func (b *signedBuilder) sign() (*jose.JSONWebSignature, error) {
|
||||||
if b.err != nil {
|
if b.err != nil {
|
||||||
return nil, b.err
|
return nil, b.err
|
||||||
|
@ -232,7 +219,7 @@ func (b *encryptedBuilder) Claims(i interface{}) Builder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *encryptedBuilder) CompactSerialize() (string, error) {
|
func (b *encryptedBuilder) Serialize() (string, error) {
|
||||||
enc, err := b.encrypt()
|
enc, err := b.encrypt()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -241,15 +228,6 @@ func (b *encryptedBuilder) CompactSerialize() (string, error) {
|
||||||
return enc.CompactSerialize()
|
return enc.CompactSerialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *encryptedBuilder) FullSerialize() (string, error) {
|
|
||||||
enc, err := b.encrypt()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return enc.FullSerialize(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *encryptedBuilder) Token() (*JSONWebToken, error) {
|
func (b *encryptedBuilder) Token() (*JSONWebToken, error) {
|
||||||
enc, err := b.encrypt()
|
enc, err := b.encrypt()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -280,6 +258,8 @@ func (b *nestedBuilder) Claims(i interface{}) NestedBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Token produced a token suitable for serialization. It cannot be decrypted
|
||||||
|
// without serializing and then deserializing.
|
||||||
func (b *nestedBuilder) Token() (*NestedJSONWebToken, error) {
|
func (b *nestedBuilder) Token() (*NestedJSONWebToken, error) {
|
||||||
enc, err := b.signAndEncrypt()
|
enc, err := b.signAndEncrypt()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -287,12 +267,13 @@ func (b *nestedBuilder) Token() (*NestedJSONWebToken, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return &NestedJSONWebToken{
|
return &NestedJSONWebToken{
|
||||||
enc: enc,
|
allowedSignatureAlgorithms: nil,
|
||||||
Headers: []jose.Header{enc.Header},
|
enc: enc,
|
||||||
|
Headers: []jose.Header{enc.Header},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *nestedBuilder) CompactSerialize() (string, error) {
|
func (b *nestedBuilder) Serialize() (string, error) {
|
||||||
enc, err := b.signAndEncrypt()
|
enc, err := b.signAndEncrypt()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
|
@ -21,7 +21,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-jose/go-jose/v3/json"
|
"github.com/go-jose/go-jose/v4/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Claims represents public claim values (as specified in RFC 7519).
|
// Claims represents public claim values (as specified in RFC 7519).
|
198
vendor/github.com/go-jose/go-jose/v4/jwt/jwt.go
generated
vendored
Normal file
198
vendor/github.com/go-jose/go-jose/v4/jwt/jwt.go
generated
vendored
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
/*-
|
||||||
|
* Copyright 2016 Zbigniew Mandziejewicz
|
||||||
|
* Copyright 2016 Square, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jwt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
jose "github.com/go-jose/go-jose/v4"
|
||||||
|
"github.com/go-jose/go-jose/v4/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
// JSONWebToken represents a JSON Web Token (as specified in RFC7519).
|
||||||
|
type JSONWebToken struct {
|
||||||
|
payload func(k interface{}) ([]byte, error)
|
||||||
|
unverifiedPayload func() []byte
|
||||||
|
Headers []jose.Header
|
||||||
|
}
|
||||||
|
|
||||||
|
type NestedJSONWebToken struct {
|
||||||
|
enc *jose.JSONWebEncryption
|
||||||
|
Headers []jose.Header
|
||||||
|
// Used when parsing and decrypting an input
|
||||||
|
allowedSignatureAlgorithms []jose.SignatureAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
// Claims deserializes a JSONWebToken into dest using the provided key.
|
||||||
|
func (t *JSONWebToken) Claims(key interface{}, dest ...interface{}) error {
|
||||||
|
b, err := t.payload(key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, d := range dest {
|
||||||
|
if err := json.Unmarshal(b, d); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnsafeClaimsWithoutVerification deserializes the claims of a
|
||||||
|
// JSONWebToken into the dests. For signed JWTs, the claims are not
|
||||||
|
// verified. This function won't work for encrypted JWTs.
|
||||||
|
func (t *JSONWebToken) UnsafeClaimsWithoutVerification(dest ...interface{}) error {
|
||||||
|
if t.unverifiedPayload == nil {
|
||||||
|
return fmt.Errorf("go-jose/go-jose: Cannot get unverified claims")
|
||||||
|
}
|
||||||
|
claims := t.unverifiedPayload()
|
||||||
|
for _, d := range dest {
|
||||||
|
if err := json.Unmarshal(claims, d); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *NestedJSONWebToken) Decrypt(decryptionKey interface{}) (*JSONWebToken, error) {
|
||||||
|
b, err := t.enc.Decrypt(decryptionKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sig, err := ParseSigned(string(b), t.allowedSignatureAlgorithms)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return sig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseSigned parses token from JWS form.
|
||||||
|
func ParseSigned(s string, signatureAlgorithms []jose.SignatureAlgorithm) (*JSONWebToken, error) {
|
||||||
|
sig, err := jose.ParseSignedCompact(s, signatureAlgorithms)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
headers := make([]jose.Header, len(sig.Signatures))
|
||||||
|
for i, signature := range sig.Signatures {
|
||||||
|
headers[i] = signature.Header
|
||||||
|
}
|
||||||
|
|
||||||
|
return &JSONWebToken{
|
||||||
|
payload: sig.Verify,
|
||||||
|
unverifiedPayload: sig.UnsafePayloadWithoutVerification,
|
||||||
|
Headers: headers,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateKeyEncryptionAlgorithm(algs []jose.KeyAlgorithm) error {
|
||||||
|
for _, alg := range algs {
|
||||||
|
switch alg {
|
||||||
|
case jose.ED25519,
|
||||||
|
jose.RSA1_5,
|
||||||
|
jose.RSA_OAEP,
|
||||||
|
jose.RSA_OAEP_256,
|
||||||
|
jose.ECDH_ES,
|
||||||
|
jose.ECDH_ES_A128KW,
|
||||||
|
jose.ECDH_ES_A192KW,
|
||||||
|
jose.ECDH_ES_A256KW:
|
||||||
|
return fmt.Errorf("asymmetric encryption algorithms not supported for JWT: "+
|
||||||
|
"invalid key encryption algorithm: %s", alg)
|
||||||
|
case jose.PBES2_HS256_A128KW,
|
||||||
|
jose.PBES2_HS384_A192KW,
|
||||||
|
jose.PBES2_HS512_A256KW:
|
||||||
|
return fmt.Errorf("password-based encryption not supported for JWT: "+
|
||||||
|
"invalid key encryption algorithm: %s", alg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseEncryptedCompact(
|
||||||
|
s string,
|
||||||
|
keyAlgorithms []jose.KeyAlgorithm,
|
||||||
|
contentEncryption []jose.ContentEncryption,
|
||||||
|
) (*jose.JSONWebEncryption, error) {
|
||||||
|
err := validateKeyEncryptionAlgorithm(keyAlgorithms)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
enc, err := jose.ParseEncryptedCompact(s, keyAlgorithms, contentEncryption)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return enc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseEncrypted parses token from JWE form.
|
||||||
|
//
|
||||||
|
// The keyAlgorithms and contentEncryption parameters are used to validate the "alg" and "enc"
|
||||||
|
// header parameters respectively. They must be nonempty, and each "alg" or "enc" header in
|
||||||
|
// parsed data must contain a value that is present in the corresponding parameter. That
|
||||||
|
// includes the protected and unprotected headers as well as all recipients. To accept
|
||||||
|
// multiple algorithms, pass a slice of all the algorithms you want to accept.
|
||||||
|
func ParseEncrypted(s string,
|
||||||
|
keyAlgorithms []jose.KeyAlgorithm,
|
||||||
|
contentEncryption []jose.ContentEncryption,
|
||||||
|
) (*JSONWebToken, error) {
|
||||||
|
enc, err := parseEncryptedCompact(s, keyAlgorithms, contentEncryption)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &JSONWebToken{
|
||||||
|
payload: enc.Decrypt,
|
||||||
|
Headers: []jose.Header{enc.Header},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseSignedAndEncrypted parses signed-then-encrypted token from JWE form.
|
||||||
|
//
|
||||||
|
// The encryptionKeyAlgorithms and contentEncryption parameters are used to validate the "alg" and "enc"
|
||||||
|
// header parameters, respectively, of the outer JWE. They must be nonempty, and each "alg" or "enc"
|
||||||
|
// header in parsed data must contain a value that is present in the corresponding parameter. That
|
||||||
|
// includes the protected and unprotected headers as well as all recipients. To accept
|
||||||
|
// multiple algorithms, pass a slice of all the algorithms you want to accept.
|
||||||
|
//
|
||||||
|
// The signatureAlgorithms parameter is used to validate the "alg" header parameter of the
|
||||||
|
// inner JWS. It must be nonempty, and the "alg" header in the inner JWS must contain a value
|
||||||
|
// that is present in the parameter.
|
||||||
|
func ParseSignedAndEncrypted(s string,
|
||||||
|
encryptionKeyAlgorithms []jose.KeyAlgorithm,
|
||||||
|
contentEncryption []jose.ContentEncryption,
|
||||||
|
signatureAlgorithms []jose.SignatureAlgorithm,
|
||||||
|
) (*NestedJSONWebToken, error) {
|
||||||
|
enc, err := parseEncryptedCompact(s, encryptionKeyAlgorithms, contentEncryption)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
contentType, _ := enc.Header.ExtraHeaders[jose.HeaderContentType].(string)
|
||||||
|
if strings.ToUpper(contentType) != "JWT" {
|
||||||
|
return nil, ErrInvalidContentType
|
||||||
|
}
|
||||||
|
|
||||||
|
return &NestedJSONWebToken{
|
||||||
|
allowedSignatureAlgorithms: signatureAlgorithms,
|
||||||
|
enc: enc,
|
||||||
|
Headers: []jose.Header{enc.Header},
|
||||||
|
}, nil
|
||||||
|
}
|
|
@ -33,8 +33,9 @@ type Expected struct {
|
||||||
Issuer string
|
Issuer string
|
||||||
// Subject matches the "sub" claim exactly.
|
// Subject matches the "sub" claim exactly.
|
||||||
Subject string
|
Subject string
|
||||||
// Audience matches the values in "aud" claim, regardless of their order.
|
// AnyAudience matches if there is a non-empty intersection between
|
||||||
Audience Audience
|
// its values and the values in the "aud" claim.
|
||||||
|
AnyAudience Audience
|
||||||
// ID matches the "jti" claim exactly.
|
// ID matches the "jti" claim exactly.
|
||||||
ID string
|
ID string
|
||||||
// Time matches the "exp", "nbf" and "iat" claims with leeway.
|
// Time matches the "exp", "nbf" and "iat" claims with leeway.
|
||||||
|
@ -88,12 +89,18 @@ func (c Claims) ValidateWithLeeway(e Expected, leeway time.Duration) error {
|
||||||
return ErrInvalidID
|
return ErrInvalidID
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(e.Audience) != 0 {
|
if len(e.AnyAudience) != 0 {
|
||||||
for _, v := range e.Audience {
|
var intersection bool
|
||||||
if !c.Audience.Contains(v) {
|
for _, v := range e.AnyAudience {
|
||||||
return ErrInvalidAudience
|
if c.Audience.Contains(v) {
|
||||||
|
intersection = true
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !intersection {
|
||||||
|
return ErrInvalidAudience
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate using the e.Time, or time.Now if not provided
|
// validate using the e.Time, or time.Now if not provided
|
|
@ -23,7 +23,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/go-jose/go-jose/v3/json"
|
"github.com/go-jose/go-jose/v4/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
// KeyAlgorithm represents a key management algorithm.
|
// KeyAlgorithm represents a key management algorithm.
|
||||||
|
@ -71,6 +71,12 @@ var (
|
||||||
// ErrUnprotectedNonce indicates that while parsing a JWS or JWE object, a
|
// ErrUnprotectedNonce indicates that while parsing a JWS or JWE object, a
|
||||||
// nonce header parameter was included in an unprotected header object.
|
// nonce header parameter was included in an unprotected header object.
|
||||||
ErrUnprotectedNonce = errors.New("go-jose/go-jose: Nonce parameter included in unprotected header")
|
ErrUnprotectedNonce = errors.New("go-jose/go-jose: Nonce parameter included in unprotected header")
|
||||||
|
|
||||||
|
// ErrMissingX5cHeader indicates that the JWT header is missing x5c headers.
|
||||||
|
ErrMissingX5cHeader = errors.New("go-jose/go-jose: no x5c header present in message")
|
||||||
|
|
||||||
|
// ErrUnsupportedEllipticCurve indicates unsupported or unknown elliptic curve has been found.
|
||||||
|
ErrUnsupportedEllipticCurve = errors.New("go-jose/go-jose: unsupported/unknown elliptic curve")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Key management algorithms
|
// Key management algorithms
|
||||||
|
@ -199,7 +205,7 @@ type Header struct {
|
||||||
// not be validated with the given verify options.
|
// not be validated with the given verify options.
|
||||||
func (h Header) Certificates(opts x509.VerifyOptions) ([][]*x509.Certificate, error) {
|
func (h Header) Certificates(opts x509.VerifyOptions) ([][]*x509.Certificate, error) {
|
||||||
if len(h.certificates) == 0 {
|
if len(h.certificates) == 0 {
|
||||||
return nil, errors.New("go-jose/go-jose: no x5c header present in message")
|
return nil, ErrMissingX5cHeader
|
||||||
}
|
}
|
||||||
|
|
||||||
leaf := h.certificates[0]
|
leaf := h.certificates[0]
|
||||||
|
@ -501,7 +507,7 @@ func curveName(crv elliptic.Curve) (string, error) {
|
||||||
case elliptic.P521():
|
case elliptic.P521():
|
||||||
return "P-521", nil
|
return "P-521", nil
|
||||||
default:
|
default:
|
||||||
return "", fmt.Errorf("go-jose/go-jose: unsupported/unknown elliptic curve")
|
return "", ErrUnsupportedEllipticCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/go-jose/go-jose/v3/json"
|
"github.com/go-jose/go-jose/v4/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NonceSource represents a source of random nonces to go into JWS objects
|
// NonceSource represents a source of random nonces to go into JWS objects
|
||||||
|
@ -49,6 +49,11 @@ type Signer interface {
|
||||||
// - JSONWebKey
|
// - JSONWebKey
|
||||||
// - []byte (an HMAC key)
|
// - []byte (an HMAC key)
|
||||||
// - Any type that satisfies the OpaqueSigner interface
|
// - Any type that satisfies the OpaqueSigner interface
|
||||||
|
//
|
||||||
|
// If the key is an HMAC key, it must have at least as many bytes as the relevant hash output:
|
||||||
|
// - HS256: 32 bytes
|
||||||
|
// - HS384: 48 bytes
|
||||||
|
// - HS512: 64 bytes
|
||||||
type SigningKey struct {
|
type SigningKey struct {
|
||||||
Algorithm SignatureAlgorithm
|
Algorithm SignatureAlgorithm
|
||||||
Key interface{}
|
Key interface{}
|
||||||
|
@ -353,8 +358,15 @@ func (ctx *genericSigner) Options() SignerOptions {
|
||||||
// - *rsa.PublicKey
|
// - *rsa.PublicKey
|
||||||
// - *JSONWebKey
|
// - *JSONWebKey
|
||||||
// - JSONWebKey
|
// - JSONWebKey
|
||||||
|
// - *JSONWebKeySet
|
||||||
|
// - JSONWebKeySet
|
||||||
// - []byte (an HMAC key)
|
// - []byte (an HMAC key)
|
||||||
// - Any type that implements the OpaqueVerifier interface.
|
// - Any type that implements the OpaqueVerifier interface.
|
||||||
|
//
|
||||||
|
// If the key is an HMAC key, it must have at least as many bytes as the relevant hash output:
|
||||||
|
// - HS256: 32 bytes
|
||||||
|
// - HS384: 48 bytes
|
||||||
|
// - HS512: 64 bytes
|
||||||
func (obj JSONWebSignature) Verify(verificationKey interface{}) ([]byte, error) {
|
func (obj JSONWebSignature) Verify(verificationKey interface{}) ([]byte, error) {
|
||||||
err := obj.DetachedVerify(obj.payload, verificationKey)
|
err := obj.DetachedVerify(obj.payload, verificationKey)
|
||||||
if err != nil {
|
if err != nil {
|
|
@ -32,7 +32,7 @@ import (
|
||||||
|
|
||||||
"golang.org/x/crypto/pbkdf2"
|
"golang.org/x/crypto/pbkdf2"
|
||||||
|
|
||||||
josecipher "github.com/go-jose/go-jose/v3/cipher"
|
josecipher "github.com/go-jose/go-jose/v4/cipher"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RandReader is a cryptographically secure random number generator (stubbed out in tests).
|
// RandReader is a cryptographically secure random number generator (stubbed out in tests).
|
||||||
|
@ -454,7 +454,7 @@ func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipien
|
||||||
func (ctx symmetricMac) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) {
|
func (ctx symmetricMac) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) {
|
||||||
mac, err := ctx.hmac(payload, alg)
|
mac, err := ctx.hmac(payload, alg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Signature{}, errors.New("go-jose/go-jose: failed to compute hmac")
|
return Signature{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Signature{
|
return Signature{
|
||||||
|
@ -486,12 +486,24 @@ func (ctx symmetricMac) verifyPayload(payload []byte, mac []byte, alg SignatureA
|
||||||
func (ctx symmetricMac) hmac(payload []byte, alg SignatureAlgorithm) ([]byte, error) {
|
func (ctx symmetricMac) hmac(payload []byte, alg SignatureAlgorithm) ([]byte, error) {
|
||||||
var hash func() hash.Hash
|
var hash func() hash.Hash
|
||||||
|
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc7518#section-3.2
|
||||||
|
// A key of the same size as the hash output (for instance, 256 bits for
|
||||||
|
// "HS256") or larger MUST be used
|
||||||
switch alg {
|
switch alg {
|
||||||
case HS256:
|
case HS256:
|
||||||
|
if len(ctx.key)*8 < 256 {
|
||||||
|
return nil, ErrInvalidKeySize
|
||||||
|
}
|
||||||
hash = sha256.New
|
hash = sha256.New
|
||||||
case HS384:
|
case HS384:
|
||||||
|
if len(ctx.key)*8 < 384 {
|
||||||
|
return nil, ErrInvalidKeySize
|
||||||
|
}
|
||||||
hash = sha512.New384
|
hash = sha512.New384
|
||||||
case HS512:
|
case HS512:
|
||||||
|
if len(ctx.key)*8 < 512 {
|
||||||
|
return nil, ErrInvalidKeySize
|
||||||
|
}
|
||||||
hash = sha512.New
|
hash = sha512.New
|
||||||
default:
|
default:
|
||||||
return nil, ErrUnsupportedAlgorithm
|
return nil, ErrUnsupportedAlgorithm
|
12
vendor/modules.txt
vendored
12
vendor/modules.txt
vendored
|
@ -181,12 +181,12 @@ github.com/docker/go-metrics
|
||||||
# github.com/felixge/httpsnoop v1.0.4
|
# github.com/felixge/httpsnoop v1.0.4
|
||||||
## explicit; go 1.13
|
## explicit; go 1.13
|
||||||
github.com/felixge/httpsnoop
|
github.com/felixge/httpsnoop
|
||||||
# github.com/go-jose/go-jose/v3 v3.0.3
|
# github.com/go-jose/go-jose/v4 v4.0.2
|
||||||
## explicit; go 1.12
|
## explicit; go 1.21
|
||||||
github.com/go-jose/go-jose/v3
|
github.com/go-jose/go-jose/v4
|
||||||
github.com/go-jose/go-jose/v3/cipher
|
github.com/go-jose/go-jose/v4/cipher
|
||||||
github.com/go-jose/go-jose/v3/json
|
github.com/go-jose/go-jose/v4/json
|
||||||
github.com/go-jose/go-jose/v3/jwt
|
github.com/go-jose/go-jose/v4/jwt
|
||||||
# github.com/go-logr/logr v1.3.0
|
# github.com/go-logr/logr v1.3.0
|
||||||
## explicit; go 1.18
|
## explicit; go 1.18
|
||||||
github.com/go-logr/logr
|
github.com/go-logr/logr
|
||||||
|
|
Loading…
Reference in a new issue