diff --git a/cmd/example-app/main.go b/cmd/example-app/main.go index d2ada840..32728841 100644 --- a/cmd/example-app/main.go +++ b/cmd/example-app/main.go @@ -17,7 +17,7 @@ import ( "strings" "time" - "github.com/ericchiang/oidc" + "github.com/coreos/go-oidc" "github.com/spf13/cobra" "golang.org/x/net/context" "golang.org/x/oauth2" @@ -173,7 +173,7 @@ func cmd() *cobra.Command { } a.provider = provider - a.verifier = provider.NewVerifier(a.ctx, oidc.VerifyAudience(a.clientID)) + a.verifier = provider.Verifier(oidc.VerifyAudience(a.clientID)) http.HandleFunc("/", a.handleIndex) http.HandleFunc("/login", a.handleLogin) @@ -269,7 +269,7 @@ func (a *app) handleCallback(w http.ResponseWriter, r *http.Request) { RefreshToken: refresh, Expiry: time.Now().Add(-time.Hour), } - token, err = oauth2Config.TokenSource(a.ctx, t).Token() + token, err = oauth2Config.TokenSource(r.Context(), t).Token() default: http.Error(w, fmt.Sprintf("no code in request: %q", r.Form), http.StatusBadRequest) return @@ -286,7 +286,7 @@ func (a *app) handleCallback(w http.ResponseWriter, r *http.Request) { return } - idToken, err := a.verifier.Verify(rawIDToken) + idToken, err := a.verifier.Verify(r.Context(), rawIDToken) if err != nil { http.Error(w, fmt.Sprintf("Failed to verify ID token: %v", err), http.StatusInternalServerError) return diff --git a/connector/oidc/oidc.go b/connector/oidc/oidc.go index c9d88191..6a71d017 100644 --- a/connector/oidc/oidc.go +++ b/connector/oidc/oidc.go @@ -6,7 +6,7 @@ import ( "fmt" "net/http" - "github.com/ericchiang/oidc" + "github.com/coreos/go-oidc" "golang.org/x/net/context" "golang.org/x/oauth2" @@ -51,7 +51,7 @@ func (c *Config) Open() (conn connector.Connector, err error) { Scopes: scopes, RedirectURL: c.RedirectURI, }, - verifier: provider.NewVerifier(ctx, + verifier: provider.Verifier( oidc.VerifyExpiry(), oidc.VerifyAudience(clientID), ), @@ -99,7 +99,7 @@ func (c *oidcConnector) HandleCallback(s connector.Scopes, r *http.Request) (ide if errType := q.Get("error"); errType != "" { return identity, &oauth2Error{errType, q.Get("error_description")} } - token, err := c.oauth2Config.Exchange(c.ctx, q.Get("code")) + token, err := c.oauth2Config.Exchange(r.Context(), q.Get("code")) if err != nil { return identity, fmt.Errorf("oidc: failed to get token: %v", err) } @@ -108,7 +108,7 @@ func (c *oidcConnector) HandleCallback(s connector.Scopes, r *http.Request) (ide if !ok { return identity, errors.New("oidc: no id_token in token response") } - idToken, err := c.verifier.Verify(rawIDToken) + idToken, err := c.verifier.Verify(r.Context(), rawIDToken) if err != nil { return identity, fmt.Errorf("oidc: failed to verify ID Token: %v", err) } diff --git a/glide.lock b/glide.lock index 41f250fa..0c79505e 100644 --- a/glide.lock +++ b/glide.lock @@ -1,12 +1,12 @@ -hash: bc7fa6bbfddcb39710064a9d6dab090d49bd88486571cae12c68c4b70093e716 -updated: 2016-11-03T14:31:37.323302694-07:00 +hash: c3530f2a60a64c2efc4c3ac499fcd15f79de2a532715ba2b9841c1d404942b2e +updated: 2016-11-17T15:18:56.701287533-08:00 imports: - name: github.com/cockroachdb/cockroach-go version: 31611c0501c812f437d4861d87d117053967c955 subpackages: - crdb -- name: github.com/ericchiang/oidc - version: 1907f0e61549f9081f26bdf269f11603496c9dee +- name: github.com/coreos/go-oidc + version: 5a7f09ab5787e846efa7f56f4a08b6d6926d08c4 - name: github.com/ghodss/yaml version: bea76d6a4713e18b7f5321a2b020738552def3ea - name: github.com/go-sql-driver/mysql @@ -52,6 +52,7 @@ imports: version: 6a513affb38dc9788b449d59ffed099b8de18fa0 subpackages: - context + - context/ctxhttp - http2 - http2/hpack - internal/timeseries @@ -87,13 +88,8 @@ imports: version: 4e86f4367175e39f69d9358a5f17b4dda270378d - name: gopkg.in/ldap.v2 version: 0e7db8eb77695b5a952f0e5d78df9ab160050c73 -- name: gopkg.in/square/go-jose.v1 - version: e3f973b66b91445ec816dd7411ad1b6495a5a2fc - subpackages: - - cipher - - json - name: gopkg.in/square/go-jose.v2 - version: f209f41628247c56938cb20ef51d589ddad6c30b + version: 8c5257b2f658f86d174ae68c6a592eaf6a9608d9 subpackages: - cipher - json diff --git a/glide.yaml b/glide.yaml index 73be313a..bd16dab6 100644 --- a/glide.yaml +++ b/glide.yaml @@ -17,7 +17,7 @@ import: version: 4e86f4367175e39f69d9358a5f17b4dda270378d - package: gopkg.in/square/go-jose.v2 - version: f209f41628247c56938cb20ef51d589ddad6c30b + version: v2.0.0 subpackages: - cipher - json @@ -26,6 +26,7 @@ import: version: 6a513affb38dc9788b449d59ffed099b8de18fa0 subpackages: - context + - context/ctxhttp - http2 - http2/hpack - internal/timeseries @@ -49,16 +50,17 @@ import: subpackages: - bcrypt -- package: github.com/ericchiang/oidc - version: 1907f0e61549f9081f26bdf269f11603496c9dee +- package: github.com/coreos/go-oidc + version: 5a7f09ab5787e846efa7f56f4a08b6d6926d08c4 - package: github.com/pquerna/cachecontrol version: c97913dcbd76de40b051a9b4cd827f7eaeb7a868 -- package: gopkg.in/square/go-jose.v1 - version: v1.0.2 - package: golang.org/x/oauth2 version: 08c8d727d2392d18286f9f88ad775ad98f09ab33 -# Not actually imported but glide detects it. Consider adding subpackages to -# the oauth2 package to eliminate. + subpackages: [] +# The oauth2 package only imports the appengine code when it's given a +# specific build tags, but glide detects it anyway. +# +# https://github.com/golang/oauth2/blob/d5040cdd/client_appengine.go - package: google.golang.org/appengine version: 267c27e7492265b84fc6719503b14a1e17975d79 subpackages: diff --git a/server/server_test.go b/server/server_test.go index 6db67d5c..a5865dfa 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -3,6 +3,7 @@ package server import ( "crypto/rsa" "crypto/x509" + "encoding/json" "encoding/pem" "errors" "fmt" @@ -17,7 +18,7 @@ import ( "testing" "time" - "github.com/ericchiang/oidc" + oidc "github.com/coreos/go-oidc" "github.com/kylelemons/godebug/pretty" "golang.org/x/crypto/bcrypt" "golang.org/x/net/context" @@ -117,17 +118,21 @@ func TestDiscovery(t *testing.T) { if err != nil { t.Fatalf("failed to get provider: %v", err) } - required := []struct { - name, val string - }{ - {"issuer", p.Issuer}, - {"authorization_endpoint", p.AuthURL}, - {"token_endpoint", p.TokenURL}, - {"jwks_uri", p.JWKSURL}, + + var got map[string]*json.RawMessage + if err := p.Claims(&got); err != nil { + t.Fatalf("failed to decode claims: %v", err) + } + + required := []string{ + "issuer", + "authorization_endpoint", + "token_endpoint", + "jwks_uri", } for _, field := range required { - if field.val == "" { - t.Errorf("server discovery is missing required field %q", field.name) + if _, ok := got[field]; !ok { + t.Errorf("server discovery is missing required field %q", field) } } } @@ -169,7 +174,7 @@ func TestOAuth2CodeFlow(t *testing.T) { if !ok { return fmt.Errorf("no id token found") } - if _, err := p.NewVerifier(ctx).Verify(idToken); err != nil { + if _, err := p.Verifier().Verify(ctx, idToken); err != nil { return fmt.Errorf("failed to verify id token: %v", err) } return nil @@ -192,7 +197,7 @@ func TestOAuth2CodeFlow(t *testing.T) { if !ok { return fmt.Errorf("no id token found") } - idToken, err := p.NewVerifier(ctx).Verify(rawIDToken) + idToken, err := p.Verifier().Verify(ctx, rawIDToken) if err != nil { return fmt.Errorf("failed to verify id token: %v", err) } @@ -230,7 +235,7 @@ func TestOAuth2CodeFlow(t *testing.T) { v.Add("grant_type", "refresh_token") v.Add("refresh_token", token.RefreshToken) v.Add("scope", strings.Join(requestedScopes, " ")) - resp, err := http.PostForm(p.TokenURL, v) + resp, err := http.PostForm(p.Endpoint().TokenURL, v) if err != nil { return err } @@ -258,7 +263,7 @@ func TestOAuth2CodeFlow(t *testing.T) { // Since we support that client we choose to be more relaxed about // scope parsing, disregarding extra whitespace. v.Add("scope", " "+strings.Join(requestedScopes, " ")) - resp, err := http.PostForm(p.TokenURL, v) + resp, err := http.PostForm(p.Endpoint().TokenURL, v) if err != nil { return err } @@ -284,7 +289,7 @@ func TestOAuth2CodeFlow(t *testing.T) { v.Add("refresh_token", token.RefreshToken) // Request a scope that wasn't requestd initially. v.Add("scope", "oidc email profile") - resp, err := http.PostForm(p.TokenURL, v) + resp, err := http.PostForm(p.Endpoint().TokenURL, v) if err != nil { return err } @@ -335,7 +340,7 @@ func TestOAuth2CodeFlow(t *testing.T) { if !ok { return fmt.Errorf("no id_token in refreshed token") } - idToken, err := p.NewVerifier(ctx).Verify(rawIDToken) + idToken, err := p.Verifier().Verify(ctx, rawIDToken) if err != nil { return fmt.Errorf("failed to verify id token: %v", err) } @@ -547,7 +552,7 @@ func TestOAuth2ImplicitFlow(t *testing.T) { src := &nonceSource{nonce: nonce} - idTokenVerifier := p.NewVerifier(ctx, oidc.VerifyAudience(client.ID), oidc.VerifyNonce(src)) + idTokenVerifier := p.Verifier(oidc.VerifyAudience(client.ID), oidc.VerifyNonce(src)) oauth2Config = &oauth2.Config{ ClientID: client.ID, @@ -569,7 +574,7 @@ func TestOAuth2ImplicitFlow(t *testing.T) { if idToken == "" { return errors.New("no id_token in fragment") } - if _, err := idTokenVerifier.Verify(idToken); err != nil { + if _, err := idTokenVerifier.Verify(ctx, idToken); err != nil { return fmt.Errorf("failed to verify id_token: %v", err) } return nil @@ -664,7 +669,7 @@ func TestCrossClientScopes(t *testing.T) { t.Errorf("no id token found: %v", err) return } - idToken, err := p.NewVerifier(ctx).Verify(rawIDToken) + idToken, err := p.Verifier().Verify(ctx, rawIDToken) if err != nil { t.Errorf("failed to parse ID Token: %v", err) return diff --git a/vendor/github.com/coreos/go-oidc/.gitignore b/vendor/github.com/coreos/go-oidc/.gitignore new file mode 100644 index 00000000..c96f2f47 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/.gitignore @@ -0,0 +1,2 @@ +/bin +/gopath diff --git a/vendor/github.com/coreos/go-oidc/.travis.yml b/vendor/github.com/coreos/go-oidc/.travis.yml new file mode 100644 index 00000000..fb89294c --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/.travis.yml @@ -0,0 +1,16 @@ +language: go + +go: + - 1.7.3 + - 1.6.3 + +install: + - go get -v -t github.com/coreos/go-oidc + - go get golang.org/x/tools/cmd/cover + - go get github.com/golang/lint/golint + +script: + - ./test + +notifications: + email: false diff --git a/vendor/github.com/coreos/go-oidc/CONTRIBUTING.md b/vendor/github.com/coreos/go-oidc/CONTRIBUTING.md new file mode 100644 index 00000000..6662073a --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/CONTRIBUTING.md @@ -0,0 +1,71 @@ +# How to Contribute + +CoreOS projects are [Apache 2.0 licensed](LICENSE) and accept contributions via +GitHub pull requests. This document outlines some of the conventions on +development workflow, commit message formatting, contact points and other +resources to make it easier to get your contribution accepted. + +# Certificate of Origin + +By contributing to this project you agree to the Developer Certificate of +Origin (DCO). This document was created by the Linux Kernel community and is a +simple statement that you, as a contributor, have the legal right to make the +contribution. See the [DCO](DCO) file for details. + +# Email and Chat + +The project currently uses the general CoreOS email list and IRC channel: +- Email: [coreos-dev](https://groups.google.com/forum/#!forum/coreos-dev) +- IRC: #[coreos](irc://irc.freenode.org:6667/#coreos) IRC channel on freenode.org + +Please avoid emailing maintainers found in the MAINTAINERS file directly. They +are very busy and read the mailing lists. + +## Getting Started + +- Fork the repository on GitHub +- Read the [README](README.md) for build and test instructions +- Play with the project, submit bugs, submit patches! + +## Contribution Flow + +This is a rough outline of what a contributor's workflow looks like: + +- Create a topic branch from where you want to base your work (usually master). +- Make commits of logical units. +- Make sure your commit messages are in the proper format (see below). +- Push your changes to a topic branch in your fork of the repository. +- Make sure the tests pass, and add any new tests as appropriate. +- Submit a pull request to the original repository. + +Thanks for your contributions! + +### Format of the Commit Message + +We follow a rough convention for commit messages that is designed to answer two +questions: what changed and why. The subject line should feature the what and +the body of the commit should describe the why. + +``` +scripts: add the test-cluster command + +this uses tmux to setup a test cluster that you can easily kill and +start for debugging. + +Fixes #38 +``` + +The format can be described more formally as follows: + +``` +<subsystem>: <what changed> +<BLANK LINE> +<why this change was made> +<BLANK LINE> +<footer> +``` + +The first line is the subject and should be no longer than 70 characters, the +second line is always blank, and other lines should be wrapped at 80 characters. +This allows the message to be easier to read on GitHub as well as in various +git tools. diff --git a/vendor/github.com/coreos/go-oidc/DCO b/vendor/github.com/coreos/go-oidc/DCO new file mode 100644 index 00000000..716561d5 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/DCO @@ -0,0 +1,36 @@ +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. diff --git a/vendor/github.com/ericchiang/oidc/LICENSE b/vendor/github.com/coreos/go-oidc/LICENSE similarity index 100% rename from vendor/github.com/ericchiang/oidc/LICENSE rename to vendor/github.com/coreos/go-oidc/LICENSE diff --git a/vendor/github.com/coreos/go-oidc/MAINTAINERS b/vendor/github.com/coreos/go-oidc/MAINTAINERS new file mode 100644 index 00000000..68079f1e --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/MAINTAINERS @@ -0,0 +1,3 @@ +Bobby Rullo <bobby.rullo@coreos.com> (@bobbyrullo) +Ed Rooth <ed.rooth@coreos.com> (@sym3tri) +Eric Chiang <eric.chiang@coreos.com> (@ericchiang) diff --git a/vendor/github.com/coreos/go-oidc/NOTICE b/vendor/github.com/coreos/go-oidc/NOTICE new file mode 100644 index 00000000..b39ddfa5 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/NOTICE @@ -0,0 +1,5 @@ +CoreOS Project +Copyright 2014 CoreOS, Inc + +This product includes software developed at CoreOS, Inc. +(http://www.coreos.com/). diff --git a/vendor/github.com/coreos/go-oidc/README.md b/vendor/github.com/coreos/go-oidc/README.md new file mode 100644 index 00000000..2a5c13e5 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/README.md @@ -0,0 +1,72 @@ +# go-oidc + +[](https://godoc.org/github.com/coreos/go-oidc) +[](https://travis-ci.org/coreos/go-oidc) + +## OpenID Connect support for Go + +This package enables OpenID Connect support for the [golang.org/x/oauth2](https://godoc.org/golang.org/x/oauth2) package. + +```go +provider, err := oidc.NewProvider(ctx, "https://accounts.google.com") +if err != nil { + // handle error +} + +// Configure an OpenID Connect aware OAuth2 client. +oauth2Config := oauth2.Config{ + ClientID: clientID, + ClientSecret: clientSecret, + RedirectURL: redirectURL, + + // Discovery returns the OAuth2 endpoints. + Endpoint: provider.Endpoint(), + + // "openid" is a required scope for OpenID Connect flows. + Scopes: []string{oidc.ScopeOpenID, "profile", "email"}, +} +``` + +OAuth2 redirects are unchanged. + +```go +func handleRedirect(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, oauth2Config.AuthCodeURL(state), http.StatusFound) +} +``` + +The on responses, the provider can be used to verify ID Tokens. + +```go +var verifier = provider.Verifier() + +func handleOAuth2Callback(w http.ResponseWriter, r *http.Request) { + // Verify state and errors. + + oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code")) + if err != nil { + // handle error + } + + // Extract the ID Token from OAuth2 token. + rawIDToken, ok := oauth2Token.Extra("id_token").(string) + if !ok { + // handle missing token + } + + // Parse and verify ID Token payload. + idToken, err := verifier.Verify(ctx, rawIDToken) + if err != nil { + // handle error + } + + // Extract custom claims + var claims struct { + Email string `json:"email"` + Verified bool `json:"email_verified"` + } + if err := idToken.Claims(&claims); err != nil { + // handle error + } +} +``` diff --git a/vendor/github.com/coreos/go-oidc/example/README.md b/vendor/github.com/coreos/go-oidc/example/README.md new file mode 100644 index 00000000..765a4ea0 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/example/README.md @@ -0,0 +1,21 @@ +# Examples + +These are example uses of the oidc package. Each requires a Google account and the client ID and secret of a registered OAuth2 application. To create one: + +1. Visit your [Google Developer Console][google-developer-console]. +2. Click "Credentials" on the left column. +3. Click the "Create credentials" button followed by "OAuth client ID". +4. Select "Web application" and add "http://127.0.0.1:5556/auth/google/callback" as an authorized redirect URI. +5. Click create and add the printed client ID and secret to your environment using the following variables: + +``` +GOOGLE_OAUTH2_CLIENT_ID +GOOGLE_OAUTH2_CLIENT_SECRET +``` + +Finally run the examples using the Go tool and navigate to http://127.0.0.1:5556. + +``` +go run ./examples/idtoken/app.go +``` +[google-developer-console]: https://console.developers.google.com/apis/dashboard diff --git a/vendor/github.com/ericchiang/oidc/examples/idtoken/app.go b/vendor/github.com/coreos/go-oidc/example/idtoken/app.go similarity index 94% rename from vendor/github.com/ericchiang/oidc/examples/idtoken/app.go rename to vendor/github.com/coreos/go-oidc/example/idtoken/app.go index b2a42a76..fe5859e9 100644 --- a/vendor/github.com/ericchiang/oidc/examples/idtoken/app.go +++ b/vendor/github.com/coreos/go-oidc/example/idtoken/app.go @@ -9,7 +9,7 @@ import ( "net/http" "os" - "github.com/ericchiang/oidc" + oidc "github.com/coreos/go-oidc" "golang.org/x/net/context" "golang.org/x/oauth2" @@ -27,7 +27,7 @@ func main() { if err != nil { log.Fatal(err) } - verifier := provider.NewVerifier(ctx) + verifier := provider.Verifier() config := oauth2.Config{ ClientID: clientID, @@ -59,7 +59,7 @@ func main() { http.Error(w, "No id_token field in oauth2 token.", http.StatusInternalServerError) return } - idToken, err := verifier.Verify(rawIDToken) + idToken, err := verifier.Verify(ctx, rawIDToken) if err != nil { http.Error(w, "Failed to verify ID Token: "+err.Error(), http.StatusInternalServerError) return diff --git a/vendor/github.com/ericchiang/oidc/examples/nonce/app.go b/vendor/github.com/coreos/go-oidc/example/nonce/app.go similarity index 93% rename from vendor/github.com/ericchiang/oidc/examples/nonce/app.go rename to vendor/github.com/coreos/go-oidc/example/nonce/app.go index 2167f962..8a78db9f 100644 --- a/vendor/github.com/ericchiang/oidc/examples/nonce/app.go +++ b/vendor/github.com/coreos/go-oidc/example/nonce/app.go @@ -10,7 +10,7 @@ import ( "net/http" "os" - "github.com/ericchiang/oidc" + oidc "github.com/coreos/go-oidc" "golang.org/x/net/context" "golang.org/x/oauth2" @@ -42,7 +42,7 @@ func main() { } // Use the nonce source to create a custom ID Token verifier. - nonceEnabledVerifier := provider.NewVerifier(ctx, oidc.VerifyNonce(nonceSource{})) + nonceEnabledVerifier := provider.Verifier(oidc.VerifyNonce(nonceSource{})) config := oauth2.Config{ ClientID: clientID, @@ -76,7 +76,7 @@ func main() { return } // Verify the ID Token signature and nonce. - idToken, err := nonceEnabledVerifier.Verify(rawIDToken) + idToken, err := nonceEnabledVerifier.Verify(ctx, rawIDToken) if err != nil { http.Error(w, "Failed to verify ID Token: "+err.Error(), http.StatusInternalServerError) return diff --git a/vendor/github.com/ericchiang/oidc/examples/userinfo/app.go b/vendor/github.com/coreos/go-oidc/example/userinfo/app.go similarity index 98% rename from vendor/github.com/ericchiang/oidc/examples/userinfo/app.go rename to vendor/github.com/coreos/go-oidc/example/userinfo/app.go index a4d55c52..0039088e 100644 --- a/vendor/github.com/ericchiang/oidc/examples/userinfo/app.go +++ b/vendor/github.com/coreos/go-oidc/example/userinfo/app.go @@ -9,7 +9,7 @@ import ( "net/http" "os" - "github.com/ericchiang/oidc" + oidc "github.com/coreos/go-oidc" "golang.org/x/net/context" "golang.org/x/oauth2" diff --git a/vendor/github.com/coreos/go-oidc/gen.go b/vendor/github.com/coreos/go-oidc/gen.go new file mode 100644 index 00000000..0c798f67 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/gen.go @@ -0,0 +1,150 @@ +// +build ignore + +// This file is used to generate keys for tests. + +package main + +import ( + "bytes" + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "encoding/hex" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "text/template" + + jose "gopkg.in/square/go-jose.v2" +) + +type key struct { + name string + new func() (crypto.Signer, error) +} + +var keys = []key{ + { + "ECDSA_256", func() (crypto.Signer, error) { + return ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + }, + }, + { + "ECDSA_384", func() (crypto.Signer, error) { + return ecdsa.GenerateKey(elliptic.P384(), rand.Reader) + }, + }, + { + "ECDSA_521", func() (crypto.Signer, error) { + return ecdsa.GenerateKey(elliptic.P521(), rand.Reader) + }, + }, + { + "RSA_1024", func() (crypto.Signer, error) { + return rsa.GenerateKey(rand.Reader, 1024) + }, + }, + { + "RSA_2048", func() (crypto.Signer, error) { + return rsa.GenerateKey(rand.Reader, 2048) + }, + }, + { + "RSA_4096", func() (crypto.Signer, error) { + return rsa.GenerateKey(rand.Reader, 4096) + }, + }, +} + +func newJWK(k key, prefix, ident string) (privBytes, pubBytes []byte, err error) { + priv, err := k.new() + if err != nil { + return nil, nil, fmt.Errorf("generate %s: %v", k.name, err) + } + pub := priv.Public() + + privKey := &jose.JSONWebKey{Key: priv} + thumbprint, err := privKey.Thumbprint(crypto.SHA256) + if err != nil { + return nil, nil, fmt.Errorf("computing thumbprint: %v", err) + } + + keyID := hex.EncodeToString(thumbprint) + privKey.KeyID = keyID + pubKey := &jose.JSONWebKey{Key: pub, KeyID: keyID} + + privBytes, err = json.MarshalIndent(privKey, prefix, ident) + if err != nil { + return + } + pubBytes, err = json.MarshalIndent(pubKey, prefix, ident) + return +} + +type keyData struct { + Name string + Priv string + Pub string +} + +var tmpl = template.Must(template.New("").Parse(`// +build !golint + +// This file contains statically created JWKs for tests created by gen.go + +package oidc + +import ( + "encoding/json" + + jose "gopkg.in/square/go-jose.v2" +) + +func mustLoadJWK(s string) jose.JSONWebKey { + var jwk jose.JSONWebKey + if err := json.Unmarshal([]byte(s), &jwk); err != nil { + panic(err) + } + return jwk +} + +var ( +{{- range $i, $key := .Keys }} + testKey{{ $key.Name }} = mustLoadJWK(` + "`" + `{{ $key.Pub }}` + "`" + `) + testKey{{ $key.Name }}_Priv = mustLoadJWK(` + "`" + `{{ $key.Priv }}` + "`" + `) +{{ end -}} +) +`)) + +func main() { + var tmplData struct { + Keys []keyData + } + for _, k := range keys { + for i := 0; i < 4; i++ { + log.Printf("generating %s", k.name) + priv, pub, err := newJWK(k, "\t", "\t") + if err != nil { + log.Fatal(err) + } + name := fmt.Sprintf("%s_%d", k.name, i) + + tmplData.Keys = append(tmplData.Keys, keyData{ + Name: name, + Priv: string(priv), + Pub: string(pub), + }) + } + } + + buff := new(bytes.Buffer) + if err := tmpl.Execute(buff, tmplData); err != nil { + log.Fatalf("excuting template: %v", err) + } + + if err := ioutil.WriteFile("jose_test.go", buff.Bytes(), 0644); err != nil { + log.Fatal(err) + } +} diff --git a/vendor/github.com/coreos/go-oidc/http/client.go b/vendor/github.com/coreos/go-oidc/http/client.go new file mode 100644 index 00000000..fd079b49 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/http/client.go @@ -0,0 +1,7 @@ +package http + +import "net/http" + +type Client interface { + Do(*http.Request) (*http.Response, error) +} diff --git a/vendor/github.com/coreos/go-oidc/http/doc.go b/vendor/github.com/coreos/go-oidc/http/doc.go new file mode 100644 index 00000000..5687e8b8 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/http/doc.go @@ -0,0 +1,2 @@ +// Package http is DEPRECATED. Use net/http instead. +package http diff --git a/vendor/github.com/coreos/go-oidc/http/http.go b/vendor/github.com/coreos/go-oidc/http/http.go new file mode 100644 index 00000000..c3f51215 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/http/http.go @@ -0,0 +1,156 @@ +package http + +import ( + "encoding/base64" + "encoding/json" + "errors" + "log" + "net/http" + "net/url" + "path" + "strconv" + "strings" + "time" +) + +func WriteError(w http.ResponseWriter, code int, msg string) { + e := struct { + Error string `json:"error"` + }{ + Error: msg, + } + b, err := json.Marshal(e) + if err != nil { + log.Printf("go-oidc: failed to marshal %#v: %v", e, err) + code = http.StatusInternalServerError + b = []byte(`{"error":"server_error"}`) + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(code) + w.Write(b) +} + +// BasicAuth parses a username and password from the request's +// Authorization header. This was pulled from golang master: +// https://codereview.appspot.com/76540043 +func BasicAuth(r *http.Request) (username, password string, ok bool) { + auth := r.Header.Get("Authorization") + if auth == "" { + return + } + + if !strings.HasPrefix(auth, "Basic ") { + return + } + c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(auth, "Basic ")) + if err != nil { + return + } + cs := string(c) + s := strings.IndexByte(cs, ':') + if s < 0 { + return + } + return cs[:s], cs[s+1:], true +} + +func cacheControlMaxAge(hdr string) (time.Duration, bool, error) { + for _, field := range strings.Split(hdr, ",") { + parts := strings.SplitN(strings.TrimSpace(field), "=", 2) + k := strings.ToLower(strings.TrimSpace(parts[0])) + if k != "max-age" { + continue + } + + if len(parts) == 1 { + return 0, false, errors.New("max-age has no value") + } + + v := strings.TrimSpace(parts[1]) + if v == "" { + return 0, false, errors.New("max-age has empty value") + } + + age, err := strconv.Atoi(v) + if err != nil { + return 0, false, err + } + + if age <= 0 { + return 0, false, nil + } + + return time.Duration(age) * time.Second, true, nil + } + + return 0, false, nil +} + +func expires(date, expires string) (time.Duration, bool, error) { + if date == "" || expires == "" { + return 0, false, nil + } + + te, err := time.Parse(time.RFC1123, expires) + if err != nil { + return 0, false, err + } + + td, err := time.Parse(time.RFC1123, date) + if err != nil { + return 0, false, err + } + + ttl := te.Sub(td) + + // headers indicate data already expired, caller should not + // have to care about this case + if ttl <= 0 { + return 0, false, nil + } + + return ttl, true, nil +} + +func Cacheable(hdr http.Header) (time.Duration, bool, error) { + ttl, ok, err := cacheControlMaxAge(hdr.Get("Cache-Control")) + if err != nil || ok { + return ttl, ok, err + } + + return expires(hdr.Get("Date"), hdr.Get("Expires")) +} + +// MergeQuery appends additional query values to an existing URL. +func MergeQuery(u url.URL, q url.Values) url.URL { + uv := u.Query() + for k, vs := range q { + for _, v := range vs { + uv.Add(k, v) + } + } + u.RawQuery = uv.Encode() + return u +} + +// NewResourceLocation appends a resource id to the end of the requested URL path. +func NewResourceLocation(reqURL *url.URL, id string) string { + var u url.URL + u = *reqURL + u.Path = path.Join(u.Path, id) + u.RawQuery = "" + u.Fragment = "" + return u.String() +} + +// CopyRequest returns a clone of the provided *http.Request. +// The returned object is a shallow copy of the struct and a +// deep copy of its Header field. +func CopyRequest(r *http.Request) *http.Request { + r2 := *r + r2.Header = make(http.Header) + for k, s := range r.Header { + r2.Header[k] = s + } + return &r2 +} diff --git a/vendor/github.com/coreos/go-oidc/http/http_test.go b/vendor/github.com/coreos/go-oidc/http/http_test.go new file mode 100644 index 00000000..8ec76a24 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/http/http_test.go @@ -0,0 +1,380 @@ +package http + +import ( + "net/http" + "net/url" + "reflect" + "strings" + "testing" + "time" +) + +func TestCacheControlMaxAgeSuccess(t *testing.T) { + tests := []struct { + hdr string + wantAge time.Duration + wantOK bool + }{ + {"max-age=12", 12 * time.Second, true}, + {"max-age=-12", 0, false}, + {"max-age=0", 0, false}, + {"public, max-age=12", 12 * time.Second, true}, + {"public, max-age=40192, must-revalidate", 40192 * time.Second, true}, + {"public, not-max-age=12, must-revalidate", time.Duration(0), false}, + } + + for i, tt := range tests { + maxAge, ok, err := cacheControlMaxAge(tt.hdr) + if err != nil { + t.Errorf("case %d: err=%v", i, err) + } + if tt.wantAge != maxAge { + t.Errorf("case %d: want=%d got=%d", i, tt.wantAge, maxAge) + } + if tt.wantOK != ok { + t.Errorf("case %d: incorrect ok value: want=%t got=%t", i, tt.wantOK, ok) + } + } +} + +func TestCacheControlMaxAgeFail(t *testing.T) { + tests := []string{ + "max-age=aasdf", + "max-age=", + "max-age", + } + + for i, tt := range tests { + _, ok, err := cacheControlMaxAge(tt) + if ok { + t.Errorf("case %d: want ok=false, got true", i) + } + if err == nil { + t.Errorf("case %d: want non-nil err", i) + } + } +} + +func TestMergeQuery(t *testing.T) { + tests := []struct { + u string + q url.Values + w string + }{ + // No values + { + u: "http://example.com", + q: nil, + w: "http://example.com", + }, + // No additional values + { + u: "http://example.com?foo=bar", + q: nil, + w: "http://example.com?foo=bar", + }, + // Simple addition + { + u: "http://example.com", + q: url.Values{ + "foo": []string{"bar"}, + }, + w: "http://example.com?foo=bar", + }, + // Addition with existing values + { + u: "http://example.com?dog=boo", + q: url.Values{ + "foo": []string{"bar"}, + }, + w: "http://example.com?dog=boo&foo=bar", + }, + // Merge + { + u: "http://example.com?dog=boo", + q: url.Values{ + "dog": []string{"elroy"}, + }, + w: "http://example.com?dog=boo&dog=elroy", + }, + // Add and merge + { + u: "http://example.com?dog=boo", + q: url.Values{ + "dog": []string{"elroy"}, + "foo": []string{"bar"}, + }, + w: "http://example.com?dog=boo&dog=elroy&foo=bar", + }, + // Multivalue merge + { + u: "http://example.com?dog=boo", + q: url.Values{ + "dog": []string{"elroy", "penny"}, + }, + w: "http://example.com?dog=boo&dog=elroy&dog=penny", + }, + } + + for i, tt := range tests { + ur, err := url.Parse(tt.u) + if err != nil { + t.Errorf("case %d: failed parsing test url: %v, error: %v", i, tt.u, err) + } + + got := MergeQuery(*ur, tt.q) + want, err := url.Parse(tt.w) + if err != nil { + t.Errorf("case %d: failed parsing want url: %v, error: %v", i, tt.w, err) + } + + if !reflect.DeepEqual(*want, got) { + t.Errorf("case %d: want: %v, got: %v", i, *want, got) + } + } +} + +func TestExpiresPass(t *testing.T) { + tests := []struct { + date string + exp string + wantTTL time.Duration + wantOK bool + }{ + // Expires and Date properly set + { + date: "Thu, 01 Dec 1983 22:00:00 GMT", + exp: "Fri, 02 Dec 1983 01:00:00 GMT", + wantTTL: 10800 * time.Second, + wantOK: true, + }, + // empty headers + { + date: "", + exp: "", + wantOK: false, + }, + // lack of Expirs short-ciruits Date parsing + { + date: "foo", + exp: "", + wantOK: false, + }, + // lack of Date short-ciruits Expires parsing + { + date: "", + exp: "foo", + wantOK: false, + }, + // no Date + { + exp: "Thu, 01 Dec 1983 22:00:00 GMT", + wantTTL: 0, + wantOK: false, + }, + // no Expires + { + date: "Thu, 01 Dec 1983 22:00:00 GMT", + wantTTL: 0, + wantOK: false, + }, + // Expires < Date + { + date: "Fri, 02 Dec 1983 01:00:00 GMT", + exp: "Thu, 01 Dec 1983 22:00:00 GMT", + wantTTL: 0, + wantOK: false, + }, + } + + for i, tt := range tests { + ttl, ok, err := expires(tt.date, tt.exp) + if err != nil { + t.Errorf("case %d: err=%v", i, err) + } + if tt.wantTTL != ttl { + t.Errorf("case %d: want=%d got=%d", i, tt.wantTTL, ttl) + } + if tt.wantOK != ok { + t.Errorf("case %d: incorrect ok value: want=%t got=%t", i, tt.wantOK, ok) + } + } +} + +func TestExpiresFail(t *testing.T) { + tests := []struct { + date string + exp string + }{ + // malformed Date header + { + date: "foo", + exp: "Fri, 02 Dec 1983 01:00:00 GMT", + }, + // malformed exp header + { + date: "Fri, 02 Dec 1983 01:00:00 GMT", + exp: "bar", + }, + } + + for i, tt := range tests { + _, _, err := expires(tt.date, tt.exp) + if err == nil { + t.Errorf("case %d: expected non-nil error", i) + } + } +} + +func TestCacheablePass(t *testing.T) { + tests := []struct { + headers http.Header + wantTTL time.Duration + wantOK bool + }{ + // valid Cache-Control + { + headers: http.Header{ + "Cache-Control": []string{"max-age=100"}, + }, + wantTTL: 100 * time.Second, + wantOK: true, + }, + // valid Date/Expires + { + headers: http.Header{ + "Date": []string{"Thu, 01 Dec 1983 22:00:00 GMT"}, + "Expires": []string{"Fri, 02 Dec 1983 01:00:00 GMT"}, + }, + wantTTL: 10800 * time.Second, + wantOK: true, + }, + // Cache-Control supersedes Date/Expires + { + headers: http.Header{ + "Cache-Control": []string{"max-age=100"}, + "Date": []string{"Thu, 01 Dec 1983 22:00:00 GMT"}, + "Expires": []string{"Fri, 02 Dec 1983 01:00:00 GMT"}, + }, + wantTTL: 100 * time.Second, + wantOK: true, + }, + // no caching headers + { + headers: http.Header{}, + wantOK: false, + }, + } + + for i, tt := range tests { + ttl, ok, err := Cacheable(tt.headers) + if err != nil { + t.Errorf("case %d: err=%v", i, err) + continue + } + if tt.wantTTL != ttl { + t.Errorf("case %d: want=%d got=%d", i, tt.wantTTL, ttl) + } + if tt.wantOK != ok { + t.Errorf("case %d: incorrect ok value: want=%t got=%t", i, tt.wantOK, ok) + } + } +} + +func TestCacheableFail(t *testing.T) { + tests := []http.Header{ + // invalid Cache-Control short-circuits + http.Header{ + "Cache-Control": []string{"max-age"}, + "Date": []string{"Thu, 01 Dec 1983 22:00:00 GMT"}, + "Expires": []string{"Fri, 02 Dec 1983 01:00:00 GMT"}, + }, + // no Cache-Control, invalid Expires + http.Header{ + "Date": []string{"Thu, 01 Dec 1983 22:00:00 GMT"}, + "Expires": []string{"boo"}, + }, + } + + for i, tt := range tests { + _, _, err := Cacheable(tt) + if err == nil { + t.Errorf("case %d: want non-nil err", i) + } + } +} + +func TestNewResourceLocation(t *testing.T) { + tests := []struct { + ru *url.URL + id string + want string + }{ + { + ru: &url.URL{ + Scheme: "http", + Host: "example.com", + }, + id: "foo", + want: "http://example.com/foo", + }, + // https + { + ru: &url.URL{ + Scheme: "https", + Host: "example.com", + }, + id: "foo", + want: "https://example.com/foo", + }, + // with path + { + ru: &url.URL{ + Scheme: "http", + Host: "example.com", + Path: "one/two/three", + }, + id: "foo", + want: "http://example.com/one/two/three/foo", + }, + // with fragment + { + ru: &url.URL{ + Scheme: "http", + Host: "example.com", + Fragment: "frag", + }, + id: "foo", + want: "http://example.com/foo", + }, + // with query + { + ru: &url.URL{ + Scheme: "http", + Host: "example.com", + RawQuery: "dog=elroy", + }, + id: "foo", + want: "http://example.com/foo", + }, + } + + for i, tt := range tests { + got := NewResourceLocation(tt.ru, tt.id) + if tt.want != got { + t.Errorf("case %d: want=%s, got=%s", i, tt.want, got) + } + } +} + +func TestCopyRequest(t *testing.T) { + r1, err := http.NewRequest("GET", "http://example.com", strings.NewReader("foo")) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + r2 := CopyRequest(r1) + if !reflect.DeepEqual(r1, r2) { + t.Fatalf("Result of CopyRequest incorrect: %#v != %#v", r1, r2) + } +} diff --git a/vendor/github.com/coreos/go-oidc/http/url.go b/vendor/github.com/coreos/go-oidc/http/url.go new file mode 100644 index 00000000..df60eb1a --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/http/url.go @@ -0,0 +1,29 @@ +package http + +import ( + "errors" + "net/url" +) + +// ParseNonEmptyURL checks that a string is a parsable URL which is also not empty +// since `url.Parse("")` does not return an error. Must contian a scheme and a host. +func ParseNonEmptyURL(u string) (*url.URL, error) { + if u == "" { + return nil, errors.New("url is empty") + } + + ur, err := url.Parse(u) + if err != nil { + return nil, err + } + + if ur.Scheme == "" { + return nil, errors.New("url scheme is empty") + } + + if ur.Host == "" { + return nil, errors.New("url host is empty") + } + + return ur, nil +} diff --git a/vendor/github.com/coreos/go-oidc/http/url_test.go b/vendor/github.com/coreos/go-oidc/http/url_test.go new file mode 100644 index 00000000..2ab25052 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/http/url_test.go @@ -0,0 +1,49 @@ +package http + +import ( + "net/url" + "testing" +) + +func TestParseNonEmptyURL(t *testing.T) { + tests := []struct { + u string + ok bool + }{ + {"", false}, + {"http://", false}, + {"example.com", false}, + {"example", false}, + {"http://example", true}, + {"http://example:1234", true}, + {"http://example.com", true}, + {"http://example.com:1234", true}, + } + + for i, tt := range tests { + u, err := ParseNonEmptyURL(tt.u) + if err != nil { + t.Logf("err: %v", err) + if tt.ok { + t.Errorf("case %d: unexpected error: %v", i, err) + } else { + continue + } + } + + if !tt.ok { + t.Errorf("case %d: expected error but got none", i) + continue + } + + uu, err := url.Parse(tt.u) + if err != nil { + t.Errorf("case %d: unexpected error: %v", i, err) + continue + } + + if uu.String() != u.String() { + t.Errorf("case %d: incorrect url value, want: %q, got: %q", i, uu.String(), u.String()) + } + } +} diff --git a/vendor/github.com/coreos/go-oidc/jose.go b/vendor/github.com/coreos/go-oidc/jose.go new file mode 100644 index 00000000..f2e6bf43 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/jose.go @@ -0,0 +1,20 @@ +// +build !golint + +// Don't lint this file. We don't want to have to add a comment to each constant. + +package oidc + +const ( + // JOSE asymmetric signing algorithm values as defined by RFC 7518 + // + // see: https://tools.ietf.org/html/rfc7518#section-3.1 + RS256 = "RS256" // RSASSA-PKCS-v1.5 using SHA-256 + RS384 = "RS384" // RSASSA-PKCS-v1.5 using SHA-384 + RS512 = "RS512" // RSASSA-PKCS-v1.5 using SHA-512 + ES256 = "ES256" // ECDSA using P-256 and SHA-256 + ES384 = "ES384" // ECDSA using P-384 and SHA-384 + ES512 = "ES512" // ECDSA using P-521 and SHA-512 + PS256 = "PS256" // RSASSA-PSS using SHA256 and MGF1-SHA256 + PS384 = "PS384" // RSASSA-PSS using SHA384 and MGF1-SHA384 + PS512 = "PS512" // RSASSA-PSS using SHA512 and MGF1-SHA512 +) diff --git a/vendor/github.com/coreos/go-oidc/jose/claims.go b/vendor/github.com/coreos/go-oidc/jose/claims.go new file mode 100644 index 00000000..8b48bfd2 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/jose/claims.go @@ -0,0 +1,126 @@ +package jose + +import ( + "encoding/json" + "fmt" + "math" + "time" +) + +type Claims map[string]interface{} + +func (c Claims) Add(name string, value interface{}) { + c[name] = value +} + +func (c Claims) StringClaim(name string) (string, bool, error) { + cl, ok := c[name] + if !ok { + return "", false, nil + } + + v, ok := cl.(string) + if !ok { + return "", false, fmt.Errorf("unable to parse claim as string: %v", name) + } + + return v, true, nil +} + +func (c Claims) StringsClaim(name string) ([]string, bool, error) { + cl, ok := c[name] + if !ok { + return nil, false, nil + } + + if v, ok := cl.([]string); ok { + return v, true, nil + } + + // When unmarshaled, []string will become []interface{}. + if v, ok := cl.([]interface{}); ok { + var ret []string + for _, vv := range v { + str, ok := vv.(string) + if !ok { + return nil, false, fmt.Errorf("unable to parse claim as string array: %v", name) + } + ret = append(ret, str) + } + return ret, true, nil + } + + return nil, false, fmt.Errorf("unable to parse claim as string array: %v", name) +} + +func (c Claims) Int64Claim(name string) (int64, bool, error) { + cl, ok := c[name] + if !ok { + return 0, false, nil + } + + v, ok := cl.(int64) + if !ok { + vf, ok := cl.(float64) + if !ok { + return 0, false, fmt.Errorf("unable to parse claim as int64: %v", name) + } + v = int64(vf) + } + + return v, true, nil +} + +func (c Claims) Float64Claim(name string) (float64, bool, error) { + cl, ok := c[name] + if !ok { + return 0, false, nil + } + + v, ok := cl.(float64) + if !ok { + vi, ok := cl.(int64) + if !ok { + return 0, false, fmt.Errorf("unable to parse claim as float64: %v", name) + } + v = float64(vi) + } + + return v, true, nil +} + +func (c Claims) TimeClaim(name string) (time.Time, bool, error) { + v, ok, err := c.Float64Claim(name) + if !ok || err != nil { + return time.Time{}, ok, err + } + + s := math.Trunc(v) + ns := (v - s) * math.Pow(10, 9) + return time.Unix(int64(s), int64(ns)).UTC(), true, nil +} + +func decodeClaims(payload []byte) (Claims, error) { + var c Claims + if err := json.Unmarshal(payload, &c); err != nil { + return nil, fmt.Errorf("malformed JWT claims, unable to decode: %v", err) + } + return c, nil +} + +func marshalClaims(c Claims) ([]byte, error) { + b, err := json.Marshal(c) + if err != nil { + return nil, err + } + return b, nil +} + +func encodeClaims(c Claims) (string, error) { + b, err := marshalClaims(c) + if err != nil { + return "", err + } + + return encodeSegment(b), nil +} diff --git a/vendor/github.com/coreos/go-oidc/jose/claims_test.go b/vendor/github.com/coreos/go-oidc/jose/claims_test.go new file mode 100644 index 00000000..e0958a9c --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/jose/claims_test.go @@ -0,0 +1,328 @@ +package jose + +import ( + "reflect" + "testing" + "time" +) + +func TestString(t *testing.T) { + tests := []struct { + cl Claims + key string + ok bool + err bool + val string + }{ + // ok, no err, claim exists + { + cl: Claims{ + "foo": "bar", + }, + key: "foo", + val: "bar", + ok: true, + err: false, + }, + // no claims + { + cl: Claims{}, + key: "foo", + val: "", + ok: false, + err: false, + }, + // missing claim + { + cl: Claims{ + "foo": "bar", + }, + key: "xxx", + val: "", + ok: false, + err: false, + }, + // unparsable: type + { + cl: Claims{ + "foo": struct{}{}, + }, + key: "foo", + val: "", + ok: false, + err: true, + }, + // unparsable: nil value + { + cl: Claims{ + "foo": nil, + }, + key: "foo", + val: "", + ok: false, + err: true, + }, + } + + for i, tt := range tests { + val, ok, err := tt.cl.StringClaim(tt.key) + + if tt.err && err == nil { + t.Errorf("case %d: want err=non-nil, got err=nil", i) + } else if !tt.err && err != nil { + t.Errorf("case %d: want err=nil, got err=%v", i, err) + } + + if tt.ok != ok { + t.Errorf("case %d: want ok=%v, got ok=%v", i, tt.ok, ok) + } + + if tt.val != val { + t.Errorf("case %d: want val=%v, got val=%v", i, tt.val, val) + } + } +} + +func TestInt64(t *testing.T) { + tests := []struct { + cl Claims + key string + ok bool + err bool + val int64 + }{ + // ok, no err, claim exists + { + cl: Claims{ + "foo": int64(100), + }, + key: "foo", + val: int64(100), + ok: true, + err: false, + }, + // no claims + { + cl: Claims{}, + key: "foo", + val: 0, + ok: false, + err: false, + }, + // missing claim + { + cl: Claims{ + "foo": "bar", + }, + key: "xxx", + val: 0, + ok: false, + err: false, + }, + // unparsable: type + { + cl: Claims{ + "foo": struct{}{}, + }, + key: "foo", + val: 0, + ok: false, + err: true, + }, + // unparsable: nil value + { + cl: Claims{ + "foo": nil, + }, + key: "foo", + val: 0, + ok: false, + err: true, + }, + } + + for i, tt := range tests { + val, ok, err := tt.cl.Int64Claim(tt.key) + + if tt.err && err == nil { + t.Errorf("case %d: want err=non-nil, got err=nil", i) + } else if !tt.err && err != nil { + t.Errorf("case %d: want err=nil, got err=%v", i, err) + } + + if tt.ok != ok { + t.Errorf("case %d: want ok=%v, got ok=%v", i, tt.ok, ok) + } + + if tt.val != val { + t.Errorf("case %d: want val=%v, got val=%v", i, tt.val, val) + } + } +} + +func TestTime(t *testing.T) { + now := time.Now().UTC() + unixNow := now.Unix() + + tests := []struct { + cl Claims + key string + ok bool + err bool + val time.Time + }{ + // ok, no err, claim exists + { + cl: Claims{ + "foo": unixNow, + }, + key: "foo", + val: time.Unix(now.Unix(), 0).UTC(), + ok: true, + err: false, + }, + // no claims + { + cl: Claims{}, + key: "foo", + val: time.Time{}, + ok: false, + err: false, + }, + // missing claim + { + cl: Claims{ + "foo": "bar", + }, + key: "xxx", + val: time.Time{}, + ok: false, + err: false, + }, + // unparsable: type + { + cl: Claims{ + "foo": struct{}{}, + }, + key: "foo", + val: time.Time{}, + ok: false, + err: true, + }, + // unparsable: nil value + { + cl: Claims{ + "foo": nil, + }, + key: "foo", + val: time.Time{}, + ok: false, + err: true, + }, + } + + for i, tt := range tests { + val, ok, err := tt.cl.TimeClaim(tt.key) + + if tt.err && err == nil { + t.Errorf("case %d: want err=non-nil, got err=nil", i) + } else if !tt.err && err != nil { + t.Errorf("case %d: want err=nil, got err=%v", i, err) + } + + if tt.ok != ok { + t.Errorf("case %d: want ok=%v, got ok=%v", i, tt.ok, ok) + } + + if tt.val != val { + t.Errorf("case %d: want val=%v, got val=%v", i, tt.val, val) + } + } +} + +func TestStringArray(t *testing.T) { + tests := []struct { + cl Claims + key string + ok bool + err bool + val []string + }{ + // ok, no err, claim exists + { + cl: Claims{ + "foo": []string{"bar", "faf"}, + }, + key: "foo", + val: []string{"bar", "faf"}, + ok: true, + err: false, + }, + // ok, no err, []interface{} + { + cl: Claims{ + "foo": []interface{}{"bar", "faf"}, + }, + key: "foo", + val: []string{"bar", "faf"}, + ok: true, + err: false, + }, + // no claims + { + cl: Claims{}, + key: "foo", + val: nil, + ok: false, + err: false, + }, + // missing claim + { + cl: Claims{ + "foo": "bar", + }, + key: "xxx", + val: nil, + ok: false, + err: false, + }, + // unparsable: type + { + cl: Claims{ + "foo": struct{}{}, + }, + key: "foo", + val: nil, + ok: false, + err: true, + }, + // unparsable: nil value + { + cl: Claims{ + "foo": nil, + }, + key: "foo", + val: nil, + ok: false, + err: true, + }, + } + + for i, tt := range tests { + val, ok, err := tt.cl.StringsClaim(tt.key) + + if tt.err && err == nil { + t.Errorf("case %d: want err=non-nil, got err=nil", i) + } else if !tt.err && err != nil { + t.Errorf("case %d: want err=nil, got err=%v", i, err) + } + + if tt.ok != ok { + t.Errorf("case %d: want ok=%v, got ok=%v", i, tt.ok, ok) + } + + if !reflect.DeepEqual(tt.val, val) { + t.Errorf("case %d: want val=%v, got val=%v", i, tt.val, val) + } + } +} diff --git a/vendor/github.com/coreos/go-oidc/jose/doc.go b/vendor/github.com/coreos/go-oidc/jose/doc.go new file mode 100644 index 00000000..b5e13217 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/jose/doc.go @@ -0,0 +1,2 @@ +// Package jose is DEPRECATED. Use gopkg.in/square/go-jose.v2 instead. +package jose diff --git a/vendor/github.com/coreos/go-oidc/jose/jose.go b/vendor/github.com/coreos/go-oidc/jose/jose.go new file mode 100644 index 00000000..62099265 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/jose/jose.go @@ -0,0 +1,112 @@ +package jose + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "strings" +) + +const ( + HeaderMediaType = "typ" + HeaderKeyAlgorithm = "alg" + HeaderKeyID = "kid" +) + +const ( + // Encryption Algorithm Header Parameter Values for JWS + // See: https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#page-6 + AlgHS256 = "HS256" + AlgHS384 = "HS384" + AlgHS512 = "HS512" + AlgRS256 = "RS256" + AlgRS384 = "RS384" + AlgRS512 = "RS512" + AlgES256 = "ES256" + AlgES384 = "ES384" + AlgES512 = "ES512" + AlgPS256 = "PS256" + AlgPS384 = "PS384" + AlgPS512 = "PS512" + AlgNone = "none" +) + +const ( + // Algorithm Header Parameter Values for JWE + // See: https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#section-4.1 + AlgRSA15 = "RSA1_5" + AlgRSAOAEP = "RSA-OAEP" + AlgRSAOAEP256 = "RSA-OAEP-256" + AlgA128KW = "A128KW" + AlgA192KW = "A192KW" + AlgA256KW = "A256KW" + AlgDir = "dir" + AlgECDHES = "ECDH-ES" + AlgECDHESA128KW = "ECDH-ES+A128KW" + AlgECDHESA192KW = "ECDH-ES+A192KW" + AlgECDHESA256KW = "ECDH-ES+A256KW" + AlgA128GCMKW = "A128GCMKW" + AlgA192GCMKW = "A192GCMKW" + AlgA256GCMKW = "A256GCMKW" + AlgPBES2HS256A128KW = "PBES2-HS256+A128KW" + AlgPBES2HS384A192KW = "PBES2-HS384+A192KW" + AlgPBES2HS512A256KW = "PBES2-HS512+A256KW" +) + +const ( + // Encryption Algorithm Header Parameter Values for JWE + // See: https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#page-22 + EncA128CBCHS256 = "A128CBC-HS256" + EncA128CBCHS384 = "A128CBC-HS384" + EncA256CBCHS512 = "A256CBC-HS512" + EncA128GCM = "A128GCM" + EncA192GCM = "A192GCM" + EncA256GCM = "A256GCM" +) + +type JOSEHeader map[string]string + +func (j JOSEHeader) Validate() error { + if _, exists := j[HeaderKeyAlgorithm]; !exists { + return fmt.Errorf("header missing %q parameter", HeaderKeyAlgorithm) + } + + return nil +} + +func decodeHeader(seg string) (JOSEHeader, error) { + b, err := decodeSegment(seg) + if err != nil { + return nil, err + } + + var h JOSEHeader + err = json.Unmarshal(b, &h) + if err != nil { + return nil, err + } + + return h, nil +} + +func encodeHeader(h JOSEHeader) (string, error) { + b, err := json.Marshal(h) + if err != nil { + return "", err + } + + return encodeSegment(b), nil +} + +// Decode JWT specific base64url encoding with padding stripped +func decodeSegment(seg string) ([]byte, error) { + if l := len(seg) % 4; l != 0 { + seg += strings.Repeat("=", 4-l) + } + return base64.URLEncoding.DecodeString(seg) +} + +// Encode JWT specific base64url encoding with padding stripped +func encodeSegment(seg []byte) string { + return strings.TrimRight(base64.URLEncoding.EncodeToString(seg), "=") +} diff --git a/vendor/github.com/coreos/go-oidc/jose/jwk.go b/vendor/github.com/coreos/go-oidc/jose/jwk.go new file mode 100644 index 00000000..b7a8e235 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/jose/jwk.go @@ -0,0 +1,135 @@ +package jose + +import ( + "bytes" + "encoding/base64" + "encoding/binary" + "encoding/json" + "math/big" + "strings" +) + +// JSON Web Key +// https://tools.ietf.org/html/draft-ietf-jose-json-web-key-36#page-5 +type JWK struct { + ID string + Type string + Alg string + Use string + Exponent int + Modulus *big.Int + Secret []byte +} + +type jwkJSON struct { + ID string `json:"kid"` + Type string `json:"kty"` + Alg string `json:"alg"` + Use string `json:"use"` + Exponent string `json:"e"` + Modulus string `json:"n"` +} + +func (j *JWK) MarshalJSON() ([]byte, error) { + t := jwkJSON{ + ID: j.ID, + Type: j.Type, + Alg: j.Alg, + Use: j.Use, + Exponent: encodeExponent(j.Exponent), + Modulus: encodeModulus(j.Modulus), + } + + return json.Marshal(&t) +} + +func (j *JWK) UnmarshalJSON(data []byte) error { + var t jwkJSON + err := json.Unmarshal(data, &t) + if err != nil { + return err + } + + e, err := decodeExponent(t.Exponent) + if err != nil { + return err + } + + n, err := decodeModulus(t.Modulus) + if err != nil { + return err + } + + j.ID = t.ID + j.Type = t.Type + j.Alg = t.Alg + j.Use = t.Use + j.Exponent = e + j.Modulus = n + + return nil +} + +type JWKSet struct { + Keys []JWK `json:"keys"` +} + +func decodeExponent(e string) (int, error) { + decE, err := decodeBase64URLPaddingOptional(e) + if err != nil { + return 0, err + } + var eBytes []byte + if len(decE) < 8 { + eBytes = make([]byte, 8-len(decE), 8) + eBytes = append(eBytes, decE...) + } else { + eBytes = decE + } + eReader := bytes.NewReader(eBytes) + var E uint64 + err = binary.Read(eReader, binary.BigEndian, &E) + if err != nil { + return 0, err + } + return int(E), nil +} + +func encodeExponent(e int) string { + b := make([]byte, 8) + binary.BigEndian.PutUint64(b, uint64(e)) + var idx int + for ; idx < 8; idx++ { + if b[idx] != 0x0 { + break + } + } + return base64.URLEncoding.EncodeToString(b[idx:]) +} + +// Turns a URL encoded modulus of a key into a big int. +func decodeModulus(n string) (*big.Int, error) { + decN, err := decodeBase64URLPaddingOptional(n) + if err != nil { + return nil, err + } + N := big.NewInt(0) + N.SetBytes(decN) + return N, nil +} + +func encodeModulus(n *big.Int) string { + return base64.URLEncoding.EncodeToString(n.Bytes()) +} + +// decodeBase64URLPaddingOptional decodes Base64 whether there is padding or not. +// The stdlib version currently doesn't handle this. +// We can get rid of this is if this bug: +// https://github.com/golang/go/issues/4237 +// ever closes. +func decodeBase64URLPaddingOptional(e string) ([]byte, error) { + if m := len(e) % 4; m != 0 { + e += strings.Repeat("=", 4-m) + } + return base64.URLEncoding.DecodeString(e) +} diff --git a/vendor/github.com/coreos/go-oidc/jose/jwk_test.go b/vendor/github.com/coreos/go-oidc/jose/jwk_test.go new file mode 100644 index 00000000..63351c45 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/jose/jwk_test.go @@ -0,0 +1,64 @@ +package jose + +import ( + "testing" +) + +func TestDecodeBase64URLPaddingOptional(t *testing.T) { + tests := []struct { + encoded string + decoded string + err bool + }{ + { + // With padding + encoded: "VGVjdG9uaWM=", + decoded: "Tectonic", + }, + { + // Without padding + encoded: "VGVjdG9uaWM", + decoded: "Tectonic", + }, + { + // Even More padding + encoded: "VGVjdG9uaQ==", + decoded: "Tectoni", + }, + { + // And take it away! + encoded: "VGVjdG9uaQ", + decoded: "Tectoni", + }, + { + // Too much padding. + encoded: "VGVjdG9uaWNh=", + decoded: "", + err: true, + }, + { + // Too much padding. + encoded: "VGVjdG9uaWNh=", + decoded: "", + err: true, + }, + } + + for i, tt := range tests { + got, err := decodeBase64URLPaddingOptional(tt.encoded) + if tt.err { + if err == nil { + t.Errorf("case %d: expected non-nil err", i) + } + continue + } + + if err != nil { + t.Errorf("case %d: want nil err, got: %v", i, err) + } + + if string(got) != tt.decoded { + t.Errorf("case %d: want=%q, got=%q", i, tt.decoded, got) + } + } +} diff --git a/vendor/github.com/coreos/go-oidc/jose/jws.go b/vendor/github.com/coreos/go-oidc/jose/jws.go new file mode 100644 index 00000000..1049ece8 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/jose/jws.go @@ -0,0 +1,51 @@ +package jose + +import ( + "fmt" + "strings" +) + +type JWS struct { + RawHeader string + Header JOSEHeader + RawPayload string + Payload []byte + Signature []byte +} + +// Given a raw encoded JWS token parses it and verifies the structure. +func ParseJWS(raw string) (JWS, error) { + parts := strings.Split(raw, ".") + if len(parts) != 3 { + return JWS{}, fmt.Errorf("malformed JWS, only %d segments", len(parts)) + } + + rawSig := parts[2] + jws := JWS{ + RawHeader: parts[0], + RawPayload: parts[1], + } + + header, err := decodeHeader(jws.RawHeader) + if err != nil { + return JWS{}, fmt.Errorf("malformed JWS, unable to decode header, %s", err) + } + if err = header.Validate(); err != nil { + return JWS{}, fmt.Errorf("malformed JWS, %s", err) + } + jws.Header = header + + payload, err := decodeSegment(jws.RawPayload) + if err != nil { + return JWS{}, fmt.Errorf("malformed JWS, unable to decode payload: %s", err) + } + jws.Payload = payload + + sig, err := decodeSegment(rawSig) + if err != nil { + return JWS{}, fmt.Errorf("malformed JWS, unable to decode signature: %s", err) + } + jws.Signature = sig + + return jws, nil +} diff --git a/vendor/github.com/coreos/go-oidc/jose/jws_test.go b/vendor/github.com/coreos/go-oidc/jose/jws_test.go new file mode 100644 index 00000000..78c81261 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/jose/jws_test.go @@ -0,0 +1,74 @@ +package jose + +import ( + "strings" + "testing" +) + +type testCase struct{ t string } + +var validInput []testCase + +var invalidInput []testCase + +func init() { + validInput = []testCase{ + { + "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk", + }, + } + + invalidInput = []testCase{ + // empty + { + "", + }, + // undecodeable + { + "aaa.bbb.ccc", + }, + // missing parts + { + "aaa", + }, + // missing parts + { + "aaa.bbb", + }, + // too many parts + { + "aaa.bbb.ccc.ddd", + }, + // invalid header + // EncodeHeader(map[string]string{"foo": "bar"}) + { + "eyJmb28iOiJiYXIifQ.bbb.ccc", + }, + } +} + +func TestParseJWS(t *testing.T) { + for i, tt := range validInput { + jws, err := ParseJWS(tt.t) + if err != nil { + t.Errorf("test: %d. expected: valid, actual: invalid", i) + } + + expectedHeader := strings.Split(tt.t, ".")[0] + if jws.RawHeader != expectedHeader { + t.Errorf("test: %d. expected: %s, actual: %s", i, expectedHeader, jws.RawHeader) + } + + expectedPayload := strings.Split(tt.t, ".")[1] + if jws.RawPayload != expectedPayload { + t.Errorf("test: %d. expected: %s, actual: %s", i, expectedPayload, jws.RawPayload) + } + } + + for i, tt := range invalidInput { + _, err := ParseJWS(tt.t) + if err == nil { + t.Errorf("test: %d. expected: invalid, actual: valid", i) + } + } +} diff --git a/vendor/github.com/coreos/go-oidc/jose/jwt.go b/vendor/github.com/coreos/go-oidc/jose/jwt.go new file mode 100644 index 00000000..3b3e9634 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/jose/jwt.go @@ -0,0 +1,82 @@ +package jose + +import "strings" + +type JWT JWS + +func ParseJWT(token string) (jwt JWT, err error) { + jws, err := ParseJWS(token) + if err != nil { + return + } + + return JWT(jws), nil +} + +func NewJWT(header JOSEHeader, claims Claims) (jwt JWT, err error) { + jwt = JWT{} + + jwt.Header = header + jwt.Header[HeaderMediaType] = "JWT" + + claimBytes, err := marshalClaims(claims) + if err != nil { + return + } + jwt.Payload = claimBytes + + eh, err := encodeHeader(header) + if err != nil { + return + } + jwt.RawHeader = eh + + ec, err := encodeClaims(claims) + if err != nil { + return + } + jwt.RawPayload = ec + + return +} + +func (j *JWT) KeyID() (string, bool) { + kID, ok := j.Header[HeaderKeyID] + return kID, ok +} + +func (j *JWT) Claims() (Claims, error) { + return decodeClaims(j.Payload) +} + +// Encoded data part of the token which may be signed. +func (j *JWT) Data() string { + return strings.Join([]string{j.RawHeader, j.RawPayload}, ".") +} + +// Full encoded JWT token string in format: header.claims.signature +func (j *JWT) Encode() string { + d := j.Data() + s := encodeSegment(j.Signature) + return strings.Join([]string{d, s}, ".") +} + +func NewSignedJWT(claims Claims, s Signer) (*JWT, error) { + header := JOSEHeader{ + HeaderKeyAlgorithm: s.Alg(), + HeaderKeyID: s.ID(), + } + + jwt, err := NewJWT(header, claims) + if err != nil { + return nil, err + } + + sig, err := s.Sign([]byte(jwt.Data())) + if err != nil { + return nil, err + } + jwt.Signature = sig + + return &jwt, nil +} diff --git a/vendor/github.com/coreos/go-oidc/jose/jwt_test.go b/vendor/github.com/coreos/go-oidc/jose/jwt_test.go new file mode 100644 index 00000000..74691769 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/jose/jwt_test.go @@ -0,0 +1,94 @@ +package jose + +import ( + "reflect" + "testing" +) + +func TestParseJWT(t *testing.T) { + tests := []struct { + r string + h JOSEHeader + c Claims + }{ + { + // Example from JWT spec: + // http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#ExampleJWT + "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk", + JOSEHeader{ + HeaderMediaType: "JWT", + HeaderKeyAlgorithm: "HS256", + }, + Claims{ + "iss": "joe", + // NOTE: test numbers must be floats for equality checks to work since values are converted form interface{} to float64 by default. + "exp": 1300819380.0, + "http://example.com/is_root": true, + }, + }, + } + + for i, tt := range tests { + jwt, err := ParseJWT(tt.r) + if err != nil { + t.Errorf("raw token should parse. test: %d. expected: valid, actual: invalid. err=%v", i, err) + } + + if !reflect.DeepEqual(tt.h, jwt.Header) { + t.Errorf("JOSE headers should match. test: %d. expected: %v, actual: %v", i, tt.h, jwt.Header) + } + + claims, err := jwt.Claims() + if err != nil { + t.Errorf("test: %d. expected: valid claim parsing. err=%v", i, err) + } + if !reflect.DeepEqual(tt.c, claims) { + t.Errorf("claims should match. test: %d. expected: %v, actual: %v", i, tt.c, claims) + } + + enc := jwt.Encode() + if enc != tt.r { + t.Errorf("encoded jwt should match raw jwt. test: %d. expected: %v, actual: %v", i, tt.r, enc) + } + } +} + +func TestNewJWTHeaderType(t *testing.T) { + jwt, err := NewJWT(JOSEHeader{}, Claims{}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + want := "JWT" + got := jwt.Header[HeaderMediaType] + if want != got { + t.Fatalf("Header %q incorrect: want=%s got=%s", HeaderMediaType, want, got) + } + +} + +func TestNewJWTHeaderKeyID(t *testing.T) { + jwt, err := NewJWT(JOSEHeader{HeaderKeyID: "foo"}, Claims{}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + want := "foo" + got, ok := jwt.KeyID() + if !ok { + t.Fatalf("KeyID not set") + } else if want != got { + t.Fatalf("KeyID incorrect: want=%s got=%s", want, got) + } +} + +func TestNewJWTHeaderKeyIDNotSet(t *testing.T) { + jwt, err := NewJWT(JOSEHeader{}, Claims{}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if _, ok := jwt.KeyID(); ok { + t.Fatalf("KeyID set, but should not be") + } +} diff --git a/vendor/github.com/coreos/go-oidc/jose/sig.go b/vendor/github.com/coreos/go-oidc/jose/sig.go new file mode 100755 index 00000000..7b2b253c --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/jose/sig.go @@ -0,0 +1,24 @@ +package jose + +import ( + "fmt" +) + +type Verifier interface { + ID() string + Alg() string + Verify(sig []byte, data []byte) error +} + +type Signer interface { + Verifier + Sign(data []byte) (sig []byte, err error) +} + +func NewVerifier(jwk JWK) (Verifier, error) { + if jwk.Type != "RSA" { + return nil, fmt.Errorf("unsupported key type %q", jwk.Type) + } + + return NewVerifierRSA(jwk) +} diff --git a/vendor/github.com/coreos/go-oidc/jose/sig_rsa.go b/vendor/github.com/coreos/go-oidc/jose/sig_rsa.go new file mode 100755 index 00000000..004e45dd --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/jose/sig_rsa.go @@ -0,0 +1,67 @@ +package jose + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" + "fmt" +) + +type VerifierRSA struct { + KeyID string + Hash crypto.Hash + PublicKey rsa.PublicKey +} + +type SignerRSA struct { + PrivateKey rsa.PrivateKey + VerifierRSA +} + +func NewVerifierRSA(jwk JWK) (*VerifierRSA, error) { + if jwk.Alg != "" && jwk.Alg != "RS256" { + return nil, fmt.Errorf("unsupported key algorithm %q", jwk.Alg) + } + + v := VerifierRSA{ + KeyID: jwk.ID, + PublicKey: rsa.PublicKey{ + N: jwk.Modulus, + E: jwk.Exponent, + }, + Hash: crypto.SHA256, + } + + return &v, nil +} + +func NewSignerRSA(kid string, key rsa.PrivateKey) *SignerRSA { + return &SignerRSA{ + PrivateKey: key, + VerifierRSA: VerifierRSA{ + KeyID: kid, + PublicKey: key.PublicKey, + Hash: crypto.SHA256, + }, + } +} + +func (v *VerifierRSA) ID() string { + return v.KeyID +} + +func (v *VerifierRSA) Alg() string { + return "RS256" +} + +func (v *VerifierRSA) Verify(sig []byte, data []byte) error { + h := v.Hash.New() + h.Write(data) + return rsa.VerifyPKCS1v15(&v.PublicKey, v.Hash, h.Sum(nil), sig) +} + +func (s *SignerRSA) Sign(data []byte) ([]byte, error) { + h := s.Hash.New() + h.Write(data) + return rsa.SignPKCS1v15(rand.Reader, &s.PrivateKey, s.Hash, h.Sum(nil)) +} diff --git a/vendor/github.com/coreos/go-oidc/jose_test.go b/vendor/github.com/coreos/go-oidc/jose_test.go new file mode 100644 index 00000000..ae5ca0e5 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/jose_test.go @@ -0,0 +1,405 @@ +// +build !golint + +// This file contains statically created JWKs for tests created by gen.go + +package oidc + +import ( + "encoding/json" + + jose "gopkg.in/square/go-jose.v2" +) + +func mustLoadJWK(s string) jose.JSONWebKey { + var jwk jose.JSONWebKey + if err := json.Unmarshal([]byte(s), &jwk); err != nil { + panic(err) + } + return jwk +} + +var ( + testKeyECDSA_256_0 = mustLoadJWK(`{ + "kty": "EC", + "kid": "bd06f3e11f523e310f8c2c8b20a892727fb558e0e23602312568b20a41e11188", + "crv": "P-256", + "x": "xK5N69f0-SAgWbjw2otcQeCGs3qqYMyqOWk4Os5Z_Xc", + "y": "AXSaOPcMklJY9UKZhkGzVevqhAIUEzE3cfZ8o-ML5xE" + }`) + testKeyECDSA_256_0_Priv = mustLoadJWK(`{ + "kty": "EC", + "kid": "bd06f3e11f523e310f8c2c8b20a892727fb558e0e23602312568b20a41e11188", + "crv": "P-256", + "x": "xK5N69f0-SAgWbjw2otcQeCGs3qqYMyqOWk4Os5Z_Xc", + "y": "AXSaOPcMklJY9UKZhkGzVevqhAIUEzE3cfZ8o-ML5xE", + "d": "L7jynYt-fMRPqw1e9vgXCGTg4yhGU4tlLxiFyNVimG4" + }`) + + testKeyECDSA_256_1 = mustLoadJWK(`{ + "kty": "EC", + "kid": "27ebd2f5bf6723e4de06caaab03703be458a37bf28a9fa748576a0826acb6ec2", + "crv": "P-256", + "x": "KZisP6wLCph4q6056jr7BH_asiX9RcLcS3HrNjdCpkw", + "y": "5DrW-kEge0sePHlKmh1d2kqd10r32JEW6eyyewy18j8" + }`) + testKeyECDSA_256_1_Priv = mustLoadJWK(`{ + "kty": "EC", + "kid": "27ebd2f5bf6723e4de06caaab03703be458a37bf28a9fa748576a0826acb6ec2", + "crv": "P-256", + "x": "KZisP6wLCph4q6056jr7BH_asiX9RcLcS3HrNjdCpkw", + "y": "5DrW-kEge0sePHlKmh1d2kqd10r32JEW6eyyewy18j8", + "d": "r6CiIpv0icIq5U4LYO39nBDVhCHCLObDFYC5IG9Y8Hk" + }`) + + testKeyECDSA_256_2 = mustLoadJWK(`{ + "kty": "EC", + "kid": "2c7d179db6006c90ece4d91f554791ff35156693c693102c00f66f473d15df17", + "crv": "P-256", + "x": "oDwcKp7SqgeRvycK5GgYjrlW4fbHn2Ybfd5iG7kDiPc", + "y": "qazib9UwdUdbHSFzdy_HN10xZEItLvufPw0v7nIJOWA" + }`) + testKeyECDSA_256_2_Priv = mustLoadJWK(`{ + "kty": "EC", + "kid": "2c7d179db6006c90ece4d91f554791ff35156693c693102c00f66f473d15df17", + "crv": "P-256", + "x": "oDwcKp7SqgeRvycK5GgYjrlW4fbHn2Ybfd5iG7kDiPc", + "y": "qazib9UwdUdbHSFzdy_HN10xZEItLvufPw0v7nIJOWA", + "d": "p79U6biKrOyrKzg-i3C7FVJiqzlqBhYQqmyOiZ9bhVM" + }`) + + testKeyECDSA_256_3 = mustLoadJWK(`{ + "kty": "EC", + "kid": "aba0a256999af470c8b2449103ae75b257d907da4d1cbfc73c1d45ab1098f544", + "crv": "P-256", + "x": "CKRYWVt1R7FzuJ43vEprfIzgB-KgIhRDhxLmd5ixiXY", + "y": "QCxTVmK31ee710OYkNqdqEgHH3rqRQNQj3Wyq0xtYq0" + }`) + testKeyECDSA_256_3_Priv = mustLoadJWK(`{ + "kty": "EC", + "kid": "aba0a256999af470c8b2449103ae75b257d907da4d1cbfc73c1d45ab1098f544", + "crv": "P-256", + "x": "CKRYWVt1R7FzuJ43vEprfIzgB-KgIhRDhxLmd5ixiXY", + "y": "QCxTVmK31ee710OYkNqdqEgHH3rqRQNQj3Wyq0xtYq0", + "d": "c_WflwwUxT_B5izk4iET49qoob0RH5hccnEScfBS9Qk" + }`) + + testKeyECDSA_384_0 = mustLoadJWK(`{ + "kty": "EC", + "kid": "bc271d0a68251171e0c5edfd384b32d154e1717af27c5089bda8fb598a474b7b", + "crv": "P-384", + "x": "FZEhxw06oB86xCAZCtKTfX3ze9tgxkdN199g-cWrpQTWF-2m6Blg1MN5D60Z9KoX", + "y": "EyWjZ46gLlqfoU08iv0zcDWut1nbfoUoTd-7La2VY4PqQeDSFrxPCOyplxFMGJIW" + }`) + testKeyECDSA_384_0_Priv = mustLoadJWK(`{ + "kty": "EC", + "kid": "bc271d0a68251171e0c5edfd384b32d154e1717af27c5089bda8fb598a474b7b", + "crv": "P-384", + "x": "FZEhxw06oB86xCAZCtKTfX3ze9tgxkdN199g-cWrpQTWF-2m6Blg1MN5D60Z9KoX", + "y": "EyWjZ46gLlqfoU08iv0zcDWut1nbfoUoTd-7La2VY4PqQeDSFrxPCOyplxFMGJIW", + "d": "t6oJHJBH2rvH3uyQkK_JwoUEwE1QHjwTnGLSDMZbNEDrEaR8BBiAo3s0p8rzGz5-" + }`) + + testKeyECDSA_384_1 = mustLoadJWK(`{ + "kty": "EC", + "kid": "58ef375d6e63cedc637fed59f4012a779cd749a83373237cf3972bba74ebd738", + "crv": "P-384", + "x": "Gj3zkiRR4RCJb0Tke3lD2spG2jzuXgX50fEwDZTRjlQIzz3Rc96Fw32FCSDectxQ", + "y": "6alqN7ilqJAmqCU1BFrJJeivJiM0s1-RqBewRoNkTQinOLLbaiZlBAQxS4iq2dRv" + }`) + testKeyECDSA_384_1_Priv = mustLoadJWK(`{ + "kty": "EC", + "kid": "58ef375d6e63cedc637fed59f4012a779cd749a83373237cf3972bba74ebd738", + "crv": "P-384", + "x": "Gj3zkiRR4RCJb0Tke3lD2spG2jzuXgX50fEwDZTRjlQIzz3Rc96Fw32FCSDectxQ", + "y": "6alqN7ilqJAmqCU1BFrJJeivJiM0s1-RqBewRoNkTQinOLLbaiZlBAQxS4iq2dRv", + "d": "-4eaoElyb_YANRownMtId_-glX2o45oc4_L_vgo3YOW5hxCq3KIBIdrhvBx1nw8B" + }`) + + testKeyECDSA_384_2 = mustLoadJWK(`{ + "kty": "EC", + "kid": "a8bab6f438b6cd2080711404549c978d1c00dd77a3c532bba12c964c4883b2ec", + "crv": "P-384", + "x": "CBKwYgZFLdTBBFnWD20q2YNUnRnOsDgTxG3y-dzCUrb65kOKm0ZFaZQPe5ZPjvDS", + "y": "WlubxLv2qH-Aw-LsESKXAwm4HF3l4H1rVn3DZRpqcac6p-QSrXmfKCtxJVHDaTNi" + }`) + testKeyECDSA_384_2_Priv = mustLoadJWK(`{ + "kty": "EC", + "kid": "a8bab6f438b6cd2080711404549c978d1c00dd77a3c532bba12c964c4883b2ec", + "crv": "P-384", + "x": "CBKwYgZFLdTBBFnWD20q2YNUnRnOsDgTxG3y-dzCUrb65kOKm0ZFaZQPe5ZPjvDS", + "y": "WlubxLv2qH-Aw-LsESKXAwm4HF3l4H1rVn3DZRpqcac6p-QSrXmfKCtxJVHDaTNi", + "d": "XfoOcxV3yfoAcRquJ9eaBvcY-H71B0XzXwx2eidolHDKLK7GHygEt9ToYSY_DvxO" + }`) + + testKeyECDSA_384_3 = mustLoadJWK(`{ + "kty": "EC", + "kid": "32f0cfc686455840530d727fb029002b9757ffff15272f98f53be9f77560718a", + "crv": "P-384", + "x": "xxED1z8EUCOUQ_jwS9nuVUDVzxs-U1rl19y8jMWrv4TdPeGHTRTgNUE57-YAL3ly", + "y": "OsCN6-HmM-LP1itE5eW15WsSQoe3dZBX7AoHyKJUKtjzWKCJNUcyu3Np07xta1Cr" + }`) + testKeyECDSA_384_3_Priv = mustLoadJWK(`{ + "kty": "EC", + "kid": "32f0cfc686455840530d727fb029002b9757ffff15272f98f53be9f77560718a", + "crv": "P-384", + "x": "xxED1z8EUCOUQ_jwS9nuVUDVzxs-U1rl19y8jMWrv4TdPeGHTRTgNUE57-YAL3ly", + "y": "OsCN6-HmM-LP1itE5eW15WsSQoe3dZBX7AoHyKJUKtjzWKCJNUcyu3Np07xta1Cr", + "d": "gfQA6wn4brWVT3OkeGiaCXGsQyTybZB9SdqTULsiSg8n6FS2T8hvK0doPwMTT5Gw" + }`) + + testKeyECDSA_521_0 = mustLoadJWK(`{ + "kty": "EC", + "kid": "f7b325c848a6c9fc5b72f131f179d2f37296837262797983c632597a4927726e", + "crv": "P-521", + "x": "AUaLqCAMEWPqiYgd-D_6F5kxpOgqnbQnjIHZ-NhjRnKKYuij9Iz7bq9pZU4F79wsODFpWxMFfISrveUfgEGt4Hy2", + "y": "AeAKfmFsfcFttwQqv2B-fUfgLhw837YoGoNWh5qNE_LqTBxbYKRUkbSLxVRHEcVNnU1t3z9yMMdYXtuMlfJ0bhjr" + }`) + testKeyECDSA_521_0_Priv = mustLoadJWK(`{ + "kty": "EC", + "kid": "f7b325c848a6c9fc5b72f131f179d2f37296837262797983c632597a4927726e", + "crv": "P-521", + "x": "AUaLqCAMEWPqiYgd-D_6F5kxpOgqnbQnjIHZ-NhjRnKKYuij9Iz7bq9pZU4F79wsODFpWxMFfISrveUfgEGt4Hy2", + "y": "AeAKfmFsfcFttwQqv2B-fUfgLhw837YoGoNWh5qNE_LqTBxbYKRUkbSLxVRHEcVNnU1t3z9yMMdYXtuMlfJ0bhjr", + "d": "AbD3guIvlVd8CZD0xUNuebgnQkE24XJnxCQ69P5VL3etEMmdr4HPJLPMQfMs10Gz8RTmrGKo-bdsU-cjS3d7dKIC" + }`) + + testKeyECDSA_521_1 = mustLoadJWK(`{ + "kty": "EC", + "kid": "4ea260ba2c9ed5fe9d6adeb998bb68b3edbab839d8bfd2be09fa628680793b90", + "crv": "P-521", + "x": "AE8-53o64GiywneanVZAHb96NcPbq0Ml6zynIcgLdKUiHGVs2te7SABq_9keFZJC6wooACeNWWT7VDK3kY77fjdh", + "y": "AAnrIu0-D7JEd3-mqTR8Rdz53kw7DLAIypv0-_u4rqn0glwTZCkMpQ17wFH71bMInXaTi2Z_uq67NuVxFvUCaTvS" + }`) + testKeyECDSA_521_1_Priv = mustLoadJWK(`{ + "kty": "EC", + "kid": "4ea260ba2c9ed5fe9d6adeb998bb68b3edbab839d8bfd2be09fa628680793b90", + "crv": "P-521", + "x": "AE8-53o64GiywneanVZAHb96NcPbq0Ml6zynIcgLdKUiHGVs2te7SABq_9keFZJC6wooACeNWWT7VDK3kY77fjdh", + "y": "AAnrIu0-D7JEd3-mqTR8Rdz53kw7DLAIypv0-_u4rqn0glwTZCkMpQ17wFH71bMInXaTi2Z_uq67NuVxFvUCaTvS", + "d": "AVVL9TIWcJCYH5r3QUhHXtsbkVXhLQSpp4sL1ta_H2_3bpRb9ZdVv10YOA-xN7Yz2wa-FMhIhj1ULe9z18ZM8dDF" + }`) + + testKeyECDSA_521_2 = mustLoadJWK(`{ + "kty": "EC", + "kid": "d69caddc821807ea63a46519b538a7d2f3135dbdda1ba628781f8567a47893d8", + "crv": "P-521", + "x": "AO618rH8GP78-mi9Z5FaPGqpyc_OVMK-BajZK-pwL89ZQdOvFa0fY0ENpB_KaRf5ELw9IP17lQh3T-O9O7jePDFj", + "y": "AT1x9pCMbY-BXqAJPQDQxp6j8Gca7IpdxL8OS4td_XPiwXtX4JKcj_VKxtOw6k64yr_VuFYCs6wImOc72jNRBjA7" + }`) + testKeyECDSA_521_2_Priv = mustLoadJWK(`{ + "kty": "EC", + "kid": "d69caddc821807ea63a46519b538a7d2f3135dbdda1ba628781f8567a47893d8", + "crv": "P-521", + "x": "AO618rH8GP78-mi9Z5FaPGqpyc_OVMK-BajZK-pwL89ZQdOvFa0fY0ENpB_KaRf5ELw9IP17lQh3T-O9O7jePDFj", + "y": "AT1x9pCMbY-BXqAJPQDQxp6j8Gca7IpdxL8OS4td_XPiwXtX4JKcj_VKxtOw6k64yr_VuFYCs6wImOc72jNRBjA7", + "d": "AYmtaW9ojb0Gb8zSqrlRnEKzRVIBIM5dsb1qWkd3mfr4Wl5tbPuiEctGLN9s6LDtY0JOL3nukOVoDbrmS4qCW64" + }`) + + testKeyECDSA_521_3 = mustLoadJWK(`{ + "kty": "EC", + "kid": "7193cba4fd9c72153133ce5cffc423857517ab8eb176a827e1a002eb5621edca", + "crv": "P-521", + "x": "AQfpLehZCER3ZTw1V1pN0RsX8-4WEW4IDxFDSGwrULQ79YHiLNmubrOSlSxiOSv2S-tHq-hxgma1PZlQRghJfemx", + "y": "AAF41XT7jptLsy8FAaVRnex2WcSfdebcjjXMGO4rn6IlD3u9qnvrpR8MBp1gz5G1C5S7_NVgIeLSIYbdfd_wjVkd" + }`) + testKeyECDSA_521_3_Priv = mustLoadJWK(`{ + "kty": "EC", + "kid": "7193cba4fd9c72153133ce5cffc423857517ab8eb176a827e1a002eb5621edca", + "crv": "P-521", + "x": "AQfpLehZCER3ZTw1V1pN0RsX8-4WEW4IDxFDSGwrULQ79YHiLNmubrOSlSxiOSv2S-tHq-hxgma1PZlQRghJfemx", + "y": "AAF41XT7jptLsy8FAaVRnex2WcSfdebcjjXMGO4rn6IlD3u9qnvrpR8MBp1gz5G1C5S7_NVgIeLSIYbdfd_wjVkd", + "d": "hzh0loqrhYFUik86SYBv3CC_GKNYankLMK95-Cfr1gLBD1l0M6W-7gn3XlyaQEInz0TBIbaQ1fL78HHjbVsNLrY" + }`) + + testKeyRSA_1024_0 = mustLoadJWK(`{ + "kty": "RSA", + "kid": "979d62e7114052027b11bcb51282d8228790134ef34e746f6372fa5aadaa9bf2", + "n": "6zxmGE5X74SUBWfDEo8Tl-YxrkVfvxljQG9vKmNPQ2RhEmJ8eplZpKn9_nlxVDHGLzfJMqqUBS19EarIfDnOqyFBRkyKRbsQdUVU9XLjDIXqImJ8aN-UmShAYoeOClJVDsBJuxBGgS3pdgG7u3YQpvTV_hGuMoJr7UYgIrqb0qc", + "e": "AQAB" + }`) + testKeyRSA_1024_0_Priv = mustLoadJWK(`{ + "kty": "RSA", + "kid": "979d62e7114052027b11bcb51282d8228790134ef34e746f6372fa5aadaa9bf2", + "n": "6zxmGE5X74SUBWfDEo8Tl-YxrkVfvxljQG9vKmNPQ2RhEmJ8eplZpKn9_nlxVDHGLzfJMqqUBS19EarIfDnOqyFBRkyKRbsQdUVU9XLjDIXqImJ8aN-UmShAYoeOClJVDsBJuxBGgS3pdgG7u3YQpvTV_hGuMoJr7UYgIrqb0qc", + "e": "AQAB", + "d": "IARggP5oyZjp7LJqwqPmrs4OBQI8Pe5eq-5-2u4ZY7rN24q8FpO4t8jLYU92NVdw-gxFvjepXesLEtSD5SSZFD7s-5rqmX9tW_t5t1a1sBY6IKz_YBSf2p2LtdTyrqgo4nRD0nYH3sMvGtOKCTV4K_vwfWPD3elXq752trG-HwE", + "p": "9tgo12L3earNNrJfyIIDD2dqgKuNfWhQzhbe-Ju17dF37uGF6xnLqR0WXU-agGpUdYTMOd7IQi_bX_xkjQBYlw", + "q": "8_YDvufPdccjGM2rAXs-2LbeP_LrzLUk1uGNpEjIt2lXEm6sQKjIfNLcAfVKh9zsqwqroveL2iI1ijsDgNAIcQ" + }`) + + testKeyRSA_1024_1 = mustLoadJWK(`{ + "kty": "RSA", + "kid": "4a3a37dc10aec5f427509e4014683102a0fafa32bec2ff9921ff19e1a55c79e8", + "n": "sn7sU7dBEkU1RBIz_LKgrswSS7-68vTlOe7n-lanAqAlczm01_6IWrvcIC7lPv1iHQqWngusskANZirCZGTv6kK8kJLHBVRSROB9VkPTPNFYnSB6dacyPa0ty0otsaYOVM2RFvcCX7lBKKat2Tmst8vITdpvnoEzTgT_eGOKGAs", + "e": "AQAB" + }`) + testKeyRSA_1024_1_Priv = mustLoadJWK(`{ + "kty": "RSA", + "kid": "4a3a37dc10aec5f427509e4014683102a0fafa32bec2ff9921ff19e1a55c79e8", + "n": "sn7sU7dBEkU1RBIz_LKgrswSS7-68vTlOe7n-lanAqAlczm01_6IWrvcIC7lPv1iHQqWngusskANZirCZGTv6kK8kJLHBVRSROB9VkPTPNFYnSB6dacyPa0ty0otsaYOVM2RFvcCX7lBKKat2Tmst8vITdpvnoEzTgT_eGOKGAs", + "e": "AQAB", + "d": "KTXqpE1sBaba7HNzc0Vemdzd4IVMyWlHPz_saTz2ZEHLQ7YwDapjmudCpF-PaCKiM2hNbAHwBluJfGwk4372cPHcJyri3Gs4GGj-cOlbXJh9aUp5o8fqn904B1UcPxMZ2DrZAKjseuLj3zyJgCsSJOGU1kJFRAkM8UN4a9cE8sE", + "p": "3ntHMGDJEXwqldLNR-DucKGp32aJVAaKnn4j9qy1Tj6KhH70o3HyFVO_TKYxGg-pG3zTV-VzyMqmeh9hDdnyKw", + "q": "zWMxEjyxvp-P-mNxhWQe1pJXZi6iPpcnt0SZLfweT3AtAzEaoSs4-4D1jT911LD4xDI8_a7-gYHgD8ME62PhoQ" + }`) + + testKeyRSA_1024_2 = mustLoadJWK(`{ + "kty": "RSA", + "kid": "d1c32aaea6c9527038e589dd94a546ca1beedb90f90e073228986cc258cf1fda", + "n": "3mZrDaB5-7e29zok9XkGuu6FXYB00FqFgnGTJAGCMpql5uHz1h9p0DljZL4vsGkkYOZUMvqFS1pCEuzdSsupPNClf0NKMRux6yLv6iIR9C4pE9RBKPUrinzJuYs634rq5JOEP4IpP_fJfKxMw4Na85otd9KposKwP14cCkOibYM", + "e": "AQAB" + }`) + testKeyRSA_1024_2_Priv = mustLoadJWK(`{ + "kty": "RSA", + "kid": "d1c32aaea6c9527038e589dd94a546ca1beedb90f90e073228986cc258cf1fda", + "n": "3mZrDaB5-7e29zok9XkGuu6FXYB00FqFgnGTJAGCMpql5uHz1h9p0DljZL4vsGkkYOZUMvqFS1pCEuzdSsupPNClf0NKMRux6yLv6iIR9C4pE9RBKPUrinzJuYs634rq5JOEP4IpP_fJfKxMw4Na85otd9KposKwP14cCkOibYM", + "e": "AQAB", + "d": "Ft2M0B_ZqsmepBh0SFCjIoD3cT-NwwYrh9fJewA0tKM1v2EnwrIEHQZpc6giGw8UUGod6gfbwH2NIYj8z33U7lyrKWPR7F8gJRm1KR5NLCBCnAnz0ukhsg24ktB25LZLZRCStRRhtCev95Vvmew0ip5081hv730Z2T_PsEyOU6E", + "p": "8moFzLKJSQuhaIYTo1qNX0w8o4NBFb1atOlthHmq6Y6rdYTm_nyM9Q3mrNBolPS7LHTiBXtCEJ_m4V5hWDuGKw", + "q": "6t0_mZKOoZ5NAE--MxkuOCcRhuqNo9VoacfPEH39CeoKAam4v5k56R7aQcb_raQK396pgV-evM2dpfdUaasiCQ" + }`) + + testKeyRSA_1024_3 = mustLoadJWK(`{ + "kty": "RSA", + "kid": "838742d0e3195b19f974fa30352db79070fa19aaaeb678ec95c499de7bd48d52", + "n": "17Uc89-QvCjqBLXDJSCWxUoohjwFPI63Gub8g-lH1GSK3flXqiohz33KfKhqrdKsLrpRjskGTMg3Vo0IcLBnMYdp1i1nceORghtYLQsDNS7tqlHiKx725fLmWldGgiuP_0Ak0Knisw-j_q7Jx3OVAJnuS1o3vPLfJxJdyq6yV1k", + "e": "AQAB" + }`) + testKeyRSA_1024_3_Priv = mustLoadJWK(`{ + "kty": "RSA", + "kid": "838742d0e3195b19f974fa30352db79070fa19aaaeb678ec95c499de7bd48d52", + "n": "17Uc89-QvCjqBLXDJSCWxUoohjwFPI63Gub8g-lH1GSK3flXqiohz33KfKhqrdKsLrpRjskGTMg3Vo0IcLBnMYdp1i1nceORghtYLQsDNS7tqlHiKx725fLmWldGgiuP_0Ak0Knisw-j_q7Jx3OVAJnuS1o3vPLfJxJdyq6yV1k", + "e": "AQAB", + "d": "B8cM-zIVauNixLa1CZKqPQTWfziMy8ktivfHJQ51O5BAfY5u_cC1JWEYuvPrnMba1Hh9VlOjOYOCk0lUg5OotMx5hxym6M5y_2rIrW90a8r3gGttPlZmyHQnFIgj2QZHlEyZGU1SIPTOtoECW5RGk7cYpA1s1_zfz8uyG-MiCPE", + "p": "2dwia5iWWLZwFcCVylPh_7QDr3Wxt3kW_TmHIbaRT10Rborj1lAwltyEx7aVpHq-aNfvrYIEBbOXWwUU8PDUrQ", + "q": "_XiDT55hn-Txi2C_peMVSDgXozf01qHKvdLirXM2uT_CI8kruYZYjWYn6_cSUptJ60eioM3WNTxjrSxWRS923Q" + }`) + + testKeyRSA_2048_0 = mustLoadJWK(`{ + "kty": "RSA", + "kid": "13b668c7b4f5d46d53de65e4b9f9c522fad4870e4d1656e459c88de6e90984d6", + "n": "1j-CxBxkn20C_M24t6ueLp02T4MMAiPLXf5yaDcj7EcsXGbnsGEhrAUwCZwdEJAxeZ0PolmlE0XBOhQfRtsCVJmwu918aknptToyDbOUBr6WtPIK_c_BuGVanLCx3SnczV4jle9Bz7tGfpj2vAXytSBrfnZhdCHNEFeefQTQMnavfMhfWf_njiTa76BRyAHjb-XZIJHKovwBu0y3glmzhSKYNsUrW11RsWx6DbueWEbE3FpsHTiEdnJipcP3UKl3Z2z6t6n9ZYtFWkx4zCVVBQu-RWUQwjr2XnR1LFwXgL9xQocDBmS1O-wMTHqL3_oosNUdV3vuMPdxs_SEoys2rQ", + "e": "AQAB" + }`) + testKeyRSA_2048_0_Priv = mustLoadJWK(`{ + "kty": "RSA", + "kid": "13b668c7b4f5d46d53de65e4b9f9c522fad4870e4d1656e459c88de6e90984d6", + "n": "1j-CxBxkn20C_M24t6ueLp02T4MMAiPLXf5yaDcj7EcsXGbnsGEhrAUwCZwdEJAxeZ0PolmlE0XBOhQfRtsCVJmwu918aknptToyDbOUBr6WtPIK_c_BuGVanLCx3SnczV4jle9Bz7tGfpj2vAXytSBrfnZhdCHNEFeefQTQMnavfMhfWf_njiTa76BRyAHjb-XZIJHKovwBu0y3glmzhSKYNsUrW11RsWx6DbueWEbE3FpsHTiEdnJipcP3UKl3Z2z6t6n9ZYtFWkx4zCVVBQu-RWUQwjr2XnR1LFwXgL9xQocDBmS1O-wMTHqL3_oosNUdV3vuMPdxs_SEoys2rQ", + "e": "AQAB", + "d": "C8F4X2Jfcw_8Nfrjw9A64bvmmv5JzmRAaGvpwyYjZneRS5Cp7demjVXLiPtz7NC8pjuj-_iHQkN1ksY_4RdrTVERjX1dskdT94m17WKJIMWcZ1lQmRSpQIDvM-HOIKCHaQ1dToDOT6Oq_o9OGosJAj9BJrNALasdIWRtYda9xcb7roLl_U3AtOlK9RiFygtt5uVBIPh1rsxaT0Y1MvMk4EMbFnv7NXXk65UM_3p2leSoPpO7LvlYs3WoRRX9ABH7zI-ppwLGEDuxfnwKzAOaRBe9oUh5rTYdazaY9XqofvekBc9Xqa2HpjkYX-L4Zy3oj04u-u5zX8KTh25jFC2a0Q", + "p": "6L6icXqV7jLSXHrhMyLpW-tdFFp2XRQ_uKk972jmUJ-sEeh1YYSx_JpK6oaUnYshGbRLfEAOLy03iweAL4uMZJcASOR44TwSnn_qJZ72PkwhD42uKhE0eJRpxVht6jf5qhsynaMNUWc_FIW5ESpQIf6j4rqFV5WrKHAt0ypChTc", + "q": "66fAza4rLvyjqQAIGQ07RiRYW4no1h4cK8RH_CDUgI3CCFQSuqTB8ZqM38r2u0tZoKzCNgHcnde6Jk07X6-LSQW2kypMXt3idbgaeVSYSbdZndgG_jTibJvoybxSjo6HfLpPvCrfsihXo7O5M13LE7VqBLbGTLI5iubOxcrfFTs" + }`) + + testKeyRSA_2048_1 = mustLoadJWK(`{ + "kty": "RSA", + "kid": "6d2b0efff0011bbbbf694de684b5b97cf454fd4086fa53b9af92545f3fa65459", + "n": "1CmSBUbxU1jjnmaBG0r0cLLmyZgCOMbfpG6Z3HwfVDgCK6P-rU3F6QQazrOgGJJ0sz6vP50VK5u7BR6vSrPBBX5CicJPM2iNdz2JuV9ODEIkDQBLeI6TfIGhNOls-14tXKOPExY8b6JdyDMP31Hwo_0pF1FaunZ7yY1bgoKCtDV5-RKGd2EgylDGNPu0Ilr92MqCsAntBC8eQSkO4CcTcti4t9cX45VY5nPtwQRmp5zIgUHnU4LV3QVTLJnU-uidaAxRVQbS1pVql5xR6nYZHYvFk1IU-wTY6gGk5WvGWWQ440UTTaMAfnJP6VFDggUXeGSlKfKkDcz7JLT2Ma2KXQ", + "e": "AQAB" + }`) + testKeyRSA_2048_1_Priv = mustLoadJWK(`{ + "kty": "RSA", + "kid": "6d2b0efff0011bbbbf694de684b5b97cf454fd4086fa53b9af92545f3fa65459", + "n": "1CmSBUbxU1jjnmaBG0r0cLLmyZgCOMbfpG6Z3HwfVDgCK6P-rU3F6QQazrOgGJJ0sz6vP50VK5u7BR6vSrPBBX5CicJPM2iNdz2JuV9ODEIkDQBLeI6TfIGhNOls-14tXKOPExY8b6JdyDMP31Hwo_0pF1FaunZ7yY1bgoKCtDV5-RKGd2EgylDGNPu0Ilr92MqCsAntBC8eQSkO4CcTcti4t9cX45VY5nPtwQRmp5zIgUHnU4LV3QVTLJnU-uidaAxRVQbS1pVql5xR6nYZHYvFk1IU-wTY6gGk5WvGWWQ440UTTaMAfnJP6VFDggUXeGSlKfKkDcz7JLT2Ma2KXQ", + "e": "AQAB", + "d": "DYJcHuPmh-UYEUT7oY5DRE3P7jQ0qALZyLGWMHji0c0DLl4x4D0chfrR7il33zisH6G1LPrGl1FCNlA-3yXU-5GPkRADVQWqRFZxx5Du-k7X1tAW_iUt9PaYGjNm0hasEsMDYDbBQGZ5TD8cGp8wEHEVRbvTaB4VQb8zfXrr8ad8XCFdtYQF5Sw_VuvUBcn-50kdl7S0MmQiwLx5xkisJHkVdVsrWbq-JJPMYrjYzPGo07kt8pQH2ecxrQD2waTzkLiAg-nytPBkAyMIFQ-XCulWCNiI-V_HJ0pqNctgVpdaSihlPCYhfllxUSU8yg4UvcfFEAN2S5yjvPyrJeJLAQ", + "p": "4db4J-vxAyb_3otKidzYISIo8NuPxPXuYeImbvVbY6CWHdAYAkPgbwADh592D1vM6kwqZLot4VQ4XUVtX7Vf8tCaq5BAzcVAslK0rhK86kSmzuvtpS1ruzm1DGwJcQWOl_9qYnaAov8Ny0NG8fTm8iwvWJH7rVa3XNtPL0t_fx0", + "q": "8H7_sX4kY3_4t90HbA8IvVkTf3-OGaIjtcDn2J3HLq1wMKAtdxwsbMtjhLYP70PPBe_FBNpcWhE7p-tOVhH1i0N0wwFqwlRQy-3Z4oiLeSsy0Npl29cfP7teO5UTrbrqfmHB0j2u_nEqA4n4iDF5QhPr_9yzxSR4Pg56z4PgFEE" + }`) + + testKeyRSA_2048_2 = mustLoadJWK(`{ + "kty": "RSA", + "kid": "7988e9dea9647281b04d094e4dc5737eaf739a412a570aa4d6deed7ee5640c30", + "n": "wWOFgHHiJ54ZjQEKxvfqYDuhYymbfvYxvreroqx7E-cAuU4QBsOvKV3HNnH_vDRQE1AlqihNWmPFLptp7wxdMrLEtDnRFqTxyTXJ7wLCaaaB6Wwx1dhpr9QEG5-8rxLGFYv2w5i0-o76JPHG0tuqf0YNmHp9oWcv524XnDBuji4-u6km1KynT1EQKHYgy57JWgUpukgJGImBEvf0hC2Y5s5mHvVby3xAD9-cFa2o9Vj3G-SBYFsrRIVR8GcVNwo88oj8F7-2nGD_wOOu_qT_5I3vfToUYGj5-2rAK1ja0bZpXFibrkPrW4w9paG-3F7I0sPeL3e_BDC_rQ8TgL03uQ", + "e": "AQAB" + }`) + testKeyRSA_2048_2_Priv = mustLoadJWK(`{ + "kty": "RSA", + "kid": "7988e9dea9647281b04d094e4dc5737eaf739a412a570aa4d6deed7ee5640c30", + "n": "wWOFgHHiJ54ZjQEKxvfqYDuhYymbfvYxvreroqx7E-cAuU4QBsOvKV3HNnH_vDRQE1AlqihNWmPFLptp7wxdMrLEtDnRFqTxyTXJ7wLCaaaB6Wwx1dhpr9QEG5-8rxLGFYv2w5i0-o76JPHG0tuqf0YNmHp9oWcv524XnDBuji4-u6km1KynT1EQKHYgy57JWgUpukgJGImBEvf0hC2Y5s5mHvVby3xAD9-cFa2o9Vj3G-SBYFsrRIVR8GcVNwo88oj8F7-2nGD_wOOu_qT_5I3vfToUYGj5-2rAK1ja0bZpXFibrkPrW4w9paG-3F7I0sPeL3e_BDC_rQ8TgL03uQ", + "e": "AQAB", + "d": "RPC4j-CJUbw_uY-Miv-oMuQvFU2o3Crh8u5BJn28ZozsKiMU_YRW9jUzJkqfczVm8muY8b7qTHXSvlmy-v_6XW9zRhhyXFMyypr9QNJIAifUmiTy4xwCGSdIy5w3RGY57UZ3EqVmpwe_TtpOGa8rabHMePX5wUcqwaLykcCGOPLOFKfPF93GqZYi2StS1IaiijLi2IAMhxQGqS6Ct3dA7yacQwegPiDzjb4Is3V9UQ9k_aS3I1w56tz5qspVmfDEuuWToc-2Qyk-eMKWl1tTDJuGTCiS5IFtMPerRpPq9GCpycV0csIW-6AnW79b038DxWjDAUsNnDPnX0y1dQ1G3Q", + "p": "yh2KH_JqU29L2SLG5-ul1EBbaSbt3F5UUZbCvm_MIsiH1TzafVyBd8FeG1d-pMR8bgGjaDSA-DNkOEDAeAlLx-UCTl2Uk2ySoMnZr5JjVLW2DTuF7e8ySKrKQSSLe63aQyKuIZh9P1RNrwrmFwvJgXdzudVjirmdTwxAN0lijE8", + "q": "9PJitAE_kk6SoKvJisXbDb7Xursj26xiYIdWuzlzQ1CbwYcJ1oORDpo0bEpy0BfIbZDiJJ79Xrh5Lo1BVysQ-IZJCXK7mavqiSa8g3B8rmqLXmAtjDbwLGsJOayCpYnsGgOLT7XIUYXCFH7C-tSSydhJ1j8JAbsJveMiBPKnUXc" + }`) + + testKeyRSA_2048_3 = mustLoadJWK(`{ + "kty": "RSA", + "kid": "d214aea9b78d15dd2667f5d19c36df6647fa50023140fd4f5866d285718b897c", + "n": "qFnqWpBOZ_5jnIr_XkXnonmHII5gKzxPhUBfvWBhGN2eH8nmnGh6aUnKyKuCLP_qfYLU7cf9bah-e00451iGite8Tg9ZMYAPFX4NM5j0rGWNv9Z6lSn1xXezJv-FU9VUwXm0DG5eVcB8OV99JWxHivQUSBzY0Q3DUlgrD7FhMd7BrrcmTUO07KnW6SxN3oTbT7fNMxfJSTRxmvU-t-6sLhYP4Wbg39zEUgI8M7b7tvHp34klSqOn2DibVBhvWGF1-IgNT7ng6pS5iAZN7Cz7NjPc9ZM_IWyngPKZV49Tf-ikX2O8uiCb4ccgRur6znwkE7MPrDTXSMHALn10sUcO_w", + "e": "AQAB" + }`) + testKeyRSA_2048_3_Priv = mustLoadJWK(`{ + "kty": "RSA", + "kid": "d214aea9b78d15dd2667f5d19c36df6647fa50023140fd4f5866d285718b897c", + "n": "qFnqWpBOZ_5jnIr_XkXnonmHII5gKzxPhUBfvWBhGN2eH8nmnGh6aUnKyKuCLP_qfYLU7cf9bah-e00451iGite8Tg9ZMYAPFX4NM5j0rGWNv9Z6lSn1xXezJv-FU9VUwXm0DG5eVcB8OV99JWxHivQUSBzY0Q3DUlgrD7FhMd7BrrcmTUO07KnW6SxN3oTbT7fNMxfJSTRxmvU-t-6sLhYP4Wbg39zEUgI8M7b7tvHp34klSqOn2DibVBhvWGF1-IgNT7ng6pS5iAZN7Cz7NjPc9ZM_IWyngPKZV49Tf-ikX2O8uiCb4ccgRur6znwkE7MPrDTXSMHALn10sUcO_w", + "e": "AQAB", + "d": "cCIT2uarktD6gFaE6cIeGzZfLuwmWiX9wX-zRWxgwDM9E2dj12IvxtmD3E2Ak4CSK69tLEQ9JUFJnc89y7pHQ0uW_VdzzWjCo0omeOu0bO_njpPJanlcXn7wMVWY9NHvdj8eEfmhk_R1ybE0piyNKpyQtcehEv3bz4kyhW1ck936zafn5GWxCEWKIF-7OcotxtOl6z1GrJ7WqglMk8ooLucnAXzaPtcvD6seUhVH0vG60yI2AEInI_jMHR-mfDxrebG2xPPJLsqH3GChqTsxG5vjuaq71sNBiY_TbaUDbmWZxV66zdjhN93Rw-lc3vCPwG5vmuA2igWH7SKsHmQOAQ", + "p": "xvTQi8nCJGyZQm3fr7HU23FkSUYkKFUN1FRDWqjyz7kFj5rJxq5dSsNrrgVBvIBNY4TwDDcYia4rvx7KSRRwfV_rsU2-TCOxBebqVlbeV0HI7xMcVZXsv61Eugz-dznUU4wqmP_rCQ8vyNcVvCBbBLBW92n-IRQXsfuSUfuHnT8", + "q": "2J64YbgvH62PB4h-0iYuaykgUzWVSrXENg3_GaF8877AMTe98gjcH7HzTDW1fOhIcNZtmYTwlA2SgFudLsIJLoOn0-kmHzZjZJTWcsZsfTVwKhHD8Lzic7QkrrlbTIZlVu4mfT2f0kvaQiZp6zDCke3SLvVjlD-ebqTFCDIlXkE" + }`) + + testKeyRSA_4096_0 = mustLoadJWK(`{ + "kty": "RSA", + "kid": "4941c1e95df518587e54ddde3510c084fedf92098473537fb3fe022eaece9b42", + "n": "ypJYaH9_PLdEgGN3WFPL1V9qhC2BRXr24f1kGocaQe7wORFlZGX6gRpOCQ1lLmziLafERoSqZuBWF4vSGLeanCRQ7UKObYalMRR_rTJk6D_VD95LNOmtR4DrucdZvjQrAuLXhM5dNLGVoSq_zQowLDMeLWDUBB_TK4WlofoxI0RVoUOt4UXnSev8M_6yIYASpOAk8gG4FAqRfmU-3JhgkZOQCD0BhKYZVw_kEhRBrS3aik-1pQkan9hYIxWwc4KKtrLHNls7--vk5xDk2Vod5sZYRLlimbqHavN2IBeAeJGJqt1grXOVlnagKqmyq2MoPKufwptJBiVrVsTplyUB9FZe9FxUfxrkkxYJHufwP-3wXhaXpPFdL86TCUuOz-jTfng1rsTpwzmwm3RqFzz01yMc1RhaeWininQqj42bhW4bgFGVRP2QqSFbCbRZ4WMW3qWr3aR2QpJ7b8ac7JUhAJoh7-UtMmyGx9UJP0gjA8Wm-O81UENoDjMMdNUaVTfpSUKB7xZppg244jNZDIm1ptq4QyvvWkr0brp9Ymxpu0e3g2HyywyPjbeyNrIelb4E2tU5vCR4Fs_zGFG71AieruJGfzPTXE7ZaiLhP8n1652NCzIH76Y7OGz4ap2D55-RKpUkSI3KqWxUKxkM0tuZ7LS2F-1wEVs8P01K6RuQpxE", + "e": "AQAB" + }`) + testKeyRSA_4096_0_Priv = mustLoadJWK(`{ + "kty": "RSA", + "kid": "4941c1e95df518587e54ddde3510c084fedf92098473537fb3fe022eaece9b42", + "n": "ypJYaH9_PLdEgGN3WFPL1V9qhC2BRXr24f1kGocaQe7wORFlZGX6gRpOCQ1lLmziLafERoSqZuBWF4vSGLeanCRQ7UKObYalMRR_rTJk6D_VD95LNOmtR4DrucdZvjQrAuLXhM5dNLGVoSq_zQowLDMeLWDUBB_TK4WlofoxI0RVoUOt4UXnSev8M_6yIYASpOAk8gG4FAqRfmU-3JhgkZOQCD0BhKYZVw_kEhRBrS3aik-1pQkan9hYIxWwc4KKtrLHNls7--vk5xDk2Vod5sZYRLlimbqHavN2IBeAeJGJqt1grXOVlnagKqmyq2MoPKufwptJBiVrVsTplyUB9FZe9FxUfxrkkxYJHufwP-3wXhaXpPFdL86TCUuOz-jTfng1rsTpwzmwm3RqFzz01yMc1RhaeWininQqj42bhW4bgFGVRP2QqSFbCbRZ4WMW3qWr3aR2QpJ7b8ac7JUhAJoh7-UtMmyGx9UJP0gjA8Wm-O81UENoDjMMdNUaVTfpSUKB7xZppg244jNZDIm1ptq4QyvvWkr0brp9Ymxpu0e3g2HyywyPjbeyNrIelb4E2tU5vCR4Fs_zGFG71AieruJGfzPTXE7ZaiLhP8n1652NCzIH76Y7OGz4ap2D55-RKpUkSI3KqWxUKxkM0tuZ7LS2F-1wEVs8P01K6RuQpxE", + "e": "AQAB", + "d": "VQagPRxm16FFC260hUqG4ASwvNIs1HEMd0bYYZobl1knU4zNthpnzxCveHU65wWk2ez1IXRF4fB_slpp0R4fszI7FZs-FRLS-4rTHGtul11TnNl9T7RVmxGt38ihDojvFMMKGyBTVu7DE2bSIsoH9kVugTWHSEPjav0pzJcrUNY56vpxXYDt18VJkrlxI0aSjMnYOAwoq6DT-O2eORFsVy5M4mhY3sipEjYFUOFXv8zjUfKrF55-omE4fWF5MsK0XoMjwtkAkHkvFx2sMN72dgsCubXmgQgeFvIhvs6eifzsf99z2NoPC5y3FbEs4Ws5VF3lLNXpDL9gEoeMVHigHKNoztIzbJnFLjzEag4wOBqbig4nWIlYEcLwd5-E0oQ8GHAhWtJn5AJQKOwTcasRWiUnRAulCcI8f9R2ATTSjAGU-juFUfhProp4w3ZlnUbO6U3tbh90KdFSJa-Bapts6ijPgEp6MHubVCHIxh1KGmod_CuGGjze1GuISEwI_4Yh0SrFlp4Wy6ecg7mKNnXvkBMd6h90LJaTzubnts33cCs-5UzCtYqmWHwXrMA6dRJuicY6su3NXxStuZn2bxU_Dn8LrS1vx_lhDU__NqMNbYj5ZvHkPvZiJNBI6Z1R5SY3pQzpH6vh9W2KRKF8cKOkushIaHNnHo7W5o4eZ9HjJFE", + "p": "7Bxd2wTrXfeSZAEGZEnfoJaMh6hKFG-fx0wLmF9U-Myq1fCIQ-gdl_Atqy0xQxYtQl2lOPUWfySegJSxpdRF8pwvLq9Yt7xFQDrV0B8d5-FjYJ_Mk2bbyHv_PhNmnfcYHqE_VdADeSaU-MRV1iknvjdbxSxYUF-U2C6llfJhn-ZbGPmZJyz8fTWHSh8aKLPJkuYV84wcNL04hpFPJq7Bhy7HAFhRYNpxXyDO3I36DCU94uvUCY758VKLl8IYMMSqjh-thluKJSkWibo85ZOJR38XFksMr70pH-DkT0WMv3PpLtNu6ac6Ry4CEkZexSWmbVk-pduRooo0On1CMsrsLw", + "q": "26K4gFJ0Ygqr4KaJ4QcRGN29XcjDpX5XTlHy3hCs_wEWc5wedydVgIH4tadiZcu0WinTtl0I6FYewWNpI8nFh8XJXsLOLepdKKR798zW8kEhzxdYdP1MTtGfy12KUCMeS2RQRsBJlmNdgrI0_m0JvW2LlGeK0EqvrqeS06L8bKGXkfY2aFGHl6ipSfUd5OpuSUqgfrj4-HgB8zywm-yAvn7UK-ySyQrP3K4PuOiY5QZmcPDxl8yqDAjorStrjPieYrlMqREHMsyq2FtAXhx4FzpKN7OljbE3EpgyFKkFN-wDTWRGxu3au0hQJx0-XthZ9i5r8zGcEbArQ5jZH7CQvw" + }`) + + testKeyRSA_4096_1 = mustLoadJWK(`{ + "kty": "RSA", + "kid": "d01929d205e83facdc5080a0a4ade80853978298a5c73780e863c035d32127c9", + "n": "ve_rSTi1vwfV2Oj7YNu1kv8Xz2ImAEqf7qo2RehTYW4FJpbj0wkMBVGmAulm4dz-1knnaPdpbrrIwkEYr1XR6kSIeB8aXkCZqDGZATxLSRqGEzu2J8Xt9z_qTQLWYD-NuMEf9H3B1CiP3Q2RnoWUlGvOr_KI5h6anzeqgZxlQLgqQujXscwqWpX7MVh3sheZ5SbyTkDcFWvQS_NEPaBGso-Au9X-yhZsHU1Ky02Nd_DPOrrRzl7uymE07hy3bIbxmrh4ZeEz6P2ixsHHYbd15GNMRlr1cWbg91RBB-akSxX0VYoLjjuqwo33UHk1hBbSATbobfpOKRruuZPZ_xOiPi-m0tUdD1Pj-h6xRcA5sZ1d55IdL8IY_9kgLc_WyU2RWFTXV6zA2SDDdE6EdEB_8q6U0oLU5T-YRxd-V2qOTqW1388qUaL5BalSvqM0g8kVfBYIM3uwLqQI_hCer4CL7_0DGlUtheye3qcoyqWVJd9iqfcWC-1S8NljAJwM9sehx6kOSM85UuUTH89VM35oAVp0rSPcLy20dPzEVQ0LAcy_iRHTkqy9nZ1z6mo-IWQE2ZyPCuESEfG7nFJ3YlUfwBJ5uZnk0N1FVYX5zgEKdkP322vShrv1blB_JtUedpbCcuS-qCaywpwjL0pzTDKpJMEqHds0-DFT7AbULcujw_U", + "e": "AQAB" + }`) + testKeyRSA_4096_1_Priv = mustLoadJWK(`{ + "kty": "RSA", + "kid": "d01929d205e83facdc5080a0a4ade80853978298a5c73780e863c035d32127c9", + "n": "ve_rSTi1vwfV2Oj7YNu1kv8Xz2ImAEqf7qo2RehTYW4FJpbj0wkMBVGmAulm4dz-1knnaPdpbrrIwkEYr1XR6kSIeB8aXkCZqDGZATxLSRqGEzu2J8Xt9z_qTQLWYD-NuMEf9H3B1CiP3Q2RnoWUlGvOr_KI5h6anzeqgZxlQLgqQujXscwqWpX7MVh3sheZ5SbyTkDcFWvQS_NEPaBGso-Au9X-yhZsHU1Ky02Nd_DPOrrRzl7uymE07hy3bIbxmrh4ZeEz6P2ixsHHYbd15GNMRlr1cWbg91RBB-akSxX0VYoLjjuqwo33UHk1hBbSATbobfpOKRruuZPZ_xOiPi-m0tUdD1Pj-h6xRcA5sZ1d55IdL8IY_9kgLc_WyU2RWFTXV6zA2SDDdE6EdEB_8q6U0oLU5T-YRxd-V2qOTqW1388qUaL5BalSvqM0g8kVfBYIM3uwLqQI_hCer4CL7_0DGlUtheye3qcoyqWVJd9iqfcWC-1S8NljAJwM9sehx6kOSM85UuUTH89VM35oAVp0rSPcLy20dPzEVQ0LAcy_iRHTkqy9nZ1z6mo-IWQE2ZyPCuESEfG7nFJ3YlUfwBJ5uZnk0N1FVYX5zgEKdkP322vShrv1blB_JtUedpbCcuS-qCaywpwjL0pzTDKpJMEqHds0-DFT7AbULcujw_U", + "e": "AQAB", + "d": "mCjZ3wDVaMJIKMsMhx28KpS9aGACfX1K_pHRhNOH6KeQ7Mc4oFnBDYnJas-8ofi_FsCB6G88QX7VUfmAYwZncjuQ8FpKb3NlJX8GSh0ZWukqu8G8PcSszMShWSyKvPRs_rOIe_87BlGwXrB-FfaBfx2WqRGtZlziFecsa0T1QJHJGW0bTs52p7c7Ut7ClSOfIBrBRrtjFK4YYp_x7US3HlkkElZvFUo9NoQzBQeN66Y4_Z2ocqFOv0Z8drz-nKzGZOKfYU62nVKD0qJurfOhOGPsOPipZD28v6b5qfC1cYmXAefjNgDK3a2JkShpHPaDKoHoViKN9xQiZvzxSQ1bjQCgB6xQWk61YwmiB44gFLshXvXkx_wcccO2XC4m-Pk0r-Uj9Ugvgizk8HYp1H9eqnp8A6sqU44mrmdt14vM68SGzEqYpLoUkcxPttHvNoORAzgQfuK3SOIFyuwFqKTMBupR4A42XvxLj_PuKEKFekS2JEK8fON2t_LuUDVemjMxwXD0gjljHSKD9HnguihI5IEd6Kgr-HlTwiiDQfzcmWQdoFUs_Je4tVCSbGRrJYju01gLouDu7gzN14pZcEyoBLPkj-_MDiiymvlBLOBU2s5nQRZCaC_0IKgHLaT_rzQaXwmVhU-OURdwdoViMVc_cdXtle4TqU1g41nNE_0tiwE", + "p": "2h_9HZCOpf3S9qpAkNS2ncMsRjBlWCpRltoCs-XBF_IgOJlCkJ-42k_V1zaf6YrgMy00XD4Ij5MMiUOharvc3w_WKJF-AwGu9JEpi38Wzj1nkY86sdB1ZPu4ihSRZm81jIjuLZ2siILqkupXaY0S_0CeNLKPoVpvfPWCb3psCWuLVRDhYECPCx9FYg2tzisbFpVAPPqNCFswMYNvhyJ2NQ0hzueKDc1uHBUanCSG6gIbeY1bQvYYFGySYkWZcQg98RSeDbLysHalCSQCgPjss3likTT9OjWLv23vpXURFhgywv6Phx4He0yWB8ZRrnskfW0S76kZ325SPUlnOhf31Q", + "q": "3urwELOa7TDpwLMeMEOf2aspz1gkMjoN-V4SMzRGxIoPy93-bQ62Z8nPduma1gK0zHOIs0gU8dDqjy8P8Z7vudJqRFJIrzjD-RnDqQDz7KM_6x0kbEN4GLQiNGVAckRhoEmYjDXXkG3teD2ST5NqlYwxGvFmCvw5RCJZG7eNjXY3KnJQrDyx61NIFEvSm3sBlJkOVhSanuqHBL6T-efaBkUcQ7mY7_iYtCt7cjMG8qQtF0KovVsMF6BUc9KDobpUvDsvPrSTAzsYJvVNkzGJtF8Strr-61VA6qZnRcDW3ukYvmJp_SdAcv3KzSkWwEmJEaAtaiOh2jWkRG9QpV_LoQ" + }`) + + testKeyRSA_4096_2 = mustLoadJWK(`{ + "kty": "RSA", + "kid": "822512e8098b9fa56c9152a56d2607bda912c89f6f4fe1b6ccf41627bf06de29", + "n": "3bcg0diA0TPvK283P02o5UT8cBf5q48uUsfuCkIbLqNFtskGuoft8OJLmik7c7pLNSK3Jad_-xRlfsrcV1fDBs8EJDFnoY-4r_xk_-GQl-OsHzuz7HMvjVYUSYeVZMtzPAfCIFJxSIaiIW9MEwCwYyrtRefedfKUV7Ax6hjOUXtyRZ5DRHj2FnNl6MjE8mTSJ8OQI9ETErg0wF448fJb6hYCidVHfeBSCOceljXMg8a9iDYHVnzsUb9ETl7xlRZDvUpIoG0jU-aC3oZdM0YgXspvw4FcXg1-ad-TbgRjEm_iJ-4KUhAYso6-Y6eimDT_TMhbKBLGE86pUjRPIXVNl6odjkXmcmh7bz9v0-gIfHB9zVbrqE8i8iIwkux-Uhq_VMiIFikjtjmDo5W-7qA_cjvq1dA1QqOJ54ijbdvglUtneuA5CnwfGHAvf94lEPebgP5eCMZjz_Dtl0FTX0u5lOVBs4qeyEfV1XANOq_h1gHmwJDlspGZVuPfSk4-YMCeBy5Ua_gNDqOIQo3EQbmjyN5yD9byFh3WDSQJN1kKT3BZSou-Q8-KT2lu9KT_CpTfVMaNXnPpXJ_H0S9Q7sbUzA6dF1h0Q_mu_bOlWTRlH3vXfZ3_3Vikjk-jhQdaRv8yjTdhX33wAVHk7omKlo76G3QJdnvjysJ6aagFPZ3qhmc", + "e": "AQAB" + }`) + testKeyRSA_4096_2_Priv = mustLoadJWK(`{ + "kty": "RSA", + "kid": "822512e8098b9fa56c9152a56d2607bda912c89f6f4fe1b6ccf41627bf06de29", + "n": "3bcg0diA0TPvK283P02o5UT8cBf5q48uUsfuCkIbLqNFtskGuoft8OJLmik7c7pLNSK3Jad_-xRlfsrcV1fDBs8EJDFnoY-4r_xk_-GQl-OsHzuz7HMvjVYUSYeVZMtzPAfCIFJxSIaiIW9MEwCwYyrtRefedfKUV7Ax6hjOUXtyRZ5DRHj2FnNl6MjE8mTSJ8OQI9ETErg0wF448fJb6hYCidVHfeBSCOceljXMg8a9iDYHVnzsUb9ETl7xlRZDvUpIoG0jU-aC3oZdM0YgXspvw4FcXg1-ad-TbgRjEm_iJ-4KUhAYso6-Y6eimDT_TMhbKBLGE86pUjRPIXVNl6odjkXmcmh7bz9v0-gIfHB9zVbrqE8i8iIwkux-Uhq_VMiIFikjtjmDo5W-7qA_cjvq1dA1QqOJ54ijbdvglUtneuA5CnwfGHAvf94lEPebgP5eCMZjz_Dtl0FTX0u5lOVBs4qeyEfV1XANOq_h1gHmwJDlspGZVuPfSk4-YMCeBy5Ua_gNDqOIQo3EQbmjyN5yD9byFh3WDSQJN1kKT3BZSou-Q8-KT2lu9KT_CpTfVMaNXnPpXJ_H0S9Q7sbUzA6dF1h0Q_mu_bOlWTRlH3vXfZ3_3Vikjk-jhQdaRv8yjTdhX33wAVHk7omKlo76G3QJdnvjysJ6aagFPZ3qhmc", + "e": "AQAB", + "d": "wcO4mAxJUAu-OsxgkR9SusPWhjQ9y5Q_XLNDso1hahng5ES9b6k55mouvlTIk3Q9I_vp6auAKrMBnJS3ilG1rK6hJOxUcBrFwm-m6QV9s3CSzV0E-mEULsYKxtQKWOOBGvaAznSeck7PRL8a0gSpIpGyeYSRo6zTverLRJZXQVjMXlFY4m-ASdCiQJWtoVVBYOUFhHfE3nhECdaOl8xCTcrcfw75AuZXa1ZpIcd0q7m1jGQDd6-HbE3m6UMKiEvD-ZsA68tVs45h0w3ER_pCcfUjRc45Ji1OzEJLezu0RbmoAVOEi4FrxCkB9N_dNn4inD0BhX0axNa4nZH_kfMNUh06081FaBiBq5bNBPQF7lWIYxAhtd2VgsUTZtNxMfHDTllt5fvbCogYngU5-Wj3UM33R_es_aakt_CFndJPmyenP0J7KEBmf4qnlrbxun-jQNV_cbEzgQA_Pt5RIhB5jgofYzT72Ib-lViZUolXpxg8mFQ5BPghqriuaX8eSc85y5rbu-nEvheDkIC7xjTtDBShXMvKKokx6t4d-x_W4sWDuLKsVNn3Veg6mbbl3O74Fr-joEM9unMg-9KCapAuBKmMv4GejOVOIj43i0lm8dsuMslNKsThWpjApE50-VBuySi1f0jD2HNEzjMTt5ZrlNi9bRFzAht46kdc_g4TWLE", + "p": "7cl5QebQldiOrfe2Z7E2aP4d1GIuX4b3u-kXUDa73HI4S2u9TooJF8lfsbrtzJIG60iIzio9Pej8R4ss2_1TPC_F7gQS67ST5yY2zuwUH6jwpt8KtVO3NKd9SNfVJse_mZLp7eAeH8CMq4bqkDig8ZI_Sp1LQtWXvhvhju40i0h1ef-MD36wbc26BE1L6tQMAoJIU-pMDawmWMGG3Q5_LxKHb1cxvV8gVfFgsJbAHMPXxcmpCdp1FqeqpvRs3bch3AMxHwjy1SFRK6lhvliS2ZvD7fdupA09PGJ-cNO3S9iHdCvsnyOHg9pMbxxcYW8Io_t1ihk6JYFvZlDIYvukXw", + "q": "7rKFf6190MlunvlLSdMvN_E2Xuow35_EThaxQ9e_7FS32LKPy847vFreXzVivVW1IVXrRSDgU8VIEyBQzftliLSriefKYdx2jtd_A84baha4p6CAK9SwfZcVEcIuU2Ul8XcUpbg53_bxUWDv4mYewZW_AqaZ0ZR1Ug14gCGHELvjs7MmWkCkJB6lEenRQZvzwcmA8tWzZ6Dlqb_RyLtxcVPbeSuc3t6YxbB1-pquwXoQLB9-XvgR_4L6vuGZrp1D-C0lzboUPoftkC_hKYb_xY6LPJTsL8LUypATcTrAnD0TBrqj5-Oy_4dMin6OcsXrfdxflxaJqkh7B-kz20oa-Q" + }`) + + testKeyRSA_4096_3 = mustLoadJWK(`{ + "kty": "RSA", + "kid": "ad60df152305985e6bdd8cb1d1f496f766aed097b7464b8b821a09b27f054dcb", + "n": "q8mHub9-A7LH2ykklO52CRQ9KYeM9FlHaoLJCMokmwdb8FF8cAMOPDQ4lx7rB107vwX5oHS6T4uhOUH6ppgZnvB083-f5cBfvZPSwvKg1csOPI3Llp-hIO3vnu7v4M-dXRJevmNdTAoHINXP8mJ1KfGL6BuxckxLEOoIX0suSmyudAE_lc6ioMAfwehkH7qTWwllIuReGwxHHYapM0AFIYysJ0z96l-WNkO3ssLm6Q7txpumM_b0d58OkdezBLujEfn0t5H8hsml_LUSE4TfWcGwm3oDBB2q5tsI2_QmVwdV6BcP43Sm8R7EbJvTQq3YXKLlC-Et4-3CDGaMV6z1eFmHBs_FMGzto-kg9MUFockOe0vkfjHj6AafNw7AFyBJRvV3EdMM-O1ysiC5cs45aowbfQIz_9H9RVbR9iMiEjq6nM34bhgfu-FvOmAcliCOJnDlBy2wWe2XyXTLQVkmn1C_w2mitcQAkP-LiktypUwSIbyDqfBai1_imO1SIVzomRLvzSju5qGrcm0ROV6o9zKXCVc546g9la9FUTTPZsI0_s3hcVRVMaF3fa122hE848serjCqNJ0Nb3CYOlVCc6JrkUdeuCj0Y-on6V6aeCFAQxFb1z_p88STsNJkrcNZp87f1JUaSB4POF01dLNQ2HTe7xJCMRmK-fpNuDCOzNM", + "e": "AQAB" + }`) + testKeyRSA_4096_3_Priv = mustLoadJWK(`{ + "kty": "RSA", + "kid": "ad60df152305985e6bdd8cb1d1f496f766aed097b7464b8b821a09b27f054dcb", + "n": "q8mHub9-A7LH2ykklO52CRQ9KYeM9FlHaoLJCMokmwdb8FF8cAMOPDQ4lx7rB107vwX5oHS6T4uhOUH6ppgZnvB083-f5cBfvZPSwvKg1csOPI3Llp-hIO3vnu7v4M-dXRJevmNdTAoHINXP8mJ1KfGL6BuxckxLEOoIX0suSmyudAE_lc6ioMAfwehkH7qTWwllIuReGwxHHYapM0AFIYysJ0z96l-WNkO3ssLm6Q7txpumM_b0d58OkdezBLujEfn0t5H8hsml_LUSE4TfWcGwm3oDBB2q5tsI2_QmVwdV6BcP43Sm8R7EbJvTQq3YXKLlC-Et4-3CDGaMV6z1eFmHBs_FMGzto-kg9MUFockOe0vkfjHj6AafNw7AFyBJRvV3EdMM-O1ysiC5cs45aowbfQIz_9H9RVbR9iMiEjq6nM34bhgfu-FvOmAcliCOJnDlBy2wWe2XyXTLQVkmn1C_w2mitcQAkP-LiktypUwSIbyDqfBai1_imO1SIVzomRLvzSju5qGrcm0ROV6o9zKXCVc546g9la9FUTTPZsI0_s3hcVRVMaF3fa122hE848serjCqNJ0Nb3CYOlVCc6JrkUdeuCj0Y-on6V6aeCFAQxFb1z_p88STsNJkrcNZp87f1JUaSB4POF01dLNQ2HTe7xJCMRmK-fpNuDCOzNM", + "e": "AQAB", + "d": "QFcw8I8aQYRaemlEfEt8BhaAeed9EZ_GscveQ96CK1ZsRuweMU3TrRTaBS_dU1rGH9u7DS_rABQKBIoDuRXKss7Y3sJ0Pvb4ZObSz5VUS_7LjD6HfBi5nr2_O8W-LnNUOyHAPoq0zOAMn221ftEFlPoVLpAAvBB7JRCiph5gbhuak3RMPm2wV4jd3CCQL5oPys8QBCuIW5UTpalkAf_-a_xmFiouB_RZLGXcjaWWGsAuqm5tp5TdJ1h5eoJRWHp2ryrxTzfsXwdzldyzsn_Xr6Rt4y2lp4r9EY4EGW2uVnY25MCOgOCWDkU5yHvselLmcHvKUdK6_11zinV2Jvhuz1FaW10P6lRXxhjuuT4bT7XmY6WkJCUvWf8w_NYrGwDbRvQa6YZcZZWKZ1l2Enkgd_P4qwtrnROQSCe0dJdNG3lSq90lrAwuvb8AhKhpQ8nJSaSQc_h2pa4NJZ-R__9m0_7CRUuEEg9k47AtgdIe09KLSpcACc3W5cBXEw2pL8ihqcf9AVgM0TFQQVrUdIst3YjUiB6r2zLVCRx8KjtT8Pmz6YtMQwDIFTrUopwwai5PEP3QEdxdNF39W1iwkqjA8uw4IlXgZAr4-9s3x617XUL0BQ4TgMpTsEghpV1U8U2HQfYGUKBcAlSqIlAi0MU4QRwcsAJrcoTIi-e34NFMbHE", + "p": "xvZ1BKzaRfrmQT2mmbK1MzKdr4amHrx4EJ_4fRsCRBj8MqB92y4Bp0iuO8rZ3iK_yOOgQQTcr4UP5UEACLOqGDPNa92UUpHu1U5XR3MiIY0kGcdovpkkGU8fq78VRpyofc1b9kvZsy4eL0e7W7jApjGu479D_evnpT7lqSOGRl1Z0QHdI3ctKmKw48-AmQWL4BibXTyaXOfRgTHm4AtbrvFshwCZ43QgzrCrwbhZlOfiIIW9wXa_OqqOhV11BLF72qTglecfTgUOxY0W5GWgbwXCVZ8Lwr6cxYKGrKjmny9UNOIjABGsJjZm5egwDNUhhoaEeXXyzXbCKynvUCDxZw", + "q": "3Qi1bRzj3TYW3Iw9KSDCuuHWBJRO_3Ke-JLB7PHEXc1hsPwF_XDqXPaBbIUtgKaqNzUehYAQbtHHhJVGqZadTKjsNkO4sO_r5nRoxUpFuGgUEmKNN0QeJa_llHzAZ9JpvUrtoyzVZX-2Em_3aAhtV5pZ6t22YToPDjZIBgqL96MQRCyKsv2FS_dbpoYKdXlG6phzkzKPn3oaWkh-VwmgAsFg7uXdiquQEsXYcOq3-77Umiuke6SBbaHLryLflTzOV7YvY7Kw3s3NycS9MDBESKYXqqX3YKvS25ZFFjYbrVOx5AluLiwajZu2biIPZb9rNbSXE77Hll3tAbmA5syJtQ" + }`) +) diff --git a/vendor/github.com/coreos/go-oidc/jwks.go b/vendor/github.com/coreos/go-oidc/jwks.go new file mode 100644 index 00000000..4ec6c3f1 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/jwks.go @@ -0,0 +1,199 @@ +package oidc + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "sync" + "time" + + "github.com/pquerna/cachecontrol" + "golang.org/x/net/context" + "golang.org/x/net/context/ctxhttp" + jose "gopkg.in/square/go-jose.v2" +) + +// keysExpiryDelta is the allowed clock skew between a client and the OpenID Connect +// server. +// +// When keys expire, they are valid for this amount of time after. +// +// If the keys have not expired, and an ID Token claims it was signed by a key not in +// the cache, if and only if the keys expire in this amount of time, the keys will be +// updated. +const keysExpiryDelta = 30 * time.Second + +func newRemoteKeySet(ctx context.Context, jwksURL string, now func() time.Time) *remoteKeySet { + if now == nil { + now = time.Now + } + return &remoteKeySet{jwksURL: jwksURL, ctx: ctx, now: now} +} + +type remoteKeySet struct { + jwksURL string + ctx context.Context + now func() time.Time + + // guard all other fields + mu sync.Mutex + + // inflightCtx is the context of the current HTTP request to update the keys. + // Its Err() method returns any errors encountered during that attempt. + // + // If nil, there is no inflight request. + inflightCtx context.Context + + // A set of cached keys and their expiry. + cachedKeys []jose.JSONWebKey + expiry time.Time +} + +// errContext is a context with a customizable Err() return value. +type errContext struct { + context.Context + + cf context.CancelFunc + err error +} + +func newErrContext(parent context.Context) *errContext { + ctx, cancel := context.WithCancel(parent) + return &errContext{ctx, cancel, nil} +} + +func (e errContext) Err() error { + return e.err +} + +// cancel cancels the errContext causing listeners on Done() to return. +func (e errContext) cancel(err error) { + e.err = err + e.cf() +} + +func (r *remoteKeySet) keysWithIDFromCache(keyIDs []string) ([]jose.JSONWebKey, bool) { + r.mu.Lock() + keys, expiry := r.cachedKeys, r.expiry + r.mu.Unlock() + + // Have the keys expired? + if expiry.Add(keysExpiryDelta).Before(r.now()) { + return nil, false + } + + var signingKeys []jose.JSONWebKey + for _, key := range keys { + if contains(keyIDs, key.KeyID) { + signingKeys = append(signingKeys, key) + } + } + + if len(signingKeys) == 0 { + // Are the keys about to expire? + if r.now().Add(keysExpiryDelta).After(expiry) { + return nil, false + } + } + + return signingKeys, true +} +func (r *remoteKeySet) keysWithID(ctx context.Context, keyIDs []string) ([]jose.JSONWebKey, error) { + keys, ok := r.keysWithIDFromCache(keyIDs) + if ok { + return keys, nil + } + + var inflightCtx context.Context + func() { + r.mu.Lock() + defer r.mu.Unlock() + + // If there's not a current inflight request, create one. + if r.inflightCtx == nil { + // Use the remoteKeySet's context instead of the requests context + // because a re-sync is unique to the keys set and will span multiple + // requests. + errCtx := newErrContext(r.ctx) + r.inflightCtx = errCtx + + go func() { + // TODO(ericchiang): Upstream Kubernetes request that we recover every time + // we spawn a goroutine, because panics in a goroutine will bring down the + // entire program. There's no way to recover from another goroutine's panic. + // + // Most users actually want to let the panic propagate and bring down the + // program because it implies some unrecoverable state. + // + // Add a context key to allow the recover behavior. + // + // See: https://github.com/coreos/go-oidc/issues/89 + + // Sync keys and close inflightCtx when that's done. + errCtx.cancel(r.updateKeys(r.inflightCtx)) + + r.mu.Lock() + defer r.mu.Unlock() + r.inflightCtx = nil + }() + } + + inflightCtx = r.inflightCtx + }() + + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-inflightCtx.Done(): + if err := inflightCtx.Err(); err != nil { + return nil, err + } + } + + // Since we've just updated keys, we don't care about the cache miss. + keys, _ = r.keysWithIDFromCache(keyIDs) + return keys, nil +} + +func (r *remoteKeySet) updateKeys(ctx context.Context) error { + req, err := http.NewRequest("GET", r.jwksURL, nil) + if err != nil { + return fmt.Errorf("oidc: can't create request: %v", err) + } + + resp, err := ctxhttp.Do(ctx, clientFromContext(ctx), req) + if err != nil { + return fmt.Errorf("oidc: get keys failed %v", err) + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("oidc: read response body: %v", err) + } + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("oidc: get keys failed: %s %s", resp.Status, body) + } + + var keySet jose.JSONWebKeySet + if err := json.Unmarshal(body, &keySet); err != nil { + return fmt.Errorf("oidc: failed to decode keys: %v %s", err, body) + } + + // If the server doesn't provide cache control headers, assume the + // keys expire immediately. + expiry := r.now() + + _, e, err := cachecontrol.CachableResponse(req, resp, cachecontrol.Options{}) + if err == nil && e.After(expiry) { + expiry = e + } + + r.mu.Lock() + defer r.mu.Unlock() + r.cachedKeys = keySet.Keys + r.expiry = expiry + + return nil +} diff --git a/vendor/github.com/coreos/go-oidc/jwks_test.go b/vendor/github.com/coreos/go-oidc/jwks_test.go new file mode 100644 index 00000000..d9735b2d --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/jwks_test.go @@ -0,0 +1,99 @@ +package oidc + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "reflect" + "testing" + "time" + + "golang.org/x/net/context" + jose "gopkg.in/square/go-jose.v2" +) + +type keyServer struct { + keys jose.JSONWebKeySet +} + +func newKeyServer(keys ...jose.JSONWebKey) keyServer { + return keyServer{ + keys: jose.JSONWebKeySet{Keys: keys}, + } +} + +func (k keyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if err := json.NewEncoder(w).Encode(k.keys); err != nil { + panic(err) + } +} + +func TestKeysFormID(t *testing.T) { + tests := []struct { + name string + keys []jose.JSONWebKey + keyIDs []string + wantKeys []jose.JSONWebKey + }{ + { + name: "single key", + keys: []jose.JSONWebKey{ + testKeyRSA_2048_0, + testKeyECDSA_256_0, + }, + keyIDs: []string{ + testKeyRSA_2048_0.KeyID, + }, + wantKeys: []jose.JSONWebKey{ + testKeyRSA_2048_0, + }, + }, + { + name: "one key id matches", + keys: []jose.JSONWebKey{ + testKeyRSA_2048_0, + testKeyECDSA_256_0, + }, + keyIDs: []string{ + testKeyRSA_2048_0.KeyID, + testKeyRSA_2048_1.KeyID, + }, + wantKeys: []jose.JSONWebKey{ + testKeyRSA_2048_0, + }, + }, + { + name: "no valid keys", + keys: []jose.JSONWebKey{ + testKeyRSA_2048_1, + testKeyECDSA_256_0, + }, + keyIDs: []string{ + testKeyRSA_2048_0.KeyID, + }, + }, + } + + t0 := time.Now() + now := func() time.Time { return t0 } + + for _, test := range tests { + func() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + server := httptest.NewServer(newKeyServer(test.keys...)) + defer server.Close() + + keySet := newRemoteKeySet(ctx, server.URL, now) + gotKeys, err := keySet.keysWithID(ctx, test.keyIDs) + if err != nil { + t.Errorf("%s: %v", test.name, err) + return + } + if !reflect.DeepEqual(gotKeys, test.wantKeys) { + t.Errorf("%s: expected keys=%#v, got=%#v", test.name, test.wantKeys, gotKeys) + } + }() + } +} diff --git a/vendor/github.com/coreos/go-oidc/key/doc.go b/vendor/github.com/coreos/go-oidc/key/doc.go new file mode 100644 index 00000000..936eec74 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/key/doc.go @@ -0,0 +1,2 @@ +// Package key is DEPRECATED. Use github.com/coreos/go-oidc instead. +package key diff --git a/vendor/github.com/coreos/go-oidc/key/key.go b/vendor/github.com/coreos/go-oidc/key/key.go new file mode 100644 index 00000000..208c1fc1 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/key/key.go @@ -0,0 +1,153 @@ +package key + +import ( + "crypto/rand" + "crypto/rsa" + "encoding/hex" + "encoding/json" + "io" + "time" + + "github.com/coreos/go-oidc/jose" +) + +func NewPublicKey(jwk jose.JWK) *PublicKey { + return &PublicKey{jwk: jwk} +} + +type PublicKey struct { + jwk jose.JWK +} + +func (k *PublicKey) MarshalJSON() ([]byte, error) { + return json.Marshal(&k.jwk) +} + +func (k *PublicKey) UnmarshalJSON(data []byte) error { + var jwk jose.JWK + if err := json.Unmarshal(data, &jwk); err != nil { + return err + } + k.jwk = jwk + return nil +} + +func (k *PublicKey) ID() string { + return k.jwk.ID +} + +func (k *PublicKey) Verifier() (jose.Verifier, error) { + return jose.NewVerifierRSA(k.jwk) +} + +type PrivateKey struct { + KeyID string + PrivateKey *rsa.PrivateKey +} + +func (k *PrivateKey) ID() string { + return k.KeyID +} + +func (k *PrivateKey) Signer() jose.Signer { + return jose.NewSignerRSA(k.ID(), *k.PrivateKey) +} + +func (k *PrivateKey) JWK() jose.JWK { + return jose.JWK{ + ID: k.KeyID, + Type: "RSA", + Alg: "RS256", + Use: "sig", + Exponent: k.PrivateKey.PublicKey.E, + Modulus: k.PrivateKey.PublicKey.N, + } +} + +type KeySet interface { + ExpiresAt() time.Time +} + +type PublicKeySet struct { + keys []PublicKey + index map[string]*PublicKey + expiresAt time.Time +} + +func NewPublicKeySet(jwks []jose.JWK, exp time.Time) *PublicKeySet { + keys := make([]PublicKey, len(jwks)) + index := make(map[string]*PublicKey) + for i, jwk := range jwks { + keys[i] = *NewPublicKey(jwk) + index[keys[i].ID()] = &keys[i] + } + return &PublicKeySet{ + keys: keys, + index: index, + expiresAt: exp, + } +} + +func (s *PublicKeySet) ExpiresAt() time.Time { + return s.expiresAt +} + +func (s *PublicKeySet) Keys() []PublicKey { + return s.keys +} + +func (s *PublicKeySet) Key(id string) *PublicKey { + return s.index[id] +} + +type PrivateKeySet struct { + keys []*PrivateKey + ActiveKeyID string + expiresAt time.Time +} + +func NewPrivateKeySet(keys []*PrivateKey, exp time.Time) *PrivateKeySet { + return &PrivateKeySet{ + keys: keys, + ActiveKeyID: keys[0].ID(), + expiresAt: exp.UTC(), + } +} + +func (s *PrivateKeySet) Keys() []*PrivateKey { + return s.keys +} + +func (s *PrivateKeySet) ExpiresAt() time.Time { + return s.expiresAt +} + +func (s *PrivateKeySet) Active() *PrivateKey { + for i, k := range s.keys { + if k.ID() == s.ActiveKeyID { + return s.keys[i] + } + } + + return nil +} + +type GeneratePrivateKeyFunc func() (*PrivateKey, error) + +func GeneratePrivateKey() (*PrivateKey, error) { + pk, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, err + } + keyID := make([]byte, 20) + if _, err := io.ReadFull(rand.Reader, keyID); err != nil { + return nil, err + } + + k := PrivateKey{ + KeyID: hex.EncodeToString(keyID), + PrivateKey: pk, + } + + return &k, nil +} diff --git a/vendor/github.com/coreos/go-oidc/key/key_test.go b/vendor/github.com/coreos/go-oidc/key/key_test.go new file mode 100644 index 00000000..d68a61f0 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/key/key_test.go @@ -0,0 +1,103 @@ +package key + +import ( + "crypto/rsa" + "math/big" + "reflect" + "testing" + "time" + + "github.com/coreos/go-oidc/jose" +) + +func TestPrivateRSAKeyJWK(t *testing.T) { + n := big.NewInt(int64(17)) + if n == nil { + panic("NewInt returned nil") + } + + k := &PrivateKey{ + KeyID: "foo", + PrivateKey: &rsa.PrivateKey{ + PublicKey: rsa.PublicKey{N: n, E: 65537}, + }, + } + + want := jose.JWK{ + ID: "foo", + Type: "RSA", + Alg: "RS256", + Use: "sig", + Modulus: n, + Exponent: 65537, + } + + got := k.JWK() + if !reflect.DeepEqual(want, got) { + t.Fatalf("JWK mismatch: want=%#v got=%#v", want, got) + } +} + +func TestPublicKeySetKey(t *testing.T) { + n := big.NewInt(int64(17)) + if n == nil { + panic("NewInt returned nil") + } + + k := jose.JWK{ + ID: "foo", + Type: "RSA", + Alg: "RS256", + Use: "sig", + Modulus: n, + Exponent: 65537, + } + now := time.Now().UTC() + ks := NewPublicKeySet([]jose.JWK{k}, now) + + want := &PublicKey{jwk: k} + got := ks.Key("foo") + if !reflect.DeepEqual(want, got) { + t.Errorf("Unexpected response from PublicKeySet.Key: want=%#v got=%#v", want, got) + } + + got = ks.Key("bar") + if got != nil { + t.Errorf("Expected nil response from PublicKeySet.Key, got %#v", got) + } +} + +func TestPublicKeyMarshalJSON(t *testing.T) { + k := jose.JWK{ + ID: "foo", + Type: "RSA", + Alg: "RS256", + Use: "sig", + Modulus: big.NewInt(int64(17)), + Exponent: 65537, + } + want := `{"kid":"foo","kty":"RSA","alg":"RS256","use":"sig","e":"AQAB","n":"EQ=="}` + pubKey := NewPublicKey(k) + gotBytes, err := pubKey.MarshalJSON() + if err != nil { + t.Fatalf("failed to marshal public key: %v", err) + } + got := string(gotBytes) + if got != want { + t.Errorf("got != want:\n%s\n%s", got, want) + } +} + +func TestGeneratePrivateKeyIDs(t *testing.T) { + key1, err := GeneratePrivateKey() + if err != nil { + t.Fatalf("GeneratePrivateKey(): %v", err) + } + key2, err := GeneratePrivateKey() + if err != nil { + t.Fatalf("GeneratePrivateKey(): %v", err) + } + if key1.KeyID == key2.KeyID { + t.Fatalf("expected different keys to have different key IDs") + } +} diff --git a/vendor/github.com/coreos/go-oidc/key/manager.go b/vendor/github.com/coreos/go-oidc/key/manager.go new file mode 100644 index 00000000..476ab6a8 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/key/manager.go @@ -0,0 +1,99 @@ +package key + +import ( + "errors" + "time" + + "github.com/jonboulle/clockwork" + + "github.com/coreos/go-oidc/jose" + "github.com/coreos/pkg/health" +) + +type PrivateKeyManager interface { + ExpiresAt() time.Time + Signer() (jose.Signer, error) + JWKs() ([]jose.JWK, error) + PublicKeys() ([]PublicKey, error) + + WritableKeySetRepo + health.Checkable +} + +func NewPrivateKeyManager() PrivateKeyManager { + return &privateKeyManager{ + clock: clockwork.NewRealClock(), + } +} + +type privateKeyManager struct { + keySet *PrivateKeySet + clock clockwork.Clock +} + +func (m *privateKeyManager) ExpiresAt() time.Time { + if m.keySet == nil { + return m.clock.Now().UTC() + } + + return m.keySet.ExpiresAt() +} + +func (m *privateKeyManager) Signer() (jose.Signer, error) { + if err := m.Healthy(); err != nil { + return nil, err + } + + return m.keySet.Active().Signer(), nil +} + +func (m *privateKeyManager) JWKs() ([]jose.JWK, error) { + if err := m.Healthy(); err != nil { + return nil, err + } + + keys := m.keySet.Keys() + jwks := make([]jose.JWK, len(keys)) + for i, k := range keys { + jwks[i] = k.JWK() + } + return jwks, nil +} + +func (m *privateKeyManager) PublicKeys() ([]PublicKey, error) { + jwks, err := m.JWKs() + if err != nil { + return nil, err + } + keys := make([]PublicKey, len(jwks)) + for i, jwk := range jwks { + keys[i] = *NewPublicKey(jwk) + } + return keys, nil +} + +func (m *privateKeyManager) Healthy() error { + if m.keySet == nil { + return errors.New("private key manager uninitialized") + } + + if len(m.keySet.Keys()) == 0 { + return errors.New("private key manager zero keys") + } + + if m.keySet.ExpiresAt().Before(m.clock.Now().UTC()) { + return errors.New("private key manager keys expired") + } + + return nil +} + +func (m *privateKeyManager) Set(keySet KeySet) error { + privKeySet, ok := keySet.(*PrivateKeySet) + if !ok { + return errors.New("unable to cast to PrivateKeySet") + } + + m.keySet = privKeySet + return nil +} diff --git a/vendor/github.com/coreos/go-oidc/key/manager_test.go b/vendor/github.com/coreos/go-oidc/key/manager_test.go new file mode 100644 index 00000000..f3c753e7 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/key/manager_test.go @@ -0,0 +1,225 @@ +package key + +import ( + "crypto/rsa" + "math/big" + "reflect" + "strconv" + "testing" + "time" + + "github.com/jonboulle/clockwork" + + "github.com/coreos/go-oidc/jose" +) + +var ( + jwk1 jose.JWK + jwk2 jose.JWK + jwk3 jose.JWK +) + +func init() { + jwk1 = jose.JWK{ + ID: "1", + Type: "RSA", + Alg: "RS256", + Use: "sig", + Modulus: big.NewInt(1), + Exponent: 65537, + } + + jwk2 = jose.JWK{ + ID: "2", + Type: "RSA", + Alg: "RS256", + Use: "sig", + Modulus: big.NewInt(2), + Exponent: 65537, + } + + jwk3 = jose.JWK{ + ID: "3", + Type: "RSA", + Alg: "RS256", + Use: "sig", + Modulus: big.NewInt(3), + Exponent: 65537, + } +} + +func generatePrivateKeyStatic(t *testing.T, idAndN int) *PrivateKey { + n := big.NewInt(int64(idAndN)) + if n == nil { + t.Fatalf("Call to NewInt(%d) failed", idAndN) + } + + pk := &rsa.PrivateKey{ + PublicKey: rsa.PublicKey{N: n, E: 65537}, + } + + return &PrivateKey{ + KeyID: strconv.Itoa(idAndN), + PrivateKey: pk, + } +} + +func TestPrivateKeyManagerJWKsRotate(t *testing.T) { + k1 := generatePrivateKeyStatic(t, 1) + k2 := generatePrivateKeyStatic(t, 2) + k3 := generatePrivateKeyStatic(t, 3) + km := NewPrivateKeyManager() + err := km.Set(&PrivateKeySet{ + keys: []*PrivateKey{k1, k2, k3}, + ActiveKeyID: k1.KeyID, + expiresAt: time.Now().Add(time.Minute), + }) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + want := []jose.JWK{jwk1, jwk2, jwk3} + got, err := km.JWKs() + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if !reflect.DeepEqual(want, got) { + t.Fatalf("JWK mismatch: want=%#v got=%#v", want, got) + } +} + +func TestPrivateKeyManagerSigner(t *testing.T) { + k := generatePrivateKeyStatic(t, 13) + + km := NewPrivateKeyManager() + err := km.Set(&PrivateKeySet{ + keys: []*PrivateKey{k}, + ActiveKeyID: k.KeyID, + expiresAt: time.Now().Add(time.Minute), + }) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + signer, err := km.Signer() + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + wantID := "13" + gotID := signer.ID() + if wantID != gotID { + t.Fatalf("Signer has incorrect ID: want=%s got=%s", wantID, gotID) + } +} + +func TestPrivateKeyManagerHealthyFail(t *testing.T) { + keyFixture := generatePrivateKeyStatic(t, 1) + tests := []*privateKeyManager{ + // keySet nil + &privateKeyManager{ + keySet: nil, + clock: clockwork.NewRealClock(), + }, + // zero keys + &privateKeyManager{ + keySet: &PrivateKeySet{ + keys: []*PrivateKey{}, + expiresAt: time.Now().Add(time.Minute), + }, + clock: clockwork.NewRealClock(), + }, + // key set expired + &privateKeyManager{ + keySet: &PrivateKeySet{ + keys: []*PrivateKey{keyFixture}, + expiresAt: time.Now().Add(-1 * time.Minute), + }, + clock: clockwork.NewRealClock(), + }, + } + + for i, tt := range tests { + if err := tt.Healthy(); err == nil { + t.Errorf("case %d: nil error", i) + } + } +} + +func TestPrivateKeyManagerHealthyFailsOtherMethods(t *testing.T) { + km := NewPrivateKeyManager() + if _, err := km.JWKs(); err == nil { + t.Fatalf("Expected non-nil error") + } + if _, err := km.Signer(); err == nil { + t.Fatalf("Expected non-nil error") + } +} + +func TestPrivateKeyManagerExpiresAt(t *testing.T) { + fc := clockwork.NewFakeClock() + now := fc.Now().UTC() + + k := generatePrivateKeyStatic(t, 17) + km := &privateKeyManager{ + clock: fc, + } + + want := fc.Now().UTC() + got := km.ExpiresAt() + if want != got { + t.Fatalf("Incorrect expiration time: want=%v got=%v", want, got) + } + + err := km.Set(&PrivateKeySet{ + keys: []*PrivateKey{k}, + ActiveKeyID: k.KeyID, + expiresAt: now.Add(2 * time.Minute), + }) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + want = fc.Now().UTC().Add(2 * time.Minute) + got = km.ExpiresAt() + if want != got { + t.Fatalf("Incorrect expiration time: want=%v got=%v", want, got) + } +} + +func TestPublicKeys(t *testing.T) { + km := NewPrivateKeyManager() + k1 := generatePrivateKeyStatic(t, 1) + k2 := generatePrivateKeyStatic(t, 2) + k3 := generatePrivateKeyStatic(t, 3) + + tests := [][]*PrivateKey{ + []*PrivateKey{k1}, + []*PrivateKey{k1, k2}, + []*PrivateKey{k1, k2, k3}, + } + + for i, tt := range tests { + ks := &PrivateKeySet{ + keys: tt, + expiresAt: time.Now().Add(time.Hour), + } + km.Set(ks) + + jwks, err := km.JWKs() + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + pks := NewPublicKeySet(jwks, time.Now().Add(time.Hour)) + want := pks.Keys() + got, err := km.PublicKeys() + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if !reflect.DeepEqual(want, got) { + t.Errorf("case %d: Invalid public keys: want=%v got=%v", i, want, got) + } + } +} diff --git a/vendor/github.com/coreos/go-oidc/key/repo.go b/vendor/github.com/coreos/go-oidc/key/repo.go new file mode 100644 index 00000000..1acdeb36 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/key/repo.go @@ -0,0 +1,55 @@ +package key + +import ( + "errors" + "sync" +) + +var ErrorNoKeys = errors.New("no keys found") + +type WritableKeySetRepo interface { + Set(KeySet) error +} + +type ReadableKeySetRepo interface { + Get() (KeySet, error) +} + +type PrivateKeySetRepo interface { + WritableKeySetRepo + ReadableKeySetRepo +} + +func NewPrivateKeySetRepo() PrivateKeySetRepo { + return &memPrivateKeySetRepo{} +} + +type memPrivateKeySetRepo struct { + mu sync.RWMutex + pks PrivateKeySet +} + +func (r *memPrivateKeySetRepo) Set(ks KeySet) error { + pks, ok := ks.(*PrivateKeySet) + if !ok { + return errors.New("unable to cast to PrivateKeySet") + } else if pks == nil { + return errors.New("nil KeySet") + } + + r.mu.Lock() + defer r.mu.Unlock() + + r.pks = *pks + return nil +} + +func (r *memPrivateKeySetRepo) Get() (KeySet, error) { + r.mu.RLock() + defer r.mu.RUnlock() + + if r.pks.keys == nil { + return nil, ErrorNoKeys + } + return KeySet(&r.pks), nil +} diff --git a/vendor/github.com/coreos/go-oidc/key/rotate.go b/vendor/github.com/coreos/go-oidc/key/rotate.go new file mode 100644 index 00000000..bc6cdfb1 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/key/rotate.go @@ -0,0 +1,159 @@ +package key + +import ( + "errors" + "log" + "time" + + ptime "github.com/coreos/pkg/timeutil" + "github.com/jonboulle/clockwork" +) + +var ( + ErrorPrivateKeysExpired = errors.New("private keys have expired") +) + +func NewPrivateKeyRotator(repo PrivateKeySetRepo, ttl time.Duration) *PrivateKeyRotator { + return &PrivateKeyRotator{ + repo: repo, + ttl: ttl, + + keep: 2, + generateKey: GeneratePrivateKey, + clock: clockwork.NewRealClock(), + } +} + +type PrivateKeyRotator struct { + repo PrivateKeySetRepo + generateKey GeneratePrivateKeyFunc + clock clockwork.Clock + keep int + ttl time.Duration +} + +func (r *PrivateKeyRotator) expiresAt() time.Time { + return r.clock.Now().UTC().Add(r.ttl) +} + +func (r *PrivateKeyRotator) Healthy() error { + pks, err := r.privateKeySet() + if err != nil { + return err + } + + if r.clock.Now().After(pks.ExpiresAt()) { + return ErrorPrivateKeysExpired + } + + return nil +} + +func (r *PrivateKeyRotator) privateKeySet() (*PrivateKeySet, error) { + ks, err := r.repo.Get() + if err != nil { + return nil, err + } + + pks, ok := ks.(*PrivateKeySet) + if !ok { + return nil, errors.New("unable to cast to PrivateKeySet") + } + return pks, nil +} + +func (r *PrivateKeyRotator) nextRotation() (time.Duration, error) { + pks, err := r.privateKeySet() + if err == ErrorNoKeys { + return 0, nil + } + if err != nil { + return 0, err + } + + now := r.clock.Now() + + // Ideally, we want to rotate after half the TTL has elapsed. + idealRotationTime := pks.ExpiresAt().Add(-r.ttl / 2) + + // If we are past the ideal rotation time, rotate immediatly. + return max(0, idealRotationTime.Sub(now)), nil +} + +func max(a, b time.Duration) time.Duration { + if a > b { + return a + } + return b +} + +func (r *PrivateKeyRotator) Run() chan struct{} { + attempt := func() { + k, err := r.generateKey() + if err != nil { + log.Printf("go-oidc: failed generating signing key: %v", err) + return + } + + exp := r.expiresAt() + if err := rotatePrivateKeys(r.repo, k, r.keep, exp); err != nil { + log.Printf("go-oidc: key rotation failed: %v", err) + return + } + } + + stop := make(chan struct{}) + go func() { + for { + var nextRotation time.Duration + var sleep time.Duration + var err error + for { + if nextRotation, err = r.nextRotation(); err == nil { + break + } + sleep = ptime.ExpBackoff(sleep, time.Minute) + log.Printf("go-oidc: error getting nextRotation, retrying in %v: %v", sleep, err) + time.Sleep(sleep) + } + + select { + case <-r.clock.After(nextRotation): + attempt() + case <-stop: + return + } + } + }() + + return stop +} + +func rotatePrivateKeys(repo PrivateKeySetRepo, k *PrivateKey, keep int, exp time.Time) error { + ks, err := repo.Get() + if err != nil && err != ErrorNoKeys { + return err + } + + var keys []*PrivateKey + if ks != nil { + pks, ok := ks.(*PrivateKeySet) + if !ok { + return errors.New("unable to cast to PrivateKeySet") + } + keys = pks.Keys() + } + + keys = append([]*PrivateKey{k}, keys...) + if l := len(keys); l > keep { + keys = keys[0:keep] + } + + nks := PrivateKeySet{ + keys: keys, + ActiveKeyID: k.ID(), + expiresAt: exp, + } + + return repo.Set(KeySet(&nks)) +} diff --git a/vendor/github.com/coreos/go-oidc/key/rotate_test.go b/vendor/github.com/coreos/go-oidc/key/rotate_test.go new file mode 100644 index 00000000..b66a4b86 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/key/rotate_test.go @@ -0,0 +1,311 @@ +package key + +import ( + "reflect" + "testing" + "time" + + "github.com/jonboulle/clockwork" +) + +func generatePrivateKeySerialFunc(t *testing.T) GeneratePrivateKeyFunc { + var n int + return func() (*PrivateKey, error) { + n++ + return generatePrivateKeyStatic(t, n), nil + } +} + +func TestRotate(t *testing.T) { + now := time.Now() + k1 := generatePrivateKeyStatic(t, 1) + k2 := generatePrivateKeyStatic(t, 2) + k3 := generatePrivateKeyStatic(t, 3) + + tests := []struct { + start *PrivateKeySet + key *PrivateKey + keep int + exp time.Time + want *PrivateKeySet + }{ + // start with nil keys + { + start: nil, + key: k1, + keep: 2, + exp: now.Add(time.Second), + want: &PrivateKeySet{ + keys: []*PrivateKey{k1}, + ActiveKeyID: k1.KeyID, + expiresAt: now.Add(time.Second), + }, + }, + // start with zero keys + { + start: &PrivateKeySet{}, + key: k1, + keep: 2, + exp: now.Add(time.Second), + want: &PrivateKeySet{ + keys: []*PrivateKey{k1}, + ActiveKeyID: k1.KeyID, + expiresAt: now.Add(time.Second), + }, + }, + // add second key + { + start: &PrivateKeySet{ + keys: []*PrivateKey{k1}, + ActiveKeyID: k1.KeyID, + expiresAt: now, + }, + key: k2, + keep: 2, + exp: now.Add(time.Second), + want: &PrivateKeySet{ + keys: []*PrivateKey{k2, k1}, + ActiveKeyID: k2.KeyID, + expiresAt: now.Add(time.Second), + }, + }, + // rotate in third key + { + start: &PrivateKeySet{ + keys: []*PrivateKey{k2, k1}, + ActiveKeyID: k2.KeyID, + expiresAt: now, + }, + key: k3, + keep: 2, + exp: now.Add(time.Second), + want: &PrivateKeySet{ + keys: []*PrivateKey{k3, k2}, + ActiveKeyID: k3.KeyID, + expiresAt: now.Add(time.Second), + }, + }, + } + + for i, tt := range tests { + repo := NewPrivateKeySetRepo() + if tt.start != nil { + err := repo.Set(tt.start) + if err != nil { + t.Fatalf("case %d: unexpected error: %v", i, err) + } + } + + rotatePrivateKeys(repo, tt.key, tt.keep, tt.exp) + got, err := repo.Get() + if err != nil { + t.Errorf("case %d: unexpected error: %v", i, err) + continue + } + if !reflect.DeepEqual(tt.want, got) { + t.Errorf("case %d: unexpected result: want=%#v got=%#v", i, tt.want, got) + } + } +} + +func TestPrivateKeyRotatorRun(t *testing.T) { + fc := clockwork.NewFakeClock() + now := fc.Now().UTC() + + k1 := generatePrivateKeyStatic(t, 1) + k2 := generatePrivateKeyStatic(t, 2) + k3 := generatePrivateKeyStatic(t, 3) + k4 := generatePrivateKeyStatic(t, 4) + + kRepo := NewPrivateKeySetRepo() + krot := NewPrivateKeyRotator(kRepo, 4*time.Second) + krot.clock = fc + krot.generateKey = generatePrivateKeySerialFunc(t) + + steps := []*PrivateKeySet{ + &PrivateKeySet{ + keys: []*PrivateKey{k1}, + ActiveKeyID: k1.KeyID, + expiresAt: now.Add(4 * time.Second), + }, + &PrivateKeySet{ + keys: []*PrivateKey{k2, k1}, + ActiveKeyID: k2.KeyID, + expiresAt: now.Add(6 * time.Second), + }, + &PrivateKeySet{ + keys: []*PrivateKey{k3, k2}, + ActiveKeyID: k3.KeyID, + expiresAt: now.Add(8 * time.Second), + }, + &PrivateKeySet{ + keys: []*PrivateKey{k4, k3}, + ActiveKeyID: k4.KeyID, + expiresAt: now.Add(10 * time.Second), + }, + } + + stop := krot.Run() + defer close(stop) + + for i, st := range steps { + // wait for the rotater to get sleepy + fc.BlockUntil(1) + + got, err := kRepo.Get() + if err != nil { + t.Fatalf("step %d: unexpected error: %v", i, err) + } + if !reflect.DeepEqual(st, got) { + t.Fatalf("step %d: unexpected state: want=%#v got=%#v", i, st, got) + } + fc.Advance(2 * time.Second) + } +} + +func TestPrivateKeyRotatorExpiresAt(t *testing.T) { + fc := clockwork.NewFakeClock() + krot := &PrivateKeyRotator{ + clock: fc, + ttl: time.Minute, + } + got := krot.expiresAt() + want := fc.Now().UTC().Add(time.Minute) + if !reflect.DeepEqual(want, got) { + t.Errorf("Incorrect expiration time: want=%v got=%v", want, got) + } +} + +func TestNextRotation(t *testing.T) { + fc := clockwork.NewFakeClock() + now := fc.Now().UTC() + + tests := []struct { + expiresAt time.Time + ttl time.Duration + numKeys int + expected time.Duration + }{ + { + // closest to prod + expiresAt: now.Add(time.Hour * 24), + ttl: time.Hour * 24, + numKeys: 2, + expected: time.Hour * 12, + }, + { + expiresAt: now.Add(time.Hour * 2), + ttl: time.Hour * 4, + numKeys: 2, + expected: 0, + }, + { + // No keys. + expiresAt: now.Add(time.Hour * 2), + ttl: time.Hour * 4, + numKeys: 0, + expected: 0, + }, + { + // Nil keyset. + expiresAt: now.Add(time.Hour * 2), + ttl: time.Hour * 4, + numKeys: -1, + expected: 0, + }, + { + // KeySet expired. + expiresAt: now.Add(time.Hour * -2), + ttl: time.Hour * 4, + numKeys: 2, + expected: 0, + }, + { + // Expiry past now + TTL + expiresAt: now.Add(time.Hour * 5), + ttl: time.Hour * 4, + numKeys: 2, + expected: 3 * time.Hour, + }, + } + + for i, tt := range tests { + kRepo := NewPrivateKeySetRepo() + krot := NewPrivateKeyRotator(kRepo, tt.ttl) + krot.clock = fc + pks := &PrivateKeySet{ + expiresAt: tt.expiresAt, + } + if tt.numKeys != -1 { + for n := 0; n < tt.numKeys; n++ { + pks.keys = append(pks.keys, generatePrivateKeyStatic(t, n)) + } + err := kRepo.Set(pks) + if err != nil { + t.Fatalf("case %d: unexpected error: %v", i, err) + } + + } + actual, err := krot.nextRotation() + if err != nil { + t.Errorf("case %d: error calling shouldRotate(): %v", i, err) + } + if actual != tt.expected { + t.Errorf("case %d: actual == %v, want %v", i, actual, tt.expected) + } + } +} + +func TestHealthy(t *testing.T) { + fc := clockwork.NewFakeClock() + now := fc.Now().UTC() + + tests := []struct { + expiresAt time.Time + numKeys int + expected error + }{ + { + expiresAt: now.Add(time.Hour), + numKeys: 2, + expected: nil, + }, + { + expiresAt: now.Add(time.Hour), + numKeys: -1, + expected: ErrorNoKeys, + }, + { + expiresAt: now.Add(time.Hour), + numKeys: 0, + expected: ErrorNoKeys, + }, + { + expiresAt: now.Add(-time.Hour), + numKeys: 2, + expected: ErrorPrivateKeysExpired, + }, + } + + for i, tt := range tests { + kRepo := NewPrivateKeySetRepo() + krot := NewPrivateKeyRotator(kRepo, time.Hour) + krot.clock = fc + pks := &PrivateKeySet{ + expiresAt: tt.expiresAt, + } + if tt.numKeys != -1 { + for n := 0; n < tt.numKeys; n++ { + pks.keys = append(pks.keys, generatePrivateKeyStatic(t, n)) + } + err := kRepo.Set(pks) + if err != nil { + t.Fatalf("case %d: unexpected error: %v", i, err) + } + + } + if err := krot.Healthy(); err != tt.expected { + t.Errorf("case %d: got==%q, want==%q", i, err, tt.expected) + } + } +} diff --git a/vendor/github.com/coreos/go-oidc/key/sync.go b/vendor/github.com/coreos/go-oidc/key/sync.go new file mode 100644 index 00000000..b887f7b5 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/key/sync.go @@ -0,0 +1,91 @@ +package key + +import ( + "errors" + "log" + "time" + + "github.com/jonboulle/clockwork" + + "github.com/coreos/pkg/timeutil" +) + +func NewKeySetSyncer(r ReadableKeySetRepo, w WritableKeySetRepo) *KeySetSyncer { + return &KeySetSyncer{ + readable: r, + writable: w, + clock: clockwork.NewRealClock(), + } +} + +type KeySetSyncer struct { + readable ReadableKeySetRepo + writable WritableKeySetRepo + clock clockwork.Clock +} + +func (s *KeySetSyncer) Run() chan struct{} { + stop := make(chan struct{}) + go func() { + var failing bool + var next time.Duration + for { + exp, err := syncKeySet(s.readable, s.writable, s.clock) + if err != nil || exp == 0 { + if !failing { + failing = true + next = time.Second + } else { + next = timeutil.ExpBackoff(next, time.Minute) + } + if exp == 0 { + log.Printf("Synced to already expired key set, retrying in %v: %v", next, err) + + } else { + log.Printf("Failed syncing key set, retrying in %v: %v", next, err) + } + } else { + failing = false + next = exp / 2 + } + + select { + case <-s.clock.After(next): + continue + case <-stop: + return + } + } + }() + + return stop +} + +func Sync(r ReadableKeySetRepo, w WritableKeySetRepo) (time.Duration, error) { + return syncKeySet(r, w, clockwork.NewRealClock()) +} + +// syncKeySet copies the keyset from r to the KeySet at w and returns the duration in which the KeySet will expire. +// If keyset has already expired, returns a zero duration. +func syncKeySet(r ReadableKeySetRepo, w WritableKeySetRepo, clock clockwork.Clock) (exp time.Duration, err error) { + var ks KeySet + ks, err = r.Get() + if err != nil { + return + } + + if ks == nil { + err = errors.New("no source KeySet") + return + } + + if err = w.Set(ks); err != nil { + return + } + + now := clock.Now() + if ks.ExpiresAt().After(now) { + exp = ks.ExpiresAt().Sub(now) + } + return +} diff --git a/vendor/github.com/coreos/go-oidc/key/sync_test.go b/vendor/github.com/coreos/go-oidc/key/sync_test.go new file mode 100644 index 00000000..1f077401 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/key/sync_test.go @@ -0,0 +1,214 @@ +package key + +import ( + "errors" + "reflect" + "sync" + "testing" + "time" + + "github.com/jonboulle/clockwork" +) + +type staticReadableKeySetRepo struct { + mu sync.RWMutex + ks KeySet + err error +} + +func (r *staticReadableKeySetRepo) Get() (KeySet, error) { + r.mu.RLock() + defer r.mu.RUnlock() + return r.ks, r.err +} + +func (r *staticReadableKeySetRepo) set(ks KeySet, err error) { + r.mu.Lock() + defer r.mu.Unlock() + r.ks, r.err = ks, err +} + +func TestKeySyncerSync(t *testing.T) { + fc := clockwork.NewFakeClock() + now := fc.Now().UTC() + + k1 := generatePrivateKeyStatic(t, 1) + k2 := generatePrivateKeyStatic(t, 2) + k3 := generatePrivateKeyStatic(t, 3) + + steps := []struct { + fromKS KeySet + fromErr error + advance time.Duration + want *PrivateKeySet + }{ + // on startup, first sync should trigger within a second + { + fromKS: &PrivateKeySet{ + keys: []*PrivateKey{k1}, + ActiveKeyID: k1.KeyID, + expiresAt: now.Add(10 * time.Second), + }, + advance: time.Second, + want: &PrivateKeySet{ + keys: []*PrivateKey{k1}, + ActiveKeyID: k1.KeyID, + expiresAt: now.Add(10 * time.Second), + }, + }, + // advance halfway into TTL, triggering sync + { + fromKS: &PrivateKeySet{ + keys: []*PrivateKey{k2, k1}, + ActiveKeyID: k2.KeyID, + expiresAt: now.Add(15 * time.Second), + }, + advance: 5 * time.Second, + want: &PrivateKeySet{ + keys: []*PrivateKey{k2, k1}, + ActiveKeyID: k2.KeyID, + expiresAt: now.Add(15 * time.Second), + }, + }, + + // advance halfway into TTL, triggering sync that fails + { + fromErr: errors.New("fail!"), + advance: 10 * time.Second, + want: &PrivateKeySet{ + keys: []*PrivateKey{k2, k1}, + ActiveKeyID: k2.KeyID, + expiresAt: now.Add(15 * time.Second), + }, + }, + + // sync retries quickly, and succeeds with fixed data + { + fromKS: &PrivateKeySet{ + keys: []*PrivateKey{k3, k2, k1}, + ActiveKeyID: k3.KeyID, + expiresAt: now.Add(25 * time.Second), + }, + advance: 3 * time.Second, + want: &PrivateKeySet{ + keys: []*PrivateKey{k3, k2, k1}, + ActiveKeyID: k3.KeyID, + expiresAt: now.Add(25 * time.Second), + }, + }, + } + + from := &staticReadableKeySetRepo{} + to := NewPrivateKeySetRepo() + + syncer := NewKeySetSyncer(from, to) + syncer.clock = fc + stop := syncer.Run() + defer close(stop) + + for i, st := range steps { + from.set(st.fromKS, st.fromErr) + + fc.Advance(st.advance) + fc.BlockUntil(1) + + ks, err := to.Get() + if err != nil { + t.Fatalf("step %d: unable to get keys: %v", i, err) + } + if !reflect.DeepEqual(st.want, ks) { + t.Fatalf("step %d: incorrect state: want=%#v got=%#v", i, st.want, ks) + } + } +} + +func TestSync(t *testing.T) { + fc := clockwork.NewFakeClock() + now := fc.Now().UTC() + + k1 := generatePrivateKeyStatic(t, 1) + k2 := generatePrivateKeyStatic(t, 2) + k3 := generatePrivateKeyStatic(t, 3) + + tests := []struct { + keySet *PrivateKeySet + want time.Duration + }{ + { + keySet: &PrivateKeySet{ + keys: []*PrivateKey{k1}, + ActiveKeyID: k1.KeyID, + expiresAt: now.Add(time.Minute), + }, + want: time.Minute, + }, + { + keySet: &PrivateKeySet{ + keys: []*PrivateKey{k2, k1}, + ActiveKeyID: k2.KeyID, + expiresAt: now.Add(time.Minute), + }, + want: time.Minute, + }, + { + keySet: &PrivateKeySet{ + keys: []*PrivateKey{k3, k2, k1}, + ActiveKeyID: k2.KeyID, + expiresAt: now.Add(time.Minute), + }, + want: time.Minute, + }, + { + keySet: &PrivateKeySet{ + keys: []*PrivateKey{k2, k1}, + ActiveKeyID: k2.KeyID, + expiresAt: now.Add(time.Hour), + }, + want: time.Hour, + }, + { + keySet: &PrivateKeySet{ + keys: []*PrivateKey{k1}, + ActiveKeyID: k1.KeyID, + expiresAt: now.Add(-time.Hour), + }, + want: 0, + }, + } + + for i, tt := range tests { + from := NewPrivateKeySetRepo() + to := NewPrivateKeySetRepo() + + err := from.Set(tt.keySet) + if err != nil { + t.Errorf("case %d: unexpected error: %v", i, err) + continue + } + exp, err := syncKeySet(from, to, fc) + if err != nil { + t.Errorf("case %d: unexpected error: %v", i, err) + continue + } + + if tt.want != exp { + t.Errorf("case %d: want=%v got=%v", i, tt.want, exp) + } + } +} + +func TestSyncFail(t *testing.T) { + tests := []error{ + nil, + errors.New("fail!"), + } + + for i, tt := range tests { + from := &staticReadableKeySetRepo{ks: nil, err: tt} + to := NewPrivateKeySetRepo() + + if _, err := syncKeySet(from, to, clockwork.NewFakeClock()); err == nil { + t.Errorf("case %d: expected non-nil error", i) + } + } +} diff --git a/vendor/github.com/coreos/go-oidc/oauth2/doc.go b/vendor/github.com/coreos/go-oidc/oauth2/doc.go new file mode 100644 index 00000000..52eb3085 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/oauth2/doc.go @@ -0,0 +1,2 @@ +// Package oauth2 is DEPRECATED. Use golang.org/x/oauth instead. +package oauth2 diff --git a/vendor/github.com/coreos/go-oidc/oauth2/error.go b/vendor/github.com/coreos/go-oidc/oauth2/error.go new file mode 100644 index 00000000..50d89094 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/oauth2/error.go @@ -0,0 +1,29 @@ +package oauth2 + +const ( + ErrorAccessDenied = "access_denied" + ErrorInvalidClient = "invalid_client" + ErrorInvalidGrant = "invalid_grant" + ErrorInvalidRequest = "invalid_request" + ErrorServerError = "server_error" + ErrorUnauthorizedClient = "unauthorized_client" + ErrorUnsupportedGrantType = "unsupported_grant_type" + ErrorUnsupportedResponseType = "unsupported_response_type" +) + +type Error struct { + Type string `json:"error"` + Description string `json:"error_description,omitempty"` + State string `json:"state,omitempty"` +} + +func (e *Error) Error() string { + if e.Description != "" { + return e.Type + ": " + e.Description + } + return e.Type +} + +func NewError(typ string) *Error { + return &Error{Type: typ} +} diff --git a/vendor/github.com/coreos/go-oidc/oauth2/oauth2.go b/vendor/github.com/coreos/go-oidc/oauth2/oauth2.go new file mode 100644 index 00000000..72d1d671 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/oauth2/oauth2.go @@ -0,0 +1,416 @@ +package oauth2 + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "mime" + "net/http" + "net/url" + "sort" + "strconv" + "strings" + + phttp "github.com/coreos/go-oidc/http" +) + +// ResponseTypesEqual compares two response_type values. If either +// contains a space, it is treated as an unordered list. For example, +// comparing "code id_token" and "id_token code" would evaluate to true. +func ResponseTypesEqual(r1, r2 string) bool { + if !strings.Contains(r1, " ") || !strings.Contains(r2, " ") { + // fast route, no split needed + return r1 == r2 + } + + // split, sort, and compare + r1Fields := strings.Fields(r1) + r2Fields := strings.Fields(r2) + if len(r1Fields) != len(r2Fields) { + return false + } + sort.Strings(r1Fields) + sort.Strings(r2Fields) + for i, r1Field := range r1Fields { + if r1Field != r2Fields[i] { + return false + } + } + return true +} + +const ( + // OAuth2.0 response types registered by OIDC. + // + // See: https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#RegistryContents + ResponseTypeCode = "code" + ResponseTypeCodeIDToken = "code id_token" + ResponseTypeCodeIDTokenToken = "code id_token token" + ResponseTypeIDToken = "id_token" + ResponseTypeIDTokenToken = "id_token token" + ResponseTypeToken = "token" + ResponseTypeNone = "none" +) + +const ( + GrantTypeAuthCode = "authorization_code" + GrantTypeClientCreds = "client_credentials" + GrantTypeUserCreds = "password" + GrantTypeImplicit = "implicit" + GrantTypeRefreshToken = "refresh_token" + + AuthMethodClientSecretPost = "client_secret_post" + AuthMethodClientSecretBasic = "client_secret_basic" + AuthMethodClientSecretJWT = "client_secret_jwt" + AuthMethodPrivateKeyJWT = "private_key_jwt" +) + +type Config struct { + Credentials ClientCredentials + Scope []string + RedirectURL string + AuthURL string + TokenURL string + + // Must be one of the AuthMethodXXX methods above. Right now, only + // AuthMethodClientSecretPost and AuthMethodClientSecretBasic are supported. + AuthMethod string +} + +type Client struct { + hc phttp.Client + creds ClientCredentials + scope []string + authURL *url.URL + redirectURL *url.URL + tokenURL *url.URL + authMethod string +} + +type ClientCredentials struct { + ID string + Secret string +} + +func NewClient(hc phttp.Client, cfg Config) (c *Client, err error) { + if len(cfg.Credentials.ID) == 0 { + err = errors.New("missing client id") + return + } + + if len(cfg.Credentials.Secret) == 0 { + err = errors.New("missing client secret") + return + } + + if cfg.AuthMethod == "" { + cfg.AuthMethod = AuthMethodClientSecretBasic + } else if cfg.AuthMethod != AuthMethodClientSecretPost && cfg.AuthMethod != AuthMethodClientSecretBasic { + err = fmt.Errorf("auth method %q is not supported", cfg.AuthMethod) + return + } + + au, err := phttp.ParseNonEmptyURL(cfg.AuthURL) + if err != nil { + return + } + + tu, err := phttp.ParseNonEmptyURL(cfg.TokenURL) + if err != nil { + return + } + + // Allow empty redirect URL in the case where the client + // only needs to verify a given token. + ru, err := url.Parse(cfg.RedirectURL) + if err != nil { + return + } + + c = &Client{ + creds: cfg.Credentials, + scope: cfg.Scope, + redirectURL: ru, + authURL: au, + tokenURL: tu, + hc: hc, + authMethod: cfg.AuthMethod, + } + + return +} + +// Return the embedded HTTP client +func (c *Client) HttpClient() phttp.Client { + return c.hc +} + +// Generate the url for initial redirect to oauth provider. +func (c *Client) AuthCodeURL(state, accessType, prompt string) string { + v := c.commonURLValues() + v.Set("state", state) + if strings.ToLower(accessType) == "offline" { + v.Set("access_type", "offline") + } + + if prompt != "" { + v.Set("prompt", prompt) + } + v.Set("response_type", "code") + + q := v.Encode() + u := *c.authURL + if u.RawQuery == "" { + u.RawQuery = q + } else { + u.RawQuery += "&" + q + } + return u.String() +} + +func (c *Client) commonURLValues() url.Values { + return url.Values{ + "redirect_uri": {c.redirectURL.String()}, + "scope": {strings.Join(c.scope, " ")}, + "client_id": {c.creds.ID}, + } +} + +func (c *Client) newAuthenticatedRequest(urlToken string, values url.Values) (*http.Request, error) { + var req *http.Request + var err error + switch c.authMethod { + case AuthMethodClientSecretPost: + values.Set("client_secret", c.creds.Secret) + req, err = http.NewRequest("POST", urlToken, strings.NewReader(values.Encode())) + if err != nil { + return nil, err + } + case AuthMethodClientSecretBasic: + req, err = http.NewRequest("POST", urlToken, strings.NewReader(values.Encode())) + if err != nil { + return nil, err + } + encodedID := url.QueryEscape(c.creds.ID) + encodedSecret := url.QueryEscape(c.creds.Secret) + req.SetBasicAuth(encodedID, encodedSecret) + default: + panic("misconfigured client: auth method not supported") + } + + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + return req, nil + +} + +// ClientCredsToken posts the client id and secret to obtain a token scoped to the OAuth2 client via the "client_credentials" grant type. +// May not be supported by all OAuth2 servers. +func (c *Client) ClientCredsToken(scope []string) (result TokenResponse, err error) { + v := url.Values{ + "scope": {strings.Join(scope, " ")}, + "grant_type": {GrantTypeClientCreds}, + } + + req, err := c.newAuthenticatedRequest(c.tokenURL.String(), v) + if err != nil { + return + } + + resp, err := c.hc.Do(req) + if err != nil { + return + } + defer resp.Body.Close() + + return parseTokenResponse(resp) +} + +// UserCredsToken posts the username and password to obtain a token scoped to the OAuth2 client via the "password" grant_type +// May not be supported by all OAuth2 servers. +func (c *Client) UserCredsToken(username, password string) (result TokenResponse, err error) { + v := url.Values{ + "scope": {strings.Join(c.scope, " ")}, + "grant_type": {GrantTypeUserCreds}, + "username": {username}, + "password": {password}, + } + + req, err := c.newAuthenticatedRequest(c.tokenURL.String(), v) + if err != nil { + return + } + + resp, err := c.hc.Do(req) + if err != nil { + return + } + defer resp.Body.Close() + + return parseTokenResponse(resp) +} + +// RequestToken requests a token from the Token Endpoint with the specified grantType. +// If 'grantType' == GrantTypeAuthCode, then 'value' should be the authorization code. +// If 'grantType' == GrantTypeRefreshToken, then 'value' should be the refresh token. +func (c *Client) RequestToken(grantType, value string) (result TokenResponse, err error) { + v := c.commonURLValues() + + v.Set("grant_type", grantType) + v.Set("client_secret", c.creds.Secret) + switch grantType { + case GrantTypeAuthCode: + v.Set("code", value) + case GrantTypeRefreshToken: + v.Set("refresh_token", value) + default: + err = fmt.Errorf("unsupported grant_type: %v", grantType) + return + } + + req, err := c.newAuthenticatedRequest(c.tokenURL.String(), v) + if err != nil { + return + } + + resp, err := c.hc.Do(req) + if err != nil { + return + } + defer resp.Body.Close() + + return parseTokenResponse(resp) +} + +func parseTokenResponse(resp *http.Response) (result TokenResponse, err error) { + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return + } + badStatusCode := resp.StatusCode < 200 || resp.StatusCode > 299 + + contentType, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return + } + + result = TokenResponse{ + RawBody: body, + } + + newError := func(typ, desc, state string) error { + if typ == "" { + return fmt.Errorf("unrecognized error %s", body) + } + return &Error{typ, desc, state} + } + + if contentType == "application/x-www-form-urlencoded" || contentType == "text/plain" { + var vals url.Values + vals, err = url.ParseQuery(string(body)) + if err != nil { + return + } + if error := vals.Get("error"); error != "" || badStatusCode { + err = newError(error, vals.Get("error_description"), vals.Get("state")) + return + } + e := vals.Get("expires_in") + if e == "" { + e = vals.Get("expires") + } + if e != "" { + result.Expires, err = strconv.Atoi(e) + if err != nil { + return + } + } + result.AccessToken = vals.Get("access_token") + result.TokenType = vals.Get("token_type") + result.IDToken = vals.Get("id_token") + result.RefreshToken = vals.Get("refresh_token") + result.Scope = vals.Get("scope") + } else { + var r struct { + AccessToken string `json:"access_token"` + TokenType string `json:"token_type"` + IDToken string `json:"id_token"` + RefreshToken string `json:"refresh_token"` + Scope string `json:"scope"` + State string `json:"state"` + ExpiresIn json.Number `json:"expires_in"` // Azure AD returns string + Expires int `json:"expires"` + Error string `json:"error"` + Desc string `json:"error_description"` + } + if err = json.Unmarshal(body, &r); err != nil { + return + } + if r.Error != "" || badStatusCode { + err = newError(r.Error, r.Desc, r.State) + return + } + result.AccessToken = r.AccessToken + result.TokenType = r.TokenType + result.IDToken = r.IDToken + result.RefreshToken = r.RefreshToken + result.Scope = r.Scope + if expiresIn, err := r.ExpiresIn.Int64(); err != nil { + result.Expires = r.Expires + } else { + result.Expires = int(expiresIn) + } + } + return +} + +type TokenResponse struct { + AccessToken string + TokenType string + Expires int + IDToken string + RefreshToken string // OPTIONAL. + Scope string // OPTIONAL, if identical to the scope requested by the client, otherwise, REQUIRED. + RawBody []byte // In case callers need some other non-standard info from the token response +} + +type AuthCodeRequest struct { + ResponseType string + ClientID string + RedirectURL *url.URL + Scope []string + State string +} + +func ParseAuthCodeRequest(q url.Values) (AuthCodeRequest, error) { + acr := AuthCodeRequest{ + ResponseType: q.Get("response_type"), + ClientID: q.Get("client_id"), + State: q.Get("state"), + Scope: make([]string, 0), + } + + qs := strings.TrimSpace(q.Get("scope")) + if qs != "" { + acr.Scope = strings.Split(qs, " ") + } + + err := func() error { + if acr.ClientID == "" { + return NewError(ErrorInvalidRequest) + } + + redirectURL := q.Get("redirect_uri") + if redirectURL != "" { + ru, err := url.Parse(redirectURL) + if err != nil { + return NewError(ErrorInvalidRequest) + } + acr.RedirectURL = ru + } + + return nil + }() + + return acr, err +} diff --git a/vendor/github.com/coreos/go-oidc/oauth2/oauth2_test.go b/vendor/github.com/coreos/go-oidc/oauth2/oauth2_test.go new file mode 100644 index 00000000..0267a84f --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/oauth2/oauth2_test.go @@ -0,0 +1,518 @@ +package oauth2 + +import ( + "errors" + "io/ioutil" + "net/http" + "net/url" + "reflect" + "strconv" + "strings" + "testing" + + phttp "github.com/coreos/go-oidc/http" +) + +func TestResponseTypesEqual(t *testing.T) { + tests := []struct { + r1, r2 string + want bool + }{ + {"code", "code", true}, + {"id_token", "code", false}, + {"code token", "token code", true}, + {"code token", "code token", true}, + {"foo", "bar code", false}, + {"code token id_token", "token id_token code", true}, + {"code token id_token", "token id_token code zoo", false}, + } + + for i, tt := range tests { + got1 := ResponseTypesEqual(tt.r1, tt.r2) + got2 := ResponseTypesEqual(tt.r2, tt.r1) + if got1 != got2 { + t.Errorf("case %d: got different answers with different orders", i) + } + if tt.want != got1 { + t.Errorf("case %d: want=%t, got=%t", i, tt.want, got1) + } + } +} + +func TestParseAuthCodeRequest(t *testing.T) { + tests := []struct { + query url.Values + wantACR AuthCodeRequest + wantErr error + }{ + // no redirect_uri + { + query: url.Values{ + "response_type": []string{"code"}, + "scope": []string{"foo bar baz"}, + "client_id": []string{"XXX"}, + "state": []string{"pants"}, + }, + wantACR: AuthCodeRequest{ + ResponseType: "code", + ClientID: "XXX", + Scope: []string{"foo", "bar", "baz"}, + State: "pants", + RedirectURL: nil, + }, + }, + + // with redirect_uri + { + query: url.Values{ + "response_type": []string{"code"}, + "redirect_uri": []string{"https://127.0.0.1:5555/callback?foo=bar"}, + "scope": []string{"foo bar baz"}, + "client_id": []string{"XXX"}, + "state": []string{"pants"}, + }, + wantACR: AuthCodeRequest{ + ResponseType: "code", + ClientID: "XXX", + Scope: []string{"foo", "bar", "baz"}, + State: "pants", + RedirectURL: &url.URL{ + Scheme: "https", + Host: "127.0.0.1:5555", + Path: "/callback", + RawQuery: "foo=bar", + }, + }, + }, + + // unsupported response_type doesn't trigger error + { + query: url.Values{ + "response_type": []string{"token"}, + "redirect_uri": []string{"https://127.0.0.1:5555/callback?foo=bar"}, + "scope": []string{"foo bar baz"}, + "client_id": []string{"XXX"}, + "state": []string{"pants"}, + }, + wantACR: AuthCodeRequest{ + ResponseType: "token", + ClientID: "XXX", + Scope: []string{"foo", "bar", "baz"}, + State: "pants", + RedirectURL: &url.URL{ + Scheme: "https", + Host: "127.0.0.1:5555", + Path: "/callback", + RawQuery: "foo=bar", + }, + }, + }, + + // unparseable redirect_uri + { + query: url.Values{ + "response_type": []string{"code"}, + "redirect_uri": []string{":"}, + "scope": []string{"foo bar baz"}, + "client_id": []string{"XXX"}, + "state": []string{"pants"}, + }, + wantACR: AuthCodeRequest{ + ResponseType: "code", + ClientID: "XXX", + Scope: []string{"foo", "bar", "baz"}, + State: "pants", + }, + wantErr: NewError(ErrorInvalidRequest), + }, + + // no client_id, redirect_uri not parsed + { + query: url.Values{ + "response_type": []string{"code"}, + "redirect_uri": []string{"https://127.0.0.1:5555/callback?foo=bar"}, + "scope": []string{"foo bar baz"}, + "client_id": []string{}, + "state": []string{"pants"}, + }, + wantACR: AuthCodeRequest{ + ResponseType: "code", + ClientID: "", + Scope: []string{"foo", "bar", "baz"}, + State: "pants", + RedirectURL: nil, + }, + wantErr: NewError(ErrorInvalidRequest), + }, + } + + for i, tt := range tests { + got, err := ParseAuthCodeRequest(tt.query) + if !reflect.DeepEqual(tt.wantErr, err) { + t.Errorf("case %d: incorrect error value: want=%q got=%q", i, tt.wantErr, err) + } + + if !reflect.DeepEqual(tt.wantACR, got) { + t.Errorf("case %d: incorrect AuthCodeRequest value: want=%#v got=%#v", i, tt.wantACR, got) + } + } +} + +type fakeBadClient struct { + Request *http.Request + err error +} + +func (f *fakeBadClient) Do(r *http.Request) (*http.Response, error) { + f.Request = r + return nil, f.err +} + +func TestClientCredsToken(t *testing.T) { + hc := &fakeBadClient{nil, errors.New("error")} + cfg := Config{ + Credentials: ClientCredentials{ID: "c#id", Secret: "c secret"}, + Scope: []string{"foo-scope", "bar-scope"}, + TokenURL: "http://example.com/token", + AuthMethod: AuthMethodClientSecretBasic, + RedirectURL: "http://example.com/redirect", + AuthURL: "http://example.com/auth", + } + + c, err := NewClient(hc, cfg) + if err != nil { + t.Errorf("unexpected error %v", err) + } + + scope := []string{"openid"} + c.ClientCredsToken(scope) + if hc.Request == nil { + t.Error("request is empty") + } + + tu := hc.Request.URL.String() + if cfg.TokenURL != tu { + t.Errorf("wrong token url, want=%v, got=%v", cfg.TokenURL, tu) + } + + ct := hc.Request.Header.Get("Content-Type") + if ct != "application/x-www-form-urlencoded" { + t.Errorf("wrong content-type, want=application/x-www-form-urlencoded, got=%v", ct) + } + + cid, secret, ok := phttp.BasicAuth(hc.Request) + if !ok { + t.Error("unexpected error parsing basic auth") + } + + if url.QueryEscape(cfg.Credentials.ID) != cid { + t.Errorf("wrong client ID, want=%v, got=%v", cfg.Credentials.ID, cid) + } + + if url.QueryEscape(cfg.Credentials.Secret) != secret { + t.Errorf("wrong client secret, want=%v, got=%v", cfg.Credentials.Secret, secret) + } + + err = hc.Request.ParseForm() + if err != nil { + t.Error("unexpected error parsing form") + } + + gt := hc.Request.PostForm.Get("grant_type") + if gt != GrantTypeClientCreds { + t.Errorf("wrong grant_type, want=%v, got=%v", GrantTypeClientCreds, gt) + } + + sc := strings.Split(hc.Request.PostForm.Get("scope"), " ") + if !reflect.DeepEqual(scope, sc) { + t.Errorf("wrong scope, want=%v, got=%v", scope, sc) + } +} + +func TestUserCredsToken(t *testing.T) { + hc := &fakeBadClient{nil, errors.New("error")} + cfg := Config{ + Credentials: ClientCredentials{ID: "c#id", Secret: "c secret"}, + Scope: []string{"foo-scope", "bar-scope"}, + TokenURL: "http://example.com/token", + AuthMethod: AuthMethodClientSecretBasic, + RedirectURL: "http://example.com/redirect", + AuthURL: "http://example.com/auth", + } + + c, err := NewClient(hc, cfg) + if err != nil { + t.Errorf("unexpected error %v", err) + } + + c.UserCredsToken("username", "password") + if hc.Request == nil { + t.Error("request is empty") + } + + tu := hc.Request.URL.String() + if cfg.TokenURL != tu { + t.Errorf("wrong token url, want=%v, got=%v", cfg.TokenURL, tu) + } + + ct := hc.Request.Header.Get("Content-Type") + if ct != "application/x-www-form-urlencoded" { + t.Errorf("wrong content-type, want=application/x-www-form-urlencoded, got=%v", ct) + } + + cid, secret, ok := phttp.BasicAuth(hc.Request) + if !ok { + t.Error("unexpected error parsing basic auth") + } + + if url.QueryEscape(cfg.Credentials.ID) != cid { + t.Errorf("wrong client ID, want=%v, got=%v", cfg.Credentials.ID, cid) + } + + if url.QueryEscape(cfg.Credentials.Secret) != secret { + t.Errorf("wrong client secret, want=%v, got=%v", cfg.Credentials.Secret, secret) + } + + err = hc.Request.ParseForm() + if err != nil { + t.Error("unexpected error parsing form") + } + + gt := hc.Request.PostForm.Get("grant_type") + if gt != GrantTypeUserCreds { + t.Errorf("wrong grant_type, want=%v, got=%v", GrantTypeUserCreds, gt) + } + + sc := strings.Split(hc.Request.PostForm.Get("scope"), " ") + if !reflect.DeepEqual(c.scope, sc) { + t.Errorf("wrong scope, want=%v, got=%v", c.scope, sc) + } +} + +func TestNewAuthenticatedRequest(t *testing.T) { + tests := []struct { + authMethod string + url string + values url.Values + }{ + { + authMethod: AuthMethodClientSecretBasic, + url: "http://example.com/token", + values: url.Values{}, + }, + { + authMethod: AuthMethodClientSecretPost, + url: "http://example.com/token", + values: url.Values{}, + }, + } + + for i, tt := range tests { + cfg := Config{ + Credentials: ClientCredentials{ID: "c#id", Secret: "c secret"}, + Scope: []string{"foo-scope", "bar-scope"}, + TokenURL: "http://example.com/token", + AuthURL: "http://example.com/auth", + RedirectURL: "http://example.com/redirect", + AuthMethod: tt.authMethod, + } + c, err := NewClient(nil, cfg) + req, err := c.newAuthenticatedRequest(tt.url, tt.values) + if err != nil { + t.Errorf("case %d: unexpected error: %v", i, err) + continue + } + err = req.ParseForm() + if err != nil { + t.Errorf("case %d: want nil err, got %v", i, err) + } + + if tt.authMethod == AuthMethodClientSecretBasic { + cid, secret, ok := phttp.BasicAuth(req) + if !ok { + t.Errorf("case %d: !ok parsing Basic Auth headers", i) + continue + } + if cid != url.QueryEscape(cfg.Credentials.ID) { + t.Errorf("case %d: want CID == %q, got CID == %q", i, cfg.Credentials.ID, cid) + } + if secret != url.QueryEscape(cfg.Credentials.Secret) { + t.Errorf("case %d: want secret == %q, got secret == %q", i, cfg.Credentials.Secret, secret) + } + } else if tt.authMethod == AuthMethodClientSecretPost { + if req.PostFormValue("client_secret") != cfg.Credentials.Secret { + t.Errorf("case %d: want client_secret == %q, got client_secret == %q", + i, cfg.Credentials.Secret, req.PostFormValue("client_secret")) + } + } + + for k, v := range tt.values { + if !reflect.DeepEqual(v, req.PostForm[k]) { + t.Errorf("case %d: key:%q want==%q, got==%q", i, k, v, req.PostForm[k]) + } + } + + if req.URL.String() != tt.url { + t.Errorf("case %d: want URL==%q, got URL==%q", i, tt.url, req.URL.String()) + } + + } +} + +func TestParseTokenResponse(t *testing.T) { + type response struct { + body string + contentType string + statusCode int // defaults to http.StatusOK + } + tests := []struct { + resp response + wantResp TokenResponse + wantError *Error + }{ + { + resp: response{ + body: "{ \"error\": \"invalid_client\", \"state\": \"foo\" }", + contentType: "application/json", + statusCode: http.StatusBadRequest, + }, + wantError: &Error{Type: "invalid_client", State: "foo"}, + }, + { + resp: response{ + body: "{ \"error\": \"invalid_request\", \"state\": \"bar\" }", + contentType: "application/json", + statusCode: http.StatusBadRequest, + }, + wantError: &Error{Type: "invalid_request", State: "bar"}, + }, + { + // Actual response from bitbucket + resp: response{ + body: `{"error_description": "Invalid OAuth client credentials", "error": "unauthorized_client"}`, + contentType: "application/json", + statusCode: http.StatusBadRequest, + }, + wantError: &Error{Type: "unauthorized_client", Description: "Invalid OAuth client credentials"}, + }, + { + // Actual response from github + resp: response{ + body: `error=incorrect_client_credentials&error_description=The+client_id+and%2For+client_secret+passed+are+incorrect.&error_uri=https%3A%2F%2Fdeveloper.github.com%2Fv3%2Foauth%2F%23incorrect-client-credentials`, + contentType: "application/x-www-form-urlencoded; charset=utf-8", + }, + wantError: &Error{Type: "incorrect_client_credentials", Description: "The client_id and/or client_secret passed are incorrect."}, + }, + { + resp: response{ + body: `{"access_token":"e72e16c7e42f292c6912e7710c838347ae178b4a", "scope":"repo,gist", "token_type":"bearer"}`, + contentType: "application/json", + }, + wantResp: TokenResponse{ + AccessToken: "e72e16c7e42f292c6912e7710c838347ae178b4a", + TokenType: "bearer", + Scope: "repo,gist", + }, + }, + { + resp: response{ + body: `access_token=e72e16c7e42f292c6912e7710c838347ae178b4a&scope=user%2Cgist&token_type=bearer`, + contentType: "application/x-www-form-urlencoded", + }, + wantResp: TokenResponse{ + AccessToken: "e72e16c7e42f292c6912e7710c838347ae178b4a", + TokenType: "bearer", + Scope: "user,gist", + }, + }, + { + resp: response{ + body: `{"access_token":"foo","id_token":"bar","expires_in":200,"token_type":"bearer","refresh_token":"spam"}`, + contentType: "application/json; charset=utf-8", + }, + wantResp: TokenResponse{ + AccessToken: "foo", + IDToken: "bar", + Expires: 200, + TokenType: "bearer", + RefreshToken: "spam", + }, + }, + { + // Azure AD returns "expires_in" value as string + resp: response{ + body: `{"access_token":"foo","id_token":"bar","expires_in":"300","token_type":"bearer","refresh_token":"spam"}`, + contentType: "application/json; charset=utf-8", + }, + wantResp: TokenResponse{ + AccessToken: "foo", + IDToken: "bar", + Expires: 300, + TokenType: "bearer", + RefreshToken: "spam", + }, + }, + { + resp: response{ + body: `{"access_token":"foo","id_token":"bar","expires":200,"token_type":"bearer","refresh_token":"spam"}`, + contentType: "application/json; charset=utf-8", + }, + wantResp: TokenResponse{ + AccessToken: "foo", + IDToken: "bar", + Expires: 200, + TokenType: "bearer", + RefreshToken: "spam", + }, + }, + { + resp: response{ + body: `access_token=foo&id_token=bar&expires_in=200&token_type=bearer&refresh_token=spam`, + contentType: "application/x-www-form-urlencoded", + }, + wantResp: TokenResponse{ + AccessToken: "foo", + IDToken: "bar", + Expires: 200, + TokenType: "bearer", + RefreshToken: "spam", + }, + }, + } + + for i, tt := range tests { + r := &http.Response{ + StatusCode: http.StatusOK, + Header: http.Header{ + "Content-Type": []string{tt.resp.contentType}, + "Content-Length": []string{strconv.Itoa(len([]byte(tt.resp.body)))}, + }, + Body: ioutil.NopCloser(strings.NewReader(tt.resp.body)), + ContentLength: int64(len([]byte(tt.resp.body))), + } + if tt.resp.statusCode != 0 { + r.StatusCode = tt.resp.statusCode + } + + result, err := parseTokenResponse(r) + if err != nil { + if tt.wantError == nil { + t.Errorf("case %d: got error==%v", i, err) + continue + } + if !reflect.DeepEqual(tt.wantError, err) { + t.Errorf("case %d: want=%+v, got=%+v", i, tt.wantError, err) + } + } else { + if tt.wantError != nil { + t.Errorf("case %d: want error==%v, got==nil", i, tt.wantError) + continue + } + // don't compare the raw body (it's really big and clogs error messages) + result.RawBody = tt.wantResp.RawBody + if !reflect.DeepEqual(tt.wantResp, result) { + t.Errorf("case %d: want=%+v, got=%+v", i, tt.wantResp, result) + } + } + } +} diff --git a/vendor/github.com/ericchiang/oidc/oidc.go b/vendor/github.com/coreos/go-oidc/oidc.go similarity index 58% rename from vendor/github.com/ericchiang/oidc/oidc.go rename to vendor/github.com/coreos/go-oidc/oidc.go index c2c7eea6..378f8f64 100644 --- a/vendor/github.com/ericchiang/oidc/oidc.go +++ b/vendor/github.com/coreos/go-oidc/oidc.go @@ -1,3 +1,4 @@ +// Package oidc implements OpenID Connect client logic for the golang.org/x/oauth2 package. package oidc import ( @@ -11,13 +12,7 @@ import ( "golang.org/x/net/context" "golang.org/x/oauth2" -) - -var ( - // ErrTokenExpired indicates that a token parsed by a verifier has expired. - ErrTokenExpired = errors.New("oidc: ID Token expired") - // ErrNotSupported indicates that the requested optional OpenID Connect endpoint is not supported by the provider. - ErrNotSupported = errors.New("oidc: endpoint not supported") + jose "gopkg.in/square/go-jose.v2" ) const ( @@ -35,23 +30,61 @@ const ( ScopeOfflineAccess = "offline_access" ) -// Provider contains the subset of the OpenID Connect provider metadata needed to request -// and verify ID Tokens. +// ClientContext returns a new Context that carries the provided HTTP client. +// +// This method sets the same context key used by the golang.org/x/oauth2 package, +// so the returned context works for that package too. +// +// myClient := &http.Client{} +// ctx := oidc.ClientContext(parentContext, myClient) +// +// // This will use the custom client +// provider, err := oidc.NewProvider(ctx, "https://accounts.example.com") +// +func ClientContext(ctx context.Context, client *http.Client) context.Context { + return context.WithValue(ctx, oauth2.HTTPClient, client) +} + +func clientFromContext(ctx context.Context) *http.Client { + if client, ok := ctx.Value(oauth2.HTTPClient).(*http.Client); ok { + return client + } + return http.DefaultClient +} + +// Provider represents an OpenID Connect server's configuration. type Provider struct { + issuer string + authURL string + tokenURL string + userInfoURL string + + // Raw claims returned by the server. + rawClaims []byte + + remoteKeySet *remoteKeySet +} + +type cachedKeys struct { + keys []jose.JSONWebKey + expiry time.Time +} + +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"` - - // Raw claims returned by the server. - rawClaims []byte } -// NewProvider uses the OpenID Connect disovery mechanism to construct a Provider. +// NewProvider uses the OpenID Connect discovery mechanism to construct a Provider. +// +// The issuer is the URL identifier for the service. For example: "https://accounts.google.com" +// or "https://login.salesforce.com". func NewProvider(ctx context.Context, issuer string) (*Provider, error) { wellKnown := strings.TrimSuffix(issuer, "/") + "/.well-known/openid-configuration" - resp, err := contextClient(ctx).Get(wellKnown) + resp, err := clientFromContext(ctx).Get(wellKnown) if err != nil { return nil, err } @@ -63,18 +96,36 @@ func NewProvider(ctx context.Context, issuer string) (*Provider, error) { return nil, fmt.Errorf("%s: %s", resp.Status, body) } defer resp.Body.Close() - var p Provider + var p providerJSON if err := json.Unmarshal(body, &p); err != nil { return nil, fmt.Errorf("oidc: failed to decode provider discovery object: %v", err) } - p.rawClaims = body if p.Issuer != issuer { return nil, fmt.Errorf("oidc: issuer did not match the issuer returned by provider, expected %q got %q", issuer, p.Issuer) } - return &p, nil + return &Provider{ + issuer: p.Issuer, + authURL: p.AuthURL, + tokenURL: p.TokenURL, + userInfoURL: p.UserInfoURL, + rawClaims: body, + remoteKeySet: newRemoteKeySet(ctx, p.JWKSURL, time.Now), + }, nil } -// Claims returns additional fields returned by the server during discovery. +// Claims unmarshals raw fields returned by the server during discovery. +// +// var claims struct { +// ScopesSupported []string `json:"scopes_supported"` +// ClaimsSupported []string `json:"claims_supported"` +// } +// +// if err := provider.Claims(&claims); err != nil { +// // handle unmarshaling error +// } +// +// For a list of fields defined by the OpenID Connect spec see: +// https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata func (p *Provider) Claims(v interface{}) error { if p.rawClaims == nil { return errors.New("oidc: claims not set") @@ -84,7 +135,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, TokenURL: p.tokenURL} } // UserInfo represents the OpenID Connect userinfo claims. @@ -107,11 +158,10 @@ func (u *UserInfo) Claims(v interface{}) error { // UserInfo uses the token source to query the provider's user info endpoint. func (p *Provider) UserInfo(ctx context.Context, tokenSource oauth2.TokenSource) (*UserInfo, error) { - if p.UserInfoURL == "" { - return nil, ErrNotSupported + if p.userInfoURL == "" { + return nil, errors.New("oidc: user info endpoint is not supported by this provider") } - cli := oauth2.NewClient(ctx, tokenSource) - resp, err := cli.Get(p.UserInfoURL) + resp, err := clientFromContext(ctx).Get(p.userInfoURL) if err != nil { return nil, err } @@ -137,19 +187,6 @@ func (p *Provider) UserInfo(ctx context.Context, tokenSource oauth2.TokenSource) // // The ID Token only holds fields OpenID Connect requires. To access additional // claims returned by the server, use the Claims method. -// -// idToken, err := idTokenVerifier.Verify(rawIDToken) -// if err != nil { -// // handle error -// } -// var claims struct { -// Email string `json:"email"` -// EmailVerified bool `json:"email_verified"` -// } -// if err := idToken.Claims(&claims); err != nil { -// // handle error -// } -// type IDToken struct { // The URL of the server which issued this token. This will always be the same // as the URL used for initial discovery. @@ -165,10 +202,24 @@ type IDToken struct { Expiry time.Time Nonce string + // Raw payload of the id_token. claims []byte } // Claims unmarshals the raw JSON payload of the ID Token into a provided struct. +// +// idToken, err := idTokenVerifier.Verify(rawIDToken) +// if err != nil { +// // handle error +// } +// var claims struct { +// Email string `json:"email"` +// EmailVerified bool `json:"email_verified"` +// } +// if err := idToken.Claims(&claims); err != nil { +// // handle error +// } +// func (i *IDToken) Claims(v interface{}) error { if i.claims == nil { return errors.New("oidc: claims not set") @@ -176,6 +227,15 @@ func (i *IDToken) Claims(v interface{}) error { return json.Unmarshal(i.claims, v) } +type idToken struct { + Issuer string `json:"iss"` + Subject string `json:"sub"` + Audience audience `json:"aud"` + Expiry jsonTime `json:"exp"` + IssuedAt jsonTime `json:"iat"` + Nonce string `json:"nonce"` +} + type audience []string func (a *audience) UnmarshalJSON(b []byte) error { @@ -192,6 +252,13 @@ func (a *audience) UnmarshalJSON(b []byte) error { return nil } +func (a audience) MarshalJSON() ([]byte, error) { + if len(a) == 1 { + return json.Marshal(a[0]) + } + return json.Marshal([]string(a)) +} + type jsonTime time.Time func (j *jsonTime) UnmarshalJSON(b []byte) error { @@ -214,113 +281,6 @@ func (j *jsonTime) UnmarshalJSON(b []byte) error { return nil } -type idToken struct { - Issuer string `json:"iss"` - Subject string `json:"sub"` - Audience audience `json:"aud"` - Expiry jsonTime `json:"exp"` - IssuedAt jsonTime `json:"iat"` - Nonce string `json:"nonce"` -} - -// IDTokenVerifier provides verification for ID Tokens. -type IDTokenVerifier struct { - issuer string - keySet *remoteKeySet - options []VerificationOption -} - -// Verify parse the raw ID Token, verifies it's been signed by the provider, preforms -// additional verification, and returns the claims. -func (v *IDTokenVerifier) Verify(rawIDToken string) (*IDToken, error) { - payload, err := v.keySet.verifyJWT(rawIDToken) - if err != nil { - return nil, err - } - var token idToken - if err := json.Unmarshal(payload, &token); err != nil { - return nil, fmt.Errorf("oidc: failed to unmarshal claims: %v", err) - } - if v.issuer != token.Issuer { - return nil, fmt.Errorf("oidc: iss field did not match provider issuer") - } - t := &IDToken{ - Issuer: token.Issuer, - Subject: token.Subject, - Audience: []string(token.Audience), - Expiry: time.Time(token.Expiry), - IssuedAt: time.Time(token.Expiry), - Nonce: token.Nonce, - claims: payload, - } - for _, option := range v.options { - if err := option.verifyIDToken(t); err != nil { - return nil, err - } - } - return t, nil -} - -// NewVerifier returns an IDTokenVerifier that uses the provider's key set to verify JWTs. -// -// The verifier queries the provider to update keys when a signature cannot be verified by the -// set of keys cached from the previous request. -func (p *Provider) NewVerifier(ctx context.Context, options ...VerificationOption) *IDTokenVerifier { - return &IDTokenVerifier{ - issuer: p.Issuer, - keySet: newRemoteKeySet(ctx, p.JWKSURL), - options: options, - } -} - -// VerificationOption is an option provided to Provider.NewVerifier. -type VerificationOption interface { - verifyIDToken(token *IDToken) error -} - -// VerifyAudience ensures that an ID Token was issued for the specific client. -// -// Note that a verified token may be valid for other clients, as OpenID Connect allows a token to have -// multiple audiences. -func VerifyAudience(clientID string) VerificationOption { - return clientVerifier{clientID} -} - -type clientVerifier struct { - clientID string -} - -func (c clientVerifier) verifyIDToken(token *IDToken) error { - for _, aud := range token.Audience { - if aud == c.clientID { - return nil - } - } - return errors.New("oidc: id token aud field did not match client_id") -} - -// VerifyExpiry ensures that an ID Token has not expired. -func VerifyExpiry() VerificationOption { - return expiryVerifier{time.Now} -} - -type expiryVerifier struct { - now func() time.Time -} - -func (e expiryVerifier) verifyIDToken(token *IDToken) error { - if e.now().After(token.Expiry) { - return ErrTokenExpired - } - return nil -} - -// This method is internal to golang.org/x/oauth2. Just copy it. -func contextClient(ctx context.Context) *http.Client { - if ctx != nil { - if hc, ok := ctx.Value(oauth2.HTTPClient).(*http.Client); ok { - return hc - } - } - return http.DefaultClient +func (j jsonTime) MarshalJSON() ([]byte, error) { + return json.Marshal(time.Time(j).Unix()) } diff --git a/vendor/github.com/coreos/go-oidc/oidc/client.go b/vendor/github.com/coreos/go-oidc/oidc/client.go new file mode 100644 index 00000000..7a3cb40f --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/oidc/client.go @@ -0,0 +1,846 @@ +package oidc + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "net/mail" + "net/url" + "sync" + "time" + + phttp "github.com/coreos/go-oidc/http" + "github.com/coreos/go-oidc/jose" + "github.com/coreos/go-oidc/key" + "github.com/coreos/go-oidc/oauth2" +) + +const ( + // amount of time that must pass after the last key sync + // completes before another attempt may begin + keySyncWindow = 5 * time.Second +) + +var ( + DefaultScope = []string{"openid", "email", "profile"} + + supportedAuthMethods = map[string]struct{}{ + oauth2.AuthMethodClientSecretBasic: struct{}{}, + oauth2.AuthMethodClientSecretPost: struct{}{}, + } +) + +type ClientCredentials oauth2.ClientCredentials + +type ClientIdentity struct { + Credentials ClientCredentials + Metadata ClientMetadata +} + +type JWAOptions struct { + // SigningAlg specifies an JWA alg for signing JWTs. + // + // Specifying this field implies different actions depending on the context. It may + // require objects be serialized and signed as a JWT instead of plain JSON, or + // require an existing JWT object use the specified alg. + // + // See: http://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata + SigningAlg string + // EncryptionAlg, if provided, specifies that the returned or sent object be stored + // (or nested) within a JWT object and encrypted with the provided JWA alg. + EncryptionAlg string + // EncryptionEnc specifies the JWA enc algorithm to use with EncryptionAlg. If + // EncryptionAlg is provided and EncryptionEnc is omitted, this field defaults + // to A128CBC-HS256. + // + // If EncryptionEnc is provided EncryptionAlg must also be specified. + EncryptionEnc string +} + +func (opt JWAOptions) valid() error { + if opt.EncryptionEnc != "" && opt.EncryptionAlg == "" { + return errors.New("encryption encoding provided with no encryption algorithm") + } + return nil +} + +func (opt JWAOptions) defaults() JWAOptions { + if opt.EncryptionAlg != "" && opt.EncryptionEnc == "" { + opt.EncryptionEnc = jose.EncA128CBCHS256 + } + return opt +} + +var ( + // Ensure ClientMetadata satisfies these interfaces. + _ json.Marshaler = &ClientMetadata{} + _ json.Unmarshaler = &ClientMetadata{} +) + +// ClientMetadata holds metadata that the authorization server associates +// with a client identifier. The fields range from human-facing display +// strings such as client name, to items that impact the security of the +// protocol, such as the list of valid redirect URIs. +// +// See http://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata +// +// TODO: support language specific claim representations +// http://openid.net/specs/openid-connect-registration-1_0.html#LanguagesAndScripts +type ClientMetadata struct { + RedirectURIs []url.URL // Required + + // A list of OAuth 2.0 "response_type" values that the client wishes to restrict + // itself to. Either "code", "token", or another registered extension. + // + // If omitted, only "code" will be used. + ResponseTypes []string + // A list of OAuth 2.0 grant types the client wishes to restrict itself to. + // The grant type values used by OIDC are "authorization_code", "implicit", + // and "refresh_token". + // + // If ommitted, only "authorization_code" will be used. + GrantTypes []string + // "native" or "web". If omitted, "web". + ApplicationType string + + // List of email addresses. + Contacts []mail.Address + // Name of client to be presented to the end-user. + ClientName string + // URL that references a logo for the Client application. + LogoURI *url.URL + // URL of the home page of the Client. + ClientURI *url.URL + // Profile data policies and terms of use to be provided to the end user. + PolicyURI *url.URL + TermsOfServiceURI *url.URL + + // URL to or the value of the client's JSON Web Key Set document. + JWKSURI *url.URL + JWKS *jose.JWKSet + + // URL referencing a flie with a single JSON array of redirect URIs. + SectorIdentifierURI *url.URL + + SubjectType string + + // Options to restrict the JWS alg and enc values used for server responses and requests. + IDTokenResponseOptions JWAOptions + UserInfoResponseOptions JWAOptions + RequestObjectOptions JWAOptions + + // Client requested authorization method and signing options for the token endpoint. + // + // Defaults to "client_secret_basic" + TokenEndpointAuthMethod string + TokenEndpointAuthSigningAlg string + + // DefaultMaxAge specifies the maximum amount of time in seconds before an authorized + // user must reauthroize. + // + // If 0, no limitation is placed on the maximum. + DefaultMaxAge int64 + // RequireAuthTime specifies if the auth_time claim in the ID token is required. + RequireAuthTime bool + + // Default Authentication Context Class Reference values for authentication requests. + DefaultACRValues []string + + // URI that a third party can use to initiate a login by the relaying party. + // + // See: http://openid.net/specs/openid-connect-core-1_0.html#ThirdPartyInitiatedLogin + InitiateLoginURI *url.URL + // Pre-registered request_uri values that may be cached by the server. + RequestURIs []url.URL +} + +// Defaults returns a shallow copy of ClientMetadata with default +// values replacing omitted fields. +func (m ClientMetadata) Defaults() ClientMetadata { + if len(m.ResponseTypes) == 0 { + m.ResponseTypes = []string{oauth2.ResponseTypeCode} + } + if len(m.GrantTypes) == 0 { + m.GrantTypes = []string{oauth2.GrantTypeAuthCode} + } + if m.ApplicationType == "" { + m.ApplicationType = "web" + } + if m.TokenEndpointAuthMethod == "" { + m.TokenEndpointAuthMethod = oauth2.AuthMethodClientSecretBasic + } + m.IDTokenResponseOptions = m.IDTokenResponseOptions.defaults() + m.UserInfoResponseOptions = m.UserInfoResponseOptions.defaults() + m.RequestObjectOptions = m.RequestObjectOptions.defaults() + return m +} + +func (m *ClientMetadata) MarshalJSON() ([]byte, error) { + e := m.toEncodableStruct() + return json.Marshal(&e) +} + +func (m *ClientMetadata) UnmarshalJSON(data []byte) error { + var e encodableClientMetadata + if err := json.Unmarshal(data, &e); err != nil { + return err + } + meta, err := e.toStruct() + if err != nil { + return err + } + if err := meta.Valid(); err != nil { + return err + } + *m = meta + return nil +} + +type encodableClientMetadata struct { + RedirectURIs []string `json:"redirect_uris"` // Required + ResponseTypes []string `json:"response_types,omitempty"` + GrantTypes []string `json:"grant_types,omitempty"` + ApplicationType string `json:"application_type,omitempty"` + Contacts []string `json:"contacts,omitempty"` + ClientName string `json:"client_name,omitempty"` + LogoURI string `json:"logo_uri,omitempty"` + ClientURI string `json:"client_uri,omitempty"` + PolicyURI string `json:"policy_uri,omitempty"` + TermsOfServiceURI string `json:"tos_uri,omitempty"` + JWKSURI string `json:"jwks_uri,omitempty"` + JWKS *jose.JWKSet `json:"jwks,omitempty"` + SectorIdentifierURI string `json:"sector_identifier_uri,omitempty"` + SubjectType string `json:"subject_type,omitempty"` + IDTokenSignedResponseAlg string `json:"id_token_signed_response_alg,omitempty"` + IDTokenEncryptedResponseAlg string `json:"id_token_encrypted_response_alg,omitempty"` + IDTokenEncryptedResponseEnc string `json:"id_token_encrypted_response_enc,omitempty"` + UserInfoSignedResponseAlg string `json:"userinfo_signed_response_alg,omitempty"` + UserInfoEncryptedResponseAlg string `json:"userinfo_encrypted_response_alg,omitempty"` + UserInfoEncryptedResponseEnc string `json:"userinfo_encrypted_response_enc,omitempty"` + RequestObjectSigningAlg string `json:"request_object_signing_alg,omitempty"` + RequestObjectEncryptionAlg string `json:"request_object_encryption_alg,omitempty"` + RequestObjectEncryptionEnc string `json:"request_object_encryption_enc,omitempty"` + TokenEndpointAuthMethod string `json:"token_endpoint_auth_method,omitempty"` + TokenEndpointAuthSigningAlg string `json:"token_endpoint_auth_signing_alg,omitempty"` + DefaultMaxAge int64 `json:"default_max_age,omitempty"` + RequireAuthTime bool `json:"require_auth_time,omitempty"` + DefaultACRValues []string `json:"default_acr_values,omitempty"` + InitiateLoginURI string `json:"initiate_login_uri,omitempty"` + RequestURIs []string `json:"request_uris,omitempty"` +} + +func (c *encodableClientMetadata) toStruct() (ClientMetadata, error) { + p := stickyErrParser{} + m := ClientMetadata{ + RedirectURIs: p.parseURIs(c.RedirectURIs, "redirect_uris"), + ResponseTypes: c.ResponseTypes, + GrantTypes: c.GrantTypes, + ApplicationType: c.ApplicationType, + Contacts: p.parseEmails(c.Contacts, "contacts"), + ClientName: c.ClientName, + LogoURI: p.parseURI(c.LogoURI, "logo_uri"), + ClientURI: p.parseURI(c.ClientURI, "client_uri"), + PolicyURI: p.parseURI(c.PolicyURI, "policy_uri"), + TermsOfServiceURI: p.parseURI(c.TermsOfServiceURI, "tos_uri"), + JWKSURI: p.parseURI(c.JWKSURI, "jwks_uri"), + JWKS: c.JWKS, + SectorIdentifierURI: p.parseURI(c.SectorIdentifierURI, "sector_identifier_uri"), + SubjectType: c.SubjectType, + TokenEndpointAuthMethod: c.TokenEndpointAuthMethod, + TokenEndpointAuthSigningAlg: c.TokenEndpointAuthSigningAlg, + DefaultMaxAge: c.DefaultMaxAge, + RequireAuthTime: c.RequireAuthTime, + DefaultACRValues: c.DefaultACRValues, + InitiateLoginURI: p.parseURI(c.InitiateLoginURI, "initiate_login_uri"), + RequestURIs: p.parseURIs(c.RequestURIs, "request_uris"), + IDTokenResponseOptions: JWAOptions{ + c.IDTokenSignedResponseAlg, + c.IDTokenEncryptedResponseAlg, + c.IDTokenEncryptedResponseEnc, + }, + UserInfoResponseOptions: JWAOptions{ + c.UserInfoSignedResponseAlg, + c.UserInfoEncryptedResponseAlg, + c.UserInfoEncryptedResponseEnc, + }, + RequestObjectOptions: JWAOptions{ + c.RequestObjectSigningAlg, + c.RequestObjectEncryptionAlg, + c.RequestObjectEncryptionEnc, + }, + } + if p.firstErr != nil { + return ClientMetadata{}, p.firstErr + } + return m, nil +} + +// stickyErrParser parses URIs and email addresses. Once it encounters +// a parse error, subsequent calls become no-op. +type stickyErrParser struct { + firstErr error +} + +func (p *stickyErrParser) parseURI(s, field string) *url.URL { + if p.firstErr != nil || s == "" { + return nil + } + u, err := url.Parse(s) + if err == nil { + if u.Host == "" { + err = errors.New("no host in URI") + } else if u.Scheme != "http" && u.Scheme != "https" { + err = errors.New("invalid URI scheme") + } + } + if err != nil { + p.firstErr = fmt.Errorf("failed to parse %s: %v", field, err) + return nil + } + return u +} + +func (p *stickyErrParser) parseURIs(s []string, field string) []url.URL { + if p.firstErr != nil || len(s) == 0 { + return nil + } + uris := make([]url.URL, len(s)) + for i, val := range s { + if val == "" { + p.firstErr = fmt.Errorf("invalid URI in field %s", field) + return nil + } + if u := p.parseURI(val, field); u != nil { + uris[i] = *u + } + } + return uris +} + +func (p *stickyErrParser) parseEmails(s []string, field string) []mail.Address { + if p.firstErr != nil || len(s) == 0 { + return nil + } + addrs := make([]mail.Address, len(s)) + for i, addr := range s { + if addr == "" { + p.firstErr = fmt.Errorf("invalid email in field %s", field) + return nil + } + a, err := mail.ParseAddress(addr) + if err != nil { + p.firstErr = fmt.Errorf("invalid email in field %s: %v", field, err) + return nil + } + addrs[i] = *a + } + return addrs +} + +func (m *ClientMetadata) toEncodableStruct() encodableClientMetadata { + return encodableClientMetadata{ + RedirectURIs: urisToStrings(m.RedirectURIs), + ResponseTypes: m.ResponseTypes, + GrantTypes: m.GrantTypes, + ApplicationType: m.ApplicationType, + Contacts: emailsToStrings(m.Contacts), + ClientName: m.ClientName, + LogoURI: uriToString(m.LogoURI), + ClientURI: uriToString(m.ClientURI), + PolicyURI: uriToString(m.PolicyURI), + TermsOfServiceURI: uriToString(m.TermsOfServiceURI), + JWKSURI: uriToString(m.JWKSURI), + JWKS: m.JWKS, + SectorIdentifierURI: uriToString(m.SectorIdentifierURI), + SubjectType: m.SubjectType, + IDTokenSignedResponseAlg: m.IDTokenResponseOptions.SigningAlg, + IDTokenEncryptedResponseAlg: m.IDTokenResponseOptions.EncryptionAlg, + IDTokenEncryptedResponseEnc: m.IDTokenResponseOptions.EncryptionEnc, + UserInfoSignedResponseAlg: m.UserInfoResponseOptions.SigningAlg, + UserInfoEncryptedResponseAlg: m.UserInfoResponseOptions.EncryptionAlg, + UserInfoEncryptedResponseEnc: m.UserInfoResponseOptions.EncryptionEnc, + RequestObjectSigningAlg: m.RequestObjectOptions.SigningAlg, + RequestObjectEncryptionAlg: m.RequestObjectOptions.EncryptionAlg, + RequestObjectEncryptionEnc: m.RequestObjectOptions.EncryptionEnc, + TokenEndpointAuthMethod: m.TokenEndpointAuthMethod, + TokenEndpointAuthSigningAlg: m.TokenEndpointAuthSigningAlg, + DefaultMaxAge: m.DefaultMaxAge, + RequireAuthTime: m.RequireAuthTime, + DefaultACRValues: m.DefaultACRValues, + InitiateLoginURI: uriToString(m.InitiateLoginURI), + RequestURIs: urisToStrings(m.RequestURIs), + } +} + +func uriToString(u *url.URL) string { + if u == nil { + return "" + } + return u.String() +} + +func urisToStrings(urls []url.URL) []string { + if len(urls) == 0 { + return nil + } + sli := make([]string, len(urls)) + for i, u := range urls { + sli[i] = u.String() + } + return sli +} + +func emailsToStrings(addrs []mail.Address) []string { + if len(addrs) == 0 { + return nil + } + sli := make([]string, len(addrs)) + for i, addr := range addrs { + sli[i] = addr.String() + } + return sli +} + +// Valid determines if a ClientMetadata conforms with the OIDC specification. +// +// Valid is called by UnmarshalJSON. +// +// NOTE(ericchiang): For development purposes Valid does not mandate 'https' for +// URLs fields where the OIDC spec requires it. This may change in future releases +// of this package. See: https://github.com/coreos/go-oidc/issues/34 +func (m *ClientMetadata) Valid() error { + if len(m.RedirectURIs) == 0 { + return errors.New("zero redirect URLs") + } + + validURI := func(u *url.URL, fieldName string) error { + if u.Host == "" { + return fmt.Errorf("no host for uri field %s", fieldName) + } + if u.Scheme != "http" && u.Scheme != "https" { + return fmt.Errorf("uri field %s scheme is not http or https", fieldName) + } + return nil + } + + uris := []struct { + val *url.URL + name string + }{ + {m.LogoURI, "logo_uri"}, + {m.ClientURI, "client_uri"}, + {m.PolicyURI, "policy_uri"}, + {m.TermsOfServiceURI, "tos_uri"}, + {m.JWKSURI, "jwks_uri"}, + {m.SectorIdentifierURI, "sector_identifier_uri"}, + {m.InitiateLoginURI, "initiate_login_uri"}, + } + + for _, uri := range uris { + if uri.val == nil { + continue + } + if err := validURI(uri.val, uri.name); err != nil { + return err + } + } + + uriLists := []struct { + vals []url.URL + name string + }{ + {m.RedirectURIs, "redirect_uris"}, + {m.RequestURIs, "request_uris"}, + } + for _, list := range uriLists { + for _, uri := range list.vals { + if err := validURI(&uri, list.name); err != nil { + return err + } + } + } + + options := []struct { + option JWAOptions + name string + }{ + {m.IDTokenResponseOptions, "id_token response"}, + {m.UserInfoResponseOptions, "userinfo response"}, + {m.RequestObjectOptions, "request_object"}, + } + for _, option := range options { + if err := option.option.valid(); err != nil { + return fmt.Errorf("invalid JWA values for %s: %v", option.name, err) + } + } + return nil +} + +type ClientRegistrationResponse struct { + ClientID string // Required + ClientSecret string + RegistrationAccessToken string + RegistrationClientURI string + // If IsZero is true, unspecified. + ClientIDIssuedAt time.Time + // Time at which the client_secret will expire. + // If IsZero is true, it will not expire. + ClientSecretExpiresAt time.Time + + ClientMetadata +} + +type encodableClientRegistrationResponse struct { + ClientID string `json:"client_id"` // Required + ClientSecret string `json:"client_secret,omitempty"` + RegistrationAccessToken string `json:"registration_access_token,omitempty"` + RegistrationClientURI string `json:"registration_client_uri,omitempty"` + ClientIDIssuedAt int64 `json:"client_id_issued_at,omitempty"` + // Time at which the client_secret will expire, in seconds since the epoch. + // If 0 it will not expire. + ClientSecretExpiresAt int64 `json:"client_secret_expires_at"` // Required + + encodableClientMetadata +} + +func unixToSec(t time.Time) int64 { + if t.IsZero() { + return 0 + } + return t.Unix() +} + +func (c *ClientRegistrationResponse) MarshalJSON() ([]byte, error) { + e := encodableClientRegistrationResponse{ + ClientID: c.ClientID, + ClientSecret: c.ClientSecret, + RegistrationAccessToken: c.RegistrationAccessToken, + RegistrationClientURI: c.RegistrationClientURI, + ClientIDIssuedAt: unixToSec(c.ClientIDIssuedAt), + ClientSecretExpiresAt: unixToSec(c.ClientSecretExpiresAt), + encodableClientMetadata: c.ClientMetadata.toEncodableStruct(), + } + return json.Marshal(&e) +} + +func secToUnix(sec int64) time.Time { + if sec == 0 { + return time.Time{} + } + return time.Unix(sec, 0) +} + +func (c *ClientRegistrationResponse) UnmarshalJSON(data []byte) error { + var e encodableClientRegistrationResponse + if err := json.Unmarshal(data, &e); err != nil { + return err + } + if e.ClientID == "" { + return errors.New("no client_id in client registration response") + } + metadata, err := e.encodableClientMetadata.toStruct() + if err != nil { + return err + } + *c = ClientRegistrationResponse{ + ClientID: e.ClientID, + ClientSecret: e.ClientSecret, + RegistrationAccessToken: e.RegistrationAccessToken, + RegistrationClientURI: e.RegistrationClientURI, + ClientIDIssuedAt: secToUnix(e.ClientIDIssuedAt), + ClientSecretExpiresAt: secToUnix(e.ClientSecretExpiresAt), + ClientMetadata: metadata, + } + return nil +} + +type ClientConfig struct { + HTTPClient phttp.Client + Credentials ClientCredentials + Scope []string + RedirectURL string + ProviderConfig ProviderConfig + KeySet key.PublicKeySet +} + +func NewClient(cfg ClientConfig) (*Client, error) { + // Allow empty redirect URL in the case where the client + // only needs to verify a given token. + ru, err := url.Parse(cfg.RedirectURL) + if err != nil { + return nil, fmt.Errorf("invalid redirect URL: %v", err) + } + + c := Client{ + credentials: cfg.Credentials, + httpClient: cfg.HTTPClient, + scope: cfg.Scope, + redirectURL: ru.String(), + providerConfig: newProviderConfigRepo(cfg.ProviderConfig), + keySet: cfg.KeySet, + } + + if c.httpClient == nil { + c.httpClient = http.DefaultClient + } + + if c.scope == nil { + c.scope = make([]string, len(DefaultScope)) + copy(c.scope, DefaultScope) + } + + return &c, nil +} + +type Client struct { + httpClient phttp.Client + providerConfig *providerConfigRepo + credentials ClientCredentials + redirectURL string + scope []string + keySet key.PublicKeySet + providerSyncer *ProviderConfigSyncer + + keySetSyncMutex sync.RWMutex + lastKeySetSync time.Time +} + +func (c *Client) Healthy() error { + now := time.Now().UTC() + + cfg := c.providerConfig.Get() + + if cfg.Empty() { + return errors.New("oidc client provider config empty") + } + + if !cfg.ExpiresAt.IsZero() && cfg.ExpiresAt.Before(now) { + return errors.New("oidc client provider config expired") + } + + return nil +} + +func (c *Client) OAuthClient() (*oauth2.Client, error) { + cfg := c.providerConfig.Get() + authMethod, err := chooseAuthMethod(cfg) + if err != nil { + return nil, err + } + + ocfg := oauth2.Config{ + Credentials: oauth2.ClientCredentials(c.credentials), + RedirectURL: c.redirectURL, + AuthURL: cfg.AuthEndpoint.String(), + TokenURL: cfg.TokenEndpoint.String(), + Scope: c.scope, + AuthMethod: authMethod, + } + + return oauth2.NewClient(c.httpClient, ocfg) +} + +func chooseAuthMethod(cfg ProviderConfig) (string, error) { + if len(cfg.TokenEndpointAuthMethodsSupported) == 0 { + return oauth2.AuthMethodClientSecretBasic, nil + } + + for _, authMethod := range cfg.TokenEndpointAuthMethodsSupported { + if _, ok := supportedAuthMethods[authMethod]; ok { + return authMethod, nil + } + } + + return "", errors.New("no supported auth methods") +} + +// SyncProviderConfig starts the provider config syncer +func (c *Client) SyncProviderConfig(discoveryURL string) chan struct{} { + r := NewHTTPProviderConfigGetter(c.httpClient, discoveryURL) + s := NewProviderConfigSyncer(r, c.providerConfig) + stop := s.Run() + s.WaitUntilInitialSync() + return stop +} + +func (c *Client) maybeSyncKeys() error { + tooSoon := func() bool { + return time.Now().UTC().Before(c.lastKeySetSync.Add(keySyncWindow)) + } + + // ignore request to sync keys if a sync operation has been + // attempted too recently + if tooSoon() { + return nil + } + + c.keySetSyncMutex.Lock() + defer c.keySetSyncMutex.Unlock() + + // check again, as another goroutine may have been holding + // the lock while updating the keys + if tooSoon() { + return nil + } + + cfg := c.providerConfig.Get() + r := NewRemotePublicKeyRepo(c.httpClient, cfg.KeysEndpoint.String()) + w := &clientKeyRepo{client: c} + _, err := key.Sync(r, w) + c.lastKeySetSync = time.Now().UTC() + + return err +} + +type clientKeyRepo struct { + client *Client +} + +func (r *clientKeyRepo) Set(ks key.KeySet) error { + pks, ok := ks.(*key.PublicKeySet) + if !ok { + return errors.New("unable to cast to PublicKey") + } + r.client.keySet = *pks + return nil +} + +func (c *Client) ClientCredsToken(scope []string) (jose.JWT, error) { + cfg := c.providerConfig.Get() + + if !cfg.SupportsGrantType(oauth2.GrantTypeClientCreds) { + return jose.JWT{}, fmt.Errorf("%v grant type is not supported", oauth2.GrantTypeClientCreds) + } + + oac, err := c.OAuthClient() + if err != nil { + return jose.JWT{}, err + } + + t, err := oac.ClientCredsToken(scope) + if err != nil { + return jose.JWT{}, err + } + + jwt, err := jose.ParseJWT(t.IDToken) + if err != nil { + return jose.JWT{}, err + } + + return jwt, c.VerifyJWT(jwt) +} + +// ExchangeAuthCode exchanges an OAuth2 auth code for an OIDC JWT ID token. +func (c *Client) ExchangeAuthCode(code string) (jose.JWT, error) { + oac, err := c.OAuthClient() + if err != nil { + return jose.JWT{}, err + } + + t, err := oac.RequestToken(oauth2.GrantTypeAuthCode, code) + if err != nil { + return jose.JWT{}, err + } + + jwt, err := jose.ParseJWT(t.IDToken) + if err != nil { + return jose.JWT{}, err + } + + return jwt, c.VerifyJWT(jwt) +} + +// RefreshToken uses a refresh token to exchange for a new OIDC JWT ID Token. +func (c *Client) RefreshToken(refreshToken string) (jose.JWT, error) { + oac, err := c.OAuthClient() + if err != nil { + return jose.JWT{}, err + } + + t, err := oac.RequestToken(oauth2.GrantTypeRefreshToken, refreshToken) + if err != nil { + return jose.JWT{}, err + } + + jwt, err := jose.ParseJWT(t.IDToken) + if err != nil { + return jose.JWT{}, err + } + + return jwt, c.VerifyJWT(jwt) +} + +func (c *Client) VerifyJWT(jwt jose.JWT) error { + var keysFunc func() []key.PublicKey + if kID, ok := jwt.KeyID(); ok { + keysFunc = c.keysFuncWithID(kID) + } else { + keysFunc = c.keysFuncAll() + } + + v := NewJWTVerifier( + c.providerConfig.Get().Issuer.String(), + c.credentials.ID, + c.maybeSyncKeys, keysFunc) + + return v.Verify(jwt) +} + +// keysFuncWithID returns a function that retrieves at most unexpired +// public key from the Client that matches the provided ID +func (c *Client) keysFuncWithID(kID string) func() []key.PublicKey { + return func() []key.PublicKey { + c.keySetSyncMutex.RLock() + defer c.keySetSyncMutex.RUnlock() + + if c.keySet.ExpiresAt().Before(time.Now()) { + return []key.PublicKey{} + } + + k := c.keySet.Key(kID) + if k == nil { + return []key.PublicKey{} + } + + return []key.PublicKey{*k} + } +} + +// keysFuncAll returns a function that retrieves all unexpired public +// keys from the Client +func (c *Client) keysFuncAll() func() []key.PublicKey { + return func() []key.PublicKey { + c.keySetSyncMutex.RLock() + defer c.keySetSyncMutex.RUnlock() + + if c.keySet.ExpiresAt().Before(time.Now()) { + return []key.PublicKey{} + } + + return c.keySet.Keys() + } +} + +type providerConfigRepo struct { + mu sync.RWMutex + config ProviderConfig // do not access directly, use Get() +} + +func newProviderConfigRepo(pc ProviderConfig) *providerConfigRepo { + return &providerConfigRepo{sync.RWMutex{}, pc} +} + +// returns an error to implement ProviderConfigSetter +func (r *providerConfigRepo) Set(cfg ProviderConfig) error { + r.mu.Lock() + defer r.mu.Unlock() + r.config = cfg + return nil +} + +func (r *providerConfigRepo) Get() ProviderConfig { + r.mu.RLock() + defer r.mu.RUnlock() + return r.config +} diff --git a/vendor/github.com/coreos/go-oidc/oidc/client_race_test.go b/vendor/github.com/coreos/go-oidc/oidc/client_race_test.go new file mode 100644 index 00000000..315eaf24 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/oidc/client_race_test.go @@ -0,0 +1,81 @@ +// This file contains tests which depend on the race detector being enabled. +// +build race + +package oidc + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "net/url" + "testing" + "time" +) + +type testProvider struct { + baseURL *url.URL +} + +func (p *testProvider) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != discoveryConfigPath { + http.NotFound(w, r) + return + } + + cfg := ProviderConfig{ + Issuer: p.baseURL, + ExpiresAt: time.Now().Add(time.Second), + } + cfg = fillRequiredProviderFields(cfg) + json.NewEncoder(w).Encode(&cfg) +} + +// This test fails by triggering the race detector, not by calling t.Error or t.Fatal. +func TestProviderSyncRace(t *testing.T) { + + prov := &testProvider{} + + s := httptest.NewServer(prov) + defer s.Close() + u, err := url.Parse(s.URL) + if err != nil { + t.Fatal(err) + } + prov.baseURL = u + + prevValue := minimumProviderConfigSyncInterval + defer func() { minimumProviderConfigSyncInterval = prevValue }() + + // Reduce the sync interval to increase the write frequencey. + minimumProviderConfigSyncInterval = 5 * time.Millisecond + + cliCfg := ClientConfig{ + HTTPClient: http.DefaultClient, + } + cli, err := NewClient(cliCfg) + if err != nil { + t.Error(err) + return + } + + if !cli.providerConfig.Get().Empty() { + t.Errorf("want c.ProviderConfig == nil, got c.ProviderConfig=%#v") + } + + // SyncProviderConfig beings a goroutine which writes to the client's provider config. + c := cli.SyncProviderConfig(s.URL) + if cli.providerConfig.Get().Empty() { + t.Errorf("want c.ProviderConfig != nil") + } + + defer func() { + // stop the background process + c <- struct{}{} + }() + + for i := 0; i < 10; i++ { + time.Sleep(5 * time.Millisecond) + // Creating an OAuth client reads from the provider config. + cli.OAuthClient() + } +} diff --git a/vendor/github.com/coreos/go-oidc/oidc/client_test.go b/vendor/github.com/coreos/go-oidc/oidc/client_test.go new file mode 100644 index 00000000..a0701b5d --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/oidc/client_test.go @@ -0,0 +1,654 @@ +package oidc + +import ( + "encoding/json" + "net/mail" + "net/url" + "reflect" + "testing" + "time" + + "github.com/coreos/go-oidc/jose" + "github.com/coreos/go-oidc/key" + "github.com/coreos/go-oidc/oauth2" + "github.com/kylelemons/godebug/pretty" +) + +func TestNewClientScopeDefault(t *testing.T) { + tests := []struct { + c ClientConfig + e []string + }{ + { + // No scope + c: ClientConfig{RedirectURL: "http://example.com/redirect"}, + e: DefaultScope, + }, + { + // Nil scope + c: ClientConfig{RedirectURL: "http://example.com/redirect", Scope: nil}, + e: DefaultScope, + }, + { + // Empty scope + c: ClientConfig{RedirectURL: "http://example.com/redirect", Scope: []string{}}, + e: []string{}, + }, + { + // Custom scope equal to default + c: ClientConfig{RedirectURL: "http://example.com/redirect", Scope: []string{"openid", "email", "profile"}}, + e: DefaultScope, + }, + { + // Custom scope not including defaults + c: ClientConfig{RedirectURL: "http://example.com/redirect", Scope: []string{"foo", "bar"}}, + e: []string{"foo", "bar"}, + }, + { + // Custom scopes overlapping with defaults + c: ClientConfig{RedirectURL: "http://example.com/redirect", Scope: []string{"openid", "foo"}}, + e: []string{"openid", "foo"}, + }, + } + + for i, tt := range tests { + c, err := NewClient(tt.c) + if err != nil { + t.Errorf("case %d: unexpected error from NewClient: %v", i, err) + continue + } + if !reflect.DeepEqual(tt.e, c.scope) { + t.Errorf("case %d: want: %v, got: %v", i, tt.e, c.scope) + } + } +} + +func TestHealthy(t *testing.T) { + now := time.Now().UTC() + + tests := []struct { + p ProviderConfig + h bool + }{ + // all ok + { + p: ProviderConfig{ + Issuer: &url.URL{Scheme: "http", Host: "example.com"}, + ExpiresAt: now.Add(time.Hour), + }, + h: true, + }, + // zero-value ProviderConfig.ExpiresAt + { + p: ProviderConfig{ + Issuer: &url.URL{Scheme: "http", Host: "example.com"}, + }, + h: true, + }, + // expired ProviderConfig + { + p: ProviderConfig{ + Issuer: &url.URL{Scheme: "http", Host: "example.com"}, + ExpiresAt: now.Add(time.Hour * -1), + }, + h: false, + }, + // empty ProviderConfig + { + p: ProviderConfig{}, + h: false, + }, + } + + for i, tt := range tests { + c := &Client{providerConfig: newProviderConfigRepo(tt.p)} + err := c.Healthy() + want := tt.h + got := (err == nil) + + if want != got { + t.Errorf("case %d: want: healthy=%v, got: healhty=%v, err: %v", i, want, got, err) + } + } +} + +func TestClientKeysFuncAll(t *testing.T) { + priv1, err := key.GeneratePrivateKey() + if err != nil { + t.Fatalf("failed to generate private key, error=%v", err) + } + + priv2, err := key.GeneratePrivateKey() + if err != nil { + t.Fatalf("failed to generate private key, error=%v", err) + } + + now := time.Now() + future := now.Add(time.Hour) + past := now.Add(-1 * time.Hour) + + tests := []struct { + keySet *key.PublicKeySet + want []key.PublicKey + }{ + // two keys, non-expired set + { + keySet: key.NewPublicKeySet([]jose.JWK{priv2.JWK(), priv1.JWK()}, future), + want: []key.PublicKey{*key.NewPublicKey(priv2.JWK()), *key.NewPublicKey(priv1.JWK())}, + }, + + // no keys, non-expired set + { + keySet: key.NewPublicKeySet([]jose.JWK{}, future), + want: []key.PublicKey{}, + }, + + // two keys, expired set + { + keySet: key.NewPublicKeySet([]jose.JWK{priv2.JWK(), priv1.JWK()}, past), + want: []key.PublicKey{}, + }, + + // no keys, expired set + { + keySet: key.NewPublicKeySet([]jose.JWK{}, past), + want: []key.PublicKey{}, + }, + } + + for i, tt := range tests { + var c Client + c.keySet = *tt.keySet + keysFunc := c.keysFuncAll() + got := keysFunc() + if !reflect.DeepEqual(tt.want, got) { + t.Errorf("case %d: want=%#v got=%#v", i, tt.want, got) + } + } +} + +func TestClientKeysFuncWithID(t *testing.T) { + priv1, err := key.GeneratePrivateKey() + if err != nil { + t.Fatalf("failed to generate private key, error=%v", err) + } + + priv2, err := key.GeneratePrivateKey() + if err != nil { + t.Fatalf("failed to generate private key, error=%v", err) + } + + now := time.Now() + future := now.Add(time.Hour) + past := now.Add(-1 * time.Hour) + + tests := []struct { + keySet *key.PublicKeySet + argID string + want []key.PublicKey + }{ + // two keys, match, non-expired set + { + keySet: key.NewPublicKeySet([]jose.JWK{priv2.JWK(), priv1.JWK()}, future), + argID: priv2.ID(), + want: []key.PublicKey{*key.NewPublicKey(priv2.JWK())}, + }, + + // two keys, no match, non-expired set + { + keySet: key.NewPublicKeySet([]jose.JWK{priv2.JWK(), priv1.JWK()}, future), + argID: "XXX", + want: []key.PublicKey{}, + }, + + // no keys, no match, non-expired set + { + keySet: key.NewPublicKeySet([]jose.JWK{}, future), + argID: priv2.ID(), + want: []key.PublicKey{}, + }, + + // two keys, match, expired set + { + keySet: key.NewPublicKeySet([]jose.JWK{priv2.JWK(), priv1.JWK()}, past), + argID: priv2.ID(), + want: []key.PublicKey{}, + }, + + // no keys, no match, expired set + { + keySet: key.NewPublicKeySet([]jose.JWK{}, past), + argID: priv2.ID(), + want: []key.PublicKey{}, + }, + } + + for i, tt := range tests { + var c Client + c.keySet = *tt.keySet + keysFunc := c.keysFuncWithID(tt.argID) + got := keysFunc() + if !reflect.DeepEqual(tt.want, got) { + t.Errorf("case %d: want=%#v got=%#v", i, tt.want, got) + } + } +} + +func TestClientMetadataValid(t *testing.T) { + tests := []ClientMetadata{ + // one RedirectURL + ClientMetadata{ + RedirectURIs: []url.URL{url.URL{Scheme: "http", Host: "example.com"}}, + }, + + // one RedirectURL w/ nonempty path + ClientMetadata{ + RedirectURIs: []url.URL{url.URL{Scheme: "http", Host: "example.com", Path: "/foo"}}, + }, + + // two RedirectURIs + ClientMetadata{ + RedirectURIs: []url.URL{ + url.URL{Scheme: "http", Host: "foo.example.com"}, + url.URL{Scheme: "http", Host: "bar.example.com"}, + }, + }, + } + + for i, tt := range tests { + if err := tt.Valid(); err != nil { + t.Errorf("case %d: unexpected error: %v", i, err) + } + } +} + +func TestClientMetadataInvalid(t *testing.T) { + tests := []ClientMetadata{ + // nil RedirectURls slice + ClientMetadata{ + RedirectURIs: nil, + }, + + // empty RedirectURIs slice + ClientMetadata{ + RedirectURIs: []url.URL{}, + }, + + // empty url.URL + ClientMetadata{ + RedirectURIs: []url.URL{url.URL{}}, + }, + + // empty url.URL following OK item + ClientMetadata{ + RedirectURIs: []url.URL{url.URL{Scheme: "http", Host: "example.com"}, url.URL{}}, + }, + + // url.URL with empty Host + ClientMetadata{ + RedirectURIs: []url.URL{url.URL{Scheme: "http", Host: ""}}, + }, + + // url.URL with empty Scheme + ClientMetadata{ + RedirectURIs: []url.URL{url.URL{Scheme: "", Host: "example.com"}}, + }, + + // url.URL with non-HTTP(S) Scheme + ClientMetadata{ + RedirectURIs: []url.URL{url.URL{Scheme: "tcp", Host: "127.0.0.1"}}, + }, + + // EncryptionEnc without EncryptionAlg + ClientMetadata{ + RedirectURIs: []url.URL{url.URL{Scheme: "http", Host: "example.com"}}, + IDTokenResponseOptions: JWAOptions{ + EncryptionEnc: "A128CBC-HS256", + }, + }, + + // List of URIs with one empty element + ClientMetadata{ + RedirectURIs: []url.URL{url.URL{Scheme: "http", Host: "example.com"}}, + RequestURIs: []url.URL{ + url.URL{Scheme: "http", Host: "example.com"}, + url.URL{}, + }, + }, + } + + for i, tt := range tests { + if err := tt.Valid(); err == nil { + t.Errorf("case %d: expected non-nil error", i) + } + } +} + +func TestChooseAuthMethod(t *testing.T) { + tests := []struct { + supported []string + chosen string + err bool + }{ + { + supported: []string{}, + chosen: oauth2.AuthMethodClientSecretBasic, + }, + { + supported: []string{oauth2.AuthMethodClientSecretBasic}, + chosen: oauth2.AuthMethodClientSecretBasic, + }, + { + supported: []string{oauth2.AuthMethodClientSecretPost}, + chosen: oauth2.AuthMethodClientSecretPost, + }, + { + supported: []string{oauth2.AuthMethodClientSecretPost, oauth2.AuthMethodClientSecretBasic}, + chosen: oauth2.AuthMethodClientSecretPost, + }, + { + supported: []string{oauth2.AuthMethodClientSecretBasic, oauth2.AuthMethodClientSecretPost}, + chosen: oauth2.AuthMethodClientSecretBasic, + }, + { + supported: []string{oauth2.AuthMethodClientSecretJWT, oauth2.AuthMethodClientSecretPost}, + chosen: oauth2.AuthMethodClientSecretPost, + }, + { + supported: []string{oauth2.AuthMethodClientSecretJWT}, + chosen: "", + err: true, + }, + } + + for i, tt := range tests { + cfg := ProviderConfig{ + TokenEndpointAuthMethodsSupported: tt.supported, + } + got, err := chooseAuthMethod(cfg) + if tt.err { + if err == nil { + t.Errorf("case %d: expected non-nil err", i) + } + continue + } + + if got != tt.chosen { + t.Errorf("case %d: want=%q, got=%q", i, tt.chosen, got) + } + } +} + +func TestClientMetadataUnmarshal(t *testing.T) { + tests := []struct { + data string + want ClientMetadata + wantErr bool + }{ + { + `{"redirect_uris":["https://example.com"]}`, + ClientMetadata{ + RedirectURIs: []url.URL{ + {Scheme: "https", Host: "example.com"}, + }, + }, + false, + }, + { + // redirect_uris required + `{}`, + ClientMetadata{}, + true, + }, + { + // must have at least one redirect_uris + `{"redirect_uris":[]}`, + ClientMetadata{}, + true, + }, + { + `{"redirect_uris":["https://example.com"],"contacts":["Ms. Foo <foo@example.com>"]}`, + ClientMetadata{ + RedirectURIs: []url.URL{ + {Scheme: "https", Host: "example.com"}, + }, + Contacts: []mail.Address{ + {Name: "Ms. Foo", Address: "foo@example.com"}, + }, + }, + false, + }, + { + // invalid URI provided for field + `{"redirect_uris":["https://example.com"],"logo_uri":"not a valid uri"}`, + ClientMetadata{}, + true, + }, + { + // logo_uri can't be a list + `{"redirect_uris":["https://example.com"],"logo_uri":["https://example.com/logo"]}`, + ClientMetadata{}, + true, + }, + { + `{ + "redirect_uris":["https://example.com"], + "userinfo_encrypted_response_alg":"RSA1_5", + "userinfo_encrypted_response_enc":"A128CBC-HS256", + "contacts": [ + "jane doe <jane.doe@example.com>", "john doe <john.doe@example.com>" + ] + }`, + ClientMetadata{ + RedirectURIs: []url.URL{ + {Scheme: "https", Host: "example.com"}, + }, + UserInfoResponseOptions: JWAOptions{ + EncryptionAlg: "RSA1_5", + EncryptionEnc: "A128CBC-HS256", + }, + Contacts: []mail.Address{ + {Name: "jane doe", Address: "jane.doe@example.com"}, + {Name: "john doe", Address: "john.doe@example.com"}, + }, + }, + false, + }, + { + // If encrypted_response_enc is provided encrypted_response_alg must also be. + `{ + "redirect_uris":["https://example.com"], + "userinfo_encrypted_response_enc":"A128CBC-HS256" + }`, + ClientMetadata{}, + true, + }, + } + for i, tt := range tests { + var got ClientMetadata + if err := got.UnmarshalJSON([]byte(tt.data)); err != nil { + if !tt.wantErr { + t.Errorf("case %d: unmarshal failed: %v", i, err) + } + continue + } + if tt.wantErr { + t.Errorf("case %d: expected unmarshal to produce error", i) + continue + } + + if diff := pretty.Compare(tt.want, got); diff != "" { + t.Errorf("case %d: results not equal: %s", i, diff) + } + } +} + +func TestClientMetadataMarshal(t *testing.T) { + + tests := []struct { + metadata ClientMetadata + want string + }{ + { + ClientMetadata{ + RedirectURIs: []url.URL{ + {Scheme: "https", Host: "example.com", Path: "/callback"}, + }, + }, + `{"redirect_uris":["https://example.com/callback"]}`, + }, + { + ClientMetadata{ + RedirectURIs: []url.URL{ + {Scheme: "https", Host: "example.com", Path: "/callback"}, + }, + RequestObjectOptions: JWAOptions{ + EncryptionAlg: "RSA1_5", + EncryptionEnc: "A128CBC-HS256", + }, + }, + `{"redirect_uris":["https://example.com/callback"],"request_object_encryption_alg":"RSA1_5","request_object_encryption_enc":"A128CBC-HS256"}`, + }, + } + + for i, tt := range tests { + got, err := json.Marshal(&tt.metadata) + if err != nil { + t.Errorf("case %d: failed to marshal metadata: %v", i, err) + continue + } + if string(got) != tt.want { + t.Errorf("case %d: marshaled string did not match expected string", i) + } + } +} + +func TestClientMetadataMarshalRoundTrip(t *testing.T) { + tests := []ClientMetadata{ + { + RedirectURIs: []url.URL{ + {Scheme: "https", Host: "example.com", Path: "/callback"}, + }, + LogoURI: &url.URL{Scheme: "https", Host: "example.com", Path: "/logo"}, + RequestObjectOptions: JWAOptions{ + EncryptionAlg: "RSA1_5", + EncryptionEnc: "A128CBC-HS256", + }, + ApplicationType: "native", + TokenEndpointAuthMethod: "client_secret_basic", + }, + } + + for i, want := range tests { + data, err := json.Marshal(&want) + if err != nil { + t.Errorf("case %d: failed to marshal metadata: %v", i, err) + continue + } + var got ClientMetadata + if err := json.Unmarshal(data, &got); err != nil { + t.Errorf("case %d: failed to unmarshal metadata: %v", i, err) + continue + } + if diff := pretty.Compare(want, got); diff != "" { + t.Errorf("case %d: struct did not survive a marshaling round trip: %s", i, diff) + } + } +} + +func TestClientRegistrationResponseUnmarshal(t *testing.T) { + tests := []struct { + data string + want ClientRegistrationResponse + wantErr bool + secretExpires bool + }{ + { + `{ + "client_id":"foo", + "client_secret":"bar", + "client_secret_expires_at": 1577858400, + "redirect_uris":[ + "https://client.example.org/callback", + "https://client.example.org/callback2" + ], + "client_name":"my_example" + }`, + ClientRegistrationResponse{ + ClientID: "foo", + ClientSecret: "bar", + ClientSecretExpiresAt: time.Unix(1577858400, 0), + ClientMetadata: ClientMetadata{ + RedirectURIs: []url.URL{ + {Scheme: "https", Host: "client.example.org", Path: "/callback"}, + {Scheme: "https", Host: "client.example.org", Path: "/callback2"}, + }, + ClientName: "my_example", + }, + }, + false, + true, + }, + { + `{ + "client_id":"foo", + "client_secret_expires_at": 0, + "redirect_uris":[ + "https://client.example.org/callback", + "https://client.example.org/callback2" + ], + "client_name":"my_example" + }`, + ClientRegistrationResponse{ + ClientID: "foo", + ClientMetadata: ClientMetadata{ + RedirectURIs: []url.URL{ + {Scheme: "https", Host: "client.example.org", Path: "/callback"}, + {Scheme: "https", Host: "client.example.org", Path: "/callback2"}, + }, + ClientName: "my_example", + }, + }, + false, + false, + }, + { + // no client id + `{ + "client_secret_expires_at": 0, + "redirect_uris":[ + "https://client.example.org/callback", + "https://client.example.org/callback2" + ], + "client_name":"my_example" + }`, + ClientRegistrationResponse{}, + true, + false, + }, + } + + for i, tt := range tests { + var got ClientRegistrationResponse + if err := json.Unmarshal([]byte(tt.data), &got); err != nil { + if !tt.wantErr { + t.Errorf("case %d: unmarshal failed: %v", i, err) + } + continue + } + + if tt.wantErr { + t.Errorf("case %d: expected unmarshal to produce error", i) + continue + } + + if diff := pretty.Compare(tt.want, got); diff != "" { + t.Errorf("case %d: results not equal: %s", i, diff) + } + if tt.secretExpires && got.ClientSecretExpiresAt.IsZero() { + t.Errorf("case %d: expected client_secret to expire, but it doesn't", i) + } else if !tt.secretExpires && !got.ClientSecretExpiresAt.IsZero() { + t.Errorf("case %d: expected client_secret to not expire, but it does", i) + } + } +} diff --git a/vendor/github.com/coreos/go-oidc/oidc/doc.go b/vendor/github.com/coreos/go-oidc/oidc/doc.go new file mode 100644 index 00000000..196611ec --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/oidc/doc.go @@ -0,0 +1,2 @@ +// Package oidc is DEPRECATED. Use github.com/coreos/go-oidc instead. +package oidc diff --git a/vendor/github.com/coreos/go-oidc/oidc/identity.go b/vendor/github.com/coreos/go-oidc/oidc/identity.go new file mode 100644 index 00000000..9bfa8e34 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/oidc/identity.go @@ -0,0 +1,44 @@ +package oidc + +import ( + "errors" + "time" + + "github.com/coreos/go-oidc/jose" +) + +type Identity struct { + ID string + Name string + Email string + ExpiresAt time.Time +} + +func IdentityFromClaims(claims jose.Claims) (*Identity, error) { + if claims == nil { + return nil, errors.New("nil claim set") + } + + var ident Identity + var err error + var ok bool + + if ident.ID, ok, err = claims.StringClaim("sub"); err != nil { + return nil, err + } else if !ok { + return nil, errors.New("missing required claim: sub") + } + + if ident.Email, _, err = claims.StringClaim("email"); err != nil { + return nil, err + } + + exp, ok, err := claims.TimeClaim("exp") + if err != nil { + return nil, err + } else if ok { + ident.ExpiresAt = exp + } + + return &ident, nil +} diff --git a/vendor/github.com/coreos/go-oidc/oidc/identity_test.go b/vendor/github.com/coreos/go-oidc/oidc/identity_test.go new file mode 100644 index 00000000..d286c669 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/oidc/identity_test.go @@ -0,0 +1,113 @@ +package oidc + +import ( + "reflect" + "testing" + "time" + + "github.com/coreos/go-oidc/jose" +) + +func TestIdentityFromClaims(t *testing.T) { + tests := []struct { + claims jose.Claims + want Identity + }{ + { + claims: jose.Claims{ + "sub": "123850281", + "name": "Elroy", + "email": "elroy@example.com", + "exp": float64(1.416935146e+09), + }, + want: Identity{ + ID: "123850281", + Name: "", + Email: "elroy@example.com", + ExpiresAt: time.Date(2014, time.November, 25, 17, 05, 46, 0, time.UTC), + }, + }, + { + claims: jose.Claims{ + "sub": "123850281", + "name": "Elroy", + "exp": float64(1.416935146e+09), + }, + want: Identity{ + ID: "123850281", + Name: "", + Email: "", + ExpiresAt: time.Date(2014, time.November, 25, 17, 05, 46, 0, time.UTC), + }, + }, + { + claims: jose.Claims{ + "sub": "123850281", + "name": "Elroy", + "email": "elroy@example.com", + "exp": int64(1416935146), + }, + want: Identity{ + ID: "123850281", + Name: "", + Email: "elroy@example.com", + ExpiresAt: time.Date(2014, time.November, 25, 17, 05, 46, 0, time.UTC), + }, + }, + { + claims: jose.Claims{ + "sub": "123850281", + "name": "Elroy", + "email": "elroy@example.com", + }, + want: Identity{ + ID: "123850281", + Name: "", + Email: "elroy@example.com", + ExpiresAt: time.Time{}, + }, + }, + } + + for i, tt := range tests { + got, err := IdentityFromClaims(tt.claims) + if err != nil { + t.Errorf("case %d: unexpected error: %v", i, err) + continue + } + if !reflect.DeepEqual(tt.want, *got) { + t.Errorf("case %d: want=%#v got=%#v", i, tt.want, *got) + } + } +} + +func TestIdentityFromClaimsFail(t *testing.T) { + tests := []jose.Claims{ + // sub incorrect type + jose.Claims{ + "sub": 123, + "name": "foo", + "email": "elroy@example.com", + }, + // email incorrect type + jose.Claims{ + "sub": "123850281", + "name": "Elroy", + "email": false, + }, + // exp incorrect type + jose.Claims{ + "sub": "123850281", + "name": "Elroy", + "email": "elroy@example.com", + "exp": "2014-11-25 18:05:46 +0000 UTC", + }, + } + + for i, tt := range tests { + _, err := IdentityFromClaims(tt) + if err == nil { + t.Errorf("case %d: expected non-nil error", i) + } + } +} diff --git a/vendor/github.com/coreos/go-oidc/oidc/interface.go b/vendor/github.com/coreos/go-oidc/oidc/interface.go new file mode 100644 index 00000000..248cac0b --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/oidc/interface.go @@ -0,0 +1,3 @@ +package oidc + +type LoginFunc func(ident Identity, sessionKey string) (redirectURL string, err error) diff --git a/vendor/github.com/coreos/go-oidc/oidc/key.go b/vendor/github.com/coreos/go-oidc/oidc/key.go new file mode 100755 index 00000000..82a0f567 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/oidc/key.go @@ -0,0 +1,67 @@ +package oidc + +import ( + "encoding/json" + "errors" + "net/http" + "time" + + phttp "github.com/coreos/go-oidc/http" + "github.com/coreos/go-oidc/jose" + "github.com/coreos/go-oidc/key" +) + +// DefaultPublicKeySetTTL is the default TTL set on the PublicKeySet if no +// Cache-Control header is provided by the JWK Set document endpoint. +const DefaultPublicKeySetTTL = 24 * time.Hour + +// NewRemotePublicKeyRepo is responsible for fetching the JWK Set document. +func NewRemotePublicKeyRepo(hc phttp.Client, ep string) *remotePublicKeyRepo { + return &remotePublicKeyRepo{hc: hc, ep: ep} +} + +type remotePublicKeyRepo struct { + hc phttp.Client + ep string +} + +// Get returns a PublicKeySet fetched from the JWK Set document endpoint. A TTL +// is set on the Key Set to avoid it having to be re-retrieved for every +// encryption event. This TTL is typically controlled by the endpoint returning +// a Cache-Control header, but defaults to 24 hours if no Cache-Control header +// is found. +func (r *remotePublicKeyRepo) Get() (key.KeySet, error) { + req, err := http.NewRequest("GET", r.ep, nil) + if err != nil { + return nil, err + } + + resp, err := r.hc.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var d struct { + Keys []jose.JWK `json:"keys"` + } + if err := json.NewDecoder(resp.Body).Decode(&d); err != nil { + return nil, err + } + + if len(d.Keys) == 0 { + return nil, errors.New("zero keys in response") + } + + ttl, ok, err := phttp.Cacheable(resp.Header) + if err != nil { + return nil, err + } + if !ok { + ttl = DefaultPublicKeySetTTL + } + + exp := time.Now().UTC().Add(ttl) + ks := key.NewPublicKeySet(d.Keys, exp) + return ks, nil +} diff --git a/vendor/github.com/coreos/go-oidc/oidc/provider.go b/vendor/github.com/coreos/go-oidc/oidc/provider.go new file mode 100644 index 00000000..ca283844 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/oidc/provider.go @@ -0,0 +1,690 @@ +package oidc + +import ( + "encoding/json" + "errors" + "fmt" + "log" + "net/http" + "net/url" + "strings" + "sync" + "time" + + "github.com/coreos/pkg/timeutil" + "github.com/jonboulle/clockwork" + + phttp "github.com/coreos/go-oidc/http" + "github.com/coreos/go-oidc/oauth2" +) + +const ( + // Subject Identifier types defined by the OIDC spec. Specifies if the provider + // should provide the same sub claim value to all clients (public) or a unique + // value for each client (pairwise). + // + // See: http://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes + SubjectTypePublic = "public" + SubjectTypePairwise = "pairwise" +) + +var ( + // Default values for omitted provider config fields. + // + // Use ProviderConfig's Defaults method to fill a provider config with these values. + DefaultGrantTypesSupported = []string{oauth2.GrantTypeAuthCode, oauth2.GrantTypeImplicit} + DefaultResponseModesSupported = []string{"query", "fragment"} + DefaultTokenEndpointAuthMethodsSupported = []string{oauth2.AuthMethodClientSecretBasic} + DefaultClaimTypesSupported = []string{"normal"} +) + +const ( + MaximumProviderConfigSyncInterval = 24 * time.Hour + MinimumProviderConfigSyncInterval = time.Minute + + discoveryConfigPath = "/.well-known/openid-configuration" +) + +// internally configurable for tests +var minimumProviderConfigSyncInterval = MinimumProviderConfigSyncInterval + +var ( + // Ensure ProviderConfig satisfies these interfaces. + _ json.Marshaler = &ProviderConfig{} + _ json.Unmarshaler = &ProviderConfig{} +) + +// ProviderConfig represents the OpenID Provider Metadata specifying what +// configurations a provider supports. +// +// See: http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata +type ProviderConfig struct { + Issuer *url.URL // Required + AuthEndpoint *url.URL // Required + TokenEndpoint *url.URL // Required if grant types other than "implicit" are supported + UserInfoEndpoint *url.URL + KeysEndpoint *url.URL // Required + RegistrationEndpoint *url.URL + EndSessionEndpoint *url.URL + CheckSessionIFrame *url.URL + + // Servers MAY choose not to advertise some supported scope values even when this + // parameter is used, although those defined in OpenID Core SHOULD be listed, if supported. + ScopesSupported []string + // OAuth2.0 response types supported. + ResponseTypesSupported []string // Required + // OAuth2.0 response modes supported. + // + // If omitted, defaults to DefaultResponseModesSupported. + ResponseModesSupported []string + // OAuth2.0 grant types supported. + // + // If omitted, defaults to DefaultGrantTypesSupported. + GrantTypesSupported []string + ACRValuesSupported []string + // SubjectTypesSupported specifies strategies for providing values for the sub claim. + SubjectTypesSupported []string // Required + + // JWA signing and encryption algorith values supported for ID tokens. + IDTokenSigningAlgValues []string // Required + IDTokenEncryptionAlgValues []string + IDTokenEncryptionEncValues []string + + // JWA signing and encryption algorith values supported for user info responses. + UserInfoSigningAlgValues []string + UserInfoEncryptionAlgValues []string + UserInfoEncryptionEncValues []string + + // JWA signing and encryption algorith values supported for request objects. + ReqObjSigningAlgValues []string + ReqObjEncryptionAlgValues []string + ReqObjEncryptionEncValues []string + + TokenEndpointAuthMethodsSupported []string + TokenEndpointAuthSigningAlgValuesSupported []string + DisplayValuesSupported []string + ClaimTypesSupported []string + ClaimsSupported []string + ServiceDocs *url.URL + ClaimsLocalsSupported []string + UILocalsSupported []string + ClaimsParameterSupported bool + RequestParameterSupported bool + RequestURIParamaterSupported bool + RequireRequestURIRegistration bool + + Policy *url.URL + TermsOfService *url.URL + + // Not part of the OpenID Provider Metadata + ExpiresAt time.Time +} + +// Defaults returns a shallow copy of ProviderConfig with default +// values replacing omitted fields. +// +// var cfg oidc.ProviderConfig +// // Fill provider config with default values for omitted fields. +// cfg = cfg.Defaults() +// +func (p ProviderConfig) Defaults() ProviderConfig { + setDefault := func(val *[]string, defaultVal []string) { + if len(*val) == 0 { + *val = defaultVal + } + } + setDefault(&p.GrantTypesSupported, DefaultGrantTypesSupported) + setDefault(&p.ResponseModesSupported, DefaultResponseModesSupported) + setDefault(&p.TokenEndpointAuthMethodsSupported, DefaultTokenEndpointAuthMethodsSupported) + setDefault(&p.ClaimTypesSupported, DefaultClaimTypesSupported) + return p +} + +func (p *ProviderConfig) MarshalJSON() ([]byte, error) { + e := p.toEncodableStruct() + return json.Marshal(&e) +} + +func (p *ProviderConfig) UnmarshalJSON(data []byte) error { + var e encodableProviderConfig + if err := json.Unmarshal(data, &e); err != nil { + return err + } + conf, err := e.toStruct() + if err != nil { + return err + } + if err := conf.Valid(); err != nil { + return err + } + *p = conf + return nil +} + +type encodableProviderConfig struct { + Issuer string `json:"issuer"` + AuthEndpoint string `json:"authorization_endpoint"` + TokenEndpoint string `json:"token_endpoint"` + UserInfoEndpoint string `json:"userinfo_endpoint,omitempty"` + KeysEndpoint string `json:"jwks_uri"` + RegistrationEndpoint string `json:"registration_endpoint,omitempty"` + EndSessionEndpoint string `json:"end_session_endpoint,omitempty"` + CheckSessionIFrame string `json:"check_session_iframe,omitempty"` + + // Use 'omitempty' for all slices as per OIDC spec: + // "Claims that return multiple values are represented as JSON arrays. + // Claims with zero elements MUST be omitted from the response." + // http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse + + ScopesSupported []string `json:"scopes_supported,omitempty"` + ResponseTypesSupported []string `json:"response_types_supported,omitempty"` + ResponseModesSupported []string `json:"response_modes_supported,omitempty"` + GrantTypesSupported []string `json:"grant_types_supported,omitempty"` + ACRValuesSupported []string `json:"acr_values_supported,omitempty"` + SubjectTypesSupported []string `json:"subject_types_supported,omitempty"` + + IDTokenSigningAlgValues []string `json:"id_token_signing_alg_values_supported,omitempty"` + IDTokenEncryptionAlgValues []string `json:"id_token_encryption_alg_values_supported,omitempty"` + IDTokenEncryptionEncValues []string `json:"id_token_encryption_enc_values_supported,omitempty"` + UserInfoSigningAlgValues []string `json:"userinfo_signing_alg_values_supported,omitempty"` + UserInfoEncryptionAlgValues []string `json:"userinfo_encryption_alg_values_supported,omitempty"` + UserInfoEncryptionEncValues []string `json:"userinfo_encryption_enc_values_supported,omitempty"` + ReqObjSigningAlgValues []string `json:"request_object_signing_alg_values_supported,omitempty"` + ReqObjEncryptionAlgValues []string `json:"request_object_encryption_alg_values_supported,omitempty"` + ReqObjEncryptionEncValues []string `json:"request_object_encryption_enc_values_supported,omitempty"` + + TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported,omitempty"` + TokenEndpointAuthSigningAlgValuesSupported []string `json:"token_endpoint_auth_signing_alg_values_supported,omitempty"` + + DisplayValuesSupported []string `json:"display_values_supported,omitempty"` + ClaimTypesSupported []string `json:"claim_types_supported,omitempty"` + ClaimsSupported []string `json:"claims_supported,omitempty"` + ServiceDocs string `json:"service_documentation,omitempty"` + ClaimsLocalsSupported []string `json:"claims_locales_supported,omitempty"` + UILocalsSupported []string `json:"ui_locales_supported,omitempty"` + ClaimsParameterSupported bool `json:"claims_parameter_supported,omitempty"` + RequestParameterSupported bool `json:"request_parameter_supported,omitempty"` + RequestURIParamaterSupported bool `json:"request_uri_parameter_supported,omitempty"` + RequireRequestURIRegistration bool `json:"require_request_uri_registration,omitempty"` + + Policy string `json:"op_policy_uri,omitempty"` + TermsOfService string `json:"op_tos_uri,omitempty"` +} + +func (cfg ProviderConfig) toEncodableStruct() encodableProviderConfig { + return encodableProviderConfig{ + Issuer: uriToString(cfg.Issuer), + AuthEndpoint: uriToString(cfg.AuthEndpoint), + TokenEndpoint: uriToString(cfg.TokenEndpoint), + UserInfoEndpoint: uriToString(cfg.UserInfoEndpoint), + KeysEndpoint: uriToString(cfg.KeysEndpoint), + RegistrationEndpoint: uriToString(cfg.RegistrationEndpoint), + EndSessionEndpoint: uriToString(cfg.EndSessionEndpoint), + CheckSessionIFrame: uriToString(cfg.CheckSessionIFrame), + ScopesSupported: cfg.ScopesSupported, + ResponseTypesSupported: cfg.ResponseTypesSupported, + ResponseModesSupported: cfg.ResponseModesSupported, + GrantTypesSupported: cfg.GrantTypesSupported, + ACRValuesSupported: cfg.ACRValuesSupported, + SubjectTypesSupported: cfg.SubjectTypesSupported, + IDTokenSigningAlgValues: cfg.IDTokenSigningAlgValues, + IDTokenEncryptionAlgValues: cfg.IDTokenEncryptionAlgValues, + IDTokenEncryptionEncValues: cfg.IDTokenEncryptionEncValues, + UserInfoSigningAlgValues: cfg.UserInfoSigningAlgValues, + UserInfoEncryptionAlgValues: cfg.UserInfoEncryptionAlgValues, + UserInfoEncryptionEncValues: cfg.UserInfoEncryptionEncValues, + ReqObjSigningAlgValues: cfg.ReqObjSigningAlgValues, + ReqObjEncryptionAlgValues: cfg.ReqObjEncryptionAlgValues, + ReqObjEncryptionEncValues: cfg.ReqObjEncryptionEncValues, + TokenEndpointAuthMethodsSupported: cfg.TokenEndpointAuthMethodsSupported, + TokenEndpointAuthSigningAlgValuesSupported: cfg.TokenEndpointAuthSigningAlgValuesSupported, + DisplayValuesSupported: cfg.DisplayValuesSupported, + ClaimTypesSupported: cfg.ClaimTypesSupported, + ClaimsSupported: cfg.ClaimsSupported, + ServiceDocs: uriToString(cfg.ServiceDocs), + ClaimsLocalsSupported: cfg.ClaimsLocalsSupported, + UILocalsSupported: cfg.UILocalsSupported, + ClaimsParameterSupported: cfg.ClaimsParameterSupported, + RequestParameterSupported: cfg.RequestParameterSupported, + RequestURIParamaterSupported: cfg.RequestURIParamaterSupported, + RequireRequestURIRegistration: cfg.RequireRequestURIRegistration, + Policy: uriToString(cfg.Policy), + TermsOfService: uriToString(cfg.TermsOfService), + } +} + +func (e encodableProviderConfig) toStruct() (ProviderConfig, error) { + p := stickyErrParser{} + conf := ProviderConfig{ + Issuer: p.parseURI(e.Issuer, "issuer"), + AuthEndpoint: p.parseURI(e.AuthEndpoint, "authorization_endpoint"), + TokenEndpoint: p.parseURI(e.TokenEndpoint, "token_endpoint"), + UserInfoEndpoint: p.parseURI(e.UserInfoEndpoint, "userinfo_endpoint"), + KeysEndpoint: p.parseURI(e.KeysEndpoint, "jwks_uri"), + RegistrationEndpoint: p.parseURI(e.RegistrationEndpoint, "registration_endpoint"), + EndSessionEndpoint: p.parseURI(e.EndSessionEndpoint, "end_session_endpoint"), + CheckSessionIFrame: p.parseURI(e.CheckSessionIFrame, "check_session_iframe"), + ScopesSupported: e.ScopesSupported, + ResponseTypesSupported: e.ResponseTypesSupported, + ResponseModesSupported: e.ResponseModesSupported, + GrantTypesSupported: e.GrantTypesSupported, + ACRValuesSupported: e.ACRValuesSupported, + SubjectTypesSupported: e.SubjectTypesSupported, + IDTokenSigningAlgValues: e.IDTokenSigningAlgValues, + IDTokenEncryptionAlgValues: e.IDTokenEncryptionAlgValues, + IDTokenEncryptionEncValues: e.IDTokenEncryptionEncValues, + UserInfoSigningAlgValues: e.UserInfoSigningAlgValues, + UserInfoEncryptionAlgValues: e.UserInfoEncryptionAlgValues, + UserInfoEncryptionEncValues: e.UserInfoEncryptionEncValues, + ReqObjSigningAlgValues: e.ReqObjSigningAlgValues, + ReqObjEncryptionAlgValues: e.ReqObjEncryptionAlgValues, + ReqObjEncryptionEncValues: e.ReqObjEncryptionEncValues, + TokenEndpointAuthMethodsSupported: e.TokenEndpointAuthMethodsSupported, + TokenEndpointAuthSigningAlgValuesSupported: e.TokenEndpointAuthSigningAlgValuesSupported, + DisplayValuesSupported: e.DisplayValuesSupported, + ClaimTypesSupported: e.ClaimTypesSupported, + ClaimsSupported: e.ClaimsSupported, + ServiceDocs: p.parseURI(e.ServiceDocs, "service_documentation"), + ClaimsLocalsSupported: e.ClaimsLocalsSupported, + UILocalsSupported: e.UILocalsSupported, + ClaimsParameterSupported: e.ClaimsParameterSupported, + RequestParameterSupported: e.RequestParameterSupported, + RequestURIParamaterSupported: e.RequestURIParamaterSupported, + RequireRequestURIRegistration: e.RequireRequestURIRegistration, + Policy: p.parseURI(e.Policy, "op_policy-uri"), + TermsOfService: p.parseURI(e.TermsOfService, "op_tos_uri"), + } + if p.firstErr != nil { + return ProviderConfig{}, p.firstErr + } + return conf, nil +} + +// Empty returns if a ProviderConfig holds no information. +// +// This case generally indicates a ProviderConfigGetter has experienced an error +// and has nothing to report. +func (p ProviderConfig) Empty() bool { + return p.Issuer == nil +} + +func contains(sli []string, ele string) bool { + for _, s := range sli { + if s == ele { + return true + } + } + return false +} + +// Valid determines if a ProviderConfig conforms with the OIDC specification. +// If Valid returns successfully it guarantees required field are non-nil and +// URLs are well formed. +// +// Valid is called by UnmarshalJSON. +// +// NOTE(ericchiang): For development purposes Valid does not mandate 'https' for +// URLs fields where the OIDC spec requires it. This may change in future releases +// of this package. See: https://github.com/coreos/go-oidc/issues/34 +func (p ProviderConfig) Valid() error { + grantTypes := p.GrantTypesSupported + if len(grantTypes) == 0 { + grantTypes = DefaultGrantTypesSupported + } + implicitOnly := true + for _, grantType := range grantTypes { + if grantType != oauth2.GrantTypeImplicit { + implicitOnly = false + break + } + } + + if len(p.SubjectTypesSupported) == 0 { + return errors.New("missing required field subject_types_supported") + } + if len(p.IDTokenSigningAlgValues) == 0 { + return errors.New("missing required field id_token_signing_alg_values_supported") + } + + if len(p.ScopesSupported) != 0 && !contains(p.ScopesSupported, "openid") { + return errors.New("scoped_supported must be unspecified or include 'openid'") + } + + if !contains(p.IDTokenSigningAlgValues, "RS256") { + return errors.New("id_token_signing_alg_values_supported must include 'RS256'") + } + if contains(p.TokenEndpointAuthMethodsSupported, "none") { + return errors.New("token_endpoint_auth_signing_alg_values_supported cannot include 'none'") + } + + uris := []struct { + val *url.URL + name string + required bool + }{ + {p.Issuer, "issuer", true}, + {p.AuthEndpoint, "authorization_endpoint", true}, + {p.TokenEndpoint, "token_endpoint", !implicitOnly}, + {p.UserInfoEndpoint, "userinfo_endpoint", false}, + {p.KeysEndpoint, "jwks_uri", true}, + {p.RegistrationEndpoint, "registration_endpoint", false}, + {p.EndSessionEndpoint, "end_session_endpoint", false}, + {p.CheckSessionIFrame, "check_session_iframe", false}, + {p.ServiceDocs, "service_documentation", false}, + {p.Policy, "op_policy_uri", false}, + {p.TermsOfService, "op_tos_uri", false}, + } + + for _, uri := range uris { + if uri.val == nil { + if !uri.required { + continue + } + return fmt.Errorf("empty value for required uri field %s", uri.name) + } + if uri.val.Host == "" { + return fmt.Errorf("no host for uri field %s", uri.name) + } + if uri.val.Scheme != "http" && uri.val.Scheme != "https" { + return fmt.Errorf("uri field %s schemeis not http or https", uri.name) + } + } + return nil +} + +// Supports determines if provider supports a client given their respective metadata. +func (p ProviderConfig) Supports(c ClientMetadata) error { + if err := p.Valid(); err != nil { + return fmt.Errorf("invalid provider config: %v", err) + } + if err := c.Valid(); err != nil { + return fmt.Errorf("invalid client config: %v", err) + } + + // Fill default values for omitted fields + c = c.Defaults() + p = p.Defaults() + + // Do the supported values list the requested one? + supports := []struct { + supported []string + requested string + name string + }{ + {p.IDTokenSigningAlgValues, c.IDTokenResponseOptions.SigningAlg, "id_token_signed_response_alg"}, + {p.IDTokenEncryptionAlgValues, c.IDTokenResponseOptions.EncryptionAlg, "id_token_encryption_response_alg"}, + {p.IDTokenEncryptionEncValues, c.IDTokenResponseOptions.EncryptionEnc, "id_token_encryption_response_enc"}, + {p.UserInfoSigningAlgValues, c.UserInfoResponseOptions.SigningAlg, "userinfo_signed_response_alg"}, + {p.UserInfoEncryptionAlgValues, c.UserInfoResponseOptions.EncryptionAlg, "userinfo_encryption_response_alg"}, + {p.UserInfoEncryptionEncValues, c.UserInfoResponseOptions.EncryptionEnc, "userinfo_encryption_response_enc"}, + {p.ReqObjSigningAlgValues, c.RequestObjectOptions.SigningAlg, "request_object_signing_alg"}, + {p.ReqObjEncryptionAlgValues, c.RequestObjectOptions.EncryptionAlg, "request_object_encryption_alg"}, + {p.ReqObjEncryptionEncValues, c.RequestObjectOptions.EncryptionEnc, "request_object_encryption_enc"}, + } + for _, field := range supports { + if field.requested == "" { + continue + } + if !contains(field.supported, field.requested) { + return fmt.Errorf("provider does not support requested value for field %s", field.name) + } + } + + stringsEqual := func(s1, s2 string) bool { return s1 == s2 } + + // For lists, are the list of requested values a subset of the supported ones? + supportsAll := []struct { + supported []string + requested []string + name string + // OAuth2.0 response_type can be space separated lists where order doesn't matter. + // For example "id_token token" is the same as "token id_token" + // Support a custom compare method. + comp func(s1, s2 string) bool + }{ + {p.GrantTypesSupported, c.GrantTypes, "grant_types", stringsEqual}, + {p.ResponseTypesSupported, c.ResponseTypes, "response_type", oauth2.ResponseTypesEqual}, + } + for _, field := range supportsAll { + requestLoop: + for _, req := range field.requested { + for _, sup := range field.supported { + if field.comp(req, sup) { + continue requestLoop + } + } + return fmt.Errorf("provider does not support requested value for field %s", field.name) + } + } + + // TODO(ericchiang): Are there more checks we feel comfortable with begin strict about? + + return nil +} + +func (p ProviderConfig) SupportsGrantType(grantType string) bool { + var supported []string + if len(p.GrantTypesSupported) == 0 { + supported = DefaultGrantTypesSupported + } else { + supported = p.GrantTypesSupported + } + + for _, t := range supported { + if t == grantType { + return true + } + } + return false +} + +type ProviderConfigGetter interface { + Get() (ProviderConfig, error) +} + +type ProviderConfigSetter interface { + Set(ProviderConfig) error +} + +type ProviderConfigSyncer struct { + from ProviderConfigGetter + to ProviderConfigSetter + clock clockwork.Clock + + initialSyncDone bool + initialSyncWait sync.WaitGroup +} + +func NewProviderConfigSyncer(from ProviderConfigGetter, to ProviderConfigSetter) *ProviderConfigSyncer { + return &ProviderConfigSyncer{ + from: from, + to: to, + clock: clockwork.NewRealClock(), + } +} + +func (s *ProviderConfigSyncer) Run() chan struct{} { + stop := make(chan struct{}) + + var next pcsStepper + next = &pcsStepNext{aft: time.Duration(0)} + + s.initialSyncWait.Add(1) + go func() { + for { + select { + case <-s.clock.After(next.after()): + next = next.step(s.sync) + case <-stop: + return + } + } + }() + + return stop +} + +func (s *ProviderConfigSyncer) WaitUntilInitialSync() { + s.initialSyncWait.Wait() +} + +func (s *ProviderConfigSyncer) sync() (time.Duration, error) { + cfg, err := s.from.Get() + if err != nil { + return 0, err + } + + if err = s.to.Set(cfg); err != nil { + return 0, fmt.Errorf("error setting provider config: %v", err) + } + + if !s.initialSyncDone { + s.initialSyncWait.Done() + s.initialSyncDone = true + } + + return nextSyncAfter(cfg.ExpiresAt, s.clock), nil +} + +type pcsStepFunc func() (time.Duration, error) + +type pcsStepper interface { + after() time.Duration + step(pcsStepFunc) pcsStepper +} + +type pcsStepNext struct { + aft time.Duration +} + +func (n *pcsStepNext) after() time.Duration { + return n.aft +} + +func (n *pcsStepNext) step(fn pcsStepFunc) (next pcsStepper) { + ttl, err := fn() + if err == nil { + next = &pcsStepNext{aft: ttl} + } else { + next = &pcsStepRetry{aft: time.Second} + log.Printf("go-oidc: provider config sync falied, retyring in %v: %v", next.after(), err) + } + return +} + +type pcsStepRetry struct { + aft time.Duration +} + +func (r *pcsStepRetry) after() time.Duration { + return r.aft +} + +func (r *pcsStepRetry) step(fn pcsStepFunc) (next pcsStepper) { + ttl, err := fn() + if err == nil { + next = &pcsStepNext{aft: ttl} + } else { + next = &pcsStepRetry{aft: timeutil.ExpBackoff(r.aft, time.Minute)} + log.Printf("go-oidc: provider config sync falied, retyring in %v: %v", next.after(), err) + } + return +} + +func nextSyncAfter(exp time.Time, clock clockwork.Clock) time.Duration { + if exp.IsZero() { + return MaximumProviderConfigSyncInterval + } + + t := exp.Sub(clock.Now()) / 2 + if t > MaximumProviderConfigSyncInterval { + t = MaximumProviderConfigSyncInterval + } else if t < minimumProviderConfigSyncInterval { + t = minimumProviderConfigSyncInterval + } + + return t +} + +type httpProviderConfigGetter struct { + hc phttp.Client + issuerURL string + clock clockwork.Clock +} + +func NewHTTPProviderConfigGetter(hc phttp.Client, issuerURL string) *httpProviderConfigGetter { + return &httpProviderConfigGetter{ + hc: hc, + issuerURL: issuerURL, + clock: clockwork.NewRealClock(), + } +} + +func (r *httpProviderConfigGetter) Get() (cfg ProviderConfig, err error) { + // If the Issuer value contains a path component, any terminating / MUST be removed before + // appending /.well-known/openid-configuration. + // https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest + discoveryURL := strings.TrimSuffix(r.issuerURL, "/") + discoveryConfigPath + req, err := http.NewRequest("GET", discoveryURL, nil) + if err != nil { + return + } + + resp, err := r.hc.Do(req) + if err != nil { + return + } + defer resp.Body.Close() + + if err = json.NewDecoder(resp.Body).Decode(&cfg); err != nil { + return + } + + var ttl time.Duration + var ok bool + ttl, ok, err = phttp.Cacheable(resp.Header) + if err != nil { + return + } else if ok { + cfg.ExpiresAt = r.clock.Now().UTC().Add(ttl) + } + + // The issuer value returned MUST be identical to the Issuer URL that was directly used to retrieve the configuration information. + // http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationValidation + if !urlEqual(cfg.Issuer.String(), r.issuerURL) { + err = fmt.Errorf(`"issuer" in config (%v) does not match provided issuer URL (%v)`, cfg.Issuer, r.issuerURL) + return + } + + return +} + +func FetchProviderConfig(hc phttp.Client, issuerURL string) (ProviderConfig, error) { + if hc == nil { + hc = http.DefaultClient + } + + g := NewHTTPProviderConfigGetter(hc, issuerURL) + return g.Get() +} + +func WaitForProviderConfig(hc phttp.Client, issuerURL string) (pcfg ProviderConfig) { + return waitForProviderConfig(hc, issuerURL, clockwork.NewRealClock()) +} + +func waitForProviderConfig(hc phttp.Client, issuerURL string, clock clockwork.Clock) (pcfg ProviderConfig) { + var sleep time.Duration + var err error + for { + pcfg, err = FetchProviderConfig(hc, issuerURL) + if err == nil { + break + } + + sleep = timeutil.ExpBackoff(sleep, time.Minute) + fmt.Printf("Failed fetching provider config, trying again in %v: %v\n", sleep, err) + time.Sleep(sleep) + } + + return +} diff --git a/vendor/github.com/coreos/go-oidc/oidc/provider_test.go b/vendor/github.com/coreos/go-oidc/oidc/provider_test.go new file mode 100644 index 00000000..8dfa121c --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/oidc/provider_test.go @@ -0,0 +1,940 @@ +package oidc + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/http" + "net/http/httptest" + "net/url" + "reflect" + "testing" + "time" + + "github.com/jonboulle/clockwork" + "github.com/kylelemons/godebug/diff" + "github.com/kylelemons/godebug/pretty" + + "github.com/coreos/go-oidc/jose" + "github.com/coreos/go-oidc/oauth2" +) + +func TestProviderConfigDefaults(t *testing.T) { + var cfg ProviderConfig + cfg = cfg.Defaults() + tests := []struct { + got, want []string + name string + }{ + {cfg.GrantTypesSupported, DefaultGrantTypesSupported, "grant types"}, + {cfg.ResponseModesSupported, DefaultResponseModesSupported, "response modes"}, + {cfg.ClaimTypesSupported, DefaultClaimTypesSupported, "claim types"}, + { + cfg.TokenEndpointAuthMethodsSupported, + DefaultTokenEndpointAuthMethodsSupported, + "token endpoint auth methods", + }, + } + + for _, tt := range tests { + if diff := pretty.Compare(tt.want, tt.got); diff != "" { + t.Errorf("%s: did not match %s", tt.name, diff) + } + } +} + +func TestProviderConfigUnmarshal(t *testing.T) { + + // helper for quickly creating uris + uri := func(path string) *url.URL { + return &url.URL{ + Scheme: "https", + Host: "server.example.com", + Path: path, + } + } + + tests := []struct { + data string + want ProviderConfig + wantErr bool + }{ + { + data: `{ + "issuer": "https://server.example.com", + "authorization_endpoint": "https://server.example.com/connect/authorize", + "token_endpoint": "https://server.example.com/connect/token", + "token_endpoint_auth_methods_supported": ["client_secret_basic", "private_key_jwt"], + "token_endpoint_auth_signing_alg_values_supported": ["RS256", "ES256"], + "userinfo_endpoint": "https://server.example.com/connect/userinfo", + "jwks_uri": "https://server.example.com/jwks.json", + "registration_endpoint": "https://server.example.com/connect/register", + "scopes_supported": [ + "openid", "profile", "email", "address", "phone", "offline_access" + ], + "response_types_supported": [ + "code", "code id_token", "id_token", "id_token token" + ], + "acr_values_supported": [ + "urn:mace:incommon:iap:silver", "urn:mace:incommon:iap:bronze" + ], + "subject_types_supported": ["public", "pairwise"], + "userinfo_signing_alg_values_supported": ["RS256", "ES256", "HS256"], + "userinfo_encryption_alg_values_supported": ["RSA1_5", "A128KW"], + "userinfo_encryption_enc_values_supported": ["A128CBC-HS256", "A128GCM"], + "id_token_signing_alg_values_supported": ["RS256", "ES256", "HS256"], + "id_token_encryption_alg_values_supported": ["RSA1_5", "A128KW"], + "id_token_encryption_enc_values_supported": ["A128CBC-HS256", "A128GCM"], + "request_object_signing_alg_values_supported": ["none", "RS256", "ES256"], + "display_values_supported": ["page", "popup"], + "claim_types_supported": ["normal", "distributed"], + "claims_supported": [ + "sub", "iss", "auth_time", "acr", "name", "given_name", + "family_name", "nickname", "profile", "picture", "website", + "email", "email_verified", "locale", "zoneinfo", + "http://example.info/claims/groups" + ], + "claims_parameter_supported": true, + "service_documentation": "https://server.example.com/connect/service_documentation.html", + "ui_locales_supported": ["en-US", "en-GB", "en-CA", "fr-FR", "fr-CA"] + } + `, + want: ProviderConfig{ + Issuer: &url.URL{Scheme: "https", Host: "server.example.com"}, + AuthEndpoint: uri("/connect/authorize"), + TokenEndpoint: uri("/connect/token"), + TokenEndpointAuthMethodsSupported: []string{ + oauth2.AuthMethodClientSecretBasic, oauth2.AuthMethodPrivateKeyJWT, + }, + TokenEndpointAuthSigningAlgValuesSupported: []string{ + jose.AlgRS256, jose.AlgES256, + }, + UserInfoEndpoint: uri("/connect/userinfo"), + KeysEndpoint: uri("/jwks.json"), + RegistrationEndpoint: uri("/connect/register"), + ScopesSupported: []string{ + "openid", "profile", "email", "address", "phone", "offline_access", + }, + ResponseTypesSupported: []string{ + oauth2.ResponseTypeCode, oauth2.ResponseTypeCodeIDToken, + oauth2.ResponseTypeIDToken, oauth2.ResponseTypeIDTokenToken, + }, + ACRValuesSupported: []string{ + "urn:mace:incommon:iap:silver", "urn:mace:incommon:iap:bronze", + }, + SubjectTypesSupported: []string{ + SubjectTypePublic, SubjectTypePairwise, + }, + UserInfoSigningAlgValues: []string{jose.AlgRS256, jose.AlgES256, jose.AlgHS256}, + UserInfoEncryptionAlgValues: []string{"RSA1_5", "A128KW"}, + UserInfoEncryptionEncValues: []string{"A128CBC-HS256", "A128GCM"}, + IDTokenSigningAlgValues: []string{jose.AlgRS256, jose.AlgES256, jose.AlgHS256}, + IDTokenEncryptionAlgValues: []string{"RSA1_5", "A128KW"}, + IDTokenEncryptionEncValues: []string{"A128CBC-HS256", "A128GCM"}, + ReqObjSigningAlgValues: []string{jose.AlgNone, jose.AlgRS256, jose.AlgES256}, + DisplayValuesSupported: []string{"page", "popup"}, + ClaimTypesSupported: []string{"normal", "distributed"}, + ClaimsSupported: []string{ + "sub", "iss", "auth_time", "acr", "name", "given_name", + "family_name", "nickname", "profile", "picture", "website", + "email", "email_verified", "locale", "zoneinfo", + "http://example.info/claims/groups", + }, + ClaimsParameterSupported: true, + ServiceDocs: uri("/connect/service_documentation.html"), + UILocalsSupported: []string{"en-US", "en-GB", "en-CA", "fr-FR", "fr-CA"}, + }, + wantErr: false, + }, + { + // missing a lot of required field + data: `{}`, + wantErr: true, + }, + { + data: `{ + "issuer": "https://server.example.com", + "authorization_endpoint": "https://server.example.com/connect/authorize", + "token_endpoint": "https://server.example.com/connect/token", + "jwks_uri": "https://server.example.com/jwks.json", + "response_types_supported": [ + "code", "code id_token", "id_token", "id_token token" + ], + "subject_types_supported": ["public", "pairwise"], + "id_token_signing_alg_values_supported": ["RS256", "ES256", "HS256"] + } + `, + want: ProviderConfig{ + Issuer: &url.URL{Scheme: "https", Host: "server.example.com"}, + AuthEndpoint: uri("/connect/authorize"), + TokenEndpoint: uri("/connect/token"), + KeysEndpoint: uri("/jwks.json"), + ResponseTypesSupported: []string{ + oauth2.ResponseTypeCode, oauth2.ResponseTypeCodeIDToken, + oauth2.ResponseTypeIDToken, oauth2.ResponseTypeIDTokenToken, + }, + SubjectTypesSupported: []string{ + SubjectTypePublic, SubjectTypePairwise, + }, + IDTokenSigningAlgValues: []string{jose.AlgRS256, jose.AlgES256, jose.AlgHS256}, + }, + wantErr: false, + }, + { + // invalid scheme 'ftp://' + data: `{ + "issuer": "https://server.example.com", + "authorization_endpoint": "https://server.example.com/connect/authorize", + "token_endpoint": "https://server.example.com/connect/token", + "jwks_uri": "ftp://server.example.com/jwks.json", + "response_types_supported": [ + "code", "code id_token", "id_token", "id_token token" + ], + "subject_types_supported": ["public", "pairwise"], + "id_token_signing_alg_values_supported": ["RS256", "ES256", "HS256"] + } + `, + wantErr: true, + }, + } + for i, tt := range tests { + var got ProviderConfig + if err := json.Unmarshal([]byte(tt.data), &got); err != nil { + if !tt.wantErr { + t.Errorf("case %d: failed to unmarshal provider config: %v", i, err) + } + continue + } + if tt.wantErr { + t.Errorf("case %d: expected error", i) + continue + } + if diff := pretty.Compare(tt.want, got); diff != "" { + t.Errorf("case %d: unmarshaled struct did not match expected %s", i, diff) + } + } + +} + +func TestProviderConfigMarshal(t *testing.T) { + tests := []struct { + cfg ProviderConfig + want string + }{ + { + cfg: ProviderConfig{ + Issuer: &url.URL{Scheme: "https", Host: "auth.example.com"}, + AuthEndpoint: &url.URL{ + Scheme: "https", Host: "auth.example.com", Path: "/auth", + }, + TokenEndpoint: &url.URL{ + Scheme: "https", Host: "auth.example.com", Path: "/token", + }, + UserInfoEndpoint: &url.URL{ + Scheme: "https", Host: "auth.example.com", Path: "/userinfo", + }, + KeysEndpoint: &url.URL{ + Scheme: "https", Host: "auth.example.com", Path: "/jwk", + }, + ResponseTypesSupported: []string{oauth2.ResponseTypeCode}, + SubjectTypesSupported: []string{SubjectTypePublic}, + IDTokenSigningAlgValues: []string{jose.AlgRS256}, + }, + // spacing must match json.MarshalIndent(cfg, "", "\t") + want: `{ + "issuer": "https://auth.example.com", + "authorization_endpoint": "https://auth.example.com/auth", + "token_endpoint": "https://auth.example.com/token", + "userinfo_endpoint": "https://auth.example.com/userinfo", + "jwks_uri": "https://auth.example.com/jwk", + "response_types_supported": [ + "code" + ], + "subject_types_supported": [ + "public" + ], + "id_token_signing_alg_values_supported": [ + "RS256" + ] +}`, + }, + { + cfg: ProviderConfig{ + Issuer: &url.URL{Scheme: "https", Host: "auth.example.com"}, + AuthEndpoint: &url.URL{ + Scheme: "https", Host: "auth.example.com", Path: "/auth", + }, + TokenEndpoint: &url.URL{ + Scheme: "https", Host: "auth.example.com", Path: "/token", + }, + UserInfoEndpoint: &url.URL{ + Scheme: "https", Host: "auth.example.com", Path: "/userinfo", + }, + KeysEndpoint: &url.URL{ + Scheme: "https", Host: "auth.example.com", Path: "/jwk", + }, + RegistrationEndpoint: &url.URL{ + Scheme: "https", Host: "auth.example.com", Path: "/register", + }, + ScopesSupported: DefaultScope, + ResponseTypesSupported: []string{oauth2.ResponseTypeCode}, + ResponseModesSupported: DefaultResponseModesSupported, + GrantTypesSupported: []string{oauth2.GrantTypeAuthCode}, + SubjectTypesSupported: []string{SubjectTypePublic}, + IDTokenSigningAlgValues: []string{jose.AlgRS256}, + ServiceDocs: &url.URL{Scheme: "https", Host: "example.com", Path: "/docs"}, + }, + // spacing must match json.MarshalIndent(cfg, "", "\t") + want: `{ + "issuer": "https://auth.example.com", + "authorization_endpoint": "https://auth.example.com/auth", + "token_endpoint": "https://auth.example.com/token", + "userinfo_endpoint": "https://auth.example.com/userinfo", + "jwks_uri": "https://auth.example.com/jwk", + "registration_endpoint": "https://auth.example.com/register", + "scopes_supported": [ + "openid", + "email", + "profile" + ], + "response_types_supported": [ + "code" + ], + "response_modes_supported": [ + "query", + "fragment" + ], + "grant_types_supported": [ + "authorization_code" + ], + "subject_types_supported": [ + "public" + ], + "id_token_signing_alg_values_supported": [ + "RS256" + ], + "service_documentation": "https://example.com/docs" +}`, + }, + } + + for i, tt := range tests { + got, err := json.MarshalIndent(&tt.cfg, "", "\t") + if err != nil { + t.Errorf("case %d: failed to marshal config: %v", i, err) + continue + } + if d := diff.Diff(string(got), string(tt.want)); d != "" { + t.Errorf("case %d: expected did not match result: %s", i, d) + } + + var cfg ProviderConfig + if err := json.Unmarshal(got, &cfg); err != nil { + t.Errorf("case %d: could not unmarshal marshal response: %v", i, err) + continue + } + + if d := pretty.Compare(tt.cfg, cfg); d != "" { + t.Errorf("case %d: config did not survive JSON marshaling round trip: %s", i, d) + } + } + +} + +func TestProviderConfigSupports(t *testing.T) { + tests := []struct { + provider ProviderConfig + client ClientMetadata + fillRequiredProviderFields bool + ok bool + }{ + { + provider: ProviderConfig{}, + client: ClientMetadata{ + RedirectURIs: []url.URL{ + {Scheme: "https", Host: "example.com", Path: "/callback"}, + }, + }, + fillRequiredProviderFields: true, + ok: true, + }, + { + // invalid provider config + provider: ProviderConfig{}, + client: ClientMetadata{ + RedirectURIs: []url.URL{ + {Scheme: "https", Host: "example.com", Path: "/callback"}, + }, + }, + fillRequiredProviderFields: false, + ok: false, + }, + { + // invalid client config + provider: ProviderConfig{}, + client: ClientMetadata{}, + fillRequiredProviderFields: true, + ok: false, + }, + } + + for i, tt := range tests { + if tt.fillRequiredProviderFields { + tt.provider = fillRequiredProviderFields(tt.provider) + } + + err := tt.provider.Supports(tt.client) + if err == nil && !tt.ok { + t.Errorf("case %d: expected non-nil error", i) + } + if err != nil && tt.ok { + t.Errorf("case %d: supports failed: %v", i, err) + } + } +} + +func newValidProviderConfig() ProviderConfig { + var cfg ProviderConfig + return fillRequiredProviderFields(cfg) +} + +// fill a provider config with enough information to be valid +func fillRequiredProviderFields(cfg ProviderConfig) ProviderConfig { + if cfg.Issuer == nil { + cfg.Issuer = &url.URL{Scheme: "https", Host: "auth.example.com"} + } + urlPath := func(path string) *url.URL { + var u url.URL + u = *cfg.Issuer + u.Path = path + return &u + } + cfg.AuthEndpoint = urlPath("/auth") + cfg.TokenEndpoint = urlPath("/token") + cfg.UserInfoEndpoint = urlPath("/userinfo") + cfg.KeysEndpoint = urlPath("/jwk") + cfg.ResponseTypesSupported = []string{oauth2.ResponseTypeCode} + cfg.SubjectTypesSupported = []string{SubjectTypePublic} + cfg.IDTokenSigningAlgValues = []string{jose.AlgRS256} + return cfg +} + +type fakeProviderConfigGetterSetter struct { + cfg *ProviderConfig + getCount int + setCount int +} + +func (g *fakeProviderConfigGetterSetter) Get() (ProviderConfig, error) { + g.getCount++ + return *g.cfg, nil +} + +func (g *fakeProviderConfigGetterSetter) Set(cfg ProviderConfig) error { + g.cfg = &cfg + g.setCount++ + return nil +} + +type fakeProviderConfigHandler struct { + cfg ProviderConfig + maxAge time.Duration +} + +func (s *fakeProviderConfigHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + b, _ := json.Marshal(&s.cfg) + if s.maxAge.Seconds() >= 0 { + w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", int(s.maxAge.Seconds()))) + } + w.Header().Set("Content-Type", "application/json") + w.Write(b) +} + +func TestProviderConfigRequiredFields(t *testing.T) { + // Ensure provider metadata responses have all the required fields. + // taken from https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata + requiredFields := []string{ + "issuer", + "authorization_endpoint", + "token_endpoint", // "This is REQUIRED unless only the Implicit Flow is used." + "jwks_uri", + "response_types_supported", + "subject_types_supported", + "id_token_signing_alg_values_supported", + } + + svr := &fakeProviderConfigHandler{ + cfg: ProviderConfig{ + Issuer: &url.URL{Scheme: "http", Host: "example.com"}, + ExpiresAt: time.Now().Add(time.Minute), + }, + maxAge: time.Minute, + } + svr.cfg = fillRequiredProviderFields(svr.cfg) + s := httptest.NewServer(svr) + defer s.Close() + + resp, err := http.Get(s.URL + "/") + if err != nil { + t.Errorf("get: %v", err) + return + } + defer resp.Body.Close() + var data map[string]interface{} + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + t.Errorf("decode: %v", err) + return + } + for _, field := range requiredFields { + if _, ok := data[field]; !ok { + t.Errorf("provider metadata does not have required field '%s'", field) + } + } +} + +type handlerClient struct { + Handler http.Handler +} + +func (hc *handlerClient) Do(r *http.Request) (*http.Response, error) { + w := httptest.NewRecorder() + hc.Handler.ServeHTTP(w, r) + + resp := http.Response{ + StatusCode: w.Code, + Header: w.Header(), + Body: ioutil.NopCloser(w.Body), + } + + return &resp, nil +} + +func TestHTTPProviderConfigGetter(t *testing.T) { + svr := &fakeProviderConfigHandler{} + hc := &handlerClient{Handler: svr} + fc := clockwork.NewFakeClock() + now := fc.Now().UTC() + + tests := []struct { + dsc string + age time.Duration + cfg ProviderConfig + ok bool + }{ + // everything is good + { + dsc: "https://example.com", + age: time.Minute, + cfg: ProviderConfig{ + Issuer: &url.URL{Scheme: "https", Host: "example.com"}, + ExpiresAt: now.Add(time.Minute), + }, + ok: true, + }, + // iss and disco url differ by scheme only (how google works) + { + dsc: "https://example.com", + age: time.Minute, + cfg: ProviderConfig{ + Issuer: &url.URL{Scheme: "https", Host: "example.com"}, + ExpiresAt: now.Add(time.Minute), + }, + ok: true, + }, + // issuer and discovery URL mismatch + { + dsc: "https://foo.com", + age: time.Minute, + cfg: ProviderConfig{ + Issuer: &url.URL{Scheme: "https", Host: "example.com"}, + ExpiresAt: now.Add(time.Minute), + }, + ok: false, + }, + // missing cache header results in zero ExpiresAt + { + dsc: "https://example.com", + age: -1, + cfg: ProviderConfig{ + Issuer: &url.URL{Scheme: "https", Host: "example.com"}, + }, + ok: true, + }, + } + + for i, tt := range tests { + tt.cfg = fillRequiredProviderFields(tt.cfg) + svr.cfg = tt.cfg + svr.maxAge = tt.age + getter := NewHTTPProviderConfigGetter(hc, tt.dsc) + getter.clock = fc + + got, err := getter.Get() + if err != nil { + if tt.ok { + t.Errorf("test %d: unexpected error: %v", i, err) + } + continue + } + + if !tt.ok { + t.Errorf("test %d: expected error", i) + continue + } + + if !reflect.DeepEqual(tt.cfg, got) { + t.Errorf("test %d: want: %#v, got: %#v", i, tt.cfg, got) + } + } +} + +func TestProviderConfigSyncerRun(t *testing.T) { + c1 := &ProviderConfig{ + Issuer: &url.URL{Scheme: "https", Host: "example.com"}, + } + c2 := &ProviderConfig{ + Issuer: &url.URL{Scheme: "https", Host: "example.com"}, + } + + tests := []struct { + first *ProviderConfig + advance time.Duration + second *ProviderConfig + firstExp time.Duration + secondExp time.Duration + count int + }{ + // exp is 10m, should have same config after 1s + { + first: c1, + firstExp: time.Duration(10 * time.Minute), + advance: time.Minute, + second: c1, + secondExp: time.Duration(10 * time.Minute), + count: 1, + }, + // exp is 10m, should have new config after 10/2 = 5m + { + first: c1, + firstExp: time.Duration(10 * time.Minute), + advance: time.Duration(5 * time.Minute), + second: c2, + secondExp: time.Duration(10 * time.Minute), + count: 2, + }, + // exp is 20m, should have new config after 20/2 = 10m + { + first: c1, + firstExp: time.Duration(20 * time.Minute), + advance: time.Duration(10 * time.Minute), + second: c2, + secondExp: time.Duration(30 * time.Minute), + count: 2, + }, + } + + assertCfg := func(i int, to *fakeProviderConfigGetterSetter, want ProviderConfig) { + got, err := to.Get() + if err != nil { + t.Fatalf("test %d: unable to get config: %v", i, err) + } + if !reflect.DeepEqual(want, got) { + t.Fatalf("test %d: incorrect state:\nwant=%#v\ngot=%#v", i, want, got) + } + } + + for i, tt := range tests { + from := &fakeProviderConfigGetterSetter{} + to := &fakeProviderConfigGetterSetter{} + + fc := clockwork.NewFakeClock() + now := fc.Now().UTC() + syncer := NewProviderConfigSyncer(from, to) + syncer.clock = fc + + tt.first.ExpiresAt = now.Add(tt.firstExp) + tt.second.ExpiresAt = now.Add(tt.secondExp) + if err := from.Set(*tt.first); err != nil { + t.Fatalf("test %d: unexpected error: %v", i, err) + } + + stop := syncer.Run() + defer close(stop) + fc.BlockUntil(1) + + // first sync + assertCfg(i, to, *tt.first) + + if err := from.Set(*tt.second); err != nil { + t.Fatalf("test %d: unexpected error: %v", i, err) + } + + fc.Advance(tt.advance) + fc.BlockUntil(1) + + // second sync + assertCfg(i, to, *tt.second) + + if tt.count != from.getCount { + t.Fatalf("test %d: want: %v, got: %v", i, tt.count, from.getCount) + } + } +} + +type staticProviderConfigGetter struct { + cfg ProviderConfig + err error +} + +func (g *staticProviderConfigGetter) Get() (ProviderConfig, error) { + return g.cfg, g.err +} + +type staticProviderConfigSetter struct { + cfg *ProviderConfig + err error +} + +func (s *staticProviderConfigSetter) Set(cfg ProviderConfig) error { + s.cfg = &cfg + return s.err +} + +func TestProviderConfigSyncerSyncFailure(t *testing.T) { + fc := clockwork.NewFakeClock() + + tests := []struct { + from *staticProviderConfigGetter + to *staticProviderConfigSetter + + // want indicates what ProviderConfig should be passed to Set. + // If nil, the Set should not be called. + want *ProviderConfig + }{ + // generic Get failure + { + from: &staticProviderConfigGetter{err: errors.New("fail")}, + to: &staticProviderConfigSetter{}, + want: nil, + }, + // generic Set failure + { + from: &staticProviderConfigGetter{cfg: ProviderConfig{ExpiresAt: fc.Now().Add(time.Minute)}}, + to: &staticProviderConfigSetter{err: errors.New("fail")}, + want: &ProviderConfig{ExpiresAt: fc.Now().Add(time.Minute)}, + }, + } + + for i, tt := range tests { + pcs := &ProviderConfigSyncer{ + from: tt.from, + to: tt.to, + clock: fc, + } + _, err := pcs.sync() + if err == nil { + t.Errorf("case %d: expected non-nil error", i) + } + if !reflect.DeepEqual(tt.want, tt.to.cfg) { + t.Errorf("case %d: Set mismatch: want=%#v got=%#v", i, tt.want, tt.to.cfg) + } + } +} + +func TestNextSyncAfter(t *testing.T) { + fc := clockwork.NewFakeClock() + + tests := []struct { + exp time.Time + want time.Duration + }{ + { + exp: fc.Now().Add(time.Hour), + want: 30 * time.Minute, + }, + // override large values with the maximum + { + exp: fc.Now().Add(168 * time.Hour), // one week + want: 24 * time.Hour, + }, + // override "now" values with the minimum + { + exp: fc.Now(), + want: time.Minute, + }, + // override negative values with the minimum + { + exp: fc.Now().Add(-1 * time.Minute), + want: time.Minute, + }, + // zero-value Time results in maximum sync interval + { + exp: time.Time{}, + want: 24 * time.Hour, + }, + } + + for i, tt := range tests { + got := nextSyncAfter(tt.exp, fc) + if tt.want != got { + t.Errorf("case %d: want=%v got=%v", i, tt.want, got) + } + } +} + +func TestProviderConfigEmpty(t *testing.T) { + cfg := ProviderConfig{} + if !cfg.Empty() { + t.Fatalf("Empty provider config reports non-empty") + } + cfg = ProviderConfig{ + Issuer: &url.URL{Scheme: "https", Host: "example.com"}, + } + if cfg.Empty() { + t.Fatalf("Non-empty provider config reports empty") + } +} + +func TestPCSStepAfter(t *testing.T) { + pass := func() (time.Duration, error) { return 7 * time.Second, nil } + fail := func() (time.Duration, error) { return 0, errors.New("fail") } + + tests := []struct { + stepper pcsStepper + stepFunc pcsStepFunc + want pcsStepper + }{ + // good step results in retry at TTL + { + stepper: &pcsStepNext{}, + stepFunc: pass, + want: &pcsStepNext{aft: 7 * time.Second}, + }, + + // good step after failed step results results in retry at TTL + { + stepper: &pcsStepRetry{aft: 2 * time.Second}, + stepFunc: pass, + want: &pcsStepNext{aft: 7 * time.Second}, + }, + + // failed step results in a retry in 1s + { + stepper: &pcsStepNext{}, + stepFunc: fail, + want: &pcsStepRetry{aft: time.Second}, + }, + + // failed retry backs off by a factor of 2 + { + stepper: &pcsStepRetry{aft: time.Second}, + stepFunc: fail, + want: &pcsStepRetry{aft: 2 * time.Second}, + }, + + // failed retry backs off by a factor of 2, up to 1m + { + stepper: &pcsStepRetry{aft: 32 * time.Second}, + stepFunc: fail, + want: &pcsStepRetry{aft: 60 * time.Second}, + }, + } + + for i, tt := range tests { + got := tt.stepper.step(tt.stepFunc) + if !reflect.DeepEqual(tt.want, got) { + t.Errorf("case %d: want=%#v got=%#v", i, tt.want, got) + } + } +} + +func TestProviderConfigSupportsGrantType(t *testing.T) { + tests := []struct { + types []string + typ string + want bool + }{ + // explicitly supported + { + types: []string{"foo_type"}, + typ: "foo_type", + want: true, + }, + + // explicitly unsupported + { + types: []string{"bar_type"}, + typ: "foo_type", + want: false, + }, + + // default type explicitly unsupported + { + types: []string{oauth2.GrantTypeImplicit}, + typ: oauth2.GrantTypeAuthCode, + want: false, + }, + + // type not found in default set + { + types: []string{}, + typ: "foo_type", + want: false, + }, + + // type found in default set + { + types: []string{}, + typ: oauth2.GrantTypeAuthCode, + want: true, + }, + } + + for i, tt := range tests { + cfg := ProviderConfig{ + GrantTypesSupported: tt.types, + } + got := cfg.SupportsGrantType(tt.typ) + if tt.want != got { + t.Errorf("case %d: assert %v supports %v: want=%t got=%t", i, tt.types, tt.typ, tt.want, got) + } + } +} + +type fakeClient struct { + resp *http.Response +} + +func (f *fakeClient) Do(req *http.Request) (*http.Response, error) { + return f.resp, nil +} + +func TestWaitForProviderConfigImmediateSuccess(t *testing.T) { + cfg := newValidProviderConfig() + b, err := json.Marshal(&cfg) + if err != nil { + t.Fatalf("Failed marshaling provider config") + } + + resp := http.Response{Body: ioutil.NopCloser(bytes.NewBuffer(b))} + hc := &fakeClient{&resp} + fc := clockwork.NewFakeClock() + + reschan := make(chan ProviderConfig) + go func() { + reschan <- waitForProviderConfig(hc, cfg.Issuer.String(), fc) + }() + + var got ProviderConfig + select { + case got = <-reschan: + case <-time.After(time.Second): + t.Fatalf("Did not receive result within 1s") + } + + if !reflect.DeepEqual(cfg, got) { + t.Fatalf("Received incorrect provider config: want=%#v got=%#v", cfg, got) + } +} diff --git a/vendor/github.com/coreos/go-oidc/oidc/transport.go b/vendor/github.com/coreos/go-oidc/oidc/transport.go new file mode 100644 index 00000000..61c926d7 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/oidc/transport.go @@ -0,0 +1,88 @@ +package oidc + +import ( + "fmt" + "net/http" + "sync" + + phttp "github.com/coreos/go-oidc/http" + "github.com/coreos/go-oidc/jose" +) + +type TokenRefresher interface { + // Verify checks if the provided token is currently valid or not. + Verify(jose.JWT) error + + // Refresh attempts to authenticate and retrieve a new token. + Refresh() (jose.JWT, error) +} + +type ClientCredsTokenRefresher struct { + Issuer string + OIDCClient *Client +} + +func (c *ClientCredsTokenRefresher) Verify(jwt jose.JWT) (err error) { + _, err = VerifyClientClaims(jwt, c.Issuer) + return +} + +func (c *ClientCredsTokenRefresher) Refresh() (jwt jose.JWT, err error) { + if err = c.OIDCClient.Healthy(); err != nil { + err = fmt.Errorf("unable to authenticate, unhealthy OIDC client: %v", err) + return + } + + jwt, err = c.OIDCClient.ClientCredsToken([]string{"openid"}) + if err != nil { + err = fmt.Errorf("unable to verify auth code with issuer: %v", err) + return + } + + return +} + +type AuthenticatedTransport struct { + TokenRefresher + http.RoundTripper + + mu sync.Mutex + jwt jose.JWT +} + +func (t *AuthenticatedTransport) verifiedJWT() (jose.JWT, error) { + t.mu.Lock() + defer t.mu.Unlock() + + if t.TokenRefresher.Verify(t.jwt) == nil { + return t.jwt, nil + } + + jwt, err := t.TokenRefresher.Refresh() + if err != nil { + return jose.JWT{}, fmt.Errorf("unable to acquire valid JWT: %v", err) + } + + t.jwt = jwt + return t.jwt, nil +} + +// SetJWT sets the JWT held by the Transport. +// This is useful for cases in which you want to set an initial JWT. +func (t *AuthenticatedTransport) SetJWT(jwt jose.JWT) { + t.mu.Lock() + defer t.mu.Unlock() + + t.jwt = jwt +} + +func (t *AuthenticatedTransport) RoundTrip(r *http.Request) (*http.Response, error) { + jwt, err := t.verifiedJWT() + if err != nil { + return nil, err + } + + req := phttp.CopyRequest(r) + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", jwt.Encode())) + return t.RoundTripper.RoundTrip(req) +} diff --git a/vendor/github.com/coreos/go-oidc/oidc/transport_test.go b/vendor/github.com/coreos/go-oidc/oidc/transport_test.go new file mode 100644 index 00000000..9ef909aa --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/oidc/transport_test.go @@ -0,0 +1,176 @@ +package oidc + +import ( + "errors" + "net/http" + "reflect" + "testing" + + "github.com/coreos/go-oidc/jose" +) + +type staticTokenRefresher struct { + verify func(jose.JWT) error + refresh func() (jose.JWT, error) +} + +func (s *staticTokenRefresher) Verify(jwt jose.JWT) error { + return s.verify(jwt) +} + +func (s *staticTokenRefresher) Refresh() (jose.JWT, error) { + return s.refresh() +} + +func TestAuthenticatedTransportVerifiedJWT(t *testing.T) { + tests := []struct { + refresher TokenRefresher + startJWT jose.JWT + wantJWT jose.JWT + wantError error + }{ + // verification succeeds, so refresh is not called + { + refresher: &staticTokenRefresher{ + verify: func(jose.JWT) error { return nil }, + refresh: func() (jose.JWT, error) { return jose.JWT{RawPayload: "2"}, nil }, + }, + startJWT: jose.JWT{RawPayload: "1"}, + wantJWT: jose.JWT{RawPayload: "1"}, + }, + + // verification fails, refresh succeeds so cached JWT changes + { + refresher: &staticTokenRefresher{ + verify: func(jose.JWT) error { return errors.New("fail!") }, + refresh: func() (jose.JWT, error) { return jose.JWT{RawPayload: "2"}, nil }, + }, + startJWT: jose.JWT{RawPayload: "1"}, + wantJWT: jose.JWT{RawPayload: "2"}, + }, + + // verification succeeds, so failing refresh isn't attempted + { + refresher: &staticTokenRefresher{ + verify: func(jose.JWT) error { return nil }, + refresh: func() (jose.JWT, error) { return jose.JWT{}, errors.New("fail!") }, + }, + startJWT: jose.JWT{RawPayload: "1"}, + wantJWT: jose.JWT{RawPayload: "1"}, + }, + + // verification fails, but refresh fails, too + { + refresher: &staticTokenRefresher{ + verify: func(jose.JWT) error { return errors.New("fail!") }, + refresh: func() (jose.JWT, error) { return jose.JWT{}, errors.New("fail!") }, + }, + startJWT: jose.JWT{RawPayload: "1"}, + wantJWT: jose.JWT{}, + wantError: errors.New("unable to acquire valid JWT: fail!"), + }, + } + + for i, tt := range tests { + at := &AuthenticatedTransport{ + TokenRefresher: tt.refresher, + } + at.SetJWT(tt.startJWT) + + gotJWT, err := at.verifiedJWT() + if !reflect.DeepEqual(tt.wantError, err) { + t.Errorf("#%d: unexpected error: want=%#v got=%#v", i, tt.wantError, err) + } + if !reflect.DeepEqual(tt.wantJWT, gotJWT) { + t.Errorf("#%d: incorrect JWT returned from verifiedJWT: want=%#v got=%#v", i, tt.wantJWT, gotJWT) + } + } +} + +func TestAuthenticatedTransportJWTCaching(t *testing.T) { + at := &AuthenticatedTransport{ + TokenRefresher: &staticTokenRefresher{ + verify: func(jose.JWT) error { return errors.New("fail!") }, + refresh: func() (jose.JWT, error) { return jose.JWT{RawPayload: "2"}, nil }, + }, + jwt: jose.JWT{RawPayload: "1"}, + } + + wantJWT := jose.JWT{RawPayload: "2"} + gotJWT, err := at.verifiedJWT() + if err != nil { + t.Fatalf("got non-nil error: %#v", err) + } + if !reflect.DeepEqual(wantJWT, gotJWT) { + t.Fatalf("incorrect JWT returned from verifiedJWT: want=%#v got=%#v", wantJWT, gotJWT) + } + + at.TokenRefresher = &staticTokenRefresher{ + verify: func(jose.JWT) error { return nil }, + refresh: func() (jose.JWT, error) { return jose.JWT{RawPayload: "3"}, nil }, + } + + // the previous JWT should still be cached on the AuthenticatedTransport since + // it is still valid, even though there's a new token ready to refresh + gotJWT, err = at.verifiedJWT() + if err != nil { + t.Fatalf("got non-nil error: %#v", err) + } + if !reflect.DeepEqual(wantJWT, gotJWT) { + t.Fatalf("incorrect JWT returned from verifiedJWT: want=%#v got=%#v", wantJWT, gotJWT) + } +} + +type fakeRoundTripper struct { + Request *http.Request + resp *http.Response +} + +func (r *fakeRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + r.Request = req + return r.resp, nil +} + +func TestAuthenticatedTransportRoundTrip(t *testing.T) { + rr := &fakeRoundTripper{nil, &http.Response{StatusCode: http.StatusOK}} + at := &AuthenticatedTransport{ + TokenRefresher: &staticTokenRefresher{ + verify: func(jose.JWT) error { return nil }, + }, + RoundTripper: rr, + jwt: jose.JWT{RawPayload: "1"}, + } + + req := http.Request{} + _, err := at.RoundTrip(&req) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + if !reflect.DeepEqual(req, http.Request{}) { + t.Errorf("http.Request object was modified") + } + + want := []string{"Bearer .1."} + got := rr.Request.Header["Authorization"] + if !reflect.DeepEqual(want, got) { + t.Errorf("incorrect Authorization header: want=%#v got=%#v", want, got) + } +} + +func TestAuthenticatedTransportRoundTripRefreshFail(t *testing.T) { + rr := &fakeRoundTripper{nil, &http.Response{StatusCode: http.StatusOK}} + at := &AuthenticatedTransport{ + TokenRefresher: &staticTokenRefresher{ + verify: func(jose.JWT) error { return errors.New("fail!") }, + refresh: func() (jose.JWT, error) { return jose.JWT{}, errors.New("fail!") }, + }, + RoundTripper: rr, + jwt: jose.JWT{RawPayload: "1"}, + } + + _, err := at.RoundTrip(&http.Request{}) + if err == nil { + t.Errorf("expected non-nil error") + } +} diff --git a/vendor/github.com/coreos/go-oidc/oidc/util.go b/vendor/github.com/coreos/go-oidc/oidc/util.go new file mode 100644 index 00000000..f2a5a195 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/oidc/util.go @@ -0,0 +1,109 @@ +package oidc + +import ( + "crypto/rand" + "encoding/base64" + "errors" + "fmt" + "net" + "net/http" + "net/url" + "strings" + "time" + + "github.com/coreos/go-oidc/jose" +) + +// RequestTokenExtractor funcs extract a raw encoded token from a request. +type RequestTokenExtractor func(r *http.Request) (string, error) + +// ExtractBearerToken is a RequestTokenExtractor which extracts a bearer token from a request's +// Authorization header. +func ExtractBearerToken(r *http.Request) (string, error) { + ah := r.Header.Get("Authorization") + if ah == "" { + return "", errors.New("missing Authorization header") + } + + if len(ah) <= 6 || strings.ToUpper(ah[0:6]) != "BEARER" { + return "", errors.New("should be a bearer token") + } + + val := ah[7:] + if len(val) == 0 { + return "", errors.New("bearer token is empty") + } + + return val, nil +} + +// CookieTokenExtractor returns a RequestTokenExtractor which extracts a token from the named cookie in a request. +func CookieTokenExtractor(cookieName string) RequestTokenExtractor { + return func(r *http.Request) (string, error) { + ck, err := r.Cookie(cookieName) + if err != nil { + return "", fmt.Errorf("token cookie not found in request: %v", err) + } + + if ck.Value == "" { + return "", errors.New("token cookie found but is empty") + } + + return ck.Value, nil + } +} + +func NewClaims(iss, sub string, aud interface{}, iat, exp time.Time) jose.Claims { + return jose.Claims{ + // required + "iss": iss, + "sub": sub, + "aud": aud, + "iat": iat.Unix(), + "exp": exp.Unix(), + } +} + +func GenClientID(hostport string) (string, error) { + b, err := randBytes(32) + if err != nil { + return "", err + } + + var host string + if strings.Contains(hostport, ":") { + host, _, err = net.SplitHostPort(hostport) + if err != nil { + return "", err + } + } else { + host = hostport + } + + return fmt.Sprintf("%s@%s", base64.URLEncoding.EncodeToString(b), host), nil +} + +func randBytes(n int) ([]byte, error) { + b := make([]byte, n) + got, err := rand.Read(b) + if err != nil { + return nil, err + } else if n != got { + return nil, errors.New("unable to generate enough random data") + } + return b, nil +} + +// urlEqual checks two urls for equality using only the host and path portions. +func urlEqual(url1, url2 string) bool { + u1, err := url.Parse(url1) + if err != nil { + return false + } + u2, err := url.Parse(url2) + if err != nil { + return false + } + + return strings.ToLower(u1.Host+u1.Path) == strings.ToLower(u2.Host+u2.Path) +} diff --git a/vendor/github.com/coreos/go-oidc/oidc/util_test.go b/vendor/github.com/coreos/go-oidc/oidc/util_test.go new file mode 100644 index 00000000..c4b8f16b --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/oidc/util_test.go @@ -0,0 +1,110 @@ +package oidc + +import ( + "fmt" + "net/http" + "reflect" + "testing" + "time" + + "github.com/coreos/go-oidc/jose" +) + +func TestCookieTokenExtractorInvalid(t *testing.T) { + ckName := "tokenCookie" + tests := []*http.Cookie{ + &http.Cookie{}, + &http.Cookie{Name: ckName}, + &http.Cookie{Name: ckName, Value: ""}, + } + + for i, tt := range tests { + r, _ := http.NewRequest("", "", nil) + r.AddCookie(tt) + _, err := CookieTokenExtractor(ckName)(r) + if err == nil { + t.Errorf("case %d: want: error for invalid cookie token, got: no error.", i) + } + } +} + +func TestCookieTokenExtractorValid(t *testing.T) { + validToken := "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk" + ckName := "tokenCookie" + tests := []*http.Cookie{ + &http.Cookie{Name: ckName, Value: "some non-empty value"}, + &http.Cookie{Name: ckName, Value: validToken}, + } + + for i, tt := range tests { + r, _ := http.NewRequest("", "", nil) + r.AddCookie(tt) + _, err := CookieTokenExtractor(ckName)(r) + if err != nil { + t.Errorf("case %d: want: valid cookie with no error, got: %v", i, err) + } + } +} + +func TestExtractBearerTokenInvalid(t *testing.T) { + tests := []string{"", "x", "Bearer", "xxxxxxx", "Bearer "} + + for i, tt := range tests { + r, _ := http.NewRequest("", "", nil) + r.Header.Add("Authorization", tt) + _, err := ExtractBearerToken(r) + if err == nil { + t.Errorf("case %d: want: invalid Authorization header, got: valid Authorization header.", i) + } + } +} + +func TestExtractBearerTokenValid(t *testing.T) { + validToken := "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk" + tests := []string{ + fmt.Sprintf("Bearer %s", validToken), + } + + for i, tt := range tests { + r, _ := http.NewRequest("", "", nil) + r.Header.Add("Authorization", tt) + _, err := ExtractBearerToken(r) + if err != nil { + t.Errorf("case %d: want: valid Authorization header, got: invalid Authorization header: %v.", i, err) + } + } +} + +func TestNewClaims(t *testing.T) { + issAt := time.Date(2, time.January, 1, 0, 0, 0, 0, time.UTC) + expAt := time.Date(2, time.January, 1, 1, 0, 0, 0, time.UTC) + + want := jose.Claims{ + "iss": "https://example.com", + "sub": "user-123", + "aud": "client-abc", + "iat": issAt.Unix(), + "exp": expAt.Unix(), + } + + got := NewClaims("https://example.com", "user-123", "client-abc", issAt, expAt) + + if !reflect.DeepEqual(want, got) { + t.Fatalf("want=%#v got=%#v", want, got) + } + + want2 := jose.Claims{ + "iss": "https://example.com", + "sub": "user-123", + "aud": []string{"client-abc", "client-def"}, + "iat": issAt.Unix(), + "exp": expAt.Unix(), + } + + got2 := NewClaims("https://example.com", "user-123", []string{"client-abc", "client-def"}, issAt, expAt) + + if !reflect.DeepEqual(want2, got2) { + t.Fatalf("want=%#v got=%#v", want2, got2) + } + +} diff --git a/vendor/github.com/coreos/go-oidc/oidc/verification.go b/vendor/github.com/coreos/go-oidc/oidc/verification.go new file mode 100644 index 00000000..d9c6afa6 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/oidc/verification.go @@ -0,0 +1,190 @@ +package oidc + +import ( + "errors" + "fmt" + "time" + + "github.com/jonboulle/clockwork" + + "github.com/coreos/go-oidc/jose" + "github.com/coreos/go-oidc/key" +) + +func VerifySignature(jwt jose.JWT, keys []key.PublicKey) (bool, error) { + jwtBytes := []byte(jwt.Data()) + for _, k := range keys { + v, err := k.Verifier() + if err != nil { + return false, err + } + if v.Verify(jwt.Signature, jwtBytes) == nil { + return true, nil + } + } + return false, nil +} + +// containsString returns true if the given string(needle) is found +// in the string array(haystack). +func containsString(needle string, haystack []string) bool { + for _, v := range haystack { + if v == needle { + return true + } + } + return false +} + +// Verify claims in accordance with OIDC spec +// http://openid.net/specs/openid-connect-basic-1_0.html#IDTokenValidation +func VerifyClaims(jwt jose.JWT, issuer, clientID string) error { + now := time.Now().UTC() + + claims, err := jwt.Claims() + if err != nil { + return err + } + + ident, err := IdentityFromClaims(claims) + if err != nil { + return err + } + + if ident.ExpiresAt.Before(now) { + return errors.New("token is expired") + } + + // iss REQUIRED. Issuer Identifier for the Issuer of the response. + // The iss value is a case sensitive URL using the https scheme that contains scheme, + // host, and optionally, port number and path components and no query or fragment components. + if iss, exists := claims["iss"].(string); exists { + if !urlEqual(iss, issuer) { + return fmt.Errorf("invalid claim value: 'iss'. expected=%s, found=%s.", issuer, iss) + } + } else { + return errors.New("missing claim: 'iss'") + } + + // iat REQUIRED. Time at which the JWT was issued. + // Its value is a JSON number representing the number of seconds from 1970-01-01T0:0:0Z + // as measured in UTC until the date/time. + if _, exists := claims["iat"].(float64); !exists { + return errors.New("missing claim: 'iat'") + } + + // aud REQUIRED. Audience(s) that this ID Token is intended for. + // It MUST contain the OAuth 2.0 client_id of the Relying Party as an audience value. + // It MAY also contain identifiers for other audiences. In the general case, the aud + // value is an array of case sensitive strings. In the common special case when there + // is one audience, the aud value MAY be a single case sensitive string. + if aud, ok, err := claims.StringClaim("aud"); err == nil && ok { + if aud != clientID { + return fmt.Errorf("invalid claims, 'aud' claim and 'client_id' do not match, aud=%s, client_id=%s", aud, clientID) + } + } else if aud, ok, err := claims.StringsClaim("aud"); err == nil && ok { + if !containsString(clientID, aud) { + return fmt.Errorf("invalid claims, cannot find 'client_id' in 'aud' claim, aud=%v, client_id=%s", aud, clientID) + } + } else { + return errors.New("invalid claim value: 'aud' is required, and should be either string or string array") + } + + return nil +} + +// VerifyClientClaims verifies all the required claims are valid for a "client credentials" JWT. +// Returns the client ID if valid, or an error if invalid. +func VerifyClientClaims(jwt jose.JWT, issuer string) (string, error) { + claims, err := jwt.Claims() + if err != nil { + return "", fmt.Errorf("failed to parse JWT claims: %v", err) + } + + iss, ok, err := claims.StringClaim("iss") + if err != nil { + return "", fmt.Errorf("failed to parse 'iss' claim: %v", err) + } else if !ok { + return "", errors.New("missing required 'iss' claim") + } else if !urlEqual(iss, issuer) { + return "", fmt.Errorf("'iss' claim does not match expected issuer, iss=%s", iss) + } + + sub, ok, err := claims.StringClaim("sub") + if err != nil { + return "", fmt.Errorf("failed to parse 'sub' claim: %v", err) + } else if !ok { + return "", errors.New("missing required 'sub' claim") + } + + if aud, ok, err := claims.StringClaim("aud"); err == nil && ok { + if aud != sub { + return "", fmt.Errorf("invalid claims, 'aud' claim and 'sub' claim do not match, aud=%s, sub=%s", aud, sub) + } + } else if aud, ok, err := claims.StringsClaim("aud"); err == nil && ok { + if !containsString(sub, aud) { + return "", fmt.Errorf("invalid claims, cannot find 'sud' in 'aud' claim, aud=%v, sub=%s", aud, sub) + } + } else { + return "", errors.New("invalid claim value: 'aud' is required, and should be either string or string array") + } + + now := time.Now().UTC() + exp, ok, err := claims.TimeClaim("exp") + if err != nil { + return "", fmt.Errorf("failed to parse 'exp' claim: %v", err) + } else if !ok { + return "", errors.New("missing required 'exp' claim") + } else if exp.Before(now) { + return "", fmt.Errorf("token already expired at: %v", exp) + } + + return sub, nil +} + +type JWTVerifier struct { + issuer string + clientID string + syncFunc func() error + keysFunc func() []key.PublicKey + clock clockwork.Clock +} + +func NewJWTVerifier(issuer, clientID string, syncFunc func() error, keysFunc func() []key.PublicKey) JWTVerifier { + return JWTVerifier{ + issuer: issuer, + clientID: clientID, + syncFunc: syncFunc, + keysFunc: keysFunc, + clock: clockwork.NewRealClock(), + } +} + +func (v *JWTVerifier) Verify(jwt jose.JWT) error { + // Verify claims before verifying the signature. This is an optimization to throw out + // tokens we know are invalid without undergoing an expensive signature check and + // possibly a re-sync event. + if err := VerifyClaims(jwt, v.issuer, v.clientID); err != nil { + return fmt.Errorf("oidc: JWT claims invalid: %v", err) + } + + ok, err := VerifySignature(jwt, v.keysFunc()) + if err != nil { + return fmt.Errorf("oidc: JWT signature verification failed: %v", err) + } else if ok { + return nil + } + + if err = v.syncFunc(); err != nil { + return fmt.Errorf("oidc: failed syncing KeySet: %v", err) + } + + ok, err = VerifySignature(jwt, v.keysFunc()) + if err != nil { + return fmt.Errorf("oidc: JWT signature verification failed: %v", err) + } else if !ok { + return errors.New("oidc: unable to verify JWT signature: no matching keys") + } + + return nil +} diff --git a/vendor/github.com/coreos/go-oidc/oidc/verification_test.go b/vendor/github.com/coreos/go-oidc/oidc/verification_test.go new file mode 100644 index 00000000..3b8c5e9f --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/oidc/verification_test.go @@ -0,0 +1,380 @@ +package oidc + +import ( + "testing" + "time" + + "github.com/coreos/go-oidc/jose" + "github.com/coreos/go-oidc/key" +) + +func TestVerifyClientClaims(t *testing.T) { + validIss := "https://example.com" + validClientID := "valid-client" + now := time.Now() + tomorrow := now.Add(24 * time.Hour) + header := jose.JOSEHeader{ + jose.HeaderKeyAlgorithm: "test-alg", + jose.HeaderKeyID: "1", + } + + tests := []struct { + claims jose.Claims + ok bool + }{ + // valid token + { + claims: jose.Claims{ + "iss": validIss, + "sub": validClientID, + "aud": validClientID, + "iat": float64(now.Unix()), + "exp": float64(tomorrow.Unix()), + }, + ok: true, + }, + // valid token, ('aud' claim is []string) + { + claims: jose.Claims{ + "iss": validIss, + "sub": validClientID, + "aud": []string{"foo", validClientID}, + "iat": float64(now.Unix()), + "exp": float64(tomorrow.Unix()), + }, + ok: true, + }, + // valid token, ('aud' claim is []interface{}) + { + claims: jose.Claims{ + "iss": validIss, + "sub": validClientID, + "aud": []interface{}{"foo", validClientID}, + "iat": float64(now.Unix()), + "exp": float64(tomorrow.Unix()), + }, + ok: true, + }, + // missing 'iss' claim + { + claims: jose.Claims{ + "sub": validClientID, + "aud": validClientID, + "iat": float64(now.Unix()), + "exp": float64(tomorrow.Unix()), + }, + ok: false, + }, + // invalid 'iss' claim + { + claims: jose.Claims{ + "iss": "INVALID", + "sub": validClientID, + "aud": validClientID, + "iat": float64(now.Unix()), + "exp": float64(tomorrow.Unix()), + }, + ok: false, + }, + // missing 'sub' claim + { + claims: jose.Claims{ + "iss": validIss, + "aud": validClientID, + "iat": float64(now.Unix()), + "exp": float64(tomorrow.Unix()), + }, + ok: false, + }, + // invalid 'sub' claim + { + claims: jose.Claims{ + "iss": validIss, + "sub": "INVALID", + "aud": validClientID, + "iat": float64(now.Unix()), + "exp": float64(tomorrow.Unix()), + }, + ok: false, + }, + // missing 'aud' claim + { + claims: jose.Claims{ + "iss": validIss, + "sub": validClientID, + "iat": float64(now.Unix()), + "exp": float64(tomorrow.Unix()), + }, + ok: false, + }, + // invalid 'aud' claim + { + claims: jose.Claims{ + "iss": validIss, + "sub": validClientID, + "aud": "INVALID", + "iat": float64(now.Unix()), + "exp": float64(tomorrow.Unix()), + }, + ok: false, + }, + // invalid 'aud' claim + { + claims: jose.Claims{ + "iss": validIss, + "sub": validClientID, + "aud": []string{"INVALID1", "INVALID2"}, + "iat": float64(now.Unix()), + "exp": float64(tomorrow.Unix()), + }, + ok: false, + }, + // invalid 'aud' type + { + claims: jose.Claims{ + "iss": validIss, + "sub": validClientID, + "aud": struct{}{}, + "iat": float64(now.Unix()), + "exp": float64(tomorrow.Unix()), + }, + ok: false, + }, + // expired + { + claims: jose.Claims{ + "iss": validIss, + "sub": validClientID, + "aud": validClientID, + "iat": float64(now.Unix()), + "exp": float64(now.Unix()), + }, + ok: false, + }, + } + + for i, tt := range tests { + jwt, err := jose.NewJWT(header, tt.claims) + if err != nil { + t.Fatalf("case %d: Failed to generate JWT, error=%v", i, err) + } + + got, err := VerifyClientClaims(jwt, validIss) + if tt.ok { + if err != nil { + t.Errorf("case %d: unexpected error, err=%v", i, err) + } + if got != validClientID { + t.Errorf("case %d: incorrect client ID, want=%s, got=%s", i, validClientID, got) + } + } else if err == nil { + t.Errorf("case %d: expected error but err is nil", i) + } + } +} + +func TestJWTVerifier(t *testing.T) { + iss := "http://example.com" + now := time.Now() + future12 := now.Add(12 * time.Hour) + past36 := now.Add(-36 * time.Hour) + past12 := now.Add(-12 * time.Hour) + + priv1, err := key.GeneratePrivateKey() + if err != nil { + t.Fatalf("failed to generate private key, error=%v", err) + } + pk1 := *key.NewPublicKey(priv1.JWK()) + + priv2, err := key.GeneratePrivateKey() + if err != nil { + t.Fatalf("failed to generate private key, error=%v", err) + } + pk2 := *key.NewPublicKey(priv2.JWK()) + + newJWT := func(issuer, subject string, aud interface{}, issuedAt, exp time.Time, signer jose.Signer) jose.JWT { + jwt, err := jose.NewSignedJWT(NewClaims(issuer, subject, aud, issuedAt, exp), signer) + if err != nil { + t.Fatal(err) + } + return *jwt + } + + tests := []struct { + name string + verifier JWTVerifier + jwt jose.JWT + wantErr bool + }{ + { + name: "JWT signed with available key", + verifier: JWTVerifier{ + issuer: "example.com", + clientID: "XXX", + syncFunc: func() error { return nil }, + keysFunc: func() []key.PublicKey { + return []key.PublicKey{pk1} + }, + }, + jwt: newJWT(iss, "XXX", "XXX", past12, future12, priv1.Signer()), + wantErr: false, + }, + { + name: "JWT signed with available key, with bad claims", + verifier: JWTVerifier{ + issuer: "example.com", + clientID: "XXX", + syncFunc: func() error { return nil }, + keysFunc: func() []key.PublicKey { + return []key.PublicKey{pk1} + }, + }, + jwt: newJWT(iss, "XXX", "YYY", past12, future12, priv1.Signer()), + wantErr: true, + }, + + { + name: "JWT signed with available key", + verifier: JWTVerifier{ + issuer: "example.com", + clientID: "XXX", + syncFunc: func() error { return nil }, + keysFunc: func() []key.PublicKey { + return []key.PublicKey{pk1} + }, + }, + jwt: newJWT(iss, "XXX", []string{"YYY", "ZZZ"}, past12, future12, priv1.Signer()), + wantErr: true, + }, + + { + name: "expired JWT signed with available key", + verifier: JWTVerifier{ + issuer: "example.com", + clientID: "XXX", + syncFunc: func() error { return nil }, + keysFunc: func() []key.PublicKey { + return []key.PublicKey{pk1} + }, + }, + jwt: newJWT(iss, "XXX", "XXX", past36, past12, priv1.Signer()), + wantErr: true, + }, + + { + name: "JWT signed with unrecognized key, verifiable after sync", + verifier: JWTVerifier{ + issuer: "example.com", + clientID: "XXX", + syncFunc: func() error { return nil }, + keysFunc: func() func() []key.PublicKey { + var i int + return func() []key.PublicKey { + defer func() { i++ }() + return [][]key.PublicKey{ + []key.PublicKey{pk1}, + []key.PublicKey{pk2}, + }[i] + } + }(), + }, + jwt: newJWT(iss, "XXX", "XXX", past36, future12, priv2.Signer()), + wantErr: false, + }, + + { + name: "JWT signed with unrecognized key, not verifiable after sync", + verifier: JWTVerifier{ + issuer: "example.com", + clientID: "XXX", + syncFunc: func() error { return nil }, + keysFunc: func() []key.PublicKey { + return []key.PublicKey{pk1} + }, + }, + jwt: newJWT(iss, "XXX", "XXX", past12, future12, priv2.Signer()), + wantErr: true, + }, + + { + name: "verifier gets no keys from keysFunc, still not verifiable after sync", + verifier: JWTVerifier{ + issuer: "example.com", + clientID: "XXX", + syncFunc: func() error { return nil }, + keysFunc: func() []key.PublicKey { + return []key.PublicKey{} + }, + }, + jwt: newJWT(iss, "XXX", "XXX", past12, future12, priv1.Signer()), + wantErr: true, + }, + + { + name: "verifier gets no keys from keysFunc, verifiable after sync", + verifier: JWTVerifier{ + issuer: "example.com", + clientID: "XXX", + syncFunc: func() error { return nil }, + keysFunc: func() func() []key.PublicKey { + var i int + return func() []key.PublicKey { + defer func() { i++ }() + return [][]key.PublicKey{ + []key.PublicKey{}, + []key.PublicKey{pk2}, + }[i] + } + }(), + }, + jwt: newJWT(iss, "XXX", "XXX", past12, future12, priv2.Signer()), + wantErr: false, + }, + + { + name: "JWT signed with available key, 'aud' is a string array", + verifier: JWTVerifier{ + issuer: "example.com", + clientID: "XXX", + syncFunc: func() error { return nil }, + keysFunc: func() []key.PublicKey { + return []key.PublicKey{pk1} + }, + }, + jwt: newJWT(iss, "XXX", []string{"ZZZ", "XXX"}, past12, future12, priv1.Signer()), + wantErr: false, + }, + { + name: "invalid issuer claim shouldn't trigger sync", + verifier: JWTVerifier{ + issuer: "example.com", + clientID: "XXX", + syncFunc: func() error { + t.Errorf("invalid issuer claim shouldn't trigger a sync") + return nil + }, + keysFunc: func() func() []key.PublicKey { + var i int + return func() []key.PublicKey { + defer func() { i++ }() + return [][]key.PublicKey{ + []key.PublicKey{}, + []key.PublicKey{pk2}, + }[i] + } + }(), + }, + jwt: newJWT("invalid-issuer", "XXX", []string{"ZZZ", "XXX"}, past12, future12, priv2.Signer()), + wantErr: true, + }, + } + + for _, tt := range tests { + err := tt.verifier.Verify(tt.jwt) + if tt.wantErr && (err == nil) { + t.Errorf("case %q: wanted non-nil error", tt.name) + } else if !tt.wantErr && (err != nil) { + t.Errorf("case %q: wanted nil error, got %v", tt.name, err) + } + } +} diff --git a/vendor/github.com/coreos/go-oidc/oidc_test.go b/vendor/github.com/coreos/go-oidc/oidc_test.go new file mode 100644 index 00000000..7307a8f5 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/oidc_test.go @@ -0,0 +1,21 @@ +package oidc + +import ( + "net/http" + "testing" + + "golang.org/x/net/context" +) + +func TestClientContext(t *testing.T) { + myClient := &http.Client{} + + ctx := ClientContext(context.Background(), myClient) + + gotClient := clientFromContext(ctx) + + // Compare pointer values. + if gotClient != myClient { + t.Fatal("clientFromContext did not return the value set by ClientContext") + } +} diff --git a/vendor/github.com/coreos/go-oidc/test b/vendor/github.com/coreos/go-oidc/test new file mode 100755 index 00000000..4b2e39f8 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/test @@ -0,0 +1,15 @@ +#!/bin/bash + +set -e + +# Filter out any files with a !golint build tag. +LINTABLE=$( go list -tags=golint -f ' + {{- range $i, $file := .GoFiles -}} + {{ $file }} {{ end }} + {{ range $i, $file := .TestGoFiles -}} + {{ $file }} {{ end }}' github.com/coreos/go-oidc ) + +go test -v -i -race github.com/coreos/go-oidc +go test -v -race github.com/coreos/go-oidc +golint $LINTABLE +go vet github.com/coreos/go-oidc diff --git a/vendor/github.com/coreos/go-oidc/verify.go b/vendor/github.com/coreos/go-oidc/verify.go new file mode 100644 index 00000000..13c0f934 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/verify.go @@ -0,0 +1,263 @@ +package oidc + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "strings" + "time" + + "golang.org/x/net/context" + "golang.org/x/oauth2" + jose "gopkg.in/square/go-jose.v2" +) + +// IDTokenVerifier provides verification for ID Tokens. +type IDTokenVerifier struct { + keySet *remoteKeySet + config *verificationConfig +} + +// verificationConfig is the unexported configuration for an IDTokenVerifier. +// +// Users interact with this struct using a VerificationOption. +type verificationConfig struct { + issuer string + // If provided, this value must be in the ID Token audiences. + audience string + // If not nil, check the expiry of the id token. + checkExpiry func() time.Time + // If specified, only these sets of algorithms may be used to sign the JWT. + requiredAlgs []string + // If not nil, don't verify nonce. + nonceSource NonceSource +} + +// VerificationOption provides additional checks on ID Tokens. +type VerificationOption interface { + // Unexport this method so other packages can't implement this interface. + updateConfig(c *verificationConfig) +} + +// Verifier returns an IDTokenVerifier that uses the provider's key set to verify JWTs. +// +// The returned IDTokenVerifier is tied to the Provider's context and its behavior is +// undefined once the Provider's context is canceled. +func (p *Provider) Verifier(options ...VerificationOption) *IDTokenVerifier { + config := &verificationConfig{issuer: p.issuer} + for _, option := range options { + option.updateConfig(config) + } + + return newVerifier(p.remoteKeySet, config) +} + +func newVerifier(keySet *remoteKeySet, config *verificationConfig) *IDTokenVerifier { + // As discussed in the godocs for VerifrySigningAlg, because almost all providers + // only support RS256, default to only allowing it. + if len(config.requiredAlgs) == 0 { + config.requiredAlgs = []string{RS256} + } + + return &IDTokenVerifier{ + keySet: keySet, + config: config, + } +} + +func parseJWT(p string) ([]byte, error) { + parts := strings.Split(p, ".") + if len(parts) < 2 { + return nil, fmt.Errorf("oidc: malformed jwt, expected 3 parts got %d", len(parts)) + } + payload, err := base64.RawURLEncoding.DecodeString(parts[1]) + if err != nil { + return nil, fmt.Errorf("oidc: malformed jwt payload: %v", err) + } + return payload, nil +} + +func contains(sli []string, ele string) bool { + for _, s := range sli { + if s == ele { + return true + } + } + return false +} + +// Verify parses a raw ID Token, verifies it's been signed by the provider, preforms +// any additional checks passed as VerifictionOptions, and returns the payload. +// +// See: https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation +// +// oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code")) +// if err != nil { +// // handle error +// } +// +// // Extract the ID Token from oauth2 token. +// rawIDToken, ok := oauth2Token.Extra("id_token").(string) +// if !ok { +// // handle error +// } +// +// token, err := verifier.Verify(ctx, rawIDToken) +// +func (v *IDTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*IDToken, error) { + jws, err := jose.ParseSigned(rawIDToken) + if err != nil { + return nil, fmt.Errorf("oidc: mallformed jwt: %v", err) + } + + // Throw out tokens with invalid claims before trying to verify the token. This lets + // us do cheap checks before possibly re-syncing keys. + payload, err := parseJWT(rawIDToken) + if err != nil { + return nil, fmt.Errorf("oidc: malformed jwt: %v", err) + } + var token idToken + if err := json.Unmarshal(payload, &token); err != nil { + return nil, fmt.Errorf("oidc: failed to unmarshal claims: %v", err) + } + + t := &IDToken{ + Issuer: token.Issuer, + Subject: token.Subject, + Audience: []string(token.Audience), + Expiry: time.Time(token.Expiry), + IssuedAt: time.Time(token.IssuedAt), + Nonce: token.Nonce, + claims: payload, + } + + // Check issuer. + if t.Issuer != v.config.issuer { + return nil, fmt.Errorf("oidc: id token issued by a different provider, expected %q got %q", v.config.issuer, t.Issuer) + } + + // If a client ID has been provided, make sure it's part of the audience. + if v.config.audience != "" { + if !contains(t.Audience, v.config.audience) { + return nil, fmt.Errorf("oidc: expected audience %q got %q", v.config.audience, t.Audience) + } + } + + // If a set of required algorithms has been provided, ensure that the signatures use those. + var keyIDs, gotAlgs []string + for _, sig := range jws.Signatures { + if len(v.config.requiredAlgs) == 0 || contains(v.config.requiredAlgs, sig.Header.Algorithm) { + keyIDs = append(keyIDs, sig.Header.KeyID) + } else { + gotAlgs = append(gotAlgs, sig.Header.Algorithm) + } + } + if len(keyIDs) == 0 { + return nil, fmt.Errorf("oidc: no signatures use a require algorithm, expected %q got %q", v.config.requiredAlgs, gotAlgs) + } + + // Get keys from the remote key set. This may trigger a re-sync. + keys, err := v.keySet.keysWithID(ctx, keyIDs) + if err != nil { + return nil, fmt.Errorf("oidc: get keys for id token: %v", err) + } + if len(keys) == 0 { + return nil, fmt.Errorf("oidc: no keys match signature ID(s) %q", keyIDs) + } + + // Try to use a key to validate the signature. + var gotPayload []byte + for _, key := range keys { + if p, err := jws.Verify(&key); err == nil { + gotPayload = p + } + } + if len(gotPayload) == 0 { + return nil, fmt.Errorf("oidc: failed to verify id token") + } + + // Ensure that the payload returned by the square actually matches the payload parsed earlier. + if !bytes.Equal(gotPayload, payload) { + return nil, errors.New("oidc: internal error, payload parsed did not match previous payload") + } + + // Check the nonce after we've verified the token. We don't want to allow unverified + // payloads to trigger a nonce lookup. + if v.config.nonceSource != nil { + if err := v.config.nonceSource.ClaimNonce(t.Nonce); err != nil { + return nil, err + } + } + + return t, nil +} + +// VerifyAudience ensures that an ID Token was issued for the specific client. +// +// Note that a verified token may be valid for other clients, as OpenID Connect allows a token to have +// multiple audiences. +func VerifyAudience(clientID string) VerificationOption { + return clientVerifier{clientID} +} + +type clientVerifier struct { + clientID string +} + +func (v clientVerifier) updateConfig(c *verificationConfig) { + c.audience = v.clientID +} + +// VerifyExpiry ensures that an ID Token has not expired. +func VerifyExpiry() VerificationOption { + return expiryVerifier{} +} + +type expiryVerifier struct{} + +func (v expiryVerifier) updateConfig(c *verificationConfig) { + c.checkExpiry = time.Now +} + +// VerifySigningAlg enforces that an ID Token is signed by a specific signing algorithm. +// +// Because so many providers only support RS256, if this verifiction option isn't used, +// the IDTokenVerifier defaults to only allowing RS256. +func VerifySigningAlg(allowedAlgs ...string) VerificationOption { + return algVerifier{allowedAlgs} +} + +type algVerifier struct { + algs []string +} + +func (v algVerifier) updateConfig(c *verificationConfig) { + c.requiredAlgs = v.algs +} + +// Nonce returns an auth code option which requires the ID Token created by the +// OpenID Connect provider to contain the specified nonce. +func Nonce(nonce string) oauth2.AuthCodeOption { + return oauth2.SetAuthURLParam("nonce", nonce) +} + +// NonceSource represents a source which can verify a nonce is valid and has not +// been claimed before. +type NonceSource interface { + ClaimNonce(nonce string) error +} + +// VerifyNonce ensures that the ID Token contains a nonce which can be claimed by the nonce source. +func VerifyNonce(source NonceSource) VerificationOption { + return nonceVerifier{source} +} + +type nonceVerifier struct { + nonceSource NonceSource +} + +func (n nonceVerifier) updateConfig(c *verificationConfig) { + c.nonceSource = n.nonceSource +} diff --git a/vendor/github.com/coreos/go-oidc/verify_test.go b/vendor/github.com/coreos/go-oidc/verify_test.go new file mode 100644 index 00000000..872f75c3 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/verify_test.go @@ -0,0 +1,265 @@ +package oidc + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rsa" + "encoding/json" + "net/http/httptest" + "testing" + "time" + + "golang.org/x/net/context" + jose "gopkg.in/square/go-jose.v2" +) + +func TestVerify(t *testing.T) { + tests := []verificationTest{ + { + name: "good token", + idToken: idToken{ + Issuer: "https://foo", + }, + config: verificationConfig{ + issuer: "https://foo", + }, + signKey: testKeyRSA_2048_0_Priv, + pubKeys: []jose.JSONWebKey{testKeyRSA_2048_0}, + }, + { + name: "invalid signature", + idToken: idToken{ + Issuer: "https://foo", + }, + config: verificationConfig{ + issuer: "https://foo", + }, + signKey: testKeyRSA_2048_0_Priv, + pubKeys: []jose.JSONWebKey{testKeyRSA_2048_1}, + wantErr: true, + }, + { + name: "invalid issuer", + idToken: idToken{ + Issuer: "https://foo", + }, + config: verificationConfig{ + issuer: "https://bar", + }, + signKey: testKeyRSA_2048_0_Priv, + pubKeys: []jose.JSONWebKey{testKeyRSA_2048_0}, + wantErr: true, + }, + } + for _, test := range tests { + test.run(t) + } +} + +func TestVerifyAudience(t *testing.T) { + tests := []verificationTest{ + { + name: "good audience", + idToken: idToken{ + Issuer: "https://foo", + Audience: []string{"client1"}, + }, + config: verificationConfig{ + issuer: "https://foo", + audience: "client1", + }, + signKey: testKeyRSA_2048_0_Priv, + pubKeys: []jose.JSONWebKey{testKeyRSA_2048_0}, + }, + { + name: "mismatched audience", + idToken: idToken{ + Issuer: "https://foo", + Audience: []string{"client2"}, + }, + config: verificationConfig{ + issuer: "https://foo", + audience: "client1", + }, + signKey: testKeyRSA_2048_0_Priv, + pubKeys: []jose.JSONWebKey{testKeyRSA_2048_0}, + wantErr: true, + }, + { + name: "multiple audiences, one matches", + idToken: idToken{ + Issuer: "https://foo", + Audience: []string{"client2", "client1"}, + }, + config: verificationConfig{ + issuer: "https://foo", + audience: "client1", + }, + signKey: testKeyRSA_2048_0_Priv, + pubKeys: []jose.JSONWebKey{testKeyRSA_2048_0}, + }, + } + for _, test := range tests { + test.run(t) + } +} + +func TestVerifySigningAlg(t *testing.T) { + tests := []verificationTest{ + { + name: "default signing alg", + idToken: idToken{ + Issuer: "https://foo", + }, + config: verificationConfig{ + issuer: "https://foo", + }, + signKey: testKeyRSA_2048_0_Priv, + signAlg: RS256, // By default we only support RS256. + pubKeys: []jose.JSONWebKey{testKeyRSA_2048_0}, + }, + { + name: "bad signing alg", + idToken: idToken{ + Issuer: "https://foo", + }, + config: verificationConfig{ + issuer: "https://foo", + }, + signKey: testKeyRSA_2048_0_Priv, + signAlg: RS512, + pubKeys: []jose.JSONWebKey{testKeyRSA_2048_0}, + wantErr: true, + }, + { + name: "ecdsa signing", + idToken: idToken{ + Issuer: "https://foo", + }, + config: verificationConfig{ + issuer: "https://foo", + requiredAlgs: []string{ES384}, + }, + signAlg: ES384, + signKey: testKeyECDSA_384_0_Priv, + pubKeys: []jose.JSONWebKey{testKeyECDSA_384_0}, + }, + { + name: "one of many supported", + idToken: idToken{ + Issuer: "https://foo", + }, + config: verificationConfig{ + issuer: "https://foo", + requiredAlgs: []string{RS256, ES384}, + }, + signAlg: ES384, + signKey: testKeyECDSA_384_0_Priv, + pubKeys: []jose.JSONWebKey{testKeyECDSA_384_0}, + }, + { + name: "not in requiredAlgs", + idToken: idToken{ + Issuer: "https://foo", + }, + config: verificationConfig{ + issuer: "https://foo", + requiredAlgs: []string{RS256, ES512}, + }, + signAlg: ES384, + signKey: testKeyECDSA_384_0_Priv, + pubKeys: []jose.JSONWebKey{testKeyECDSA_384_0}, + wantErr: true, + }, + } + for _, test := range tests { + test.run(t) + } +} + +type verificationTest struct { + name string + + // ID token claims and a signing key to create the JWT. + idToken idToken + signKey jose.JSONWebKey + // If supplied use this signing algorithm. If not, guess + // from the signingKey. + signAlg string + + config verificationConfig + pubKeys []jose.JSONWebKey + + wantErr bool +} + +func algForKey(t *testing.T, k jose.JSONWebKey) string { + switch key := k.Key.(type) { + case *rsa.PrivateKey: + return RS256 + case *ecdsa.PrivateKey: + name := key.PublicKey.Params().Name + switch name { + case elliptic.P256().Params().Name: + return ES256 + case elliptic.P384().Params().Name: + return ES384 + case elliptic.P521().Params().Name: + return ES512 + } + t.Fatalf("unsupported ecdsa curve: %s", name) + default: + t.Fatalf("unsupported key type %T", key) + } + return "" +} + +func (v verificationTest) run(t *testing.T) { + payload, err := json.Marshal(v.idToken) + if err != nil { + t.Fatal(err) + } + signingAlg := v.signAlg + if signingAlg == "" { + signingAlg = algForKey(t, v.signKey) + } + + signer, err := jose.NewSigner(jose.SigningKey{ + Algorithm: jose.SignatureAlgorithm(signingAlg), + Key: &v.signKey, + }, nil) + if err != nil { + t.Fatal(err) + } + + jws, err := signer.Sign(payload) + if err != nil { + t.Fatal(err) + } + + token, err := jws.CompactSerialize() + if err != nil { + t.Fatal(err) + } + + t0 := time.Now() + now := func() time.Time { return t0 } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + server := httptest.NewServer(newKeyServer(v.pubKeys...)) + defer server.Close() + + verifier := newVerifier(newRemoteKeySet(ctx, server.URL, now), &v.config) + + if _, err := verifier.Verify(ctx, token); err != nil { + if !v.wantErr { + t.Errorf("%s: verify %v", v.name, err) + } + } else { + if v.wantErr { + t.Errorf("%s: expected error", v.name) + } + } +} diff --git a/vendor/github.com/ericchiang/oidc/.travis.yml b/vendor/github.com/ericchiang/oidc/.travis.yml deleted file mode 100644 index 9d73a45f..00000000 --- a/vendor/github.com/ericchiang/oidc/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -language: go - -go: -- 1.5.4 -- 1.6.3 -- tip - -notifications: - email: false - -matrix: - allow_failures: - - go: tip diff --git a/vendor/github.com/ericchiang/oidc/README.md b/vendor/github.com/ericchiang/oidc/README.md deleted file mode 100644 index c6adbbf3..00000000 --- a/vendor/github.com/ericchiang/oidc/README.md +++ /dev/null @@ -1,106 +0,0 @@ -# OpenID Connect client support for Go - -[](https://godoc.org/github.com/ericchiang/oidc) - -This package implements OpenID Connect client logic for the golang.org/x/oauth2 package. - -```go -provider, err := oidc.NewProvider(ctx, "https://accounts.example.com") -if err != nil { - return err -} - -// Configure an OpenID Connect aware OAuth2 client. -oauth2Config := oauth2.Config{ - ClientID: clientID, - ClientSecret: clientSecret, - RedirectURL: redirectURL, - Endpoint: provider.Endpoint(), - Scopes: []string{oidc.ScopeOpenID, "profile", "email"}, -} -``` - -OAuth2 redirects are unchanged. - -```go -func handleRedirect(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, oauth2Config.AuthCodeURL(state), http.StatusFound) -}) -``` - -For callbacks the provider can be used to query for [user information](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo) such as email. - -```go -func handleOAuth2Callback(w http.ResponseWriter, r *http.Request) { - // Verify state... - - oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code")) - if err != nil { - http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError) - return - } - - userinfo, err := provider.UserInfo(ctx, oauth2.StaticTokenSource(oauth2Token)) - if err != nil { - http.Error(w, "Failed to get userinfo: "+err.Error(), http.StatusInternalServerError) - return - } - - // ... -}) -``` - -Or the provider can be used to verify and inspect the OpenID Connect -[ID Token](https://openid.net/specs/openid-connect-core-1_0.html#IDToken) in the -[token response](https://openid.net/specs/openid-connect-core-1_0.html#TokenResponse). - -```go -verifier := provider.NewVerifier(ctx) -``` - -The verifier itself can be constructed with addition checks, such as verifing a -token was issued for a specific client or hasn't expired. - -```go -verifier := provier.NewVerifier(ctx, oidc.VerifyAudience(clientID), oidc.VerifyExpiry()) -``` - -The returned verifier can be used to ensure the ID Token (a JWT) is signed by the provider. - -```go -func handleOAuth2Callback(w http.ResponseWriter, r *http.Request) { - // Verify state... - - oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code")) - if err != nil { - http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError) - return - } - - // Extract the ID Token from oauth2 token. - rawIDToken, ok := oauth2Token.Extra("id_token").(string) - if !ok { - http.Error(w, "No ID Token found", http.StatusInternalServerError) - return - } - - // Verify that the ID Token is signed by the provider. - idToken, err := verifier.Verify(rawIDToken) - if err != nil { - http.Error(w, "Failed to verify ID Token: "+err.Error(), http.StatusInternalServerError) - return - } - - // Unmarshal ID Token for expected custom claims. - var claims struct { - Email string `json:"email"` - EmailVerified bool `json:"email_verified"` - } - if err := idToken.Claims(&claims); err != nil { - http.Error(w, "Failed to unmarshal ID Token claims: "+err.Error(), http.StatusInternalServerError) - return - } - - // ... -}) -``` diff --git a/vendor/github.com/ericchiang/oidc/doc.go b/vendor/github.com/ericchiang/oidc/doc.go deleted file mode 100644 index cbc1ec4f..00000000 --- a/vendor/github.com/ericchiang/oidc/doc.go +++ /dev/null @@ -1,145 +0,0 @@ -/* -Package oidc implements OpenID Connect client logic for the golang.org/x/oauth2 package. - - provider, err := oidc.NewProvider(ctx, "https://accounts.example.com") - if err != nil { - return err - } - - // Configure an OpenID Connect aware OAuth2 client. - oauth2Config := oauth2.Config{ - ClientID: clientID, - ClientSecret: clientSecret, - RedirectURL: redirectURL, - Endpoint: provider.Endpoint(), - Scopes: []string{oidc.ScopeOpenID, "profile", "email"}, - } - -OAuth2 redirects are unchanged. - - func handleRedirect(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, oauth2Config.AuthCodeURL(state), http.StatusFound) - }) - -For callbacks the provider can be used to query for user information such as email. - - func handleOAuth2Callback(w http.ResponseWriter, r *http.Request) { - // Verify state... - - oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code")) - if err != nil { - http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError) - return - } - - userinfo, err := provider.UserInfo(ctx, oauth2.StaticTokenSource(oauth2Token)) - if err != nil { - http.Error(w, "Failed to get userinfo: "+err.Error(), http.StatusInternalServerError) - return - } - - // ... - }) - -The provider also has the ability to verify ID Tokens. - - verifier := provider.NewVerifier(ctx) - -The returned verifier can be used to perform basic validation on ID Token issued by the provider, -including verifying the JWT signature. It then returns the payload. - - func handleOAuth2Callback(w http.ResponseWriter, r *http.Request) { - // Verify state... - - oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code")) - if err != nil { - http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError) - return - } - - // Extract the ID Token from oauth2 token. - rawIDToken, ok := oauth2Token.Extra("id_token").(string) - if !ok { - http.Error(w, "No ID Token found", http.StatusInternalServerError) - return - } - - // Verify that the ID Token is signed by the provider. - idToken, err := verifier.Verify(rawIDToken) - if err != nil { - http.Error(w, "Failed to verify ID Token: "+err.Error(), http.StatusInternalServerError) - return - } - - // Unmarshal ID Token for expected custom claims. - var claims struct { - Email string `json:"email"` - EmailVerified bool `json:"email_verified"` - } - if err := idToken.Claims(&claims); err != nil { - http.Error(w, "Failed to unmarshal ID Token custom claims: "+err.Error(), http.StatusInternalServerError) - return - } - - // ... - }) - -ID Token nonces are supported. - -First, provide a nonce source for nonce validation. This will then be used to wrap the existing -provider ID Token verifier. - - // A verifier which boths verifies the ID Token signature and nonce. - nonceEnabledVerifier := provider.NewVerifier(ctx, oidc.VerifyNonce(nonceSource)) - -For the redirect provide a nonce auth code option. This will be placed as a URL parameter during -the client redirect. - - func handleRedirect(w http.ResponseWriter, r *http.Request) { - nonce, err := newNonce() - if err != nil { - // ... - } - // Provide a nonce for the OpenID Connect ID Token. - http.Redirect(w, r, oauth2Config.AuthCodeURL(state, oidc.Nonce(nonce)), http.StatusFound) - }) - -The nonce enabled verifier can then be used to verify the nonce while unpacking the ID Token. - - func handleOAuth2Callback(w http.ResponseWriter, r *http.Request) { - // Verify state... - - oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code")) - if err != nil { - http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError) - return - } - - // Extract the ID Token from oauth2 token. - rawIDToken, ok := oauth2Token.Extra("id_token").(string) - if !ok { - http.Error(w, "No ID Token found", http.StatusInternalServerError) - return - } - - // Verify that the ID Token is signed by the provider and verify the nonce. - idToken, err := nonceEnabledVerifier.Verify(rawIDToken) - if err != nil { - http.Error(w, "Failed to verify ID Token: "+err.Error(), http.StatusInternalServerError) - return - } - - // Continue as above... - }) - -This package uses contexts to derive HTTP clients in the same way as the oauth2 package. To configure -a custom client, use the oauth2 packages HTTPClient context key when constructing the context. - - myClient := &http.Client{} - - myCtx := context.WithValue(parentCtx, oauth2.HTTPClient, myClient) - - // NewProvider will use myClient to make the request. - provider, err := oidc.NewProvider(myCtx, "https://accounts.example.com") -*/ -package oidc diff --git a/vendor/github.com/ericchiang/oidc/examples/README.md b/vendor/github.com/ericchiang/oidc/examples/README.md deleted file mode 100644 index b44a2236..00000000 --- a/vendor/github.com/ericchiang/oidc/examples/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Examples - -These are example uses of the oidc package. Each requires a Google account and the -client ID and secret of a registered OAuth2 application. The client ID and secret -should be set as the following environment variables: - -``` -GOOGLE_OAUTH2_CLIENT_ID -GOOGLE_OAUTH2_CLIENT_SECRET -``` - -See Google's documentation on how to set up an OAuth2 app: -https://developers.google.com/identity/protocols/OpenIDConnect?hl=en - -Note that one of the redirect URL's must be "http://127.0.0.1:5556/auth/google/callback" diff --git a/vendor/github.com/ericchiang/oidc/internal/oidc.go b/vendor/github.com/ericchiang/oidc/internal/oidc.go deleted file mode 100644 index 01bd7972..00000000 --- a/vendor/github.com/ericchiang/oidc/internal/oidc.go +++ /dev/null @@ -1,7 +0,0 @@ -// Package internal contains support packages for the oidc package. -package internal - -// ContextKey is just an empty struct. It exists so context keys can be an immutable -// public variable with a unique type. It's immutable because nobody else can create -// a ContextKey, being unexported. -type ContextKey struct{} diff --git a/vendor/github.com/ericchiang/oidc/jwks.go b/vendor/github.com/ericchiang/oidc/jwks.go deleted file mode 100644 index 89847915..00000000 --- a/vendor/github.com/ericchiang/oidc/jwks.go +++ /dev/null @@ -1,188 +0,0 @@ -package oidc - -import ( - "encoding/json" - "errors" - "fmt" - "io" - "io/ioutil" - "net/http" - "sync" - "sync/atomic" - "time" - - "github.com/pquerna/cachecontrol" - "golang.org/x/net/context" - jose "gopkg.in/square/go-jose.v1" -) - -// No matter what insist on caching keys. This is so our request code can be -// asynchronous from matching keys. If the request code retrieved keys that -// expired immediately, the goroutine to match a JWT to a key would always see -// expired keys. -// -// TODO(ericchiang): Review this logic. -var minCache = 2 * time.Minute - -type cachedKeys struct { - keys map[string]jose.JsonWebKey // immutable - expiry time.Time -} - -type remoteKeySet struct { - client *http.Client - - // "jwks_uri" from discovery. - keysURL string - - // The value is always of type *cachedKeys. - // - // To ensure consistency always call keyCache.Store when holding cond.L. - keyCache atomic.Value - - // cond.L guards all following fields. sync.Cond is used in place of a mutex - // so multiple processes can wait on a single request to update keys. - cond sync.Cond - // Is there an existing request to get the remote keys? - inflight bool - // If the last attempt to refresh keys failed, the error will be saved here. - // - // TODO(ericchiang): If a routine sets this before calling cond.Broadcast(), - // there's no guarentee that a routine calling cond.Wait() will actual see - // the error called by the previous routine. Since Broadcast() unlocks - // cond.L and Wait() must reacquire the lock, other routines waiting on the - // lock might acquire it first. Maybe just log the error? - lastErr error -} - -func newRemoteKeySet(ctx context.Context, jwksURL string) *remoteKeySet { - r := &remoteKeySet{ - client: contextClient(ctx), - keysURL: jwksURL, - cond: sync.Cond{L: new(sync.Mutex)}, - } - return r -} - -func (r *remoteKeySet) verifyJWT(jwt string) (payload []byte, err error) { - jws, err := jose.ParseSigned(jwt) - if err != nil { - return nil, fmt.Errorf("parsing jwt: %v", err) - } - keyIDs := make([]string, len(jws.Signatures)) - for i, signature := range jws.Signatures { - keyIDs[i] = signature.Header.KeyID - } - key, err := r.getKey(keyIDs) - if err != nil { - return nil, fmt.Errorf("oidc: %s", err) - } - return jws.Verify(key) -} - -func (r *remoteKeySet) getKeyFromCache(keyIDs []string) (*jose.JsonWebKey, bool) { - cachedKeys, ok := r.keyCache.Load().(*cachedKeys) - if !ok { - return nil, false - } - if time.Now().After(cachedKeys.expiry) { - return nil, false - } - for _, keyID := range keyIDs { - if key, ok := cachedKeys.keys[keyID]; ok { - return &key, true - } - } - return nil, false -} - -func (r *remoteKeySet) getKey(keyIDs []string) (*jose.JsonWebKey, error) { - // Fast path. Just do an atomic load. - if key, ok := r.getKeyFromCache(keyIDs); ok { - return key, nil - } - - // Didn't find keys, use the slow path. - r.cond.L.Lock() - defer r.cond.L.Unlock() - - // Check again within the mutex. - if key, ok := r.getKeyFromCache(keyIDs); ok { - return key, nil - } - - // Keys have expired or we're trying to verify a JWT we don't have a key for. - - if !r.inflight { - // There isn't currently an inflight request to update keys, start a - // goroutine to do so. - r.inflight = true - go func() { - newKeys, newExpiry, err := requestKeys(r.client, r.keysURL) - - r.cond.L.Lock() - defer r.cond.L.Unlock() - - r.inflight = false - if err != nil { - r.lastErr = err - } else { - r.keyCache.Store(&cachedKeys{newKeys, newExpiry}) - r.lastErr = nil - } - - r.cond.Broadcast() // Wake all r.cond.Wait() calls. - }() - } - - // Wait for r.cond.Broadcast() to be called. This unlocks r.cond.L and - // reacquires it after its done waiting. - r.cond.Wait() - - if key, ok := r.getKeyFromCache(keyIDs); ok { - return key, nil - } - if r.lastErr != nil { - return nil, r.lastErr - } - return nil, errors.New("no signing keys can validate the signature") -} - -func requestKeys(client *http.Client, keysURL string) (map[string]jose.JsonWebKey, time.Time, error) { - req, err := http.NewRequest("GET", keysURL, nil) - if err != nil { - return nil, time.Time{}, fmt.Errorf("can't create request: %v", err) - } - resp, err := client.Do(req) - if err != nil { - return nil, time.Time{}, fmt.Errorf("can't GET new keys %v", err) - } - defer resp.Body.Close() - - body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20)) - if err != nil { - return nil, time.Time{}, fmt.Errorf("can't fetch new keys: %v", err) - } - if resp.StatusCode != http.StatusOK { - return nil, time.Time{}, fmt.Errorf("can't fetch new keys: %s %s", resp.Status, body) - } - - var keySet jose.JsonWebKeySet - if err := json.Unmarshal(body, &keySet); err != nil { - return nil, time.Time{}, fmt.Errorf("can't decode keys: %v %s", err, body) - } - - keys := make(map[string]jose.JsonWebKey, len(keySet.Keys)) - for _, key := range keySet.Keys { - keys[key.KeyID] = key - } - - minExpiry := time.Now().Add(minCache) - - if _, expiry, err := cachecontrol.CachableResponse(req, resp, cachecontrol.Options{}); err == nil { - if minExpiry.Before(expiry) { - return keys, expiry, nil - } - } - return keys, minExpiry, nil -} diff --git a/vendor/github.com/ericchiang/oidc/jwks_test.go b/vendor/github.com/ericchiang/oidc/jwks_test.go deleted file mode 100644 index ef668165..00000000 --- a/vendor/github.com/ericchiang/oidc/jwks_test.go +++ /dev/null @@ -1,285 +0,0 @@ -package oidc - -import ( - "bytes" - "crypto/x509" - "encoding/hex" - "encoding/json" - "encoding/pem" - "hash/fnv" - "io/ioutil" - "net/http" - "net/http/httptest" - "net/url" - "path/filepath" - "strings" - "testing" - "time" - - "golang.org/x/net/context" - - jose "gopkg.in/square/go-jose.v1" -) - -func loadKeySet(t *testing.T, keyfiles ...string) jose.JsonWebKeySet { - var set jose.JsonWebKeySet - set.Keys = make([]jose.JsonWebKey, len(keyfiles)) - for i, keyfile := range keyfiles { - set.Keys[i], _ = loadKey(t, keyfile) - } - return set -} - -func loadKey(t *testing.T, keyfile string) (pub, priv jose.JsonWebKey) { - data, err := ioutil.ReadFile(keyfile) - if err != nil { - t.Fatalf("can't read key file %s: %v", keyfile, err) - } - - block, _ := pem.Decode(data) - if block == nil { - t.Fatalf("no PEM data found in key file: %s", keyfile) - } - - keyID := hex.EncodeToString(fnv.New64().Sum(block.Bytes)) - - if strings.HasPrefix(filepath.Base(keyfile), "ecdsa") { - p, err := x509.ParseECPrivateKey(block.Bytes) - if err != nil { - t.Fatalf("failed to parse ecdsa key %s: %v", keyfile, err) - } - priv = jose.JsonWebKey{Key: p, Algorithm: "EC", Use: "sig", KeyID: keyID} - pub = jose.JsonWebKey{Key: p.Public(), Algorithm: "EC", Use: "sig", KeyID: keyID} - } else { - p, err := x509.ParsePKCS1PrivateKey(block.Bytes) - if err != nil { - t.Fatalf("failed to parse rsa key %s: %v", keyfile, err) - } - priv = jose.JsonWebKey{Key: p, Algorithm: "RSA", Use: "sig", KeyID: keyID} - pub = jose.JsonWebKey{Key: p.Public(), Algorithm: "RSA", Use: "sig", KeyID: keyID} - } - return -} - -func signPayload(t *testing.T, keyfile string, payload []byte) string { - _, key := loadKey(t, keyfile) - var ( - signer jose.Signer - err error - ) - if strings.HasPrefix(filepath.Base(keyfile), "rsa") { - signer, err = jose.NewSigner(jose.RS512, &key) - } else { - signer, err = jose.NewSigner(jose.ES512, &key) - } - if err != nil { - t.Fatalf("failed to create signer for %s: %v", keyfile, err) - } - jws, err := signer.Sign(payload) - if err != nil { - t.Fatalf("failed to sign payload: %v", err) - } - s, err := jws.CompactSerialize() - if err != nil { - t.Fatalf("failed to serialize signature: %v", err) - } - return s -} - -func TestVerify(t *testing.T) { - jwks := loadKeySet(t, - "testdata/ecdsa_521_1.pem", - "testdata/rsa_2048_1.pem", - ) - s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if err := json.NewEncoder(w).Encode(jwks); err != nil { - t.Errorf("failed to encode jwks: %v", err) - } - })) - defer s.Close() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - r := newRemoteKeySet(ctx, s.URL) - - tests := []struct { - key string - wantErr bool - }{ - {"testdata/ecdsa_521_1.pem", false}, - {"testdata/rsa_2048_1.pem", false}, - {"testdata/ecdsa_521_2.pem", true}, - {"testdata/rsa_2048_2.pem", true}, - {"testdata/ecdsa_521_1.pem", false}, - {"testdata/rsa_2048_1.pem", false}, - } - - for _, tc := range tests { - data := []byte("foobar") - payload, err := r.verifyJWT(signPayload(t, tc.key, data)) - if err != nil { - if !tc.wantErr { - t.Fatalf("failed to verify JWT signed by %s: %v", tc.key, err) - } - continue - } - - if tc.wantErr { - t.Fatalf("didn't expecte to be able to verify payload with %s", tc.key) - } - - if bytes.Compare(payload, data) != 0 { - t.Errorf("want %q got %q", data, payload) - } - } -} - -func TestCacheControl(t *testing.T) { - tests := []struct { - transport http.RoundTripper - expectedMaxAge time.Duration - url string - nKeys int - }{ - { - transport: new(googleKeysRoundTripper), - expectedMaxAge: 18213 * time.Second, - url: "https://googleapis.com/oauth2/v3/certs", - nKeys: 4, - }, - { - // SalesForce insists on not caching keys. - transport: new(salesForceKeysRoundTripper), - expectedMaxAge: minCache, - url: "https://login.salesforce.com/id/keys", - nKeys: 8, - }, - } - for _, tc := range tests { - client := &http.Client{Transport: tc.transport} - - before := time.Now().Add(tc.expectedMaxAge) - keys, expiry, err := requestKeys(client, tc.url) - if err != nil { - t.Fatalf("Request keys failed: %v", err) - } - tAfter := time.Now() - after := tAfter.Add(tc.expectedMaxAge) - approxExpiration := expiry.Sub(tAfter) - - if expiry.Before(before) || expiry.After(after) { - t.Errorf("expected keys to expire in %s: got about %s", tc.expectedMaxAge, approxExpiration) - } - if len(keys) != tc.nKeys { - t.Errorf("expected %d keys got %d", tc.nKeys, len(keys)) - } - } -} - -var googleKeysReq = http.Request{ - Method: "GET", - URL: &url.URL{Scheme: "https", Host: "www.googleapis.com", Path: "/oauth2/v3/certs"}, - Proto: "HTTP/1.1", - ProtoMajor: 1, - ProtoMinor: 1, - Header: http.Header{ - "Host": []string{"www.googleapis.com"}, - "User-Agent": []string{"curl/7.43.0"}, - "Accept": []string{"*/*"}, - }, - Host: "www.googleapis.com", -} - -type googleKeysRoundTripper struct{} - -func (_ *googleKeysRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) { - return &http.Response{ - Status: "200 OK", - StatusCode: 200, - Proto: "HTTP/1.1", - ProtoMajor: 1, - ProtoMinor: 1, - Header: http.Header{ - "Expires": []string{"Sat, 16 Jul 2016 01:27:10 GMT"}, - "Date": []string{"Fri, 15 Jul 2016 20:23:37 GMT"}, - "Vary": []string{"Origin", "X-Origin"}, - "Content-Type": []string{"application/json; charset=UTF-8"}, - "X-Content-Type-Options": []string{"nosniff"}, - "X-Frame-Options": []string{"SAMEORIGIN"}, - "X-XSS-Protection": []string{"1; mode=block"}, - "Content-Length": []string{"1957"}, - "Server": []string{"GSE"}, - "Cache-Control": []string{"public, max-age=18213, must-revalidate, no-transform"}, - "Age": []string{"13156"}, - "Alternate-Protocol": []string{"443:quic"}, - "Alt-Svc": []string{`quic=":443"; ma=2592000; v="36,35,34,33,32,31,30,29,28,27,26,25"`}, - }, - ContentLength: 1957, - Body: ioutil.NopCloser(strings.NewReader(googleKeysBody)), - }, nil -} - -type salesForceKeysRoundTripper struct{} - -func (_ *salesForceKeysRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) { - return &http.Response{ - Status: "200 OK", - StatusCode: 200, - Proto: "HTTP/1.1", - ProtoMajor: 1, - ProtoMinor: 1, - Header: http.Header{ - "Date": []string{"Mon, 18 Jul 2016 23:06:26 GMT"}, - "Strict-Transport-Security": []string{"max-age=10886400; includeSubDomains; preload"}, - "Content-Security-Policy-Report-Only": []string{"default-src https:; script-src https: 'unsafe-inline' 'unsafe-eval'; style-src https: 'unsafe-inline'; img-src https: data:; font-src https: data:; report-uri /_/ContentDomainCSPNoAuth?type=login"}, - "Set-Cookie": []string{"BrowserId=YQwZPagMRDiCEqn6s7D4cg;Path=/;Domain=.salesforce.com;Expires=Fri, 16-Sep-2016 23:06:26 GMT"}, - "Expires": []string{"Thu, 01 Jan 1970 00:00:00 GMT"}, - "Content-Type": []string{"application/json;charset=UTF-8"}, - "Pragma": []string{"no-cache"}, - "Cache-Control": []string{"no-cache, no-store"}, - "Transfer-Encoding": []string{"chunked"}, - }, - ContentLength: -1, - Body: ioutil.NopCloser(strings.NewReader(salesForceKeysBody)), - }, nil -} - -var googleKeysBody = `{ - "keys": [ - { - "kty": "RSA", - "alg": "RS256", - "use": "sig", - "kid": "40aa42edac0614d7ca3f57f97ee866cdfba3b61a", - "n": "6lm9AEGLPFpVqnfeVFuTIZsj7vz_kxla6uW1WWtosM_MtIjXkyyiSolxiSOs3bzG66iVm71023QyOzKYFbio0hI-yZauG3g9nH-zb_AHScsjAKagHtrHmTdtq0JcNkQnAaaUwxVbjwMlYAcOh87W5jWj_MAcPvc-qjy8-WJ81UgoOUZNiKByuF4-9igxKZeskGRXuTPX64kWGBmKl-tM7VnCGMKoK3m92NPrktfBoNN_EGGthNfQsKFUdQFJFtpMuiXp9Gib7dcMGabxcG2GUl-PU086kPUyUdUYiMN2auKSOxSUZgDjT7DcI8Sn8kdQ0-tImaHi54JNa1PNNdKRpw", - "e": "AQAB" - }, - { - "kty": "RSA", - "alg": "RS256", - "use": "sig", - "kid": "8fbbeea40332d2c0d27e37e1904af29b64594e57", - "n": "z7h6_rt35-j6NV2iQvYIuR3xvsxmEImgMl8dc8CFl4SzEWrry3QILajKxQZA9YYYfXIcZUG_6R6AghVMJetNIl2AhCoEr3RQjjNsm9PE6h5p2kQ-zIveFeb__4oIkVihYtxtoYBSdVj69nXLUAJP2bxPfU8RDp5X7hT62pKR05H8QLxH8siIQ5qR2LGFw_dJcitAVRRQofuaj_9u0CLZBfinqyRkBc7a0zi7pBxtEiIbn9sRr8Kkb_Boap6BHbnLS-YFBVarcgFBbifRf7NlK5dqE9z4OUb-dx8wCMRIPVAx_hV4Qx2anTgp1sDA6V4vd4NaCOZX-mSctNZqQmKtNw", - "e": "AQAB" - }, - { - "kty": "RSA", - "alg": "RS256", - "use": "sig", - "kid": "6758b0b8eb341e90454860432d6a1648bf4de03b", - "n": "5K0rYaA7xtqSe1nFn_nCA10uUXY81NcohMeFsYLbBlx_NdpsmbpgtXJ6ektYR7rUdtMMLu2IONlNhkWlx-lge91okyacUrWHP88PycilUE-RnyVjbPEm3seR0VefgALfN4y_e77ljq2F7W2_kbUkTvDzriDIWvQT0WwVF5FIOBydfDDs92S-queaKgLBwt50SXJCZryLew5ODrwVsFGI4Et6MLqjS-cgWpCNwzcRqjBRsse6DXnex_zSRII4ODzKIfX4qdFBKZHO_BkTsK9DNkUayrr9cz8rFRK6TEH6XTVabgsyd6LP6PTxhpiII_pTYRSWk7CGMnm2nO0dKxzaFQ", - "e": "AQAB" - }, - { - "kty": "RSA", - "alg": "RS256", - "use": "sig", - "kid": "0b915c6a65e2651ddcd0977757ebc644220a23b5", - "n": "vKa1quTzaKez5hyWc1SRYA_pgCEUx2FgpcfHEz33IjruqM57QTg4jZ2sukU5JdkjegtZm_ry9FuFKB-tIFszNqRBc4hgKWNzvaXicPpGP-tWFxhe30esaXUhF4WpZd4uhLfowJxuXKkM0qjRFAbQiP_N64fauozjquLfESaT0WdclK-wACzb-Mo9GoxYzgmPSRvmNJ83ZfBOimcIfuCQZkIFUjHrYRJ-kZfS02_tkkqpP8KaU3QL0igsQglawJpvH3TTZbA0xuJMOwGESN1xq4Xr_7o3OfNTTfybdVv1o8FMT8snIATGwKXvi9J3P7OEV6r8_pdFUhCOAvRlUYw5-Q", - "e": "AQAB" - } - ] -}` - -var salesForceKeysBody = `{"keys":[{"kty":"RSA","n":"nOcQOvHV8rc-hcfP_RmxMGjyVlruSLeFXTojYcbixaAH36scUejjaws31orUjmYqB5isE9ntdsL4DnsdP_MDJ2mtYD2FIh8tBkJjgXitjdcDclrwELAx846wBIlSES8wR6czpdJZfSwhL_92EGpDH6z7lKEClqhDlbtZ-yFKFj9BQRwaEXWV7uuq23gxXOqyEN0WXl3ZJPgsodCnlXRn9y_r5CNV9V4wvzXGlJhT3Nv_N_Z5XNZIjZnHdCuE_itT4a1xENEEds7Jjg5mRTlVFzYv5iQtBo7jdY5ogMTgKPmRh6hYuqLeki3AOAUff1AGaN9TZH60UxwTw03-DQJL5C2SuC_vM5KIWxZxQniubfegUCBXpJSAJbLt8zSFztTcrLS4-wgUHo1A8TDNaO28_KsBUTWsrieOr3NfCn4bPNb7t8G90U60lW0GIhEda3fNYnV0WWpZVO1jCRNy_JYUs3ECo0E1ZQJZD72Dm6UjiuH7eR3ZgNKR9tlLNdyZSpZUZPErLrXJ90d5XbmJYvRX9r93z6GQqOv5FQy1JhatwefxhKdyhkDEHsqELO0XDqnDnmgxkEEU-lHYSVGz-iDlUZOUYTTCtxsPDmBIXOMuwp0UydJphO36qRQaDyEjHNsYKLj5KVvjDHS8Gw1FhbFvsoUrBHre4hLY9Pa5meatV_k","e":"AQAB","alg":"RS256","use":"sig","kid":"192"},{"kty":"RSA","n":"sTuRuR0_kfPcrMug1dg4e3y4_elyaKDyzqlvEpzo6FgYTAuR-G9wv4Au6O363PDF8Yd8wg5JSwLjcHlfp3auXb_9pzJyhMBUAQwUYhVOPyxGOiyGYwCwkUX_GUA8IGPQZ6XOISX65492upv2DXW85vxYjPGJypWIQ3GjYrYtqmEg2fkuXX3IwITMxHoxYSmvR5Qcn4acnxRKMqCohxLANDEN4xyZBGbuchXnb0TGFwcGNAO8mz-O4E_A9k3QQwz750Qz6xEe8_UWPRvyX32EvGN19tX90DcT488YT0ImdUt6sVZEyOU_vxYXFFpWhf2_C1_AcjUQejex5y4mQn3idtVSAVGQ_eqhS3ZpyY_IhAGSC1JR_TJfrgnJoQrvv7Toz4K-iskOkFDNZYjhkKSla0EaJOUjRdZxmFTlzydmJFCD16-dbB8cCYGEQWVhnmBnYRQ2zquTCGReuILEcV0ALo9sR7zA5u8xSoJpZO1-KM_9lfbBH5M8V6R-ECWJI5MqPJxK0D3BCiNNxI5Kdslb_49SNA3qukQciassa-xbm26MGyYjt7Kn2KC2Tdh8L0z3328Pe6Wn7-vixSbtSc_qy15OifYKHr7BE1JjZfbkFQ6ZY5WgHZOZWG31-9aIhKsCKJGfGwyaN-I2-IzygSIihJNrFnSrO_s80li3H8P-4A0","e":"AQAB","alg":"RS256","use":"sig","kid":"198"},{"kty":"RSA","n":"n9ggEbWxKGy-cke0GpNQlblk47vL7-rtSsPwehgCdt7vANf6n6eqopLN1WPfq-xRJo7aE4gBXV3hQ4Ts1vm3_lXZmUCcFl8iVEYtDUZakmCYlDZExpo5AuRpwIayaidOzFP2qpMNstd9iVN_0ShCNjN-I_kJXAliqxuEZw5A-P-mcUBkE5ar_xVV3TaAXsxU5cecou9fbQjSLSKpQzfKVdUr0japx4iwSv0vdjXzItUfu7CykHU03Xmd4ucv47gdE0FeESgtPvC6cz6W6qCddP3rjBlgYAg0xYu_uyfLLYyaZZoEx8PfYUXfC4zM2a9p7bT1RxfRJDIqA5S507CylpzeAdBdp0N9UCtecENVgVcRotFBv1GOuVEhkba93spdddCGTSjWqzbq2hVj4napeGZ3I1st0mLQCD2reUh6injMFkVllnpfOitjvM98V-c_cxiTJYfg8K338atpWM7nR9Yo5D684Pr-z77xTl0_WY8mIukmGQtznX2dEYYbyzj_PLtK4YSPyrvnqdGFcZpxstSWOHnCeLNigzWCCBbcO2B8F3QGDagW6Zu7IH_P3KRPqwKWDzGzpmDJPGYexz46qezjVnPm-aCcx_m6cnqXqMMt60YtCMFCLxyyBJrTx66LNUpxVRun2vAu2HHdwxv1lzrEvDMzwrSW9JZVmAJ_eg0","e":"AQAB","alg":"RS256","use":"sig","kid":"202"},{"kty":"RSA","n":"o8Fz0jXjZ0Rz5Kt2TmzP0xVokf-Q4Az-MQg5i5MCxNNTQiZp7VkwAZeM0mJ-mKDbCzPm9ws43v8cxeiIkVZQqrAocnnb90MDCnU-7oD7MvOU4SbmhuLzVCyVZPIBRq5z0OgjcwLeD4trOoogkLOu0kyuyzNoFkr712m_GZ1xic-X0MlFKq3-2cI4U2nEuuh-Xcy7bUqCx0zTJFPOOKghGYEZZ6biZ04VC-ERcW6cC19pEWm6vCqZJEsKPCfazVAoHKZAukNd0XLPQd_W6xAaGnp8e7a5tFHn6dU6ikhI94ZieVp6WItWsQTDwJH-D7bVpVRG-lWL74lgcuQdFAtldm__k7FvlTXdqiLrd0rYuDnTFiwUSsUXWBJbmGVsEOylZVPQAL-K7G7p3BRY4X26vOgfludwCOj7L7WFbd0IXziTm74xe2KZGKsFpoCjJI0z_D5Oe5bofswr1Ceafhl97suG7OoInobt7QAQnnLcBVzUPz_TflOXDc5UiePptA0bxdd8MVENiDbTGGNz6DCzfL986QfcJLPB8aZa3lFN0kWPBkOclZagL4WpyIllB6euvZ2hfpt8IY2_bmUN06luo6N7Fy0hSSFMWvfzaD8_Ff3czb1Kv-b0xI6Ugk4d67RNNSbTcRM2Muvx-dJgOyXqrc_hE96OOqcMjrGZJoXnCAM","e":"AQAB","alg":"RS256","use":"sig","kid":"190"},{"kty":"RSA","n":"wIQtK09qsu1qCCQu1mHh6d_EyyOlbqMCV8WMacOyhZng1sbaFJY-0PIH46Kw1uhjbHg94_r2UELYd30vF8xwViGhCmpPuSGhkxNoT5CMoJPS6JW-zBpR7suHqBUnaGdZ6G2uYZDpwWYs_4SJDuWzxVBrQqIM_ZVgUqutniQPmjMAX5MqznBTnG3zm728BmNzS7T2gtzxs3jAgDsSAu3Kxp3D6NDGERhaAJ8jOgwHvmQK5xFi9Adw7sv2nCH-wM-C5fLJYmpGOSrTP1HLOlq--TROAvWL9gcNEeq4arryIYux5syg66rHT8U2Uhb1PdXt7ReQY8wBnP2BBH1QH7rzOZ7UbqFLbQUQsZFAVMcfm7gJN8JWLlcSJZdC2zaY0wI5q8PWN-N_GgAK64FKZQ7pB0bRQ5AQx-D3U4sYE4EcgSvV8fW86PaF1VXaHMFcom48gZ1GzE_V25uPb-0yue0cv9lejrIKDvRiJ5UiyUPphro4Aw2ZcDi_8r8rqfglWhcnB4bGSri4kEBb_IdwvqKwRCqxlNdRnU1ooQeUBaVRwdbpj23Z1qtYjB55Wf2KOCJ6ewMyddq4bEAG6KIqPmssT7_exvygUyuW6qhnCV-gTZEwFI0A6djsHM5itfkzNY47BeuAtGXjuaRnVYIEvTrnSj3Lx7YfvCIiGqFrG6y31Ak","e":"AQAB","alg":"RS256","use":"sig","kid":"188"},{"kty":"RSA","n":"hsqiqMXZmxJHzWfZwbSffKfc9YYMxj83-aWhA91jtI8k-GMsEB6mtoNWLP6vmz6x6BQ8Sn6kmn65n1IGCIlWxhPn9yqfXBDBaHFGYED9bBloSEMFnnS9-ACsWrHl5UtDQ3nh-VQTKg1LBmjJMmAOHdBLoUikfpx8fjA1LfDn_1iNWnguj2ehgjWCuTn64UdUd84YNcfO8Ha0TAhWHOhkiluMyzGS0dtN0h8Ybyi5oL6Bf1sfhtOncUh1JuWMcmvICbGEkA_0vBbMp9nCvXdMlpzMOCIoYYkQ-25SRZ0GpIr_oBIZByEm1XaJIqNXoC7qJ95iAyWkUiSegY_IcBV3nMXr-kDNn9Vm2cgLEJGymOiDQKH8g7VjraCIrqWPD3DWv3Z6RsExs6i0gG3JU9cVVFwz87d05_yk3L5ubWb96uxsP9rkwZ3h8eJTfFrgMhk1ZwR-63Dk3ZLYisiAU0zKgr4vQ9qsCNPqDg0rkeqOY5k7Gy201_wh6Sw5dCNTTGmZZ1rNE-gyDu4-a1H40n8f2JFiH-xIOD9-w8HGYOu_oGlobK2KvzFYHTk-w7vtfhZ0j96UkjaBhVjYSMi4hf43xNbB4xJoHhHLESABLp9IYDlnzBeBXKumXDO5aRk3sFAEAWxj57Ec_DyK6UwXSR9Xqji5a1lEArUdFPYzVZ_YCec","e":"AQAB","alg":"RS256","use":"sig","kid":"194"},{"kty":"RSA","n":"5SGw1jcqyFYEZaf39RoxAhlq-hfRSOsneVtsT2k09yEQhwB2myvf3ckVAwFyBF6y0Hr1psvu1FlPzKQ9YfcQkfge4e7eeQ7uaez9mMQ8RpyAFZprq1iFCix4XQw-jKW47LAevr9w1ttZY932gFrGJ4gkf_uqutUny82vupVUETpQ6HDmIL958SxYb_-d436zi5LMlHnTxcR5TWIQGGxip-CrD7vOA3hrssYLhNGQdwVYtwI768EvwE8h4VJDgIrovoHPH1ofDQk8-oG20eEmZeWugI1K3z33fZJS-E_2p_OiDVr0EmgFMTvPTnQ75h_9vyF1qhzikJpN9P8KcEm8oGu7KJGIn8ggUY0ftqKG2KcWTaKiirFFYQ981PhLHryH18eOIxMpoh9pRXf2y7DfNTyid99ig0GUH-lzAlbKY0EV2sIuvEsIoo6G8YT2uI72xzl7sCcp41FS7oFwbUyHp_uHGiTZgN7g-18nm2TFmQ_wGB1xCwJMFzjIXq1PwEjmg3W5NBuMLSbG-aDwjeNrcD_4vfB6yg548GztQO2MpV_BuxtrZDJQm-xhJXdm4FfrJzWdwX_JN9qfsP0YU1_mxtSU_m6EKgmwFdE3Yh1WM0-kRRSk3gmNvXpiKeVduzm8I5_Jl7kwLgBw24QUVaLZn8jC2xWRk_jcBNFFLQgOf9U","e":"AQAB","alg":"RS256","use":"sig","kid":"196"},{"kty":"RSA","n":"m-rZsEmySPnZLZ9tdoQ9bW0sI_nudwy70vAXK1JZV9AhCJQB5ZHykaK90mpmwAdt8XsrOuQ6Nd9hrgZOHgq-RznNCFhE9qpnOHg68ywsUeZHXD9FqX_QlKPzMBBQXhyWLz58_LHMmGhn4740rB7tXftvDBOctX41I_hilm8_vKbSPE2ov1h9dYMt0a9jFTd6xk5dnj_r3KiswIl4FG4KoXHCv7Hzgv6iVyOJJgTgJcmRw9ydDxvEzkyXlIYFsOW0IKxAoA28ECAImPmiRX37-oP_IK6ZrjxNHd7SqQS12uc33N1ZfI3WR35GGWfLTH7IjjLj0c8lvUrQOQTZc686wqySRZz-BWSFBAYR8OLA-T1SZVk3R_FyZ26mXNYo33I9DcuK5y6c_AMoO0c4Mw-WmTkrAu5QTPWJ-iQsbMjfIDC7XkxkBoIMqIzynZoV1fH5yO_2CyWQfsr1PJRjp3tFhcVgUBrhVY47IsDQ9pmE8XgtRe9dQGT2wXn-aANqF6vQkw_kJ-zDD5VWQ_IQol_znwyktAoYB7iRwS2Ut4l4YsFE5D9SywHK0F1mvIQWENNbw2WjIxgjI8DtJFPdTN_dhnZDfkKjsxvQlchIiMZGAErhaveyyx4ezbUo4XAtzj84QAj9IjtEjOkEo6MqDDeCh6pXlIV_UBdblTXoX37DRpc","e":"AQAB","alg":"RS256","use":"sig","kid":"200"}]}` diff --git a/vendor/github.com/ericchiang/oidc/nonce.go b/vendor/github.com/ericchiang/oidc/nonce.go deleted file mode 100644 index c0bb213f..00000000 --- a/vendor/github.com/ericchiang/oidc/nonce.go +++ /dev/null @@ -1,35 +0,0 @@ -package oidc - -import ( - "errors" - - "golang.org/x/oauth2" -) - -// Nonce returns an auth code option which requires the ID Token created by the -// OpenID Connect provider to contain the specified nonce. -func Nonce(nonce string) oauth2.AuthCodeOption { - return oauth2.SetAuthURLParam("nonce", nonce) -} - -// NonceSource represents a source which can verify a nonce is valid and has not -// been claimed before. -type NonceSource interface { - ClaimNonce(nonce string) error -} - -// VerifyNonce ensures that the ID Token contains a nonce which can be claimed by the nonce source. -func VerifyNonce(source NonceSource) VerificationOption { - return nonceVerifier{source} -} - -type nonceVerifier struct { - nonceSource NonceSource -} - -func (n nonceVerifier) verifyIDToken(token *IDToken) error { - if token.Nonce == "" { - return errors.New("oidc: no nonce present in ID Token") - } - return n.nonceSource.ClaimNonce(token.Nonce) -} diff --git a/vendor/github.com/ericchiang/oidc/oidc_test.go b/vendor/github.com/ericchiang/oidc/oidc_test.go deleted file mode 100644 index 3a31646f..00000000 --- a/vendor/github.com/ericchiang/oidc/oidc_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package oidc - -import ( - "encoding/json" - "reflect" - "testing" -) - -func TestClientVerifier(t *testing.T) { - tests := []struct { - clientID string - aud []string - wantErr bool - }{ - { - clientID: "1", - aud: []string{"1"}, - }, - { - clientID: "1", - aud: []string{"2"}, - wantErr: true, - }, - { - clientID: "1", - aud: []string{"2", "1"}, - }, - { - clientID: "3", - aud: []string{"1", "2"}, - wantErr: true, - }, - } - - for i, tc := range tests { - token := IDToken{Audience: tc.aud} - err := (clientVerifier{tc.clientID}).verifyIDToken(&token) - if err != nil && !tc.wantErr { - t.Errorf("case %d: %v", i) - } - if err == nil && tc.wantErr { - t.Errorf("case %d: expected error") - } - } -} - -func TestUnmarshalAudience(t *testing.T) { - tests := []struct { - data string - want audience - wantErr bool - }{ - {`"foo"`, audience{"foo"}, false}, - {`["foo","bar"]`, audience{"foo", "bar"}, false}, - {"foo", nil, true}, // invalid JSON - } - - for _, tc := range tests { - var a audience - if err := json.Unmarshal([]byte(tc.data), &a); err != nil { - if !tc.wantErr { - t.Errorf("failed to unmarshal %q: %v", tc.data, err) - } - continue - } - - if tc.wantErr { - t.Errorf("did not expected to be able to unmarshal %q", tc.data) - continue - } - - if !reflect.DeepEqual(tc.want, a) { - t.Errorf("from %q expected %q got %q", tc.data, tc.want, a) - } - } -} diff --git a/vendor/github.com/ericchiang/oidc/oidcproxy/main.go b/vendor/github.com/ericchiang/oidc/oidcproxy/main.go deleted file mode 100644 index fc54f8ed..00000000 --- a/vendor/github.com/ericchiang/oidc/oidcproxy/main.go +++ /dev/null @@ -1,283 +0,0 @@ -package main - -import ( - "crypto/rand" - "encoding/gob" - "flag" - "fmt" - "io" - "log" - "net/http" - "net/http/httputil" - "net/url" - "os" - "regexp" - "strconv" - "strings" - "time" - - "github.com/ericchiang/oidc" - "github.com/gorilla/securecookie" - - "golang.org/x/net/context" - "golang.org/x/oauth2" -) - -const ( - cookieName = "oidc-proxy" - // This header will be set by oidcproxy during authentication and - // passed to the backend. - emailHeaderName = "X-User-Email" -) - -// Session represents a logged in user's active session. -type Session struct { - Email string - Expires time.Time -} - -func init() { - gob.Register(&Session{}) -} - -var ( - // Flags. - issuer string - backend string - scopes string - allow string - httpAddr string - httpsAddr string - cookieExp time.Duration - - // Set up during initial configuration. - oauth2Config = new(oauth2.Config) - oidcProvider *oidc.Provider - backendHandler *httputil.ReverseProxy - verifier *oidc.IDTokenVerifier - - // Regexps of emails to allow. - allowEmail []*regexp.Regexp - - nonceSource *memNonceSource - - cookieEncrypter *securecookie.SecureCookie -) - -func main() { - flag.StringVar(&issuer, "issuer", "https://accounts.google.com", "The issuer URL of the OpenID Connect provider.") - flag.StringVar(&backend, "backend", "", "The URL of the backened to proxy to.") - flag.StringVar(&oauth2Config.RedirectURL, "redirect-url", "", "A full OAuth2 redirect URL.") - flag.StringVar(&oauth2Config.ClientID, "client-id", "", "The client ID of the OAuth2 client.") - flag.StringVar(&oauth2Config.ClientSecret, "client-secret", "", "The client secret of the OAuth2 client.") - flag.StringVar(&scopes, "scopes", "openid,email,profile", `A comma seprated list of OAuth2 scopes to request ("openid" required).`) - flag.StringVar(&allow, "allow-email", ".*", "Comma seperated list of email regexp's to match for access to the backend.") - flag.StringVar(&httpAddr, "http", "127.0.0.1:5556", "Default address to listen on.") - flag.DurationVar(&cookieExp, "cookie-exp", time.Hour*24, "Duration for which a login cookie is valid for.") - flag.Parse() - - // Set flags from environment variables. - flag.VisitAll(func(f *flag.Flag) { - if f.Value.String() != f.DefValue { - return - } - - // Convert flag name, e.g. "redirect-url" becomes "OIDC_PROXY_REDIRECT_URL" - envVar := "OIDC_PROXY_" + strings.ToUpper(strings.Replace(f.Name, "-", "_", -1)) - - if envVal := os.Getenv(envVar); envVal != "" { - if err := flag.Set(f.Name, envVal); err != nil { - log.Fatal(err) - } - } - // All flags are manditory. - if f.Value.String() == "" { - flag.Usage() - os.Exit(2) - } - }) - - // compile email regexps - for _, expr := range strings.Split(allow, ",") { - allowEmailRegexp, err := regexp.Compile(expr) - if err != nil { - log.Fatalf("invalid regexp: %q %v", expr, err) - } - allowEmail = append(allowEmail, allowEmailRegexp) - } - - // configure reverse proxy - backendURL, err := url.Parse(backend) - if err != nil { - log.Fatalf("failed to parse backend: %v", err) - } - backendHandler = httputil.NewSingleHostReverseProxy(backendURL) - - redirectURL, err := url.Parse(oauth2Config.RedirectURL) - if err != nil { - log.Fatalf("failed to parse redirect URL: %v", err) - } - - // Query for the provider. - oidcProvider, err = oidc.NewProvider(context.TODO(), issuer) - if err != nil { - log.Fatalf("failed to get provider: %v", err) - } - - nonceSource = newNonceSource(context.TODO()) - verifier = oidcProvider.NewVerifier(context.TODO(), oidc.VerifyNonce(nonceSource)) - - oauth2Config.Endpoint = oidcProvider.Endpoint() - oauth2Config.Scopes = strings.Split(scopes, ",") - - // Initialize secure cookies. - // TODO(ericchiang): make these configurable - hashKey := make([]byte, 64) - blockKey := make([]byte, 32) - if _, err := io.ReadFull(rand.Reader, hashKey); err != nil { - log.Fatalf("failed to initialize hash key: %v", err) - } - if _, err := io.ReadFull(rand.Reader, blockKey); err != nil { - log.Fatalf("failed to initialize block key: %v", err) - } - cookieEncrypter = securecookie.New(hashKey, blockKey) - - mux := http.NewServeMux() - mux.HandleFunc("/", handleProxy) - mux.HandleFunc("/login", handleRedirect) - mux.HandleFunc("/logout", handleLogout) - mux.HandleFunc(redirectURL.Path, handleCallback) - - log.Printf("Listening on: %s", httpAddr) - http.ListenAndServe(httpAddr, mux) -} - -// httpRedirect returns a handler which redirects to the provided path. -func httpRedirect(path string) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, path, http.StatusFound) - }) -} - -// httpError returns a handler which presents an error to the end user. -func httpError(status int, format string, a ...interface{}) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - http.Error(w, fmt.Sprintf(format, a...), http.StatusInternalServerError) - }) -} - -func handleCallback(w http.ResponseWriter, r *http.Request) { - func() http.Handler { - state := r.URL.Query().Get("state") - if state == "" { - log.Printf("State not set") - return httpError(http.StatusInternalServerError, "Authentication failed") - } - if err := nonceSource.ClaimNonce(state); err != nil { - log.Printf("Failed to claim nonce: %v", err) - return httpError(http.StatusInternalServerError, "Authentication failed") - } - - oauth2Token, err := oauth2Config.Exchange(context.TODO(), r.URL.Query().Get("code")) - if err != nil { - log.Printf("Failed to exchange token: %v", err) - return httpError(http.StatusInternalServerError, "Authentication failed") - } - - // Extract the ID Token from oauth2 token. - rawIDToken, ok := oauth2Token.Extra("id_token").(string) - if !ok { - log.Println("No ID Token found") - return httpError(http.StatusInternalServerError, "Authentication failed") - } - - idToken, err := verifier.Verify(rawIDToken) - if err != nil { - log.Printf("Failed to verify token: %v", err) - return httpError(http.StatusInternalServerError, "Authentication failed") - } - var claims struct { - Email string `json:"email"` - EmailVerified bool `json:"email_verified"` - } - - if err := idToken.Claims(&claims); err != nil { - log.Printf("Failed to decode claims: %v", err) - return httpError(http.StatusInternalServerError, "Authentication failed") - } - if !claims.EmailVerified || claims.Email == "" { - log.Println("Failed to verify email") - return httpError(http.StatusInternalServerError, "Authentication failed") - } - - s := Session{Email: claims.Email, Expires: time.Now().Add(cookieExp)} - encoded, err := cookieEncrypter.Encode(cookieName, s) - if err != nil { - log.Printf("Failed to encrypt session: %v", err) - return httpError(http.StatusInternalServerError, "Authentication failed") - } - - // Set the encoded cookie - cookie := &http.Cookie{Name: cookieName, Value: encoded, HttpOnly: true, Path: "/"} - http.SetCookie(w, cookie) - return httpRedirect("/") - - }().ServeHTTP(w, r) -} - -func handleRedirect(w http.ResponseWriter, r *http.Request) { - // TODO(ericchiang): since arbitrary requests can create nonces, rate limit this endpoint. - func() http.Handler { - nonce, err := nonceSource.Nonce() - if err != nil { - log.Printf("Failed to create nonce: %v", err) - return httpError(http.StatusInternalServerError, "Failed to generate redirect") - } - state, err := nonceSource.Nonce() - if err != nil { - log.Printf("Failed to create state: %v", err) - return httpError(http.StatusInternalServerError, "Failed to generate redirect") - } - return httpRedirect(oauth2Config.AuthCodeURL(state, oauth2.ApprovalForce, oidc.Nonce(nonce))) - }().ServeHTTP(w, r) -} - -func handleLogout(w http.ResponseWriter, r *http.Request) { - cookie := &http.Cookie{Name: cookieName, Value: "", HttpOnly: true, Path: "/"} - http.SetCookie(w, cookie) - httpRedirect("/login").ServeHTTP(w, r) -} - -func handleProxy(w http.ResponseWriter, r *http.Request) { - func() http.Handler { - cookie, err := r.Cookie(cookieName) - if err != nil { - // Only error can be ErrNoCookie https://goo.gl/o5fZ49 - return httpRedirect("/login") - } - var s Session - if err := cookieEncrypter.Decode(cookieName, cookie.Value, &s); err != nil { - log.Printf("Failed to decode cookie: %v", err) - return http.HandlerFunc(handleLogout) // clear the cookie - } - if time.Now().After(s.Expires) { - log.Printf("Cookie for %q expired", s.Email) - return http.HandlerFunc(handleLogout) // clear the cookie - } - - for _, allow := range allowEmail { - if allow.MatchString(s.Email) { - r.Header.Set(emailHeaderName, s.Email) - return backendHandler - } - } - log.Printf("Denying %q", s.Email) - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - resp := []byte(`<html><head></head><body>Provided email does not have permission to login. <a href="/logout">Try a different account.</a></body></html>`) - w.Header().Set("Content-Type", "text/html") - w.Header().Set("Content-Length", strconv.Itoa(len(resp))) - w.WriteHeader(http.StatusForbidden) - w.Write(resp) - }) - }().ServeHTTP(w, r) -} diff --git a/vendor/github.com/ericchiang/oidc/oidcproxy/nonce.go b/vendor/github.com/ericchiang/oidc/oidcproxy/nonce.go deleted file mode 100644 index a52a3ed2..00000000 --- a/vendor/github.com/ericchiang/oidc/oidcproxy/nonce.go +++ /dev/null @@ -1,72 +0,0 @@ -package main - -import ( - "crypto/rand" - "encoding/base64" - "errors" - "io" - "sync" - "time" - - "golang.org/x/net/context" -) - -var ( - gcInterval = time.Minute - expiresIn = time.Minute * 10 -) - -type memNonceSource struct { - mu sync.Mutex - nonces map[string]time.Time -} - -func newNonceSource(ctx context.Context) *memNonceSource { - s := &memNonceSource{nonces: make(map[string]time.Time)} - go s.garbageCollect(ctx) - return s -} - -func (s *memNonceSource) Nonce() (string, error) { - buff := make([]byte, 32) - if _, err := io.ReadFull(rand.Reader, buff); err != nil { - return "", err - } - nonce := base64.RawURLEncoding.EncodeToString(buff) - - s.mu.Lock() - defer s.mu.Unlock() - s.nonces[nonce] = time.Now().Add(expiresIn) - - return nonce, nil -} - -func (s *memNonceSource) ClaimNonce(nonce string) error { - s.mu.Lock() - defer s.mu.Unlock() - - if _, ok := s.nonces[nonce]; ok { - delete(s.nonces, nonce) - return nil - } - return errors.New("invalid nonce") -} - -func (s *memNonceSource) garbageCollect(ctx context.Context) { - for { - select { - case <-ctx.Done(): - case <-time.After(gcInterval): - s.mu.Lock() - now := time.Now() - - for nonce, exp := range s.nonces { - if now.After(exp) { - delete(s.nonces, nonce) - } - } - - s.mu.Unlock() - } - } -} diff --git a/vendor/github.com/ericchiang/oidc/testdata/ecdsa_521_1.pem b/vendor/github.com/ericchiang/oidc/testdata/ecdsa_521_1.pem deleted file mode 100644 index fa032198..00000000 --- a/vendor/github.com/ericchiang/oidc/testdata/ecdsa_521_1.pem +++ /dev/null @@ -1,7 +0,0 @@ ------BEGIN EC PRIVATE KEY----- -MIHcAgEBBEIA3zFNhDsB70vMBOzeK48Zn6oic5wfx9xto4ErduEVKFST0aJEfjLO -/kzNrKDgXArCEl2KYJQfb8J9lslA7cLvpVSgBwYFK4EEACOhgYkDgYYABABxyN4Y -6VxH/86lgejSlHGrjKVSzn6YeOukabBSiU8PS/o/wfGXKX4eKCkJYqVq18zGAfcL -q+UM09ZQv/De7mGkXwC67qQv7fS7tJ/t0uFcxriQNtVGPsL4/+YmWrFJBTlK0OgD -mkSBG7ERdb5x/JzNFbajSbX0wKzs4VOZU0VVj/2DJw== ------END EC PRIVATE KEY----- diff --git a/vendor/github.com/ericchiang/oidc/testdata/ecdsa_521_2.pem b/vendor/github.com/ericchiang/oidc/testdata/ecdsa_521_2.pem deleted file mode 100644 index 19c5e55f..00000000 --- a/vendor/github.com/ericchiang/oidc/testdata/ecdsa_521_2.pem +++ /dev/null @@ -1,7 +0,0 @@ ------BEGIN EC PRIVATE KEY----- -MIHcAgEBBEIAtlsqZlQW4bWtSmDgLjbhcCgmmWEVwtzHMTZRoQnUv4rdTxCeKLY3 -hjxzd5hPCmtfP68GyJhKQgKofKYD/DgQLc2gBwYFK4EEACOhgYkDgYYABAABrcbo -t8KEfgzslg4Bb7t0khCgFrT2hX5htSWnwwHiScs1yO9egRcftZg/WAoIo/QDID+i -OB4f5Flg5PygZpm/SwE4B1E8dGpyLRmBg3cC0/PfuRkGZ2E5POKZqsiRU5TkvC2D -AkwVr6UPpXPheStrp2qh6ptBUtZRzn8Q4lVFKoKe9g== ------END EC PRIVATE KEY----- diff --git a/vendor/github.com/ericchiang/oidc/testdata/ecdsa_521_3.pem b/vendor/github.com/ericchiang/oidc/testdata/ecdsa_521_3.pem deleted file mode 100644 index 0b09692c..00000000 --- a/vendor/github.com/ericchiang/oidc/testdata/ecdsa_521_3.pem +++ /dev/null @@ -1,7 +0,0 @@ ------BEGIN EC PRIVATE KEY----- -MIHcAgEBBEIAzUq62J5hiC8B5xw9M/e9KlSeO66uq9PHRSGcFY4d9MFgFKILKU0u -cfUBCwbhOnDWkdUTp1DkLWeNhE0UUvN2FgegBwYFK4EEACOhgYkDgYYABAHH7ZyR -YXJ9oDJ/KohiQFFXqkvspk3ljvBAFUFRyfL4Q40TtgKGt5YBNmU3SHNHn3fJdjQy -xe2OZcmKYxzwCjx6mgACI0IoiIdgZN0RBy8UMhgn810C/iDg+nOZScl7P0t3DFcv -H3K5+tkVWe8lLBIUOkqEyHmmfHGYcn6Kc5jHEnAebA== ------END EC PRIVATE KEY----- diff --git a/vendor/github.com/ericchiang/oidc/testdata/ecdsa_521_4.pem b/vendor/github.com/ericchiang/oidc/testdata/ecdsa_521_4.pem deleted file mode 100644 index 50a51f1f..00000000 --- a/vendor/github.com/ericchiang/oidc/testdata/ecdsa_521_4.pem +++ /dev/null @@ -1,7 +0,0 @@ ------BEGIN EC PRIVATE KEY----- -MIHcAgEBBEIBt+T9wRbTfN3T5kSqfT5nqCt65w+SGAQ5DXQgcf7gCXId+Ux/57MA -/Dld+PvG+T8mobr1/jaFiGOLLRsjtnc5Ml+gBwYFK4EEACOhgYkDgYYABAE/ka2T -p7MsBezSgeATljES2xBY4wDOcjMmI6MzHdiO9hU/xcIQnhc2tjML2QZSMTuLy1ZQ -Yjhu0ZRg5Dxj4m7mgAFp2f/FqtOSAR5vuikaYPzHwosvNFIIpRDJCZ23j6qbtemF -5qXUlSXf2+W491rfb2njNwTWx8BLn1M3fFhobK+O9Q== ------END EC PRIVATE KEY----- diff --git a/vendor/github.com/ericchiang/oidc/testdata/gen.sh b/vendor/github.com/ericchiang/oidc/testdata/gen.sh deleted file mode 100755 index e2385133..00000000 --- a/vendor/github.com/ericchiang/oidc/testdata/gen.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -set -e - -for i in $(seq 1 4); do - openssl ecparam -out ecdsa_521_${i}.pem -name secp521r1 -genkey -noout -done - -for i in $(seq 1 4); do - openssl genrsa -out rsa_2048_${i}.pem 2048 -done - -for i in $(seq 1 4); do - openssl genrsa -out rsa_4096_${i}.pem 4096 -done diff --git a/vendor/github.com/ericchiang/oidc/testdata/rsa_2048_1.pem b/vendor/github.com/ericchiang/oidc/testdata/rsa_2048_1.pem deleted file mode 100644 index 75ab64e8..00000000 --- a/vendor/github.com/ericchiang/oidc/testdata/rsa_2048_1.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEA05kMh0SJfLTbcAhpq1w8jpTuo+Shy95WgvYc1KzxcH6ol/nq -vFSn1VCT/OHyg1t8BGeIfeuckIppT+cRtTjrsqS6FEmJA3lAiRqJZLWbewyXgoaZ -eCsmMS0s3w8KUon4Z9t8rFVqv2fkki6p7FtLPjPD0PsTRNCPwtOGM8Ci+0qFuCrt -4flUr6DpiALkqN1PSJCAwL22y8C86S6PPBU2seR3TWdD+iMAmr3Rezh+J/JqYXK+ -4qORzE6mA3hjn+goULQif5fUbYG0vRSyEp8UrlzevS88+ZzxyS9iTZ6H1ympLcsV -PeqvCIXPd1OJXn6ZGSuIgOlgZuaieKeuYLHDCQIDAQABAoIBAHrxC+R0H+YDNxRq -7uqPlufJBLbZGmDXeDBzSuEO8uFH1jEnFgoCrdk1Dib6KOvFddMhTJ7NDJS2tuWj -/hfrUJblOvCaoS8Rfjuq3XVUR1hBQq6mAfleKLyd4NphZL/8RgYh8tg2cOVxOc7t -qfEYQimL7hQ4LUPoYf7y46CiJpAV+BIwrR74k9Y3vcpumLwWrkwlfLTMWmcaiJE+ -gQBVl5+CQZmVKohTPDfCnQ9+ISzxvh4nesiQORMljG/ssQaZi5h7VEJDqGOaDgVB -CsFp9fxLrQzx1Gjxv/uEhG/k45uAU1qgNZcL3/XQhyCgadUsuMM3yOndIaiQNbNN -7bm1b9kCgYEA6XICJjg6vRbIO7nS5VYW1efFhLlqYjFimjv5f5vaTWA3FM8S1crL -HG4Q8yh+CBOwY0mtcwr8RjlnX5Dsi+wGJgFjaL7OG0MDojv9/YKZse4dIHELUPKC -Wj1zxiE23JsMAWKXvkhgGJgkC4mksZHVVszcLOEvmn2uZghDtTxhpw8CgYEA6Aqr -NipU2MPPOe0Hu2Ar6Kb/EaJyJA2G8lABWILwyI6Wv13dpZz1FfjcpnsZVRNBuYla -O3OKZun537kQOfHMWJfZyj2fOrJ0z3rsWZ+nbbUy1um8Z6jHTPqQyNoelE5/QTvs -CJ0jDzotbUYsvE4TdOH2EzSneecAgBN7Brz/tGcCgYEAuSpcOBKbzMZYVr+DX7NU -c6Dek/M6Rd6kNnBh620k0AEET7YcW4X6a3eGbEjvBtsPKwIS2VCaX91CeJQMfMPe -8KBjSH8oHomeRT3Orhm8bVzQr53a+v8QlCFwRnSr/nnhIOwiLqVby8ZJuPkZsFtb -W/ksn1CSoLkV7wqZIhVd49MCgYEArkFU0hh4H1DtDlMyu0Q9tTmz00pq7Sg7bz0l -xZKPwA1Up+GV0glNBHMfQOaw33LWqL69RGhAR4juXVRdGya6js16gKZGLY5Wqnll -hOigk4K/6yUcl7vn76c7k5o53KYWaqbVWqKm8Yh/FNDeR4takSwf38xq+ODBP21h -tm24mYECgYBUS4Ox2dzDBPIKVfxyuV+FviM9wYKBd9G2xUYavF5Id/0byYOkOy11 -K9L4NJI4Xztzw6KZw7ngUsBmK9AN60mLO1SkHyMr7dLanyt03X46jhtEQem+wDqf -HgKcqIz0gxaU6+widaEM33/cTi+kafH2uLr13aHU23ZfMV7oeRk3XQ== ------END RSA PRIVATE KEY----- diff --git a/vendor/github.com/ericchiang/oidc/testdata/rsa_2048_2.pem b/vendor/github.com/ericchiang/oidc/testdata/rsa_2048_2.pem deleted file mode 100644 index a694ee70..00000000 --- a/vendor/github.com/ericchiang/oidc/testdata/rsa_2048_2.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpQIBAAKCAQEAsB/GrXd+Ygz/Gq0nxKX06cLh4ECe+AfINVz+LdBCY1R8ossV -1v2Rk6M59EMZwsR3XuzCoioEGzI+QyEUUQO+FpsDMpZ3iL4+dTCZIo9XXfHyQEnq -JCy2w6Al2Xr6qTvfHBCMn61IGwAgDhDDb0djclJhqLhgxAVwXyxt3+pVSnUqdKtt -h0tgLqyEgqX19QjDEh0xhFT+zB/IHmolaJO1DelvDLYmyaxoOCizGb2WyMcjkZ6m -sdV/rYpzBW1BjA9N1+gnTsPF1cQ6wJJTtDRjovKWtqCQSmmPnQSVNwIz8+zI9FUc -qPM6gP6v56bcpmqPtPylY6T3GbGxBQtW+1Z2/QIDAQABAoIBAFNvikiNTlMXAxdZ -JnjTgfXn++en1Wd9EEyvdD6x5XF3CeB5QyxpTbjaX88mpqKNPlu63+3A59cWc0aL -+jrzAe9lmhsyCwi9z4rm7fTgYSxBPVlVatWeVSrRyHyB9RONKIH8GRJgHcOkyIrB -SESEVklHW7p5NmZGiVidDKRCOAugMpnYbOB2Nf7wI7cxHT8QcxcKFaQTdCSnYv0D -eMWpEmP8vsEmnme8Q6Uax470yBHQQvI7JfWUIbh3wZVdDLllbr+E17ej8EHatCyD -A6J1lLZ72DJy37G/n3kLLtHy0oWjVk9ZT2m+HEQHSEnAqgHz0UzGKGVoeSnbAxUw -FATBNYUCgYEA3KowvUgRyc1ydi2L6YqQQg8Mhq6SdSPkC91p3pI8pMJGwco0tI2y -95EFMsFUg3m+v2URDKxjt3itx+vqWxSqeQBgUzKk+0TRuKt0+8dav5BUA3njtLEk -VdlzwofbvagUnE+slg0lQKsK/IzODPCckcOjBcUyJI1PNYdDjfQjv7cCgYEAzFO1 -vtexZEUw1QGsrxlxrwBu7ui3NvpfC7rGqJIiUxm7cLrfDbOSHJxutCNOZH90zDZl -xiVBTc3tvpdXdj+zuUkf9Q4dKNTIq3+Hwm3iGXS+yw6rT2C2I3h+IaSCF1YpKakw -MSjOwlmnYIckeYGHe2kfEgNiL7qRD+SxJCjSVusCgYEA2/3Mc5h7K35oQ8tqtl1P -LpyEN22pU6GBhBasqpmOXg/VrPPjkbHHH6tzzEMT97OTaIrg8YqYK1zjm/HmBgHX -ZqTqY2eVNXBJyVseWLlKDrtcFs8ZJZaJDBGrp9/8QdtlGOURwdK/NfaQEHJsJlhn -L6ckSudq8yfyNQJyZf5k+YcCgYEArSPqCBNiICN5Y6YNnDqlWLO3TP8p8Y5rZ9cX -a9SY/W36pWXUiRm3IEN2k3KvhP10DW+zAhqjobh0U2KPHIaSVtmeGNui3eyhNqHU -em7+fq+s1QhTJeo/rQL3bq6mBfxe2Qyi56U6vvmVmXgq8kNOeMb1KyBu3R7suVkC -ui9VPY0CgYEArXpn5sZz8ERHXjeRHTVxaTk2djvgYtzVKjEGR1LzX6Bi7drbw/lF -M1Fjqog/k0tsDM7pC30EoxpRR10hSFdC7dQ+STTeTr1gmB6xJmA8boe1jpAfhFXm -sjjrsFsw3mUpsJD3Ck482T3BA0iZP3NvC/+ge0IkRUC1/j8KP0zQKZI= ------END RSA PRIVATE KEY----- diff --git a/vendor/github.com/ericchiang/oidc/testdata/rsa_2048_3.pem b/vendor/github.com/ericchiang/oidc/testdata/rsa_2048_3.pem deleted file mode 100644 index 92dfe581..00000000 --- a/vendor/github.com/ericchiang/oidc/testdata/rsa_2048_3.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAv9Dpk+nfW9lOSHdt8UO5f/ELRA+JM4vX9GkbJy9/M/NTv7gg -HBNpxYA/RhIMnifeXFAomLYMov0Gxh4+85Dv4YT3lhxlUiwi2yaOIXlgy15R8kGR -OCFVKgfqfQw3WnWhjGyH3Rb4Znk4IGh2qj+w2lBlnbeKCxXq+SMB/Kc3TMmJg4Cx -P73vVmT3VifW9goOO4DDNAv0uTl/KgWnaVoPc8eRmYxBc9chTxjlmoLHsCy8z9+p -pn33eTyfcNF5DS310sMHLcvpP25fPMpkbzDDjnCzBouI0YkJJ1F2m1GPEqnsN0is -1F9TGJppcAOpb9aaPmtU2wlzMs5Rwm5vQ6/9wQIDAQABAoIBAQCPczl7+QeltRoq -b8a1DCUKXcZDHCtLdWYHzyMTZx4GSA917cl1tb8AiSzIxm7RSJevCfOSYXOJ4RjT -yYLivJ3pVnuis5HCpmda5badqhyNevhl6EsmYydBy7G92wj6icZLMk9ZNPiIClfD -RNyZ7g/g9QdJsB14tOeJcnjl7lgY/8RQOv52mWC2AfvCRGf6fHoopMP4ZlSTDP3q -2LMGNUW+cEwjZg+AIheJCoCOs51pvm/B2DXeb9T/GMzway3qFBKU/RYi+ceOm6UF -+BZwStxpXMRbnXrXv9wS3S6260+NSdgIVF7ErzYjinLhZL9Wc6khoRVOCg2ESewr -+2sJqoABAoGBAOST6/PgAfvODs0m3tzb8dDYMcCtD7p3QoC3HiG7mEu6vnb/GdZZ -6B1XHiK36xLL0/8tf2BtJIqmrDqOJ8pdiT1sbQUtv8lmbdr2PoV2P86v11F8ZBkq -szjpQ6gGaItT3dAjJxSGCiwdvMw4za5GJAUPTQZk+t1XasiaAQznMimBAoGBANbT -9tv+mqwvzK4/Pw/gyIJkIQrbgXTFdMjhZJkNVxCm2R7YnNDRLSxmng0nJCB2Tgkp -EouWNYi9rsWnmR+AOinSSoAb6znQwOZhQzQb2KzkfBnlZ4XFUxvW4OZmJDZILmyr -/8uHnEcT7xwT7L90j9cSxq/+WCbB7GGpFmmVpXRBAoGAd7l/ElsXzuOsXwpoGzjd -HS3QSYKcRWfoHnFLyBFxgOEMmFmgF+U5rfyOnVLGPy8iGHulR0WDqVgJyBXjg5yg -oNqk89x1ozESg2kNcGxymXkDB/xmlcQG4d1UgbLxmWDRQw7Wjmpy846T8Egke47j -mP7dsma7+6mpFe+Mc0y5uoECgYBp1D+u/oz5uA5v5G5Phx+fxG3WqG3stX0jnI1v -LHgwltEs9e7Cm9lSHzdLKXYNm9ozfw1IwHWc6DyZ2EeBkiyU/6h91cMaVzFADLgL -ipBCE8jjBPTrnFqlw0RFnBnIt+RO2qiHfkXJahOH1HTzmBtoCzLf7j9E0JF/Rsno -t7SrQQKBgDxAKKHMLT2zm0kz6eTSLSB7WDNxZ14+u32fZj2ln4JMERTk3byGojPX -zf9poWwCWJOLd7uVtLl2dQZFUZG9GJuZXO15qmKAaXMI5yVwH1ygzzZvDLOlH8Nn -19ZjFhRLreVeAFLMTyOapEH+5QZxaszMR8Xzna9BJnschht/kPM7 ------END RSA PRIVATE KEY----- diff --git a/vendor/github.com/ericchiang/oidc/testdata/rsa_2048_4.pem b/vendor/github.com/ericchiang/oidc/testdata/rsa_2048_4.pem deleted file mode 100644 index a55ac961..00000000 --- a/vendor/github.com/ericchiang/oidc/testdata/rsa_2048_4.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEogIBAAKCAQEArmoiX5G36MKPiVGS1sicruEaGRrbhPbIKOf97aGGQRjXVngo -Knwd2L4T9CRyABgQm3tLHHcT5crODoy46wX2g9onTZWViWWuhJ5wxXNmUbCAPWHb -j9SunW53WuLYZ/IJLNZt5XYCAFPjAakWp8uMuuDwWo5EyFaw85X3FSMhVmmaYDd0 -cn+1H4+NS/52wX7tWmyvGUNJ8lzjFAnnOtBJByvkyIC7HDphkLQV4j//sMNY1mPX -HbsYgFv2J/LIJtkjdYO2UoDhZG3Gvj16fMy2JE2owA8IX4/s+XAmA2PiTfd0J5b4 -drAKEcdDl83G6L3depEkTkfvp0ZLsh9xupAvIwIDAQABAoIBABKGgWonPyKA7+AF -AxS/MC0/CZebC6/+ylnV8lm4K1tkuRKdJp8EmeL4pYPsDxPFepYZLWwzlbB1rxdK -iSWld36fwEb0WXLDkxrQ/Wdrj3Wjyqs6ZqjLTVS5dAH6UEQSKDlT+U5DD4lbX6RA -goCGFUeQNtdXfyTMWHU2+4yKM7NKzUpczFky+0d10Mg0ANj3/4IILdr3hqkmMSI9 -1TB9ksWBXJxt3nGxAjzSFihQFUlc231cey/HhYbvAX5fN0xhLxOk88adDcdXE7br -3Ser1q6XaaFQSMj4oi1+h3RAT9MUjJ6johEqjw0PbEZtOqXvA1x5vfFdei6SqgKn -Am3BspkCgYEA2lIiKEkT/Je6ZH4Omhv9atbGoBdETAstL3FnNQjkyVau9f6bxQkl -4/sz985JpaiasORQBiTGY8JDT/hXjROkut91agi2Vafhr29L/mto7KZglfDsT4b2 -9z/EZH8wHw7eYhvdoBbMbqNDSI8RrGa4mpLpuN+E0wsFTzSZEL+QMQUCgYEAzIQh -xnreQvDAhNradMqLmxRpayn1ORaPReD4/off+mi7hZRLKtP0iNgEVEWHJ6HEqqi1 -r38XAc8ap/lfOVMar2MLyCFOhYspdHZ+TGLZfr8gg/Fzeq9IRGKYadmIKVwjMeyH -REPqg1tyrvMOE0HI5oqkko8JTDJ0OyVC0Vc6+AcCgYAqCzkywugLc/jcU35iZVOH -WLdFq1Vmw5w/D7rNdtoAgCYPj6nV5y4Z2o2mgl6ifXbU7BMRK9Hc8lNeOjg6HfdS -WahV9DmRA1SuIWPkKjE5qczd81i+9AHpmakrpWbSBF4FTNKAewOBpwVVGuBPcDTK -59IE3V7J+cxa9YkotYuCNQKBgCwGla7AbHBEm2z+H+DcaUktD7R+B8gOTzFfyLoi -Tdj+CsAquDO0BQQgXG43uWySql+CifoJhc5h4v8d853HggsXa0XdxaWB256yk2Wm -MePTCRDePVm/ufLetqiyp1kf+IOaw1Oyux0j5oA62mDS3Iikd+EE4Z+BjPvefY/L -E2qpAoGAZo5Wwwk7q8b1n9n/ACh4LpE+QgbFdlJxlfFLJCKstl37atzS8UewOSZj -FDWV28nTP9sqbtsmU8Tem2jzMvZ7C/Q0AuDoKELFUpux8shm8wfIhyaPnXUGZoAZ -Np4vUwMSYV5mopESLWOg3loBxKyLGFtgGKVCjGiQvy6zISQ4fQo= ------END RSA PRIVATE KEY----- diff --git a/vendor/github.com/ericchiang/oidc/testdata/rsa_4096_1.pem b/vendor/github.com/ericchiang/oidc/testdata/rsa_4096_1.pem deleted file mode 100644 index 15cff3cb..00000000 --- a/vendor/github.com/ericchiang/oidc/testdata/rsa_4096_1.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKAIBAAKCAgEAsj0DJZqC86gp5P0B/KXk4CoWLa06uEpMu4K+0QFNR6XwbB7A -FFXtcjemUnuqnwqHqouG7pcc/KJAiakWPYPbAtwb0JQo3lRPRf6EewjHmspk9vqd -YBqump9VZedCs7acHCT/xoHhsMI4PyTX5VzhTJoZSMSm1aTX91ZTG2R/KkFhchPd -b4kMjc2LK9O1CN8vQoX/IolCOlUOVcVrF20jEiOIFpc+5t4V+jRvx4A52dG0DqBm -Isx3+OioRHD1qRKbi8L851a4UAeX16rW0xsuyCy5htfh3Kbts6AFCtzmvR12QYBl -+aL1Ksj4lraUmQOkwuj6p+w6x2AU3z5Z48qOmUdcmBnSJE9k37wQEqT4Y09O4+Zb -UcaAdcexT1YLEKaL6ghGw9pE7gPTmohk+gTYpiPnQ4H/M7HamN8dHN/Ch1hvaI0/ -ZdxSEU2jWlkEeg7c5n7Bmdbc+yMAXeWvS/BeH4yGNh6TtwvTYpq7aDcP4cYw7L7j -ODIWEmCVgOzdDxn5REHmrOx3qSjimCA/Dia2PHSqkO/pJ0Lu/YNHWdEhxJXYtX8x -Ldk7i3syJl+s3Qbi1GrNzPRr+enyAWVk5wM4XVwiNlRvvHh8c+gPs7a5qTS2FulW -psAUB+pCC9Znv8ixgVR9ZMxLALZRIzgZ3s16OZP7pl9t8u87LnbyeihY03sCAwEA -AQKCAgEAm4gqCtI9mykPBcbRyQlqI0IWgF09dDtBog6BPBiKuw7OMUrUCerBfH2b -ITbQuF+T6vo+EEzE+p8K+hUWVy+MGX7Ats3Sq8+eLVHfgQ00QJqEaBBg68/ctQh8 -mKOozPF4YAbZOvtzWa7hLhiUXI0j/JgroBgaDSv/WNF3S9vyK4lJ4yX6gK1yyvql -iuT+gHNg5gfPju9/Xy+Bhs7ymEqf4+AljLEGLqd1PhQrxkbaNHyNRoYpGgyaVBWR -X8fCVnrqSJcp4SUHSK6XjZaCR0zdEcgVTNltOgJgQfJM9CG3JydiXd4RHjlY/rDI -W5uPJ8bKK1rp/0ZgNEJfdD8QaXoD28AzoSiL2U8w2wipuq0zAExX9H7ZhgdCMv8n -1yZ3UWhva1LKzjP/fs9ER4CGK7S5U4tmzF8rYSw0A/RuP5bBhGRAJoCs9ZHmM0TU -Z9JDAPLy1/P2ICRuHH6mIVOZkHp0IedSJIPqSJ8LywpPNCyh2lIxksnyL0WnI0ai -8sIu+1t8NGOv/41yUKwJ9LUG+oaWmnUNqduECvYmuX0ByoU/E9YY3RsadiCd5P8l -xviITyRQG+M5BqOTzd7Xa7K9VBy2V7Vnk/gf6bX8fbv/2TYVD4nnjxNd/H6Uzlj5 -i1R7/gip4rEA6n/ZbioM8TATaJLG2ZAlnxFAGDw10S3cj1W8gMECggEBANxNwBSx -zbm2vFWCyYY73D+el5cgvSpoXhhu6WotN1aQMBMg4DVQxUSD92erSWvYlb1HOBJL -wjNLMz5NOanRy42mEXso+oN0PMpykGOb1WmoMx186xj7o7p9NK9AFF7ll2DQMYHd -9Y/F8ggmj+4orvMMBL//pYDmdguZ2O+KAr/VZAVj6YTnO/tdmk6n4EhAvB5Hy5wZ -kqi+YD3W01l6vmtWUQBKquaO5XktsAJtv5NapOmy1Fu08gEYwAgADI5Y8CLyMtzX -NmCSoTZ+GNYPIbW4DocSqyH+N6YpyqLDifb3wE7uvyFwYK6y68TNKhO1F7JLQQNM -7pM7F1YkUagzE5kCggEBAM8eYUkXqdno5TTubYcH0KOt7kX2ZB4ABFcYnQsMLAC1 -29yElr562jMqgYhpIV+pPMSxKec91g42p6Uiqo1VeciCkFkIcVUyZO1yBFsHnWcS -z1GFDb0ePDAddLIrOmpKQveRpubPThfioqnrrVjm4YObWVwaO/BiAvFF9ZIPgB7N -VmXINKJ75Zof6NsCq4Z27dnnXlh1N2kRDEVV2P/x1HRCq1BB84IePGmSjcHBXRqU -PD0F8PdIYlcW1xDlQuj/x9iWso1MWBWzeuGROTp0LYGA0A6NChU5ldTK3T3+evNo -KO8xzrjbUH6I6XvWitWG1hvHf31UhoiYOuxjF/++zDMCggEAVgSRrELkdc/w516C -u0PiMoEE5YBl/An2O4oK32c6RTVVYBKlGIwqCh+Q2UybBV3y0Y3eSd6EvCxvnLLg -gfslhHBEQRd2AR/AoLdsw0fUY0XGd4wP65hNjIJYsNjPW2I/4hBIVFHLENEUOLR9 -3FrMPKADtsfl4leZ3du7RYRYoHh8blJdmoQC+pnIp0+LFgsYqKYVzSR7DCIRR/P6 -X+S6NwTj6b49znobBV6ea8RYWfu5inpFymzzVRRJ3pXOUUJOuQZib7IkTD7UbYd8 -wQ/1dJOiMIFMiqBNMDb/JOA+nUyNLQSxYigTyAKaZiRJeppp3zbc8qH2QUyARyU1 -MPyIeQKCAQA5WO4S8Oxkm6mrKEFHXBCW4XfSA1DhRZvuCbCh+HLOl4wS2NtsTlPQ -SvqmrIVDGXbr9ynlDygPs25juN+EVqBrtksFe+L1dgif/ivakJcyjPC+X5rYPGDp -6Z4AHxwDhiBYsAmIaunyjxv+9HSA4xyZ9g+eAt2Jx3mNGJPQJ16QKMa9U9vPCYMf -U6qDyY94ocFlzjw/PeVjwAanxAdbhrgOoM8SX9BuvLR5fsylU0bWLykmtFht/6rK -9lYCJZiLLxdEjyVNHlBdYd6qSi2QU86txt7UyJR8H/+udaUgny+n6bU71YypfoAh -KQOM+HBkgvsRogFY0GiXtZ7LCP0CIPAlAoIBAG49Ot1AghC9OwLXjcneGRbsML4w -vkQT9vBkStonR56RkCnN8xGDvLwQQXOJ0Fodo8C3Tup5AHrTNi+V69YVQYCtuM4+ -heWNZxpYDxzZ1IU5a7ITlH5TJFP5paaaH+tIqRZRp+p2+j5y/CyAe5hoYY4K1Xog -Zuz2piLO5IWKxtznwlQGjlsZ1n6p+WS89NFHuIF2r9NrovPjkEDDqnEcLRTDUnLT -8FcpdlNkarS/X01ZTBFja5EfW49UWW8sQNRLzVfnP+MECvGFy+oA+4vGprSBuxmt -WBY7y+z5VD3o+JVHLqkdETwIP9YKLMVhqUb5DQb2EYwggU+1HgZNqTdbfDk= ------END RSA PRIVATE KEY----- diff --git a/vendor/github.com/ericchiang/oidc/testdata/rsa_4096_2.pem b/vendor/github.com/ericchiang/oidc/testdata/rsa_4096_2.pem deleted file mode 100644 index f331902a..00000000 --- a/vendor/github.com/ericchiang/oidc/testdata/rsa_4096_2.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKgIBAAKCAgEAobIQNPQNtGZlPSpbtZC/c/gb5OxoJfoq07h6X8kyeR3xq9qv -js8u8YQMt/AtgYw1tSdgNeaYzsvMgaSVgexf8ysqys5obWjcB/sjRR6UJY4hgSm5 -oLoGy8fi8lbSEtpthHStA7UVUGU95Ypga8r++Kkizk84wDq+0jZAGaEPvhO7RJne -nbEKhhZVIFb13LlkaX1eNOAxAcTszFgX/DaZWFcfzKop6gS7agU5NLCtyAjKdSRz -CXYMVyVnXhZaxZtLawk9Ld8qIqwmytiPwnlRugoFhTrCPIPfAb1UaESKj4EuupUi -brlByun/ENiWvbzTZyGLSuYA6rruyfUll4oDT/SwsDMJ0jZiu5C1bTgA+gY6kB+I -A3EvcP2Eh1uiUvwy5adAr29wlP7/8YwWvyEAAaO9SKbh36xcbsJH9OIQbFati99L -kn7jfyktUGKxgmwYilF2ehtB1tPOMKRor8HJGoMEcrjiu4Qvc3ygaZtomfY4zxL1 -uNeZ8c51pm5lmo30kJ5q5aWX6iS4Sp+ncFiPjxj7bV7nXOGqwmxK23tk4qdUOK4s -z26uc+CChP6VdOwLU1uu2FdRvBCPSnvBFIk5qaFZkhbk/gA+iS8R82TUf1DWfvZ4 -why6XJDPQRnscySiIckN3X30TD68dpJ5VtzpRzCvNN9GxoRFY6apcJXoV0sCAwEA -AQKCAgAKR01scE8mtpOc7cJiqk7hSlZLmRONxndOeh2dVSbWOCcSq5YZV+Y+CAze -7G+YGpeXamddRclU6/OWEiZG2gXHaWkQ90oAGnhSMY6uaCE2ufA7S7G3G9wuvAgb -K5WzCRuJHfmZkLtIHwduPfufHopSuD20K6kJ3zIeHsC4YFql1I9E7xsNnyFyIJ1M -rvp2C3rskcGZTt8Oo7wByV/M8pOQ4Ajvc6mybJaVSLu4M7r4SkbEZ4rAgTaLm58U -hgtDIHoM1cuDzPnatmLI5jdNP3UIhHaRX4jVW/SjIavp7OF5+dZEmhJUQ4aBJZrH -MV1ztjsiBSnbmv9X7IYdZG39UhKfuqph20l3NQQ9lGhJH68/V5YEt/301O7Jz2jo -hPP56TvvP8sNJZqkiD3JQ9Do4V4s0pUR6RQiim34gdivhRNntPjL1hfBZVoIIxOY -Ek//7OmsfrPwHEatZP6UT+IJTVZRbGp6a5Qu0YmQUh1/MVK+h2PU+FVpkgYALm1u -6e2dXqRK1NI0v0icFMUTBZAw8h7mob7IlkoA/uLWjgkVZf48/2wCiCtxjDiU31Hu -QGV3AZFccjKPwjc+lcWjjH3+nM/q4Z9b2r9x5XaPOlq4mXF4QnheSgQ7URhiEG2I -RI1pmV20HCU/qmAiiGGBWOBYlOSBuRbun5/eSOY8c3FXIMOckQKCAQEAy7F3eqYR -0tXCJRzR9EQld13QVDeP4PpADbMVzuGXbG6+DDjXgRQiDh0gnS2hSKATd61ZRg+m -RHVEPAf9lfEyDutr6qZRZJs14auI0Z4eBfdq+pL23e5tcmj5GuqBZ6qR5ZYHblz+ -OT0pEa0VfCW63e0XU/1iG0y6PGbSQiHmqas6E3s7ccDjZZE7lRCBn4+RouBBxtFo -leugrTK+ahObFeSYSQqjG5vGP13gatui5hib8cGG6QUV6GQvezvLvo/rBmCNFrjm -wtQ4vfICifadKYYxyTNNB/EKDrXB1E0NyDM8pmXcfFlajQU0fucvSCNmXIDF8nnL -TCB6mGQBX5VUPwKCAQEAyze5K7YQXe5lp7PLz9IHYyY5dtc4wzwHI4PGe2TAKEtQ -jEOjSi4bgUYGyalJ8vqmOrGkCBW4NCF8Ywb6wNHgK/O6BGbaSl8N2cYqA9wSnVYB -c57ieUCg1A/3mdEwO+jHng1G6Me2Tf7VzkO/XPXyA2Fy6XhIg4xf+mxlW010dbKS -kn4CWljY8XhuX1DZMjA+UAsMzaI6DFcxbIcgcVMP6lp9mnk0Fnm5T5x/EpNjuAHV -D0m9x9TlQ86akFdf+FljEsj/drDIZ8mkU4JTKiH88wrwfKHrOqhv1NX7hLCXs7wb -tkxVF71M2qOcYl5TAvJz9uv2U4O9G5l1jL9wT9eJ9QKCAQEAniUAwGajO+/eNfY0 -Q9OMyyo5Dsm8mU1x4bEC44ZejD9GqjKPjpXVAuQ2aBH/QGWX97jMsQqBanEpMvp5 -Nar31IGPXbUXSGcA5F7LcQO0B6nakwT7Sb9NliBOF0mugo/5iih7SIJGlqYXdrPN -FIAunxLuo7T8MHnXtgGWiOXNMjnQc0OgGWdKpZamjcss+Hb8+Vnnd7cp3gv8ybu1 -/qGOLOc4HK13iX3d42C9VfmEdeTxXjeEyPG72pu+CY2ZWDBgpqjboaKY9vbRvxdg -RUEFMDISAUYlLl9EEbun626Pnrm5Au/eyWSOWyKJaWWQXg+t72/DP8izwD0PMbWj -I1TK/QKCAQEAiYSK5R6OUtIprmPILzlE0H6kclxQSCXN+uWIoiXatynINzLqRB+R -c1is7TiHF0swxBVEGEiCX5ytbOHjPCqKVZPYNHRZkexjFhS4h+YcHqZ90v0Y6s6m -RvsLJebeihwLQVRgwNOs9XjWvH8x9zlj7Y+7UGyaPZL3vCIwMKnofmE6OLHW68am -ADnsDspKQGFPOaFQp7L5LzKt+nAyrx1zbraPusH8Up1Knqobf7mHyJRM1syjBaB3 -CPy9saG/CvOKTMMBxRL6eumELxLJLoDTiLDFbsGvygEDtHadfvx1nCZWZnWfO7JZ -WLdQ82w7JoplmRmylm9WwF+HoZhG63DDJQKCAQEAjkYiyHgh/drr0tQLlpELy3+8 -3CfKgijIs4Q+UGBsygEkPUzS/oFYl9oJ/yFSSOCsyGTYF9ojXYbdaLyHFS1YcpWN -pcPcZRmd68RdVk2gOMAHBAvT2YN56OQJiDwghMA0bIoY2BI7+h5mFLAgudSZXHY8 -RYt9IjEaFai9cvR88p7p9bQJNuIXWXia9PcdSldHJ01S+GhWHNbd6J0BfPhfAO+R -VJl98+U6jLyXyY1Ma1v9AfI6dC43SUKG4Z8b2B0ynxMANiOWXCj823owTk542yFi -ylWbhUGqQ4uMn8ojPArNY+Ndpmlgc8MpgXjaEzMil7BfnhlTHz5sQkMM4ByhaA== ------END RSA PRIVATE KEY----- diff --git a/vendor/github.com/ericchiang/oidc/testdata/rsa_4096_3.pem b/vendor/github.com/ericchiang/oidc/testdata/rsa_4096_3.pem deleted file mode 100644 index d899b970..00000000 --- a/vendor/github.com/ericchiang/oidc/testdata/rsa_4096_3.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKAIBAAKCAgEAtZndOamd6RPyspdd28050is48R/ozNBdi5xa2023cQ/lWl7j -TFTvYyrp/pJ0GvIIUwG3BKQyQBTKcDhLrQzrTO8fxqKfNSQ9/9J2AVChFulVcaJR -dAJ+k+jjvR+1LMjOxpxjv+RJyTSB4Z6W9xOyqfwAyNsc3nZ2jukHlOCvkpwUJQW2 -AMQFLlZoIZ4FyZ5qkSPP8kw6E6bA76/qSY8/bVMleX7HySIAfA7car3lZuxGMkP1 -aV7Mak4/5kHZwB75SjuiQVwj3urMA1vEoCuwXgdisMS4lmB8iLyW/XJSWHmLrEnQ -JWbRpie7MYTz2tOJ5GlQpD0U93eV51sn3nQ7If+08IpfZwnsqf+Eew3+cb1scbrI -rwoS2JI9MW97I4iUiLnXl8ZDYy2pIIt3bs5gFIdNVEr29uDnmtvmZfFaj/RuPLO+ -4YxNILYo42j8grJyuNikhvCMOWvB/6QMWGUc0BEz4QiZJLzqGWpTP0Nli3AedAxT -6CIbOhfl8RKyZUl4gDD9ZIsXUSZ0dL5CnIYU8yhq9yw45wh8gwPKTo/fiDog44WK -cFiQOlTNg1G6x0fUcbfrsfGwNfc9skXwDz+hcdD7dePZfN7jKeQxtX5dj5DIJAhX -9wanwaoqEj+G5KyXbsn4tuYu2SipSad3uA53UVAHzLapDkD9iiaJ+HrNn3ECAwEA -AQKCAgAW+spgspL13H1YlgjdeIG5k5iYAoat7Cv6L6XbnGD7IJzQK7OthA3qyZJk -kVm50yi0gEINh02IiFj5jFYfJsRbruKhexCUY+qohZRDJFXOFWang3e1K1+jDdRL -qUh+y0ZHIaEJtjSUDl3lE/FcgJSaJ/ZddESZ7fmgqeI4t5nf/noaGTfnruZM78gr -gNiQo8guZ463xWeP9wjxC5ylBEhtaBkU37MeQ3w2NpcztqXhuUJEuA7E76cESLST -SX/pbMH038jvZl5vpdx9DE68Ser+awbVAX+uH7WChALDPYUoBvFistBw+yrKULrC -UGWfKieHzL/UmJofmnVQmltYLfMRarjnGrit2uinJb+xxjt4yKMA6ZCHr6jF7ZkK -F+vKEbLMGZGZcOQvrhMwTkI6asoj3AoDl8pv1AtFlYJi43hlF8fqGHyaEE8KY90M -z5SC0wxhUfUGV4FVHRdq/UvCGQf/bJhRfGftnC1BNc9NkGQHZAbjeu4YSkIjFPEO -k2LwKt8XVFX2bhFNMApP9ktTFlUtOV1Ljjn7+R2L7P/hjRmajnMcSgDZYbQ5Ndw9 -fsZ0gTr1NZERgud4CPLgtc6uwp0rYiIwWn2VoMGNM9WUVDhFmOlznKnPqrkw3zH8 -uor7/+AI1AGltarvTcth2kriS34AUUoaGysZwIBcYgJSjNPvMQKCAQEA3UVUzjyn -J8UCl+KS5vkXJjIp3mvpT4ZlomSJwuWFChXRHiy033rb2Pz6gCKj8Vh/xHfRSn20 -ZlR/hodqhgyZAEkqvvzcBXm6coEwFywZbhN+cJPpqFkrehLBOC+ukOejUo+0Fdm6 -pF42hGZngq5GbAi5A60FJO1xOIjPj0chBOk5SUTPz7C8TZnE5ae3iSHEcqB2f7qY -OLLrAAs0YQmOOAB17S6UYcRXTAik6yzONboYFUiyTSid6fuMr9akWP7BGOMNTgdB -RbZB4PYcvMDqJdCOwJV1eqhNz87i3GPAq/D+K+YItxBi4flh8ijrPjpZ4hH22aeL -tmfxZVK5TilW9QKCAQEA0hqZbmnLCj0TV71VZsodb2G+6BRAnS15/ppW1BHuNvmF -noRMnTPAVqbzZ5VZshK0dVsQa66lB6z0uPzOG3euxozrK/Y0a4i0bbdsTO1C+MQ4 -ssIasCWTiKaBWuQ+v9yglEvi9nhaQbMUGhwH029xd1e2t7m10O4HYXF1N640j6gu -sLvMc0xNvom/Sv+MP8YoXX0NwwLqqxFVQhPqFsim0EhpfGrxRxmWeby1UB0yxn1N -y87UrF8Ap8MxBImz9avL/5I8LI729xMZywa+RNG3YM9AYsnP2JbZPz3kc2oP0XsL -83lUd8QD+Z2sFB2rL15XnBD1GETOxSvHqjJCcSBBDQKCAQBDp10kqbraGAyQ7//G -i0aesRvIG+p8HDWbD25nntGsobsMpNKwudnaYI8e+nhx5IM8SP4+7mxoFVHgiirx -zYxCYBynxJxpOCzfscxIaX1lAKTaOv9oL8txSaa2TS3stEZlifaf77B3bS7yEHV5 -qVty0L/w9cfq4IaLqJj9z9uyqrSPSHDZqcoJWAixxzQAw8hS2+kfaKf+PgZIPyTG -vqszSEDGQkWwFt4yKzpxhYOPPdT7PPz3RoHx9q2vXctmQo4708BPqTw12mIOLHHg -7IMrCLd8/rWqySbxcOpARGe2qrqsJWtovaPeP+fIqOY0Ypb03lVBe07meKWAO2jZ -Ex65AoIBAFDfIDPZ0OeN/sYFALxiC9Z1n1Ahi4V0ncKckdNrW3AZt47+iabw5pX0 -CTjTygS7Im8RsE5imO9NaZ1S4dq8xK90SolPaXoC0sBwm+U4ZlDu5owYHsGylQlC -XgQoWubq+3xZgXExfjxPu+sY4wJFoT04rAIoH43eMUUWsPHPwjeRmvc4MkgnFL3E -s7cgilF56suheQyZMM7MCy82DyLZ9Suy07eqSlj9xmfxdTDzLDouvSU35bC7mLr6 -bQG8J2Lmz8z98t+L4A/WcFUvsUk4GAfRfo0H9VL/LXwkTK0IJDKT1FPRXewDrSwF -vti3Ws8O11YhSNYglh5a7a3bTqvQqHkCggEBAM/oLCz33rLaNQ7Ogcqd3i08vHrT -CYL+Yxb1ZEIegGgfpIMlYYByhneCcqhXTp8jleJJjRDoaJLfG2rzm4yQ/xIXh2nA -c31BSicJffk+DV1N7BoZG/OC7CSo0GTlJZ8sAKhAfUApYjVHuDp1GZdHBpLmeYs/ -zSilstxPBgaKUZ92QNt32gjT/nrq/xYmPDR8AEaurS7cZtkAIsmWEJajmqP9PBNZ -eeCpKaa8m8cSjnSDbTXrA3l7ga2gdEcy++fh+5+VPmpPfnegzImJidC3eBfbDqjn -/zmUYntVTJ6FepsULoaXz1mQGNzgqCx8y3s0TZ4KmwakiRxwS1OUOWSRvtg= ------END RSA PRIVATE KEY----- diff --git a/vendor/github.com/ericchiang/oidc/testdata/rsa_4096_4.pem b/vendor/github.com/ericchiang/oidc/testdata/rsa_4096_4.pem deleted file mode 100644 index 2fe15ba8..00000000 --- a/vendor/github.com/ericchiang/oidc/testdata/rsa_4096_4.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKgIBAAKCAgEAoDHNjtZEnbu8o4nWZR8gV4ugYlysC1QP2mxVN66RSXbeuxHi -aYHjMTYxMSaMLBuj240kP5brwmZ/Tpwy6xpDKmfNFebd1HmE38KuWn9fnZQdgylo -w9glcsTLxNOsI/wSV4htF/nRzsvAx95nM7HHbdbHNtPwRhExoNJQ6AOuCVtIAe9B -yAvzdV5voOhWJIahblPbVwU5NX4elm+XGsvlDP66lKcss9lBTjDnWAP4ZcMABBvb -h5Kc5T9aRBz1WAmUP0qs6+b/F/XYPIKzrOfRpWt+I/l60h1a/RkmlMR3xujLBh96 -r6ATrYdIHDrg33snCkMUyEe6WfPbD02UTZCIttrfZqWy3ddpmV2FKRiGqPvSHIQa -GBHYZtjV33bdSfmZE25c3LHLCCCP6xJfzK4JMGPEhsDhk3XPWeY3qpKiNAcKSsjg -B8ibgpwCtP8Hb+h1GG3ZLfoMLYmoladL4crnff/RXhab7vrM67Ggz4uyqB+lXbDx -NK3UnmkY6xkX86REMSvJw/tCWBpFxn/2X8UlhgDd7fjgxD7HecowWlXSk+DOdKeD -pDsEGfi2J6/kfRn02QUKnJO6x7gOB5PQPoOjWwQkCaFx4zRngHFXYGuBIv9qNk8W -EtkuSnCbuLnjZrUmSCjgMsxQQqaXo7Cy/UKH97PSaNsD7hXaUmZLd2+kdpcCAwEA -AQKCAgEAi1Ckpxsa015owIToKks2kkxAsCpOCRATNW7PcbxkZ9J0A5abJAysq6io -gUk30Eg9aXvG0XKMGCWRg6j9806EqQVa6zg7JUSFVR/3B4cMfXtJaz8A+IkqkDQr -zkITy7u1q+Bel+JQH5s9TdTSRbfPa2vFFp6csCLV2Tnu2MgSe9qhteUAfVw/X4xA -YlyMRfm7vLo63+QQC8BiE4x6ifhWe8WwOAVnMAW58KlBGF9jkARVKD2d3rqXrhs5 -glD44ZZ7Ecv8tK/Qm2LXqlA0uCNnRIhGTDz0HnUfI0vTLL/sNtVPc0S/Kqt5UYl8 -IejmlhSBMECEe2U94Grd0OI0HnybF6KtZ1/YDxDS0AQyO6z4CLs8hUKbaLm6V/i4 -+9fkWvn4DcS7stTeUe4+I9ua/uaDrrvTNBRH0gaLsl3m3ptPiqN8yigApsCyGrgm -hnxvE1DQCvRw+yeNjgEgtHEJe9z6pcX/0jrw0zH9hIMFM7nSXWONXGQ5AvWuaD1b -cF3JhAP8IYXPqwNDj6dr61vszMqh7iJgQklJm0YbiUiaf00vGpcVKdHYzInIXdoD -rGgPtpoDWx2rAOAdw56IltWV35lSz+zYaPGqQglc+pcMfTqE+7FoFFJmOz2cXxIl -jZehd7dXmBx/yYYedcamLH1A4vmsazJJIxXSRtt2pQ52hdvkU7kCggEBANHr3Ldc -ZYH7KYcowfjfmreAJaRP9SVo+1FtqiwU+I9pzYtT4aXqXLzlJUbHwkWyf3ameghb -EWRKuutC39zz71UsongjVpIPCFZjXXPlo5qxMLAZ0I5KBq4xYEoAplNSJye02HvT -F9KU/J84YwZuJFhWcFXPp5JiFxpW8t1t8iEOuFdvtNkZ1eOByFCC83a/l4sjKCaZ -xemvQCAX9rmZptqAsWAgUmnhJaTcpjv5jfbwqmJDfGgpPxBap8HlpRlF8V0tG/jZ -O0EYoPiHloOD/QoCWaqttQGxxxJW+FfDFo4rlo9xst//6nIVXPU59GbHYBXhio9f -s2UJXOC8sSh9lUsCggEBAMNbouqyltGiX8w3vicEAJDzWubrVkVsRdNyKLh7ARuM -uO1faQ/HuCyoagFx0tt+AnlOQRPU4YprmBZj1f8PSHoCtwfm3FXo7mpJVDu8T5zD -Ja7YOAjmLA42m33ZeAxMdZPTvRYSAP4uxMozrg/tl4md8/lYvStAcoMy5jYvUXF1 -E1iWum5EJV4gqkBI5c18QYIzd/hbluxOUhI/fA9AQLK+I56fGMw0hdT48M5Iu/OU -nBVF8tuw7CXNUhxbfIP4Q8ahBcTu3FDBl/qIqsd7Zv7ckaS5Xr7J57189x8zZqNf -k/oHZ0Tdwl5cr29B28YC1vtJ7TLotcDZXXPvOeY/sGUCggEABSkCHPPFfwN4it0C -n6aHfBlHU5mvkgLZoq/Kbhj53zSfm9wtANIZA3+ygeHpMaNopLcE6u2qKMf5fkz/ -icPpTzOwrrlXqHF8J/t7UZ0Ef4n5g2qvCMBjF6cZEdigPg4X7k7wv2J6BHArIZLW -RFMyy4Ucb8+R8/Q7UyduAulv+UYOW//f9zI+YsBO90Owzmt5Qy9TDlfbWJo5PlC4 -fOl9A4QEWDOTMw0Yysutvm2tArP5zD6ScVEKPtGrrAWEIHHqs/qm5GAap8f+NP3I -QmVdNADIyXxJpcgD97xxkF64UDhcFBycZAs7bSB/T3vkOR6PixonOM0GcOZhBRk+ -VZt4rwKCAQEAoal+SwvYpMfi0KM8VxsHwOuxSLBs5uwvaEfrDKa1hu/PxJcU4Psc -HNCNUH65x+sh7vJkBh4/OgXJiJW7a+NgzZ7bic1wfiNQ0GG4M+qkUwxmbab9z9dx -k5161Q0WO8816UvqCI6DhdR8Avv7SbEKmtY8JBZcDKO7X3jKawKDOglxJfktc7wu -1BLh8GqiyIXPzAf9emeIoCo73l/ssM4x+/g+j7AGnE3GhjQvSfWEm5BaDXyh+U0S -TkH3dgH7K1ZR99geZxZm+OkLdEaOVJ943uT2HUNM9UMt42+7LHWjtQSN9vUTbzi3 -9NBsWPw9+0E0WCSYBm3uohT+McdAuZnwxQKCAQEAvQhO9GqMMtOnN/QUdU4FHVKl -R8vuJpT3w0ywBcHj5aYwPgkLxCYmfnZtD0kNPkP1FjlYz6C+cGUgNPNifoj6CwCA -oRrw7mgyhHuoum+7qlFwJzhuyx94Z4B7RbINfEsqk4mXGRssUUUBGd4Sh29w54SU -dQMn7s0LiwN6AOwOaAnjD9RlBE+021N9Dax71DuBu1RzA/Z1sC5EFYkL1C5B4kSJ -BYd0Nvru5DRidDrJuxr8tnGqOkNqT2kaujkVmFy6ra6nshkbErbd1LXZksM2PP96 -bM5Pta4jX4ylcS8e5F16zmZZrtuBDla1kizM7tGdPQQztXeBHZzvdQDlWTHqkw== ------END RSA PRIVATE KEY----- diff --git a/vendor/gopkg.in/square/go-jose.v1/.gitcookies.sh.enc b/vendor/gopkg.in/square/go-jose.v1/.gitcookies.sh.enc deleted file mode 100644 index 730e569b..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/.gitcookies.sh.enc +++ /dev/null @@ -1 +0,0 @@ -'|�&{t�U|gG�(�Cy=+���c�:u:/p�#~��["�4�!�n�A�DK<�uf�h�a��:�����B/�ؤ���_�h��S�T*w�x����-�|���Ӄ�����㣗A$$�6���G)8n�p��ˡ3̚�o��v�B�3��]xݓ�2l�G�|qRޯ �2 5R����$��Y��ݙl�˫yAI"ی���û��k�|K��[9����=�����|@S�3�#�x?�V�,��S����wP�og�6&V6 �D.dB�7 \ No newline at end of file diff --git a/vendor/gopkg.in/square/go-jose.v1/.gitignore b/vendor/gopkg.in/square/go-jose.v1/.gitignore deleted file mode 100644 index 5b4d73b6..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -*~ -.*.swp -*.out -*.test -*.pem -*.cov -jose-util/jose-util diff --git a/vendor/gopkg.in/square/go-jose.v1/.travis.yml b/vendor/gopkg.in/square/go-jose.v1/.travis.yml deleted file mode 100644 index 7582bb72..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/.travis.yml +++ /dev/null @@ -1,45 +0,0 @@ -language: go - -sudo: false - -matrix: - fast_finish: true - allow_failures: - - go: tip - -go: -- 1.3 -- 1.4 -- 1.5 -- 1.6 -- tip - -go_import_path: gopkg.in/square/go-jose.v1 - -before_script: -- export PATH=$HOME/.local/bin:$PATH - -before_install: -# Install encrypted gitcookies to get around bandwidth-limits -# that is causing Travis-CI builds to fail. For more info, see -# https://github.com/golang/go/issues/12933 -- openssl aes-256-cbc -K $encrypted_1528c3c2cafd_key -iv $encrypted_1528c3c2cafd_iv -in .gitcookies.sh.enc -out .gitcookies.sh -d || true -- bash .gitcookies.sh || true -- go get github.com/wadey/gocovmerge -- go get github.com/mattn/goveralls -- go get golang.org/x/tools/cmd/cover || true -- go get code.google.com/p/go.tools/cmd/cover || true -- pip install cram --user `whoami` - -script: -- go test . -v -covermode=count -coverprofile=profile.cov -- go test . -tags std_json -v -covermode=count -coverprofile=profile-std-json.cov -- go test ./cipher -v -covermode=count -coverprofile=cipher/profile.cov -- go test ./json -v # no coverage for forked encoding/json package -- cd jose-util && go build && PATH=$PWD:$PATH cram -v jose-util.t -- cd .. - -after_success: -- gocovmerge *.cov */*.cov > merged.coverprofile -- $HOME/gopath/bin/goveralls -coverprofile merged.coverprofile -service=travis-ci - diff --git a/vendor/gopkg.in/square/go-jose.v1/BUG-BOUNTY.md b/vendor/gopkg.in/square/go-jose.v1/BUG-BOUNTY.md deleted file mode 100644 index 97e61dbb..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/BUG-BOUNTY.md +++ /dev/null @@ -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://hackerone.com/square-open-source>. - diff --git a/vendor/gopkg.in/square/go-jose.v1/CONTRIBUTING.md b/vendor/gopkg.in/square/go-jose.v1/CONTRIBUTING.md deleted file mode 100644 index 61b18365..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/CONTRIBUTING.md +++ /dev/null @@ -1,14 +0,0 @@ -# Contributing - -If you would like to contribute code to go-jose you can do so through GitHub by -forking the repository and sending a pull request. - -When submitting code, please make every effort to follow existing conventions -and style in order to keep the code as readable as possible. Please also make -sure all tests pass by running `go test`, and format your code with `go fmt`. -We also recommend using `golint` and `errcheck`. - -Before your code can be accepted into the project you must also sign the -[Individual Contributor License Agreement][1]. - - [1]: https://spreadsheets.google.com/spreadsheet/viewform?formkey=dDViT2xzUHAwRkI3X3k5Z0lQM091OGc6MQ&ndplr=1 diff --git a/vendor/gopkg.in/square/go-jose.v1/LICENSE b/vendor/gopkg.in/square/go-jose.v1/LICENSE deleted file mode 100644 index d6456956..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/vendor/gopkg.in/square/go-jose.v1/README.md b/vendor/gopkg.in/square/go-jose.v1/README.md deleted file mode 100644 index fd859da7..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/README.md +++ /dev/null @@ -1,209 +0,0 @@ -# Go JOSE - -[](https://godoc.org/gopkg.in/square/go-jose.v1) [](https://raw.githubusercontent.com/square/go-jose/master/LICENSE) [](https://travis-ci.org/square/go-jose) [](https://coveralls.io/r/square/go-jose) - -Package jose aims to provide an implementation of the Javascript Object Signing -and Encryption set of standards. For the moment, it mainly focuses on encryption -and signing based on the JSON Web Encryption and JSON Web Signature standards. - -**Disclaimer**: This library contains encryption software that is subject to -the U.S. Export Administration Regulations. You may not export, re-export, -transfer or download this code or any part of it in violation of any United -States law, directive or regulation. In particular this software may not be -exported or re-exported in any form or on any media to Iran, North Sudan, -Syria, Cuba, or North Korea, or to denied persons or entities mentioned on any -US maintained blocked list. - -## Overview - -The implementation follows the -[JSON Web Encryption](http://dx.doi.org/10.17487/RFC7516) -standard (RFC 7516) and -[JSON Web Signature](http://dx.doi.org/10.17487/RFC7515) -standard (RFC 7515). Tables of supported algorithms are shown below. -The library supports both the compact and full serialization formats, and has -optional support for multiple recipients. It also comes with a small -command-line utility -([`jose-util`](https://github.com/square/go-jose/tree/master/jose-util)) -for dealing with JOSE messages in a shell. - -**Note**: We use a forked version of the `encoding/json` package from the Go -standard library which uses case-sensitive matching for member names (instead -of [case-insensitive matching](https://www.ietf.org/mail-archive/web/json/current/msg03763.html)). -This is to avoid differences in interpretation of messages between go-jose and -libraries in other languages. If you do not like this behavior, you can use the -`std_json` build tag to disable it (though we do not recommend doing so). - -### Versions - -We use [gopkg.in](https://gopkg.in) for versioning. - -[Version 1](https://gopkg.in/square/go-jose.v1) is the current stable version: - - import "gopkg.in/square/go-jose.v1" - -The interface for [go-jose.v1](https://gopkg.in/square/go-jose.v1) will remain -backwards compatible. We're currently sketching out ideas for a new version, to -clean up the interface a bit. If you have ideas or feature requests [please let -us know](https://github.com/square/go-jose/issues/64)! - -### 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) -standard where possible. The -[Godoc reference](https://godoc.org/github.com/square/go-jose#pkg-constants) -has a list of constants. - - Key encryption | Algorithm identifier(s) - :------------------------- | :------------------------------ - RSA-PKCS#1v1.5 | RSA1_5 - RSA-OAEP | RSA-OAEP, RSA-OAEP-256 - AES key wrap | A128KW, A192KW, A256KW - AES-GCM key wrap | A128GCMKW, A192GCMKW, A256GCMKW - ECDH-ES + AES key wrap | ECDH-ES+A128KW, ECDH-ES+A192KW, ECDH-ES+A256KW - ECDH-ES (direct) | ECDH-ES<sup>1</sup> - Direct encryption | dir<sup>1</sup> - -<sup>1. Not supported in multi-recipient mode</sup> - - Signing / MAC | Algorithm identifier(s) - :------------------------- | :------------------------------ - RSASSA-PKCS#1v1.5 | RS256, RS384, RS512 - RSASSA-PSS | PS256, PS384, PS512 - HMAC | HS256, HS384, HS512 - ECDSA | ES256, ES384, ES512 - - Content encryption | Algorithm identifier(s) - :------------------------- | :------------------------------ - AES-CBC+HMAC | A128CBC-HS256, A192CBC-HS384, A256CBC-HS512 - AES-GCM | A128GCM, A192GCM, A256GCM - - Compression | Algorithm identifiers(s) - :------------------------- | ------------------------------- - DEFLATE (RFC 1951) | DEF - -### Supported key types - -See below for a table of supported key types. These are understood by the -library, and can be passed to corresponding functions such as `NewEncrypter` or -`NewSigner`. Note that if you are creating a new encrypter or signer with a -JsonWebKey, the key id of the JsonWebKey (if present) will be added to any -resulting messages. - - Algorithm(s) | Corresponding types - :------------------------- | ------------------------------- - RSA | *[rsa.PublicKey](http://golang.org/pkg/crypto/rsa/#PublicKey), *[rsa.PrivateKey](http://golang.org/pkg/crypto/rsa/#PrivateKey), *[jose.JsonWebKey](https://godoc.org/github.com/square/go-jose#JsonWebKey) - ECDH, ECDSA | *[ecdsa.PublicKey](http://golang.org/pkg/crypto/ecdsa/#PublicKey), *[ecdsa.PrivateKey](http://golang.org/pkg/crypto/ecdsa/#PrivateKey), *[jose.JsonWebKey](https://godoc.org/github.com/square/go-jose#JsonWebKey) - AES, HMAC | []byte, *[jose.JsonWebKey](https://godoc.org/github.com/square/go-jose#JsonWebKey) - -## Examples - -Encryption/decryption example using RSA: - -```Go -// Generate a public/private key pair to use for this example. The library -// also provides two utility functions (LoadPublicKey and LoadPrivateKey) -// that can be used to load keys from PEM/DER-encoded data. -privateKey, err := rsa.GenerateKey(rand.Reader, 2048) -if err != nil { - panic(err) -} - -// Instantiate an encrypter using RSA-OAEP with AES128-GCM. An error would -// indicate that the selected algorithm(s) are not currently supported. -publicKey := &privateKey.PublicKey -encrypter, err := NewEncrypter(RSA_OAEP, A128GCM, publicKey) -if err != nil { - panic(err) -} - -// Encrypt a sample plaintext. Calling the encrypter returns an encrypted -// JWE object, which can then be serialized for output afterwards. An error -// would indicate a problem in an underlying cryptographic primitive. -var plaintext = []byte("Lorem ipsum dolor sit amet") -object, err := encrypter.Encrypt(plaintext) -if err != nil { - panic(err) -} - -// Serialize the encrypted object using the full serialization format. -// Alternatively you can also use the compact format here by calling -// object.CompactSerialize() instead. -serialized := object.FullSerialize() - -// Parse the serialized, encrypted JWE object. An error would indicate that -// the given input did not represent a valid message. -object, err = ParseEncrypted(serialized) -if err != nil { - panic(err) -} - -// Now we can decrypt and get back our original plaintext. An error here -// would indicate the the message failed to decrypt, e.g. because the auth -// tag was broken or the message was tampered with. -decrypted, err := object.Decrypt(privateKey) -if err != nil { - panic(err) -} - -fmt.Printf(string(decrypted)) -// output: Lorem ipsum dolor sit amet -``` - -Signing/verification example using RSA: - -```Go -// Generate a public/private key pair to use for this example. The library -// also provides two utility functions (LoadPublicKey and LoadPrivateKey) -// that can be used to load keys from PEM/DER-encoded data. -privateKey, err := rsa.GenerateKey(rand.Reader, 2048) -if err != nil { - panic(err) -} - -// Instantiate a signer using RSASSA-PSS (SHA512) with the given private key. -signer, err := NewSigner(PS512, privateKey) -if err != nil { - panic(err) -} - -// Sign a sample payload. Calling the signer returns a protected JWS object, -// which can then be serialized for output afterwards. An error would -// indicate a problem in an underlying cryptographic primitive. -var payload = []byte("Lorem ipsum dolor sit amet") -object, err := signer.Sign(payload) -if err != nil { - panic(err) -} - -// Serialize the encrypted object using the full serialization format. -// Alternatively you can also use the compact format here by calling -// object.CompactSerialize() instead. -serialized := object.FullSerialize() - -// Parse the serialized, protected JWS object. An error would indicate that -// the given input did not represent a valid message. -object, err = ParseSigned(serialized) -if err != nil { - panic(err) -} - -// Now we can verify the signature on the payload. An error here would -// indicate the the message failed to verify, e.g. because the signature was -// broken or the message was tampered with. -output, err := object.Verify(&privateKey.PublicKey) -if err != nil { - panic(err) -} - -fmt.Printf(string(output)) -// output: Lorem ipsum dolor sit amet -``` - -More examples can be found in the [Godoc -reference](https://godoc.org/github.com/square/go-jose) for this package. The -[`jose-util`](https://github.com/square/go-jose/tree/master/jose-util) -subdirectory also contains a small command-line utility which might -be useful as an example. diff --git a/vendor/gopkg.in/square/go-jose.v1/asymmetric.go b/vendor/gopkg.in/square/go-jose.v1/asymmetric.go deleted file mode 100644 index 381156ca..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/asymmetric.go +++ /dev/null @@ -1,498 +0,0 @@ -/*- - * Copyright 2014 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 jose - -import ( - "crypto" - "crypto/aes" - "crypto/ecdsa" - "crypto/rand" - "crypto/rsa" - "crypto/sha1" - "crypto/sha256" - "errors" - "fmt" - "math/big" - - "gopkg.in/square/go-jose.v1/cipher" -) - -// A generic RSA-based encrypter/verifier -type rsaEncrypterVerifier struct { - publicKey *rsa.PublicKey -} - -// A generic RSA-based decrypter/signer -type rsaDecrypterSigner struct { - privateKey *rsa.PrivateKey -} - -// A generic EC-based encrypter/verifier -type ecEncrypterVerifier struct { - publicKey *ecdsa.PublicKey -} - -// A key generator for ECDH-ES -type ecKeyGenerator struct { - size int - algID string - publicKey *ecdsa.PublicKey -} - -// A generic EC-based decrypter/signer -type ecDecrypterSigner struct { - privateKey *ecdsa.PrivateKey -} - -// newRSARecipient creates recipientKeyInfo based on the given key. -func newRSARecipient(keyAlg KeyAlgorithm, publicKey *rsa.PublicKey) (recipientKeyInfo, error) { - // Verify that key management algorithm is supported by this encrypter - switch keyAlg { - case RSA1_5, RSA_OAEP, RSA_OAEP_256: - default: - return recipientKeyInfo{}, ErrUnsupportedAlgorithm - } - - return recipientKeyInfo{ - keyAlg: keyAlg, - keyEncrypter: &rsaEncrypterVerifier{ - publicKey: publicKey, - }, - }, nil -} - -// newRSASigner creates a recipientSigInfo based on the given key. -func newRSASigner(sigAlg SignatureAlgorithm, privateKey *rsa.PrivateKey) (recipientSigInfo, error) { - // Verify that key management algorithm is supported by this encrypter - switch sigAlg { - case RS256, RS384, RS512, PS256, PS384, PS512: - default: - return recipientSigInfo{}, ErrUnsupportedAlgorithm - } - - return recipientSigInfo{ - sigAlg: sigAlg, - publicKey: &JsonWebKey{ - Key: &privateKey.PublicKey, - }, - signer: &rsaDecrypterSigner{ - privateKey: privateKey, - }, - }, nil -} - -// newECDHRecipient creates recipientKeyInfo based on the given key. -func newECDHRecipient(keyAlg KeyAlgorithm, publicKey *ecdsa.PublicKey) (recipientKeyInfo, error) { - // Verify that key management algorithm is supported by this encrypter - switch keyAlg { - case ECDH_ES, ECDH_ES_A128KW, ECDH_ES_A192KW, ECDH_ES_A256KW: - default: - return recipientKeyInfo{}, ErrUnsupportedAlgorithm - } - - return recipientKeyInfo{ - keyAlg: keyAlg, - keyEncrypter: &ecEncrypterVerifier{ - publicKey: publicKey, - }, - }, nil -} - -// newECDSASigner creates a recipientSigInfo based on the given key. -func newECDSASigner(sigAlg SignatureAlgorithm, privateKey *ecdsa.PrivateKey) (recipientSigInfo, error) { - // Verify that key management algorithm is supported by this encrypter - switch sigAlg { - case ES256, ES384, ES512: - default: - return recipientSigInfo{}, ErrUnsupportedAlgorithm - } - - return recipientSigInfo{ - sigAlg: sigAlg, - publicKey: &JsonWebKey{ - Key: &privateKey.PublicKey, - }, - signer: &ecDecrypterSigner{ - privateKey: privateKey, - }, - }, nil -} - -// Encrypt the given payload and update the object. -func (ctx rsaEncrypterVerifier) encryptKey(cek []byte, alg KeyAlgorithm) (recipientInfo, error) { - encryptedKey, err := ctx.encrypt(cek, alg) - if err != nil { - return recipientInfo{}, err - } - - return recipientInfo{ - encryptedKey: encryptedKey, - header: &rawHeader{}, - }, nil -} - -// Encrypt the given payload. Based on the key encryption algorithm, -// this will either use RSA-PKCS1v1.5 or RSA-OAEP (with SHA-1 or SHA-256). -func (ctx rsaEncrypterVerifier) encrypt(cek []byte, alg KeyAlgorithm) ([]byte, error) { - switch alg { - case RSA1_5: - return rsa.EncryptPKCS1v15(randReader, ctx.publicKey, cek) - case RSA_OAEP: - return rsa.EncryptOAEP(sha1.New(), randReader, ctx.publicKey, cek, []byte{}) - case RSA_OAEP_256: - return rsa.EncryptOAEP(sha256.New(), randReader, ctx.publicKey, cek, []byte{}) - } - - return nil, ErrUnsupportedAlgorithm -} - -// Decrypt the given payload and return the content encryption key. -func (ctx rsaDecrypterSigner) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) { - return ctx.decrypt(recipient.encryptedKey, KeyAlgorithm(headers.Alg), generator) -} - -// Decrypt the given payload. Based on the key encryption algorithm, -// this will either use RSA-PKCS1v1.5 or RSA-OAEP (with SHA-1 or SHA-256). -func (ctx rsaDecrypterSigner) decrypt(jek []byte, alg KeyAlgorithm, generator keyGenerator) ([]byte, error) { - // Note: The random reader on decrypt operations is only used for blinding, - // so stubbing is meanlingless (hence the direct use of rand.Reader). - switch alg { - case RSA1_5: - defer func() { - // DecryptPKCS1v15SessionKey sometimes panics on an invalid payload - // because of an index out of bounds error, which we want to ignore. - // This has been fixed in Go 1.3.1 (released 2014/08/13), the recover() - // only exists for preventing crashes with unpatched versions. - // See: https://groups.google.com/forum/#!topic/golang-dev/7ihX6Y6kx9k - // See: https://code.google.com/p/go/source/detail?r=58ee390ff31602edb66af41ed10901ec95904d33 - _ = recover() - }() - - // Perform some input validation. - keyBytes := ctx.privateKey.PublicKey.N.BitLen() / 8 - if keyBytes != len(jek) { - // Input size is incorrect, the encrypted payload should always match - // the size of the public modulus (e.g. using a 2048 bit key will - // produce 256 bytes of output). Reject this since it's invalid input. - return nil, ErrCryptoFailure - } - - cek, _, err := generator.genKey() - if err != nil { - return nil, ErrCryptoFailure - } - - // When decrypting an RSA-PKCS1v1.5 payload, we must take precautions to - // prevent chosen-ciphertext attacks as described in RFC 3218, "Preventing - // the Million Message Attack on Cryptographic Message Syntax". We are - // therefore deliberatly ignoring errors here. - _ = rsa.DecryptPKCS1v15SessionKey(rand.Reader, ctx.privateKey, jek, cek) - - return cek, nil - case RSA_OAEP: - // Use rand.Reader for RSA blinding - return rsa.DecryptOAEP(sha1.New(), rand.Reader, ctx.privateKey, jek, []byte{}) - case RSA_OAEP_256: - // Use rand.Reader for RSA blinding - return rsa.DecryptOAEP(sha256.New(), rand.Reader, ctx.privateKey, jek, []byte{}) - } - - return nil, ErrUnsupportedAlgorithm -} - -// Sign the given payload -func (ctx rsaDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) { - var hash crypto.Hash - - switch alg { - case RS256, PS256: - hash = crypto.SHA256 - case RS384, PS384: - hash = crypto.SHA384 - case RS512, PS512: - hash = crypto.SHA512 - default: - return Signature{}, ErrUnsupportedAlgorithm - } - - hasher := hash.New() - - // According to documentation, Write() on hash never fails - _, _ = hasher.Write(payload) - hashed := hasher.Sum(nil) - - var out []byte - var err error - - switch alg { - case RS256, RS384, RS512: - out, err = rsa.SignPKCS1v15(randReader, ctx.privateKey, hash, hashed) - case PS256, PS384, PS512: - out, err = rsa.SignPSS(randReader, ctx.privateKey, hash, hashed, &rsa.PSSOptions{ - SaltLength: rsa.PSSSaltLengthAuto, - }) - } - - if err != nil { - return Signature{}, err - } - - return Signature{ - Signature: out, - protected: &rawHeader{}, - }, nil -} - -// Verify the given payload -func (ctx rsaEncrypterVerifier) verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error { - var hash crypto.Hash - - switch alg { - case RS256, PS256: - hash = crypto.SHA256 - case RS384, PS384: - hash = crypto.SHA384 - case RS512, PS512: - hash = crypto.SHA512 - default: - return ErrUnsupportedAlgorithm - } - - hasher := hash.New() - - // According to documentation, Write() on hash never fails - _, _ = hasher.Write(payload) - hashed := hasher.Sum(nil) - - switch alg { - case RS256, RS384, RS512: - return rsa.VerifyPKCS1v15(ctx.publicKey, hash, hashed, signature) - case PS256, PS384, PS512: - return rsa.VerifyPSS(ctx.publicKey, hash, hashed, signature, nil) - } - - return ErrUnsupportedAlgorithm -} - -// Encrypt the given payload and update the object. -func (ctx ecEncrypterVerifier) encryptKey(cek []byte, alg KeyAlgorithm) (recipientInfo, error) { - switch alg { - case ECDH_ES: - // ECDH-ES mode doesn't wrap a key, the shared secret is used directly as the key. - return recipientInfo{ - header: &rawHeader{}, - }, nil - case ECDH_ES_A128KW, ECDH_ES_A192KW, ECDH_ES_A256KW: - default: - return recipientInfo{}, ErrUnsupportedAlgorithm - } - - generator := ecKeyGenerator{ - algID: string(alg), - publicKey: ctx.publicKey, - } - - switch alg { - case ECDH_ES_A128KW: - generator.size = 16 - case ECDH_ES_A192KW: - generator.size = 24 - case ECDH_ES_A256KW: - generator.size = 32 - } - - kek, header, err := generator.genKey() - if err != nil { - return recipientInfo{}, err - } - - block, err := aes.NewCipher(kek) - if err != nil { - return recipientInfo{}, err - } - - jek, err := josecipher.KeyWrap(block, cek) - if err != nil { - return recipientInfo{}, err - } - - return recipientInfo{ - encryptedKey: jek, - header: &header, - }, nil -} - -// Get key size for EC key generator -func (ctx ecKeyGenerator) keySize() int { - return ctx.size -} - -// Get a content encryption key for ECDH-ES -func (ctx ecKeyGenerator) genKey() ([]byte, rawHeader, error) { - priv, err := ecdsa.GenerateKey(ctx.publicKey.Curve, randReader) - if err != nil { - return nil, rawHeader{}, err - } - - out := josecipher.DeriveECDHES(ctx.algID, []byte{}, []byte{}, priv, ctx.publicKey, ctx.size) - - headers := rawHeader{ - Epk: &JsonWebKey{ - Key: &priv.PublicKey, - }, - } - - return out, headers, nil -} - -// Decrypt the given payload and return the content encryption key. -func (ctx ecDecrypterSigner) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) { - if headers.Epk == nil { - return nil, errors.New("square/go-jose: missing epk header") - } - - publicKey, ok := headers.Epk.Key.(*ecdsa.PublicKey) - if publicKey == nil || !ok { - return nil, errors.New("square/go-jose: invalid epk header") - } - - apuData := headers.Apu.bytes() - apvData := headers.Apv.bytes() - - deriveKey := func(algID string, size int) []byte { - return josecipher.DeriveECDHES(algID, apuData, apvData, ctx.privateKey, publicKey, size) - } - - var keySize int - - switch KeyAlgorithm(headers.Alg) { - case ECDH_ES: - // ECDH-ES uses direct key agreement, no key unwrapping necessary. - return deriveKey(string(headers.Enc), generator.keySize()), nil - case ECDH_ES_A128KW: - keySize = 16 - case ECDH_ES_A192KW: - keySize = 24 - case ECDH_ES_A256KW: - keySize = 32 - default: - return nil, ErrUnsupportedAlgorithm - } - - key := deriveKey(headers.Alg, keySize) - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - - return josecipher.KeyUnwrap(block, recipient.encryptedKey) -} - -// Sign the given payload -func (ctx ecDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) { - var expectedBitSize int - var hash crypto.Hash - - switch alg { - case ES256: - expectedBitSize = 256 - hash = crypto.SHA256 - case ES384: - expectedBitSize = 384 - hash = crypto.SHA384 - case ES512: - expectedBitSize = 521 - hash = crypto.SHA512 - } - - curveBits := ctx.privateKey.Curve.Params().BitSize - if expectedBitSize != curveBits { - return Signature{}, fmt.Errorf("square/go-jose: expected %d bit key, got %d bits instead", expectedBitSize, curveBits) - } - - hasher := hash.New() - - // According to documentation, Write() on hash never fails - _, _ = hasher.Write(payload) - hashed := hasher.Sum(nil) - - r, s, err := ecdsa.Sign(randReader, ctx.privateKey, hashed) - if err != nil { - return Signature{}, err - } - - keyBytes := curveBits / 8 - if curveBits%8 > 0 { - keyBytes += 1 - } - - // We serialize the outpus (r and s) into big-endian byte arrays and pad - // them with zeros on the left to make sure the sizes work out. Both arrays - // must be keyBytes long, and the output must be 2*keyBytes long. - rBytes := r.Bytes() - rBytesPadded := make([]byte, keyBytes) - copy(rBytesPadded[keyBytes-len(rBytes):], rBytes) - - sBytes := s.Bytes() - sBytesPadded := make([]byte, keyBytes) - copy(sBytesPadded[keyBytes-len(sBytes):], sBytes) - - out := append(rBytesPadded, sBytesPadded...) - - return Signature{ - Signature: out, - protected: &rawHeader{}, - }, nil -} - -// Verify the given payload -func (ctx ecEncrypterVerifier) verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error { - var keySize int - var hash crypto.Hash - - switch alg { - case ES256: - keySize = 32 - hash = crypto.SHA256 - case ES384: - keySize = 48 - hash = crypto.SHA384 - case ES512: - keySize = 66 - hash = crypto.SHA512 - } - - if len(signature) != 2*keySize { - return fmt.Errorf("square/go-jose: invalid signature size, have %d bytes, wanted %d", len(signature), 2*keySize) - } - - hasher := hash.New() - - // According to documentation, Write() on hash never fails - _, _ = hasher.Write(payload) - hashed := hasher.Sum(nil) - - r := big.NewInt(0).SetBytes(signature[:keySize]) - s := big.NewInt(0).SetBytes(signature[keySize:]) - - match := ecdsa.Verify(ctx.publicKey, hashed, r, s) - if !match { - return errors.New("square/go-jose: ecdsa signature failed to verify") - } - - return nil -} diff --git a/vendor/gopkg.in/square/go-jose.v1/asymmetric_test.go b/vendor/gopkg.in/square/go-jose.v1/asymmetric_test.go deleted file mode 100644 index 1c8c8b34..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/asymmetric_test.go +++ /dev/null @@ -1,431 +0,0 @@ -/*- - * Copyright 2014 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 jose - -import ( - "bytes" - "crypto/rand" - "crypto/rsa" - "errors" - "io" - "math/big" - "testing" -) - -func TestVectorsRSA(t *testing.T) { - // Sources: - // http://www.emc.com/emc-plus/rsa-labs/standards-initiatives/pkcs-rsa-cryptography-standard.htm - // ftp://ftp.rsa.com/pub/rsalabs/tmp/pkcs1v15crypt-vectors.txt - priv := &rsa.PrivateKey{ - PublicKey: rsa.PublicKey{ - N: fromHexInt(` - a8b3b284af8eb50b387034a860f146c4919f318763cd6c5598c8 - ae4811a1e0abc4c7e0b082d693a5e7fced675cf4668512772c0c - bc64a742c6c630f533c8cc72f62ae833c40bf25842e984bb78bd - bf97c0107d55bdb662f5c4e0fab9845cb5148ef7392dd3aaff93 - ae1e6b667bb3d4247616d4f5ba10d4cfd226de88d39f16fb`), - E: 65537, - }, - D: fromHexInt(` - 53339cfdb79fc8466a655c7316aca85c55fd8f6dd898fdaf1195 - 17ef4f52e8fd8e258df93fee180fa0e4ab29693cd83b152a553d - 4ac4d1812b8b9fa5af0e7f55fe7304df41570926f3311f15c4d6 - 5a732c483116ee3d3d2d0af3549ad9bf7cbfb78ad884f84d5beb - 04724dc7369b31def37d0cf539e9cfcdd3de653729ead5d1`), - Primes: []*big.Int{ - fromHexInt(` - d32737e7267ffe1341b2d5c0d150a81b586fb3132bed2f8d5262 - 864a9cb9f30af38be448598d413a172efb802c21acf1c11c520c - 2f26a471dcad212eac7ca39d`), - fromHexInt(` - cc8853d1d54da630fac004f471f281c7b8982d8224a490edbeb3 - 3d3e3d5cc93c4765703d1dd791642f1f116a0dd852be2419b2af - 72bfe9a030e860b0288b5d77`), - }, - } - - input := fromHexBytes( - "6628194e12073db03ba94cda9ef9532397d50dba79b987004afefe34") - - expectedPKCS := fromHexBytes(` - 50b4c14136bd198c2f3c3ed243fce036e168d56517984a263cd66492b808 - 04f169d210f2b9bdfb48b12f9ea05009c77da257cc600ccefe3a6283789d - 8ea0e607ac58e2690ec4ebc10146e8cbaa5ed4d5cce6fe7b0ff9efc1eabb - 564dbf498285f449ee61dd7b42ee5b5892cb90601f30cda07bf26489310b - cd23b528ceab3c31`) - - expectedOAEP := fromHexBytes(` - 354fe67b4a126d5d35fe36c777791a3f7ba13def484e2d3908aff722fad4 - 68fb21696de95d0be911c2d3174f8afcc201035f7b6d8e69402de5451618 - c21a535fa9d7bfc5b8dd9fc243f8cf927db31322d6e881eaa91a996170e6 - 57a05a266426d98c88003f8477c1227094a0d9fa1e8c4024309ce1ecccb5 - 210035d47ac72e8a`) - - // Mock random reader - randReader = bytes.NewReader(fromHexBytes(` - 017341ae3875d5f87101f8cc4fa9b9bc156bb04628fccdb2f4f11e905bd3 - a155d376f593bd7304210874eba08a5e22bcccb4c9d3882a93a54db022f5 - 03d16338b6b7ce16dc7f4bbf9a96b59772d6606e9747c7649bf9e083db98 - 1884a954ab3c6f18b776ea21069d69776a33e96bad48e1dda0a5ef`)) - defer resetRandReader() - - // RSA-PKCS1v1.5 encrypt - enc := new(rsaEncrypterVerifier) - enc.publicKey = &priv.PublicKey - encryptedPKCS, err := enc.encrypt(input, RSA1_5) - if err != nil { - t.Error("Encryption failed:", err) - return - } - - if bytes.Compare(encryptedPKCS, expectedPKCS) != 0 { - t.Error("Output does not match expected value (PKCS1v1.5)") - } - - // RSA-OAEP encrypt - encryptedOAEP, err := enc.encrypt(input, RSA_OAEP) - if err != nil { - t.Error("Encryption failed:", err) - return - } - - if bytes.Compare(encryptedOAEP, expectedOAEP) != 0 { - t.Error("Output does not match expected value (OAEP)") - } - - // Need fake cipher for PKCS1v1.5 decrypt - resetRandReader() - aes := newAESGCM(len(input)) - - keygen := randomKeyGenerator{ - size: aes.keySize(), - } - - // RSA-PKCS1v1.5 decrypt - dec := new(rsaDecrypterSigner) - dec.privateKey = priv - decryptedPKCS, err := dec.decrypt(encryptedPKCS, RSA1_5, keygen) - if err != nil { - t.Error("Decryption failed:", err) - return - } - - if bytes.Compare(input, decryptedPKCS) != 0 { - t.Error("Output does not match expected value (PKCS1v1.5)") - } - - // RSA-OAEP decrypt - decryptedOAEP, err := dec.decrypt(encryptedOAEP, RSA_OAEP, keygen) - if err != nil { - t.Error("decryption failed:", err) - return - } - - if bytes.Compare(input, decryptedOAEP) != 0 { - t.Error("output does not match expected value (OAEP)") - } -} - -func TestInvalidAlgorithmsRSA(t *testing.T) { - _, err := newRSARecipient("XYZ", nil) - if err != ErrUnsupportedAlgorithm { - t.Error("should return error on invalid algorithm") - } - - _, err = newRSASigner("XYZ", nil) - if err != ErrUnsupportedAlgorithm { - t.Error("should return error on invalid algorithm") - } - - enc := new(rsaEncrypterVerifier) - enc.publicKey = &rsaTestKey.PublicKey - _, err = enc.encryptKey([]byte{}, "XYZ") - if err != ErrUnsupportedAlgorithm { - t.Error("should return error on invalid algorithm") - } - - err = enc.verifyPayload([]byte{}, []byte{}, "XYZ") - if err != ErrUnsupportedAlgorithm { - t.Error("should return error on invalid algorithm") - } - - dec := new(rsaDecrypterSigner) - dec.privateKey = rsaTestKey - _, err = dec.decrypt(make([]byte, 256), "XYZ", randomKeyGenerator{size: 16}) - if err != ErrUnsupportedAlgorithm { - t.Error("should return error on invalid algorithm") - } - - _, err = dec.signPayload([]byte{}, "XYZ") - if err != ErrUnsupportedAlgorithm { - t.Error("should return error on invalid algorithm") - } -} - -type failingKeyGenerator struct{} - -func (ctx failingKeyGenerator) keySize() int { - return 0 -} - -func (ctx failingKeyGenerator) genKey() ([]byte, rawHeader, error) { - return nil, rawHeader{}, errors.New("failed to generate key") -} - -func TestPKCSKeyGeneratorFailure(t *testing.T) { - dec := new(rsaDecrypterSigner) - dec.privateKey = rsaTestKey - generator := failingKeyGenerator{} - _, err := dec.decrypt(make([]byte, 256), RSA1_5, generator) - if err != ErrCryptoFailure { - t.Error("should return error on invalid algorithm") - } -} - -func TestInvalidAlgorithmsEC(t *testing.T) { - _, err := newECDHRecipient("XYZ", nil) - if err != ErrUnsupportedAlgorithm { - t.Error("should return error on invalid algorithm") - } - - _, err = newECDSASigner("XYZ", nil) - if err != ErrUnsupportedAlgorithm { - t.Error("should return error on invalid algorithm") - } - - enc := new(ecEncrypterVerifier) - enc.publicKey = &ecTestKey256.PublicKey - _, err = enc.encryptKey([]byte{}, "XYZ") - if err != ErrUnsupportedAlgorithm { - t.Error("should return error on invalid algorithm") - } -} - -func TestInvalidECKeyGen(t *testing.T) { - gen := ecKeyGenerator{ - size: 16, - algID: "A128GCM", - publicKey: &ecTestKey256.PublicKey, - } - - if gen.keySize() != 16 { - t.Error("ec key generator reported incorrect key size") - } - - _, _, err := gen.genKey() - if err != nil { - t.Error("ec key generator failed to generate key", err) - } -} - -func TestInvalidECDecrypt(t *testing.T) { - dec := ecDecrypterSigner{ - privateKey: ecTestKey256, - } - - generator := randomKeyGenerator{size: 16} - - // Missing epk header - headers := rawHeader{ - Alg: string(ECDH_ES), - } - - _, err := dec.decryptKey(headers, nil, generator) - if err == nil { - t.Error("ec decrypter accepted object with missing epk header") - } - - // Invalid epk header - headers.Epk = &JsonWebKey{} - - _, err = dec.decryptKey(headers, nil, generator) - if err == nil { - t.Error("ec decrypter accepted object with invalid epk header") - } -} - -func TestDecryptWithIncorrectSize(t *testing.T) { - priv, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - t.Error(err) - return - } - - dec := new(rsaDecrypterSigner) - dec.privateKey = priv - aes := newAESGCM(16) - - keygen := randomKeyGenerator{ - size: aes.keySize(), - } - - payload := make([]byte, 254) - _, err = dec.decrypt(payload, RSA1_5, keygen) - if err == nil { - t.Error("Invalid payload size should return error") - } - - payload = make([]byte, 257) - _, err = dec.decrypt(payload, RSA1_5, keygen) - if err == nil { - t.Error("Invalid payload size should return error") - } -} - -func TestPKCSDecryptNeverFails(t *testing.T) { - // We don't want RSA-PKCS1 v1.5 decryption to ever fail, in order to prevent - // side-channel timing attacks (Bleichenbacher attack in particular). - priv, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - t.Error(err) - return - } - - dec := new(rsaDecrypterSigner) - dec.privateKey = priv - aes := newAESGCM(16) - - keygen := randomKeyGenerator{ - size: aes.keySize(), - } - - for i := 1; i < 50; i++ { - payload := make([]byte, 256) - _, err := io.ReadFull(rand.Reader, payload) - if err != nil { - t.Error("Unable to get random data:", err) - return - } - _, err = dec.decrypt(payload, RSA1_5, keygen) - if err != nil { - t.Error("PKCS1v1.5 decrypt should never fail:", err) - return - } - } -} - -func BenchmarkPKCSDecryptWithValidPayloads(b *testing.B) { - priv, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - panic(err) - } - - enc := new(rsaEncrypterVerifier) - enc.publicKey = &priv.PublicKey - dec := new(rsaDecrypterSigner) - dec.privateKey = priv - aes := newAESGCM(32) - - b.StopTimer() - b.ResetTimer() - for i := 0; i < b.N; i++ { - plaintext := make([]byte, 32) - _, err = io.ReadFull(rand.Reader, plaintext) - if err != nil { - panic(err) - } - - ciphertext, err := enc.encrypt(plaintext, RSA1_5) - if err != nil { - panic(err) - } - - keygen := randomKeyGenerator{ - size: aes.keySize(), - } - - b.StartTimer() - _, err = dec.decrypt(ciphertext, RSA1_5, keygen) - b.StopTimer() - if err != nil { - panic(err) - } - } -} - -func BenchmarkPKCSDecryptWithInvalidPayloads(b *testing.B) { - priv, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - panic(err) - } - - enc := new(rsaEncrypterVerifier) - enc.publicKey = &priv.PublicKey - dec := new(rsaDecrypterSigner) - dec.privateKey = priv - aes := newAESGCM(16) - - keygen := randomKeyGenerator{ - size: aes.keySize(), - } - - b.StopTimer() - b.ResetTimer() - for i := 0; i < b.N; i++ { - plaintext := make([]byte, 16) - _, err = io.ReadFull(rand.Reader, plaintext) - if err != nil { - panic(err) - } - - ciphertext, err := enc.encrypt(plaintext, RSA1_5) - if err != nil { - panic(err) - } - - // Do some simple scrambling - ciphertext[128] ^= 0xFF - - b.StartTimer() - _, err = dec.decrypt(ciphertext, RSA1_5, keygen) - b.StopTimer() - if err != nil { - panic(err) - } - } -} - -func TestInvalidEllipticCurve(t *testing.T) { - signer256 := ecDecrypterSigner{privateKey: ecTestKey256} - signer384 := ecDecrypterSigner{privateKey: ecTestKey384} - signer521 := ecDecrypterSigner{privateKey: ecTestKey521} - - _, err := signer256.signPayload([]byte{}, ES384) - if err == nil { - t.Error("should not generate ES384 signature with P-256 key") - } - _, err = signer256.signPayload([]byte{}, ES512) - if err == nil { - t.Error("should not generate ES512 signature with P-256 key") - } - _, err = signer384.signPayload([]byte{}, ES256) - if err == nil { - t.Error("should not generate ES256 signature with P-384 key") - } - _, err = signer384.signPayload([]byte{}, ES512) - if err == nil { - t.Error("should not generate ES512 signature with P-384 key") - } - _, err = signer521.signPayload([]byte{}, ES256) - if err == nil { - t.Error("should not generate ES256 signature with P-521 key") - } - _, err = signer521.signPayload([]byte{}, ES384) - if err == nil { - t.Error("should not generate ES384 signature with P-521 key") - } -} diff --git a/vendor/gopkg.in/square/go-jose.v1/cipher/cbc_hmac.go b/vendor/gopkg.in/square/go-jose.v1/cipher/cbc_hmac.go deleted file mode 100644 index a5c35834..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/cipher/cbc_hmac.go +++ /dev/null @@ -1,196 +0,0 @@ -/*- - * Copyright 2014 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 josecipher - -import ( - "bytes" - "crypto/cipher" - "crypto/hmac" - "crypto/sha256" - "crypto/sha512" - "crypto/subtle" - "encoding/binary" - "errors" - "hash" -) - -const ( - nonceBytes = 16 -) - -// NewCBCHMAC instantiates a new AEAD based on CBC+HMAC. -func NewCBCHMAC(key []byte, newBlockCipher func([]byte) (cipher.Block, error)) (cipher.AEAD, error) { - keySize := len(key) / 2 - integrityKey := key[:keySize] - encryptionKey := key[keySize:] - - blockCipher, err := newBlockCipher(encryptionKey) - if err != nil { - return nil, err - } - - var hash func() hash.Hash - switch keySize { - case 16: - hash = sha256.New - case 24: - hash = sha512.New384 - case 32: - hash = sha512.New - } - - return &cbcAEAD{ - hash: hash, - blockCipher: blockCipher, - authtagBytes: keySize, - integrityKey: integrityKey, - }, nil -} - -// An AEAD based on CBC+HMAC -type cbcAEAD struct { - hash func() hash.Hash - authtagBytes int - integrityKey []byte - blockCipher cipher.Block -} - -func (ctx *cbcAEAD) NonceSize() int { - return nonceBytes -} - -func (ctx *cbcAEAD) Overhead() int { - // Maximum overhead is block size (for padding) plus auth tag length, where - // the length of the auth tag is equivalent to the key size. - return ctx.blockCipher.BlockSize() + ctx.authtagBytes -} - -// Seal encrypts and authenticates the plaintext. -func (ctx *cbcAEAD) Seal(dst, nonce, plaintext, data []byte) []byte { - // Output buffer -- must take care not to mangle plaintext input. - ciphertext := make([]byte, len(plaintext)+ctx.Overhead())[:len(plaintext)] - copy(ciphertext, plaintext) - ciphertext = padBuffer(ciphertext, ctx.blockCipher.BlockSize()) - - cbc := cipher.NewCBCEncrypter(ctx.blockCipher, nonce) - - cbc.CryptBlocks(ciphertext, ciphertext) - authtag := ctx.computeAuthTag(data, nonce, ciphertext) - - ret, out := resize(dst, len(dst)+len(ciphertext)+len(authtag)) - copy(out, ciphertext) - copy(out[len(ciphertext):], authtag) - - return ret -} - -// Open decrypts and authenticates the ciphertext. -func (ctx *cbcAEAD) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { - if len(ciphertext) < ctx.authtagBytes { - return nil, errors.New("square/go-jose: invalid ciphertext (too short)") - } - - offset := len(ciphertext) - ctx.authtagBytes - expectedTag := ctx.computeAuthTag(data, nonce, ciphertext[:offset]) - match := subtle.ConstantTimeCompare(expectedTag, ciphertext[offset:]) - if match != 1 { - return nil, errors.New("square/go-jose: invalid ciphertext (auth tag mismatch)") - } - - cbc := cipher.NewCBCDecrypter(ctx.blockCipher, nonce) - - // Make copy of ciphertext buffer, don't want to modify in place - buffer := append([]byte{}, []byte(ciphertext[:offset])...) - - if len(buffer)%ctx.blockCipher.BlockSize() > 0 { - return nil, errors.New("square/go-jose: invalid ciphertext (invalid length)") - } - - cbc.CryptBlocks(buffer, buffer) - - // Remove padding - plaintext, err := unpadBuffer(buffer, ctx.blockCipher.BlockSize()) - if err != nil { - return nil, err - } - - ret, out := resize(dst, len(dst)+len(plaintext)) - copy(out, plaintext) - - return ret, nil -} - -// Compute an authentication tag -func (ctx *cbcAEAD) computeAuthTag(aad, nonce, ciphertext []byte) []byte { - buffer := make([]byte, len(aad)+len(nonce)+len(ciphertext)+8) - n := 0 - n += copy(buffer, aad) - n += copy(buffer[n:], nonce) - n += copy(buffer[n:], ciphertext) - binary.BigEndian.PutUint64(buffer[n:], uint64(len(aad)*8)) - - // According to documentation, Write() on hash.Hash never fails. - hmac := hmac.New(ctx.hash, ctx.integrityKey) - _, _ = hmac.Write(buffer) - - return hmac.Sum(nil)[:ctx.authtagBytes] -} - -// resize ensures the the given slice has a capacity of at least n bytes. -// If the capacity of the slice is less than n, a new slice is allocated -// and the existing data will be copied. -func resize(in []byte, n int) (head, tail []byte) { - if cap(in) >= n { - head = in[:n] - } else { - head = make([]byte, n) - copy(head, in) - } - - tail = head[len(in):] - return -} - -// Apply padding -func padBuffer(buffer []byte, blockSize int) []byte { - missing := blockSize - (len(buffer) % blockSize) - ret, out := resize(buffer, len(buffer)+missing) - padding := bytes.Repeat([]byte{byte(missing)}, missing) - copy(out, padding) - return ret -} - -// Remove padding -func unpadBuffer(buffer []byte, blockSize int) ([]byte, error) { - if len(buffer)%blockSize != 0 { - return nil, errors.New("square/go-jose: invalid padding") - } - - last := buffer[len(buffer)-1] - count := int(last) - - if count == 0 || count > blockSize || count > len(buffer) { - return nil, errors.New("square/go-jose: invalid padding") - } - - padding := bytes.Repeat([]byte{last}, count) - if !bytes.HasSuffix(buffer, padding) { - return nil, errors.New("square/go-jose: invalid padding") - } - - return buffer[:len(buffer)-count], nil -} diff --git a/vendor/gopkg.in/square/go-jose.v1/cipher/cbc_hmac_test.go b/vendor/gopkg.in/square/go-jose.v1/cipher/cbc_hmac_test.go deleted file mode 100644 index c230271b..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/cipher/cbc_hmac_test.go +++ /dev/null @@ -1,498 +0,0 @@ -/*- - * Copyright 2014 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 josecipher - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "crypto/rand" - "io" - "strings" - "testing" -) - -func TestInvalidInputs(t *testing.T) { - key := []byte{ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - } - - nonce := []byte{ - 92, 80, 104, 49, 133, 25, 161, 215, 173, 101, 219, 211, 136, 91, 210, 145} - - aead, _ := NewCBCHMAC(key, aes.NewCipher) - ciphertext := aead.Seal(nil, nonce, []byte("plaintext"), []byte("aad")) - - // Changed AAD, must fail - _, err := aead.Open(nil, nonce, ciphertext, []byte("INVALID")) - if err == nil { - t.Error("must detect invalid aad") - } - - // Empty ciphertext, must fail - _, err = aead.Open(nil, nonce, []byte{}, []byte("aad")) - if err == nil { - t.Error("must detect invalid/empty ciphertext") - } - - // Corrupt ciphertext, must fail - corrupt := make([]byte, len(ciphertext)) - copy(corrupt, ciphertext) - corrupt[0] ^= 0xFF - - _, err = aead.Open(nil, nonce, corrupt, []byte("aad")) - if err == nil { - t.Error("must detect corrupt ciphertext") - } - - // Corrupt authtag, must fail - copy(corrupt, ciphertext) - corrupt[len(ciphertext)-1] ^= 0xFF - - _, err = aead.Open(nil, nonce, corrupt, []byte("aad")) - if err == nil { - t.Error("must detect corrupt authtag") - } - - // Truncated data, must fail - _, err = aead.Open(nil, nonce, ciphertext[:10], []byte("aad")) - if err == nil { - t.Error("must detect corrupt authtag") - } -} - -func TestVectorsAESCBC128(t *testing.T) { - // Source: http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption-29#appendix-A.2 - plaintext := []byte{ - 76, 105, 118, 101, 32, 108, 111, 110, 103, 32, 97, 110, 100, 32, - 112, 114, 111, 115, 112, 101, 114, 46} - - aad := []byte{ - 101, 121, 74, 104, 98, 71, 99, 105, 79, 105, 74, 83, 85, 48, 69, - 120, 88, 122, 85, 105, 76, 67, 74, 108, 98, 109, 77, 105, 79, 105, - 74, 66, 77, 84, 73, 52, 81, 48, 74, 68, 76, 85, 104, 84, 77, 106, 85, - 50, 73, 110, 48} - - expectedCiphertext := []byte{ - 40, 57, 83, 181, 119, 33, 133, 148, 198, 185, 243, 24, 152, 230, 6, - 75, 129, 223, 127, 19, 210, 82, 183, 230, 168, 33, 215, 104, 143, - 112, 56, 102} - - expectedAuthtag := []byte{ - 246, 17, 244, 190, 4, 95, 98, 3, 231, 0, 115, 157, 242, 203, 100, - 191} - - key := []byte{ - 4, 211, 31, 197, 84, 157, 252, 254, 11, 100, 157, 250, 63, 170, 106, 206, - 107, 124, 212, 45, 111, 107, 9, 219, 200, 177, 0, 240, 143, 156, 44, 207} - - nonce := []byte{ - 3, 22, 60, 12, 43, 67, 104, 105, 108, 108, 105, 99, 111, 116, 104, 101} - - enc, err := NewCBCHMAC(key, aes.NewCipher) - out := enc.Seal(nil, nonce, plaintext, aad) - if err != nil { - t.Error("Unable to encrypt:", err) - return - } - - if bytes.Compare(out[:len(out)-16], expectedCiphertext) != 0 { - t.Error("Ciphertext did not match") - } - if bytes.Compare(out[len(out)-16:], expectedAuthtag) != 0 { - t.Error("Auth tag did not match") - } -} - -func TestVectorsAESCBC256(t *testing.T) { - // Source: https://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-05#section-5.4 - plaintext := []byte{ - 0x41, 0x20, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20, - 0x6d, 0x75, 0x73, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, - 0x69, 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, 0x74, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, - 0x65, 0x20, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x66, 0x61, 0x6c, 0x6c, 0x20, 0x69, - 0x6e, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x20, 0x6f, 0x66, - 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x65, 0x6d, 0x79, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, - 0x75, 0x74, 0x20, 0x69, 0x6e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x69, 0x65, 0x6e, 0x63, 0x65} - - aad := []byte{ - 0x54, 0x68, 0x65, 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x63, - 0x69, 0x70, 0x6c, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x41, 0x75, 0x67, 0x75, 0x73, 0x74, 0x65, 0x20, - 0x4b, 0x65, 0x72, 0x63, 0x6b, 0x68, 0x6f, 0x66, 0x66, 0x73} - - expectedCiphertext := []byte{ - 0x4a, 0xff, 0xaa, 0xad, 0xb7, 0x8c, 0x31, 0xc5, 0xda, 0x4b, 0x1b, 0x59, 0x0d, 0x10, 0xff, 0xbd, - 0x3d, 0xd8, 0xd5, 0xd3, 0x02, 0x42, 0x35, 0x26, 0x91, 0x2d, 0xa0, 0x37, 0xec, 0xbc, 0xc7, 0xbd, - 0x82, 0x2c, 0x30, 0x1d, 0xd6, 0x7c, 0x37, 0x3b, 0xcc, 0xb5, 0x84, 0xad, 0x3e, 0x92, 0x79, 0xc2, - 0xe6, 0xd1, 0x2a, 0x13, 0x74, 0xb7, 0x7f, 0x07, 0x75, 0x53, 0xdf, 0x82, 0x94, 0x10, 0x44, 0x6b, - 0x36, 0xeb, 0xd9, 0x70, 0x66, 0x29, 0x6a, 0xe6, 0x42, 0x7e, 0xa7, 0x5c, 0x2e, 0x08, 0x46, 0xa1, - 0x1a, 0x09, 0xcc, 0xf5, 0x37, 0x0d, 0xc8, 0x0b, 0xfe, 0xcb, 0xad, 0x28, 0xc7, 0x3f, 0x09, 0xb3, - 0xa3, 0xb7, 0x5e, 0x66, 0x2a, 0x25, 0x94, 0x41, 0x0a, 0xe4, 0x96, 0xb2, 0xe2, 0xe6, 0x60, 0x9e, - 0x31, 0xe6, 0xe0, 0x2c, 0xc8, 0x37, 0xf0, 0x53, 0xd2, 0x1f, 0x37, 0xff, 0x4f, 0x51, 0x95, 0x0b, - 0xbe, 0x26, 0x38, 0xd0, 0x9d, 0xd7, 0xa4, 0x93, 0x09, 0x30, 0x80, 0x6d, 0x07, 0x03, 0xb1, 0xf6} - - expectedAuthtag := []byte{ - 0x4d, 0xd3, 0xb4, 0xc0, 0x88, 0xa7, 0xf4, 0x5c, 0x21, 0x68, 0x39, 0x64, 0x5b, 0x20, 0x12, 0xbf, - 0x2e, 0x62, 0x69, 0xa8, 0xc5, 0x6a, 0x81, 0x6d, 0xbc, 0x1b, 0x26, 0x77, 0x61, 0x95, 0x5b, 0xc5} - - key := []byte{ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f} - - nonce := []byte{ - 0x1a, 0xf3, 0x8c, 0x2d, 0xc2, 0xb9, 0x6f, 0xfd, 0xd8, 0x66, 0x94, 0x09, 0x23, 0x41, 0xbc, 0x04} - - enc, err := NewCBCHMAC(key, aes.NewCipher) - out := enc.Seal(nil, nonce, plaintext, aad) - if err != nil { - t.Error("Unable to encrypt:", err) - return - } - - if bytes.Compare(out[:len(out)-32], expectedCiphertext) != 0 { - t.Error("Ciphertext did not match, got", out[:len(out)-32], "wanted", expectedCiphertext) - } - if bytes.Compare(out[len(out)-32:], expectedAuthtag) != 0 { - t.Error("Auth tag did not match, got", out[len(out)-32:], "wanted", expectedAuthtag) - } -} - -func TestAESCBCRoundtrip(t *testing.T) { - key128 := []byte{ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} - - key192 := []byte{ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 0, 1, 2, 3, 4, 5, 6, 7, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 0, 1, 2, 3, 4, 5, 6, 7} - - key256 := []byte{ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} - - nonce := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} - - RunRoundtrip(t, key128, nonce) - RunRoundtrip(t, key192, nonce) - RunRoundtrip(t, key256, nonce) -} - -func RunRoundtrip(t *testing.T, key, nonce []byte) { - aead, err := NewCBCHMAC(key, aes.NewCipher) - if err != nil { - panic(err) - } - - if aead.NonceSize() != len(nonce) { - panic("invalid nonce") - } - - // Test pre-existing data in dst buffer - dst := []byte{15, 15, 15, 15} - plaintext := []byte{0, 0, 0, 0} - aad := []byte{4, 3, 2, 1} - - result := aead.Seal(dst, nonce, plaintext, aad) - if bytes.Compare(dst, result[:4]) != 0 { - t.Error("Existing data in dst not preserved") - } - - // Test pre-existing (empty) dst buffer with sufficient capacity - dst = make([]byte, 256)[:0] - result, err = aead.Open(dst, nonce, result[4:], aad) - if err != nil { - panic(err) - } - - if bytes.Compare(result, plaintext) != 0 { - t.Error("Plaintext does not match output") - } -} - -func TestAESCBCOverhead(t *testing.T) { - aead, err := NewCBCHMAC(make([]byte, 32), aes.NewCipher) - if err != nil { - panic(err) - } - - if aead.Overhead() != 32 { - t.Error("CBC-HMAC reports incorrect overhead value") - } -} - -func TestPadding(t *testing.T) { - for i := 0; i < 256; i++ { - slice := make([]byte, i) - padded := padBuffer(slice, 16) - if len(padded)%16 != 0 { - t.Error("failed to pad slice properly", i) - return - } - unpadded, err := unpadBuffer(padded, 16) - if err != nil || len(unpadded) != i { - t.Error("failed to unpad slice properly", i) - return - } - } -} - -func TestInvalidKey(t *testing.T) { - key := make([]byte, 30) - _, err := NewCBCHMAC(key, aes.NewCipher) - if err == nil { - t.Error("should not be able to instantiate CBC-HMAC with invalid key") - } -} - -func TestTruncatedCiphertext(t *testing.T) { - key := make([]byte, 32) - nonce := make([]byte, 16) - data := make([]byte, 32) - - io.ReadFull(rand.Reader, key) - io.ReadFull(rand.Reader, nonce) - - aead, err := NewCBCHMAC(key, aes.NewCipher) - if err != nil { - panic(err) - } - - ctx := aead.(*cbcAEAD) - ct := aead.Seal(nil, nonce, data, nil) - - // Truncated ciphertext, but with correct auth tag - truncated, tail := resize(ct[:len(ct)-ctx.authtagBytes-2], len(ct)-2) - copy(tail, ctx.computeAuthTag(nil, nonce, truncated[:len(truncated)-ctx.authtagBytes])) - - // Open should fail - _, err = aead.Open(nil, nonce, truncated, nil) - if err == nil { - t.Error("open on truncated ciphertext should fail") - } -} - -func TestInvalidPaddingOpen(t *testing.T) { - key := make([]byte, 32) - nonce := make([]byte, 16) - - // Plaintext with invalid padding - plaintext := padBuffer(make([]byte, 28), aes.BlockSize) - plaintext[len(plaintext)-1] = 0xFF - - io.ReadFull(rand.Reader, key) - io.ReadFull(rand.Reader, nonce) - - block, _ := aes.NewCipher(key) - cbc := cipher.NewCBCEncrypter(block, nonce) - buffer := append([]byte{}, plaintext...) - cbc.CryptBlocks(buffer, buffer) - - aead, _ := NewCBCHMAC(key, aes.NewCipher) - ctx := aead.(*cbcAEAD) - - // Mutated ciphertext, but with correct auth tag - size := len(buffer) - ciphertext, tail := resize(buffer, size+(len(key)/2)) - copy(tail, ctx.computeAuthTag(nil, nonce, ciphertext[:size])) - - // Open should fail (b/c of invalid padding, even though tag matches) - _, err := aead.Open(nil, nonce, ciphertext, nil) - if err == nil || !strings.Contains(err.Error(), "invalid padding") { - t.Error("no or unexpected error on open with invalid padding:", err) - } -} - -func TestInvalidPadding(t *testing.T) { - for i := 0; i < 256; i++ { - slice := make([]byte, i) - padded := padBuffer(slice, 16) - if len(padded)%16 != 0 { - t.Error("failed to pad slice properly", i) - return - } - - paddingBytes := 16 - (i % 16) - - // Mutate padding for testing - for j := 1; j <= paddingBytes; j++ { - mutated := make([]byte, len(padded)) - copy(mutated, padded) - mutated[len(mutated)-j] ^= 0xFF - - _, err := unpadBuffer(mutated, 16) - if err == nil { - t.Error("unpad on invalid padding should fail", i) - return - } - } - - // Test truncated padding - _, err := unpadBuffer(padded[:len(padded)-1], 16) - if err == nil { - t.Error("unpad on truncated padding should fail", i) - return - } - } -} - -func TestZeroLengthPadding(t *testing.T) { - data := make([]byte, 16) - data, err := unpadBuffer(data, 16) - if err == nil { - t.Error("padding with 0x00 should never be valid") - } -} - -func benchEncryptCBCHMAC(b *testing.B, keySize, chunkSize int) { - key := make([]byte, keySize*2) - nonce := make([]byte, 16) - - io.ReadFull(rand.Reader, key) - io.ReadFull(rand.Reader, nonce) - - chunk := make([]byte, chunkSize) - - aead, err := NewCBCHMAC(key, aes.NewCipher) - if err != nil { - panic(err) - } - - b.SetBytes(int64(chunkSize)) - b.ResetTimer() - for i := 0; i < b.N; i++ { - aead.Seal(nil, nonce, chunk, nil) - } -} - -func benchDecryptCBCHMAC(b *testing.B, keySize, chunkSize int) { - key := make([]byte, keySize*2) - nonce := make([]byte, 16) - - io.ReadFull(rand.Reader, key) - io.ReadFull(rand.Reader, nonce) - - chunk := make([]byte, chunkSize) - - aead, err := NewCBCHMAC(key, aes.NewCipher) - if err != nil { - panic(err) - } - - out := aead.Seal(nil, nonce, chunk, nil) - - b.SetBytes(int64(chunkSize)) - b.ResetTimer() - for i := 0; i < b.N; i++ { - aead.Open(nil, nonce, out, nil) - } -} - -func BenchmarkEncryptAES128_CBCHMAC_1k(b *testing.B) { - benchEncryptCBCHMAC(b, 16, 1024) -} - -func BenchmarkEncryptAES128_CBCHMAC_64k(b *testing.B) { - benchEncryptCBCHMAC(b, 16, 65536) -} - -func BenchmarkEncryptAES128_CBCHMAC_1MB(b *testing.B) { - benchEncryptCBCHMAC(b, 16, 1048576) -} - -func BenchmarkEncryptAES128_CBCHMAC_64MB(b *testing.B) { - benchEncryptCBCHMAC(b, 16, 67108864) -} - -func BenchmarkDecryptAES128_CBCHMAC_1k(b *testing.B) { - benchDecryptCBCHMAC(b, 16, 1024) -} - -func BenchmarkDecryptAES128_CBCHMAC_64k(b *testing.B) { - benchDecryptCBCHMAC(b, 16, 65536) -} - -func BenchmarkDecryptAES128_CBCHMAC_1MB(b *testing.B) { - benchDecryptCBCHMAC(b, 16, 1048576) -} - -func BenchmarkDecryptAES128_CBCHMAC_64MB(b *testing.B) { - benchDecryptCBCHMAC(b, 16, 67108864) -} - -func BenchmarkEncryptAES192_CBCHMAC_64k(b *testing.B) { - benchEncryptCBCHMAC(b, 24, 65536) -} - -func BenchmarkEncryptAES192_CBCHMAC_1MB(b *testing.B) { - benchEncryptCBCHMAC(b, 24, 1048576) -} - -func BenchmarkEncryptAES192_CBCHMAC_64MB(b *testing.B) { - benchEncryptCBCHMAC(b, 24, 67108864) -} - -func BenchmarkDecryptAES192_CBCHMAC_1k(b *testing.B) { - benchDecryptCBCHMAC(b, 24, 1024) -} - -func BenchmarkDecryptAES192_CBCHMAC_64k(b *testing.B) { - benchDecryptCBCHMAC(b, 24, 65536) -} - -func BenchmarkDecryptAES192_CBCHMAC_1MB(b *testing.B) { - benchDecryptCBCHMAC(b, 24, 1048576) -} - -func BenchmarkDecryptAES192_CBCHMAC_64MB(b *testing.B) { - benchDecryptCBCHMAC(b, 24, 67108864) -} - -func BenchmarkEncryptAES256_CBCHMAC_64k(b *testing.B) { - benchEncryptCBCHMAC(b, 32, 65536) -} - -func BenchmarkEncryptAES256_CBCHMAC_1MB(b *testing.B) { - benchEncryptCBCHMAC(b, 32, 1048576) -} - -func BenchmarkEncryptAES256_CBCHMAC_64MB(b *testing.B) { - benchEncryptCBCHMAC(b, 32, 67108864) -} - -func BenchmarkDecryptAES256_CBCHMAC_1k(b *testing.B) { - benchDecryptCBCHMAC(b, 32, 1032) -} - -func BenchmarkDecryptAES256_CBCHMAC_64k(b *testing.B) { - benchDecryptCBCHMAC(b, 32, 65536) -} - -func BenchmarkDecryptAES256_CBCHMAC_1MB(b *testing.B) { - benchDecryptCBCHMAC(b, 32, 1048576) -} - -func BenchmarkDecryptAES256_CBCHMAC_64MB(b *testing.B) { - benchDecryptCBCHMAC(b, 32, 67108864) -} diff --git a/vendor/gopkg.in/square/go-jose.v1/cipher/concat_kdf.go b/vendor/gopkg.in/square/go-jose.v1/cipher/concat_kdf.go deleted file mode 100644 index cbb5f7b8..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/cipher/concat_kdf.go +++ /dev/null @@ -1,75 +0,0 @@ -/*- - * Copyright 2014 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 josecipher - -import ( - "crypto" - "encoding/binary" - "hash" - "io" -) - -type concatKDF struct { - z, info []byte - i uint32 - cache []byte - hasher hash.Hash -} - -// NewConcatKDF builds a KDF reader based on the given inputs. -func NewConcatKDF(hash crypto.Hash, z, algID, ptyUInfo, ptyVInfo, supPubInfo, supPrivInfo []byte) io.Reader { - buffer := make([]byte, len(algID)+len(ptyUInfo)+len(ptyVInfo)+len(supPubInfo)+len(supPrivInfo)) - n := 0 - n += copy(buffer, algID) - n += copy(buffer[n:], ptyUInfo) - n += copy(buffer[n:], ptyVInfo) - n += copy(buffer[n:], supPubInfo) - copy(buffer[n:], supPrivInfo) - - hasher := hash.New() - - return &concatKDF{ - z: z, - info: buffer, - hasher: hasher, - cache: []byte{}, - i: 1, - } -} - -func (ctx *concatKDF) Read(out []byte) (int, error) { - copied := copy(out, ctx.cache) - ctx.cache = ctx.cache[copied:] - - for copied < len(out) { - ctx.hasher.Reset() - - // Write on a hash.Hash never fails - _ = binary.Write(ctx.hasher, binary.BigEndian, ctx.i) - _, _ = ctx.hasher.Write(ctx.z) - _, _ = ctx.hasher.Write(ctx.info) - - hash := ctx.hasher.Sum(nil) - chunkCopied := copy(out[copied:], hash) - copied += chunkCopied - ctx.cache = hash[chunkCopied:] - - ctx.i++ - } - - return copied, nil -} diff --git a/vendor/gopkg.in/square/go-jose.v1/cipher/concat_kdf_test.go b/vendor/gopkg.in/square/go-jose.v1/cipher/concat_kdf_test.go deleted file mode 100644 index 48219b3e..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/cipher/concat_kdf_test.go +++ /dev/null @@ -1,150 +0,0 @@ -/*- - * Copyright 2014 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 josecipher - -import ( - "bytes" - "crypto" - "testing" -) - -// Taken from: https://tools.ietf.org/id/draft-ietf-jose-json-web-algorithms-38.txt -func TestVectorConcatKDF(t *testing.T) { - z := []byte{ - 158, 86, 217, 29, 129, 113, 53, 211, 114, 131, 66, 131, 191, 132, - 38, 156, 251, 49, 110, 163, 218, 128, 106, 72, 246, 218, 167, 121, - 140, 254, 144, 196} - - algID := []byte{0, 0, 0, 7, 65, 49, 50, 56, 71, 67, 77} - - ptyUInfo := []byte{0, 0, 0, 5, 65, 108, 105, 99, 101} - ptyVInfo := []byte{0, 0, 0, 3, 66, 111, 98} - - supPubInfo := []byte{0, 0, 0, 128} - supPrivInfo := []byte{} - - expected := []byte{ - 86, 170, 141, 234, 248, 35, 109, 32, 92, 34, 40, 205, 113, 167, 16, 26} - - ckdf := NewConcatKDF(crypto.SHA256, z, algID, ptyUInfo, ptyVInfo, supPubInfo, supPrivInfo) - - out0 := make([]byte, 9) - out1 := make([]byte, 7) - - read0, err := ckdf.Read(out0) - if err != nil { - t.Error("error when reading from concat kdf reader", err) - return - } - - read1, err := ckdf.Read(out1) - if err != nil { - t.Error("error when reading from concat kdf reader", err) - return - } - - if read0+read1 != len(out0)+len(out1) { - t.Error("did not receive enough bytes from concat kdf reader") - return - } - - out := []byte{} - out = append(out, out0...) - out = append(out, out1...) - - if bytes.Compare(out, expected) != 0 { - t.Error("did not receive expected output from concat kdf reader") - return - } -} - -func TestCache(t *testing.T) { - z := []byte{ - 158, 86, 217, 29, 129, 113, 53, 211, 114, 131, 66, 131, 191, 132, - 38, 156, 251, 49, 110, 163, 218, 128, 106, 72, 246, 218, 167, 121, - 140, 254, 144, 196} - - algID := []byte{1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4} - - ptyUInfo := []byte{1, 2, 3, 4} - ptyVInfo := []byte{4, 3, 2, 1} - - supPubInfo := []byte{} - supPrivInfo := []byte{} - - outputs := [][]byte{} - - // Read the same amount of data in different chunk sizes - chunkSizes := []int{1, 2, 4, 8, 16, 32, 64, 128, 256, 512} - - for _, c := range chunkSizes { - out := make([]byte, 1024) - reader := NewConcatKDF(crypto.SHA256, z, algID, ptyUInfo, ptyVInfo, supPubInfo, supPrivInfo) - - for i := 0; i < 1024; i += c { - _, _ = reader.Read(out[i : i+c]) - } - - outputs = append(outputs, out) - } - - for i := range outputs { - if bytes.Compare(outputs[i], outputs[(i+1)%len(outputs)]) != 0 { - t.Error("not all outputs from KDF matched") - } - } -} - -func benchmarkKDF(b *testing.B, total int) { - z := []byte{ - 158, 86, 217, 29, 129, 113, 53, 211, 114, 131, 66, 131, 191, 132, - 38, 156, 251, 49, 110, 163, 218, 128, 106, 72, 246, 218, 167, 121, - 140, 254, 144, 196} - - algID := []byte{1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4} - - ptyUInfo := []byte{1, 2, 3, 4} - ptyVInfo := []byte{4, 3, 2, 1} - - supPubInfo := []byte{} - supPrivInfo := []byte{} - - out := make([]byte, total) - reader := NewConcatKDF(crypto.SHA256, z, algID, ptyUInfo, ptyVInfo, supPubInfo, supPrivInfo) - - b.ResetTimer() - b.SetBytes(int64(total)) - for i := 0; i < b.N; i++ { - _, _ = reader.Read(out) - } -} - -func BenchmarkConcatKDF_1k(b *testing.B) { - benchmarkKDF(b, 1024) -} - -func BenchmarkConcatKDF_64k(b *testing.B) { - benchmarkKDF(b, 65536) -} - -func BenchmarkConcatKDF_1MB(b *testing.B) { - benchmarkKDF(b, 1048576) -} - -func BenchmarkConcatKDF_64MB(b *testing.B) { - benchmarkKDF(b, 67108864) -} diff --git a/vendor/gopkg.in/square/go-jose.v1/cipher/ecdh_es.go b/vendor/gopkg.in/square/go-jose.v1/cipher/ecdh_es.go deleted file mode 100644 index c6a5a821..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/cipher/ecdh_es.go +++ /dev/null @@ -1,51 +0,0 @@ -/*- - * Copyright 2014 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 josecipher - -import ( - "crypto" - "crypto/ecdsa" - "encoding/binary" -) - -// DeriveECDHES derives a shared encryption key using ECDH/ConcatKDF as described in JWE/JWA. -func DeriveECDHES(alg string, apuData, apvData []byte, priv *ecdsa.PrivateKey, pub *ecdsa.PublicKey, size int) []byte { - // algId, partyUInfo, partyVInfo inputs must be prefixed with the length - algID := lengthPrefixed([]byte(alg)) - ptyUInfo := lengthPrefixed(apuData) - ptyVInfo := lengthPrefixed(apvData) - - // suppPubInfo is the encoded length of the output size in bits - supPubInfo := make([]byte, 4) - binary.BigEndian.PutUint32(supPubInfo, uint32(size)*8) - - z, _ := priv.PublicKey.Curve.ScalarMult(pub.X, pub.Y, priv.D.Bytes()) - reader := NewConcatKDF(crypto.SHA256, z.Bytes(), algID, ptyUInfo, ptyVInfo, supPubInfo, []byte{}) - - key := make([]byte, size) - - // Read on the KDF will never fail - _, _ = reader.Read(key) - return key -} - -func lengthPrefixed(data []byte) []byte { - out := make([]byte, len(data)+4) - binary.BigEndian.PutUint32(out, uint32(len(data))) - copy(out[4:], data) - return out -} diff --git a/vendor/gopkg.in/square/go-jose.v1/cipher/ecdh_es_test.go b/vendor/gopkg.in/square/go-jose.v1/cipher/ecdh_es_test.go deleted file mode 100644 index f92abb17..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/cipher/ecdh_es_test.go +++ /dev/null @@ -1,98 +0,0 @@ -/*- - * Copyright 2014 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 josecipher - -import ( - "bytes" - "crypto/ecdsa" - "crypto/elliptic" - "encoding/base64" - "math/big" - "testing" -) - -// Example keys from JWA, Appendix C -var aliceKey = &ecdsa.PrivateKey{ - PublicKey: ecdsa.PublicKey{ - Curve: elliptic.P256(), - X: fromBase64Int("gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0="), - Y: fromBase64Int("SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps="), - }, - D: fromBase64Int("0_NxaRPUMQoAJt50Gz8YiTr8gRTwyEaCumd-MToTmIo="), -} - -var bobKey = &ecdsa.PrivateKey{ - PublicKey: ecdsa.PublicKey{ - Curve: elliptic.P256(), - X: fromBase64Int("weNJy2HscCSM6AEDTDg04biOvhFhyyWvOHQfeF_PxMQ="), - Y: fromBase64Int("e8lnCO-AlStT-NJVX-crhB7QRYhiix03illJOVAOyck="), - }, - D: fromBase64Int("VEmDZpDXXK8p8N0Cndsxs924q6nS1RXFASRl6BfUqdw="), -} - -// Build big int from base64-encoded string. Strips whitespace (for testing). -func fromBase64Int(data string) *big.Int { - val, err := base64.URLEncoding.DecodeString(data) - if err != nil { - panic("Invalid test data") - } - return new(big.Int).SetBytes(val) -} - -func TestVectorECDHES(t *testing.T) { - apuData := []byte("Alice") - apvData := []byte("Bob") - - expected := []byte{ - 86, 170, 141, 234, 248, 35, 109, 32, 92, 34, 40, 205, 113, 167, 16, 26} - - output := DeriveECDHES("A128GCM", apuData, apvData, bobKey, &aliceKey.PublicKey, 16) - - if bytes.Compare(output, expected) != 0 { - t.Error("output did not match what we expect, got", output, "wanted", expected) - } -} - -func BenchmarkECDHES_128(b *testing.B) { - apuData := []byte("APU") - apvData := []byte("APV") - - b.ResetTimer() - for i := 0; i < b.N; i++ { - DeriveECDHES("ID", apuData, apvData, bobKey, &aliceKey.PublicKey, 16) - } -} - -func BenchmarkECDHES_192(b *testing.B) { - apuData := []byte("APU") - apvData := []byte("APV") - - b.ResetTimer() - for i := 0; i < b.N; i++ { - DeriveECDHES("ID", apuData, apvData, bobKey, &aliceKey.PublicKey, 24) - } -} - -func BenchmarkECDHES_256(b *testing.B) { - apuData := []byte("APU") - apvData := []byte("APV") - - b.ResetTimer() - for i := 0; i < b.N; i++ { - DeriveECDHES("ID", apuData, apvData, bobKey, &aliceKey.PublicKey, 32) - } -} diff --git a/vendor/gopkg.in/square/go-jose.v1/cipher/key_wrap.go b/vendor/gopkg.in/square/go-jose.v1/cipher/key_wrap.go deleted file mode 100644 index 1d36d501..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/cipher/key_wrap.go +++ /dev/null @@ -1,109 +0,0 @@ -/*- - * Copyright 2014 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 josecipher - -import ( - "crypto/cipher" - "crypto/subtle" - "encoding/binary" - "errors" -) - -var defaultIV = []byte{0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6} - -// KeyWrap implements NIST key wrapping; it wraps a content encryption key (cek) with the given block cipher. -func KeyWrap(block cipher.Block, cek []byte) ([]byte, error) { - if len(cek)%8 != 0 { - return nil, errors.New("square/go-jose: key wrap input must be 8 byte blocks") - } - - n := len(cek) / 8 - r := make([][]byte, n) - - for i := range r { - r[i] = make([]byte, 8) - copy(r[i], cek[i*8:]) - } - - buffer := make([]byte, 16) - tBytes := make([]byte, 8) - copy(buffer, defaultIV) - - for t := 0; t < 6*n; t++ { - copy(buffer[8:], r[t%n]) - - block.Encrypt(buffer, buffer) - - binary.BigEndian.PutUint64(tBytes, uint64(t+1)) - - for i := 0; i < 8; i++ { - buffer[i] = buffer[i] ^ tBytes[i] - } - copy(r[t%n], buffer[8:]) - } - - out := make([]byte, (n+1)*8) - copy(out, buffer[:8]) - for i := range r { - copy(out[(i+1)*8:], r[i]) - } - - return out, nil -} - -// KeyUnwrap implements NIST key unwrapping; it unwraps a content encryption key (cek) with the given block cipher. -func KeyUnwrap(block cipher.Block, ciphertext []byte) ([]byte, error) { - if len(ciphertext)%8 != 0 { - return nil, errors.New("square/go-jose: key wrap input must be 8 byte blocks") - } - - n := (len(ciphertext) / 8) - 1 - r := make([][]byte, n) - - for i := range r { - r[i] = make([]byte, 8) - copy(r[i], ciphertext[(i+1)*8:]) - } - - buffer := make([]byte, 16) - tBytes := make([]byte, 8) - copy(buffer[:8], ciphertext[:8]) - - for t := 6*n - 1; t >= 0; t-- { - binary.BigEndian.PutUint64(tBytes, uint64(t+1)) - - for i := 0; i < 8; i++ { - buffer[i] = buffer[i] ^ tBytes[i] - } - copy(buffer[8:], r[t%n]) - - block.Decrypt(buffer, buffer) - - copy(r[t%n], buffer[8:]) - } - - if subtle.ConstantTimeCompare(buffer[:8], defaultIV) == 0 { - return nil, errors.New("square/go-jose: failed to unwrap key") - } - - out := make([]byte, n*8) - for i := range r { - copy(out[i*8:], r[i]) - } - - return out, nil -} diff --git a/vendor/gopkg.in/square/go-jose.v1/cipher/key_wrap_test.go b/vendor/gopkg.in/square/go-jose.v1/cipher/key_wrap_test.go deleted file mode 100644 index ceecf812..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/cipher/key_wrap_test.go +++ /dev/null @@ -1,133 +0,0 @@ -/*- - * Copyright 2014 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 josecipher - -import ( - "bytes" - "crypto/aes" - "encoding/hex" - "testing" -) - -func TestAesKeyWrap(t *testing.T) { - // Test vectors from: http://csrc.nist.gov/groups/ST/toolkit/documents/kms/key-wrap.pdf - kek0, _ := hex.DecodeString("000102030405060708090A0B0C0D0E0F") - cek0, _ := hex.DecodeString("00112233445566778899AABBCCDDEEFF") - - expected0, _ := hex.DecodeString("1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5") - - kek1, _ := hex.DecodeString("000102030405060708090A0B0C0D0E0F1011121314151617") - cek1, _ := hex.DecodeString("00112233445566778899AABBCCDDEEFF") - - expected1, _ := hex.DecodeString("96778B25AE6CA435F92B5B97C050AED2468AB8A17AD84E5D") - - kek2, _ := hex.DecodeString("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F") - cek2, _ := hex.DecodeString("00112233445566778899AABBCCDDEEFF0001020304050607") - - expected2, _ := hex.DecodeString("A8F9BC1612C68B3FF6E6F4FBE30E71E4769C8B80A32CB8958CD5D17D6B254DA1") - - block0, _ := aes.NewCipher(kek0) - block1, _ := aes.NewCipher(kek1) - block2, _ := aes.NewCipher(kek2) - - out0, _ := KeyWrap(block0, cek0) - out1, _ := KeyWrap(block1, cek1) - out2, _ := KeyWrap(block2, cek2) - - if bytes.Compare(out0, expected0) != 0 { - t.Error("output 0 not as expected, got", out0, "wanted", expected0) - } - - if bytes.Compare(out1, expected1) != 0 { - t.Error("output 1 not as expected, got", out1, "wanted", expected1) - } - - if bytes.Compare(out2, expected2) != 0 { - t.Error("output 2 not as expected, got", out2, "wanted", expected2) - } - - unwrap0, _ := KeyUnwrap(block0, out0) - unwrap1, _ := KeyUnwrap(block1, out1) - unwrap2, _ := KeyUnwrap(block2, out2) - - if bytes.Compare(unwrap0, cek0) != 0 { - t.Error("key unwrap did not return original input, got", unwrap0, "wanted", cek0) - } - - if bytes.Compare(unwrap1, cek1) != 0 { - t.Error("key unwrap did not return original input, got", unwrap1, "wanted", cek1) - } - - if bytes.Compare(unwrap2, cek2) != 0 { - t.Error("key unwrap did not return original input, got", unwrap2, "wanted", cek2) - } -} - -func TestAesKeyWrapInvalid(t *testing.T) { - kek, _ := hex.DecodeString("000102030405060708090A0B0C0D0E0F") - - // Invalid unwrap input (bit flipped) - input0, _ := hex.DecodeString("1EA68C1A8112B447AEF34BD8FB5A7B828D3E862371D2CFE5") - - block, _ := aes.NewCipher(kek) - - _, err := KeyUnwrap(block, input0) - if err == nil { - t.Error("key unwrap failed to detect invalid input") - } - - // Invalid unwrap input (truncated) - input1, _ := hex.DecodeString("1EA68C1A8112B447AEF34BD8FB5A7B828D3E862371D2CF") - - _, err = KeyUnwrap(block, input1) - if err == nil { - t.Error("key unwrap failed to detect truncated input") - } - - // Invalid wrap input (not multiple of 8) - input2, _ := hex.DecodeString("0123456789ABCD") - - _, err = KeyWrap(block, input2) - if err == nil { - t.Error("key wrap accepted invalid input") - } - -} - -func BenchmarkAesKeyWrap(b *testing.B) { - kek, _ := hex.DecodeString("000102030405060708090A0B0C0D0E0F") - key, _ := hex.DecodeString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") - - block, _ := aes.NewCipher(kek) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - KeyWrap(block, key) - } -} - -func BenchmarkAesKeyUnwrap(b *testing.B) { - kek, _ := hex.DecodeString("000102030405060708090A0B0C0D0E0F") - input, _ := hex.DecodeString("1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5") - - block, _ := aes.NewCipher(kek) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - KeyUnwrap(block, input) - } -} diff --git a/vendor/gopkg.in/square/go-jose.v1/crypter.go b/vendor/gopkg.in/square/go-jose.v1/crypter.go deleted file mode 100644 index f61af2c0..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/crypter.go +++ /dev/null @@ -1,349 +0,0 @@ -/*- - * Copyright 2014 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 jose - -import ( - "crypto/ecdsa" - "crypto/rsa" - "fmt" - "reflect" -) - -// Encrypter represents an encrypter which produces an encrypted JWE object. -type Encrypter interface { - Encrypt(plaintext []byte) (*JsonWebEncryption, error) - EncryptWithAuthData(plaintext []byte, aad []byte) (*JsonWebEncryption, error) - SetCompression(alg CompressionAlgorithm) -} - -// MultiEncrypter represents an encrypter which supports multiple recipients. -type MultiEncrypter interface { - Encrypt(plaintext []byte) (*JsonWebEncryption, error) - EncryptWithAuthData(plaintext []byte, aad []byte) (*JsonWebEncryption, error) - SetCompression(alg CompressionAlgorithm) - AddRecipient(alg KeyAlgorithm, encryptionKey interface{}) error -} - -// A generic content cipher -type contentCipher interface { - keySize() int - encrypt(cek []byte, aad, plaintext []byte) (*aeadParts, error) - decrypt(cek []byte, aad []byte, parts *aeadParts) ([]byte, error) -} - -// A key generator (for generating/getting a CEK) -type keyGenerator interface { - keySize() int - genKey() ([]byte, rawHeader, error) -} - -// A generic key encrypter -type keyEncrypter interface { - encryptKey(cek []byte, alg KeyAlgorithm) (recipientInfo, error) // Encrypt a key -} - -// A generic key decrypter -type keyDecrypter interface { - decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) // Decrypt a key -} - -// A generic encrypter based on the given key encrypter and content cipher. -type genericEncrypter struct { - contentAlg ContentEncryption - compressionAlg CompressionAlgorithm - cipher contentCipher - recipients []recipientKeyInfo - keyGenerator keyGenerator -} - -type recipientKeyInfo struct { - keyID string - keyAlg KeyAlgorithm - keyEncrypter keyEncrypter -} - -// SetCompression sets a compression algorithm to be applied before encryption. -func (ctx *genericEncrypter) SetCompression(compressionAlg CompressionAlgorithm) { - ctx.compressionAlg = compressionAlg -} - -// NewEncrypter creates an appropriate encrypter based on the key type -func NewEncrypter(alg KeyAlgorithm, enc ContentEncryption, encryptionKey interface{}) (Encrypter, error) { - encrypter := &genericEncrypter{ - contentAlg: enc, - compressionAlg: NONE, - recipients: []recipientKeyInfo{}, - cipher: getContentCipher(enc), - } - - if encrypter.cipher == nil { - return nil, ErrUnsupportedAlgorithm - } - - var keyID string - var rawKey interface{} - switch encryptionKey := encryptionKey.(type) { - case *JsonWebKey: - keyID = encryptionKey.KeyID - rawKey = encryptionKey.Key - default: - rawKey = encryptionKey - } - - switch alg { - case DIRECT: - // Direct encryption mode must be treated differently - if reflect.TypeOf(rawKey) != reflect.TypeOf([]byte{}) { - return nil, ErrUnsupportedKeyType - } - encrypter.keyGenerator = staticKeyGenerator{ - key: rawKey.([]byte), - } - recipient, _ := newSymmetricRecipient(alg, rawKey.([]byte)) - if keyID != "" { - recipient.keyID = keyID - } - encrypter.recipients = []recipientKeyInfo{recipient} - 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{}) { - return nil, ErrUnsupportedKeyType - } - encrypter.keyGenerator = ecKeyGenerator{ - size: encrypter.cipher.keySize(), - algID: string(enc), - publicKey: rawKey.(*ecdsa.PublicKey), - } - recipient, _ := newECDHRecipient(alg, rawKey.(*ecdsa.PublicKey)) - if keyID != "" { - recipient.keyID = keyID - } - encrypter.recipients = []recipientKeyInfo{recipient} - return encrypter, nil - default: - // Can just add a standard recipient - encrypter.keyGenerator = randomKeyGenerator{ - size: encrypter.cipher.keySize(), - } - err := encrypter.AddRecipient(alg, encryptionKey) - return encrypter, err - } -} - -// NewMultiEncrypter creates a multi-encrypter based on the given parameters -func NewMultiEncrypter(enc ContentEncryption) (MultiEncrypter, error) { - cipher := getContentCipher(enc) - - if cipher == nil { - return nil, ErrUnsupportedAlgorithm - } - - encrypter := &genericEncrypter{ - contentAlg: enc, - compressionAlg: NONE, - recipients: []recipientKeyInfo{}, - cipher: cipher, - keyGenerator: randomKeyGenerator{ - size: cipher.keySize(), - }, - } - - return encrypter, nil -} - -func (ctx *genericEncrypter) AddRecipient(alg KeyAlgorithm, encryptionKey interface{}) (err error) { - var recipient recipientKeyInfo - - switch alg { - case DIRECT, ECDH_ES: - return fmt.Errorf("square/go-jose: key algorithm '%s' not supported in multi-recipient mode", alg) - } - - recipient, err = makeJWERecipient(alg, encryptionKey) - - if err == nil { - ctx.recipients = append(ctx.recipients, recipient) - } - return err -} - -func makeJWERecipient(alg KeyAlgorithm, encryptionKey interface{}) (recipientKeyInfo, error) { - switch encryptionKey := encryptionKey.(type) { - case *rsa.PublicKey: - return newRSARecipient(alg, encryptionKey) - case *ecdsa.PublicKey: - return newECDHRecipient(alg, encryptionKey) - case []byte: - return newSymmetricRecipient(alg, encryptionKey) - case *JsonWebKey: - recipient, err := makeJWERecipient(alg, encryptionKey.Key) - if err == nil && encryptionKey.KeyID != "" { - recipient.keyID = encryptionKey.KeyID - } - return recipient, err - default: - return recipientKeyInfo{}, ErrUnsupportedKeyType - } -} - -// newDecrypter creates an appropriate decrypter based on the key type -func newDecrypter(decryptionKey interface{}) (keyDecrypter, error) { - switch decryptionKey := decryptionKey.(type) { - case *rsa.PrivateKey: - return &rsaDecrypterSigner{ - privateKey: decryptionKey, - }, nil - case *ecdsa.PrivateKey: - return &ecDecrypterSigner{ - privateKey: decryptionKey, - }, nil - case []byte: - return &symmetricKeyCipher{ - key: decryptionKey, - }, nil - case *JsonWebKey: - return newDecrypter(decryptionKey.Key) - default: - return nil, ErrUnsupportedKeyType - } -} - -// Implementation of encrypt method producing a JWE object. -func (ctx *genericEncrypter) Encrypt(plaintext []byte) (*JsonWebEncryption, error) { - return ctx.EncryptWithAuthData(plaintext, nil) -} - -// Implementation of encrypt method producing a JWE object. -func (ctx *genericEncrypter) EncryptWithAuthData(plaintext, aad []byte) (*JsonWebEncryption, error) { - obj := &JsonWebEncryption{} - obj.aad = aad - - obj.protected = &rawHeader{ - Enc: ctx.contentAlg, - } - obj.recipients = make([]recipientInfo, len(ctx.recipients)) - - if len(ctx.recipients) == 0 { - return nil, fmt.Errorf("square/go-jose: no recipients to encrypt to") - } - - cek, headers, err := ctx.keyGenerator.genKey() - if err != nil { - return nil, err - } - - obj.protected.merge(&headers) - - for i, info := range ctx.recipients { - recipient, err := info.keyEncrypter.encryptKey(cek, info.keyAlg) - if err != nil { - return nil, err - } - - recipient.header.Alg = string(info.keyAlg) - if info.keyID != "" { - recipient.header.Kid = info.keyID - } - obj.recipients[i] = recipient - } - - if len(ctx.recipients) == 1 { - // Move per-recipient headers into main protected header if there's - // only a single recipient. - obj.protected.merge(obj.recipients[0].header) - obj.recipients[0].header = nil - } - - if ctx.compressionAlg != NONE { - plaintext, err = compress(ctx.compressionAlg, plaintext) - if err != nil { - return nil, err - } - - obj.protected.Zip = ctx.compressionAlg - } - - authData := obj.computeAuthData() - parts, err := ctx.cipher.encrypt(cek, authData, plaintext) - if err != nil { - return nil, err - } - - obj.iv = parts.iv - obj.ciphertext = parts.ciphertext - obj.tag = parts.tag - - return obj, nil -} - -// Decrypt and validate the object and return the plaintext. -func (obj JsonWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error) { - headers := obj.mergedHeaders(nil) - - if len(headers.Crit) > 0 { - return nil, fmt.Errorf("square/go-jose: unsupported crit header") - } - - decrypter, err := newDecrypter(decryptionKey) - if err != nil { - return nil, err - } - - cipher := getContentCipher(headers.Enc) - if cipher == nil { - return nil, fmt.Errorf("square/go-jose: unsupported enc value '%s'", string(headers.Enc)) - } - - generator := randomKeyGenerator{ - size: cipher.keySize(), - } - - parts := &aeadParts{ - iv: obj.iv, - ciphertext: obj.ciphertext, - tag: obj.tag, - } - - authData := obj.computeAuthData() - - var plaintext []byte - for _, recipient := range obj.recipients { - recipientHeaders := obj.mergedHeaders(&recipient) - - cek, err := decrypter.decryptKey(recipientHeaders, &recipient, generator) - if err == nil { - // Found a valid CEK -- let's try to decrypt. - plaintext, err = cipher.decrypt(cek, authData, parts) - if err == nil { - break - } - } - } - - if plaintext == nil { - return nil, ErrCryptoFailure - } - - // The "zip" header paramter may only be present in the protected header. - if obj.protected.Zip != "" { - plaintext, err = decompress(obj.protected.Zip, plaintext) - } - - return plaintext, err -} diff --git a/vendor/gopkg.in/square/go-jose.v1/crypter_test.go b/vendor/gopkg.in/square/go-jose.v1/crypter_test.go deleted file mode 100644 index 86b8fc0a..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/crypter_test.go +++ /dev/null @@ -1,784 +0,0 @@ -/*- - * Copyright 2014 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 jose - -import ( - "bytes" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/rsa" - "fmt" - "io" - "testing" -) - -// We generate only a single RSA and EC key for testing, speeds up tests. -var rsaTestKey, _ = rsa.GenerateKey(rand.Reader, 2048) - -var ecTestKey256, _ = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) -var ecTestKey384, _ = ecdsa.GenerateKey(elliptic.P384(), rand.Reader) -var ecTestKey521, _ = ecdsa.GenerateKey(elliptic.P521(), rand.Reader) - -func RoundtripJWE(keyAlg KeyAlgorithm, encAlg ContentEncryption, compressionAlg CompressionAlgorithm, serializer func(*JsonWebEncryption) (string, error), corrupter func(*JsonWebEncryption) bool, aad []byte, encryptionKey interface{}, decryptionKey interface{}) error { - enc, err := NewEncrypter(keyAlg, encAlg, encryptionKey) - if err != nil { - return fmt.Errorf("error on new encrypter: %s", err) - } - - enc.SetCompression(compressionAlg) - - input := []byte("Lorem ipsum dolor sit amet") - obj, err := enc.EncryptWithAuthData(input, aad) - if err != nil { - return fmt.Errorf("error in encrypt: %s", err) - } - - msg, err := serializer(obj) - if err != nil { - return fmt.Errorf("error in serializer: %s", err) - } - - parsed, err := ParseEncrypted(msg) - if err != nil { - return fmt.Errorf("error in parse: %s, on msg '%s'", err, msg) - } - - // (Maybe) mangle object - skip := corrupter(parsed) - if skip { - return fmt.Errorf("corrupter indicated message should be skipped") - } - - if bytes.Compare(parsed.GetAuthData(), aad) != 0 { - return fmt.Errorf("auth data in parsed object does not match") - } - - output, err := parsed.Decrypt(decryptionKey) - if err != nil { - return fmt.Errorf("error on decrypt: %s", err) - } - - if bytes.Compare(input, output) != 0 { - return fmt.Errorf("Decrypted output does not match input, got '%s' but wanted '%s'", output, input) - } - - return nil -} - -func TestRoundtripsJWE(t *testing.T) { - // Test matrix - keyAlgs := []KeyAlgorithm{ - DIRECT, ECDH_ES, ECDH_ES_A128KW, ECDH_ES_A192KW, ECDH_ES_A256KW, A128KW, A192KW, A256KW, - RSA1_5, RSA_OAEP, RSA_OAEP_256, A128GCMKW, A192GCMKW, A256GCMKW} - encAlgs := []ContentEncryption{A128GCM, A192GCM, A256GCM, A128CBC_HS256, A192CBC_HS384, A256CBC_HS512} - zipAlgs := []CompressionAlgorithm{NONE, DEFLATE} - - serializers := []func(*JsonWebEncryption) (string, error){ - func(obj *JsonWebEncryption) (string, error) { return obj.CompactSerialize() }, - func(obj *JsonWebEncryption) (string, error) { return obj.FullSerialize(), nil }, - } - - corrupter := func(obj *JsonWebEncryption) bool { return false } - - // Note: can't use AAD with compact serialization - aads := [][]byte{ - nil, - []byte("Ut enim ad minim veniam"), - } - - // Test all different configurations - for _, alg := range keyAlgs { - for _, enc := range encAlgs { - for _, key := range generateTestKeys(alg, enc) { - for _, zip := range zipAlgs { - for i, serializer := range serializers { - err := RoundtripJWE(alg, enc, zip, serializer, corrupter, aads[i], key.enc, key.dec) - if err != nil { - t.Error(err, alg, enc, zip, i) - } - } - } - } - } - } -} - -func TestRoundtripsJWECorrupted(t *testing.T) { - // Test matrix - keyAlgs := []KeyAlgorithm{DIRECT, ECDH_ES, ECDH_ES_A128KW, A128KW, RSA1_5, RSA_OAEP, RSA_OAEP_256, A128GCMKW} - encAlgs := []ContentEncryption{A128GCM, A192GCM, A256GCM, A128CBC_HS256, A192CBC_HS384, A256CBC_HS512} - zipAlgs := []CompressionAlgorithm{NONE, DEFLATE} - - serializers := []func(*JsonWebEncryption) (string, error){ - func(obj *JsonWebEncryption) (string, error) { return obj.CompactSerialize() }, - func(obj *JsonWebEncryption) (string, error) { return obj.FullSerialize(), nil }, - } - - bitflip := func(slice []byte) bool { - if len(slice) > 0 { - slice[0] ^= 0xFF - return false - } - return true - } - - corrupters := []func(*JsonWebEncryption) bool{ - func(obj *JsonWebEncryption) bool { - // Set invalid ciphertext - return bitflip(obj.ciphertext) - }, - func(obj *JsonWebEncryption) bool { - // Set invalid auth tag - return bitflip(obj.tag) - }, - func(obj *JsonWebEncryption) bool { - // Set invalid AAD - return bitflip(obj.aad) - }, - func(obj *JsonWebEncryption) bool { - // Mess with encrypted key - return bitflip(obj.recipients[0].encryptedKey) - }, - func(obj *JsonWebEncryption) bool { - // Mess with GCM-KW auth tag - return bitflip(obj.protected.Tag.bytes()) - }, - } - - // Note: can't use AAD with compact serialization - aads := [][]byte{ - nil, - []byte("Ut enim ad minim veniam"), - } - - // Test all different configurations - for _, alg := range keyAlgs { - for _, enc := range encAlgs { - for _, key := range generateTestKeys(alg, enc) { - for _, zip := range zipAlgs { - for i, serializer := range serializers { - for j, corrupter := range corrupters { - err := RoundtripJWE(alg, enc, zip, serializer, corrupter, aads[i], key.enc, key.dec) - if err == nil { - t.Error("failed to detect corrupt data", err, alg, enc, zip, i, j) - } - } - } - } - } - } - } -} - -func TestEncrypterWithJWKAndKeyID(t *testing.T) { - enc, err := NewEncrypter(A128KW, A128GCM, &JsonWebKey{ - KeyID: "test-id", - Key: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, - }) - if err != nil { - t.Error(err) - } - - ciphertext, _ := enc.Encrypt([]byte("Lorem ipsum dolor sit amet")) - - serialized1, _ := ciphertext.CompactSerialize() - serialized2 := ciphertext.FullSerialize() - - parsed1, _ := ParseEncrypted(serialized1) - parsed2, _ := ParseEncrypted(serialized2) - - if parsed1.Header.KeyID != "test-id" { - t.Errorf("expected message to have key id from JWK, but found '%s' instead", parsed1.Header.KeyID) - } - if parsed2.Header.KeyID != "test-id" { - t.Errorf("expected message to have key id from JWK, but found '%s' instead", parsed2.Header.KeyID) - } -} - -func TestEncrypterWithBrokenRand(t *testing.T) { - keyAlgs := []KeyAlgorithm{ECDH_ES_A128KW, A128KW, RSA1_5, RSA_OAEP, RSA_OAEP_256, A128GCMKW} - encAlgs := []ContentEncryption{A128GCM, A192GCM, A256GCM, A128CBC_HS256, A192CBC_HS384, A256CBC_HS512} - - serializer := func(obj *JsonWebEncryption) (string, error) { return obj.CompactSerialize() } - corrupter := func(obj *JsonWebEncryption) bool { return false } - - // Break rand reader - readers := []func() io.Reader{ - // Totally broken - func() io.Reader { return bytes.NewReader([]byte{}) }, - // Not enough bytes - func() io.Reader { return io.LimitReader(rand.Reader, 20) }, - } - - defer resetRandReader() - - for _, alg := range keyAlgs { - for _, enc := range encAlgs { - for _, key := range generateTestKeys(alg, enc) { - for i, getReader := range readers { - randReader = getReader() - err := RoundtripJWE(alg, enc, NONE, serializer, corrupter, nil, key.enc, key.dec) - if err == nil { - t.Error("encrypter should fail if rand is broken", i) - } - } - } - } - } -} - -func TestNewEncrypterErrors(t *testing.T) { - _, err := NewEncrypter("XYZ", "XYZ", nil) - if err == nil { - t.Error("was able to instantiate encrypter with invalid cipher") - } - - _, err = NewMultiEncrypter("XYZ") - if err == nil { - t.Error("was able to instantiate multi-encrypter with invalid cipher") - } - - _, err = NewEncrypter(DIRECT, A128GCM, nil) - if err == nil { - t.Error("was able to instantiate encrypter with invalid direct key") - } - - _, err = NewEncrypter(ECDH_ES, A128GCM, nil) - if err == nil { - t.Error("was able to instantiate encrypter with invalid EC key") - } -} - -func TestMultiRecipientJWE(t *testing.T) { - enc, err := NewMultiEncrypter(A128GCM) - if err != nil { - panic(err) - } - - err = enc.AddRecipient(RSA_OAEP, &rsaTestKey.PublicKey) - if err != nil { - t.Error("error when adding RSA recipient", err) - } - - sharedKey := []byte{ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - } - - err = enc.AddRecipient(A256GCMKW, sharedKey) - if err != nil { - t.Error("error when adding AES recipient: ", err) - return - } - - input := []byte("Lorem ipsum dolor sit amet") - obj, err := enc.Encrypt(input) - if err != nil { - t.Error("error in encrypt: ", err) - return - } - - msg := obj.FullSerialize() - - parsed, err := ParseEncrypted(msg) - if err != nil { - t.Error("error in parse: ", err) - return - } - - output, err := parsed.Decrypt(rsaTestKey) - if err != nil { - t.Error("error on decrypt with RSA: ", err) - return - } - - if bytes.Compare(input, output) != 0 { - t.Error("Decrypted output does not match input: ", output, input) - return - } - - output, err = parsed.Decrypt(sharedKey) - if err != nil { - t.Error("error on decrypt with AES: ", err) - return - } - - if bytes.Compare(input, output) != 0 { - t.Error("Decrypted output does not match input", output, input) - return - } -} - -func TestMultiRecipientErrors(t *testing.T) { - enc, err := NewMultiEncrypter(A128GCM) - if err != nil { - panic(err) - } - - input := []byte("Lorem ipsum dolor sit amet") - _, err = enc.Encrypt(input) - if err == nil { - t.Error("should fail when encrypting to zero recipients") - } - - err = enc.AddRecipient(DIRECT, nil) - if err == nil { - t.Error("should reject DIRECT mode when encrypting to multiple recipients") - } - - err = enc.AddRecipient(ECDH_ES, nil) - if err == nil { - t.Error("should reject ECDH_ES mode when encrypting to multiple recipients") - } - - err = enc.AddRecipient(RSA1_5, nil) - if err == nil { - t.Error("should reject invalid recipient key") - } -} - -type testKey struct { - enc, dec interface{} -} - -func symmetricTestKey(size int) []testKey { - key, _, _ := randomKeyGenerator{size: size}.genKey() - - return []testKey{ - testKey{ - enc: key, - dec: key, - }, - testKey{ - enc: &JsonWebKey{KeyID: "test", Key: key}, - dec: &JsonWebKey{KeyID: "test", Key: key}, - }, - } -} - -func generateTestKeys(keyAlg KeyAlgorithm, encAlg ContentEncryption) []testKey { - switch keyAlg { - case DIRECT: - return symmetricTestKey(getContentCipher(encAlg).keySize()) - case ECDH_ES, ECDH_ES_A128KW, ECDH_ES_A192KW, ECDH_ES_A256KW: - return []testKey{ - testKey{ - dec: ecTestKey256, - enc: &ecTestKey256.PublicKey, - }, - testKey{ - dec: ecTestKey384, - enc: &ecTestKey384.PublicKey, - }, - testKey{ - dec: ecTestKey521, - enc: &ecTestKey521.PublicKey, - }, - testKey{ - dec: &JsonWebKey{KeyID: "test", Key: ecTestKey256}, - enc: &JsonWebKey{KeyID: "test", Key: &ecTestKey256.PublicKey}, - }, - } - case A128GCMKW, A128KW: - return symmetricTestKey(16) - case A192GCMKW, A192KW: - return symmetricTestKey(24) - case A256GCMKW, A256KW: - return symmetricTestKey(32) - case RSA1_5, RSA_OAEP, RSA_OAEP_256: - return []testKey{testKey{ - dec: rsaTestKey, - enc: &rsaTestKey.PublicKey, - }} - } - - panic("Must update test case") -} - -func RunRoundtripsJWE(b *testing.B, alg KeyAlgorithm, enc ContentEncryption, zip CompressionAlgorithm, priv, pub interface{}) { - serializer := func(obj *JsonWebEncryption) (string, error) { - return obj.CompactSerialize() - } - - corrupter := func(obj *JsonWebEncryption) bool { return false } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - err := RoundtripJWE(alg, enc, zip, serializer, corrupter, nil, pub, priv) - if err != nil { - b.Error(err) - } - } -} - -var ( - chunks = map[string][]byte{ - "1B": make([]byte, 1), - "64B": make([]byte, 64), - "1KB": make([]byte, 1024), - "64KB": make([]byte, 65536), - "1MB": make([]byte, 1048576), - "64MB": make([]byte, 67108864), - } - - symKey, _, _ = randomKeyGenerator{size: 32}.genKey() - - encrypters = map[string]Encrypter{ - "OAEPAndGCM": mustEncrypter(RSA_OAEP, A128GCM, &rsaTestKey.PublicKey), - "PKCSAndGCM": mustEncrypter(RSA1_5, A128GCM, &rsaTestKey.PublicKey), - "OAEPAndCBC": mustEncrypter(RSA_OAEP, A128CBC_HS256, &rsaTestKey.PublicKey), - "PKCSAndCBC": mustEncrypter(RSA1_5, A128CBC_HS256, &rsaTestKey.PublicKey), - "DirectGCM128": mustEncrypter(DIRECT, A128GCM, symKey), - "DirectCBC128": mustEncrypter(DIRECT, A128CBC_HS256, symKey), - "DirectGCM256": mustEncrypter(DIRECT, A256GCM, symKey), - "DirectCBC256": mustEncrypter(DIRECT, A256CBC_HS512, symKey), - "AESKWAndGCM128": mustEncrypter(A128KW, A128GCM, symKey), - "AESKWAndCBC256": mustEncrypter(A256KW, A256GCM, symKey), - "ECDHOnP256AndGCM128": mustEncrypter(ECDH_ES, A128GCM, &ecTestKey256.PublicKey), - "ECDHOnP384AndGCM128": mustEncrypter(ECDH_ES, A128GCM, &ecTestKey384.PublicKey), - "ECDHOnP521AndGCM128": mustEncrypter(ECDH_ES, A128GCM, &ecTestKey521.PublicKey), - } -) - -func BenchmarkEncrypt1BWithOAEPAndGCM(b *testing.B) { benchEncrypt("1B", "OAEPAndGCM", b) } -func BenchmarkEncrypt64BWithOAEPAndGCM(b *testing.B) { benchEncrypt("64B", "OAEPAndGCM", b) } -func BenchmarkEncrypt1KBWithOAEPAndGCM(b *testing.B) { benchEncrypt("1KB", "OAEPAndGCM", b) } -func BenchmarkEncrypt64KBWithOAEPAndGCM(b *testing.B) { benchEncrypt("64KB", "OAEPAndGCM", b) } -func BenchmarkEncrypt1MBWithOAEPAndGCM(b *testing.B) { benchEncrypt("1MB", "OAEPAndGCM", b) } -func BenchmarkEncrypt64MBWithOAEPAndGCM(b *testing.B) { benchEncrypt("64MB", "OAEPAndGCM", b) } - -func BenchmarkEncrypt1BWithPKCSAndGCM(b *testing.B) { benchEncrypt("1B", "PKCSAndGCM", b) } -func BenchmarkEncrypt64BWithPKCSAndGCM(b *testing.B) { benchEncrypt("64B", "PKCSAndGCM", b) } -func BenchmarkEncrypt1KBWithPKCSAndGCM(b *testing.B) { benchEncrypt("1KB", "PKCSAndGCM", b) } -func BenchmarkEncrypt64KBWithPKCSAndGCM(b *testing.B) { benchEncrypt("64KB", "PKCSAndGCM", b) } -func BenchmarkEncrypt1MBWithPKCSAndGCM(b *testing.B) { benchEncrypt("1MB", "PKCSAndGCM", b) } -func BenchmarkEncrypt64MBWithPKCSAndGCM(b *testing.B) { benchEncrypt("64MB", "PKCSAndGCM", b) } - -func BenchmarkEncrypt1BWithOAEPAndCBC(b *testing.B) { benchEncrypt("1B", "OAEPAndCBC", b) } -func BenchmarkEncrypt64BWithOAEPAndCBC(b *testing.B) { benchEncrypt("64B", "OAEPAndCBC", b) } -func BenchmarkEncrypt1KBWithOAEPAndCBC(b *testing.B) { benchEncrypt("1KB", "OAEPAndCBC", b) } -func BenchmarkEncrypt64KBWithOAEPAndCBC(b *testing.B) { benchEncrypt("64KB", "OAEPAndCBC", b) } -func BenchmarkEncrypt1MBWithOAEPAndCBC(b *testing.B) { benchEncrypt("1MB", "OAEPAndCBC", b) } -func BenchmarkEncrypt64MBWithOAEPAndCBC(b *testing.B) { benchEncrypt("64MB", "OAEPAndCBC", b) } - -func BenchmarkEncrypt1BWithPKCSAndCBC(b *testing.B) { benchEncrypt("1B", "PKCSAndCBC", b) } -func BenchmarkEncrypt64BWithPKCSAndCBC(b *testing.B) { benchEncrypt("64B", "PKCSAndCBC", b) } -func BenchmarkEncrypt1KBWithPKCSAndCBC(b *testing.B) { benchEncrypt("1KB", "PKCSAndCBC", b) } -func BenchmarkEncrypt64KBWithPKCSAndCBC(b *testing.B) { benchEncrypt("64KB", "PKCSAndCBC", b) } -func BenchmarkEncrypt1MBWithPKCSAndCBC(b *testing.B) { benchEncrypt("1MB", "PKCSAndCBC", b) } -func BenchmarkEncrypt64MBWithPKCSAndCBC(b *testing.B) { benchEncrypt("64MB", "PKCSAndCBC", b) } - -func BenchmarkEncrypt1BWithDirectGCM128(b *testing.B) { benchEncrypt("1B", "DirectGCM128", b) } -func BenchmarkEncrypt64BWithDirectGCM128(b *testing.B) { benchEncrypt("64B", "DirectGCM128", b) } -func BenchmarkEncrypt1KBWithDirectGCM128(b *testing.B) { benchEncrypt("1KB", "DirectGCM128", b) } -func BenchmarkEncrypt64KBWithDirectGCM128(b *testing.B) { benchEncrypt("64KB", "DirectGCM128", b) } -func BenchmarkEncrypt1MBWithDirectGCM128(b *testing.B) { benchEncrypt("1MB", "DirectGCM128", b) } -func BenchmarkEncrypt64MBWithDirectGCM128(b *testing.B) { benchEncrypt("64MB", "DirectGCM128", b) } - -func BenchmarkEncrypt1BWithDirectCBC128(b *testing.B) { benchEncrypt("1B", "DirectCBC128", b) } -func BenchmarkEncrypt64BWithDirectCBC128(b *testing.B) { benchEncrypt("64B", "DirectCBC128", b) } -func BenchmarkEncrypt1KBWithDirectCBC128(b *testing.B) { benchEncrypt("1KB", "DirectCBC128", b) } -func BenchmarkEncrypt64KBWithDirectCBC128(b *testing.B) { benchEncrypt("64KB", "DirectCBC128", b) } -func BenchmarkEncrypt1MBWithDirectCBC128(b *testing.B) { benchEncrypt("1MB", "DirectCBC128", b) } -func BenchmarkEncrypt64MBWithDirectCBC128(b *testing.B) { benchEncrypt("64MB", "DirectCBC128", b) } - -func BenchmarkEncrypt1BWithDirectGCM256(b *testing.B) { benchEncrypt("1B", "DirectGCM256", b) } -func BenchmarkEncrypt64BWithDirectGCM256(b *testing.B) { benchEncrypt("64B", "DirectGCM256", b) } -func BenchmarkEncrypt1KBWithDirectGCM256(b *testing.B) { benchEncrypt("1KB", "DirectGCM256", b) } -func BenchmarkEncrypt64KBWithDirectGCM256(b *testing.B) { benchEncrypt("64KB", "DirectGCM256", b) } -func BenchmarkEncrypt1MBWithDirectGCM256(b *testing.B) { benchEncrypt("1MB", "DirectGCM256", b) } -func BenchmarkEncrypt64MBWithDirectGCM256(b *testing.B) { benchEncrypt("64MB", "DirectGCM256", b) } - -func BenchmarkEncrypt1BWithDirectCBC256(b *testing.B) { benchEncrypt("1B", "DirectCBC256", b) } -func BenchmarkEncrypt64BWithDirectCBC256(b *testing.B) { benchEncrypt("64B", "DirectCBC256", b) } -func BenchmarkEncrypt1KBWithDirectCBC256(b *testing.B) { benchEncrypt("1KB", "DirectCBC256", b) } -func BenchmarkEncrypt64KBWithDirectCBC256(b *testing.B) { benchEncrypt("64KB", "DirectCBC256", b) } -func BenchmarkEncrypt1MBWithDirectCBC256(b *testing.B) { benchEncrypt("1MB", "DirectCBC256", b) } -func BenchmarkEncrypt64MBWithDirectCBC256(b *testing.B) { benchEncrypt("64MB", "DirectCBC256", b) } - -func BenchmarkEncrypt1BWithAESKWAndGCM128(b *testing.B) { benchEncrypt("1B", "AESKWAndGCM128", b) } -func BenchmarkEncrypt64BWithAESKWAndGCM128(b *testing.B) { benchEncrypt("64B", "AESKWAndGCM128", b) } -func BenchmarkEncrypt1KBWithAESKWAndGCM128(b *testing.B) { benchEncrypt("1KB", "AESKWAndGCM128", b) } -func BenchmarkEncrypt64KBWithAESKWAndGCM128(b *testing.B) { benchEncrypt("64KB", "AESKWAndGCM128", b) } -func BenchmarkEncrypt1MBWithAESKWAndGCM128(b *testing.B) { benchEncrypt("1MB", "AESKWAndGCM128", b) } -func BenchmarkEncrypt64MBWithAESKWAndGCM128(b *testing.B) { benchEncrypt("64MB", "AESKWAndGCM128", b) } - -func BenchmarkEncrypt1BWithAESKWAndCBC256(b *testing.B) { benchEncrypt("1B", "AESKWAndCBC256", b) } -func BenchmarkEncrypt64BWithAESKWAndCBC256(b *testing.B) { benchEncrypt("64B", "AESKWAndCBC256", b) } -func BenchmarkEncrypt1KBWithAESKWAndCBC256(b *testing.B) { benchEncrypt("1KB", "AESKWAndCBC256", b) } -func BenchmarkEncrypt64KBWithAESKWAndCBC256(b *testing.B) { benchEncrypt("64KB", "AESKWAndCBC256", b) } -func BenchmarkEncrypt1MBWithAESKWAndCBC256(b *testing.B) { benchEncrypt("1MB", "AESKWAndCBC256", b) } -func BenchmarkEncrypt64MBWithAESKWAndCBC256(b *testing.B) { benchEncrypt("64MB", "AESKWAndCBC256", b) } - -func BenchmarkEncrypt1BWithECDHOnP256AndGCM128(b *testing.B) { - benchEncrypt("1B", "ECDHOnP256AndGCM128", b) -} -func BenchmarkEncrypt64BWithECDHOnP256AndGCM128(b *testing.B) { - benchEncrypt("64B", "ECDHOnP256AndGCM128", b) -} -func BenchmarkEncrypt1KBWithECDHOnP256AndGCM128(b *testing.B) { - benchEncrypt("1KB", "ECDHOnP256AndGCM128", b) -} -func BenchmarkEncrypt64KBWithECDHOnP256AndGCM128(b *testing.B) { - benchEncrypt("64KB", "ECDHOnP256AndGCM128", b) -} -func BenchmarkEncrypt1MBWithECDHOnP256AndGCM128(b *testing.B) { - benchEncrypt("1MB", "ECDHOnP256AndGCM128", b) -} -func BenchmarkEncrypt64MBWithECDHOnP256AndGCM128(b *testing.B) { - benchEncrypt("64MB", "ECDHOnP256AndGCM128", b) -} - -func BenchmarkEncrypt1BWithECDHOnP384AndGCM128(b *testing.B) { - benchEncrypt("1B", "ECDHOnP384AndGCM128", b) -} -func BenchmarkEncrypt64BWithECDHOnP384AndGCM128(b *testing.B) { - benchEncrypt("64B", "ECDHOnP384AndGCM128", b) -} -func BenchmarkEncrypt1KBWithECDHOnP384AndGCM128(b *testing.B) { - benchEncrypt("1KB", "ECDHOnP384AndGCM128", b) -} -func BenchmarkEncrypt64KBWithECDHOnP384AndGCM128(b *testing.B) { - benchEncrypt("64KB", "ECDHOnP384AndGCM128", b) -} -func BenchmarkEncrypt1MBWithECDHOnP384AndGCM128(b *testing.B) { - benchEncrypt("1MB", "ECDHOnP384AndGCM128", b) -} -func BenchmarkEncrypt64MBWithECDHOnP384AndGCM128(b *testing.B) { - benchEncrypt("64MB", "ECDHOnP384AndGCM128", b) -} - -func BenchmarkEncrypt1BWithECDHOnP521AndGCM128(b *testing.B) { - benchEncrypt("1B", "ECDHOnP521AndGCM128", b) -} -func BenchmarkEncrypt64BWithECDHOnP521AndGCM128(b *testing.B) { - benchEncrypt("64B", "ECDHOnP521AndGCM128", b) -} -func BenchmarkEncrypt1KBWithECDHOnP521AndGCM128(b *testing.B) { - benchEncrypt("1KB", "ECDHOnP521AndGCM128", b) -} -func BenchmarkEncrypt64KBWithECDHOnP521AndGCM128(b *testing.B) { - benchEncrypt("64KB", "ECDHOnP521AndGCM128", b) -} -func BenchmarkEncrypt1MBWithECDHOnP521AndGCM128(b *testing.B) { - benchEncrypt("1MB", "ECDHOnP521AndGCM128", b) -} -func BenchmarkEncrypt64MBWithECDHOnP521AndGCM128(b *testing.B) { - benchEncrypt("64MB", "ECDHOnP521AndGCM128", b) -} - -func benchEncrypt(chunkKey, primKey string, b *testing.B) { - data, ok := chunks[chunkKey] - if !ok { - b.Fatalf("unknown chunk size %s", chunkKey) - } - - enc, ok := encrypters[primKey] - if !ok { - b.Fatalf("unknown encrypter %s", primKey) - } - - b.SetBytes(int64(len(data))) - for i := 0; i < b.N; i++ { - enc.Encrypt(data) - } -} - -var ( - decryptionKeys = map[string]interface{}{ - "OAEPAndGCM": rsaTestKey, - "PKCSAndGCM": rsaTestKey, - "OAEPAndCBC": rsaTestKey, - "PKCSAndCBC": rsaTestKey, - - "DirectGCM128": symKey, - "DirectCBC128": symKey, - "DirectGCM256": symKey, - "DirectCBC256": symKey, - - "AESKWAndGCM128": symKey, - "AESKWAndCBC256": symKey, - - "ECDHOnP256AndGCM128": ecTestKey256, - "ECDHOnP384AndGCM128": ecTestKey384, - "ECDHOnP521AndGCM128": ecTestKey521, - } -) - -func BenchmarkDecrypt1BWithOAEPAndGCM(b *testing.B) { benchDecrypt("1B", "OAEPAndGCM", b) } -func BenchmarkDecrypt64BWithOAEPAndGCM(b *testing.B) { benchDecrypt("64B", "OAEPAndGCM", b) } -func BenchmarkDecrypt1KBWithOAEPAndGCM(b *testing.B) { benchDecrypt("1KB", "OAEPAndGCM", b) } -func BenchmarkDecrypt64KBWithOAEPAndGCM(b *testing.B) { benchDecrypt("64KB", "OAEPAndGCM", b) } -func BenchmarkDecrypt1MBWithOAEPAndGCM(b *testing.B) { benchDecrypt("1MB", "OAEPAndGCM", b) } -func BenchmarkDecrypt64MBWithOAEPAndGCM(b *testing.B) { benchDecrypt("64MB", "OAEPAndGCM", b) } - -func BenchmarkDecrypt1BWithPKCSAndGCM(b *testing.B) { benchDecrypt("1B", "PKCSAndGCM", b) } -func BenchmarkDecrypt64BWithPKCSAndGCM(b *testing.B) { benchDecrypt("64B", "PKCSAndGCM", b) } -func BenchmarkDecrypt1KBWithPKCSAndGCM(b *testing.B) { benchDecrypt("1KB", "PKCSAndGCM", b) } -func BenchmarkDecrypt64KBWithPKCSAndGCM(b *testing.B) { benchDecrypt("64KB", "PKCSAndGCM", b) } -func BenchmarkDecrypt1MBWithPKCSAndGCM(b *testing.B) { benchDecrypt("1MB", "PKCSAndGCM", b) } -func BenchmarkDecrypt64MBWithPKCSAndGCM(b *testing.B) { benchDecrypt("64MB", "PKCSAndGCM", b) } - -func BenchmarkDecrypt1BWithOAEPAndCBC(b *testing.B) { benchDecrypt("1B", "OAEPAndCBC", b) } -func BenchmarkDecrypt64BWithOAEPAndCBC(b *testing.B) { benchDecrypt("64B", "OAEPAndCBC", b) } -func BenchmarkDecrypt1KBWithOAEPAndCBC(b *testing.B) { benchDecrypt("1KB", "OAEPAndCBC", b) } -func BenchmarkDecrypt64KBWithOAEPAndCBC(b *testing.B) { benchDecrypt("64KB", "OAEPAndCBC", b) } -func BenchmarkDecrypt1MBWithOAEPAndCBC(b *testing.B) { benchDecrypt("1MB", "OAEPAndCBC", b) } -func BenchmarkDecrypt64MBWithOAEPAndCBC(b *testing.B) { benchDecrypt("64MB", "OAEPAndCBC", b) } - -func BenchmarkDecrypt1BWithPKCSAndCBC(b *testing.B) { benchDecrypt("1B", "PKCSAndCBC", b) } -func BenchmarkDecrypt64BWithPKCSAndCBC(b *testing.B) { benchDecrypt("64B", "PKCSAndCBC", b) } -func BenchmarkDecrypt1KBWithPKCSAndCBC(b *testing.B) { benchDecrypt("1KB", "PKCSAndCBC", b) } -func BenchmarkDecrypt64KBWithPKCSAndCBC(b *testing.B) { benchDecrypt("64KB", "PKCSAndCBC", b) } -func BenchmarkDecrypt1MBWithPKCSAndCBC(b *testing.B) { benchDecrypt("1MB", "PKCSAndCBC", b) } -func BenchmarkDecrypt64MBWithPKCSAndCBC(b *testing.B) { benchDecrypt("64MB", "PKCSAndCBC", b) } - -func BenchmarkDecrypt1BWithDirectGCM128(b *testing.B) { benchDecrypt("1B", "DirectGCM128", b) } -func BenchmarkDecrypt64BWithDirectGCM128(b *testing.B) { benchDecrypt("64B", "DirectGCM128", b) } -func BenchmarkDecrypt1KBWithDirectGCM128(b *testing.B) { benchDecrypt("1KB", "DirectGCM128", b) } -func BenchmarkDecrypt64KBWithDirectGCM128(b *testing.B) { benchDecrypt("64KB", "DirectGCM128", b) } -func BenchmarkDecrypt1MBWithDirectGCM128(b *testing.B) { benchDecrypt("1MB", "DirectGCM128", b) } -func BenchmarkDecrypt64MBWithDirectGCM128(b *testing.B) { benchDecrypt("64MB", "DirectGCM128", b) } - -func BenchmarkDecrypt1BWithDirectCBC128(b *testing.B) { benchDecrypt("1B", "DirectCBC128", b) } -func BenchmarkDecrypt64BWithDirectCBC128(b *testing.B) { benchDecrypt("64B", "DirectCBC128", b) } -func BenchmarkDecrypt1KBWithDirectCBC128(b *testing.B) { benchDecrypt("1KB", "DirectCBC128", b) } -func BenchmarkDecrypt64KBWithDirectCBC128(b *testing.B) { benchDecrypt("64KB", "DirectCBC128", b) } -func BenchmarkDecrypt1MBWithDirectCBC128(b *testing.B) { benchDecrypt("1MB", "DirectCBC128", b) } -func BenchmarkDecrypt64MBWithDirectCBC128(b *testing.B) { benchDecrypt("64MB", "DirectCBC128", b) } - -func BenchmarkDecrypt1BWithDirectGCM256(b *testing.B) { benchDecrypt("1B", "DirectGCM256", b) } -func BenchmarkDecrypt64BWithDirectGCM256(b *testing.B) { benchDecrypt("64B", "DirectGCM256", b) } -func BenchmarkDecrypt1KBWithDirectGCM256(b *testing.B) { benchDecrypt("1KB", "DirectGCM256", b) } -func BenchmarkDecrypt64KBWithDirectGCM256(b *testing.B) { benchDecrypt("64KB", "DirectGCM256", b) } -func BenchmarkDecrypt1MBWithDirectGCM256(b *testing.B) { benchDecrypt("1MB", "DirectGCM256", b) } -func BenchmarkDecrypt64MBWithDirectGCM256(b *testing.B) { benchDecrypt("64MB", "DirectGCM256", b) } - -func BenchmarkDecrypt1BWithDirectCBC256(b *testing.B) { benchDecrypt("1B", "DirectCBC256", b) } -func BenchmarkDecrypt64BWithDirectCBC256(b *testing.B) { benchDecrypt("64B", "DirectCBC256", b) } -func BenchmarkDecrypt1KBWithDirectCBC256(b *testing.B) { benchDecrypt("1KB", "DirectCBC256", b) } -func BenchmarkDecrypt64KBWithDirectCBC256(b *testing.B) { benchDecrypt("64KB", "DirectCBC256", b) } -func BenchmarkDecrypt1MBWithDirectCBC256(b *testing.B) { benchDecrypt("1MB", "DirectCBC256", b) } -func BenchmarkDecrypt64MBWithDirectCBC256(b *testing.B) { benchDecrypt("64MB", "DirectCBC256", b) } - -func BenchmarkDecrypt1BWithAESKWAndGCM128(b *testing.B) { benchDecrypt("1B", "AESKWAndGCM128", b) } -func BenchmarkDecrypt64BWithAESKWAndGCM128(b *testing.B) { benchDecrypt("64B", "AESKWAndGCM128", b) } -func BenchmarkDecrypt1KBWithAESKWAndGCM128(b *testing.B) { benchDecrypt("1KB", "AESKWAndGCM128", b) } -func BenchmarkDecrypt64KBWithAESKWAndGCM128(b *testing.B) { benchDecrypt("64KB", "AESKWAndGCM128", b) } -func BenchmarkDecrypt1MBWithAESKWAndGCM128(b *testing.B) { benchDecrypt("1MB", "AESKWAndGCM128", b) } -func BenchmarkDecrypt64MBWithAESKWAndGCM128(b *testing.B) { benchDecrypt("64MB", "AESKWAndGCM128", b) } - -func BenchmarkDecrypt1BWithAESKWAndCBC256(b *testing.B) { benchDecrypt("1B", "AESKWAndCBC256", b) } -func BenchmarkDecrypt64BWithAESKWAndCBC256(b *testing.B) { benchDecrypt("64B", "AESKWAndCBC256", b) } -func BenchmarkDecrypt1KBWithAESKWAndCBC256(b *testing.B) { benchDecrypt("1KB", "AESKWAndCBC256", b) } -func BenchmarkDecrypt64KBWithAESKWAndCBC256(b *testing.B) { benchDecrypt("64KB", "AESKWAndCBC256", b) } -func BenchmarkDecrypt1MBWithAESKWAndCBC256(b *testing.B) { benchDecrypt("1MB", "AESKWAndCBC256", b) } -func BenchmarkDecrypt64MBWithAESKWAndCBC256(b *testing.B) { benchDecrypt("64MB", "AESKWAndCBC256", b) } - -func BenchmarkDecrypt1BWithECDHOnP256AndGCM128(b *testing.B) { - benchDecrypt("1B", "ECDHOnP256AndGCM128", b) -} -func BenchmarkDecrypt64BWithECDHOnP256AndGCM128(b *testing.B) { - benchDecrypt("64B", "ECDHOnP256AndGCM128", b) -} -func BenchmarkDecrypt1KBWithECDHOnP256AndGCM128(b *testing.B) { - benchDecrypt("1KB", "ECDHOnP256AndGCM128", b) -} -func BenchmarkDecrypt64KBWithECDHOnP256AndGCM128(b *testing.B) { - benchDecrypt("64KB", "ECDHOnP256AndGCM128", b) -} -func BenchmarkDecrypt1MBWithECDHOnP256AndGCM128(b *testing.B) { - benchDecrypt("1MB", "ECDHOnP256AndGCM128", b) -} -func BenchmarkDecrypt64MBWithECDHOnP256AndGCM128(b *testing.B) { - benchDecrypt("64MB", "ECDHOnP256AndGCM128", b) -} - -func BenchmarkDecrypt1BWithECDHOnP384AndGCM128(b *testing.B) { - benchDecrypt("1B", "ECDHOnP384AndGCM128", b) -} -func BenchmarkDecrypt64BWithECDHOnP384AndGCM128(b *testing.B) { - benchDecrypt("64B", "ECDHOnP384AndGCM128", b) -} -func BenchmarkDecrypt1KBWithECDHOnP384AndGCM128(b *testing.B) { - benchDecrypt("1KB", "ECDHOnP384AndGCM128", b) -} -func BenchmarkDecrypt64KBWithECDHOnP384AndGCM128(b *testing.B) { - benchDecrypt("64KB", "ECDHOnP384AndGCM128", b) -} -func BenchmarkDecrypt1MBWithECDHOnP384AndGCM128(b *testing.B) { - benchDecrypt("1MB", "ECDHOnP384AndGCM128", b) -} -func BenchmarkDecrypt64MBWithECDHOnP384AndGCM128(b *testing.B) { - benchDecrypt("64MB", "ECDHOnP384AndGCM128", b) -} - -func BenchmarkDecrypt1BWithECDHOnP521AndGCM128(b *testing.B) { - benchDecrypt("1B", "ECDHOnP521AndGCM128", b) -} -func BenchmarkDecrypt64BWithECDHOnP521AndGCM128(b *testing.B) { - benchDecrypt("64B", "ECDHOnP521AndGCM128", b) -} -func BenchmarkDecrypt1KBWithECDHOnP521AndGCM128(b *testing.B) { - benchDecrypt("1KB", "ECDHOnP521AndGCM128", b) -} -func BenchmarkDecrypt64KBWithECDHOnP521AndGCM128(b *testing.B) { - benchDecrypt("64KB", "ECDHOnP521AndGCM128", b) -} -func BenchmarkDecrypt1MBWithECDHOnP521AndGCM128(b *testing.B) { - benchDecrypt("1MB", "ECDHOnP521AndGCM128", b) -} -func BenchmarkDecrypt64MBWithECDHOnP521AndGCM128(b *testing.B) { - benchDecrypt("64MB", "ECDHOnP521AndGCM128", b) -} - -func benchDecrypt(chunkKey, primKey string, b *testing.B) { - chunk, ok := chunks[chunkKey] - if !ok { - b.Fatalf("unknown chunk size %s", chunkKey) - } - - enc, ok := encrypters[primKey] - if !ok { - b.Fatalf("unknown encrypter %s", primKey) - } - - dec, ok := decryptionKeys[primKey] - if !ok { - b.Fatalf("unknown decryption key %s", primKey) - } - - data, err := enc.Encrypt(chunk) - if err != nil { - b.Fatal(err) - } - - b.SetBytes(int64(len(chunk))) - b.ResetTimer() - for i := 0; i < b.N; i++ { - data.Decrypt(dec) - } -} - -func mustEncrypter(keyAlg KeyAlgorithm, encAlg ContentEncryption, encryptionKey interface{}) Encrypter { - enc, err := NewEncrypter(keyAlg, encAlg, encryptionKey) - if err != nil { - panic(err) - } - return enc -} diff --git a/vendor/gopkg.in/square/go-jose.v1/doc.go b/vendor/gopkg.in/square/go-jose.v1/doc.go deleted file mode 100644 index b4cd1e98..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/doc.go +++ /dev/null @@ -1,26 +0,0 @@ -/*- - * Copyright 2014 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 jose aims to provide an implementation of the Javascript Object Signing -and Encryption set of standards. For the moment, it mainly focuses on -encryption and signing based on the JSON Web Encryption and JSON Web Signature -standards. The library supports both the compact and full serialization -formats, and has optional support for multiple recipients. - -*/ -package jose // import "gopkg.in/square/go-jose.v1" diff --git a/vendor/gopkg.in/square/go-jose.v1/doc_test.go b/vendor/gopkg.in/square/go-jose.v1/doc_test.go deleted file mode 100644 index 50468295..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/doc_test.go +++ /dev/null @@ -1,226 +0,0 @@ -/*- - * Copyright 2014 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 jose - -import ( - "crypto/ecdsa" - "crypto/rand" - "crypto/rsa" - "fmt" -) - -// Dummy encrypter for use in examples -var encrypter, _ = NewEncrypter(DIRECT, A128GCM, []byte{}) - -func Example_jWE() { - // Generate a public/private key pair to use for this example. The library - // also provides two utility functions (LoadPublicKey and LoadPrivateKey) - // that can be used to load keys from PEM/DER-encoded data. - privateKey, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - panic(err) - } - - // Instantiate an encrypter using RSA-OAEP with AES128-GCM. An error would - // indicate that the selected algorithm(s) are not currently supported. - publicKey := &privateKey.PublicKey - encrypter, err := NewEncrypter(RSA_OAEP, A128GCM, publicKey) - if err != nil { - panic(err) - } - - // Encrypt a sample plaintext. Calling the encrypter returns an encrypted - // JWE object, which can then be serialized for output afterwards. An error - // would indicate a problem in an underlying cryptographic primitive. - var plaintext = []byte("Lorem ipsum dolor sit amet") - object, err := encrypter.Encrypt(plaintext) - if err != nil { - panic(err) - } - - // Serialize the encrypted object using the full serialization format. - // Alternatively you can also use the compact format here by calling - // object.CompactSerialize() instead. - serialized := object.FullSerialize() - - // Parse the serialized, encrypted JWE object. An error would indicate that - // the given input did not represent a valid message. - object, err = ParseEncrypted(serialized) - if err != nil { - panic(err) - } - - // Now we can decrypt and get back our original plaintext. An error here - // would indicate the the message failed to decrypt, e.g. because the auth - // tag was broken or the message was tampered with. - decrypted, err := object.Decrypt(privateKey) - if err != nil { - panic(err) - } - - fmt.Printf(string(decrypted)) - // output: Lorem ipsum dolor sit amet -} - -func Example_jWS() { - // Generate a public/private key pair to use for this example. The library - // also provides two utility functions (LoadPublicKey and LoadPrivateKey) - // that can be used to load keys from PEM/DER-encoded data. - privateKey, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - panic(err) - } - - // Instantiate a signer using RSASSA-PSS (SHA512) with the given private key. - signer, err := NewSigner(PS512, privateKey) - if err != nil { - panic(err) - } - - // Sign a sample payload. Calling the signer returns a protected JWS object, - // which can then be serialized for output afterwards. An error would - // indicate a problem in an underlying cryptographic primitive. - var payload = []byte("Lorem ipsum dolor sit amet") - object, err := signer.Sign(payload) - if err != nil { - panic(err) - } - - // Serialize the encrypted object using the full serialization format. - // Alternatively you can also use the compact format here by calling - // object.CompactSerialize() instead. - serialized := object.FullSerialize() - - // Parse the serialized, protected JWS object. An error would indicate that - // the given input did not represent a valid message. - object, err = ParseSigned(serialized) - if err != nil { - panic(err) - } - - // Now we can verify the signature on the payload. An error here would - // indicate the the message failed to verify, e.g. because the signature was - // broken or the message was tampered with. - output, err := object.Verify(&privateKey.PublicKey) - if err != nil { - panic(err) - } - - fmt.Printf(string(output)) - // output: Lorem ipsum dolor sit amet -} - -func ExampleNewEncrypter_publicKey() { - var publicKey *rsa.PublicKey - - // Instantiate an encrypter using RSA-OAEP with AES128-GCM. - NewEncrypter(RSA_OAEP, A128GCM, publicKey) - - // Instantiate an encrypter using RSA-PKCS1v1.5 with AES128-CBC+HMAC. - NewEncrypter(RSA1_5, A128CBC_HS256, publicKey) -} - -func ExampleNewEncrypter_symmetric() { - var sharedKey []byte - - // Instantiate an encrypter using AES128-GCM with AES-GCM key wrap. - NewEncrypter(A128GCMKW, A128GCM, sharedKey) - - // Instantiate an encrypter using AES256-GCM directly, w/o key wrapping. - NewEncrypter(DIRECT, A256GCM, sharedKey) -} - -func ExampleNewSigner_publicKey() { - var rsaPrivateKey *rsa.PrivateKey - var ecdsaPrivateKey *ecdsa.PrivateKey - - // Instantiate a signer using RSA-PKCS#1v1.5 with SHA-256. - NewSigner(RS256, rsaPrivateKey) - - // Instantiate a signer using ECDSA with SHA-384. - NewSigner(ES384, ecdsaPrivateKey) -} - -func ExampleNewSigner_symmetric() { - var sharedKey []byte - - // Instantiate an signer using HMAC-SHA256. - NewSigner(HS256, sharedKey) - - // Instantiate an signer using HMAC-SHA512. - NewSigner(HS512, sharedKey) -} - -func ExampleNewMultiEncrypter() { - var publicKey *rsa.PublicKey - var sharedKey []byte - - // Instantiate an encrypter using AES-GCM. - encrypter, err := NewMultiEncrypter(A128GCM) - if err != nil { - panic(err) - } - - // Add a recipient using a shared key with AES-GCM key wap - err = encrypter.AddRecipient(A128GCMKW, sharedKey) - if err != nil { - panic(err) - } - - // Add a recipient using an RSA public key with RSA-OAEP - err = encrypter.AddRecipient(RSA_OAEP, publicKey) - if err != nil { - panic(err) - } -} - -func ExampleNewMultiSigner() { - var privateKey *rsa.PrivateKey - var sharedKey []byte - - // Instantiate a signer for multiple recipients. - signer := NewMultiSigner() - - // Add a recipient using a shared key with HMAC-SHA256 - err := signer.AddRecipient(HS256, sharedKey) - if err != nil { - panic(err) - } - - // Add a recipient using an RSA private key with RSASSA-PSS with SHA384 - err = signer.AddRecipient(PS384, privateKey) - if err != nil { - panic(err) - } -} - -func ExampleEncrypter_encrypt() { - // Encrypt a plaintext in order to get an encrypted JWE object. - var plaintext = []byte("This is a secret message") - - encrypter.Encrypt(plaintext) -} - -func ExampleEncrypter_encryptWithAuthData() { - // Encrypt a plaintext in order to get an encrypted JWE object. Also attach - // some additional authenticated data (AAD) to the object. Note that objects - // with attached AAD can only be represented using full serialization. - var plaintext = []byte("This is a secret message") - var aad = []byte("This is authenticated, but public data") - - encrypter.EncryptWithAuthData(plaintext, aad) -} diff --git a/vendor/gopkg.in/square/go-jose.v1/encoding.go b/vendor/gopkg.in/square/go-jose.v1/encoding.go deleted file mode 100644 index 3e2ac0ae..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/encoding.go +++ /dev/null @@ -1,191 +0,0 @@ -/*- - * Copyright 2014 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 jose - -import ( - "bytes" - "compress/flate" - "encoding/base64" - "encoding/binary" - "io" - "math/big" - "regexp" - "strings" -) - -var stripWhitespaceRegex = regexp.MustCompile("\\s") - -// Url-safe base64 encode that strips padding -func base64URLEncode(data []byte) string { - var result = base64.URLEncoding.EncodeToString(data) - return strings.TrimRight(result, "=") -} - -// Url-safe base64 decoder that adds padding -func base64URLDecode(data string) ([]byte, error) { - var missing = (4 - len(data)%4) % 4 - data += strings.Repeat("=", missing) - return base64.URLEncoding.DecodeString(data) -} - -// Helper function to serialize known-good objects. -// Precondition: value is not a nil pointer. -func mustSerializeJSON(value interface{}) []byte { - out, err := MarshalJSON(value) - if err != nil { - panic(err) - } - // We never want to serialize the top-level value "null," since it's not a - // valid JOSE message. But if a caller passes in a nil pointer to this method, - // MarshalJSON will happily serialize it as the top-level value "null". If - // that value is then embedded in another operation, for instance by being - // base64-encoded and fed as input to a signing algorithm - // (https://github.com/square/go-jose/issues/22), the result will be - // incorrect. Because this method is intended for known-good objects, and a nil - // pointer is not a known-good object, we are free to panic in this case. - // Note: It's not possible to directly check whether the data pointed at by an - // interface is a nil pointer, so we do this hacky workaround. - // https://groups.google.com/forum/#!topic/golang-nuts/wnH302gBa4I - if string(out) == "null" { - panic("Tried to serialize a nil pointer.") - } - return out -} - -// Strip all newlines and whitespace -func stripWhitespace(data string) string { - return stripWhitespaceRegex.ReplaceAllString(data, "") -} - -// Perform compression based on algorithm -func compress(algorithm CompressionAlgorithm, input []byte) ([]byte, error) { - switch algorithm { - case DEFLATE: - return deflate(input) - default: - return nil, ErrUnsupportedAlgorithm - } -} - -// Perform decompression based on algorithm -func decompress(algorithm CompressionAlgorithm, input []byte) ([]byte, error) { - switch algorithm { - case DEFLATE: - return inflate(input) - default: - return nil, ErrUnsupportedAlgorithm - } -} - -// Compress with DEFLATE -func deflate(input []byte) ([]byte, error) { - output := new(bytes.Buffer) - - // Writing to byte buffer, err is always nil - writer, _ := flate.NewWriter(output, 1) - _, _ = io.Copy(writer, bytes.NewBuffer(input)) - - err := writer.Close() - return output.Bytes(), err -} - -// Decompress with DEFLATE -func inflate(input []byte) ([]byte, error) { - output := new(bytes.Buffer) - reader := flate.NewReader(bytes.NewBuffer(input)) - - _, err := io.Copy(output, reader) - if err != nil { - return nil, err - } - - err = reader.Close() - return output.Bytes(), err -} - -// byteBuffer represents a slice of bytes that can be serialized to url-safe base64. -type byteBuffer struct { - data []byte -} - -func newBuffer(data []byte) *byteBuffer { - if data == nil { - return nil - } - return &byteBuffer{ - data: data, - } -} - -func newFixedSizeBuffer(data []byte, length int) *byteBuffer { - if len(data) > length { - panic("square/go-jose: invalid call to newFixedSizeBuffer (len(data) > length)") - } - pad := make([]byte, length-len(data)) - return newBuffer(append(pad, data...)) -} - -func newBufferFromInt(num uint64) *byteBuffer { - data := make([]byte, 8) - binary.BigEndian.PutUint64(data, num) - return newBuffer(bytes.TrimLeft(data, "\x00")) -} - -func (b *byteBuffer) MarshalJSON() ([]byte, error) { - return MarshalJSON(b.base64()) -} - -func (b *byteBuffer) UnmarshalJSON(data []byte) error { - var encoded string - err := UnmarshalJSON(data, &encoded) - if err != nil { - return err - } - - if encoded == "" { - return nil - } - - decoded, err := base64URLDecode(encoded) - if err != nil { - return err - } - - *b = *newBuffer(decoded) - - return nil -} - -func (b *byteBuffer) base64() string { - return base64URLEncode(b.data) -} - -func (b *byteBuffer) bytes() []byte { - // Handling nil here allows us to transparently handle nil slices when serializing. - if b == nil { - return nil - } - return b.data -} - -func (b byteBuffer) bigInt() *big.Int { - return new(big.Int).SetBytes(b.data) -} - -func (b byteBuffer) toInt() int { - return int(b.bigInt().Int64()) -} diff --git a/vendor/gopkg.in/square/go-jose.v1/encoding_test.go b/vendor/gopkg.in/square/go-jose.v1/encoding_test.go deleted file mode 100644 index e2f8d979..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/encoding_test.go +++ /dev/null @@ -1,173 +0,0 @@ -/*- - * Copyright 2014 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 jose - -import ( - "bytes" - "strings" - "testing" -) - -func TestBase64URLEncode(t *testing.T) { - // Test arrays with various sizes - if base64URLEncode([]byte{}) != "" { - t.Error("failed to encode empty array") - } - - if base64URLEncode([]byte{0}) != "AA" { - t.Error("failed to encode [0x00]") - } - - if base64URLEncode([]byte{0, 1}) != "AAE" { - t.Error("failed to encode [0x00, 0x01]") - } - - if base64URLEncode([]byte{0, 1, 2}) != "AAEC" { - t.Error("failed to encode [0x00, 0x01, 0x02]") - } - - if base64URLEncode([]byte{0, 1, 2, 3}) != "AAECAw" { - t.Error("failed to encode [0x00, 0x01, 0x02, 0x03]") - } -} - -func TestBase64URLDecode(t *testing.T) { - // Test arrays with various sizes - val, err := base64URLDecode("") - if err != nil || !bytes.Equal(val, []byte{}) { - t.Error("failed to decode empty array") - } - - val, err = base64URLDecode("AA") - if err != nil || !bytes.Equal(val, []byte{0}) { - t.Error("failed to decode [0x00]") - } - - val, err = base64URLDecode("AAE") - if err != nil || !bytes.Equal(val, []byte{0, 1}) { - t.Error("failed to decode [0x00, 0x01]") - } - - val, err = base64URLDecode("AAEC") - if err != nil || !bytes.Equal(val, []byte{0, 1, 2}) { - t.Error("failed to decode [0x00, 0x01, 0x02]") - } - - val, err = base64URLDecode("AAECAw") - if err != nil || !bytes.Equal(val, []byte{0, 1, 2, 3}) { - t.Error("failed to decode [0x00, 0x01, 0x02, 0x03]") - } -} - -func TestDeflateRoundtrip(t *testing.T) { - original := []byte("Lorem ipsum dolor sit amet") - - compressed, err := deflate(original) - if err != nil { - panic(err) - } - - output, err := inflate(compressed) - if err != nil { - panic(err) - } - - if bytes.Compare(output, original) != 0 { - t.Error("Input and output do not match") - } -} - -func TestInvalidCompression(t *testing.T) { - _, err := compress("XYZ", []byte{}) - if err == nil { - t.Error("should not accept invalid algorithm") - } - - _, err = decompress("XYZ", []byte{}) - if err == nil { - t.Error("should not accept invalid algorithm") - } - - _, err = decompress(DEFLATE, []byte{1, 2, 3, 4}) - if err == nil { - t.Error("should not accept invalid data") - } -} - -func TestByteBufferTrim(t *testing.T) { - buf := newBufferFromInt(1) - if !bytes.Equal(buf.data, []byte{1}) { - t.Error("Byte buffer for integer '1' should contain [0x01]") - } - - buf = newBufferFromInt(65537) - if !bytes.Equal(buf.data, []byte{1, 0, 1}) { - t.Error("Byte buffer for integer '65537' should contain [0x01, 0x00, 0x01]") - } -} - -func TestFixedSizeBuffer(t *testing.T) { - data0 := []byte{} - data1 := []byte{1} - data2 := []byte{1, 2} - data3 := []byte{1, 2, 3} - data4 := []byte{1, 2, 3, 4} - - buf0 := newFixedSizeBuffer(data0, 4) - buf1 := newFixedSizeBuffer(data1, 4) - buf2 := newFixedSizeBuffer(data2, 4) - buf3 := newFixedSizeBuffer(data3, 4) - buf4 := newFixedSizeBuffer(data4, 4) - - if !bytes.Equal(buf0.data, []byte{0, 0, 0, 0}) { - t.Error("Invalid padded buffer for buf0") - } - if !bytes.Equal(buf1.data, []byte{0, 0, 0, 1}) { - t.Error("Invalid padded buffer for buf1") - } - if !bytes.Equal(buf2.data, []byte{0, 0, 1, 2}) { - t.Error("Invalid padded buffer for buf2") - } - if !bytes.Equal(buf3.data, []byte{0, 1, 2, 3}) { - t.Error("Invalid padded buffer for buf3") - } - if !bytes.Equal(buf4.data, []byte{1, 2, 3, 4}) { - t.Error("Invalid padded buffer for buf4") - } -} - -func TestSerializeJSONRejectsNil(t *testing.T) { - defer func() { - r := recover() - if r == nil || !strings.Contains(r.(string), "nil pointer") { - t.Error("serialize function should not accept nil pointer") - } - }() - - mustSerializeJSON(nil) -} - -func TestFixedSizeBufferTooLarge(t *testing.T) { - defer func() { - r := recover() - if r == nil { - t.Error("should not be able to create fixed size buffer with oversized data") - } - }() - - newFixedSizeBuffer(make([]byte, 2), 1) -} diff --git a/vendor/gopkg.in/square/go-jose.v1/jose-util/README.md b/vendor/gopkg.in/square/go-jose.v1/jose-util/README.md deleted file mode 100644 index 6cfe6a71..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/jose-util/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# JOSE CLI - -The `jose-util` command line utility allows for encryption, decryption, signing -and verification of JOSE messages. Its main purpose is to facilitate dealing -with JOSE messages when testing or debugging. - -## Usage - -The utility includes the subcommands `encrypt`, `decrypt`, `sign`, `verify` and -`expand`. Examples for each command can be found below. - -Algorithms are selected via the `--alg` and `--enc` flags, which influence the -`alg` and `enc` headers in respectively. For JWE, `--alg` specifies the key -managment algorithm (e.g. `RSA-OAEP`) and `--enc` specifies the content -encryption algorithm (e.g. `A128GCM`). For JWS, `--alg` specifies the -signature algorithm (e.g. `PS256`). - -Input and output files can be specified via the `--in` and `--out` flags. -Either flag can be omitted, in which case `jose-util` uses stdin/stdout for -input/output respectively. By default each command will output a compact -message, but it's possible to get the full serialization by supplying the -`--full` flag. - -Keys are specified via the `--key` flag. Supported key types are naked RSA/EC -keys and X.509 certificates with embedded RSA/EC keys. Keys must be in PEM -or DER formats. - -## Examples - -### Encrypt - -Takes a plaintext as input, encrypts, and prints the encrypted message. - - jose-util encrypt -k public-key.pem --alg RSA-OAEP --enc A128GCM - -### Decrypt - -Takes an encrypted message (JWE) as input, decrypts, and prints the plaintext. - - jose-util decrypt -k private-key.pem - -### Sign - -Takes a payload as input, signs it, and prints the signed message with the embedded payload. - - jose-util sign -k private-key.pem --alg PS256 - -### Verify - -Reads a signed message (JWS), verifies it, and extracts the payload. - - jose-util verify -k public-key.pem - -### Expand - -Expands a compact message to the full serialization format. - - jose-util expand --format JWE # Expands a compact JWE to full format - jose-util expand --format JWS # Expands a compact JWS to full format diff --git a/vendor/gopkg.in/square/go-jose.v1/jose-util/jose-util.t b/vendor/gopkg.in/square/go-jose.v1/jose-util/jose-util.t deleted file mode 100644 index 5e42ba4d..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/jose-util/jose-util.t +++ /dev/null @@ -1,88 +0,0 @@ -Set up test keys. - - $ cat > rsa.pub <<EOF - > -----BEGIN PUBLIC KEY----- - > MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAslWybuiNYR7uOgKuvaBw - > qVk8saEutKhOAaW+3hWF65gJei+ZV8QFfYDxs9ZaRZlWAUMtncQPnw7ZQlXO9ogN - > 5cMcN50C6qMOOZzghK7danalhF5lUETC4Hk3Eisbi/PR3IfVyXaRmqL6X66MKj/J - > AKyD9NFIDVy52K8A198Jojnrw2+XXQW72U68fZtvlyl/BTBWQ9Re5JSTpEcVmpCR - > 8FrFc0RPMBm+G5dRs08vvhZNiTT2JACO5V+J5ZrgP3s5hnGFcQFZgDnXLInDUdoi - > 1MuCjaAU0ta8/08pHMijNix5kFofdPEB954MiZ9k4kQ5/utt02I9x2ssHqw71ojj - > vwIDAQAB - > -----END PUBLIC KEY----- - > EOF - - $ cat > rsa.key <<EOF - > -----BEGIN RSA PRIVATE KEY----- - > MIIEogIBAAKCAQEAslWybuiNYR7uOgKuvaBwqVk8saEutKhOAaW+3hWF65gJei+Z - > V8QFfYDxs9ZaRZlWAUMtncQPnw7ZQlXO9ogN5cMcN50C6qMOOZzghK7danalhF5l - > UETC4Hk3Eisbi/PR3IfVyXaRmqL6X66MKj/JAKyD9NFIDVy52K8A198Jojnrw2+X - > XQW72U68fZtvlyl/BTBWQ9Re5JSTpEcVmpCR8FrFc0RPMBm+G5dRs08vvhZNiTT2 - > JACO5V+J5ZrgP3s5hnGFcQFZgDnXLInDUdoi1MuCjaAU0ta8/08pHMijNix5kFof - > dPEB954MiZ9k4kQ5/utt02I9x2ssHqw71ojjvwIDAQABAoIBABrYDYDmXom1BzUS - > PE1s/ihvt1QhqA8nmn5i/aUeZkc9XofW7GUqq4zlwPxKEtKRL0IHY7Fw1s0hhhCX - > LA0uE7F3OiMg7lR1cOm5NI6kZ83jyCxxrRx1DUSO2nxQotfhPsDMbaDiyS4WxEts - > 0cp2SYJhdYd/jTH9uDfmt+DGwQN7Jixio1Dj3vwB7krDY+mdre4SFY7Gbk9VxkDg - > LgCLMoq52m+wYufP8CTgpKFpMb2/yJrbLhuJxYZrJ3qd/oYo/91k6v7xlBKEOkwD - > 2veGk9Dqi8YPNxaRktTEjnZb6ybhezat93+VVxq4Oem3wMwou1SfXrSUKtgM/p2H - > vfw/76ECgYEA2fNL9tC8u9M0wjA+kvvtDG96qO6O66Hksssy6RWInD+Iqk3MtHQt - > LeoCjvX+zERqwOb6SI6empk5pZ9E3/9vJ0dBqkxx3nqn4M/nRWnExGgngJsL959t - > f50cdxva8y1RjNhT4kCwTrupX/TP8lAG8SfG1Alo2VFR8iWd8hDQcTECgYEA0Xfj - > EgqAsVh4U0s3lFxKjOepEyp0G1Imty5J16SvcOEAD1Mrmz94aSSp0bYhXNVdbf7n - > Rk77htWC7SE29fGjOzZRS76wxj/SJHF+rktHB2Zt23k1jBeZ4uLMPMnGLY/BJ099 - > 5DTGo0yU0rrPbyXosx+ukfQLAHFuggX4RNeM5+8CgYB7M1J/hGMLcUpjcs4MXCgV - > XXbiw2c6v1r9zmtK4odEe42PZ0cNwpY/XAZyNZAAe7Q0stxL44K4NWEmxC80x7lX - > ZKozz96WOpNnO16qGC3IMHAT/JD5Or+04WTT14Ue7UEp8qcIQDTpbJ9DxKk/eglS - > jH+SIHeKULOXw7fSu7p4IQKBgBnyVchIUMSnBtCagpn4DKwDjif3nEY+GNmb/D2g - > ArNiy5UaYk5qwEmV5ws5GkzbiSU07AUDh5ieHgetk5dHhUayZcOSLWeBRFCLVnvU - > i0nZYEZNb1qZGdDG8zGcdNXz9qMd76Qy/WAA/nZT+Zn1AiweAovFxQ8a/etRPf2Z - > DbU1AoGAHpCgP7B/4GTBe49H0AQueQHBn4RIkgqMy9xiMeR+U+U0vaY0TlfLhnX+ - > 5PkNfkPXohXlfL7pxwZNYa6FZhCAubzvhKCdUASivkoGaIEk6g1VTVYS/eDVQ4CA - > slfl+elXtLq/l1kQ8C14jlHrQzSXx4PQvjDEnAmaHSJNz4mP9Fg= - > -----END RSA PRIVATE KEY----- - > EOF - - $ cat > ec.pub <<EOF - > -----BEGIN PUBLIC KEY----- - > MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE9yoUEAgxTd9svwe9oPqjhcP+f2jcdTL2 - > Wq8Aw2v9ht1dBy00tFRPNrCxFCkvMcJFhSPoDUV5NL7zfh3/psiSNYziGPrWEJYf - > gmYihjSeoOf0ru1erpBrTflImPrMftCy - > -----END PUBLIC KEY----- - > EOF - - $ cat > ec.key <<EOF - > -----BEGIN EC PRIVATE KEY----- - > MIGkAgEBBDDvoj/bM1HokUjYWO/IDFs26Jo0GIFtU3tMQQu7ZabKscDMK3dZA0mK - > v97ij7BBFbCgBwYFK4EEACKhZANiAAT3KhQQCDFN32y/B72g+qOFw/5/aNx1MvZa - > rwDDa/2G3V0HLTS0VE82sLEUKS8xwkWFI+gNRXk0vvN+Hf+myJI1jOIY+tYQlh+C - > ZiKGNJ6g5/Su7V6ukGtN+UiY+sx+0LI= - > -----END EC PRIVATE KEY----- - > EOF - -Encrypt and then decrypt a test message (RSA). - - $ echo "Lorem ipsum dolor sit amet" | - > jose-util encrypt --alg RSA-OAEP --enc A128GCM --key rsa.pub | - > jose-util decrypt --key rsa.key - Lorem ipsum dolor sit amet - -Encrypt and then decrypt a test message (EC). - - $ echo "Lorem ipsum dolor sit amet" | - > jose-util encrypt --alg ECDH-ES+A128KW --enc A128GCM --key ec.pub | - > jose-util decrypt --key ec.key - Lorem ipsum dolor sit amet - -Sign and verify a test message (RSA). - - $ echo "Lorem ipsum dolor sit amet" | - > jose-util sign --alg PS256 --key rsa.key | - > jose-util verify --key rsa.pub - Lorem ipsum dolor sit amet - -Sign and verify a test message (EC). - - $ echo "Lorem ipsum dolor sit amet" | - > jose-util sign --alg ES384 --key ec.key | - > jose-util verify --key ec.pub - Lorem ipsum dolor sit amet diff --git a/vendor/gopkg.in/square/go-jose.v1/jose-util/main.go b/vendor/gopkg.in/square/go-jose.v1/jose-util/main.go deleted file mode 100644 index 48f01091..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/jose-util/main.go +++ /dev/null @@ -1,184 +0,0 @@ -/*- - * Copyright 2014 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 main - -import ( - "fmt" - "io/ioutil" - "os" - - "gopkg.in/alecthomas/kingpin.v2" - "gopkg.in/square/go-jose.v1" -) - -var ( - app = kingpin.New("jose-util", "A command-line utility for dealing with JOSE objects.") - - keyFile = app.Flag("key", "Path to key file (PEM or DER-encoded)").Required().ExistingFile() - inFile = app.Flag("in", "Path to input file (stdin if missing)").ExistingFile() - outFile = app.Flag("out", "Path to output file (stdout if missing)").ExistingFile() - - encryptCommand = app.Command("encrypt", "Encrypt a plaintext, output ciphertext.") - algFlag = encryptCommand.Flag("alg", "Key management algorithm (e.g. RSA-OAEP)").Required().String() - encFlag = encryptCommand.Flag("enc", "Content encryption algorithm (e.g. A128GCM)").Required().String() - - decryptCommand = app.Command("decrypt", "Decrypt a ciphertext, output plaintext.") - - signCommand = app.Command("sign", "Sign a payload, output signed message.") - sigAlgFlag = signCommand.Flag("alg", "Key management algorithm (e.g. RSA-OAEP)").Required().String() - - verifyCommand = app.Command("verify", "Verify a signed message, output payload.") - - expandCommand = app.Command("expand", "Expand JOSE object to full serialization format.") - formatFlag = expandCommand.Flag("format", "Type of message to expand (JWS or JWE, defaults to JWE)").String() - - full = app.Flag("full", "Use full serialization format (instead of compact)").Bool() -) - -func main() { - app.Version("v1") - - command := kingpin.MustParse(app.Parse(os.Args[1:])) - - keyBytes, err := ioutil.ReadFile(*keyFile) - exitOnError(err, "unable to read key file") - - switch command { - case "encrypt": - pub, err := jose.LoadPublicKey(keyBytes) - exitOnError(err, "unable to read public key") - - alg := jose.KeyAlgorithm(*algFlag) - enc := jose.ContentEncryption(*encFlag) - - crypter, err := jose.NewEncrypter(alg, enc, pub) - exitOnError(err, "unable to instantiate encrypter") - - obj, err := crypter.Encrypt(readInput(*inFile)) - exitOnError(err, "unable to encrypt") - - var msg string - if *full { - msg = obj.FullSerialize() - } else { - msg, err = obj.CompactSerialize() - exitOnError(err, "unable to serialize message") - } - - writeOutput(*outFile, []byte(msg)) - case "decrypt": - priv, err := jose.LoadPrivateKey(keyBytes) - exitOnError(err, "unable to read private key") - - obj, err := jose.ParseEncrypted(string(readInput(*inFile))) - exitOnError(err, "unable to parse message") - - plaintext, err := obj.Decrypt(priv) - exitOnError(err, "unable to decrypt message") - - writeOutput(*outFile, plaintext) - case "sign": - signingKey, err := jose.LoadPrivateKey(keyBytes) - exitOnError(err, "unable to read private key") - - alg := jose.SignatureAlgorithm(*sigAlgFlag) - signer, err := jose.NewSigner(alg, signingKey) - exitOnError(err, "unable to make signer") - - obj, err := signer.Sign(readInput(*inFile)) - exitOnError(err, "unable to sign") - - var msg string - if *full { - msg = obj.FullSerialize() - } else { - msg, err = obj.CompactSerialize() - exitOnError(err, "unable to serialize message") - } - - writeOutput(*outFile, []byte(msg)) - case "verify": - verificationKey, err := jose.LoadPublicKey(keyBytes) - exitOnError(err, "unable to read private key") - - obj, err := jose.ParseSigned(string(readInput(*inFile))) - exitOnError(err, "unable to parse message") - - plaintext, err := obj.Verify(verificationKey) - exitOnError(err, "invalid signature") - - writeOutput(*outFile, plaintext) - case "expand": - input := string(readInput(*inFile)) - - var serialized string - var err error - switch *formatFlag { - case "", "JWE": - var jwe *jose.JsonWebEncryption - jwe, err = jose.ParseEncrypted(input) - if err == nil { - serialized = jwe.FullSerialize() - } - case "JWS": - var jws *jose.JsonWebSignature - jws, err = jose.ParseSigned(input) - if err == nil { - serialized = jws.FullSerialize() - } - } - - exitOnError(err, "unable to expand message") - writeOutput(*outFile, []byte(serialized)) - } -} - -// Exit and print error message if we encountered a problem -func exitOnError(err error, msg string) { - if err != nil { - fmt.Fprintf(os.Stderr, "%s: %s\n", msg, err) - os.Exit(1) - } -} - -// Read input from file or stdin -func readInput(path string) []byte { - var bytes []byte - var err error - - if path != "" { - bytes, err = ioutil.ReadFile(path) - } else { - bytes, err = ioutil.ReadAll(os.Stdin) - } - - exitOnError(err, "unable to read input") - return bytes -} - -// Write output to file or stdin -func writeOutput(path string, data []byte) { - var err error - - if path != "" { - err = ioutil.WriteFile(path, data, 0644) - } else { - _, err = os.Stdout.Write(data) - } - - exitOnError(err, "unable to write output") -} diff --git a/vendor/gopkg.in/square/go-jose.v1/json/LICENSE b/vendor/gopkg.in/square/go-jose.v1/json/LICENSE deleted file mode 100644 index 74487567..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/json/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2012 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/gopkg.in/square/go-jose.v1/json/README.md b/vendor/gopkg.in/square/go-jose.v1/json/README.md deleted file mode 100644 index 86de5e55..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/json/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Safe JSON - -This repository contains a fork of the `encoding/json` package from Go 1.6. - -The following changes were made: - -* Object deserialization uses case-sensitive member name matching instead of - [case-insensitive matching](https://www.ietf.org/mail-archive/web/json/current/msg03763.html). - This is to avoid differences in the interpretation of JOSE messages between - go-jose and libraries written in other languages. -* When deserializing a JSON object, we check for duplicate keys and reject the - input whenever we detect a duplicate. Rather than trying to work with malformed - data, we prefer to reject it right away. diff --git a/vendor/gopkg.in/square/go-jose.v1/json/bench_test.go b/vendor/gopkg.in/square/go-jose.v1/json/bench_test.go deleted file mode 100644 index ed89d115..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/json/bench_test.go +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Large data benchmark. -// The JSON data is a summary of agl's changes in the -// go, webkit, and chromium open source projects. -// We benchmark converting between the JSON form -// and in-memory data structures. - -package json - -import ( - "bytes" - "compress/gzip" - "io/ioutil" - "os" - "strings" - "testing" -) - -type codeResponse struct { - Tree *codeNode `json:"tree"` - Username string `json:"username"` -} - -type codeNode struct { - Name string `json:"name"` - Kids []*codeNode `json:"kids"` - CLWeight float64 `json:"cl_weight"` - Touches int `json:"touches"` - MinT int64 `json:"min_t"` - MaxT int64 `json:"max_t"` - MeanT int64 `json:"mean_t"` -} - -var codeJSON []byte -var codeStruct codeResponse - -func codeInit() { - f, err := os.Open("testdata/code.json.gz") - if err != nil { - panic(err) - } - defer f.Close() - gz, err := gzip.NewReader(f) - if err != nil { - panic(err) - } - data, err := ioutil.ReadAll(gz) - if err != nil { - panic(err) - } - - codeJSON = data - - if err := Unmarshal(codeJSON, &codeStruct); err != nil { - panic("unmarshal code.json: " + err.Error()) - } - - if data, err = Marshal(&codeStruct); err != nil { - panic("marshal code.json: " + err.Error()) - } - - if !bytes.Equal(data, codeJSON) { - println("different lengths", len(data), len(codeJSON)) - for i := 0; i < len(data) && i < len(codeJSON); i++ { - if data[i] != codeJSON[i] { - println("re-marshal: changed at byte", i) - println("orig: ", string(codeJSON[i-10:i+10])) - println("new: ", string(data[i-10:i+10])) - break - } - } - panic("re-marshal code.json: different result") - } -} - -func BenchmarkCodeEncoder(b *testing.B) { - if codeJSON == nil { - b.StopTimer() - codeInit() - b.StartTimer() - } - enc := NewEncoder(ioutil.Discard) - for i := 0; i < b.N; i++ { - if err := enc.Encode(&codeStruct); err != nil { - b.Fatal("Encode:", err) - } - } - b.SetBytes(int64(len(codeJSON))) -} - -func BenchmarkCodeMarshal(b *testing.B) { - if codeJSON == nil { - b.StopTimer() - codeInit() - b.StartTimer() - } - for i := 0; i < b.N; i++ { - if _, err := Marshal(&codeStruct); err != nil { - b.Fatal("Marshal:", err) - } - } - b.SetBytes(int64(len(codeJSON))) -} - -func BenchmarkCodeDecoder(b *testing.B) { - if codeJSON == nil { - b.StopTimer() - codeInit() - b.StartTimer() - } - var buf bytes.Buffer - dec := NewDecoder(&buf) - var r codeResponse - for i := 0; i < b.N; i++ { - buf.Write(codeJSON) - // hide EOF - buf.WriteByte('\n') - buf.WriteByte('\n') - buf.WriteByte('\n') - if err := dec.Decode(&r); err != nil { - b.Fatal("Decode:", err) - } - } - b.SetBytes(int64(len(codeJSON))) -} - -func BenchmarkDecoderStream(b *testing.B) { - b.StopTimer() - var buf bytes.Buffer - dec := NewDecoder(&buf) - buf.WriteString(`"` + strings.Repeat("x", 1000000) + `"` + "\n\n\n") - var x interface{} - if err := dec.Decode(&x); err != nil { - b.Fatal("Decode:", err) - } - ones := strings.Repeat(" 1\n", 300000) + "\n\n\n" - b.StartTimer() - for i := 0; i < b.N; i++ { - if i%300000 == 0 { - buf.WriteString(ones) - } - x = nil - if err := dec.Decode(&x); err != nil || x != 1.0 { - b.Fatalf("Decode: %v after %d", err, i) - } - } -} - -func BenchmarkCodeUnmarshal(b *testing.B) { - if codeJSON == nil { - b.StopTimer() - codeInit() - b.StartTimer() - } - for i := 0; i < b.N; i++ { - var r codeResponse - if err := Unmarshal(codeJSON, &r); err != nil { - b.Fatal("Unmmarshal:", err) - } - } - b.SetBytes(int64(len(codeJSON))) -} - -func BenchmarkCodeUnmarshalReuse(b *testing.B) { - if codeJSON == nil { - b.StopTimer() - codeInit() - b.StartTimer() - } - var r codeResponse - for i := 0; i < b.N; i++ { - if err := Unmarshal(codeJSON, &r); err != nil { - b.Fatal("Unmmarshal:", err) - } - } -} - -func BenchmarkUnmarshalString(b *testing.B) { - data := []byte(`"hello, world"`) - var s string - - for i := 0; i < b.N; i++ { - if err := Unmarshal(data, &s); err != nil { - b.Fatal("Unmarshal:", err) - } - } -} - -func BenchmarkUnmarshalFloat64(b *testing.B) { - var f float64 - data := []byte(`3.14`) - - for i := 0; i < b.N; i++ { - if err := Unmarshal(data, &f); err != nil { - b.Fatal("Unmarshal:", err) - } - } -} - -func BenchmarkUnmarshalInt64(b *testing.B) { - var x int64 - data := []byte(`3`) - - for i := 0; i < b.N; i++ { - if err := Unmarshal(data, &x); err != nil { - b.Fatal("Unmarshal:", err) - } - } -} - -func BenchmarkIssue10335(b *testing.B) { - b.ReportAllocs() - var s struct{} - j := []byte(`{"a":{ }}`) - for n := 0; n < b.N; n++ { - if err := Unmarshal(j, &s); err != nil { - b.Fatal(err) - } - } -} diff --git a/vendor/gopkg.in/square/go-jose.v1/json/decode.go b/vendor/gopkg.in/square/go-jose.v1/json/decode.go deleted file mode 100644 index 37457e5a..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/json/decode.go +++ /dev/null @@ -1,1183 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Represents JSON data structure using native Go types: booleans, floats, -// strings, arrays, and maps. - -package json - -import ( - "bytes" - "encoding" - "encoding/base64" - "errors" - "fmt" - "reflect" - "runtime" - "strconv" - "unicode" - "unicode/utf16" - "unicode/utf8" -) - -// Unmarshal parses the JSON-encoded data and stores the result -// in the value pointed to by v. -// -// Unmarshal uses the inverse of the encodings that -// Marshal uses, allocating maps, slices, and pointers as necessary, -// with the following additional rules: -// -// To unmarshal JSON into a pointer, Unmarshal first handles the case of -// the JSON being the JSON literal null. In that case, Unmarshal sets -// the pointer to nil. Otherwise, Unmarshal unmarshals the JSON into -// the value pointed at by the pointer. If the pointer is nil, Unmarshal -// allocates a new value for it to point to. -// -// To unmarshal JSON into a struct, Unmarshal matches incoming object -// keys to the keys used by Marshal (either the struct field name or its tag), -// preferring an exact match but also accepting a case-insensitive match. -// Unmarshal will only set exported fields of the struct. -// -// To unmarshal JSON into an interface value, -// Unmarshal stores one of these in the interface value: -// -// bool, for JSON booleans -// float64, for JSON numbers -// string, for JSON strings -// []interface{}, for JSON arrays -// map[string]interface{}, for JSON objects -// nil for JSON null -// -// To unmarshal a JSON array into a slice, Unmarshal resets the slice length -// to zero and then appends each element to the slice. -// As a special case, to unmarshal an empty JSON array into a slice, -// Unmarshal replaces the slice with a new empty slice. -// -// To unmarshal a JSON array into a Go array, Unmarshal decodes -// JSON array elements into corresponding Go array elements. -// If the Go array is smaller than the JSON array, -// the additional JSON array elements are discarded. -// If the JSON array is smaller than the Go array, -// the additional Go array elements are set to zero values. -// -// To unmarshal a JSON object into a string-keyed map, Unmarshal first -// establishes a map to use, If the map is nil, Unmarshal allocates a new map. -// Otherwise Unmarshal reuses the existing map, keeping existing entries. -// Unmarshal then stores key-value pairs from the JSON object into the map. -// -// If a JSON value is not appropriate for a given target type, -// or if a JSON number overflows the target type, Unmarshal -// skips that field and completes the unmarshaling as best it can. -// If no more serious errors are encountered, Unmarshal returns -// an UnmarshalTypeError describing the earliest such error. -// -// 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 -// 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 - // before discovering a JSON syntax error. - var d decodeState - err := checkValid(data, &d.scan) - if err != nil { - return err - } - - d.init(data) - return d.unmarshal(v) -} - -// Unmarshaler is the interface implemented by objects -// that can unmarshal a JSON description of themselves. -// The input can be assumed to be a valid encoding of -// a JSON value. UnmarshalJSON must copy the JSON data -// if it wishes to retain the data after returning. -type Unmarshaler interface { - UnmarshalJSON([]byte) error -} - -// An UnmarshalTypeError describes a JSON value that was -// not appropriate for a value of a specific Go type. -type UnmarshalTypeError struct { - Value string // description of JSON value - "bool", "array", "number -5" - Type reflect.Type // type of Go value it could not be assigned to - Offset int64 // error occurred after reading Offset bytes -} - -func (e *UnmarshalTypeError) Error() string { - return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String() -} - -// An UnmarshalFieldError describes a JSON object key that -// led to an unexported (and therefore unwritable) struct field. -// (No longer used; kept for compatibility.) -type UnmarshalFieldError struct { - Key string - Type reflect.Type - Field reflect.StructField -} - -func (e *UnmarshalFieldError) Error() string { - return "json: cannot unmarshal object key " + strconv.Quote(e.Key) + " into unexported field " + e.Field.Name + " of type " + e.Type.String() -} - -// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal. -// (The argument to Unmarshal must be a non-nil pointer.) -type InvalidUnmarshalError struct { - Type reflect.Type -} - -func (e *InvalidUnmarshalError) Error() string { - if e.Type == nil { - return "json: Unmarshal(nil)" - } - - if e.Type.Kind() != reflect.Ptr { - return "json: Unmarshal(non-pointer " + e.Type.String() + ")" - } - return "json: Unmarshal(nil " + e.Type.String() + ")" -} - -func (d *decodeState) unmarshal(v interface{}) (err error) { - defer func() { - if r := recover(); r != nil { - if _, ok := r.(runtime.Error); ok { - panic(r) - } - err = r.(error) - } - }() - - rv := reflect.ValueOf(v) - if rv.Kind() != reflect.Ptr || rv.IsNil() { - return &InvalidUnmarshalError{reflect.TypeOf(v)} - } - - d.scan.reset() - // We decode rv not rv.Elem because the Unmarshaler interface - // test must be applied at the top level of the value. - d.value(rv) - return d.savedError -} - -// A Number represents a JSON number literal. -type Number string - -// String returns the literal text of the number. -func (n Number) String() string { return string(n) } - -// Float64 returns the number as a float64. -func (n Number) Float64() (float64, error) { - return strconv.ParseFloat(string(n), 64) -} - -// Int64 returns the number as an int64. -func (n Number) Int64() (int64, error) { - return strconv.ParseInt(string(n), 10, 64) -} - -// isValidNumber reports whether s is a valid JSON number literal. -func isValidNumber(s string) bool { - // This function implements the JSON numbers grammar. - // See https://tools.ietf.org/html/rfc7159#section-6 - // and http://json.org/number.gif - - if s == "" { - return false - } - - // Optional - - if s[0] == '-' { - s = s[1:] - if s == "" { - return false - } - } - - // Digits - switch { - default: - return false - - case s[0] == '0': - s = s[1:] - - case '1' <= s[0] && s[0] <= '9': - s = s[1:] - for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { - s = s[1:] - } - } - - // . followed by 1 or more digits. - if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' { - s = s[2:] - for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { - s = s[1:] - } - } - - // e or E followed by an optional - or + and - // 1 or more digits. - if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') { - s = s[1:] - if s[0] == '+' || s[0] == '-' { - s = s[1:] - if s == "" { - return false - } - } - for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { - s = s[1:] - } - } - - // Make sure we are at the end. - return s == "" -} - -// decodeState represents the state while decoding a JSON value. -type decodeState struct { - data []byte - off int // read offset in data - scan scanner - nextscan scanner // for calls to nextValue - savedError error - useNumber bool -} - -// errPhase is used for errors that should not happen unless -// there is a bug in the JSON decoder or something is editing -// the data slice while the decoder executes. -var errPhase = errors.New("JSON decoder out of sync - data changing underfoot?") - -func (d *decodeState) init(data []byte) *decodeState { - d.data = data - d.off = 0 - d.savedError = nil - return d -} - -// error aborts the decoding by panicking with err. -func (d *decodeState) error(err error) { - panic(err) -} - -// saveError saves the first err it is called with, -// for reporting at the end of the unmarshal. -func (d *decodeState) saveError(err error) { - if d.savedError == nil { - d.savedError = err - } -} - -// next cuts off and returns the next full JSON value in d.data[d.off:]. -// The next value is known to be an object or array, not a literal. -func (d *decodeState) next() []byte { - c := d.data[d.off] - item, rest, err := nextValue(d.data[d.off:], &d.nextscan) - if err != nil { - d.error(err) - } - d.off = len(d.data) - len(rest) - - // Our scanner has seen the opening brace/bracket - // and thinks we're still in the middle of the object. - // invent a closing brace/bracket to get it out. - if c == '{' { - d.scan.step(&d.scan, '}') - } else { - d.scan.step(&d.scan, ']') - } - - return item -} - -// scanWhile processes bytes in d.data[d.off:] until it -// receives a scan code not equal to op. -// It updates d.off and returns the new scan code. -func (d *decodeState) scanWhile(op int) int { - var newOp int - for { - if d.off >= len(d.data) { - newOp = d.scan.eof() - d.off = len(d.data) + 1 // mark processed EOF with len+1 - } else { - c := d.data[d.off] - d.off++ - newOp = d.scan.step(&d.scan, c) - } - if newOp != op { - break - } - } - return newOp -} - -// value decodes a JSON value from d.data[d.off:] into the value. -// it updates d.off to point past the decoded value. -func (d *decodeState) value(v reflect.Value) { - if !v.IsValid() { - _, rest, err := nextValue(d.data[d.off:], &d.nextscan) - if err != nil { - d.error(err) - } - d.off = len(d.data) - len(rest) - - // d.scan thinks we're still at the beginning of the item. - // Feed in an empty string - the shortest, simplest value - - // so that it knows we got to the end of the value. - if d.scan.redo { - // rewind. - d.scan.redo = false - d.scan.step = stateBeginValue - } - d.scan.step(&d.scan, '"') - d.scan.step(&d.scan, '"') - - n := len(d.scan.parseState) - if n > 0 && d.scan.parseState[n-1] == parseObjectKey { - // d.scan thinks we just read an object key; finish the object - d.scan.step(&d.scan, ':') - d.scan.step(&d.scan, '"') - d.scan.step(&d.scan, '"') - d.scan.step(&d.scan, '}') - } - - return - } - - switch op := d.scanWhile(scanSkipSpace); op { - default: - d.error(errPhase) - - case scanBeginArray: - d.array(v) - - case scanBeginObject: - d.object(v) - - case scanBeginLiteral: - d.literal(v) - } -} - -type unquotedValue struct{} - -// valueQuoted is like value but decodes a -// quoted string literal or literal null into an interface value. -// If it finds anything other than a quoted string literal or null, -// valueQuoted returns unquotedValue{}. -func (d *decodeState) valueQuoted() interface{} { - switch op := d.scanWhile(scanSkipSpace); op { - default: - d.error(errPhase) - - case scanBeginArray: - d.array(reflect.Value{}) - - case scanBeginObject: - d.object(reflect.Value{}) - - case scanBeginLiteral: - switch v := d.literalInterface().(type) { - case nil, string: - return v - } - } - return unquotedValue{} -} - -// indirect walks down v allocating pointers as needed, -// until it gets to a non-pointer. -// if it encounters an Unmarshaler, indirect stops and returns that. -// if decodingNull is true, indirect stops at the last pointer so it can be set to nil. -func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, encoding.TextUnmarshaler, reflect.Value) { - // If v is a named type and is addressable, - // start with its address, so that if the type has pointer methods, - // we find them. - if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() { - v = v.Addr() - } - for { - // Load value from interface, but only if the result will be - // usefully addressable. - if v.Kind() == reflect.Interface && !v.IsNil() { - e := v.Elem() - if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) { - v = e - continue - } - } - - if v.Kind() != reflect.Ptr { - break - } - - if v.Elem().Kind() != reflect.Ptr && decodingNull && v.CanSet() { - break - } - if v.IsNil() { - v.Set(reflect.New(v.Type().Elem())) - } - if v.Type().NumMethod() > 0 { - if u, ok := v.Interface().(Unmarshaler); ok { - return u, nil, reflect.Value{} - } - if u, ok := v.Interface().(encoding.TextUnmarshaler); ok { - return nil, u, reflect.Value{} - } - } - v = v.Elem() - } - return nil, nil, v -} - -// array consumes an array from d.data[d.off-1:], decoding into the value v. -// the first byte of the array ('[') has been read already. -func (d *decodeState) array(v reflect.Value) { - // Check for unmarshaler. - u, ut, pv := d.indirect(v, false) - if u != nil { - d.off-- - err := u.UnmarshalJSON(d.next()) - if err != nil { - d.error(err) - } - return - } - if ut != nil { - d.saveError(&UnmarshalTypeError{"array", v.Type(), int64(d.off)}) - d.off-- - d.next() - return - } - - v = pv - - // Check type of target. - switch v.Kind() { - case reflect.Interface: - if v.NumMethod() == 0 { - // Decoding into nil interface? Switch to non-reflect code. - v.Set(reflect.ValueOf(d.arrayInterface())) - return - } - // Otherwise it's invalid. - fallthrough - default: - d.saveError(&UnmarshalTypeError{"array", v.Type(), int64(d.off)}) - d.off-- - d.next() - return - case reflect.Array: - case reflect.Slice: - break - } - - i := 0 - for { - // Look ahead for ] - can only happen on first iteration. - op := d.scanWhile(scanSkipSpace) - if op == scanEndArray { - break - } - - // Back up so d.value can have the byte we just read. - d.off-- - d.scan.undo(op) - - // Get element of array, growing if necessary. - if v.Kind() == reflect.Slice { - // Grow slice if necessary - if i >= v.Cap() { - newcap := v.Cap() + v.Cap()/2 - if newcap < 4 { - newcap = 4 - } - newv := reflect.MakeSlice(v.Type(), v.Len(), newcap) - reflect.Copy(newv, v) - v.Set(newv) - } - if i >= v.Len() { - v.SetLen(i + 1) - } - } - - if i < v.Len() { - // Decode into element. - d.value(v.Index(i)) - } else { - // Ran out of fixed array: skip. - d.value(reflect.Value{}) - } - i++ - - // Next token must be , or ]. - op = d.scanWhile(scanSkipSpace) - if op == scanEndArray { - break - } - if op != scanArrayValue { - d.error(errPhase) - } - } - - if i < v.Len() { - if v.Kind() == reflect.Array { - // Array. Zero the rest. - z := reflect.Zero(v.Type().Elem()) - for ; i < v.Len(); i++ { - v.Index(i).Set(z) - } - } else { - v.SetLen(i) - } - } - if i == 0 && v.Kind() == reflect.Slice { - v.Set(reflect.MakeSlice(v.Type(), 0, 0)) - } -} - -var nullLiteral = []byte("null") - -// object consumes an object from d.data[d.off-1:], decoding into the value v. -// the first byte ('{') of the object has been read already. -func (d *decodeState) object(v reflect.Value) { - // Check for unmarshaler. - u, ut, pv := d.indirect(v, false) - if u != nil { - d.off-- - err := u.UnmarshalJSON(d.next()) - if err != nil { - d.error(err) - } - return - } - if ut != nil { - d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) - d.off-- - d.next() // skip over { } in input - return - } - v = pv - - // Decoding into nil interface? Switch to non-reflect code. - if v.Kind() == reflect.Interface && v.NumMethod() == 0 { - v.Set(reflect.ValueOf(d.objectInterface())) - return - } - - // Check type of target: struct or map[string]T - switch v.Kind() { - case reflect.Map: - // map must have string kind - t := v.Type() - if t.Key().Kind() != reflect.String { - d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) - d.off-- - d.next() // skip over { } in input - return - } - if v.IsNil() { - v.Set(reflect.MakeMap(t)) - } - case reflect.Struct: - - default: - d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) - d.off-- - d.next() // skip over { } in input - return - } - - var mapElem reflect.Value - keys := map[string]bool{} - - for { - // Read opening " of string key or closing }. - op := d.scanWhile(scanSkipSpace) - if op == scanEndObject { - // closing } - can only happen on first iteration. - break - } - if op != scanBeginLiteral { - d.error(errPhase) - } - - // Read key. - start := d.off - 1 - op = d.scanWhile(scanContinue) - item := d.data[start : d.off-1] - key, ok := unquote(item) - if !ok { - d.error(errPhase) - } - - // Check for duplicate keys. - _, ok = keys[key] - if !ok { - keys[key] = true - } else { - d.error(fmt.Errorf("json: duplicate key '%s' in object", key)) - } - - // Figure out field corresponding to key. - var subv reflect.Value - destring := false // whether the value is wrapped in a string to be decoded first - - if v.Kind() == reflect.Map { - elemType := v.Type().Elem() - if !mapElem.IsValid() { - mapElem = reflect.New(elemType).Elem() - } else { - mapElem.Set(reflect.Zero(elemType)) - } - subv = mapElem - } else { - var f *field - fields := cachedTypeFields(v.Type()) - for i := range fields { - ff := &fields[i] - if bytes.Equal(ff.nameBytes, []byte(key)) { - f = ff - break - } - } - if f != nil { - subv = v - destring = f.quoted - for _, i := range f.index { - if subv.Kind() == reflect.Ptr { - if subv.IsNil() { - subv.Set(reflect.New(subv.Type().Elem())) - } - subv = subv.Elem() - } - subv = subv.Field(i) - } - } - } - - // Read : before value. - if op == scanSkipSpace { - op = d.scanWhile(scanSkipSpace) - } - if op != scanObjectKey { - d.error(errPhase) - } - - // Read value. - if destring { - switch qv := d.valueQuoted().(type) { - case nil: - d.literalStore(nullLiteral, subv, false) - case string: - d.literalStore([]byte(qv), subv, true) - default: - d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal unquoted value into %v", subv.Type())) - } - } else { - d.value(subv) - } - - // Write value back to map; - // if using struct, subv points into struct already. - if v.Kind() == reflect.Map { - kv := reflect.ValueOf(key).Convert(v.Type().Key()) - v.SetMapIndex(kv, subv) - } - - // Next token must be , or }. - op = d.scanWhile(scanSkipSpace) - if op == scanEndObject { - break - } - if op != scanObjectValue { - d.error(errPhase) - } - } -} - -// literal consumes a literal from d.data[d.off-1:], decoding into the value v. -// The first byte of the literal has been read already -// (that's how the caller knows it's a literal). -func (d *decodeState) literal(v reflect.Value) { - // All bytes inside literal return scanContinue op code. - start := d.off - 1 - op := d.scanWhile(scanContinue) - - // Scan read one byte too far; back up. - d.off-- - d.scan.undo(op) - - d.literalStore(d.data[start:d.off], v, false) -} - -// convertNumber converts the number literal s to a float64 or a Number -// depending on the setting of d.useNumber. -func (d *decodeState) convertNumber(s string) (interface{}, error) { - if d.useNumber { - return Number(s), nil - } - f, err := strconv.ParseFloat(s, 64) - if err != nil { - return nil, &UnmarshalTypeError{"number " + s, reflect.TypeOf(0.0), int64(d.off)} - } - return f, nil -} - -var numberType = reflect.TypeOf(Number("")) - -// literalStore decodes a literal stored in item into v. -// -// fromQuoted indicates whether this literal came from unwrapping a -// string from the ",string" struct tag option. this is used only to -// produce more helpful error messages. -func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool) { - // Check for unmarshaler. - if len(item) == 0 { - //Empty string given - d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - return - } - wantptr := item[0] == 'n' // null - u, ut, pv := d.indirect(v, wantptr) - if u != nil { - err := u.UnmarshalJSON(item) - if err != nil { - d.error(err) - } - return - } - if ut != nil { - if item[0] != '"' { - if fromQuoted { - d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - } else { - d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)}) - } - return - } - s, ok := unquoteBytes(item) - if !ok { - if fromQuoted { - d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - } else { - d.error(errPhase) - } - } - err := ut.UnmarshalText(s) - if err != nil { - d.error(err) - } - return - } - - v = pv - - switch c := item[0]; c { - case 'n': // null - switch v.Kind() { - case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: - v.Set(reflect.Zero(v.Type())) - // otherwise, ignore null for primitives/string - } - case 't', 'f': // true, false - value := c == 't' - switch v.Kind() { - default: - if fromQuoted { - d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - } else { - d.saveError(&UnmarshalTypeError{"bool", v.Type(), int64(d.off)}) - } - case reflect.Bool: - v.SetBool(value) - case reflect.Interface: - if v.NumMethod() == 0 { - v.Set(reflect.ValueOf(value)) - } else { - d.saveError(&UnmarshalTypeError{"bool", v.Type(), int64(d.off)}) - } - } - - case '"': // string - s, ok := unquoteBytes(item) - if !ok { - if fromQuoted { - d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - } else { - d.error(errPhase) - } - } - switch v.Kind() { - default: - d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)}) - case reflect.Slice: - if v.Type().Elem().Kind() != reflect.Uint8 { - d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)}) - break - } - b := make([]byte, base64.StdEncoding.DecodedLen(len(s))) - n, err := base64.StdEncoding.Decode(b, s) - if err != nil { - d.saveError(err) - break - } - v.SetBytes(b[:n]) - case reflect.String: - v.SetString(string(s)) - case reflect.Interface: - if v.NumMethod() == 0 { - v.Set(reflect.ValueOf(string(s))) - } else { - d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)}) - } - } - - default: // number - if c != '-' && (c < '0' || c > '9') { - if fromQuoted { - d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - } else { - d.error(errPhase) - } - } - s := string(item) - switch v.Kind() { - default: - if v.Kind() == reflect.String && v.Type() == numberType { - v.SetString(s) - if !isValidNumber(s) { - d.error(fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", item)) - } - break - } - if fromQuoted { - d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - } else { - d.error(&UnmarshalTypeError{"number", v.Type(), int64(d.off)}) - } - case reflect.Interface: - n, err := d.convertNumber(s) - if err != nil { - d.saveError(err) - break - } - if v.NumMethod() != 0 { - d.saveError(&UnmarshalTypeError{"number", v.Type(), int64(d.off)}) - break - } - v.Set(reflect.ValueOf(n)) - - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - n, err := strconv.ParseInt(s, 10, 64) - if err != nil || v.OverflowInt(n) { - d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)}) - break - } - v.SetInt(n) - - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - n, err := strconv.ParseUint(s, 10, 64) - if err != nil || v.OverflowUint(n) { - d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)}) - break - } - v.SetUint(n) - - case reflect.Float32, reflect.Float64: - n, err := strconv.ParseFloat(s, v.Type().Bits()) - if err != nil || v.OverflowFloat(n) { - d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)}) - break - } - v.SetFloat(n) - } - } -} - -// The xxxInterface routines build up a value to be stored -// in an empty interface. They are not strictly necessary, -// but they avoid the weight of reflection in this common case. - -// valueInterface is like value but returns interface{} -func (d *decodeState) valueInterface() interface{} { - switch d.scanWhile(scanSkipSpace) { - default: - d.error(errPhase) - panic("unreachable") - case scanBeginArray: - return d.arrayInterface() - case scanBeginObject: - return d.objectInterface() - case scanBeginLiteral: - return d.literalInterface() - } -} - -// arrayInterface is like array but returns []interface{}. -func (d *decodeState) arrayInterface() []interface{} { - var v = make([]interface{}, 0) - for { - // Look ahead for ] - can only happen on first iteration. - op := d.scanWhile(scanSkipSpace) - if op == scanEndArray { - break - } - - // Back up so d.value can have the byte we just read. - d.off-- - d.scan.undo(op) - - v = append(v, d.valueInterface()) - - // Next token must be , or ]. - op = d.scanWhile(scanSkipSpace) - if op == scanEndArray { - break - } - if op != scanArrayValue { - d.error(errPhase) - } - } - return v -} - -// objectInterface is like object but returns map[string]interface{}. -func (d *decodeState) objectInterface() map[string]interface{} { - m := make(map[string]interface{}) - keys := map[string]bool{} - - for { - // Read opening " of string key or closing }. - op := d.scanWhile(scanSkipSpace) - if op == scanEndObject { - // closing } - can only happen on first iteration. - break - } - if op != scanBeginLiteral { - d.error(errPhase) - } - - // Read string key. - start := d.off - 1 - op = d.scanWhile(scanContinue) - item := d.data[start : d.off-1] - key, ok := unquote(item) - if !ok { - d.error(errPhase) - } - - // Check for duplicate keys. - _, ok = keys[key] - if !ok { - keys[key] = true - } else { - d.error(fmt.Errorf("json: duplicate key '%s' in object", key)) - } - - // Read : before value. - if op == scanSkipSpace { - op = d.scanWhile(scanSkipSpace) - } - if op != scanObjectKey { - d.error(errPhase) - } - - // Read value. - m[key] = d.valueInterface() - - // Next token must be , or }. - op = d.scanWhile(scanSkipSpace) - if op == scanEndObject { - break - } - if op != scanObjectValue { - d.error(errPhase) - } - } - return m -} - -// literalInterface is like literal but returns an interface value. -func (d *decodeState) literalInterface() interface{} { - // All bytes inside literal return scanContinue op code. - start := d.off - 1 - op := d.scanWhile(scanContinue) - - // Scan read one byte too far; back up. - d.off-- - d.scan.undo(op) - item := d.data[start:d.off] - - switch c := item[0]; c { - case 'n': // null - return nil - - case 't', 'f': // true, false - return c == 't' - - case '"': // string - s, ok := unquote(item) - if !ok { - d.error(errPhase) - } - return s - - default: // number - if c != '-' && (c < '0' || c > '9') { - d.error(errPhase) - } - n, err := d.convertNumber(string(item)) - if err != nil { - d.saveError(err) - } - return n - } -} - -// getu4 decodes \uXXXX from the beginning of s, returning the hex value, -// or it returns -1. -func getu4(s []byte) rune { - if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { - return -1 - } - r, err := strconv.ParseUint(string(s[2:6]), 16, 64) - if err != nil { - return -1 - } - return rune(r) -} - -// unquote converts a quoted JSON string literal s into an actual string t. -// The rules are different than for Go, so cannot use strconv.Unquote. -func unquote(s []byte) (t string, ok bool) { - s, ok = unquoteBytes(s) - t = string(s) - return -} - -func unquoteBytes(s []byte) (t []byte, ok bool) { - if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { - return - } - s = s[1 : len(s)-1] - - // Check for unusual characters. If there are none, - // then no unquoting is needed, so return a slice of the - // original bytes. - r := 0 - for r < len(s) { - c := s[r] - if c == '\\' || c == '"' || c < ' ' { - break - } - if c < utf8.RuneSelf { - r++ - continue - } - rr, size := utf8.DecodeRune(s[r:]) - if rr == utf8.RuneError && size == 1 { - break - } - r += size - } - if r == len(s) { - return s, true - } - - b := make([]byte, len(s)+2*utf8.UTFMax) - w := copy(b, s[0:r]) - for r < len(s) { - // Out of room? Can only happen if s is full of - // malformed UTF-8 and we're replacing each - // byte with RuneError. - if w >= len(b)-2*utf8.UTFMax { - nb := make([]byte, (len(b)+utf8.UTFMax)*2) - copy(nb, b[0:w]) - b = nb - } - switch c := s[r]; { - case c == '\\': - r++ - if r >= len(s) { - return - } - switch s[r] { - default: - return - case '"', '\\', '/', '\'': - b[w] = s[r] - r++ - w++ - case 'b': - b[w] = '\b' - r++ - w++ - case 'f': - b[w] = '\f' - r++ - w++ - case 'n': - b[w] = '\n' - r++ - w++ - case 'r': - b[w] = '\r' - r++ - w++ - case 't': - b[w] = '\t' - r++ - w++ - case 'u': - r-- - rr := getu4(s[r:]) - if rr < 0 { - return - } - r += 6 - if utf16.IsSurrogate(rr) { - rr1 := getu4(s[r:]) - if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar { - // A valid pair; consume. - r += 6 - w += utf8.EncodeRune(b[w:], dec) - break - } - // Invalid surrogate; fall back to replacement rune. - rr = unicode.ReplacementChar - } - w += utf8.EncodeRune(b[w:], rr) - } - - // Quote, control characters are invalid. - case c == '"', c < ' ': - return - - // ASCII - case c < utf8.RuneSelf: - b[w] = c - r++ - w++ - - // Coerce to well-formed UTF-8. - default: - rr, size := utf8.DecodeRune(s[r:]) - r += size - w += utf8.EncodeRune(b[w:], rr) - } - } - return b[0:w], true -} diff --git a/vendor/gopkg.in/square/go-jose.v1/json/decode_test.go b/vendor/gopkg.in/square/go-jose.v1/json/decode_test.go deleted file mode 100644 index 7577b21a..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/json/decode_test.go +++ /dev/null @@ -1,1474 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package json - -import ( - "bytes" - "encoding" - "fmt" - "image" - "net" - "reflect" - "strings" - "testing" - "time" -) - -type T struct { - X string - Y int - Z int `json:"-"` -} - -type U struct { - Alphabet string `json:"alpha"` -} - -type V struct { - F1 interface{} - F2 int32 - F3 Number -} - -// ifaceNumAsFloat64/ifaceNumAsNumber are used to test unmarshaling with and -// without UseNumber -var ifaceNumAsFloat64 = map[string]interface{}{ - "k1": float64(1), - "k2": "s", - "k3": []interface{}{float64(1), float64(2.0), float64(3e-3)}, - "k4": map[string]interface{}{"kk1": "s", "kk2": float64(2)}, -} - -var ifaceNumAsNumber = map[string]interface{}{ - "k1": Number("1"), - "k2": "s", - "k3": []interface{}{Number("1"), Number("2.0"), Number("3e-3")}, - "k4": map[string]interface{}{"kk1": "s", "kk2": Number("2")}, -} - -type tx struct { - x int -} - -// A type that can unmarshal itself. - -type unmarshaler struct { - T bool -} - -func (u *unmarshaler) UnmarshalJSON(b []byte) error { - *u = unmarshaler{true} // All we need to see that UnmarshalJSON is called. - return nil -} - -type ustruct struct { - M unmarshaler -} - -type unmarshalerText struct { - T bool -} - -// needed for re-marshaling tests -func (u *unmarshalerText) MarshalText() ([]byte, error) { - return []byte(""), nil -} - -func (u *unmarshalerText) UnmarshalText(b []byte) error { - *u = unmarshalerText{true} // All we need to see that UnmarshalText is called. - return nil -} - -var _ encoding.TextUnmarshaler = (*unmarshalerText)(nil) - -type ustructText struct { - M unmarshalerText -} - -var ( - um0, um1 unmarshaler // target2 of unmarshaling - ump = &um1 - umtrue = unmarshaler{true} - umslice = []unmarshaler{{true}} - umslicep = new([]unmarshaler) - umstruct = ustruct{unmarshaler{true}} - - um0T, um1T unmarshalerText // target2 of unmarshaling - umpT = &um1T - umtrueT = unmarshalerText{true} - umsliceT = []unmarshalerText{{true}} - umslicepT = new([]unmarshalerText) - umstructT = ustructText{unmarshalerText{true}} -) - -// Test data structures for anonymous fields. - -type Point struct { - Z int -} - -type Top struct { - Level0 int - Embed0 - *Embed0a - *Embed0b `json:"e,omitempty"` // treated as named - Embed0c `json:"-"` // ignored - Loop - Embed0p // has Point with X, Y, used - Embed0q // has Point with Z, used - embed // contains exported field -} - -type Embed0 struct { - Level1a int // overridden by Embed0a's Level1a with json tag - Level1b int // used because Embed0a's Level1b is renamed - Level1c int // used because Embed0a's Level1c is ignored - Level1d int // annihilated by Embed0a's Level1d - Level1e int `json:"x"` // annihilated by Embed0a.Level1e -} - -type Embed0a struct { - Level1a int `json:"Level1a,omitempty"` - Level1b int `json:"LEVEL1B,omitempty"` - Level1c int `json:"-"` - Level1d int // annihilated by Embed0's Level1d - Level1f int `json:"x"` // annihilated by Embed0's Level1e -} - -type Embed0b Embed0 - -type Embed0c Embed0 - -type Embed0p struct { - image.Point -} - -type Embed0q struct { - Point -} - -type embed struct { - Q int -} - -type Loop struct { - Loop1 int `json:",omitempty"` - Loop2 int `json:",omitempty"` - *Loop -} - -// From reflect test: -// The X in S6 and S7 annihilate, but they also block the X in S8.S9. -type S5 struct { - S6 - S7 - S8 -} - -type S6 struct { - X int -} - -type S7 S6 - -type S8 struct { - S9 -} - -type S9 struct { - X int - Y int -} - -// From reflect test: -// The X in S11.S6 and S12.S6 annihilate, but they also block the X in S13.S8.S9. -type S10 struct { - S11 - S12 - S13 -} - -type S11 struct { - S6 -} - -type S12 struct { - S6 -} - -type S13 struct { - S8 -} - -type unmarshalTest struct { - in string - ptr interface{} - out interface{} - err error - useNumber bool -} - -type XYZ struct { - X interface{} - Y interface{} - Z interface{} -} - -func sliceAddr(x []int) *[]int { return &x } -func mapAddr(x map[string]int) *map[string]int { return &x } - -var unmarshalTests = []unmarshalTest{ - // basic types - {in: `true`, ptr: new(bool), out: true}, - {in: `1`, ptr: new(int), out: 1}, - {in: `1.2`, ptr: new(float64), out: 1.2}, - {in: `-5`, ptr: new(int16), out: int16(-5)}, - {in: `2`, ptr: new(Number), out: Number("2"), useNumber: true}, - {in: `2`, ptr: new(Number), out: Number("2")}, - {in: `2`, ptr: new(interface{}), out: float64(2.0)}, - {in: `2`, ptr: new(interface{}), out: Number("2"), useNumber: true}, - {in: `"a\u1234"`, ptr: new(string), out: "a\u1234"}, - {in: `"http:\/\/"`, ptr: new(string), out: "http://"}, - {in: `"g-clef: \uD834\uDD1E"`, ptr: new(string), out: "g-clef: \U0001D11E"}, - {in: `"invalid: \uD834x\uDD1E"`, ptr: new(string), out: "invalid: \uFFFDx\uFFFD"}, - {in: "null", ptr: new(interface{}), out: nil}, - {in: `{"X": [1,2,3], "Y": 4}`, ptr: new(T), out: T{Y: 4}, err: &UnmarshalTypeError{"array", reflect.TypeOf(""), 7}}, - {in: `{"x": 1}`, ptr: new(tx), out: tx{}}, - {in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: float64(1), F2: int32(2), F3: Number("3")}}, - {in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: Number("1"), F2: int32(2), F3: Number("3")}, useNumber: true}, - {in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(interface{}), out: ifaceNumAsFloat64}, - {in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(interface{}), out: ifaceNumAsNumber, useNumber: true}, - - // raw values with whitespace - {in: "\n true ", ptr: new(bool), out: true}, - {in: "\t 1 ", ptr: new(int), out: 1}, - {in: "\r 1.2 ", ptr: new(float64), out: 1.2}, - {in: "\t -5 \n", ptr: new(int16), out: int16(-5)}, - {in: "\t \"a\\u1234\" \n", ptr: new(string), out: "a\u1234"}, - - // Z has a "-" tag. - {in: `{"Y": 1, "Z": 2}`, ptr: new(T), out: T{Y: 1}}, - - {in: `{"alpha": "abc", "alphabet": "xyz"}`, ptr: new(U), out: U{Alphabet: "abc"}}, - {in: `{"alpha": "abc"}`, ptr: new(U), out: U{Alphabet: "abc"}}, - {in: `{"alphabet": "xyz"}`, ptr: new(U), out: U{}}, - - // syntax errors - {in: `{"X": "foo", "Y"}`, err: &SyntaxError{"invalid character '}' after object key", 17}}, - {in: `[1, 2, 3+]`, err: &SyntaxError{"invalid character '+' after array element", 9}}, - {in: `{"X":12x}`, err: &SyntaxError{"invalid character 'x' after object key:value pair", 8}, useNumber: true}, - - // raw value errors - {in: "\x01 42", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}}, - {in: " 42 \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 5}}, - {in: "\x01 true", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}}, - {in: " false \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 8}}, - {in: "\x01 1.2", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}}, - {in: " 3.4 \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 6}}, - {in: "\x01 \"string\"", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}}, - {in: " \"string\" \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 11}}, - - // array tests - {in: `[1, 2, 3]`, ptr: new([3]int), out: [3]int{1, 2, 3}}, - {in: `[1, 2, 3]`, ptr: new([1]int), out: [1]int{1}}, - {in: `[1, 2, 3]`, ptr: new([5]int), out: [5]int{1, 2, 3, 0, 0}}, - - // empty array to interface test - {in: `[]`, ptr: new([]interface{}), out: []interface{}{}}, - {in: `null`, ptr: new([]interface{}), out: []interface{}(nil)}, - {in: `{"T":[]}`, ptr: new(map[string]interface{}), out: map[string]interface{}{"T": []interface{}{}}}, - {in: `{"T":null}`, ptr: new(map[string]interface{}), out: map[string]interface{}{"T": interface{}(nil)}}, - - // composite tests - {in: allValueIndent, ptr: new(All), out: allValue}, - {in: allValueCompact, ptr: new(All), out: allValue}, - {in: allValueIndent, ptr: new(*All), out: &allValue}, - {in: allValueCompact, ptr: new(*All), out: &allValue}, - {in: pallValueIndent, ptr: new(All), out: pallValue}, - {in: pallValueCompact, ptr: new(All), out: pallValue}, - {in: pallValueIndent, ptr: new(*All), out: &pallValue}, - {in: pallValueCompact, ptr: new(*All), out: &pallValue}, - - // unmarshal interface test - {in: `{"T":false}`, ptr: &um0, out: umtrue}, // use "false" so test will fail if custom unmarshaler is not called - {in: `{"T":false}`, ptr: &ump, out: &umtrue}, - {in: `[{"T":false}]`, ptr: &umslice, out: umslice}, - {in: `[{"T":false}]`, ptr: &umslicep, out: &umslice}, - {in: `{"M":{"T":false}}`, ptr: &umstruct, out: umstruct}, - - // UnmarshalText interface test - {in: `"X"`, ptr: &um0T, out: umtrueT}, // use "false" so test will fail if custom unmarshaler is not called - {in: `"X"`, ptr: &umpT, out: &umtrueT}, - {in: `["X"]`, ptr: &umsliceT, out: umsliceT}, - {in: `["X"]`, ptr: &umslicepT, out: &umsliceT}, - {in: `{"M":"X"}`, ptr: &umstructT, out: umstructT}, - - // Overwriting of data. - // This is different from package xml, but it's what we've always done. - // Now documented and tested. - {in: `[2]`, ptr: sliceAddr([]int{1}), out: []int{2}}, - {in: `{"key": 2}`, ptr: mapAddr(map[string]int{"old": 0, "key": 1}), out: map[string]int{"key": 2}}, - - { - in: `{ - "Level0": 1, - "Level1b": 2, - "Level1c": 3, - "x": 4, - "Level1a": 5, - "LEVEL1B": 6, - "e": { - "Level1a": 8, - "Level1b": 9, - "Level1c": 10, - "Level1d": 11, - "x": 12 - }, - "Loop1": 13, - "Loop2": 14, - "X": 15, - "Y": 16, - "Z": 17, - "Q": 18 - }`, - ptr: new(Top), - out: Top{ - Level0: 1, - Embed0: Embed0{ - Level1b: 2, - Level1c: 3, - }, - Embed0a: &Embed0a{ - Level1a: 5, - Level1b: 6, - }, - Embed0b: &Embed0b{ - Level1a: 8, - Level1b: 9, - Level1c: 10, - Level1d: 11, - Level1e: 12, - }, - Loop: Loop{ - Loop1: 13, - Loop2: 14, - }, - Embed0p: Embed0p{ - Point: image.Point{X: 15, Y: 16}, - }, - Embed0q: Embed0q{ - Point: Point{Z: 17}, - }, - embed: embed{ - Q: 18, - }, - }, - }, - { - in: `{"X": 1,"Y":2}`, - ptr: new(S5), - out: S5{S8: S8{S9: S9{Y: 2}}}, - }, - { - in: `{"X": 1,"Y":2}`, - ptr: new(S10), - out: S10{S13: S13{S8: S8{S9: S9{Y: 2}}}}, - }, - - // invalid UTF-8 is coerced to valid UTF-8. - { - in: "\"hello\xffworld\"", - ptr: new(string), - out: "hello\ufffdworld", - }, - { - in: "\"hello\xc2\xc2world\"", - ptr: new(string), - out: "hello\ufffd\ufffdworld", - }, - { - in: "\"hello\xc2\xffworld\"", - ptr: new(string), - out: "hello\ufffd\ufffdworld", - }, - { - in: "\"hello\\ud800world\"", - ptr: new(string), - out: "hello\ufffdworld", - }, - { - in: "\"hello\\ud800\\ud800world\"", - ptr: new(string), - out: "hello\ufffd\ufffdworld", - }, - { - in: "\"hello\\ud800\\ud800world\"", - ptr: new(string), - out: "hello\ufffd\ufffdworld", - }, - { - in: "\"hello\xed\xa0\x80\xed\xb0\x80world\"", - ptr: new(string), - out: "hello\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdworld", - }, - - // issue 8305 - { - in: `{"2009-11-10T23:00:00Z": "hello world"}`, - ptr: &map[time.Time]string{}, - err: &UnmarshalTypeError{"object", reflect.TypeOf(map[time.Time]string{}), 1}, - }, -} - -func TestMarshal(t *testing.T) { - b, err := Marshal(allValue) - if err != nil { - t.Fatalf("Marshal allValue: %v", err) - } - if string(b) != allValueCompact { - t.Errorf("Marshal allValueCompact") - diff(t, b, []byte(allValueCompact)) - return - } - - b, err = Marshal(pallValue) - if err != nil { - t.Fatalf("Marshal pallValue: %v", err) - } - if string(b) != pallValueCompact { - t.Errorf("Marshal pallValueCompact") - diff(t, b, []byte(pallValueCompact)) - return - } -} - -var badUTF8 = []struct { - in, out string -}{ - {"hello\xffworld", `"hello\ufffdworld"`}, - {"", `""`}, - {"\xff", `"\ufffd"`}, - {"\xff\xff", `"\ufffd\ufffd"`}, - {"a\xffb", `"a\ufffdb"`}, - {"\xe6\x97\xa5\xe6\x9c\xac\xff\xaa\x9e", `"日本\ufffd\ufffd\ufffd"`}, -} - -func TestMarshalBadUTF8(t *testing.T) { - for _, tt := range badUTF8 { - b, err := Marshal(tt.in) - if string(b) != tt.out || err != nil { - t.Errorf("Marshal(%q) = %#q, %v, want %#q, nil", tt.in, b, err, tt.out) - } - } -} - -func TestMarshalNumberZeroVal(t *testing.T) { - var n Number - out, err := Marshal(n) - if err != nil { - t.Fatal(err) - } - outStr := string(out) - if outStr != "0" { - t.Fatalf("Invalid zero val for Number: %q", outStr) - } -} - -func TestMarshalEmbeds(t *testing.T) { - top := &Top{ - Level0: 1, - Embed0: Embed0{ - Level1b: 2, - Level1c: 3, - }, - Embed0a: &Embed0a{ - Level1a: 5, - Level1b: 6, - }, - Embed0b: &Embed0b{ - Level1a: 8, - Level1b: 9, - Level1c: 10, - Level1d: 11, - Level1e: 12, - }, - Loop: Loop{ - Loop1: 13, - Loop2: 14, - }, - Embed0p: Embed0p{ - Point: image.Point{X: 15, Y: 16}, - }, - Embed0q: Embed0q{ - Point: Point{Z: 17}, - }, - embed: embed{ - Q: 18, - }, - } - b, err := Marshal(top) - if err != nil { - t.Fatal(err) - } - want := "{\"Level0\":1,\"Level1b\":2,\"Level1c\":3,\"Level1a\":5,\"LEVEL1B\":6,\"e\":{\"Level1a\":8,\"Level1b\":9,\"Level1c\":10,\"Level1d\":11,\"x\":12},\"Loop1\":13,\"Loop2\":14,\"X\":15,\"Y\":16,\"Z\":17,\"Q\":18}" - if string(b) != want { - t.Errorf("Wrong marshal result.\n got: %q\nwant: %q", b, want) - } -} - -func TestUnmarshal(t *testing.T) { - for i, tt := range unmarshalTests { - var scan scanner - in := []byte(tt.in) - if err := checkValid(in, &scan); err != nil { - if !reflect.DeepEqual(err, tt.err) { - t.Errorf("#%d: checkValid: %#v", i, err) - continue - } - } - if tt.ptr == nil { - continue - } - - // v = new(right-type) - v := reflect.New(reflect.TypeOf(tt.ptr).Elem()) - dec := NewDecoder(bytes.NewReader(in)) - if tt.useNumber { - dec.UseNumber() - } - if err := dec.Decode(v.Interface()); !reflect.DeepEqual(err, tt.err) { - t.Errorf("#%d: %v, want %v", i, err, tt.err) - continue - } else if err != nil { - continue - } - if !reflect.DeepEqual(v.Elem().Interface(), tt.out) { - t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), tt.out) - data, _ := Marshal(v.Elem().Interface()) - println(string(data)) - data, _ = Marshal(tt.out) - println(string(data)) - continue - } - - // Check round trip. - if tt.err == nil { - enc, err := Marshal(v.Interface()) - if err != nil { - t.Errorf("#%d: error re-marshaling: %v", i, err) - continue - } - vv := reflect.New(reflect.TypeOf(tt.ptr).Elem()) - dec = NewDecoder(bytes.NewReader(enc)) - if tt.useNumber { - dec.UseNumber() - } - if err := dec.Decode(vv.Interface()); err != nil { - t.Errorf("#%d: error re-unmarshaling %#q: %v", i, enc, err) - continue - } - if !reflect.DeepEqual(v.Elem().Interface(), vv.Elem().Interface()) { - t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), vv.Elem().Interface()) - t.Errorf(" In: %q", strings.Map(noSpace, string(in))) - t.Errorf("Marshal: %q", strings.Map(noSpace, string(enc))) - continue - } - } - } -} - -func TestUnmarshalMarshal(t *testing.T) { - initBig() - var v interface{} - if err := Unmarshal(jsonBig, &v); err != nil { - t.Fatalf("Unmarshal: %v", err) - } - b, err := Marshal(v) - if err != nil { - t.Fatalf("Marshal: %v", err) - } - if !bytes.Equal(jsonBig, b) { - t.Errorf("Marshal jsonBig") - diff(t, b, jsonBig) - return - } -} - -var numberTests = []struct { - in string - i int64 - intErr string - f float64 - floatErr string -}{ - {in: "-1.23e1", intErr: "strconv.ParseInt: parsing \"-1.23e1\": invalid syntax", f: -1.23e1}, - {in: "-12", i: -12, f: -12.0}, - {in: "1e1000", intErr: "strconv.ParseInt: parsing \"1e1000\": invalid syntax", floatErr: "strconv.ParseFloat: parsing \"1e1000\": value out of range"}, -} - -// Independent of Decode, basic coverage of the accessors in Number -func TestNumberAccessors(t *testing.T) { - for _, tt := range numberTests { - n := Number(tt.in) - if s := n.String(); s != tt.in { - t.Errorf("Number(%q).String() is %q", tt.in, s) - } - if i, err := n.Int64(); err == nil && tt.intErr == "" && i != tt.i { - t.Errorf("Number(%q).Int64() is %d", tt.in, i) - } else if (err == nil && tt.intErr != "") || (err != nil && err.Error() != tt.intErr) { - t.Errorf("Number(%q).Int64() wanted error %q but got: %v", tt.in, tt.intErr, err) - } - if f, err := n.Float64(); err == nil && tt.floatErr == "" && f != tt.f { - t.Errorf("Number(%q).Float64() is %g", tt.in, f) - } else if (err == nil && tt.floatErr != "") || (err != nil && err.Error() != tt.floatErr) { - t.Errorf("Number(%q).Float64() wanted error %q but got: %v", tt.in, tt.floatErr, err) - } - } -} - -func TestLargeByteSlice(t *testing.T) { - s0 := make([]byte, 2000) - for i := range s0 { - s0[i] = byte(i) - } - b, err := Marshal(s0) - if err != nil { - t.Fatalf("Marshal: %v", err) - } - var s1 []byte - if err := Unmarshal(b, &s1); err != nil { - t.Fatalf("Unmarshal: %v", err) - } - if !bytes.Equal(s0, s1) { - t.Errorf("Marshal large byte slice") - diff(t, s0, s1) - } -} - -type Xint struct { - X int -} - -func TestUnmarshalInterface(t *testing.T) { - var xint Xint - var i interface{} = &xint - if err := Unmarshal([]byte(`{"X":1}`), &i); err != nil { - t.Fatalf("Unmarshal: %v", err) - } - if xint.X != 1 { - t.Fatalf("Did not write to xint") - } -} - -func TestUnmarshalPtrPtr(t *testing.T) { - var xint Xint - pxint := &xint - if err := Unmarshal([]byte(`{"X":1}`), &pxint); err != nil { - t.Fatalf("Unmarshal: %v", err) - } - if xint.X != 1 { - t.Fatalf("Did not write to xint") - } -} - -func TestEscape(t *testing.T) { - const input = `"foobar"<html>` + " [\u2028 \u2029]" - const expected = `"\"foobar\"\u003chtml\u003e [\u2028 \u2029]"` - b, err := Marshal(input) - if err != nil { - t.Fatalf("Marshal error: %v", err) - } - if s := string(b); s != expected { - t.Errorf("Encoding of [%s]:\n got [%s]\nwant [%s]", input, s, expected) - } -} - -// WrongString is a struct that's misusing the ,string modifier. -type WrongString struct { - Message string `json:"result,string"` -} - -type wrongStringTest struct { - in, err string -} - -var wrongStringTests = []wrongStringTest{ - {`{"result":"x"}`, `json: invalid use of ,string struct tag, trying to unmarshal "x" into string`}, - {`{"result":"foo"}`, `json: invalid use of ,string struct tag, trying to unmarshal "foo" into string`}, - {`{"result":"123"}`, `json: invalid use of ,string struct tag, trying to unmarshal "123" into string`}, - {`{"result":123}`, `json: invalid use of ,string struct tag, trying to unmarshal unquoted value into string`}, -} - -// If people misuse the ,string modifier, the error message should be -// helpful, telling the user that they're doing it wrong. -func TestErrorMessageFromMisusedString(t *testing.T) { - for n, tt := range wrongStringTests { - r := strings.NewReader(tt.in) - var s WrongString - err := NewDecoder(r).Decode(&s) - got := fmt.Sprintf("%v", err) - if got != tt.err { - t.Errorf("%d. got err = %q, want %q", n, got, tt.err) - } - } -} - -func noSpace(c rune) rune { - if isSpace(byte(c)) { //only used for ascii - return -1 - } - return c -} - -type All struct { - Bool bool - Int int - Int8 int8 - Int16 int16 - Int32 int32 - Int64 int64 - Uint uint - Uint8 uint8 - Uint16 uint16 - Uint32 uint32 - Uint64 uint64 - Uintptr uintptr - Float32 float32 - Float64 float64 - - Foo string `json:"bar"` - Foo2 string `json:"bar2,dummyopt"` - - IntStr int64 `json:",string"` - - PBool *bool - PInt *int - PInt8 *int8 - PInt16 *int16 - PInt32 *int32 - PInt64 *int64 - PUint *uint - PUint8 *uint8 - PUint16 *uint16 - PUint32 *uint32 - PUint64 *uint64 - PUintptr *uintptr - PFloat32 *float32 - PFloat64 *float64 - - String string - PString *string - - Map map[string]Small - MapP map[string]*Small - PMap *map[string]Small - PMapP *map[string]*Small - - EmptyMap map[string]Small - NilMap map[string]Small - - Slice []Small - SliceP []*Small - PSlice *[]Small - PSliceP *[]*Small - - EmptySlice []Small - NilSlice []Small - - StringSlice []string - ByteSlice []byte - - Small Small - PSmall *Small - PPSmall **Small - - Interface interface{} - PInterface *interface{} - - unexported int -} - -type Small struct { - Tag string -} - -var allValue = All{ - Bool: true, - Int: 2, - Int8: 3, - Int16: 4, - Int32: 5, - Int64: 6, - Uint: 7, - Uint8: 8, - Uint16: 9, - Uint32: 10, - Uint64: 11, - Uintptr: 12, - Float32: 14.1, - Float64: 15.1, - Foo: "foo", - Foo2: "foo2", - IntStr: 42, - String: "16", - Map: map[string]Small{ - "17": {Tag: "tag17"}, - "18": {Tag: "tag18"}, - }, - MapP: map[string]*Small{ - "19": {Tag: "tag19"}, - "20": nil, - }, - EmptyMap: map[string]Small{}, - Slice: []Small{{Tag: "tag20"}, {Tag: "tag21"}}, - SliceP: []*Small{{Tag: "tag22"}, nil, {Tag: "tag23"}}, - EmptySlice: []Small{}, - StringSlice: []string{"str24", "str25", "str26"}, - ByteSlice: []byte{27, 28, 29}, - Small: Small{Tag: "tag30"}, - PSmall: &Small{Tag: "tag31"}, - Interface: 5.2, -} - -var pallValue = All{ - PBool: &allValue.Bool, - PInt: &allValue.Int, - PInt8: &allValue.Int8, - PInt16: &allValue.Int16, - PInt32: &allValue.Int32, - PInt64: &allValue.Int64, - PUint: &allValue.Uint, - PUint8: &allValue.Uint8, - PUint16: &allValue.Uint16, - PUint32: &allValue.Uint32, - PUint64: &allValue.Uint64, - PUintptr: &allValue.Uintptr, - PFloat32: &allValue.Float32, - PFloat64: &allValue.Float64, - PString: &allValue.String, - PMap: &allValue.Map, - PMapP: &allValue.MapP, - PSlice: &allValue.Slice, - PSliceP: &allValue.SliceP, - PPSmall: &allValue.PSmall, - PInterface: &allValue.Interface, -} - -var allValueIndent = `{ - "Bool": true, - "Int": 2, - "Int8": 3, - "Int16": 4, - "Int32": 5, - "Int64": 6, - "Uint": 7, - "Uint8": 8, - "Uint16": 9, - "Uint32": 10, - "Uint64": 11, - "Uintptr": 12, - "Float32": 14.1, - "Float64": 15.1, - "bar": "foo", - "bar2": "foo2", - "IntStr": "42", - "PBool": null, - "PInt": null, - "PInt8": null, - "PInt16": null, - "PInt32": null, - "PInt64": null, - "PUint": null, - "PUint8": null, - "PUint16": null, - "PUint32": null, - "PUint64": null, - "PUintptr": null, - "PFloat32": null, - "PFloat64": null, - "String": "16", - "PString": null, - "Map": { - "17": { - "Tag": "tag17" - }, - "18": { - "Tag": "tag18" - } - }, - "MapP": { - "19": { - "Tag": "tag19" - }, - "20": null - }, - "PMap": null, - "PMapP": null, - "EmptyMap": {}, - "NilMap": null, - "Slice": [ - { - "Tag": "tag20" - }, - { - "Tag": "tag21" - } - ], - "SliceP": [ - { - "Tag": "tag22" - }, - null, - { - "Tag": "tag23" - } - ], - "PSlice": null, - "PSliceP": null, - "EmptySlice": [], - "NilSlice": null, - "StringSlice": [ - "str24", - "str25", - "str26" - ], - "ByteSlice": "Gxwd", - "Small": { - "Tag": "tag30" - }, - "PSmall": { - "Tag": "tag31" - }, - "PPSmall": null, - "Interface": 5.2, - "PInterface": null -}` - -var allValueCompact = strings.Map(noSpace, allValueIndent) - -var pallValueIndent = `{ - "Bool": false, - "Int": 0, - "Int8": 0, - "Int16": 0, - "Int32": 0, - "Int64": 0, - "Uint": 0, - "Uint8": 0, - "Uint16": 0, - "Uint32": 0, - "Uint64": 0, - "Uintptr": 0, - "Float32": 0, - "Float64": 0, - "bar": "", - "bar2": "", - "IntStr": "0", - "PBool": true, - "PInt": 2, - "PInt8": 3, - "PInt16": 4, - "PInt32": 5, - "PInt64": 6, - "PUint": 7, - "PUint8": 8, - "PUint16": 9, - "PUint32": 10, - "PUint64": 11, - "PUintptr": 12, - "PFloat32": 14.1, - "PFloat64": 15.1, - "String": "", - "PString": "16", - "Map": null, - "MapP": null, - "PMap": { - "17": { - "Tag": "tag17" - }, - "18": { - "Tag": "tag18" - } - }, - "PMapP": { - "19": { - "Tag": "tag19" - }, - "20": null - }, - "EmptyMap": null, - "NilMap": null, - "Slice": null, - "SliceP": null, - "PSlice": [ - { - "Tag": "tag20" - }, - { - "Tag": "tag21" - } - ], - "PSliceP": [ - { - "Tag": "tag22" - }, - null, - { - "Tag": "tag23" - } - ], - "EmptySlice": null, - "NilSlice": null, - "StringSlice": null, - "ByteSlice": null, - "Small": { - "Tag": "" - }, - "PSmall": null, - "PPSmall": { - "Tag": "tag31" - }, - "Interface": null, - "PInterface": 5.2 -}` - -var pallValueCompact = strings.Map(noSpace, pallValueIndent) - -func TestRefUnmarshal(t *testing.T) { - type S struct { - // Ref is defined in encode_test.go. - R0 Ref - R1 *Ref - R2 RefText - R3 *RefText - } - want := S{ - R0: 12, - R1: new(Ref), - R2: 13, - R3: new(RefText), - } - *want.R1 = 12 - *want.R3 = 13 - - var got S - if err := Unmarshal([]byte(`{"R0":"ref","R1":"ref","R2":"ref","R3":"ref"}`), &got); err != nil { - t.Fatalf("Unmarshal: %v", err) - } - if !reflect.DeepEqual(got, want) { - t.Errorf("got %+v, want %+v", got, want) - } -} - -// Test that the empty string doesn't panic decoding when ,string is specified -// Issue 3450 -func TestEmptyString(t *testing.T) { - type T2 struct { - Number1 int `json:",string"` - Number2 int `json:",string"` - } - data := `{"Number1":"1", "Number2":""}` - dec := NewDecoder(strings.NewReader(data)) - var t2 T2 - err := dec.Decode(&t2) - if err == nil { - t.Fatal("Decode: did not return error") - } - if t2.Number1 != 1 { - t.Fatal("Decode: did not set Number1") - } -} - -// Test that a null for ,string is not replaced with the previous quoted string (issue 7046). -// It should also not be an error (issue 2540, issue 8587). -func TestNullString(t *testing.T) { - type T struct { - A int `json:",string"` - B int `json:",string"` - C *int `json:",string"` - } - data := []byte(`{"A": "1", "B": null, "C": null}`) - var s T - s.B = 1 - s.C = new(int) - *s.C = 2 - err := Unmarshal(data, &s) - if err != nil { - t.Fatalf("Unmarshal: %v", err) - } - if s.B != 1 || s.C != nil { - t.Fatalf("after Unmarshal, s.B=%d, s.C=%p, want 1, nil", s.B, s.C) - } -} - -func intp(x int) *int { - p := new(int) - *p = x - return p -} - -func intpp(x *int) **int { - pp := new(*int) - *pp = x - return pp -} - -var interfaceSetTests = []struct { - pre interface{} - json string - post interface{} -}{ - {"foo", `"bar"`, "bar"}, - {"foo", `2`, 2.0}, - {"foo", `true`, true}, - {"foo", `null`, nil}, - - {nil, `null`, nil}, - {new(int), `null`, nil}, - {(*int)(nil), `null`, nil}, - {new(*int), `null`, new(*int)}, - {(**int)(nil), `null`, nil}, - {intp(1), `null`, nil}, - {intpp(nil), `null`, intpp(nil)}, - {intpp(intp(1)), `null`, intpp(nil)}, -} - -func TestInterfaceSet(t *testing.T) { - for _, tt := range interfaceSetTests { - b := struct{ X interface{} }{tt.pre} - blob := `{"X":` + tt.json + `}` - if err := Unmarshal([]byte(blob), &b); err != nil { - t.Errorf("Unmarshal %#q: %v", blob, err) - continue - } - if !reflect.DeepEqual(b.X, tt.post) { - t.Errorf("Unmarshal %#q into %#v: X=%#v, want %#v", blob, tt.pre, b.X, tt.post) - } - } -} - -// JSON null values should be ignored for primitives and string values instead of resulting in an error. -// Issue 2540 -func TestUnmarshalNulls(t *testing.T) { - jsonData := []byte(`{ - "Bool" : null, - "Int" : null, - "Int8" : null, - "Int16" : null, - "Int32" : null, - "Int64" : null, - "Uint" : null, - "Uint8" : null, - "Uint16" : null, - "Uint32" : null, - "Uint64" : null, - "Float32" : null, - "Float64" : null, - "String" : null}`) - - nulls := All{ - Bool: true, - Int: 2, - Int8: 3, - Int16: 4, - Int32: 5, - Int64: 6, - Uint: 7, - Uint8: 8, - Uint16: 9, - Uint32: 10, - Uint64: 11, - Float32: 12.1, - Float64: 13.1, - String: "14"} - - err := Unmarshal(jsonData, &nulls) - if err != nil { - t.Errorf("Unmarshal of null values failed: %v", err) - } - if !nulls.Bool || nulls.Int != 2 || nulls.Int8 != 3 || nulls.Int16 != 4 || nulls.Int32 != 5 || nulls.Int64 != 6 || - nulls.Uint != 7 || nulls.Uint8 != 8 || nulls.Uint16 != 9 || nulls.Uint32 != 10 || nulls.Uint64 != 11 || - nulls.Float32 != 12.1 || nulls.Float64 != 13.1 || nulls.String != "14" { - - t.Errorf("Unmarshal of null values affected primitives") - } -} - -func TestStringKind(t *testing.T) { - type stringKind string - - var m1, m2 map[stringKind]int - m1 = map[stringKind]int{ - "foo": 42, - } - - data, err := Marshal(m1) - if err != nil { - t.Errorf("Unexpected error marshaling: %v", err) - } - - err = Unmarshal(data, &m2) - if err != nil { - t.Errorf("Unexpected error unmarshaling: %v", err) - } - - if !reflect.DeepEqual(m1, m2) { - t.Error("Items should be equal after encoding and then decoding") - } -} - -// Custom types with []byte as underlying type could not be marshalled -// and then unmarshalled. -// Issue 8962. -func TestByteKind(t *testing.T) { - type byteKind []byte - - a := byteKind("hello") - - data, err := Marshal(a) - if err != nil { - t.Error(err) - } - var b byteKind - err = Unmarshal(data, &b) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(a, b) { - t.Errorf("expected %v == %v", a, b) - } -} - -// The fix for issue 8962 introduced a regression. -// Issue 12921. -func TestSliceOfCustomByte(t *testing.T) { - type Uint8 uint8 - - a := []Uint8("hello") - - data, err := Marshal(a) - if err != nil { - t.Fatal(err) - } - var b []Uint8 - err = Unmarshal(data, &b) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(a, b) { - t.Fatal("expected %v == %v", a, b) - } -} - -var decodeTypeErrorTests = []struct { - dest interface{} - src string -}{ - {new(string), `{"user": "name"}`}, // issue 4628. - {new(error), `{}`}, // issue 4222 - {new(error), `[]`}, - {new(error), `""`}, - {new(error), `123`}, - {new(error), `true`}, -} - -func TestUnmarshalTypeError(t *testing.T) { - for _, item := range decodeTypeErrorTests { - err := Unmarshal([]byte(item.src), item.dest) - if _, ok := err.(*UnmarshalTypeError); !ok { - t.Errorf("expected type error for Unmarshal(%q, type %T): got %T", - item.src, item.dest, err) - } - } -} - -var unmarshalSyntaxTests = []string{ - "tru", - "fals", - "nul", - "123e", - `"hello`, - `[1,2,3`, - `{"key":1`, - `{"key":1,`, -} - -func TestUnmarshalSyntax(t *testing.T) { - var x interface{} - for _, src := range unmarshalSyntaxTests { - err := Unmarshal([]byte(src), &x) - if _, ok := err.(*SyntaxError); !ok { - t.Errorf("expected syntax error for Unmarshal(%q): got %T", src, err) - } - } -} - -// Test handling of unexported fields that should be ignored. -// Issue 4660 -type unexportedFields struct { - Name string - m map[string]interface{} `json:"-"` - m2 map[string]interface{} `json:"abcd"` -} - -func TestUnmarshalUnexported(t *testing.T) { - input := `{"Name": "Bob", "m": {"x": 123}, "m2": {"y": 456}, "abcd": {"z": 789}}` - want := &unexportedFields{Name: "Bob"} - - out := &unexportedFields{} - err := Unmarshal([]byte(input), out) - if err != nil { - t.Errorf("got error %v, expected nil", err) - } - if !reflect.DeepEqual(out, want) { - t.Errorf("got %q, want %q", out, want) - } -} - -// Time3339 is a time.Time which encodes to and from JSON -// as an RFC 3339 time in UTC. -type Time3339 time.Time - -func (t *Time3339) UnmarshalJSON(b []byte) error { - if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' { - return fmt.Errorf("types: failed to unmarshal non-string value %q as an RFC 3339 time", b) - } - tm, err := time.Parse(time.RFC3339, string(b[1:len(b)-1])) - if err != nil { - return err - } - *t = Time3339(tm) - return nil -} - -func TestUnmarshalJSONLiteralError(t *testing.T) { - var t3 Time3339 - err := Unmarshal([]byte(`"0000-00-00T00:00:00Z"`), &t3) - if err == nil { - t.Fatalf("expected error; got time %v", time.Time(t3)) - } - if !strings.Contains(err.Error(), "range") { - t.Errorf("got err = %v; want out of range error", err) - } -} - -// Test that extra object elements in an array do not result in a -// "data changing underfoot" error. -// Issue 3717 -func TestSkipArrayObjects(t *testing.T) { - json := `[{}]` - var dest [0]interface{} - - err := Unmarshal([]byte(json), &dest) - if err != nil { - t.Errorf("got error %q, want nil", err) - } -} - -// Test semantics of pre-filled struct fields and pre-filled map fields. -// Issue 4900. -func TestPrefilled(t *testing.T) { - ptrToMap := func(m map[string]interface{}) *map[string]interface{} { return &m } - - // Values here change, cannot reuse table across runs. - var prefillTests = []struct { - in string - ptr interface{} - out interface{} - }{ - { - in: `{"X": 1, "Y": 2}`, - ptr: &XYZ{X: float32(3), Y: int16(4), Z: 1.5}, - out: &XYZ{X: float64(1), Y: float64(2), Z: 1.5}, - }, - { - in: `{"X": 1, "Y": 2}`, - ptr: ptrToMap(map[string]interface{}{"X": float32(3), "Y": int16(4), "Z": 1.5}), - out: ptrToMap(map[string]interface{}{"X": float64(1), "Y": float64(2), "Z": 1.5}), - }, - } - - for _, tt := range prefillTests { - ptrstr := fmt.Sprintf("%v", tt.ptr) - err := Unmarshal([]byte(tt.in), tt.ptr) // tt.ptr edited here - if err != nil { - t.Errorf("Unmarshal: %v", err) - } - if !reflect.DeepEqual(tt.ptr, tt.out) { - t.Errorf("Unmarshal(%#q, %s): have %v, want %v", tt.in, ptrstr, tt.ptr, tt.out) - } - } -} - -var invalidUnmarshalTests = []struct { - v interface{} - want string -}{ - {nil, "json: Unmarshal(nil)"}, - {struct{}{}, "json: Unmarshal(non-pointer struct {})"}, - {(*int)(nil), "json: Unmarshal(nil *int)"}, -} - -func TestInvalidUnmarshal(t *testing.T) { - buf := []byte(`{"a":"1"}`) - for _, tt := range invalidUnmarshalTests { - err := Unmarshal(buf, tt.v) - if err == nil { - t.Errorf("Unmarshal expecting error, got nil") - continue - } - if got := err.Error(); got != tt.want { - t.Errorf("Unmarshal = %q; want %q", got, tt.want) - } - } -} - -var invalidUnmarshalTextTests = []struct { - v interface{} - want string -}{ - {nil, "json: Unmarshal(nil)"}, - {struct{}{}, "json: Unmarshal(non-pointer struct {})"}, - {(*int)(nil), "json: Unmarshal(nil *int)"}, - {new(net.IP), "json: cannot unmarshal string into Go value of type *net.IP"}, -} - -func TestInvalidUnmarshalText(t *testing.T) { - buf := []byte(`123`) - for _, tt := range invalidUnmarshalTextTests { - err := Unmarshal(buf, tt.v) - if err == nil { - t.Errorf("Unmarshal expecting error, got nil") - continue - } - if got := err.Error(); got != tt.want { - t.Errorf("Unmarshal = %q; want %q", got, tt.want) - } - } -} - -// Test that string option is ignored for invalid types. -// Issue 9812. -func TestInvalidStringOption(t *testing.T) { - num := 0 - item := struct { - T time.Time `json:",string"` - M map[string]string `json:",string"` - S []string `json:",string"` - A [1]string `json:",string"` - I interface{} `json:",string"` - P *int `json:",string"` - }{M: make(map[string]string), S: make([]string, 0), I: num, P: &num} - - data, err := Marshal(item) - if err != nil { - t.Fatalf("Marshal: %v", err) - } - - err = Unmarshal(data, &item) - if err != nil { - t.Fatalf("Unmarshal: %v", err) - } -} diff --git a/vendor/gopkg.in/square/go-jose.v1/json/encode.go b/vendor/gopkg.in/square/go-jose.v1/json/encode.go deleted file mode 100644 index 1dae8bb7..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/json/encode.go +++ /dev/null @@ -1,1197 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package json implements encoding and decoding of JSON objects as defined in -// RFC 4627. The mapping between JSON objects and Go values is described -// in the documentation for the Marshal and Unmarshal functions. -// -// See "JSON and Go" for an introduction to this package: -// https://golang.org/doc/articles/json_and_go.html -package json - -import ( - "bytes" - "encoding" - "encoding/base64" - "fmt" - "math" - "reflect" - "runtime" - "sort" - "strconv" - "strings" - "sync" - "unicode" - "unicode/utf8" -) - -// Marshal returns the JSON encoding of v. -// -// Marshal traverses the value v recursively. -// If an encountered value implements the Marshaler interface -// and is not a nil pointer, Marshal calls its MarshalJSON method -// to produce JSON. If no MarshalJSON method is present but the -// value implements encoding.TextMarshaler instead, Marshal calls -// its MarshalText method. -// The nil pointer exception is not strictly necessary -// but mimics a similar, necessary exception in the behavior of -// UnmarshalJSON. -// -// Otherwise, Marshal uses the following type-dependent default encodings: -// -// Boolean values encode as JSON booleans. -// -// Floating point, integer, and Number values encode as JSON numbers. -// -// String values encode as JSON strings coerced to valid UTF-8, -// replacing invalid bytes with the Unicode replacement rune. -// The angle brackets "<" and ">" are escaped to "\u003c" and "\u003e" -// to keep some browsers from misinterpreting JSON output as HTML. -// Ampersand "&" is also escaped to "\u0026" for the same reason. -// -// Array and slice values encode as JSON arrays, except that -// []byte encodes as a base64-encoded string, and a nil slice -// encodes as the null JSON object. -// -// Struct values encode as JSON objects. Each exported struct field -// 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 -// but can be specified in the struct field's tag value. The "json" key in -// 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 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 "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"` -// -// The key name will be used if it's a non-empty string consisting of -// only Unicode letters, digits, dollar signs, percent signs, hyphens, -// underscores and slashes. -// -// Anonymous struct fields are usually marshaled as if their inner exported fields -// were fields in the outer struct, subject to the usual Go visibility rules amended -// as described in the next paragraph. -// An anonymous struct field with a name given in its JSON tag is treated as -// having that name, rather than being anonymous. -// An anonymous struct field of interface type is treated the same as having -// that type as its name, rather than being anonymous. -// -// The Go visibility rules for struct fields are amended for JSON when -// deciding which field to marshal or unmarshal. If there are -// multiple fields at the same level, and that level is the least -// nested (and would therefore be the nesting level selected by the -// usual Go rules), the following extra rules apply: -// -// 1) Of those fields, if any are JSON-tagged, only tagged fields are considered, -// even if there are multiple untagged fields that would otherwise conflict. -// 2) If there is exactly one field (tagged or not according to the first rule), that is selected. -// 3) Otherwise there are multiple fields, and all are ignored; no error occurs. -// -// Handling of anonymous struct fields is new in Go 1.1. -// Prior to Go 1.1, anonymous struct fields were ignored. To force ignoring of -// an anonymous struct field in both current and earlier versions, give the field -// a JSON tag of "-". -// -// Map values encode as JSON objects. -// The map's key type must be string; the map keys are used as JSON object -// keys, subject to the UTF-8 coercion described for string values above. -// -// Pointer values encode as the value pointed to. -// A nil pointer encodes as the null JSON object. -// -// Interface values encode as the value contained in the interface. -// A nil interface value encodes as the null JSON object. -// -// Channel, complex, and function values cannot be encoded in JSON. -// Attempting to encode such a value causes Marshal to return -// an UnsupportedTypeError. -// -// 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) - if err != nil { - return nil, err - } - return e.Bytes(), nil -} - -// MarshalIndent is like Marshal but applies Indent to format the output. -func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { - b, err := Marshal(v) - if err != nil { - return nil, err - } - var buf bytes.Buffer - err = Indent(&buf, b, prefix, indent) - if err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -// HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029 -// characters inside string literals changed to \u003c, \u003e, \u0026, \u2028, \u2029 -// so that the JSON will be safe to embed inside HTML <script> tags. -// For historical reasons, web browsers don't honor standard HTML -// escaping within <script> tags, so an alternative JSON encoding must -// be used. -func HTMLEscape(dst *bytes.Buffer, src []byte) { - // The characters can only appear in string literals, - // so just scan the string one byte at a time. - start := 0 - for i, c := range src { - if c == '<' || c == '>' || c == '&' { - if start < i { - dst.Write(src[start:i]) - } - dst.WriteString(`\u00`) - dst.WriteByte(hex[c>>4]) - dst.WriteByte(hex[c&0xF]) - start = i + 1 - } - // Convert U+2028 and U+2029 (E2 80 A8 and E2 80 A9). - if c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 { - if start < i { - dst.Write(src[start:i]) - } - dst.WriteString(`\u202`) - dst.WriteByte(hex[src[i+2]&0xF]) - start = i + 3 - } - } - if start < len(src) { - dst.Write(src[start:]) - } -} - -// Marshaler is the interface implemented by objects that -// can marshal themselves into valid JSON. -type Marshaler interface { - MarshalJSON() ([]byte, error) -} - -// An UnsupportedTypeError is returned by Marshal when attempting -// to encode an unsupported value type. -type UnsupportedTypeError struct { - Type reflect.Type -} - -func (e *UnsupportedTypeError) Error() string { - return "json: unsupported type: " + e.Type.String() -} - -type UnsupportedValueError struct { - Value reflect.Value - Str string -} - -func (e *UnsupportedValueError) Error() string { - return "json: unsupported value: " + e.Str -} - -// Before Go 1.2, an InvalidUTF8Error was returned by Marshal when -// attempting to encode a string value with invalid UTF-8 sequences. -// As of Go 1.2, Marshal instead coerces the string to valid UTF-8 by -// replacing invalid bytes with the Unicode replacement rune U+FFFD. -// This error is no longer generated but is kept for backwards compatibility -// with programs that might mention it. -type InvalidUTF8Error struct { - S string // the whole string value that caused the error -} - -func (e *InvalidUTF8Error) Error() string { - return "json: invalid UTF-8 in string: " + strconv.Quote(e.S) -} - -type MarshalerError struct { - Type reflect.Type - Err error -} - -func (e *MarshalerError) Error() string { - return "json: error calling MarshalJSON for type " + e.Type.String() + ": " + e.Err.Error() -} - -var hex = "0123456789abcdef" - -// An encodeState encodes JSON into a bytes.Buffer. -type encodeState struct { - bytes.Buffer // accumulated output - scratch [64]byte -} - -var encodeStatePool sync.Pool - -func newEncodeState() *encodeState { - if v := encodeStatePool.Get(); v != nil { - e := v.(*encodeState) - e.Reset() - return e - } - return new(encodeState) -} - -func (e *encodeState) marshal(v interface{}) (err error) { - defer func() { - if r := recover(); r != nil { - if _, ok := r.(runtime.Error); ok { - panic(r) - } - if s, ok := r.(string); ok { - panic(s) - } - err = r.(error) - } - }() - e.reflectValue(reflect.ValueOf(v)) - return nil -} - -func (e *encodeState) error(err error) { - panic(err) -} - -func isEmptyValue(v reflect.Value) bool { - switch v.Kind() { - case reflect.Array, reflect.Map, reflect.Slice, reflect.String: - return v.Len() == 0 - case reflect.Bool: - return !v.Bool() - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return v.Int() == 0 - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return v.Uint() == 0 - case reflect.Float32, reflect.Float64: - return v.Float() == 0 - case reflect.Interface, reflect.Ptr: - return v.IsNil() - } - return false -} - -func (e *encodeState) reflectValue(v reflect.Value) { - valueEncoder(v)(e, v, false) -} - -type encoderFunc func(e *encodeState, v reflect.Value, quoted bool) - -var encoderCache struct { - sync.RWMutex - m map[reflect.Type]encoderFunc -} - -func valueEncoder(v reflect.Value) encoderFunc { - if !v.IsValid() { - return invalidValueEncoder - } - return typeEncoder(v.Type()) -} - -func typeEncoder(t reflect.Type) encoderFunc { - encoderCache.RLock() - f := encoderCache.m[t] - encoderCache.RUnlock() - if f != nil { - return f - } - - // To deal with recursive types, populate the map with an - // indirect func before we build it. This type waits on the - // real func (f) to be ready and then calls it. This indirect - // func is only used for recursive types. - encoderCache.Lock() - if encoderCache.m == nil { - encoderCache.m = make(map[reflect.Type]encoderFunc) - } - var wg sync.WaitGroup - wg.Add(1) - encoderCache.m[t] = func(e *encodeState, v reflect.Value, quoted bool) { - wg.Wait() - f(e, v, quoted) - } - encoderCache.Unlock() - - // Compute fields without lock. - // Might duplicate effort but won't hold other computations back. - f = newTypeEncoder(t, true) - wg.Done() - encoderCache.Lock() - encoderCache.m[t] = f - encoderCache.Unlock() - return f -} - -var ( - marshalerType = reflect.TypeOf(new(Marshaler)).Elem() - textMarshalerType = reflect.TypeOf(new(encoding.TextMarshaler)).Elem() -) - -// newTypeEncoder constructs an encoderFunc for a type. -// The returned encoder only checks CanAddr when allowAddr is true. -func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc { - if t.Implements(marshalerType) { - return marshalerEncoder - } - if t.Kind() != reflect.Ptr && allowAddr { - if reflect.PtrTo(t).Implements(marshalerType) { - return newCondAddrEncoder(addrMarshalerEncoder, newTypeEncoder(t, false)) - } - } - - if t.Implements(textMarshalerType) { - return textMarshalerEncoder - } - if t.Kind() != reflect.Ptr && allowAddr { - if reflect.PtrTo(t).Implements(textMarshalerType) { - return newCondAddrEncoder(addrTextMarshalerEncoder, newTypeEncoder(t, false)) - } - } - - switch t.Kind() { - case reflect.Bool: - return boolEncoder - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return intEncoder - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return uintEncoder - case reflect.Float32: - return float32Encoder - case reflect.Float64: - return float64Encoder - case reflect.String: - return stringEncoder - case reflect.Interface: - return interfaceEncoder - case reflect.Struct: - return newStructEncoder(t) - case reflect.Map: - return newMapEncoder(t) - case reflect.Slice: - return newSliceEncoder(t) - case reflect.Array: - return newArrayEncoder(t) - case reflect.Ptr: - return newPtrEncoder(t) - default: - return unsupportedTypeEncoder - } -} - -func invalidValueEncoder(e *encodeState, v reflect.Value, quoted bool) { - e.WriteString("null") -} - -func marshalerEncoder(e *encodeState, v reflect.Value, quoted bool) { - if v.Kind() == reflect.Ptr && v.IsNil() { - e.WriteString("null") - return - } - m := v.Interface().(Marshaler) - b, err := m.MarshalJSON() - if err == nil { - // copy JSON into buffer, checking validity. - err = compact(&e.Buffer, b, true) - } - if err != nil { - e.error(&MarshalerError{v.Type(), err}) - } -} - -func addrMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) { - va := v.Addr() - if va.IsNil() { - e.WriteString("null") - return - } - m := va.Interface().(Marshaler) - b, err := m.MarshalJSON() - if err == nil { - // copy JSON into buffer, checking validity. - err = compact(&e.Buffer, b, true) - } - if err != nil { - e.error(&MarshalerError{v.Type(), err}) - } -} - -func textMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) { - if v.Kind() == reflect.Ptr && v.IsNil() { - e.WriteString("null") - return - } - m := v.Interface().(encoding.TextMarshaler) - b, err := m.MarshalText() - if err != nil { - e.error(&MarshalerError{v.Type(), err}) - } - e.stringBytes(b) -} - -func addrTextMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) { - va := v.Addr() - if va.IsNil() { - e.WriteString("null") - return - } - m := va.Interface().(encoding.TextMarshaler) - b, err := m.MarshalText() - if err != nil { - e.error(&MarshalerError{v.Type(), err}) - } - e.stringBytes(b) -} - -func boolEncoder(e *encodeState, v reflect.Value, quoted bool) { - if quoted { - e.WriteByte('"') - } - if v.Bool() { - e.WriteString("true") - } else { - e.WriteString("false") - } - if quoted { - e.WriteByte('"') - } -} - -func intEncoder(e *encodeState, v reflect.Value, quoted bool) { - b := strconv.AppendInt(e.scratch[:0], v.Int(), 10) - if quoted { - e.WriteByte('"') - } - e.Write(b) - if quoted { - e.WriteByte('"') - } -} - -func uintEncoder(e *encodeState, v reflect.Value, quoted bool) { - b := strconv.AppendUint(e.scratch[:0], v.Uint(), 10) - if quoted { - e.WriteByte('"') - } - e.Write(b) - if quoted { - e.WriteByte('"') - } -} - -type floatEncoder int // number of bits - -func (bits floatEncoder) encode(e *encodeState, v reflect.Value, quoted bool) { - f := v.Float() - if math.IsInf(f, 0) || math.IsNaN(f) { - e.error(&UnsupportedValueError{v, strconv.FormatFloat(f, 'g', -1, int(bits))}) - } - b := strconv.AppendFloat(e.scratch[:0], f, 'g', -1, int(bits)) - if quoted { - e.WriteByte('"') - } - e.Write(b) - if quoted { - e.WriteByte('"') - } -} - -var ( - float32Encoder = (floatEncoder(32)).encode - float64Encoder = (floatEncoder(64)).encode -) - -func stringEncoder(e *encodeState, v reflect.Value, quoted bool) { - if v.Type() == numberType { - numStr := v.String() - // In Go1.5 the empty string encodes to "0", while this is not a valid number literal - // we keep compatibility so check validity after this. - if numStr == "" { - numStr = "0" // Number's zero-val - } - if !isValidNumber(numStr) { - e.error(fmt.Errorf("json: invalid number literal %q", numStr)) - } - e.WriteString(numStr) - return - } - if quoted { - sb, err := Marshal(v.String()) - if err != nil { - e.error(err) - } - e.string(string(sb)) - } else { - e.string(v.String()) - } -} - -func interfaceEncoder(e *encodeState, v reflect.Value, quoted bool) { - if v.IsNil() { - e.WriteString("null") - return - } - e.reflectValue(v.Elem()) -} - -func unsupportedTypeEncoder(e *encodeState, v reflect.Value, quoted bool) { - e.error(&UnsupportedTypeError{v.Type()}) -} - -type structEncoder struct { - fields []field - fieldEncs []encoderFunc -} - -func (se *structEncoder) encode(e *encodeState, v reflect.Value, quoted bool) { - e.WriteByte('{') - first := true - for i, f := range se.fields { - fv := fieldByIndex(v, f.index) - if !fv.IsValid() || f.omitEmpty && isEmptyValue(fv) { - continue - } - if first { - first = false - } else { - e.WriteByte(',') - } - e.string(f.name) - e.WriteByte(':') - se.fieldEncs[i](e, fv, f.quoted) - } - e.WriteByte('}') -} - -func newStructEncoder(t reflect.Type) encoderFunc { - fields := cachedTypeFields(t) - se := &structEncoder{ - fields: fields, - fieldEncs: make([]encoderFunc, len(fields)), - } - for i, f := range fields { - se.fieldEncs[i] = typeEncoder(typeByIndex(t, f.index)) - } - return se.encode -} - -type mapEncoder struct { - elemEnc encoderFunc -} - -func (me *mapEncoder) encode(e *encodeState, v reflect.Value, _ bool) { - if v.IsNil() { - e.WriteString("null") - return - } - e.WriteByte('{') - var sv stringValues = v.MapKeys() - sort.Sort(sv) - for i, k := range sv { - if i > 0 { - e.WriteByte(',') - } - e.string(k.String()) - e.WriteByte(':') - me.elemEnc(e, v.MapIndex(k), false) - } - e.WriteByte('}') -} - -func newMapEncoder(t reflect.Type) encoderFunc { - if t.Key().Kind() != reflect.String { - return unsupportedTypeEncoder - } - me := &mapEncoder{typeEncoder(t.Elem())} - return me.encode -} - -func encodeByteSlice(e *encodeState, v reflect.Value, _ bool) { - if v.IsNil() { - e.WriteString("null") - return - } - s := v.Bytes() - e.WriteByte('"') - if len(s) < 1024 { - // for small buffers, using Encode directly is much faster. - dst := make([]byte, base64.StdEncoding.EncodedLen(len(s))) - base64.StdEncoding.Encode(dst, s) - e.Write(dst) - } else { - // for large buffers, avoid unnecessary extra temporary - // buffer space. - enc := base64.NewEncoder(base64.StdEncoding, e) - enc.Write(s) - enc.Close() - } - e.WriteByte('"') -} - -// sliceEncoder just wraps an arrayEncoder, checking to make sure the value isn't nil. -type sliceEncoder struct { - arrayEnc encoderFunc -} - -func (se *sliceEncoder) encode(e *encodeState, v reflect.Value, _ bool) { - if v.IsNil() { - e.WriteString("null") - return - } - se.arrayEnc(e, v, false) -} - -func newSliceEncoder(t reflect.Type) encoderFunc { - // Byte slices get special treatment; arrays don't. - if t.Elem().Kind() == reflect.Uint8 { - return encodeByteSlice - } - enc := &sliceEncoder{newArrayEncoder(t)} - return enc.encode -} - -type arrayEncoder struct { - elemEnc encoderFunc -} - -func (ae *arrayEncoder) encode(e *encodeState, v reflect.Value, _ bool) { - e.WriteByte('[') - n := v.Len() - for i := 0; i < n; i++ { - if i > 0 { - e.WriteByte(',') - } - ae.elemEnc(e, v.Index(i), false) - } - e.WriteByte(']') -} - -func newArrayEncoder(t reflect.Type) encoderFunc { - enc := &arrayEncoder{typeEncoder(t.Elem())} - return enc.encode -} - -type ptrEncoder struct { - elemEnc encoderFunc -} - -func (pe *ptrEncoder) encode(e *encodeState, v reflect.Value, quoted bool) { - if v.IsNil() { - e.WriteString("null") - return - } - pe.elemEnc(e, v.Elem(), quoted) -} - -func newPtrEncoder(t reflect.Type) encoderFunc { - enc := &ptrEncoder{typeEncoder(t.Elem())} - return enc.encode -} - -type condAddrEncoder struct { - canAddrEnc, elseEnc encoderFunc -} - -func (ce *condAddrEncoder) encode(e *encodeState, v reflect.Value, quoted bool) { - if v.CanAddr() { - ce.canAddrEnc(e, v, quoted) - } else { - ce.elseEnc(e, v, quoted) - } -} - -// newCondAddrEncoder returns an encoder that checks whether its value -// CanAddr and delegates to canAddrEnc if so, else to elseEnc. -func newCondAddrEncoder(canAddrEnc, elseEnc encoderFunc) encoderFunc { - enc := &condAddrEncoder{canAddrEnc: canAddrEnc, elseEnc: elseEnc} - return enc.encode -} - -func isValidTag(s string) bool { - if s == "" { - return false - } - for _, c := range s { - switch { - case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c): - // Backslash and quote chars are reserved, but - // otherwise any punctuation chars are allowed - // in a tag name. - default: - if !unicode.IsLetter(c) && !unicode.IsDigit(c) { - return false - } - } - } - return true -} - -func fieldByIndex(v reflect.Value, index []int) reflect.Value { - for _, i := range index { - if v.Kind() == reflect.Ptr { - if v.IsNil() { - return reflect.Value{} - } - v = v.Elem() - } - v = v.Field(i) - } - return v -} - -func typeByIndex(t reflect.Type, index []int) reflect.Type { - for _, i := range index { - if t.Kind() == reflect.Ptr { - t = t.Elem() - } - t = t.Field(i).Type - } - return t -} - -// stringValues is a slice of reflect.Value holding *reflect.StringValue. -// It implements the methods to sort by string. -type stringValues []reflect.Value - -func (sv stringValues) Len() int { return len(sv) } -func (sv stringValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] } -func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) } -func (sv stringValues) get(i int) string { return sv[i].String() } - -// NOTE: keep in sync with stringBytes below. -func (e *encodeState) string(s string) int { - len0 := e.Len() - e.WriteByte('"') - start := 0 - for i := 0; i < len(s); { - if b := s[i]; b < utf8.RuneSelf { - if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' { - i++ - continue - } - if start < i { - e.WriteString(s[start:i]) - } - switch b { - case '\\', '"': - e.WriteByte('\\') - e.WriteByte(b) - case '\n': - e.WriteByte('\\') - e.WriteByte('n') - case '\r': - e.WriteByte('\\') - e.WriteByte('r') - case '\t': - e.WriteByte('\\') - e.WriteByte('t') - default: - // This encodes bytes < 0x20 except for \n and \r, - // as well as <, > and &. The latter are escaped because they - // can lead to security holes when user-controlled strings - // are rendered into JSON and served to some browsers. - e.WriteString(`\u00`) - e.WriteByte(hex[b>>4]) - e.WriteByte(hex[b&0xF]) - } - i++ - start = i - continue - } - c, size := utf8.DecodeRuneInString(s[i:]) - if c == utf8.RuneError && size == 1 { - if start < i { - e.WriteString(s[start:i]) - } - e.WriteString(`\ufffd`) - i += size - start = i - continue - } - // U+2028 is LINE SEPARATOR. - // U+2029 is PARAGRAPH SEPARATOR. - // They are both technically valid characters in JSON strings, - // but don't work in JSONP, which has to be evaluated as JavaScript, - // and can lead to security holes there. It is valid JSON to - // escape them, so we do so unconditionally. - // See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion. - if c == '\u2028' || c == '\u2029' { - if start < i { - e.WriteString(s[start:i]) - } - e.WriteString(`\u202`) - e.WriteByte(hex[c&0xF]) - i += size - start = i - continue - } - i += size - } - if start < len(s) { - e.WriteString(s[start:]) - } - e.WriteByte('"') - return e.Len() - len0 -} - -// NOTE: keep in sync with string above. -func (e *encodeState) stringBytes(s []byte) int { - len0 := e.Len() - e.WriteByte('"') - start := 0 - for i := 0; i < len(s); { - if b := s[i]; b < utf8.RuneSelf { - if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' { - i++ - continue - } - if start < i { - e.Write(s[start:i]) - } - switch b { - case '\\', '"': - e.WriteByte('\\') - e.WriteByte(b) - case '\n': - e.WriteByte('\\') - e.WriteByte('n') - case '\r': - e.WriteByte('\\') - e.WriteByte('r') - case '\t': - e.WriteByte('\\') - e.WriteByte('t') - default: - // This encodes bytes < 0x20 except for \n and \r, - // as well as <, >, and &. The latter are escaped because they - // can lead to security holes when user-controlled strings - // are rendered into JSON and served to some browsers. - e.WriteString(`\u00`) - e.WriteByte(hex[b>>4]) - e.WriteByte(hex[b&0xF]) - } - i++ - start = i - continue - } - c, size := utf8.DecodeRune(s[i:]) - if c == utf8.RuneError && size == 1 { - if start < i { - e.Write(s[start:i]) - } - e.WriteString(`\ufffd`) - i += size - start = i - continue - } - // U+2028 is LINE SEPARATOR. - // U+2029 is PARAGRAPH SEPARATOR. - // They are both technically valid characters in JSON strings, - // but don't work in JSONP, which has to be evaluated as JavaScript, - // and can lead to security holes there. It is valid JSON to - // escape them, so we do so unconditionally. - // See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion. - if c == '\u2028' || c == '\u2029' { - if start < i { - e.Write(s[start:i]) - } - e.WriteString(`\u202`) - e.WriteByte(hex[c&0xF]) - i += size - start = i - continue - } - i += size - } - if start < len(s) { - e.Write(s[start:]) - } - e.WriteByte('"') - return e.Len() - len0 -} - -// A field represents a single field found in a struct. -type field struct { - name string - nameBytes []byte // []byte(name) - - tag bool - index []int - typ reflect.Type - omitEmpty bool - quoted bool -} - -func fillField(f field) field { - f.nameBytes = []byte(f.name) - return f -} - -// byName sorts field by name, breaking ties with depth, -// then breaking ties with "name came from json tag", then -// breaking ties with index sequence. -type byName []field - -func (x byName) Len() int { return len(x) } - -func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } - -func (x byName) Less(i, j int) bool { - if x[i].name != x[j].name { - return x[i].name < x[j].name - } - if len(x[i].index) != len(x[j].index) { - return len(x[i].index) < len(x[j].index) - } - if x[i].tag != x[j].tag { - return x[i].tag - } - return byIndex(x).Less(i, j) -} - -// byIndex sorts field by index sequence. -type byIndex []field - -func (x byIndex) Len() int { return len(x) } - -func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] } - -func (x byIndex) Less(i, j int) bool { - for k, xik := range x[i].index { - if k >= len(x[j].index) { - return false - } - if xik != x[j].index[k] { - return xik < x[j].index[k] - } - } - return len(x[i].index) < len(x[j].index) -} - -// typeFields returns a list of fields that JSON should recognize for the given type. -// The algorithm is breadth-first search over the set of structs to include - the top struct -// and then any reachable anonymous structs. -func typeFields(t reflect.Type) []field { - // Anonymous fields to explore at the current level and the next. - current := []field{} - next := []field{{typ: t}} - - // Count of queued names for current level and the next. - count := map[reflect.Type]int{} - nextCount := map[reflect.Type]int{} - - // Types already visited at an earlier level. - visited := map[reflect.Type]bool{} - - // Fields found. - var fields []field - - for len(next) > 0 { - current, next = next, current[:0] - count, nextCount = nextCount, map[reflect.Type]int{} - - for _, f := range current { - if visited[f.typ] { - continue - } - visited[f.typ] = true - - // Scan f.typ for fields to include. - for i := 0; i < f.typ.NumField(); i++ { - sf := f.typ.Field(i) - if sf.PkgPath != "" && !sf.Anonymous { // unexported - continue - } - tag := sf.Tag.Get("json") - if tag == "-" { - continue - } - name, opts := parseTag(tag) - if !isValidTag(name) { - name = "" - } - index := make([]int, len(f.index)+1) - copy(index, f.index) - index[len(f.index)] = i - - ft := sf.Type - if ft.Name() == "" && ft.Kind() == reflect.Ptr { - // Follow pointer. - ft = ft.Elem() - } - - // Only strings, floats, integers, and booleans can be quoted. - quoted := false - if opts.Contains("string") { - switch ft.Kind() { - case reflect.Bool, - reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, - reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, - reflect.Float32, reflect.Float64, - reflect.String: - quoted = true - } - } - - // Record found field and index sequence. - if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct { - tagged := name != "" - if name == "" { - name = sf.Name - } - fields = append(fields, fillField(field{ - name: name, - tag: tagged, - index: index, - typ: ft, - omitEmpty: opts.Contains("omitempty"), - quoted: quoted, - })) - if count[f.typ] > 1 { - // If there were multiple instances, add a second, - // so that the annihilation code will see a duplicate. - // It only cares about the distinction between 1 or 2, - // so don't bother generating any more copies. - fields = append(fields, fields[len(fields)-1]) - } - continue - } - - // Record new anonymous struct to explore in next round. - nextCount[ft]++ - if nextCount[ft] == 1 { - next = append(next, fillField(field{name: ft.Name(), index: index, typ: ft})) - } - } - } - } - - sort.Sort(byName(fields)) - - // Delete all fields that are hidden by the Go rules for embedded fields, - // except that fields with JSON tags are promoted. - - // The fields are sorted in primary order of name, secondary order - // of field index length. Loop over names; for each name, delete - // hidden fields by choosing the one dominant field that survives. - out := fields[:0] - for advance, i := 0, 0; i < len(fields); i += advance { - // One iteration per name. - // Find the sequence of fields with the name of this first field. - fi := fields[i] - name := fi.name - for advance = 1; i+advance < len(fields); advance++ { - fj := fields[i+advance] - if fj.name != name { - break - } - } - if advance == 1 { // Only one field with this name - out = append(out, fi) - continue - } - dominant, ok := dominantField(fields[i : i+advance]) - if ok { - out = append(out, dominant) - } - } - - fields = out - sort.Sort(byIndex(fields)) - - return fields -} - -// dominantField looks through the fields, all of which are known to -// have the same name, to find the single field that dominates the -// others using Go's embedding rules, modified by the presence of -// JSON tags. If there are multiple top-level fields, the boolean -// will be false: This condition is an error in Go and we skip all -// the fields. -func dominantField(fields []field) (field, bool) { - // The fields are sorted in increasing index-length order. The winner - // must therefore be one with the shortest index length. Drop all - // longer entries, which is easy: just truncate the slice. - length := len(fields[0].index) - tagged := -1 // Index of first tagged field. - for i, f := range fields { - if len(f.index) > length { - fields = fields[:i] - break - } - if f.tag { - if tagged >= 0 { - // Multiple tagged fields at the same level: conflict. - // Return no field. - return field{}, false - } - tagged = i - } - } - if tagged >= 0 { - return fields[tagged], true - } - // All remaining fields have the same length. If there's more than one, - // we have a conflict (two fields named "X" at the same level) and we - // return no field. - if len(fields) > 1 { - return field{}, false - } - return fields[0], true -} - -var fieldCache struct { - sync.RWMutex - m map[reflect.Type][]field -} - -// cachedTypeFields is like typeFields but uses a cache to avoid repeated work. -func cachedTypeFields(t reflect.Type) []field { - fieldCache.RLock() - f := fieldCache.m[t] - fieldCache.RUnlock() - if f != nil { - return f - } - - // Compute fields without lock. - // Might duplicate effort but won't hold other computations back. - f = typeFields(t) - if f == nil { - f = []field{} - } - - fieldCache.Lock() - if fieldCache.m == nil { - fieldCache.m = map[reflect.Type][]field{} - } - fieldCache.m[t] = f - fieldCache.Unlock() - return f -} diff --git a/vendor/gopkg.in/square/go-jose.v1/json/encode_test.go b/vendor/gopkg.in/square/go-jose.v1/json/encode_test.go deleted file mode 100644 index c00491e0..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/json/encode_test.go +++ /dev/null @@ -1,538 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package json - -import ( - "bytes" - "math" - "reflect" - "testing" - "unicode" -) - -type Optionals struct { - Sr string `json:"sr"` - So string `json:"so,omitempty"` - Sw string `json:"-"` - - Ir int `json:"omitempty"` // actually named omitempty, not an option - Io int `json:"io,omitempty"` - - Slr []string `json:"slr,random"` - Slo []string `json:"slo,omitempty"` - - Mr map[string]interface{} `json:"mr"` - Mo map[string]interface{} `json:",omitempty"` - - Fr float64 `json:"fr"` - Fo float64 `json:"fo,omitempty"` - - Br bool `json:"br"` - Bo bool `json:"bo,omitempty"` - - Ur uint `json:"ur"` - Uo uint `json:"uo,omitempty"` - - Str struct{} `json:"str"` - Sto struct{} `json:"sto,omitempty"` -} - -var optionalsExpected = `{ - "sr": "", - "omitempty": 0, - "slr": null, - "mr": {}, - "fr": 0, - "br": false, - "ur": 0, - "str": {}, - "sto": {} -}` - -func TestOmitEmpty(t *testing.T) { - var o Optionals - o.Sw = "something" - o.Mr = map[string]interface{}{} - o.Mo = map[string]interface{}{} - - got, err := MarshalIndent(&o, "", " ") - if err != nil { - t.Fatal(err) - } - if got := string(got); got != optionalsExpected { - t.Errorf(" got: %s\nwant: %s\n", got, optionalsExpected) - } -} - -type StringTag struct { - BoolStr bool `json:",string"` - IntStr int64 `json:",string"` - StrStr string `json:",string"` -} - -var stringTagExpected = `{ - "BoolStr": "true", - "IntStr": "42", - "StrStr": "\"xzbit\"" -}` - -func TestStringTag(t *testing.T) { - var s StringTag - s.BoolStr = true - s.IntStr = 42 - s.StrStr = "xzbit" - got, err := MarshalIndent(&s, "", " ") - if err != nil { - t.Fatal(err) - } - if got := string(got); got != stringTagExpected { - t.Fatalf(" got: %s\nwant: %s\n", got, stringTagExpected) - } - - // Verify that it round-trips. - var s2 StringTag - err = NewDecoder(bytes.NewReader(got)).Decode(&s2) - if err != nil { - t.Fatalf("Decode: %v", err) - } - if !reflect.DeepEqual(s, s2) { - t.Fatalf("decode didn't match.\nsource: %#v\nEncoded as:\n%s\ndecode: %#v", s, string(got), s2) - } -} - -// byte slices are special even if they're renamed types. -type renamedByte byte -type renamedByteSlice []byte -type renamedRenamedByteSlice []renamedByte - -func TestEncodeRenamedByteSlice(t *testing.T) { - s := renamedByteSlice("abc") - result, err := Marshal(s) - if err != nil { - t.Fatal(err) - } - expect := `"YWJj"` - if string(result) != expect { - t.Errorf(" got %s want %s", result, expect) - } - r := renamedRenamedByteSlice("abc") - result, err = Marshal(r) - if err != nil { - t.Fatal(err) - } - if string(result) != expect { - t.Errorf(" got %s want %s", result, expect) - } -} - -var unsupportedValues = []interface{}{ - math.NaN(), - math.Inf(-1), - math.Inf(1), -} - -func TestUnsupportedValues(t *testing.T) { - for _, v := range unsupportedValues { - if _, err := Marshal(v); err != nil { - if _, ok := err.(*UnsupportedValueError); !ok { - t.Errorf("for %v, got %T want UnsupportedValueError", v, err) - } - } else { - t.Errorf("for %v, expected error", v) - } - } -} - -// Ref has Marshaler and Unmarshaler methods with pointer receiver. -type Ref int - -func (*Ref) MarshalJSON() ([]byte, error) { - return []byte(`"ref"`), nil -} - -func (r *Ref) UnmarshalJSON([]byte) error { - *r = 12 - return nil -} - -// Val has Marshaler methods with value receiver. -type Val int - -func (Val) MarshalJSON() ([]byte, error) { - return []byte(`"val"`), nil -} - -// RefText has Marshaler and Unmarshaler methods with pointer receiver. -type RefText int - -func (*RefText) MarshalText() ([]byte, error) { - return []byte(`"ref"`), nil -} - -func (r *RefText) UnmarshalText([]byte) error { - *r = 13 - return nil -} - -// ValText has Marshaler methods with value receiver. -type ValText int - -func (ValText) MarshalText() ([]byte, error) { - return []byte(`"val"`), nil -} - -func TestRefValMarshal(t *testing.T) { - var s = struct { - R0 Ref - R1 *Ref - R2 RefText - R3 *RefText - V0 Val - V1 *Val - V2 ValText - V3 *ValText - }{ - R0: 12, - R1: new(Ref), - R2: 14, - R3: new(RefText), - V0: 13, - V1: new(Val), - V2: 15, - V3: new(ValText), - } - const want = `{"R0":"ref","R1":"ref","R2":"\"ref\"","R3":"\"ref\"","V0":"val","V1":"val","V2":"\"val\"","V3":"\"val\""}` - b, err := Marshal(&s) - if err != nil { - t.Fatalf("Marshal: %v", err) - } - if got := string(b); got != want { - t.Errorf("got %q, want %q", got, want) - } -} - -// C implements Marshaler and returns unescaped JSON. -type C int - -func (C) MarshalJSON() ([]byte, error) { - return []byte(`"<&>"`), nil -} - -// CText implements Marshaler and returns unescaped text. -type CText int - -func (CText) MarshalText() ([]byte, error) { - return []byte(`"<&>"`), nil -} - -func TestMarshalerEscaping(t *testing.T) { - var c C - want := `"\u003c\u0026\u003e"` - b, err := Marshal(c) - if err != nil { - t.Fatalf("Marshal(c): %v", err) - } - if got := string(b); got != want { - t.Errorf("Marshal(c) = %#q, want %#q", got, want) - } - - var ct CText - want = `"\"\u003c\u0026\u003e\""` - b, err = Marshal(ct) - if err != nil { - t.Fatalf("Marshal(ct): %v", err) - } - if got := string(b); got != want { - t.Errorf("Marshal(ct) = %#q, want %#q", got, want) - } -} - -type IntType int - -type MyStruct struct { - IntType -} - -func TestAnonymousNonstruct(t *testing.T) { - var i IntType = 11 - a := MyStruct{i} - const want = `{"IntType":11}` - - b, err := Marshal(a) - if err != nil { - t.Fatalf("Marshal: %v", err) - } - if got := string(b); got != want { - t.Errorf("got %q, want %q", got, want) - } -} - -type BugA struct { - S string -} - -type BugB struct { - BugA - S string -} - -type BugC struct { - S string -} - -// Legal Go: We never use the repeated embedded field (S). -type BugX struct { - A int - BugA - BugB -} - -// Issue 5245. -func TestEmbeddedBug(t *testing.T) { - v := BugB{ - BugA{"A"}, - "B", - } - b, err := Marshal(v) - if err != nil { - t.Fatal("Marshal:", err) - } - want := `{"S":"B"}` - got := string(b) - if got != want { - t.Fatalf("Marshal: got %s want %s", got, want) - } - // Now check that the duplicate field, S, does not appear. - x := BugX{ - A: 23, - } - b, err = Marshal(x) - if err != nil { - t.Fatal("Marshal:", err) - } - want = `{"A":23}` - got = string(b) - if got != want { - t.Fatalf("Marshal: got %s want %s", got, want) - } -} - -type BugD struct { // Same as BugA after tagging. - XXX string `json:"S"` -} - -// BugD's tagged S field should dominate BugA's. -type BugY struct { - BugA - BugD -} - -// Test that a field with a tag dominates untagged fields. -func TestTaggedFieldDominates(t *testing.T) { - v := BugY{ - BugA{"BugA"}, - BugD{"BugD"}, - } - b, err := Marshal(v) - if err != nil { - t.Fatal("Marshal:", err) - } - want := `{"S":"BugD"}` - got := string(b) - if got != want { - t.Fatalf("Marshal: got %s want %s", got, want) - } -} - -// There are no tags here, so S should not appear. -type BugZ struct { - BugA - BugC - BugY // Contains a tagged S field through BugD; should not dominate. -} - -func TestDuplicatedFieldDisappears(t *testing.T) { - v := BugZ{ - BugA{"BugA"}, - BugC{"BugC"}, - BugY{ - BugA{"nested BugA"}, - BugD{"nested BugD"}, - }, - } - b, err := Marshal(v) - if err != nil { - t.Fatal("Marshal:", err) - } - want := `{}` - got := string(b) - if got != want { - t.Fatalf("Marshal: got %s want %s", got, want) - } -} - -func TestStringBytes(t *testing.T) { - // Test that encodeState.stringBytes and encodeState.string use the same encoding. - es := &encodeState{} - var r []rune - for i := '\u0000'; i <= unicode.MaxRune; i++ { - r = append(r, i) - } - s := string(r) + "\xff\xff\xffhello" // some invalid UTF-8 too - es.string(s) - - esBytes := &encodeState{} - esBytes.stringBytes([]byte(s)) - - enc := es.Buffer.String() - encBytes := esBytes.Buffer.String() - if enc != encBytes { - i := 0 - for i < len(enc) && i < len(encBytes) && enc[i] == encBytes[i] { - i++ - } - enc = enc[i:] - encBytes = encBytes[i:] - i = 0 - for i < len(enc) && i < len(encBytes) && enc[len(enc)-i-1] == encBytes[len(encBytes)-i-1] { - i++ - } - enc = enc[:len(enc)-i] - encBytes = encBytes[:len(encBytes)-i] - - if len(enc) > 20 { - enc = enc[:20] + "..." - } - if len(encBytes) > 20 { - encBytes = encBytes[:20] + "..." - } - - t.Errorf("encodings differ at %#q vs %#q", enc, encBytes) - } -} - -func TestIssue6458(t *testing.T) { - type Foo struct { - M RawMessage - } - x := Foo{RawMessage(`"foo"`)} - - b, err := Marshal(&x) - if err != nil { - t.Fatal(err) - } - if want := `{"M":"foo"}`; string(b) != want { - t.Errorf("Marshal(&x) = %#q; want %#q", b, want) - } - - b, err = Marshal(x) - if err != nil { - t.Fatal(err) - } - - if want := `{"M":"ImZvbyI="}`; string(b) != want { - t.Errorf("Marshal(x) = %#q; want %#q", b, want) - } -} - -func TestIssue10281(t *testing.T) { - type Foo struct { - N Number - } - x := Foo{Number(`invalid`)} - - b, err := Marshal(&x) - if err == nil { - t.Errorf("Marshal(&x) = %#q; want error", b) - } -} - -func TestHTMLEscape(t *testing.T) { - var b, want bytes.Buffer - m := `{"M":"<html>foo &` + "\xe2\x80\xa8 \xe2\x80\xa9" + `</html>"}` - want.Write([]byte(`{"M":"\u003chtml\u003efoo \u0026\u2028 \u2029\u003c/html\u003e"}`)) - HTMLEscape(&b, []byte(m)) - if !bytes.Equal(b.Bytes(), want.Bytes()) { - t.Errorf("HTMLEscape(&b, []byte(m)) = %s; want %s", b.Bytes(), want.Bytes()) - } -} - -// golang.org/issue/8582 -func TestEncodePointerString(t *testing.T) { - type stringPointer struct { - N *int64 `json:"n,string"` - } - var n int64 = 42 - b, err := Marshal(stringPointer{N: &n}) - if err != nil { - t.Fatalf("Marshal: %v", err) - } - if got, want := string(b), `{"n":"42"}`; got != want { - t.Errorf("Marshal = %s, want %s", got, want) - } - var back stringPointer - err = Unmarshal(b, &back) - if err != nil { - t.Fatalf("Unmarshal: %v", err) - } - if back.N == nil { - t.Fatalf("Unmarshalled nil N field") - } - if *back.N != 42 { - t.Fatalf("*N = %d; want 42", *back.N) - } -} - -var encodeStringTests = []struct { - in string - out string -}{ - {"\x00", `"\u0000"`}, - {"\x01", `"\u0001"`}, - {"\x02", `"\u0002"`}, - {"\x03", `"\u0003"`}, - {"\x04", `"\u0004"`}, - {"\x05", `"\u0005"`}, - {"\x06", `"\u0006"`}, - {"\x07", `"\u0007"`}, - {"\x08", `"\u0008"`}, - {"\x09", `"\t"`}, - {"\x0a", `"\n"`}, - {"\x0b", `"\u000b"`}, - {"\x0c", `"\u000c"`}, - {"\x0d", `"\r"`}, - {"\x0e", `"\u000e"`}, - {"\x0f", `"\u000f"`}, - {"\x10", `"\u0010"`}, - {"\x11", `"\u0011"`}, - {"\x12", `"\u0012"`}, - {"\x13", `"\u0013"`}, - {"\x14", `"\u0014"`}, - {"\x15", `"\u0015"`}, - {"\x16", `"\u0016"`}, - {"\x17", `"\u0017"`}, - {"\x18", `"\u0018"`}, - {"\x19", `"\u0019"`}, - {"\x1a", `"\u001a"`}, - {"\x1b", `"\u001b"`}, - {"\x1c", `"\u001c"`}, - {"\x1d", `"\u001d"`}, - {"\x1e", `"\u001e"`}, - {"\x1f", `"\u001f"`}, -} - -func TestEncodeString(t *testing.T) { - for _, tt := range encodeStringTests { - b, err := Marshal(tt.in) - if err != nil { - t.Errorf("Marshal(%q): %v", tt.in, err) - continue - } - out := string(b) - if out != tt.out { - t.Errorf("Marshal(%q) = %#q, want %#q", tt.in, out, tt.out) - } - } -} diff --git a/vendor/gopkg.in/square/go-jose.v1/json/indent.go b/vendor/gopkg.in/square/go-jose.v1/json/indent.go deleted file mode 100644 index 7cd9f4db..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/json/indent.go +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package json - -import "bytes" - -// Compact appends to dst the JSON-encoded src with -// insignificant space characters elided. -func Compact(dst *bytes.Buffer, src []byte) error { - return compact(dst, src, false) -} - -func compact(dst *bytes.Buffer, src []byte, escape bool) error { - origLen := dst.Len() - var scan scanner - scan.reset() - start := 0 - for i, c := range src { - if escape && (c == '<' || c == '>' || c == '&') { - if start < i { - dst.Write(src[start:i]) - } - dst.WriteString(`\u00`) - dst.WriteByte(hex[c>>4]) - dst.WriteByte(hex[c&0xF]) - start = i + 1 - } - // Convert U+2028 and U+2029 (E2 80 A8 and E2 80 A9). - if c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 { - if start < i { - dst.Write(src[start:i]) - } - dst.WriteString(`\u202`) - dst.WriteByte(hex[src[i+2]&0xF]) - start = i + 3 - } - v := scan.step(&scan, c) - if v >= scanSkipSpace { - if v == scanError { - break - } - if start < i { - dst.Write(src[start:i]) - } - start = i + 1 - } - } - if scan.eof() == scanError { - dst.Truncate(origLen) - return scan.err - } - if start < len(src) { - dst.Write(src[start:]) - } - return nil -} - -func newline(dst *bytes.Buffer, prefix, indent string, depth int) { - dst.WriteByte('\n') - dst.WriteString(prefix) - for i := 0; i < depth; i++ { - dst.WriteString(indent) - } -} - -// Indent appends to dst an indented form of the JSON-encoded src. -// Each element in a JSON object or array begins on a new, -// indented line beginning with prefix followed by one or more -// copies of indent according to the indentation nesting. -// The data appended to dst does not begin with the prefix nor -// any indentation, to make it easier to embed inside other formatted JSON data. -// Although leading space characters (space, tab, carriage return, newline) -// at the beginning of src are dropped, trailing space characters -// at the end of src are preserved and copied to dst. -// For example, if src has no trailing spaces, neither will dst; -// if src ends in a trailing newline, so will dst. -func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error { - origLen := dst.Len() - var scan scanner - scan.reset() - needIndent := false - depth := 0 - for _, c := range src { - scan.bytes++ - v := scan.step(&scan, c) - if v == scanSkipSpace { - continue - } - if v == scanError { - break - } - if needIndent && v != scanEndObject && v != scanEndArray { - needIndent = false - depth++ - newline(dst, prefix, indent, depth) - } - - // Emit semantically uninteresting bytes - // (in particular, punctuation in strings) unmodified. - if v == scanContinue { - dst.WriteByte(c) - continue - } - - // Add spacing around real punctuation. - switch c { - case '{', '[': - // delay indent so that empty object and array are formatted as {} and []. - needIndent = true - dst.WriteByte(c) - - case ',': - dst.WriteByte(c) - newline(dst, prefix, indent, depth) - - case ':': - dst.WriteByte(c) - dst.WriteByte(' ') - - case '}', ']': - if needIndent { - // suppress indent in empty object/array - needIndent = false - } else { - depth-- - newline(dst, prefix, indent, depth) - } - dst.WriteByte(c) - - default: - dst.WriteByte(c) - } - } - if scan.eof() == scanError { - dst.Truncate(origLen) - return scan.err - } - return nil -} diff --git a/vendor/gopkg.in/square/go-jose.v1/json/number_test.go b/vendor/gopkg.in/square/go-jose.v1/json/number_test.go deleted file mode 100644 index 4e63cf9c..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/json/number_test.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package json - -import ( - "regexp" - "testing" -) - -func TestNumberIsValid(t *testing.T) { - // From: http://stackoverflow.com/a/13340826 - var jsonNumberRegexp = regexp.MustCompile(`^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$`) - - validTests := []string{ - "0", - "-0", - "1", - "-1", - "0.1", - "-0.1", - "1234", - "-1234", - "12.34", - "-12.34", - "12E0", - "12E1", - "12e34", - "12E-0", - "12e+1", - "12e-34", - "-12E0", - "-12E1", - "-12e34", - "-12E-0", - "-12e+1", - "-12e-34", - "1.2E0", - "1.2E1", - "1.2e34", - "1.2E-0", - "1.2e+1", - "1.2e-34", - "-1.2E0", - "-1.2E1", - "-1.2e34", - "-1.2E-0", - "-1.2e+1", - "-1.2e-34", - "0E0", - "0E1", - "0e34", - "0E-0", - "0e+1", - "0e-34", - "-0E0", - "-0E1", - "-0e34", - "-0E-0", - "-0e+1", - "-0e-34", - } - - for _, test := range validTests { - if !isValidNumber(test) { - t.Errorf("%s should be valid", test) - } - - var f float64 - if err := Unmarshal([]byte(test), &f); err != nil { - t.Errorf("%s should be valid but Unmarshal failed: %v", test, err) - } - - if !jsonNumberRegexp.MatchString(test) { - t.Errorf("%s should be valid but regexp does not match", test) - } - } - - invalidTests := []string{ - "", - "invalid", - "1.0.1", - "1..1", - "-1-2", - "012a42", - "01.2", - "012", - "12E12.12", - "1e2e3", - "1e+-2", - "1e--23", - "1e", - "e1", - "1e+", - "1ea", - "1a", - "1.a", - "1.", - "01", - "1.e1", - } - - for _, test := range invalidTests { - if isValidNumber(test) { - t.Errorf("%s should be invalid", test) - } - - var f float64 - if err := Unmarshal([]byte(test), &f); err == nil { - t.Errorf("%s should be invalid but unmarshal wrote %v", test, f) - } - - if jsonNumberRegexp.MatchString(test) { - t.Errorf("%s should be invalid but matches regexp", test) - } - } -} - -func BenchmarkNumberIsValid(b *testing.B) { - s := "-61657.61667E+61673" - for i := 0; i < b.N; i++ { - isValidNumber(s) - } -} - -func BenchmarkNumberIsValidRegexp(b *testing.B) { - var jsonNumberRegexp = regexp.MustCompile(`^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$`) - s := "-61657.61667E+61673" - for i := 0; i < b.N; i++ { - jsonNumberRegexp.MatchString(s) - } -} diff --git a/vendor/gopkg.in/square/go-jose.v1/json/scanner.go b/vendor/gopkg.in/square/go-jose.v1/json/scanner.go deleted file mode 100644 index ee6622e8..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/json/scanner.go +++ /dev/null @@ -1,623 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package json - -// JSON value parser state machine. -// Just about at the limit of what is reasonable to write by hand. -// Some parts are a bit tedious, but overall it nicely factors out the -// otherwise common code from the multiple scanning functions -// in this package (Compact, Indent, checkValid, nextValue, etc). -// -// This file starts with two simple examples using the scanner -// before diving into the scanner itself. - -import "strconv" - -// checkValid verifies that data is valid JSON-encoded data. -// scan is passed in for use by checkValid to avoid an allocation. -func checkValid(data []byte, scan *scanner) error { - scan.reset() - for _, c := range data { - scan.bytes++ - if scan.step(scan, c) == scanError { - return scan.err - } - } - if scan.eof() == scanError { - return scan.err - } - return nil -} - -// nextValue splits data after the next whole JSON value, -// returning that value and the bytes that follow it as separate slices. -// scan is passed in for use by nextValue to avoid an allocation. -func nextValue(data []byte, scan *scanner) (value, rest []byte, err error) { - scan.reset() - for i, c := range data { - v := scan.step(scan, c) - if v >= scanEndObject { - switch v { - // probe the scanner with a space to determine whether we will - // get scanEnd on the next character. Otherwise, if the next character - // is not a space, scanEndTop allocates a needless error. - case scanEndObject, scanEndArray: - if scan.step(scan, ' ') == scanEnd { - return data[:i+1], data[i+1:], nil - } - case scanError: - return nil, nil, scan.err - case scanEnd: - return data[:i], data[i:], nil - } - } - } - if scan.eof() == scanError { - return nil, nil, scan.err - } - return data, nil, nil -} - -// A SyntaxError is a description of a JSON syntax error. -type SyntaxError struct { - msg string // description of error - Offset int64 // error occurred after reading Offset bytes -} - -func (e *SyntaxError) Error() string { return e.msg } - -// A scanner is a JSON scanning state machine. -// Callers call scan.reset() and then pass bytes in one at a time -// by calling scan.step(&scan, c) for each byte. -// The return value, referred to as an opcode, tells the -// caller about significant parsing events like beginning -// and ending literals, objects, and arrays, so that the -// caller can follow along if it wishes. -// The return value scanEnd indicates that a single top-level -// JSON value has been completed, *before* the byte that -// just got passed in. (The indication must be delayed in order -// to recognize the end of numbers: is 123 a whole value or -// the beginning of 12345e+6?). -type scanner struct { - // The step is a func to be called to execute the next transition. - // Also tried using an integer constant and a single func - // with a switch, but using the func directly was 10% faster - // on a 64-bit Mac Mini, and it's nicer to read. - step func(*scanner, byte) int - - // Reached end of top-level value. - endTop bool - - // Stack of what we're in the middle of - array values, object keys, object values. - parseState []int - - // Error that happened, if any. - err error - - // 1-byte redo (see undo method) - redo bool - redoCode int - redoState func(*scanner, byte) int - - // total bytes consumed, updated by decoder.Decode - bytes int64 -} - -// These values are returned by the state transition functions -// assigned to scanner.state and the method scanner.eof. -// They give details about the current state of the scan that -// callers might be interested to know about. -// It is okay to ignore the return value of any particular -// call to scanner.state: if one call returns scanError, -// every subsequent call will return scanError too. -const ( - // Continue. - scanContinue = iota // uninteresting byte - scanBeginLiteral // end implied by next result != scanContinue - scanBeginObject // begin object - scanObjectKey // just finished object key (string) - scanObjectValue // just finished non-last object value - scanEndObject // end object (implies scanObjectValue if possible) - scanBeginArray // begin array - scanArrayValue // just finished array value - scanEndArray // end array (implies scanArrayValue if possible) - scanSkipSpace // space byte; can skip; known to be last "continue" result - - // Stop. - scanEnd // top-level value ended *before* this byte; known to be first "stop" result - scanError // hit an error, scanner.err. -) - -// These values are stored in the parseState stack. -// They give the current state of a composite value -// being scanned. If the parser is inside a nested value -// the parseState describes the nested state, outermost at entry 0. -const ( - parseObjectKey = iota // parsing object key (before colon) - parseObjectValue // parsing object value (after colon) - parseArrayValue // parsing array value -) - -// reset prepares the scanner for use. -// It must be called before calling s.step. -func (s *scanner) reset() { - s.step = stateBeginValue - s.parseState = s.parseState[0:0] - s.err = nil - s.redo = false - s.endTop = false -} - -// eof tells the scanner that the end of input has been reached. -// It returns a scan status just as s.step does. -func (s *scanner) eof() int { - if s.err != nil { - return scanError - } - if s.endTop { - return scanEnd - } - s.step(s, ' ') - if s.endTop { - return scanEnd - } - if s.err == nil { - s.err = &SyntaxError{"unexpected end of JSON input", s.bytes} - } - return scanError -} - -// pushParseState pushes a new parse state p onto the parse stack. -func (s *scanner) pushParseState(p int) { - s.parseState = append(s.parseState, p) -} - -// popParseState pops a parse state (already obtained) off the stack -// and updates s.step accordingly. -func (s *scanner) popParseState() { - n := len(s.parseState) - 1 - s.parseState = s.parseState[0:n] - s.redo = false - if n == 0 { - s.step = stateEndTop - s.endTop = true - } else { - s.step = stateEndValue - } -} - -func isSpace(c byte) bool { - return c == ' ' || c == '\t' || c == '\r' || c == '\n' -} - -// stateBeginValueOrEmpty is the state after reading `[`. -func stateBeginValueOrEmpty(s *scanner, c byte) int { - if c <= ' ' && isSpace(c) { - return scanSkipSpace - } - if c == ']' { - return stateEndValue(s, c) - } - return stateBeginValue(s, c) -} - -// stateBeginValue is the state at the beginning of the input. -func stateBeginValue(s *scanner, c byte) int { - if c <= ' ' && isSpace(c) { - return scanSkipSpace - } - switch c { - case '{': - s.step = stateBeginStringOrEmpty - s.pushParseState(parseObjectKey) - return scanBeginObject - case '[': - s.step = stateBeginValueOrEmpty - s.pushParseState(parseArrayValue) - return scanBeginArray - case '"': - s.step = stateInString - return scanBeginLiteral - case '-': - s.step = stateNeg - return scanBeginLiteral - case '0': // beginning of 0.123 - s.step = state0 - return scanBeginLiteral - case 't': // beginning of true - s.step = stateT - return scanBeginLiteral - case 'f': // beginning of false - s.step = stateF - return scanBeginLiteral - case 'n': // beginning of null - s.step = stateN - return scanBeginLiteral - } - if '1' <= c && c <= '9' { // beginning of 1234.5 - s.step = state1 - return scanBeginLiteral - } - return s.error(c, "looking for beginning of value") -} - -// stateBeginStringOrEmpty is the state after reading `{`. -func stateBeginStringOrEmpty(s *scanner, c byte) int { - if c <= ' ' && isSpace(c) { - return scanSkipSpace - } - if c == '}' { - n := len(s.parseState) - s.parseState[n-1] = parseObjectValue - return stateEndValue(s, c) - } - return stateBeginString(s, c) -} - -// stateBeginString is the state after reading `{"key": value,`. -func stateBeginString(s *scanner, c byte) int { - if c <= ' ' && isSpace(c) { - return scanSkipSpace - } - if c == '"' { - s.step = stateInString - return scanBeginLiteral - } - return s.error(c, "looking for beginning of object key string") -} - -// stateEndValue is the state after completing a value, -// such as after reading `{}` or `true` or `["x"`. -func stateEndValue(s *scanner, c byte) int { - n := len(s.parseState) - if n == 0 { - // Completed top-level before the current byte. - s.step = stateEndTop - s.endTop = true - return stateEndTop(s, c) - } - if c <= ' ' && isSpace(c) { - s.step = stateEndValue - return scanSkipSpace - } - ps := s.parseState[n-1] - switch ps { - case parseObjectKey: - if c == ':' { - s.parseState[n-1] = parseObjectValue - s.step = stateBeginValue - return scanObjectKey - } - return s.error(c, "after object key") - case parseObjectValue: - if c == ',' { - s.parseState[n-1] = parseObjectKey - s.step = stateBeginString - return scanObjectValue - } - if c == '}' { - s.popParseState() - return scanEndObject - } - return s.error(c, "after object key:value pair") - case parseArrayValue: - if c == ',' { - s.step = stateBeginValue - return scanArrayValue - } - if c == ']' { - s.popParseState() - return scanEndArray - } - return s.error(c, "after array element") - } - return s.error(c, "") -} - -// stateEndTop is the state after finishing the top-level value, -// such as after reading `{}` or `[1,2,3]`. -// Only space characters should be seen now. -func stateEndTop(s *scanner, c byte) int { - if c != ' ' && c != '\t' && c != '\r' && c != '\n' { - // Complain about non-space byte on next call. - s.error(c, "after top-level value") - } - return scanEnd -} - -// stateInString is the state after reading `"`. -func stateInString(s *scanner, c byte) int { - if c == '"' { - s.step = stateEndValue - return scanContinue - } - if c == '\\' { - s.step = stateInStringEsc - return scanContinue - } - if c < 0x20 { - return s.error(c, "in string literal") - } - return scanContinue -} - -// stateInStringEsc is the state after reading `"\` during a quoted string. -func stateInStringEsc(s *scanner, c byte) int { - switch c { - case 'b', 'f', 'n', 'r', 't', '\\', '/', '"': - s.step = stateInString - return scanContinue - case 'u': - s.step = stateInStringEscU - return scanContinue - } - return s.error(c, "in string escape code") -} - -// stateInStringEscU is the state after reading `"\u` during a quoted string. -func stateInStringEscU(s *scanner, c byte) int { - if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' { - s.step = stateInStringEscU1 - return scanContinue - } - // numbers - return s.error(c, "in \\u hexadecimal character escape") -} - -// stateInStringEscU1 is the state after reading `"\u1` during a quoted string. -func stateInStringEscU1(s *scanner, c byte) int { - if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' { - s.step = stateInStringEscU12 - return scanContinue - } - // numbers - return s.error(c, "in \\u hexadecimal character escape") -} - -// stateInStringEscU12 is the state after reading `"\u12` during a quoted string. -func stateInStringEscU12(s *scanner, c byte) int { - if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' { - s.step = stateInStringEscU123 - return scanContinue - } - // numbers - return s.error(c, "in \\u hexadecimal character escape") -} - -// stateInStringEscU123 is the state after reading `"\u123` during a quoted string. -func stateInStringEscU123(s *scanner, c byte) int { - if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' { - s.step = stateInString - return scanContinue - } - // numbers - return s.error(c, "in \\u hexadecimal character escape") -} - -// stateNeg is the state after reading `-` during a number. -func stateNeg(s *scanner, c byte) int { - if c == '0' { - s.step = state0 - return scanContinue - } - if '1' <= c && c <= '9' { - s.step = state1 - return scanContinue - } - return s.error(c, "in numeric literal") -} - -// state1 is the state after reading a non-zero integer during a number, -// such as after reading `1` or `100` but not `0`. -func state1(s *scanner, c byte) int { - if '0' <= c && c <= '9' { - s.step = state1 - return scanContinue - } - return state0(s, c) -} - -// state0 is the state after reading `0` during a number. -func state0(s *scanner, c byte) int { - if c == '.' { - s.step = stateDot - return scanContinue - } - if c == 'e' || c == 'E' { - s.step = stateE - return scanContinue - } - return stateEndValue(s, c) -} - -// stateDot is the state after reading the integer and decimal point in a number, -// such as after reading `1.`. -func stateDot(s *scanner, c byte) int { - if '0' <= c && c <= '9' { - s.step = stateDot0 - return scanContinue - } - return s.error(c, "after decimal point in numeric literal") -} - -// stateDot0 is the state after reading the integer, decimal point, and subsequent -// digits of a number, such as after reading `3.14`. -func stateDot0(s *scanner, c byte) int { - if '0' <= c && c <= '9' { - return scanContinue - } - if c == 'e' || c == 'E' { - s.step = stateE - return scanContinue - } - return stateEndValue(s, c) -} - -// stateE is the state after reading the mantissa and e in a number, -// such as after reading `314e` or `0.314e`. -func stateE(s *scanner, c byte) int { - if c == '+' || c == '-' { - s.step = stateESign - return scanContinue - } - return stateESign(s, c) -} - -// stateESign is the state after reading the mantissa, e, and sign in a number, -// such as after reading `314e-` or `0.314e+`. -func stateESign(s *scanner, c byte) int { - if '0' <= c && c <= '9' { - s.step = stateE0 - return scanContinue - } - return s.error(c, "in exponent of numeric literal") -} - -// stateE0 is the state after reading the mantissa, e, optional sign, -// and at least one digit of the exponent in a number, -// such as after reading `314e-2` or `0.314e+1` or `3.14e0`. -func stateE0(s *scanner, c byte) int { - if '0' <= c && c <= '9' { - return scanContinue - } - return stateEndValue(s, c) -} - -// stateT is the state after reading `t`. -func stateT(s *scanner, c byte) int { - if c == 'r' { - s.step = stateTr - return scanContinue - } - return s.error(c, "in literal true (expecting 'r')") -} - -// stateTr is the state after reading `tr`. -func stateTr(s *scanner, c byte) int { - if c == 'u' { - s.step = stateTru - return scanContinue - } - return s.error(c, "in literal true (expecting 'u')") -} - -// stateTru is the state after reading `tru`. -func stateTru(s *scanner, c byte) int { - if c == 'e' { - s.step = stateEndValue - return scanContinue - } - return s.error(c, "in literal true (expecting 'e')") -} - -// stateF is the state after reading `f`. -func stateF(s *scanner, c byte) int { - if c == 'a' { - s.step = stateFa - return scanContinue - } - return s.error(c, "in literal false (expecting 'a')") -} - -// stateFa is the state after reading `fa`. -func stateFa(s *scanner, c byte) int { - if c == 'l' { - s.step = stateFal - return scanContinue - } - return s.error(c, "in literal false (expecting 'l')") -} - -// stateFal is the state after reading `fal`. -func stateFal(s *scanner, c byte) int { - if c == 's' { - s.step = stateFals - return scanContinue - } - return s.error(c, "in literal false (expecting 's')") -} - -// stateFals is the state after reading `fals`. -func stateFals(s *scanner, c byte) int { - if c == 'e' { - s.step = stateEndValue - return scanContinue - } - return s.error(c, "in literal false (expecting 'e')") -} - -// stateN is the state after reading `n`. -func stateN(s *scanner, c byte) int { - if c == 'u' { - s.step = stateNu - return scanContinue - } - return s.error(c, "in literal null (expecting 'u')") -} - -// stateNu is the state after reading `nu`. -func stateNu(s *scanner, c byte) int { - if c == 'l' { - s.step = stateNul - return scanContinue - } - return s.error(c, "in literal null (expecting 'l')") -} - -// stateNul is the state after reading `nul`. -func stateNul(s *scanner, c byte) int { - if c == 'l' { - s.step = stateEndValue - return scanContinue - } - return s.error(c, "in literal null (expecting 'l')") -} - -// stateError is the state after reaching a syntax error, -// such as after reading `[1}` or `5.1.2`. -func stateError(s *scanner, c byte) int { - return scanError -} - -// error records an error and switches to the error state. -func (s *scanner) error(c byte, context string) int { - s.step = stateError - s.err = &SyntaxError{"invalid character " + quoteChar(c) + " " + context, s.bytes} - return scanError -} - -// quoteChar formats c as a quoted character literal -func quoteChar(c byte) string { - // special cases - different from quoted strings - if c == '\'' { - return `'\''` - } - if c == '"' { - return `'"'` - } - - // use quoted string with different quotation marks - s := strconv.Quote(string(c)) - return "'" + s[1:len(s)-1] + "'" -} - -// undo causes the scanner to return scanCode from the next state transition. -// This gives callers a simple 1-byte undo mechanism. -func (s *scanner) undo(scanCode int) { - if s.redo { - panic("json: invalid use of scanner") - } - s.redoCode = scanCode - s.redoState = s.step - s.step = stateRedo - s.redo = true -} - -// stateRedo helps implement the scanner's 1-byte undo. -func stateRedo(s *scanner, c byte) int { - s.redo = false - s.step = s.redoState - return s.redoCode -} diff --git a/vendor/gopkg.in/square/go-jose.v1/json/scanner_test.go b/vendor/gopkg.in/square/go-jose.v1/json/scanner_test.go deleted file mode 100644 index 66383ef0..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/json/scanner_test.go +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package json - -import ( - "bytes" - "math" - "math/rand" - "reflect" - "testing" -) - -// Tests of simple examples. - -type example struct { - compact string - indent string -} - -var examples = []example{ - {`1`, `1`}, - {`{}`, `{}`}, - {`[]`, `[]`}, - {`{"":2}`, "{\n\t\"\": 2\n}"}, - {`[3]`, "[\n\t3\n]"}, - {`[1,2,3]`, "[\n\t1,\n\t2,\n\t3\n]"}, - {`{"x":1}`, "{\n\t\"x\": 1\n}"}, - {ex1, ex1i}, -} - -var ex1 = `[true,false,null,"x",1,1.5,0,-5e+2]` - -var ex1i = `[ - true, - false, - null, - "x", - 1, - 1.5, - 0, - -5e+2 -]` - -func TestCompact(t *testing.T) { - var buf bytes.Buffer - for _, tt := range examples { - buf.Reset() - if err := Compact(&buf, []byte(tt.compact)); err != nil { - t.Errorf("Compact(%#q): %v", tt.compact, err) - } else if s := buf.String(); s != tt.compact { - t.Errorf("Compact(%#q) = %#q, want original", tt.compact, s) - } - - buf.Reset() - if err := Compact(&buf, []byte(tt.indent)); err != nil { - t.Errorf("Compact(%#q): %v", tt.indent, err) - continue - } else if s := buf.String(); s != tt.compact { - t.Errorf("Compact(%#q) = %#q, want %#q", tt.indent, s, tt.compact) - } - } -} - -func TestCompactSeparators(t *testing.T) { - // U+2028 and U+2029 should be escaped inside strings. - // They should not appear outside strings. - tests := []struct { - in, compact string - }{ - {"{\"\u2028\": 1}", `{"\u2028":1}`}, - {"{\"\u2029\" :2}", `{"\u2029":2}`}, - } - for _, tt := range tests { - var buf bytes.Buffer - if err := Compact(&buf, []byte(tt.in)); err != nil { - t.Errorf("Compact(%q): %v", tt.in, err) - } else if s := buf.String(); s != tt.compact { - t.Errorf("Compact(%q) = %q, want %q", tt.in, s, tt.compact) - } - } -} - -func TestIndent(t *testing.T) { - var buf bytes.Buffer - for _, tt := range examples { - buf.Reset() - if err := Indent(&buf, []byte(tt.indent), "", "\t"); err != nil { - t.Errorf("Indent(%#q): %v", tt.indent, err) - } else if s := buf.String(); s != tt.indent { - t.Errorf("Indent(%#q) = %#q, want original", tt.indent, s) - } - - buf.Reset() - if err := Indent(&buf, []byte(tt.compact), "", "\t"); err != nil { - t.Errorf("Indent(%#q): %v", tt.compact, err) - continue - } else if s := buf.String(); s != tt.indent { - t.Errorf("Indent(%#q) = %#q, want %#q", tt.compact, s, tt.indent) - } - } -} - -// Tests of a large random structure. - -func TestCompactBig(t *testing.T) { - initBig() - var buf bytes.Buffer - if err := Compact(&buf, jsonBig); err != nil { - t.Fatalf("Compact: %v", err) - } - b := buf.Bytes() - if !bytes.Equal(b, jsonBig) { - t.Error("Compact(jsonBig) != jsonBig") - diff(t, b, jsonBig) - return - } -} - -func TestIndentBig(t *testing.T) { - initBig() - var buf bytes.Buffer - if err := Indent(&buf, jsonBig, "", "\t"); err != nil { - t.Fatalf("Indent1: %v", err) - } - b := buf.Bytes() - if len(b) == len(jsonBig) { - // jsonBig is compact (no unnecessary spaces); - // indenting should make it bigger - t.Fatalf("Indent(jsonBig) did not get bigger") - } - - // should be idempotent - var buf1 bytes.Buffer - if err := Indent(&buf1, b, "", "\t"); err != nil { - t.Fatalf("Indent2: %v", err) - } - b1 := buf1.Bytes() - if !bytes.Equal(b1, b) { - t.Error("Indent(Indent(jsonBig)) != Indent(jsonBig)") - diff(t, b1, b) - return - } - - // should get back to original - buf1.Reset() - if err := Compact(&buf1, b); err != nil { - t.Fatalf("Compact: %v", err) - } - b1 = buf1.Bytes() - if !bytes.Equal(b1, jsonBig) { - t.Error("Compact(Indent(jsonBig)) != jsonBig") - diff(t, b1, jsonBig) - return - } -} - -type indentErrorTest struct { - in string - err error -} - -var indentErrorTests = []indentErrorTest{ - {`{"X": "foo", "Y"}`, &SyntaxError{"invalid character '}' after object key", 17}}, - {`{"X": "foo" "Y": "bar"}`, &SyntaxError{"invalid character '\"' after object key:value pair", 13}}, -} - -func TestIndentErrors(t *testing.T) { - for i, tt := range indentErrorTests { - slice := make([]uint8, 0) - buf := bytes.NewBuffer(slice) - if err := Indent(buf, []uint8(tt.in), "", ""); err != nil { - if !reflect.DeepEqual(err, tt.err) { - t.Errorf("#%d: Indent: %#v", i, err) - continue - } - } - } -} - -func TestNextValueBig(t *testing.T) { - initBig() - var scan scanner - item, rest, err := nextValue(jsonBig, &scan) - if err != nil { - t.Fatalf("nextValue: %s", err) - } - if len(item) != len(jsonBig) || &item[0] != &jsonBig[0] { - t.Errorf("invalid item: %d %d", len(item), len(jsonBig)) - } - if len(rest) != 0 { - t.Errorf("invalid rest: %d", len(rest)) - } - - item, rest, err = nextValue(append(jsonBig, "HELLO WORLD"...), &scan) - if err != nil { - t.Fatalf("nextValue extra: %s", err) - } - if len(item) != len(jsonBig) { - t.Errorf("invalid item: %d %d", len(item), len(jsonBig)) - } - if string(rest) != "HELLO WORLD" { - t.Errorf("invalid rest: %d", len(rest)) - } -} - -var benchScan scanner - -func BenchmarkSkipValue(b *testing.B) { - initBig() - b.ResetTimer() - for i := 0; i < b.N; i++ { - nextValue(jsonBig, &benchScan) - } - b.SetBytes(int64(len(jsonBig))) -} - -func diff(t *testing.T, a, b []byte) { - for i := 0; ; i++ { - if i >= len(a) || i >= len(b) || a[i] != b[i] { - j := i - 10 - if j < 0 { - j = 0 - } - t.Errorf("diverge at %d: «%s» vs «%s»", i, trim(a[j:]), trim(b[j:])) - return - } - } -} - -func trim(b []byte) []byte { - if len(b) > 20 { - return b[0:20] - } - return b -} - -// Generate a random JSON object. - -var jsonBig []byte - -func initBig() { - n := 10000 - if testing.Short() { - n = 100 - } - b, err := Marshal(genValue(n)) - if err != nil { - panic(err) - } - jsonBig = b -} - -func genValue(n int) interface{} { - if n > 1 { - switch rand.Intn(2) { - case 0: - return genArray(n) - case 1: - return genMap(n) - } - } - switch rand.Intn(3) { - case 0: - return rand.Intn(2) == 0 - case 1: - return rand.NormFloat64() - case 2: - return genString(30) - } - panic("unreachable") -} - -func genString(stddev float64) string { - n := int(math.Abs(rand.NormFloat64()*stddev + stddev/2)) - c := make([]rune, n) - for i := range c { - f := math.Abs(rand.NormFloat64()*64 + 32) - if f > 0x10ffff { - f = 0x10ffff - } - c[i] = rune(f) - } - return string(c) -} - -func genArray(n int) []interface{} { - f := int(math.Abs(rand.NormFloat64()) * math.Min(10, float64(n/2))) - if f > n { - f = n - } - if f < 1 { - f = 1 - } - x := make([]interface{}, f) - for i := range x { - x[i] = genValue(((i+1)*n)/f - (i*n)/f) - } - return x -} - -func genMap(n int) map[string]interface{} { - f := int(math.Abs(rand.NormFloat64()) * math.Min(10, float64(n/2))) - if f > n { - f = n - } - if n > 0 && f == 0 { - f = 1 - } - x := make(map[string]interface{}) - for i := 0; i < f; i++ { - x[genString(10)] = genValue(((i+1)*n)/f - (i*n)/f) - } - return x -} diff --git a/vendor/gopkg.in/square/go-jose.v1/json/stream.go b/vendor/gopkg.in/square/go-jose.v1/json/stream.go deleted file mode 100644 index 8ddcf4d2..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/json/stream.go +++ /dev/null @@ -1,480 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package json - -import ( - "bytes" - "errors" - "io" -) - -// A Decoder reads and decodes JSON objects from an input stream. -type Decoder struct { - r io.Reader - buf []byte - d decodeState - scanp int // start of unread data in buf - scan scanner - err error - - tokenState int - tokenStack []int -} - -// NewDecoder returns a new decoder that reads from r. -// -// The decoder introduces its own buffering and may -// read data from r beyond the JSON values requested. -func NewDecoder(r io.Reader) *Decoder { - return &Decoder{r: r} -} - -// UseNumber causes the Decoder to unmarshal a number into an interface{} as a -// Number instead of as a float64. -func (dec *Decoder) UseNumber() { dec.d.useNumber = true } - -// Decode reads the next JSON-encoded value from its -// input and stores it in the value pointed to by v. -// -// See the documentation for Unmarshal for details about -// the conversion of JSON into a Go value. -func (dec *Decoder) Decode(v interface{}) error { - if dec.err != nil { - return dec.err - } - - if err := dec.tokenPrepareForDecode(); err != nil { - return err - } - - if !dec.tokenValueAllowed() { - return &SyntaxError{msg: "not at beginning of value"} - } - - // Read whole value into buffer. - n, err := dec.readValue() - if err != nil { - return err - } - dec.d.init(dec.buf[dec.scanp : dec.scanp+n]) - dec.scanp += n - - // Don't save err from unmarshal into dec.err: - // the connection is still usable since we read a complete JSON - // object from it before the error happened. - err = dec.d.unmarshal(v) - - // fixup token streaming state - dec.tokenValueEnd() - - return err -} - -// Buffered returns a reader of the data remaining in the Decoder's -// buffer. The reader is valid until the next call to Decode. -func (dec *Decoder) Buffered() io.Reader { - return bytes.NewReader(dec.buf[dec.scanp:]) -} - -// readValue reads a JSON value into dec.buf. -// It returns the length of the encoding. -func (dec *Decoder) readValue() (int, error) { - dec.scan.reset() - - scanp := dec.scanp - var err error -Input: - for { - // Look in the buffer for a new value. - for i, c := range dec.buf[scanp:] { - dec.scan.bytes++ - v := dec.scan.step(&dec.scan, c) - if v == scanEnd { - scanp += i - break Input - } - // scanEnd is delayed one byte. - // We might block trying to get that byte from src, - // so instead invent a space byte. - if (v == scanEndObject || v == scanEndArray) && dec.scan.step(&dec.scan, ' ') == scanEnd { - scanp += i + 1 - break Input - } - if v == scanError { - dec.err = dec.scan.err - return 0, dec.scan.err - } - } - scanp = len(dec.buf) - - // Did the last read have an error? - // Delayed until now to allow buffer scan. - if err != nil { - if err == io.EOF { - if dec.scan.step(&dec.scan, ' ') == scanEnd { - break Input - } - if nonSpace(dec.buf) { - err = io.ErrUnexpectedEOF - } - } - dec.err = err - return 0, err - } - - n := scanp - dec.scanp - err = dec.refill() - scanp = dec.scanp + n - } - return scanp - dec.scanp, nil -} - -func (dec *Decoder) refill() error { - // Make room to read more into the buffer. - // First slide down data already consumed. - if dec.scanp > 0 { - n := copy(dec.buf, dec.buf[dec.scanp:]) - dec.buf = dec.buf[:n] - dec.scanp = 0 - } - - // Grow buffer if not large enough. - const minRead = 512 - if cap(dec.buf)-len(dec.buf) < minRead { - newBuf := make([]byte, len(dec.buf), 2*cap(dec.buf)+minRead) - copy(newBuf, dec.buf) - dec.buf = newBuf - } - - // Read. Delay error for next iteration (after scan). - n, err := dec.r.Read(dec.buf[len(dec.buf):cap(dec.buf)]) - dec.buf = dec.buf[0 : len(dec.buf)+n] - - return err -} - -func nonSpace(b []byte) bool { - for _, c := range b { - if !isSpace(c) { - return true - } - } - return false -} - -// An Encoder writes JSON objects to an output stream. -type Encoder struct { - w io.Writer - err error -} - -// NewEncoder returns a new encoder that writes to w. -func NewEncoder(w io.Writer) *Encoder { - return &Encoder{w: w} -} - -// Encode writes the JSON encoding of v to the stream, -// followed by a newline character. -// -// See the documentation for Marshal for details about the -// conversion of Go values to JSON. -func (enc *Encoder) Encode(v interface{}) error { - if enc.err != nil { - return enc.err - } - e := newEncodeState() - err := e.marshal(v) - if err != nil { - return err - } - - // Terminate each value with a newline. - // This makes the output look a little nicer - // when debugging, and some kind of space - // is required if the encoded value was a number, - // so that the reader knows there aren't more - // digits coming. - e.WriteByte('\n') - - if _, err = enc.w.Write(e.Bytes()); err != nil { - enc.err = err - } - encodeStatePool.Put(e) - return err -} - -// RawMessage is a raw encoded JSON object. -// It implements Marshaler and Unmarshaler and can -// be used to delay JSON decoding or precompute a JSON encoding. -type RawMessage []byte - -// MarshalJSON returns *m as the JSON encoding of m. -func (m *RawMessage) MarshalJSON() ([]byte, error) { - return *m, nil -} - -// UnmarshalJSON sets *m to a copy of data. -func (m *RawMessage) UnmarshalJSON(data []byte) error { - if m == nil { - return errors.New("json.RawMessage: UnmarshalJSON on nil pointer") - } - *m = append((*m)[0:0], data...) - return nil -} - -var _ Marshaler = (*RawMessage)(nil) -var _ Unmarshaler = (*RawMessage)(nil) - -// A Token holds a value of one of these types: -// -// Delim, for the four JSON delimiters [ ] { } -// bool, for JSON booleans -// float64, for JSON numbers -// Number, for JSON numbers -// string, for JSON string literals -// nil, for JSON null -// -type Token interface{} - -const ( - tokenTopValue = iota - tokenArrayStart - tokenArrayValue - tokenArrayComma - tokenObjectStart - tokenObjectKey - tokenObjectColon - tokenObjectValue - tokenObjectComma -) - -// advance tokenstate from a separator state to a value state -func (dec *Decoder) tokenPrepareForDecode() error { - // Note: Not calling peek before switch, to avoid - // putting peek into the standard Decode path. - // peek is only called when using the Token API. - switch dec.tokenState { - case tokenArrayComma: - c, err := dec.peek() - if err != nil { - return err - } - if c != ',' { - return &SyntaxError{"expected comma after array element", 0} - } - dec.scanp++ - dec.tokenState = tokenArrayValue - case tokenObjectColon: - c, err := dec.peek() - if err != nil { - return err - } - if c != ':' { - return &SyntaxError{"expected colon after object key", 0} - } - dec.scanp++ - dec.tokenState = tokenObjectValue - } - return nil -} - -func (dec *Decoder) tokenValueAllowed() bool { - switch dec.tokenState { - case tokenTopValue, tokenArrayStart, tokenArrayValue, tokenObjectValue: - return true - } - return false -} - -func (dec *Decoder) tokenValueEnd() { - switch dec.tokenState { - case tokenArrayStart, tokenArrayValue: - dec.tokenState = tokenArrayComma - case tokenObjectValue: - dec.tokenState = tokenObjectComma - } -} - -// A Delim is a JSON array or object delimiter, one of [ ] { or }. -type Delim rune - -func (d Delim) String() string { - return string(d) -} - -// Token returns the next JSON token in the input stream. -// At the end of the input stream, Token returns nil, io.EOF. -// -// Token guarantees that the delimiters [ ] { } it returns are -// properly nested and matched: if Token encounters an unexpected -// delimiter in the input, it will return an error. -// -// The input stream consists of basic JSON values—bool, string, -// number, and null—along with delimiters [ ] { } of type Delim -// to mark the start and end of arrays and objects. -// Commas and colons are elided. -func (dec *Decoder) Token() (Token, error) { - for { - c, err := dec.peek() - if err != nil { - return nil, err - } - switch c { - case '[': - if !dec.tokenValueAllowed() { - return dec.tokenError(c) - } - dec.scanp++ - dec.tokenStack = append(dec.tokenStack, dec.tokenState) - dec.tokenState = tokenArrayStart - return Delim('['), nil - - case ']': - if dec.tokenState != tokenArrayStart && dec.tokenState != tokenArrayComma { - return dec.tokenError(c) - } - dec.scanp++ - dec.tokenState = dec.tokenStack[len(dec.tokenStack)-1] - dec.tokenStack = dec.tokenStack[:len(dec.tokenStack)-1] - dec.tokenValueEnd() - return Delim(']'), nil - - case '{': - if !dec.tokenValueAllowed() { - return dec.tokenError(c) - } - dec.scanp++ - dec.tokenStack = append(dec.tokenStack, dec.tokenState) - dec.tokenState = tokenObjectStart - return Delim('{'), nil - - case '}': - if dec.tokenState != tokenObjectStart && dec.tokenState != tokenObjectComma { - return dec.tokenError(c) - } - dec.scanp++ - dec.tokenState = dec.tokenStack[len(dec.tokenStack)-1] - dec.tokenStack = dec.tokenStack[:len(dec.tokenStack)-1] - dec.tokenValueEnd() - return Delim('}'), nil - - case ':': - if dec.tokenState != tokenObjectColon { - return dec.tokenError(c) - } - dec.scanp++ - dec.tokenState = tokenObjectValue - continue - - case ',': - if dec.tokenState == tokenArrayComma { - dec.scanp++ - dec.tokenState = tokenArrayValue - continue - } - if dec.tokenState == tokenObjectComma { - dec.scanp++ - dec.tokenState = tokenObjectKey - continue - } - return dec.tokenError(c) - - case '"': - if dec.tokenState == tokenObjectStart || dec.tokenState == tokenObjectKey { - var x string - old := dec.tokenState - dec.tokenState = tokenTopValue - err := dec.Decode(&x) - dec.tokenState = old - if err != nil { - clearOffset(err) - return nil, err - } - dec.tokenState = tokenObjectColon - return x, nil - } - fallthrough - - default: - if !dec.tokenValueAllowed() { - return dec.tokenError(c) - } - var x interface{} - if err := dec.Decode(&x); err != nil { - clearOffset(err) - return nil, err - } - return x, nil - } - } -} - -func clearOffset(err error) { - if s, ok := err.(*SyntaxError); ok { - s.Offset = 0 - } -} - -func (dec *Decoder) tokenError(c byte) (Token, error) { - var context string - switch dec.tokenState { - case tokenTopValue: - context = " looking for beginning of value" - case tokenArrayStart, tokenArrayValue, tokenObjectValue: - context = " looking for beginning of value" - case tokenArrayComma: - context = " after array element" - case tokenObjectKey: - context = " looking for beginning of object key string" - case tokenObjectColon: - context = " after object key" - case tokenObjectComma: - context = " after object key:value pair" - } - return nil, &SyntaxError{"invalid character " + quoteChar(c) + " " + context, 0} -} - -// More reports whether there is another element in the -// current array or object being parsed. -func (dec *Decoder) More() bool { - c, err := dec.peek() - return err == nil && c != ']' && c != '}' -} - -func (dec *Decoder) peek() (byte, error) { - var err error - for { - for i := dec.scanp; i < len(dec.buf); i++ { - c := dec.buf[i] - if isSpace(c) { - continue - } - dec.scanp = i - return c, nil - } - // buffer has been scanned, now report any error - if err != nil { - return 0, err - } - err = dec.refill() - } -} - -/* -TODO - -// EncodeToken writes the given JSON token to the stream. -// It returns an error if the delimiters [ ] { } are not properly used. -// -// EncodeToken does not call Flush, because usually it is part of -// a larger operation such as Encode, and those will call Flush when finished. -// Callers that create an Encoder and then invoke EncodeToken directly, -// without using Encode, need to call Flush when finished to ensure that -// the JSON is written to the underlying writer. -func (e *Encoder) EncodeToken(t Token) error { - ... -} - -*/ diff --git a/vendor/gopkg.in/square/go-jose.v1/json/stream_test.go b/vendor/gopkg.in/square/go-jose.v1/json/stream_test.go deleted file mode 100644 index eccf365b..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/json/stream_test.go +++ /dev/null @@ -1,354 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package json - -import ( - "bytes" - "io" - "io/ioutil" - "log" - "net" - "net/http" - "net/http/httptest" - "reflect" - "strings" - "testing" -) - -// Test values for the stream test. -// One of each JSON kind. -var streamTest = []interface{}{ - 0.1, - "hello", - nil, - true, - false, - []interface{}{"a", "b", "c"}, - map[string]interface{}{"K": "Kelvin", "ß": "long s"}, - 3.14, // another value to make sure something can follow map -} - -var streamEncoded = `0.1 -"hello" -null -true -false -["a","b","c"] -{"ß":"long s","K":"Kelvin"} -3.14 -` - -func TestEncoder(t *testing.T) { - for i := 0; i <= len(streamTest); i++ { - var buf bytes.Buffer - enc := NewEncoder(&buf) - for j, v := range streamTest[0:i] { - if err := enc.Encode(v); err != nil { - t.Fatalf("encode #%d: %v", j, err) - } - } - if have, want := buf.String(), nlines(streamEncoded, i); have != want { - t.Errorf("encoding %d items: mismatch", i) - diff(t, []byte(have), []byte(want)) - break - } - } -} - -func TestDecoder(t *testing.T) { - for i := 0; i <= len(streamTest); i++ { - // Use stream without newlines as input, - // just to stress the decoder even more. - // Our test input does not include back-to-back numbers. - // Otherwise stripping the newlines would - // merge two adjacent JSON values. - var buf bytes.Buffer - for _, c := range nlines(streamEncoded, i) { - if c != '\n' { - buf.WriteRune(c) - } - } - out := make([]interface{}, i) - dec := NewDecoder(&buf) - for j := range out { - if err := dec.Decode(&out[j]); err != nil { - t.Fatalf("decode #%d/%d: %v", j, i, err) - } - } - if !reflect.DeepEqual(out, streamTest[0:i]) { - t.Errorf("decoding %d items: mismatch", i) - for j := range out { - if !reflect.DeepEqual(out[j], streamTest[j]) { - t.Errorf("#%d: have %v want %v", j, out[j], streamTest[j]) - } - } - break - } - } -} - -func TestDecoderBuffered(t *testing.T) { - r := strings.NewReader(`{"Name": "Gopher"} extra `) - var m struct { - Name string - } - d := NewDecoder(r) - err := d.Decode(&m) - if err != nil { - t.Fatal(err) - } - if m.Name != "Gopher" { - t.Errorf("Name = %q; want Gopher", m.Name) - } - rest, err := ioutil.ReadAll(d.Buffered()) - if err != nil { - t.Fatal(err) - } - if g, w := string(rest), " extra "; g != w { - t.Errorf("Remaining = %q; want %q", g, w) - } -} - -func nlines(s string, n int) string { - if n <= 0 { - return "" - } - for i, c := range s { - if c == '\n' { - if n--; n == 0 { - return s[0 : i+1] - } - } - } - return s -} - -func TestRawMessage(t *testing.T) { - // TODO(rsc): Should not need the * in *RawMessage - var data struct { - X float64 - Id *RawMessage - Y float32 - } - const raw = `["\u0056",null]` - const msg = `{"X":0.1,"Id":["\u0056",null],"Y":0.2}` - err := Unmarshal([]byte(msg), &data) - if err != nil { - t.Fatalf("Unmarshal: %v", err) - } - if string([]byte(*data.Id)) != raw { - t.Fatalf("Raw mismatch: have %#q want %#q", []byte(*data.Id), raw) - } - b, err := Marshal(&data) - if err != nil { - t.Fatalf("Marshal: %v", err) - } - if string(b) != msg { - t.Fatalf("Marshal: have %#q want %#q", b, msg) - } -} - -func TestNullRawMessage(t *testing.T) { - // TODO(rsc): Should not need the * in *RawMessage - var data struct { - X float64 - Id *RawMessage - Y float32 - } - data.Id = new(RawMessage) - const msg = `{"X":0.1,"Id":null,"Y":0.2}` - err := Unmarshal([]byte(msg), &data) - if err != nil { - t.Fatalf("Unmarshal: %v", err) - } - if data.Id != nil { - t.Fatalf("Raw mismatch: have non-nil, want nil") - } - b, err := Marshal(&data) - if err != nil { - t.Fatalf("Marshal: %v", err) - } - if string(b) != msg { - t.Fatalf("Marshal: have %#q want %#q", b, msg) - } -} - -var blockingTests = []string{ - `{"x": 1}`, - `[1, 2, 3]`, -} - -func TestBlocking(t *testing.T) { - for _, enc := range blockingTests { - r, w := net.Pipe() - go w.Write([]byte(enc)) - var val interface{} - - // If Decode reads beyond what w.Write writes above, - // it will block, and the test will deadlock. - if err := NewDecoder(r).Decode(&val); err != nil { - t.Errorf("decoding %s: %v", enc, err) - } - r.Close() - w.Close() - } -} - -func BenchmarkEncoderEncode(b *testing.B) { - b.ReportAllocs() - type T struct { - X, Y string - } - v := &T{"foo", "bar"} - for i := 0; i < b.N; i++ { - if err := NewEncoder(ioutil.Discard).Encode(v); err != nil { - b.Fatal(err) - } - } -} - -type tokenStreamCase struct { - json string - expTokens []interface{} -} - -type decodeThis struct { - v interface{} -} - -var tokenStreamCases []tokenStreamCase = []tokenStreamCase{ - // streaming token cases - {json: `10`, expTokens: []interface{}{float64(10)}}, - {json: ` [10] `, expTokens: []interface{}{ - Delim('['), float64(10), Delim(']')}}, - {json: ` [false,10,"b"] `, expTokens: []interface{}{ - Delim('['), false, float64(10), "b", Delim(']')}}, - {json: `{ "a": 1 }`, expTokens: []interface{}{ - Delim('{'), "a", float64(1), Delim('}')}}, - {json: `{"a": 1, "b":"3"}`, expTokens: []interface{}{ - Delim('{'), "a", float64(1), "b", "3", Delim('}')}}, - {json: ` [{"a": 1},{"a": 2}] `, expTokens: []interface{}{ - Delim('['), - Delim('{'), "a", float64(1), Delim('}'), - Delim('{'), "a", float64(2), Delim('}'), - Delim(']')}}, - {json: `{"obj": {"a": 1}}`, expTokens: []interface{}{ - Delim('{'), "obj", Delim('{'), "a", float64(1), Delim('}'), - Delim('}')}}, - {json: `{"obj": [{"a": 1}]}`, expTokens: []interface{}{ - Delim('{'), "obj", Delim('['), - Delim('{'), "a", float64(1), Delim('}'), - Delim(']'), Delim('}')}}, - - // streaming tokens with intermittent Decode() - {json: `{ "a": 1 }`, expTokens: []interface{}{ - Delim('{'), "a", - decodeThis{float64(1)}, - Delim('}')}}, - {json: ` [ { "a" : 1 } ] `, expTokens: []interface{}{ - Delim('['), - decodeThis{map[string]interface{}{"a": float64(1)}}, - Delim(']')}}, - {json: ` [{"a": 1},{"a": 2}] `, expTokens: []interface{}{ - Delim('['), - decodeThis{map[string]interface{}{"a": float64(1)}}, - decodeThis{map[string]interface{}{"a": float64(2)}}, - Delim(']')}}, - {json: `{ "obj" : [ { "a" : 1 } ] }`, expTokens: []interface{}{ - Delim('{'), "obj", Delim('['), - decodeThis{map[string]interface{}{"a": float64(1)}}, - Delim(']'), Delim('}')}}, - - {json: `{"obj": {"a": 1}}`, expTokens: []interface{}{ - Delim('{'), "obj", - decodeThis{map[string]interface{}{"a": float64(1)}}, - Delim('}')}}, - {json: `{"obj": [{"a": 1}]}`, expTokens: []interface{}{ - Delim('{'), "obj", - decodeThis{[]interface{}{ - map[string]interface{}{"a": float64(1)}, - }}, - Delim('}')}}, - {json: ` [{"a": 1} {"a": 2}] `, expTokens: []interface{}{ - Delim('['), - decodeThis{map[string]interface{}{"a": float64(1)}}, - decodeThis{&SyntaxError{"expected comma after array element", 0}}, - }}, - {json: `{ "a" 1 }`, expTokens: []interface{}{ - Delim('{'), "a", - decodeThis{&SyntaxError{"expected colon after object key", 0}}, - }}, -} - -func TestDecodeInStream(t *testing.T) { - - for ci, tcase := range tokenStreamCases { - - dec := NewDecoder(strings.NewReader(tcase.json)) - for i, etk := range tcase.expTokens { - - var tk interface{} - var err error - - if dt, ok := etk.(decodeThis); ok { - etk = dt.v - err = dec.Decode(&tk) - } else { - tk, err = dec.Token() - } - if experr, ok := etk.(error); ok { - if err == nil || err.Error() != experr.Error() { - t.Errorf("case %v: Expected error %v in %q, but was %v", ci, experr, tcase.json, err) - } - break - } else if err == io.EOF { - t.Errorf("case %v: Unexpected EOF in %q", ci, tcase.json) - break - } else if err != nil { - t.Errorf("case %v: Unexpected error '%v' in %q", ci, err, tcase.json) - break - } - if !reflect.DeepEqual(tk, etk) { - t.Errorf(`case %v: %q @ %v expected %T(%v) was %T(%v)`, ci, tcase.json, i, etk, etk, tk, tk) - break - } - } - } - -} - -// Test from golang.org/issue/11893 -func TestHTTPDecoding(t *testing.T) { - const raw = `{ "foo": "bar" }` - - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(raw)) - })) - defer ts.Close() - res, err := http.Get(ts.URL) - if err != nil { - log.Fatalf("GET failed: %v", err) - } - defer res.Body.Close() - - foo := struct { - Foo string `json:"foo"` - }{} - - d := NewDecoder(res.Body) - err = d.Decode(&foo) - if err != nil { - t.Fatalf("Decode: %v", err) - } - if foo.Foo != "bar" { - t.Errorf("decoded %q; want \"bar\"", foo.Foo) - } - - // make sure we get the EOF the second time - err = d.Decode(&foo) - if err != io.EOF { - t.Errorf("err = %v; want io.EOF", err) - } -} diff --git a/vendor/gopkg.in/square/go-jose.v1/json/tagkey_test.go b/vendor/gopkg.in/square/go-jose.v1/json/tagkey_test.go deleted file mode 100644 index 85bb4ba8..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/json/tagkey_test.go +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package json - -import ( - "testing" -) - -type basicLatin2xTag struct { - V string `json:"$%-/"` -} - -type basicLatin3xTag struct { - V string `json:"0123456789"` -} - -type basicLatin4xTag struct { - V string `json:"ABCDEFGHIJKLMO"` -} - -type basicLatin5xTag struct { - V string `json:"PQRSTUVWXYZ_"` -} - -type basicLatin6xTag struct { - V string `json:"abcdefghijklmno"` -} - -type basicLatin7xTag struct { - V string `json:"pqrstuvwxyz"` -} - -type miscPlaneTag struct { - V string `json:"色は匂へど"` -} - -type percentSlashTag struct { - V string `json:"text/html%"` // https://golang.org/issue/2718 -} - -type punctuationTag struct { - V string `json:"!#$%&()*+-./:<=>?@[]^_{|}~"` // https://golang.org/issue/3546 -} - -type emptyTag struct { - W string -} - -type misnamedTag struct { - X string `jsom:"Misnamed"` -} - -type badFormatTag struct { - Y string `:"BadFormat"` -} - -type badCodeTag struct { - Z string `json:" !\"#&'()*+,."` -} - -type spaceTag struct { - Q string `json:"With space"` -} - -type unicodeTag struct { - W string `json:"Ελλάδα"` -} - -var structTagObjectKeyTests = []struct { - raw interface{} - value string - key string -}{ - {basicLatin2xTag{"2x"}, "2x", "$%-/"}, - {basicLatin3xTag{"3x"}, "3x", "0123456789"}, - {basicLatin4xTag{"4x"}, "4x", "ABCDEFGHIJKLMO"}, - {basicLatin5xTag{"5x"}, "5x", "PQRSTUVWXYZ_"}, - {basicLatin6xTag{"6x"}, "6x", "abcdefghijklmno"}, - {basicLatin7xTag{"7x"}, "7x", "pqrstuvwxyz"}, - {miscPlaneTag{"いろはにほへと"}, "いろはにほへと", "色は匂へど"}, - {emptyTag{"Pour Moi"}, "Pour Moi", "W"}, - {misnamedTag{"Animal Kingdom"}, "Animal Kingdom", "X"}, - {badFormatTag{"Orfevre"}, "Orfevre", "Y"}, - {badCodeTag{"Reliable Man"}, "Reliable Man", "Z"}, - {percentSlashTag{"brut"}, "brut", "text/html%"}, - {punctuationTag{"Union Rags"}, "Union Rags", "!#$%&()*+-./:<=>?@[]^_{|}~"}, - {spaceTag{"Perreddu"}, "Perreddu", "With space"}, - {unicodeTag{"Loukanikos"}, "Loukanikos", "Ελλάδα"}, -} - -func TestStructTagObjectKey(t *testing.T) { - for _, tt := range structTagObjectKeyTests { - b, err := Marshal(tt.raw) - if err != nil { - t.Fatalf("Marshal(%#q) failed: %v", tt.raw, err) - } - var f interface{} - err = Unmarshal(b, &f) - if err != nil { - t.Fatalf("Unmarshal(%#q) failed: %v", b, err) - } - for i, v := range f.(map[string]interface{}) { - switch i { - case tt.key: - if s, ok := v.(string); !ok || s != tt.value { - t.Fatalf("Unexpected value: %#q, want %v", s, tt.value) - } - default: - t.Fatalf("Unexpected key: %#q, from %#q", i, b) - } - } - } -} diff --git a/vendor/gopkg.in/square/go-jose.v1/json/tags.go b/vendor/gopkg.in/square/go-jose.v1/json/tags.go deleted file mode 100644 index c38fd510..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/json/tags.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package json - -import ( - "strings" -) - -// tagOptions is the string following a comma in a struct field's "json" -// tag, or the empty string. It does not include the leading comma. -type tagOptions string - -// parseTag splits a struct field's json tag into its name and -// comma-separated options. -func parseTag(tag string) (string, tagOptions) { - if idx := strings.Index(tag, ","); idx != -1 { - return tag[:idx], tagOptions(tag[idx+1:]) - } - return tag, tagOptions("") -} - -// Contains reports whether a comma-separated list of options -// contains a particular substr flag. substr must be surrounded by a -// string boundary or commas. -func (o tagOptions) Contains(optionName string) bool { - if len(o) == 0 { - return false - } - s := string(o) - for s != "" { - var next string - i := strings.Index(s, ",") - if i >= 0 { - s, next = s[:i], s[i+1:] - } - if s == optionName { - return true - } - s = next - } - return false -} diff --git a/vendor/gopkg.in/square/go-jose.v1/json/tags_test.go b/vendor/gopkg.in/square/go-jose.v1/json/tags_test.go deleted file mode 100644 index 91fb1883..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/json/tags_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package json - -import ( - "testing" -) - -func TestTagParsing(t *testing.T) { - name, opts := parseTag("field,foobar,foo") - if name != "field" { - t.Fatalf("name = %q, want field", name) - } - for _, tt := range []struct { - opt string - want bool - }{ - {"foobar", true}, - {"foo", true}, - {"bar", false}, - } { - if opts.Contains(tt.opt) != tt.want { - t.Errorf("Contains(%q) = %v", tt.opt, !tt.want) - } - } -} diff --git a/vendor/gopkg.in/square/go-jose.v1/json/testdata/code.json.gz b/vendor/gopkg.in/square/go-jose.v1/json/testdata/code.json.gz deleted file mode 100644 index 0e2895b5..00000000 Binary files a/vendor/gopkg.in/square/go-jose.v1/json/testdata/code.json.gz and /dev/null differ diff --git a/vendor/gopkg.in/square/go-jose.v1/json_fork.go b/vendor/gopkg.in/square/go-jose.v1/json_fork.go deleted file mode 100644 index 333e02de..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/json_fork.go +++ /dev/null @@ -1,31 +0,0 @@ -// +build !std_json - -/*- - * Copyright 2014 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 jose - -import ( - "gopkg.in/square/go-jose.v1/json" -) - -func MarshalJSON(v interface{}) ([]byte, error) { - return json.Marshal(v) -} - -func UnmarshalJSON(data []byte, v interface{}) error { - return json.Unmarshal(data, v) -} diff --git a/vendor/gopkg.in/square/go-jose.v1/json_fork_test.go b/vendor/gopkg.in/square/go-jose.v1/json_fork_test.go deleted file mode 100644 index cacf5d5d..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/json_fork_test.go +++ /dev/null @@ -1,105 +0,0 @@ -// +build !std_json - -/*- - * Copyright 2014 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 jose - -import ( - "testing" -) - -type CaseSensitive struct { - A int `json:"Test"` - B int `json:"test"` - C int `json:"TEST"` -} - -type UnicodeTest struct { - Sig string `json:"sig"` -} - -func TestUnicodeComparison(t *testing.T) { - // Some tests from RFC 7515, Section 10.13 - raw := []byte(`{"\u0073ig":"foo"}`) - var ut UnicodeTest - err := UnmarshalJSON(raw, &ut) - if err != nil { - t.Error(err) - } - - if ut.Sig != "foo" { - t.Error("strings 'sig' and '\\u0073ig' should be equal") - } - - raw = []byte(`{"si\u0047":"bar"}`) - var ut2 UnicodeTest - err = UnmarshalJSON(raw, &ut2) - if err != nil { - t.Error(err) - } - - if ut2.Sig != "" { - t.Error("strings 'sig' and 'si\\u0047' should not be equal") - } -} - -func TestCaseSensitiveJSON(t *testing.T) { - raw := []byte(`{"test":42}`) - var cs CaseSensitive - err := UnmarshalJSON(raw, &cs) - if err != nil { - t.Error(err) - } - - if cs.A != 0 || cs.B != 42 || cs.C != 0 { - t.Errorf("parsing JSON should be case-sensitive (got %v)", cs) - } -} - -func TestRejectDuplicateKeysObject(t *testing.T) { - raw := []byte(`{"test":42,"test":43}`) - var cs CaseSensitive - err := UnmarshalJSON(raw, &cs) - if err == nil { - t.Error("should reject JSON with duplicate keys, but didn't") - } -} - -func TestRejectDuplicateKeysInterface(t *testing.T) { - raw := []byte(`{"test":42,"test":43}`) - var m interface{} - err := UnmarshalJSON(raw, &m) - if err == nil { - t.Error("should reject JSON with duplicate keys, but didn't") - } -} - -func TestParseCaseSensitiveJWE(t *testing.T) { - invalidJWE := `{"protected":"eyJlbmMiOiJYWVoiLCJBTEciOiJYWVoifQo","encrypted_key":"QUJD","iv":"QUJD","ciphertext":"QUJD","tag":"QUJD"}` - _, err := ParseEncrypted(invalidJWE) - if err == nil { - t.Error("Able to parse message with case-invalid headers", invalidJWE) - } -} - -func TestParseCaseSensitiveJWS(t *testing.T) { - invalidJWS := `{"PAYLOAD":"CUJD","signatures":[{"protected":"e30","signature":"CUJD"}]}` - _, err := ParseSigned(invalidJWS) - if err == nil { - t.Error("Able to parse message with case-invalid headers", invalidJWS) - } -} diff --git a/vendor/gopkg.in/square/go-jose.v1/json_std.go b/vendor/gopkg.in/square/go-jose.v1/json_std.go deleted file mode 100644 index cb3c3555..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/json_std.go +++ /dev/null @@ -1,31 +0,0 @@ -// +build std_json - -/*- - * Copyright 2014 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 jose - -import ( - "encoding/json" -) - -func MarshalJSON(v interface{}) ([]byte, error) { - return json.Marshal(v) -} - -func UnmarshalJSON(data []byte, v interface{}) error { - return json.Unmarshal(data, v) -} diff --git a/vendor/gopkg.in/square/go-jose.v1/json_std_test.go b/vendor/gopkg.in/square/go-jose.v1/json_std_test.go deleted file mode 100644 index 338d96b3..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/json_std_test.go +++ /dev/null @@ -1,124 +0,0 @@ -// +build std_json - -/*- - * Copyright 2014 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 jose - -import ( - "testing" -) - -type CaseInsensitive struct { - A int `json:"TEST"` -} - -type UnicodeTest struct { - Sig string `json:"sig"` -} - -func TestUnicodeComparison(t *testing.T) { - // Some tests from RFC 7515, Section 10.13 - raw := []byte(`{"\u0073ig":"foo"}`) - var ut UnicodeTest - err := UnmarshalJSON(raw, &ut) - if err != nil { - t.Error(err) - } - - if ut.Sig != "foo" { - t.Error("strings 'sig' and '\\u0073ig' should be equal") - } -} - -func TestCaseInsensitiveJSON(t *testing.T) { - raw := []byte(`{"test":42}`) - var ci CaseInsensitive - err := UnmarshalJSON(raw, &ci) - if err != nil { - t.Error(err) - } - - if ci.A != 42 { - t.Errorf("parsing JSON should be case-insensitive (got %v)", ci) - } -} - -func TestParseCaseInsensitiveJWE(t *testing.T) { - invalidJWE := `{"protected":"eyJlbmMiOiJYWVoiLCJBTEciOiJYWVoifQo","encrypted_key":"QUJD","iv":"QUJD","ciphertext":"QUJD","tag":"QUJD"}` - _, err := ParseEncrypted(invalidJWE) - if err != nil { - t.Error("Unable to parse message with case-invalid headers", invalidJWE) - } -} - -func TestParseCaseInsensitiveJWS(t *testing.T) { - invalidJWS := `{"PAYLOAD":"CUJD","signatures":[{"protected":"e30","signature":"CUJD"}]}` - _, err := ParseSigned(invalidJWS) - if err != nil { - t.Error("Unable to parse message with case-invalid headers", invalidJWS) - } -} - -var JWKSetDuplicates = stripWhitespace(`{ - "keys": [{ - "kty": "RSA", - "kid": "exclude-me", - "use": "sig", - "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT - -O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqV - wGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj- - oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde - 3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuC - LqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5g - HdrNP5zw", - "e": "AQAB" - }], - "keys": [{ - "kty": "RSA", - "kid": "include-me", - "use": "sig", - "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT - -O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqV - wGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj- - oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde - 3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuC - LqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5g - HdrNP5zw", - "e": "AQAB" - }], - "custom": "exclude-me", - "custom": "include-me" - }`) - -func TestDuplicateJWKSetMembersIgnored(t *testing.T) { - type CustomSet struct { - JsonWebKeySet - CustomMember string `json:"custom"` - } - data := []byte(JWKSetDuplicates) - var set CustomSet - UnmarshalJSON(data, &set) - if len(set.Keys) != 1 { - t.Error("expected only one key in set") - } - if set.Keys[0].KeyID != "include-me" { - t.Errorf("expected key with kid: \"include-me\", got: %s", set.Keys[0].KeyID) - } - if set.CustomMember != "include-me" { - t.Errorf("expected custom member value: \"include-me\", got: %s", set.CustomMember) - } -} diff --git a/vendor/gopkg.in/square/go-jose.v1/jwe.go b/vendor/gopkg.in/square/go-jose.v1/jwe.go deleted file mode 100644 index b88cb797..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/jwe.go +++ /dev/null @@ -1,278 +0,0 @@ -/*- - * Copyright 2014 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 jose - -import ( - "fmt" - "strings" -) - -// rawJsonWebEncryption represents a raw JWE JSON object. Used for parsing/serializing. -type rawJsonWebEncryption struct { - Protected *byteBuffer `json:"protected,omitempty"` - Unprotected *rawHeader `json:"unprotected,omitempty"` - Header *rawHeader `json:"header,omitempty"` - Recipients []rawRecipientInfo `json:"recipients,omitempty"` - Aad *byteBuffer `json:"aad,omitempty"` - EncryptedKey *byteBuffer `json:"encrypted_key,omitempty"` - Iv *byteBuffer `json:"iv,omitempty"` - Ciphertext *byteBuffer `json:"ciphertext,omitempty"` - Tag *byteBuffer `json:"tag,omitempty"` -} - -// rawRecipientInfo represents a raw JWE Per-Recipient header JSON object. Used for parsing/serializing. -type rawRecipientInfo struct { - Header *rawHeader `json:"header,omitempty"` - EncryptedKey string `json:"encrypted_key,omitempty"` -} - -// JsonWebEncryption represents an encrypted JWE object after parsing. -type JsonWebEncryption struct { - Header JoseHeader - protected, unprotected *rawHeader - recipients []recipientInfo - aad, iv, ciphertext, tag []byte - original *rawJsonWebEncryption -} - -// recipientInfo represents a raw JWE Per-Recipient header JSON object after parsing. -type recipientInfo struct { - header *rawHeader - encryptedKey []byte -} - -// GetAuthData retrieves the (optional) authenticated data attached to the object. -func (obj JsonWebEncryption) GetAuthData() []byte { - if obj.aad != nil { - out := make([]byte, len(obj.aad)) - copy(out, obj.aad) - return out - } - - return nil -} - -// Get the merged header values -func (obj JsonWebEncryption) mergedHeaders(recipient *recipientInfo) rawHeader { - out := rawHeader{} - out.merge(obj.protected) - out.merge(obj.unprotected) - - if recipient != nil { - out.merge(recipient.header) - } - - return out -} - -// Get the additional authenticated data from a JWE object. -func (obj JsonWebEncryption) computeAuthData() []byte { - var protected string - - if obj.original != nil { - protected = obj.original.Protected.base64() - } else { - protected = base64URLEncode(mustSerializeJSON((obj.protected))) - } - - output := []byte(protected) - if obj.aad != nil { - output = append(output, '.') - output = append(output, []byte(base64URLEncode(obj.aad))...) - } - - return output -} - -// ParseEncrypted parses an encrypted message in compact or full serialization format. -func ParseEncrypted(input string) (*JsonWebEncryption, error) { - input = stripWhitespace(input) - if strings.HasPrefix(input, "{") { - return parseEncryptedFull(input) - } - - return parseEncryptedCompact(input) -} - -// parseEncryptedFull parses a message in compact format. -func parseEncryptedFull(input string) (*JsonWebEncryption, error) { - var parsed rawJsonWebEncryption - err := UnmarshalJSON([]byte(input), &parsed) - if err != nil { - return nil, err - } - - return parsed.sanitized() -} - -// sanitized produces a cleaned-up JWE object from the raw JSON. -func (parsed *rawJsonWebEncryption) sanitized() (*JsonWebEncryption, error) { - obj := &JsonWebEncryption{ - original: parsed, - unprotected: parsed.Unprotected, - } - - // Check that there is not a nonce in the unprotected headers - if (parsed.Unprotected != nil && parsed.Unprotected.Nonce != "") || - (parsed.Header != nil && parsed.Header.Nonce != "") { - return nil, ErrUnprotectedNonce - } - - if parsed.Protected != nil && len(parsed.Protected.bytes()) > 0 { - err := UnmarshalJSON(parsed.Protected.bytes(), &obj.protected) - if err != nil { - return nil, fmt.Errorf("square/go-jose: invalid protected header: %s, %s", err, parsed.Protected.base64()) - } - } - - // Note: this must be called _after_ we parse the protected header, - // otherwise fields from the protected header will not get picked up. - obj.Header = obj.mergedHeaders(nil).sanitized() - - if len(parsed.Recipients) == 0 { - obj.recipients = []recipientInfo{ - recipientInfo{ - header: parsed.Header, - encryptedKey: parsed.EncryptedKey.bytes(), - }, - } - } else { - obj.recipients = make([]recipientInfo, len(parsed.Recipients)) - for r := range parsed.Recipients { - encryptedKey, err := base64URLDecode(parsed.Recipients[r].EncryptedKey) - if err != nil { - return nil, err - } - - // Check that there is not a nonce in the unprotected header - if parsed.Recipients[r].Header != nil && parsed.Recipients[r].Header.Nonce != "" { - return nil, ErrUnprotectedNonce - } - - obj.recipients[r].header = parsed.Recipients[r].Header - obj.recipients[r].encryptedKey = encryptedKey - } - } - - for _, recipient := range obj.recipients { - headers := obj.mergedHeaders(&recipient) - if headers.Alg == "" || headers.Enc == "" { - return nil, fmt.Errorf("square/go-jose: message is missing alg/enc headers") - } - } - - obj.iv = parsed.Iv.bytes() - obj.ciphertext = parsed.Ciphertext.bytes() - obj.tag = parsed.Tag.bytes() - obj.aad = parsed.Aad.bytes() - - return obj, nil -} - -// parseEncryptedCompact parses a message in compact format. -func parseEncryptedCompact(input string) (*JsonWebEncryption, error) { - parts := strings.Split(input, ".") - if len(parts) != 5 { - return nil, fmt.Errorf("square/go-jose: compact JWE format must have five parts") - } - - rawProtected, err := base64URLDecode(parts[0]) - if err != nil { - return nil, err - } - - encryptedKey, err := base64URLDecode(parts[1]) - if err != nil { - return nil, err - } - - iv, err := base64URLDecode(parts[2]) - if err != nil { - return nil, err - } - - ciphertext, err := base64URLDecode(parts[3]) - if err != nil { - return nil, err - } - - tag, err := base64URLDecode(parts[4]) - if err != nil { - return nil, err - } - - raw := &rawJsonWebEncryption{ - Protected: newBuffer(rawProtected), - EncryptedKey: newBuffer(encryptedKey), - Iv: newBuffer(iv), - Ciphertext: newBuffer(ciphertext), - Tag: newBuffer(tag), - } - - return raw.sanitized() -} - -// CompactSerialize serializes an object using the compact serialization format. -func (obj JsonWebEncryption) CompactSerialize() (string, error) { - if len(obj.recipients) != 1 || obj.unprotected != nil || - obj.protected == nil || obj.recipients[0].header != nil { - return "", ErrNotSupported - } - - serializedProtected := mustSerializeJSON(obj.protected) - - return fmt.Sprintf( - "%s.%s.%s.%s.%s", - base64URLEncode(serializedProtected), - base64URLEncode(obj.recipients[0].encryptedKey), - base64URLEncode(obj.iv), - base64URLEncode(obj.ciphertext), - base64URLEncode(obj.tag)), nil -} - -// FullSerialize serializes an object using the full JSON serialization format. -func (obj JsonWebEncryption) FullSerialize() string { - raw := rawJsonWebEncryption{ - Unprotected: obj.unprotected, - Iv: newBuffer(obj.iv), - Ciphertext: newBuffer(obj.ciphertext), - EncryptedKey: newBuffer(obj.recipients[0].encryptedKey), - Tag: newBuffer(obj.tag), - Aad: newBuffer(obj.aad), - Recipients: []rawRecipientInfo{}, - } - - if len(obj.recipients) > 1 { - for _, recipient := range obj.recipients { - info := rawRecipientInfo{ - Header: recipient.header, - EncryptedKey: base64URLEncode(recipient.encryptedKey), - } - raw.Recipients = append(raw.Recipients, info) - } - } else { - // Use flattened serialization - raw.Header = obj.recipients[0].header - raw.EncryptedKey = newBuffer(obj.recipients[0].encryptedKey) - } - - if obj.protected != nil { - raw.Protected = newBuffer(mustSerializeJSON(obj.protected)) - } - - return string(mustSerializeJSON(raw)) -} diff --git a/vendor/gopkg.in/square/go-jose.v1/jwe_test.go b/vendor/gopkg.in/square/go-jose.v1/jwe_test.go deleted file mode 100644 index ab03fd00..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/jwe_test.go +++ /dev/null @@ -1,537 +0,0 @@ -/*- - * Copyright 2014 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 jose - -import ( - "bytes" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rsa" - "math/big" - "testing" -) - -func TestCompactParseJWE(t *testing.T) { - // Should parse - msg := "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.dGVzdA.dGVzdA.dGVzdA" - _, err := ParseEncrypted(msg) - if err != nil { - t.Error("Unable to parse valid message:", err) - } - - // Messages that should fail to parse - failures := []string{ - // Too many parts - "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.dGVzdA.dGVzdA.dGVzdA.dGVzdA", - // Not enough parts - "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.dGVzdA.dGVzdA", - // Invalid encrypted key - "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.//////.dGVzdA.dGVzdA.dGVzdA", - // Invalid IV - "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.//////.dGVzdA.dGVzdA", - // Invalid ciphertext - "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.dGVzdA.//////.dGVzdA", - // Invalid tag - "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.dGVzdA.dGVzdA.//////", - // Invalid header - "W10.dGVzdA.dGVzdA.dGVzdA.dGVzdA", - // Invalid header - "######.dGVzdA.dGVzdA.dGVzdA.dGVzdA", - // Missing alc/enc params - "e30.dGVzdA.dGVzdA.dGVzdA.dGVzdA", - } - - for _, msg := range failures { - _, err = ParseEncrypted(msg) - if err == nil { - t.Error("Able to parse invalid message", msg) - } - } -} - -func TestFullParseJWE(t *testing.T) { - // Messages that should succeed to parse - successes := []string{ - // Flattened serialization, single recipient - "{\"protected\":\"eyJhbGciOiJYWVoiLCJlbmMiOiJYWVoifQo\",\"encrypted_key\":\"QUJD\",\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"QUJD\"}", - // Unflattened serialization, single recipient - "{\"protected\":\"\",\"unprotected\":{\"enc\":\"XYZ\"},\"recipients\":[{\"header\":{\"alg\":\"XYZ\"},\"encrypted_key\":\"QUJD\"}],\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"QUJD\"}", - } - - for i := range successes { - _, err := ParseEncrypted(successes[i]) - if err != nil { - t.Error("Unble to parse valid message", err, successes[i]) - } - } - - // Messages that should fail to parse - failures := []string{ - // Empty - "{}", - // Invalid JSON - "{XX", - // Invalid protected header - "{\"protected\":\"###\"}", - // Invalid protected header - "{\"protected\":\"e1gK\"}", - // Invalid encrypted key - "{\"protected\":\"e30\",\"encrypted_key\":\"###\"}", - // Invalid IV - "{\"protected\":\"e30\",\"encrypted_key\":\"QUJD\",\"iv\":\"###\"}", - // Invalid ciphertext - "{\"protected\":\"e30\",\"encrypted_key\":\"QUJD\",\"iv\":\"QUJD\",\"ciphertext\":\"###\"}", - // Invalid tag - "{\"protected\":\"e30\",\"encrypted_key\":\"QUJD\",\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"###\"}", - // Invalid AAD - "{\"protected\":\"e30\",\"encrypted_key\":\"QUJD\",\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"QUJD\",\"aad\":\"###\"}", - // Missing alg/enc headers - "{\"protected\":\"e30\",\"encrypted_key\":\"QUJD\",\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"QUJD\"}", - // Missing enc header - "{\"protected\":\"eyJhbGciOiJYWVoifQ\",\"encrypted_key\":\"QUJD\",\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"QUJD\"}", - // Missing alg header - "{\"protected\":\"eyJlbmMiOiJYWVoifQ\",\"encrypted_key\":\"QUJD\",\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"QUJD\"}", - // Unflattened serialization, single recipient, invalid encrypted_key - "{\"protected\":\"\",\"recipients\":[{\"header\":{\"alg\":\"XYZ\", \"enc\":\"XYZ\"},\"encrypted_key\":\"###\"}],\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"QUJD\"}", - // Unflattened serialization, single recipient, missing alg - "{\"protected\":\"eyJhbGciOiJYWVoifQ\",\"recipients\":[{\"encrypted_key\":\"QUJD\"}],\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"QUJD\"}", - } - - for i := range failures { - _, err := ParseEncrypted(failures[i]) - if err == nil { - t.Error("Able to parse invalid message", err, failures[i]) - } - } -} - -func TestMissingInvalidHeaders(t *testing.T) { - obj := &JsonWebEncryption{ - protected: &rawHeader{Enc: A128GCM}, - unprotected: &rawHeader{}, - recipients: []recipientInfo{ - recipientInfo{}, - }, - } - - _, err := obj.Decrypt(nil) - if err != ErrUnsupportedKeyType { - t.Error("should detect invalid key") - } - - obj.unprotected.Crit = []string{"1", "2"} - - _, err = obj.Decrypt(nil) - if err == nil { - t.Error("should reject message with crit header") - } - - obj.unprotected.Crit = nil - obj.protected = &rawHeader{Alg: string(RSA1_5)} - - _, err = obj.Decrypt(rsaTestKey) - if err == nil || err == ErrCryptoFailure { - t.Error("should detect missing enc header") - } -} - -func TestRejectUnprotectedJWENonce(t *testing.T) { - // No need to test compact, since that's always protected - - // Flattened JSON - input := `{ - "header": { - "alg": "XYZ", "enc": "XYZ", - "nonce": "should-cause-an-error" - }, - "encrypted_key": "does-not-matter", - "aad": "does-not-matter", - "iv": "does-not-matter", - "ciphertext": "does-not-matter", - "tag": "does-not-matter" - }` - _, err := ParseEncrypted(input) - if err == nil { - t.Error("JWE with an unprotected nonce parsed as valid.") - } else if err.Error() != "square/go-jose: Nonce parameter included in unprotected header" { - t.Errorf("Improper error for unprotected nonce: %v", err) - } - - input = `{ - "unprotected": { - "alg": "XYZ", "enc": "XYZ", - "nonce": "should-cause-an-error" - }, - "encrypted_key": "does-not-matter", - "aad": "does-not-matter", - "iv": "does-not-matter", - "ciphertext": "does-not-matter", - "tag": "does-not-matter" - }` - _, err = ParseEncrypted(input) - if err == nil { - t.Error("JWE with an unprotected nonce parsed as valid.") - } else if err.Error() != "square/go-jose: Nonce parameter included in unprotected header" { - t.Errorf("Improper error for unprotected nonce: %v", err) - } - - // Full JSON - input = `{ - "header": { "alg": "XYZ", "enc": "XYZ" }, - "aad": "does-not-matter", - "iv": "does-not-matter", - "ciphertext": "does-not-matter", - "tag": "does-not-matter", - "recipients": [{ - "header": { "nonce": "should-cause-an-error" }, - "encrypted_key": "does-not-matter" - }] - }` - _, err = ParseEncrypted(input) - if err == nil { - t.Error("JWS with an unprotected nonce parsed as valid.") - } else if err.Error() != "square/go-jose: Nonce parameter included in unprotected header" { - t.Errorf("Improper error for unprotected nonce: %v", err) - } -} - -func TestCompactSerialize(t *testing.T) { - // Compact serialization must fail if we have unprotected headers - obj := &JsonWebEncryption{ - unprotected: &rawHeader{Alg: "XYZ"}, - } - - _, err := obj.CompactSerialize() - if err == nil { - t.Error("Object with unprotected headers can't be compact serialized") - } -} - -func TestVectorsJWE(t *testing.T) { - plaintext := []byte("The true sign of intelligence is not knowledge but imagination.") - - publicKey := &rsa.PublicKey{ - N: fromBase64Int(` - oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUW - cJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3S - psk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2a - sbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMS - tPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2dj - YgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw`), - E: 65537, - } - - expectedCompact := stripWhitespace(` - eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.ROQCfge4JPm_ - yACxv1C1NSXmwNbL6kvmCuyxBRGpW57DvlwByjyjsb6g8m7wtLMqKEyhFCn - tV7sjippEePIlKln6BvVnz5ZLXHNYQgmubuNq8MC0KTwcaGJ_C0z_T8j4PZ - a1nfpbhSe-ePYaALrf_nIsSRKu7cWsrwOSlaRPecRnYeDd_ytAxEQWYEKFi - Pszc70fP9geZOB_09y9jq0vaOF0jGmpIAmgk71lCcUpSdrhNokTKo5y8MH8 - 3NcbIvmuZ51cjXQj1f0_AwM9RW3oCh2Hu0z0C5l4BujZVsDuGgMsGZsjUhS - RZsAQSXHCAmlJ2NlnN60U7y4SPJhKv5tKYw.48V1_ALb6US04U3b.5eym8T - W_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiS - diwkIr3ajwQzaBtQD_A.XFBoMYUZodetZdvTiFvSkQ`) - - expectedFull := stripWhitespace(` - { "protected":"eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ", - "encrypted_key": - "ROQCfge4JPm_yACxv1C1NSXmwNbL6kvmCuyxBRGpW57DvlwByjyjsb - 6g8m7wtLMqKEyhFCntV7sjippEePIlKln6BvVnz5ZLXHNYQgmubuNq - 8MC0KTwcaGJ_C0z_T8j4PZa1nfpbhSe-ePYaALrf_nIsSRKu7cWsrw - OSlaRPecRnYeDd_ytAxEQWYEKFiPszc70fP9geZOB_09y9jq0vaOF0 - jGmpIAmgk71lCcUpSdrhNokTKo5y8MH83NcbIvmuZ51cjXQj1f0_Aw - M9RW3oCh2Hu0z0C5l4BujZVsDuGgMsGZsjUhSRZsAQSXHCAmlJ2Nln - N60U7y4SPJhKv5tKYw", - "iv": "48V1_ALb6US04U3b", - "ciphertext": - "5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFS - hS8iB7j6jiSdiwkIr3ajwQzaBtQD_A", - "tag":"XFBoMYUZodetZdvTiFvSkQ" }`) - - // Mock random reader - randReader = bytes.NewReader([]byte{ - // Encryption key - 177, 161, 244, 128, 84, 143, 225, 115, 63, 180, 3, 255, 107, 154, - 212, 246, 138, 7, 110, 91, 112, 46, 34, 105, 47, 130, 203, 46, 122, - 234, 64, 252, - // Randomness for RSA-OAEP - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // Initialization vector - 227, 197, 117, 252, 2, 219, 233, 68, 180, 225, 77, 219}) - defer resetRandReader() - - // Encrypt with a dummy key - encrypter, err := NewEncrypter(RSA_OAEP, A256GCM, publicKey) - if err != nil { - panic(err) - } - - object, err := encrypter.Encrypt(plaintext) - if err != nil { - panic(err) - } - - serialized, err := object.CompactSerialize() - if serialized != expectedCompact { - t.Error("Compact serialization is not what we expected", serialized, expectedCompact) - } - - serialized = object.FullSerialize() - if serialized != expectedFull { - t.Error("Full serialization is not what we expected") - } -} - -func TestVectorsJWECorrupt(t *testing.T) { - priv := &rsa.PrivateKey{ - PublicKey: rsa.PublicKey{ - N: fromHexInt(` - a8b3b284af8eb50b387034a860f146c4919f318763cd6c5598c8 - ae4811a1e0abc4c7e0b082d693a5e7fced675cf4668512772c0c - bc64a742c6c630f533c8cc72f62ae833c40bf25842e984bb78bd - bf97c0107d55bdb662f5c4e0fab9845cb5148ef7392dd3aaff93 - ae1e6b667bb3d4247616d4f5ba10d4cfd226de88d39f16fb`), - E: 65537, - }, - D: fromHexInt(` - 53339cfdb79fc8466a655c7316aca85c55fd8f6dd898fdaf1195 - 17ef4f52e8fd8e258df93fee180fa0e4ab29693cd83b152a553d - 4ac4d1812b8b9fa5af0e7f55fe7304df41570926f3311f15c4d6 - 5a732c483116ee3d3d2d0af3549ad9bf7cbfb78ad884f84d5beb - 04724dc7369b31def37d0cf539e9cfcdd3de653729ead5d1`), - Primes: []*big.Int{ - fromHexInt(` - d32737e7267ffe1341b2d5c0d150a81b586fb3132bed2f8d5262 - 864a9cb9f30af38be448598d413a172efb802c21acf1c11c520c - 2f26a471dcad212eac7ca39d`), - fromHexInt(` - cc8853d1d54da630fac004f471f281c7b8982d8224a490edbeb3 - 3d3e3d5cc93c4765703d1dd791642f1f116a0dd852be2419b2af - 72bfe9a030e860b0288b5d77`), - }, - } - - corruptCiphertext := stripWhitespace(` - eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.NFl09dehy - IR2Oh5iSsvEa82Ps7DLjRHeo0RnuTuSR45OsaIP6U8yu7vLlWaZKSZMy - B2qRBSujf-5XIRoNhtyIyjk81eJRXGa_Bxaor1XBCMyyhGchW2H2P71f - PhDO6ufSC7kV4bNqgHR-4ziS7KXwzN83_5kogXqxUpymUoJDNc.tk-GT - W_VVhiTIKFF.D_BE6ImZUl9F.52a-zFnRb3YQwIC7UrhVyQ`) - - corruptAuthtag := stripWhitespace(` - eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.NFl09dehy - IR2Oh5iSsvEa82Ps7DLjRHeo0RnuTuSR45OsaIP6U8yu7vLlWaZKSZMy - B2qRBSujf-5XIRoNhtyIyjk81eJRXGa_Bxaor1XBCMyyhGchW2H2P71f - PhDO6ufSC7kV4bNqgHR-4ziS7KNwzN83_5kogXqxUpymUoJDNc.tk-GT - W_VVhiTIKFF.D_BE6ImZUl9F.52a-zFnRb3YQwiC7UrhVyQ`) - - msg, _ := ParseEncrypted(corruptCiphertext) - _, err := msg.Decrypt(priv) - if err != ErrCryptoFailure { - t.Error("should detect corrupt ciphertext") - } - - msg, _ = ParseEncrypted(corruptAuthtag) - _, err = msg.Decrypt(priv) - if err != ErrCryptoFailure { - t.Error("should detect corrupt auth tag") - } -} - -// Test vectors generated with nimbus-jose-jwt -func TestSampleNimbusJWEMessagesRSA(t *testing.T) { - rsaPrivateKey, err := LoadPrivateKey(fromBase64Bytes(` - MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCNRCEmf5PlbXKuT4uwnb - wGKvFrtpi+bDYxOZxxqxdVkZM/bYATAnD1fg9pNvLMKeF+MWJ9kPIMmDgOh9RdnRdLvQGb - BzhLmxwhhcua2QYiHEZizXmiaXvNP12bzEBhebdX7ObW8izMVW0p0lqHPNzkK3K75B0Sxo - FMVKkZ7KtBHgepBT5yPhPPcNe5lXQeTne5bo3I60DRcN9jTBgMJOXdq0I9o4y6ZmoXdNTm - 0EyLzn9/EYiHqBxtKFh791EHR7wYgyi/t+nOKr4sO74NbEByP0mHDil+mPvZSzFW4l7fPx - OclRZvpRIKIub2TroZA9s2WsshGf79eqqXYbBB9NNRAgMBAAECggEAIExbZ/nzTplfhwsY - 3SCzRJW87OuqsJ79JPQPGM4NX7sQ94eJqM7+FKLl0yCFErjgnYGdCyiArvB+oJPdsimgke - h83X0hGeg03lVA3/6OsG3WifCAxulnLN44AM8KST8S9D9t5+cm5vEBLHazzAfWWTS13s+g - 9hH8rf8NSqgZ36EutjKlvLdHx1mWcKX7SREFVHT8FWPAbdhTLEHUjoWHrfSektnczaSHnt - q8fFJy6Ld13QkF1ZJRUhtA24XrD+qLTc+M36IuedjeZaLHFB+KyhYR3YvXEtrbCug7dCRd - uG6uTlDCSaSy7xHeTPolWtWo9F202jal54otxiAJFGUHgQKBgQDRAT0s6YQZUfwE0wluXV - k0JdhDdCo8sC1aMmKlRKWUkBAqrDl7BI3MF56VOr4ybr90buuscshFf9TtrtBOjHSGcfDI - tSKfhhkW5ewQKB0YqyHzoD6UKT0/XAshFY3esc3uCxuJ/6vOiXV0og9o7eFvr51O0TfDFh - mcTvW4wirKlQKBgQCtB7UAu8I9Nn8czkd6oXLDRyTWYviuiqFmxR+PM9klgZtsumkeSxO1 - lkfFoj9+G8nFaqYEBA9sPeNtJVTSROCvj/iQtoqpV2NiI/wWeVszpBwsswx2mlks4LJa8a - Yz9xrsfNoroKYVppefc/MCoSx4M+99RSm3FSpLGZQHAUGyzQKBgQDMQmq4JuuMF1y2lk0E - SESyuz21BqV0tDVOjilsHT+5hmXWXoS6nkO6L2czrrpM7YE82F6JJZBmo7zEIXHBInGLJ3 - XLoYLZ5qNEhqYDUEDHaBCBWZ1vDTKnZlwWFEuXVavNNZvPbUhKTHq25t8qjDki/r09Vykp - BsM2yNBKpbBOVQKBgCJyUVd3CaFUExQyAMrqD0XPCQdhJq7gzGcAQVsp8EXmOoH3zmuIeM - ECzQEMXuWFNLMHm0tbX5Kl83vMHcnKioyI9ewhWxOBYTitf0ceG8j5F97SOl32NmCXzwoJ - 55Oa0xJXfLuIvOe8hZzp4WwZmBfKBxiCR166aPQQgIawelrVAoGAEJsHomfCI4epxH4oMw - qYJMCGy95zloB+2+c86BZCOJAGwnfzbtc2eutWZw61/9sSO8sQCfzA8oX+5HwAgnFVzwW4 - lNMZohppYcpwN9EyjkPaCXuALC7p5rF2o63wY7JLvnjS2aYZliknh2yW6X6fSB0PK0Cpvd - lAIyRw6Kud0zI=`)) - if err != nil { - panic(err) - } - - rsaSampleMessages := []string{ - "eyJlbmMiOiJBMTI4R0NNIiwiYWxnIjoiUlNBMV81In0.EW0KOhHeoAxTBnLjYhh2T6HjwI-srNs6RpcSdZvE-GJ5iww3EYWBCmeGGj1UVz6OcBfwW3wllZ6GPOHU-hxVQH5KYpVOjkmrFIYU6-8BHhxBP_PjSJEBCZzjOgsCm9Th4-zmlO7UWTdK_UtwE7nk4X-kkmEy-aZBCShA8nFe2MVvqD5F7nvEWNFBOHh8ae_juo-kvycoIzvxLV9g1B0Zn8K9FAlu8YF1KiL5NFekn76f3jvAwlExuRbFPUx4gJN6CeBDK_D57ABsY2aBVDSiQceuYZxvCIAajqSS6dMT382FNJzAiQhToOpo_1w5FnnBjzJLLEKDk_I-Eo2YCWxxsQ.5mCMuxJqLRuPXGAr.Ghe4INeBhP3MDWGvyNko7qanKdZIzKjfeiU.ja3UlVWJXKNFJ-rZsJWycw", - "eyJlbmMiOiJBMTkyR0NNIiwiYWxnIjoiUlNBMV81In0.JsJeYoP0St1bRYNUaAmA34DAA27usE7RNuC2grGikBRmh1xrwUOpnEIXXpwr7fjVmNi52zzWkNHC8JkkRTrLcCh2VXvnOnarpH8DCr9qM6440bSrahzbxIvDds8z8q0wT1W4kjVnq1mGwGxg8RQNBWTV6Sp2FLQkZyjzt_aXsgYzr3zEmLZxB-d41lBS81Mguk_hdFJIg_WO4ao54lozvxkCn_uMiIZ8eLb8qHy0h-N21tiHGCaiC2vV8KXomwoqbJ0SXrEH4r9_R2J844H80TBZdbvNBd8whvoQNHvOX659LNs9EQ9xxvHU2kqGZekXBu7sDXXTjctMkMITobGSzw.1v5govaDvanP3LGp.llwYNBDrD7MwVLaFHesljlratfmndWs4XPQ.ZGT1zk9_yIKi2GzW6CuAyA", - "eyJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBMV81In0.fBv3fA3TMS3ML8vlsCuvwdsKvB0ym8R30jJrlOiqkWKk7WVUkjDInFzr1zw3Owla6c5BqOJNoACXt4IWbkLbkoWV3tweXlWwpafuaWPkjLOUH_K31rS2fCX5x-MTj8_hScquVQXpbz3vk2EfulRmGXZc_8JU2NqQCAsYy3a28houqP3rDe5jEAvZS2SOFvJkKW--f5S-z39t1D7fNz1N8Btd9SmXWQzjbul5YNxI9ctqxhJpkKYpxOLlvrzdA6YdJjOlDx3n6S-HnSZGM6kQd_xKtAf8l1EGwhQmhbXhMhjVxMvGwE5BX7PAb8Ccde5bzOCJx-PVbVetuLb169ZYqQ._jiZbOPRR82FEWMZ.88j68LI-K2KT6FMBEdlz6amG5nvaJU8a-90.EnEbUTJsWNqJYKzfO0x4Yw", - "eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiUlNBMV81In0.bN6FN0qmGxhkESiVukrCaDVG3woL0xE-0bHN_Mu0WZXTQWbzzT-7jOvaN1xhGK8nzi8qpCSRgE5onONNB9i8OnJm3MMIxF7bUUEAXO9SUAFn2v--wNc4drPc5OjIu0RiJrDVDkkGjNrBDIuBaEQcke7A0v91PH58dXE7o4TLPzC8UJmRtXWhUSwjXVF3-UmYRMht2rjHJlvRbtm6Tu2LMBIopRL0zj6tlPP4Dm7I7sz9OEB3VahYAhpXnFR7D_f8RjLSXQmBvB1FiI5l_vMz2NFt2hYUmQF3EJMLIEdHvvPp3iHDGiXC1obJrDID_CCf3qs9UY7DMYL622KLvP2NIg.qb72oxECzxd_aNuHVR0aNg.Gwet9Ms8hB8rKEb0h4RGdFNRq97Qs2LQaJM0HWrCqoI.03ljVThOFvgXzMmQJ79VjQ", - "eyJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwiYWxnIjoiUlNBMV81In0.ZbEOP6rqdiIP4g7Nl1PL5gwhgDwv9RinyiUQxZXPOmD7kwEZrZ093dJnhqI9kEd3QGFlHDpB7HgNz53d27z2zmEj1-27v6miizq6tH4sN2MoeZLwSyk16O1_n3bVdDmROawsTYYFJfHsuLwyVJxPd37duIYnbUCFO9J8lLIv-2VI50KJ1t47YfE4P-Wt9jVzxP2CVUQaJwTlcwfiDLJTagYmfyrDjf525WlQFlgfJGqsJKp8BX9gmKvAo-1iCBAM8VpEjS0u0_hW9VSye36yh8BthVV-VJkhJ-0tMpto3bbBmj7M25Xf4gbTrrVU7Nz6wb18YZuhHZWmj2Y2nHV6Jg.AjnS44blTrIIfFlqVw0_Mg.muCRgaEXNKKpW8rMfW7jf7Zpn3VwSYDz-JTRg16jZxY.qjc9OGlMaaWKDWQSIwVpR4K556Pp6SF9", - "eyJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYWxnIjoiUlNBMV81In0.c7_F1lMlRHQQE3WbKmtHBYTosdZrG9hPfs-F9gNQYet61zKG8NXVkSy0Zf2UFHt0vhcO8hP2qrqOFsy7vmRj20xnGHQ2EE29HH6hwX5bx1Jj3uE5WT9Gvh0OewpvF9VubbwWTIObBpdEG7XdJsMAQlIxtXUmQYAtLTWcy2ZJipyJtVlWQLaPuE8BKfZH-XAsp2CpQNiRPI8Ftza3EAspiyRfVQbjKt7nF8nuZ2sESjt7Y50q4CSiiCuGT28T3diMN0_rWrH-I-xx7OQvJlrQaNGglGtu3jKUcrJDcvxW2e1OxriaTeuQ848ayuRvGUNeSv6WoVYmkiK1x_gNwUAAbw.7XtSqHJA7kjt6JrfxJMwiA.Yvi4qukAbdT-k-Fd2s4G8xzL4VFxaFC0ZIzgFDAI6n0.JSWPJ-HjOE3SK9Lm0yHclmjS7Z1ahtQga9FHGCWVRcc", - "eyJlbmMiOiJBMTI4R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ.SYVxJbCnJ_tcR13LJpaqHQj-nGNkMxre4A1FmnUdxnvzeJwuvyrLiUdRsZR1IkP4fqLtDON2mumx39QeJQf0WIObPBYlIxycRLkwxDHRVlyTmPvdZHAxN26jPrk09wa5SgK1UF1W1VSQIPm-Tek8jNAmarF1Yxzxl-t54wZFlQiHP4TuaczugO5f-J4nlWenfla2mU1snDgdUMlEZGOAQ_gTEtwSgd1MqXmK_7LZBkoDqqoCujMZhziafJPXPDaUUqBLW3hHkkDA7GpVec3XcTtNUWQJqOpMyQhqo1KQMc8jg3fuirILp-hjvvNVtBnCRBvbrKUCPzu2_yH3HM_agA.2VsdijtonAxShNIW.QzzB3P9CxYP3foNKN0Ma1Z9tMwijAlkWo08.ZdQkIPDY_M-hxqi5fD4NGw", - "eyJlbmMiOiJBMTkyR0NNIiwiYWxnIjoiUlNBLU9BRVAifQ.Z2oTJXXib1u-S38Vn3DRKE3JnhnwgUa92UhsefzY2Wpdn0dmxMfYt9iRoJGFfSAcA97MOfjyvXVRCKWXGrG5AZCMAXEqU8SNQwKPRjlcqojcVzQyMucXI0ikLC4mUgeRlfKTwsBicq6JZZylzRoLGGSNJQbni3_BLsf7H3Qor0BYg0FPCLG9Z2OVvrFzvjTLmZtV6gFlVrMHBxJub_aUet9gAkxiu1Wx_Kx46TlLX2tkumXIpTGlzX6pef6jLeZ5EIg_K-Uz4tkWgWQIEkLD7qmTyk5pAGmzukHa_08jIh5-U-Sd8XGZdx4J1pVPJ5CPg0qDJGZ_cfgkgpWbP_wB6A.4qgKfokK1EwYxz20._Md82bv_KH2Vru0Ue2Eb6oAqHP2xBBP5jF8.WFRojvQpD5VmZlOr_dN0rQ", - "eyJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ.JzCUgJcBJmBgByp4PBAABUfhezPvndxBIVzaoZ96DAS0HPni0OjMbsOGsz6JwNsiTr1gSn_S6R1WpZM8GJc9R2z0EKKVP67TR62ZSG0MEWyLpHmG_4ug0fAp1HWWMa9bT4ApSaOLgwlpVAb_-BPZZgIu6c8cREuMon6UBHDqW1euTBbzk8zix3-FTZ6p5b_3soDL1wXfRiRBEsxxUGMnpryx1OFb8Od0JdyGF0GgfLt6OoaujDJpo-XtLRawu1Xlg6GqRs0NQwSHZ5jXgQ6-zgCufXonAmYTiIyBXY2no9XmECTexjwrS_05nA7H-UyIZEBOCp3Yhz2zxrt5j_0pvQ.SJR-ghhaUKP4zXtZ.muiuzLfZA0y0BDNsroGTw2r2-l73SLf9lK8.XFMH1oHr1G6ByP3dWSUUPA", - "eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiUlNBLU9BRVAifQ.U946MVfIm4Dpk_86HrnIA-QXyiUu0LZ67PL93CMLmEtJemMNDqmRd9fXyenCIhAC7jPIV1aaqW7gS194xyrrnUpBoJBdbegiPqOfquy493Iq_GQ8OXnFxFibPNQ6rU0l8BwIfh28ei_VIF2jqN6bhxFURCVW7fG6n6zkCCuEyc7IcxWafSHjH2FNttREuVj-jS-4LYDZsFzSKbpqoYF6mHt8H3btNEZDTSmy_6v0fV1foNtUKNfWopCp-iE4hNh4EzJfDuU8eXLhDb03aoOockrUiUCh-E0tQx9su4rOv-mDEOHHAQK7swm5etxoa7__9PC3Hg97_p4GM9gC9ykNgw.pnXwvoSPi0kMQP54of-HGg.RPJt1CMWs1nyotx1fOIfZ8760mYQ69HlyDp3XmdVsZ8.Yxw2iPVWaBROFE_FGbvodA", - "eyJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwiYWxnIjoiUlNBLU9BRVAifQ.eKEOIJUJpXmO_ghH_nGCJmoEspqKyiy3D5l0P8lKutlo8AuYHPQlgOsaFYnDkypyUVWd9zi-JaQuCeo7dzoBiS1L71nAZo-SUoN0anQBkVuyuRjr-deJMhPPfq1H86tTk-4rKzPr1Ivd2RGXMtWsrUpNGk81r1v8DdMntLE7UxZQqT34ONuZg1IXnD_U6di7k07unI29zuU1ySeUr6w1YPw5aUDErMlpZcEJWrgOEYWaS2nuC8sWGlPGYEjqkACMFGn-y40UoS_JatNZO6gHK3SKZnXD7vN5NAaMo_mFNbh50e1t_zO8DaUdLtXPOBLcx_ULoteNd9H8HyDGWqwAPw.0xmtzJfeVMoIT1Cp68QrXA.841l1aA4c3uvSYfw6l180gn5JZQjL53WQ5fr8ejtvoI.lojzeWql_3gDq-AoaIbl_aGQRH_54w_f", - "eyJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYWxnIjoiUlNBLU9BRVAifQ.D0QkvIXR1TL7dIHWuPNMybmmD8UPyQd1bRKjRDNbA2HmKGpamCtcJmpNB_EetNFe-LDmhe44BYI_XN2wIBbYURKgDK_WG9BH0LQw_nCVqQ-sKqjtj3yQeytXhLHYTDmiF0TO-uW-RFR7GbPAdARBfuf4zj82r_wDD9sD5WSCGx89iPfozDOYQ_OLwdL2WD99VvDyfwS3ZhxA-9IMSYv5pwqPkxj4C0JdjCqrN0YNrZn_1ORgjtsVmcWXsmusObTozUGA7n5GeVepfZdU1vrMulAwdRYqOYtlqKaOpFowe9xFN3ncBG7wb4f9pmzbS_Dgt-1_Ii_4SEB9GQ4NiuBZ0w.N4AZeCxMGUv52A0UVJsaZw.5eHOGbZdtahnp3l_PDY-YojYib4ft4SRmdsQ2kggrTs.WsmGH8ZDv4ctBFs7qsQvw2obe4dVToRcAQaZ3PYL34E", - "eyJlbmMiOiJBMTI4R0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.fDTxO_ZzZ3Jdrdw-bxvg7u-xWB2q1tp3kI5zH6JfhLUm4h6rt9qDA_wZlRym8-GzEtkUjkTtQGs6HgQx_qlyy8ylCakY5GHsNhCG4m0UNhRiNfcasAs03JSXfON9-tfTJimWD9n4k5OHHhvcrsCW1G3jYeLsK9WHCGRIhNz5ULbo8HBrCTbmZ6bOEQ9mqhdssLpdV24HDpebotf3bgPJqoaTfWU6Uy7tLmPiNuuNRLQ-iTpLyNMTVvGqqZhpcV3lAEN5l77QabI5xLJYucvYjrXQhAEZ7YXO8oRYhGkdG2XXIRcwr87rBeRH-47HAyhZgF_PBPBhhrJNS9UNMqdfBw.FvU4_s7Md6vxnXWd.fw29Q4_gHt4f026DPPV-CNebQ8plJ6IVLX8._apBZrw7WsT8HOmxgCrTwA", - "eyJlbmMiOiJBMTkyR0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.bYuorK-rHMbO4c2CRWtvyOEaM1EN-o-wLRZ0wFWRX9mCXQ-iTNarZn7ksYM1XnGmZ4u3CSowX1Hpca9Rg72_VJCmKapqCT7r3YfasN4_oeLwuSKI_gT-uVOznod97tn3Gf_EDv0y1V4H0k9BEIFGbajAcG1znTD_ODY3j2KZJxisfrsBoslc6N-HI0kKZMC2hSGuHOcOf8HN1sTE-BLqZCtoj-zxQECJK8Wh14Ih4jzzdmmiu_qmSR780K6su-4PRt3j8uY7oCiLBfwpCsCmhJgp8rKd91zoedZmamfvX38mJIfE52j4fG6HmIYw9Ov814fk9OffV6tzixjcg54Q2g.yeVJz4aSh2s-GUr9.TBzzWP5llEiDdugpP2SmPf2U4MEGG9EoPWk.g25UoWpsBaOd45J__FX7mA", - "eyJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.h9tFtmh762JuffBxlSQbJujCyI4Zs9yc3IOb1yR8g65W4ZHosIvzVGHWbShj4EY9MNrz-RbKtHfqQGGzDeo3Xb4-HcQ2ZDHyWoUg7VfA8JafJ5zIKL1npz8eUExOVMLsAaRfHg8qNfczodg3egoSmX5Q-nrx4DeidDSXYZaZjV0C72stLTPcuQ7XPV7z1tvERAkqpvcsRmJn_PiRNxIbAgoyHMJ4Gijuzt1bWZwezlxYmw0TEuwCTVC2fl9NJTZyxOntS1Lcm-WQGlPkVYeVgYTOQXLlp7tF9t-aAvYpth2oWGT6Y-hbPrjx_19WaKD0XyWCR46V32DlXEVDP3Xl2A.NUgfnzQyEaJjzt9r.k2To43B2YVWMeR-w3n4Pr2b5wYq2o87giHk.X8_QYCg0IGnn1pJqe8p_KA", - "eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.EDq6cNP6Yp1sds5HZ4CkXYp7bs9plIYVZScKvuyxUy0H1VyBC_YWg0HvndPNb-vwh1LA6KMxRazlOwJ9iPR9YzHnYmGgPM3Je_ZzBfiPlRfq6hQBpGnNaypBI1XZ2tyFBhulsVLqyJe2SmM2Ud00kasOdMYgcN8FNFzq7IOE7E0FUQkIwLdUL1nrzepiYDp-5bGkxWRcL02cYfdqdm00G4m0GkUxAmdxa3oPNxZlt2NeBI_UVWQSgJE-DJVJQkDcyA0id27TV2RCDnmujYauNT_wYlyb0bFDx3pYzzNXfAXd4wHZxt75QaLZ5APJ0EVfiXJ0qki6kT-GRVmOimUbQA.vTULZL7LvS0WD8kR8ZUtLg.mb2f0StEmmkuuvsyz8UplMvF58FtZzlu8eEwzvPUvN0.hbhveEN40V-pgG2hSVgyKg", - "eyJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.DuYk92p7u-YIN-JKn-XThmlVcnhU9x5TieQ2uhsLQVNlo0iWC9JJPP6bT6aI6u_1BIS3yE8_tSGGL7eM-zyEk6LuTqSWFRaZcZC06d0MnS9eYZcw1T2D17fL-ki-NtCaTahJD7jE2s0HevRVW49YtL-_V8whnO_EyVjvXIAQlPYqhH_o-0Nzcpng9ggdAnuF2rY1_6iRPYFJ3BLQvG1oWhyJ9s6SBttlOa0i6mmFCVLHx6sRpdGAB3lbCL3wfmHq4tpIv77gfoYUNP0SNff-zNmBXF_wp3dCntLZFTjbfMpGyHlruF_uoaLqwdjYpUGNUFVUoeSiMnSbMKm9NxiDgQ.6Mdgcqz7bMU1UeoAwFC8pg.W36QWOlBaJezakUX5FMZzbAgeAu_R14AYKZCQmuhguw.5OeyIJ03olxmJft8uBmjuOFQPWNZMYLI", - "eyJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.ECulJArWFsPL2FlpCN0W8E7IseSjJg1cZqE3wz5jk9gvwgNForAUEv5KYZqhNI-p5IxkGV0f8K6Y2X8pWzbLwiPIjZe8_dVqHYJoINxqCSgWLBhz0V36qL9Nc_xARTBk4-ZteIu75NoXVeos9gNvFnkOCj4tm-jGo8z8EFO9XfODgjhiR4xv8VqUtvrkjo9GQConaga5zpV-J4JQlXbdqbDjnuwacnJAxYpFyuemqcgqsl6BnFX3tovGkmSUPqcvF1A6tiHqr-TEmcgVqo5C3xswknRBKTQRM00iAmJ92WlVdkoOCx6E6O7cVHFawZ14BLzWzm66Crb4tv0ucYvk_Q.mxolwUaoj5S5kHCfph0w8g.nFpgYdnYg3blHCCEi2XXQGkkKQBXs2OkZaH11m3PRvk.k8BAVT4EcyrUFVIKr-KOSPbF89xyL0Vri2rFTu2iIWM", - } - - for _, msg := range rsaSampleMessages { - obj, err := ParseEncrypted(msg) - if err != nil { - t.Error("unable to parse message", msg, err) - continue - } - plaintext, err := obj.Decrypt(rsaPrivateKey) - if err != nil { - t.Error("unable to decrypt message", msg, err) - continue - } - if string(plaintext) != "Lorem ipsum dolor sit amet" { - t.Error("plaintext is not what we expected for msg", msg) - } - } -} - -// Test vectors generated with nimbus-jose-jwt -func TestSampleNimbusJWEMessagesAESKW(t *testing.T) { - aesTestKeys := [][]byte{ - fromHexBytes("DF1FA4F36FFA7FC42C81D4B3C033928D"), - fromHexBytes("DF1FA4F36FFA7FC42C81D4B3C033928D95EC9CDC2D82233C"), - fromHexBytes("DF1FA4F36FFA7FC42C81D4B3C033928D95EC9CDC2D82233C333C35BA29044E90"), - } - - aesSampleMessages := [][]string{ - []string{ - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4R0NNIiwidGFnIjoib2ZMd2Q5NGloVWFRckJ0T1pQUDdjUSIsImFsZyI6IkExMjhHQ01LVyIsIml2IjoiV2Z3TnN5cjEwWUFjY2p2diJ9.9x3RxdqIS6P9xjh93Eu1bQ.6fs3_fSGt2jull_5.YDlzr6sWACkFg_GU5MEc-ZEWxNLwI_JMKe_jFA.f-pq-V7rlSSg_q2e1gDygw", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyR0NNIiwidGFnIjoic2RneXB1ckFjTEFzTmZJU0lkZUNpUSIsImFsZyI6IkExMjhHQ01LVyIsIml2IjoieVFMR0dCdDJFZ0c1THdyViJ9.arslKo4aKlh6f4s0z1_-U-8JbmhAoZHN.Xw2Q-GX98YXwuc4i.halTEWMWAYZbv-qOD52G6bte4x6sxlh1_VpGEA.Z1spn016v58cW6Q2o0Qxag", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2R0NNIiwidGFnIjoicTNzejF5VUlhbVBDYXJfZ05kSVJqQSIsImFsZyI6IkExMjhHQ01LVyIsIml2IjoiM0ZRM0FsLWJWdWhmcEIyQyJ9.dhVipWbzIdsINttuZM4hnjpHvwEHf0VsVrOp4GAg01g.dk7dUyt1Qj13Pipw.5Tt70ONATF0BZAS8dBkYmCV7AQUrfb8qmKNLmw.A6ton9MQjZg0b3C0QcW-hg", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwidGFnIjoiUHNpTGphZnJZNE16UlRmNlBPLTZfdyIsImFsZyI6IkExMjhHQ01LVyIsIml2IjoiSUFPbnd2ODR5YXFEaUxtbSJ9.swf92_LyCvjsvkynHTuMNXRl_MX2keU-fMDWIMezHG4.LOp9SVIXzs4yTnOtMyXZYQ.HUlXrzqJ1qXYl3vUA-ydezCg77WvJNtKdmZ3FPABoZw.8UYl1LOofQLAxHHvWqoTbg", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwidGFnIjoiWGRndHQ5dUVEMVlVeU1rVHl6M3lqZyIsImFsZyI6IkExMjhHQ01LVyIsIml2IjoiWF90V2RhSmh6X3J1SHJvQSJ9.JQ3dS1JSgzIFi5M9ig63FoFU1nHBTmPwXY_ovNE2m1JOSUvHtalmihIuraPDloCf.e920JVryUIWt7zJJQM-www.8DUrl4LmsxIEhRr9RLTHG9tBTOcwXqEbQHAJd_qMHzE.wHinoqGUhL4O7lx125kponpwNtlp8VGJ", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwidGFnIjoicGgyaTdoY0FWNlh3ZkQta1RHYlVXdyIsImFsZyI6IkExMjhHQ01LVyIsIml2IjoiaG41Smk4Wm1rUmRrSUxWVSJ9._bQlJXl22dhsBgYPhkxUyinBNi871teGWbviOueWj2PqG9OPxIc9SDS8a27YLSVDMircd5Q1Df28--vcXIABQA.DssmhrAg6w_f2VDaPpxTbQ.OGclEmqrxwvZqAfn7EgXlIfXgr0wiGvEbZz3zADnqJs.YZeP0uKVEiDl8VyC-s20YN-RbdyGNsbdtoGDP3eMof8", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4R0NNIiwiYWxnIjoiQTEyOEtXIn0.TEMcXEoY8WyqGjYs5GZgS-M_Niwu6wDY.i-26KtTt51Td6Iwd.wvhkagvPsLj3QxhPBbfH_th8OqxisUtme2UadQ.vlfvBPv3bw2Zk2H60JVNLQ", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyR0NNIiwiYWxnIjoiQTEyOEtXIn0.gPaR6mgQ9TUx05V6DRfgTQeZxl0ZSzBa5uQd-qw6yLs.MojplOD77FkMooS-.2yuD7dKR_C3sFbhgwiBccKKOF8DrSvNiwX7wPQ.qDKUbSvMnJv0qifjpWC14g", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiQTEyOEtXIn0.Fg-dgSkUW1KEaL5YDPoWHNL8fpX1WxWVLA9OOWsjIFhQVDKyUZI7BQ.mjRBpyJTZf7H-quf.YlNHezMadtaSKp23G-ozmYhHOeHwuJnvWGTtGg.YagnR7awBItUlMDo4uklvg", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiQTEyOEtXIn0.x1vYzUE-E2XBWva9OPuwtqfQaf9rlJCIBAyAe6N2q2kWfJrkxGxFsQ.gAwe78dyODFaoP2IOityAA.Yh5YfovkWxGBNAs1sVhvXow_2izHHsBiYEc9JYD6kVg.mio1p3ncp2wLEaEaRa7P0w", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwiYWxnIjoiQTEyOEtXIn0.szGrdnmF7D5put2aRBvSSFfp0vRgkRGYaafijJIqAF6PWd1IxsysZRV8aQkQOW1cB6d0fXsTfYM.Ru25LVOOk4xhaK-cIZ0ThA.pF9Ok5zot7elVqXFW5YYHV8MuF9gVGzpQnG1XDs_g_w.-7la0uwcNPpteev185pMHZjbVDXlrec8", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYWxnIjoiQTEyOEtXIn0.cz-hRv0xR5CnOcnoRWNK8Q9poyVYzRCVTjfmEXQN6xPOZUkJ3zKNqb8Pir_FS0o2TVvxmIbuxeISeATTR2Ttx_YGCNgMkc93.SF5rEQT94lZR-UORcMKqGw.xphygoU7zE0ZggOczXCi_ytt-Evln8CL-7WLDlWcUHg.5h99r8xCCwP2PgDbZqzCJ13oFfB2vZWetD5qZjmmVho", - }, - []string{ - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4R0NNIiwidGFnIjoiVWR5WUVKdEJ5ZTA5dzdjclY0cXI1QSIsImFsZyI6IkExOTJHQ01LVyIsIml2IjoiZlBBV0QwUmdSbHlFdktQcCJ9.P1uTfTuH-imL-NJJMpuTRA.22yqZ1NIfx3KNPgc.hORWZaTSgni1FS-JT90vJly-cU37qTn-tWSqTg.gMN0ufXF92rSXupTtBNkhA", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyR0NNIiwidGFnIjoiOU9qX3B2LTJSNW5lZl9YbWVkUWltUSIsImFsZyI6IkExOTJHQ01LVyIsIml2IjoiY3BybGEwYUYzREVQNmFJTSJ9.6NVpAm_APiC7km2v-oNR8g23K9U_kf1-.jIg-p8tNwSvwxch0.1i-GPaxS4qR6Gy4tzeVtSdRFRSKQSMpmn-VhzA.qhFWPqtA6vVPl7OM3DThsA", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2R0NNIiwidGFnIjoiOVc3THg3MVhGQVJCb3NaLVZ5dXc4ZyIsImFsZyI6IkExOTJHQ01LVyIsIml2IjoiZ1N4ZE5heFdBSVBRR0tHYiJ9.3YjPz6dVQwAtCekvtXiHZrooOUlmCsMSvyfwmGwdrOA.hA_C0IDJmGaRzsB0.W4l7OPqpFxiVOZTGfAlRktquyRTo4cEOk9KurQ.l4bGxOkO_ql_jlPo3Oz3TQ", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwidGFnIjoiOHJYbWl2WXFWZjNfbHhhd2NUbHJoUSIsImFsZyI6IkExOTJHQ01LVyIsIml2IjoiVXBWeXprVTNKcjEwYXRqYyJ9.8qft-Q_xqUbo5j_aVrVNHchooeLttR4Kb6j01O8k98M.hXO-5IKBYCL9UdwBFVm0tg.EBM4lCZX_K6tfqYmfoDxVPHcf6cT--AegXTTjfSqsIw.Of8xUvEQSh3xgFT3uENnAg", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwidGFnIjoiVnItSnVaX0tqV2hSWWMzdzFwZ3cwdyIsImFsZyI6IkExOTJHQ01LVyIsIml2IjoiRGg2R3dISVBVS3ljZGNZeCJ9.YSEDjCnGWr_n9H94AvLoRnwm6bdU9w6-Q67k-QQRVcKRd6673pgH9zEF9A9Dt6o1.gcmVN4kxqBuMq6c7GrK3UQ.vWzJb0He6OY1lhYYjYS7CLh55REAAq1O7yNN-ND4R5Q.OD0B6nwyFaDr_92ysDOtlVnJaeoIqhGw", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwidGFnIjoieEtad1BGYURpQ3NqUnBqZUprZHhmZyIsImFsZyI6IkExOTJHQ01LVyIsIml2IjoieTVHRFdteXdkb2R1SDJlYyJ9.AW0gbhWqlptOQ1y9aoNVwrTIIkBfrp33C2OWJsbrDRk6lhxg_IgFhMDTE37moReySGUtttC4CXQD_7etHmd3Hw.OvKXK-aRKlXHOpJQ9ZY_YQ.Ngv7WarDDvR2uBj_DavPAR3DYuIaygvSSdcHrc8-ZqM.MJ6ElitzFCKf_0h5fIJw8uOLC6ps7dKZPozF8juQmUY", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4R0NNIiwiYWxnIjoiQTE5MktXIn0.8qu63pppcSvp1vv37WrZ44qcCTg7dQMA.cDp-f8dJTrDEpZW4.H6OBJYs4UvFR_IZHLYQZxB6u9a0wOdAif2LNfQ.1dB-id0UIwRSlmwHx5BJCg", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyR0NNIiwiYWxnIjoiQTE5MktXIn0._FdoKQvC8qUs7K0upriEihUwztK8gOwonXpOxdIwrfs.UO38ok8gDdpLVa1T.x1GvHdVCy4fxoQRg-OQK4Ez3jDOvu9gllLPeEA.3dLeZGIprh_nHizOTVi1xw", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiQTE5MktXIn0.uzCJskgSIK6VkjJIu-dQi18biqaY0INc_A1Ehx0oESafgtR99_n4IA.W2eKK8Y14WwTowI_.J2cJC7R6Bz6maR0s1UBMPyRi5BebNUAmof4pvw.-7w6htAlc4iUsOJ6I04rFg", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiQTE5MktXIn0.gImQeQETp_6dfJypFDPLlv7c5pCzuq86U16gzrLiCXth6X9XfxJpvQ.YlC4MxjtLWrsyEvlFhvsqw.Vlpvmg9F3gkz4e1xG01Yl2RXx-jG99rF5UvCxOBXSLc.RZUrU_FoR5bG3M-j3GY0Dw", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwiYWxnIjoiQTE5MktXIn0.T2EfQ6Tu2wJyRMgZzfvBYmQNCCfdMudMrg86ibEMVAOUKJPtR3WMPEb_Syy9p2VjrLKRlv7nebo.GPc8VbarPPRtzIRATB8NsA.ugPCqLvVLwh55bWlwjsFkmWzJ31z5z-wuih2oJqmG_U.m7FY3EjvV6mKosEYJ5cY7ezFoVQoJS8X", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYWxnIjoiQTE5MktXIn0.OgLMhZ-2ZhslQyHfzOfyC-qmT6bNg9AdpP59B4jtyxWkQu3eW475WCdiAjojjeyBtVRGQ5vOomwaOIFejY_IekzH6I_taii3.U9x44MF6Wyz5TIwIzwhoxQ.vK7yvSF2beKdNxNY_7n4XdF7JluCGZoxdFJyTJVkSmI.bXRlI8KL-g7gpprQxGmXjVYjYghhWJq7mlCfWI8q2uA", - }, - []string{ - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4R0NNIiwidGFnIjoiR3BjX3pfbjduZjJVZlEtWGdsaTBaQSIsImFsZyI6IkEyNTZHQ01LVyIsIml2IjoiUk40eUdhOVlvYlFhUmZ1TCJ9.Q4ukD6_hZpmASAVcqWJ9Wg.Zfhny_1WNdlp4fH-.3sekDCjkExQCcv28ZW4yrcFnz0vma3vgoenSXA.g8_Ird2Y0itTCDP61du-Yg", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyR0NNIiwidGFnIjoiWC05UkNVWVh4U3NRelcwelVJS01VUSIsImFsZyI6IkEyNTZHQ01LVyIsIml2IjoiY3JNMnJfa3RrdWpyQ1h5OSJ9.c0q2jCxxV4y1h9u_Xvn7FqUDnbkmNEG4.S_noOTZKuUo9z1l6.ez0RdA25vXMUGH96iXmj3DEVox0J7TasJMnzgg.RbuSPTte_NzTtEEokbc5Ig", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2R0NNIiwidGFnIjoiWmwyaDFpUW11QWZWd2lJeVp5RHloZyIsImFsZyI6IkEyNTZHQ01LVyIsIml2Ijoib19xZmljb0N0NzNzRWo1QyJ9.NpJxRJ0aqcpekD6HU2u9e6_pL_11JXjWvjfeQnAKkZU.4c5qBcBBrMWi27Lf.NKwNIb4b6cRDJ1TwMKsPrjs7ADn6aNoBdQClVw.yNWmSSRBqQfIQObzj8zDqw", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwidGFnIjoiMXdwVEI3LWhjdzZUVXhCbVh2UzdhUSIsImFsZyI6IkEyNTZHQ01LVyIsIml2IjoiOUdIVnZJaDZ0a09vX2pHUSJ9.MFgIhp9mzlq9hoPqqKVKHJ3HL79EBYtV4iNhD63yqiU.UzW5iq8ou21VpZYJgKEN8A.1gOEzA4uAPvHP76GMfs9uLloAV10mKaxiZVAeL7iQA0.i1X_2i0bCAz-soXF9bI_zw", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwidGFnIjoiNThocUtsSk15Y1BFUEFRUlNfSzlNUSIsImFsZyI6IkEyNTZHQ01LVyIsIml2IjoiUDh3aTBWMTluVnZqNXpkOSJ9.FXidOWHNFJODO74Thq3J2cC-Z2B8UZkn7SikeosU0bUK6Jx_lzzmUZ-Lafadpdpj.iLfcDbpuBKFiSfiBzUQc7Q.VZK-aD7BFspqfvbwa0wE2wwWxdomzk2IKMetFe8bI44.7wC6rJRGa4x48xbYMd6NH9VzK8uNn4Cb", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwidGFnIjoicGcwOEpUcXdzMXdEaXBaRUlpVExoQSIsImFsZyI6IkEyNTZHQ01LVyIsIml2IjoiSlpodk9CdU1RUDFFZTZTNSJ9.wqVgTPm6TcYCTkpbwmn9sW4mgJROH2A3dIdSXo5oKIQUIVbQsmy7KXH8UYO2RS9slMGtb869C8o0My67GKg9dQ.ogrRiLlqjB1S5j-7a05OwA.2Y_LyqhU4S_RXMsB74bxcBacd23J2Sp5Lblw-sOkaUY.XGMiYoU-f3GaEzSvG41vpJP2DMGbeDFoWmkUGLUjc4M", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4R0NNIiwiYWxnIjoiQTI1NktXIn0.QiIZm9NYfahqYFIbiaoUhCCHjotHMkup.EsU0XLn4FjzzCILn.WuCoQkm9vzo95E7hxBtfYpt-Mooc_vmSTyzj6Q.NbeeYVy6gQPlmhoWDrZwaQ", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyR0NNIiwiYWxnIjoiQTI1NktXIn0.1ol3j_Lt0Os3UMe2Gypj0o8b77k0FSmqD7kNRNoMa9U.vZ2HMTgN2dgUd42h.JvNcy8-c8sYzOC089VtFSg2BOQx3YF8CqSTuJw.t03LRioWWKN3d7SjinU6SQ", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiQTI1NktXIn0.gbkk03l1gyrE9qGEMVtORiyyUqKsgzbqjLd8lw0RQ07WWn--TV4BgA.J8ThH4ac2UhSsMIP.g-W1piEGrdi3tNwQDJXpYm3fQjTf82mtVCrCOg.-vY05P4kiB9FgF2vwrSeXQ", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiQTI1NktXIn0.k86pQs7gmQIzuIWRFwesF32XY2xi1WbYxi7XUf_CYlOlehwGCTINHg.3NcC9VzfQgsECISKf4xy-g.v2amdo-rgeGsg-II_tvPukX9D-KAP27xxf2uQJ277Ws.E4LIE3fte3glAnPpnd8D9Q", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwiYWxnIjoiQTI1NktXIn0.b8iN0Am3fCUvj7sBd7Z0lpfzBjh1MOgojV7J5rDfrcTU3b35RGYgEV1RdcrtUTBgUwITDjmU7jM.wsSDBFghDga_ERv36I2AOg.6uJsucCb2YReFOJGBdo4zidTIKLUmZBIXfm_M0AJpKk.YwdAfXI3HHcw2wLSnfCRtw4huZQtSKhz", - "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYWxnIjoiQTI1NktXIn0.akY9pHCbkHPh5VpXIrX0At41XnJIKBR9iMMkf301vKeJNAZYJTxWzeJhFd-DhQ47tMctc3YYkwZkQ5I_9fGYb_f0oBcw4esh.JNwuuHud78h6S99NO1oBQQ.0RwckPYATBgvw67upkAQ1AezETHc-gh3rryz19i5ryc.3XClRTScgzfMgLCHxHHoRF8mm9VVGXv_Ahtx65PskKQ", - }, - } - - for i, msgs := range aesSampleMessages { - for _, msg := range msgs { - obj, err := ParseEncrypted(msg) - if err != nil { - t.Error("unable to parse message", msg, err) - continue - } - plaintext, err := obj.Decrypt(aesTestKeys[i]) - if err != nil { - t.Error("unable to decrypt message", msg, err) - continue - } - if string(plaintext) != "Lorem ipsum dolor sit amet" { - t.Error("plaintext is not what we expected for msg", msg) - } - } - } -} - -// Test vectors generated with jose4j -func TestSampleJose4jJWEMessagesECDH(t *testing.T) { - ecTestKey := &ecdsa.PrivateKey{ - PublicKey: ecdsa.PublicKey{ - Curve: elliptic.P256(), - X: fromBase64Int("weNJy2HscCSM6AEDTDg04biOvhFhyyWvOHQfeF_PxMQ"), - Y: fromBase64Int("e8lnCO-AlStT-NJVX-crhB7QRYhiix03illJOVAOyck"), - }, - D: fromBase64Int("VEmDZpDXXK8p8N0Cndsxs924q6nS1RXFASRl6BfUqdw"), - } - - ecSampleMessages := []string{ - "eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOENCQy1IUzI1NiIsImVwayI6eyJrdHkiOiJFQyIsIngiOiJTQzAtRnJHUkVvVkpKSmg1TGhORmZqZnFXMC1XSUFyd3RZMzJzQmFQVVh3IiwieSI6ImFQMWlPRENveU9laTVyS1l2VENMNlRMZFN5UEdUN0djMnFsRnBwNXdiWFEiLCJjcnYiOiJQLTI1NiJ9fQ..3mifklTnTTGuA_etSUBBCw.dj8KFM8OlrQ3rT35nHcHZ7A5p84VB2OZb054ghSjS-M.KOIgnJjz87LGqMtikXGxXw", - "eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTE5MkNCQy1IUzM4NCIsImVwayI6eyJrdHkiOiJFQyIsIngiOiJUaHRGc0lRZ1E5MkZOYWFMbUFDQURLbE93dmNGVlRORHc4ampfWlJidUxjIiwieSI6IjJmRDZ3UXc3YmpYTm1nVThXMGpFbnl5ZUZkX3Y4ZmpDa3l1R29vTFhGM0EiLCJjcnYiOiJQLTI1NiJ9fQ..90zFayMkKc-fQC_19f6P3A.P1Y_7lMnfkUQOXW_en31lKZ3zAn1nEYn6fXLjmyVPrQ.hrgwy1cePVfhMWT0h-crKTXldglHZ-4g", - "eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTI1NkNCQy1IUzUxMiIsImVwayI6eyJrdHkiOiJFQyIsIngiOiI5R1Z6c3VKNWgySl96UURVUFR3WU5zUkFzVzZfY2RzN0pELVQ2RDREQ1ZVIiwieSI6InFZVGl1dVU4aTB1WFpoaS14VGlRNlZJQm5vanFoWENPVnpmWm1pR2lRTEUiLCJjcnYiOiJQLTI1NiJ9fQ..v2reRlDkIsw3eWEsTCc1NA.0qakrFdbhtBCTSl7EREf9sxgHBP9I-Xw29OTJYnrqP8.54ozViEBYYmRkcKp7d2Ztt4hzjQ9Vb5zCeijN_RQrcI", - "eyJhbGciOiJFQ0RILUVTK0EyNTZLVyIsImVuYyI6IkExMjhDQkMtSFMyNTYiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiOElUemg3VVFaaUthTWtfME9qX1hFaHZENXpUWjE2Ti13WVdjeTJYUC1tdyIsInkiOiJPNUJiVEk0bUFpU005ZmpCejBRU3pXaU5vbnl3cWlQLUN0RGgwdnNGYXNRIiwiY3J2IjoiUC0yNTYifX0.D3DP3wqPvJv4TYYfhnfrOG6nsM-MMH_CqGfnOGjgdXHNF7xRwEJBOA.WL9Kz3gNYA7S5Rs5mKcXmA.EmQkXhO_nFqAwxJWaM0DH4s3pmCscZovB8YWJ3Ru4N8.Bf88uzwfxiyTjpejU5B0Ng", - "eyJhbGciOiJFQ0RILUVTK0EyNTZLVyIsImVuYyI6IkExOTJDQkMtSFMzODQiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiMjlJMk4zRkF0UlBlNGhzYjRLWlhTbmVyV0wyTVhtSUN1LXJJaXhNSHpJQSIsInkiOiJvMjY1bzFReEdmbDhzMHQ0U1JROS00RGNpc3otbXh4NlJ6WVF4SktyeWpJIiwiY3J2IjoiUC0yNTYifX0.DRmsmXz6fCnLc_njDIKdpM7Oc4jTqd_yd9J94TOUksAstEUkAl9Ie3Wg-Ji_LzbdX2xRLXIimcw.FwJOHPQhnqKJCfxt1_qRnQ.ssx3q1ZYILsMTln5q-K8HVn93BVPI5ViusstKMxZzRs.zzcfzWNYSdNDdQ4CiHfymj0bePaAbVaT", - "eyJhbGciOiJFQ0RILUVTK0EyNTZLVyIsImVuYyI6IkEyNTZDQkMtSFM1MTIiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiRUp6bTViQnRzVXJNYTl2Y1Q2d1hZRXI3ZjNMcjB0N1V4SDZuZzdGcFF0VSIsInkiOiJRYTNDSDllVTFXYjItdFdVSDN3Sk9fTDVMZXRsRUlMQWNkNE9XR2tFd0hZIiwiY3J2IjoiUC0yNTYifX0.5WxwluZpVWAOJdVrsnDIlEc4_wfRE1gXOaQyx_rKkElNz157Ykf-JsAD7aEvXfx--NKF4js5zYyjeCtxWBhRWPOoNNZJlqV_.Iuo82-qsP2S1SgQQklAnrw.H4wB6XoLKOKWCu6Y3LPAEuHkvyvr-xAh4IBm53uRF8g._fOLKq0bqDZ8KNjni_MJ4olHNaYz376dV9eNmp9O9PU", - "eyJhbGciOiJFQ0RILUVTK0ExOTJLVyIsImVuYyI6IkExMjhDQkMtSFMyNTYiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiZktNSG5sRkoxajBTSnJ3WGtVWlpaX3BtWHdUQlJtcHhlaTkxdUpaczUycyIsInkiOiJLRkxKaXhEUTJQcjEybWp1aFdYb3pna2U1V3lhWnhmTWlxZkJ0OEJpbkRvIiwiY3J2IjoiUC0yNTYifX0.2LSD2Mw4tyYJyfsmpVmzBtJRd12jMEYGdlhFbaXIbKi5A33CGNQ1tg.s40aAjmZOvK8Us86FCBdHg.jpYSMAKp___oMCoWM495mTfbi_YC80ObeoCmGE3H_gs.A6V-jJJRY1yz24CaXGUbzg", - "eyJhbGciOiJFQ0RILUVTK0ExOTJLVyIsImVuYyI6IkExOTJDQkMtSFMzODQiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiSDRxcFUzeWtuRktWRnV4SmxLa3NZSE5ieHF3aXM0WWtCVVFHVE1Td05JQSIsInkiOiJHb0lpRUZaUGRRSHJCbVR4ZTA3akJoZmxrdWNqUjVoX1QwNWVXc3Zib0prIiwiY3J2IjoiUC0yNTYifX0.KTrwwV2uzD--gf3PGG-kjEAGgi7u0eMqZPZfa4kpyFGm3x8t2m1NHdz3t9rfiqjuaqsxPKhF4gs.cu16fEOzYaSxhHu_Ht9w4g.BRJdxVBI9spVtY5KQ6gTR4CNcKvmLUMKZap0AO-RF2I.DZyUaa2p6YCIaYtjWOjC9GN_VIYgySlZ", - "eyJhbGciOiJFQ0RILUVTK0ExOTJLVyIsImVuYyI6IkEyNTZDQkMtSFM1MTIiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoieDBYSGRkSGM2Q0ktSnlfbUVMOEZZRExhWnV0UkVFczR4c3BMQmcwZk1jbyIsInkiOiJEa0xzOUJGTlBkTTVTNkpLYVJ3cnV1TWMwcUFzWW9yNW9fZWp6NXBNVXFrIiwiY3J2IjoiUC0yNTYifX0.mfCxJ7JYIqTMqcAh5Vp2USF0eF7OhOeluqda7YagOUJNwxA9wC9o23DSoLUylfrZUfanZrJJJcG69awlv-LY7anOLHlp3Ht5.ec48A_JWb4qa_PVHWZaTfQ.kDAjIDb3LzJpfxNh-DiAmAuaKMYaOGSTb0rkiJLuVeY.oxGCpPlii4pr89XMk4b9s084LucTqPGU6TLbOW2MZoc", - "eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImVuYyI6IkExMjhDQkMtSFMyNTYiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiQXB5TnlqU2d0bmRUcFg0eENYenNDRnZva1l3X18weXg2dGRUYzdPUUhIMCIsInkiOiJYUHdHMDVDaW1vOGlhWmxZbDNsMEp3ZllhY1FZWHFuM2RRZEJUWFpldDZBIiwiY3J2IjoiUC0yNTYifX0.yTA2PwK9IPqkaGPenZ9R-gOn9m9rvcSEfuX_Nm8AkuwHIYLzzYeAEA.ZW1F1iyHYKfo-YoanNaIVg.PouKQD94DlPA5lbpfGJXY-EJhidC7l4vSayVN2vVzvA.MexquqtGaXKUvX7WBmD4bA", - "eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImVuYyI6IkExOTJDQkMtSFMzODQiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiaDRWeGNzNVUzWk1fTlp4WmJxQ3hMTVB5UmEtR2ktSVNZa0xDTzE1RHJkZyIsInkiOiJFeVotS3dWNVE5OXlnWk5zU0lpSldpR3hqbXNLUk1WVE5sTTNSd1VYTFRvIiwiY3J2IjoiUC0yNTYifX0.wo56VISyL1QAbi2HLuVut5NGF2FvxKt7B8zHzJ3FpmavPozfbVZV08-GSYQ6jLQWJ4xsO80I4Kg.3_9Bo5ozvD96WHGhqp_tfQ.48UkJ6jk6WK70QItb2QZr0edKH7O-aMuVahTEeqyfW4.ulMlY2tbC341ct20YSmNdtc84FRz1I4g", - "eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImVuYyI6IkEyNTZDQkMtSFM1MTIiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiN0xZRzZZWTJkel9ZaGNvNnRCcG1IX0tPREQ2X2hwX05tajdEc1c2RXgxcyIsInkiOiI5Y2lPeDcwUkdGT0tpVnBRX0NHQXB5NVlyeThDazBmUkpwNHVrQ2tjNmQ0IiwiY3J2IjoiUC0yNTYifX0.bWwW3J80k46HG1fQAZxUroko2OO8OKkeRavr_o3AnhJDMvp78OR229x-fZUaBm4uWv27_Yjm0X9T2H2lhlIli2Rl9v1PNC77.1NmsJBDGI1fDjRzyc4mtyA.9KfCFynQj7LmJq08qxAG4c-6ZPz1Lh3h3nUbgVwB0TI.cqech0d8XHzWfkWqgKZq1SlAfmO0PUwOsNVkuByVGWk", - } - - for _, msg := range ecSampleMessages { - obj, err := ParseEncrypted(msg) - if err != nil { - t.Error("unable to parse message", msg, err) - continue - } - plaintext, err := obj.Decrypt(ecTestKey) - if err != nil { - t.Error("unable to decrypt message", msg, err) - continue - } - if string(plaintext) != "Lorem ipsum dolor sit amet." { - t.Error("plaintext is not what we expected for msg", msg) - } - } -} diff --git a/vendor/gopkg.in/square/go-jose.v1/jwk.go b/vendor/gopkg.in/square/go-jose.v1/jwk.go deleted file mode 100644 index ccdfb2e8..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/jwk.go +++ /dev/null @@ -1,408 +0,0 @@ -/*- - * Copyright 2014 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 jose - -import ( - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rsa" - "fmt" - "math/big" - "reflect" - "strings" -) - -// rawJsonWebKey represents a public or private key in JWK format, used for parsing/serializing. -type rawJsonWebKey struct { - Use string `json:"use,omitempty"` - Kty string `json:"kty,omitempty"` - Kid string `json:"kid,omitempty"` - Crv string `json:"crv,omitempty"` - Alg string `json:"alg,omitempty"` - K *byteBuffer `json:"k,omitempty"` - X *byteBuffer `json:"x,omitempty"` - Y *byteBuffer `json:"y,omitempty"` - N *byteBuffer `json:"n,omitempty"` - E *byteBuffer `json:"e,omitempty"` - // -- Following fields are only used for private keys -- - // RSA uses D, P and Q, while ECDSA uses only D. Fields Dp, Dq, and Qi are - // completely optional. Therefore for RSA/ECDSA, D != nil is a contract that - // we have a private key whereas D == nil means we have only a public key. - D *byteBuffer `json:"d,omitempty"` - P *byteBuffer `json:"p,omitempty"` - Q *byteBuffer `json:"q,omitempty"` - Dp *byteBuffer `json:"dp,omitempty"` - Dq *byteBuffer `json:"dq,omitempty"` - Qi *byteBuffer `json:"qi,omitempty"` -} - -// JsonWebKey represents a public or private key in JWK format. -type JsonWebKey struct { - Key interface{} - KeyID string - Algorithm string - Use string -} - -// MarshalJSON serializes the given key to its JSON representation. -func (k JsonWebKey) MarshalJSON() ([]byte, error) { - var raw *rawJsonWebKey - var err error - - switch key := k.Key.(type) { - case *ecdsa.PublicKey: - raw, err = fromEcPublicKey(key) - case *rsa.PublicKey: - raw = fromRsaPublicKey(key) - case *ecdsa.PrivateKey: - raw, err = fromEcPrivateKey(key) - case *rsa.PrivateKey: - raw, err = fromRsaPrivateKey(key) - case []byte: - raw, err = fromSymmetricKey(key) - default: - return nil, fmt.Errorf("square/go-jose: unknown key type '%s'", reflect.TypeOf(key)) - } - - if err != nil { - return nil, err - } - - raw.Kid = k.KeyID - raw.Alg = k.Algorithm - raw.Use = k.Use - - return MarshalJSON(raw) -} - -// UnmarshalJSON reads a key from its JSON representation. -func (k *JsonWebKey) UnmarshalJSON(data []byte) (err error) { - var raw rawJsonWebKey - err = UnmarshalJSON(data, &raw) - if err != nil { - return err - } - - var key interface{} - switch raw.Kty { - case "EC": - if raw.D != nil { - key, err = raw.ecPrivateKey() - } else { - key, err = raw.ecPublicKey() - } - case "RSA": - if raw.D != nil { - key, err = raw.rsaPrivateKey() - } else { - key, err = raw.rsaPublicKey() - } - case "oct": - key, err = raw.symmetricKey() - default: - err = fmt.Errorf("square/go-jose: unknown json web key type '%s'", raw.Kty) - } - - if err == nil { - *k = JsonWebKey{Key: key, KeyID: raw.Kid, Algorithm: raw.Alg, Use: raw.Use} - } - return -} - -// JsonWebKeySet represents a JWK Set object. -type JsonWebKeySet struct { - Keys []JsonWebKey `json:"keys"` -} - -// Key convenience method returns keys by key ID. Specification states -// that a JWK Set "SHOULD" use distinct key IDs, but allows for some -// cases where they are not distinct. Hence method returns a slice -// of JsonWebKeys. -func (s *JsonWebKeySet) Key(kid string) []JsonWebKey { - var keys []JsonWebKey - for _, key := range s.Keys { - if key.KeyID == kid { - keys = append(keys, key) - } - } - - return keys -} - -const rsaThumbprintTemplate = `{"e":"%s","kty":"RSA","n":"%s"}` -const ecThumbprintTemplate = `{"crv":"%s","kty":"EC","x":"%s","y":"%s"}` - -func ecThumbprintInput(curve elliptic.Curve, x, y *big.Int) (string, error) { - coordLength := curveSize(curve) - crv, err := curveName(curve) - if err != nil { - return "", err - } - - return fmt.Sprintf(ecThumbprintTemplate, crv, - newFixedSizeBuffer(x.Bytes(), coordLength).base64(), - newFixedSizeBuffer(y.Bytes(), coordLength).base64()), nil -} - -func rsaThumbprintInput(n *big.Int, e int) (string, error) { - return fmt.Sprintf(rsaThumbprintTemplate, - newBufferFromInt(uint64(e)).base64(), - newBuffer(n.Bytes()).base64()), nil -} - -// Thumbprint computes the JWK Thumbprint of a key using the -// indicated hash algorithm. -func (k *JsonWebKey) Thumbprint(hash crypto.Hash) ([]byte, error) { - var input string - var err error - switch key := k.Key.(type) { - case *ecdsa.PublicKey: - input, err = ecThumbprintInput(key.Curve, key.X, key.Y) - case *ecdsa.PrivateKey: - input, err = ecThumbprintInput(key.Curve, key.X, key.Y) - case *rsa.PublicKey: - input, err = rsaThumbprintInput(key.N, key.E) - case *rsa.PrivateKey: - input, err = rsaThumbprintInput(key.N, key.E) - default: - return nil, fmt.Errorf("square/go-jose: unknown key type '%s'", reflect.TypeOf(key)) - } - - if err != nil { - return nil, err - } - - h := hash.New() - h.Write([]byte(input)) - return h.Sum(nil), nil -} - -// Valid checks that the key contains the expected parameters -func (k *JsonWebKey) Valid() bool { - if k.Key == nil { - return false - } - switch key := k.Key.(type) { - case *ecdsa.PublicKey: - if key.Curve == nil || key.X == nil || key.Y == nil { - return false - } - case *ecdsa.PrivateKey: - if key.Curve == nil || key.X == nil || key.Y == nil || key.D == nil { - return false - } - case *rsa.PublicKey: - if key.N == nil || key.E == 0 { - return false - } - case *rsa.PrivateKey: - if key.N == nil || key.E == 0 || key.D == nil || len(key.Primes) < 2 { - return false - } - default: - return false - } - return true -} - -func (key rawJsonWebKey) rsaPublicKey() (*rsa.PublicKey, error) { - if key.N == nil || key.E == nil { - return nil, fmt.Errorf("square/go-jose: invalid RSA key, missing n/e values") - } - - return &rsa.PublicKey{ - N: key.N.bigInt(), - E: key.E.toInt(), - }, nil -} - -func fromRsaPublicKey(pub *rsa.PublicKey) *rawJsonWebKey { - return &rawJsonWebKey{ - Kty: "RSA", - N: newBuffer(pub.N.Bytes()), - E: newBufferFromInt(uint64(pub.E)), - } -} - -func (key rawJsonWebKey) ecPublicKey() (*ecdsa.PublicKey, error) { - var curve elliptic.Curve - switch key.Crv { - case "P-256": - curve = elliptic.P256() - case "P-384": - curve = elliptic.P384() - case "P-521": - curve = elliptic.P521() - default: - return nil, fmt.Errorf("square/go-jose: unsupported elliptic curve '%s'", key.Crv) - } - - if key.X == nil || key.Y == nil { - return nil, fmt.Errorf("square/go-jose: invalid EC key, missing x/y values") - } - - return &ecdsa.PublicKey{ - Curve: curve, - X: key.X.bigInt(), - Y: key.Y.bigInt(), - }, nil -} - -func fromEcPublicKey(pub *ecdsa.PublicKey) (*rawJsonWebKey, error) { - if pub == nil || pub.X == nil || pub.Y == nil { - return nil, fmt.Errorf("square/go-jose: invalid EC key (nil, or X/Y missing)") - } - - name, err := curveName(pub.Curve) - if err != nil { - return nil, err - } - - size := curveSize(pub.Curve) - - xBytes := pub.X.Bytes() - yBytes := pub.Y.Bytes() - - if len(xBytes) > size || len(yBytes) > size { - return nil, fmt.Errorf("square/go-jose: invalid EC key (X/Y too large)") - } - - key := &rawJsonWebKey{ - Kty: "EC", - Crv: name, - X: newFixedSizeBuffer(xBytes, size), - Y: newFixedSizeBuffer(yBytes, size), - } - - return key, nil -} - -func (key rawJsonWebKey) rsaPrivateKey() (*rsa.PrivateKey, error) { - var missing []string - switch { - case key.N == nil: - missing = append(missing, "N") - case key.E == nil: - missing = append(missing, "E") - case key.D == nil: - missing = append(missing, "D") - case key.P == nil: - missing = append(missing, "P") - case key.Q == nil: - missing = append(missing, "Q") - } - - if len(missing) > 0 { - return nil, fmt.Errorf("square/go-jose: invalid RSA private key, missing %s value(s)", strings.Join(missing, ", ")) - } - - rv := &rsa.PrivateKey{ - PublicKey: rsa.PublicKey{ - N: key.N.bigInt(), - E: key.E.toInt(), - }, - D: key.D.bigInt(), - Primes: []*big.Int{ - key.P.bigInt(), - key.Q.bigInt(), - }, - } - - if key.Dp != nil { - rv.Precomputed.Dp = key.Dp.bigInt() - } - if key.Dq != nil { - rv.Precomputed.Dq = key.Dq.bigInt() - } - if key.Qi != nil { - rv.Precomputed.Qinv = key.Qi.bigInt() - } - - err := rv.Validate() - return rv, err -} - -func fromRsaPrivateKey(rsa *rsa.PrivateKey) (*rawJsonWebKey, error) { - if len(rsa.Primes) != 2 { - return nil, ErrUnsupportedKeyType - } - - raw := fromRsaPublicKey(&rsa.PublicKey) - - raw.D = newBuffer(rsa.D.Bytes()) - raw.P = newBuffer(rsa.Primes[0].Bytes()) - raw.Q = newBuffer(rsa.Primes[1].Bytes()) - - return raw, nil -} - -func (key rawJsonWebKey) ecPrivateKey() (*ecdsa.PrivateKey, error) { - var curve elliptic.Curve - switch key.Crv { - case "P-256": - curve = elliptic.P256() - case "P-384": - curve = elliptic.P384() - case "P-521": - curve = elliptic.P521() - default: - return nil, fmt.Errorf("square/go-jose: unsupported elliptic curve '%s'", key.Crv) - } - - if key.X == nil || key.Y == nil || key.D == nil { - return nil, fmt.Errorf("square/go-jose: invalid EC private key, missing x/y/d values") - } - - return &ecdsa.PrivateKey{ - PublicKey: ecdsa.PublicKey{ - Curve: curve, - X: key.X.bigInt(), - Y: key.Y.bigInt(), - }, - D: key.D.bigInt(), - }, nil -} - -func fromEcPrivateKey(ec *ecdsa.PrivateKey) (*rawJsonWebKey, error) { - raw, err := fromEcPublicKey(&ec.PublicKey) - if err != nil { - return nil, err - } - - if ec.D == nil { - return nil, fmt.Errorf("square/go-jose: invalid EC private key") - } - - raw.D = newBuffer(ec.D.Bytes()) - - return raw, nil -} - -func fromSymmetricKey(key []byte) (*rawJsonWebKey, error) { - return &rawJsonWebKey{ - Kty: "oct", - K: newBuffer(key), - }, nil -} - -func (key rawJsonWebKey) symmetricKey() ([]byte, error) { - if key.K == nil { - return nil, fmt.Errorf("square/go-jose: invalid OCT (symmetric) key, missing k value") - } - return key.K.bytes(), nil -} diff --git a/vendor/gopkg.in/square/go-jose.v1/jwk_test.go b/vendor/gopkg.in/square/go-jose.v1/jwk_test.go deleted file mode 100644 index a87b42a1..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/jwk_test.go +++ /dev/null @@ -1,552 +0,0 @@ -/*- - * Copyright 2014 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 jose - -import ( - "bytes" - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rsa" - "encoding/hex" - "fmt" - "math/big" - "reflect" - "testing" -) - -func TestCurveSize(t *testing.T) { - size256 := curveSize(elliptic.P256()) - size384 := curveSize(elliptic.P384()) - size521 := curveSize(elliptic.P521()) - if size256 != 32 { - t.Error("P-256 have 32 bytes") - } - if size384 != 48 { - t.Error("P-384 have 48 bytes") - } - if size521 != 66 { - t.Error("P-521 have 66 bytes") - } -} - -func TestRoundtripRsaPrivate(t *testing.T) { - jwk, err := fromRsaPrivateKey(rsaTestKey) - if err != nil { - t.Error("problem constructing JWK from rsa key", err) - } - - rsa2, err := jwk.rsaPrivateKey() - if err != nil { - t.Error("problem converting RSA private -> JWK", err) - } - - if rsa2.N.Cmp(rsaTestKey.N) != 0 { - t.Error("RSA private N mismatch") - } - if rsa2.E != rsaTestKey.E { - t.Error("RSA private E mismatch") - } - if rsa2.D.Cmp(rsaTestKey.D) != 0 { - t.Error("RSA private D mismatch") - } - if len(rsa2.Primes) != 2 { - t.Error("RSA private roundtrip expected two primes") - } - if rsa2.Primes[0].Cmp(rsaTestKey.Primes[0]) != 0 { - t.Error("RSA private P mismatch") - } - if rsa2.Primes[1].Cmp(rsaTestKey.Primes[1]) != 0 { - t.Error("RSA private Q mismatch") - } -} - -func TestRsaPrivateInsufficientPrimes(t *testing.T) { - brokenRsaPrivateKey := rsa.PrivateKey{ - PublicKey: rsa.PublicKey{ - N: rsaTestKey.N, - E: rsaTestKey.E, - }, - D: rsaTestKey.D, - Primes: []*big.Int{rsaTestKey.Primes[0]}, - } - - _, err := fromRsaPrivateKey(&brokenRsaPrivateKey) - if err != ErrUnsupportedKeyType { - t.Error("expected unsupported key type error, got", err) - } -} - -func TestRsaPrivateExcessPrimes(t *testing.T) { - brokenRsaPrivateKey := rsa.PrivateKey{ - PublicKey: rsa.PublicKey{ - N: rsaTestKey.N, - E: rsaTestKey.E, - }, - D: rsaTestKey.D, - Primes: []*big.Int{ - rsaTestKey.Primes[0], - rsaTestKey.Primes[1], - big.NewInt(3), - }, - } - - _, err := fromRsaPrivateKey(&brokenRsaPrivateKey) - if err != ErrUnsupportedKeyType { - t.Error("expected unsupported key type error, got", err) - } -} - -func TestRoundtripEcPublic(t *testing.T) { - for i, ecTestKey := range []*ecdsa.PrivateKey{ecTestKey256, ecTestKey384, ecTestKey521} { - jwk, err := fromEcPublicKey(&ecTestKey.PublicKey) - - ec2, err := jwk.ecPublicKey() - if err != nil { - t.Error("problem converting ECDSA private -> JWK", i, err) - } - - if !reflect.DeepEqual(ec2.Curve, ecTestKey.Curve) { - t.Error("ECDSA private curve mismatch", i) - } - if ec2.X.Cmp(ecTestKey.X) != 0 { - t.Error("ECDSA X mismatch", i) - } - if ec2.Y.Cmp(ecTestKey.Y) != 0 { - t.Error("ECDSA Y mismatch", i) - } - } -} - -func TestRoundtripEcPrivate(t *testing.T) { - for i, ecTestKey := range []*ecdsa.PrivateKey{ecTestKey256, ecTestKey384, ecTestKey521} { - jwk, err := fromEcPrivateKey(ecTestKey) - - ec2, err := jwk.ecPrivateKey() - if err != nil { - t.Error("problem converting ECDSA private -> JWK", i, err) - } - - if !reflect.DeepEqual(ec2.Curve, ecTestKey.Curve) { - t.Error("ECDSA private curve mismatch", i) - } - if ec2.X.Cmp(ecTestKey.X) != 0 { - t.Error("ECDSA X mismatch", i) - } - if ec2.Y.Cmp(ecTestKey.Y) != 0 { - t.Error("ECDSA Y mismatch", i) - } - if ec2.D.Cmp(ecTestKey.D) != 0 { - t.Error("ECDSA D mismatch", i) - } - } -} - -func TestMarshalUnmarshal(t *testing.T) { - kid := "DEADBEEF" - - for i, key := range []interface{}{ecTestKey256, ecTestKey384, ecTestKey521, rsaTestKey} { - for _, use := range []string{"", "sig", "enc"} { - jwk := JsonWebKey{Key: key, KeyID: kid, Algorithm: "foo"} - if use != "" { - jwk.Use = use - } - - jsonbar, err := jwk.MarshalJSON() - if err != nil { - t.Error("problem marshaling", i, err) - } - - var jwk2 JsonWebKey - err = jwk2.UnmarshalJSON(jsonbar) - if err != nil { - t.Error("problem unmarshalling", i, err) - } - - jsonbar2, err := jwk2.MarshalJSON() - if err != nil { - t.Error("problem marshaling", i, err) - } - - if !bytes.Equal(jsonbar, jsonbar2) { - t.Error("roundtrip should not lose information", i) - } - if jwk2.KeyID != kid { - t.Error("kid did not roundtrip JSON marshalling", i) - } - - if jwk2.Algorithm != "foo" { - t.Error("alg did not roundtrip JSON marshalling", i) - } - - if jwk2.Use != use { - t.Error("use did not roundtrip JSON marshalling", i) - } - } - } -} - -func TestMarshalNonPointer(t *testing.T) { - type EmbedsKey struct { - Key JsonWebKey - } - - keyJson := []byte(`{ - "e": "AQAB", - "kty": "RSA", - "n": "vd7rZIoTLEe-z1_8G1FcXSw9CQFEJgV4g9V277sER7yx5Qjz_Pkf2YVth6wwwFJEmzc0hoKY-MMYFNwBE4hQHw" - }`) - var parsedKey JsonWebKey - err := UnmarshalJSON(keyJson, &parsedKey) - if err != nil { - t.Error(fmt.Sprintf("Error unmarshalling key: %v", err)) - return - } - ek := EmbedsKey{ - Key: parsedKey, - } - out, err := MarshalJSON(ek) - if err != nil { - t.Error(fmt.Sprintf("Error marshalling JSON: %v", err)) - return - } - expected := "{\"Key\":{\"kty\":\"RSA\",\"n\":\"vd7rZIoTLEe-z1_8G1FcXSw9CQFEJgV4g9V277sER7yx5Qjz_Pkf2YVth6wwwFJEmzc0hoKY-MMYFNwBE4hQHw\",\"e\":\"AQAB\"}}" - if string(out) != expected { - t.Error("Failed to marshal embedded non-pointer JWK properly:", string(out)) - } -} - -func TestMarshalUnmarshalInvalid(t *testing.T) { - // Make an invalid curve coordinate by creating a byte array that is one - // byte too large, and setting the first byte to 1 (otherwise it's just zero). - invalidCoord := make([]byte, curveSize(ecTestKey256.Curve)+1) - invalidCoord[0] = 1 - - keys := []interface{}{ - // Empty keys - &rsa.PrivateKey{}, - &ecdsa.PrivateKey{}, - // Invalid keys - &ecdsa.PrivateKey{ - PublicKey: ecdsa.PublicKey{ - // Missing values in pub key - Curve: elliptic.P256(), - }, - }, - &ecdsa.PrivateKey{ - PublicKey: ecdsa.PublicKey{ - // Invalid curve - Curve: nil, - X: ecTestKey256.X, - Y: ecTestKey256.Y, - }, - }, - &ecdsa.PrivateKey{ - // Valid pub key, but missing priv key values - PublicKey: ecTestKey256.PublicKey, - }, - &ecdsa.PrivateKey{ - // Invalid pub key, values too large - PublicKey: ecdsa.PublicKey{ - Curve: ecTestKey256.Curve, - X: big.NewInt(0).SetBytes(invalidCoord), - Y: big.NewInt(0).SetBytes(invalidCoord), - }, - D: ecTestKey256.D, - }, - nil, - } - - for i, key := range keys { - jwk := JsonWebKey{Key: key} - _, err := jwk.MarshalJSON() - if err == nil { - t.Error("managed to serialize invalid key", i) - } - } -} - -func TestWebKeyVectorsInvalid(t *testing.T) { - keys := []string{ - // Invalid JSON - "{X", - // Empty key - "{}", - // Invalid RSA keys - `{"kty":"RSA"}`, - `{"kty":"RSA","e":""}`, - `{"kty":"RSA","e":"XXXX"}`, - `{"kty":"RSA","d":"XXXX"}`, - // Invalid EC keys - `{"kty":"EC","crv":"ABC"}`, - `{"kty":"EC","crv":"P-256"}`, - `{"kty":"EC","crv":"P-256","d":"XXX"}`, - `{"kty":"EC","crv":"ABC","d":"dGVzdA","x":"dGVzdA"}`, - `{"kty":"EC","crv":"P-256","d":"dGVzdA","x":"dGVzdA"}`, - } - - for _, key := range keys { - var jwk2 JsonWebKey - err := jwk2.UnmarshalJSON([]byte(key)) - if err == nil { - t.Error("managed to parse invalid key:", key) - } - } -} - -// Test vectors from RFC 7520 -var cookbookJWKs = []string{ - // EC Public - stripWhitespace(`{ - "kty": "EC", - "kid": "bilbo.baggins@hobbiton.example", - "use": "sig", - "crv": "P-521", - "x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9 - A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt", - "y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVy - SsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1" - }`), - - // EC Private - stripWhitespace(`{ - "kty": "EC", - "kid": "bilbo.baggins@hobbiton.example", - "use": "sig", - "crv": "P-521", - "x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9 - A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt", - "y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVy - SsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1", - "d": "AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zb - KipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt" - }`), - - // RSA Public - stripWhitespace(`{ - "kty": "RSA", - "kid": "bilbo.baggins@hobbiton.example", - "use": "sig", - "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT - -O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqV - wGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj- - oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde - 3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuC - LqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5g - HdrNP5zw", - "e": "AQAB" - }`), - - // RSA Private - stripWhitespace(`{"kty":"RSA", - "kid":"juliet@capulet.lit", - "use":"enc", - "n":"t6Q8PWSi1dkJj9hTP8hNYFlvadM7DflW9mWepOJhJ66w7nyoK1gPNqFMSQRy - O125Gp-TEkodhWr0iujjHVx7BcV0llS4w5ACGgPrcAd6ZcSR0-Iqom-QFcNP - 8Sjg086MwoqQU_LYywlAGZ21WSdS_PERyGFiNnj3QQlO8Yns5jCtLCRwLHL0 - Pb1fEv45AuRIuUfVcPySBWYnDyGxvjYGDSM-AqWS9zIQ2ZilgT-GqUmipg0X - OC0Cc20rgLe2ymLHjpHciCKVAbY5-L32-lSeZO-Os6U15_aXrk9Gw8cPUaX1 - _I8sLGuSiVdt3C_Fn2PZ3Z8i744FPFGGcG1qs2Wz-Q", - "e":"AQAB", - "d":"GRtbIQmhOZtyszfgKdg4u_N-R_mZGU_9k7JQ_jn1DnfTuMdSNprTeaSTyWfS - NkuaAwnOEbIQVy1IQbWVV25NY3ybc_IhUJtfri7bAXYEReWaCl3hdlPKXy9U - vqPYGR0kIXTQRqns-dVJ7jahlI7LyckrpTmrM8dWBo4_PMaenNnPiQgO0xnu - ToxutRZJfJvG4Ox4ka3GORQd9CsCZ2vsUDmsXOfUENOyMqADC6p1M3h33tsu - rY15k9qMSpG9OX_IJAXmxzAh_tWiZOwk2K4yxH9tS3Lq1yX8C1EWmeRDkK2a - hecG85-oLKQt5VEpWHKmjOi_gJSdSgqcN96X52esAQ", - "p":"2rnSOV4hKSN8sS4CgcQHFbs08XboFDqKum3sc4h3GRxrTmQdl1ZK9uw-PIHf - QP0FkxXVrx-WE-ZEbrqivH_2iCLUS7wAl6XvARt1KkIaUxPPSYB9yk31s0Q8 - UK96E3_OrADAYtAJs-M3JxCLfNgqh56HDnETTQhH3rCT5T3yJws", - "q":"1u_RiFDP7LBYh3N4GXLT9OpSKYP0uQZyiaZwBtOCBNJgQxaj10RWjsZu0c6I - edis4S7B_coSKB0Kj9PaPaBzg-IySRvvcQuPamQu66riMhjVtG6TlV8CLCYK - rYl52ziqK0E_ym2QnkwsUX7eYTB7LbAHRK9GqocDE5B0f808I4s", - "dp":"KkMTWqBUefVwZ2_Dbj1pPQqyHSHjj90L5x_MOzqYAJMcLMZtbUtwKqvVDq3 - tbEo3ZIcohbDtt6SbfmWzggabpQxNxuBpoOOf_a_HgMXK_lhqigI4y_kqS1w - Y52IwjUn5rgRrJ-yYo1h41KR-vz2pYhEAeYrhttWtxVqLCRViD6c", - "dq":"AvfS0-gRxvn0bwJoMSnFxYcK1WnuEjQFluMGfwGitQBWtfZ1Er7t1xDkbN9 - GQTB9yqpDoYaN06H7CFtrkxhJIBQaj6nkF5KKS3TQtQ5qCzkOkmxIe3KRbBy - mXxkb5qwUpX5ELD5xFc6FeiafWYY63TmmEAu_lRFCOJ3xDea-ots", - "qi":"lSQi-w9CpyUReMErP1RsBLk7wNtOvs5EQpPqmuMvqW57NBUczScEoPwmUqq - abu9V0-Py4dQ57_bapoKRu1R90bvuFnU63SHWEFglZQvJDMeAvmj4sm-Fp0o - Yu_neotgQ0hzbI5gry7ajdYy9-2lNx_76aBZoOUu9HCJ-UsfSOI8"}`), -} - -// SHA-256 thumbprints of the above keys, hex-encoded -var cookbookJWKThumbprints = []string{ - "747ae2dd2003664aeeb21e4753fe7402846170a16bc8df8f23a8cf06d3cbe793", - "747ae2dd2003664aeeb21e4753fe7402846170a16bc8df8f23a8cf06d3cbe793", - "f63838e96077ad1fc01c3f8405774dedc0641f558ebb4b40dccf5f9b6d66a932", - "0fc478f8579325fcee0d4cbc6d9d1ce21730a6e97e435d6008fb379b0ebe47d4", -} - -func TestWebKeyVectorsValid(t *testing.T) { - for _, key := range cookbookJWKs { - var jwk2 JsonWebKey - err := jwk2.UnmarshalJSON([]byte(key)) - if err != nil { - t.Error("unable to parse valid key:", key, err) - } - } -} - -func TestThumbprint(t *testing.T) { - for i, key := range cookbookJWKs { - var jwk2 JsonWebKey - err := jwk2.UnmarshalJSON([]byte(key)) - if err != nil { - t.Error("unable to parse valid key:", key, err) - } - - tp, err := jwk2.Thumbprint(crypto.SHA256) - if err != nil { - t.Error("unable to compute thumbprint:", key, err) - } - - tpHex := hex.EncodeToString(tp) - if cookbookJWKThumbprints[i] != tpHex { - t.Error("incorrect thumbprint:", i, cookbookJWKThumbprints[i], tpHex) - } - } -} - -func TestMarshalUnmarshalJWKSet(t *testing.T) { - jwk1 := JsonWebKey{Key: rsaTestKey, KeyID: "ABCDEFG", Algorithm: "foo"} - jwk2 := JsonWebKey{Key: rsaTestKey, KeyID: "GFEDCBA", Algorithm: "foo"} - var set JsonWebKeySet - set.Keys = append(set.Keys, jwk1) - set.Keys = append(set.Keys, jwk2) - - jsonbar, err := MarshalJSON(&set) - if err != nil { - t.Error("problem marshalling set", err) - } - var set2 JsonWebKeySet - err = UnmarshalJSON(jsonbar, &set2) - if err != nil { - t.Error("problem unmarshalling set", err) - } - jsonbar2, err := MarshalJSON(&set2) - if err != nil { - t.Error("problem marshalling set", err) - } - if !bytes.Equal(jsonbar, jsonbar2) { - t.Error("roundtrip should not lose information") - } -} - -func TestJWKSetKey(t *testing.T) { - jwk1 := JsonWebKey{Key: rsaTestKey, KeyID: "ABCDEFG", Algorithm: "foo"} - jwk2 := JsonWebKey{Key: rsaTestKey, KeyID: "GFEDCBA", Algorithm: "foo"} - var set JsonWebKeySet - set.Keys = append(set.Keys, jwk1) - set.Keys = append(set.Keys, jwk2) - k := set.Key("ABCDEFG") - if len(k) != 1 { - t.Errorf("method should return slice with one key not %d", len(k)) - } - if k[0].KeyID != "ABCDEFG" { - t.Error("method should return key with ID ABCDEFG") - } -} - -func TestJWKSymmetricKey(t *testing.T) { - sample1 := `{"kty":"oct","alg":"A128KW","k":"GawgguFyGrWKav7AX4VKUg"}` - sample2 := `{"kty":"oct","k":"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow","kid":"HMAC key used in JWS spec Appendix A.1 example"}` - - var jwk1 JsonWebKey - UnmarshalJSON([]byte(sample1), &jwk1) - - if jwk1.Algorithm != "A128KW" { - t.Errorf("expected Algorithm to be A128KW, but was '%s'", jwk1.Algorithm) - } - expected1 := fromHexBytes("19ac2082e1721ab58a6afec05f854a52") - if !bytes.Equal(jwk1.Key.([]byte), expected1) { - t.Errorf("expected Key to be '%s', but was '%s'", hex.EncodeToString(expected1), hex.EncodeToString(jwk1.Key.([]byte))) - } - - var jwk2 JsonWebKey - UnmarshalJSON([]byte(sample2), &jwk2) - - if jwk2.KeyID != "HMAC key used in JWS spec Appendix A.1 example" { - t.Errorf("expected KeyID to be 'HMAC key used in JWS spec Appendix A.1 example', but was '%s'", jwk2.KeyID) - } - expected2 := fromHexBytes(` - 0323354b2b0fa5bc837e0665777ba68f5ab328e6f054c928a90f84b2d2502ebf - d3fb5a92d20647ef968ab4c377623d223d2e2172052e4f08c0cd9af567d080a3`) - if !bytes.Equal(jwk2.Key.([]byte), expected2) { - t.Errorf("expected Key to be '%s', but was '%s'", hex.EncodeToString(expected2), hex.EncodeToString(jwk2.Key.([]byte))) - } -} - -func TestJWKSymmetricRoundtrip(t *testing.T) { - jwk1 := JsonWebKey{Key: []byte{1, 2, 3, 4}} - marshaled, err := jwk1.MarshalJSON() - if err != nil { - t.Errorf("failed to marshal valid JWK object", err) - } - - var jwk2 JsonWebKey - err = jwk2.UnmarshalJSON(marshaled) - if err != nil { - t.Errorf("failed to unmarshal valid JWK object", err) - } - - if !bytes.Equal(jwk1.Key.([]byte), jwk2.Key.([]byte)) { - t.Error("round-trip of symmetric JWK gave different raw keys") - } -} - -func TestJWKSymmetricInvalid(t *testing.T) { - invalid := JsonWebKey{} - _, err := invalid.MarshalJSON() - if err == nil { - t.Error("excepted error on marshaling invalid symmetric JWK object") - } - - var jwk JsonWebKey - err = jwk.UnmarshalJSON([]byte(`{"kty":"oct"}`)) - if err == nil { - t.Error("excepted error on unmarshaling invalid symmetric JWK object") - } -} - -func TestJWKValid(t *testing.T) { - bigInt := big.NewInt(0) - eccPub := ecdsa.PublicKey{elliptic.P256(), bigInt, bigInt} - rsaPub := rsa.PublicKey{bigInt, 1} - cases := []struct { - key interface{} - expectedValidity bool - }{ - {nil, false}, - {&ecdsa.PublicKey{}, false}, - {&eccPub, true}, - {&ecdsa.PrivateKey{}, false}, - {&ecdsa.PrivateKey{eccPub, bigInt}, true}, - {&rsa.PublicKey{}, false}, - {&rsaPub, true}, - {&rsa.PrivateKey{}, false}, - {&rsa.PrivateKey{rsaPub, bigInt, []*big.Int{bigInt, bigInt}, rsa.PrecomputedValues{}}, true}, - } - - for _, tc := range cases { - k := &JsonWebKey{Key: tc.key} - if valid := k.Valid(); valid != tc.expectedValidity { - t.Errorf("expected Valid to return %t, got %t", tc.expectedValidity, valid) - } - } -} diff --git a/vendor/gopkg.in/square/go-jose.v1/jws.go b/vendor/gopkg.in/square/go-jose.v1/jws.go deleted file mode 100644 index 87173c91..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/jws.go +++ /dev/null @@ -1,252 +0,0 @@ -/*- - * Copyright 2014 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 jose - -import ( - "fmt" - "strings" -) - -// rawJsonWebSignature represents a raw JWS JSON object. Used for parsing/serializing. -type rawJsonWebSignature struct { - Payload *byteBuffer `json:"payload,omitempty"` - Signatures []rawSignatureInfo `json:"signatures,omitempty"` - Protected *byteBuffer `json:"protected,omitempty"` - Header *rawHeader `json:"header,omitempty"` - Signature *byteBuffer `json:"signature,omitempty"` -} - -// rawSignatureInfo represents a single JWS signature over the JWS payload and protected header. -type rawSignatureInfo struct { - Protected *byteBuffer `json:"protected,omitempty"` - Header *rawHeader `json:"header,omitempty"` - Signature *byteBuffer `json:"signature,omitempty"` -} - -// JsonWebSignature represents a signed JWS object after parsing. -type JsonWebSignature struct { - payload []byte - Signatures []Signature -} - -// Signature represents a single signature over the JWS payload and protected header. -type Signature struct { - // Header fields, such as the signature algorithm - Header JoseHeader - - // The actual signature value - Signature []byte - - protected *rawHeader - header *rawHeader - original *rawSignatureInfo -} - -// ParseSigned parses an encrypted message in compact or full serialization format. -func ParseSigned(input string) (*JsonWebSignature, error) { - input = stripWhitespace(input) - if strings.HasPrefix(input, "{") { - return parseSignedFull(input) - } - - return parseSignedCompact(input) -} - -// Get a header value -func (sig Signature) mergedHeaders() rawHeader { - out := rawHeader{} - out.merge(sig.protected) - out.merge(sig.header) - return out -} - -// Compute data to be signed -func (obj JsonWebSignature) computeAuthData(signature *Signature) []byte { - var serializedProtected string - - if signature.original != nil && signature.original.Protected != nil { - serializedProtected = signature.original.Protected.base64() - } else if signature.protected != nil { - serializedProtected = base64URLEncode(mustSerializeJSON(signature.protected)) - } else { - serializedProtected = "" - } - - return []byte(fmt.Sprintf("%s.%s", - serializedProtected, - base64URLEncode(obj.payload))) -} - -// parseSignedFull parses a message in full format. -func parseSignedFull(input string) (*JsonWebSignature, error) { - var parsed rawJsonWebSignature - err := UnmarshalJSON([]byte(input), &parsed) - if err != nil { - return nil, err - } - - return parsed.sanitized() -} - -// sanitized produces a cleaned-up JWS object from the raw JSON. -func (parsed *rawJsonWebSignature) sanitized() (*JsonWebSignature, error) { - if parsed.Payload == nil { - return nil, fmt.Errorf("square/go-jose: missing payload in JWS message") - } - - obj := &JsonWebSignature{ - payload: parsed.Payload.bytes(), - Signatures: make([]Signature, len(parsed.Signatures)), - } - - if len(parsed.Signatures) == 0 { - // No signatures array, must be flattened serialization - signature := Signature{} - if parsed.Protected != nil && len(parsed.Protected.bytes()) > 0 { - signature.protected = &rawHeader{} - err := UnmarshalJSON(parsed.Protected.bytes(), signature.protected) - if err != nil { - return nil, err - } - } - - if parsed.Header != nil && parsed.Header.Nonce != "" { - return nil, ErrUnprotectedNonce - } - - signature.header = parsed.Header - signature.Signature = parsed.Signature.bytes() - // Make a fake "original" rawSignatureInfo to store the unprocessed - // Protected header. This is necessary because the Protected header can - // contain arbitrary fields not registered as part of the spec. See - // https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-4 - // If we unmarshal Protected into a rawHeader with its explicit list of fields, - // we cannot marshal losslessly. So we have to keep around the original bytes. - // This is used in computeAuthData, which will first attempt to use - // the original bytes of a protected header, and fall back on marshaling the - // header struct only if those bytes are not available. - signature.original = &rawSignatureInfo{ - Protected: parsed.Protected, - Header: parsed.Header, - Signature: parsed.Signature, - } - - signature.Header = signature.mergedHeaders().sanitized() - obj.Signatures = append(obj.Signatures, signature) - } - - for i, sig := range parsed.Signatures { - if sig.Protected != nil && len(sig.Protected.bytes()) > 0 { - obj.Signatures[i].protected = &rawHeader{} - err := UnmarshalJSON(sig.Protected.bytes(), obj.Signatures[i].protected) - if err != nil { - return nil, err - } - } - - // Check that there is not a nonce in the unprotected header - if sig.Header != nil && sig.Header.Nonce != "" { - return nil, ErrUnprotectedNonce - } - - obj.Signatures[i].Signature = sig.Signature.bytes() - - // Copy value of sig - original := sig - - obj.Signatures[i].header = sig.Header - obj.Signatures[i].original = &original - obj.Signatures[i].Header = obj.Signatures[i].mergedHeaders().sanitized() - } - - return obj, nil -} - -// parseSignedCompact parses a message in compact format. -func parseSignedCompact(input string) (*JsonWebSignature, error) { - parts := strings.Split(input, ".") - if len(parts) != 3 { - return nil, fmt.Errorf("square/go-jose: compact JWS format must have three parts") - } - - rawProtected, err := base64URLDecode(parts[0]) - if err != nil { - return nil, err - } - - payload, err := base64URLDecode(parts[1]) - if err != nil { - return nil, err - } - - signature, err := base64URLDecode(parts[2]) - if err != nil { - return nil, err - } - - raw := &rawJsonWebSignature{ - Payload: newBuffer(payload), - Protected: newBuffer(rawProtected), - Signature: newBuffer(signature), - } - return raw.sanitized() -} - -// CompactSerialize serializes an object using the compact serialization format. -func (obj JsonWebSignature) CompactSerialize() (string, error) { - if len(obj.Signatures) != 1 || obj.Signatures[0].header != nil || obj.Signatures[0].protected == nil { - return "", ErrNotSupported - } - - serializedProtected := mustSerializeJSON(obj.Signatures[0].protected) - - return fmt.Sprintf( - "%s.%s.%s", - base64URLEncode(serializedProtected), - base64URLEncode(obj.payload), - base64URLEncode(obj.Signatures[0].Signature)), nil -} - -// FullSerialize serializes an object using the full JSON serialization format. -func (obj JsonWebSignature) FullSerialize() string { - raw := rawJsonWebSignature{ - Payload: newBuffer(obj.payload), - } - - if len(obj.Signatures) == 1 { - if obj.Signatures[0].protected != nil { - serializedProtected := mustSerializeJSON(obj.Signatures[0].protected) - raw.Protected = newBuffer(serializedProtected) - } - raw.Header = obj.Signatures[0].header - raw.Signature = newBuffer(obj.Signatures[0].Signature) - } else { - raw.Signatures = make([]rawSignatureInfo, len(obj.Signatures)) - for i, signature := range obj.Signatures { - raw.Signatures[i] = rawSignatureInfo{ - Header: signature.header, - Signature: newBuffer(signature.Signature), - } - - if signature.protected != nil { - raw.Signatures[i].Protected = newBuffer(mustSerializeJSON(signature.protected)) - } - } - } - - return string(mustSerializeJSON(raw)) -} diff --git a/vendor/gopkg.in/square/go-jose.v1/jws_test.go b/vendor/gopkg.in/square/go-jose.v1/jws_test.go deleted file mode 100644 index d8f94c14..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/jws_test.go +++ /dev/null @@ -1,302 +0,0 @@ -/*- - * Copyright 2014 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 jose - -import ( - "fmt" - "strings" - "testing" -) - -func TestCompactParseJWS(t *testing.T) { - // Should parse - msg := "eyJhbGciOiJYWVoifQ.cGF5bG9hZA.c2lnbmF0dXJl" - _, err := ParseSigned(msg) - if err != nil { - t.Error("Unable to parse valid message:", err) - } - - // Messages that should fail to parse - failures := []string{ - // Not enough parts - "eyJhbGciOiJYWVoifQ.cGF5bG9hZA", - // Invalid signature - "eyJhbGciOiJYWVoifQ.cGF5bG9hZA.////", - // Invalid payload - "eyJhbGciOiJYWVoifQ.////.c2lnbmF0dXJl", - // Invalid header - "////.eyJhbGciOiJYWVoifQ.c2lnbmF0dXJl", - // Invalid header - "cGF5bG9hZA.cGF5bG9hZA.c2lnbmF0dXJl", - } - - for i := range failures { - _, err = ParseSigned(failures[i]) - if err == nil { - t.Error("Able to parse invalid message") - } - } -} - -func TestFullParseJWS(t *testing.T) { - // Messages that should succeed to parse - successes := []string{ - "{\"payload\":\"CUJD\",\"signatures\":[{\"protected\":\"e30\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"CUJD\"},{\"protected\":\"e30\",\"signature\":\"CUJD\"}]}", - } - - for i := range successes { - _, err := ParseSigned(successes[i]) - if err != nil { - t.Error("Unble to parse valid message", err, successes[i]) - } - } - - // Messages that should fail to parse - failures := []string{ - // Empty - "{}", - // Invalid JSON - "{XX", - // Invalid protected header - "{\"payload\":\"CUJD\",\"signatures\":[{\"protected\":\"CUJD\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"CUJD\"}]}", - // Invalid protected header - "{\"payload\":\"CUJD\",\"protected\":\"CUJD\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"CUJD\"}", - // Invalid protected header - "{\"payload\":\"CUJD\",\"signatures\":[{\"protected\":\"###\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"CUJD\"}]}", - // Invalid payload - "{\"payload\":\"###\",\"signatures\":[{\"protected\":\"CUJD\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"CUJD\"}]}", - // Invalid payload - "{\"payload\":\"CUJD\",\"signatures\":[{\"protected\":\"e30\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"###\"}]}", - } - - for i := range failures { - _, err := ParseSigned(failures[i]) - if err == nil { - t.Error("Able to parse invalid message", err, failures[i]) - } - } -} - -func TestRejectUnprotectedJWSNonce(t *testing.T) { - // No need to test compact, since that's always protected - - // Flattened JSON - input := `{ - "header": { "nonce": "should-cause-an-error" }, - "payload": "does-not-matter", - "signature": "does-not-matter" - }` - _, err := ParseSigned(input) - if err == nil { - t.Error("JWS with an unprotected nonce parsed as valid.") - } else if err != ErrUnprotectedNonce { - t.Errorf("Improper error for unprotected nonce: %v", err) - } - - // Full JSON - input = `{ - "payload": "does-not-matter", - "signatures": [{ - "header": { "nonce": "should-cause-an-error" }, - "signature": "does-not-matter" - }] - }` - _, err = ParseSigned(input) - if err == nil { - t.Error("JWS with an unprotected nonce parsed as valid.") - } else if err != ErrUnprotectedNonce { - t.Errorf("Improper error for unprotected nonce: %v", err) - } -} - -func TestVerifyFlattenedWithIncludedUnprotectedKey(t *testing.T) { - input := `{ - "header": { - "alg": "RS256", - "jwk": { - "e": "AQAB", - "kty": "RSA", - "n": "tSwgy3ORGvc7YJI9B2qqkelZRUC6F1S5NwXFvM4w5-M0TsxbFsH5UH6adigV0jzsDJ5imAechcSoOhAh9POceCbPN1sTNwLpNbOLiQQ7RD5mY_pSUHWXNmS9R4NZ3t2fQAzPeW7jOfF0LKuJRGkekx6tXP1uSnNibgpJULNc4208dgBaCHo3mvaE2HV2GmVl1yxwWX5QZZkGQGjNDZYnjFfa2DKVvFs0QbAk21ROm594kAxlRlMMrvqlf24Eq4ERO0ptzpZgm_3j_e4hGRD39gJS7kAzK-j2cacFQ5Qi2Y6wZI2p-FCq_wiYsfEAIkATPBiLKl_6d_Jfcvs_impcXQ" - } - }, - "payload": "Zm9vCg", - "signature": "hRt2eYqBd_MyMRNIh8PEIACoFtmBi7BHTLBaAhpSU6zyDAFdEBaX7us4VB9Vo1afOL03Q8iuoRA0AT4akdV_mQTAQ_jhTcVOAeXPr0tB8b8Q11UPQ0tXJYmU4spAW2SapJIvO50ntUaqU05kZd0qw8-noH1Lja-aNnU-tQII4iYVvlTiRJ5g8_CADsvJqOk6FcHuo2mG643TRnhkAxUtazvHyIHeXMxydMMSrpwUwzMtln4ZJYBNx4QGEq6OhpAD_VSp-w8Lq5HOwGQoNs0bPxH1SGrArt67LFQBfjlVr94E1sn26p4vigXm83nJdNhWAMHHE9iV67xN-r29LT-FjA" - }` - - jws, err := ParseSigned(input) - if err != nil { - t.Error("Unable to parse valid message.") - } - if len(jws.Signatures) != 1 { - t.Error("Too many or too few signatures.") - } - sig := jws.Signatures[0] - if sig.Header.JsonWebKey == nil { - t.Error("No JWK in signature header.") - } - payload, err := jws.Verify(sig.Header.JsonWebKey) - if err != nil { - t.Error(fmt.Sprintf("Signature did not validate: %v", err)) - } - if string(payload) != "foo\n" { - t.Error(fmt.Sprintf("Payload was incorrect: '%s' should have been 'foo\\n'", string(payload))) - } -} - -func TestVerifyFlattenedWithPrivateProtected(t *testing.T) { - // The protected field contains a Private Header Parameter name, per - // https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-4 - // Base64-decoded, it's '{"nonce":"8HIepUNFZUa-exKTrXVf4g"}' - input := `{"header":{"alg":"RS256","jwk":{"kty":"RSA","n":"7ixeydcbxxppzxrBphrW1atUiEZqTpiHDpI-79olav5XxAgWolHmVsJyxzoZXRxmtED8PF9-EICZWBGdSAL9ZTD0hLUCIsPcpdgT_LqNW3Sh2b2caPL2hbMF7vsXvnCGg9varpnHWuYTyRrCLUF9vM7ES-V3VCYTa7LcCSRm56Gg9r19qar43Z9kIKBBxpgt723v2cC4bmLmoAX2s217ou3uCpCXGLOeV_BesG4--Nl3pso1VhCfO85wEWjmW6lbv7Kg4d7Jdkv5DjDZfJ086fkEAYZVYGRpIgAvJBH3d3yKDCrSByUEud1bWuFjQBmMaeYOrVDXO_mbYg5PwUDMhw","e":"AQAB"}},"protected":"eyJub25jZSI6IjhISWVwVU5GWlVhLWV4S1RyWFZmNGcifQ","payload":"eyJjb250YWN0IjpbIm1haWx0bzpmb29AYmFyLmNvbSJdfQ","signature":"AyvVGMgXsQ1zTdXrZxE_gyO63pQgotL1KbI7gv6Wi8I7NRy0iAOkDAkWcTQT9pcCYApJ04lXfEDZfP5i0XgcFUm_6spxi5mFBZU-NemKcvK9dUiAbXvb4hB3GnaZtZiuVnMQUb_ku4DOaFFKbteA6gOYCnED_x7v0kAPHIYrQnvIa-KZ6pTajbV9348zgh9TL7NgGIIsTcMHd-Jatr4z1LQ0ubGa8tS300hoDhVzfoDQaEetYjCo1drR1RmdEN1SIzXdHOHfubjA3ZZRbrF_AJnNKpRRoIwzu1VayOhRmdy1qVSQZq_tENF4VrQFycEL7DhG7JLoXC4T2p1urwMlsw"}` - - jws, err := ParseSigned(input) - if err != nil { - t.Error("Unable to parse valid message.") - } - if len(jws.Signatures) != 1 { - t.Error("Too many or too few signatures.") - } - sig := jws.Signatures[0] - if sig.Header.JsonWebKey == nil { - t.Error("No JWK in signature header.") - } - payload, err := jws.Verify(sig.Header.JsonWebKey) - if err != nil { - t.Error(fmt.Sprintf("Signature did not validate: %v", err)) - } - expected := "{\"contact\":[\"mailto:foo@bar.com\"]}" - if string(payload) != expected { - t.Error(fmt.Sprintf("Payload was incorrect: '%s' should have been '%s'", string(payload), expected)) - } -} - -// Test vectors generated with nimbus-jose-jwt -func TestSampleNimbusJWSMessagesRSA(t *testing.T) { - rsaPublicKey, err := LoadPublicKey(fromBase64Bytes(` - MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3aLSGwbeX0ZA2Ha+EvELaIFGzO - 91+Q15JQc/tdGdCgGW3XAbrh7ZUhDh1XKzbs+UOQxqn3Eq4YOx18IG0WsJSuCaHQIxnDlZ - t/GP8WLwjMC0izlJLm2SyfM/EEoNpmTC3w6MQ2dHK7SZ9Zoq+sKijQd+V7CYdr8zHMpDrd - NKoEcR0HjmvzzdMoUChhkGH5TaNbZyollULTggepaYUKS8QphqdSDMWiSetKG+g6V87lv6 - CVYyK1FF6g7Esp5OOj5pNn3/bmF+7V+b7TvK91NCIlURCjE9toRgNoIP4TDnWRn/vvfZ3G - zNrtWmlizqz3r5KdvIs71ahWgMUSD4wfazrwIDAQAB`)) - if err != nil { - panic(err) - } - - rsaSampleMessages := []string{ - "eyJhbGciOiJSUzI1NiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.YHX849fvekz6wJGeyqnQhFqyHFcUXNJKj3o2w3ddR46YLlsCopUJrlifRU_ZuTWzpYxt5oC--T2eoqMhlCvltSWrE5_1_EumqiMfAYsZULx9E6Jns7q3w7mttonYFSIh7aR3-yg2HMMfTCgoAY1y_AZ4VjXwHDcZ5gu1oZDYgvZF4uXtCmwT6e5YtR1m8abiWPF8BgoTG_BD3KV6ClLj_QQiNFdfdxAMDw7vKVOKG1T7BFtz6cDs2Q3ILS4To5E2IjcVSSYS8mi77EitCrWmrqbK_G3WCdKeUFGnMnyuKXaCDy_7FLpAZ6Z5RomRr5iskXeJZdZqIKcJV8zl4fpsPA", - "eyJhbGciOiJSUzM4NCJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.meyfoOTjAAjXHFYiNlU7EEnsYtbeUYeEglK6BL_cxISEr2YAGLr1Gwnn2HnucTnH6YilyRio7ZC1ohy_ZojzmaljPHqpr8kn1iqNFu9nFE2M16ZPgJi38-PGzppcDNliyzOQO-c7L-eA-v8Gfww5uyRaOJdiWg-hUJmeGBIngPIeLtSVmhJtz8oTeqeNdUOqQv7f7VRCuvagLhW1PcEM91VUS-gS0WEUXoXWZ2lp91No0v1O24izgX3__FKiX_16XhrOfAgJ82F61vjbTIQYwhexHPZyYTlXYt_scNRzFGhSKeGFin4zVdFLOXWJqKWdUd5IrDP5Nya3FSoWbWDXAg", - "eyJhbGciOiJSUzUxMiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.rQPz0PDh8KyE2AX6JorgI0MLwv-qi1tcWlz6tuZuWQG1hdrlzq5tR1tQg1evYNc_SDDX87DWTSKXT7JEqhKoFixLfZa13IJrOc7FB8r5ZLx7OwOBC4F--OWrvxMA9Y3MTJjPN3FemQePUo-na2vNUZv-YgkcbuOgbO3hTxwQ7j1JGuqy-YutXOFnccdXvntp3t8zYZ4Mg1It_IyL9pzgGqHIEmMV1pCFGHsDa-wStB4ffmdhrADdYZc0q_SvxUdobyC_XzZCz9ENzGIhgwYxyyrqg7kjqUGoKmCLmoSlUFW7goTk9IC5SXdUyLPuESxOWNfHoRClGav230GYjPFQFA", - "eyJhbGciOiJQUzI1NiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.UTtxjsv_6x4CdlAmZfAW6Lun3byMjJbcwRp_OlPH2W4MZaZar7aql052mIB_ddK45O9VUz2aphYVRvKPZY8WHmvlTUU30bk0z_cDJRYB9eIJVMOiRCYj0oNkz1iEZqsP0YgngxwuUDv4Q4A6aJ0Bo5E_rZo3AnrVHMHUjPp_ZRRSBFs30tQma1qQ0ApK4Gxk0XYCYAcxIv99e78vldVRaGzjEZmQeAVZx4tGcqZP20vG1L84nlhSGnOuZ0FhR8UjRFLXuob6M7EqtMRoqPgRYw47EI3fYBdeSivAg98E5S8R7R1NJc7ef-l03RvfUSY0S3_zBq_4PlHK6A-2kHb__w", - "eyJhbGciOiJSUzM4NCJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.meyfoOTjAAjXHFYiNlU7EEnsYtbeUYeEglK6BL_cxISEr2YAGLr1Gwnn2HnucTnH6YilyRio7ZC1ohy_ZojzmaljPHqpr8kn1iqNFu9nFE2M16ZPgJi38-PGzppcDNliyzOQO-c7L-eA-v8Gfww5uyRaOJdiWg-hUJmeGBIngPIeLtSVmhJtz8oTeqeNdUOqQv7f7VRCuvagLhW1PcEM91VUS-gS0WEUXoXWZ2lp91No0v1O24izgX3__FKiX_16XhrOfAgJ82F61vjbTIQYwhexHPZyYTlXYt_scNRzFGhSKeGFin4zVdFLOXWJqKWdUd5IrDP5Nya3FSoWbWDXAg", - "eyJhbGciOiJSUzUxMiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.rQPz0PDh8KyE2AX6JorgI0MLwv-qi1tcWlz6tuZuWQG1hdrlzq5tR1tQg1evYNc_SDDX87DWTSKXT7JEqhKoFixLfZa13IJrOc7FB8r5ZLx7OwOBC4F--OWrvxMA9Y3MTJjPN3FemQePUo-na2vNUZv-YgkcbuOgbO3hTxwQ7j1JGuqy-YutXOFnccdXvntp3t8zYZ4Mg1It_IyL9pzgGqHIEmMV1pCFGHsDa-wStB4ffmdhrADdYZc0q_SvxUdobyC_XzZCz9ENzGIhgwYxyyrqg7kjqUGoKmCLmoSlUFW7goTk9IC5SXdUyLPuESxOWNfHoRClGav230GYjPFQFA", - } - - for _, msg := range rsaSampleMessages { - obj, err := ParseSigned(msg) - if err != nil { - t.Error("unable to parse message", msg, err) - continue - } - payload, err := obj.Verify(rsaPublicKey) - if err != nil { - t.Error("unable to verify message", msg, err) - continue - } - if string(payload) != "Lorem ipsum dolor sit amet" { - t.Error("payload is not what we expected for msg", msg) - } - } -} - -// Test vectors generated with nimbus-jose-jwt -func TestSampleNimbusJWSMessagesEC(t *testing.T) { - ecPublicKeyP256, err := LoadPublicKey(fromBase64Bytes("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIg62jq6FyL1otEj9Up7S35BUrwGF9TVrAzrrY1rHUKZqYIGEg67u/imjgadVcr7y9Q32I0gB8W8FHqbqt696rA==")) - if err != nil { - panic(err) - } - ecPublicKeyP384, err := LoadPublicKey(fromBase64Bytes("MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEPXsVlqCtN2oTY+F+hFZm3M0ldYpb7IeeJM5wYmT0k1RaqzBFDhDMNnYK5Q5x+OyssZrAtHgYDFw02AVJhhng/eHRp7mqmL/vI3wbxJtrLKYldIbBA+9fYBQcKeibjlu5")) - if err != nil { - panic(err) - } - ecPublicKeyP521, err := LoadPublicKey(fromBase64Bytes("MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAa2w3MMJ5FWD6tSf68G+Wy5jIhWXOD3IA7pE5IC/myQzo1lWcD8KS57SM6nm4POtPcxyLmDhL7FLuh8DKoIZyvtAAdK8+tOQP7XXRlT2bkvzIuazp05It3TAPu00YzTIpKfDlc19Y1lvf7etrbFqhShD92B+hHmhT4ddrdbPCBDW8hvU=")) - if err != nil { - panic(err) - } - - ecPublicKeys := []interface{}{ecPublicKeyP256, ecPublicKeyP384, ecPublicKeyP521} - - ecSampleMessages := []string{ - "eyJhbGciOiJFUzI1NiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.MEWJVlvGRQyzMEGOYm4rwuiwxrX-6LjnlbaRDAuhwmnBm2Gtn7pRpGXRTMFZUXsSGDz2L1p-Hz1qn8j9bFIBtQ", - "eyJhbGciOiJFUzM4NCJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.nbdjPnJPYQtVNNdBIx8-KbFKplTxrz-hnW5UNhYUY7SBkwHK4NZnqc2Lv4DXoA0aWHq9eiypgOh1kmyPWGEmqKAHUx0xdIEkBoHk3ZsbmhOQuq2jL_wcMUG6nTWNhLrB", - "eyJhbGciOiJFUzUxMiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.AeYNFC1rwIgQv-5fwd8iRyYzvTaSCYTEICepgu9gRId-IW99kbSVY7yH0MvrQnqI-a0L8zwKWDR35fW5dukPAYRkADp3Y1lzqdShFcEFziUVGo46vqbiSajmKFrjBktJcCsfjKSaLHwxErF-T10YYPCQFHWb2nXJOOI3CZfACYqgO84g", - } - - for i, msg := range ecSampleMessages { - obj, err := ParseSigned(msg) - if err != nil { - t.Error("unable to parse message", msg, err) - continue - } - payload, err := obj.Verify(ecPublicKeys[i]) - if err != nil { - t.Error("unable to verify message", msg, err) - continue - } - if string(payload) != "Lorem ipsum dolor sit amet" { - t.Error("payload is not what we expected for msg", msg) - } - } -} - -// Test vectors generated with nimbus-jose-jwt -func TestSampleNimbusJWSMessagesHMAC(t *testing.T) { - hmacTestKey := fromHexBytes("DF1FA4F36FFA7FC42C81D4B3C033928D") - - hmacSampleMessages := []string{ - "eyJhbGciOiJIUzI1NiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.W5tc_EUhxexcvLYEEOckyyvdb__M5DQIVpg6Nmk1XGM", - "eyJhbGciOiJIUzM4NCJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.sBu44lXOJa4Nd10oqOdYH2uz3lxlZ6o32QSGHaoGdPtYTDG5zvSja6N48CXKqdAh", - "eyJhbGciOiJIUzUxMiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.M0yR4tmipsORIix-BitIbxEPGaxPchDfj8UNOpKuhDEfnb7URjGvCKn4nOlyQ1z9mG1FKbwnqR1hOVAWSzAU_w", - } - - for _, msg := range hmacSampleMessages { - obj, err := ParseSigned(msg) - if err != nil { - t.Error("unable to parse message", msg, err) - continue - } - payload, err := obj.Verify(hmacTestKey) - if err != nil { - t.Error("unable to verify message", msg, err) - continue - } - if string(payload) != "Lorem ipsum dolor sit amet" { - t.Error("payload is not what we expected for msg", msg) - } - } -} - -// Test vectors generated with nimbus-jose-jwt -func TestErrorMissingPayloadJWS(t *testing.T) { - _, err := (&rawJsonWebSignature{}).sanitized() - if err == nil { - t.Error("was able to parse message with missing payload") - } - if !strings.Contains(err.Error(), "missing payload") { - t.Errorf("unexpected error message, should contain 'missing payload': %s", err) - } -} diff --git a/vendor/gopkg.in/square/go-jose.v1/shared.go b/vendor/gopkg.in/square/go-jose.v1/shared.go deleted file mode 100644 index 9d895a91..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/shared.go +++ /dev/null @@ -1,224 +0,0 @@ -/*- - * Copyright 2014 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 jose - -import ( - "crypto/elliptic" - "errors" - "fmt" -) - -// KeyAlgorithm represents a key management algorithm. -type KeyAlgorithm string - -// SignatureAlgorithm represents a signature (or MAC) algorithm. -type SignatureAlgorithm string - -// ContentEncryption represents a content encryption algorithm. -type ContentEncryption string - -// CompressionAlgorithm represents an algorithm used for plaintext compression. -type CompressionAlgorithm string - -var ( - // ErrCryptoFailure represents an error in cryptographic primitive. This - // occurs when, for example, a message had an invalid authentication tag or - // could not be decrypted. - ErrCryptoFailure = errors.New("square/go-jose: error in cryptographic primitive") - - // ErrUnsupportedAlgorithm indicates that a selected algorithm is not - // supported. This occurs when trying to instantiate an encrypter for an - // algorithm that is not yet implemented. - ErrUnsupportedAlgorithm = errors.New("square/go-jose: unknown/unsupported algorithm") - - // ErrUnsupportedKeyType indicates that the given key type/format is not - // supported. This occurs when trying to instantiate an encrypter and passing - // it a key of an unrecognized type or with unsupported parameters, such as - // an RSA private key with more than two primes. - ErrUnsupportedKeyType = errors.New("square/go-jose: unsupported key type/format") - - // ErrNotSupported serialization of object is not supported. This occurs when - // trying to compact-serialize an object which can't be represented in - // compact form. - ErrNotSupported = errors.New("square/go-jose: compact serialization not supported for object") - - // ErrUnprotectedNonce indicates that while parsing a JWS or JWE object, a - // nonce header parameter was included in an unprotected header object. - ErrUnprotectedNonce = errors.New("square/go-jose: Nonce parameter included in unprotected header") -) - -// Key management algorithms -const ( - RSA1_5 = KeyAlgorithm("RSA1_5") // RSA-PKCS1v1.5 - RSA_OAEP = KeyAlgorithm("RSA-OAEP") // RSA-OAEP-SHA1 - RSA_OAEP_256 = KeyAlgorithm("RSA-OAEP-256") // RSA-OAEP-SHA256 - A128KW = KeyAlgorithm("A128KW") // AES key wrap (128) - A192KW = KeyAlgorithm("A192KW") // AES key wrap (192) - A256KW = KeyAlgorithm("A256KW") // AES key wrap (256) - DIRECT = KeyAlgorithm("dir") // Direct encryption - ECDH_ES = KeyAlgorithm("ECDH-ES") // ECDH-ES - ECDH_ES_A128KW = KeyAlgorithm("ECDH-ES+A128KW") // ECDH-ES + AES key wrap (128) - ECDH_ES_A192KW = KeyAlgorithm("ECDH-ES+A192KW") // ECDH-ES + AES key wrap (192) - ECDH_ES_A256KW = KeyAlgorithm("ECDH-ES+A256KW") // ECDH-ES + AES key wrap (256) - A128GCMKW = KeyAlgorithm("A128GCMKW") // AES-GCM key wrap (128) - A192GCMKW = KeyAlgorithm("A192GCMKW") // AES-GCM key wrap (192) - A256GCMKW = KeyAlgorithm("A256GCMKW") // AES-GCM key wrap (256) - PBES2_HS256_A128KW = KeyAlgorithm("PBES2-HS256+A128KW") // PBES2 + HMAC-SHA256 + AES key wrap (128) - PBES2_HS384_A192KW = KeyAlgorithm("PBES2-HS384+A192KW") // PBES2 + HMAC-SHA384 + AES key wrap (192) - PBES2_HS512_A256KW = KeyAlgorithm("PBES2-HS512+A256KW") // PBES2 + HMAC-SHA512 + AES key wrap (256) -) - -// Signature algorithms -const ( - HS256 = SignatureAlgorithm("HS256") // HMAC using SHA-256 - HS384 = SignatureAlgorithm("HS384") // HMAC using SHA-384 - HS512 = SignatureAlgorithm("HS512") // HMAC using SHA-512 - RS256 = SignatureAlgorithm("RS256") // RSASSA-PKCS-v1.5 using SHA-256 - RS384 = SignatureAlgorithm("RS384") // RSASSA-PKCS-v1.5 using SHA-384 - RS512 = SignatureAlgorithm("RS512") // RSASSA-PKCS-v1.5 using SHA-512 - ES256 = SignatureAlgorithm("ES256") // ECDSA using P-256 and SHA-256 - ES384 = SignatureAlgorithm("ES384") // ECDSA using P-384 and SHA-384 - ES512 = SignatureAlgorithm("ES512") // ECDSA using P-521 and SHA-512 - PS256 = SignatureAlgorithm("PS256") // RSASSA-PSS using SHA256 and MGF1-SHA256 - PS384 = SignatureAlgorithm("PS384") // RSASSA-PSS using SHA384 and MGF1-SHA384 - PS512 = SignatureAlgorithm("PS512") // RSASSA-PSS using SHA512 and MGF1-SHA512 -) - -// Content encryption algorithms -const ( - A128CBC_HS256 = ContentEncryption("A128CBC-HS256") // AES-CBC + HMAC-SHA256 (128) - A192CBC_HS384 = ContentEncryption("A192CBC-HS384") // AES-CBC + HMAC-SHA384 (192) - A256CBC_HS512 = ContentEncryption("A256CBC-HS512") // AES-CBC + HMAC-SHA512 (256) - A128GCM = ContentEncryption("A128GCM") // AES-GCM (128) - A192GCM = ContentEncryption("A192GCM") // AES-GCM (192) - A256GCM = ContentEncryption("A256GCM") // AES-GCM (256) -) - -// Compression algorithms -const ( - NONE = CompressionAlgorithm("") // No compression - DEFLATE = CompressionAlgorithm("DEF") // DEFLATE (RFC 1951) -) - -// rawHeader represents the JOSE header for JWE/JWS objects (used for parsing). -type rawHeader struct { - Alg string `json:"alg,omitempty"` - Enc ContentEncryption `json:"enc,omitempty"` - Zip CompressionAlgorithm `json:"zip,omitempty"` - Crit []string `json:"crit,omitempty"` - Apu *byteBuffer `json:"apu,omitempty"` - Apv *byteBuffer `json:"apv,omitempty"` - Epk *JsonWebKey `json:"epk,omitempty"` - Iv *byteBuffer `json:"iv,omitempty"` - Tag *byteBuffer `json:"tag,omitempty"` - Jwk *JsonWebKey `json:"jwk,omitempty"` - Kid string `json:"kid,omitempty"` - Nonce string `json:"nonce,omitempty"` -} - -// JoseHeader represents the read-only JOSE header for JWE/JWS objects. -type JoseHeader struct { - KeyID string - JsonWebKey *JsonWebKey - Algorithm string - Nonce string -} - -// sanitized produces a cleaned-up header object from the raw JSON. -func (parsed rawHeader) sanitized() JoseHeader { - return JoseHeader{ - KeyID: parsed.Kid, - JsonWebKey: parsed.Jwk, - Algorithm: parsed.Alg, - Nonce: parsed.Nonce, - } -} - -// Merge headers from src into dst, giving precedence to headers from l. -func (dst *rawHeader) merge(src *rawHeader) { - if src == nil { - return - } - - if dst.Alg == "" { - dst.Alg = src.Alg - } - if dst.Enc == "" { - dst.Enc = src.Enc - } - if dst.Zip == "" { - dst.Zip = src.Zip - } - if dst.Crit == nil { - dst.Crit = src.Crit - } - if dst.Crit == nil { - dst.Crit = src.Crit - } - if dst.Apu == nil { - dst.Apu = src.Apu - } - if dst.Apv == nil { - dst.Apv = src.Apv - } - if dst.Epk == nil { - dst.Epk = src.Epk - } - if dst.Iv == nil { - dst.Iv = src.Iv - } - if dst.Tag == nil { - dst.Tag = src.Tag - } - if dst.Kid == "" { - dst.Kid = src.Kid - } - if dst.Jwk == nil { - dst.Jwk = src.Jwk - } - if dst.Nonce == "" { - dst.Nonce = src.Nonce - } -} - -// Get JOSE name of curve -func curveName(crv elliptic.Curve) (string, error) { - switch crv { - case elliptic.P256(): - return "P-256", nil - case elliptic.P384(): - return "P-384", nil - case elliptic.P521(): - return "P-521", nil - default: - return "", fmt.Errorf("square/go-jose: unsupported/unknown elliptic curve") - } -} - -// Get size of curve in bytes -func curveSize(crv elliptic.Curve) int { - bits := crv.Params().BitSize - - div := bits / 8 - mod := bits % 8 - - if mod == 0 { - return div - } - - return div + 1 -} diff --git a/vendor/gopkg.in/square/go-jose.v1/signing.go b/vendor/gopkg.in/square/go-jose.v1/signing.go deleted file mode 100644 index c6ed2c92..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/signing.go +++ /dev/null @@ -1,218 +0,0 @@ -/*- - * Copyright 2014 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 jose - -import ( - "crypto/ecdsa" - "crypto/rsa" - "fmt" -) - -// NonceSource represents a source of random nonces to go into JWS objects -type NonceSource interface { - Nonce() (string, error) -} - -// Signer represents a signer which takes a payload and produces a signed JWS object. -type Signer interface { - Sign(payload []byte) (*JsonWebSignature, error) - SetNonceSource(source NonceSource) - SetEmbedJwk(embed bool) -} - -// MultiSigner represents a signer which supports multiple recipients. -type MultiSigner interface { - Sign(payload []byte) (*JsonWebSignature, error) - SetNonceSource(source NonceSource) - SetEmbedJwk(embed bool) - AddRecipient(alg SignatureAlgorithm, signingKey interface{}) error -} - -type payloadSigner interface { - signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) -} - -type payloadVerifier interface { - verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error -} - -type genericSigner struct { - recipients []recipientSigInfo - nonceSource NonceSource - embedJwk bool -} - -type recipientSigInfo struct { - sigAlg SignatureAlgorithm - keyID string - publicKey *JsonWebKey - signer payloadSigner -} - -// NewSigner creates an appropriate signer based on the key type -func NewSigner(alg SignatureAlgorithm, signingKey interface{}) (Signer, error) { - // NewMultiSigner never fails (currently) - signer := NewMultiSigner() - - err := signer.AddRecipient(alg, signingKey) - if err != nil { - return nil, err - } - - return signer, nil -} - -// NewMultiSigner creates a signer for multiple recipients -func NewMultiSigner() MultiSigner { - return &genericSigner{ - recipients: []recipientSigInfo{}, - embedJwk: true, - } -} - -// newVerifier creates a verifier based on the key type -func newVerifier(verificationKey interface{}) (payloadVerifier, error) { - switch verificationKey := verificationKey.(type) { - case *rsa.PublicKey: - return &rsaEncrypterVerifier{ - publicKey: verificationKey, - }, nil - case *ecdsa.PublicKey: - return &ecEncrypterVerifier{ - publicKey: verificationKey, - }, nil - case []byte: - return &symmetricMac{ - key: verificationKey, - }, nil - case *JsonWebKey: - return newVerifier(verificationKey.Key) - default: - return nil, ErrUnsupportedKeyType - } -} - -func (ctx *genericSigner) AddRecipient(alg SignatureAlgorithm, signingKey interface{}) error { - recipient, err := makeJWSRecipient(alg, signingKey) - if err != nil { - return err - } - - ctx.recipients = append(ctx.recipients, recipient) - return nil -} - -func makeJWSRecipient(alg SignatureAlgorithm, signingKey interface{}) (recipientSigInfo, error) { - switch signingKey := signingKey.(type) { - case *rsa.PrivateKey: - return newRSASigner(alg, signingKey) - case *ecdsa.PrivateKey: - return newECDSASigner(alg, signingKey) - case []byte: - return newSymmetricSigner(alg, signingKey) - case *JsonWebKey: - recipient, err := makeJWSRecipient(alg, signingKey.Key) - if err != nil { - return recipientSigInfo{}, err - } - recipient.keyID = signingKey.KeyID - return recipient, nil - default: - return recipientSigInfo{}, ErrUnsupportedKeyType - } -} - -func (ctx *genericSigner) Sign(payload []byte) (*JsonWebSignature, error) { - obj := &JsonWebSignature{} - obj.payload = payload - obj.Signatures = make([]Signature, len(ctx.recipients)) - - for i, recipient := range ctx.recipients { - protected := &rawHeader{ - Alg: string(recipient.sigAlg), - } - - if recipient.publicKey != nil && ctx.embedJwk { - protected.Jwk = recipient.publicKey - } - if recipient.keyID != "" { - protected.Kid = recipient.keyID - } - - if ctx.nonceSource != nil { - nonce, err := ctx.nonceSource.Nonce() - if err != nil { - return nil, fmt.Errorf("square/go-jose: Error generating nonce: %v", err) - } - protected.Nonce = nonce - } - - serializedProtected := mustSerializeJSON(protected) - - input := []byte(fmt.Sprintf("%s.%s", - base64URLEncode(serializedProtected), - base64URLEncode(payload))) - - signatureInfo, err := recipient.signer.signPayload(input, recipient.sigAlg) - if err != nil { - return nil, err - } - - signatureInfo.protected = protected - obj.Signatures[i] = signatureInfo - } - - return obj, nil -} - -// SetNonceSource provides or updates a nonce pool to the first recipients. -// After this method is called, the signer will consume one nonce per -// signature, returning an error it is unable to get a nonce. -func (ctx *genericSigner) SetNonceSource(source NonceSource) { - ctx.nonceSource = source -} - -// SetEmbedJwk specifies if the signing key should be embedded in the protected header, -// if any. It defaults to 'true'. -func (ctx *genericSigner) SetEmbedJwk(embed bool) { - ctx.embedJwk = embed -} - -// Verify validates the signature on the object and returns the payload. -func (obj JsonWebSignature) Verify(verificationKey interface{}) ([]byte, error) { - verifier, err := newVerifier(verificationKey) - if err != nil { - return nil, err - } - - for _, signature := range obj.Signatures { - headers := signature.mergedHeaders() - if len(headers.Crit) > 0 { - // Unsupported crit header - continue - } - - input := obj.computeAuthData(&signature) - alg := SignatureAlgorithm(headers.Alg) - err := verifier.verifyPayload(input, signature.Signature, alg) - if err == nil { - return obj.payload, nil - } - } - - return nil, ErrCryptoFailure -} diff --git a/vendor/gopkg.in/square/go-jose.v1/signing_test.go b/vendor/gopkg.in/square/go-jose.v1/signing_test.go deleted file mode 100644 index 981770d5..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/signing_test.go +++ /dev/null @@ -1,447 +0,0 @@ -/*- - * Copyright 2014 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 jose - -import ( - "bytes" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "fmt" - "io" - "testing" -) - -type staticNonceSource string - -func (sns staticNonceSource) Nonce() (string, error) { - return string(sns), nil -} - -func RoundtripJWS(sigAlg SignatureAlgorithm, serializer func(*JsonWebSignature) (string, error), corrupter func(*JsonWebSignature), signingKey interface{}, verificationKey interface{}, nonce string) error { - signer, err := NewSigner(sigAlg, signingKey) - if err != nil { - return fmt.Errorf("error on new signer: %s", err) - } - - if nonce != "" { - signer.SetNonceSource(staticNonceSource(nonce)) - } - - input := []byte("Lorem ipsum dolor sit amet") - obj, err := signer.Sign(input) - if err != nil { - return fmt.Errorf("error on sign: %s", err) - } - - msg, err := serializer(obj) - if err != nil { - return fmt.Errorf("error on serialize: %s", err) - } - - obj, err = ParseSigned(msg) - if err != nil { - return fmt.Errorf("error on parse: %s", err) - } - - // (Maybe) mangle the object - corrupter(obj) - - output, err := obj.Verify(verificationKey) - if err != nil { - return fmt.Errorf("error on verify: %s", err) - } - - // Check that verify works with embedded keys (if present) - for i, sig := range obj.Signatures { - if sig.Header.JsonWebKey != nil { - _, err = obj.Verify(sig.Header.JsonWebKey) - if err != nil { - return fmt.Errorf("error on verify with embedded key %d: %s", i, err) - } - } - - // Check that the nonce correctly round-tripped (if present) - if sig.Header.Nonce != nonce { - return fmt.Errorf("Incorrect nonce returned: [%s]", sig.Header.Nonce) - } - } - - if bytes.Compare(output, input) != 0 { - return fmt.Errorf("input/output do not match, got '%s', expected '%s'", output, input) - } - - return nil -} - -func TestRoundtripsJWS(t *testing.T) { - // Test matrix - sigAlgs := []SignatureAlgorithm{RS256, RS384, RS512, PS256, PS384, PS512, HS256, HS384, HS512, ES256, ES384, ES512} - - serializers := []func(*JsonWebSignature) (string, error){ - func(obj *JsonWebSignature) (string, error) { return obj.CompactSerialize() }, - func(obj *JsonWebSignature) (string, error) { return obj.FullSerialize(), nil }, - } - - corrupter := func(obj *JsonWebSignature) {} - - for _, alg := range sigAlgs { - signingKey, verificationKey := GenerateSigningTestKey(alg) - - for i, serializer := range serializers { - err := RoundtripJWS(alg, serializer, corrupter, signingKey, verificationKey, "test_nonce") - if err != nil { - t.Error(err, alg, i) - } - } - } -} - -func TestRoundtripsJWSCorruptSignature(t *testing.T) { - // Test matrix - sigAlgs := []SignatureAlgorithm{RS256, RS384, RS512, PS256, PS384, PS512, HS256, HS384, HS512, ES256, ES384, ES512} - - serializers := []func(*JsonWebSignature) (string, error){ - func(obj *JsonWebSignature) (string, error) { return obj.CompactSerialize() }, - func(obj *JsonWebSignature) (string, error) { return obj.FullSerialize(), nil }, - } - - corrupters := []func(*JsonWebSignature){ - func(obj *JsonWebSignature) { - // Changes bytes in signature - obj.Signatures[0].Signature[10]++ - }, - func(obj *JsonWebSignature) { - // Set totally invalid signature - obj.Signatures[0].Signature = []byte("###") - }, - } - - // Test all different configurations - for _, alg := range sigAlgs { - signingKey, verificationKey := GenerateSigningTestKey(alg) - - for i, serializer := range serializers { - for j, corrupter := range corrupters { - err := RoundtripJWS(alg, serializer, corrupter, signingKey, verificationKey, "test_nonce") - if err == nil { - t.Error("failed to detect corrupt signature", err, alg, i, j) - } - } - } - } -} - -func TestSignerWithBrokenRand(t *testing.T) { - sigAlgs := []SignatureAlgorithm{RS256, RS384, RS512, PS256, PS384, PS512} - - serializer := func(obj *JsonWebSignature) (string, error) { return obj.CompactSerialize() } - corrupter := func(obj *JsonWebSignature) {} - - // Break rand reader - readers := []func() io.Reader{ - // Totally broken - func() io.Reader { return bytes.NewReader([]byte{}) }, - // Not enough bytes - func() io.Reader { return io.LimitReader(rand.Reader, 20) }, - } - - defer resetRandReader() - - for _, alg := range sigAlgs { - signingKey, verificationKey := GenerateSigningTestKey(alg) - for i, getReader := range readers { - randReader = getReader() - err := RoundtripJWS(alg, serializer, corrupter, signingKey, verificationKey, "test_nonce") - if err == nil { - t.Error("signer should fail if rand is broken", alg, i) - } - } - } -} - -func TestJWSInvalidKey(t *testing.T) { - signingKey0, verificationKey0 := GenerateSigningTestKey(RS256) - _, verificationKey1 := GenerateSigningTestKey(ES256) - - signer, err := NewSigner(RS256, signingKey0) - if err != nil { - panic(err) - } - - input := []byte("Lorem ipsum dolor sit amet") - obj, err := signer.Sign(input) - if err != nil { - panic(err) - } - - // Must work with correct key - _, err = obj.Verify(verificationKey0) - if err != nil { - t.Error("error on verify", err) - } - - // Must not work with incorrect key - _, err = obj.Verify(verificationKey1) - if err == nil { - t.Error("verification should fail with incorrect key") - } - - // Must not work with invalid key - _, err = obj.Verify("") - if err == nil { - t.Error("verification should fail with incorrect key") - } -} - -func TestMultiRecipientJWS(t *testing.T) { - signer := NewMultiSigner() - - sharedKey := []byte{ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - } - - signer.AddRecipient(RS256, rsaTestKey) - signer.AddRecipient(HS384, sharedKey) - - input := []byte("Lorem ipsum dolor sit amet") - obj, err := signer.Sign(input) - if err != nil { - t.Error("error on sign: ", err) - return - } - - _, err = obj.CompactSerialize() - if err == nil { - t.Error("message with multiple recipient was compact serialized") - } - - msg := obj.FullSerialize() - - obj, err = ParseSigned(msg) - if err != nil { - t.Error("error on parse: ", err) - return - } - - output, err := obj.Verify(&rsaTestKey.PublicKey) - if err != nil { - t.Error("error on verify: ", err) - return - } - - if bytes.Compare(output, input) != 0 { - t.Error("input/output do not match", output, input) - return - } - - output, err = obj.Verify(sharedKey) - if err != nil { - t.Error("error on verify: ", err) - return - } - - if bytes.Compare(output, input) != 0 { - t.Error("input/output do not match", output, input) - return - } -} - -func GenerateSigningTestKey(sigAlg SignatureAlgorithm) (sig, ver interface{}) { - switch sigAlg { - case RS256, RS384, RS512, PS256, PS384, PS512: - sig = rsaTestKey - ver = &rsaTestKey.PublicKey - case HS256, HS384, HS512: - sig, _, _ = randomKeyGenerator{size: 16}.genKey() - ver = sig - case ES256: - key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - sig = key - ver = &key.PublicKey - case ES384: - key, _ := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) - sig = key - ver = &key.PublicKey - case ES512: - key, _ := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) - sig = key - ver = &key.PublicKey - default: - panic("Must update test case") - } - - return -} - -func TestInvalidSignerAlg(t *testing.T) { - _, err := NewSigner("XYZ", nil) - if err == nil { - t.Error("should not accept invalid algorithm") - } - - _, err = NewSigner("XYZ", []byte{}) - if err == nil { - t.Error("should not accept invalid algorithm") - } -} - -func TestInvalidJWS(t *testing.T) { - signer, err := NewSigner(PS256, rsaTestKey) - if err != nil { - panic(err) - } - - obj, err := signer.Sign([]byte("Lorem ipsum dolor sit amet")) - obj.Signatures[0].header = &rawHeader{ - Crit: []string{"TEST"}, - } - - _, err = obj.Verify(&rsaTestKey.PublicKey) - if err == nil { - t.Error("should not verify message with unknown crit header") - } - - // Try without alg header - obj.Signatures[0].protected = &rawHeader{} - obj.Signatures[0].header = &rawHeader{} - - _, err = obj.Verify(&rsaTestKey.PublicKey) - if err == nil { - t.Error("should not verify message with missing headers") - } -} - -func TestSignerKid(t *testing.T) { - kid := "DEADBEEF" - payload := []byte("Lorem ipsum dolor sit amet") - - key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - t.Error("problem generating test signing key", err) - } - - basejwk := JsonWebKey{Key: key} - jsonbar, err := basejwk.MarshalJSON() - if err != nil { - t.Error("problem marshalling base JWK", err) - } - - var jsonmsi map[string]interface{} - err = UnmarshalJSON(jsonbar, &jsonmsi) - if err != nil { - t.Error("problem unmarshalling base JWK", err) - } - jsonmsi["kid"] = kid - jsonbar2, err := MarshalJSON(jsonmsi) - if err != nil { - t.Error("problem marshalling kided JWK", err) - } - - var jwk JsonWebKey - err = jwk.UnmarshalJSON(jsonbar2) - if err != nil { - t.Error("problem unmarshalling kided JWK", err) - } - - signer, err := NewSigner(ES256, &jwk) - if err != nil { - t.Error("problem creating signer", err) - } - signed, err := signer.Sign(payload) - - serialized := signed.FullSerialize() - - parsed, err := ParseSigned(serialized) - if err != nil { - t.Error("problem parsing signed object", err) - } - - if parsed.Signatures[0].Header.KeyID != kid { - t.Error("KeyID did not survive trip") - } -} - -func TestEmbedJwk(t *testing.T) { - var payload = []byte("Lorem ipsum dolor sit amet") - key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - t.Error("Failed to generate key") - } - - signer, err := NewSigner(ES256, key) - if err != nil { - t.Error("Failed to create signer") - } - - object, err := signer.Sign(payload) - if err != nil { - t.Error("Failed to sign payload") - } - - object, err = ParseSigned(object.FullSerialize()) - if err != nil { - t.Error("Failed to parse jws") - } - - if object.Signatures[0].protected.Jwk == nil { - t.Error("JWK isn't set in protected header") - } - - // Now sign it again, but don't embed JWK. - signer.SetEmbedJwk(false) - - object, err = signer.Sign(payload) - if err != nil { - t.Error("Failed to sign payload") - } - - object, err = ParseSigned(object.FullSerialize()) - if err != nil { - t.Error("Failed to parse jws") - } - - if object.Signatures[0].protected.Jwk != nil { - t.Error("JWK is set in protected header") - } -} - -func TestSignerWithJWKAndKeyID(t *testing.T) { - enc, err := NewSigner(HS256, &JsonWebKey{ - KeyID: "test-id", - Key: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, - }) - if err != nil { - t.Error(err) - } - - signed, _ := enc.Sign([]byte("Lorem ipsum dolor sit amet")) - - serialized1, _ := signed.CompactSerialize() - serialized2 := signed.FullSerialize() - - parsed1, _ := ParseSigned(serialized1) - parsed2, _ := ParseSigned(serialized2) - - if parsed1.Signatures[0].Header.KeyID != "test-id" { - t.Errorf("expected message to have key id from JWK, but found '%s' instead", parsed1.Signatures[0].Header.KeyID) - } - if parsed2.Signatures[0].Header.KeyID != "test-id" { - t.Errorf("expected message to have key id from JWK, but found '%s' instead", parsed2.Signatures[0].Header.KeyID) - } -} diff --git a/vendor/gopkg.in/square/go-jose.v1/symmetric.go b/vendor/gopkg.in/square/go-jose.v1/symmetric.go deleted file mode 100644 index 51f8cb39..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/symmetric.go +++ /dev/null @@ -1,349 +0,0 @@ -/*- - * Copyright 2014 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 jose - -import ( - "crypto/aes" - "crypto/cipher" - "crypto/hmac" - "crypto/rand" - "crypto/sha256" - "crypto/sha512" - "crypto/subtle" - "errors" - "hash" - "io" - - "gopkg.in/square/go-jose.v1/cipher" -) - -// Random reader (stubbed out in tests) -var randReader = rand.Reader - -// Dummy key cipher for shared symmetric key mode -type symmetricKeyCipher struct { - key []byte // Pre-shared content-encryption key -} - -// Signer/verifier for MAC modes -type symmetricMac struct { - key []byte -} - -// Input/output from an AEAD operation -type aeadParts struct { - iv, ciphertext, tag []byte -} - -// A content cipher based on an AEAD construction -type aeadContentCipher struct { - keyBytes int - authtagBytes int - getAead func(key []byte) (cipher.AEAD, error) -} - -// Random key generator -type randomKeyGenerator struct { - size int -} - -// Static key generator -type staticKeyGenerator struct { - key []byte -} - -// Create a new content cipher based on AES-GCM -func newAESGCM(keySize int) contentCipher { - return &aeadContentCipher{ - keyBytes: keySize, - authtagBytes: 16, - getAead: func(key []byte) (cipher.AEAD, error) { - aes, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - - return cipher.NewGCM(aes) - }, - } -} - -// Create a new content cipher based on AES-CBC+HMAC -func newAESCBC(keySize int) contentCipher { - return &aeadContentCipher{ - keyBytes: keySize * 2, - authtagBytes: 16, - getAead: func(key []byte) (cipher.AEAD, error) { - return josecipher.NewCBCHMAC(key, aes.NewCipher) - }, - } -} - -// Get an AEAD cipher object for the given content encryption algorithm -func getContentCipher(alg ContentEncryption) contentCipher { - switch alg { - case A128GCM: - return newAESGCM(16) - case A192GCM: - return newAESGCM(24) - case A256GCM: - return newAESGCM(32) - case A128CBC_HS256: - return newAESCBC(16) - case A192CBC_HS384: - return newAESCBC(24) - case A256CBC_HS512: - return newAESCBC(32) - default: - return nil - } -} - -// newSymmetricRecipient creates a JWE encrypter based on AES-GCM key wrap. -func newSymmetricRecipient(keyAlg KeyAlgorithm, key []byte) (recipientKeyInfo, error) { - switch keyAlg { - case DIRECT, A128GCMKW, A192GCMKW, A256GCMKW, A128KW, A192KW, A256KW: - default: - return recipientKeyInfo{}, ErrUnsupportedAlgorithm - } - - return recipientKeyInfo{ - keyAlg: keyAlg, - keyEncrypter: &symmetricKeyCipher{ - key: key, - }, - }, nil -} - -// newSymmetricSigner creates a recipientSigInfo based on the given key. -func newSymmetricSigner(sigAlg SignatureAlgorithm, key []byte) (recipientSigInfo, error) { - // Verify that key management algorithm is supported by this encrypter - switch sigAlg { - case HS256, HS384, HS512: - default: - return recipientSigInfo{}, ErrUnsupportedAlgorithm - } - - return recipientSigInfo{ - sigAlg: sigAlg, - signer: &symmetricMac{ - key: key, - }, - }, nil -} - -// Generate a random key for the given content cipher -func (ctx randomKeyGenerator) genKey() ([]byte, rawHeader, error) { - key := make([]byte, ctx.size) - _, err := io.ReadFull(randReader, key) - if err != nil { - return nil, rawHeader{}, err - } - - return key, rawHeader{}, nil -} - -// Key size for random generator -func (ctx randomKeyGenerator) keySize() int { - return ctx.size -} - -// Generate a static key (for direct mode) -func (ctx staticKeyGenerator) genKey() ([]byte, rawHeader, error) { - cek := make([]byte, len(ctx.key)) - copy(cek, ctx.key) - return cek, rawHeader{}, nil -} - -// Key size for static generator -func (ctx staticKeyGenerator) keySize() int { - return len(ctx.key) -} - -// Get key size for this cipher -func (ctx aeadContentCipher) keySize() int { - return ctx.keyBytes -} - -// Encrypt some data -func (ctx aeadContentCipher) encrypt(key, aad, pt []byte) (*aeadParts, error) { - // Get a new AEAD instance - aead, err := ctx.getAead(key) - if err != nil { - return nil, err - } - - // Initialize a new nonce - iv := make([]byte, aead.NonceSize()) - _, err = io.ReadFull(randReader, iv) - if err != nil { - return nil, err - } - - ciphertextAndTag := aead.Seal(nil, iv, pt, aad) - offset := len(ciphertextAndTag) - ctx.authtagBytes - - return &aeadParts{ - iv: iv, - ciphertext: ciphertextAndTag[:offset], - tag: ciphertextAndTag[offset:], - }, nil -} - -// Decrypt some data -func (ctx aeadContentCipher) decrypt(key, aad []byte, parts *aeadParts) ([]byte, error) { - aead, err := ctx.getAead(key) - if err != nil { - return nil, err - } - - return aead.Open(nil, parts.iv, append(parts.ciphertext, parts.tag...), aad) -} - -// Encrypt the content encryption key. -func (ctx *symmetricKeyCipher) encryptKey(cek []byte, alg KeyAlgorithm) (recipientInfo, error) { - switch alg { - case DIRECT: - return recipientInfo{ - header: &rawHeader{}, - }, nil - case A128GCMKW, A192GCMKW, A256GCMKW: - aead := newAESGCM(len(ctx.key)) - - parts, err := aead.encrypt(ctx.key, []byte{}, cek) - if err != nil { - return recipientInfo{}, err - } - - return recipientInfo{ - header: &rawHeader{ - Iv: newBuffer(parts.iv), - Tag: newBuffer(parts.tag), - }, - encryptedKey: parts.ciphertext, - }, nil - case A128KW, A192KW, A256KW: - block, err := aes.NewCipher(ctx.key) - if err != nil { - return recipientInfo{}, err - } - - jek, err := josecipher.KeyWrap(block, cek) - if err != nil { - return recipientInfo{}, err - } - - return recipientInfo{ - encryptedKey: jek, - header: &rawHeader{}, - }, nil - } - - return recipientInfo{}, ErrUnsupportedAlgorithm -} - -// Decrypt the content encryption key. -func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) { - switch KeyAlgorithm(headers.Alg) { - case DIRECT: - cek := make([]byte, len(ctx.key)) - copy(cek, ctx.key) - return cek, nil - case A128GCMKW, A192GCMKW, A256GCMKW: - aead := newAESGCM(len(ctx.key)) - - parts := &aeadParts{ - iv: headers.Iv.bytes(), - ciphertext: recipient.encryptedKey, - tag: headers.Tag.bytes(), - } - - cek, err := aead.decrypt(ctx.key, []byte{}, parts) - if err != nil { - return nil, err - } - - return cek, nil - case A128KW, A192KW, A256KW: - block, err := aes.NewCipher(ctx.key) - if err != nil { - return nil, err - } - - cek, err := josecipher.KeyUnwrap(block, recipient.encryptedKey) - if err != nil { - return nil, err - } - return cek, nil - } - - return nil, ErrUnsupportedAlgorithm -} - -// Sign the given payload -func (ctx symmetricMac) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) { - mac, err := ctx.hmac(payload, alg) - if err != nil { - return Signature{}, errors.New("square/go-jose: failed to compute hmac") - } - - return Signature{ - Signature: mac, - protected: &rawHeader{}, - }, nil -} - -// Verify the given payload -func (ctx symmetricMac) verifyPayload(payload []byte, mac []byte, alg SignatureAlgorithm) error { - expected, err := ctx.hmac(payload, alg) - if err != nil { - return errors.New("square/go-jose: failed to compute hmac") - } - - if len(mac) != len(expected) { - return errors.New("square/go-jose: invalid hmac") - } - - match := subtle.ConstantTimeCompare(mac, expected) - if match != 1 { - return errors.New("square/go-jose: invalid hmac") - } - - return nil -} - -// Compute the HMAC based on the given alg value -func (ctx symmetricMac) hmac(payload []byte, alg SignatureAlgorithm) ([]byte, error) { - var hash func() hash.Hash - - switch alg { - case HS256: - hash = sha256.New - case HS384: - hash = sha512.New384 - case HS512: - hash = sha512.New - default: - return nil, ErrUnsupportedAlgorithm - } - - hmac := hmac.New(hash, ctx.key) - - // According to documentation, Write() on hash never fails - _, _ = hmac.Write(payload) - return hmac.Sum(nil), nil -} diff --git a/vendor/gopkg.in/square/go-jose.v1/symmetric_test.go b/vendor/gopkg.in/square/go-jose.v1/symmetric_test.go deleted file mode 100644 index 67f535e3..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/symmetric_test.go +++ /dev/null @@ -1,131 +0,0 @@ -/*- - * Copyright 2014 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 jose - -import ( - "bytes" - "crypto/cipher" - "crypto/rand" - "io" - "testing" -) - -func TestInvalidSymmetricAlgorithms(t *testing.T) { - _, err := newSymmetricRecipient("XYZ", []byte{}) - if err != ErrUnsupportedAlgorithm { - t.Error("should not accept invalid algorithm") - } - - enc := &symmetricKeyCipher{} - _, err = enc.encryptKey([]byte{}, "XYZ") - if err != ErrUnsupportedAlgorithm { - t.Error("should not accept invalid algorithm") - } -} - -func TestAeadErrors(t *testing.T) { - aead := &aeadContentCipher{ - keyBytes: 16, - authtagBytes: 16, - getAead: func(key []byte) (cipher.AEAD, error) { - return nil, ErrCryptoFailure - }, - } - - parts, err := aead.encrypt([]byte{}, []byte{}, []byte{}) - if err != ErrCryptoFailure { - t.Error("should handle aead failure") - } - - _, err = aead.decrypt([]byte{}, []byte{}, parts) - if err != ErrCryptoFailure { - t.Error("should handle aead failure") - } -} - -func TestInvalidKey(t *testing.T) { - gcm := newAESGCM(16).(*aeadContentCipher) - _, err := gcm.getAead([]byte{}) - if err == nil { - t.Error("should not accept invalid key") - } -} - -func TestStaticKeyGen(t *testing.T) { - key := make([]byte, 32) - io.ReadFull(rand.Reader, key) - - gen := &staticKeyGenerator{key: key} - if gen.keySize() != len(key) { - t.Error("static key generator reports incorrect size") - } - - generated, _, err := gen.genKey() - if err != nil { - t.Error("static key generator should always succeed", err) - } - if !bytes.Equal(generated, key) { - t.Error("static key generator returns different data") - } -} - -func TestVectorsAESGCM(t *testing.T) { - // Source: http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption-29#appendix-A.1 - plaintext := []byte{ - 84, 104, 101, 32, 116, 114, 117, 101, 32, 115, 105, 103, 110, 32, - 111, 102, 32, 105, 110, 116, 101, 108, 108, 105, 103, 101, 110, 99, - 101, 32, 105, 115, 32, 110, 111, 116, 32, 107, 110, 111, 119, 108, - 101, 100, 103, 101, 32, 98, 117, 116, 32, 105, 109, 97, 103, 105, - 110, 97, 116, 105, 111, 110, 46} - - aad := []byte{ - 101, 121, 74, 104, 98, 71, 99, 105, 79, 105, 74, 83, 85, 48, 69, - 116, 84, 48, 70, 70, 85, 67, 73, 115, 73, 109, 86, 117, 89, 121, 73, - 54, 73, 107, 69, 121, 78, 84, 90, 72, 81, 48, 48, 105, 102, 81} - - expectedCiphertext := []byte{ - 229, 236, 166, 241, 53, 191, 115, 196, 174, 43, 73, 109, 39, 122, - 233, 96, 140, 206, 120, 52, 51, 237, 48, 11, 190, 219, 186, 80, 111, - 104, 50, 142, 47, 167, 59, 61, 181, 127, 196, 21, 40, 82, 242, 32, - 123, 143, 168, 226, 73, 216, 176, 144, 138, 247, 106, 60, 16, 205, - 160, 109, 64, 63, 192} - - expectedAuthtag := []byte{ - 92, 80, 104, 49, 133, 25, 161, 215, 173, 101, 219, 211, 136, 91, 210, 145} - - // Mock random reader - randReader = bytes.NewReader([]byte{ - 177, 161, 244, 128, 84, 143, 225, 115, 63, 180, 3, 255, 107, 154, - 212, 246, 138, 7, 110, 91, 112, 46, 34, 105, 47, 130, 203, 46, 122, - 234, 64, 252, 227, 197, 117, 252, 2, 219, 233, 68, 180, 225, 77, 219}) - defer resetRandReader() - - enc := newAESGCM(32) - key, _, _ := randomKeyGenerator{size: 32}.genKey() - out, err := enc.encrypt(key, aad, plaintext) - if err != nil { - t.Error("Unable to encrypt:", err) - return - } - - if bytes.Compare(out.ciphertext, expectedCiphertext) != 0 { - t.Error("Ciphertext did not match") - } - if bytes.Compare(out.tag, expectedAuthtag) != 0 { - t.Error("Auth tag did not match") - } -} diff --git a/vendor/gopkg.in/square/go-jose.v1/utils.go b/vendor/gopkg.in/square/go-jose.v1/utils.go deleted file mode 100644 index 4ca2bc06..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/utils.go +++ /dev/null @@ -1,74 +0,0 @@ -/*- - * Copyright 2014 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 jose - -import ( - "crypto/x509" - "encoding/pem" - "fmt" -) - -// LoadPublicKey loads a public key from PEM/DER-encoded data. -func LoadPublicKey(data []byte) (interface{}, error) { - input := data - - block, _ := pem.Decode(data) - if block != nil { - input = block.Bytes - } - - // Try to load SubjectPublicKeyInfo - pub, err0 := x509.ParsePKIXPublicKey(input) - if err0 == nil { - return pub, nil - } - - cert, err1 := x509.ParseCertificate(input) - if err1 == nil { - return cert.PublicKey, nil - } - - return nil, fmt.Errorf("square/go-jose: parse error, got '%s' and '%s'", err0, err1) -} - -// LoadPrivateKey loads a private key from PEM/DER-encoded data. -func LoadPrivateKey(data []byte) (interface{}, error) { - input := data - - block, _ := pem.Decode(data) - if block != nil { - input = block.Bytes - } - - var priv interface{} - priv, err0 := x509.ParsePKCS1PrivateKey(input) - if err0 == nil { - return priv, nil - } - - priv, err1 := x509.ParsePKCS8PrivateKey(input) - if err1 == nil { - return priv, nil - } - - priv, err2 := x509.ParseECPrivateKey(input) - if err2 == nil { - return priv, nil - } - - return nil, fmt.Errorf("square/go-jose: parse error, got '%s', '%s' and '%s'", err0, err1, err2) -} diff --git a/vendor/gopkg.in/square/go-jose.v1/utils_test.go b/vendor/gopkg.in/square/go-jose.v1/utils_test.go deleted file mode 100644 index 6ad622da..00000000 --- a/vendor/gopkg.in/square/go-jose.v1/utils_test.go +++ /dev/null @@ -1,225 +0,0 @@ -/*- - * Copyright 2014 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 jose - -import ( - "crypto/ecdsa" - "crypto/rand" - "crypto/rsa" - "encoding/base64" - "encoding/hex" - "math/big" - "regexp" - "testing" -) - -// Reset random reader to original value -func resetRandReader() { - randReader = rand.Reader -} - -// Build big int from hex-encoded string. Strips whitespace (for testing). -func fromHexInt(base16 string) *big.Int { - re := regexp.MustCompile(`\s+`) - val, ok := new(big.Int).SetString(re.ReplaceAllString(base16, ""), 16) - if !ok { - panic("Invalid test data") - } - return val -} - -// Build big int from base64-encoded string. Strips whitespace (for testing). -func fromBase64Int(base64 string) *big.Int { - re := regexp.MustCompile(`\s+`) - val, err := base64URLDecode(re.ReplaceAllString(base64, "")) - if err != nil { - panic("Invalid test data") - } - return new(big.Int).SetBytes(val) -} - -// Decode hex-encoded string into byte array. Strips whitespace (for testing). -func fromHexBytes(base16 string) []byte { - re := regexp.MustCompile(`\s+`) - val, err := hex.DecodeString(re.ReplaceAllString(base16, "")) - if err != nil { - panic("Invalid test data") - } - return val -} - -// Decode base64-encoded string into byte array. Strips whitespace (for testing). -func fromBase64Bytes(b64 string) []byte { - re := regexp.MustCompile(`\s+`) - val, err := base64.StdEncoding.DecodeString(re.ReplaceAllString(b64, "")) - if err != nil { - panic("Invalid test data") - } - return val -} - -// Test vectors below taken from crypto/x509/x509_test.go in the Go std lib. - -var pkixPublicKey = `-----BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3VoPN9PKUjKFLMwOge6+ -wnDi8sbETGIx2FKXGgqtAKpzmem53kRGEQg8WeqRmp12wgp74TGpkEXsGae7RS1k -enJCnma4fii+noGH7R0qKgHvPrI2Bwa9hzsH8tHxpyM3qrXslOmD45EH9SxIDUBJ -FehNdaPbLP1gFyahKMsdfxFJLUvbUycuZSJ2ZnIgeVxwm4qbSvZInL9Iu4FzuPtg -fINKcbbovy1qq4KvPIrXzhbY3PWDc6btxCf3SE0JdE1MCPThntB62/bLMSQ7xdDR -FF53oIpvxe/SCOymfWq/LW849Ytv3Xwod0+wzAP8STXG4HSELS4UedPYeHJJJYcZ -+QIDAQAB ------END PUBLIC KEY-----` - -var pkcs1PrivateKey = `-----BEGIN RSA PRIVATE KEY----- -MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0 -fd7Ai2KW5ToIwzFofvJcS/STa6HA5gQenRUCAwEAAQJBAIq9amn00aS0h/CrjXqu -/ThglAXJmZhOMPVn4eiu7/ROixi9sex436MaVeMqSNf7Ex9a8fRNfWss7Sqd9eWu -RTUCIQDasvGASLqmjeffBNLTXV2A5g4t+kLVCpsEIZAycV5GswIhANEPLmax0ME/ -EO+ZJ79TJKN5yiGBRsv5yvx5UiHxajEXAiAhAol5N4EUyq6I9w1rYdhPMGpLfk7A -IU2snfRJ6Nq2CQIgFrPsWRCkV+gOYcajD17rEqmuLrdIRexpg8N1DOSXoJ8CIGlS -tAboUGBxTDq3ZroNism3DaMIbKPyYrAqhKov1h5V ------END RSA PRIVATE KEY-----` - -var ecdsaSHA256p384CertPem = ` ------BEGIN CERTIFICATE----- -MIICSjCCAdECCQDje/no7mXkVzAKBggqhkjOPQQDAjCBjjELMAkGA1UEBhMCVVMx -EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDAS -BgNVBAoMC0dvb2dsZSwgSW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEG -CSqGSIb3DQEJARYUZ29sYW5nLWRldkBnbWFpbC5jb20wHhcNMTIwNTIxMDYxMDM0 -WhcNMjIwNTE5MDYxMDM0WjCBjjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlm -b3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDASBgNVBAoMC0dvb2dsZSwg -SW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEGCSqGSIb3DQEJARYUZ29s -YW5nLWRldkBnbWFpbC5jb20wdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARRuzRNIKRK -jIktEmXanNmrTR/q/FaHXLhWRZ6nHWe26Fw7Rsrbk+VjGy4vfWtNn7xSFKrOu5ze -qxKnmE0h5E480MNgrUiRkaGO2GMJJVmxx20aqkXOk59U8yGA4CghE6MwCgYIKoZI -zj0EAwIDZwAwZAIwBZEN8gvmRmfeP/9C1PRLzODIY4JqWub2PLRT4mv9GU+yw3Gr -PU9A3CHMdEcdw/MEAjBBO1lId8KOCh9UZunsSMfqXiVurpzmhWd6VYZ/32G+M+Mh -3yILeYQzllt/g0rKVRk= ------END CERTIFICATE-----` - -var ecdsaSHA256p384CertDer = fromBase64Bytes(` -MIICSjCCAdECCQDje/no7mXkVzAKBggqhkjOPQQDAjCBjjELMAkGA1UEBhMCVVMx -EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDAS -BgNVBAoMC0dvb2dsZSwgSW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEG -CSqGSIb3DQEJARYUZ29sYW5nLWRldkBnbWFpbC5jb20wHhcNMTIwNTIxMDYxMDM0 -WhcNMjIwNTE5MDYxMDM0WjCBjjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlm -b3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDASBgNVBAoMC0dvb2dsZSwg -SW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEGCSqGSIb3DQEJARYUZ29s -YW5nLWRldkBnbWFpbC5jb20wdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARRuzRNIKRK -jIktEmXanNmrTR/q/FaHXLhWRZ6nHWe26Fw7Rsrbk+VjGy4vfWtNn7xSFKrOu5ze -qxKnmE0h5E480MNgrUiRkaGO2GMJJVmxx20aqkXOk59U8yGA4CghE6MwCgYIKoZI -zj0EAwIDZwAwZAIwBZEN8gvmRmfeP/9C1PRLzODIY4JqWub2PLRT4mv9GU+yw3Gr -PU9A3CHMdEcdw/MEAjBBO1lId8KOCh9UZunsSMfqXiVurpzmhWd6VYZ/32G+M+Mh -3yILeYQzllt/g0rKVRk=`) - -var pkcs8ECPrivateKey = ` ------BEGIN PRIVATE KEY----- -MIHtAgEAMBAGByqGSM49AgEGBSuBBAAjBIHVMIHSAgEBBEHqkl65VsjYDQWIHfgv -zQLPa0JZBsaJI16mjiH8k6VA4lgfK/KNldlEsY433X7wIzo43u8OpX7Nv7n8pVRH -15XWK6GBiQOBhgAEAfDuikMI4bWsyse7t8iSCmjt9fneW/qStZuIPuVLo7mSJdud -Cs3J/x9wOnnhLv1u+0atnq5HKKdL4ff3itJPlhmSAQzByKQ5LTvB7d6fn95GJVK/ -hNuS5qGBpB7qeMXVFoki0/2RZIOway8/fXjmNYwe4v/XB5LLn4hcTvEUGYcF8M9K ------END PRIVATE KEY-----` - -var ecPrivateKey = ` ------BEGIN EC PRIVATE KEY----- -MIHcAgEBBEIBv2rdY9mWGD/UgiuXB0LJcUzgaB6TXq/Ra1jrZKBV3IGSacM5QDFu -N8yrywiQaTDEqn1zVcLwrnqoQux3gWN1jxugBwYFK4EEACOhgYkDgYYABAFJgaM/ -2a3+gE6Khm/1PYftqNwAzQ21HSLp27q2lTN+GBFho691ARFRkr9UzlQ8gRnhkTbu -yGfASamlHsYlr3Tv+gFc4BY8SU0q8kzpQ0dOHWFk7dfGFmKwhJrSFIIOeRn/LY03 -XsVFctNDsGhobS2JguQrxhGx8Ll7vQCakV/PEmCQJA== ------END EC PRIVATE KEY-----` - -var ecPrivateKeyDer = fromBase64Bytes(` -MIHcAgEBBEIBv2rdY9mWGD/UgiuXB0LJcUzgaB6TXq/Ra1jrZKBV3IGSacM5QDFu -N8yrywiQaTDEqn1zVcLwrnqoQux3gWN1jxugBwYFK4EEACOhgYkDgYYABAFJgaM/ -2a3+gE6Khm/1PYftqNwAzQ21HSLp27q2lTN+GBFho691ARFRkr9UzlQ8gRnhkTbu -yGfASamlHsYlr3Tv+gFc4BY8SU0q8kzpQ0dOHWFk7dfGFmKwhJrSFIIOeRn/LY03 -XsVFctNDsGhobS2JguQrxhGx8Ll7vQCakV/PEmCQJA==`) - -var invalidPemKey = ` ------BEGIN PUBLIC KEY----- -MIHcAgEBBEIBv2rdY9mWGD/UgiuXB0LJcUzgaB6TXq/Ra1jrZKBV3IGSacM5QDFu -XsVFctNDsGhobS2JguQrxhGx8Ll7vQCakV/PEmCQJA== ------END PUBLIC KEY-----` - -func TestLoadPublicKey(t *testing.T) { - pub, err := LoadPublicKey([]byte(pkixPublicKey)) - switch pub.(type) { - case *rsa.PublicKey: - default: - t.Error("failed to parse RSA PKIX public key:", err) - } - - pub, err = LoadPublicKey([]byte(ecdsaSHA256p384CertPem)) - switch pub.(type) { - case *ecdsa.PublicKey: - default: - t.Error("failed to parse ECDSA X.509 cert:", err) - } - - pub, err = LoadPublicKey([]byte(ecdsaSHA256p384CertDer)) - switch pub.(type) { - case *ecdsa.PublicKey: - default: - t.Error("failed to parse ECDSA X.509 cert:", err) - } - - pub, err = LoadPublicKey([]byte("###")) - if err == nil { - t.Error("should not parse invalid key") - } - - pub, err = LoadPublicKey([]byte(invalidPemKey)) - if err == nil { - t.Error("should not parse invalid key") - } -} - -func TestLoadPrivateKey(t *testing.T) { - priv, err := LoadPrivateKey([]byte(pkcs1PrivateKey)) - switch priv.(type) { - case *rsa.PrivateKey: - default: - t.Error("failed to parse RSA PKCS1 private key:", err) - } - - priv, err = LoadPrivateKey([]byte(pkcs8ECPrivateKey)) - if _, ok := priv.(*ecdsa.PrivateKey); !ok { - t.Error("failed to parse EC PKCS8 private key:", err) - } - - priv, err = LoadPrivateKey([]byte(ecPrivateKey)) - if _, ok := priv.(*ecdsa.PrivateKey); !ok { - t.Error("failed to parse EC private key:", err) - } - - priv, err = LoadPrivateKey([]byte(ecPrivateKeyDer)) - if _, ok := priv.(*ecdsa.PrivateKey); !ok { - t.Error("failed to parse EC private key:", err) - } - - priv, err = LoadPrivateKey([]byte("###")) - if err == nil { - t.Error("should not parse invalid key") - } - - priv, err = LoadPrivateKey([]byte(invalidPemKey)) - if err == nil { - t.Error("should not parse invalid key") - } -} diff --git a/vendor/gopkg.in/square/go-jose.v2/.travis.yml b/vendor/gopkg.in/square/go-jose.v2/.travis.yml index 3dc44183..d612b420 100644 --- a/vendor/gopkg.in/square/go-jose.v2/.travis.yml +++ b/vendor/gopkg.in/square/go-jose.v2/.travis.yml @@ -10,9 +10,10 @@ matrix: go: - 1.5 - 1.6 +- 1.7 - tip -go_import_path: gopkg.in/square/go-jose.v1 +go_import_path: gopkg.in/square/go-jose.v2 before_script: - export PATH=$HOME/.local/bin:$PATH @@ -32,7 +33,6 @@ before_install: script: - go test . -v -covermode=count -coverprofile=profile.cov -- go test . -tags std_json -v -covermode=count -coverprofile=profile-std-json.cov - go test ./cipher -v -covermode=count -coverprofile=cipher/profile.cov - go test ./jwt -v -covermode=count -coverprofile=jwt/profile.cov - go test ./json -v # no coverage for forked encoding/json package diff --git a/vendor/gopkg.in/square/go-jose.v2/README.md b/vendor/gopkg.in/square/go-jose.v2/README.md index be79e582..43bf1fbe 100644 --- a/vendor/gopkg.in/square/go-jose.v2/README.md +++ b/vendor/gopkg.in/square/go-jose.v2/README.md @@ -1,16 +1,14 @@ # Go JOSE -**Please note that this branch is still under active development.** - -[](https://godoc.org/gopkg.in/square/go-jose.v2) -[](https://raw.githubusercontent.com/square/go-jose/master/LICENSE) -[](https://travis-ci.org/square/go-jose) -[](https://coveralls.io/r/square/go-jose) -[](https://goreportcard.com/report/github.com/square/go-jose) +[](https://godoc.org/gopkg.in/square/go-jose.v1) +[](https://godoc.org/gopkg.in/square/go-jose.v2) +[](https://raw.githubusercontent.com/square/go-jose/master/LICENSE) +[](https://travis-ci.org/square/go-jose) +[](https://coveralls.io/r/square/go-jose) Package jose aims to provide an implementation of the Javascript Object Signing -and Encryption set of standards. For the moment, it mainly focuses on encryption -and signing based on the JSON Web Encryption and JSON Web Signature standards. +and Encryption set of standards. This includes support for JSON Web Encryption, +JSON Web Signature, and JSON Web Token standards. **Disclaimer**: This library contains encryption software that is subject to the U.S. Export Administration Regulations. You may not export, re-export, @@ -23,44 +21,50 @@ US maintained blocked list. ## Overview The implementation follows the -[JSON Web Encryption](http://dx.doi.org/10.17487/RFC7516) -standard (RFC 7516) and -[JSON Web Signature](http://dx.doi.org/10.17487/RFC7515) -standard (RFC 7515). Tables of supported algorithms are shown below. -The library supports both the compact and full serialization formats, and has -optional support for multiple recipients. It also comes with a small -command-line utility -([`jose-util`](https://github.com/square/go-jose/tree/master/jose-util)) +[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). +Tables of supported algorithms are shown below. The library supports both +the compact and full serialization formats, and has optional support for +multiple recipients. It also comes with a small command-line utility +([`jose-util`](https://github.com/square/go-jose/tree/v2/jose-util)) for dealing with JOSE messages in a shell. **Note**: We use a forked version of the `encoding/json` package from the Go standard library which uses case-sensitive matching for member names (instead of [case-insensitive matching](https://www.ietf.org/mail-archive/web/json/current/msg03763.html)). This is to avoid differences in interpretation of messages between go-jose and -libraries in other languages. If you do not like this behavior, you can use the -`std_json` build tag to disable it (though we do not recommend doing so). +libraries in other languages. ### Versions We use [gopkg.in](https://gopkg.in) for versioning. -[Version 1](https://gopkg.in/square/go-jose.v1) is the current stable version: +[Version 1](https://gopkg.in/square/go-jose.v1) is the old stable version: import "gopkg.in/square/go-jose.v1" +[Version 2](https://gopkg.in/square/go-jose.v2) is for new development: + + import "gopkg.in/square/go-jose.v2" + The interface for [go-jose.v1](https://gopkg.in/square/go-jose.v1) will remain -backwards compatible. We're currently sketching out ideas for a new version, to -clean up the interface a bit. If you have ideas or feature requests [please let -us know](https://github.com/square/go-jose/issues/64)! +backwards compatible. No new feature development will take place on the `v1` branch, +however bug fixes and security fixes will be backported. + +The interface for [go-jose.v2](https://gopkg.in/square/go-jose.v2) is mostly +stable, but we suggest pinning to a particular revision for now as we still reserve +the right to make changes. New feature development happens on this branch. + +New in [go-jose.v2](https://gopkg.in/square/go-jose.v2) is a +[jwt](https://godoc.org/gopkg.in/square/go-jose.v2/jwt) sub-package +contributed by [@shaxbee](https://github.com/shaxbee). ### 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) -standard where possible. The -[Godoc reference](https://godoc.org/github.com/square/go-jose#pkg-constants) -has a list of constants. +the names in the [JSON Web Algorithms](http://dx.doi.org/10.17487/RFC7518) +standard where possible. The Godoc reference has a list of constants. Key encryption | Algorithm identifier(s) :------------------------- | :------------------------------ @@ -94,122 +98,22 @@ has a list of constants. See below for a table of supported key types. These are understood by the library, and can be passed to corresponding functions such as `NewEncrypter` or -`NewSigner`. Note that if you are creating a new encrypter or signer with a -JSONWebKey, the key id of the JSONWebKey (if present) will be added to any -resulting messages. +`NewSigner`. Each of these keys can also be wrapped in a JWK if desired, which +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), *[jose.JSONWebKey](https://godoc.org/gopkg.in/square/go-jose.v2#JSONWebKey) - ECDH, ECDSA | *[ecdsa.PublicKey](http://golang.org/pkg/crypto/ecdsa/#PublicKey), *[ecdsa.PrivateKey](http://golang.org/pkg/crypto/ecdsa/#PrivateKey), *[jose.JSONWebKey](https://godoc.org/gopkg.in/square/go-jose.v2#JSONWebKey) - AES, HMAC | []byte, *[jose.JSONWebKey](https://godoc.org/gopkg.in/square/go-jose.v2#JSONWebKey) + 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) + AES, HMAC | []byte ## Examples -Encryption/decryption example using RSA: +[](https://godoc.org/gopkg.in/square/go-jose.v1) +[](https://godoc.org/gopkg.in/square/go-jose.v2) -```Go -// Generate a public/private key pair to use for this example. The library -// also provides two utility functions (LoadPublicKey and LoadPrivateKey) -// that can be used to load keys from PEM/DER-encoded data. -privateKey, err := rsa.GenerateKey(rand.Reader, 2048) -if err != nil { - panic(err) -} - -// Instantiate an encrypter using RSA-OAEP with AES128-GCM. An error would -// indicate that the selected algorithm(s) are not currently supported. -publicKey := &privateKey.PublicKey -encrypter, err := NewEncrypter(A128GCM, Recipient{Algorithm: RSA_OAEP, Key: publicKey}, nil) -if err != nil { - panic(err) -} - -// Encrypt a sample plaintext. Calling the encrypter returns an encrypted -// JWE object, which can then be serialized for output afterwards. An error -// would indicate a problem in an underlying cryptographic primitive. -var plaintext = []byte("Lorem ipsum dolor sit amet") -object, err := encrypter.Encrypt(plaintext) -if err != nil { - panic(err) -} - -// Serialize the encrypted object using the full serialization format. -// Alternatively you can also use the compact format here by calling -// object.CompactSerialize() instead. -serialized := object.FullSerialize() - -// Parse the serialized, encrypted JWE object. An error would indicate that -// the given input did not represent a valid message. -object, err = ParseEncrypted(serialized) -if err != nil { - panic(err) -} - -// Now we can decrypt and get back our original plaintext. An error here -// would indicate the the message failed to decrypt, e.g. because the auth -// tag was broken or the message was tampered with. -decrypted, err := object.Decrypt(privateKey) -if err != nil { - panic(err) -} - -fmt.Printf(string(decrypted)) -// output: Lorem ipsum dolor sit amet -``` - -Signing/verification example using RSA: - -```Go -// Generate a public/private key pair to use for this example. The library -// also provides two utility functions (LoadPublicKey and LoadPrivateKey) -// that can be used to load keys from PEM/DER-encoded data. -privateKey, err := rsa.GenerateKey(rand.Reader, 2048) -if err != nil { - panic(err) -} - -// Instantiate a signer using RSASSA-PSS (SHA512) with the given private key. -signer, err := NewSigner(SigningKey{Algorithm: PS512, Key: privateKey}, nil) -if err != nil { - panic(err) -} - -// Sign a sample payload. Calling the signer returns a protected JWS object, -// which can then be serialized for output afterwards. An error would -// indicate a problem in an underlying cryptographic primitive. -var payload = []byte("Lorem ipsum dolor sit amet") -object, err := signer.Sign(payload) -if err != nil { - panic(err) -} - -// Serialize the encrypted object using the full serialization format. -// Alternatively you can also use the compact format here by calling -// object.CompactSerialize() instead. -serialized := object.FullSerialize() - -// Parse the serialized, protected JWS object. An error would indicate that -// the given input did not represent a valid message. -object, err = ParseSigned(serialized) -if err != nil { - panic(err) -} - -// Now we can verify the signature on the payload. An error here would -// indicate the the message failed to verify, e.g. because the signature was -// broken or the message was tampered with. -output, err := object.Verify(&privateKey.PublicKey) -if err != nil { - panic(err) -} - -fmt.Printf(string(output)) -// output: Lorem ipsum dolor sit amet -``` - -More examples can be found in the [Godoc -reference](https://godoc.org/github.com/square/go-jose) for this package. The -[`jose-util`](https://github.com/square/go-jose/tree/master/jose-util) -subdirectory also contains a small command-line utility which might -be useful as an example. +Examples can be found in the Godoc +reference for this package. The +[`jose-util`](https://github.com/square/go-jose/tree/v2/jose-util) +subdirectory also contains a small command-line utility which might be useful +as an example. diff --git a/vendor/gopkg.in/square/go-jose.v2/asymmetric.go b/vendor/gopkg.in/square/go-jose.v2/asymmetric.go index eb12579a..b238b4ad 100644 --- a/vendor/gopkg.in/square/go-jose.v2/asymmetric.go +++ b/vendor/gopkg.in/square/go-jose.v2/asymmetric.go @@ -67,6 +67,10 @@ func newRSARecipient(keyAlg KeyAlgorithm, publicKey *rsa.PublicKey) (recipientKe return recipientKeyInfo{}, ErrUnsupportedAlgorithm } + if publicKey == nil { + return recipientKeyInfo{}, errors.New("invalid public key") + } + return recipientKeyInfo{ keyAlg: keyAlg, keyEncrypter: &rsaEncrypterVerifier{ @@ -84,6 +88,10 @@ func newRSASigner(sigAlg SignatureAlgorithm, privateKey *rsa.PrivateKey) (recipi return recipientSigInfo{}, ErrUnsupportedAlgorithm } + if privateKey == nil { + return recipientSigInfo{}, errors.New("invalid private key") + } + return recipientSigInfo{ sigAlg: sigAlg, publicKey: &JSONWebKey{ @@ -104,6 +112,10 @@ func newECDHRecipient(keyAlg KeyAlgorithm, publicKey *ecdsa.PublicKey) (recipien return recipientKeyInfo{}, ErrUnsupportedAlgorithm } + if publicKey == nil || !publicKey.Curve.IsOnCurve(publicKey.X, publicKey.Y) { + return recipientKeyInfo{}, errors.New("invalid public key") + } + return recipientKeyInfo{ keyAlg: keyAlg, keyEncrypter: &ecEncrypterVerifier{ @@ -121,6 +133,10 @@ func newECDSASigner(sigAlg SignatureAlgorithm, privateKey *ecdsa.PrivateKey) (re return recipientSigInfo{}, ErrUnsupportedAlgorithm } + if privateKey == nil { + return recipientSigInfo{}, errors.New("invalid private key") + } + return recipientSigInfo{ sigAlg: sigAlg, publicKey: &JSONWebKey{ @@ -370,6 +386,10 @@ func (ctx ecDecrypterSigner) decryptKey(headers rawHeader, recipient *recipientI return nil, errors.New("square/go-jose: invalid epk header") } + if !ctx.privateKey.Curve.IsOnCurve(publicKey.X, publicKey.Y) { + return nil, errors.New("square/go-jose: invalid public key in epk header") + } + apuData := headers.Apu.bytes() apvData := headers.Apv.bytes() @@ -474,6 +494,8 @@ func (ctx ecEncrypterVerifier) verifyPayload(payload []byte, signature []byte, a case ES512: keySize = 66 hash = crypto.SHA512 + default: + return ErrUnsupportedAlgorithm } if len(signature) != 2*keySize { diff --git a/vendor/gopkg.in/square/go-jose.v2/asymmetric_test.go b/vendor/gopkg.in/square/go-jose.v2/asymmetric_test.go index c97b6a55..79bacc4b 100644 --- a/vendor/gopkg.in/square/go-jose.v2/asymmetric_test.go +++ b/vendor/gopkg.in/square/go-jose.v2/asymmetric_test.go @@ -18,6 +18,8 @@ package jose import ( "bytes" + "crypto/ecdsa" + "crypto/elliptic" "crypto/rand" "crypto/rsa" "errors" @@ -429,3 +431,38 @@ func TestInvalidEllipticCurve(t *testing.T) { t.Error("should not generate ES384 signature with P-521 key") } } + +func estInvalidECPublicKey(t *testing.T) { + // Invalid key + invalid := &ecdsa.PrivateKey{ + PublicKey: ecdsa.PublicKey{ + Curve: elliptic.P256(), + X: fromBase64Int("MTEx"), + Y: fromBase64Int("MTEx"), + }, + D: fromBase64Int("0_NxaRPUMQoAJt50Gz8YiTr8gRTwyEaCumd-MToTmIo"), + } + + headers := rawHeader{ + Alg: string(ECDH_ES), + Epk: &JSONWebKey{ + Key: &invalid.PublicKey, + }, + } + + dec := ecDecrypterSigner{ + privateKey: ecTestKey256, + } + + _, err := dec.decryptKey(headers, nil, randomKeyGenerator{size: 16}) + if err == nil { + t.Fatal("decrypter accepted JWS with invalid ECDH public key") + } +} + +func TestInvalidAlgorithmEC(t *testing.T) { + err := ecEncrypterVerifier{publicKey: &ecTestKey256.PublicKey}.verifyPayload([]byte{}, []byte{}, "XYZ") + if err != ErrUnsupportedAlgorithm { + t.Fatal("should not accept invalid/unsupported algorithm") + } +} diff --git a/vendor/gopkg.in/square/go-jose.v2/cipher/cbc_hmac.go b/vendor/gopkg.in/square/go-jose.v2/cipher/cbc_hmac.go index a5c35834..126b85ce 100644 --- a/vendor/gopkg.in/square/go-jose.v2/cipher/cbc_hmac.go +++ b/vendor/gopkg.in/square/go-jose.v2/cipher/cbc_hmac.go @@ -82,7 +82,7 @@ func (ctx *cbcAEAD) Overhead() int { // Seal encrypts and authenticates the plaintext. func (ctx *cbcAEAD) Seal(dst, nonce, plaintext, data []byte) []byte { // Output buffer -- must take care not to mangle plaintext input. - ciphertext := make([]byte, len(plaintext)+ctx.Overhead())[:len(plaintext)] + ciphertext := make([]byte, uint64(len(plaintext))+uint64(ctx.Overhead()))[:len(plaintext)] copy(ciphertext, plaintext) ciphertext = padBuffer(ciphertext, ctx.blockCipher.BlockSize()) @@ -91,7 +91,7 @@ func (ctx *cbcAEAD) Seal(dst, nonce, plaintext, data []byte) []byte { cbc.CryptBlocks(ciphertext, ciphertext) authtag := ctx.computeAuthTag(data, nonce, ciphertext) - ret, out := resize(dst, len(dst)+len(ciphertext)+len(authtag)) + ret, out := resize(dst, uint64(len(dst))+uint64(len(ciphertext))+uint64(len(authtag))) copy(out, ciphertext) copy(out[len(ciphertext):], authtag) @@ -128,7 +128,7 @@ func (ctx *cbcAEAD) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { return nil, err } - ret, out := resize(dst, len(dst)+len(plaintext)) + ret, out := resize(dst, uint64(len(dst))+uint64(len(plaintext))) copy(out, plaintext) return ret, nil @@ -136,12 +136,12 @@ func (ctx *cbcAEAD) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { // Compute an authentication tag func (ctx *cbcAEAD) computeAuthTag(aad, nonce, ciphertext []byte) []byte { - buffer := make([]byte, len(aad)+len(nonce)+len(ciphertext)+8) + buffer := make([]byte, uint64(len(aad))+uint64(len(nonce))+uint64(len(ciphertext))+8) n := 0 n += copy(buffer, aad) n += copy(buffer[n:], nonce) n += copy(buffer[n:], ciphertext) - binary.BigEndian.PutUint64(buffer[n:], uint64(len(aad)*8)) + binary.BigEndian.PutUint64(buffer[n:], uint64(len(aad))*8) // According to documentation, Write() on hash.Hash never fails. hmac := hmac.New(ctx.hash, ctx.integrityKey) @@ -153,8 +153,8 @@ func (ctx *cbcAEAD) computeAuthTag(aad, nonce, ciphertext []byte) []byte { // resize ensures the the given slice has a capacity of at least n bytes. // If the capacity of the slice is less than n, a new slice is allocated // and the existing data will be copied. -func resize(in []byte, n int) (head, tail []byte) { - if cap(in) >= n { +func resize(in []byte, n uint64) (head, tail []byte) { + if uint64(cap(in)) >= n { head = in[:n] } else { head = make([]byte, n) @@ -168,7 +168,7 @@ func resize(in []byte, n int) (head, tail []byte) { // Apply padding func padBuffer(buffer []byte, blockSize int) []byte { missing := blockSize - (len(buffer) % blockSize) - ret, out := resize(buffer, len(buffer)+missing) + ret, out := resize(buffer, uint64(len(buffer))+uint64(missing)) padding := bytes.Repeat([]byte{byte(missing)}, missing) copy(out, padding) return ret diff --git a/vendor/gopkg.in/square/go-jose.v2/cipher/cbc_hmac_test.go b/vendor/gopkg.in/square/go-jose.v2/cipher/cbc_hmac_test.go index c230271b..40bcb20f 100644 --- a/vendor/gopkg.in/square/go-jose.v2/cipher/cbc_hmac_test.go +++ b/vendor/gopkg.in/square/go-jose.v2/cipher/cbc_hmac_test.go @@ -283,7 +283,7 @@ func TestTruncatedCiphertext(t *testing.T) { ct := aead.Seal(nil, nonce, data, nil) // Truncated ciphertext, but with correct auth tag - truncated, tail := resize(ct[:len(ct)-ctx.authtagBytes-2], len(ct)-2) + truncated, tail := resize(ct[:len(ct)-ctx.authtagBytes-2], uint64(len(ct))-2) copy(tail, ctx.computeAuthTag(nil, nonce, truncated[:len(truncated)-ctx.authtagBytes])) // Open should fail @@ -313,8 +313,8 @@ func TestInvalidPaddingOpen(t *testing.T) { ctx := aead.(*cbcAEAD) // Mutated ciphertext, but with correct auth tag - size := len(buffer) - ciphertext, tail := resize(buffer, size+(len(key)/2)) + size := uint64(len(buffer)) + ciphertext, tail := resize(buffer, size+(uint64(len(key))/2)) copy(tail, ctx.computeAuthTag(nil, nonce, ciphertext[:size])) // Open should fail (b/c of invalid padding, even though tag matches) diff --git a/vendor/gopkg.in/square/go-jose.v2/cipher/concat_kdf.go b/vendor/gopkg.in/square/go-jose.v2/cipher/concat_kdf.go index cbb5f7b8..f62c3bdb 100644 --- a/vendor/gopkg.in/square/go-jose.v2/cipher/concat_kdf.go +++ b/vendor/gopkg.in/square/go-jose.v2/cipher/concat_kdf.go @@ -32,7 +32,7 @@ type concatKDF struct { // NewConcatKDF builds a KDF reader based on the given inputs. func NewConcatKDF(hash crypto.Hash, z, algID, ptyUInfo, ptyVInfo, supPubInfo, supPrivInfo []byte) io.Reader { - buffer := make([]byte, len(algID)+len(ptyUInfo)+len(ptyVInfo)+len(supPubInfo)+len(supPrivInfo)) + buffer := make([]byte, uint64(len(algID))+uint64(len(ptyUInfo))+uint64(len(ptyVInfo))+uint64(len(supPubInfo))+uint64(len(supPrivInfo))) n := 0 n += copy(buffer, algID) n += copy(buffer[n:], ptyUInfo) diff --git a/vendor/gopkg.in/square/go-jose.v2/cipher/ecdh_es.go b/vendor/gopkg.in/square/go-jose.v2/cipher/ecdh_es.go index c6a5a821..f23d49e1 100644 --- a/vendor/gopkg.in/square/go-jose.v2/cipher/ecdh_es.go +++ b/vendor/gopkg.in/square/go-jose.v2/cipher/ecdh_es.go @@ -23,7 +23,14 @@ import ( ) // DeriveECDHES derives a shared encryption key using ECDH/ConcatKDF as described in JWE/JWA. +// It is an error to call this function with a private/public key that are not on the same +// curve. Callers must ensure that the keys are valid before calling this function. Output +// size may be at most 1<<16 bytes (64 KiB). func DeriveECDHES(alg string, apuData, apvData []byte, priv *ecdsa.PrivateKey, pub *ecdsa.PublicKey, size int) []byte { + if size > 1<<16 { + panic("ECDH-ES output size too large, must be less than 1<<16") + } + // algId, partyUInfo, partyVInfo inputs must be prefixed with the length algID := lengthPrefixed([]byte(alg)) ptyUInfo := lengthPrefixed(apuData) @@ -33,6 +40,10 @@ func DeriveECDHES(alg string, apuData, apvData []byte, priv *ecdsa.PrivateKey, p supPubInfo := make([]byte, 4) binary.BigEndian.PutUint32(supPubInfo, uint32(size)*8) + if !priv.PublicKey.Curve.IsOnCurve(pub.X, pub.Y) { + panic("public key not on same curve as private key") + } + z, _ := priv.PublicKey.Curve.ScalarMult(pub.X, pub.Y, priv.D.Bytes()) reader := NewConcatKDF(crypto.SHA256, z.Bytes(), algID, ptyUInfo, ptyVInfo, supPubInfo, []byte{}) diff --git a/vendor/gopkg.in/square/go-jose.v2/cipher/ecdh_es_test.go b/vendor/gopkg.in/square/go-jose.v2/cipher/ecdh_es_test.go index f92abb17..58fb4c67 100644 --- a/vendor/gopkg.in/square/go-jose.v2/cipher/ecdh_es_test.go +++ b/vendor/gopkg.in/square/go-jose.v2/cipher/ecdh_es_test.go @@ -48,7 +48,7 @@ var bobKey = &ecdsa.PrivateKey{ func fromBase64Int(data string) *big.Int { val, err := base64.URLEncoding.DecodeString(data) if err != nil { - panic("Invalid test data") + panic("Invalid test data: " + err.Error()) } return new(big.Int).SetBytes(val) } @@ -67,6 +67,23 @@ func TestVectorECDHES(t *testing.T) { } } +func TestInvalidECPublicKey(t *testing.T) { + defer func() { recover() }() + + // Invalid key + invalid := &ecdsa.PrivateKey{ + PublicKey: ecdsa.PublicKey{ + Curve: elliptic.P256(), + X: fromBase64Int("MTEx"), + Y: fromBase64Int("MTEx"), + }, + D: fromBase64Int("0_NxaRPUMQoAJt50Gz8YiTr8gRTwyEaCumd-MToTmIo="), + } + + DeriveECDHES("A128GCM", []byte{}, []byte{}, bobKey, &invalid.PublicKey, 16) + t.Fatal("should panic if public key was invalid") +} + func BenchmarkECDHES_128(b *testing.B) { apuData := []byte("APU") apvData := []byte("APV") diff --git a/vendor/gopkg.in/square/go-jose.v2/crypter.go b/vendor/gopkg.in/square/go-jose.v2/crypter.go index e4a0e806..bc7e043b 100644 --- a/vendor/gopkg.in/square/go-jose.v2/crypter.go +++ b/vendor/gopkg.in/square/go-jose.v2/crypter.go @@ -19,6 +19,7 @@ package jose import ( "crypto/ecdsa" "crypto/rsa" + "errors" "fmt" "reflect" ) @@ -308,10 +309,16 @@ func (ctx *genericEncrypter) EncryptWithAuthData(plaintext, aad []byte) (*JSONWe return obj, nil } -// Decrypt and validate the object and return the plaintext. +// Decrypt and validate the object and return the plaintext. Note that this +// function does not support multi-recipient, if you desire multi-recipient +// decryption use DecryptMulti instead. func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error) { headers := obj.mergedHeaders(nil) + if len(obj.recipients) > 1 { + return nil, errors.New("square/go-jose: too many recipients in payload; expecting only one") + } + if len(headers.Crit) > 0 { return nil, fmt.Errorf("square/go-jose: unsupported crit header") } @@ -339,17 +346,13 @@ func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error) authData := obj.computeAuthData() var plaintext []byte - for _, recipient := range obj.recipients { - recipientHeaders := obj.mergedHeaders(&recipient) + recipient := obj.recipients[0] + recipientHeaders := obj.mergedHeaders(&recipient) - cek, err := decrypter.decryptKey(recipientHeaders, &recipient, generator) - if err == nil { - // Found a valid CEK -- let's try to decrypt. - plaintext, err = cipher.decrypt(cek, authData, parts) - if err == nil { - break - } - } + cek, err := decrypter.decryptKey(recipientHeaders, &recipient, generator) + if err == nil { + // Found a valid CEK -- let's try to decrypt. + plaintext, err = cipher.decrypt(cek, authData, parts) } if plaintext == nil { @@ -363,3 +366,67 @@ func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error) return plaintext, err } + +// 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. +func (obj JSONWebEncryption) DecryptMulti(decryptionKey interface{}) (int, Header, []byte, error) { + globalHeaders := obj.mergedHeaders(nil) + + if len(globalHeaders.Crit) > 0 { + return -1, Header{}, nil, fmt.Errorf("square/go-jose: unsupported crit header") + } + + decrypter, err := newDecrypter(decryptionKey) + if err != nil { + return -1, Header{}, nil, err + } + + cipher := getContentCipher(globalHeaders.Enc) + if cipher == nil { + return -1, Header{}, nil, fmt.Errorf("square/go-jose: unsupported enc value '%s'", string(globalHeaders.Enc)) + } + + generator := randomKeyGenerator{ + size: cipher.keySize(), + } + + parts := &aeadParts{ + iv: obj.iv, + ciphertext: obj.ciphertext, + tag: obj.tag, + } + + authData := obj.computeAuthData() + + index := -1 + var plaintext []byte + var headers rawHeader + + for i, recipient := range obj.recipients { + recipientHeaders := obj.mergedHeaders(&recipient) + + cek, err := decrypter.decryptKey(recipientHeaders, &recipient, generator) + if err == nil { + // Found a valid CEK -- let's try to decrypt. + plaintext, err = cipher.decrypt(cek, authData, parts) + if err == nil { + index = i + headers = recipientHeaders + break + } + } + } + + if plaintext == nil || err != nil { + return -1, Header{}, nil, ErrCryptoFailure + } + + // The "zip" header parameter may only be present in the protected header. + if obj.protected.Zip != "" { + plaintext, err = decompress(obj.protected.Zip, plaintext) + } + + return index, headers.sanitized(), plaintext, err +} diff --git a/vendor/gopkg.in/square/go-jose.v2/crypter_test.go b/vendor/gopkg.in/square/go-jose.v2/crypter_test.go index 4275b275..ba70acd9 100644 --- a/vendor/gopkg.in/square/go-jose.v2/crypter_test.go +++ b/vendor/gopkg.in/square/go-jose.v2/crypter_test.go @@ -279,38 +279,40 @@ func TestMultiRecipientJWE(t *testing.T) { input := []byte("Lorem ipsum dolor sit amet") obj, err := enc.Encrypt(input) if err != nil { - t.Error("error in encrypt: ", err) - return + t.Fatal("error in encrypt: ", err) } msg := obj.FullSerialize() parsed, err := ParseEncrypted(msg) if err != nil { - t.Error("error in parse: ", err) - return + t.Fatal("error in parse: ", err) } - output, err := parsed.Decrypt(rsaTestKey) + i, _, output, err := parsed.DecryptMulti(rsaTestKey) if err != nil { - t.Error("error on decrypt with RSA: ", err) - return + t.Fatal("error on decrypt with RSA: ", err) + } + + if i != 0 { + t.Fatal("recipient index should be 0 for RSA key") } if bytes.Compare(input, output) != 0 { - t.Error("Decrypted output does not match input: ", output, input) - return + t.Fatal("Decrypted output does not match input: ", output, input) } - output, err = parsed.Decrypt(sharedKey) + i, _, output, err = parsed.DecryptMulti(sharedKey) if err != nil { - t.Error("error on decrypt with AES: ", err) - return + t.Fatal("error on decrypt with AES: ", err) + } + + if i != 1 { + t.Fatal("recipient index should be 1 for shared key") } if bytes.Compare(input, output) != 0 { - t.Error("Decrypted output does not match input", output, input) - return + t.Fatal("Decrypted output does not match input", output, input) } } diff --git a/vendor/gopkg.in/square/go-jose.v2/encoding.go b/vendor/gopkg.in/square/go-jose.v2/encoding.go index e997ebf6..9f37ef46 100644 --- a/vendor/gopkg.in/square/go-jose.v2/encoding.go +++ b/vendor/gopkg.in/square/go-jose.v2/encoding.go @@ -21,6 +21,7 @@ import ( "compress/flate" "encoding/base64" "encoding/binary" + "encoding/json" "io" "math/big" "regexp" @@ -31,7 +32,7 @@ var stripWhitespaceRegex = regexp.MustCompile("\\s") // Helper function to serialize known-good objects. // Precondition: value is not a nil pointer. func mustSerializeJSON(value interface{}) []byte { - out, err := MarshalJSON(value) + out, err := json.Marshal(value) if err != nil { panic(err) } @@ -132,12 +133,12 @@ func newBufferFromInt(num uint64) *byteBuffer { } func (b *byteBuffer) MarshalJSON() ([]byte, error) { - return MarshalJSON(b.base64()) + return json.Marshal(b.base64()) } func (b *byteBuffer) UnmarshalJSON(data []byte) error { var encoded string - err := UnmarshalJSON(data, &encoded) + err := json.Unmarshal(data, &encoded) if err != nil { return err } diff --git a/vendor/gopkg.in/square/go-jose.v2/jose-util/jose-util.t b/vendor/gopkg.in/square/go-jose.v2/jose-util/jose-util.t index 5e42ba4d..c0d747bb 100644 --- a/vendor/gopkg.in/square/go-jose.v2/jose-util/jose-util.t +++ b/vendor/gopkg.in/square/go-jose.v2/jose-util/jose-util.t @@ -86,3 +86,9 @@ Sign and verify a test message (EC). > jose-util sign --alg ES384 --key ec.key | > jose-util verify --key ec.pub Lorem ipsum dolor sit amet + +Expand a compact message to full format. + + $ echo "eyJhbGciOiJFUzM4NCJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQK.QPU35XY913Im7ZEaN2yHykfbtPqjHZvYp-lV8OcTAJZs67bJFSdTSkQhQWE9ch6tvYrj_7py6HKaWVFLll_s_Rm6bmwq3JszsHrIvFFm1NydruYHhvAnx7rjYiqwOu0W" | + > jose-util expand --format JWS + {"payload":"TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQK","protected":"eyJhbGciOiJFUzM4NCJ9","signature":"QPU35XY913Im7ZEaN2yHykfbtPqjHZvYp-lV8OcTAJZs67bJFSdTSkQhQWE9ch6tvYrj_7py6HKaWVFLll_s_Rm6bmwq3JszsHrIvFFm1NydruYHhvAnx7rjYiqwOu0W"} diff --git a/vendor/gopkg.in/square/go-jose.v2/jose-util/main.go b/vendor/gopkg.in/square/go-jose.v2/jose-util/main.go index 0a5d6a45..356bc2c4 100644 --- a/vendor/gopkg.in/square/go-jose.v2/jose-util/main.go +++ b/vendor/gopkg.in/square/go-jose.v2/jose-util/main.go @@ -28,7 +28,7 @@ import ( var ( app = kingpin.New("jose-util", "A command-line utility for dealing with JOSE objects.") - keyFile = app.Flag("key", "Path to key file (PEM or DER-encoded)").Required().ExistingFile() + keyFile = app.Flag("key", "Path to key file (PEM or DER-encoded)").ExistingFile() inFile = app.Flag("in", "Path to input file (stdin if missing)").ExistingFile() outFile = app.Flag("out", "Path to output file (stdout if missing)").ExistingFile() @@ -54,8 +54,12 @@ func main() { command := kingpin.MustParse(app.Parse(os.Args[1:])) - keyBytes, err := ioutil.ReadFile(*keyFile) - exitOnError(err, "unable to read key file") + var keyBytes []byte + var err error + if command != "expand" { + keyBytes, err = ioutil.ReadFile(*keyFile) + exitOnError(err, "unable to read key file") + } switch command { case "encrypt": @@ -144,6 +148,7 @@ func main() { exitOnError(err, "unable to expand message") writeOutput(*outFile, []byte(serialized)) + writeOutput(*outFile, []byte("\n")) } } diff --git a/vendor/gopkg.in/square/go-jose.v2/json/decode_test.go b/vendor/gopkg.in/square/go-jose.v2/json/decode_test.go index 7577b21a..32394654 100644 --- a/vendor/gopkg.in/square/go-jose.v2/json/decode_test.go +++ b/vendor/gopkg.in/square/go-jose.v2/json/decode_test.go @@ -1258,7 +1258,7 @@ func TestSliceOfCustomByte(t *testing.T) { t.Fatal(err) } if !reflect.DeepEqual(a, b) { - t.Fatal("expected %v == %v", a, b) + t.Fatalf("expected %v == %v", a, b) } } diff --git a/vendor/gopkg.in/square/go-jose.v2/json_fork.go b/vendor/gopkg.in/square/go-jose.v2/json_fork.go deleted file mode 100644 index 38d887f7..00000000 --- a/vendor/gopkg.in/square/go-jose.v2/json_fork.go +++ /dev/null @@ -1,31 +0,0 @@ -// +build !std_json - -/*- - * Copyright 2014 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 jose - -import ( - "gopkg.in/square/go-jose.v2/json" -) - -func MarshalJSON(v interface{}) ([]byte, error) { - return json.Marshal(v) -} - -func UnmarshalJSON(data []byte, v interface{}) error { - return json.Unmarshal(data, v) -} diff --git a/vendor/gopkg.in/square/go-jose.v2/json_fork_test.go b/vendor/gopkg.in/square/go-jose.v2/json_fork_test.go deleted file mode 100644 index cacf5d5d..00000000 --- a/vendor/gopkg.in/square/go-jose.v2/json_fork_test.go +++ /dev/null @@ -1,105 +0,0 @@ -// +build !std_json - -/*- - * Copyright 2014 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 jose - -import ( - "testing" -) - -type CaseSensitive struct { - A int `json:"Test"` - B int `json:"test"` - C int `json:"TEST"` -} - -type UnicodeTest struct { - Sig string `json:"sig"` -} - -func TestUnicodeComparison(t *testing.T) { - // Some tests from RFC 7515, Section 10.13 - raw := []byte(`{"\u0073ig":"foo"}`) - var ut UnicodeTest - err := UnmarshalJSON(raw, &ut) - if err != nil { - t.Error(err) - } - - if ut.Sig != "foo" { - t.Error("strings 'sig' and '\\u0073ig' should be equal") - } - - raw = []byte(`{"si\u0047":"bar"}`) - var ut2 UnicodeTest - err = UnmarshalJSON(raw, &ut2) - if err != nil { - t.Error(err) - } - - if ut2.Sig != "" { - t.Error("strings 'sig' and 'si\\u0047' should not be equal") - } -} - -func TestCaseSensitiveJSON(t *testing.T) { - raw := []byte(`{"test":42}`) - var cs CaseSensitive - err := UnmarshalJSON(raw, &cs) - if err != nil { - t.Error(err) - } - - if cs.A != 0 || cs.B != 42 || cs.C != 0 { - t.Errorf("parsing JSON should be case-sensitive (got %v)", cs) - } -} - -func TestRejectDuplicateKeysObject(t *testing.T) { - raw := []byte(`{"test":42,"test":43}`) - var cs CaseSensitive - err := UnmarshalJSON(raw, &cs) - if err == nil { - t.Error("should reject JSON with duplicate keys, but didn't") - } -} - -func TestRejectDuplicateKeysInterface(t *testing.T) { - raw := []byte(`{"test":42,"test":43}`) - var m interface{} - err := UnmarshalJSON(raw, &m) - if err == nil { - t.Error("should reject JSON with duplicate keys, but didn't") - } -} - -func TestParseCaseSensitiveJWE(t *testing.T) { - invalidJWE := `{"protected":"eyJlbmMiOiJYWVoiLCJBTEciOiJYWVoifQo","encrypted_key":"QUJD","iv":"QUJD","ciphertext":"QUJD","tag":"QUJD"}` - _, err := ParseEncrypted(invalidJWE) - if err == nil { - t.Error("Able to parse message with case-invalid headers", invalidJWE) - } -} - -func TestParseCaseSensitiveJWS(t *testing.T) { - invalidJWS := `{"PAYLOAD":"CUJD","signatures":[{"protected":"e30","signature":"CUJD"}]}` - _, err := ParseSigned(invalidJWS) - if err == nil { - t.Error("Able to parse message with case-invalid headers", invalidJWS) - } -} diff --git a/vendor/gopkg.in/square/go-jose.v2/json_std.go b/vendor/gopkg.in/square/go-jose.v2/json_std.go deleted file mode 100644 index cb3c3555..00000000 --- a/vendor/gopkg.in/square/go-jose.v2/json_std.go +++ /dev/null @@ -1,31 +0,0 @@ -// +build std_json - -/*- - * Copyright 2014 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 jose - -import ( - "encoding/json" -) - -func MarshalJSON(v interface{}) ([]byte, error) { - return json.Marshal(v) -} - -func UnmarshalJSON(data []byte, v interface{}) error { - return json.Unmarshal(data, v) -} diff --git a/vendor/gopkg.in/square/go-jose.v2/json_std_test.go b/vendor/gopkg.in/square/go-jose.v2/json_std_test.go deleted file mode 100644 index 8acd97a4..00000000 --- a/vendor/gopkg.in/square/go-jose.v2/json_std_test.go +++ /dev/null @@ -1,124 +0,0 @@ -// +build std_json - -/*- - * Copyright 2014 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 jose - -import ( - "testing" -) - -type CaseInsensitive struct { - A int `json:"TEST"` -} - -type UnicodeTest struct { - Sig string `json:"sig"` -} - -func TestUnicodeComparison(t *testing.T) { - // Some tests from RFC 7515, Section 10.13 - raw := []byte(`{"\u0073ig":"foo"}`) - var ut UnicodeTest - err := UnmarshalJSON(raw, &ut) - if err != nil { - t.Error(err) - } - - if ut.Sig != "foo" { - t.Error("strings 'sig' and '\\u0073ig' should be equal") - } -} - -func TestCaseInsensitiveJSON(t *testing.T) { - raw := []byte(`{"test":42}`) - var ci CaseInsensitive - err := UnmarshalJSON(raw, &ci) - if err != nil { - t.Error(err) - } - - if ci.A != 42 { - t.Errorf("parsing JSON should be case-insensitive (got %v)", ci) - } -} - -func TestParseCaseInsensitiveJWE(t *testing.T) { - invalidJWE := `{"protected":"eyJlbmMiOiJYWVoiLCJBTEciOiJYWVoifQo","encrypted_key":"QUJD","iv":"QUJD","ciphertext":"QUJD","tag":"QUJD"}` - _, err := ParseEncrypted(invalidJWE) - if err != nil { - t.Error("Unable to parse message with case-invalid headers", invalidJWE) - } -} - -func TestParseCaseInsensitiveJWS(t *testing.T) { - invalidJWS := `{"PAYLOAD":"CUJD","signatures":[{"protected":"e30","signature":"CUJD"}]}` - _, err := ParseSigned(invalidJWS) - if err != nil { - t.Error("Unable to parse message with case-invalid headers", invalidJWS) - } -} - -var JWKSetDuplicates = stripWhitespace(`{ - "keys": [{ - "kty": "RSA", - "kid": "exclude-me", - "use": "sig", - "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT - -O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqV - wGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj- - oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde - 3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuC - LqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5g - HdrNP5zw", - "e": "AQAB" - }], - "keys": [{ - "kty": "RSA", - "kid": "include-me", - "use": "sig", - "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT - -O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqV - wGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj- - oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde - 3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuC - LqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5g - HdrNP5zw", - "e": "AQAB" - }], - "custom": "exclude-me", - "custom": "include-me" - }`) - -func TestDuplicateJWKSetMembersIgnored(t *testing.T) { - type CustomSet struct { - JSONWebKeySet - CustomMember string `json:"custom"` - } - data := []byte(JWKSetDuplicates) - var set CustomSet - UnmarshalJSON(data, &set) - if len(set.Keys) != 1 { - t.Error("expected only one key in set") - } - if set.Keys[0].KeyID != "include-me" { - t.Errorf("expected key with kid: \"include-me\", got: %s", set.Keys[0].KeyID) - } - if set.CustomMember != "include-me" { - t.Errorf("expected custom member value: \"include-me\", got: %s", set.CustomMember) - } -} diff --git a/vendor/gopkg.in/square/go-jose.v2/jwe.go b/vendor/gopkg.in/square/go-jose.v2/jwe.go index b3358a63..b6e29c6a 100644 --- a/vendor/gopkg.in/square/go-jose.v2/jwe.go +++ b/vendor/gopkg.in/square/go-jose.v2/jwe.go @@ -18,6 +18,7 @@ package jose import ( "encoding/base64" + "encoding/json" "fmt" "strings" ) @@ -112,7 +113,7 @@ func ParseEncrypted(input string) (*JSONWebEncryption, error) { // parseEncryptedFull parses a message in compact format. func parseEncryptedFull(input string) (*JSONWebEncryption, error) { var parsed rawJSONWebEncryption - err := UnmarshalJSON([]byte(input), &parsed) + err := json.Unmarshal([]byte(input), &parsed) if err != nil { return nil, err } @@ -134,7 +135,7 @@ func (parsed *rawJSONWebEncryption) sanitized() (*JSONWebEncryption, error) { } if parsed.Protected != nil && len(parsed.Protected.bytes()) > 0 { - err := UnmarshalJSON(parsed.Protected.bytes(), &obj.protected) + err := json.Unmarshal(parsed.Protected.bytes(), &obj.protected) if err != nil { return nil, fmt.Errorf("square/go-jose: invalid protected header: %s, %s", err, parsed.Protected.base64()) } diff --git a/vendor/gopkg.in/square/go-jose.v2/jwk.go b/vendor/gopkg.in/square/go-jose.v2/jwk.go index 0b5b238a..9abe3002 100644 --- a/vendor/gopkg.in/square/go-jose.v2/jwk.go +++ b/vendor/gopkg.in/square/go-jose.v2/jwk.go @@ -21,10 +21,15 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rsa" + "crypto/x509" + "encoding/base64" + "errors" "fmt" "math/big" "reflect" "strings" + + "gopkg.in/square/go-jose.v2/json" ) // rawJSONWebKey represents a public or private key in JWK format, used for parsing/serializing. @@ -49,14 +54,17 @@ type rawJSONWebKey struct { Dp *byteBuffer `json:"dp,omitempty"` Dq *byteBuffer `json:"dq,omitempty"` Qi *byteBuffer `json:"qi,omitempty"` + // Certificates + X5c []string `json:"x5c,omitempty"` } // JSONWebKey represents a public or private key in JWK format. type JSONWebKey struct { - Key interface{} - KeyID string - Algorithm string - Use string + Key interface{} + Certificates []*x509.Certificate + KeyID string + Algorithm string + Use string } // MarshalJSON serializes the given key to its JSON representation. @@ -87,13 +95,17 @@ func (k JSONWebKey) MarshalJSON() ([]byte, error) { raw.Alg = k.Algorithm raw.Use = k.Use - return MarshalJSON(raw) + for _, cert := range k.Certificates { + raw.X5c = append(raw.X5c, base64.StdEncoding.EncodeToString(cert.Raw)) + } + + return json.Marshal(raw) } // UnmarshalJSON reads a key from its JSON representation. func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) { var raw rawJSONWebKey - err = UnmarshalJSON(data, &raw) + err = json.Unmarshal(data, &raw) if err != nil { return err } @@ -121,6 +133,19 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) { if err == nil { *k = JSONWebKey{Key: key, KeyID: raw.Kid, Algorithm: raw.Alg, Use: raw.Use} } + + k.Certificates = make([]*x509.Certificate, len(raw.X5c)) + for i, cert := range raw.X5c { + raw, err := base64.StdEncoding.DecodeString(cert) + if err != nil { + return err + } + k.Certificates[i], err = x509.ParseCertificate(raw) + if err != nil { + return err + } + } + return } @@ -192,7 +217,17 @@ func (k *JSONWebKey) Thumbprint(hash crypto.Hash) ([]byte, error) { return h.Sum(nil), nil } -// Valid checks that the key contains the expected parameters +// IsPublic returns true if the JWK represents a public key (not symmetric, not private). +func (k *JSONWebKey) IsPublic() bool { + switch k.Key.(type) { + case *ecdsa.PublicKey, *rsa.PublicKey: + return true + default: + return false + } +} + +// Valid checks that the key contains the expected parameters. func (k *JSONWebKey) Valid() bool { if k.Key == nil { return false @@ -253,13 +288,20 @@ func (key rawJSONWebKey) ecPublicKey() (*ecdsa.PublicKey, error) { } if key.X == nil || key.Y == nil { - return nil, fmt.Errorf("square/go-jose: invalid EC key, missing x/y values") + return nil, errors.New("square/go-jose: invalid EC key, missing x/y values") + } + + x := key.X.bigInt() + y := key.Y.bigInt() + + if !curve.IsOnCurve(x, y) { + return nil, errors.New("square/go-jose: invalid EC key, X/Y are not on declared curve") } return &ecdsa.PublicKey{ Curve: curve, - X: key.X.bigInt(), - Y: key.Y.bigInt(), + X: x, + Y: y, }, nil } @@ -368,11 +410,18 @@ func (key rawJSONWebKey) ecPrivateKey() (*ecdsa.PrivateKey, error) { return nil, fmt.Errorf("square/go-jose: invalid EC private key, missing x/y/d values") } + x := key.X.bigInt() + y := key.Y.bigInt() + + if !curve.IsOnCurve(x, y) { + return nil, errors.New("square/go-jose: invalid EC key, X/Y are not on declared curve") + } + return &ecdsa.PrivateKey{ PublicKey: ecdsa.PublicKey{ Curve: curve, - X: key.X.bigInt(), - Y: key.Y.bigInt(), + X: x, + Y: y, }, D: key.D.bigInt(), }, nil diff --git a/vendor/gopkg.in/square/go-jose.v2/jwk_test.go b/vendor/gopkg.in/square/go-jose.v2/jwk_test.go index 3009d424..86364bd2 100644 --- a/vendor/gopkg.in/square/go-jose.v2/jwk_test.go +++ b/vendor/gopkg.in/square/go-jose.v2/jwk_test.go @@ -22,12 +22,57 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rsa" + "crypto/x509" "encoding/hex" "math/big" "reflect" "testing" + + "gopkg.in/square/go-jose.v2/json" ) +// Test chain of two X.509 certificates +var testCertificates, _ = x509.ParseCertificates(fromBase64Bytes(` +MIIDfDCCAmSgAwIBAgIJANWAkzF7PA8/MA0GCSqGSIb3DQEBCwUAMFUxCzAJ +BgNVBAYTAlVTMQswCQYDVQQIEwJDQTEQMA4GA1UEChMHY2VydGlnbzEQMA4G +A1UECxMHZXhhbXBsZTEVMBMGA1UEAxMMZXhhbXBsZS1sZWFmMB4XDTE2MDYx +MDIyMTQxMVoXDTIzMDQxNTIyMTQxMVowVTELMAkGA1UEBhMCVVMxCzAJBgNV +BAgTAkNBMRAwDgYDVQQKEwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRUw +EwYDVQQDEwxleGFtcGxlLWxlYWYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQC7stSvfQyGuHw3v34fisqIdDXberrFoFk9ht/WdXgYzX2uLNKd +sR/J5sbWSl8K/5djpzj31eIzqU69w8v7SChM5x9bouDsABHz3kZucx5cSafE +gJojysBkcrq3VY+aJanzbL+qErYX+lhRpPcZK6JMWIwar8Y3B2la4yWwieec +w2/WfEVvG0M/DOYKnR8QHFsfl3US1dnBM84czKPyt9r40gDk2XiH/lGts5a9 +4rAGvbr8IMCtq0mA5aH3Fx3mDSi3+4MZwygCAHrF5O5iSV9rEI+m2+7j2S+j +HDUnvV+nqcpb9m6ENECnYX8FD2KcqlOjTmw8smDy09N2Np6i464lAgMBAAGj +TzBNMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAsBgNVHREEJTAj +hwR/AAABhxAAAAAAAAAAAAAAAAAAAAABgglsb2NhbGhvc3QwDQYJKoZIhvcN +AQELBQADggEBAGM4aa/qrURUweZBIwZYv8O9b2+r4l0HjGAh982/B9sMlM05 +kojyDCUGvj86z18Lm8mKr4/y+i0nJ+vDIksEvfDuzw5ALAXGcBzPJKtICUf7 +LstA/n9NNpshWz0kld9ylnB5mbUzSFDncVyeXkEf5sGQXdIIZT9ChRBoiloS +aa7dvBVCcsX1LGP2LWqKtD+7nUnw5qCwtyAVT8pthEUxFTpywoiJS5ZdzeEx +8MNGvUeLFj2kleqPF78EioEQlSOxViCuctEtnQuPcDLHNFr10byTZY9roObi +qdsJLMVvb2XliJjAqaPa9AkYwGE6xHw2ispwg64Rse0+AtKups19WIUwggNT +MIICO6ADAgECAgkAqD4tCWKt9/AwDQYJKoZIhvcNAQELBQAwVTELMAkGA1UE +BhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQKEwdjZXJ0aWdvMRAwDgYDVQQL +EwdleGFtcGxlMRUwEwYDVQQDEwxleGFtcGxlLXJvb3QwHhcNMTYwNjEwMjIx +NDExWhcNMjMwNDE1MjIxNDExWjBVMQswCQYDVQQGEwJVUzELMAkGA1UECBMC +Q0ExEDAOBgNVBAoTB2NlcnRpZ28xEDAOBgNVBAsTB2V4YW1wbGUxFTATBgNV +BAMTDGV4YW1wbGUtcm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAMo4ShKI2MxDz/NQVxBbz0tbD5R5NcobA0NKkaPKLyMEpnWVY9ucyauM +joNn1F568cfOoF0pm3700U8UTPt2MMxEHIi4mFG/OF8UF+Voh1J42Tb42lRo +W5RRR3ogh4+7QB1G94nxkYddHAJ4QMhUJlLigFg8c6Ff/MxYODy9I7ilLFOM +Zzsjx8fFpRKRXNQFt471P/V4WTSba7GzdTOJRyTZf/xipF36n8RoEQPvyde8 +pEAsCC4oDOrEiCTdxw8rRJVAU0Wr55XX+qjxyi55C6oykIC/BWR+lUqGd7IL +Y2Uyt/OVxllt8b+KuVKNCfn4TFlfgizLWkJRs6JV9KuwJ20CAwEAAaMmMCQw +DgYDVR0PAQH/BAQDAgIEMBIGA1UdEwEB/wQIMAYBAf8CAQAwDQYJKoZIhvcN +AQELBQADggEBAIsQlTrm9NT6gts0cs4JHp8AutuMrvGyLpIUOlJcEybvgxaz +LebIMGZek5w3yEJiCyCK9RdNDP3Kdc/+nM6PhvzfPOVo58+0tMCYyEpZVXhD +zmasNDP4fMbiUpczvx5OwPw/KuhwD+1ITuZUQnQlqXgTYoj9n39+qlgUsHos +WXHmfzd6Fcz96ADSXg54IL2cEoJ41Q3ewhA7zmWWPLMAl21aex2haiAmzqqN +xXyfZTnGNnE3lkV1yVguOrqDZyMRdcxDFvxvtmEeMtYV2Mc/zlS9ccrcOkrc +mZSDxthLu3UMl98NA2NrCGWwzJwpk36vQ0PRSbibsCMarFspP8zbIoU=`)) + func TestCurveSize(t *testing.T) { size256 := curveSize(elliptic.P256()) size384 := curveSize(elliptic.P384()) @@ -155,6 +200,38 @@ func TestRoundtripEcPrivate(t *testing.T) { } } +func TestRoundtripX5C(t *testing.T) { + jwk := JSONWebKey{ + Key: rsaTestKey, + KeyID: "bar", + Algorithm: "foo", + Certificates: testCertificates, + } + + jsonbar, err := jwk.MarshalJSON() + if err != nil { + t.Error("problem marshaling", err) + } + + var jwk2 JSONWebKey + err = jwk2.UnmarshalJSON(jsonbar) + if err != nil { + t.Error("problem unmarshalling", err) + } + + if !reflect.DeepEqual(testCertificates, jwk2.Certificates) { + t.Error("Certificates not equal", jwk.Certificates, jwk2.Certificates) + } + + jsonbar2, err := jwk2.MarshalJSON() + if err != nil { + t.Error("problem marshaling", err) + } + if !bytes.Equal(jsonbar, jsonbar2) { + t.Error("roundtrip should not lose information") + } +} + func TestMarshalUnmarshal(t *testing.T) { kid := "DEADBEEF" @@ -210,7 +287,7 @@ func TestMarshalNonPointer(t *testing.T) { "n": "vd7rZIoTLEe-z1_8G1FcXSw9CQFEJgV4g9V277sER7yx5Qjz_Pkf2YVth6wwwFJEmzc0hoKY-MMYFNwBE4hQHw" }`) var parsedKey JSONWebKey - err := UnmarshalJSON(keyJSON, &parsedKey) + err := json.Unmarshal(keyJSON, &parsedKey) if err != nil { t.Errorf("Error unmarshalling key: %v", err) return @@ -218,7 +295,7 @@ func TestMarshalNonPointer(t *testing.T) { ek := EmbedsKey{ Key: parsedKey, } - out, err := MarshalJSON(ek) + out, err := json.Marshal(ek) if err != nil { t.Errorf("Error marshalling JSON: %v", err) return @@ -382,6 +459,38 @@ var cookbookJWKs = []string{ "qi":"lSQi-w9CpyUReMErP1RsBLk7wNtOvs5EQpPqmuMvqW57NBUczScEoPwmUqq abu9V0-Py4dQ57_bapoKRu1R90bvuFnU63SHWEFglZQvJDMeAvmj4sm-Fp0o Yu_neotgQ0hzbI5gry7ajdYy9-2lNx_76aBZoOUu9HCJ-UsfSOI8"}`), + + // X.509 Certificate Chain + stripWhitespace(`{"kty":"RSA", + "use":"sig", + "kid":"1b94c", + "n":"vrjOfz9Ccdgx5nQudyhdoR17V-IubWMeOZCwX_jj0hgAsz2J_pqYW08 + PLbK_PdiVGKPrqzmDIsLI7sA25VEnHU1uCLNwBuUiCO11_-7dYbsr4iJmG0Q + u2j8DsVyT1azpJC_NG84Ty5KKthuCaPod7iI7w0LK9orSMhBEwwZDCxTWq4a + YWAchc8t-emd9qOvWtVMDC2BXksRngh6X5bUYLy6AyHKvj-nUy1wgzjYQDwH + MTplCoLtU-o-8SNnZ1tmRoGE9uJkBLdh5gFENabWnU5m1ZqZPdwS-qo-meMv + VfJb6jJVWRpl2SUtCnYG2C32qvbWbjZ_jBPD5eunqsIo1vQ", + "e":"AQAB", + "x5c": + ["MIIDQjCCAiqgAwIBAgIGATz/FuLiMA0GCSqGSIb3DQEBBQUAMGIxCzAJB + gNVBAYTAlVTMQswCQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVudmVyMRwwGgYD + VQQKExNQaW5nIElkZW50aXR5IENvcnAuMRcwFQYDVQQDEw5CcmlhbiBDYW1 + wYmVsbDAeFw0xMzAyMjEyMzI5MTVaFw0xODA4MTQyMjI5MTVaMGIxCzAJBg + NVBAYTAlVTMQswCQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVudmVyMRwwGgYDV + QQKExNQaW5nIElkZW50aXR5IENvcnAuMRcwFQYDVQQDEw5CcmlhbiBDYW1w + YmVsbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL64zn8/QnH + YMeZ0LncoXaEde1fiLm1jHjmQsF/449IYALM9if6amFtPDy2yvz3YlRij66 + s5gyLCyO7ANuVRJx1NbgizcAblIgjtdf/u3WG7K+IiZhtELto/A7Fck9Ws6 + SQvzRvOE8uSirYbgmj6He4iO8NCyvaK0jIQRMMGQwsU1quGmFgHIXPLfnpn + fajr1rVTAwtgV5LEZ4Iel+W1GC8ugMhyr4/p1MtcIM42EA8BzE6ZQqC7VPq + PvEjZ2dbZkaBhPbiZAS3YeYBRDWm1p1OZtWamT3cEvqqPpnjL1XyW+oyVVk + aZdklLQp2Btgt9qr21m42f4wTw+Xrp6rCKNb0CAwEAATANBgkqhkiG9w0BA + QUFAAOCAQEAh8zGlfSlcI0o3rYDPBB07aXNswb4ECNIKG0CETTUxmXl9KUL + +9gGlqCz5iWLOgWsnrcKcY0vXPG9J1r9AqBNTqNgHq2G03X09266X5CpOe1 + zFo+Owb1zxtp3PehFdfQJ610CDLEaS9V9Rqp17hCyybEpOGVwe8fnk+fbEL + 2Bo3UPGrpsHzUoaGpDftmWssZkhpBJKVMJyf/RuP2SmmaIzmnw9JiSlYhzo + 4tpzd5rFXhjRbg4zW9C+2qok+2+qDM1iJ684gPHMIY8aLWrdgQTxkumGmTq + gawR+N5MDtdPTEQ0XfIBc2cJEUyMTY5MPvACWpkA6SdS4xSvdXK3IVfOWA=="]}`), } // SHA-256 thumbprints of the above keys, hex-encoded @@ -390,6 +499,7 @@ var cookbookJWKThumbprints = []string{ "747ae2dd2003664aeeb21e4753fe7402846170a16bc8df8f23a8cf06d3cbe793", "f63838e96077ad1fc01c3f8405774dedc0641f558ebb4b40dccf5f9b6d66a932", "0fc478f8579325fcee0d4cbc6d9d1ce21730a6e97e435d6008fb379b0ebe47d4", + "0ddb05bfedbec2070fa037324ba397396561d3425d6d69245570c261dc49dee3", } func TestWebKeyVectorsValid(t *testing.T) { @@ -429,16 +539,16 @@ func TestMarshalUnmarshalJWKSet(t *testing.T) { set.Keys = append(set.Keys, jwk1) set.Keys = append(set.Keys, jwk2) - jsonbar, err := MarshalJSON(&set) + jsonbar, err := json.Marshal(&set) if err != nil { t.Error("problem marshalling set", err) } var set2 JSONWebKeySet - err = UnmarshalJSON(jsonbar, &set2) + err = json.Unmarshal(jsonbar, &set2) if err != nil { t.Error("problem unmarshalling set", err) } - jsonbar2, err := MarshalJSON(&set2) + jsonbar2, err := json.Marshal(&set2) if err != nil { t.Error("problem marshalling set", err) } @@ -467,7 +577,7 @@ func TestJWKSymmetricKey(t *testing.T) { sample2 := `{"kty":"oct","k":"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow","kid":"HMAC key used in JWS spec Appendix A.1 example"}` var jwk1 JSONWebKey - UnmarshalJSON([]byte(sample1), &jwk1) + json.Unmarshal([]byte(sample1), &jwk1) if jwk1.Algorithm != "A128KW" { t.Errorf("expected Algorithm to be A128KW, but was '%s'", jwk1.Algorithm) @@ -478,7 +588,7 @@ func TestJWKSymmetricKey(t *testing.T) { } var jwk2 JSONWebKey - UnmarshalJSON([]byte(sample2), &jwk2) + json.Unmarshal([]byte(sample2), &jwk2) if jwk2.KeyID != "HMAC key used in JWS spec Appendix A.1 example" { t.Errorf("expected KeyID to be 'HMAC key used in JWS spec Appendix A.1 example', but was '%s'", jwk2.KeyID) diff --git a/vendor/gopkg.in/square/go-jose.v2/jws.go b/vendor/gopkg.in/square/go-jose.v2/jws.go index 0c2978d1..5555f43a 100644 --- a/vendor/gopkg.in/square/go-jose.v2/jws.go +++ b/vendor/gopkg.in/square/go-jose.v2/jws.go @@ -18,8 +18,11 @@ package jose import ( "encoding/base64" + "errors" "fmt" "strings" + + "gopkg.in/square/go-jose.v2/json" ) // rawJSONWebSignature represents a raw JWS JSON object. Used for parsing/serializing. @@ -40,7 +43,10 @@ type rawSignatureInfo struct { // JSONWebSignature represents a signed JWS object after parsing. type JSONWebSignature struct { - payload []byte + payload []byte + // Signatures attached to this object (may be more than one for multi-sig). + // Be careful about accessing these directly, prefer to use Verify() or + // VerifyMulti() to ensure that the data you're getting is verified. Signatures []Signature } @@ -57,7 +63,7 @@ type Signature struct { original *rawSignatureInfo } -// ParseSigned parses an encrypted message in compact or full serialization format. +// ParseSigned parses a signed message in compact or full serialization format. func ParseSigned(input string) (*JSONWebSignature, error) { input = stripWhitespace(input) if strings.HasPrefix(input, "{") { @@ -95,7 +101,7 @@ func (obj JSONWebSignature) computeAuthData(signature *Signature) []byte { // parseSignedFull parses a message in full format. func parseSignedFull(input string) (*JSONWebSignature, error) { var parsed rawJSONWebSignature - err := UnmarshalJSON([]byte(input), &parsed) + err := json.Unmarshal([]byte(input), &parsed) if err != nil { return nil, err } @@ -119,12 +125,13 @@ func (parsed *rawJSONWebSignature) sanitized() (*JSONWebSignature, error) { signature := Signature{} if parsed.Protected != nil && len(parsed.Protected.bytes()) > 0 { signature.protected = &rawHeader{} - err := UnmarshalJSON(parsed.Protected.bytes(), signature.protected) + err := json.Unmarshal(parsed.Protected.bytes(), signature.protected) if err != nil { return nil, err } } + // Check that there is not a nonce in the unprotected header if parsed.Header != nil && parsed.Header.Nonce != "" { return nil, ErrUnprotectedNonce } @@ -147,13 +154,20 @@ func (parsed *rawJSONWebSignature) sanitized() (*JSONWebSignature, error) { } signature.Header = signature.mergedHeaders().sanitized() + + // As per RFC 7515 Section 4.1.3, only public keys are allowed to be embedded. + jwk := signature.Header.JSONWebKey + if jwk != nil && (!jwk.Valid() || !jwk.IsPublic()) { + return nil, errors.New("square/go-jose: invalid embedded jwk, must be public key") + } + obj.Signatures = append(obj.Signatures, signature) } for i, sig := range parsed.Signatures { if sig.Protected != nil && len(sig.Protected.bytes()) > 0 { obj.Signatures[i].protected = &rawHeader{} - err := UnmarshalJSON(sig.Protected.bytes(), obj.Signatures[i].protected) + err := json.Unmarshal(sig.Protected.bytes(), obj.Signatures[i].protected) if err != nil { return nil, err } @@ -164,14 +178,20 @@ func (parsed *rawJSONWebSignature) sanitized() (*JSONWebSignature, error) { return nil, ErrUnprotectedNonce } + obj.Signatures[i].Header = obj.Signatures[i].mergedHeaders().sanitized() obj.Signatures[i].Signature = sig.Signature.bytes() + // As per RFC 7515 Section 4.1.3, only public keys are allowed to be embedded. + jwk := obj.Signatures[i].Header.JSONWebKey + if jwk != nil && (!jwk.Valid() || !jwk.IsPublic()) { + return nil, errors.New("square/go-jose: invalid embedded jwk, must be public key") + } + // Copy value of sig original := sig obj.Signatures[i].header = sig.Header obj.Signatures[i].original = &original - obj.Signatures[i].Header = obj.Signatures[i].mergedHeaders().sanitized() } return obj, nil diff --git a/vendor/gopkg.in/square/go-jose.v2/jws_test.go b/vendor/gopkg.in/square/go-jose.v2/jws_test.go index c3547b04..abdbc73b 100644 --- a/vendor/gopkg.in/square/go-jose.v2/jws_test.go +++ b/vendor/gopkg.in/square/go-jose.v2/jws_test.go @@ -22,6 +22,16 @@ import ( "testing" ) +func TestEmbeddedHMAC(t *testing.T) { + // protected: {"alg":"HS256", "jwk":{"kty":"oct", "k":"MTEx"}}, aka HMAC key. + msg := `{"payload":"TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ","protected":"eyJhbGciOiJIUzI1NiIsICJqd2siOnsia3R5Ijoib2N0IiwgImsiOiJNVEV4In19","signature":"lvo41ZZsuHwQvSh0uJtEXRR3vmuBJ7in6qMoD7p9jyo"}` + + _, err := ParseSigned(msg) + if err == nil { + t.Error("should not allow parsing JWS with embedded JWK with HMAC key") + } +} + func TestCompactParseJWS(t *testing.T) { // Should parse msg := "eyJhbGciOiJYWVoifQ.cGF5bG9hZA.c2lnbmF0dXJl" diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/builder.go b/vendor/gopkg.in/square/go-jose.v2/jwt/builder.go index 0fe90b25..969b5a9d 100644 --- a/vendor/gopkg.in/square/go-jose.v2/jwt/builder.go +++ b/vendor/gopkg.in/square/go-jose.v2/jwt/builder.go @@ -17,111 +17,211 @@ package jwt -import "gopkg.in/square/go-jose.v2" +import ( + "reflect" + + "gopkg.in/square/go-jose.v2/json" + + "gopkg.in/square/go-jose.v2" +) // Builder is a utility for making JSON Web Tokens. Calls can be chained, and // errors are accumulated until the final call to CompactSerialize/FullSerialize. -type Builder struct { - transform func([]byte) (serializer, payload, error) - payload payload - serializer serializer - err error -} - -type payload func(interface{}) ([]byte, error) - -type serializer interface { - FullSerialize() string +type Builder interface { + // Claims encodes claims into JWE/JWS form. Multiple calls will merge claims + // into single JSON object. + Claims(i interface{}) Builder + // Token builds a JSONWebToken from provided data. + Token() (*JSONWebToken, error) + // FullSerialize serializes a token using the full serialization format. + FullSerialize() (string, error) + // CompactSerialize serializes a token using the compact serialization format. CompactSerialize() (string, error) } +type builder struct { + payload map[string]interface{} + err error +} + +type signedBuilder struct { + builder + sig jose.Signer +} + +type encryptedBuilder struct { + builder + enc jose.Encrypter +} + // Signed creates builder for signed tokens. -func Signed(sig jose.Signer) *Builder { - return &Builder{ - transform: func(b []byte) (serializer, payload, error) { - s, err := sig.Sign(b) - if err != nil { - return nil, nil, err - } - return s, s.Verify, nil - }, +func Signed(sig jose.Signer) Builder { + return &signedBuilder{ + sig: sig, } } // Encrypted creates builder for encrypted tokens. -func Encrypted(enc jose.Encrypter) *Builder { - return &Builder{ - transform: func(b []byte) (serializer, payload, error) { - e, err := enc.Encrypt(b) - if err != nil { - return nil, nil, err - } - return e, e.Decrypt, nil - }, +func Encrypted(enc jose.Encrypter) Builder { + return &encryptedBuilder{ + enc: enc, } } -// Claims encodes claims into the builder. -func (b *Builder) Claims(c interface{}) *Builder { - if b.transform == nil { - panic("Signer/Encrypter not set") +func (b builder) claims(i interface{}) builder { + if b.err != nil { + return b } - if b.payload != nil { - panic("Claims already set") - } - - raw, err := marshalClaims(c) - if err != nil { - return &Builder{ - err: err, + m, ok := i.(map[string]interface{}) + switch { + case ok: + return b.merge(m) + case reflect.Indirect(reflect.ValueOf(i)).Kind() == reflect.Struct: + m, err := normalize(i) + if err != nil { + return builder{ + err: err, + } + } + return b.merge(m) + default: + return builder{ + err: ErrInvalidClaims, } } +} - ser, pl, err := b.transform(raw) - return &Builder{ - transform: b.transform, - serializer: ser, - payload: pl, - err: err, +func normalize(i interface{}) (map[string]interface{}, error) { + m := make(map[string]interface{}) + + raw, err := json.Marshal(i) + if err != nil { + return nil, err + } + if err := json.Unmarshal(raw, &m); err != nil { + return nil, err + } + + return m, nil +} + +func (b *builder) merge(m map[string]interface{}) builder { + p := make(map[string]interface{}) + for k, v := range b.payload { + p[k] = v + } + for k, v := range m { + p[k] = v + } + + return builder{ + payload: p, } } -// Token builds a JSONWebToken from provided data. -func (b *Builder) Token() (*JSONWebToken, error) { +func (b *builder) token(p func(interface{}) ([]byte, error), h []jose.Header) (*JSONWebToken, error) { + return &JSONWebToken{ + payload: p, + Headers: h, + }, nil +} + +func (b *signedBuilder) Claims(i interface{}) Builder { + return &signedBuilder{ + builder: b.builder.claims(i), + sig: b.sig, + } +} + +func (b *signedBuilder) Token() (*JSONWebToken, error) { + sig, err := b.sign() + if err != nil { + return nil, err + } + + h := make([]jose.Header, len(sig.Signatures)) + for i, v := range sig.Signatures { + h[i] = v.Header + } + + return b.builder.token(sig.Verify, h) +} + +func (b *signedBuilder) CompactSerialize() (string, error) { + sig, err := b.sign() + if err != nil { + return "", err + } + + 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 } - if b.payload == nil { - return nil, ErrInvalidClaims + p, err := json.Marshal(b.payload) + if err != nil { + return nil, err } - return &JSONWebToken{b.payload}, nil + return b.sig.Sign(p) } -// FullSerialize serializes a token using the full serialization format. -func (b *Builder) FullSerialize() (string, error) { +func (b *encryptedBuilder) Claims(i interface{}) Builder { + return &encryptedBuilder{ + builder: b.builder.claims(i), + enc: b.enc, + } +} + +func (b *encryptedBuilder) CompactSerialize() (string, error) { + enc, err := b.encrypt() + if err != nil { + return "", err + } + + 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 { + return nil, err + } + + return b.builder.token(enc.Decrypt, []jose.Header{enc.Header}) +} + +func (b *encryptedBuilder) encrypt() (*jose.JSONWebEncryption, error) { if b.err != nil { - return "", b.err + return nil, b.err } - if b.serializer == nil { - return "", ErrInvalidClaims + p, err := json.Marshal(b.payload) + if err != nil { + return nil, err } - return b.serializer.FullSerialize(), nil -} - -// CompactSerialize serializes a token using the compact serialization format. -func (b *Builder) CompactSerialize() (string, error) { - if b.err != nil { - return "", b.err - } - - if b.serializer == nil { - return "", ErrInvalidClaims - } - - return b.serializer.CompactSerialize() + return b.enc.Encrypt(p) } diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/builder_test.go b/vendor/gopkg.in/square/go-jose.v2/jwt/builder_test.go new file mode 100644 index 00000000..1738e509 --- /dev/null +++ b/vendor/gopkg.in/square/go-jose.v2/jwt/builder_test.go @@ -0,0 +1,415 @@ +/*- + * 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 ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/hex" + "encoding/pem" + "errors" + "io" + "reflect" + "sort" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "gopkg.in/square/go-jose.v2" +) + +type testClaims struct { + Subject string `json:"sub"` +} + +type invalidMarshalClaims struct { +} + +var errInvalidMarshalClaims = errors.New("Failed marshaling invalid claims.") + +func (c invalidMarshalClaims) MarshalJSON() ([]byte, error) { + return nil, errInvalidMarshalClaims +} + +var sampleClaims = Claims{ + Subject: "42", + IssuedAt: NewNumericDate(time.Date(2016, 1, 1, 0, 0, 0, 0, time.UTC)), + Issuer: "issuer", + Audience: Audience{"a1", "a2"}, +} + +func TestBuilderCustomClaimsNonPointer(t *testing.T) { + jwt, err := Signed(rsaSigner).Claims(testClaims{"foo"}).CompactSerialize() + require.NoError(t, err, "Error creating JWT.") + + parsed, err := ParseSigned(jwt) + require.NoError(t, err, "Error parsing JWT.") + + out := &testClaims{} + if assert.NoError(t, parsed.Claims(&testPrivRSAKey1.PublicKey, out), "Error unmarshaling claims.") { + assert.Equal(t, "foo", out.Subject) + } +} + +func TestBuilderCustomClaimsPointer(t *testing.T) { + jwt, err := Signed(rsaSigner).Claims(&testClaims{"foo"}).CompactSerialize() + require.NoError(t, err, "Error creating JWT.") + + parsed, err := ParseSigned(jwt) + require.NoError(t, err, "Error parsing JWT.") + + out := &testClaims{} + if assert.NoError(t, parsed.Claims(&testPrivRSAKey1.PublicKey, out), "Error unmarshaling claims.") { + assert.Equal(t, "foo", out.Subject) + } +} + +func TestBuilderMergeClaims(t *testing.T) { + jwt, err := Signed(rsaSigner). + Claims(&Claims{ + Subject: "42", + }). + Claims(map[string]interface{}{ + "Scopes": []string{"read:users"}, + }). + CompactSerialize() + require.NoError(t, err, "Error creating JWT.") + + parsed, err := ParseSigned(jwt) + require.NoError(t, err, "Error parsing JWT.") + + out := make(map[string]interface{}) + if assert.NoError(t, parsed.Claims(&testPrivRSAKey1.PublicKey, &out), "Error unmarshaling claims.") { + assert.Equal(t, map[string]interface{}{ + "sub": "42", + "Scopes": []interface{}{"read:users"}, + }, out) + } + + _, err = Signed(rsaSigner).Claims("invalid-claims").Claims(&testClaims{"foo"}).CompactSerialize() + assert.Equal(t, err, ErrInvalidClaims) + + _, err = Signed(rsaSigner).Claims(&invalidMarshalClaims{}).CompactSerialize() + assert.EqualError(t, err, "json: error calling MarshalJSON for type *jwt.invalidMarshalClaims: Failed marshaling invalid claims.") +} + +func TestSignedFullSerializeAndToken(t *testing.T) { + b := Signed(rsaSigner).Claims(&testClaims{"foo"}) + + jwt, err := b.FullSerialize() + require.NoError(t, err, "Error creating JWT.") + parsed, err := ParseSigned(jwt) + require.NoError(t, err, "Error parsing JWT.") + out := &testClaims{} + if assert.NoError(t, parsed.Claims(&testPrivRSAKey1.PublicKey, &out), "Error unmarshaling claims.") { + assert.Equal(t, &testClaims{ + Subject: "foo", + }, out) + } + + jwt2, err := b.Token() + require.NoError(t, err, "Error creating JWT.") + out2 := &testClaims{} + if assert.NoError(t, jwt2.Claims(&testPrivRSAKey1.PublicKey, &out2), "Error unmarshaling claims.") { + assert.Equal(t, &testClaims{ + Subject: "foo", + }, out2) + } + + b2 := Signed(rsaSigner).Claims(&invalidMarshalClaims{}) + _, err = b2.FullSerialize() + require.EqualError(t, err, "json: error calling MarshalJSON for type *jwt.invalidMarshalClaims: Failed marshaling invalid claims.") + _, err = b2.Token() + require.EqualError(t, err, "json: error calling MarshalJSON for type *jwt.invalidMarshalClaims: Failed marshaling invalid claims.") +} + +func TestEncryptedFullSerializeAndToken(t *testing.T) { + recipient := jose.Recipient{ + Algorithm: jose.RSA1_5, + Key: testPrivRSAKey1.Public(), + } + encrypter, err := jose.NewEncrypter(jose.A128CBC_HS256, recipient, nil) + require.NoError(t, err, "Error creating encrypter.") + + b := Encrypted(encrypter).Claims(&testClaims{"foo"}) + + jwt, err := b.FullSerialize() + require.NoError(t, err, "Error creating JWT.") + parsed, err := ParseEncrypted(jwt) + require.NoError(t, err, "Error parsing JWT.") + out := &testClaims{} + if assert.NoError(t, parsed.Claims(testPrivRSAKey1, &out)) { + assert.Equal(t, &testClaims{ + Subject: "foo", + }, out) + } + + jwt2, err := b.Token() + require.NoError(t, err, "Error creating JWT.") + out2 := &testClaims{} + if assert.NoError(t, jwt2.Claims(testPrivRSAKey1, &out2)) { + assert.Equal(t, &testClaims{ + Subject: "foo", + }, out2) + } + + b2 := Encrypted(encrypter).Claims(&invalidMarshalClaims{}) + + _, err = b2.FullSerialize() + require.EqualError(t, err, "json: error calling MarshalJSON for type *jwt.invalidMarshalClaims: Failed marshaling invalid claims.") + _, err = b2.Token() + require.EqualError(t, err, "json: error calling MarshalJSON for type *jwt.invalidMarshalClaims: Failed marshaling invalid claims.") +} + +func TestBuilderHeadersSigner(t *testing.T) { + tests := []struct { + Keys []*rsa.PrivateKey + Claims interface{} + }{ + { + Keys: []*rsa.PrivateKey{testPrivRSAKey1}, + Claims: &Claims{Issuer: "foo"}, + }, + { + Keys: []*rsa.PrivateKey{testPrivRSAKey1, testPrivRSAKey2}, + Claims: &Claims{Issuer: "foo"}, + }, + } + + for i, tc := range tests { + wantKeyIDs := make([]string, len(tc.Keys)) + signingKeys := make([]jose.SigningKey, len(tc.Keys)) + + for j, key := range tc.Keys { + keyIDBytes := make([]byte, 20) + if _, err := io.ReadFull(rand.Reader, keyIDBytes); err != nil { + t.Fatalf("failed to read random bytes: %v", err) + } + keyID := hex.EncodeToString(keyIDBytes) + + wantKeyIDs[j] = keyID + signingKeys[j] = jose.SigningKey{ + Algorithm: jose.RS256, + Key: &jose.JSONWebKey{ + KeyID: keyID, + Algorithm: "RSA", + Key: key, + }, + } + } + + signer, err := jose.NewMultiSigner(signingKeys, nil) + if err != nil { + t.Errorf("case %d: NewMultiSigner(): %v", i, err) + continue + } + + var token string + if len(tc.Keys) == 1 { + token, err = Signed(signer).Claims(tc.Claims).CompactSerialize() + } else { + token, err = Signed(signer).Claims(tc.Claims).FullSerialize() + } + if err != nil { + t.Errorf("case %d: failed to create token: %v", i, err) + continue + } + jws, err := jose.ParseSigned(token) + if err != nil { + t.Errorf("case %d: parse signed: %v", i, err) + continue + } + gotKeyIDs := make([]string, len(jws.Signatures)) + for i, sig := range jws.Signatures { + gotKeyIDs[i] = sig.Header.KeyID + } + sort.Strings(wantKeyIDs) + sort.Strings(gotKeyIDs) + if !reflect.DeepEqual(wantKeyIDs, gotKeyIDs) { + t.Errorf("case %d: wanted=%q got=%q", i, wantKeyIDs, gotKeyIDs) + } + } +} + +func TestBuilderHeadersEncrypter(t *testing.T) { + key := testPrivRSAKey1 + claims := &Claims{Issuer: "foo"} + + keyIDBytes := make([]byte, 20) + if _, err := io.ReadFull(rand.Reader, keyIDBytes); err != nil { + t.Fatalf("failed to read random bytes: %v", err) + } + keyID := hex.EncodeToString(keyIDBytes) + + wantKeyID := keyID + recipient := jose.Recipient{ + Algorithm: jose.RSA1_5, + Key: key.Public(), + KeyID: keyID, + } + + encrypter, err := jose.NewEncrypter(jose.A128CBC_HS256, recipient, nil) + if err != nil { + t.Errorf("NewEncrypter(): %v", err) + return + } + + token, err := Encrypted(encrypter).Claims(claims).CompactSerialize() + if err != nil { + t.Errorf("failed to create token: %v", err) + return + } + jwe, err := jose.ParseEncrypted(token) + if err != nil { + t.Errorf("parse signed: %v", err) + return + } + if gotKeyID := jwe.Header.KeyID; gotKeyID != wantKeyID { + t.Errorf("wanted=%q got=%q", wantKeyID, gotKeyID) + } +} + +func BenchmarkMapClaims(b *testing.B) { + m := map[string]interface{}{ + "sub": "42", + "iat": 1451606400, + "iss": "issuer", + "aud": []string{"a1", "a2"}, + } + + for i := 0; i < b.N; i++ { + Signed(rsaSigner).Claims(m) + } +} + +func BenchmarkStructClaims(b *testing.B) { + for i := 0; i < b.N; i++ { + Signed(rsaSigner).Claims(sampleClaims) + } +} + +func BenchmarkSignedCompactSerializeRSA(b *testing.B) { + tb := Signed(rsaSigner).Claims(sampleClaims) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + tb.CompactSerialize() + } +} + +func BenchmarkSignedCompactSerializeSHA(b *testing.B) { + tb := Signed(hmacSigner).Claims(sampleClaims) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + tb.CompactSerialize() + } +} + +func mustUnmarshalRSA(data string) *rsa.PrivateKey { + block, _ := pem.Decode([]byte(data)) + if block == nil { + panic("failed to decode PEM data") + } + key, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + panic("failed to parse RSA key: " + err.Error()) + } + if key, ok := key.(*rsa.PrivateKey); ok { + return key + } + panic("key is not of type *rsa.PrivateKey") +} + +func mustMakeSigner(alg jose.SignatureAlgorithm, k interface{}) jose.Signer { + sig, err := jose.NewSigner(jose.SigningKey{Algorithm: alg, Key: k}, nil) + if err != nil { + panic("failed to create signer:" + err.Error()) + } + + return sig +} + +var ( + sharedKey = []byte("secret") + sharedEncryptionKey = []byte("itsa16bytesecret") + + testPrivRSAKey1 = mustUnmarshalRSA(`-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDIHBvDHAr7jh8h +xaqBCl11fjI9YZtdC5b3HtXTXZW3c2dIOImNUjffT8POP6p5OpzivmC1om7iOyuZ +3nJjC9LT3zqqs3f2i5d4mImxEuqG6uWdryFfkp0uIv5VkjVO+iQWd6pDAPGP7r1Z +foXCleyCtmyNH4JSkJneNPOk/4BxO8vcvRnCMT/Gv81IT6H+OQ6OovWOuJr8RX9t +1wuCjC9ezZxeI9ONffhiO5FMrVh5H9LJTl3dPOVa4aEcOvgd45hBmvxAyXqf8daE +6Kl2O7vQ4uwgnSTVXYIIjCjbepuersApIMGx/XPSgiU1K3Xtah/TBvep+S3VlwPc +q/QH25S9AgMBAAECggEAe+y8XKYfPw4SxY1uPB+5JSwT3ON3nbWxtjSIYy9Pqp5z +Vcx9kuFZ7JevQSk4X38m7VzM8282kC/ono+d8yy9Uayq3k/qeOqV0X9Vti1qxEbw +ECkG1/MqGApfy4qSLOjINInDDV+mOWa2KJgsKgdCwuhKbVMYGB2ozG2qfYIlfvlY +vLcBEpGWmswJHNmkcjTtGFIyJgPbsI6ndkkOeQbqQKAaadXtG1xUzH+vIvqaUl/l +AkNf+p4qhPkHsoAWXf1qu9cYa2T8T+mEo79AwlgVC6awXQWNRTiyClDJC7cu6NBy +ZHXCLFMbalzWF9qeI2OPaFX2x3IBWrbyDxcJ4TSdQQKBgQD/Fp/uQonMBh1h4Vi4 +HlxZdqSOArTitXValdLFGVJ23MngTGV/St4WH6eRp4ICfPyldsfcv6MZpNwNm1Rn +lB5Gtpqpby1dsrOSfvVbY7U3vpLnd8+hJ/lT5zCYt5Eor46N6iWRkYWzNe4PixiF +z1puGUvFCbZdeeACVrPLmW3JKQKBgQDI0y9WTf8ezKPbtap4UEE6yBf49ftohVGz +p4iD6Ng1uqePwKahwoVXKOc179CjGGtW/UUBORAoKRmxdHajHq6LJgsBxpaARz21 +COPy99BUyp9ER5P8vYn63lC7Cpd/K7uyMjaz1DAzYBZIeVZHIw8O9wuGNJKjRFy9 +SZyD3V0ddQKBgFMdohrWH2QVEfnUnT3Q1rJn0BJdm2bLTWOosbZ7G72TD0xAWEnz +sQ1wXv88n0YER6X6YADziEdQykq8s/HT91F/KkHO8e83zP8M0xFmGaQCOoelKEgQ +aFMIX3NDTM7+9OoUwwz9Z50PE3SJFAJ1n7eEEoYvNfabQXxBl+/dHEKRAoGAPEvU +EaiXacrtg8EWrssB2sFLGU/ZrTciIbuybFCT4gXp22pvXXAHEvVP/kzDqsRhLhwb +BNP6OuSkNziNikpjA5pngZ/7fgZly54gusmW/m5bxWdsUl0iOXVYbeAvPlqGH2me +LP4Pfs1hw17S/cbT9Z1NE31jbavP4HFikeD73SUCgYEArQfuudml6ei7XZ1Emjq8 +jZiD+fX6e6BD/ISatVnuyZmGj9wPFsEhY2BpLiAMQHMDIvH9nlKzsFvjkTPB86qG +jCh3D67Os8eSBk5uRC6iW3Fc4DXvB5EFS0W9/15Sl+V5vXAcrNMpYS82OTSMG2Gt +b9Ym/nxaqyTu0PxajXkKm5Q= +-----END PRIVATE KEY-----`) + + testPrivRSAKey2 = mustUnmarshalRSA(`-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCxJ09jkXZ5Okyq +FrEKrs+GTzZRvoLziyzDTIZLJC6BVryau4gaFjuBG+pnm4z53oDP0XVnjFsx1mBw +R6RHeXlXbxLXsMfJpMzU9I2SRen9DokpD187CAnjLOoN9QRl1h8CA+sqR5Jw9mdl +mdaBKC99M9QYAPK3vGNfPC4soo8LDSBiemmt5raL4WSfoYh/6qg5rHUTymY28uxV +ew3I9Yp+3ltIw+WlRDtW5l+MM5CSUofjj2zcgcG3LEuPtvyZ+CSObxxcZZugm9zc +JdiazNyUxtX8yAj3Xg8Hde0jt0QDXv7A+U0KMVi9lX6PJEaNj4tOhOmQhJVMzAyr +1W/bifZVAgMBAAECggEAduKnn21GMZLTUi4KP94SvNK55F/Sp7hVoPbhBNpSL1BT +IBAMBV24LyvZwhAcqq8MiOrLPGNv6+EvNQqPD7xQl0GeRouHeCYVpDA+NdSfc8jm +eVysjwQVBpTkudsdSW5JvuN8VRJVD2P8/a0gy+p4/C/k/Prd6DoQAiBz6FZrYoEd +iYgIegHOMXWd4vzO3ENOWSIUI6ci7Aro+Y0Z75kfiVokAGhUcFgrZ58E82fBYh8I +cxO20oMnucGrLicQzj536jx4wX3Cdd4jr9UVEJ9ZII1ldlp03nZlFLXqJH1547Aq +ZM+3vVcBGoJ8T9ZQ4VDAL++0K2DLC9JkTARAYCEi/QKBgQDebIc1+2zblhQtVQ/e +IbEErZcB7v+TkUoRoBfR0lj7bKBFJgRe37fgu1xf95/s63okdnOw/OuQqtGmgx/J +TL3yULBdNcwTCRm41t+cqoGymjK0VRbqk6CWBId0E3r5TaCVWedk2JI2XwTvIJ1A +eDiqfJeDHUD44yaonwbysj9ZDwKBgQDL5VQfTppVaJk2PXNwhAkRQklZ8RFmt/7p +yA3dddQNdwMk4Fl8F7QuO1gBxDiHdnwIrlEOz6fTsM3LwIS+Q12P1vYFIhpo7HDB +wvjfMwCPxBIS4jI28RgcAf0VbZ/+CHAm6bb9iDwsjXhh1J5oOm5VKnju6/rPH/QY ++md40pnSWwKBgBnKPbdNquafNUG4XjmkcHEZa6wGuU20CAGZLYnfuP+WLdM2wET7 +7cc6ElDyVnHTL/twXKPF/85rcBm9lH7zzgZ9wqVcKoh+gqQDDjSNNLKv3Hc6cojK +i1E5vzb/Vz/290q5/PGdhv6U7+6GOpWSGwfxoGPMjY8OT5o3rkeP0XaTAoGBALLR +GQmr4eZtqZDMK+XNpjYgsDvVE7HGRCW7cY17vNFiQruglloiX778BJ7n+7uxye3D +EwuuSj15ncLHwKMsaW2w1GqEEi1azzjfSWxWSnPLPR6aifdtUfueMtsMHXio5dL6 +vaV0SXG5UI5b7eDy/bhrW0wOYRQtreIKGZz49jZpAoGBAIvxYngkLwmq6g6MmnAc +YK4oT6YAm2wfSy2mzpEQP5r1igp1rN7T46o7FMUPDLS9wK3ESAaIYe01qT6Yftcc +5qF+yiOGDTr9XQiHwe4BcyrNEMfUjDhDU5ao2gH8+t1VGr1KspLsUNbedrJwZsY4 +UCZVKEEDHzKfLO/iBgKjJQF7 +-----END PRIVATE KEY-----`) + + rsaSigner = mustMakeSigner(jose.RS256, testPrivRSAKey1) + hmacSigner = mustMakeSigner(jose.HS256, sharedKey) +) diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/claims.go b/vendor/gopkg.in/square/go-jose.v2/jwt/claims.go index 4b21015c..60de9400 100644 --- a/vendor/gopkg.in/square/go-jose.v2/jwt/claims.go +++ b/vendor/gopkg.in/square/go-jose.v2/jwt/claims.go @@ -18,66 +18,98 @@ package jwt import ( + "encoding/json" + "strconv" "time" - - "gopkg.in/square/go-jose.v2" ) // Claims represents public claim values (as specified in RFC 7519). type Claims struct { - Issuer string `json:"-"` - Subject string `json:"-"` - Audience []string `json:"-"` - Expiry time.Time `json:"-"` - NotBefore time.Time `json:"-"` - IssuedAt time.Time `json:"-"` - ID string `json:"-"` + Issuer string `json:"iss,omitempty"` + Subject string `json:"sub,omitempty"` + Audience Audience `json:"aud,omitempty"` + Expiry NumericDate `json:"exp,omitempty"` + NotBefore NumericDate `json:"nbf,omitempty"` + IssuedAt NumericDate `json:"iat,omitempty"` + ID string `json:"jti,omitempty"` } -type rawClaims struct { - Iss string `json:"iss,omitempty"` - Sub string `json:"sub,omitempty"` - Aud audience `json:"aud,omitempty"` - Exp NumericDate `json:"exp,omitempty"` - Nbf NumericDate `json:"nbf,omitempty"` - Iat NumericDate `json:"iat,omitempty"` - Jti string `json:"jti,omitempty"` -} +// NumericDate represents date and time as the number of seconds since the +// epoch, including leap seconds. Non-integer values can be represented +// in the serialized format, but we round to the nearest second. +type NumericDate int64 -func (c *Claims) marshalJSON() ([]byte, error) { - t := rawClaims{ - Iss: c.Issuer, - Sub: c.Subject, - Aud: audience(c.Audience), - Exp: TimeToNumericDate(c.Expiry), - Nbf: TimeToNumericDate(c.NotBefore), - Iat: TimeToNumericDate(c.IssuedAt), - Jti: c.ID, +// NewNumericDate constructs NumericDate from time.Time value. +func NewNumericDate(t time.Time) NumericDate { + if t.IsZero() { + return NumericDate(0) } - b, err := jose.MarshalJSON(t) + // While RFC 7519 technically states that NumericDate values may be + // non-integer values, we don't bother serializing timestamps in + // claims with sub-second accurancy and just round to the nearest + // second instead. Not convined sub-second accuracy is useful here. + return NumericDate(t.Unix()) +} +// MarshalJSON serializes the given NumericDate into its JSON representation. +func (n NumericDate) MarshalJSON() ([]byte, error) { + return []byte(strconv.FormatInt(int64(n), 10)), nil +} + +// UnmarshalJSON reads a date from its JSON representation. +func (n *NumericDate) UnmarshalJSON(b []byte) error { + s := string(b) + + f, err := strconv.ParseFloat(s, 64) if err != nil { - return nil, err + return ErrUnmarshalNumericDate } - return b, err + *n = NumericDate(f) + return nil } -func (c *Claims) unmarshalJSON(b []byte) error { - t := rawClaims{} +// Time returns time.Time representation of NumericDate. +func (n NumericDate) Time() time.Time { + return time.Unix(int64(n), 0) +} - if err := jose.UnmarshalJSON(b, &t); err != nil { +// Audience represents the recipents that the token is intended for. +type Audience []string + +// UnmarshalJSON reads an audience from its JSON representation. +func (s *Audience) UnmarshalJSON(b []byte) error { + var v interface{} + if err := json.Unmarshal(b, &v); err != nil { return err } - c.Issuer = t.Iss - c.Subject = t.Sub - c.Audience = []string(t.Aud) - c.Expiry = t.Exp.Time() - c.NotBefore = t.Nbf.Time() - c.IssuedAt = t.Iat.Time() - c.ID = t.Jti + switch v := v.(type) { + case string: + *s = []string{v} + case []interface{}: + a := make([]string, len(v)) + for i, e := range v { + s, ok := e.(string) + if !ok { + return ErrUnmarshalAudience + } + a[i] = s + } + *s = a + default: + return ErrUnmarshalAudience + } return nil } + +func (s Audience) Contains(v string) bool { + for _, a := range s { + if a == v { + return true + } + } + return false +} diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/claims_test.go b/vendor/gopkg.in/square/go-jose.v2/jwt/claims_test.go index 45087534..6799aab2 100644 --- a/vendor/gopkg.in/square/go-jose.v2/jwt/claims_test.go +++ b/vendor/gopkg.in/square/go-jose.v2/jwt/claims_test.go @@ -21,6 +21,8 @@ import ( "testing" "time" + "gopkg.in/square/go-jose.v2/json" + "github.com/stretchr/testify/assert" ) @@ -28,14 +30,15 @@ func TestEncodeClaims(t *testing.T) { now := time.Date(2016, 1, 1, 0, 0, 0, 0, time.UTC) c := Claims{ - Issuer: "issuer", - Subject: "subject", - Audience: []string{"a1", "a2"}, - IssuedAt: now, - Expiry: now.Add(1 * time.Hour), + Issuer: "issuer", + Subject: "subject", + Audience: Audience{"a1", "a2"}, + NotBefore: NewNumericDate(time.Time{}), + IssuedAt: NewNumericDate(now), + Expiry: NewNumericDate(now.Add(1 * time.Hour)), } - b, err := c.marshalJSON() + b, err := json.Marshal(c) assert.NoError(t, err) expected := `{"iss":"issuer","sub":"subject","aud":["a1","a2"],"exp":1451610000,"iat":1451606400}` @@ -47,12 +50,31 @@ func TestDecodeClaims(t *testing.T) { now := time.Date(2016, 1, 1, 0, 0, 0, 0, time.UTC) c := Claims{} - err := c.unmarshalJSON(s) - assert.NoError(t, err) + if err := json.Unmarshal(s, &c); assert.NoError(t, err) { + assert.Equal(t, "issuer", c.Issuer) + assert.Equal(t, "subject", c.Subject) + assert.Equal(t, Audience{"a1", "a2"}, c.Audience) + assert.True(t, now.Equal(c.IssuedAt.Time())) + assert.True(t, now.Add(1*time.Hour).Equal(c.Expiry.Time())) + } - assert.Equal(t, "issuer", c.Issuer) - assert.Equal(t, "subject", c.Subject) - assert.Equal(t, []string{"a1", "a2"}, c.Audience) - assert.True(t, now.Equal(c.IssuedAt)) - assert.True(t, now.Add(1*time.Hour).Equal(c.Expiry)) + s2 := []byte(`{"aud": "a1"}`) + c2 := Claims{} + if err := json.Unmarshal(s2, &c2); assert.NoError(t, err) { + assert.Equal(t, Audience{"a1"}, c2.Audience) + } + + invalid := []struct { + Raw string + Err error + }{ + {`{"aud": 5}`, ErrUnmarshalAudience}, + {`{"aud": ["foo", 5, "bar"]}`, ErrUnmarshalAudience}, + {`{"exp": "invalid"}`, ErrUnmarshalNumericDate}, + } + + for _, v := range invalid { + c := Claims{} + assert.Equal(t, v.Err, json.Unmarshal([]byte(v.Raw), &c)) + } } diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/example_test.go b/vendor/gopkg.in/square/go-jose.v2/jwt/example_test.go new file mode 100644 index 00000000..9d4ee007 --- /dev/null +++ b/vendor/gopkg.in/square/go-jose.v2/jwt/example_test.go @@ -0,0 +1,200 @@ +/*- + * 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_test + +import ( + "fmt" + "strings" + "time" + + "gopkg.in/square/go-jose.v2" + "gopkg.in/square/go-jose.v2/jwt" +) + +var sharedKey = []byte("secret") +var sharedEncryptionKey = []byte("itsa16bytesecret") +var signer, _ = jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: sharedKey}, &jose.SignerOptions{}) + +func ExampleParseSigned() { + raw := `eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJpc3N1ZXIiLCJzdWIiOiJzdWJqZWN0In0.gpHyA1B1H6X4a4Edm9wo7D3X2v3aLSDBDG2_5BzXYe0` + tok, err := jwt.ParseSigned(raw) + if err != nil { + panic(err) + } + + out := jwt.Claims{} + if err := tok.Claims(sharedKey, &out); err != nil { + panic(err) + } + fmt.Printf("iss: %s, sub: %s\n", out.Issuer, out.Subject) + // Output: iss: issuer, sub: subject +} + +func ExampleParseEncrypted() { + key := []byte("itsa16bytesecret") + raw := `eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIn0..jg45D9nmr6-8awml.z-zglLlEw9MVkYHi-Znd9bSwc-oRGbqKzf9WjXqZxno.kqji2DiZHZmh-1bLF6ARPw` + tok, err := jwt.ParseEncrypted(raw) + if err != nil { + panic(err) + } + + out := jwt.Claims{} + if err := tok.Claims(key, &out); err != nil { + panic(err) + } + fmt.Printf("iss: %s, sub: %s\n", out.Issuer, out.Subject) + //Output: iss: issuer, sub: subject +} + +func ExampleClaims_Validate() { + cl := jwt.Claims{ + Subject: "subject", + Issuer: "issuer", + NotBefore: jwt.NewNumericDate(time.Date(2016, 1, 1, 0, 0, 0, 0, time.UTC)), + Expiry: jwt.NewNumericDate(time.Date(2016, 1, 1, 0, 15, 0, 0, time.UTC)), + Audience: jwt.Audience{"leela", "fry"}, + } + + err := cl.Validate(jwt.Expected{ + Issuer: "issuer", + Time: time.Date(2016, 1, 1, 0, 10, 0, 0, time.UTC), + }) + if err != nil { + panic(err) + } + + fmt.Printf("valid!") + // Output: valid! +} + +func ExampleClaims_Validate_withParse() { + raw := `eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJpc3N1ZXIiLCJzdWIiOiJzdWJqZWN0In0.gpHyA1B1H6X4a4Edm9wo7D3X2v3aLSDBDG2_5BzXYe0` + tok, err := jwt.ParseSigned(raw) + if err != nil { + panic(err) + } + + cl := jwt.Claims{} + if err := tok.Claims(sharedKey, &cl); err != nil { + panic(err) + } + + err = cl.Validate(jwt.Expected{ + Issuer: "issuer", + Subject: "subject", + }) + if err != nil { + panic(err) + } + + fmt.Printf("valid!") + // Output: valid! +} + +func ExampleSigned() { + key := []byte("secret") + sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: key}, &jose.SignerOptions{}) + if err != nil { + panic(err) + } + + cl := jwt.Claims{ + Subject: "subject", + Issuer: "issuer", + NotBefore: jwt.NewNumericDate(time.Date(2016, 1, 1, 0, 0, 0, 0, time.UTC)), + Audience: jwt.Audience{"leela", "fry"}, + } + raw, err := jwt.Signed(sig).Claims(cl).CompactSerialize() + if err != nil { + panic(err) + } + + fmt.Println(raw) + // Output: eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOlsibGVlbGEiLCJmcnkiXSwiaXNzIjoiaXNzdWVyIiwibmJmIjoxLjQ1MTYwNjRlKzA5LCJzdWIiOiJzdWJqZWN0In0.uazfxZNgnlLdNDK7JkuYj3LlT4jSyEDG8EWISBPUuME +} + +func ExampleEncrypted() { + enc, err := jose.NewEncrypter(jose.A128GCM, jose.Recipient{Algorithm: jose.DIRECT, Key: sharedEncryptionKey}, nil) + if err != nil { + panic(err) + } + + cl := jwt.Claims{ + Subject: "subject", + Issuer: "issuer", + } + raw, err := jwt.Encrypted(enc).Claims(cl).CompactSerialize() + if err != nil { + panic(err) + } + + fmt.Println(raw) +} + +func ExampleSigned_multipleClaims() { + c := &jwt.Claims{ + Subject: "subject", + Issuer: "issuer", + } + c2 := struct { + Scopes []string + }{ + []string{"foo", "bar"}, + } + raw, err := jwt.Signed(signer).Claims(c).Claims(c2).CompactSerialize() + if err != nil { + panic(err) + } + + fmt.Println(raw) + // Output: eyJhbGciOiJIUzI1NiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sImlzcyI6Imlzc3VlciIsInN1YiI6InN1YmplY3QifQ.esKOIsmwkudr_gnfnB4SngxIr-7pspd5XzG3PImfQ6Y +} + +func ExampleJSONWebToken_Claims_map() { + raw := `eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJpc3N1ZXIiLCJzdWIiOiJzdWJqZWN0In0.gpHyA1B1H6X4a4Edm9wo7D3X2v3aLSDBDG2_5BzXYe0` + tok, err := jwt.ParseSigned(raw) + if err != nil { + panic(err) + } + + out := make(map[string]interface{}) + if err := tok.Claims(sharedKey, &out); err != nil { + panic(err) + } + + fmt.Printf("iss: %s, sub: %s\n", out["iss"], out["sub"]) + // Output: iss: issuer, sub: subject +} + +func ExampleJSONWebToken_Claims_multiple() { + raw := `eyJhbGciOiJIUzI1NiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sImlzcyI6Imlzc3VlciIsInN1YiI6InN1YmplY3QifQ.esKOIsmwkudr_gnfnB4SngxIr-7pspd5XzG3PImfQ6Y` + tok, err := jwt.ParseSigned(raw) + if err != nil { + panic(err) + } + + out := jwt.Claims{} + out2 := struct { + Scopes []string + }{} + if err := tok.Claims(sharedKey, &out, &out2); err != nil { + panic(err) + } + fmt.Printf("iss: %s, sub: %s, scopes: %s\n", out.Issuer, out.Subject, strings.Join(out2.Scopes, ",")) + // Output: iss: issuer, sub: subject, scopes: foo,bar +} diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/json.go b/vendor/gopkg.in/square/go-jose.v2/jwt/json.go deleted file mode 100644 index 8a49c968..00000000 --- a/vendor/gopkg.in/square/go-jose.v2/jwt/json.go +++ /dev/null @@ -1,179 +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 ( - "math" - "reflect" - "strconv" - "time" - - "gopkg.in/square/go-jose.v2" -) - -// NumericDate represents date and time as the number of seconds since the -// epoch, including leap seconds. Non-integer values can be represented -// in the serialized format, but we round to the nearest second. -type NumericDate int64 - -// TimeToNumericDate converts time.Time value into NumericDate. -func TimeToNumericDate(t time.Time) NumericDate { - if t.IsZero() { - return NumericDate(0) - } - - // While RFC 7519 technically states that NumericDate values may be - // non-integer values, we don't bother serializing timestamps in - // claims with sub-second accurancy and just round to the nearest - // second instead. Not convined sub-second accuracy is useful here. - return NumericDate(t.Unix()) -} - -// MarshalJSON serializes the given NumericDate into its JSON representation. -func (n NumericDate) MarshalJSON() ([]byte, error) { - return []byte(strconv.FormatInt(int64(n), 10)), nil -} - -// UnmarshalJSON reads a date from its JSON representation. -func (n *NumericDate) UnmarshalJSON(b []byte) error { - s := string(b) - - f, err := strconv.ParseFloat(s, 64) - if err != nil { - return ErrUnmarshalNumericDate - } - - *n = NumericDate(f) - return nil -} - -// Time returns time.Time representation of NumericDate. -func (n NumericDate) Time() time.Time { - i, f := math.Modf(float64(n)) - return time.Unix(int64(i), int64(f*float64(time.Second))) -} - -type audience []string - -func (s *audience) UnmarshalJSON(b []byte) error { - var v interface{} - if err := jose.UnmarshalJSON(b, &v); err != nil { - return err - } - - switch v := v.(type) { - case string: - *s = append(*s, v) - case []interface{}: - a := make([]string, len(v)) - for i, e := range v { - s, ok := e.(string) - if !ok { - return ErrUnmarshalAudience - } - a[i] = s - } - *s = a - default: - return ErrUnmarshalAudience - } - - return nil -} - -var claimsType = reflect.TypeOf((*Claims)(nil)).Elem() - -func publicClaims(cl interface{}) (*Claims, error) { - v := reflect.ValueOf(cl) - if v.IsNil() || v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct { - return nil, ErrInvalidClaims - } - - v = v.Elem() - f := v.FieldByName("Claims") - if !f.IsValid() || f.Type() != claimsType { - return nil, nil - } - - c := f.Addr().Interface().(*Claims) - return c, nil -} - -func marshalClaims(cl interface{}) ([]byte, error) { - switch cl := cl.(type) { - case *Claims: - return cl.marshalJSON() - case map[string]interface{}: - return jose.MarshalJSON(cl) - } - - public, err := publicClaims(cl) - if err != nil { - return nil, err - } - // i doesn't contain nested jwt.Claims - if public == nil { - return jose.MarshalJSON(cl) - } - - // marshal jwt.Claims - b1, err := public.marshalJSON() - if err != nil { - return nil, err - } - - // marshal private claims - b2, err := jose.MarshalJSON(cl) - if err != nil { - return nil, err - } - - // merge claims - r := make([]byte, len(b1)+len(b2)-1) - copy(r, b1) - r[len(b1)-1] = ',' - copy(r[len(b1):], b2[1:]) - - return r, nil -} - -func unmarshalClaims(b []byte, cl interface{}) error { - switch cl := cl.(type) { - case *Claims: - return cl.unmarshalJSON(b) - case map[string]interface{}: - return jose.UnmarshalJSON(b, cl) - } - - if err := jose.UnmarshalJSON(b, cl); err != nil { - return err - } - - public, err := publicClaims(cl) - if err != nil { - return err - } - // unmarshal jwt.Claims - if public != nil { - if err := public.unmarshalJSON(b); err != nil { - return err - } - } - - return nil -} diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/jwt.go b/vendor/gopkg.in/square/go-jose.v2/jwt/jwt.go index 6ad6fbe0..73adfcc2 100644 --- a/vendor/gopkg.in/square/go-jose.v2/jwt/jwt.go +++ b/vendor/gopkg.in/square/go-jose.v2/jwt/jwt.go @@ -17,20 +17,31 @@ package jwt -import "gopkg.in/square/go-jose.v2" +import ( + "gopkg.in/square/go-jose.v2" + "gopkg.in/square/go-jose.v2/json" +) // JSONWebToken represents a JSON Web Token (as specified in RFC7519). type JSONWebToken struct { payload func(k interface{}) ([]byte, error) + Headers []jose.Header } // Claims deserializes a JSONWebToken into dest using the provided key. -func (t *JSONWebToken) Claims(dest interface{}, key interface{}) error { +func (t *JSONWebToken) Claims(key interface{}, dest ...interface{}) error { b, err := t.payload(key) if err != nil { return err } - return unmarshalClaims(b, dest) + + for _, d := range dest { + if err := json.Unmarshal(b, d); err != nil { + return err + } + } + + return nil } // ParseSigned parses token from JWS form. @@ -39,8 +50,12 @@ func ParseSigned(s string) (*JSONWebToken, error) { 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{sig.Verify}, nil + return &JSONWebToken{sig.Verify, headers}, nil } // ParseEncrypted parses token from JWE form. @@ -50,5 +65,5 @@ func ParseEncrypted(s string) (*JSONWebToken, error) { return nil, err } - return &JSONWebToken{enc.Decrypt}, nil + return &JSONWebToken{enc.Decrypt, []jose.Header{enc.Header}}, nil } diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/jwt_test.go b/vendor/gopkg.in/square/go-jose.v2/jwt/jwt_test.go index 515b6462..d322da3a 100644 --- a/vendor/gopkg.in/square/go-jose.v2/jwt/jwt_test.go +++ b/vendor/gopkg.in/square/go-jose.v2/jwt/jwt_test.go @@ -21,57 +21,96 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "gopkg.in/square/go-jose.v2" ) -var encryptionKey = []byte("secret") -var rawToken = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdWJqZWN0IiwiaXNzIjoiaXNzdWVyIiwic2NvcGVzIjpbInMxIiwiczIiXX0.Y6_PfQHrzRJ_Vlxij5VI07-pgDIuJNN3Z_g5sSaGQ0c` +var ( + hmacSignedToken = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdWJqZWN0IiwiaXNzIjoiaXNzdWVyIiwic2NvcGVzIjpbInMxIiwiczIiXX0.Y6_PfQHrzRJ_Vlxij5VI07-pgDIuJNN3Z_g5sSaGQ0c` + rsaSignedToken = `eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJpc3N1ZXIiLCJzY29wZXMiOlsiczEiLCJzMiJdLCJzdWIiOiJzdWJqZWN0In0.UDDtyK9gC9kyHltcP7E_XODsnqcJWZIiXeGmSAH7SE9YKy3N0KSfFIN85dCNjTfs6zvy4rkrCHzLB7uKAtzMearh3q7jL4nxbhUMhlUcs_9QDVoN4q_j58XmRqBqRnBk-RmDu9TgcV8RbErP4awpIhwWb5UU-hR__4_iNbHdKqwSUPDKYGlf5eicuiYrPxH8mxivk4LRD-vyRdBZZKBt0XIDnEU4TdcNCzAXojkftqcFWYsczwS8R4JHd1qYsMyiaWl4trdHZkO4QkeLe34z4ZAaPMt3wE-gcU-VoqYTGxz-K3Le2VaZ0r3j_z6bOInsv0yngC_cD1dCXMyQJWnWjQ` + invalidPayloadSignedToken = `eyJhbGciOiJIUzI1NiJ9.aW52YWxpZC1wYXlsb2Fk.ScBKKm18jcaMLGYDNRUqB5gVMRZl4DM6dh3ShcxeNgY` + invalidPartsSignedToken = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdWJqZWN0IiwiaXNzIjoiaXNzdWVyIiwic2NvcGVzIjpbInMxIiwiczIiXX0` + hmacEncryptedToken = `eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIn0..NZrU98U4QNO0y-u6.HSq5CvlmkUT1BPqLGZ4.1-zuiZ4RbHrTTUoA8Dvfhg` + rsaEncryptedToken = `eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.IvkVHHiI8JwwavvTR80xGjYvkzubMrZ-TDDx8k8SNJMEylfFfNUc7F2rC3WAABF_xmJ3SW2A6on-S6EAG97k0RsjqHHNqZuaFpDvjeuLqZFfYKzI45aCtkGG4C2ij2GbeySqJ784CcvFJPUWJ-6VPN2Ho2nhefUSqig0jE2IvOKy1ywTj_VBVBxF_dyXFnXwxPKGUQr3apxrWeRJfDh2Cf8YPBlLiRznjfBfwgePB1jP7WCZNwItj10L7hsT_YWEx01XJcbxHaXFLwKyVzwWaDhreFyaWMRbGqEfqVuOT34zfmhLDhQlgLLwkXrvYqX90NsQ9Ftg0LLIfRMbsfdgug.BFy2Tj1RZN8yq2Lk-kMiZQ.9Z0eOyPiv5cEzmXh64RlAQ36Uvz0WpZgqRcc2_69zHTmUOv0Vnl1I6ks8sTraUEvukAilolNBjBj47s0b4b-Og.VM8-eJg5ZsqnTqs0LtGX_Q` + invalidPayloadEncryptedToken = `eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIn0..T4jCS4Yyw1GCH0aW.y4gFaMITdBs_QZM8RKrL.6MPyk1cMVaOJFoNGlEuaRQ` + invalidPartsEncryptedToken = `eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIn0..NZrU98U4QNO0y-u6.HSq5CvlmkUT1BPqLGZ4` +) type customClaims struct { - Claims Scopes []string `json:"scopes,omitempty"` } func TestDecodeToken(t *testing.T) { - tok, err := ParseSigned(rawToken) - assert.NoError(t, err) - c := &Claims{} - if assert.NoError(t, tok.Claims(c, encryptionKey)) { - assert.Equal(t, c.Subject, "subject") - assert.Equal(t, c.Issuer, "issuer") + tok, err := ParseSigned(hmacSignedToken) + if assert.NoError(t, err, "Error parsing signed token.") { + c := &Claims{} + c2 := &customClaims{} + if assert.NoError(t, tok.Claims(sharedKey, c, c2)) { + assert.Equal(t, "subject", c.Subject) + assert.Equal(t, "issuer", c.Issuer) + assert.Equal(t, []string{"s1", "s2"}, c2.Scopes) + } + } + assert.EqualError(t, tok.Claims([]byte("invalid-secret")), "square/go-jose: error in cryptographic primitive") + + tok2, err := ParseSigned(rsaSignedToken) + if assert.NoError(t, err, "Error parsing encrypted token.") { + c := make(map[string]interface{}) + if assert.NoError(t, tok2.Claims(&testPrivRSAKey1.PublicKey, &c)) { + assert.Equal(t, map[string]interface{}{ + "sub": "subject", + "iss": "issuer", + "scopes": []interface{}{"s1", "s2"}, + }, c) + } + } + assert.EqualError(t, tok.Claims(&testPrivRSAKey2.PublicKey), "square/go-jose: error in cryptographic primitive") + + tok3, err := ParseSigned(invalidPayloadSignedToken) + if assert.NoError(t, err, "Error parsing signed token.") { + assert.Error(t, tok3.Claims(sharedKey, &Claims{}), "Expected unmarshaling claims to fail.") } - c2 := &customClaims{} - if assert.NoError(t, tok.Claims(c2, encryptionKey)) { - assert.Equal(t, c2.Subject, "subject") - assert.Equal(t, c2.Issuer, "issuer") - assert.Equal(t, c2.Scopes, []string{"s1", "s2"}) + _, err = ParseSigned(invalidPartsSignedToken) + assert.EqualError(t, err, "square/go-jose: compact JWS format must have three parts") + + tok4, err := ParseEncrypted(hmacEncryptedToken) + if assert.NoError(t, err, "Error parsing encrypted token.") { + c := Claims{} + if assert.NoError(t, tok4.Claims(sharedEncryptionKey, &c)) { + assert.Equal(t, "foo", c.Subject) + } + } + assert.EqualError(t, tok4.Claims([]byte("invalid-secret-key")), "square/go-jose: error in cryptographic primitive") + + tok5, err := ParseEncrypted(rsaEncryptedToken) + if assert.NoError(t, err, "Error parsing encrypted token.") { + c := make(map[string]interface{}) + if assert.NoError(t, tok5.Claims(testPrivRSAKey1, &c)) { + assert.Equal(t, map[string]interface{}{ + "sub": "subject", + "iss": "issuer", + "scopes": []interface{}{"s1", "s2"}, + }, c) + } + } + assert.EqualError(t, tok5.Claims(testPrivRSAKey2), "square/go-jose: error in cryptographic primitive") + + tok6, err := ParseEncrypted(invalidPayloadEncryptedToken) + if assert.NoError(t, err, "Error parsing encrypted token.") { + assert.Error(t, tok6.Claims(sharedEncryptionKey, &Claims{})) + } + + _, err = ParseEncrypted(invalidPartsEncryptedToken) + assert.EqualError(t, err, "square/go-jose: compact JWE format must have five parts") +} + +func BenchmarkDecodeSignedToken(b *testing.B) { + for i := 0; i < b.N; i++ { + ParseSigned(hmacSignedToken) } } -func TestEncodeToken(t *testing.T) { - signer, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: encryptionKey}, &jose.SignerOptions{}) - require.NoError(t, err) - - c := &customClaims{ - Claims: Claims{ - Subject: "subject", - Issuer: "issuer", - }, - Scopes: []string{"s1", "s2"}, - } - - raw, err := Signed(signer).Claims(c).CompactSerialize() - require.NoError(t, err) - - tok, err := ParseSigned(raw) - require.NoError(t, err) - - c2 := &customClaims{} - if assert.NoError(t, tok.Claims(c2, encryptionKey)) { - assert.Equal(t, c2.Subject, "subject") - assert.Equal(t, c2.Issuer, "issuer") - assert.Equal(t, c2.Scopes, []string{"s1", "s2"}) +func BenchmarkDecodeEncryptedHMACToken(b *testing.B) { + for i := 0; i < b.N; i++ { + ParseEncrypted(hmacEncryptedToken) } } diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/validation.go b/vendor/gopkg.in/square/go-jose.v2/jwt/validation.go index c84fc7ff..143af3dc 100644 --- a/vendor/gopkg.in/square/go-jose.v2/jwt/validation.go +++ b/vendor/gopkg.in/square/go-jose.v2/jwt/validation.go @@ -24,13 +24,19 @@ const ( DefaultLeeway = 1.0 * time.Minute ) -// Expected defines values used for claims validation. +// Expected defines values used for protected claims validation. +// If field has zero value then validation is skipped. type Expected struct { - Issuer string - Subject string - Audience []string - ID string - Time time.Time + // Issuer matches the "iss" claim exactly. + Issuer string + // Subject matches the "sub" claim exactly. + Subject string + // Audience matches the values in "aud" claim, regardless of their order. + Audience Audience + // ID matches the "jti" claim exactly. + ID string + // Time matches the "exp" and "ebf" claims with leeway. + Time time.Time } // WithTime copies expectations with new time. @@ -68,18 +74,18 @@ func (c Claims) ValidateWithLeeway(e Expected, leeway time.Duration) error { return ErrInvalidAudience } - for i, a := range e.Audience { - if a != c.Audience[i] { + for _, v := range e.Audience { + if !c.Audience.Contains(v) { return ErrInvalidAudience } } } - if !e.Time.IsZero() && e.Time.Add(leeway).Before(c.NotBefore) { + if !e.Time.IsZero() && e.Time.Add(leeway).Before(c.NotBefore.Time()) { return ErrNotValidYet } - if !e.Time.IsZero() && e.Time.Add(-leeway).After(c.Expiry) { + if !e.Time.IsZero() && e.Time.Add(-leeway).After(c.Expiry.Time()) { return ErrExpired } diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/validation_test.go b/vendor/gopkg.in/square/go-jose.v2/jwt/validation_test.go index a534b89a..952ddcc6 100644 --- a/vendor/gopkg.in/square/go-jose.v2/jwt/validation_test.go +++ b/vendor/gopkg.in/square/go-jose.v2/jwt/validation_test.go @@ -32,28 +32,32 @@ func TestFieldsMatch(t *testing.T) { ID: "42", } - assert.NoError(t, c.Validate(Expected{Issuer: "issuer"})) - err := c.Validate(Expected{Issuer: "invalid-issuer"}) - if assert.Error(t, err) { - assert.Equal(t, err, ErrInvalidIssuer) + valid := []Expected{ + {Issuer: "issuer"}, + {Subject: "subject"}, + {Audience: Audience{"a1", "a2"}}, + {Audience: Audience{"a2", "a1"}}, + {ID: "42"}, } - assert.NoError(t, c.Validate(Expected{Subject: "subject"})) - err = c.Validate(Expected{Subject: "invalid-subject"}) - if assert.Error(t, err) { - assert.Equal(t, err, ErrInvalidSubject) + for _, v := range valid { + assert.NoError(t, c.Validate(v)) } - assert.NoError(t, c.Validate(Expected{Audience: []string{"a1", "a2"}})) - err = c.Validate(Expected{Audience: []string{"invalid-audience"}}) - if assert.Error(t, err) { - assert.Equal(t, err, ErrInvalidAudience) + invalid := []struct { + Expected Expected + Error error + }{ + {Expected{Issuer: "invalid-issuer"}, ErrInvalidIssuer}, + {Expected{Subject: "invalid-subject"}, ErrInvalidSubject}, + {Expected{Audience: Audience{"a1"}}, ErrInvalidAudience}, + {Expected{Audience: Audience{"a1", "invalid-audience"}}, ErrInvalidAudience}, + {Expected{Audience: Audience{"invalid-audience"}}, ErrInvalidAudience}, + {Expected{ID: "invalid-id"}, ErrInvalidID}, } - assert.NoError(t, c.Validate(Expected{ID: "42"})) - err = c.Validate(Expected{ID: "invalid-id"}) - if assert.Error(t, err) { - assert.Equal(t, err, ErrInvalidID) + for _, v := range invalid { + assert.Equal(t, v.Error, c.Validate(v.Expected)) } } @@ -62,9 +66,9 @@ func TestExpiryAndNotBefore(t *testing.T) { twelveHoursAgo := now.Add(-12 * time.Hour) c := Claims{ - IssuedAt: twelveHoursAgo, - NotBefore: twelveHoursAgo, - Expiry: now, + IssuedAt: NewNumericDate(twelveHoursAgo), + NotBefore: NewNumericDate(twelveHoursAgo), + Expiry: NewNumericDate(now), } // expired - default leeway (1 minute) diff --git a/vendor/gopkg.in/square/go-jose.v2/signing.go b/vendor/gopkg.in/square/go-jose.v2/signing.go index a834e619..c314b125 100644 --- a/vendor/gopkg.in/square/go-jose.v2/signing.go +++ b/vendor/gopkg.in/square/go-jose.v2/signing.go @@ -20,6 +20,7 @@ import ( "crypto/ecdsa" "crypto/rsa" "encoding/base64" + "errors" "fmt" ) @@ -185,13 +186,50 @@ func (ctx *genericSigner) Sign(payload []byte) (*JSONWebSignature, error) { } // Verify validates the signature on the object and returns the payload. +// This function does not support multi-signature, if you desire multi-sig +// 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. func (obj JSONWebSignature) Verify(verificationKey interface{}) ([]byte, error) { verifier, err := newVerifier(verificationKey) if err != nil { return nil, err } - for _, signature := range obj.Signatures { + if len(obj.Signatures) > 1 { + return nil, errors.New("square/go-jose: too many signatures in payload; expecting only one") + } + + signature := obj.Signatures[0] + headers := signature.mergedHeaders() + if len(headers.Crit) > 0 { + // Unsupported crit header + return nil, ErrCryptoFailure + } + + input := obj.computeAuthData(&signature) + alg := SignatureAlgorithm(headers.Alg) + err = verifier.verifyPayload(input, signature.Signature, alg) + if err == nil { + return obj.payload, nil + } + + return nil, ErrCryptoFailure +} + +// VerifyMulti validates (one of the multiple) signatures on the object and +// 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. +func (obj JSONWebSignature) VerifyMulti(verificationKey interface{}) (int, Signature, []byte, error) { + verifier, err := newVerifier(verificationKey) + if err != nil { + return -1, Signature{}, nil, err + } + + for i, signature := range obj.Signatures { headers := signature.mergedHeaders() if len(headers.Crit) > 0 { // Unsupported crit header @@ -202,9 +240,9 @@ func (obj JSONWebSignature) Verify(verificationKey interface{}) ([]byte, error) alg := SignatureAlgorithm(headers.Alg) err := verifier.verifyPayload(input, signature.Signature, alg) if err == nil { - return obj.payload, nil + return i, signature, obj.payload, nil } } - return nil, ErrCryptoFailure + return -1, Signature{}, nil, ErrCryptoFailure } diff --git a/vendor/gopkg.in/square/go-jose.v2/signing_test.go b/vendor/gopkg.in/square/go-jose.v2/signing_test.go index 9502bc5b..de287f5b 100644 --- a/vendor/gopkg.in/square/go-jose.v2/signing_test.go +++ b/vendor/gopkg.in/square/go-jose.v2/signing_test.go @@ -24,6 +24,8 @@ import ( "fmt" "io" "testing" + + "gopkg.in/square/go-jose.v2/json" ) type staticNonceSource string @@ -223,43 +225,45 @@ func TestMultiRecipientJWS(t *testing.T) { input := []byte("Lorem ipsum dolor sit amet") obj, err := signer.Sign(input) if err != nil { - t.Error("error on sign: ", err) - return + t.Fatal("error on sign: ", err) } _, err = obj.CompactSerialize() if err == nil { - t.Error("message with multiple recipient was compact serialized") + t.Fatal("message with multiple recipient was compact serialized") } msg := obj.FullSerialize() obj, err = ParseSigned(msg) if err != nil { - t.Error("error on parse: ", err) - return + t.Fatal("error on parse: ", err) } - output, err := obj.Verify(&rsaTestKey.PublicKey) + i, _, output, err := obj.VerifyMulti(&rsaTestKey.PublicKey) if err != nil { - t.Error("error on verify: ", err) - return + t.Fatal("error on verify: ", err) + } + + if i != 0 { + t.Fatal("signature index should be 0 for RSA key") } if bytes.Compare(output, input) != 0 { - t.Error("input/output do not match", output, input) - return + t.Fatal("input/output do not match", output, input) } - output, err = obj.Verify(sharedKey) + i, _, output, err = obj.VerifyMulti(sharedKey) if err != nil { - t.Error("error on verify: ", err) - return + t.Fatal("error on verify: ", err) + } + + if i != 1 { + t.Fatal("signature index should be 1 for EC key") } if bytes.Compare(output, input) != 0 { - t.Error("input/output do not match", output, input) - return + t.Fatal("input/output do not match", output, input) } } @@ -344,12 +348,12 @@ func TestSignerKid(t *testing.T) { } var jsonmsi map[string]interface{} - err = UnmarshalJSON(jsonbar, &jsonmsi) + err = json.Unmarshal(jsonbar, &jsonmsi) if err != nil { t.Error("problem unmarshalling base JWK", err) } jsonmsi["kid"] = kid - jsonbar2, err := MarshalJSON(jsonmsi) + jsonbar2, err := json.Marshal(jsonmsi) if err != nil { t.Error("problem marshalling kided JWK", err) } diff --git a/vendor/gopkg.in/square/go-jose.v2/utils_test.go b/vendor/gopkg.in/square/go-jose.v2/utils_test.go index 96f55ab8..09440303 100644 --- a/vendor/gopkg.in/square/go-jose.v2/utils_test.go +++ b/vendor/gopkg.in/square/go-jose.v2/utils_test.go @@ -44,7 +44,7 @@ func fromBase64Int(encoded string) *big.Int { re := regexp.MustCompile(`\s+`) val, err := base64.RawURLEncoding.DecodeString(re.ReplaceAllString(encoded, "")) if err != nil { - panic("Invalid test data") + panic("Invalid test data: " + err.Error()) } return new(big.Int).SetBytes(val) }