mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-07-27 19:29:57 +00:00
TUN-8333: Bump go-jose dependency to v4
This commit is contained in:
15
vendor/github.com/coreos/go-oidc/v3/oidc/jose.go
generated
vendored
15
vendor/github.com/coreos/go-oidc/v3/oidc/jose.go
generated
vendored
@@ -1,5 +1,7 @@
|
||||
package oidc
|
||||
|
||||
import jose "github.com/go-jose/go-jose/v4"
|
||||
|
||||
// JOSE asymmetric signing algorithm values as defined by RFC 7518
|
||||
//
|
||||
// see: https://tools.ietf.org/html/rfc7518#section-3.1
|
||||
@@ -15,3 +17,16 @@ const (
|
||||
PS512 = "PS512" // RSASSA-PSS using SHA512 and MGF1-SHA512
|
||||
EdDSA = "EdDSA" // Ed25519 using SHA-512
|
||||
)
|
||||
|
||||
var allAlgs = []jose.SignatureAlgorithm{
|
||||
jose.RS256,
|
||||
jose.RS384,
|
||||
jose.RS512,
|
||||
jose.ES256,
|
||||
jose.ES384,
|
||||
jose.ES512,
|
||||
jose.PS256,
|
||||
jose.PS384,
|
||||
jose.PS512,
|
||||
jose.EdDSA,
|
||||
}
|
||||
|
21
vendor/github.com/coreos/go-oidc/v3/oidc/jwks.go
generated
vendored
21
vendor/github.com/coreos/go-oidc/v3/oidc/jwks.go
generated
vendored
@@ -8,12 +8,12 @@ import (
|
||||
"crypto/rsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
jose "github.com/go-jose/go-jose/v3"
|
||||
jose "github.com/go-jose/go-jose/v4"
|
||||
)
|
||||
|
||||
// StaticKeySet is a verifier that validates JWT against a static set of public keys.
|
||||
@@ -25,7 +25,9 @@ type StaticKeySet struct {
|
||||
|
||||
// VerifySignature compares the signature against a static set of public keys.
|
||||
func (s *StaticKeySet) VerifySignature(ctx context.Context, jwt string) ([]byte, error) {
|
||||
jws, err := jose.ParseSigned(jwt)
|
||||
// Algorithms are already checked by Verifier, so this parse method accepts
|
||||
// any algorithm.
|
||||
jws, err := jose.ParseSigned(jwt, allAlgs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing jwt: %v", err)
|
||||
}
|
||||
@@ -127,8 +129,13 @@ var parsedJWTKey contextKey
|
||||
func (r *RemoteKeySet) VerifySignature(ctx context.Context, jwt string) ([]byte, error) {
|
||||
jws, ok := ctx.Value(parsedJWTKey).(*jose.JSONWebSignature)
|
||||
if !ok {
|
||||
// The algorithm values are already enforced by the Validator, which also sets
|
||||
// the context value above to pre-parsed signature.
|
||||
//
|
||||
// Practically, this codepath isn't called in normal use of this package, but
|
||||
// if it is, the algorithms have already been checked.
|
||||
var err error
|
||||
jws, err = jose.ParseSigned(jwt)
|
||||
jws, err = jose.ParseSigned(jwt, allAlgs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("oidc: malformed jwt: %v", err)
|
||||
}
|
||||
@@ -159,7 +166,7 @@ func (r *RemoteKeySet) verify(ctx context.Context, jws *jose.JSONWebSignature) (
|
||||
// https://openid.net/specs/openid-connect-core-1_0.html#RotateSigKeys
|
||||
keys, err := r.keysFromRemote(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fetching keys %v", err)
|
||||
return nil, fmt.Errorf("fetching keys %w", err)
|
||||
}
|
||||
|
||||
for _, key := range keys {
|
||||
@@ -228,11 +235,11 @@ func (r *RemoteKeySet) updateKeys() ([]jose.JSONWebKey, error) {
|
||||
|
||||
resp, err := doRequest(r.ctx, req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("oidc: get keys failed %v", err)
|
||||
return nil, fmt.Errorf("oidc: get keys failed %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read response body: %v", err)
|
||||
}
|
||||
|
71
vendor/github.com/coreos/go-oidc/v3/oidc/oidc.go
generated
vendored
71
vendor/github.com/coreos/go-oidc/v3/oidc/oidc.go
generated
vendored
@@ -10,7 +10,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
"strings"
|
||||
@@ -79,7 +79,7 @@ func getClient(ctx context.Context) *http.Client {
|
||||
// provider, err := oidc.NewProvider(ctx, discoveryBaseURL)
|
||||
//
|
||||
// This is insecure because validating the correct issuer is critical for multi-tenant
|
||||
// proivders. Any overrides here MUST be carefully reviewed.
|
||||
// providers. Any overrides here MUST be carefully reviewed.
|
||||
func InsecureIssuerURLContext(ctx context.Context, issuerURL string) context.Context {
|
||||
return context.WithValue(ctx, issuerURLKey, issuerURL)
|
||||
}
|
||||
@@ -94,12 +94,13 @@ func doRequest(ctx context.Context, req *http.Request) (*http.Response, error) {
|
||||
|
||||
// Provider represents an OpenID Connect server's configuration.
|
||||
type Provider struct {
|
||||
issuer string
|
||||
authURL string
|
||||
tokenURL string
|
||||
userInfoURL string
|
||||
jwksURL string
|
||||
algorithms []string
|
||||
issuer string
|
||||
authURL string
|
||||
tokenURL string
|
||||
deviceAuthURL string
|
||||
userInfoURL string
|
||||
jwksURL string
|
||||
algorithms []string
|
||||
|
||||
// Raw claims returned by the server.
|
||||
rawClaims []byte
|
||||
@@ -128,12 +129,13 @@ func (p *Provider) remoteKeySet() KeySet {
|
||||
}
|
||||
|
||||
type providerJSON struct {
|
||||
Issuer string `json:"issuer"`
|
||||
AuthURL string `json:"authorization_endpoint"`
|
||||
TokenURL string `json:"token_endpoint"`
|
||||
JWKSURL string `json:"jwks_uri"`
|
||||
UserInfoURL string `json:"userinfo_endpoint"`
|
||||
Algorithms []string `json:"id_token_signing_alg_values_supported"`
|
||||
Issuer string `json:"issuer"`
|
||||
AuthURL string `json:"authorization_endpoint"`
|
||||
TokenURL string `json:"token_endpoint"`
|
||||
DeviceAuthURL string `json:"device_authorization_endpoint"`
|
||||
JWKSURL string `json:"jwks_uri"`
|
||||
UserInfoURL string `json:"userinfo_endpoint"`
|
||||
Algorithms []string `json:"id_token_signing_alg_values_supported"`
|
||||
}
|
||||
|
||||
// supportedAlgorithms is a list of algorithms explicitly supported by this
|
||||
@@ -165,6 +167,9 @@ type ProviderConfig struct {
|
||||
// TokenURL is the endpoint used by the provider to support the OAuth 2.0
|
||||
// token endpoint.
|
||||
TokenURL string
|
||||
// DeviceAuthURL is the endpoint used by the provider to support the OAuth 2.0
|
||||
// device authorization endpoint.
|
||||
DeviceAuthURL string
|
||||
// UserInfoURL is the endpoint used by the provider to support the OpenID
|
||||
// Connect UserInfo flow.
|
||||
//
|
||||
@@ -185,13 +190,14 @@ type ProviderConfig struct {
|
||||
// through discovery.
|
||||
func (p *ProviderConfig) NewProvider(ctx context.Context) *Provider {
|
||||
return &Provider{
|
||||
issuer: p.IssuerURL,
|
||||
authURL: p.AuthURL,
|
||||
tokenURL: p.TokenURL,
|
||||
userInfoURL: p.UserInfoURL,
|
||||
jwksURL: p.JWKSURL,
|
||||
algorithms: p.Algorithms,
|
||||
client: getClient(ctx),
|
||||
issuer: p.IssuerURL,
|
||||
authURL: p.AuthURL,
|
||||
tokenURL: p.TokenURL,
|
||||
deviceAuthURL: p.DeviceAuthURL,
|
||||
userInfoURL: p.UserInfoURL,
|
||||
jwksURL: p.JWKSURL,
|
||||
algorithms: p.Algorithms,
|
||||
client: getClient(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,7 +217,7 @@ func NewProvider(ctx context.Context, issuer string) (*Provider, error) {
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read response body: %v", err)
|
||||
}
|
||||
@@ -240,14 +246,15 @@ func NewProvider(ctx context.Context, issuer string) (*Provider, error) {
|
||||
}
|
||||
}
|
||||
return &Provider{
|
||||
issuer: issuerURL,
|
||||
authURL: p.AuthURL,
|
||||
tokenURL: p.TokenURL,
|
||||
userInfoURL: p.UserInfoURL,
|
||||
jwksURL: p.JWKSURL,
|
||||
algorithms: algs,
|
||||
rawClaims: body,
|
||||
client: getClient(ctx),
|
||||
issuer: issuerURL,
|
||||
authURL: p.AuthURL,
|
||||
tokenURL: p.TokenURL,
|
||||
deviceAuthURL: p.DeviceAuthURL,
|
||||
userInfoURL: p.UserInfoURL,
|
||||
jwksURL: p.JWKSURL,
|
||||
algorithms: algs,
|
||||
rawClaims: body,
|
||||
client: getClient(ctx),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -273,7 +280,7 @@ func (p *Provider) Claims(v interface{}) error {
|
||||
|
||||
// Endpoint returns the OAuth2 auth and token endpoints for the given provider.
|
||||
func (p *Provider) Endpoint() oauth2.Endpoint {
|
||||
return oauth2.Endpoint{AuthURL: p.authURL, TokenURL: p.tokenURL}
|
||||
return oauth2.Endpoint{AuthURL: p.authURL, DeviceAuthURL: p.deviceAuthURL, TokenURL: p.tokenURL}
|
||||
}
|
||||
|
||||
// UserInfoEndpoint returns the OpenID Connect userinfo endpoint for the given
|
||||
@@ -332,7 +339,7 @@ func (p *Provider) UserInfo(ctx context.Context, tokenSource oauth2.TokenSource)
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
27
vendor/github.com/coreos/go-oidc/v3/oidc/verify.go
generated
vendored
27
vendor/github.com/coreos/go-oidc/v3/oidc/verify.go
generated
vendored
@@ -7,12 +7,12 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
jose "github.com/go-jose/go-jose/v3"
|
||||
jose "github.com/go-jose/go-jose/v4"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
@@ -182,7 +182,7 @@ func resolveDistributedClaim(ctx context.Context, verifier *IDTokenVerifier, src
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read response body: %v", err)
|
||||
}
|
||||
@@ -310,7 +310,16 @@ func (v *IDTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*IDTok
|
||||
return t, nil
|
||||
}
|
||||
|
||||
jws, err := jose.ParseSigned(rawIDToken)
|
||||
var supportedSigAlgs []jose.SignatureAlgorithm
|
||||
for _, alg := range v.config.SupportedSigningAlgs {
|
||||
supportedSigAlgs = append(supportedSigAlgs, jose.SignatureAlgorithm(alg))
|
||||
}
|
||||
if len(supportedSigAlgs) == 0 {
|
||||
// If no algorithms were specified by both the config and discovery, default
|
||||
// to the one mandatory algorithm "RS256".
|
||||
supportedSigAlgs = []jose.SignatureAlgorithm{jose.RS256}
|
||||
}
|
||||
jws, err := jose.ParseSigned(rawIDToken, supportedSigAlgs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("oidc: malformed jwt: %v", err)
|
||||
}
|
||||
@@ -322,17 +331,7 @@ func (v *IDTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*IDTok
|
||||
default:
|
||||
return nil, fmt.Errorf("oidc: multiple signatures on id token not supported")
|
||||
}
|
||||
|
||||
sig := jws.Signatures[0]
|
||||
supportedSigAlgs := v.config.SupportedSigningAlgs
|
||||
if len(supportedSigAlgs) == 0 {
|
||||
supportedSigAlgs = []string{RS256}
|
||||
}
|
||||
|
||||
if !contains(supportedSigAlgs, sig.Header.Algorithm) {
|
||||
return nil, fmt.Errorf("oidc: id token signed with unsupported algorithm, expected %q got %q", supportedSigAlgs, sig.Header.Algorithm)
|
||||
}
|
||||
|
||||
t.sigAlgorithm = sig.Header.Algorithm
|
||||
|
||||
ctx = context.WithValue(ctx, parsedJWTKey, jws)
|
||||
|
10
vendor/github.com/go-jose/go-jose/v3/BUG-BOUNTY.md
generated
vendored
10
vendor/github.com/go-jose/go-jose/v3/BUG-BOUNTY.md
generated
vendored
@@ -1,10 +0,0 @@
|
||||
Serious about security
|
||||
======================
|
||||
|
||||
Square recognizes the important contributions the security research community
|
||||
can make. We therefore encourage reporting security issues with the code
|
||||
contained in this repository.
|
||||
|
||||
If you believe you have discovered a security vulnerability, please follow the
|
||||
guidelines at <https://bugcrowd.com/squareopensource>.
|
||||
|
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
|
||||
}
|
72
vendor/github.com/go-jose/go-jose/v4/CHANGELOG.md
generated
vendored
Normal file
72
vendor/github.com/go-jose/go-jose/v4/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
# v4.0.1
|
||||
|
||||
## Fixed
|
||||
|
||||
- An attacker could send a JWE containing compressed data that used large
|
||||
amounts of memory and CPU when decompressed by `Decrypt` or `DecryptMulti`.
|
||||
Those functions now return an error if the decompressed data would exceed
|
||||
250kB or 10x the compressed size (whichever is larger). Thanks to
|
||||
Enze Wang@Alioth and Jianjun Chen@Zhongguancun Lab (@zer0yu and @chenjj)
|
||||
for reporting.
|
||||
|
||||
# v4.0.0
|
||||
|
||||
This release makes some breaking changes in order to more thoroughly
|
||||
address the vulnerabilities discussed in [Three New Attacks Against JSON Web
|
||||
Tokens][1], "Sign/encrypt confusion", "Billion hash attack", and "Polyglot
|
||||
token".
|
||||
|
||||
## Changed
|
||||
|
||||
- Limit JWT encryption types (exclude password or public key types) (#78)
|
||||
- Enforce minimum length for HMAC keys (#85)
|
||||
- jwt: match any audience in a list, rather than requiring all audiences (#81)
|
||||
- jwt: accept only Compact Serialization (#75)
|
||||
- jws: Add expected algorithms for signatures (#74)
|
||||
- Require specifying expected algorithms for ParseEncrypted,
|
||||
ParseSigned, ParseDetached, jwt.ParseEncrypted, jwt.ParseSigned,
|
||||
jwt.ParseSignedAndEncrypted (#69, #74)
|
||||
- Usually there is a small, known set of appropriate algorithms for a program
|
||||
to use and it's a mistake to allow unexpected algorithms. For instance the
|
||||
"billion hash attack" relies in part on programs accepting the PBES2
|
||||
encryption algorithm and doing the necessary work even if they weren't
|
||||
specifically configured to allow PBES2.
|
||||
- Revert "Strip padding off base64 strings" (#82)
|
||||
- The specs require base64url encoding without padding.
|
||||
- Minimum supported Go version is now 1.21
|
||||
|
||||
## Added
|
||||
|
||||
- ParseSignedCompact, ParseSignedJSON, ParseEncryptedCompact, ParseEncryptedJSON.
|
||||
- These allow parsing a specific serialization, as opposed to ParseSigned and
|
||||
ParseEncrypted, which try to automatically detect which serialization was
|
||||
provided. It's common to require a specific serialization for a specific
|
||||
protocol - for instance JWT requires Compact serialization.
|
||||
|
||||
[1]: https://i.blackhat.com/BH-US-23/Presentations/US-23-Tervoort-Three-New-Attacks-Against-JSON-Web-Tokens.pdf
|
||||
|
||||
# v3.0.2
|
||||
|
||||
## Fixed
|
||||
|
||||
- DecryptMulti: handle decompression error (#19)
|
||||
|
||||
## Changed
|
||||
|
||||
- jwe/CompactSerialize: improve performance (#67)
|
||||
- Increase the default number of PBKDF2 iterations to 600k (#48)
|
||||
- Return the proper algorithm for ECDSA keys (#45)
|
||||
|
||||
## Added
|
||||
|
||||
- Add Thumbprint support for opaque signers (#38)
|
||||
|
||||
# v3.0.1
|
||||
|
||||
## Fixed
|
||||
|
||||
- Security issue: an attacker specifying a large "p2c" value can cause
|
||||
JSONWebEncryption.Decrypt and JSONWebEncryption.DecryptMulti to consume large
|
||||
amounts of CPU, causing a DoS. Thanks to Matt Schwager (@mschwager) for the
|
||||
disclosure and to Tom Tervoort for originally publishing the category of attack.
|
||||
https://i.blackhat.com/BH-US-23/Presentations/US-23-Tervoort-Three-New-Attacks-Against-JSON-Web-Tokens.pdf
|
@@ -1,10 +1,9 @@
|
||||
# Go JOSE
|
||||
|
||||
[](https://godoc.org/gopkg.in/go-jose/go-jose.v2)
|
||||
[](https://godoc.org/gopkg.in/go-jose/go-jose.v2/jwt)
|
||||
[](https://raw.githubusercontent.com/go-jose/go-jose/master/LICENSE)
|
||||
[](https://travis-ci.org/go-jose/go-jose)
|
||||
[](https://coveralls.io/r/go-jose/go-jose)
|
||||
[](https://pkg.go.dev/github.com/go-jose/go-jose/v4)
|
||||
[](https://pkg.go.dev/github.com/go-jose/go-jose/v4/jwt)
|
||||
[](https://raw.githubusercontent.com/go-jose/go-jose/master/LICENSE)
|
||||
[](https://github.com/go-jose/go-jose/actions)
|
||||
|
||||
Package jose aims to provide an implementation of the Javascript Object Signing
|
||||
and Encryption set of standards. This includes support for JSON Web Encryption,
|
||||
@@ -21,13 +20,13 @@ US maintained blocked list.
|
||||
## Overview
|
||||
|
||||
The implementation follows the
|
||||
[JSON Web Encryption](http://dx.doi.org/10.17487/RFC7516) (RFC 7516),
|
||||
[JSON Web Signature](http://dx.doi.org/10.17487/RFC7515) (RFC 7515), and
|
||||
[JSON Web Token](http://dx.doi.org/10.17487/RFC7519) (RFC 7519) specifications.
|
||||
[JSON Web Encryption](https://dx.doi.org/10.17487/RFC7516) (RFC 7516),
|
||||
[JSON Web Signature](https://dx.doi.org/10.17487/RFC7515) (RFC 7515), and
|
||||
[JSON Web Token](https://dx.doi.org/10.17487/RFC7519) (RFC 7519) specifications.
|
||||
Tables of supported algorithms are shown below. The library supports both
|
||||
the compact and JWS/JWE JSON Serialization formats, and has optional support for
|
||||
multiple recipients. It also comes with a small command-line utility
|
||||
([`jose-util`](https://github.com/go-jose/go-jose/tree/master/jose-util))
|
||||
([`jose-util`](https://pkg.go.dev/github.com/go-jose/go-jose/jose-util))
|
||||
for dealing with JOSE messages in a shell.
|
||||
|
||||
**Note**: We use a forked version of the `encoding/json` package from the Go
|
||||
@@ -38,29 +37,22 @@ libraries in other languages.
|
||||
|
||||
### Versions
|
||||
|
||||
[Version 2](https://gopkg.in/go-jose/go-jose.v2)
|
||||
([branch](https://github.com/go-jose/go-jose/tree/v2),
|
||||
[doc](https://godoc.org/gopkg.in/go-jose/go-jose.v2)) is the current stable version:
|
||||
[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 "gopkg.in/go-jose/go-jose.v2"
|
||||
import "github.com/go-jose/go-jose/v4"
|
||||
|
||||
[Version 3](https://github.com/go-jose/go-jose)
|
||||
([branch](https://github.com/go-jose/go-jose/tree/master),
|
||||
[doc](https://godoc.org/github.com/go-jose/go-jose)) is the under development/unstable version (not released yet):
|
||||
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.
|
||||
|
||||
import "github.com/go-jose/go-jose/v3"
|
||||
|
||||
All new feature development takes place on the `master` branch, which we are
|
||||
preparing to release as version 3 soon. Version 2 will continue to receive
|
||||
critical bug and security fixes. Note that starting with version 3 we are
|
||||
using Go modules for versioning instead of `gopkg.in` as before. Version 3 also will require Go version 1.13 or higher.
|
||||
|
||||
Version 1 (on the `v1` branch) is frozen and not supported anymore.
|
||||
Version 3, in this repo, is still receiving security fixes but not functionality
|
||||
updates.
|
||||
|
||||
### Supported algorithms
|
||||
|
||||
See below for a table of supported algorithms. Algorithm identifiers match
|
||||
the names in the [JSON Web Algorithms](http://dx.doi.org/10.17487/RFC7518)
|
||||
the names in the [JSON Web Algorithms](https://dx.doi.org/10.17487/RFC7518)
|
||||
standard where possible. The Godoc reference has a list of constants.
|
||||
|
||||
Key encryption | Algorithm identifier(s)
|
||||
@@ -103,20 +95,20 @@ allows attaching a key id.
|
||||
|
||||
Algorithm(s) | Corresponding types
|
||||
:------------------------- | -------------------------------
|
||||
RSA | *[rsa.PublicKey](http://golang.org/pkg/crypto/rsa/#PublicKey), *[rsa.PrivateKey](http://golang.org/pkg/crypto/rsa/#PrivateKey)
|
||||
ECDH, ECDSA | *[ecdsa.PublicKey](http://golang.org/pkg/crypto/ecdsa/#PublicKey), *[ecdsa.PrivateKey](http://golang.org/pkg/crypto/ecdsa/#PrivateKey)
|
||||
EdDSA<sup>1</sup> | [ed25519.PublicKey](https://godoc.org/pkg/crypto/ed25519#PublicKey), [ed25519.PrivateKey](https://godoc.org/pkg/crypto/ed25519#PrivateKey)
|
||||
RSA | *[rsa.PublicKey](https://pkg.go.dev/crypto/rsa/#PublicKey), *[rsa.PrivateKey](https://pkg.go.dev/crypto/rsa/#PrivateKey)
|
||||
ECDH, ECDSA | *[ecdsa.PublicKey](https://pkg.go.dev/crypto/ecdsa/#PublicKey), *[ecdsa.PrivateKey](https://pkg.go.dev/crypto/ecdsa/#PrivateKey)
|
||||
EdDSA<sup>1</sup> | [ed25519.PublicKey](https://pkg.go.dev/crypto/ed25519#PublicKey), [ed25519.PrivateKey](https://pkg.go.dev/crypto/ed25519#PrivateKey)
|
||||
AES, HMAC | []byte
|
||||
|
||||
<sup>1. Only available in version 2 or later of the package</sup>
|
||||
|
||||
## Examples
|
||||
|
||||
[](https://godoc.org/gopkg.in/go-jose/go-jose.v2)
|
||||
[](https://godoc.org/gopkg.in/go-jose/go-jose.v2/jwt)
|
||||
[](https://pkg.go.dev/github.com/go-jose/go-jose/v4)
|
||||
[](https://pkg.go.dev/github.com/go-jose/go-jose/v4/jwt)
|
||||
|
||||
Examples can be found in the Godoc
|
||||
reference for this package. The
|
||||
[`jose-util`](https://github.com/go-jose/go-jose/tree/master/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
|
||||
as an example as well.
|
13
vendor/github.com/go-jose/go-jose/v4/SECURITY.md
generated
vendored
Normal file
13
vendor/github.com/go-jose/go-jose/v4/SECURITY.md
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# Security Policy
|
||||
This document explains how to contact the Let's Encrypt security team to report security vulnerabilities.
|
||||
|
||||
## Supported Versions
|
||||
| Version | Supported |
|
||||
| ------- | ----------|
|
||||
| >= v3 | ✓ |
|
||||
| v2 | ✗ |
|
||||
| v1 | ✗ |
|
||||
|
||||
## Reporting a vulnerability
|
||||
|
||||
Please see [https://letsencrypt.org/contact/#security](https://letsencrypt.org/contact/#security) for the email address to report a vulnerability. Ensure that the subject line for your report contains the word `vulnerability` and is descriptive. Your email should be acknowledged within 24 hours. If you do not receive a response within 24 hours, please follow-up again with another email.
|
@@ -29,8 +29,8 @@ import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
josecipher "github.com/go-jose/go-jose/v3/cipher"
|
||||
"github.com/go-jose/go-jose/v3/json"
|
||||
josecipher "github.com/go-jose/go-jose/v4/cipher"
|
||||
"github.com/go-jose/go-jose/v4/json"
|
||||
)
|
||||
|
||||
// A generic RSA-based encrypter/verifier
|
||||
@@ -285,6 +285,9 @@ func (ctx rsaDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm
|
||||
|
||||
switch alg {
|
||||
case RS256, RS384, RS512:
|
||||
// TODO(https://github.com/go-jose/go-jose/issues/40): As of go1.20, the
|
||||
// random parameter is legacy and ignored, and it can be nil.
|
||||
// https://cs.opensource.google/go/go/+/refs/tags/go1.20:src/crypto/rsa/pkcs1v15.go;l=263;bpv=0;bpt=1
|
||||
out, err = rsa.SignPKCS1v15(RandReader, ctx.privateKey, hash, hashed)
|
||||
case PS256, PS384, PS512:
|
||||
out, err = rsa.SignPSS(RandReader, ctx.privateKey, hash, hashed, &rsa.PSSOptions{
|
@@ -21,9 +21,8 @@ import (
|
||||
"crypto/rsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"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.
|
||||
@@ -76,14 +75,24 @@ type recipientKeyInfo struct {
|
||||
type EncrypterOptions struct {
|
||||
Compression CompressionAlgorithm
|
||||
|
||||
// Optional map of additional keys to be inserted into the protected header
|
||||
// of a JWS object. Some specifications which make use of JWS like to insert
|
||||
// additional values here. All values must be JSON-serializable.
|
||||
// Optional map of name/value pairs to be inserted into the protected
|
||||
// header of a JWS object. Some specifications which make use of
|
||||
// JWS require additional values here.
|
||||
//
|
||||
// Values will be serialized by [json.Marshal] and must be valid inputs to
|
||||
// that function.
|
||||
//
|
||||
// [json.Marshal]: https://pkg.go.dev/encoding/json#Marshal
|
||||
ExtraHeaders map[HeaderKey]interface{}
|
||||
}
|
||||
|
||||
// WithHeader adds an arbitrary value to the ExtraHeaders map, initializing it
|
||||
// if necessary. It returns itself and so can be used in a fluent style.
|
||||
// if necessary, and returns the updated EncrypterOptions.
|
||||
//
|
||||
// The v parameter will be serialized by [json.Marshal] and must be a valid
|
||||
// input to that function.
|
||||
//
|
||||
// [json.Marshal]: https://pkg.go.dev/encoding/json#Marshal
|
||||
func (eo *EncrypterOptions) WithHeader(k HeaderKey, v interface{}) *EncrypterOptions {
|
||||
if eo.ExtraHeaders == nil {
|
||||
eo.ExtraHeaders = map[HeaderKey]interface{}{}
|
||||
@@ -111,7 +120,17 @@ func (eo *EncrypterOptions) WithType(typ ContentType) *EncrypterOptions {
|
||||
// default of 100000 will be used for the count and a 128-bit random salt will
|
||||
// be generated.
|
||||
type Recipient struct {
|
||||
Algorithm KeyAlgorithm
|
||||
Algorithm KeyAlgorithm
|
||||
// Key must have one of these types:
|
||||
// - ed25519.PublicKey
|
||||
// - *ecdsa.PublicKey
|
||||
// - *rsa.PublicKey
|
||||
// - *JSONWebKey
|
||||
// - JSONWebKey
|
||||
// - []byte (a symmetric key)
|
||||
// - Any type that satisfies the OpaqueKeyEncrypter interface
|
||||
//
|
||||
// The type of Key must match the value of Algorithm.
|
||||
Key interface{}
|
||||
KeyID string
|
||||
PBES2Count int
|
||||
@@ -150,16 +169,17 @@ func NewEncrypter(enc ContentEncryption, rcpt Recipient, opts *EncrypterOptions)
|
||||
switch rcpt.Algorithm {
|
||||
case DIRECT:
|
||||
// Direct encryption mode must be treated differently
|
||||
if reflect.TypeOf(rawKey) != reflect.TypeOf([]byte{}) {
|
||||
keyBytes, ok := rawKey.([]byte)
|
||||
if !ok {
|
||||
return nil, ErrUnsupportedKeyType
|
||||
}
|
||||
if encrypter.cipher.keySize() != len(rawKey.([]byte)) {
|
||||
if encrypter.cipher.keySize() != len(keyBytes) {
|
||||
return nil, ErrInvalidKeySize
|
||||
}
|
||||
encrypter.keyGenerator = staticKeyGenerator{
|
||||
key: rawKey.([]byte),
|
||||
key: keyBytes,
|
||||
}
|
||||
recipientInfo, _ := newSymmetricRecipient(rcpt.Algorithm, rawKey.([]byte))
|
||||
recipientInfo, _ := newSymmetricRecipient(rcpt.Algorithm, keyBytes)
|
||||
recipientInfo.keyID = keyID
|
||||
if rcpt.KeyID != "" {
|
||||
recipientInfo.keyID = rcpt.KeyID
|
||||
@@ -168,16 +188,16 @@ func NewEncrypter(enc ContentEncryption, rcpt Recipient, opts *EncrypterOptions)
|
||||
return encrypter, nil
|
||||
case ECDH_ES:
|
||||
// ECDH-ES (w/o key wrapping) is similar to DIRECT mode
|
||||
typeOf := reflect.TypeOf(rawKey)
|
||||
if typeOf != reflect.TypeOf(&ecdsa.PublicKey{}) {
|
||||
keyDSA, ok := rawKey.(*ecdsa.PublicKey)
|
||||
if !ok {
|
||||
return nil, ErrUnsupportedKeyType
|
||||
}
|
||||
encrypter.keyGenerator = ecKeyGenerator{
|
||||
size: encrypter.cipher.keySize(),
|
||||
algID: string(enc),
|
||||
publicKey: rawKey.(*ecdsa.PublicKey),
|
||||
publicKey: keyDSA,
|
||||
}
|
||||
recipientInfo, _ := newECDHRecipient(rcpt.Algorithm, rawKey.(*ecdsa.PublicKey))
|
||||
recipientInfo, _ := newECDHRecipient(rcpt.Algorithm, keyDSA)
|
||||
recipientInfo.keyID = keyID
|
||||
if rcpt.KeyID != "" {
|
||||
recipientInfo.keyID = rcpt.KeyID
|
||||
@@ -270,9 +290,8 @@ func makeJWERecipient(alg KeyAlgorithm, encryptionKey interface{}) (recipientKey
|
||||
recipient, err := makeJWERecipient(alg, encryptionKey.Key)
|
||||
recipient.keyID = encryptionKey.KeyID
|
||||
return recipient, err
|
||||
}
|
||||
if encrypter, ok := encryptionKey.(OpaqueKeyEncrypter); ok {
|
||||
return newOpaqueKeyEncrypter(alg, encrypter)
|
||||
case OpaqueKeyEncrypter:
|
||||
return newOpaqueKeyEncrypter(alg, encryptionKey)
|
||||
}
|
||||
return recipientKeyInfo{}, ErrUnsupportedKeyType
|
||||
}
|
||||
@@ -300,11 +319,11 @@ func newDecrypter(decryptionKey interface{}) (keyDecrypter, error) {
|
||||
return newDecrypter(decryptionKey.Key)
|
||||
case *JSONWebKey:
|
||||
return newDecrypter(decryptionKey.Key)
|
||||
case OpaqueKeyDecrypter:
|
||||
return &opaqueKeyDecrypter{decrypter: decryptionKey}, nil
|
||||
default:
|
||||
return nil, ErrUnsupportedKeyType
|
||||
}
|
||||
if okd, ok := decryptionKey.(OpaqueKeyDecrypter); ok {
|
||||
return &opaqueKeyDecrypter{decrypter: okd}, nil
|
||||
}
|
||||
return nil, ErrUnsupportedKeyType
|
||||
}
|
||||
|
||||
// Implementation of encrypt method producing a JWE object.
|
||||
@@ -403,9 +422,27 @@ func (ctx *genericEncrypter) Options() EncrypterOptions {
|
||||
}
|
||||
}
|
||||
|
||||
// Decrypt and validate the object and return the plaintext. Note that this
|
||||
// function does not support multi-recipient, if you desire multi-recipient
|
||||
// Decrypt and validate the object and return the plaintext. This
|
||||
// function does not support multi-recipient. If you desire multi-recipient
|
||||
// decryption use DecryptMulti instead.
|
||||
//
|
||||
// The decryptionKey argument must contain a private or symmetric key
|
||||
// and must have one of these types:
|
||||
// - *ecdsa.PrivateKey
|
||||
// - *rsa.PrivateKey
|
||||
// - *JSONWebKey
|
||||
// - JSONWebKey
|
||||
// - *JSONWebKeySet
|
||||
// - JSONWebKeySet
|
||||
// - []byte (a symmetric key)
|
||||
// - string (a symmetric key)
|
||||
// - Any type that satisfies the OpaqueKeyDecrypter interface.
|
||||
//
|
||||
// Note that ed25519 is only available for signatures, not encryption, so is
|
||||
// not an option here.
|
||||
//
|
||||
// Automatically decompresses plaintext, but returns an error if the decompressed
|
||||
// data would be >250kB or >10x the size of the compressed data, whichever is larger.
|
||||
func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error) {
|
||||
headers := obj.mergedHeaders(nil)
|
||||
|
||||
@@ -462,15 +499,24 @@ func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error)
|
||||
// The "zip" header parameter may only be present in the protected header.
|
||||
if comp := obj.protected.getCompression(); comp != "" {
|
||||
plaintext, err = decompress(comp, plaintext)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("go-jose/go-jose: failed to decompress plaintext: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return plaintext, err
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
// DecryptMulti decrypts and validates the object and returns the plaintexts,
|
||||
// with support for multiple recipients. It returns the index of the recipient
|
||||
// for which the decryption was successful, the merged headers for that recipient,
|
||||
// and the plaintext.
|
||||
//
|
||||
// The decryptionKey argument must have one of the types allowed for the
|
||||
// decryptionKey argument of Decrypt().
|
||||
//
|
||||
// Automatically decompresses plaintext, but returns an error if the decompressed
|
||||
// data would be >250kB or >3x the size of the compressed data, whichever is larger.
|
||||
func (obj JSONWebEncryption) DecryptMulti(decryptionKey interface{}) (int, Header, []byte, error) {
|
||||
globalHeaders := obj.mergedHeaders(nil)
|
||||
|
||||
@@ -532,7 +578,10 @@ func (obj JSONWebEncryption) DecryptMulti(decryptionKey interface{}) (int, Heade
|
||||
|
||||
// The "zip" header parameter may only be present in the protected header.
|
||||
if comp := obj.protected.getCompression(); comp != "" {
|
||||
plaintext, _ = decompress(comp, plaintext)
|
||||
plaintext, err = decompress(comp, plaintext)
|
||||
if err != nil {
|
||||
return -1, Header{}, nil, fmt.Errorf("go-jose/go-jose: failed to decompress plaintext: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
sanitized, err := headers.sanitized()
|
@@ -15,13 +15,11 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
Package jose aims to provide an implementation of the Javascript Object Signing
|
||||
and Encryption set of standards. It implements encryption and signing based on
|
||||
the JSON Web Encryption and JSON Web Signature standards, with optional JSON Web
|
||||
Token support available in a sub-package. The library supports both the compact
|
||||
and JWS/JWE JSON Serialization formats, and has optional support for multiple
|
||||
recipients.
|
||||
|
||||
*/
|
||||
package jose
|
@@ -21,12 +21,13 @@ import (
|
||||
"compress/flate"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/go-jose/go-jose/v3/json"
|
||||
"github.com/go-jose/go-jose/v4/json"
|
||||
)
|
||||
|
||||
// Helper function to serialize known-good objects.
|
||||
@@ -85,7 +86,7 @@ func decompress(algorithm CompressionAlgorithm, input []byte) ([]byte, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Compress with DEFLATE
|
||||
// deflate compresses the input.
|
||||
func deflate(input []byte) ([]byte, error) {
|
||||
output := new(bytes.Buffer)
|
||||
|
||||
@@ -97,15 +98,24 @@ func deflate(input []byte) ([]byte, error) {
|
||||
return output.Bytes(), err
|
||||
}
|
||||
|
||||
// Decompress with DEFLATE
|
||||
// inflate decompresses the input.
|
||||
//
|
||||
// Errors if the decompressed data would be >250kB or >10x the size of the
|
||||
// compressed data, whichever is larger.
|
||||
func inflate(input []byte) ([]byte, error) {
|
||||
output := new(bytes.Buffer)
|
||||
reader := flate.NewReader(bytes.NewBuffer(input))
|
||||
|
||||
_, err := io.Copy(output, reader)
|
||||
if err != nil {
|
||||
maxCompressedSize := max(250_000, 10*int64(len(input)))
|
||||
|
||||
limit := maxCompressedSize + 1
|
||||
n, err := io.CopyN(output, reader, limit)
|
||||
if err != nil && err != io.EOF {
|
||||
return nil, err
|
||||
}
|
||||
if n == limit {
|
||||
return nil, fmt.Errorf("uncompressed data would be too large (>%d bytes)", maxCompressedSize)
|
||||
}
|
||||
|
||||
err = reader.Close()
|
||||
return output.Bytes(), err
|
||||
@@ -154,7 +164,7 @@ func (b *byteBuffer) UnmarshalJSON(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
decoded, err := base64URLDecode(encoded)
|
||||
decoded, err := base64.RawURLEncoding.DecodeString(encoded)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -184,8 +194,35 @@ func (b byteBuffer) toInt() int {
|
||||
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 {
|
||||
return base64.RawURLEncoding.EncodedLen(len(sl))
|
||||
}
|
||||
|
||||
func base64JoinWithDots(inputs ...[]byte) string {
|
||||
if len(inputs) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Count of dots.
|
||||
totalCount := len(inputs) - 1
|
||||
|
||||
for _, input := range inputs {
|
||||
totalCount += base64EncodeLen(input)
|
||||
}
|
||||
|
||||
out := make([]byte, totalCount)
|
||||
startEncode := 0
|
||||
for i, input := range inputs {
|
||||
base64.RawURLEncoding.Encode(out[startEncode:], input)
|
||||
|
||||
if i == len(inputs)-1 {
|
||||
continue
|
||||
}
|
||||
|
||||
startEncode += base64EncodeLen(input)
|
||||
out[startEncode] = '.'
|
||||
startEncode++
|
||||
}
|
||||
|
||||
return string(out)
|
||||
}
|
@@ -75,14 +75,13 @@ import (
|
||||
//
|
||||
// The JSON null value unmarshals into an interface, map, pointer, or slice
|
||||
// by setting that Go value to nil. Because null is often used in JSON to mean
|
||||
// ``not present,'' unmarshaling a JSON null into any other Go type has no effect
|
||||
// “not present,” unmarshaling a JSON null into any other Go type has no effect
|
||||
// on the value and produces no error.
|
||||
//
|
||||
// When unmarshaling quoted strings, invalid UTF-8 or
|
||||
// invalid UTF-16 surrogate pairs are not treated as an error.
|
||||
// Instead, they are replaced by the Unicode replacement
|
||||
// character U+FFFD.
|
||||
//
|
||||
func Unmarshal(data []byte, v interface{}) error {
|
||||
// Check for well-formedness.
|
||||
// Avoids filling out half a data structure
|
@@ -58,6 +58,7 @@ import (
|
||||
// becomes a member of the object unless
|
||||
// - the field's tag is "-", or
|
||||
// - the field is empty and its tag specifies the "omitempty" option.
|
||||
//
|
||||
// The empty values are false, 0, any
|
||||
// nil pointer or interface value, and any array, slice, map, or string of
|
||||
// length zero. The object's default key string is the struct field name
|
||||
@@ -65,28 +66,28 @@ import (
|
||||
// the struct field's tag value is the key name, followed by an optional comma
|
||||
// and options. Examples:
|
||||
//
|
||||
// // Field is ignored by this package.
|
||||
// Field int `json:"-"`
|
||||
// // Field is ignored by this package.
|
||||
// Field int `json:"-"`
|
||||
//
|
||||
// // Field appears in JSON as key "myName".
|
||||
// Field int `json:"myName"`
|
||||
// // Field appears in JSON as key "myName".
|
||||
// Field int `json:"myName"`
|
||||
//
|
||||
// // Field appears in JSON as key "myName" and
|
||||
// // the field is omitted from the object if its value is empty,
|
||||
// // as defined above.
|
||||
// Field int `json:"myName,omitempty"`
|
||||
// // Field appears in JSON as key "myName" and
|
||||
// // the field is omitted from the object if its value is empty,
|
||||
// // as defined above.
|
||||
// Field int `json:"myName,omitempty"`
|
||||
//
|
||||
// // Field appears in JSON as key "Field" (the default), but
|
||||
// // the field is skipped if empty.
|
||||
// // Note the leading comma.
|
||||
// Field int `json:",omitempty"`
|
||||
// // Field appears in JSON as key "Field" (the default), but
|
||||
// // the field is skipped if empty.
|
||||
// // Note the leading comma.
|
||||
// Field int `json:",omitempty"`
|
||||
//
|
||||
// The "string" option signals that a field is stored as JSON inside a
|
||||
// JSON-encoded string. It applies only to fields of string, floating point,
|
||||
// integer, or boolean types. This extra level of encoding is sometimes used
|
||||
// when communicating with JavaScript programs:
|
||||
//
|
||||
// Int64String int64 `json:",string"`
|
||||
// Int64String int64 `json:",string"`
|
||||
//
|
||||
// The key name will be used if it's a non-empty string consisting of
|
||||
// only Unicode letters, digits, dollar signs, percent signs, hyphens,
|
||||
@@ -133,7 +134,6 @@ import (
|
||||
// JSON cannot represent cyclic data structures and Marshal does not
|
||||
// handle them. Passing cyclic structures to Marshal will result in
|
||||
// an infinite recursion.
|
||||
//
|
||||
func Marshal(v interface{}) ([]byte, error) {
|
||||
e := &encodeState{}
|
||||
err := e.marshal(v)
|
@@ -240,7 +240,6 @@ var _ Unmarshaler = (*RawMessage)(nil)
|
||||
// Number, for JSON numbers
|
||||
// string, for JSON string literals
|
||||
// nil, for JSON null
|
||||
//
|
||||
type Token interface{}
|
||||
|
||||
const (
|
148
vendor/github.com/go-jose/go-jose/v3/jwe.go → vendor/github.com/go-jose/go-jose/v4/jwe.go
generated
vendored
148
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 (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"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.
|
||||
@@ -104,29 +105,75 @@ func (obj JSONWebEncryption) computeAuthData() []byte {
|
||||
return output
|
||||
}
|
||||
|
||||
// ParseEncrypted parses an encrypted message in compact or JWE JSON Serialization format.
|
||||
func ParseEncrypted(input string) (*JSONWebEncryption, error) {
|
||||
input = stripWhitespace(input)
|
||||
if strings.HasPrefix(input, "{") {
|
||||
return parseEncryptedFull(input)
|
||||
func containsKeyAlgorithm(haystack []KeyAlgorithm, needle KeyAlgorithm) bool {
|
||||
for _, algorithm := range haystack {
|
||||
if algorithm == needle {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return parseEncryptedCompact(input)
|
||||
return false
|
||||
}
|
||||
|
||||
// parseEncryptedFull parses a message in compact format.
|
||||
func parseEncryptedFull(input string) (*JSONWebEncryption, error) {
|
||||
func containsContentEncryption(haystack []ContentEncryption, needle ContentEncryption) bool {
|
||||
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
|
||||
err := json.Unmarshal([]byte(input), &parsed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return parsed.sanitized()
|
||||
return parsed.sanitized(keyEncryptionAlgorithms, contentEncryption)
|
||||
}
|
||||
|
||||
// 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{
|
||||
original: parsed,
|
||||
unprotected: parsed.Unprotected,
|
||||
@@ -170,7 +217,7 @@ func (parsed *rawJSONWebEncryption) sanitized() (*JSONWebEncryption, error) {
|
||||
} else {
|
||||
obj.recipients = make([]recipientInfo, len(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 {
|
||||
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)
|
||||
if headers.getAlgorithm() == "" || headers.getEncryption() == "" {
|
||||
return nil, fmt.Errorf("go-jose/go-jose: message is missing alg/enc headers")
|
||||
if headers.getAlgorithm() == "" {
|
||||
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
|
||||
}
|
||||
|
||||
// parseEncryptedCompact parses a message in compact format.
|
||||
func parseEncryptedCompact(input string) (*JSONWebEncryption, error) {
|
||||
func validateAlgEnc(headers rawHeader, keyAlgorithms []KeyAlgorithm, contentEncryption []ContentEncryption) 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, ".")
|
||||
if len(parts) != 5 {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
encryptedKey, err := base64URLDecode(parts[1])
|
||||
encryptedKey, err := base64.RawURLEncoding.DecodeString(parts[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
iv, err := base64URLDecode(parts[2])
|
||||
iv, err := base64.RawURLEncoding.DecodeString(parts[2])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ciphertext, err := base64URLDecode(parts[3])
|
||||
ciphertext, err := base64.RawURLEncoding.DecodeString(parts[3])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tag, err := base64URLDecode(parts[4])
|
||||
tag, err := base64.RawURLEncoding.DecodeString(parts[4])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -240,7 +326,7 @@ func parseEncryptedCompact(input string) (*JSONWebEncryption, error) {
|
||||
Tag: newBuffer(tag),
|
||||
}
|
||||
|
||||
return raw.sanitized()
|
||||
return raw.sanitized(keyAlgorithms, contentEncryption)
|
||||
}
|
||||
|
||||
// CompactSerialize serializes an object using the compact serialization format.
|
||||
@@ -252,13 +338,13 @@ func (obj JSONWebEncryption) CompactSerialize() (string, error) {
|
||||
|
||||
serializedProtected := mustSerializeJSON(obj.protected)
|
||||
|
||||
return fmt.Sprintf(
|
||||
"%s.%s.%s.%s.%s",
|
||||
base64.RawURLEncoding.EncodeToString(serializedProtected),
|
||||
base64.RawURLEncoding.EncodeToString(obj.recipients[0].encryptedKey),
|
||||
base64.RawURLEncoding.EncodeToString(obj.iv),
|
||||
base64.RawURLEncoding.EncodeToString(obj.ciphertext),
|
||||
base64.RawURLEncoding.EncodeToString(obj.tag)), nil
|
||||
return base64JoinWithDots(
|
||||
serializedProtected,
|
||||
obj.recipients[0].encryptedKey,
|
||||
obj.iv,
|
||||
obj.ciphertext,
|
||||
obj.tag,
|
||||
), nil
|
||||
}
|
||||
|
||||
// FullSerialize serializes an object using the full JSON serialization format.
|
@@ -35,7 +35,7 @@ import (
|
||||
"reflect"
|
||||
"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.
|
||||
@@ -67,9 +67,21 @@ type rawJSONWebKey struct {
|
||||
X5tSHA256 string `json:"x5t#S256,omitempty"`
|
||||
}
|
||||
|
||||
// JSONWebKey represents a public or private key in JWK format.
|
||||
// JSONWebKey represents a public or private key in JWK format. It can be
|
||||
// marshaled into JSON and unmarshaled from JSON.
|
||||
type JSONWebKey struct {
|
||||
// Cryptographic key, can be a symmetric or asymmetric key.
|
||||
// Key is the Go in-memory representation of this key. It must have one
|
||||
// of these types:
|
||||
// - ed25519.PublicKey
|
||||
// - ed25519.PrivateKey
|
||||
// - *ecdsa.PublicKey
|
||||
// - *ecdsa.PrivateKey
|
||||
// - *rsa.PublicKey
|
||||
// - *rsa.PrivateKey
|
||||
// - []byte (a symmetric key)
|
||||
//
|
||||
// When marshaling this JSONWebKey into JSON, the "kty" header parameter
|
||||
// will be automatically set based on the type of this field.
|
||||
Key interface{}
|
||||
// Key identifier, parsed from `kid` header.
|
||||
KeyID string
|
||||
@@ -254,7 +266,7 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) {
|
||||
|
||||
// x5t parameters are base64url-encoded SHA thumbprints
|
||||
// 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 {
|
||||
return errors.New("go-jose/go-jose: invalid JWK, x5t header has invalid encoding")
|
||||
}
|
||||
@@ -274,7 +286,7 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) {
|
||||
|
||||
k.CertificateThumbprintSHA1 = x5tSHA1bytes
|
||||
|
||||
x5tSHA256bytes, err := base64URLDecode(raw.X5tSHA256)
|
||||
x5tSHA256bytes, err := base64.RawURLEncoding.DecodeString(raw.X5tSHA256)
|
||||
if err != nil {
|
||||
return errors.New("go-jose/go-jose: invalid JWK, x5t#S256 header has invalid encoding")
|
||||
}
|
||||
@@ -389,6 +401,8 @@ func (k *JSONWebKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
|
||||
input, err = rsaThumbprintInput(key.N, key.E)
|
||||
case ed25519.PrivateKey:
|
||||
input, err = edThumbprintInput(ed25519.PublicKey(key[32:]))
|
||||
case OpaqueSigner:
|
||||
return key.Public().Thumbprint(hash)
|
||||
default:
|
||||
return nil, fmt.Errorf("go-jose/go-jose: unknown key type '%s'", reflect.TypeOf(key))
|
||||
}
|
@@ -23,7 +23,7 @@ import (
|
||||
"fmt"
|
||||
"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.
|
||||
@@ -75,22 +75,41 @@ type Signature struct {
|
||||
original *rawSignatureInfo
|
||||
}
|
||||
|
||||
// ParseSigned parses a signed message in compact or JWS JSON Serialization format.
|
||||
func ParseSigned(signature string) (*JSONWebSignature, error) {
|
||||
// ParseSigned parses a signed message in JWS Compact or JWS JSON Serialization.
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/rfc7515#section-7
|
||||
func ParseSigned(
|
||||
signature string,
|
||||
signatureAlgorithms []SignatureAlgorithm,
|
||||
) (*JSONWebSignature, error) {
|
||||
signature = stripWhitespace(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.
|
||||
func ParseDetached(signature string, payload []byte) (*JSONWebSignature, error) {
|
||||
func ParseDetached(
|
||||
signature string,
|
||||
payload []byte,
|
||||
signatureAlgorithms []SignatureAlgorithm,
|
||||
) (*JSONWebSignature, error) {
|
||||
if payload == nil {
|
||||
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
|
||||
@@ -137,19 +156,36 @@ func (obj JSONWebSignature) computeAuthData(payload []byte, signature *Signature
|
||||
return authData.Bytes(), nil
|
||||
}
|
||||
|
||||
// parseSignedFull parses a message in full format.
|
||||
func parseSignedFull(input string) (*JSONWebSignature, error) {
|
||||
// ParseSignedJSON parses a message in JWS JSON Serialization.
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/rfc7515#section-7.2
|
||||
func ParseSignedJSON(
|
||||
input string,
|
||||
signatureAlgorithms []SignatureAlgorithm,
|
||||
) (*JSONWebSignature, error) {
|
||||
var parsed rawJSONWebSignature
|
||||
err := json.Unmarshal([]byte(input), &parsed)
|
||||
if err != nil {
|
||||
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.
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
signature.Unprotected, err = signature.header.sanitized()
|
||||
if err != nil {
|
||||
@@ -241,6 +283,12 @@ func (parsed *rawJSONWebSignature) sanitized() (*JSONWebSignature, error) {
|
||||
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 {
|
||||
obj.Signatures[i].Unprotected, err = obj.Signatures[i].header.sanitized()
|
||||
if err != nil {
|
||||
@@ -274,7 +322,11 @@ func (parsed *rawJSONWebSignature) sanitized() (*JSONWebSignature, error) {
|
||||
}
|
||||
|
||||
// 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, ".")
|
||||
if len(parts) != 3 {
|
||||
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")
|
||||
}
|
||||
|
||||
rawProtected, err := base64URLDecode(parts[0])
|
||||
rawProtected, err := base64.RawURLEncoding.DecodeString(parts[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if payload == nil {
|
||||
payload, err = base64URLDecode(parts[1])
|
||||
payload, err = base64.RawURLEncoding.DecodeString(parts[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
signature, err := base64URLDecode(parts[2])
|
||||
signature, err := base64.RawURLEncoding.DecodeString(parts[2])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -306,7 +358,7 @@ func parseSignedCompact(input string, payload []byte) (*JSONWebSignature, error)
|
||||
Protected: newBuffer(rawProtected),
|
||||
Signature: newBuffer(signature),
|
||||
}
|
||||
return raw.sanitized()
|
||||
return raw.sanitized(signatureAlgorithms)
|
||||
}
|
||||
|
||||
func (obj JSONWebSignature) compactSerialize(detached bool) (string, error) {
|
||||
@@ -314,15 +366,18 @@ func (obj JSONWebSignature) compactSerialize(detached bool) (string, error) {
|
||||
return "", ErrNotSupported
|
||||
}
|
||||
|
||||
serializedProtected := base64.RawURLEncoding.EncodeToString(mustSerializeJSON(obj.Signatures[0].protected))
|
||||
payload := ""
|
||||
signature := base64.RawURLEncoding.EncodeToString(obj.Signatures[0].Signature)
|
||||
serializedProtected := mustSerializeJSON(obj.Signatures[0].protected)
|
||||
|
||||
var payload []byte
|
||||
if !detached {
|
||||
payload = base64.RawURLEncoding.EncodeToString(obj.payload)
|
||||
payload = obj.payload
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s.%s.%s", serializedProtected, payload, signature), nil
|
||||
return base64JoinWithDots(
|
||||
serializedProtected,
|
||||
payload,
|
||||
obj.Signatures[0].Signature,
|
||||
), nil
|
||||
}
|
||||
|
||||
// CompactSerialize serializes an object using the compact serialization format.
|
@@ -21,13 +21,13 @@ import (
|
||||
"bytes"
|
||||
"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
|
||||
// errors are accumulated until the final call to CompactSerialize/FullSerialize.
|
||||
// errors are accumulated until the final call to Serialize.
|
||||
type Builder interface {
|
||||
// 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
|
||||
@@ -36,15 +36,13 @@ type Builder interface {
|
||||
Claims(i interface{}) Builder
|
||||
// Token builds a JSONWebToken from provided data.
|
||||
Token() (*JSONWebToken, error)
|
||||
// FullSerialize serializes a token using the JWS/JWE JSON Serialization format.
|
||||
FullSerialize() (string, error)
|
||||
// CompactSerialize serializes a token using the compact serialization format.
|
||||
CompactSerialize() (string, error)
|
||||
// Serialize serializes a token.
|
||||
Serialize() (string, error)
|
||||
}
|
||||
|
||||
// NestedBuilder is a utility for making Signed-Then-Encrypted JSON Web Tokens.
|
||||
// Calls can be chained, and errors are accumulated until final call to
|
||||
// CompactSerialize/FullSerialize.
|
||||
// Serialize.
|
||||
type NestedBuilder interface {
|
||||
// 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
|
||||
@@ -53,10 +51,8 @@ type NestedBuilder interface {
|
||||
Claims(i interface{}) NestedBuilder
|
||||
// Token builds a NestedJSONWebToken from provided data.
|
||||
Token() (*NestedJSONWebToken, error)
|
||||
// FullSerialize serializes a token using the JSON Serialization format.
|
||||
FullSerialize() (string, error)
|
||||
// CompactSerialize serializes a token using the compact serialization format.
|
||||
CompactSerialize() (string, error)
|
||||
// Serialize serializes a token.
|
||||
Serialize() (string, error)
|
||||
}
|
||||
|
||||
type builder struct {
|
||||
@@ -194,7 +190,7 @@ func (b *signedBuilder) Token() (*JSONWebToken, error) {
|
||||
return b.builder.token(sig.Verify, h)
|
||||
}
|
||||
|
||||
func (b *signedBuilder) CompactSerialize() (string, error) {
|
||||
func (b *signedBuilder) Serialize() (string, error) {
|
||||
sig, err := b.sign()
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -203,15 +199,6 @@ func (b *signedBuilder) CompactSerialize() (string, error) {
|
||||
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) {
|
||||
if b.err != nil {
|
||||
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()
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -241,15 +228,6 @@ func (b *encryptedBuilder) CompactSerialize() (string, error) {
|
||||
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) {
|
||||
enc, err := b.encrypt()
|
||||
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) {
|
||||
enc, err := b.signAndEncrypt()
|
||||
if err != nil {
|
||||
@@ -287,12 +267,13 @@ func (b *nestedBuilder) Token() (*NestedJSONWebToken, error) {
|
||||
}
|
||||
|
||||
return &NestedJSONWebToken{
|
||||
enc: enc,
|
||||
Headers: []jose.Header{enc.Header},
|
||||
allowedSignatureAlgorithms: nil,
|
||||
enc: enc,
|
||||
Headers: []jose.Header{enc.Header},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *nestedBuilder) CompactSerialize() (string, error) {
|
||||
func (b *nestedBuilder) Serialize() (string, error) {
|
||||
enc, err := b.signAndEncrypt()
|
||||
if err != nil {
|
||||
return "", err
|
@@ -21,7 +21,7 @@ import (
|
||||
"strconv"
|
||||
"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).
|
||||
@@ -119,7 +119,7 @@ func (s Audience) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal([]string(s))
|
||||
}
|
||||
|
||||
//Contains checks whether a given string is included in the Audience
|
||||
// Contains checks whether a given string is included in the Audience
|
||||
func (s Audience) Contains(v string) bool {
|
||||
for _, a := range s {
|
||||
if a == v {
|
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
Package jwt provides an implementation of the JSON Web Token standard.
|
||||
|
||||
*/
|
||||
package jwt
|
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
|
||||
// Subject matches the "sub" claim exactly.
|
||||
Subject string
|
||||
// Audience matches the values in "aud" claim, regardless of their order.
|
||||
Audience Audience
|
||||
// AnyAudience matches if there is a non-empty intersection between
|
||||
// its values and the values in the "aud" claim.
|
||||
AnyAudience Audience
|
||||
// ID matches the "jti" claim exactly.
|
||||
ID string
|
||||
// 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
|
||||
}
|
||||
|
||||
if len(e.Audience) != 0 {
|
||||
for _, v := range e.Audience {
|
||||
if !c.Audience.Contains(v) {
|
||||
return ErrInvalidAudience
|
||||
if len(e.AnyAudience) != 0 {
|
||||
var intersection bool
|
||||
for _, v := range e.AnyAudience {
|
||||
if c.Audience.Contains(v) {
|
||||
intersection = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !intersection {
|
||||
return ErrInvalidAudience
|
||||
}
|
||||
}
|
||||
|
||||
// validate using the e.Time, or time.Now if not provided
|
@@ -121,7 +121,7 @@ func (oke *opaqueKeyEncrypter) encryptKey(cek []byte, alg KeyAlgorithm) (recipie
|
||||
return oke.encrypter.encryptKey(cek, alg)
|
||||
}
|
||||
|
||||
//OpaqueKeyDecrypter is an interface that supports decrypting keys with an opaque key.
|
||||
// OpaqueKeyDecrypter is an interface that supports decrypting keys with an opaque key.
|
||||
type OpaqueKeyDecrypter interface {
|
||||
DecryptKey(encryptedKey []byte, header Header) ([]byte, error)
|
||||
}
|
@@ -23,7 +23,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-jose/go-jose/v3/json"
|
||||
"github.com/go-jose/go-jose/v4/json"
|
||||
)
|
||||
|
||||
// KeyAlgorithm represents a key management algorithm.
|
||||
@@ -183,8 +183,13 @@ type Header struct {
|
||||
// Unverified certificate chain parsed from x5c header.
|
||||
certificates []*x509.Certificate
|
||||
|
||||
// Any headers not recognised above get unmarshalled
|
||||
// from JSON in a generic manner and placed in this map.
|
||||
// At parse time, each header parameter with a name other than "kid",
|
||||
// "jwk", "alg", "nonce", or "x5c" will have its value passed to
|
||||
// [json.Unmarshal] to unmarshal it into an interface value.
|
||||
// The resulting value will be stored in this map, with the header
|
||||
// parameter name as the key.
|
||||
//
|
||||
// [json.Unmarshal]: https://pkg.go.dev/encoding/json#Unmarshal
|
||||
ExtraHeaders map[HeaderKey]interface{}
|
||||
}
|
||||
|
@@ -25,7 +25,7 @@ import (
|
||||
"errors"
|
||||
"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
|
||||
@@ -40,6 +40,20 @@ type Signer interface {
|
||||
}
|
||||
|
||||
// SigningKey represents an algorithm/key used to sign a message.
|
||||
//
|
||||
// Key must have one of these types:
|
||||
// - ed25519.PrivateKey
|
||||
// - *ecdsa.PrivateKey
|
||||
// - *rsa.PrivateKey
|
||||
// - *JSONWebKey
|
||||
// - JSONWebKey
|
||||
// - []byte (an HMAC key)
|
||||
// - 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 {
|
||||
Algorithm SignatureAlgorithm
|
||||
Key interface{}
|
||||
@@ -52,12 +66,22 @@ type SignerOptions struct {
|
||||
|
||||
// Optional map of additional keys to be inserted into the protected header
|
||||
// of a JWS object. Some specifications which make use of JWS like to insert
|
||||
// additional values here. All values must be JSON-serializable.
|
||||
// additional values here.
|
||||
//
|
||||
// Values will be serialized by [json.Marshal] and must be valid inputs to
|
||||
// that function.
|
||||
//
|
||||
// [json.Marshal]: https://pkg.go.dev/encoding/json#Marshal
|
||||
ExtraHeaders map[HeaderKey]interface{}
|
||||
}
|
||||
|
||||
// WithHeader adds an arbitrary value to the ExtraHeaders map, initializing it
|
||||
// if necessary. It returns itself and so can be used in a fluent style.
|
||||
// if necessary, and returns the updated SignerOptions.
|
||||
//
|
||||
// The v argument will be serialized by [json.Marshal] and must be a valid
|
||||
// input to that function.
|
||||
//
|
||||
// [json.Marshal]: https://pkg.go.dev/encoding/json#Marshal
|
||||
func (so *SignerOptions) WithHeader(k HeaderKey, v interface{}) *SignerOptions {
|
||||
if so.ExtraHeaders == nil {
|
||||
so.ExtraHeaders = map[HeaderKey]interface{}{}
|
||||
@@ -173,11 +197,11 @@ func newVerifier(verificationKey interface{}) (payloadVerifier, error) {
|
||||
return newVerifier(verificationKey.Key)
|
||||
case *JSONWebKey:
|
||||
return newVerifier(verificationKey.Key)
|
||||
case OpaqueVerifier:
|
||||
return &opaqueVerifier{verifier: verificationKey}, nil
|
||||
default:
|
||||
return nil, ErrUnsupportedKeyType
|
||||
}
|
||||
if ov, ok := verificationKey.(OpaqueVerifier); ok {
|
||||
return &opaqueVerifier{verifier: ov}, nil
|
||||
}
|
||||
return nil, ErrUnsupportedKeyType
|
||||
}
|
||||
|
||||
func (ctx *genericSigner) addRecipient(alg SignatureAlgorithm, signingKey interface{}) error {
|
||||
@@ -204,11 +228,11 @@ func makeJWSRecipient(alg SignatureAlgorithm, signingKey interface{}) (recipient
|
||||
return newJWKSigner(alg, signingKey)
|
||||
case *JSONWebKey:
|
||||
return newJWKSigner(alg, *signingKey)
|
||||
case OpaqueSigner:
|
||||
return newOpaqueSigner(alg, signingKey)
|
||||
default:
|
||||
return recipientSigInfo{}, ErrUnsupportedKeyType
|
||||
}
|
||||
if signer, ok := signingKey.(OpaqueSigner); ok {
|
||||
return newOpaqueSigner(alg, signer)
|
||||
}
|
||||
return recipientSigInfo{}, ErrUnsupportedKeyType
|
||||
}
|
||||
|
||||
func newJWKSigner(alg SignatureAlgorithm, signingKey JSONWebKey) (recipientSigInfo, error) {
|
||||
@@ -321,12 +345,26 @@ func (ctx *genericSigner) Options() SignerOptions {
|
||||
}
|
||||
|
||||
// Verify validates the signature on the object and returns the payload.
|
||||
// This function does not support multi-signature, if you desire multi-sig
|
||||
// This function does not support multi-signature. If you desire multi-signature
|
||||
// verification use VerifyMulti instead.
|
||||
//
|
||||
// Be careful when verifying signatures based on embedded JWKs inside the
|
||||
// payload header. You cannot assume that the key received in a payload is
|
||||
// trusted.
|
||||
//
|
||||
// The verificationKey argument must have one of these types:
|
||||
// - ed25519.PublicKey
|
||||
// - *ecdsa.PublicKey
|
||||
// - *rsa.PublicKey
|
||||
// - *JSONWebKey
|
||||
// - JSONWebKey
|
||||
// - []byte (an HMAC key)
|
||||
// - 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) {
|
||||
err := obj.DetachedVerify(obj.payload, verificationKey)
|
||||
if err != nil {
|
||||
@@ -346,6 +384,9 @@ func (obj JSONWebSignature) UnsafePayloadWithoutVerification() []byte {
|
||||
// most cases, you will probably want to use Verify instead. DetachedVerify
|
||||
// is only useful if you have a payload and signature that are separated from
|
||||
// each other.
|
||||
//
|
||||
// The verificationKey argument must have one of the types allowed for the
|
||||
// verificationKey argument of JSONWebSignature.Verify().
|
||||
func (obj JSONWebSignature) DetachedVerify(payload []byte, verificationKey interface{}) error {
|
||||
key := tryJWKS(verificationKey, obj.headers()...)
|
||||
verifier, err := newVerifier(key)
|
||||
@@ -388,6 +429,9 @@ func (obj JSONWebSignature) DetachedVerify(payload []byte, verificationKey inter
|
||||
// returns the index of the signature that was verified, along with the signature
|
||||
// object and the payload. We return the signature and index to guarantee that
|
||||
// callers are getting the verified value.
|
||||
//
|
||||
// The verificationKey argument must have one of the types allowed for the
|
||||
// verificationKey argument of JSONWebSignature.Verify().
|
||||
func (obj JSONWebSignature) VerifyMulti(verificationKey interface{}) (int, Signature, []byte, error) {
|
||||
idx, sig, err := obj.DetachedVerifyMulti(obj.payload, verificationKey)
|
||||
if err != nil {
|
||||
@@ -405,6 +449,9 @@ func (obj JSONWebSignature) VerifyMulti(verificationKey interface{}) (int, Signa
|
||||
// DetachedVerifyMulti is only useful if you have a payload and signature that are
|
||||
// separated from each other, and the signature can have multiple signers at the
|
||||
// same time.
|
||||
//
|
||||
// The verificationKey argument must have one of the types allowed for the
|
||||
// verificationKey argument of JSONWebSignature.Verify().
|
||||
func (obj JSONWebSignature) DetachedVerifyMulti(payload []byte, verificationKey interface{}) (int, Signature, error) {
|
||||
key := tryJWKS(verificationKey, obj.headers()...)
|
||||
verifier, err := newVerifier(key)
|
@@ -32,7 +32,7 @@ import (
|
||||
|
||||
"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).
|
||||
@@ -40,12 +40,17 @@ var RandReader = rand.Reader
|
||||
|
||||
const (
|
||||
// RFC7518 recommends a minimum of 1,000 iterations:
|
||||
// https://tools.ietf.org/html/rfc7518#section-4.8.1.2
|
||||
// - https://tools.ietf.org/html/rfc7518#section-4.8.1.2
|
||||
//
|
||||
// NIST recommends a minimum of 10,000:
|
||||
// https://pages.nist.gov/800-63-3/sp800-63b.html
|
||||
// 1Password uses 100,000:
|
||||
// https://support.1password.com/pbkdf2/
|
||||
defaultP2C = 100000
|
||||
// - https://pages.nist.gov/800-63-3/sp800-63b.html
|
||||
//
|
||||
// 1Password increased in 2023 from 100,000 to 650,000:
|
||||
// - https://support.1password.com/pbkdf2/
|
||||
//
|
||||
// OWASP recommended 600,000 in Dec 2022:
|
||||
// - https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2
|
||||
defaultP2C = 600000
|
||||
// Default salt size: 128 bits
|
||||
defaultP2SSize = 16
|
||||
)
|
||||
@@ -415,6 +420,11 @@ func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipien
|
||||
if p2c <= 0 {
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid P2C: must be a positive integer")
|
||||
}
|
||||
if p2c > 1000000 {
|
||||
// An unauthenticated attacker can set a high P2C value. Set an upper limit to avoid
|
||||
// DoS attacks.
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid P2C: too high")
|
||||
}
|
||||
|
||||
// salt is UTF8(Alg) || 0x00 || Salt Input
|
||||
alg := headers.getAlgorithm()
|
||||
@@ -444,7 +454,7 @@ func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipien
|
||||
func (ctx symmetricMac) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) {
|
||||
mac, err := ctx.hmac(payload, alg)
|
||||
if err != nil {
|
||||
return Signature{}, errors.New("go-jose/go-jose: failed to compute hmac")
|
||||
return Signature{}, err
|
||||
}
|
||||
|
||||
return Signature{
|
||||
@@ -476,12 +486,24 @@ func (ctx symmetricMac) verifyPayload(payload []byte, mac []byte, alg SignatureA
|
||||
func (ctx symmetricMac) hmac(payload []byte, alg SignatureAlgorithm) ([]byte, error) {
|
||||
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 {
|
||||
case HS256:
|
||||
if len(ctx.key)*8 < 256 {
|
||||
return nil, ErrInvalidKeySize
|
||||
}
|
||||
hash = sha256.New
|
||||
case HS384:
|
||||
if len(ctx.key)*8 < 384 {
|
||||
return nil, ErrInvalidKeySize
|
||||
}
|
||||
hash = sha512.New384
|
||||
case HS512:
|
||||
if len(ctx.key)*8 < 512 {
|
||||
return nil, ErrInvalidKeySize
|
||||
}
|
||||
hash = sha512.New
|
||||
default:
|
||||
return nil, ErrUnsupportedAlgorithm
|
Reference in New Issue
Block a user