From 531b14524c5e0d1256e294d4a11fd287d38addfa Mon Sep 17 00:00:00 2001 From: Maxim Lebedev Date: Thu, 9 Jun 2022 23:53:45 +0500 Subject: [PATCH] :arrow_up: Upgraded JWX package for secure and fast decoding access tokens --- go.mod | 13 +- go.sum | 28 +- .../github.com/brianvoe/gofakeit/v6/lookup.go | 1 + vendor/github.com/brianvoe/gofakeit/v6/sql.go | 157 +++ .../lestrrat-go/backoff/v2/.golangci.yml | 7 - .../github.com/lestrrat-go/backoff/v2/Changes | 8 - .../lestrrat-go/backoff/v2/README.md | 183 ---- .../lestrrat-go/backoff/v2/backoff.go | 31 - .../lestrrat-go/backoff/v2/constant.go | 66 -- .../lestrrat-go/backoff/v2/controller.go | 99 -- .../github.com/lestrrat-go/backoff/v2/doc.go | 6 - .../lestrrat-go/backoff/v2/exponential.go | 107 -- .../lestrrat-go/backoff/v2/interface.go | 30 - .../lestrrat-go/backoff/v2/jitter.go | 59 -- .../github.com/lestrrat-go/backoff/v2/null.go | 51 - .../lestrrat-go/backoff/v2/options.go | 127 --- .../{backoff/v2 => httprc}/.gitignore | 7 +- .../lestrrat-go/httprc/.golangci.yml | 82 ++ vendor/github.com/lestrrat-go/httprc/Changes | 8 + .../{backoff/v2 => httprc}/LICENSE | 2 +- .../github.com/lestrrat-go/httprc/README.md | 130 +++ vendor/github.com/lestrrat-go/httprc/cache.go | 171 ++++ .../github.com/lestrrat-go/httprc/fetcher.go | 182 ++++ .../github.com/lestrrat-go/httprc/httprc.go | 22 + .../lestrrat-go/httprc/options.yaml | 119 +++ .../lestrrat-go/httprc/options_gen.go | 221 ++++ vendor/github.com/lestrrat-go/httprc/queue.go | 446 ++++++++ .../lestrrat-go/httprc/whitelist.go | 73 ++ vendor/github.com/lestrrat-go/jwx/Changes | 723 ------------- vendor/github.com/lestrrat-go/jwx/README.md | 134 --- vendor/github.com/lestrrat-go/jwx/gen.sh | 14 - .../github.com/lestrrat-go/jwx/jwa/README.md | 3 - vendor/github.com/lestrrat-go/jwx/jwa/gen.sh | 14 - vendor/github.com/lestrrat-go/jwx/jwa/jwa.go | 4 - .../lestrrat-go/jwx/jwe/compress.go | 41 - .../github.com/lestrrat-go/jwx/jwe/encrypt.go | 145 --- vendor/github.com/lestrrat-go/jwx/jwe/gen.sh | 14 - .../lestrrat-go/jwx/jwe/interface.go | 101 -- vendor/github.com/lestrrat-go/jwx/jwe/io.go | 23 - vendor/github.com/lestrrat-go/jwx/jwe/jwe.go | 377 ------- .../github.com/lestrrat-go/jwx/jwe/message.go | 648 ------------ .../github.com/lestrrat-go/jwx/jwe/options.go | 87 -- .../lestrrat-go/jwx/jwe/serializer.go | 94 -- .../github.com/lestrrat-go/jwx/jwk/README.md | 274 ----- .../lestrrat-go/jwx/jwk/certchain.go | 85 -- vendor/github.com/lestrrat-go/jwx/jwk/gen.sh | 14 - vendor/github.com/lestrrat-go/jwx/jwk/io.go | 29 - .../github.com/lestrrat-go/jwx/jwk/option.go | 197 ---- .../github.com/lestrrat-go/jwx/jwk/refresh.go | 653 ------------ vendor/github.com/lestrrat-go/jwx/jws/gen.sh | 14 - vendor/github.com/lestrrat-go/jwx/jws/io.go | 23 - vendor/github.com/lestrrat-go/jwx/jws/jws.go | 961 ------------------ .../github.com/lestrrat-go/jwx/jws/option.go | 119 --- vendor/github.com/lestrrat-go/jwx/jwt/gen.sh | 9 - .../jwx/jwt/internal/types/date.go | 98 -- vendor/github.com/lestrrat-go/jwx/jwt/io.go | 29 - .../github.com/lestrrat-go/jwx/jwt/options.go | 538 ---------- .../lestrrat-go/jwx/{ => v2}/.gitignore | 2 + .../lestrrat-go/jwx/{ => v2}/.golangci.yml | 1 - vendor/github.com/lestrrat-go/jwx/v2/Changes | 75 ++ .../lestrrat-go/jwx/v2/Changes-v2.md | 390 +++++++ .../lestrrat-go/jwx/{ => v2}/LICENSE | 0 .../lestrrat-go/jwx/{ => v2}/Makefile | 1 - .../github.com/lestrrat-go/jwx/v2/README.md | 248 +++++ .../lestrrat-go/jwx/v2/cert/cert.go | 48 + .../lestrrat-go/jwx/v2/cert/chain.go | 78 ++ .../github.com/lestrrat-go/jwx/v2/codecov.yml | 2 + .../lestrrat-go/jwx/{ => v2}/format.go | 0 .../jwx/{ => v2}/formatkind_string_gen.go | 0 .../jwx/{ => v2}/internal/base64/base64.go | 5 +- .../jwx/{ => v2}/internal/ecutil/ecutil.go | 2 +- .../jwx/{ => v2}/internal/iter/mapiter.go | 4 +- .../jwx/{ => v2}/internal/json/goccy.go | 0 .../jwx/{ => v2}/internal/json/json.go | 18 +- .../jwx/{ => v2}/internal/json/registry.go | 7 +- .../jwx/{ => v2}/internal/json/stdlib.go | 0 .../jwx/{ => v2}/internal/keyconv/keyconv.go | 36 +- .../jwx/{ => v2}/internal/pool/pool.go | 0 .../lestrrat-go/jwx/v2/jwa/README.md | 3 + .../jwx/{ => v2}/jwa/compression_gen.go | 6 +- .../{ => v2}/jwa/content_encryption_gen.go | 6 +- .../jwx/{ => v2}/jwa/elliptic_gen.go | 6 +- .../github.com/lestrrat-go/jwx/v2/jwa/jwa.go | 61 ++ .../jwx/{ => v2}/jwa/key_encryption_gen.go | 6 +- .../jwx/{ => v2}/jwa/key_type_gen.go | 6 +- .../lestrrat-go/jwx/{ => v2}/jwa/secp2561k.go | 0 .../jwx/{ => v2}/jwa/signature_gen.go | 6 +- .../lestrrat-go/jwx/{ => v2}/jwe/README.md | 6 +- .../lestrrat-go/jwx/v2/jwe/compress.go | 36 + .../lestrrat-go/jwx/{ => v2}/jwe/decrypt.go | 104 +- .../lestrrat-go/jwx/{ => v2}/jwe/headers.go | 22 +- .../jwx/{ => v2}/jwe/headers_gen.go | 103 +- .../lestrrat-go/jwx/v2/jwe/interface.go | 159 +++ .../{ => v2}/jwe/internal/aescbc/aescbc.go | 20 +- .../{ => v2}/jwe/internal/cipher/cipher.go | 37 +- .../{ => v2}/jwe/internal/cipher/interface.go | 2 +- .../jwe/internal/concatkdf/concatkdf.go | 9 +- .../internal/content_crypt/content_crypt.go | 11 +- .../jwe/internal/content_crypt/interface.go | 4 +- .../{ => v2}/jwe/internal/keyenc/interface.go | 4 +- .../{ => v2}/jwe/internal/keyenc/keyenc.go | 95 +- .../{ => v2}/jwe/internal/keygen/interface.go | 4 +- .../{ => v2}/jwe/internal/keygen/keygen.go | 38 +- .../github.com/lestrrat-go/jwx/v2/jwe/io.go | 42 + .../github.com/lestrrat-go/jwx/v2/jwe/jwe.go | 793 +++++++++++++++ .../lestrrat-go/jwx/v2/jwe/key_provider.go | 161 +++ .../lestrrat-go/jwx/v2/jwe/message.go | 547 ++++++++++ .../lestrrat-go/jwx/v2/jwe/options.go | 107 ++ .../lestrrat-go/jwx/v2/jwe/options.yaml | 122 +++ .../lestrrat-go/jwx/v2/jwe/options_gen.go | 255 +++++ .../lestrrat-go/jwx/v2/jwk/README.md | 223 ++++ .../lestrrat-go/jwx/v2/jwk/cache.go | 348 +++++++ .../lestrrat-go/jwx/{ => v2}/jwk/ecdsa.go | 35 +- .../lestrrat-go/jwx/{ => v2}/jwk/ecdsa_gen.go | 259 ++--- .../lestrrat-go/jwx/{ => v2}/jwk/es256k.go | 4 +- .../lestrrat-go/jwx/v2/jwk/fetch.go | 76 ++ .../lestrrat-go/jwx/{ => v2}/jwk/interface.go | 68 +- .../jwx/{ => v2}/jwk/interface_gen.go | 13 +- .../github.com/lestrrat-go/jwx/v2/jwk/io.go | 42 + .../lestrrat-go/jwx/{ => v2}/jwk/jwk.go | 412 ++++---- .../lestrrat-go/jwx/{ => v2}/jwk/key_ops.go | 10 +- .../lestrrat-go/jwx/{ => v2}/jwk/okp.go | 29 +- .../lestrrat-go/jwx/{ => v2}/jwk/okp_gen.go | 247 ++--- .../lestrrat-go/jwx/v2/jwk/options.go | 38 + .../lestrrat-go/jwx/v2/jwk/options.yaml | 142 +++ .../lestrrat-go/jwx/v2/jwk/options_gen.go | 274 +++++ .../lestrrat-go/jwx/{ => v2}/jwk/rsa.go | 25 +- .../lestrrat-go/jwx/{ => v2}/jwk/rsa_gen.go | 267 ++--- .../lestrrat-go/jwx/{ => v2}/jwk/set.go | 84 +- .../lestrrat-go/jwx/{ => v2}/jwk/symmetric.go | 11 +- .../jwx/{ => v2}/jwk/symmetric_gen.go | 121 +-- .../lestrrat-go/jwx/{ => v2}/jwk/usage.go | 8 +- .../lestrrat-go/jwx/{ => v2}/jwk/whitelist.go | 0 .../lestrrat-go/jwx/{ => v2}/jws/README.md | 62 +- .../lestrrat-go/jwx/{ => v2}/jws/ecdsa.go | 27 +- .../lestrrat-go/jwx/{ => v2}/jws/eddsa.go | 18 +- .../lestrrat-go/jwx/{ => v2}/jws/es256k.go | 2 +- .../lestrrat-go/jwx/{ => v2}/jws/headers.go | 10 +- .../jwx/{ => v2}/jws/headers_gen.go | 116 ++- .../lestrrat-go/jwx/{ => v2}/jws/hmac.go | 16 +- .../lestrrat-go/jwx/{ => v2}/jws/interface.go | 4 +- .../github.com/lestrrat-go/jwx/v2/jws/io.go | 42 + .../github.com/lestrrat-go/jwx/v2/jws/jws.go | 716 +++++++++++++ .../lestrrat-go/jwx/v2/jws/key_provider.go | 253 +++++ .../lestrrat-go/jwx/{ => v2}/jws/message.go | 117 ++- .../lestrrat-go/jwx/v2/jws/options.go | 156 +++ .../lestrrat-go/jwx/v2/jws/options.yaml | 158 +++ .../lestrrat-go/jwx/v2/jws/options_gen.go | 317 ++++++ .../lestrrat-go/jwx/{ => v2}/jws/rsa.go | 20 +- .../lestrrat-go/jwx/{ => v2}/jws/signer.go | 7 +- .../lestrrat-go/jwx/{ => v2}/jws/verifier.go | 7 +- .../lestrrat-go/jwx/{ => v2}/jwt/README.md | 14 +- .../jwx/{ => v2}/jwt/builder_gen.go | 5 +- .../lestrrat-go/jwx/{ => v2}/jwt/http.go | 12 +- .../lestrrat-go/jwx/{ => v2}/jwt/interface.go | 4 +- .../jwx/v2/jwt/internal/types/date.go | 191 ++++ .../jwx/{ => v2}/jwt/internal/types/string.go | 10 +- .../github.com/lestrrat-go/jwx/v2/jwt/io.go | 42 + .../lestrrat-go/jwx/{ => v2}/jwt/jwt.go | 389 +++---- .../lestrrat-go/jwx/v2/jwt/options.go | 297 ++++++ .../lestrrat-go/jwx/v2/jwt/options.yaml | 203 ++++ .../lestrrat-go/jwx/v2/jwt/options_gen.go | 372 +++++++ .../lestrrat-go/jwx/{ => v2}/jwt/serialize.go | 153 +-- .../lestrrat-go/jwx/{ => v2}/jwt/token_gen.go | 54 +- .../lestrrat-go/jwx/{ => v2}/jwt/validate.go | 225 +++- .../lestrrat-go/jwx/{ => v2}/jwx.go | 9 +- .../lestrrat-go/jwx/{ => v2}/options.go | 0 .../lestrrat-go/jwx/{ => v2}/x25519/x25519.go | 7 +- vendor/github.com/pkg/errors/.gitignore | 24 - vendor/github.com/pkg/errors/.travis.yml | 10 - vendor/github.com/pkg/errors/LICENSE | 23 - vendor/github.com/pkg/errors/Makefile | 44 - vendor/github.com/pkg/errors/README.md | 59 -- vendor/github.com/pkg/errors/appveyor.yml | 32 - vendor/github.com/pkg/errors/errors.go | 288 ------ vendor/github.com/pkg/errors/go113.go | 38 - vendor/github.com/pkg/errors/stack.go | 177 ---- .../x/tools/go/gcexportdata/gcexportdata.go | 1 - .../x/tools/go/gcexportdata/importer.go | 1 - .../x/tools/go/internal/gcimporter/bexport.go | 20 +- .../go/internal/gcimporter/gcimporter.go | 133 ++- .../x/tools/go/internal/gcimporter/iexport.go | 5 +- .../x/tools/go/internal/gcimporter/iimport.go | 10 +- vendor/golang.org/x/tools/go/packages/doc.go | 1 - .../golang.org/x/tools/go/packages/golist.go | 85 +- .../x/tools/go/packages/loadmode_string.go | 4 +- .../x/tools/go/packages/packages.go | 73 +- .../x/tools/internal/gocommand/invoke.go | 6 +- .../internal/packagesinternal/packages.go | 2 + .../x/tools/internal/typeparams/common.go | 21 +- .../x/tools/internal/typeparams/coretype.go | 122 +++ .../x/tools/internal/typeparams/normalize.go | 12 +- .../x/tools/internal/typeparams/termlist.go | 9 - vendor/modules.txt | 62 +- .../source.toby3d.me/toby3d/middleware/jwt.go | 8 +- 195 files changed, 10853 insertions(+), 8787 deletions(-) create mode 100644 vendor/github.com/brianvoe/gofakeit/v6/sql.go delete mode 100644 vendor/github.com/lestrrat-go/backoff/v2/.golangci.yml delete mode 100644 vendor/github.com/lestrrat-go/backoff/v2/Changes delete mode 100644 vendor/github.com/lestrrat-go/backoff/v2/README.md delete mode 100644 vendor/github.com/lestrrat-go/backoff/v2/backoff.go delete mode 100644 vendor/github.com/lestrrat-go/backoff/v2/constant.go delete mode 100644 vendor/github.com/lestrrat-go/backoff/v2/controller.go delete mode 100644 vendor/github.com/lestrrat-go/backoff/v2/doc.go delete mode 100644 vendor/github.com/lestrrat-go/backoff/v2/exponential.go delete mode 100644 vendor/github.com/lestrrat-go/backoff/v2/interface.go delete mode 100644 vendor/github.com/lestrrat-go/backoff/v2/jitter.go delete mode 100644 vendor/github.com/lestrrat-go/backoff/v2/null.go delete mode 100644 vendor/github.com/lestrrat-go/backoff/v2/options.go rename vendor/github.com/lestrrat-go/{backoff/v2 => httprc}/.gitignore (53%) create mode 100644 vendor/github.com/lestrrat-go/httprc/.golangci.yml create mode 100644 vendor/github.com/lestrrat-go/httprc/Changes rename vendor/github.com/lestrrat-go/{backoff/v2 => httprc}/LICENSE (97%) create mode 100644 vendor/github.com/lestrrat-go/httprc/README.md create mode 100644 vendor/github.com/lestrrat-go/httprc/cache.go create mode 100644 vendor/github.com/lestrrat-go/httprc/fetcher.go create mode 100644 vendor/github.com/lestrrat-go/httprc/httprc.go create mode 100644 vendor/github.com/lestrrat-go/httprc/options.yaml create mode 100644 vendor/github.com/lestrrat-go/httprc/options_gen.go create mode 100644 vendor/github.com/lestrrat-go/httprc/queue.go create mode 100644 vendor/github.com/lestrrat-go/httprc/whitelist.go delete mode 100644 vendor/github.com/lestrrat-go/jwx/Changes delete mode 100644 vendor/github.com/lestrrat-go/jwx/README.md delete mode 100644 vendor/github.com/lestrrat-go/jwx/gen.sh delete mode 100644 vendor/github.com/lestrrat-go/jwx/jwa/README.md delete mode 100644 vendor/github.com/lestrrat-go/jwx/jwa/gen.sh delete mode 100644 vendor/github.com/lestrrat-go/jwx/jwa/jwa.go delete mode 100644 vendor/github.com/lestrrat-go/jwx/jwe/compress.go delete mode 100644 vendor/github.com/lestrrat-go/jwx/jwe/encrypt.go delete mode 100644 vendor/github.com/lestrrat-go/jwx/jwe/gen.sh delete mode 100644 vendor/github.com/lestrrat-go/jwx/jwe/interface.go delete mode 100644 vendor/github.com/lestrrat-go/jwx/jwe/io.go delete mode 100644 vendor/github.com/lestrrat-go/jwx/jwe/jwe.go delete mode 100644 vendor/github.com/lestrrat-go/jwx/jwe/message.go delete mode 100644 vendor/github.com/lestrrat-go/jwx/jwe/options.go delete mode 100644 vendor/github.com/lestrrat-go/jwx/jwe/serializer.go delete mode 100644 vendor/github.com/lestrrat-go/jwx/jwk/README.md delete mode 100644 vendor/github.com/lestrrat-go/jwx/jwk/certchain.go delete mode 100644 vendor/github.com/lestrrat-go/jwx/jwk/gen.sh delete mode 100644 vendor/github.com/lestrrat-go/jwx/jwk/io.go delete mode 100644 vendor/github.com/lestrrat-go/jwx/jwk/option.go delete mode 100644 vendor/github.com/lestrrat-go/jwx/jwk/refresh.go delete mode 100644 vendor/github.com/lestrrat-go/jwx/jws/gen.sh delete mode 100644 vendor/github.com/lestrrat-go/jwx/jws/io.go delete mode 100644 vendor/github.com/lestrrat-go/jwx/jws/jws.go delete mode 100644 vendor/github.com/lestrrat-go/jwx/jws/option.go delete mode 100644 vendor/github.com/lestrrat-go/jwx/jwt/gen.sh delete mode 100644 vendor/github.com/lestrrat-go/jwx/jwt/internal/types/date.go delete mode 100644 vendor/github.com/lestrrat-go/jwx/jwt/io.go delete mode 100644 vendor/github.com/lestrrat-go/jwx/jwt/options.go rename vendor/github.com/lestrrat-go/jwx/{ => v2}/.gitignore (96%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/.golangci.yml (97%) create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/Changes create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/Changes-v2.md rename vendor/github.com/lestrrat-go/jwx/{ => v2}/LICENSE (100%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/Makefile (97%) create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/README.md create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/cert/cert.go create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/cert/chain.go create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/codecov.yml rename vendor/github.com/lestrrat-go/jwx/{ => v2}/format.go (100%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/formatkind_string_gen.go (100%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/internal/base64/base64.go (93%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/internal/ecutil/ecutil.go (98%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/internal/iter/mapiter.go (91%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/internal/json/goccy.go (100%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/internal/json/json.go (86%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/internal/json/registry.go (84%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/internal/json/stdlib.go (100%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/internal/keyconv/keyconv.go (72%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/internal/pool/pool.go (100%) create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jwa/README.md rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwa/compression_gen.go (91%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwa/content_encryption_gen.go (93%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwa/elliptic_gen.go (92%) create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jwa/jwa.go rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwa/key_encryption_gen.go (95%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwa/key_type_gen.go (91%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwa/secp2561k.go (100%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwa/signature_gen.go (94%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwe/README.md (92%) create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jwe/compress.go rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwe/decrypt.go (58%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwe/headers.go (77%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwe/headers_gen.go (81%) create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jwe/interface.go rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwe/internal/aescbc/aescbc.go (89%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwe/internal/cipher/cipher.go (72%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwe/internal/cipher/interface.go (92%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwe/internal/concatkdf/concatkdf.go (84%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwe/internal/content_crypt/content_crypt.go (74%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwe/internal/content_crypt/interface.go (81%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwe/internal/keyenc/interface.go (96%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwe/internal/keyenc/keyenc.go (81%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwe/internal/keygen/interface.go (95%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwe/internal/keygen/keygen.go (80%) create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jwe/io.go create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jwe/jwe.go create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jwe/key_provider.go create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jwe/message.go create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jwe/options.go create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jwe/options.yaml create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jwe/options_gen.go create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jwk/README.md create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jwk/cache.go rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwk/ecdsa.go (82%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwk/ecdsa_gen.go (75%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwk/es256k.go (65%) create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jwk/fetch.go rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwk/interface.go (70%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwk/interface_gen.go (91%) create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jwk/io.go rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwk/jwk.go (62%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwk/key_ops.go (79%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwk/okp.go (81%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwk/okp_gen.go (75%) create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jwk/options.go create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jwk/options.yaml create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jwk/options_gen.go rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwk/rsa.go (86%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwk/rsa_gen.go (75%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwk/set.go (69%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwk/symmetric.go (79%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwk/symmetric_gen.go (75%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwk/usage.go (65%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwk/whitelist.go (100%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jws/README.md (70%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jws/ecdsa.go (81%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jws/eddsa.go (69%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jws/es256k.go (74%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jws/headers.go (84%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jws/headers_gen.go (82%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jws/hmac.go (76%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jws/interface.go (97%) create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jws/io.go create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jws/jws.go create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jws/key_provider.go rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jws/message.go (72%) create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jws/options.go create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jws/options.yaml create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jws/options_gen.go rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jws/rsa.go (80%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jws/signer.go (93%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jws/verifier.go (93%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwt/README.md (94%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwt/builder_gen.go (94%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwt/http.go (94%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwt/interface.go (74%) create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jwt/internal/types/date.go rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwt/internal/types/string.go (72%) create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jwt/io.go rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwt/jwt.go (50%) create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jwt/options.go create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jwt/options.yaml create mode 100644 vendor/github.com/lestrrat-go/jwx/v2/jwt/options_gen.go rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwt/serialize.go (51%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwt/token_gen.go (87%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwt/validate.go (59%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/jwx.go (88%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/options.go (100%) rename vendor/github.com/lestrrat-go/jwx/{ => v2}/x25519/x25519.go (95%) delete mode 100644 vendor/github.com/pkg/errors/.gitignore delete mode 100644 vendor/github.com/pkg/errors/.travis.yml delete mode 100644 vendor/github.com/pkg/errors/LICENSE delete mode 100644 vendor/github.com/pkg/errors/Makefile delete mode 100644 vendor/github.com/pkg/errors/README.md delete mode 100644 vendor/github.com/pkg/errors/appveyor.yml delete mode 100644 vendor/github.com/pkg/errors/errors.go delete mode 100644 vendor/github.com/pkg/errors/go113.go delete mode 100644 vendor/github.com/pkg/errors/stack.go create mode 100644 vendor/golang.org/x/tools/internal/typeparams/coretype.go diff --git a/go.mod b/go.mod index 5156e09..b6b1abe 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,11 @@ go 1.18 require ( github.com/DATA-DOG/go-sqlmock v1.5.0 - github.com/brianvoe/gofakeit/v6 v6.15.0 + github.com/brianvoe/gofakeit/v6 v6.16.0 github.com/fasthttp/router v1.4.10 github.com/goccy/go-json v0.9.7 github.com/jmoiron/sqlx v1.3.5 - github.com/lestrrat-go/jwx v1.2.25 + github.com/lestrrat-go/jwx/v2 v2.0.2 github.com/spf13/viper v1.12.0 github.com/stretchr/testify v1.7.2 github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 @@ -21,7 +21,7 @@ require ( inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6 modernc.org/sqlite v1.17.3 source.toby3d.me/toby3d/form v0.3.0 - source.toby3d.me/toby3d/middleware v0.9.1 + source.toby3d.me/toby3d/middleware v0.9.2 willnorris.com/go/microformats v1.1.1 ) @@ -36,9 +36,9 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/klauspost/compress v1.15.6 // indirect - github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect github.com/lestrrat-go/blackmagic v1.0.1 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect + github.com/lestrrat-go/httprc v1.0.1 // indirect github.com/lestrrat-go/iter v1.0.2 // indirect github.com/lestrrat-go/option v1.0.0 // indirect github.com/magiconair/properties v1.8.6 // indirect @@ -47,7 +47,6 @@ require ( github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.2 // indirect github.com/philhofer/fwd v1.1.1 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect github.com/savsgio/dictpool v0.0.0-20220406081701-03de5edb2e6d // indirect @@ -62,10 +61,10 @@ require ( go4.org/intern v0.0.0-20220301175310-a089fc204883 // indirect go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 // indirect golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect - golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect + golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/net v0.0.0-20220607020251-c690dde0001d // indirect golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68 // indirect - golang.org/x/tools v0.1.10 // indirect + golang.org/x/tools v0.1.11 // indirect gopkg.in/ini.v1 v1.66.6 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 470b32f..e531e6c 100644 --- a/go.sum +++ b/go.sum @@ -47,8 +47,8 @@ github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= -github.com/brianvoe/gofakeit/v6 v6.15.0 h1:lJPGJZ2/07TRGDazyTzD5b18N3y4tmmJpdhCUw18FlI= -github.com/brianvoe/gofakeit/v6 v6.15.0/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8= +github.com/brianvoe/gofakeit/v6 v6.16.0 h1:EelCqtfArd8ppJ0z+TpOxXH8sVWNPBadPNdCDSMMw7k= +github.com/brianvoe/gofakeit/v6 v6.16.0/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -62,7 +62,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= @@ -185,18 +184,16 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A= -github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= -github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ= github.com/lestrrat-go/blackmagic v1.0.1 h1:lS5Zts+5HIC/8og6cGHb0uCcNCa3OUt1ygh3Qz2Fe80= github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= -github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc= +github.com/lestrrat-go/httprc v1.0.1 h1:Cnc4NxIySph38pQPzKbjg5OkKsGR/Cf5xcWt5OlSUDI= +github.com/lestrrat-go/httprc v1.0.1/go.mod h1:5Ml+nB++j6IC0e6LzefJnrpMQDKgDwDCaIQQzhbqhJM= github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= -github.com/lestrrat-go/jwx v1.2.25 h1:tAx93jN2SdPvFn08fHNAhqFJazn5mBBOB8Zli0g0otA= -github.com/lestrrat-go/jwx v1.2.25/go.mod h1:zoNuZymNl5lgdcu6P7K6ie2QRll5HVfF4xwxBBK1NxY= +github.com/lestrrat-go/jwx/v2 v2.0.2 h1:wkq9jwCkF3xrykISzn0Eksd7NEMOZ9yvCdnEpovIJX8= +github.com/lestrrat-go/jwx/v2 v2.0.2/go.mod h1:xV8+xRcrKbmnScV8adOzUuuTrL8aAZJoY4q2JAqIYU8= github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4= github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -230,7 +227,6 @@ github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI= github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ= github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -342,8 +338,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -518,8 +514,8 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20= -golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY= +golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -685,7 +681,7 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= source.toby3d.me/toby3d/form v0.3.0 h1:kI8apdFeVr+koqTTGVoIRiR5NMqjrhCJlajYlu+1bVw= source.toby3d.me/toby3d/form v0.3.0/go.mod h1:drlHMC+j/gb5zsttCSwx8qcYsbaRW+wFfE8bK6y+oeY= -source.toby3d.me/toby3d/middleware v0.9.1 h1:MjXDs57SbrIndEVFJHN36ZiO2OgnXNSajrLqa87vzZQ= -source.toby3d.me/toby3d/middleware v0.9.1/go.mod h1:Z67Z26wyW5USWnAYBYiA6mLubyzsHkbY6sWuIzs4304= +source.toby3d.me/toby3d/middleware v0.9.2 h1:HInjjZaN7GTqlWq32XscJs4Wf0taFG6OhyTAJrED1vA= +source.toby3d.me/toby3d/middleware v0.9.2/go.mod h1:MWedNnEpLCOk2rgjlfjpkn38t+1j53htSrp3lf6pC34= willnorris.com/go/microformats v1.1.1 h1:h5tk2luq6KBIRcwMGdksxdeea4GGuWrRFie5460OAbo= willnorris.com/go/microformats v1.1.1/go.mod h1:kvVnWrkkEscVAIITCEoiTX66Hcyg59C7q0E49mb9TJ0= diff --git a/vendor/github.com/brianvoe/gofakeit/v6/lookup.go b/vendor/github.com/brianvoe/gofakeit/v6/lookup.go index a9fadd5..633053d 100644 --- a/vendor/github.com/brianvoe/gofakeit/v6/lookup.go +++ b/vendor/github.com/brianvoe/gofakeit/v6/lookup.go @@ -95,6 +95,7 @@ func initLookup() { addWeightedLookup() addMinecraftLookup() addCelebrityLookup() + addDatabaseSQLLookup() } // NewMapParams will create a new MapParams diff --git a/vendor/github.com/brianvoe/gofakeit/v6/sql.go b/vendor/github.com/brianvoe/gofakeit/v6/sql.go new file mode 100644 index 0000000..9434d83 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/v6/sql.go @@ -0,0 +1,157 @@ +package gofakeit + +import ( + "encoding/json" + "errors" + "fmt" + "math/rand" + "strings" +) + +type SQLOptions struct { + Table string `json:"table" xml:"table"` // Table name we are inserting into + Count int `json:"count" xml:"count"` // How many entries (tuples) we're generating + Fields []Field `json:"fields" xml:"fields"` // The fields to be generated +} + +func SQL(so *SQLOptions) (string, error) { return sqlFunc(globalFaker.Rand, so) } + +func (f *Faker) SQL(so *SQLOptions) (string, error) { return sqlFunc(f.Rand, so) } + +func sqlFunc(r *rand.Rand, so *SQLOptions) (string, error) { + if so.Table == "" { + return "", errors.New("must provide table name to generate SQL") + } + if so.Fields == nil || len(so.Fields) <= 0 { + return "", errors.New(("must pass fields in order to generate SQL queries")) + } + if so.Count <= 0 { + return "", errors.New("must have entry count") + } + + var sb strings.Builder + sb.WriteString("INSERT INTO " + so.Table + " ") + + // Loop through each field and put together column names + var cols []string + for _, f := range so.Fields { + cols = append(cols, f.Name) + } + sb.WriteString("(" + strings.Join(cols, ", ") + ")") + + sb.WriteString(" VALUES ") + for i := 0; i < so.Count; i++ { + // Start opening value + sb.WriteString("(") + + // Now, we need to add all of our fields + var endStr string + for ii, field := range so.Fields { + // Set end of value string + endStr = ", " + if ii == len(so.Fields)-1 { + endStr = "" + } + + // If autoincrement, add based upon loop + if field.Function == "autoincrement" { + sb.WriteString(fmt.Sprintf("%d%s", i+1, endStr)) + continue + } + + // Get the function info for the field + funcInfo := GetFuncLookup(field.Function) + if funcInfo == nil { + return "", errors.New("invalid function, " + field.Function + " does not exist") + } + + // Generate the value + val, err := funcInfo.Generate(r, &field.Params, funcInfo) + if err != nil { + return "", err + } + + // Convert the output value to the proper SQL type + convertType := sqlConvertType(funcInfo.Output, val) + + // If its the last field, we need to close the value + sb.WriteString(convertType + endStr) + } + + // If its the last value, we need to close the value + if i == so.Count-1 { + sb.WriteString(");") + } else { + sb.WriteString("),") + } + } + + return sb.String(), nil +} + +// sqlConvertType will take in a type and value and convert it to the proper SQL type +func sqlConvertType(t string, val interface{}) string { + switch t { + case "string": + return `'` + fmt.Sprintf("%v", val) + `'` + case "[]byte": + return `'` + fmt.Sprintf("%s", val) + `'` + default: + return fmt.Sprintf("%v", val) + } +} + +func addDatabaseSQLLookup() { + AddFuncLookup("sql", Info{ + Display: "SQL", + Category: "database", + Description: "Generates an object or an array of objects in json format", + Example: `INSERT INTO people + (id, first_name, price, age, created_at) + VALUES + (1, 'Markus', 804.92, 21, '1937-01-30 07:58:01'), + (2, 'Santino', 235.13, 40, '1964-07-07 22:25:40');`, + Output: "string", + ContentType: "application/sql", + Params: []Param{ + {Field: "table", Display: "Table", Type: "string", Description: "Name of the table to insert into"}, + {Field: "count", Display: "Count", Type: "int", Default: "100", Description: "Number of inserts to generate"}, + {Field: "fields", Display: "Fields", Type: "[]Field", Description: "Fields containing key name and function to run in json format"}, + }, + Generate: func(r *rand.Rand, m *MapParams, info *Info) (interface{}, error) { + so := SQLOptions{} + + table, err := info.GetString(m, "table") + if err != nil { + return nil, err + } + so.Table = table + + count, err := info.GetInt(m, "count") + if err != nil { + return nil, err + } + so.Count = count + + fieldsStr, err := info.GetStringArray(m, "fields") + if err != nil { + return nil, err + } + + // Check to make sure fields has length + if len(fieldsStr) > 0 { + so.Fields = make([]Field, len(fieldsStr)) + + for i, f := range fieldsStr { + // Unmarshal fields string into fields array + err = json.Unmarshal([]byte(f), &so.Fields[i]) + if err != nil { + return nil, err + } + } + } + + return sqlFunc(r, &so) + }, + }) +} diff --git a/vendor/github.com/lestrrat-go/backoff/v2/.golangci.yml b/vendor/github.com/lestrrat-go/backoff/v2/.golangci.yml deleted file mode 100644 index 90029a8..0000000 --- a/vendor/github.com/lestrrat-go/backoff/v2/.golangci.yml +++ /dev/null @@ -1,7 +0,0 @@ -issues: - exclude-rules: - - path: /*_example_test.go - linters: - - errcheck - - forbidigo - diff --git a/vendor/github.com/lestrrat-go/backoff/v2/Changes b/vendor/github.com/lestrrat-go/backoff/v2/Changes deleted file mode 100644 index 3c04b04..0000000 --- a/vendor/github.com/lestrrat-go/backoff/v2/Changes +++ /dev/null @@ -1,8 +0,0 @@ -v2.0.8 - 28 Feb 2021 - * Fix possible goroutine leak (#30) - -v2.0.7 - 26 Jan 2021 - * Cosmetic go.mod / go.sum changes - -v2.0.6 - 25 Jan 2021 - * Add jitter to constant backoff diff --git a/vendor/github.com/lestrrat-go/backoff/v2/README.md b/vendor/github.com/lestrrat-go/backoff/v2/README.md deleted file mode 100644 index bb3205d..0000000 --- a/vendor/github.com/lestrrat-go/backoff/v2/README.md +++ /dev/null @@ -1,183 +0,0 @@ -# backoff ![](https://github.com/lestrrat-go/backoff/workflows/CI/badge.svg) [![Go Reference](https://pkg.go.dev/badge/github.com/lestrrat-go/backoff/v2.svg)](https://pkg.go.dev/github.com/lestrrat-go/backoff/v2) - -Idiomatic backoff for Go - -This library is an implementation of backoff algorithm for retrying operations -in an idiomatic Go way. It respects `context.Context` natively, and the critical -notifications are done through *channel operations*, allowing you to write code -that is both more explicit and flexibile. - -For a longer discussion, [please read this article](https://medium.com/@lestrrat/yak-shaving-with-backoff-libraries-in-go-80240f0aa30c) - -# IMPORT - -```go -import "github.com/lestrrat-go/backoff/v2" -``` - -# SYNOPSIS - -```go -func ExampleExponential() { - p := backoff.Exponential( - backoff.WithMinInterval(time.Second), - backoff.WithMaxInterval(time.Minute), - backoff.WithJitterFactor(0.05), - ) - - flakyFunc := func(a int) (int, error) { - // silly function that only succeeds if the current call count is - // divisible by either 3 or 5 but not both - switch { - case a%15 == 0: - return 0, errors.New(`invalid`) - case a%3 == 0 || a%5 == 0: - return a, nil - } - return 0, errors.New(`invalid`) - } - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - retryFunc := func(v int) (int, error) { - b := p.Start(ctx) - for backoff.Continue(b) { - x, err := flakyFunc(v) - if err == nil { - return x, nil - } - } - return 0, errors.New(`failed to get value`) - } - - retryFunc(15) -} -``` - -# POLICIES - -Policy objects describe a backoff policy, and are factories to create backoff Controller objects. -Controller objects does the actual coordination. -Create a new controller for each invocation of a backoff enabled operation. -This way the controller object is protected from concurrent access (if you have one) and can easily be discarded - -## Null - -A null policy means there's no backoff. - -For example, if you were to support both using and not using a backoff in your code you can say - -```go - var p backoff.Policy - if useBackoff { - p = backoff.Exponential(...) - } else { - p = backoff.Null() - } - c := p.Start(ctx) - for backoff.Continue(c) { - if err := doSomething(); err != nil { - continue - } - return - } -``` - -Instead of - -```go - if useBackoff { - p := backoff.Exponential(...) - c := p.Start(ctx) - for backoff.Continue(c) { - if err := doSomething(); err != nil { - continue - } - return - } - } else { - if err := doSomething(); err != nil { - continue - } - } -``` - -## Constant - -A constant policy implements are backoff where the intervals are always the same - -## Exponential - -This is the most "common" of the backoffs. Intervals between calls are spaced out such that as you keep retrying, the intervals keep increasing. - -# FAQ - -## I'm getting "package github.com/lestrrat-go/backoff/v2: no Go files in /go/src/github.com/lestrrat-go/backoff/v2" - -You are using Go in GOPATH mode, which was the way before [Go Modules](https://blog.golang.org/using-go-modules) were introduced in Go 1.11 (Aug 2018). -GOPATH has slowly been phased out, and in Go 1.14 onwards, Go Modules pretty much Just Work. -Go 1.16 introduced more visible changes that forces users to be aware of the existance of go.mod files. - -The short answer when you you get the above error is: **Migrate to using Go Modules**. -This is simple: All you need to do is to include a go.mod (and go.sum) file to your library or app. - -For example, if you have previously been doing this: - -``` -git clone git@github.com:myusername/myawesomeproject.git -cd myawesomeproject -go get ./... -``` - -First include go.mod and go.sum in your repository: - -``` -git clone git@github.com:myusername/myawesomeproject.git -cd myawesomeproject -go mod init -go mod tidy -git add go.mod go.sum -git commit -m "Add go.mod and go.sum" -a -git push -``` - -Then from subsequent calls: - -``` -git clone git@github.com:myusername/myawesomeproject.git -cd myawesomeproject -go build # or go test, or go run, or whatever. -``` - -This will tell go to respect tags, and will automatically pick up the latest version of github.com/lestrrat-go/backoff - -If you really can't do this, then the quick and dirty workaround is to just copy the files over to /v2 directory of this library - -``` -BACKOFF=github.com/lestrrat-go/backoff -go get github.com/lestrrat-go/backoff -if [[ if ! -d "$GOPATH/src/$BACKOFF/v2" ]]; then - mkdir "$GOPATH/src/$BACKOFF/v2" # just to make sure it exists -fi -cp "$GOPATH/src/$BACKOFF/*.go" "$GOPATH/src/$BACKOFF/v2" - -git clone git@github.com:myusername/myawesomeproject.git -cd myawesomeproject -go get ./... -``` - -## Why won't you just add the files in /v2? - -Because it's hard to maintain multiple sources of truth. Sorry, I don't get paid to do this. -I will not hold anything against you if you decided to fork this to your repository, and move files to your own /v2 directory. -Then, if you have a go.mod in your app, you can just do - -``` -go mod edit -replace github.com/lestrrat-go/backoff/v2=github.com/myusername/myawesomemfork/v2 -``` - -Oh, wait, then you already have go.mod, so this is a non-issue. - -...Yeah, just migrate to using go modules, please? - diff --git a/vendor/github.com/lestrrat-go/backoff/v2/backoff.go b/vendor/github.com/lestrrat-go/backoff/v2/backoff.go deleted file mode 100644 index e3313b1..0000000 --- a/vendor/github.com/lestrrat-go/backoff/v2/backoff.go +++ /dev/null @@ -1,31 +0,0 @@ -package backoff - -// Null creates a new NullPolicy object -func Null() Policy { - return NewNull() -} - -// Constant creates a new ConstantPolicy object -func Constant(options ...Option) Policy { - return NewConstantPolicy(options...) -} - -// Constant creates a new ExponentialPolicy object -func Exponential(options ...ExponentialOption) Policy { - return NewExponentialPolicy(options...) -} - -// Continue is a convenience function to check when we can fire -// the next invocation of the desired backoff code -// -// for backoff.Continue(c) { -// ... your code ... -// } -func Continue(c Controller) bool { - select { - case <-c.Done(): - return false - case _, ok := <-c.Next(): - return ok - } -} diff --git a/vendor/github.com/lestrrat-go/backoff/v2/constant.go b/vendor/github.com/lestrrat-go/backoff/v2/constant.go deleted file mode 100644 index a7b75e5..0000000 --- a/vendor/github.com/lestrrat-go/backoff/v2/constant.go +++ /dev/null @@ -1,66 +0,0 @@ -package backoff - -import ( - "context" - "time" -) - -type ConstantInterval struct { - interval time.Duration - jitter jitter -} - -func NewConstantInterval(options ...ConstantOption) *ConstantInterval { - jitterFactor := 0.0 - interval := time.Minute - var rng Random - - for _, option := range options { - switch option.Ident() { - case identInterval{}: - interval = option.Value().(time.Duration) - case identJitterFactor{}: - jitterFactor = option.Value().(float64) - case identRNG{}: - rng = option.Value().(Random) - } - } - - return &ConstantInterval{ - interval: interval, - jitter: newJitter(jitterFactor, rng), - } -} - -func (g *ConstantInterval) Next() time.Duration { - return time.Duration(g.jitter.apply(float64(g.interval))) -} - -type ConstantPolicy struct { - cOptions []ControllerOption - igOptions []ConstantOption -} - -func NewConstantPolicy(options ...Option) *ConstantPolicy { - var cOptions []ControllerOption - var igOptions []ConstantOption - - for _, option := range options { - switch opt := option.(type) { - case ControllerOption: - cOptions = append(cOptions, opt) - default: - igOptions = append(igOptions, opt.(ConstantOption)) - } - } - - return &ConstantPolicy{ - cOptions: cOptions, - igOptions: igOptions, - } -} - -func (p *ConstantPolicy) Start(ctx context.Context) Controller { - ig := NewConstantInterval(p.igOptions...) - return newController(ctx, ig, p.cOptions...) -} diff --git a/vendor/github.com/lestrrat-go/backoff/v2/controller.go b/vendor/github.com/lestrrat-go/backoff/v2/controller.go deleted file mode 100644 index 1d6c721..0000000 --- a/vendor/github.com/lestrrat-go/backoff/v2/controller.go +++ /dev/null @@ -1,99 +0,0 @@ -package backoff - -import ( - "context" - "sync" - "time" -) - -type controller struct { - ctx context.Context - cancel func() - ig IntervalGenerator - maxRetries int - mu *sync.RWMutex - next chan struct{} // user-facing channel - resetTimer chan time.Duration - retries int - timer *time.Timer -} - -func newController(ctx context.Context, ig IntervalGenerator, options ...ControllerOption) *controller { - cctx, cancel := context.WithCancel(ctx) // DO NOT fire this cancel here - - maxRetries := 10 - for _, option := range options { - switch option.Ident() { - case identMaxRetries{}: - maxRetries = option.Value().(int) - } - } - - c := &controller{ - cancel: cancel, - ctx: cctx, - ig: ig, - maxRetries: maxRetries, - mu: &sync.RWMutex{}, - next: make(chan struct{}, 1), - resetTimer: make(chan time.Duration, 1), - timer: time.NewTimer(ig.Next()), - } - - // enqueue a single fake event so the user gets to retry once - c.next <- struct{}{} - - go c.loop() - return c -} - -func (c *controller) loop() { - for { - select { - case <-c.ctx.Done(): - return - case d := <-c.resetTimer: - if !c.timer.Stop() { - select { - case <-c.timer.C: - default: - } - } - c.timer.Reset(d) - case <-c.timer.C: - select { - case <-c.ctx.Done(): - return - case c.next <- struct{}{}: - } - if c.maxRetries > 0 { - c.retries++ - } - - if !c.check() { - c.cancel() - return - } - c.resetTimer <- c.ig.Next() - } - } -} - -func (c *controller) check() bool { - if c.maxRetries > 0 && c.retries >= c.maxRetries { - return false - } - return true -} - -func (c *controller) Done() <-chan struct{} { - c.mu.RLock() - defer c.mu.RUnlock() - return c.ctx.Done() -} - -func (c *controller) Next() <-chan struct{} { - c.mu.RLock() - defer c.mu.RUnlock() - return c.next -} diff --git a/vendor/github.com/lestrrat-go/backoff/v2/doc.go b/vendor/github.com/lestrrat-go/backoff/v2/doc.go deleted file mode 100644 index 614ba6f..0000000 --- a/vendor/github.com/lestrrat-go/backoff/v2/doc.go +++ /dev/null @@ -1,6 +0,0 @@ -// Package backoff implments backoff algorithms for retrying operations. -// -// Users first create an appropriate `Policy` object, and when the operation -// that needs retrying is about to start, they kick the actual backoff -// -package backoff \ No newline at end of file diff --git a/vendor/github.com/lestrrat-go/backoff/v2/exponential.go b/vendor/github.com/lestrrat-go/backoff/v2/exponential.go deleted file mode 100644 index 93aafd3..0000000 --- a/vendor/github.com/lestrrat-go/backoff/v2/exponential.go +++ /dev/null @@ -1,107 +0,0 @@ -package backoff - -import ( - "context" - "time" -) - -type ExponentialInterval struct { - current float64 - maxInterval float64 - minInterval float64 - multiplier float64 - jitter jitter -} - -const ( - defaultMaxInterval = float64(time.Minute) - defaultMinInterval = float64(500 * time.Millisecond) - defaultMultiplier = 1.5 -) - -func NewExponentialInterval(options ...ExponentialOption) *ExponentialInterval { - jitterFactor := 0.0 - maxInterval := defaultMaxInterval - minInterval := defaultMinInterval - multiplier := defaultMultiplier - var rng Random - - for _, option := range options { - switch option.Ident() { - case identJitterFactor{}: - jitterFactor = option.Value().(float64) - case identMaxInterval{}: - maxInterval = float64(option.Value().(time.Duration)) - case identMinInterval{}: - minInterval = float64(option.Value().(time.Duration)) - case identMultiplier{}: - multiplier = option.Value().(float64) - case identRNG{}: - rng = option.Value().(Random) - } - } - - if minInterval > maxInterval { - minInterval = maxInterval - } - if multiplier <= 1 { - multiplier = defaultMultiplier - } - - return &ExponentialInterval{ - maxInterval: maxInterval, - minInterval: minInterval, - multiplier: multiplier, - jitter: newJitter(jitterFactor, rng), - } -} - -func (g *ExponentialInterval) Next() time.Duration { - var next float64 - if g.current == 0 { - next = g.minInterval - } else { - next = g.current * g.multiplier - } - - if next > g.maxInterval { - next = g.maxInterval - } - if next < g.minInterval { - next = g.minInterval - } - - // Apply jitter *AFTER* we calculate the base interval - next = g.jitter.apply(next) - g.current = next - return time.Duration(next) -} - -type ExponentialPolicy struct { - cOptions []ControllerOption - igOptions []ExponentialOption -} - -func NewExponentialPolicy(options ...ExponentialOption) *ExponentialPolicy { - var cOptions []ControllerOption - var igOptions []ExponentialOption - - for _, option := range options { - switch opt := option.(type) { - case ControllerOption: - cOptions = append(cOptions, opt) - default: - igOptions = append(igOptions, opt) - } - } - - return &ExponentialPolicy{ - cOptions: cOptions, - igOptions: igOptions, - } -} - -func (p *ExponentialPolicy) Start(ctx context.Context) Controller { - ig := NewExponentialInterval(p.igOptions...) - return newController(ctx, ig, p.cOptions...) -} diff --git a/vendor/github.com/lestrrat-go/backoff/v2/interface.go b/vendor/github.com/lestrrat-go/backoff/v2/interface.go deleted file mode 100644 index bc92ce9..0000000 --- a/vendor/github.com/lestrrat-go/backoff/v2/interface.go +++ /dev/null @@ -1,30 +0,0 @@ -package backoff - -import ( - "context" - "time" - - "github.com/lestrrat-go/option" -) - -type Option = option.Interface - -type Controller interface { - Done() <-chan struct{} - Next() <-chan struct{} -} - -type IntervalGenerator interface { - Next() time.Duration -} - -// Policy is an interface for the backoff policies that this package -// implements. Users must create a controller object from this -// policy to actually do anything with it -type Policy interface { - Start(context.Context) Controller -} - -type Random interface { - Float64() float64 -} diff --git a/vendor/github.com/lestrrat-go/backoff/v2/jitter.go b/vendor/github.com/lestrrat-go/backoff/v2/jitter.go deleted file mode 100644 index cfdbb38..0000000 --- a/vendor/github.com/lestrrat-go/backoff/v2/jitter.go +++ /dev/null @@ -1,59 +0,0 @@ -package backoff - -import ( - "math/rand" - "time" -) - -type jitter interface { - apply(interval float64) float64 -} - -func newJitter(jitterFactor float64, rng Random) jitter { - if jitterFactor <= 0 || jitterFactor >= 1 { - return newNopJitter() - } - return newRandomJitter(jitterFactor, rng) -} - -type nopJitter struct{} - -func newNopJitter() *nopJitter { - return &nopJitter{} -} - -func (j *nopJitter) apply(interval float64) float64 { - return interval -} - -type randomJitter struct { - jitterFactor float64 - rng Random -} - -func newRandomJitter(jitterFactor float64, rng Random) *randomJitter { - if rng == nil { - // if we have a jitter factor, and no RNG is provided, create one. - // This is definitely not "secure", but well, if you care enough, - // you would provide one - rng = rand.New(rand.NewSource(time.Now().UnixNano())) - } - - return &randomJitter{ - jitterFactor: jitterFactor, - rng: rng, - } -} - -func (j *randomJitter) apply(interval float64) float64 { - jitterDelta := interval * j.jitterFactor - jitterMin := interval - jitterDelta - jitterMax := interval + jitterDelta - - // Get a random value from the range [minInterval, maxInterval]. - // The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then - // we want a 33% chance for selecting either 1, 2 or 3. - // - // see also: https://github.com/cenkalti/backoff/blob/c2975ffa541a1caeca5f76c396cb8c3e7b3bb5f8/exponential.go#L154-L157 - return jitterMin + j.rng.Float64()*(jitterMax-jitterMin+1) -} diff --git a/vendor/github.com/lestrrat-go/backoff/v2/null.go b/vendor/github.com/lestrrat-go/backoff/v2/null.go deleted file mode 100644 index 2f7f6d2..0000000 --- a/vendor/github.com/lestrrat-go/backoff/v2/null.go +++ /dev/null @@ -1,51 +0,0 @@ -package backoff - -import ( - "context" - "sync" -) - -// NullPolicy does not do any backoff. It allows the caller -// to execute the desired code once, and no more -type NullPolicy struct{} - -func NewNull() *NullPolicy { - return &NullPolicy{} -} - -func (p *NullPolicy) Start(ctx context.Context) Controller { - return newNullController(ctx) -} - -type nullController struct { - mu *sync.RWMutex - ctx context.Context - next chan struct{} -} - -func newNullController(ctx context.Context) *nullController { - cctx, cancel := context.WithCancel(ctx) - c := &nullController{ - mu: &sync.RWMutex{}, - ctx: cctx, - next: make(chan struct{}), // NO BUFFER - } - go func(ch chan struct{}, cancel func()) { - ch <- struct{}{} - close(ch) - cancel() - }(c.next, cancel) - return c -} - -func (c *nullController) Done() <-chan struct{} { - c.mu.RLock() - defer c.mu.RUnlock() - return c.ctx.Done() -} - -func (c *nullController) Next() <-chan struct{} { - c.mu.RLock() - defer c.mu.RUnlock() - return c.next -} diff --git a/vendor/github.com/lestrrat-go/backoff/v2/options.go b/vendor/github.com/lestrrat-go/backoff/v2/options.go deleted file mode 100644 index f288f99..0000000 --- a/vendor/github.com/lestrrat-go/backoff/v2/options.go +++ /dev/null @@ -1,127 +0,0 @@ -package backoff - -import ( - "time" - - "github.com/lestrrat-go/option" -) - -type identInterval struct{} -type identJitterFactor struct{} -type identMaxInterval struct{} -type identMaxRetries struct{} -type identMinInterval struct{} -type identMultiplier struct{} -type identRNG struct{} - -// ControllerOption is an option that may be passed to Policy objects, -// but are ultimately passed down to the Controller objects. -// (Normally you do not have to care about the distinction) -type ControllerOption interface { - ConstantOption - ExponentialOption - CommonOption - controllerOption() -} - -type controllerOption struct { - Option -} - -func (*controllerOption) exponentialOption() {} -func (*controllerOption) controllerOption() {} -func (*controllerOption) constantOption() {} - -// ConstantOption is an option that is used by the Constant policy. -type ConstantOption interface { - Option - constantOption() -} - -type constantOption struct { - Option -} - -func (*constantOption) constantOption() {} - -// ExponentialOption is an option that is used by the Exponential policy. -type ExponentialOption interface { - Option - exponentialOption() -} - -type exponentialOption struct { - Option -} - -func (*exponentialOption) exponentialOption() {} - -// CommonOption is an option that can be passed to any of the backoff policies. -type CommonOption interface { - ExponentialOption - ConstantOption -} - -type commonOption struct { - Option -} - -func (*commonOption) constantOption() {} -func (*commonOption) exponentialOption() {} - -// WithMaxRetries specifies the maximum number of attempts that can be made -// by the backoff policies. By default each policy tries up to 10 times. -// -// If you would like to retry forever, specify "0" and pass to the constructor -// of each policy. -// -// This option can be passed to all policy constructors except for NullPolicy -func WithMaxRetries(v int) ControllerOption { - return &controllerOption{option.New(identMaxRetries{}, v)} -} - -// WithInterval specifies the constant interval used in ConstantPolicy and -// ConstantInterval. -// The default value is 1 minute. -func WithInterval(v time.Duration) ConstantOption { - return &constantOption{option.New(identInterval{}, v)} -} - -// WithMaxInterval specifies the maximum duration used in exponential backoff -// The default value is 1 minute. -func WithMaxInterval(v time.Duration) ExponentialOption { - return &exponentialOption{option.New(identMaxInterval{}, v)} -} - -// WithMinInterval specifies the minimum duration used in exponential backoff. -// The default value is 500ms. -func WithMinInterval(v time.Duration) ExponentialOption { - return &exponentialOption{option.New(identMinInterval{}, v)} -} - -// WithMultiplier specifies the factor in which the backoff intervals are -// increased. By default this value is set to 1.5, which means that for -// every iteration a 50% increase in the interval for every iteration -// (up to the value specified by WithMaxInterval). this value must be greater -// than 1.0. If the value is less than equal to 1.0, the default value -// of 1.5 is used. -func WithMultiplier(v float64) ExponentialOption { - return &exponentialOption{option.New(identMultiplier{}, v)} -} - -// WithJitterFactor enables some randomness (jittering) in the computation of -// the backoff intervals. This value must be between 0.0 < v < 1.0. If a -// value outside of this range is specified, the value will be silently -// ignored and jittering is disabled. -// -// This option can be passed to ExponentialPolicy or ConstantPolicy constructor -func WithJitterFactor(v float64) CommonOption { - return &commonOption{option.New(identJitterFactor{}, v)} -} - -// WithRNG specifies the random number generator used for jittering. -// If not provided one will be created, but if you want a truly random -// jittering, make sure to provide one that you explicitly initialized -func WithRNG(v Random) CommonOption { - return &commonOption{option.New(identRNG{}, v)} -} diff --git a/vendor/github.com/lestrrat-go/backoff/v2/.gitignore b/vendor/github.com/lestrrat-go/httprc/.gitignore similarity index 53% rename from vendor/github.com/lestrrat-go/backoff/v2/.gitignore rename to vendor/github.com/lestrrat-go/httprc/.gitignore index a1338d6..66fd13c 100644 --- a/vendor/github.com/lestrrat-go/backoff/v2/.gitignore +++ b/vendor/github.com/lestrrat-go/httprc/.gitignore @@ -1,14 +1,15 @@ # Binaries for programs and plugins *.exe +*.exe~ *.dll *.so *.dylib -# Test binary, build with `go test -c` +# Test binary, built with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out -# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 -.glide/ +# Dependency directories (remove the comment below to include it) +# vendor/ diff --git a/vendor/github.com/lestrrat-go/httprc/.golangci.yml b/vendor/github.com/lestrrat-go/httprc/.golangci.yml new file mode 100644 index 0000000..b5afb66 --- /dev/null +++ b/vendor/github.com/lestrrat-go/httprc/.golangci.yml @@ -0,0 +1,82 @@ +run: + +linters-settings: + govet: + enable-all: true + disable: + - shadow + - fieldalignment + +linters: + enable-all: true + disable: + - cyclop + - dupl + - exhaustive + - exhaustivestruct + - errorlint + - funlen + - gci + - gochecknoglobals + - gochecknoinits + - gocognit + - gocritic + - gocyclo + - godot + - godox + - goerr113 + - gofumpt + - golint #deprecated + - gomnd + - gosec + - govet + - interfacer # deprecated + - ifshort + - lll + - maligned # deprecated + - makezero + - nakedret + - nestif + - nlreturn + - paralleltest + - scopelint # deprecated + - tagliatelle + - testpackage + - thelper + - wrapcheck + - wsl + +issues: + exclude-rules: + # not needed + - path: /*.go + text: "ST1003: should not use underscores in package names" + linters: + - stylecheck + - path: /*.go + text: "don't use an underscore in package name" + linters: + - revive + - path: /main.go + linters: + - errcheck + - path: internal/codegen/codegen.go + linters: + - errcheck + - path: /*_test.go + linters: + - errcheck + - forcetypeassert + - path: /*_example_test.go + linters: + - forbidigo + - path: cmd/jwx/jwx.go + linters: + - forbidigo + + # Maximum issues count per one linter. Set to 0 to disable. Default is 50. + max-issues-per-linter: 0 + + # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. + max-same-issues: 0 + diff --git a/vendor/github.com/lestrrat-go/httprc/Changes b/vendor/github.com/lestrrat-go/httprc/Changes new file mode 100644 index 0000000..e6ce28e --- /dev/null +++ b/vendor/github.com/lestrrat-go/httprc/Changes @@ -0,0 +1,8 @@ +Changes +======= + +v1.0.1 29 Mar 2022 + * Bump dependency for github.com/lestrrat-go/httpcc to v1.0.1 + +v1.0.0 29 Mar 2022 + * Initial release, refactored out of github.com/lestrrat-go/jwx diff --git a/vendor/github.com/lestrrat-go/backoff/v2/LICENSE b/vendor/github.com/lestrrat-go/httprc/LICENSE similarity index 97% rename from vendor/github.com/lestrrat-go/backoff/v2/LICENSE rename to vendor/github.com/lestrrat-go/httprc/LICENSE index 3c0d132..3e19689 100644 --- a/vendor/github.com/lestrrat-go/backoff/v2/LICENSE +++ b/vendor/github.com/lestrrat-go/httprc/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018 lestrrat +Copyright (c) 2022 lestrrat Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/github.com/lestrrat-go/httprc/README.md b/vendor/github.com/lestrrat-go/httprc/README.md new file mode 100644 index 0000000..1583806 --- /dev/null +++ b/vendor/github.com/lestrrat-go/httprc/README.md @@ -0,0 +1,130 @@ +# httprc + +`httprc` is a HTTP "Refresh" Cache. Its aim is to cache a remote resource that +can be fetched via HTTP, but keep the cached content up-to-date based on periodic +refreshing. + +# SYNOPSIS + + +```go +package httprc_test + +import ( + "context" + "fmt" + "net/http" + "net/http/httptest" + "sync" + "time" + + "github.com/lestrrat-go/httprc" +) + +const ( + helloWorld = `Hello World!` + goodbyeWorld = `Goodbye World!` +) + +func ExampleCache() { + var mu sync.RWMutex + + msg := helloWorld + + srv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set(`Cache-Control`, fmt.Sprintf(`max-age=%d`, 2)) + w.WriteHeader(http.StatusOK) + mu.RLock() + fmt.Fprint(w, msg) + mu.RUnlock() + })) + defer srv.Close() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + errSink := httprc.ErrSinkFunc(func(err error) { + fmt.Printf("%s\n", err) + }) + + c := httprc.NewCache(ctx, + httprc.WithErrSink(errSink), + httprc.WithRefreshWindow(time.Second), // force checks every second + ) + + c.Register(srv.URL, + httprc.WithHTTPClient(srv.Client()), // we need client with TLS settings + httprc.WithMinRefreshInterval(time.Second), // allow max-age=1 (smallest) + ) + + payload, err := c.Get(ctx, srv.URL) + if err != nil { + fmt.Printf("%s\n", err) + return + } + + if string(payload.([]byte)) != helloWorld { + fmt.Printf("payload mismatch: %s\n", payload) + return + } + + mu.Lock() + msg = goodbyeWorld + mu.Unlock() + + time.Sleep(4 * time.Second) + + payload, err = c.Get(ctx, srv.URL) + if err != nil { + fmt.Printf("%s\n", err) + return + } + + if string(payload.([]byte)) != goodbyeWorld { + fmt.Printf("payload mismatch: %s\n", payload) + return + } + + cancel() + + // OUTPUT: +} +``` +source: [httprc_example_test.go](https://github.com/lestrrat-go/jwx/blob/main/httprc_example_test.go) + + +# Sequence Diagram + +```mermaid +sequenceDiagram + autonumber + actor User + participant httprc.Cache + participant httprc.Storage + User->>httprc.Cache: Fetch URL `u` + activate httprc.Storage + httprc.Cache->>httprc.Storage: Fetch local cache for `u` + alt Cache exists + httprc.Storage-->httprc.Cache: Return local cache + httprc.Cache-->>User: Return data + Note over httprc.Storage: If the cache exists, there's nothing more to do.
The cached content will be updated periodically in httprc.Refresher + deactivate httprc.Storage + else Cache does not exist + activate httprc.Fetcher + httprc.Cache->>httprc.Fetcher: Fetch remote resource `u` + httprc.Fetcher-->>httprc.Cache: Return fetched data + deactivate httprc.Fetcher + httprc.Cache-->>User: Return data + httprc.Cache-)httprc.Refresher: Enqueue into auto-refresh queue + activate httprc.Refresher + loop Refresh Loop + Note over httprc.Storage,httprc.Fetcher: Cached contents are updated synchronously + httprc.Refresher->>httprc.Refresher: Wait until next refresh + httprc.Refresher-->>httprc.Fetcher: Request fetch + httprc.Fetcher->>httprc.Refresher: Return fetched data + httprc.Refresher-->>httprc.Storage: Store new version in cache + httprc.Refresher->>httprc.Refresher: Enqueue into auto-refresh queue (again) + end + deactivate httprc.Refresher + end +``` diff --git a/vendor/github.com/lestrrat-go/httprc/cache.go b/vendor/github.com/lestrrat-go/httprc/cache.go new file mode 100644 index 0000000..069ce02 --- /dev/null +++ b/vendor/github.com/lestrrat-go/httprc/cache.go @@ -0,0 +1,171 @@ +package httprc + +import ( + "context" + "fmt" + "net/http" + "sync" + "time" +) + +// ErrSink is an abstraction that allows users to consume errors +// produced while the cache queue is running. +type HTTPClient interface { + Get(string) (*http.Response, error) +} + +// Cache represents a cache that stores resources locally, while +// periodically refreshing the contents based on HTTP header values +// and/or user-supplied hints. +// +// Refresh is performed _periodically_, and therefore the contents +// are not kept up-to-date in real time. The interval between checks +// for refreshes is called the refresh window. +// +// The default refresh window is 15 minutes. This means that if a +// resource is fetched is at time T, and it is supposed to be +// refreshed in 20 minutes, the next refresh for this resource will +// happen at T+30 minutes (15+15 minutes). +type Cache struct { + mu sync.RWMutex + queue *queue + wl Whitelist +} + +const defaultRefreshWindow = 15 * time.Minute + +// New creates a new Cache object. +// +// The context object in the argument controls the life-cycle of the +// auto-refresh worker. If you cancel the `ctx`, then the automatic +// refresh will stop working. +// +// Refresh will only be performed periodically where the interval between +// refreshes are controlled by the `refresh window` variable. For example, +// if the refresh window is every 5 minutes and the resource was queued +// to be refreshed at 7 minutes, the resource will be refreshed after 10 +// minutes (in 2 refresh window time). +// +// The refresh window can be configured by using `httprc.WithRefreshWindow` +// option. If you want refreshes to be performed more often, provide a smaller +// refresh window. If you specify a refresh window that is smaller than 1 +// second, it will automatically be set to the default value, which is 15 +// minutes. +// +// Internally the HTTP fetching is done using a pool of HTTP fetch +// workers. The default number of workers is 3. You may change this +// number by specifying the `httprc.WithFetcherWorkerCount` +func NewCache(ctx context.Context, options ...CacheOption) *Cache { + var refreshWindow time.Duration + var errSink ErrSink + var wl Whitelist + var fetcherOptions []FetcherOption + for _, option := range options { + //nolint:forcetypeassert + switch option.Ident() { + case identRefreshWindow{}: + refreshWindow = option.Value().(time.Duration) + case identFetcherWorkerCount{}, identWhitelist{}: + fetcherOptions = append(fetcherOptions, option) + case identErrSink{}: + errSink = option.Value().(ErrSink) + } + } + + if refreshWindow < time.Second { + refreshWindow = defaultRefreshWindow + } + + fetch := NewFetcher(ctx, fetcherOptions...) + queue := newQueue(ctx, refreshWindow, fetch, errSink) + + return &Cache{ + queue: queue, + wl: wl, + } +} + +// Register configures a URL to be stored in the cache. +// +// For any given URL, the URL must be registered _BEFORE_ it is +// accessed using `Get()` method. +func (c *Cache) Register(u string, options ...RegisterOption) error { + c.mu.Lock() + defer c.mu.Unlock() + + if wl := c.wl; wl != nil { + if !wl.IsAllowed(u) { + return fmt.Errorf(`httprc.Cache: url %q has been rejected by whitelist`, u) + } + } + + return c.queue.Register(u, options...) +} + +// Unregister removes the given URL `u` from the cache. +// +// Subsequent calls to `Get()` will fail until `u` is registered again. +func (c *Cache) Unregister(u string) error { + c.mu.Lock() + defer c.mu.Unlock() + return c.queue.Unregister(u) +} + +// IsRegistered returns true if the given URL `u` has already been +// registered in the cache. +func (c *Cache) IsRegistered(u string) bool { + c.mu.RLock() + defer c.mu.RUnlock() + return c.queue.IsRegistered(u) +} + +// Refresh is identical to Get(), except it always fetches the +// specified resource anew, and updates the cached content +func (c *Cache) Refresh(ctx context.Context, u string) (interface{}, error) { + return c.getOrFetch(ctx, u, true) +} + +// Get returns the cached object. +// +// The context.Context argument is used to control the timeout for +// synchronous fetches, when they need to happen. Synchronous fetches +// will be performed when the cache does not contain the specified +// resource. +func (c *Cache) Get(ctx context.Context, u string) (interface{}, error) { + return c.getOrFetch(ctx, u, false) +} + +func (c *Cache) getOrFetch(ctx context.Context, u string, forceRefresh bool) (interface{}, error) { + c.mu.RLock() + e, ok := c.queue.getRegistered(u) + if !ok { + c.mu.RUnlock() + return nil, fmt.Errorf(`url %q is not registered (did you make sure to call Register() first?)`, u) + } + c.mu.RUnlock() + + // Only one goroutine may enter this section. + e.acquireSem() + + // has this entry been fetched? (but ignore and do a fetch + // if forceRefresh is true) + if forceRefresh || !e.hasBeenFetched() { + if err := c.queue.fetchAndStore(ctx, e); err != nil { + return nil, fmt.Errorf(`failed to fetch %q: %w`, u, err) + } + } + + e.releaseSem() + + e.mu.RLock() + data := e.data + e.mu.RUnlock() + + return data, nil +} + +func (c *Cache) Snapshot() *Snapshot { + c.mu.RLock() + defer c.mu.RUnlock() + return c.queue.snapshot() +} diff --git a/vendor/github.com/lestrrat-go/httprc/fetcher.go b/vendor/github.com/lestrrat-go/httprc/fetcher.go new file mode 100644 index 0000000..0bce87a --- /dev/null +++ b/vendor/github.com/lestrrat-go/httprc/fetcher.go @@ -0,0 +1,182 @@ +package httprc + +import ( + "context" + "fmt" + "net/http" + "sync" +) + +type fetchRequest struct { + mu sync.RWMutex + + // client contains the HTTP Client that can be used to make a + // request. By setting a custom *http.Client, you can for example + // provide a custom http.Transport + // + // If not specified, http.DefaultClient will be used. + client HTTPClient + + wl Whitelist + + // u contains the URL to be fetched + url string + + // reply is a field that is only used by the internals of the fetcher + // it is used to return the result of fetching + reply chan *fetchResult +} + +type fetchResult struct { + mu sync.RWMutex + res *http.Response + err error +} + +func (fr *fetchResult) reply(ctx context.Context, reply chan *fetchResult) error { + select { + case <-ctx.Done(): + return ctx.Err() + case reply <- fr: + } + + close(reply) + return nil +} + +type fetcher struct { + requests chan *fetchRequest +} + +type Fetcher interface { + Fetch(context.Context, string, ...FetchOption) (*http.Response, error) + fetch(context.Context, *fetchRequest) (*http.Response, error) +} + +func NewFetcher(ctx context.Context, options ...FetcherOption) Fetcher { + var nworkers int + var wl Whitelist + for _, option := range options { + //nolint:forcetypeassert + switch option.Ident() { + case identFetcherWorkerCount{}: + nworkers = option.Value().(int) + case identWhitelist{}: + wl = option.Value().(Whitelist) + } + } + + if nworkers < 1 { + nworkers = 3 + } + + incoming := make(chan *fetchRequest) + for i := 0; i < nworkers; i++ { + go runFetchWorker(ctx, incoming, wl) + } + return &fetcher{ + requests: incoming, + } +} + +func (f *fetcher) Fetch(ctx context.Context, u string, options ...FetchOption) (*http.Response, error) { + var client HTTPClient + var wl Whitelist + for _, option := range options { + //nolint:forcetypeassert + switch option.Ident() { + case identHTTPClient{}: + client = option.Value().(HTTPClient) + case identWhitelist{}: + wl = option.Value().(Whitelist) + } + } + + req := fetchRequest{ + client: client, + url: u, + wl: wl, + } + + return f.fetch(ctx, &req) +} + +// fetch (unexported) is the main fetching implemntation. +// it allows the caller to reuse the same *fetchRequest object +func (f *fetcher) fetch(ctx context.Context, req *fetchRequest) (*http.Response, error) { + reply := make(chan *fetchResult, 1) + req.mu.Lock() + req.reply = reply + req.mu.Unlock() + + // Send a request to the backend + select { + case <-ctx.Done(): + return nil, ctx.Err() + case f.requests <- req: + } + + // wait until we get a reply + select { + case <-ctx.Done(): + return nil, ctx.Err() + case fr := <-reply: + fr.mu.RLock() + res := fr.res + err := fr.err + fr.mu.RUnlock() + return res, err + } +} + +func runFetchWorker(ctx context.Context, incoming chan *fetchRequest, wl Whitelist) { +LOOP: + for { + select { + case <-ctx.Done(): + break LOOP + case req := <-incoming: + req.mu.RLock() + reply := req.reply + client := req.client + if client == nil { + client = http.DefaultClient + } + url := req.url + reqwl := req.wl + req.mu.RUnlock() + + var wls []Whitelist + for _, v := range []Whitelist{wl, reqwl} { + if v != nil { + wls = append(wls, v) + } + } + + if len(wls) > 0 { + for _, wl := range wls { + if !wl.IsAllowed(url) { + r := &fetchResult{ + err: fmt.Errorf(`fetching url %q rejected by whitelist`, url), + } + if err := r.reply(ctx, reply); err != nil { + break LOOP + } + continue LOOP + } + } + } + + // The body is handled by the consumer of the fetcher + //nolint:bodyclose + res, err := client.Get(url) + r := &fetchResult{ + res: res, + err: err, + } + if err := r.reply(ctx, reply); err != nil { + break LOOP + } + } + } +} diff --git a/vendor/github.com/lestrrat-go/httprc/httprc.go b/vendor/github.com/lestrrat-go/httprc/httprc.go new file mode 100644 index 0000000..8ae056a --- /dev/null +++ b/vendor/github.com/lestrrat-go/httprc/httprc.go @@ -0,0 +1,22 @@ +//go:generate tools/genoptions.sh + +// Package httprc implements a cache for resources available +// over http(s). Its aim is not only to cache these resources so +// that it saves on HTTP roundtrips, but it also periodically +// attempts to auto-refresh these resources once they are cached +// based on the user-specified intervals and HTTP `Expires` and +// `Cache-Control` headers, thus keeping the entries _relatively_ fresh. +package httprc + +import "fmt" + +// RefreshError is the underlying error type that is sent to +// the `httprc.ErrSink` objects +type RefreshError struct { + URL string + Err error +} + +func (re *RefreshError) Error() string { + return fmt.Sprintf(`refresh error (%q): %s`, re.URL, re.Err) +} diff --git a/vendor/github.com/lestrrat-go/httprc/options.yaml b/vendor/github.com/lestrrat-go/httprc/options.yaml new file mode 100644 index 0000000..5a5139c --- /dev/null +++ b/vendor/github.com/lestrrat-go/httprc/options.yaml @@ -0,0 +1,119 @@ +package_name: httprc +output: options_gen.go +interfaces: + - name: RegisterOption + comment: | + RegisterOption desribes options that can be passed to `(httprc.Cache).Register()` + - name: CacheOption + comment: | + CacheOption desribes options that can be passed to `New()` + - name: FetcherOption + methods: + - cacheOption + comment: | + FetcherOption describes options that can be passed to `(httprc.Fetcher).NewFetcher()` + - name: FetchOption + comment: | + FetchOption describes options that can be passed to `(httprc.Fetcher).Fetch()` + - name: FetchRegisterOption + methods: + - fetchOption + - registerOption + - name: FetchFetcherRegisterOption + methods: + - fetchOption + - fetcherOption + - registerOption +options: + - ident: FetcherWorkerCount + interface: FetcherOption + argument_type: int + comment: | + WithFetchWorkerCount specifies the number of HTTP fetch workers that are spawned + in the backend. By default 3 workers are spawned. + - ident: Whitelist + interface: FetchFetcherRegisterOption + argument_type: Whitelist + comment: | + WithWhitelist specifies the Whitelist object that can control which URLs are + allowed to be processed. + + It can be passed to `httprc.NewCache` as a whitelist applied to all + URLs that are fetched by the cache, or it can be passed on a per-URL + basis using `(httprc.Cache).Register()`. If both are specified, + the url must fulfill _both_ the cache-wide whitelist and the per-URL + whitelist. + - ident: Transformer + interface: RegisterOption + argument_type: Transformer + comment: | + WithTransformer specifies the `httprc.Transformer` object that should be applied + to the fetched resource. The `Transform()` method is only called if the HTTP request + returns a `200 OK` status. + - ident: HTTPClient + interface: FetchRegisterOption + argument_type: HTTPClient + comment: | + WithHTTPClient specififes the HTTP Client object that should be used to fetch + the resource. For example, if you need an `*http.Client` instance that requires + special TLS or Authorization setup, you might want to pass it using this option. + - ident: MinRefreshInterval + interface: RegisterOption + argument_type: time.Duration + comment: | + WithMinRefreshInterval specifies the minimum refresh interval to be used. + + When we fetch the key from a remote URL, we first look at the `max-age` + directive from `Cache-Control` response header. If this value is present, + we compare the `max-age` value and the value specified by this option + and take the larger one (e.g. if `max-age` = 5 minutes and `min refresh` = 10 + minutes, then next fetch will happen in 10 minutes) + + Next we check for the `Expires` header, and similarly if the header is + present, we compare it against the value specified by this option, + and take the larger one. + + Finally, if neither of the above headers are present, we use the + value specified by this option as the interval until the next refresh. + + If unspecified, the minimum refresh interval is 1 hour. + + This value and the header values are ignored if `WithRefreshInterval` is specified. + - ident: RefreshInterval + interface: RegisterOption + argument_type: time.Duration + comment: | + WithRefreshInterval specifies the static interval between refreshes + of resources controlled by `httprc.Cache`. + + Providing this option overrides the adaptive token refreshing based + on Cache-Control/Expires header (and `httprc.WithMinRefreshInterval`), + and refreshes will *always* happen in this interval. + + You generally do not want to make this value too small, as it can easily + be considered a DoS attack, and there is no backoff mechanism for failed + attempts. + - ident: RefreshWindow + interface: CacheOption + argument_type: time.Duration + comment: | + WithRefreshWindow specifies the interval between checks for refreshes. + `httprc.Cache` does not check for refreshes in exact intervals. Instead, + it wakes up at every tick that occurs in the interval specified by + `WithRefreshWindow` option, and refreshes all entries that need to be + refreshed within this window. + + The default value is 15 minutes. + + You generally do not want to make this value too small, as it can easily + be considered a DoS attack, and there is no backoff mechanism for failed + attempts. + - ident: ErrSink + interface: CacheOption + argument_type: ErrSink + comment: | + WithErrSink specifies the `httprc.ErrSink` object that handles errors + that occurred during the cache's execution. For example, you will be + able to intercept errors that occurred during the execution of Transformers. + + diff --git a/vendor/github.com/lestrrat-go/httprc/options_gen.go b/vendor/github.com/lestrrat-go/httprc/options_gen.go new file mode 100644 index 0000000..daaf65f --- /dev/null +++ b/vendor/github.com/lestrrat-go/httprc/options_gen.go @@ -0,0 +1,221 @@ +// This file is auto-generated by github.com/lestrrat-go/option/cmd/genoptions. DO NOT EDIT + +package httprc + +import ( + "time" + + "github.com/lestrrat-go/option" +) + +type Option = option.Interface + +// CacheOption desribes options that can be passed to `New()` +type CacheOption interface { + Option + cacheOption() +} + +type cacheOption struct { + Option +} + +func (*cacheOption) cacheOption() {} + +type FetchFetcherRegisterOption interface { + Option + fetchOption() + fetcherOption() + registerOption() +} + +type fetchFetcherRegisterOption struct { + Option +} + +func (*fetchFetcherRegisterOption) fetchOption() {} + +func (*fetchFetcherRegisterOption) fetcherOption() {} + +func (*fetchFetcherRegisterOption) registerOption() {} + +// FetchOption describes options that can be passed to `(httprc.Fetcher).Fetch()` +type FetchOption interface { + Option + fetchOption() +} + +type fetchOption struct { + Option +} + +func (*fetchOption) fetchOption() {} + +type FetchRegisterOption interface { + Option + fetchOption() + registerOption() +} + +type fetchRegisterOption struct { + Option +} + +func (*fetchRegisterOption) fetchOption() {} + +func (*fetchRegisterOption) registerOption() {} + +// FetcherOption describes options that can be passed to `(httprc.Fetcher).NewFetcher()` +type FetcherOption interface { + Option + cacheOption() +} + +type fetcherOption struct { + Option +} + +func (*fetcherOption) cacheOption() {} + +// RegisterOption desribes options that can be passed to `(httprc.Cache).Register()` +type RegisterOption interface { + Option + registerOption() +} + +type registerOption struct { + Option +} + +func (*registerOption) registerOption() {} + +type identErrSink struct{} +type identFetcherWorkerCount struct{} +type identHTTPClient struct{} +type identMinRefreshInterval struct{} +type identRefreshInterval struct{} +type identRefreshWindow struct{} +type identTransformer struct{} +type identWhitelist struct{} + +func (identErrSink) String() string { + return "WithErrSink" +} + +func (identFetcherWorkerCount) String() string { + return "WithFetcherWorkerCount" +} + +func (identHTTPClient) String() string { + return "WithHTTPClient" +} + +func (identMinRefreshInterval) String() string { + return "WithMinRefreshInterval" +} + +func (identRefreshInterval) String() string { + return "WithRefreshInterval" +} + +func (identRefreshWindow) String() string { + return "WithRefreshWindow" +} + +func (identTransformer) String() string { + return "WithTransformer" +} + +func (identWhitelist) String() string { + return "WithWhitelist" +} + +// WithErrSink specifies the `httprc.ErrSink` object that handles errors +// that occurred during the cache's execution. For example, you will be +// able to intercept errors that occurred during the execution of Transformers. +func WithErrSink(v ErrSink) CacheOption { + return &cacheOption{option.New(identErrSink{}, v)} +} + +// WithFetchWorkerCount specifies the number of HTTP fetch workers that are spawned +// in the backend. By default 3 workers are spawned. +func WithFetcherWorkerCount(v int) FetcherOption { + return &fetcherOption{option.New(identFetcherWorkerCount{}, v)} +} + +// WithHTTPClient specififes the HTTP Client object that should be used to fetch +// the resource. For example, if you need an `*http.Client` instance that requires +// special TLS or Authorization setup, you might want to pass it using this option. +func WithHTTPClient(v HTTPClient) FetchRegisterOption { + return &fetchRegisterOption{option.New(identHTTPClient{}, v)} +} + +// WithMinRefreshInterval specifies the minimum refresh interval to be used. +// +// When we fetch the key from a remote URL, we first look at the `max-age` +// directive from `Cache-Control` response header. If this value is present, +// we compare the `max-age` value and the value specified by this option +// and take the larger one (e.g. if `max-age` = 5 minutes and `min refresh` = 10 +// minutes, then next fetch will happen in 10 minutes) +// +// Next we check for the `Expires` header, and similarly if the header is +// present, we compare it against the value specified by this option, +// and take the larger one. +// +// Finally, if neither of the above headers are present, we use the +// value specified by this option as the interval until the next refresh. +// +// If unspecified, the minimum refresh interval is 1 hour. +// +// This value and the header values are ignored if `WithRefreshInterval` is specified. +func WithMinRefreshInterval(v time.Duration) RegisterOption { + return ®isterOption{option.New(identMinRefreshInterval{}, v)} +} + +// WithRefreshInterval specifies the static interval between refreshes +// of resources controlled by `httprc.Cache`. +// +// Providing this option overrides the adaptive token refreshing based +// on Cache-Control/Expires header (and `httprc.WithMinRefreshInterval`), +// and refreshes will *always* happen in this interval. +// +// You generally do not want to make this value too small, as it can easily +// be considered a DoS attack, and there is no backoff mechanism for failed +// attempts. +func WithRefreshInterval(v time.Duration) RegisterOption { + return ®isterOption{option.New(identRefreshInterval{}, v)} +} + +// WithRefreshWindow specifies the interval between checks for refreshes. +// `httprc.Cache` does not check for refreshes in exact intervals. Instead, +// it wakes up at every tick that occurs in the interval specified by +// `WithRefreshWindow` option, and refreshes all entries that need to be +// refreshed within this window. +// +// The default value is 15 minutes. +// +// You generally do not want to make this value too small, as it can easily +// be considered a DoS attack, and there is no backoff mechanism for failed +// attempts. +func WithRefreshWindow(v time.Duration) CacheOption { + return &cacheOption{option.New(identRefreshWindow{}, v)} +} + +// WithTransformer specifies the `httprc.Transformer` object that should be applied +// to the fetched resource. The `Transform()` method is only called if the HTTP request +// returns a `200 OK` status. +func WithTransformer(v Transformer) RegisterOption { + return ®isterOption{option.New(identTransformer{}, v)} +} + +// WithWhitelist specifies the Whitelist object that can control which URLs are +// allowed to be processed. +// +// It can be passed to `httprc.NewCache` as a whitelist applied to all +// URLs that are fetched by the cache, or it can be passed on a per-URL +// basis using `(httprc.Cache).Register()`. If both are specified, +// the url must fulfill _both_ the cache-wide whitelist and the per-URL +// whitelist. +func WithWhitelist(v Whitelist) FetchFetcherRegisterOption { + return &fetchFetcherRegisterOption{option.New(identWhitelist{}, v)} +} diff --git a/vendor/github.com/lestrrat-go/httprc/queue.go b/vendor/github.com/lestrrat-go/httprc/queue.go new file mode 100644 index 0000000..3a50ce6 --- /dev/null +++ b/vendor/github.com/lestrrat-go/httprc/queue.go @@ -0,0 +1,446 @@ +package httprc + +import ( + "bytes" + "context" + "fmt" + "io/ioutil" + "net/http" + "sync" + "time" + + "github.com/lestrrat-go/httpcc" +) + +// ErrSink is an abstraction that allows users to consume errors +// produced while the cache queue is running. +type ErrSink interface { + // Error accepts errors produced during the cache queue's execution. + // The method should never block, otherwise the fetch loop may be + // paused for a prolonged amount of time. + Error(error) +} + +type ErrSinkFunc func(err error) + +func (f ErrSinkFunc) Error(err error) { + f(err) +} + +// Transformer is responsible for converting an HTTP response +// into an appropriate form of your choosing. +type Transformer interface { + // Transform receives an HTTP response object, and should + // return an appropriate object that suits your needs. + // + // If you happen to use the response body, you are responsible + // for closing the body + Transform(string, *http.Response) (interface{}, error) +} + +type TransformFunc func(string, *http.Response) (interface{}, error) + +func (f TransformFunc) Transform(u string, res *http.Response) (interface{}, error) { + return f(u, res) +} + +// BodyBytes is the default Transformer applied to all resources. +// It takes an *http.Response object and extracts the body +// of the response as `[]byte` +type BodyBytes struct{} + +func (BodyBytes) Transform(_ string, res *http.Response) (interface{}, error) { + buf, err := ioutil.ReadAll(res.Body) + defer res.Body.Close() + if err != nil { + return nil, fmt.Errorf(`failed to read response body: %w`, err) + } + + return buf, nil +} + +type rqentry struct { + fireAt time.Time + url string +} + +// entry represents a resource to be fetched over HTTP, +// long with optional specifications such as the *http.Client +// object to use. +type entry struct { + mu sync.RWMutex + sem chan struct{} + + lastFetch time.Time + + // Interval between refreshes are calculated two ways. + // 1) You can set an explicit refresh interval by using WithRefreshInterval(). + // In this mode, it doesn't matter what the HTTP response says in its + // Cache-Control or Expires headers + // 2) You can let us calculate the time-to-refresh based on the key's + // Cache-Control or Expires headers. + // First, the user provides us the absolute minimum interval before + // refreshes. We will never check for refreshes before this specified + // amount of time. + // + // Next, max-age directive in the Cache-Control header is consulted. + // If `max-age` is not present, we skip the following section, and + // proceed to the next option. + // If `max-age > user-supplied minimum interval`, then we use the max-age, + // otherwise the user-supplied minimum interval is used. + // + // Next, the value specified in Expires header is consulted. + // If the header is not present, we skip the following seciont and + // proceed to the next option. + // We take the time until expiration `expires - time.Now()`, and + // if `time-until-expiration > user-supplied minimum interval`, then + // we use the expires value, otherwise the user-supplied minimum interval is used. + // + // If all of the above fails, we used the user-supplied minimum interval + refreshInterval time.Duration + minRefreshInterval time.Duration + + request *fetchRequest + + transform Transformer + data interface{} +} + +func (e *entry) acquireSem() { + e.sem <- struct{}{} +} + +func (e *entry) releaseSem() { + <-e.sem +} + +func (e *entry) hasBeenFetched() bool { + e.mu.RLock() + defer e.mu.RUnlock() + return !e.lastFetch.IsZero() +} + +// queue is responsible for updating the contents of the storage +type queue struct { + mu sync.RWMutex + registry map[string]*entry + windowSize time.Duration + fetch Fetcher + fetchCond *sync.Cond + fetchQueue []*rqentry + + // list is a sorted list of urls to their expected fire time + // when we get a new tick in the RQ loop, we process everything + // that can be fired up to the point the tick was called + list []*rqentry +} + +func newQueue(ctx context.Context, window time.Duration, fetch Fetcher, errSink ErrSink) *queue { + fetchLocker := &sync.Mutex{} + rq := &queue{ + windowSize: window, + fetch: fetch, + fetchCond: sync.NewCond(fetchLocker), + registry: make(map[string]*entry), + } + + go rq.refreshLoop(ctx, errSink) + + return rq +} + +func (q *queue) Register(u string, options ...RegisterOption) error { + var refreshInterval time.Duration + var client HTTPClient + var wl Whitelist + var transform Transformer = BodyBytes{} + + minRefreshInterval := 15 * time.Minute + for _, option := range options { + //nolint:forcetypeassert + switch option.Ident() { + case identHTTPClient{}: + client = option.Value().(HTTPClient) + case identRefreshInterval{}: + refreshInterval = option.Value().(time.Duration) + case identMinRefreshInterval{}: + minRefreshInterval = option.Value().(time.Duration) + case identTransformer{}: + transform = option.Value().(Transformer) + case identWhitelist{}: + wl = option.Value().(Whitelist) + } + } + + q.mu.RLock() + rWindow := q.windowSize + q.mu.RUnlock() + + if refreshInterval > 0 && refreshInterval < rWindow { + return fmt.Errorf(`refresh interval (%s) is smaller than refresh window (%s): this will not as expected`, refreshInterval, rWindow) + } + + e := entry{ + sem: make(chan struct{}, 1), + minRefreshInterval: minRefreshInterval, + transform: transform, + refreshInterval: refreshInterval, + request: &fetchRequest{ + client: client, + url: u, + wl: wl, + }, + } + q.mu.Lock() + q.registry[u] = &e + q.mu.Unlock() + return nil +} + +func (q *queue) Unregister(u string) error { + q.mu.Lock() + defer q.mu.Unlock() + _, ok := q.registry[u] + if !ok { + return fmt.Errorf(`url %q has not been registered`, u) + } + delete(q.registry, u) + return nil +} + +func (q *queue) getRegistered(u string) (*entry, bool) { + q.mu.RLock() + e, ok := q.registry[u] + q.mu.RUnlock() + + return e, ok +} + +func (q *queue) IsRegistered(u string) bool { + _, ok := q.getRegistered(u) + return ok +} + +func (q *queue) fetchLoop(ctx context.Context, errSink ErrSink) { + for { + q.fetchCond.L.Lock() + for len(q.fetchQueue) <= 0 { + select { + case <-ctx.Done(): + return + default: + q.fetchCond.Wait() + } + } + list := make([]*rqentry, len(q.fetchQueue)) + copy(list, q.fetchQueue) + q.fetchQueue = q.fetchQueue[:0] + q.fetchCond.L.Unlock() + + for _, rq := range list { + select { + case <-ctx.Done(): + return + default: + } + + e, ok := q.getRegistered(rq.url) + if !ok { + continue + } + if err := q.fetchAndStore(ctx, e); err != nil { + if errSink != nil { + errSink.Error(&RefreshError{ + URL: rq.url, + Err: err, + }) + } + } + } + } +} + +// This loop is responsible for periodically updating the cached content +func (q *queue) refreshLoop(ctx context.Context, errSink ErrSink) { + // Tick every q.windowSize duration. + ticker := time.NewTicker(q.windowSize) + + go q.fetchLoop(ctx, errSink) + defer q.fetchCond.Signal() + + for { + select { + case <-ctx.Done(): + return + case t := <-ticker.C: + t = t.Round(time.Second) + // To avoid getting stuck here, we just copy the relevant + // items, and release the lock within this critical section + var list []*rqentry + q.mu.Lock() + var max int + for i, r := range q.list { + if r.fireAt.Before(t) || r.fireAt.Equal(t) { + max = i + list = append(list, r) + continue + } + break + } + + if len(list) > 0 { + q.list = q.list[max+1:] + } + q.mu.Unlock() // release lock + + if len(list) > 0 { + // Now we need to fetch these, but do this elsewhere so + // that we don't block this main loop + q.fetchCond.L.Lock() + q.fetchQueue = append(q.fetchQueue, list...) + q.fetchCond.L.Unlock() + q.fetchCond.Signal() + } + } + } +} + +func (q *queue) fetchAndStore(ctx context.Context, e *entry) error { + e.mu.Lock() + defer e.mu.Unlock() + + // synchronously go fetch + e.lastFetch = time.Now() + res, err := q.fetch.fetch(ctx, e.request) + if err != nil { + // Even if the request failed, we need to queue the next fetch + q.enqueueNextFetch(nil, e) + return fmt.Errorf(`failed to fetch %q: %w`, e.request.url, err) + } + + q.enqueueNextFetch(res, e) + + data, err := e.transform.Transform(e.request.url, res) + if err != nil { + return fmt.Errorf(`failed to transform HTTP response for %q: %w`, e.request.url, err) + } + e.data = data + + return nil +} + +func (q *queue) Enqueue(u string, interval time.Duration) error { + fireAt := time.Now().Add(interval).Round(time.Second) + + q.mu.Lock() + defer q.mu.Unlock() + + list := q.list + + ll := len(list) + if ll == 0 || list[ll-1].fireAt.Before(fireAt) { + list = append(list, &rqentry{ + fireAt: fireAt, + url: u, + }) + } else { + for i := 0; i < ll; i++ { + if i == ll-1 || list[i].fireAt.After(fireAt) { + // insert here + list = append(append(list[:i], &rqentry{fireAt: fireAt, url: u}), list[i:]...) + break + } + } + } + + q.list = list + return nil +} + +func (q *queue) MarshalJSON() ([]byte, error) { + var buf bytes.Buffer + buf.WriteString(`{"list":[`) + q.mu.RLock() + for i, e := range q.list { + if i > 0 { + buf.WriteByte(',') + } + fmt.Fprintf(&buf, `{"fire_at":%q,"url":%q}`, e.fireAt.Format(time.RFC3339), e.url) + } + q.mu.RUnlock() + buf.WriteString(`]}`) + return buf.Bytes(), nil +} + +func (q *queue) enqueueNextFetch(res *http.Response, e *entry) { + dur := calculateRefreshDuration(res, e) + // TODO send to error sink + _ = q.Enqueue(e.request.url, dur) +} + +func calculateRefreshDuration(res *http.Response, e *entry) time.Duration { + if e.refreshInterval > 0 { + return e.refreshInterval + } + + if res != nil { + if v := res.Header.Get(`Cache-Control`); v != "" { + dir, err := httpcc.ParseResponse(v) + if err == nil { + maxAge, ok := dir.MaxAge() + if ok { + resDuration := time.Duration(maxAge) * time.Second + if resDuration > e.minRefreshInterval { + return resDuration + } + return e.minRefreshInterval + } + // fallthrough + } + // fallthrough + } + + if v := res.Header.Get(`Expires`); v != "" { + expires, err := http.ParseTime(v) + if err == nil { + resDuration := time.Until(expires) + if resDuration > e.minRefreshInterval { + return resDuration + } + return e.minRefreshInterval + } + // fallthrough + } + } + + // Previous fallthroughs are a little redandunt, but hey, it's all good. + return e.minRefreshInterval +} + +type SnapshotEntry struct { + URL string `json:"url"` + Data interface{} `json:"data"` + LastFetched time.Time `json:"last_fetched"` +} +type Snapshot struct { + Entries []SnapshotEntry `json:"entries"` +} + +// Snapshot returns the contents of the cache at the given moment. +func (q *queue) snapshot() *Snapshot { + q.mu.RLock() + list := make([]SnapshotEntry, 0, len(q.registry)) + + for url, e := range q.registry { + list = append(list, SnapshotEntry{ + URL: url, + LastFetched: e.lastFetch, + Data: e.data, + }) + } + q.mu.RUnlock() + + return &Snapshot{ + Entries: list, + } +} diff --git a/vendor/github.com/lestrrat-go/httprc/whitelist.go b/vendor/github.com/lestrrat-go/httprc/whitelist.go new file mode 100644 index 0000000..b80332a --- /dev/null +++ b/vendor/github.com/lestrrat-go/httprc/whitelist.go @@ -0,0 +1,73 @@ +package httprc + +import "regexp" + +// Whitelist is an interface for a set of URL whitelists. When provided +// to fetching operations, urls are checked against this object, and +// the object must return true for urls to be fetched. +type Whitelist interface { + IsAllowed(string) bool +} + +// InsecureWhitelist allows any URLs to be fetched. +type InsecureWhitelist struct{} + +func (InsecureWhitelist) IsAllowed(string) bool { + return true +} + +// RegexpWhitelist is a httprc.Whitelist object comprised of a list of *regexp.Regexp +// objects. All entries in the list are tried until one matches. If none of the +// *regexp.Regexp objects match, then the URL is deemed unallowed. +type RegexpWhitelist struct { + patterns []*regexp.Regexp +} + +func NewRegexpWhitelist() *RegexpWhitelist { + return &RegexpWhitelist{} +} + +func (w *RegexpWhitelist) Add(pat *regexp.Regexp) *RegexpWhitelist { + w.patterns = append(w.patterns, pat) + return w +} + +// IsAlloed returns true if any of the patterns in the whitelist +// returns true. +func (w *RegexpWhitelist) IsAllowed(u string) bool { + for _, pat := range w.patterns { + if pat.MatchString(u) { + return true + } + } + return false +} + +// MapWhitelist is a httprc.Whitelist object comprised of a map of strings. +// If the URL exists in the map, then the URL is allowed to be fetched. +type MapWhitelist struct { + store map[string]struct{} +} + +func NewMapWhitelist() *MapWhitelist { + return &MapWhitelist{store: make(map[string]struct{})} +} + +func (w *MapWhitelist) Add(pat string) *MapWhitelist { + w.store[pat] = struct{}{} + return w +} + +func (w *MapWhitelist) IsAllowed(u string) bool { + _, b := w.store[u] + return b +} + +// WhitelistFunc is a httprc.Whitelist object based on a function. +// You can perform any sort of check against the given URL to determine +// if it can be fetched or not. +type WhitelistFunc func(string) bool + +func (w WhitelistFunc) IsAllowed(u string) bool { + return w(u) +} diff --git a/vendor/github.com/lestrrat-go/jwx/Changes b/vendor/github.com/lestrrat-go/jwx/Changes deleted file mode 100644 index ee88798..0000000 --- a/vendor/github.com/lestrrat-go/jwx/Changes +++ /dev/null @@ -1,723 +0,0 @@ -Changes -======= - -v1.2.25 23 May 2022 -[Bug Fixes][Security] - * [jwe] An old bug from at least 7 years ago existed in handling AES-CBC unpadding, - where the unpad operation might remove more bytes than necessary (#744) - This affects all jwx code that is available before v2.0.2 and v1.2.25. - -v1.2.24 05 May 2022 -[Security] - * Upgrade golang.org/x/crypto (#724) - -v1.2.23 13 Apr 2022 -[Bug fixes] - * [jwk] jwk.AutoRefresh had a race condition when `Configure()` was - called concurrently (#686) - (It has been patched correctly, but we may come back to revisit - the design choices in the near future) - -v1.2.22 08 Apr 2022 -[Bug fixes] - * [jws] jws.Verify was ignoring the `b64` header when it was present - in the protected headers (#681). Now the following should work: - - jws.Sign(..., jws.WithDetachedPayload(payload)) - // previously payload had to be base64 encoded - jws.Verify(..., jws.WithDetachedPayload(payload)) - - (note: v2 branch was not affected) - -v1.2.21 30 Mar 2022 -[Bug fixes] - * [jwk] RSA keys without p and q can now be parsed. - -v1.2.20 03 Mar 2022 -[Miscellaneous] - * Dependency on golang.org/x/crypto has been upgraded to - v0.0.0-20220214200702-86341886e292 to address - https://nvd.nist.gov/vuln/detail/CVE-2020-14040 (#598) - -v1.2.19 22 Feb 2022 -[New Feature] - * [jwk] jwk.Parse (and (jwk.AutoRefresh).Configure) can accept a new - option `jwk.WithIgnoreParseError(bool)`, which allows users to ignore - errors during parsing of each key contained in the JWKS, allowing - you to "skip" invalid keys. - - This option should not be used lightly, as it hides the presence of - possibly faulty keys. However, this can be an escape hatch if you are - faced with a faulty JWKS that you do not control. - -v1.2.18 23 Jan 2022 -[Bug fixes] - * [jwe] When presented with jwk.Key with a key ID, the jwe encryption - code path did not assign this key ID to the resulting data structure. - This has been fixed, and now the key ID is properly applied to the - `kid` field. - * [jws] Use for `crypto.Signer`s were implemented for signing, but verification was - never properly implemented. This has been fixed. - -[Miscellaneous] - * [jws] Because of fixes to code path that deals with `crypto.Signer`s, we are - now able to fully integrate with Cloud services, such as Google's Cloud KMS - and AWS KMS, that provide key management and signing payloads - - An implementation for these are available at https://github.com/jwx-go/crypto-signer. - - Suppot `crypto.Signer` in JWE encryption has not been implemented. - -v1.2.17 12 Jan 2022 -[Miscellaneous] - * Re-release v1.2.16 as v1.2.17 because of an error in the release process. - The code is exactly the same as what v1.2.16 intended to release. - v1.2.16 has been retracted in go.mod. - -v1.2.16 12 Jan 2022 - - THIS VERSION HAS BEEN RETRACTED. PLEASE USE v1.2.17 - -[Bug Fixes] - * Peviously, `jws.Sign()` could not create a signed payload with - detached and unencoded payload, even when the documentation said it could. - Now you may use the `jws.Sign()` in the following way to create - a JWS message with detached, unencoded state: - - hdrs := jws.NewHeaders() - hdrs.Set("b64", false) - hdrs.Set("crit", "b64") - jws.Sign(nil, alg, key, jws.WithDetachedPayload(payload), jws.WithHeaders(hdrs)) - - Notice the use of `nil` for the first parameter, and the use of - `jws.WithDetachedPayload()`. - - We realize this is not exactly a clean API, but this is currently the - only way to implement this in a backward-compatible fashion. Most likely - this will change in a future major version. -[Miscellaneous] - * `jws.WithDetachedPayload()` is now of type `jws.SignVerifyOption`, which - satisfies both `jws.SignOption` and `jws.VerifyOption` - - -v1.2.15 07 Jan 2022 -[New Features] - * `(jwk.AutoRefresh).Remove()` has been implemented. -[Bug Fixes] - * ES256K is now included in the list of JWS inferred algorithms, if it's - enabled via -tags jwx_es256k -[Miscellaneous] - * `jwt.Parse` has been improved for efficiency and has more tests to - cover corner cases. - * Documentation fixes - -v1.2.14 22 Dec 2021 -[New Features] - * `jwk.Fetch()` and `(*jwk.AutoRefresh).Configure()` can now take `jwk.Whitelist` - object to check for the validity of a url to be fetched - * `jws.VerifyAuto()` has been added to verify payloads that can be verified - using the JWK set provided in the "jku" field. This function is purposely - separated from the `jws.Verify()` function because 1) the required parameters - are different, and 2) Users MUST be aware that they are doing a totally - different operation than a regular `jws.Verify()` - * `(jwk.AutoRefresh).IsRegistered()` has been added. - -[Bug fixes] - * `jws.SignMulti()` has been fixed to assign the "kid" field of the key used - for signing the payload - * `jws.SignMulti()` has been fixed to respect the "kid" field of the protected - header, not the public header - -v1.2.13 07 Dec 2021 -[New Features] - * `jwt` package now has a `Builder` that may make it easier to programmatically - create a JWT for some users. - * `jwt` errors now can be distinguished between validation errors and others. - Use `jwt.IsValidationError()` to check if it's a validation error, and then - use `errors.Is()` to check if it's one of the known (oft-used) errors - -v1.2.12 01 Dec 2021 -[New Features] - * `jwk.Set` can now parse private parameters. For example, after parsing - a JWKS serialized as `{"foo": "bar", "keys": [...]}`, users can get to - the value of `"foo"` by calling `set.Field("foo")` - * `jwk.Set` now has `Set()` method to set field values. - -v1.2.11 14 Nov 2021 -[Security Fix] - * It was reported that since v1.2.6, it was possible to craft - a special JSON object to bypass JWT verification via `jwt.Parse`. - If you relied on this module to perform all the verification, - upgrade is strongly recommended. - -v1.2.10 09 Nov 2021 -[Bug fixes] - * Parsing OpenID claims were not working for some fields. - This was caused by the same problem as the problem fixed in v1.2.9. - Proper tests have been added. - -v1.2.9 26 Oct 2021 -[Bug fixes] - * Parsing `key_ops` for JWKs which was broken in v1.2.8 has been fixed. - -v1.2.8 21 Oct 2021 -[Miscellaneous] - * `jws.Message`, `jws.Signature`, `jws.Headers` have been reworked - to allow JSON messages to be verified correctly. The problem can - be caused when protected headers are serialized one way (perhaps - `{"c":3","a":1,"b":2}` was used before being base64-encoded) but - the Go serialization differed from it (Go serializes in alphabetical - order: `{"a":1,"b":2,"c":3}`) - - Messages serialized in compact form do NOT suffer from the - same problem. - - This is close to fixes that went in v1.2.2. It boils down to the - fact that once deserialized, the JWS messages lose part of its - information (namely, the raw, original protected header value), - and neither users nor the developers of this library should - rely on it. - - * Code generation has be refactored. The main go.mod should now - have slightly less dependencies. - -v1.2.7 26 Sep 2021 -[New features] - * `jwt.InferAlgorithmFromKey()` option is now available to "guess" - the algorithm used to verify the JWS signature on a JWT using - a JWKS (key set). This allows you to match JWKs that do not have - the `alg` field populated. - - We understand that some providers do not provide the `alg` field, - which is a nuisance to users. But from a purely security minded PoV, - we don't think that this "try until something works" approach is a - good one, even if there are no known exploits. This is why the - default `jwt.Parse` mechanism is unchanged, and an explicit option - has been added. - - * Types `jwt.KeySetProvider` and `jwk.KeySetProviderFunc` have been - added. Along with `jwt.WithKeySetProvider()` option, `jwt.Parse` - can now choose the `jwk.Set` to use for signature verification - dynamically using the UNVERFIEID token as a clue. - - You should NOT trust the token information too much. For example, - DO NOT directly use values from the token as verificatin parameters - (such as the signature algorithm) - - * `jwt.WithValidator()` has been added to allow users pass in aribtrary - validation code to the `jwt.Validate()` method. - - It is also now possible to pass in a `context.Context` object to - `jwt.Validate()` using `jwt.WithContext()` option. - -[Miscellaneous] - * Make the error messages when `jwt.ParseRequest` fails a bit better. - * Moved around documentation within the repository - * Validation logic for `jwt.Validate()` has been refactored to use the - new `jwt.Validator` mechanism - -v1.2.6 24 Aug 2021 -[New features] - * Support `crypto.Signer` keys for RSA, ECDSA, and EdDSA family - of signatures in `jws.Sign` -[Miscellaneous] - * `jwx.GuessFormat()` now requires the presense of both `payload` and - `signatures` keys for it to guess that a JSON object is a JWS message. - * Slightly enhance `jwt.Parse()` performance. - -v1.2.5 04 Aug 2021 -[New features] - * Implement RFC7797. The value of the header field `b64` changes - how the payload is treated in JWS - * Implement detached payloads for JWS - * Implement (jwk.AutoRefresh).ErrorSink() to register a channel - where you can receive errors from fetches and parses that occur during - JWK(s) retrieval. - -v1.2.4 15 Jul 2021 -[Bug fixes] - * We had the same off-by-one in another place and jumped the gun on - releasing a new version. At least we were making mistakes uniformally :/ - `(jwk.Set).Remove` should finally be fixed. - -[New features] - * `(jwk.Set).Clone()` has been added. - -v1.2.3 15 Jul 2021 -[Bug fixes] - * jwk.Set incorrectly removed 2 elements instead of one. - -[Miscellaneous] - * github.com/goccy/go-json has been upgraded to v0.7.4 - -v1.2.2 13 Jul 2021 -[Deprecation notice] - * `(jwe.Message).Decrypt()` will be removed from the API upon the next - major release. - -[Bug Fixes] - * `jwe.Decrypt` and `(jwe.Message).Decrypt()` failed to decrypt even - with the correct message contents when used along with `jwe.RegisterCustomField` - -[New features] - JWX - * Add GuessFormat() function to guess what the payload is. - - JWT - * Options `jwt.WithMinDelta()`, `jwt.WithMaxDelta()` have been added. - These can be used to compare time-based fields in the JWT object. - * Option `jwt.WithRequiredClaim()` has been added. This can be used - to check that JWT contains the given claim. - * `jwt.Parse` now understands payloads that have been encrypted _and_ signed. - This is more in line with the RFC than the previous implementation, but - due to the fact that it requires a couple of extra unmarshaling, it may - add some amount of overhead. - * `jwt.Serializer` has been added as an easy wrapper to perform multiple - levels of serializations (e.g. apply JWS, then JWE) - - JWE - * Option `jwe.WithMessage()` has been added. This allows the user to - obtain both the decrypted payload _and_ the raw `*jwe.Message` in one - go when `jwe.Decrypt()` is called - * Option `jwe.WithPostParser()`, along with `jwe.PostParser` and `jwe.PostParseFunc` - has been added. This allows advanced users to hook into the `jwe.Decrypt()` - process. The hook is called right after the JWE message has been parsed, - but before the actual decryption has taken place. - * `(jwe.Message).Decrypt()` has been marked for deprecation in a next major release. - - JWS - * Option `jwe.WithMessage()` has been added. This allows the user to - obtain both the verified payload _and_ the raw `*jws.Message` in one - go when `jws.Verify()` is called - * Options to `jws.Sign()` are not of type `jws.SignOption`. There should be - no user-visible effects unless you were storing these somewhere. - -v1.2.1 02 Jun 2021 -[New features] - * Option `jwt.WithTypedClaim()` and `jwk.WithTypedField()` have been added. - They allow a per-object custom conversion from their JSON representation - to a Go object, much like `RegisterCustomField`. - - The difference is that whereas `RegisterCustomField` has global effect, - these typed fields only take effect in the call where the option was - explicitly passed. - - `jws` and `jwe` does not have these options because - (1) JWS and JWE messages don't generally carry much in terms of custom data - (2) This requires changes in function signatures. - - Only use these options when you absolutely need to. While it is a powerful - tool, they do have many caveats, and abusing these features will have - negative effects. See the documentation for details - -v1.2.0 30 Apr 2021 - -This is a security fix release with minor incompatibilities from earlier version -with regards to the behavior of `jwt.Verify()` function - -[Security Fix] - * `jwt.Verify()` had improperly used the `"alg"` header from the JWS message - when `jwt.WithKeySet()` option was used (potentially allowing exploits - described in https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/. - This has been fixed by ONLY trusting the keys that you provide and - using the `"alg"` header from the keys themselves. (#375, #381) - - As a side effect, `jwt.WithKeySet()` requires that all applicable keys - to contain a valid `"alg"` header. Without this we cannot safely choose a key to use, - and hence verification will fail. - - The requirement for the `"alg"` header on keys is an INCOMPATIBLE behavior. - This may break existing code, if the key does not already have an `"alg"` header. - -[New features] - * `jwt.Settings()` and `jwt.WithFlattenAudience(bool)` has been added - to control how the "aud" claim is serialized into JSON. When this - is enabled, all JWTs with a single "aud" claim will serialize - the field as a single string, instead of an array of strings with - a single element, i.e.: - - // jwt.WithFlattenAudience(true) - {"aud": "foo"} - - // jwt.WithFlattenAudience(false) - {"aud": ["foo"]} - - This setting has a global effect. - -[Buf fixes] - * jwt.Validate now returns true if the value in `nbf` field is exactly - the same as what the clock returns (e.g. token.nbf == time.Now()) - -v1.1.7 02 Apr 2021 -[New features] - * `jwk.New` `jwk.Parse`, `jwk.ParseKey` can now take a Certificate in - ASN.1 DER format in PEM encoding to create a JWK. - -[Bug fixes] - * Protect `jwk.New()` from invalid RSA/ECDSA keys (#360, #361) - -[Miscellaneous] - * Removed "internal/blackmagic" and separated it to its own repository. - * Removed unused "marshal proxy" objects in jwt - * Added FAQ in `jwt` package - -v1.1.6 28 Mar 2021 -[Bug fixes] - * When an object (e.g. JWT) has a null value and `AsMap()` is called, - `github.com/lestrrat-go/iter` would panic. - This should be fixed in `github.com/lestrrat-go/iter@v1.0.1` and - the dependency has been updated accordingly - -[Miscellaneous] - * Added How-to style docs under `docs/` - * github.com/goccy/go-json dependency has been updated to v0.4.8 - -v1.1.5 12 Mar 2021 - This is a security fix release. The JWT validation could be skipped - for empty values. Upgrade recommended - -[Security Fix] - * JWT validation could be skipped for empty fields (#352). - -[Bug fixes] - * Allow setting JWT "typ" fields to any value (#351). - * Remove stray replace directive in cmd/jwx/go.mod (#349) - -v1.1.4 02 Mar 2021 -[New features] - * jwt.ParseRequest, jwt.ParseHeader, jwt.ParseForm have been added. - They are convenience functions to parse JWTs out of a HTTP request. - -[Miscellaneous] - * Fix jwt.Equals() so that comparison between values containing time.Time - actually work - - * ES256K has been made non-default. You must enable it using a build tag - - go build -tags jwx_es256k ... - - Your program will still compile without this tag, but it will return - an error during runtime, when ES256K is encountered. - This feature is still experimental. - -v1.1.3 22 Feb 2021 -[New features] - * Implemented ES256K signing (#337) - This feature should be considered experimental - -[Miscellaneous] - * Bump minimum required version to go1.15 - * Fix examples, bench, and cmd/jwx accidentally requiring go1.16 - * Dependencies for "github.com/goccy/go-json" has been upgraded to - v0.4.7 - -v1.1.2 16 Feb 2021 -[New features] - * `RegisterCustomField()` has been added, which allows users to - specify a private claim/field/header to decode into a particular - object of choice, instead of map[string]interface{} or []interface{} (#332, #333) - -[Bug fixes] - * Failures for `jwk.Key.MarshalJSON()` were not properly reported (#330, #331) - -[Miscellaneous] - * `jwe.Encrypt()` now takes options. This should not matter unless you - were somehow depending on its method signature. - * Dependencies for "github.com/goccy/go-json" has been upgraded to - v0.4.2 - -v1.1.1 05 Feb 2021 -[New features] - * Command line tool `jwx` has ben completely reworked, and it is - now actually useful. - - * JWKs can now be serialized into PEM files with ASN.1 DER format - data, which is useful when you need to work between JSON and PEM - data formats. - - * Constants in jwa package now have can be listed via functions - in each category. - - * jwe.Encrypt and jwe.Decrypt can now handle jwk.Key objects - -v1.1.0 31 Jan 2021 - v1.1.0 is a release that attempts to fix as many of the quirky APIs - that survived the API breaking change of v0.9.x -> v1.0.0. This is - hopefully the last releases that change backwards compatibility - in a major way, at least for some time to come. - - It is unfortunate that we need to introduce API changes, but we - keep learning how the library is being used and the pain points - of using this library. Most of the times these pain points are - things that we initially did not think about, which in turn - requires us to rethink of the API. - - If you do not wish to spend the time fixing your usage, make sure - you have your go.mod set up to not automatically track the latest - changes. - - However, if you do decide to use the latest version, we believe - the API is more uniform across packages, and generally is easier - to understand. We hope this library helps some of you out there. - -[BREAKING CHANGES] - * `jwk.Parse(io.Reader)`, `jws.Parse(io.Reader)`, `jwt.Parse(io.Reader)`, - have all been changed to `Parse([]byte)`. To use an `io.Reader`, - use `ParseReader(io.Reader)`. `jwe.Parse` already took `[]byte`, so - has not been changed. - - With this change, all four package `jwe`, `jwk`, `jws`, and `jwt` follow - the same API design, which should make things easier to navigate: - - Parse([]byte) - ParseString(string) - ParseReader(io.Reader) - - * `jwk.Set` is now an interface, not a struct. `jwk.Set` now has a - well-defined API to access and modify the `jwk.Key` objects that it holds. - - Add(jwk.Key) bool - Clear() - Get(int) (jwk.Key, bool) - Index(jwk.Key) int - Len() int - LookupKeyID() (jwk.Key, bool) // Read the section about it below - Remove(jwk.Key) bool - Iterate(context.Context) KeyIterator - - * `(jwk.Set).LookupKeyID()` no longer returns an array of `jwk.Key`. - Instead, only the first key matching the given key ID will be returned. - If you need to work with multiple keys, use `(jwk.Set).Iterate()` or - `(jwk.Set).Get()` to look for matching keys. - - * `jwk.PublicKeyOf()` has been renamed to `jwk.PublicRawKeyOf()`, - which converts raw keys (e.g. `rsa.PrivateKey`) to their public - counter part (e.g. `rsa.PublicKey`) - - `jwk.PublicKeyOf()` is now used to get the public counter part of - `jwk.Key` objects (e.g. `jwk.RSAPrivateKey` to `jwk.RSAPublicKey`) - - `jwk.PublicSetOf()` has been added to get a new `jwk.Set` but with - all keys transformed to public keys via `jwk.PublicKeyOf()` - - * `jwk.FetchXXXX` functions have been removed. `jwk.Fetch()` remains, but - it now takes `context.Context`, and doesn't support retrieving files - from the local file system. See `ReadFile()` for that. - - * `jws.VerifyWithJKU()`, `jws.VerifyWithJWK()`, `jwk.VerifyWithJWKSet()` - have all been removed, but `jwk.VerifySet(jwk.Set)` has been added. - - * `jws.SplitCompact(io.Reader)` has been changd to `jws.SplitCompact([]byte)` - Similar to `Parse()`, `SplitCompactReader(io.Reader)` and `SplitCompactString(string)` - have been added - - * `jws.SignLiteral` has been removed. - - * `jws.PayloadSigner` has been removed (but should not matter, because - this as internal-use only anyways) - - * `jwe.WithPrettyJSONFormat` has been renamed to `jwe.WithPrettyFormat` - - * `jwt.Verify` has been removed. Use `jwt.Parse()` aloing with the `jwt.WithVerify()` - option to perform signature verification. Validation of verified data - can be performed via `(jwt.Token).Validate()` method, which has been available - since v1.0.6 - - * Package `buffer` has been removed. This package should have been an internal - package to start with, but it was left because it had been incorporated - in the public API in our initial versions. - - * `(jwk.Key).Get(jwk.X509CertChainKey)` no longer returns a `jwk.CertificateChain`. - Instead it returns a raw []*x509.Certificate. - - * `(jwt.Token).Size() has been removed. - - * `jwt.WithOpenIDClaims()` has been removed. Use `jwt.WithToken(openid.New())` instead. - -[New Features] - * `jwe.ReadFile(string)`, `jwk.ReadFile(string)`, `jws.ReadFile(string)`, and - `jwt.ReadFile(string)` have been added. In the future, we plan to introduce - a `WithFS` option so you can read from an arbitrary file system, but this cannot - be added while we keep go < 1.16 compatibility. If you want something like that, - you will need to put an adapter over the jwx for the time being. - - * `(jwk.Key).PublicKey()` has been added. This method creates a corresponding - public key, with all fields (except those that shouldn't be) copied over. - This allows you to easily create a public key of a private key with the - same "kid" attribute. - - * Both `jws.Verify` and `jws.Sign` methods can now handle `jwk.Key` objects, on - top of raw keys (e.g. rsa.PrivateKey). You no longer need to conver the - `jwk.Key` objects that you have in to raw keys before using these functions. - - * `(jws.Header).Remove(string)`, `(jwk.Key).Remove(string)`, and - `(jwt.Token).Remove(string)` have been added. `jwe.Header` already had a `Remove()` - method, so it has not been changed. - - * `(jwk.Key).Clone() has been added. - -[Miscellaneous] - * Default branch for the repository is now `main`. - - * Options have been reworked. In most instances, option types should now reflect - better the contexts in which they can be used. For example, `jwk` now has - `AutoRefreshOption` and `FetchOption` instead of a single `Option`. - - * JSON marshaling should be 10~30% faster by default (though they may take - more allocations to achieve this). - - However, if performance is really bogging you down, you can try to enable - the optional module github.com/goccy/go-json by enabling the "jwx_goccy" tag - - go build -tags jwx_goccy ... - - In some cases you get an extra 40~50% performance improvement in serailization - https://github.com/lestrrat-go/jwx/pull/314#issue-560594020 - https://github.com/lestrrat-go/jwx/pull/314#issuecomment-766343888 - - * Location for examples and benchmarks have changed: Now examples/ and bench/ - are their respective locations, and they are each a standalone module, - so that in case we need extra imports (such as the case in examples) - they do not interfere with users who just want to include jwx in their projects. - -v1.0.8 15 Jan 2021 -[New features] - * Fixed `jws.Message` and `jws.Signature` to be properly formatted when - marshaled into JSON. In the same manner, `json.Unmarshal` should also - work as expected. - * Added API to programatically manipulate `jws.Message` and `jws.Signature` -[Miscellaneous] - * The order of keys are now consistent as when used with `json.Marshal`. - Previously some objects used their own ordering, but now the code goes - through one extra roundtrip of `json.Unmarshal`/`json.Marshal` to preserve - compatible behavior. This *may* lead to slightly slower performance if - you are performing `json.Marshal` over and over in very quick succession. - Please file an issue if you have real world cases where the change - causes problems for you. - * Added more examples in various places. - * Tests runs have been sped up for the most oft used cases - -v1.0.7 11 Jan 2021 -[New features] - * Added jwk.AutoRefresh, which is a tool to periodically refresh JWKS. (#265) - * Added experimental ed25519 support (#252) -[Bug fixes] - * Fix `Set()` method for jwk Keys to properly accept either `jwk.KeyUsageType` - or a simple string. -[Miscellaneous] - * Updated dependencies - * Changed options to use github.com/lestrrat-go/option - * Various typos, unused annotations, etc, have been fixed by contributors - * Nobody except for the author really should care, but the underlying - `pdebug` utility, which is used for print debugging, has been - upgraded to v3, which should stop parallel test execution from throwing - an error when run with -race - -v1.0.6 17 Dec 2020 - * Fix ECDHES ciphers where padding in AAD et al was creating - incomptabile values with jose tool - * Also fix ECDH-ES cek handling (#248) - * Implement direct key encoding (#213, #249) - * Allow JWT tokens to use default JWK if only one key is given - and the JWT does not necessarily specifies a key (#214) - * Deprecate jwt.Verify and introduce jwt.Validate. JWS verification - used the term Verify, which was confusing when users wanted to - validate the JWT token itself. (#220) - * JWT library optins have been explicitly typed as ValidationOption - and ParseOption (#220, #223) - * Add jwx.DecoderSettings and jwx.WithUseNumber option to globally - change how jwx parses JSON objects (#222) - * Encode x5c field as base64 with padding (#244) - * Add more interoperability tests against jose tool. - * Special thanks to anatol and imirkin! - -v1.0.5 - 28 Sep 2020 - * Reinstate PrivateParams() method in jws and jwe packages. - These used to be available until v1.0.0, but somehow got lost during the - big change. - As a workaround for users of versions 1.0.0 to 1.0.4, you could have - achieved the same thing using AsMap() methods, albeit with a slight - performance penality (#205, #206) - -v1.0.4 - 15 Aug 2020 - * Fix jwt.WithOpenIDClaims(). Looks like something got lost along - the way, and it never really worked. (#201 #202) - -v1.0.3 - 08 Jul 2020 - * `jws.Sign`, and therefore `jwt.Sign` now accept `jwk.Key` as the - key to use for signature. (#199) - * `jwt.Sign` could sometimes return a nil error when setting bad - values to the protected header failed (#195) - * More golangci-lint cleanup (#193) - -v1.0.2 - 07 May 2020 - * Since 1.0.0, we took some time to play the test coverage game. - The coverage is around 30% better, and we _did_ uncover some - inconsistencies in the API, which got promptly fixed. - But I'm tired of the coverage game for the time being. PR's welcome! - * Add jwk.AssignKeyID to automatically assign a `kid` field to a JWK - * Fix jwe.Encrypt / jwe.Decrypt to properly look at the `zip` field - * Change jwe.Message accessors to return []byte, not buffer.Buffer - -v1.0.1 - 04 May 2020 - * Normalize all JWK serialization to use padding-less base64 encoding (#185) - * Fix edge case unmarshaling openid.AddressClaim within a openid.Token - * Fix edge case unmarshaling jwe.Message - * Export JWK key-specific constants, such as jwk.RSANKey, jwk.SymmetricOctetsKey, etc - * Remove some unused code - -v1.0.0 - 03 May 2020 - * All packages (`jws`, `jwe`, `jwk`, `jwt`) have all been reworked from - the ground-up. - * These packages now hide the actual implementation of the main structs behind an interface. - * Header/Token structs must now be instantiated using proper constructors - (most notably, json.Unmarshal will miserably fail if you just pass - and empty interface via `xxx.Token` or similar) - * Token/Header interfaces are now more or less standardized. - The following API should be consistent between all relevant packages: - * New() - * Get() - * Set() - * Remove() - * Iterate() - * Walk() - * AsMap() - * Oft-used fields are no longer directly accessible: - e.g. `token.KeyID = v` is no longer valid. You must set using `Set` - (and `Remove`, if you are removing it), and use either `Get` or - one of the utility methods such as `token.KeyID()` - * Many helper functions and structs have been unexported. They were never - meant to be anything useful for end-users, and hopefully it does not - cause any problems. - * Most errors type/instances have been removed from the public API - * `jwt` package can now work with different token types, such as OpenID tokens. - * `token.Sign` and `token.Verify` have been changed from methods to - package functions `jwt.Sign` and `jwt.Verify`, to allow different - types of tokens to be passed to the same logic. - * Added a custom token type in `openid` sub-package to make it easier to - work with OpenID claims - * `jwt.Parse` (and its siblings) now accept `jwt.WithOpenIDClaims()` - * `jwe` API has been reworked: - * `MultiEncrypt` has been removed. - * Serializer structs have been removed. Now you just need to call - `jwe.Compact` or `jwe.JSON` - * `jwk` API has been reworked: - * `jwk.ParseKey` has been added - * `jwk.Materialize` has been renamed to `Raw()`. A new corresponding - method to initialize the key from a raw key (RSA/ECDSA/byte keys) - called `FromRaw()` has also been added, which makes a nice pair. - * `jws` API has been reworked - * CI has been changed from Travis CI to Github Actions, and tests now - include linting via `golangci-lint` - -v0.9.2 - 15 Apr 2020 - * Maintenance release to protect users from upcoming breaking changes - -v0.9.1 - 27 Feb 2020 - * Fix error wrapping in certain cases - * Add Claims(), Walk(), and AsMap() to iterate claims, as well as - getting the entire data out as a single map - * Work with alternate base64 encodings when decoding - -v0.9.0 - 22 May 2019 - * Start tagging versions for good measure. diff --git a/vendor/github.com/lestrrat-go/jwx/README.md b/vendor/github.com/lestrrat-go/jwx/README.md deleted file mode 100644 index 89ce8d8..0000000 --- a/vendor/github.com/lestrrat-go/jwx/README.md +++ /dev/null @@ -1,134 +0,0 @@ -# github.com/lestrrat-go/jwx ![](https://github.com/lestrrat-go/jwx/workflows/CI/badge.svg) [![Go Reference](https://pkg.go.dev/badge/github.com/lestrrat-go/jwx.svg)](https://pkg.go.dev/github.com/lestrrat-go/jwx) [![codecov.io](http://codecov.io/github/lestrrat-go/jwx/coverage.svg?branch=main)](http://codecov.io/github/lestrrat-go/jwx?branch=main) - -Various libraries implementing various JWx technologies. Please click on the package names in the table below to find the synopsis/description for each package. - -If you are using this module in your product or your company, please add your product and/or company name in the [Wiki](https://github.com/lestrrat-go/jwx/wiki/Users)! It really helps keeping up our motivation. - -| Package name | Notes | -|-----------------------------------------------------------|-------------------------------------------------| -| [jwt](https://github.com/lestrrat-go/jwx/tree/main/jwt) | [RFC 7519](https://tools.ietf.org/html/rfc7519) | -| [jwk](https://github.com/lestrrat-go/jwx/tree/main/jwk) | [RFC 7517](https://tools.ietf.org/html/rfc7517) + [RFC 7638](https://tools.ietf.org/html/rfc7638) | -| [jwa](https://github.com/lestrrat-go/jwx/tree/main/jwa) | [RFC 7518](https://tools.ietf.org/html/rfc7518) | -| [jws](https://github.com/lestrrat-go/jwx/tree/main/jws) | [RFC 7515](https://tools.ietf.org/html/rfc7515) + [RFC 7797](https://tools.ietf.org/html/rfc7797) | -| [jwe](https://github.com/lestrrat-go/jwx/tree/main/jwe) | [RFC 7516](https://tools.ietf.org/html/rfc7516) | - -# How to Use - -* [API documentation](https://pkg.go.dev/github.com/lestrrat-go/jwx) -* [How-to style documentation](./docs) -* [Runnable Examples](./examples) - -# Description - -## History - -My goal was to write a server that heavily uses JWK and JWT. At first glance -the libraries that already exist seemed sufficient, but soon I realized that - -1. To completely implement the protocols, I needed the entire JWT, JWK, JWS, JWE (and JWA, by necessity). -2. Most of the libraries that existed only deal with a subset of the various JWx specifications that were necessary to implement their specific needs - -For example, a certain library looks like it had most of JWS, JWE, JWK covered, but then it lacked the ability to include private claims in its JWT responses. Another library had support of all the private claims, but completely lacked in its flexibility to generate various different response formats. - -Because I was writing the server side (and the client side for testing), I needed the *entire* JOSE toolset to properly implement my server, **and** they needed to be *flexible* enough to fulfill the entire spec that I was writing. - -So here's `github.com/lestrrat-go/jwx`. This library is extensible, customizable, and hopefully well organized to the point that it is easy for you to slice and dice it. - -## Why would I use this library? - -There are several other major Go modules that handle JWT and related data formats, -so why should you use this library? - -From a purely functional perspective, the only major difference is this: -Whereas most other projects only deal with what they seem necessary to handle -JWTs, this module handles the **_entire_** spectrum of JWS, JWE, JWK, and JWT. - -That is, if you need to not only parse JWTs, but also to control JWKs, or -if you need to handle payloads that are NOT JWTs, you should probably consider -using this module. You should also note that JWT is built _on top_ of those -other technologies. You simply cannot have a complete JWT package without -implementing the entirety of JWS/JWS/JWK, which this library does. - -Next, from an implementation perspective, this module differs significantly -from others in that it tries very hard to expose only the APIs, and not the -internal data. For example, individual JWT claims are not accessible through -struct field lookups. You need to use one of the getter methods. - -This is because this library takes the stance that the end user is fully capable -and even willing to shoot themselves on the foot when presented with a lax -API. By making sure that users do not have access to open structs, we can protect -users from doing silly things like creating _incomplete_ structs, or access the -structs concurrently without any protection. This structure also allows -us to put extra smarts in the structs, such as doing the right thing when -you want to parse / write custom fields (this module does not require the user -to specify alternate structs to parse objects with custom fields) - -In the end I think it comes down to your usage pattern, and priorities. -Some general guidelines that come to mind are: - -* If you want a single library to handle everything JWx, such as using JWE, JWK, JWS, handling [auto-refreshing JWKs](https://github.com/lestrrat-go/jwx/blob/main/docs/04-jwk.md#auto-refreshing-remote-keys), use this module. -* If you want to honor all possible custom fields transparently, use this module. -* If you want a standardized clean API, use this module. - -Otherwise, feel free to choose something else. - -# Command Line Tool - -Since v1.1.1 we have a command line tool `jwx` (*). With `jwx` you can create JWKs (from PEM files, even), sign and verify JWS message, encrypt and decrypt JWE messages, etc. - -(*) Okay, it existed since a long time ago, but it was never useful. - -## Installation - -``` -go install github.com/lestrrat-go/jwx/cmd/jwx -``` - -# Caveats - -## Backwards Compatibility Notice - -### Users of github.com/lestrrat/go-jwx - -Uh, why are you using such an ancient version? You know that repository is archived for a reason, yeah? Please use the new version. - -### Pre-1.0.0 users - -The API has been reworked quite substantially between pre- and post 1.0.0 releases. Please check out the [Changes](./Changes) file (or the [diff](https://github.com/lestrrat-go/jwx/compare/v0.9.2...v1.0.0), if you are into that sort of thing) - -### v1.0.x users - -The API has gone under some changes for v1.1.0. If you are upgrading, you might want to read the relevant parts in the [Changes](./Changes) file. - -# Contributions - -## Issues - -For bug reports and feature requests, please try to follow the issue templates as much as possible. -For either bug reports or feature requests, failing tests are even better. - -## Pull Requests - -Please make sure to include tests that excercise the changes you made. - -If you are editing auto-generated files (those files with the `_gen.go` suffix, please make sure that you do the following: - -1. Edit the generator, not the generated files (e.g. internal/cmd/genreadfile/main.go) -2. Run `make generate` (or `go generate`) to generate the new code -3. Commit _both_ the generator _and_ the generated files - -## Discussions / Usage - -Please try [discussions](https://github.com/lestrrat-go/jwx/discussions) first. - -# Related Modules - -* [github.com/jwx-go/crypto-signer/gcp](https://github.com/jwx-go/crypto-signer/tree/main/gcp) - GCP KMS wrapper that implements [`crypto.Signer`](https://pkg.go.dev/crypto#Signer) -* [github.com/jwx-go/crypto-signer/aws](https://github.com/jwx-go/crypto-signer/tree/main/aws) - AWS KMS wrapper that implements [`crypto.Signer`](https://pkg.go.dev/crypto#Signer) - -# Credits - -* Initial work on this library was generously sponsored by HDE Inc (https://www.hde.co.jp) -* Lots of code, especially JWE was taken from go-jose library (https://github.com/square/go-jose) -* Lots of individual contributors have helped this project over the years. Thank each and everyone of you very much. - diff --git a/vendor/github.com/lestrrat-go/jwx/gen.sh b/vendor/github.com/lestrrat-go/jwx/gen.sh deleted file mode 100644 index 74c4710..0000000 --- a/vendor/github.com/lestrrat-go/jwx/gen.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -# Script to perform code generation. This exists to overcome -# the fact that go:generate doesn't really allow you to change directories - -set -e - -pushd internal/cmd/genreadfile -go build -o genreadfile main.go -popd - -./internal/cmd/genreadfile/genreadfile - -rm internal/cmd/genreadfile/genreadfile diff --git a/vendor/github.com/lestrrat-go/jwx/jwa/README.md b/vendor/github.com/lestrrat-go/jwx/jwa/README.md deleted file mode 100644 index eafb291..0000000 --- a/vendor/github.com/lestrrat-go/jwx/jwa/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# JWA [![Go Reference](https://pkg.go.dev/badge/github.com/lestrrat-go/jwx/jwa.svg)](https://pkg.go.dev/github.com/lestrrat-go/jwx/jwa) - -Package [github.com/lestrrat-go/jwx/jwa](./jwa) defines the various algorithm described in [RFC7518](https://tools.ietf.org/html/rfc7518) diff --git a/vendor/github.com/lestrrat-go/jwx/jwa/gen.sh b/vendor/github.com/lestrrat-go/jwx/jwa/gen.sh deleted file mode 100644 index 93e1683..0000000 --- a/vendor/github.com/lestrrat-go/jwx/jwa/gen.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -# Script to perform code generation. This exists to overcome -# the fact that go:generate doesn't really allow you to change directories - -set -e - -pushd internal/cmd/gentypes -go build -o gentypes main.go -popd - -./internal/cmd/gentypes/gentypes -objects=internal/cmd/gentypes/objects.yml - -rm internal/cmd/gentypes/gentypes diff --git a/vendor/github.com/lestrrat-go/jwx/jwa/jwa.go b/vendor/github.com/lestrrat-go/jwx/jwa/jwa.go deleted file mode 100644 index c34ad16..0000000 --- a/vendor/github.com/lestrrat-go/jwx/jwa/jwa.go +++ /dev/null @@ -1,4 +0,0 @@ -//go:generate ./gen.sh - -// Package jwa defines the various algorithm described in https://tools.ietf.org/html/rfc7518 -package jwa diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/compress.go b/vendor/github.com/lestrrat-go/jwx/jwe/compress.go deleted file mode 100644 index e3836a6..0000000 --- a/vendor/github.com/lestrrat-go/jwx/jwe/compress.go +++ /dev/null @@ -1,41 +0,0 @@ -package jwe - -import ( - "bytes" - "compress/flate" - "io/ioutil" - - "github.com/lestrrat-go/jwx/internal/pool" - "github.com/lestrrat-go/jwx/jwa" - "github.com/pkg/errors" -) - -func uncompress(plaintext []byte) ([]byte, error) { - return ioutil.ReadAll(flate.NewReader(bytes.NewReader(plaintext))) -} - -func compress(plaintext []byte, alg jwa.CompressionAlgorithm) ([]byte, error) { - if alg == jwa.NoCompress { - return plaintext, nil - } - - buf := pool.GetBytesBuffer() - defer pool.ReleaseBytesBuffer(buf) - - w, _ := flate.NewWriter(buf, 1) - in := plaintext - for len(in) > 0 { - n, err := w.Write(in) - if err != nil { - return nil, errors.Wrap(err, `failed to write to compression writer`) - } - in = in[n:] - } - if err := w.Close(); err != nil { - return nil, errors.Wrap(err, "failed to close compression writer") - } - - ret := make([]byte, buf.Len()) - copy(ret, buf.Bytes()) - return ret, nil -} diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/encrypt.go b/vendor/github.com/lestrrat-go/jwx/jwe/encrypt.go deleted file mode 100644 index 230b5ab..0000000 --- a/vendor/github.com/lestrrat-go/jwx/jwe/encrypt.go +++ /dev/null @@ -1,145 +0,0 @@ -package jwe - -import ( - "context" - "sync" - - "github.com/lestrrat-go/jwx/internal/base64" - "github.com/lestrrat-go/jwx/jwa" - "github.com/pkg/errors" -) - -var encryptCtxPool = sync.Pool{ - New: func() interface{} { - return &encryptCtx{} - }, -} - -func getEncryptCtx() *encryptCtx { - //nolint:forcetypeassert - return encryptCtxPool.Get().(*encryptCtx) -} - -func releaseEncryptCtx(ctx *encryptCtx) { - ctx.protected = nil - ctx.contentEncrypter = nil - ctx.generator = nil - ctx.keyEncrypters = nil - ctx.compress = jwa.NoCompress - encryptCtxPool.Put(ctx) -} - -// Encrypt takes the plaintext and encrypts into a JWE message. -func (e encryptCtx) Encrypt(plaintext []byte) (*Message, error) { - bk, err := e.generator.Generate() - if err != nil { - return nil, errors.Wrap(err, "failed to generate key") - } - cek := bk.Bytes() - - if e.protected == nil { - // shouldn't happen, but... - e.protected = NewHeaders() - } - - if err := e.protected.Set(ContentEncryptionKey, e.contentEncrypter.Algorithm()); err != nil { - return nil, errors.Wrap(err, `failed to set "enc" in protected header`) - } - - compression := e.compress - if compression != jwa.NoCompress { - if err := e.protected.Set(CompressionKey, compression); err != nil { - return nil, errors.Wrap(err, `failed to set "zip" in protected header`) - } - } - - // In JWE, multiple recipients may exist -- they receive an - // encrypted version of the CEK, using their key encryption - // algorithm of choice. - recipients := make([]Recipient, len(e.keyEncrypters)) - for i, enc := range e.keyEncrypters { - r := NewRecipient() - if err := r.Headers().Set(AlgorithmKey, enc.Algorithm()); err != nil { - return nil, errors.Wrap(err, "failed to set header") - } - if v := enc.KeyID(); v != "" { - if err := r.Headers().Set(KeyIDKey, v); err != nil { - return nil, errors.Wrap(err, "failed to set header") - } - } - - enckey, err := enc.Encrypt(cek) - if err != nil { - return nil, errors.Wrap(err, `failed to encrypt key`) - } - if enc.Algorithm() == jwa.ECDH_ES || enc.Algorithm() == jwa.DIRECT { - if len(e.keyEncrypters) > 1 { - return nil, errors.Errorf("unable to support multiple recipients for ECDH-ES") - } - cek = enckey.Bytes() - } else { - if err := r.SetEncryptedKey(enckey.Bytes()); err != nil { - return nil, errors.Wrap(err, "failed to set encrypted key") - } - } - if hp, ok := enckey.(populater); ok { - if err := hp.Populate(r.Headers()); err != nil { - return nil, errors.Wrap(err, "failed to populate") - } - } - recipients[i] = r - } - - // If there's only one recipient, you want to include that in the - // protected header - if len(recipients) == 1 { - h, err := e.protected.Merge(context.TODO(), recipients[0].Headers()) - if err != nil { - return nil, errors.Wrap(err, "failed to merge protected headers") - } - e.protected = h - } - - aad, err := e.protected.Encode() - if err != nil { - return nil, errors.Wrap(err, "failed to base64 encode protected headers") - } - - plaintext, err = compress(plaintext, compression) - if err != nil { - return nil, errors.Wrap(err, `failed to compress payload before encryption`) - } - - // ...on the other hand, there's only one content cipher. - iv, ciphertext, tag, err := e.contentEncrypter.Encrypt(cek, plaintext, aad) - if err != nil { - return nil, errors.Wrap(err, "failed to encrypt payload") - } - - msg := NewMessage() - - decodedAad, err := base64.Decode(aad) - if err != nil { - return nil, errors.Wrap(err, "failed to decode base64") - } - if err := msg.Set(AuthenticatedDataKey, decodedAad); err != nil { - return nil, errors.Wrapf(err, `failed to set %s`, AuthenticatedDataKey) - } - if err := msg.Set(CipherTextKey, ciphertext); err != nil { - return nil, errors.Wrapf(err, `failed to set %s`, CipherTextKey) - } - if err := msg.Set(InitializationVectorKey, iv); err != nil { - return nil, errors.Wrapf(err, `failed to set %s`, InitializationVectorKey) - } - if err := msg.Set(ProtectedHeadersKey, e.protected); err != nil { - return nil, errors.Wrapf(err, `failed to set %s`, ProtectedHeadersKey) - } - if err := msg.Set(RecipientsKey, recipients); err != nil { - return nil, errors.Wrapf(err, `failed to set %s`, RecipientsKey) - } - if err := msg.Set(TagKey, tag); err != nil { - return nil, errors.Wrapf(err, `failed to set %s`, TagKey) - } - - return msg, nil -} diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/gen.sh b/vendor/github.com/lestrrat-go/jwx/jwe/gen.sh deleted file mode 100644 index dde877e..0000000 --- a/vendor/github.com/lestrrat-go/jwx/jwe/gen.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -# Script to perform code generation. This exists to overcome -# the fact that go:generate doesn't really allow you to change directories - -set -e - -pushd internal/cmd/genheader -go build -o genheader main.go -popd - -./internal/cmd/genheader/genheader -objects=internal/cmd/genheader/objects.yml - -rm internal/cmd/genheader/genheader diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/interface.go b/vendor/github.com/lestrrat-go/jwx/jwe/interface.go deleted file mode 100644 index c231208..0000000 --- a/vendor/github.com/lestrrat-go/jwx/jwe/interface.go +++ /dev/null @@ -1,101 +0,0 @@ -package jwe - -import ( - "github.com/lestrrat-go/iter/mapiter" - "github.com/lestrrat-go/jwx/internal/iter" - "github.com/lestrrat-go/jwx/jwa" - "github.com/lestrrat-go/jwx/jwe/internal/keyenc" - "github.com/lestrrat-go/jwx/jwe/internal/keygen" -) - -// Recipient holds the encrypted key and hints to decrypt the key -type Recipient interface { - Headers() Headers - EncryptedKey() []byte - SetHeaders(Headers) error - SetEncryptedKey([]byte) error -} - -type stdRecipient struct { - headers Headers - encryptedKey []byte -} - -// Message contains the entire encrypted JWE message. You should not -// expect to use Message for anything other than inspecting the -// state of an encrypted message. This is because encryption is -// highly context sensitive, and once we parse the original payload -// into an object, we may not always be able to recreate the exact -// context in which the encryption happened. -// -// For example, it is totally valid for if the protected header's -// integrity was calculated using a non-standard line breaks: -// -// {"a dummy": -// "protected header"} -// -// Once parsed, though, we can only serialize the protected header as: -// -// {"a dummy":"protected header"} -// -// which would obviously result in a contradicting integrity value -// if we tried to re-calculate it from a parsed message. -//nolint:govet -type Message struct { - authenticatedData []byte - cipherText []byte - initializationVector []byte - tag []byte - recipients []Recipient - protectedHeaders Headers - unprotectedHeaders Headers - - // These two fields below are not available for the public consumers of this object. - // rawProtectedHeaders stores the original protected header buffer - rawProtectedHeaders []byte - // storeProtectedHeaders is a hint to be used in UnmarshalJSON(). - // When this flag is true, UnmarshalJSON() will populate the - // rawProtectedHeaders field - storeProtectedHeaders bool -} - -// contentEncrypter encrypts the content using the content using the -// encrypted key -type contentEncrypter interface { - Algorithm() jwa.ContentEncryptionAlgorithm - Encrypt([]byte, []byte, []byte) ([]byte, []byte, []byte, error) -} - -//nolint:govet -type encryptCtx struct { - keyEncrypters []keyenc.Encrypter - protected Headers - contentEncrypter contentEncrypter - generator keygen.Generator - compress jwa.CompressionAlgorithm -} - -// populater is an interface for things that may modify the -// JWE header. e.g. ByteWithECPrivateKey -type populater interface { - Populate(keygen.Setter) error -} - -type Visitor = iter.MapVisitor -type VisitorFunc = iter.MapVisitorFunc -type HeaderPair = mapiter.Pair -type Iterator = mapiter.Iterator - -// PostParser is used in conjunction with jwe.WithPostParser(). -// This hook is called right after the JWE message has been parsed -// but before the actual decryption takes place during `jwe.Decrypt()`. -type PostParser interface { - PostParse(DecryptCtx) error -} - -// PostParseFunc is a PostParser that is represented by a single function -type PostParseFunc func(DecryptCtx) error - -func (fn PostParseFunc) PostParse(ctx DecryptCtx) error { - return fn(ctx) -} diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/io.go b/vendor/github.com/lestrrat-go/jwx/jwe/io.go deleted file mode 100644 index 031f610..0000000 --- a/vendor/github.com/lestrrat-go/jwx/jwe/io.go +++ /dev/null @@ -1,23 +0,0 @@ -// Automatically generated by internal/cmd/genreadfile/main.go. DO NOT EDIT - -package jwe - -import "os" - -// ReadFileOption describes options that can be passed to ReadFile. -// Currently there are no options available that can be passed to ReadFile, but -// it is provided here for anticipated future additions -type ReadFileOption interface { - Option - readFileOption() -} - -func ReadFile(path string, _ ...ReadFileOption) (*Message, error) { - f, err := os.Open(path) - if err != nil { - return nil, err - } - - defer f.Close() - return ParseReader(f) -} diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/jwe.go b/vendor/github.com/lestrrat-go/jwx/jwe/jwe.go deleted file mode 100644 index 8b45287..0000000 --- a/vendor/github.com/lestrrat-go/jwx/jwe/jwe.go +++ /dev/null @@ -1,377 +0,0 @@ -//go:generate ./gen.sh - -// Package jwe implements JWE as described in https://tools.ietf.org/html/rfc7516 -package jwe - -import ( - "bytes" - "crypto/ecdsa" - "crypto/rsa" - "io" - "io/ioutil" - - "github.com/lestrrat-go/jwx/internal/base64" - "github.com/lestrrat-go/jwx/internal/json" - "github.com/lestrrat-go/jwx/internal/keyconv" - "github.com/lestrrat-go/jwx/jwk" - - "github.com/lestrrat-go/jwx/jwa" - "github.com/lestrrat-go/jwx/jwe/internal/content_crypt" - "github.com/lestrrat-go/jwx/jwe/internal/keyenc" - "github.com/lestrrat-go/jwx/jwe/internal/keygen" - "github.com/lestrrat-go/jwx/x25519" - "github.com/pkg/errors" -) - -var registry = json.NewRegistry() - -// Encrypt takes the plaintext payload and encrypts it in JWE compact format. -// `key` should be a public key, and it may be a raw key (e.g. rsa.PublicKey) or a jwk.Key -// -// Encrypt currently does not support multi-recipient messages. -func Encrypt(payload []byte, keyalg jwa.KeyEncryptionAlgorithm, key interface{}, contentalg jwa.ContentEncryptionAlgorithm, compressalg jwa.CompressionAlgorithm, options ...EncryptOption) ([]byte, error) { - var protected Headers - for _, option := range options { - //nolint:forcetypeassert - switch option.Ident() { - case identProtectedHeader{}: - protected = option.Value().(Headers) - } - } - if protected == nil { - protected = NewHeaders() - } - - contentcrypt, err := content_crypt.NewGeneric(contentalg) - if err != nil { - return nil, errors.Wrap(err, `failed to create AES encrypter`) - } - - var keyID string - if jwkKey, ok := key.(jwk.Key); ok { - keyID = jwkKey.KeyID() - - var raw interface{} - if err := jwkKey.Raw(&raw); err != nil { - return nil, errors.Wrapf(err, `failed to retrieve raw key out of %T`, key) - } - - key = raw - } - - var enc keyenc.Encrypter - switch keyalg { - case jwa.RSA1_5: - var pubkey rsa.PublicKey - if err := keyconv.RSAPublicKey(&pubkey, key); err != nil { - return nil, errors.Wrapf(err, "failed to generate public key from key (%T)", key) - } - - enc, err = keyenc.NewRSAPKCSEncrypt(keyalg, &pubkey) - if err != nil { - return nil, errors.Wrap(err, "failed to create RSA PKCS encrypter") - } - case jwa.RSA_OAEP, jwa.RSA_OAEP_256: - var pubkey rsa.PublicKey - if err := keyconv.RSAPublicKey(&pubkey, key); err != nil { - return nil, errors.Wrapf(err, "failed to generate public key from key (%T)", key) - } - - enc, err = keyenc.NewRSAOAEPEncrypt(keyalg, &pubkey) - if err != nil { - return nil, errors.Wrap(err, "failed to create RSA OAEP encrypter") - } - case jwa.A128KW, jwa.A192KW, jwa.A256KW, - jwa.A128GCMKW, jwa.A192GCMKW, jwa.A256GCMKW, - jwa.PBES2_HS256_A128KW, jwa.PBES2_HS384_A192KW, jwa.PBES2_HS512_A256KW: - sharedkey, ok := key.([]byte) - if !ok { - return nil, errors.New("invalid key: []byte required") - } - switch keyalg { - case jwa.A128KW, jwa.A192KW, jwa.A256KW: - enc, err = keyenc.NewAES(keyalg, sharedkey) - case jwa.PBES2_HS256_A128KW, jwa.PBES2_HS384_A192KW, jwa.PBES2_HS512_A256KW: - enc, err = keyenc.NewPBES2Encrypt(keyalg, sharedkey) - default: - enc, err = keyenc.NewAESGCMEncrypt(keyalg, sharedkey) - } - if err != nil { - return nil, errors.Wrap(err, "failed to create key wrap encrypter") - } - // NOTE: there was formerly a restriction, introduced - // in PR #26, which disallowed certain key/content - // algorithm combinations. This seemed bogus, and - // interop with the jose tool demonstrates it. - case jwa.ECDH_ES, jwa.ECDH_ES_A128KW, jwa.ECDH_ES_A192KW, jwa.ECDH_ES_A256KW: - var keysize int - switch keyalg { - case jwa.ECDH_ES: - // https://tools.ietf.org/html/rfc7518#page-15 - // In Direct Key Agreement mode, the output of the Concat KDF MUST be a - // key of the same length as that used by the "enc" algorithm. - keysize = contentcrypt.KeySize() - case jwa.ECDH_ES_A128KW: - keysize = 16 - case jwa.ECDH_ES_A192KW: - keysize = 24 - case jwa.ECDH_ES_A256KW: - keysize = 32 - } - - switch key := key.(type) { - case x25519.PublicKey: - enc, err = keyenc.NewECDHESEncrypt(keyalg, contentalg, keysize, key) - default: - var pubkey ecdsa.PublicKey - if err := keyconv.ECDSAPublicKey(&pubkey, key); err != nil { - return nil, errors.Wrapf(err, "failed to generate public key from key (%T)", key) - } - enc, err = keyenc.NewECDHESEncrypt(keyalg, contentalg, keysize, &pubkey) - } - if err != nil { - return nil, errors.Wrap(err, "failed to create ECDHS key wrap encrypter") - } - case jwa.DIRECT: - sharedkey, ok := key.([]byte) - if !ok { - return nil, errors.New("invalid key: []byte required") - } - enc, _ = keyenc.NewNoop(keyalg, sharedkey) - default: - return nil, errors.Errorf(`invalid key encryption algorithm (%s)`, keyalg) - } - - if keyID != "" { - enc.SetKeyID(keyID) - } - - keysize := contentcrypt.KeySize() - encctx := getEncryptCtx() - defer releaseEncryptCtx(encctx) - - encctx.protected = protected - encctx.contentEncrypter = contentcrypt - encctx.generator = keygen.NewRandom(keysize) - encctx.keyEncrypters = []keyenc.Encrypter{enc} - encctx.compress = compressalg - msg, err := encctx.Encrypt(payload) - if err != nil { - return nil, errors.Wrap(err, "failed to encrypt payload") - } - - return Compact(msg) -} - -// DecryptCtx is used internally when jwe.Decrypt is called, and is -// passed for hooks that you may pass into it. -// -// Regular users should not have to touch this object, but if you need advanced handling -// of messages, you might have to use it. Only use it when you really -// understand how JWE processing works in this library. -type DecryptCtx interface { - Algorithm() jwa.KeyEncryptionAlgorithm - SetAlgorithm(jwa.KeyEncryptionAlgorithm) - Key() interface{} - SetKey(interface{}) - Message() *Message - SetMessage(*Message) -} - -type decryptCtx struct { - alg jwa.KeyEncryptionAlgorithm - key interface{} - msg *Message -} - -func (ctx *decryptCtx) Algorithm() jwa.KeyEncryptionAlgorithm { - return ctx.alg -} - -func (ctx *decryptCtx) SetAlgorithm(v jwa.KeyEncryptionAlgorithm) { - ctx.alg = v -} - -func (ctx *decryptCtx) Key() interface{} { - return ctx.key -} - -func (ctx *decryptCtx) SetKey(v interface{}) { - ctx.key = v -} - -func (ctx *decryptCtx) Message() *Message { - return ctx.msg -} - -func (ctx *decryptCtx) SetMessage(m *Message) { - ctx.msg = m -} - -// Decrypt takes the key encryption algorithm and the corresponding -// key to decrypt the JWE message, and returns the decrypted payload. -// The JWE message can be either compact or full JSON format. -// -// `key` must be a private key. It can be either in its raw format (e.g. *rsa.PrivateKey) or a jwk.Key -func Decrypt(buf []byte, alg jwa.KeyEncryptionAlgorithm, key interface{}, options ...DecryptOption) ([]byte, error) { - var ctx decryptCtx - ctx.key = key - ctx.alg = alg - - var dst *Message - var postParse PostParser - //nolint:forcetypeassert - for _, option := range options { - switch option.Ident() { - case identMessage{}: - dst = option.Value().(*Message) - case identPostParser{}: - postParse = option.Value().(PostParser) - } - } - - msg, err := parseJSONOrCompact(buf, true) - if err != nil { - return nil, errors.Wrap(err, "failed to parse buffer for Decrypt") - } - - ctx.msg = msg - if postParse != nil { - if err := postParse.PostParse(&ctx); err != nil { - return nil, errors.Wrap(err, `failed to execute PostParser hook`) - } - } - - payload, err := doDecryptCtx(&ctx) - if err != nil { - return nil, errors.Wrap(err, `failed to decrypt message`) - } - - if dst != nil { - *dst = *msg - dst.rawProtectedHeaders = nil - dst.storeProtectedHeaders = false - } - - return payload, nil -} - -// Parse parses the JWE message into a Message object. The JWE message -// can be either compact or full JSON format. -func Parse(buf []byte) (*Message, error) { - return parseJSONOrCompact(buf, false) -} - -func parseJSONOrCompact(buf []byte, storeProtectedHeaders bool) (*Message, error) { - buf = bytes.TrimSpace(buf) - if len(buf) == 0 { - return nil, errors.New("empty buffer") - } - - if buf[0] == '{' { - return parseJSON(buf, storeProtectedHeaders) - } - return parseCompact(buf, storeProtectedHeaders) -} - -// ParseString is the same as Parse, but takes a string. -func ParseString(s string) (*Message, error) { - return Parse([]byte(s)) -} - -// ParseReader is the same as Parse, but takes an io.Reader. -func ParseReader(src io.Reader) (*Message, error) { - buf, err := ioutil.ReadAll(src) - if err != nil { - return nil, errors.Wrap(err, `failed to read from io.Reader`) - } - return Parse(buf) -} - -func parseJSON(buf []byte, storeProtectedHeaders bool) (*Message, error) { - m := NewMessage() - m.storeProtectedHeaders = storeProtectedHeaders - if err := json.Unmarshal(buf, &m); err != nil { - return nil, errors.Wrap(err, "failed to parse JSON") - } - return m, nil -} - -func parseCompact(buf []byte, storeProtectedHeaders bool) (*Message, error) { - parts := bytes.Split(buf, []byte{'.'}) - if len(parts) != 5 { - return nil, errors.Errorf(`compact JWE format must have five parts (%d)`, len(parts)) - } - - hdrbuf, err := base64.Decode(parts[0]) - if err != nil { - return nil, errors.Wrap(err, `failed to parse first part of compact form`) - } - - protected := NewHeaders() - if err := json.Unmarshal(hdrbuf, protected); err != nil { - return nil, errors.Wrap(err, "failed to parse header JSON") - } - - ivbuf, err := base64.Decode(parts[2]) - if err != nil { - return nil, errors.Wrap(err, "failed to base64 decode iv") - } - - ctbuf, err := base64.Decode(parts[3]) - if err != nil { - return nil, errors.Wrap(err, "failed to base64 decode content") - } - - tagbuf, err := base64.Decode(parts[4]) - if err != nil { - return nil, errors.Wrap(err, "failed to base64 decode tag") - } - - m := NewMessage() - if err := m.Set(CipherTextKey, ctbuf); err != nil { - return nil, errors.Wrapf(err, `failed to set %s`, CipherTextKey) - } - if err := m.Set(InitializationVectorKey, ivbuf); err != nil { - return nil, errors.Wrapf(err, `failed to set %s`, InitializationVectorKey) - } - if err := m.Set(ProtectedHeadersKey, protected); err != nil { - return nil, errors.Wrapf(err, `failed to set %s`, ProtectedHeadersKey) - } - - if err := m.makeDummyRecipient(string(parts[1]), protected); err != nil { - return nil, errors.Wrap(err, `failed to setup recipient`) - } - - if err := m.Set(TagKey, tagbuf); err != nil { - return nil, errors.Wrapf(err, `failed to set %s`, TagKey) - } - - if storeProtectedHeaders { - // This is later used for decryption. - m.rawProtectedHeaders = parts[0] - } - - return m, nil -} - -// RegisterCustomField allows users to specify that a private field -// be decoded as an instance of the specified type. This option has -// a global effect. -// -// For example, suppose you have a custom field `x-birthday`, which -// you want to represent as a string formatted in RFC3339 in JSON, -// but want it back as `time.Time`. -// -// In that case you would register a custom field as follows -// -// jwe.RegisterCustomField(`x-birthday`, timeT) -// -// Then `hdr.Get("x-birthday")` will still return an `interface{}`, -// but you can convert its type to `time.Time` -// -// bdayif, _ := hdr.Get(`x-birthday`) -// bday := bdayif.(time.Time) -func RegisterCustomField(name string, object interface{}) { - registry.Register(name, object) -} diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/message.go b/vendor/github.com/lestrrat-go/jwx/jwe/message.go deleted file mode 100644 index 6609a69..0000000 --- a/vendor/github.com/lestrrat-go/jwx/jwe/message.go +++ /dev/null @@ -1,648 +0,0 @@ -package jwe - -import ( - "context" - "crypto/ecdsa" - "fmt" - - "github.com/lestrrat-go/jwx/internal/json" - "github.com/lestrrat-go/jwx/internal/pool" - "github.com/lestrrat-go/jwx/jwk" - - "github.com/lestrrat-go/jwx/internal/base64" - "github.com/lestrrat-go/jwx/jwa" - "github.com/pkg/errors" -) - -// NewRecipient creates a Recipient object -func NewRecipient() Recipient { - return &stdRecipient{ - headers: NewHeaders(), - } -} - -func (r *stdRecipient) SetHeaders(h Headers) error { - r.headers = h - return nil -} - -func (r *stdRecipient) SetEncryptedKey(v []byte) error { - r.encryptedKey = v - return nil -} - -func (r *stdRecipient) Headers() Headers { - return r.headers -} - -func (r *stdRecipient) EncryptedKey() []byte { - return r.encryptedKey -} - -type recipientMarshalProxy struct { - Headers Headers `json:"header"` - EncryptedKey string `json:"encrypted_key"` -} - -func (r *stdRecipient) UnmarshalJSON(buf []byte) error { - var proxy recipientMarshalProxy - proxy.Headers = NewHeaders() - if err := json.Unmarshal(buf, &proxy); err != nil { - return errors.Wrap(err, `failed to unmarshal json into recipient`) - } - - r.headers = proxy.Headers - decoded, err := base64.DecodeString(proxy.EncryptedKey) - if err != nil { - return errors.Wrap(err, `failed to decode "encrypted_key"`) - } - r.encryptedKey = decoded - return nil -} - -func (r *stdRecipient) MarshalJSON() ([]byte, error) { - buf := pool.GetBytesBuffer() - defer pool.ReleaseBytesBuffer(buf) - - buf.WriteString(`{"header":`) - hdrbuf, err := r.headers.MarshalJSON() - if err != nil { - return nil, errors.Wrap(err, `failed to marshal recipient header`) - } - buf.Write(hdrbuf) - buf.WriteString(`,"encrypted_key":"`) - buf.WriteString(base64.EncodeToString(r.encryptedKey)) - buf.WriteString(`"}`) - - ret := make([]byte, buf.Len()) - copy(ret, buf.Bytes()) - return ret, nil -} - -// NewMessage creates a new message -func NewMessage() *Message { - return &Message{} -} - -func (m *Message) AuthenticatedData() []byte { - return m.authenticatedData -} - -func (m *Message) CipherText() []byte { - return m.cipherText -} - -func (m *Message) InitializationVector() []byte { - return m.initializationVector -} - -func (m *Message) Tag() []byte { - return m.tag -} - -func (m *Message) ProtectedHeaders() Headers { - return m.protectedHeaders -} - -func (m *Message) Recipients() []Recipient { - return m.recipients -} - -func (m *Message) UnprotectedHeaders() Headers { - return m.unprotectedHeaders -} - -const ( - AuthenticatedDataKey = "aad" - CipherTextKey = "ciphertext" - CountKey = "p2c" - InitializationVectorKey = "iv" - ProtectedHeadersKey = "protected" - RecipientsKey = "recipients" - SaltKey = "p2s" - TagKey = "tag" - UnprotectedHeadersKey = "unprotected" - HeadersKey = "header" - EncryptedKeyKey = "encrypted_key" -) - -func (m *Message) Set(k string, v interface{}) error { - switch k { - case AuthenticatedDataKey: - buf, ok := v.([]byte) - if !ok { - return errors.Errorf(`invalid value %T for %s key`, v, AuthenticatedDataKey) - } - m.authenticatedData = buf - case CipherTextKey: - buf, ok := v.([]byte) - if !ok { - return errors.Errorf(`invalid value %T for %s key`, v, CipherTextKey) - } - m.cipherText = buf - case InitializationVectorKey: - buf, ok := v.([]byte) - if !ok { - return errors.Errorf(`invalid value %T for %s key`, v, InitializationVectorKey) - } - m.initializationVector = buf - case ProtectedHeadersKey: - cv, ok := v.(Headers) - if !ok { - return errors.Errorf(`invalid value %T for %s key`, v, ProtectedHeadersKey) - } - m.protectedHeaders = cv - case RecipientsKey: - cv, ok := v.([]Recipient) - if !ok { - return errors.Errorf(`invalid value %T for %s key`, v, RecipientsKey) - } - m.recipients = cv - case TagKey: - buf, ok := v.([]byte) - if !ok { - return errors.Errorf(`invalid value %T for %s key`, v, TagKey) - } - m.tag = buf - case UnprotectedHeadersKey: - cv, ok := v.(Headers) - if !ok { - return errors.Errorf(`invalid value %T for %s key`, v, UnprotectedHeadersKey) - } - m.unprotectedHeaders = cv - default: - if m.unprotectedHeaders == nil { - m.unprotectedHeaders = NewHeaders() - } - return m.unprotectedHeaders.Set(k, v) - } - return nil -} - -type messageMarshalProxy struct { - AuthenticatedData string `json:"aad,omitempty"` - CipherText string `json:"ciphertext"` - InitializationVector string `json:"iv,omitempty"` - ProtectedHeaders json.RawMessage `json:"protected"` - Recipients []json.RawMessage `json:"recipients,omitempty"` - Tag string `json:"tag,omitempty"` - UnprotectedHeaders Headers `json:"unprotected,omitempty"` - - // For flattened structure. Headers is NOT a Headers type, - // so that we can detect its presence by checking proxy.Headers != nil - Headers json.RawMessage `json:"header,omitempty"` - EncryptedKey string `json:"encrypted_key,omitempty"` -} - -func (m *Message) MarshalJSON() ([]byte, error) { - // This is slightly convoluted, but we need to encode the - // protected headers, so we do it by hand - buf := pool.GetBytesBuffer() - defer pool.ReleaseBytesBuffer(buf) - enc := json.NewEncoder(buf) - fmt.Fprintf(buf, `{`) - - var wrote bool - if aad := m.AuthenticatedData(); len(aad) > 0 { - wrote = true - fmt.Fprintf(buf, `%#v:`, AuthenticatedDataKey) - if err := enc.Encode(base64.EncodeToString(aad)); err != nil { - return nil, errors.Wrapf(err, `failed to encode %s field`, AuthenticatedDataKey) - } - } - if cipherText := m.CipherText(); len(cipherText) > 0 { - if wrote { - fmt.Fprintf(buf, `,`) - } - wrote = true - fmt.Fprintf(buf, `%#v:`, CipherTextKey) - if err := enc.Encode(base64.EncodeToString(cipherText)); err != nil { - return nil, errors.Wrapf(err, `failed to encode %s field`, CipherTextKey) - } - } - - if iv := m.InitializationVector(); len(iv) > 0 { - if wrote { - fmt.Fprintf(buf, `,`) - } - wrote = true - fmt.Fprintf(buf, `%#v:`, InitializationVectorKey) - if err := enc.Encode(base64.EncodeToString(iv)); err != nil { - return nil, errors.Wrapf(err, `failed to encode %s field`, InitializationVectorKey) - } - } - - if h := m.ProtectedHeaders(); h != nil { - encodedHeaders, err := h.Encode() - if err != nil { - return nil, errors.Wrap(err, `failed to encode protected headers`) - } - - if len(encodedHeaders) > 2 { - if wrote { - fmt.Fprintf(buf, `,`) - } - wrote = true - fmt.Fprintf(buf, `%#v:%#v`, ProtectedHeadersKey, string(encodedHeaders)) - } - } - - if recipients := m.Recipients(); len(recipients) > 0 { - if wrote { - fmt.Fprintf(buf, `,`) - } - if len(recipients) == 1 { // Use flattened format - fmt.Fprintf(buf, `%#v:`, HeadersKey) - if err := enc.Encode(recipients[0].Headers()); err != nil { - return nil, errors.Wrapf(err, `failed to encode %s field`, HeadersKey) - } - if ek := recipients[0].EncryptedKey(); len(ek) > 0 { - fmt.Fprintf(buf, `,%#v:`, EncryptedKeyKey) - if err := enc.Encode(base64.EncodeToString(ek)); err != nil { - return nil, errors.Wrapf(err, `failed to encode %s field`, EncryptedKeyKey) - } - } - } else { - fmt.Fprintf(buf, `%#v:`, RecipientsKey) - if err := enc.Encode(recipients); err != nil { - return nil, errors.Wrapf(err, `failed to encode %s field`, RecipientsKey) - } - } - } - - if tag := m.Tag(); len(tag) > 0 { - if wrote { - fmt.Fprintf(buf, `,`) - } - fmt.Fprintf(buf, `%#v:`, TagKey) - if err := enc.Encode(base64.EncodeToString(tag)); err != nil { - return nil, errors.Wrapf(err, `failed to encode %s field`, TagKey) - } - } - - if h := m.UnprotectedHeaders(); h != nil { - unprotected, err := json.Marshal(h) - if err != nil { - return nil, errors.Wrap(err, `failed to encode unprotected headers`) - } - - if len(unprotected) > 2 { - fmt.Fprintf(buf, `,%#v:%#v`, UnprotectedHeadersKey, string(unprotected)) - } - } - fmt.Fprintf(buf, `}`) - - ret := make([]byte, buf.Len()) - copy(ret, buf.Bytes()) - return ret, nil -} - -func (m *Message) UnmarshalJSON(buf []byte) error { - var proxy messageMarshalProxy - proxy.UnprotectedHeaders = NewHeaders() - - if err := json.Unmarshal(buf, &proxy); err != nil { - return errors.Wrap(err, `failed to unmashal JSON into message`) - } - - // Get the string value - var protectedHeadersStr string - if err := json.Unmarshal(proxy.ProtectedHeaders, &protectedHeadersStr); err != nil { - return errors.Wrap(err, `failed to decode protected headers (1)`) - } - - // It's now in _quoted_ base64 string. Decode it - protectedHeadersRaw, err := base64.DecodeString(protectedHeadersStr) - if err != nil { - return errors.Wrap(err, "failed to base64 decoded protected headers buffer") - } - - h := NewHeaders() - if err := json.Unmarshal(protectedHeadersRaw, h); err != nil { - return errors.Wrap(err, `failed to decode protected headers (2)`) - } - - // if this were a flattened message, we would see a "header" and "ciphertext" - // field. TODO: do both of these conditions need to meet, or just one? - if proxy.Headers != nil || len(proxy.EncryptedKey) > 0 { - recipient := NewRecipient() - hdrs := NewHeaders() - if err := json.Unmarshal(proxy.Headers, hdrs); err != nil { - return errors.Wrap(err, `failed to decode headers field`) - } - - if err := recipient.SetHeaders(hdrs); err != nil { - return errors.Wrap(err, `failed to set new headers`) - } - - if v := proxy.EncryptedKey; len(v) > 0 { - buf, err := base64.DecodeString(v) - if err != nil { - return errors.Wrap(err, `failed to decode encrypted key`) - } - if err := recipient.SetEncryptedKey(buf); err != nil { - return errors.Wrap(err, `failed to set encrypted key`) - } - } - - m.recipients = append(m.recipients, recipient) - } else { - for i, recipientbuf := range proxy.Recipients { - recipient := NewRecipient() - if err := json.Unmarshal(recipientbuf, recipient); err != nil { - return errors.Wrapf(err, `failed to decode recipient at index %d`, i) - } - - m.recipients = append(m.recipients, recipient) - } - } - - if src := proxy.AuthenticatedData; len(src) > 0 { - v, err := base64.DecodeString(src) - if err != nil { - return errors.Wrap(err, `failed to decode "aad"`) - } - m.authenticatedData = v - } - - if src := proxy.CipherText; len(src) > 0 { - v, err := base64.DecodeString(src) - if err != nil { - return errors.Wrap(err, `failed to decode "ciphertext"`) - } - m.cipherText = v - } - - if src := proxy.InitializationVector; len(src) > 0 { - v, err := base64.DecodeString(src) - if err != nil { - return errors.Wrap(err, `failed to decode "iv"`) - } - m.initializationVector = v - } - - if src := proxy.Tag; len(src) > 0 { - v, err := base64.DecodeString(src) - if err != nil { - return errors.Wrap(err, `failed to decode "tag"`) - } - m.tag = v - } - - m.protectedHeaders = h - if m.storeProtectedHeaders { - // this is later used for decryption - m.rawProtectedHeaders = base64.Encode(protectedHeadersRaw) - } - - if iz, ok := proxy.UnprotectedHeaders.(isZeroer); ok { - if !iz.isZero() { - m.unprotectedHeaders = proxy.UnprotectedHeaders - } - } - - if len(m.recipients) == 0 { - if err := m.makeDummyRecipient(proxy.EncryptedKey, m.protectedHeaders); err != nil { - return errors.Wrap(err, `failed to setup recipient`) - } - } - - return nil -} - -func (m *Message) makeDummyRecipient(enckeybuf string, protected Headers) error { - // Recipients in this case should not contain the content encryption key, - // so move that out - hdrs, err := protected.Clone(context.TODO()) - if err != nil { - return errors.Wrap(err, `failed to clone headers`) - } - - if err := hdrs.Remove(ContentEncryptionKey); err != nil { - return errors.Wrapf(err, "failed to remove %#v from public header", ContentEncryptionKey) - } - - enckey, err := base64.DecodeString(enckeybuf) - if err != nil { - return errors.Wrap(err, `failed to decode encrypted key`) - } - - if err := m.Set(RecipientsKey, []Recipient{ - &stdRecipient{ - headers: hdrs, - encryptedKey: enckey, - }, - }); err != nil { - return errors.Wrapf(err, `failed to set %s`, RecipientsKey) - } - return nil -} - -// Decrypt decrypts the message using the specified algorithm and key. -// -// `key` must be a private key in its "raw" format (i.e. something like -// *rsa.PrivateKey, instead of jwk.Key) -// -// This method is marked for deprecation. It will be removed from the API -// in the next major release. You should not rely on this method -// to work 100% of the time, especially when it was obtained via jwe.Parse -// instead of being constructed from scratch by this library. -func (m *Message) Decrypt(alg jwa.KeyEncryptionAlgorithm, key interface{}) ([]byte, error) { - var ctx decryptCtx - ctx.alg = alg - ctx.key = key - ctx.msg = m - - return doDecryptCtx(&ctx) -} - -func doDecryptCtx(dctx *decryptCtx) ([]byte, error) { - m := dctx.msg - alg := dctx.alg - key := dctx.key - - if jwkKey, ok := key.(jwk.Key); ok { - var raw interface{} - if err := jwkKey.Raw(&raw); err != nil { - return nil, errors.Wrapf(err, `failed to retrieve raw key from %T`, key) - } - key = raw - } - - var err error - ctx := context.TODO() - h, err := m.protectedHeaders.Clone(ctx) - if err != nil { - return nil, errors.Wrap(err, `failed to copy protected headers`) - } - h, err = h.Merge(ctx, m.unprotectedHeaders) - if err != nil { - return nil, errors.Wrap(err, "failed to merge headers for message decryption") - } - - enc := m.protectedHeaders.ContentEncryption() - var aad []byte - if aadContainer := m.authenticatedData; aadContainer != nil { - aad = base64.Encode(aadContainer) - } - - var computedAad []byte - if len(m.rawProtectedHeaders) > 0 { - computedAad = m.rawProtectedHeaders - } else { - // this is probably not required once msg.Decrypt is deprecated - var err error - computedAad, err = m.protectedHeaders.Encode() - if err != nil { - return nil, errors.Wrap(err, "failed to encode protected headers") - } - } - - dec := NewDecrypter(alg, enc, key). - AuthenticatedData(aad). - ComputedAuthenticatedData(computedAad). - InitializationVector(m.initializationVector). - Tag(m.tag) - - var plaintext []byte - var lastError error - - // if we have no recipients, pretend like we only have one - recipients := m.recipients - if len(recipients) == 0 { - r := NewRecipient() - if err := r.SetHeaders(m.protectedHeaders); err != nil { - return nil, errors.Wrap(err, `failed to set headers to recipient`) - } - recipients = append(recipients, r) - } - - for _, recipient := range recipients { - // strategy: try each recipient. If we fail in one of the steps, - // keep looping because there might be another key with the same algo - if recipient.Headers().Algorithm() != alg { - // algorithms don't match - continue - } - - h2, err := h.Clone(ctx) - if err != nil { - lastError = errors.Wrap(err, `failed to copy headers (1)`) - continue - } - - h2, err = h2.Merge(ctx, recipient.Headers()) - if err != nil { - lastError = errors.Wrap(err, `failed to copy headers (2)`) - continue - } - - switch alg { - case jwa.ECDH_ES, jwa.ECDH_ES_A128KW, jwa.ECDH_ES_A192KW, jwa.ECDH_ES_A256KW: - epkif, ok := h2.Get(EphemeralPublicKeyKey) - if !ok { - return nil, errors.New("failed to get 'epk' field") - } - switch epk := epkif.(type) { - case jwk.ECDSAPublicKey: - var pubkey ecdsa.PublicKey - if err := epk.Raw(&pubkey); err != nil { - return nil, errors.Wrap(err, "failed to get public key") - } - dec.PublicKey(&pubkey) - case jwk.OKPPublicKey: - var pubkey interface{} - if err := epk.Raw(&pubkey); err != nil { - return nil, errors.Wrap(err, "failed to get public key") - } - dec.PublicKey(pubkey) - default: - return nil, errors.Errorf("unexpected 'epk' type %T for alg %s", epkif, alg) - } - - if apu := h2.AgreementPartyUInfo(); len(apu) > 0 { - dec.AgreementPartyUInfo(apu) - } - - if apv := h2.AgreementPartyVInfo(); len(apv) > 0 { - dec.AgreementPartyVInfo(apv) - } - case jwa.A128GCMKW, jwa.A192GCMKW, jwa.A256GCMKW: - ivB64, ok := h2.Get(InitializationVectorKey) - if !ok { - return nil, errors.New("failed to get 'iv' field") - } - ivB64Str, ok := ivB64.(string) - if !ok { - return nil, errors.Errorf("unexpected type for 'iv': %T", ivB64) - } - tagB64, ok := h2.Get(TagKey) - if !ok { - return nil, errors.New("failed to get 'tag' field") - } - tagB64Str, ok := tagB64.(string) - if !ok { - return nil, errors.Errorf("unexpected type for 'tag': %T", tagB64) - } - iv, err := base64.DecodeString(ivB64Str) - if err != nil { - return nil, errors.Wrap(err, "failed to b64-decode 'iv'") - } - tag, err := base64.DecodeString(tagB64Str) - if err != nil { - return nil, errors.Wrap(err, "failed to b64-decode 'tag'") - } - dec.KeyInitializationVector(iv) - dec.KeyTag(tag) - case jwa.PBES2_HS256_A128KW, jwa.PBES2_HS384_A192KW, jwa.PBES2_HS512_A256KW: - saltB64, ok := h2.Get(SaltKey) - if !ok { - return nil, errors.New("failed to get 'p2s' field") - } - saltB64Str, ok := saltB64.(string) - if !ok { - return nil, errors.Errorf("unexpected type for 'p2s': %T", saltB64) - } - - count, ok := h2.Get(CountKey) - if !ok { - return nil, errors.New("failed to get 'p2c' field") - } - countFlt, ok := count.(float64) - if !ok { - return nil, errors.Errorf("unexpected type for 'p2c': %T", count) - } - salt, err := base64.DecodeString(saltB64Str) - if err != nil { - return nil, errors.Wrap(err, "failed to b64-decode 'salt'") - } - dec.KeySalt(salt) - dec.KeyCount(int(countFlt)) - } - - plaintext, err = dec.Decrypt(recipient.EncryptedKey(), m.cipherText) - if err != nil { - lastError = errors.Wrap(err, `failed to decrypt`) - continue - } - - if h2.Compression() == jwa.Deflate { - buf, err := uncompress(plaintext) - if err != nil { - lastError = errors.Wrap(err, `failed to uncompress payload`) - continue - } - plaintext = buf - } - break - } - - if plaintext == nil { - if lastError != nil { - return nil, errors.Errorf(`failed to find matching recipient to decrypt key (last error = %s)`, lastError) - } - return nil, errors.New("failed to find matching recipient") - } - - return plaintext, nil -} diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/options.go b/vendor/github.com/lestrrat-go/jwx/jwe/options.go deleted file mode 100644 index 617e0e4..0000000 --- a/vendor/github.com/lestrrat-go/jwx/jwe/options.go +++ /dev/null @@ -1,87 +0,0 @@ -package jwe - -import ( - "context" - - "github.com/lestrrat-go/option" -) - -type Option = option.Interface -type identMessage struct{} -type identPostParser struct{} -type identPrettyFormat struct{} -type identProtectedHeader struct{} - -type DecryptOption interface { - Option - decryptOption() -} - -type decryptOption struct { - Option -} - -func (*decryptOption) decryptOption() {} - -type SerializerOption interface { - Option - serializerOption() -} - -type serializerOption struct { - Option -} - -func (*serializerOption) serializerOption() {} - -type EncryptOption interface { - Option - encryptOption() -} - -type encryptOption struct { - Option -} - -func (*encryptOption) encryptOption() {} - -// WithPrettyFormat specifies if the `jwe.JSON` serialization tool -// should generate pretty-formatted output -func WithPrettyFormat(b bool) SerializerOption { - return &serializerOption{option.New(identPrettyFormat{}, b)} -} - -// Specify contents of the protected header. Some fields such as -// "enc" and "zip" will be overwritten when encryption is performed. -func WithProtectedHeaders(h Headers) EncryptOption { - cloned, _ := h.Clone(context.Background()) - return &encryptOption{option.New(identProtectedHeader{}, cloned)} -} - -// WithMessage provides a message object to be populated by `jwe.Decrpt` -// Using this option allows you to decrypt AND obtain the `jwe.Message` -// in one go. -// -// Note that you should NOT be using the message object for anything other -// than inspecting its contents. Particularly, do not expect the message -// reliable when you call `Decrypt` on it. `(jwe.Message).Decrypt` is -// slated to be deprecated in the next major version. -func WithMessage(m *Message) DecryptOption { - return &decryptOption{option.New(identMessage{}, m)} -} - -// WithPostParser specifies the handler to be called immediately -// after the JWE message has been parsed, but before decryption -// takes place during `jwe.Decrypt`. -// -// This option exists to allow advanced users that require the use -// of information stored in the JWE message to determine how the -// decryption should be handled. -// -// For security reasons it is highly recommended that you thoroughly -// study how the process works before using this option. This is especially -// true if you are trying to infer key algorithms and keys to use to -// decrypt a message using non-standard hints. -func WithPostParser(p PostParser) DecryptOption { - return &decryptOption{option.New(identPostParser{}, p)} -} diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/serializer.go b/vendor/github.com/lestrrat-go/jwx/jwe/serializer.go deleted file mode 100644 index 27ca9b8..0000000 --- a/vendor/github.com/lestrrat-go/jwx/jwe/serializer.go +++ /dev/null @@ -1,94 +0,0 @@ -package jwe - -import ( - "context" - - "github.com/lestrrat-go/jwx/internal/base64" - "github.com/lestrrat-go/jwx/internal/json" - - "github.com/lestrrat-go/jwx/internal/pool" - "github.com/pkg/errors" -) - -// Compact encodes the given message into a JWE compact serialization format. -// -// Currently `Compact()` does not take any options, but the API is -// set up as such to allow future expansions -func Compact(m *Message, _ ...SerializerOption) ([]byte, error) { - if len(m.recipients) != 1 { - return nil, errors.New("wrong number of recipients for compact serialization") - } - - recipient := m.recipients[0] - - // The protected header must be a merge between the message-wide - // protected header AND the recipient header - - // There's something wrong if m.protectedHeaders is nil, but - // it could happen - if m.protectedHeaders == nil { - return nil, errors.New("invalid protected header") - } - - ctx := context.TODO() - hcopy, err := m.protectedHeaders.Clone(ctx) - if err != nil { - return nil, errors.Wrap(err, "failed to copy protected header") - } - hcopy, err = hcopy.Merge(ctx, m.unprotectedHeaders) - if err != nil { - return nil, errors.Wrap(err, "failed to merge unprotected header") - } - hcopy, err = hcopy.Merge(ctx, recipient.Headers()) - if err != nil { - return nil, errors.Wrap(err, "failed to merge recipient header") - } - - protected, err := hcopy.Encode() - if err != nil { - return nil, errors.Wrap(err, "failed to encode header") - } - - encryptedKey := base64.Encode(recipient.EncryptedKey()) - iv := base64.Encode(m.initializationVector) - cipher := base64.Encode(m.cipherText) - tag := base64.Encode(m.tag) - - buf := pool.GetBytesBuffer() - defer pool.ReleaseBytesBuffer(buf) - - buf.Grow(len(protected) + len(encryptedKey) + len(iv) + len(cipher) + len(tag) + 4) - buf.Write(protected) - buf.WriteByte('.') - buf.Write(encryptedKey) - buf.WriteByte('.') - buf.Write(iv) - buf.WriteByte('.') - buf.Write(cipher) - buf.WriteByte('.') - buf.Write(tag) - - result := make([]byte, buf.Len()) - copy(result, buf.Bytes()) - return result, nil -} - -// JSON encodes the message into a JWE JSON serialization format. -// -// If `WithPrettyFormat(true)` is passed as an option, the returned -// value will be formatted using `json.MarshalIndent()` -func JSON(m *Message, options ...SerializerOption) ([]byte, error) { - var pretty bool - for _, option := range options { - //nolint:forcetypeassert - switch option.Ident() { - case identPrettyFormat{}: - pretty = option.Value().(bool) - } - } - - if pretty { - return json.MarshalIndent(m, "", " ") - } - return json.Marshal(m) -} diff --git a/vendor/github.com/lestrrat-go/jwx/jwk/README.md b/vendor/github.com/lestrrat-go/jwx/jwk/README.md deleted file mode 100644 index cc38e75..0000000 --- a/vendor/github.com/lestrrat-go/jwx/jwk/README.md +++ /dev/null @@ -1,274 +0,0 @@ -# JWK [![Go Reference](https://pkg.go.dev/badge/github.com/lestrrat-go/jwx/jwk.svg)](https://pkg.go.dev/github.com/lestrrat-go/jwx/jwk) - -Package jwk implements JWK as described in [RFC7517](https://tools.ietf.org/html/rfc7517) - -* Parse and work with RSA/EC/Symmetric/OKP JWK types - * Convert to and from JSON - * Convert to and from raw key types (e.g. *rsa.PrivateKey) -* Ability to keep a JWKS fresh. - -How-to style documentation can be found in the [docs directory](../docs). - -Examples are located in the examples directory ([jwk_example_test.go](../examples/jwk_example_test.go)) - -Supported key types: - -| kty | Curve | Go Key Type | -|:----|:------------------------|:----------------------------------------------| -| RSA | N/A | rsa.PrivateKey / rsa.PublicKey (2) | -| EC | P-256
P-384
P-521
secp256k1 (1) | ecdsa.PrivateKey / ecdsa.PublicKey (2) | -| oct | N/A | []byte | -| OKP | Ed25519 (1) | ed25519.PrivateKey / ed25519.PublicKey (2) | -| | X25519 (1) | (jwx/)x25519.PrivateKey / x25519.PublicKey (2)| - -* Note 1: Experimental -* Note 2: Either value or pointers accepted (e.g. rsa.PrivateKey or *rsa.PrivateKey) - -# SYNOPSIS - -## Parse a JWK or a JWK set - -```go - // Parse a single JWK key. - key, err := jwk.ParseKey(...) - - // Parse a JWK set (or a single JWK key) - set, err := jwk.Parse(...) -``` - -## Create JWK keys from raw keys - -```go -func ExampleNew() { - // New returns different underlying types of jwk.Key objects - // depending on the input value. - - // []byte -> jwk.SymmetricKey - { - raw := []byte("Lorem Ipsum") - key, err := jwk.New(raw) - if err != nil { - fmt.Printf("failed to create symmetric key: %s\n", err) - return - } - if _, ok := key.(jwk.SymmetricKey); !ok { - fmt.Printf("expected jwk.SymmetricKey, got %T\n", key) - return - } - } - - // *rsa.PrivateKey -> jwk.RSAPrivateKey - // *rsa.PublicKey -> jwk.RSAPublicKey - { - raw, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - fmt.Printf("failed to generate new RSA privatre key: %s\n", err) - return - } - - key, err := jwk.New(raw) - if err != nil { - fmt.Printf("failed to create symmetric key: %s\n", err) - return - } - if _, ok := key.(jwk.RSAPrivateKey); !ok { - fmt.Printf("expected jwk.SymmetricKey, got %T\n", key) - return - } - // PublicKey is omitted for brevity - } - - // *ecdsa.PrivateKey -> jwk.ECDSAPrivateKey - // *ecdsa.PublicKey -> jwk.ECDSAPublicKey - { - raw, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) - if err != nil { - fmt.Printf("failed to generate new ECDSA privatre key: %s\n", err) - return - } - - key, err := jwk.New(raw) - if err != nil { - fmt.Printf("failed to create symmetric key: %s\n", err) - return - } - if _, ok := key.(jwk.ECDSAPrivateKey); !ok { - fmt.Printf("expected jwk.SymmetricKey, got %T\n", key) - return - } - // PublicKey is omitted for brevity - } - - // OUTPUT: -} -``` - -# Get the JSON representation of a key - -```go -func ExampleMarshalJSON() { - // to get the same values every time, we need to create a static source - // of "randomness" - rdr := bytes.NewReader([]byte("01234567890123456789012345678901234567890123456789ABCDEF")) - raw, err := ecdsa.GenerateKey(elliptic.P384(), rdr) - if err != nil { - fmt.Printf("failed to generate new ECDSA privatre key: %s\n", err) - return - } - - key, err := jwk.New(raw) - if err != nil { - fmt.Printf("failed to create symmetric key: %s\n", err) - return - } - if _, ok := key.(jwk.ECDSAPrivateKey); !ok { - fmt.Printf("expected jwk.SymmetricKey, got %T\n", key) - return - } - - key.Set(jwk.KeyIDKey, "mykey") - - buf, err := json.MarshalIndent(key, "", " ") - if err != nil { - fmt.Printf("failed to marshal key into JSON: %s\n", err) - return - } - fmt.Printf("%s\n", buf) - - // OUTPUT: - // { - // "kty": "EC", - // "crv": "P-384", - // "d": "ODkwMTIzNDU2Nzg5MDEyMz7deMbyLt8g4cjcxozuIoygLLlAeoQ1AfM9TSvxkFHJ", - // "kid": "mykey", - // "x": "gvvRMqm1w5aHn7sVNA2QUJeOVcedUnmiug6VhU834gzS9k87crVwu9dz7uLOdoQl", - // "y": "7fVF7b6J_6_g6Wu9RuJw8geWxEi5ja9Gp2TSdELm5u2E-M7IF-bsxqcdOj3n1n7N" - // } -} -``` - -# Auto-Refresh a key during a long running process - -```go -func ExampleAutoRefresh() { - ctx, cancel := context.WithCancel(context.Background()) - - const googleCerts = `https://www.googleapis.com/oauth2/v3/certs` - ar := jwk.NewAutoRefresh(ctx) - - // Tell *jwk.AutoRefresh that we only want to refresh this JWKS - // when it needs to (based on Cache-Control or Expires header from - // the HTTP response). If the calculated minimum refresh interval is less - // than 15 minutes, don't go refreshing any earlier than 15 minutes. - ar.Configure(googleCerts, jwk.WithMinRefreshInterval(15*time.Minute)) - - // Refresh the JWKS once before getting into the main loop. - // This allows you to check if the JWKS is available before we start - // a long-running program - _, err := ar.Refresh(ctx, googleCerts) - if err != nil { - fmt.Printf("failed to refresh google JWKS: %s\n", err) - return - } - - // Pretend that this is your program's main loop -MAIN: - for { - select { - case <-ctx.Done(): - break MAIN - default: - } - keyset, err := ar.Fetch(ctx, googleCerts) - if err != nil { - fmt.Printf("failed to fetch google JWKS: %s\n", err) - return - } - _ = keyset - - // Do interesting stuff with the keyset... but here, we just - // sleep for a bit - time.Sleep(time.Second) - - // Because we're a dummy program, we just cancel the loop now. - // If this were a real program, you prosumably loop forever - cancel() - } - // OUTPUT: -} -``` - -Parse and use a JWK key: - -```go - -import ( - "encoding/json" - "log" - - "github.com/lestrrat-go/jwx/jwk" -) - -func main() { - set, err := jwk.Fetch(context.Background(), "https://www.googleapis.com/oauth2/v3/certs") - if err != nil { - log.Printf("failed to parse JWK: %s", err) - return - } - - // Key sets can be serialized back to JSON - { - jsonbuf, err := json.Marshal(set) - if err != nil { - log.Printf("failed to marshal key set into JSON: %s", err) - return - } - log.Printf("%s", jsonbuf) - } - - for it := set.Iterate(context.Background()); it.Next(context.Background()); { - pair := it.Pair() - key := pair.Value.(jwk.Key) - - var rawkey interface{} // This is the raw key, like *rsa.PrivateKey or *ecdsa.PrivateKey - if err := key.Raw(&rawkey); err != nil { - log.Printf("failed to create public key: %s", err) - return - } - // Use rawkey for jws.Verify() or whatever. - _ = rawkey - - // You can create jwk.Key from a raw key, too - fromRawKey, err := jwk.New(rawkey) - - - // Keys can be serialized back to JSON - jsonbuf, err := json.Marshal(key) - if err != nil { - log.Printf("failed to marshal key into JSON: %s", err) - return - } - log.Printf("%s", jsonbuf) - - // If you know the underlying Key type (RSA, EC, Symmetric), you can - // create an empy instance first - // key := jwk.NewRSAPrivateKey() - // ..and then use json.Unmarshal - // json.Unmarshal(key, jsonbuf) - // - // but if you don't know the type first, you have an abstract type - // jwk.Key, which can't be used as the first argument to json.Unmarshal - // - // In this case, use jwk.Parse() - fromJsonKey, err := jwk.Parse(jsonbuf) - if err != nil { - log.Printf("failed to parse json: %s", err) - return - } - _ = fromJsonKey - _ = fromRawKey - } -} -``` - - diff --git a/vendor/github.com/lestrrat-go/jwx/jwk/certchain.go b/vendor/github.com/lestrrat-go/jwx/jwk/certchain.go deleted file mode 100644 index 9ab7e46..0000000 --- a/vendor/github.com/lestrrat-go/jwx/jwk/certchain.go +++ /dev/null @@ -1,85 +0,0 @@ -package jwk - -import ( - "crypto/x509" - - "github.com/lestrrat-go/jwx/internal/json" - - "github.com/lestrrat-go/jwx/internal/base64" - "github.com/pkg/errors" -) - -func (c CertificateChain) MarshalJSON() ([]byte, error) { - certs := c.Get() - encoded := make([]string, len(certs)) - for i := 0; i < len(certs); i++ { - encoded[i] = base64.EncodeToStringStd(certs[i].Raw) - } - return json.Marshal(encoded) -} - -func (c *CertificateChain) UnmarshalJSON(buf []byte) error { - var list []string - if err := json.Unmarshal(buf, &list); err != nil { - return errors.Wrap(err, `failed to unmarshal JSON into []string`) - } - - var tmp CertificateChain - if err := tmp.Accept(list); err != nil { - return err - } - - *c = tmp - return nil -} - -func (c CertificateChain) Get() []*x509.Certificate { - return c.certs -} - -func (c *CertificateChain) Accept(v interface{}) error { - var list []string - - switch x := v.(type) { - case string: - list = []string{x} - case []interface{}: - list = make([]string, len(x)) - for i, e := range x { - if es, ok := e.(string); ok { - list[i] = es - continue - } - return errors.Errorf(`invalid list element type: expected string, got %T at element %d`, e, i) - } - case []string: - list = x - case CertificateChain: - certs := make([]*x509.Certificate, len(x.certs)) - copy(certs, x.certs) - *c = CertificateChain{ - certs: certs, - } - return nil - default: - return errors.Errorf(`invalid type for CertificateChain: %T`, v) - } - - certs := make([]*x509.Certificate, len(list)) - for i, e := range list { - buf, err := base64.DecodeString(e) - if err != nil { - return errors.Wrap(err, `failed to base64 decode list element`) - } - cert, err := x509.ParseCertificate(buf) - if err != nil { - return errors.Wrap(err, `failed to parse certificate`) - } - certs[i] = cert - } - - *c = CertificateChain{ - certs: certs, - } - return nil -} diff --git a/vendor/github.com/lestrrat-go/jwx/jwk/gen.sh b/vendor/github.com/lestrrat-go/jwx/jwk/gen.sh deleted file mode 100644 index dde877e..0000000 --- a/vendor/github.com/lestrrat-go/jwx/jwk/gen.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -# Script to perform code generation. This exists to overcome -# the fact that go:generate doesn't really allow you to change directories - -set -e - -pushd internal/cmd/genheader -go build -o genheader main.go -popd - -./internal/cmd/genheader/genheader -objects=internal/cmd/genheader/objects.yml - -rm internal/cmd/genheader/genheader diff --git a/vendor/github.com/lestrrat-go/jwx/jwk/io.go b/vendor/github.com/lestrrat-go/jwx/jwk/io.go deleted file mode 100644 index 6f01e25..0000000 --- a/vendor/github.com/lestrrat-go/jwx/jwk/io.go +++ /dev/null @@ -1,29 +0,0 @@ -// Automatically generated by internal/cmd/genreadfile/main.go. DO NOT EDIT - -package jwk - -import "os" - -// ReadFileOption describes options that can be passed to ReadFile. -type ReadFileOption interface { - Option - readFileOption() -} - -func ReadFile(path string, options ...ReadFileOption) (Set, error) { - var parseOptions []ParseOption - for _, option := range options { - switch option := option.(type) { - case ParseOption: - parseOptions = append(parseOptions, option) - } - } - - f, err := os.Open(path) - if err != nil { - return nil, err - } - - defer f.Close() - return ParseReader(f, parseOptions...) -} diff --git a/vendor/github.com/lestrrat-go/jwx/jwk/option.go b/vendor/github.com/lestrrat-go/jwx/jwk/option.go deleted file mode 100644 index 47bf493..0000000 --- a/vendor/github.com/lestrrat-go/jwx/jwk/option.go +++ /dev/null @@ -1,197 +0,0 @@ -package jwk - -import ( - "crypto" - "time" - - "github.com/lestrrat-go/backoff/v2" - "github.com/lestrrat-go/jwx/internal/json" - "github.com/lestrrat-go/option" -) - -type Option = option.Interface - -type identHTTPClient struct{} -type identThumbprintHash struct{} -type identRefreshInterval struct{} -type identMinRefreshInterval struct{} -type identFetchBackoff struct{} -type identPEM struct{} -type identTypedField struct{} -type identLocalRegistry struct{} -type identFetchWhitelist struct{} -type identIgnoreParseError struct{} - -// AutoRefreshOption is a type of Option that can be passed to the -// AutoRefresh object. -type AutoRefreshOption interface { - Option - autoRefreshOption() -} - -type autoRefreshOption struct { - Option -} - -func (*autoRefreshOption) autoRefreshOption() {} - -// FetchOption is a type of Option that can be passed to `jwk.Fetch()` -// FetchOption also implements the `AutoRefreshOption`, and thus can -// safely be passed to `(*jwk.AutoRefresh).Configure()` -type FetchOption interface { - AutoRefreshOption - fetchOption() -} - -type fetchOption struct { - Option -} - -func (*fetchOption) autoRefreshOption() {} -func (*fetchOption) fetchOption() {} - -// ParseOption is a type of Option that can be passed to `jwk.Parse()` -// ParseOption also implmentsthe `ReadFileOPtion` and `AutoRefreshOption`, -// and thus safely be passed to `jwk.ReadFile` and `(*jwk.AutoRefresh).Configure()` -type ParseOption interface { - ReadFileOption - AutoRefreshOption - parseOption() -} - -type parseOption struct { - Option -} - -func (*parseOption) autoRefreshOption() {} -func (*parseOption) parseOption() {} -func (*parseOption) readFileOption() {} - -// WithHTTPClient allows users to specify the "net/http".Client object that -// is used when fetching jwk.Set objects. -func WithHTTPClient(cl HTTPClient) FetchOption { - return &fetchOption{option.New(identHTTPClient{}, cl)} -} - -// WithFetchBackoff specifies the backoff policy to use when -// refreshing a JWKS from a remote server fails. -// -// This does not have any effect on initial `Fetch()`, or any of the `Refresh()` calls -- -// the backoff is applied ONLY on the background refreshing goroutine. -func WithFetchBackoff(v backoff.Policy) FetchOption { - return &fetchOption{option.New(identFetchBackoff{}, v)} -} - -func WithThumbprintHash(h crypto.Hash) Option { - return option.New(identThumbprintHash{}, h) -} - -// WithRefreshInterval specifies the static interval between refreshes -// of jwk.Set objects controlled by jwk.AutoRefresh. -// -// Providing this option overrides the adaptive token refreshing based -// on Cache-Control/Expires header (and jwk.WithMinRefreshInterval), -// and refreshes will *always* happen in this interval. -func WithRefreshInterval(d time.Duration) AutoRefreshOption { - return &autoRefreshOption{ - option.New(identRefreshInterval{}, d), - } -} - -// WithMinRefreshInterval specifies the minimum refresh interval to be used -// when using AutoRefresh. This value is ONLY used if you did not specify -// a user-supplied static refresh interval via `WithRefreshInterval`. -// -// This value is used as a fallback value when tokens are refreshed. -// -// When we fetch the key from a remote URL, we first look at the max-age -// directive from Cache-Control response header. If this value is present, -// we compare the max-age value and the value specified by this option -// and take the larger one. -// -// Next we check for the Expires header, and similarly if the header is -// present, we compare it against the value specified by this option, -// and take the larger one. -// -// Finally, if neither of the above headers are present, we use the -// value specified by this option as the next refresh timing -// -// If unspecified, the minimum refresh interval is 1 hour -func WithMinRefreshInterval(d time.Duration) AutoRefreshOption { - return &autoRefreshOption{ - option.New(identMinRefreshInterval{}, d), - } -} - -// WithPEM specifies that the input to `Parse()` is a PEM encoded key. -func WithPEM(v bool) ParseOption { - return &parseOption{ - option.New(identPEM{}, v), - } -} - -type typedFieldPair struct { - Name string - Value interface{} -} - -// WithTypedField allows a private field to be parsed into the object type of -// your choice. It works much like the RegisterCustomField, but the effect -// is only applicable to the jwt.Parse function call which receives this option. -// -// While this can be extremely useful, this option should be used with caution: -// There are many caveats that your entire team/user-base needs to be aware of, -// and therefore in general its use is discouraged. Only use it when you know -// what you are doing, and you document its use clearly for others. -// -// First and foremost, this is a "per-object" option. Meaning that given the same -// serialized format, it is possible to generate two objects whose internal -// representations may differ. That is, if you parse one _WITH_ the option, -// and the other _WITHOUT_, their internal representation may completely differ. -// This could potentially lead to problems. -// -// Second, specifying this option will slightly slow down the decoding process -// as it needs to consult multiple definitions sources (global and local), so -// be careful if you are decoding a large number of tokens, as the effects will stack up. -func WithTypedField(name string, object interface{}) ParseOption { - return &parseOption{ - option.New(identTypedField{}, - typedFieldPair{Name: name, Value: object}, - ), - } -} - -// This option is only available for internal code. Users don't get to play with it -func withLocalRegistry(r *json.Registry) ParseOption { - return &parseOption{option.New(identLocalRegistry{}, r)} -} - -// WithFetchWhitelist specifies the Whitelist object to use when -// fetching JWKs from a remote source. This option can be passed -// to both `jwk.Fetch()`, `jwk.NewAutoRefresh()`, and `(*jwk.AutoRefresh).Configure()` -func WithFetchWhitelist(w Whitelist) FetchOption { - return &fetchOption{option.New(identFetchWhitelist{}, w)} -} - -// WithIgnoreParseError is only applicable when used with `jwk.Parse()` -// (i.e. to parse JWK sets). If passed to `jwk.ParseKey()`, the function -// will return an error no matter what the input is. -// -// DO NOT USE WITHOUT EXHAUSTING ALL OTHER ROUTES FIRST. -// -// The option specifies that errors found during parsing of individual -// keys are ignored. For example, if you had keys A, B, C where B is -// invalid (e.g. it does not contain the required fields), then the -// resulting JWKS will contain keys A and C only. -// -// This options exists as an escape hatch for those times when a -// key in a JWKS that is irrelevant for your use case is causing -// your JWKS parsing to fail, and you want to get to the rest of the -// keys in the JWKS. -// -// Again, DO NOT USE unless you have exhausted all other routes. -// When you use this option, you will not be able to tell if you are -// using a faulty JWKS, except for when there are JSON syntax errors. -func WithIgnoreParseError(b bool) ParseOption { - return &parseOption{option.New(identIgnoreParseError{}, b)} -} diff --git a/vendor/github.com/lestrrat-go/jwx/jwk/refresh.go b/vendor/github.com/lestrrat-go/jwx/jwk/refresh.go deleted file mode 100644 index 0a8f754..0000000 --- a/vendor/github.com/lestrrat-go/jwx/jwk/refresh.go +++ /dev/null @@ -1,653 +0,0 @@ -package jwk - -import ( - "context" - "net/http" - "reflect" - "sync" - "time" - - "github.com/lestrrat-go/backoff/v2" - "github.com/lestrrat-go/httpcc" - "github.com/pkg/errors" -) - -// AutoRefresh is a container that keeps track of jwk.Set object by their source URLs. -// The jwk.Set objects are refreshed automatically behind the scenes. -// -// Before retrieving the jwk.Set objects, the user must pre-register the -// URLs they intend to use by calling `Configure()` -// -// ar := jwk.NewAutoRefresh(ctx) -// ar.Configure(url, options...) -// -// Once registered, you can call `Fetch()` to retrieve the jwk.Set object. -// -// All JWKS objects that are retrieved via the auto-fetch mechanism should be -// treated read-only, as they are shared among the consumers and this object. -type AutoRefresh struct { - errSink chan AutoRefreshError - cache map[string]Set - configureCh chan struct{} - removeCh chan removeReq - fetching map[string]chan struct{} - muErrSink sync.Mutex - muCache sync.RWMutex - muFetching sync.Mutex - muRegistry sync.RWMutex - registry map[string]*target - resetTimerCh chan *resetTimerReq -} - -type target struct { - // The backoff policy to use when fetching the JWKS fails - backoff backoff.Policy - - // The HTTP client to use. The user may opt to use a client which is - // aware of HTTP caching, or one that goes through a proxy - httpcl HTTPClient - - // Interval between refreshes are calculated two ways. - // 1) You can set an explicit refresh interval by using WithRefreshInterval(). - // In this mode, it doesn't matter what the HTTP response says in its - // Cache-Control or Expires headers - // 2) You can let us calculate the time-to-refresh based on the key's - // Cache-Control or Expires headers. - // First, the user provides us the absolute minimum interval before - // refreshes. We will never check for refreshes before this specified - // amount of time. - // - // Next, max-age directive in the Cache-Control header is consulted. - // If `max-age` is not present, we skip the following section, and - // proceed to the next option. - // If `max-age > user-supplied minimum interval`, then we use the max-age, - // otherwise the user-supplied minimum interval is used. - // - // Next, the value specified in Expires header is consulted. - // If the header is not present, we skip the following seciont and - // proceed to the next option. - // We take the time until expiration `expires - time.Now()`, and - // if `time-until-expiration > user-supplied minimum interval`, then - // we use the expires value, otherwise the user-supplied minimum interval is used. - // - // If all of the above fails, we used the user-supplied minimum interval - refreshInterval *time.Duration - minRefreshInterval time.Duration - - url string - - // The timer for refreshing the keyset. should not be set by anyone - // other than the refreshing goroutine - timer *time.Timer - - // Semaphore to limit the number of concurrent refreshes in the background - sem chan struct{} - - // for debugging, snapshoting - lastRefresh time.Time - nextRefresh time.Time - - wl Whitelist - parseOptions []ParseOption -} - -type resetTimerReq struct { - t *target - d time.Duration -} - -// NewAutoRefresh creates a container that keeps track of JWKS objects which -// are automatically refreshed. -// -// The context object in the argument controls the life-span of the -// auto-refresh worker. If you are using this in a long running process, this -// should mostly be set to a context that ends when the main loop/part of your -// program exits: -// -// func MainLoop() { -// ctx, cancel := context.WithCancel(context.Background()) -// defer cancel() -// ar := jwk.AutoRefresh(ctx) -// for ... { -// ... -// } -// } -func NewAutoRefresh(ctx context.Context) *AutoRefresh { - af := &AutoRefresh{ - cache: make(map[string]Set), - configureCh: make(chan struct{}), - removeCh: make(chan removeReq), - fetching: make(map[string]chan struct{}), - registry: make(map[string]*target), - resetTimerCh: make(chan *resetTimerReq), - } - go af.refreshLoop(ctx) - return af -} - -func (af *AutoRefresh) getCached(url string) (Set, bool) { - af.muCache.RLock() - ks, ok := af.cache[url] - af.muCache.RUnlock() - if ok { - return ks, true - } - return nil, false -} - -type removeReq struct { - replyCh chan error - url string -} - -// Remove removes `url` from the list of urls being watched by jwk.AutoRefresh. -// If the url is not already registered, returns an error. -func (af *AutoRefresh) Remove(url string) error { - ch := make(chan error) - af.removeCh <- removeReq{replyCh: ch, url: url} - return <-ch -} - -// Configure registers the url to be controlled by AutoRefresh, and also -// sets any options associated to it. -// -// Note that options are treated as a whole -- you can't just update -// one value. For example, if you did: -// -// ar.Configure(url, jwk.WithHTTPClient(...)) -// ar.Configure(url, jwk.WithRefreshInterval(...)) -// The the end result is that `url` is ONLY associated with the options -// given in the second call to `Configure()`, i.e. `jwk.WithRefreshInterval`. -// The other unspecified options, including the HTTP client, is set to -// their default values. -// -// Configuration must propagate between goroutines, and therefore are -// not atomic (But changes should be felt "soon enough" for practical -// purposes) -func (af *AutoRefresh) Configure(url string, options ...AutoRefreshOption) { - var httpcl HTTPClient = http.DefaultClient - var hasRefreshInterval bool - var refreshInterval time.Duration - var wl Whitelist - var parseOptions []ParseOption - minRefreshInterval := time.Hour - bo := backoff.Null() - for _, option := range options { - if v, ok := option.(ParseOption); ok { - parseOptions = append(parseOptions, v) - continue - } - - //nolint:forcetypeassert - switch option.Ident() { - case identFetchBackoff{}: - bo = option.Value().(backoff.Policy) - case identRefreshInterval{}: - refreshInterval = option.Value().(time.Duration) - hasRefreshInterval = true - case identMinRefreshInterval{}: - minRefreshInterval = option.Value().(time.Duration) - case identHTTPClient{}: - httpcl = option.Value().(HTTPClient) - case identFetchWhitelist{}: - wl = option.Value().(Whitelist) - } - } - - af.muRegistry.Lock() - t, ok := af.registry[url] - if ok { - if t.httpcl != httpcl { - t.httpcl = httpcl - } - - if t.minRefreshInterval != minRefreshInterval { - t.minRefreshInterval = minRefreshInterval - } - - if t.refreshInterval != nil { - if !hasRefreshInterval { - t.refreshInterval = nil - } else if *t.refreshInterval != refreshInterval { - *t.refreshInterval = refreshInterval - } - } else { - if hasRefreshInterval { - t.refreshInterval = &refreshInterval - } - } - - if t.wl != wl { - t.wl = wl - } - - t.parseOptions = parseOptions - } else { - t = &target{ - backoff: bo, - httpcl: httpcl, - minRefreshInterval: minRefreshInterval, - url: url, - sem: make(chan struct{}, 1), - // This is a placeholder timer so we can call Reset() on it later - // Make it sufficiently in the future so that we don't have bogus - // events firing - timer: time.NewTimer(24 * time.Hour), - wl: wl, - parseOptions: parseOptions, - } - if hasRefreshInterval { - t.refreshInterval = &refreshInterval - } - - // Record this in the registry - af.registry[url] = t - } - af.muRegistry.Unlock() - - // Tell the backend to reconfigure itself - af.configureCh <- struct{}{} -} - -func (af *AutoRefresh) releaseFetching(url string) { - // first delete the entry from the map, then close the channel or - // otherwise we may end up getting multiple groutines doing the fetch - af.muFetching.Lock() - fetchingCh, ok := af.fetching[url] - if !ok { - // Juuuuuuust in case. But shouldn't happen - af.muFetching.Unlock() - return - } - delete(af.fetching, url) - close(fetchingCh) - af.muFetching.Unlock() -} - -// IsRegistered checks if `url` is registered already. -func (af *AutoRefresh) IsRegistered(url string) bool { - _, ok := af.getRegistered(url) - return ok -} - -// Fetch returns a jwk.Set from the given url. -func (af *AutoRefresh) getRegistered(url string) (*target, bool) { - af.muRegistry.RLock() - t, ok := af.registry[url] - af.muRegistry.RUnlock() - return t, ok -} - -// Fetch returns a jwk.Set from the given url. -// -// If it has previously been fetched, then a cached value is returned. -// -// If this the first time `url` was requested, an HTTP request will be -// sent, synchronously. -// -// When accessed via multiple goroutines concurrently, and the cache -// has not been populated yet, only the first goroutine is -// allowed to perform the initialization (HTTP fetch and cache population). -// All other goroutines will be blocked until the operation is completed. -// -// DO NOT modify the jwk.Set object returned by this method, as the -// objects are shared among all consumers and the backend goroutine -func (af *AutoRefresh) Fetch(ctx context.Context, url string) (Set, error) { - if _, ok := af.getRegistered(url); !ok { - return nil, errors.Errorf(`url %s must be configured using "Configure()" first`, url) - } - - ks, found := af.getCached(url) - if found { - return ks, nil - } - - return af.refresh(ctx, url) -} - -// Refresh is the same as Fetch(), except that HTTP fetching is done synchronously. -// -// This is useful when you want to force an HTTP fetch instead of waiting -// for the background goroutine to do it, for example when you want to -// make sure the AutoRefresh cache is warmed up before starting your main loop -func (af *AutoRefresh) Refresh(ctx context.Context, url string) (Set, error) { - if _, ok := af.getRegistered(url); !ok { - return nil, errors.Errorf(`url %s must be configured using "Configure()" first`, url) - } - - return af.refresh(ctx, url) -} - -func (af *AutoRefresh) refresh(ctx context.Context, url string) (Set, error) { - // To avoid a thundering herd, only one goroutine per url may enter into this - // initial fetch phase. - af.muFetching.Lock() - fetchingCh, fetching := af.fetching[url] - // unlock happens in each of the if/else clauses because we need to perform - // the channel initialization when there is no channel present - if fetching { - af.muFetching.Unlock() - select { - case <-ctx.Done(): - return nil, ctx.Err() - case <-fetchingCh: - } - } else { - fetchingCh = make(chan struct{}) - af.fetching[url] = fetchingCh - af.muFetching.Unlock() - - // Register a cleanup handler, to make sure we always - defer af.releaseFetching(url) - - // The first time around, we need to fetch the keyset - if err := af.doRefreshRequest(ctx, url, false); err != nil { - return nil, errors.Wrapf(err, `failed to fetch resource pointed by %s`, url) - } - } - - // the cache should now be populated - ks, ok := af.getCached(url) - if !ok { - return nil, errors.New("cache was not populated after explicit refresh") - } - - return ks, nil -} - -// Keeps looping, while refreshing the KeySet. -func (af *AutoRefresh) refreshLoop(ctx context.Context) { - // reflect.Select() is slow IF we are executing it over and over - // in a very fast iteration, but we assume here that refreshes happen - // seldom enough that being able to call one `select{}` with multiple - // targets / channels outweighs the speed penalty of using reflect. - // - const ( - ctxDoneIdx = iota - configureIdx - resetTimerIdx - removeIdx - baseSelcasesLen - ) - - baseSelcases := make([]reflect.SelectCase, baseSelcasesLen) - baseSelcases[ctxDoneIdx] = reflect.SelectCase{ - Dir: reflect.SelectRecv, - Chan: reflect.ValueOf(ctx.Done()), - } - baseSelcases[configureIdx] = reflect.SelectCase{ - Dir: reflect.SelectRecv, - Chan: reflect.ValueOf(af.configureCh), - } - baseSelcases[resetTimerIdx] = reflect.SelectCase{ - Dir: reflect.SelectRecv, - Chan: reflect.ValueOf(af.resetTimerCh), - } - baseSelcases[removeIdx] = reflect.SelectCase{ - Dir: reflect.SelectRecv, - Chan: reflect.ValueOf(af.removeCh), - } - - var targets []*target - var selcases []reflect.SelectCase - for { - // It seems silly, but it's much easier to keep track of things - // if we re-build the select cases every iteration - - af.muRegistry.RLock() - if cap(targets) < len(af.registry) { - targets = make([]*target, 0, len(af.registry)) - } else { - targets = targets[:0] - } - - if cap(selcases) < len(af.registry) { - selcases = make([]reflect.SelectCase, 0, len(af.registry)+baseSelcasesLen) - } else { - selcases = selcases[:0] - } - selcases = append(selcases, baseSelcases...) - - for _, data := range af.registry { - targets = append(targets, data) - selcases = append(selcases, reflect.SelectCase{ - Dir: reflect.SelectRecv, - Chan: reflect.ValueOf(data.timer.C), - }) - } - af.muRegistry.RUnlock() - - chosen, recv, recvOK := reflect.Select(selcases) - switch chosen { - case ctxDoneIdx: - // <-ctx.Done(). Just bail out of this loop - return - case configureIdx: - // <-configureCh. rebuild the select list from the registry. - // since we're rebuilding everything for each iteration, - // we just need to start the loop all over again - continue - case resetTimerIdx: - // <-resetTimerCh. interrupt polling, and reset the timer on - // a single target. this needs to be handled inside this select - if !recvOK { - continue - } - - req := recv.Interface().(*resetTimerReq) //nolint:forcetypeassert - t := req.t - d := req.d - if !t.timer.Stop() { - select { - case <-t.timer.C: - default: - } - } - t.timer.Reset(d) - case removeIdx: - // <-removeCh. remove the URL from future fetching - //nolint:forcetypeassert - req := recv.Interface().(removeReq) - replyCh := req.replyCh - url := req.url - af.muRegistry.Lock() - if _, ok := af.registry[url]; !ok { - replyCh <- errors.Errorf(`invalid url %q (not registered)`, url) - } else { - delete(af.registry, url) - replyCh <- nil - } - af.muRegistry.Unlock() - default: - // Do not fire a refresh in case the channel was closed. - if !recvOK { - continue - } - - // Time to refresh a target - t := targets[chosen-baseSelcasesLen] - - // Check if there are other goroutines still doing the refresh asynchronously. - // This could happen if the refreshing goroutine is stuck on a backoff - // waiting for the HTTP request to complete. - select { - case t.sem <- struct{}{}: - // There can only be one refreshing goroutine - default: - continue - } - - go func() { - //nolint:errcheck - af.doRefreshRequest(ctx, t.url, true) - <-t.sem - }() - } - } -} - -func (af *AutoRefresh) doRefreshRequest(ctx context.Context, url string, enableBackoff bool) error { - af.muRegistry.RLock() - t, ok := af.registry[url] - - if !ok { - af.muRegistry.RUnlock() - return errors.Errorf(`url "%s" is not registered`, url) - } - - // In case the refresh fails due to errors in fetching/parsing the JWKS, - // we want to retry. Create a backoff object, - parseOptions := t.parseOptions - fetchOptions := []FetchOption{WithHTTPClient(t.httpcl)} - if enableBackoff { - fetchOptions = append(fetchOptions, WithFetchBackoff(t.backoff)) - } - if t.wl != nil { - fetchOptions = append(fetchOptions, WithFetchWhitelist(t.wl)) - } - af.muRegistry.RUnlock() - - res, err := fetch(ctx, url, fetchOptions...) - if err == nil { - if res.StatusCode != http.StatusOK { - // now, can there be a remote resource that responds with a status code - // other than 200 and still be valid...? naaaaaaahhhhhh.... - err = errors.Errorf(`bad response status code (%d)`, res.StatusCode) - } else { - defer res.Body.Close() - keyset, parseErr := ParseReader(res.Body, parseOptions...) - if parseErr == nil { - // Got a new key set. replace the keyset in the target - af.muCache.Lock() - af.cache[url] = keyset - af.muCache.Unlock() - af.muRegistry.RLock() - nextInterval := calculateRefreshDuration(res, t.refreshInterval, t.minRefreshInterval) - af.muRegistry.RUnlock() - rtr := &resetTimerReq{ - t: t, - d: nextInterval, - } - select { - case <-ctx.Done(): - return ctx.Err() - case af.resetTimerCh <- rtr: - } - - now := time.Now() - af.muRegistry.Lock() - t.lastRefresh = now.Local() - t.nextRefresh = now.Add(nextInterval).Local() - af.muRegistry.Unlock() - return nil - } - err = parseErr - } - } - - // At this point if err != nil, we know that there was something wrong - // in either the fetching or the parsing. Send this error to be processed, - // but take the extra mileage to not block regular processing by - // discarding the error if we fail to send it through the channel - if err != nil { - select { - case af.errSink <- AutoRefreshError{Error: err, URL: url}: - default: - } - } - - // We either failed to perform the HTTP GET, or we failed to parse the - // JWK set. Even in case of errors, we don't delete the old key. - // We persist the old key set, even if it may be stale so the user has something to work with - // TODO: maybe this behavior should be customizable? - - // If we failed to get a single time, then queue another fetch in the future. - rtr := &resetTimerReq{ - t: t, - d: calculateRefreshDuration(res, t.refreshInterval, t.minRefreshInterval), - } - select { - case <-ctx.Done(): - return ctx.Err() - case af.resetTimerCh <- rtr: - } - - return err -} - -// ErrorSink sets a channel to receive JWK fetch errors, if any. -// Only the errors that occurred *after* the channel was set will be sent. -// -// The user is responsible for properly draining the channel. If the channel -// is not drained properly, errors will be discarded. -// -// To disable, set a nil channel. -func (af *AutoRefresh) ErrorSink(ch chan AutoRefreshError) { - af.muErrSink.Lock() - af.errSink = ch - af.muErrSink.Unlock() -} - -func calculateRefreshDuration(res *http.Response, refreshInterval *time.Duration, minRefreshInterval time.Duration) time.Duration { - // This always has precedence - if refreshInterval != nil { - return *refreshInterval - } - - if res != nil { - if v := res.Header.Get(`Cache-Control`); v != "" { - dir, err := httpcc.ParseResponse(v) - if err == nil { - maxAge, ok := dir.MaxAge() - if ok { - resDuration := time.Duration(maxAge) * time.Second - if resDuration > minRefreshInterval { - return resDuration - } - return minRefreshInterval - } - // fallthrough - } - // fallthrough - } - - if v := res.Header.Get(`Expires`); v != "" { - expires, err := http.ParseTime(v) - if err == nil { - resDuration := time.Until(expires) - if resDuration > minRefreshInterval { - return resDuration - } - return minRefreshInterval - } - // fallthrough - } - } - - // Previous fallthroughs are a little redandunt, but hey, it's all good. - return minRefreshInterval -} - -// TargetSnapshot is the structure returned by the Snapshot method. -// It contains information about a url that has been configured -// in AutoRefresh. -type TargetSnapshot struct { - URL string - NextRefresh time.Time - LastRefresh time.Time -} - -func (af *AutoRefresh) Snapshot() <-chan TargetSnapshot { - af.muRegistry.Lock() - ch := make(chan TargetSnapshot, len(af.registry)) - for url, t := range af.registry { - ch <- TargetSnapshot{ - URL: url, - NextRefresh: t.nextRefresh, - LastRefresh: t.lastRefresh, - } - } - af.muRegistry.Unlock() - close(ch) - return ch -} diff --git a/vendor/github.com/lestrrat-go/jwx/jws/gen.sh b/vendor/github.com/lestrrat-go/jwx/jws/gen.sh deleted file mode 100644 index dde877e..0000000 --- a/vendor/github.com/lestrrat-go/jwx/jws/gen.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -# Script to perform code generation. This exists to overcome -# the fact that go:generate doesn't really allow you to change directories - -set -e - -pushd internal/cmd/genheader -go build -o genheader main.go -popd - -./internal/cmd/genheader/genheader -objects=internal/cmd/genheader/objects.yml - -rm internal/cmd/genheader/genheader diff --git a/vendor/github.com/lestrrat-go/jwx/jws/io.go b/vendor/github.com/lestrrat-go/jwx/jws/io.go deleted file mode 100644 index da4b1df..0000000 --- a/vendor/github.com/lestrrat-go/jwx/jws/io.go +++ /dev/null @@ -1,23 +0,0 @@ -// Automatically generated by internal/cmd/genreadfile/main.go. DO NOT EDIT - -package jws - -import "os" - -// ReadFileOption describes options that can be passed to ReadFile. -// Currently there are no options available that can be passed to ReadFile, but -// it is provided here for anticipated future additions -type ReadFileOption interface { - Option - readFileOption() -} - -func ReadFile(path string, _ ...ReadFileOption) (*Message, error) { - f, err := os.Open(path) - if err != nil { - return nil, err - } - - defer f.Close() - return ParseReader(f) -} diff --git a/vendor/github.com/lestrrat-go/jwx/jws/jws.go b/vendor/github.com/lestrrat-go/jwx/jws/jws.go deleted file mode 100644 index 6908dd6..0000000 --- a/vendor/github.com/lestrrat-go/jwx/jws/jws.go +++ /dev/null @@ -1,961 +0,0 @@ -//go:generate ./gen.sh - -// Package jws implements the digital signature on JSON based data -// structures as described in https://tools.ietf.org/html/rfc7515 -// -// If you do not care about the details, the only things that you -// would need to use are the following functions: -// -// jws.Sign(payload, algorithm, key) -// jws.Verify(encodedjws, algorithm, key) -// -// To sign, simply use `jws.Sign`. `payload` is a []byte buffer that -// contains whatever data you want to sign. `alg` is one of the -// jwa.SignatureAlgorithm constants from package jwa. For RSA and -// ECDSA family of algorithms, you will need to prepare a private key. -// For HMAC family, you just need a []byte value. The `jws.Sign` -// function will return the encoded JWS message on success. -// -// To verify, use `jws.Verify`. It will parse the `encodedjws` buffer -// and verify the result using `algorithm` and `key`. Upon successful -// verification, the original payload is returned, so you can work on it. -package jws - -import ( - "bufio" - "bytes" - "context" - "crypto/ecdsa" - "crypto/ed25519" - "crypto/rsa" - "fmt" - "io" - "io/ioutil" - "net/http" - "net/url" - "reflect" - "strings" - "sync" - "unicode" - "unicode/utf8" - - "github.com/lestrrat-go/backoff/v2" - "github.com/lestrrat-go/jwx/internal/base64" - "github.com/lestrrat-go/jwx/internal/json" - "github.com/lestrrat-go/jwx/internal/pool" - "github.com/lestrrat-go/jwx/jwa" - "github.com/lestrrat-go/jwx/jwk" - "github.com/lestrrat-go/jwx/x25519" - "github.com/pkg/errors" -) - -var registry = json.NewRegistry() - -type payloadSigner struct { - signer Signer - key interface{} - protected Headers - public Headers -} - -func (s *payloadSigner) Sign(payload []byte) ([]byte, error) { - return s.signer.Sign(payload, s.key) -} - -func (s *payloadSigner) Algorithm() jwa.SignatureAlgorithm { - return s.signer.Algorithm() -} - -func (s *payloadSigner) ProtectedHeader() Headers { - return s.protected -} - -func (s *payloadSigner) PublicHeader() Headers { - return s.public -} - -var signers = make(map[jwa.SignatureAlgorithm]Signer) -var muSigner = &sync.Mutex{} - -// Sign generates a signature for the given payload, and serializes -// it in compact serialization format. In this format you may NOT use -// multiple signers. -// -// The `alg` parameter is the identifier for the signature algorithm -// that should be used. -// -// For the `key` parameter, any of the following is accepted: -// * A "raw" key (e.g. rsa.PrivateKey, ecdsa.PrivateKey, etc) -// * A crypto.Signer -// * A jwk.Key -// -// A `crypto.Signer` is used when the private part of a key is -// kept in an inaccessible location, such as hardware. -// `crypto.Signer` is currently supported for RSA, ECDSA, and EdDSA -// family of algorithms. -// -// If the key is a jwk.Key and the key contains a key ID (`kid` field), -// then it is added to the protected header generated by the signature -// -// The algorithm specified in the `alg` parameter must be able to support -// the type of key you provided, otherwise an error is returned. -// -// If you would like to pass custom headers, use the WithHeaders option. -// -// If the headers contain "b64" field, then the boolean value for the field -// is respected when creating the compact serialization form. That is, -// if you specify a header with `{"b64": false}`, then the payload is -// not base64 encoded. -// -// If you want to use a detached payload, use `jws.WithDetachedPayload()` as -// one of the options. When you use this option, you must always set the -// first parameter (`payload`) to `nil`, or the function will return an error -func Sign(payload []byte, alg jwa.SignatureAlgorithm, key interface{}, options ...SignOption) ([]byte, error) { - var hdrs Headers - var detached bool - for _, o := range options { - //nolint:forcetypeassert - switch o.Ident() { - case identHeaders{}: - hdrs = o.Value().(Headers) - case identDetachedPayload{}: - detached = true - if payload != nil { - return nil, errors.New(`jws.Sign: payload must be nil when jws.WithDetachedPayload() is specified`) - } - payload = o.Value().([]byte) - } - } - - muSigner.Lock() - signer, ok := signers[alg] - if !ok { - v, err := NewSigner(alg) - if err != nil { - muSigner.Unlock() - return nil, errors.Wrap(err, `failed to create signer`) - } - signers[alg] = v - signer = v - } - muSigner.Unlock() - - // XXX This is cheating. Ideally `detached` should be passed as a parameter - // but since this is an exported method, we can't change this without bumping - // major versions.... But we don't want to do that now, so we will cheat by - // making it part of the object - sig := &Signature{ - protected: hdrs, - detached: detached, - } - _, signature, err := sig.Sign(payload, signer, key) - if err != nil { - return nil, errors.Wrap(err, `failed sign payload`) - } - - return signature, nil -} - -// SignMulti accepts multiple signers via the options parameter, -// and creates a JWS in JSON serialization format that contains -// signatures from applying aforementioned signers. -// -// Use `jws.WithSigner(...)` to specify values how to generate -// each signature in the `"signatures": [ ... ]` field. -func SignMulti(payload []byte, options ...Option) ([]byte, error) { - var signers []*payloadSigner - for _, o := range options { - //nolint:forcetypeassert - switch o.Ident() { - case identPayloadSigner{}: - signers = append(signers, o.Value().(*payloadSigner)) - } - } - - if len(signers) == 0 { - return nil, errors.New(`no signers provided`) - } - - var result Message - - result.payload = payload - - result.signatures = make([]*Signature, 0, len(signers)) - for i, signer := range signers { - protected := signer.ProtectedHeader() - if protected == nil { - protected = NewHeaders() - } - - if err := protected.Set(AlgorithmKey, signer.Algorithm()); err != nil { - return nil, errors.Wrap(err, `failed to set "alg" header`) - } - - if key, ok := signer.key.(jwk.Key); ok { - if kid := key.KeyID(); kid != "" { - if err := protected.Set(KeyIDKey, kid); err != nil { - return nil, errors.Wrap(err, `failed to set "kid" header`) - } - } - } - sig := &Signature{ - headers: signer.PublicHeader(), - protected: protected, - } - _, _, err := sig.Sign(payload, signer.signer, signer.key) - if err != nil { - return nil, errors.Wrapf(err, `failed to generate signature for signer #%d (alg=%s)`, i, signer.Algorithm()) - } - - result.signatures = append(result.signatures, sig) - } - - return json.Marshal(result) -} - -type verifyCtx struct { - dst *Message - detachedPayload []byte - alg jwa.SignatureAlgorithm - key interface{} - useJKU bool - jwksFetcher JWKSetFetcher - // This is only used to differentiate compact/JSON serialization - // because certain features are enabled/disabled in each - isJSON bool -} - -var allowNoneWhitelist = jwk.WhitelistFunc(func(string) bool { - return false -}) - -// VerifyAuto is a special case of Verify(), where verification is done -// using verifications parameters that can be obtained using the information -// that is carried within the JWS message itself. -// -// Currently it only supports verification via `jku` which will be fetched -// using the object specified in `jws.JWKSetFetcher`. Note that URLs in `jku` can -// only have https scheme. -// -// Using this function will result in your program accessing remote resources via https, -// and therefore extreme caution should be taken which urls can be accessed. -// -// Without specifying extra arguments, the default `jws.JWKSetFetcher` will be -// configured with a whitelist that rejects *ALL URLSs*. This is to -// protect users from unintentionally allowing their projects to -// make unwanted requests. Therefore you must explicitly provide an -// instance of `jwk.Whitelist` that does what you want. -// -// If you want open access to any URLs in the `jku`, you can do this by -// using `jwk.InsecureWhitelist` as the whitelist, but this should be avoided in -// most cases, especially if the payload comes from outside of a controlled -// environment. -// -// It is also advised that you consider using some sort of backoff via `jws.WithFetchBackoff` -// -// Alternatively, you can provide your own `jws.JWKSetFetcher`. In this case -// there is no way for the framework to force you to set a whitelist, so the -// default behavior is to allow any URLs. You are responsible for providing -// your own safety measures. -func VerifyAuto(buf []byte, options ...VerifyOption) ([]byte, error) { - var ctx verifyCtx - // enable JKU processing - ctx.useJKU = true - - var fetchOptions []jwk.FetchOption - - //nolint:forcetypeassert - for _, option := range options { - switch option.Ident() { - case identMessage{}: - ctx.dst = option.Value().(*Message) - case identDetachedPayload{}: - ctx.detachedPayload = option.Value().([]byte) - case identJWKSetFetcher{}: - ctx.jwksFetcher = option.Value().(JWKSetFetcher) - case identFetchWhitelist{}: - fetchOptions = append(fetchOptions, jwk.WithFetchWhitelist(option.Value().(jwk.Whitelist))) - case identFetchBackoff{}: - fetchOptions = append(fetchOptions, jwk.WithFetchBackoff(option.Value().(backoff.Policy))) - case identHTTPClient{}: - fetchOptions = append(fetchOptions, jwk.WithHTTPClient(option.Value().(*http.Client))) - } - } - - // We shove the default Whitelist in the front of the option list. - // If the user provided one, it will overwrite our default value - if ctx.jwksFetcher == nil { - fetchOptions = append([]jwk.FetchOption{jwk.WithFetchWhitelist(allowNoneWhitelist)}, fetchOptions...) - ctx.jwksFetcher = NewJWKSetFetcher(fetchOptions...) - } - - return ctx.verify(buf) -} - -// Verify checks if the given JWS message is verifiable using `alg` and `key`. -// `key` may be a "raw" key (e.g. rsa.PublicKey) or a jwk.Key -// -// If the verification is successful, `err` is nil, and the content of the -// payload that was signed is returned. If you need more fine-grained -// control of the verification process, manually generate a -// `Verifier` in `verify` subpackage, and call `Verify` method on it. -// If you need to access signatures and JOSE headers in a JWS message, -// use `Parse` function to get `Message` object. -func Verify(buf []byte, alg jwa.SignatureAlgorithm, key interface{}, options ...VerifyOption) ([]byte, error) { - var ctx verifyCtx - ctx.alg = alg - ctx.key = key - //nolint:forcetypeassert - for _, option := range options { - switch option.Ident() { - case identMessage{}: - ctx.dst = option.Value().(*Message) - case identDetachedPayload{}: - ctx.detachedPayload = option.Value().([]byte) - default: - return nil, errors.Errorf(`invalid jws.VerifyOption %q passed`, `With`+strings.TrimPrefix(fmt.Sprintf(`%T`, option.Ident()), `jws.ident`)) - } - } - - return ctx.verify(buf) -} - -func (ctx *verifyCtx) verify(buf []byte) ([]byte, error) { - buf = bytes.TrimSpace(buf) - if len(buf) == 0 { - return nil, errors.New(`attempt to verify empty buffer`) - } - - if buf[0] == '{' { - return ctx.verifyJSON(buf) - } - return ctx.verifyCompact(buf) -} - -// VerifySet uses keys store in a jwk.Set to verify the payload in `buf`. -// -// In order for `VerifySet()` to use a key in the given set, the -// `jwk.Key` object must have a valid "alg" field, and it also must -// have either an empty value or the value "sig" in the "use" field. -// -// Furthermore if the JWS signature asks for a spefici "kid", the -// `jwk.Key` must have the same "kid" as the signature. -func VerifySet(buf []byte, set jwk.Set) ([]byte, error) { - n := set.Len() - for i := 0; i < n; i++ { - key, ok := set.Get(i) - if !ok { - continue - } - if key.Algorithm() == "" { // algorithm is not - continue - } - - if usage := key.KeyUsage(); usage != "" && usage != jwk.ForSignature.String() { - continue - } - - buf, err := Verify(buf, jwa.SignatureAlgorithm(key.Algorithm()), key) - if err != nil { - continue - } - - return buf, nil - } - - return nil, errors.New(`failed to verify message with any of the keys in the jwk.Set object`) -} - -func (ctx *verifyCtx) verifyJSON(signed []byte) ([]byte, error) { - ctx.isJSON = true - - var m Message - m.SetDecodeCtx(collectRawCtx{}) - defer m.clearRaw() - if err := json.Unmarshal(signed, &m); err != nil { - return nil, errors.Wrap(err, `failed to unmarshal JSON message`) - } - m.SetDecodeCtx(nil) - - if len(m.payload) != 0 && ctx.detachedPayload != nil { - return nil, errors.New(`can't specify detached payload for JWS with payload`) - } - - if ctx.detachedPayload != nil { - m.payload = ctx.detachedPayload - } - - // Pre-compute the base64 encoded version of payload - var payload string - if m.b64 { - payload = base64.EncodeToString(m.payload) - } else { - payload = string(m.payload) - } - - buf := pool.GetBytesBuffer() - defer pool.ReleaseBytesBuffer(buf) - - for i, sig := range m.signatures { - buf.Reset() - - var encodedProtectedHeader string - if rbp, ok := sig.protected.(interface{ rawBuffer() []byte }); ok { - if raw := rbp.rawBuffer(); raw != nil { - encodedProtectedHeader = base64.EncodeToString(raw) - } - } - - if encodedProtectedHeader == "" { - protected, err := json.Marshal(sig.protected) - if err != nil { - return nil, errors.Wrapf(err, `failed to marshal "protected" for signature #%d`, i+1) - } - - encodedProtectedHeader = base64.EncodeToString(protected) - } - - buf.WriteString(encodedProtectedHeader) - buf.WriteByte('.') - buf.WriteString(payload) - - if !ctx.useJKU { - if hdr := sig.protected; hdr != nil && hdr.KeyID() != "" { - if jwkKey, ok := ctx.key.(jwk.Key); ok { - if jwkKey.KeyID() != hdr.KeyID() { - continue - } - } - } - - verifier, err := NewVerifier(ctx.alg) - if err != nil { - return nil, errors.Wrap(err, "failed to create verifier") - } - - if _, err := ctx.tryVerify(verifier, sig.protected, buf.Bytes(), sig.signature, m.payload); err == nil { - if ctx.dst != nil { - *(ctx.dst) = m - } - return m.payload, nil - } - // Don't fallthrough or bail out. Try the next signature. - continue - } - - if _, err := ctx.verifyJKU(sig.protected, buf.Bytes(), sig.signature, m.payload); err == nil { - if ctx.dst != nil { - *(ctx.dst) = m - } - return m.payload, nil - } - // try next - } - return nil, errors.New(`could not verify with any of the signatures`) -} - -// get the value of b64 header field. -// If the field does not exist, returns true (default) -// Otherwise return the value specified by the header field. -func getB64Value(hdr Headers) bool { - b64raw, ok := hdr.Get("b64") - if !ok { - return true // default - } - - b64, ok := b64raw.(bool) // default - if !ok { - return false - } - return b64 -} - -func (ctx *verifyCtx) verifyCompact(signed []byte) ([]byte, error) { - protected, payload, signature, err := SplitCompact(signed) - if err != nil { - return nil, errors.Wrap(err, `failed extract from compact serialization format`) - } - - decodedSignature, err := base64.Decode(signature) - if err != nil { - return nil, errors.Wrap(err, `failed to decode signature`) - } - - hdr := NewHeaders() - decodedProtected, err := base64.Decode(protected) - if err != nil { - return nil, errors.Wrap(err, `failed to decode headers`) - } - - if err := json.Unmarshal(decodedProtected, hdr); err != nil { - return nil, errors.Wrap(err, `failed to decode headers`) - } - - verifyBuf := pool.GetBytesBuffer() - defer pool.ReleaseBytesBuffer(verifyBuf) - - verifyBuf.Write(protected) - verifyBuf.WriteByte('.') - if len(payload) == 0 && ctx.detachedPayload != nil { - if getB64Value(hdr) { - payload = base64.Encode(ctx.detachedPayload) - } else { - payload = ctx.detachedPayload - } - } - verifyBuf.Write(payload) - - if !ctx.useJKU { - if hdr.KeyID() != "" { - if jwkKey, ok := ctx.key.(jwk.Key); ok { - if jwkKey.KeyID() != hdr.KeyID() { - return nil, errors.New(`"kid" fields do not match`) - } - } - } - - verifier, err := NewVerifier(ctx.alg) - if err != nil { - return nil, errors.Wrap(err, "failed to create verifier") - } - - return ctx.tryVerify(verifier, hdr, verifyBuf.Bytes(), decodedSignature, payload) - } - - return ctx.verifyJKU(hdr, verifyBuf.Bytes(), decodedSignature, payload) -} - -// JWKSetFetcher is used to fetch JWK Set spcified in the `jku` field. -type JWKSetFetcher interface { - Fetch(string) (jwk.Set, error) -} - -// SimpleJWKSetFetcher is the default object used to fetch JWK Sets specified in `jku`, -// which uses `jwk.Fetch()` -// -// For more complicated cases, such as using `jwk.AutoRefetch`, you will have to -// create your custom instance of `jws.JWKSetFetcher` -type SimpleJWKSetFetcher struct { - options []jwk.FetchOption -} - -func NewJWKSetFetcher(options ...jwk.FetchOption) *SimpleJWKSetFetcher { - return &SimpleJWKSetFetcher{options: options} -} - -func (f *SimpleJWKSetFetcher) Fetch(u string) (jwk.Set, error) { - return jwk.Fetch(context.TODO(), u, f.options...) -} - -type JWKSetFetchFunc func(string) (jwk.Set, error) - -func (f JWKSetFetchFunc) Fetch(u string) (jwk.Set, error) { - return f(u) -} - -func (ctx *verifyCtx) verifyJKU(hdr Headers, verifyBuf, decodedSignature, payload []byte) ([]byte, error) { - u := hdr.JWKSetURL() - if u == "" { - return nil, errors.New(`use of "jku" field specified, but the field is empty`) - } - uo, err := url.Parse(u) - if err != nil { - return nil, errors.Wrap(err, `failed to parse "jku"`) - } - if uo.Scheme != "https" { - return nil, errors.New(`url in "jku" must be HTTPS`) - } - - set, err := ctx.jwksFetcher.Fetch(u) - if err != nil { - return nil, errors.Wrapf(err, `failed to fetch "jku"`) - } - - // Because we're using a JWKS here, we MUST have "kid" that matches - // the payload - if hdr.KeyID() == "" { - return nil, errors.Errorf(`"kid" is required on the JWS message to use "jku"`) - } - - key, ok := set.LookupKeyID(hdr.KeyID()) - if !ok { - return nil, errors.New(`key specified via "kid" is not present in the JWK set specified by "jku"`) - } - - // hooray, we found a key. Now the algorithm will have to be inferred. - algs, err := AlgorithmsForKey(key) - if err != nil { - return nil, errors.Wrapf(err, `failed to get a list of signature methods for key type %s`, key.KeyType()) - } - - // for each of these algorithms, just ... keep trying ... - ctx.key = key - hdrAlg := hdr.Algorithm() - for _, alg := range algs { - // if we have a "alg" field in the JWS, we can only proceed if - // the inferred algorithm matches - if hdrAlg != "" && hdrAlg != alg { - continue - } - - verifier, err := NewVerifier(alg) - if err != nil { - return nil, errors.Wrap(err, "failed to create verifier") - } - - if decoded, err := ctx.tryVerify(verifier, hdr, verifyBuf, decodedSignature, payload); err == nil { - return decoded, nil - } - } - return nil, errors.New(`failed to verify payload using key in "jku"`) -} - -func (ctx *verifyCtx) tryVerify(verifier Verifier, hdr Headers, buf, decodedSignature, payload []byte) ([]byte, error) { - if err := verifier.Verify(buf, decodedSignature, ctx.key); err != nil { - return nil, errors.Wrap(err, `failed to verify message`) - } - - var decodedPayload []byte - - // When verifying JSON messages, we do not need to decode - // the payload, as we already have it - if !ctx.isJSON { - // This is a special case for RFC7797 - if !getB64Value(hdr) { // it's not base64 encoded - decodedPayload = payload - } - - if decodedPayload == nil { - v, err := base64.Decode(payload) - if err != nil { - return nil, errors.Wrap(err, `message verified, failed to decode payload`) - } - decodedPayload = v - } - - // For compact serialization, we need to create and assign the message - // if requested - if ctx.dst != nil { - // Construct a new Message object - m := NewMessage() - m.SetPayload(decodedPayload) - sig := NewSignature() - sig.SetProtectedHeaders(hdr) - sig.SetSignature(decodedSignature) - m.AppendSignature(sig) - - *(ctx.dst) = *m - } - } - return decodedPayload, nil -} - -// This is an "optimized" ioutil.ReadAll(). It will attempt to read -// all of the contents from the reader IF the reader is of a certain -// concrete type. -func readAll(rdr io.Reader) ([]byte, bool) { - switch rdr.(type) { - case *bytes.Reader, *bytes.Buffer, *strings.Reader: - data, err := ioutil.ReadAll(rdr) - if err != nil { - return nil, false - } - return data, true - default: - return nil, false - } -} - -// Parse parses contents from the given source and creates a jws.Message -// struct. The input can be in either compact or full JSON serialization. -func Parse(src []byte) (*Message, error) { - for i := 0; i < len(src); i++ { - r := rune(src[i]) - if r >= utf8.RuneSelf { - r, _ = utf8.DecodeRune(src) - } - if !unicode.IsSpace(r) { - if r == '{' { - return parseJSON(src) - } - return parseCompact(src) - } - } - return nil, errors.New("invalid byte sequence") -} - -// Parse parses contents from the given source and creates a jws.Message -// struct. The input can be in either compact or full JSON serialization. -func ParseString(src string) (*Message, error) { - return Parse([]byte(src)) -} - -// Parse parses contents from the given source and creates a jws.Message -// struct. The input can be in either compact or full JSON serialization. -func ParseReader(src io.Reader) (*Message, error) { - if data, ok := readAll(src); ok { - return Parse(data) - } - - rdr := bufio.NewReader(src) - var first rune - for { - r, _, err := rdr.ReadRune() - if err != nil { - return nil, errors.Wrap(err, `failed to read rune`) - } - if !unicode.IsSpace(r) { - first = r - if err := rdr.UnreadRune(); err != nil { - return nil, errors.Wrap(err, `failed to unread rune`) - } - - break - } - } - - var parser func(io.Reader) (*Message, error) - if first == '{' { - parser = parseJSONReader - } else { - parser = parseCompactReader - } - - m, err := parser(rdr) - if err != nil { - return nil, errors.Wrap(err, `failed to parse jws message`) - } - - return m, nil -} - -func parseJSONReader(src io.Reader) (result *Message, err error) { - var m Message - if err := json.NewDecoder(src).Decode(&m); err != nil { - return nil, errors.Wrap(err, `failed to unmarshal jws message`) - } - return &m, nil -} - -func parseJSON(data []byte) (result *Message, err error) { - var m Message - if err := json.Unmarshal(data, &m); err != nil { - return nil, errors.Wrap(err, `failed to unmarshal jws message`) - } - return &m, nil -} - -// SplitCompact splits a JWT and returns its three parts -// separately: protected headers, payload and signature. -func SplitCompact(src []byte) ([]byte, []byte, []byte, error) { - parts := bytes.Split(src, []byte(".")) - if len(parts) < 3 { - return nil, nil, nil, errors.New(`invalid number of segments`) - } - return parts[0], parts[1], parts[2], nil -} - -// SplitCompactString splits a JWT and returns its three parts -// separately: protected headers, payload and signature. -func SplitCompactString(src string) ([]byte, []byte, []byte, error) { - parts := strings.Split(src, ".") - if len(parts) < 3 { - return nil, nil, nil, errors.New(`invalid number of segments`) - } - return []byte(parts[0]), []byte(parts[1]), []byte(parts[2]), nil -} - -// SplitCompactReader splits a JWT and returns its three parts -// separately: protected headers, payload and signature. -func SplitCompactReader(rdr io.Reader) ([]byte, []byte, []byte, error) { - if data, ok := readAll(rdr); ok { - return SplitCompact(data) - } - - var protected []byte - var payload []byte - var signature []byte - var periods int - var state int - - buf := make([]byte, 4096) - var sofar []byte - - for { - // read next bytes - n, err := rdr.Read(buf) - // return on unexpected read error - if err != nil && err != io.EOF { - return nil, nil, nil, errors.Wrap(err, `unexpected end of input`) - } - - // append to current buffer - sofar = append(sofar, buf[:n]...) - // loop to capture multiple '.' in current buffer - for loop := true; loop; { - var i = bytes.IndexByte(sofar, '.') - if i == -1 && err != io.EOF { - // no '.' found -> exit and read next bytes (outer loop) - loop = false - continue - } else if i == -1 && err == io.EOF { - // no '.' found -> process rest and exit - i = len(sofar) - loop = false - } else { - // '.' found - periods++ - } - - // Reaching this point means we have found a '.' or EOF and process the rest of the buffer - switch state { - case 0: - protected = sofar[:i] - state++ - case 1: - payload = sofar[:i] - state++ - case 2: - signature = sofar[:i] - } - // Shorten current buffer - if len(sofar) > i { - sofar = sofar[i+1:] - } - } - // Exit on EOF - if err == io.EOF { - break - } - } - if periods != 2 { - return nil, nil, nil, errors.New(`invalid number of segments`) - } - - return protected, payload, signature, nil -} - -// parseCompactReader parses a JWS value serialized via compact serialization. -func parseCompactReader(rdr io.Reader) (m *Message, err error) { - protected, payload, signature, err := SplitCompactReader(rdr) - if err != nil { - return nil, errors.Wrap(err, `invalid compact serialization format`) - } - return parse(protected, payload, signature) -} - -func parseCompact(data []byte) (m *Message, err error) { - protected, payload, signature, err := SplitCompact(data) - if err != nil { - return nil, errors.Wrap(err, `invalid compact serialization format`) - } - return parse(protected, payload, signature) -} - -func parse(protected, payload, signature []byte) (*Message, error) { - decodedHeader, err := base64.Decode(protected) - if err != nil { - return nil, errors.Wrap(err, `failed to decode protected headers`) - } - - hdr := NewHeaders() - if err := json.Unmarshal(decodedHeader, hdr); err != nil { - return nil, errors.Wrap(err, `failed to parse JOSE headers`) - } - - decodedPayload, err := base64.Decode(payload) - if err != nil { - return nil, errors.Wrap(err, `failed to decode payload`) - } - - decodedSignature, err := base64.Decode(signature) - if err != nil { - return nil, errors.Wrap(err, `failed to decode signature`) - } - - var msg Message - msg.payload = decodedPayload - msg.signatures = append(msg.signatures, &Signature{ - protected: hdr, - signature: decodedSignature, - }) - return &msg, nil -} - -// RegisterCustomField allows users to specify that a private field -// be decoded as an instance of the specified type. This option has -// a global effect. -// -// For example, suppose you have a custom field `x-birthday`, which -// you want to represent as a string formatted in RFC3339 in JSON, -// but want it back as `time.Time`. -// -// In that case you would register a custom field as follows -// -// jwe.RegisterCustomField(`x-birthday`, timeT) -// -// Then `hdr.Get("x-birthday")` will still return an `interface{}`, -// but you can convert its type to `time.Time` -// -// bdayif, _ := hdr.Get(`x-birthday`) -// bday := bdayif.(time.Time) -// -func RegisterCustomField(name string, object interface{}) { - registry.Register(name, object) -} - -// Helpers for signature verification -var rawKeyToKeyType = make(map[reflect.Type]jwa.KeyType) -var keyTypeToAlgorithms = make(map[jwa.KeyType][]jwa.SignatureAlgorithm) - -func init() { - rawKeyToKeyType[reflect.TypeOf([]byte(nil))] = jwa.OctetSeq - rawKeyToKeyType[reflect.TypeOf(ed25519.PublicKey(nil))] = jwa.OKP - rawKeyToKeyType[reflect.TypeOf(rsa.PublicKey{})] = jwa.RSA - rawKeyToKeyType[reflect.TypeOf((*rsa.PublicKey)(nil))] = jwa.RSA - rawKeyToKeyType[reflect.TypeOf(ecdsa.PublicKey{})] = jwa.EC - rawKeyToKeyType[reflect.TypeOf((*ecdsa.PublicKey)(nil))] = jwa.EC - - addAlgorithmForKeyType(jwa.OKP, jwa.EdDSA) - for _, alg := range []jwa.SignatureAlgorithm{jwa.HS256, jwa.HS384, jwa.HS512} { - addAlgorithmForKeyType(jwa.OctetSeq, alg) - } - for _, alg := range []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512} { - addAlgorithmForKeyType(jwa.RSA, alg) - } - for _, alg := range []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512} { - addAlgorithmForKeyType(jwa.EC, alg) - } -} - -func addAlgorithmForKeyType(kty jwa.KeyType, alg jwa.SignatureAlgorithm) { - keyTypeToAlgorithms[kty] = append(keyTypeToAlgorithms[kty], alg) -} - -// AlgorithmsForKey returns the possible signature algorithms that can -// be used for a given key. It only takes in consideration keys/algorithms -// for verification purposes, as this is the only usage where one may need -// dynamically figure out which method to use. -func AlgorithmsForKey(key interface{}) ([]jwa.SignatureAlgorithm, error) { - var kty jwa.KeyType - switch key := key.(type) { - case jwk.Key: - kty = key.KeyType() - case rsa.PublicKey, *rsa.PublicKey, rsa.PrivateKey, *rsa.PrivateKey: - kty = jwa.RSA - case ecdsa.PublicKey, *ecdsa.PublicKey, ecdsa.PrivateKey, *ecdsa.PrivateKey: - kty = jwa.EC - case ed25519.PublicKey, ed25519.PrivateKey, x25519.PublicKey, x25519.PrivateKey: - kty = jwa.OKP - case []byte: - kty = jwa.OctetSeq - default: - return nil, errors.Errorf(`invalid key %T`, key) - } - - algs, ok := keyTypeToAlgorithms[kty] - if !ok { - return nil, errors.Errorf(`invalid key type %q`, kty) - } - return algs, nil -} diff --git a/vendor/github.com/lestrrat-go/jwx/jws/option.go b/vendor/github.com/lestrrat-go/jwx/jws/option.go deleted file mode 100644 index dfa6ded..0000000 --- a/vendor/github.com/lestrrat-go/jwx/jws/option.go +++ /dev/null @@ -1,119 +0,0 @@ -package jws - -import ( - "net/http" - - "github.com/lestrrat-go/backoff/v2" - "github.com/lestrrat-go/jwx/jwk" - "github.com/lestrrat-go/option" -) - -type Option = option.Interface - -type identPayloadSigner struct{} -type identDetachedPayload struct{} -type identHeaders struct{} -type identMessage struct{} -type identFetchBackoff struct{} -type identFetchWhitelist struct{} -type identHTTPClient struct{} -type identJWKSetFetcher struct{} - -func WithSigner(signer Signer, key interface{}, public, protected Headers) Option { - return option.New(identPayloadSigner{}, &payloadSigner{ - signer: signer, - key: key, - protected: protected, - public: public, - }) -} - -type SignOption interface { - Option - signOption() -} - -type signOption struct { - Option -} - -func (*signOption) signOption() {} - -// WithHeaders allows you to specify extra header values to include in the -// final JWS message -func WithHeaders(h Headers) SignOption { - return &signOption{option.New(identHeaders{}, h)} -} - -// VerifyOption describes an option that can be passed to the jws.Verify function -type VerifyOption interface { - Option - verifyOption() -} - -type verifyOption struct { - Option -} - -func (*verifyOption) verifyOption() {} - -// WithMessage can be passed to Verify() to obtain the jws.Message upon -// a successful verification. -func WithMessage(m *Message) VerifyOption { - return &verifyOption{option.New(identMessage{}, m)} -} - -type SignVerifyOption interface { - SignOption - VerifyOption -} - -type signVerifyOption struct { - Option -} - -func (*signVerifyOption) signOption() {} -func (*signVerifyOption) verifyOption() {} - -// WithDetachedPayload can be used to both sign or verify a JWS message with a -// detached payload. -// -// When this option is used for `jws.Sign()`, the first parameter (normally the payload) -// must be set to `nil`. -// -// If you have to verify using this option, you should know exactly how and why this works. -func WithDetachedPayload(v []byte) SignVerifyOption { - return &signVerifyOption{option.New(identDetachedPayload{}, v)} -} - -// WithFetchWhitelist specifies the whitelist object to be passed -// to `jwk.Fetch()` when `jws.VerifyAuto()` is used. If you do not -// specify a whitelist, `jws.VerifyAuto()` will ALWAYS fail. -// -// This option is ignored if WithJWKSetFetcher is specified. -func WithFetchWhitelist(wl jwk.Whitelist) VerifyOption { - return &verifyOption{option.New(identFetchWhitelist{}, wl)} -} - -// WithFetchBackoff specifies the backoff.Policy object to be passed -// to `jwk.Fetch()` when `jws.VerifyAuto()` is used. -// -// This option is ignored if WithJWKSetFetcher is specified. -func WithFetchBackoff(b backoff.Policy) VerifyOption { - return &verifyOption{option.New(identFetchBackoff{}, b)} -} - -// WithHTTPClient specifies the *http.Client object to be passed -// to `jwk.Fetch()` when `jws.VerifyAuto()` is used. -// -// This option is ignored if WithJWKSetFetcher is specified. -func WithHTTPClient(httpcl *http.Client) VerifyOption { - return &verifyOption{option.New(identHTTPClient{}, httpcl)} -} - -// WithJWKSetFetcher specifies the JWKSetFetcher object to be -// used when `jws.VerifyAuto()`, for example, to use `jwk.AutoRefetch` -// instead of the default `jwk.Fetch()` -func WithJWKSetFetcher(f JWKSetFetcher) VerifyOption { - return &verifyOption{option.New(identJWKSetFetcher{}, f)} -} diff --git a/vendor/github.com/lestrrat-go/jwx/jwt/gen.sh b/vendor/github.com/lestrrat-go/jwx/jwt/gen.sh deleted file mode 100644 index ceee3bd..0000000 --- a/vendor/github.com/lestrrat-go/jwx/jwt/gen.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -pushd internal/cmd/gentoken -go build -o gentoken main.go -popd - -./internal/cmd/gentoken/gentoken -objects=internal/cmd/gentoken/objects.yml - -rm internal/cmd/gentoken/gentoken diff --git a/vendor/github.com/lestrrat-go/jwx/jwt/internal/types/date.go b/vendor/github.com/lestrrat-go/jwx/jwt/internal/types/date.go deleted file mode 100644 index 2675cfd..0000000 --- a/vendor/github.com/lestrrat-go/jwx/jwt/internal/types/date.go +++ /dev/null @@ -1,98 +0,0 @@ -package types - -import ( - "strconv" - "time" - - "github.com/lestrrat-go/jwx/internal/json" - - "github.com/pkg/errors" -) - -// NumericDate represents the date format used in the 'nbf' claim -type NumericDate struct { - time.Time -} - -func (n *NumericDate) Get() time.Time { - if n == nil { - return (time.Time{}).UTC() - } - return n.Time -} - -func numericToTime(v interface{}, t *time.Time) bool { - var n int64 - switch x := v.(type) { - case int64: - n = x - case int32: - n = int64(x) - case int16: - n = int64(x) - case int8: - n = int64(x) - case int: - n = int64(x) - case float32: - n = int64(x) - case float64: - n = int64(x) - default: - return false - } - - *t = time.Unix(n, 0) - return true -} - -func (n *NumericDate) Accept(v interface{}) error { - var t time.Time - - switch x := v.(type) { - case string: - i, err := strconv.ParseInt(x[:], 10, 64) - if err != nil { - return errors.Errorf(`invalid epoch value %#v`, x) - } - t = time.Unix(i, 0) - - case json.Number: - intval, err := x.Int64() - if err != nil { - return errors.Wrapf(err, `failed to convert json value %#v to int64`, x) - } - t = time.Unix(intval, 0) - case time.Time: - t = x - default: - if !numericToTime(v, &t) { - return errors.Errorf(`invalid type %T`, v) - } - } - n.Time = t.UTC() - return nil -} - -// MarshalJSON translates from internal representation to JSON NumericDate -// See https://tools.ietf.org/html/rfc7519#page-6 -func (n *NumericDate) MarshalJSON() ([]byte, error) { - if n.IsZero() { - return json.Marshal(nil) - } - return json.Marshal(n.Unix()) -} - -func (n *NumericDate) UnmarshalJSON(data []byte) error { - var v interface{} - if err := json.Unmarshal(data, &v); err != nil { - return errors.Wrap(err, `failed to unmarshal date`) - } - - var n2 NumericDate - if err := n2.Accept(v); err != nil { - return errors.Wrap(err, `invalid value for NumericDate`) - } - *n = n2 - return nil -} diff --git a/vendor/github.com/lestrrat-go/jwx/jwt/io.go b/vendor/github.com/lestrrat-go/jwx/jwt/io.go deleted file mode 100644 index e696170..0000000 --- a/vendor/github.com/lestrrat-go/jwx/jwt/io.go +++ /dev/null @@ -1,29 +0,0 @@ -// Automatically generated by internal/cmd/genreadfile/main.go. DO NOT EDIT - -package jwt - -import "os" - -// ReadFileOption describes options that can be passed to ReadFile. -type ReadFileOption interface { - Option - readFileOption() -} - -func ReadFile(path string, options ...ReadFileOption) (Token, error) { - var parseOptions []ParseOption - for _, option := range options { - switch option := option.(type) { - case ParseOption: - parseOptions = append(parseOptions, option) - } - } - - f, err := os.Open(path) - if err != nil { - return nil, err - } - - defer f.Close() - return ParseReader(f, parseOptions...) -} diff --git a/vendor/github.com/lestrrat-go/jwx/jwt/options.go b/vendor/github.com/lestrrat-go/jwx/jwt/options.go deleted file mode 100644 index e905c35..0000000 --- a/vendor/github.com/lestrrat-go/jwx/jwt/options.go +++ /dev/null @@ -1,538 +0,0 @@ -package jwt - -import ( - "context" - "net/http" - "time" - - "github.com/lestrrat-go/backoff/v2" - "github.com/lestrrat-go/jwx/jwa" - "github.com/lestrrat-go/jwx/jwe" - "github.com/lestrrat-go/jwx/jwk" - "github.com/lestrrat-go/jwx/jws" - "github.com/lestrrat-go/option" -) - -type Option = option.Interface - -// GlobalOption describes an Option that can be passed to `Settings()`. -type GlobalOption interface { - Option - globalOption() -} - -type globalOption struct { - Option -} - -func (*globalOption) globalOption() {} - -// ParseRequestOption describes an Option that can be passed to `ParseRequest()`. -type ParseRequestOption interface { - ParseOption - httpParseOption() -} - -type httpParseOption struct { - ParseOption -} - -func (*httpParseOption) httpParseOption() {} - -// ParseOption describes an Option that can be passed to `Parse()`. -// ParseOption also implements ReadFileOption, therefore it may be -// safely pass them to `jwt.ReadFile()` -type ParseOption interface { - ReadFileOption - parseOption() -} - -type parseOption struct { - Option -} - -func newParseOption(n interface{}, v interface{}) ParseOption { - return &parseOption{option.New(n, v)} -} - -func (*parseOption) parseOption() {} -func (*parseOption) readFileOption() {} - -// SignOption describes an Option that can be passed to Sign() or -// (jwt.Serializer).Sign -type SignOption interface { - Option - signOption() -} - -type signOption struct { - Option -} - -func newSignOption(n interface{}, v interface{}) SignOption { - return &signOption{option.New(n, v)} -} - -func (*signOption) signOption() {} - -// EncryptOption describes an Option that can be passed to Encrypt() or -// (jwt.Serializer).Encrypt -type EncryptOption interface { - Option - encryptOption() -} - -type encryptOption struct { - Option -} - -func newEncryptOption(n interface{}, v interface{}) EncryptOption { - return &encryptOption{option.New(n, v)} -} - -func (*encryptOption) encryptOption() {} - -// ValidateOption describes an Option that can be passed to Validate(). -// ValidateOption also implements ParseOption, therefore it may be -// safely passed to `Parse()` (and thus `jwt.ReadFile()`) -type ValidateOption interface { - ParseOption - validateOption() -} - -type validateOption struct { - ParseOption -} - -func newValidateOption(n interface{}, v interface{}) ValidateOption { - return &validateOption{newParseOption(n, v)} -} - -func (*validateOption) validateOption() {} - -type identAcceptableSkew struct{} -type identClock struct{} -type identContext struct{} -type identDecrypt struct{} -type identDefault struct{} -type identFlattenAudience struct{} -type identInferAlgorithmFromKey struct{} -type identJweHeaders struct{} -type identJwsHeaders struct{} -type identKeySet struct{} -type identKeySetProvider struct{} -type identPedantic struct{} -type identValidator struct{} -type identToken struct{} -type identTypedClaim struct{} -type identValidate struct{} -type identVerify struct{} -type identVerifyAuto struct{} -type identFetchBackoff struct{} -type identFetchWhitelist struct{} -type identHTTPClient struct{} -type identJWKSetFetcher struct{} - -type identHeaderKey struct{} -type identFormKey struct{} - -type VerifyParameters interface { - Algorithm() jwa.SignatureAlgorithm - Key() interface{} -} - -type verifyParams struct { - alg jwa.SignatureAlgorithm - key interface{} -} - -func (p *verifyParams) Algorithm() jwa.SignatureAlgorithm { - return p.alg -} - -func (p *verifyParams) Key() interface{} { - return p.key -} - -// WithVerify forces the Parse method to verify the JWT message -// using the given key. XXX Should have been named something like -// WithVerificationKey -func WithVerify(alg jwa.SignatureAlgorithm, key interface{}) ParseOption { - return newParseOption(identVerify{}, &verifyParams{ - alg: alg, - key: key, - }) -} - -// WithKeySet forces the Parse method to verify the JWT message -// using one of the keys in the given key set. -// -// The key and the JWT MUST have a proper `kid` field set. -// The key to use for signature verification is chosen by matching -// the Key ID of the JWT and the ID of the given key set. -// -// When using this option, keys MUST have a proper 'alg' field -// set. This is because we need to know the exact algorithm that -// you (the user) wants to use to verify the token. We do NOT -// trust the token's headers, because they can easily be tampered with. -// -// However, there _is_ a workaround if you do understand the risks -// of allowing a library to automatically choose a signature verification strategy, -// and you do not mind the verification process having to possibly -// attempt using multiple times before succeeding to verify. See -// `jwt.InferAlgorithmFromKey` option -// -// If you have only one key in the set, and are sure you want to -// use that key, you can use the `jwt.WithDefaultKey` option. -// -// If provided with WithKeySetProvider(), this option takes precedence. -func WithKeySet(set jwk.Set) ParseOption { - return newParseOption(identKeySet{}, set) -} - -// UseDefaultKey is used in conjunction with the option WithKeySet -// to instruct the Parse method to default to the single key in a key -// set when no Key ID is included in the JWT. If the key set contains -// multiple keys then the default behavior is unchanged -- that is, -// the since we can't determine the key to use, it returns an error. -func UseDefaultKey(value bool) ParseOption { - return newParseOption(identDefault{}, value) -} - -// WithToken specifies the token instance that is used when parsing -// JWT tokens. -func WithToken(t Token) ParseOption { - return newParseOption(identToken{}, t) -} - -// WithHeaders is passed to `jwt.Sign()` function, to allow specifying arbitrary -// header values to be included in the header section of the jws message -// -// This option will be deprecated in the next major version. Use -// jwt.WithJwsHeaders() instead. -func WithHeaders(hdrs jws.Headers) SignOption { - return WithJwsHeaders(hdrs) -} - -// WithJwsHeaders is passed to `jwt.Sign()` function or -// "jwt.Serializer".Sign() method, to allow specifying arbitrary -// header values to be included in the header section of the JWE message -func WithJwsHeaders(hdrs jws.Headers) SignOption { - return newSignOption(identJwsHeaders{}, hdrs) -} - -// WithJweHeaders is passed to "jwt.Serializer".Encrypt() method to allow -// specifying arbitrary header values to be included in the protected header -// of the JWE message -func WithJweHeaders(hdrs jwe.Headers) EncryptOption { - return newEncryptOption(identJweHeaders{}, hdrs) -} - -// WithValidate is passed to `Parse()` method to denote that the -// validation of the JWT token should be performed after a successful -// parsing of the incoming payload. -func WithValidate(b bool) ParseOption { - return newParseOption(identValidate{}, b) -} - -// WithClock specifies the `Clock` to be used when verifying -// claims exp and nbf. -func WithClock(c Clock) ValidateOption { - return newValidateOption(identClock{}, c) -} - -// WithAcceptableSkew specifies the duration in which exp and nbf -// claims may differ by. This value should be positive -func WithAcceptableSkew(dur time.Duration) ValidateOption { - return newValidateOption(identAcceptableSkew{}, dur) -} - -// WithIssuer specifies that expected issuer value. If not specified, -// the value of issuer is not verified at all. -func WithIssuer(s string) ValidateOption { - return WithValidator(ClaimValueIs(IssuerKey, s)) -} - -// WithSubject specifies that expected subject value. If not specified, -// the value of subject is not verified at all. -func WithSubject(s string) ValidateOption { - return WithValidator(ClaimValueIs(SubjectKey, s)) -} - -// WithJwtID specifies that expected jti value. If not specified, -// the value of jti is not verified at all. -func WithJwtID(s string) ValidateOption { - return WithValidator(ClaimValueIs(JwtIDKey, s)) -} - -// WithAudience specifies that expected audience value. -// `Validate()` will return true if one of the values in the `aud` element -// matches this value. If not specified, the value of issuer is not -// verified at all. -func WithAudience(s string) ValidateOption { - return WithValidator(ClaimContainsString(AudienceKey, s)) -} - -// WithClaimValue specifies the expected value for a given claim -func WithClaimValue(name string, v interface{}) ValidateOption { - return WithValidator(ClaimValueIs(name, v)) -} - -// WithHeaderKey is used to specify header keys to search for tokens. -// -// While the type system allows this option to be passed to jwt.Parse() directly, -// doing so will have no effect. Only use it for HTTP request parsing functions -func WithHeaderKey(v string) ParseRequestOption { - return &httpParseOption{newParseOption(identHeaderKey{}, v)} -} - -// WithFormKey is used to specify header keys to search for tokens. -// -// While the type system allows this option to be passed to jwt.Parse() directly, -// doing so will have no effect. Only use it for HTTP request parsing functions -func WithFormKey(v string) ParseRequestOption { - return &httpParseOption{newParseOption(identFormKey{}, v)} -} - -// WithFlattenAudience specifies if the "aud" claim should be flattened -// to a single string upon the token being serialized to JSON. -// -// This is sometimes important when a JWT consumer does not understand that -// the "aud" claim can actually take the form of an array of strings. -// -// The default value is `false`, which means that "aud" claims are always -// rendered as a arrays of strings. This setting has a global effect, -// and will change the behavior for all JWT serialization. -func WithFlattenAudience(v bool) GlobalOption { - return &globalOption{option.New(identFlattenAudience{}, v)} -} - -type claimPair struct { - Name string - Value interface{} -} - -// WithTypedClaim allows a private claim to be parsed into the object type of -// your choice. It works much like the RegisterCustomField, but the effect -// is only applicable to the jwt.Parse function call which receives this option. -// -// While this can be extremely useful, this option should be used with caution: -// There are many caveats that your entire team/user-base needs to be aware of, -// and therefore in general its use is discouraged. Only use it when you know -// what you are doing, and you document its use clearly for others. -// -// First and foremost, this is a "per-object" option. Meaning that given the same -// serialized format, it is possible to generate two objects whose internal -// representations may differ. That is, if you parse one _WITH_ the option, -// and the other _WITHOUT_, their internal representation may completely differ. -// This could potentially lead to problems. -// -// Second, specifying this option will slightly slow down the decoding process -// as it needs to consult multiple definitions sources (global and local), so -// be careful if you are decoding a large number of tokens, as the effects will stack up. -// -// Finally, this option will also NOT work unless the tokens themselves support such -// parsing mechanism. For example, while tokens obtained from `jwt.New()` and -// `openid.New()` will respect this option, if you provide your own custom -// token type, it will need to implement the TokenWithDecodeCtx interface. -func WithTypedClaim(name string, object interface{}) ParseOption { - return newParseOption(identTypedClaim{}, claimPair{Name: name, Value: object}) -} - -// WithRequiredClaim specifies that the claim identified the given name -// must exist in the token. Only the existence of the claim is checked: -// the actual value associated with that field is not checked. -func WithRequiredClaim(name string) ValidateOption { - return WithValidator(IsRequired(name)) -} - -// WithMaxDelta specifies that given two claims `c1` and `c2` that represent time, the difference in -// time.Duration must be less than equal to the value specified by `d`. If `c1` or `c2` is the -// empty string, the current time (as computed by `time.Now` or the object passed via -// `WithClock()`) is used for the comparison. -// -// `c1` and `c2` are also assumed to be required, therefore not providing either claim in the -// token will result in an error. -// -// Because there is no way of reliably knowing how to parse private claims, we currently only -// support `iat`, `exp`, and `nbf` claims. -// -// If the empty string is passed to c1 or c2, then the current time (as calculated by time.Now() or -// the clock object provided via WithClock()) is used. -// -// For example, in order to specify that `exp` - `iat` should be less than 10*time.Second, you would write -// -// jwt.Validate(token, jwt.WithMaxDelta(10*time.Second, jwt.ExpirationKey, jwt.IssuedAtKey)) -// -// If AcceptableSkew of 2 second is specified, the above will return valid for any value of -// `exp` - `iat` between 8 (10-2) and 12 (10+2). -func WithMaxDelta(dur time.Duration, c1, c2 string) ValidateOption { - return WithValidator(MaxDeltaIs(c1, c2, dur)) -} - -// WithMinDelta is almost exactly the same as WithMaxDelta, but force validation to fail if -// the difference between time claims are less than dur. -// -// For example, in order to specify that `exp` - `iat` should be greater than 10*time.Second, you would write -// -// jwt.Validate(token, jwt.WithMinDelta(10*time.Second, jwt.ExpirationKey, jwt.IssuedAtKey)) -// -// The validation would fail if the difference is less than 10 seconds. -// -func WithMinDelta(dur time.Duration, c1, c2 string) ValidateOption { - return WithValidator(MinDeltaIs(c1, c2, dur)) -} - -// WithValidator validates the token with the given Validator. -// -// For example, in order to validate tokens that are only valid during August, you would write -// -// validator := jwt.ValidatorFunc(func(_ context.Context, t jwt.Token) error { -// if time.Now().Month() != 8 { -// return fmt.Errorf(`tokens are only valid during August!`) -// } -// return nil -// }) -// err := jwt.Validate(token, jwt.WithValidator(validator)) -// -func WithValidator(v Validator) ValidateOption { - return newValidateOption(identValidator{}, v) -} - -type decryptParams struct { - alg jwa.KeyEncryptionAlgorithm - key interface{} -} - -type DecryptParameters interface { - Algorithm() jwa.KeyEncryptionAlgorithm - Key() interface{} -} - -func (dp *decryptParams) Algorithm() jwa.KeyEncryptionAlgorithm { - return dp.alg -} - -func (dp *decryptParams) Key() interface{} { - return dp.key -} - -// WithDecrypt allows users to specify parameters for decryption using -// `jwe.Decrypt`. You must specify this if your JWT is encrypted. -func WithDecrypt(alg jwa.KeyEncryptionAlgorithm, key interface{}) ParseOption { - return newParseOption(identDecrypt{}, &decryptParams{ - alg: alg, - key: key, - }) -} - -// WithPedantic enables pedantic mode for parsing JWTs. Currently this only -// applies to checking for the correct `typ` and/or `cty` when necessary. -func WithPedantic(v bool) ParseOption { - return newParseOption(identPedantic{}, v) -} - -// InferAlgorithmFromKey allows jwt.Parse to guess the signature algorithm -// passed to `jws.Verify()`, in case the key you provided does not have a proper `alg` header. -// -// Compared to providing explicit `alg` from the key this is slower, and in -// case our heuristics are wrong or outdated, may fail to verify the token. -// Also, automatic detection of signature verification methods are always -// more vulnerable for potential attack vectors. -// -// It is highly recommended that you fix your key to contain a proper `alg` -// header field instead of resorting to using this option, but sometimes -// it just needs to happen. -// -// Your JWT still need to have an `alg` field, and it must match one of the -// candidates that we produce for your key -func InferAlgorithmFromKey(v bool) ParseOption { - return newParseOption(identInferAlgorithmFromKey{}, v) -} - -// KeySetProvider is an interface for objects that can choose the appropriate -// jwk.Set to be used when verifying JWTs -type KeySetProvider interface { - // KeySetFrom returns the jwk.Set to be used to verify the token. - // Keep in mind that the token at the point when the method is called is NOT VERIFIED. - // DO NOT trust the contents of the Token too much. For example, do not take the - // hint as to which signature algorithm to use from the token itself. - KeySetFrom(Token) (jwk.Set, error) -} - -// KeySetProviderFunc is an implementation of KeySetProvider that is based -// on a function. -type KeySetProviderFunc func(Token) (jwk.Set, error) - -func (fn KeySetProviderFunc) KeySetFrom(t Token) (jwk.Set, error) { - return fn(t) -} - -// WithKeySetProvider allows users to specify an object to choose which -// jwk.Set to use for verification. -// -// If provided with WithKeySet(), WithKeySet() option takes precedence. -func WithKeySetProvider(p KeySetProvider) ParseOption { - return newParseOption(identKeySetProvider{}, p) -} - -// WithContext allows you to specify a context.Context object to be used -// with `jwt.Validate()` option. -// -// Please be aware that in the next major release of this library, -// `jwt.Validate()`'s signature will change to include an explicit -// `context.Context` object. -func WithContext(ctx context.Context) ValidateOption { - return newValidateOption(identContext{}, ctx) -} - -// WithVerifyAuto specifies that the JWS verification should be performed -// using `jws.VerifyAuto()`, which in turn attempts to verify the message -// using values that are stored within the JWS message. -// -// Only passing this option to `jwt.Parse()` will not result in a successful -// verification. Please make sure to carefully read the documentation in -// `jws.VerifyAuto()`, and provide the necessary Whitelist object via -// `jwt.WithFetchWhitelist()` -// -// You might also consider using a backoff policy by using `jwt.WithFetchBackoff()` -// to control the number of requests being made. -func WithVerifyAuto(v bool) ParseOption { - return newParseOption(identVerifyAuto{}, v) -} - -// WithFetchWhitelist specifies the `jwk.Whitelist` object that should be -// passed to `jws.VerifyAuto()`, which in turn will be passed to `jwk.Fetch()` -// -// This is a wrapper over `jws.WithFetchWhitelist()` that can be passed -// to `jwt.Parse()`, and will be ignored if you spcify `jws.WithJWKSetFetcher()` -func WithFetchWhitelist(wl jwk.Whitelist) ParseOption { - return newParseOption(identFetchWhitelist{}, wl) -} - -// WithHTTPClient specifies the `*http.Client` object that should be -// passed to `jws.VerifyAuto()`, which in turn will be passed to `jwk.Fetch()` -// -// This is a wrapper over `jws.WithHTTPClient()` that can be passed -// to `jwt.Parse()`, and will be ignored if you spcify `jws.WithJWKSetFetcher()` -func WithHTTPClient(httpcl *http.Client) ParseOption { - return newParseOption(identHTTPClient{}, httpcl) -} - -// WithFetchBackoff specifies the `backoff.Policy` object that should be -// passed to `jws.VerifyAuto()`, which in turn will be passed to `jwk.Fetch()` -// -// This is a wrapper over `jws.WithFetchBackoff()` that can be passed -// to `jwt.Parse()`, and will be ignored if you spcify `jws.WithJWKSetFetcher()` -func WithFetchBackoff(b backoff.Policy) ParseOption { - return newParseOption(identFetchBackoff{}, b) -} - -// WithJWKSetFetcher specifies the `jws.JWKSetFetcher` object that should be -// passed to `jws.VerifyAuto()` -// -// This is a wrapper over `jws.WithJWKSetFetcher()` that can be passed -// to `jwt.Parse()`. -func WithJWKSetFetcher(f jws.JWKSetFetcher) ParseOption { - return newParseOption(identJWKSetFetcher{}, f) -} diff --git a/vendor/github.com/lestrrat-go/jwx/.gitignore b/vendor/github.com/lestrrat-go/jwx/v2/.gitignore similarity index 96% rename from vendor/github.com/lestrrat-go/jwx/.gitignore rename to vendor/github.com/lestrrat-go/jwx/v2/.gitignore index 5b2720b..605219c 100644 --- a/vendor/github.com/lestrrat-go/jwx/.gitignore +++ b/vendor/github.com/lestrrat-go/jwx/v2/.gitignore @@ -33,3 +33,5 @@ coverage.out # I redirect my test output to files named "out" way too often out + +cmd/jwx/jwx diff --git a/vendor/github.com/lestrrat-go/jwx/.golangci.yml b/vendor/github.com/lestrrat-go/jwx/v2/.golangci.yml similarity index 97% rename from vendor/github.com/lestrrat-go/jwx/.golangci.yml rename to vendor/github.com/lestrrat-go/jwx/v2/.golangci.yml index f2b0845..33508e1 100644 --- a/vendor/github.com/lestrrat-go/jwx/.golangci.yml +++ b/vendor/github.com/lestrrat-go/jwx/v2/.golangci.yml @@ -28,7 +28,6 @@ linters: - gofumpt - golint #deprecated - gomnd - - gomoddirectives # I think it's broken - gosec - govet - interfacer # deprecated diff --git a/vendor/github.com/lestrrat-go/jwx/v2/Changes b/vendor/github.com/lestrrat-go/jwx/v2/Changes new file mode 100644 index 0000000..7f7e98e --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/Changes @@ -0,0 +1,75 @@ +Changes +======= + +v2 has many incompatibilities with v1. To see the full list of differences between +v1 and v2, please read the Changes-v2.md file (https://github.com/lestrrat-go/jwx/blob/develop/v2/Changes-v2.md) + +v2.0.2 - 23 May 2022 +[Bug Fixes][Security] + * [jwe] An old bug from at least 7 years ago existed in handling AES-CBC unpadding, + where the unpad operation might remove more bytes than necessary (#744) + This affects all jwx code that is available before v2.0.2 and v1.2.25. + +[New Features] + * [jwt] RFC3339 timestamps are also accepted for Numeric Date types in JWT tokens. + This allows users to parse servers that errnously use RFC3339 timestamps in + some pre-defined fields. You can change this behavior by setting + `jwt.WithNumericDateParsePedantic` to `false` + * [jwt] `jwt.WithNumericDateParsePedantic` has been added. This is a global + option that is set using `jwt.Settings` + +v2.0.1 - 06 May 2022 + * [jwk] `jwk.Set` had erronously been documented as not returning an error + when the same key already exists in the set. This is a behavior change + since v2, and it was missing in the docs (#730) + * [jwt] `jwt.ErrMissingRequiredClaim` has been deprecated. Please use + `jwt.ErrRequiredClaim` instead. + * [jwt] `jwt.WithNumericDateParsePrecision` and `jwt.WithNumericDateFormatPrecision` + have been added to parse and format fractional seconds. These options can be + passed to `jwt.Settings`. + The default precision is set to 0, and fractional portions are not parsed nor + formatted. The precision may be set up to 9. + * `golang.org/x/crypto` has been upgraded (#724) + * `io/ioutil` has been removed from the source code. + +v2.0.0 - 24 Apr 2022 + * This i the first v2 release, which represents a set of design changes + that were learnt over the previous 2 years. As a result the v2 API + should be much more consistent and uniform across packages, and + should be much more flexible to accomodate real-world needs. + + For a complete list of changes, please see the Changes-v2.md file, + or check the diff at https://github.com/lestrrat-go/jwx/compare/v1...v2 + +[Miscellaneous] + * Minor house cleaning on code generation tools + +[jwt] + * `jwt.ErrMissingRequiredClaim()` has been added + +v2.0.0-beta2 - 16 Apr 2022 +[jwk] + * Updated `jwk.Set` API and reflected pending changes from v1 which were + left over. Please see Changes-v2.md file for details. + + * Added `jwk.CachedSet`, a shim over `jwk.Cache` that allows you to + have to write wrappers around `jwk.Cache` that retrieves a particular + `jwk.Set` out of it. You can use it to, for example, pass `jwk.CachedSet` + to a `jws.Verify` + + cache := jwk.NewCache(ctx) + cache.Register(ctx, jwksURL) + cachedSet := jwk.NewCachedSet(cache, jwksURL) + jws.Verify(signed, jws.WithKeySet(cachedSet)) + +v2.0.0-beta1 - 09 Apr 2022 +[Miscellaneous] + * Renamed Changes.v2 to Changes-v2.md + * Housecleaning for lint action. + * While v2 was not affected, ported over equivalent test for #681 to catch + regressions in the future. + * Please note that there is no stability guarantees on pre-releases. + +v2.0.0-alpha1 - 04 Apr 2022 + * Initial pre-release of v2 line. Please note that there is no stability guarantees + on pre-releases. diff --git a/vendor/github.com/lestrrat-go/jwx/v2/Changes-v2.md b/vendor/github.com/lestrrat-go/jwx/v2/Changes-v2.md new file mode 100644 index 0000000..1395c39 --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/Changes-v2.md @@ -0,0 +1,390 @@ +# Incompatible Changes from v1 to v2 + +These are changes that are incompatible with the v1.x.x version. + +* [tl;dr](#tldr) - If you don't feel like reading the details -- but you will read the details, right? +* [Detailed List of Changes](#detailed-list-of-changes) - A comprehensive list of changes from v1 to v2 + +# tl;dr + +## JWT + +```go +// most basic +jwt.Parse(serialized, jwt.WithKey(alg, key)) // NOTE: verification and validation are ENABLED by default! +jwt.Sign(token, jwt.WithKey(alg,key)) + +// with a jwk.Set +jwt.Parse(serialized, jwt.WithKeySet(set)) + +// UseDefault/InferAlgorithm with JWKS +jwt.Parse(serialized, jwt.WithKeySet(set, + jws.WithUseDefault(true), jws.WithInferAlgorithm(true)) + +// Use `jku` +jwt.Parse(serialized, jwt.WithVerifyAuto(...)) + +// Any other custom key provisioning (using functions in this +// example, but can be anything that fulfills jws.KeyProvider) +jwt.Parse(serialized, jwt.WithKeyProvider(jws.KeyProviderFunc(...))) +``` + +## JWK + +```go +// jwk.New() was confusing. Renamed to fit the actual implementation +key, err := jwk.FromRaw(rawKey) + +// Algorithm() now returns jwa.KeyAlgorithm type. `jws.Sign()` +// and other function that receive JWK algorithm names accept +// this new type, so you can use the same key and do the following +// (previosly you needed to type assert) +jws.Sign(payload, jws.WithKey(key.Algorithm(), key)) + +// If you need the specific type, type assert +key.Algorithm().(jwa.SignatureAlgorithm) + +// jwk.AutoRefresh is no more. Use jwk.Cache +cache := jwk.NewCache(ctx, options...) + +// Certificate chains are no longer jwk.CertificateChain type, but +// *(github.com/lestrrat-go/jwx/cert).Chain +cc := key.X509CertChain() // this is *cert.Chain now +``` + +## JWS + +```go +// basic +jws.Sign(payload, jws.WithKey(alg, key)) +jws.Sign(payload, jws.WithKey(alg, key), jws.WithKey(alg, key), jws.WithJSON(true)) +jws.Verify(signed, jws.WithKey(alg, key)) + +// other ways to pass the key +jws.Sign(payload, jws.WithKeySet(jwks)) +jws.Sign(payload, jws.WithKeyProvider(kp)) + +// retrieve the key that succeeded in verifying +var keyUsed interface{} +jws.Verify(signed, jws.WithKeySet(jwks), jws.WithKeyUsed(&keyUsed)) +``` + +## JWE + +```go +// basic +jwe.Encrypt(payload, jwe.WithKey(alg, key)) // other defaults are infered +jwe.Encrypt(payload, jwe.WithKey(alg, key), jwe.WithKey(alg, key), jwe.WithJSON(true)) +jwe.Decrypt(encrypted, jwe.WithKey(alg, key)) + +// other ways to pass the key +jwe.Encrypt(payload, jwe.WithKeySet(jwks)) +jwe.Encrypt(payload, jwe.WithKeyProvider(kp)) + +// retrieve the key that succeeded in decrypting +var keyUsed interface{} +jwe.Verify(signed, jwe.WithKeySet(jwks), jwe.WithKeyUsed(&keyUsed)) +``` + +# Detailed List of Changes + +## Module + +* Module now requires go 1.16 + +* Use of github.com/pkg/errors is no more. If you were relying on bevaior + that depends on the errors being an instance of github.com/pkg/errors + then you need to change your code + +* File-generation tools have been moved out of internal/ directories. + These files pre-dates Go modules, and they were in internal/ in order + to avoid being listed in the `go doc` -- however, now that we can + make them separate modules this is no longer necessary. + +* New package `cert` has been added to handle `x5c` certificate + chains, and to work with certificates + * cert.Chain to store base64 encoded ASN.1 DER format certificates + * cert.EncodeBase64 to encode ASN.1 DER format certificate using base64 + * cert.Create to create a base64 encoded ASN.1 DER format certificates + * cert.Parse to parse base64 encoded ASN.1 DER format certificates + +## JWE + +* `jwe.Compact()`'s signature has changed to + `jwe.Compact(*jwe.Message, ...jwe.CompactOption)` + +* `jwe.JSON()` has been removed. You can generate JSON serialization + using `jwe.Encrypt(jwe.WitJSON())` or `json.Marshal(jwe.Message)` + +* `(jwe.Message).Decrypt()` has been removed. Since formatting of the + original serialized message matters (including whitespace), using a parsed + object was inherently confusing. + +* `jwe.Encrypt()` can now generate JWE messages in either compact or JSON + forms. By default, the compact form is used. JSON format can be + enabled by using the `jwe.WithJSON` option. + +* `jwe.Encrypt()` can now accept multiple keys by passing multiple + `jwe.WithKey()` options. This can be used with `jwe.WithJSON` to + create JWE messages with multiple recipients. + +* `jwe.DecryptEncryptOption()` has been renamed to `jwe.EncryptDecryptOption()`. + This is so that it is more uniform with `jws` equivalent of `jws.SignVerifyOption()` + where the producer (`Sign`) comes before the consumer (`Verify`) in the naming + +* `jwe.WithCompact` and `jwe.WithJSON` options have been added + to control the serialization format. + +* jwe.Decrypt()'s method signature has been changed to `jwt.Decrypt([]byte, ...jwe.DecryptOption) ([]byte, error)`. + These options can be stacked. Therefore, you could configure the + verification process to attempt a static key pair, a JWKS, and only + try other forms if the first two fails, for example. + + - For static key pair, use `jwe.WithKey()` + - For static JWKS, use `jwe.WithKeySet()` (NOTE: InferAlgorithmFromKey like in `jws` package is NOT supported) + - For custom, possibly dynamic key provisioning, use `jwe.WithKeyProvider()` + +* jwe.Decrypter has been unexported. Users did not need this. + +* jwe.WithKeyProvider() has been added to specify arbitrary + code to specify which keys to try. + +* jwe.KeyProvider interface has been added + +* jwe.KeyProviderFunc has been added + +* `WithPostParser()` has been removed. You can achieve the same effect + by using `jwe.WithKeyProvider()`. Because this was the only consumer for + `jwe.DecryptCtx`, this type has been removed as well. + +* `x5c` field type has been changed to `*cert.Chain` instead of `[]string` + +* Method signature for `jwe.Parse()` has been changed to include options, + but options are currently not used + +* `jwe.ReadFile` now supports the option `jwe.WithFS` which allows you to + read data from arbitrary `fs.FS` objects + +* jwe.WithKeyUsed has been added to allow users to retrieve + the key used for decryption. This is useful in cases you provided + multiple keys and you want to know which one was successful + +## JWK + +* `jwk.New()` has been renamed to `jwk.FromRaw()`, which hopefully will + make it easier for the users what the input should be. + +* `jwk.Set` has many interface changes: + * Changed methods to match jwk.Key and its semantics: + * Field is now Get() (returns values for arbitrary fields other than keys). Fetching a key is done via Key() + * Remove() now removes arbitrary fields, not keys. to remove keys, use RemoveKey() + * Iterate has been added to iterate through all non-key fields. + * Add is now AddKey(Key) string, and returns an error when the same key is added + * Get is now Key(int) (Key, bool) + * Remove is now RemoveKey(Key) error + * Iterate is now Keys(context.Context) KeyIterator + * Clear is now Clear() error + +* `jwk.CachedSet` has been added. You can create a `jwk.Set` that is backed by + `jwk.Cache` so you can do this: + +```go +cache := jkw.NewCache(ctx) +cachedSet := jwk.NewCachedSet(cache, jwksURI) + +// cachedSet is always the refreshed, cached version from jwk.Cache +jws.Verify(signed, jws.WithKeySet(cachedSet)) +``` + +* `jwk.NewRSAPRivateKey()`, `jwk.NewECDSAPrivateKey()`, etc have been removed. + There is no longer any way to create concrete types of `jwk.Key` + +* `jwk.Key` type no longer supports direct unmarshaling via `json.Unmarshal()`, + because you can no longer instantiate concrete `jwk.Key` types. You will need to + use `jwk.ParseKey()`. See the documentation for ways to parse JWKs. + +* `(jwk.Key).Algorithm()` is now of `jwk.KeyAlgorithm` type. This field used + to be `string` and therefore could not be passed directly to `jwt.Sign()` + `jws.Sign()`, `jwe.Encrypt()`, et al. This is no longer the case, and + now you can pass it directly. See + https://github.com/lestrrat-go/jwx/blob/v2/docs/99-faq.md#why-is-jwkkeyalgorithm-and-jwakeyalgorithm-so-confusing + for more details + +* `jwk.Fetcher` and `jwk.FetchFunc` has been added. + They represent something that can fetch a `jwk.Set` + +* `jwk.CertificateChain` has been removed, use `*cert.Chain` +* `x5c` field type has been changed to `*cert.Chain` instead of `[]*x509.Certificate` + +* `jwk.ReadFile` now supports the option `jwk.WithFS` which allows you to + read data from arbitrary `fs.FS` objects + +* Added `jwk.PostFetcher`, `jwk.PostFetchFunc`, and `jwk.WithPostFetch` to + allow users to get at the `jwk.Set` that was fetched in `jwk.Cache`. + This will make it possible for users to supply extra information and edit + `jwk.Set` after it has been fetched and parsed, but before it is cached. + You could, for example, modify the `alg` field so that it's easier to + work with when you use it in `jws.Verify` later. + +* Reworked `jwk.AutoRefresh` in terms of `github.com/lestrrat-go/httprc` + and renamed it `jwk.Cache`. + + Major difference between `jwk.AutoRefresh` and `jwk.Cache` is that while + former used one `time.Timer` per resource, the latter uses a static timer + (based on `jwk.WithRefreshWindow()` value, default 15 minutes) that periodically + refreshes all resources that were due to be refreshed within that time frame. + + This method may cause your updates to happen slightly later, but uses significantly + less resources and is less prone to clogging. + +* Reimplemented `jwk.Fetch` in terms of `github.com/lestrrat-go/httprc`. + +* Previously `jwk.Fetch` and `jwk.AutoRefresh` respected backoff options, + but this has been removed. This is to avoid unwanted clogging of the fetch workers + which is the default processing mode in `github.com/lestrrat-go/httprc`. + + If you are using backoffs, you need to control your inputs more carefully so as to + not clog your fetch queue, and therefore you should be writing custom code that + suits your needs + +## JWS + +* `jws.Sign()` can now generate JWS messages in either compact or JSON + forms. By default, the compact form is used. JSON format can be + enabled by using the `jws.WithJSON` option. + +* `jws.Sign()` can now accept multiple keys by passing multiple + `jws.WithKey()` options. This can be used with `jws.WithJSON` to + create JWS messages with multiple signatures. + +* `jws.WithCompact` and `jws.WithJSON` options have been added + to control the serialization format. + +* jws.Verify()'s method signature has been changed to `jwt.Verify([]byte, ...jws.VerifyOption) ([]byte, error)`. + These options can be stacked. Therefore, you could configure the + verification process to attempt a static key pair, a JWKS, and only + try other forms if the first two fails, for example. + + - For static key pair, use `jws.WithKey()` + - For static JWKS, use `jws.WithKeySet()` + - For enabling verification using `jku`, use `jws.WithVerifyAuto()` + - For custom, possibly dynamic key provisioning, use `jws.WithKeyProvider()` + +* jws.WithVerify() has been removed. + +* jws.WithKey() has been added to specify an algorithm + key to + verify the payload with. + +* jws.WithKeySet() has been added to specify a JWKS to be used for + verification. By default `kid` AND `alg` must match between the signature + and the key. + + The option can take further suboptions: + +```go +jws.Parse(serialized, + jws.WithKeySet(set, + // by default `kid` is required. set false to disable. + jws.WithRequireKid(false), + // optionally skip matching kid if there's exactly one key in set + jws.WithUseDefault(true), + // infer algorithm name from key type + jws.WithInferAlgorithm(true), + ), +) +``` + +* `jws.VerifuAuto` has been removed in favor of using + `jws.WithVerifyAuto` option with `jws.Verify()` + +* `jws.WithVerifyAuto` has been added to enable verification + using `jku`. + + The first argument must be a jwk.Fetcher object, but can be + set to `nil` to use the default implementation which is `jwk.Fetch` + + The rest of the arguments are treated as options passed to the + `(jwk.Fetcher).Fetch()` function. + +* Remove `jws.WithPayloadSigner()`. This should be completely repleceable + using `jws.WithKey()` + +* jws.WithKeyProvider() has been added to specify arbitrary + code to specify which keys to try. + +* jws.KeyProvider interface has been added + +* jws.KeyProviderFunc has been added + +* jws.WithKeyUsed has been added to allow users to retrieve + the key used for verification. This is useful in cases you provided + multiple keys and you want to know which one was successful + +* `x5c` field type has been changed to `*cert.Chain` instead of `[]string` + +* `jws.ReadFile` now supports the option `jws.WithFS` which allows you to + read data from arbitrary `fs.FS` objects + +## JWT + +* `jwt.Parse` now verifies the signature and validates the token + by default. You must disable it explicitly using `jwt.WithValidate(false)` + and/or `jwt.WithVerify(false)` if you only want to parse the JWT message. + + If you don't want either, a convenience function `jwt.ParseInsecure` + has been added. + +* `jwt.Parse` can only parse raw JWT (JSON) or JWS (JSON or Compact). + It no longer accepts JWE messages. + +* `jwt.WithDecrypt` has been removed + +* `jwt.WithJweHeaders` has been removed + +* `jwt.WithVerify()` has been renamed to `jwt.WithKey()`. The option can + be used for signing, encryption, and parsing. + +* `jwt.Validator` has been changed to return `jwt.ValidationError`. + If you provide a custom validator, you should wrap the error with + `jwt.NewValidationError()` + +* `jwt.UseDefault()` has been removed. You should use `jws.WithUseDefault()` + as a suboption in the `jwt.WithKeySet()` option. + +```go +jwt.Parse(serialized, jwt.WithKeySet(set, jws.WithUseDefault(true))) +``` + +* `jwt.InferAlgorithmFromKey()` has been removed. You should use + `jws.WithInferAlgorithmFromKey()` as a suboption in the `jwt.WithKeySet()` option. + +```go +jwt.Parse(serialized, jwt.WithKeySet(set, jws.WithInferAlgorithmFromKey(true))) +``` + +* jwt.WithKeySetProvider has been removed. Use `jwt.WithKeyProvider()` + instead. If jwt.WithKeyProvider seems a bit complicated, use a combination of + JWS parse, no-verify/validate JWT parse, and an extra JWS verify: + +```go +msg, _ := jws.Parse(signed) +token, _ := jwt.Parse(msg.Payload(), jwt.WithVerify(false), jwt.WithValidate(false)) +// Get information out of token, for example, `iss` +switch token.Issuer() { +case ...: + jws.Verify(signed, jwt.WithKey(...)) +} +``` + +* `jwt.WithHeaders` and `jwt.WithJwsHeaders` have been removed. + You should be able to use the new `jwt.WithKey` option to pass headers + +* `jwt.WithSignOption` and `jwt.WithEncryptOption` have been added as + escape hatches for options that are declared in `jws` and `jwe` packages + but not in `jwt` + +* `jwt.ReadFile` now supports the option `jwt.WithFS` which allows you to + read data from arbitrary `fs.FS` objects + +* `jwt.Sign()` has been changed so that it works more like the new `jws.Sign()` + diff --git a/vendor/github.com/lestrrat-go/jwx/LICENSE b/vendor/github.com/lestrrat-go/jwx/v2/LICENSE similarity index 100% rename from vendor/github.com/lestrrat-go/jwx/LICENSE rename to vendor/github.com/lestrrat-go/jwx/v2/LICENSE diff --git a/vendor/github.com/lestrrat-go/jwx/Makefile b/vendor/github.com/lestrrat-go/jwx/v2/Makefile similarity index 97% rename from vendor/github.com/lestrrat-go/jwx/Makefile rename to vendor/github.com/lestrrat-go/jwx/v2/Makefile index 6b50544..f107725 100644 --- a/vendor/github.com/lestrrat-go/jwx/Makefile +++ b/vendor/github.com/lestrrat-go/jwx/v2/Makefile @@ -4,7 +4,6 @@ generate: @$(MAKE) generate-jwa generate-jwe generate-jwk generate-jws generate-jwt generate-%: - @echo "> Generating for $(patsubst generate-%,%,$@)" @go generate $(shell pwd -P)/$(patsubst generate-%,%,$@) realclean: diff --git a/vendor/github.com/lestrrat-go/jwx/v2/README.md b/vendor/github.com/lestrrat-go/jwx/v2/README.md new file mode 100644 index 0000000..537ae31 --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/README.md @@ -0,0 +1,248 @@ +# github.com/lestrrat-go/jwx/v2 ![](https://github.com/lestrrat-go/jwx/workflows/CI/badge.svg?branch=v2) [![Go Reference](https://pkg.go.dev/badge/github.com/lestrrat-go/jwx/v2.svg)](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2) [![codecov.io](https://codecov.io/github/lestrrat-go/jwx/coverage.svg?branch=v2)](https://codecov.io/github/lestrrat-go/jwx?branch=v2) + +Go module implementing various JWx (JWA/JWE/JWK/JWS/JWT, otherwise known as JOSE) technologies. + +If you are using this module in your product or your company, please add your product and/or company name in the [Wiki](https://github.com/lestrrat-go/jwx/wiki/Users)! It really helps keeping up our motivation. + +# Features + +* Complete coverage of JWA/JWE/JWK/JWS/JWT, not just JWT+minimum tool set. + * Supports JWS messages with multiple signatures, both compact and JSON serialization + * Supports JWS with detached payload + * Supports JWS with unencoded payload (RFC7797) + * Supports JWE messages with multiple recipients, both compact and JSON serialization + * Most operations work with either JWK or raw keys e.g. *rsa.PrivateKey, *ecdsa.PrivateKey, etc). +* Opinionated, but very uniform API. Everything is symmetric, and follows a standard convetion + * jws.Parse/Verify/Sign + * jwe.Parse/Encrypt/Decrypt + * Arguments are organized as explicit required paramters and optional WithXXXX() style options. +* Extra utilities + * `jwk.Cache` to always keep a JWKS up-to-date + +Some more in-depth discussion on why you might want to use this library over others +can be found in the [Description section](#description) + +# SYNOPSIS + + +```go +package examples_test + +import ( + "bytes" + "fmt" + "net/http" + "time" + + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwe" + "github.com/lestrrat-go/jwx/v2/jwk" + "github.com/lestrrat-go/jwx/v2/jws" + "github.com/lestrrat-go/jwx/v2/jwt" +) + +func ExampleJWX() { + // Parse, serialize, slice and dice JWKs! + privkey, err := jwk.ParseKey(jsonRSAPrivateKey) + if err != nil { + fmt.Printf("failed to parse JWK: %s\n", err) + return + } + + pubkey, err := jwk.PublicKeyOf(privkey) + if err != nil { + fmt.Printf("failed to get public key: %s\n", err) + return + } + + // Work with JWTs! + { + // Build a JWT! + tok, err := jwt.NewBuilder(). + Issuer(`github.com/lestrrat-go/jwx`). + IssuedAt(time.Now()). + Build() + if err != nil { + fmt.Printf("failed to build token: %s\n", err) + return + } + + // Sign a JWT! + signed, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, privkey)) + if err != nil { + fmt.Printf("failed to sign token: %s\n", err) + return + } + + // Verify a JWT! + { + verifiedToken, err := jwt.Parse(signed, jwt.WithKey(jwa.RS256, pubkey)) + if err != nil { + fmt.Printf("failed to verify JWS: %s\n", err) + return + } + _ = verifiedToken + } + + // Work with *http.Request! + { + req, err := http.NewRequest(http.MethodGet, `https://github.com/lestrrat-go/jwx`, nil) + req.Header.Set(`Authorization`, fmt.Sprintf(`Bearer %s`, signed)) + + verifiedToken, err := jwt.ParseRequest(req, jwt.WithKey(jwa.RS256, pubkey)) + if err != nil { + fmt.Printf("failed to verify token from HTTP request: %s\n", err) + return + } + _ = verifiedToken + } + } + + // Encrypt and Decrypt arbitrary payload with JWE! + { + encrypted, err := jwe.Encrypt(payloadLoremIpsum, jwe.WithKey(jwa.RSA_OAEP, jwkRSAPublicKey)) + if err != nil { + fmt.Printf("failed to encrypt payload: %s\n", err) + return + } + + decrypted, err := jwe.Decrypt(encrypted, jwe.WithKey(jwa.RSA_OAEP, jwkRSAPrivateKey)) + if err != nil { + fmt.Printf("failed to decrypt payload: %s\n", err) + return + } + + if !bytes.Equal(decrypted, payloadLoremIpsum) { + fmt.Printf("verified payload did not match\n") + return + } + } + + // Sign and Verify arbitrary payload with JWS! + { + signed, err := jws.Sign(payloadLoremIpsum, jws.WithKey(jwa.RS256, jwkRSAPrivateKey)) + if err != nil { + fmt.Printf("failed to sign payload: %s\n", err) + return + } + + verified, err := jws.Verify(signed, jws.WithKey(jwa.RS256, jwkRSAPublicKey)) + if err != nil { + fmt.Printf("failed to verify payload: %s\n", err) + return + } + + if !bytes.Equal(verified, payloadLoremIpsum) { + fmt.Printf("verified payload did not match\n") + return + } + } + // OUTPUT: +} +``` +source: [examples/jwx_readme_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwx_readme_example_test.go) + + +# How-to Documentation + +* [API documentation](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2) +* [How-to style documentation](./docs) +* [Runnable Examples](./examples) + +# Description + +This Go module implements JWA, JWE, JWK, JWS, and JWT. Please see the following table for the list of +available packages: + +| Package name | Notes | +|-----------------------------------------------------------|-------------------------------------------------| +| [jwt](https://github.com/lestrrat-go/jwx/tree/v2/jwt) | [RFC 7519](https://tools.ietf.org/html/rfc7519) | +| [jwk](https://github.com/lestrrat-go/jwx/tree/v2/jwk) | [RFC 7517](https://tools.ietf.org/html/rfc7517) + [RFC 7638](https://tools.ietf.org/html/rfc7638) | +| [jwa](https://github.com/lestrrat-go/jwx/tree/v2/jwa) | [RFC 7518](https://tools.ietf.org/html/rfc7518) | +| [jws](https://github.com/lestrrat-go/jwx/tree/v2/jws) | [RFC 7515](https://tools.ietf.org/html/rfc7515) + [RFC 7797](https://tools.ietf.org/html/rfc7797) | +| [jwe](https://github.com/lestrrat-go/jwx/tree/v2/jwe) | [RFC 7516](https://tools.ietf.org/html/rfc7516) | +## History + +My goal was to write a server that heavily uses JWK and JWT. At first glance +the libraries that already exist seemed sufficient, but soon I realized that + +1. To completely implement the protocols, I needed the entire JWT, JWK, JWS, JWE (and JWA, by necessity). +2. Most of the libraries that existed only deal with a subset of the various JWx specifications that were necessary to implement their specific needs + +For example, a certain library looks like it had most of JWS, JWE, JWK covered, but then it lacked the ability to include private claims in its JWT responses. Another library had support of all the private claims, but completely lacked in its flexibility to generate various different response formats. + +Because I was writing the server side (and the client side for testing), I needed the *entire* JOSE toolset to properly implement my server, **and** they needed to be *flexible* enough to fulfill the entire spec that I was writing. + +So here's `github.com/lestrrat-go/jwx/v2`. This library is extensible, customizable, and hopefully well organized to the point that it is easy for you to slice and dice it. + +## Why would I use this library? + +There are several other major Go modules that handle JWT and related data formats, +so why should you use this library? + +From a purely functional perspective, the only major difference is this: +Whereas most other projects only deal with what they seem necessary to handle +JWTs, this module handles the **_entire_** spectrum of JWS, JWE, JWK, and JWT. + +That is, if you need to not only parse JWTs, but also to control JWKs, or +if you need to handle payloads that are NOT JWTs, you should probably consider +using this module. You should also note that JWT is built _on top_ of those +other technologies. You simply cannot have a complete JWT package without +implementing the entirety of JWS/JWE/JWK, which this library does. + +Next, from an implementation perspective, this module differs significantly +from others in that it tries very hard to expose only the APIs, and not the +internal data. For example, individual JWT claims are not accessible through +struct field lookups. You need to use one of the getter methods. + +This is because this library takes the stance that the end user is fully capable +and even willing to shoot themselves on the foot when presented with a lax +API. By making sure that users do not have access to open structs, we can protect +users from doing silly things like creating _incomplete_ structs, or access the +structs concurrently without any protection. This structure also allows +us to put extra smarts in the structs, such as doing the right thing when +you want to parse / write custom fields (this module does not require the user +to specify alternate structs to parse objects with custom fields) + +In the end I think it comes down to your usage pattern, and priorities. +Some general guidelines that come to mind are: + +* If you want a single library to handle everything JWx, such as using JWE, JWK, JWS, handling [auto-refreshing JWKs](https://github.com/lestrrat-go/jwx/blob/v2/docs/04-jwk.md#auto-refreshing-remote-keys), use this module. +* If you want to honor all possible custom fields transparently, use this module. +* If you want a standardized clean API, use this module. + +Otherwise, feel free to choose something else. + +# Contributions + +## Issues + +For bug reports and feature requests, please try to follow the issue templates as much as possible. +For either bug reports or feature requests, failing tests are even better. + +## Pull Requests + +Please make sure to include tests that excercise the changes you made. + +If you are editing auto-generated files (those files with the `_gen.go` suffix, please make sure that you do the following: + +1. Edit the generator, not the generated files (e.g. internal/cmd/genreadfile/main.go) +2. Run `make generate` (or `go generate`) to generate the new code +3. Commit _both_ the generator _and_ the generated files + +## Discussions / Usage + +Please try [discussions](https://github.com/lestrrat-go/jwx/tree/v2/discussions) first. + +# Related Modules + +* [github.com/lestrrat-go/echo-middileware-jwx](https://github.com/lestrrat-go/echo-middleware-jwx) - Sample Echo middleware +* [github.com/jwx-go/crypto-signer/gcp](https://github.com/jwx-go/crypto-signer/tree/main/gcp) - GCP KMS wrapper that implements [`crypto.Signer`](https://pkg.go.dev/crypto#Signer) +* [github.com/jwx-go/crypto-signer/aws](https://github.com/jwx-go/crypto-signer/tree/main/aws) - AWS KMS wrapper that implements [`crypto.Signer`](https://pkg.go.dev/crypto#Signer) + +# Credits + +* Initial work on this library was generously sponsored by HDE Inc (https://www.hde.co.jp) +* Lots of code, especially JWE was initially taken from go-jose library (https://github.com/square/go-jose) +* Lots of individual contributors have helped this project over the years. Thank each and everyone of you very much. + diff --git a/vendor/github.com/lestrrat-go/jwx/v2/cert/cert.go b/vendor/github.com/lestrrat-go/jwx/v2/cert/cert.go new file mode 100644 index 0000000..1dfdec6 --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/cert/cert.go @@ -0,0 +1,48 @@ +package cert + +import ( + "crypto/x509" + stdlibb64 "encoding/base64" + "fmt" + "io" + + "github.com/lestrrat-go/jwx/v2/internal/base64" +) + +// Create is a wrapper around x509.CreateCertificate, but it additionally +// encodes it in base64 so that it can be easily added to `x5c` fields +func Create(rand io.Reader, template, parent *x509.Certificate, pub, priv interface{}) ([]byte, error) { + der, err := x509.CreateCertificate(rand, template, parent, pub, priv) + if err != nil { + return nil, fmt.Errorf(`failed to create x509 certificate: %w`, err) + } + return EncodeBase64(der) +} + +// EncodeBase64 is a utility function to encode ASN.1 DER certificates +// using base64 encoding. This operation is normally done by `pem.Encode` +// but since PEM would include the markers (`-----BEGIN`, and the like) +// while `x5c` fields do not need this, this function can be used to +// shave off a few lines +func EncodeBase64(der []byte) ([]byte, error) { + enc := stdlibb64.StdEncoding + dst := make([]byte, enc.EncodedLen(len(der))) + enc.Encode(dst, der) + return dst, nil +} + +// Parse is a utility function to decode a base64 encoded +// ASN.1 DER format certificate, and to parse the byte sequence. +// The certificate must be in PKIX format, and it must not contain PEM markers +func Parse(src []byte) (*x509.Certificate, error) { + dst, err := base64.Decode(src) + if err != nil { + return nil, fmt.Errorf(`failed to base64 decode the certificate: %w`, err) + } + + cert, err := x509.ParseCertificate(dst) + if err != nil { + return nil, fmt.Errorf(`failed to parse x509 certificate: %w`, err) + } + return cert, nil +} diff --git a/vendor/github.com/lestrrat-go/jwx/v2/cert/chain.go b/vendor/github.com/lestrrat-go/jwx/v2/cert/chain.go new file mode 100644 index 0000000..9396ce9 --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/cert/chain.go @@ -0,0 +1,78 @@ +package cert + +import ( + "bytes" + "encoding/json" + "fmt" +) + +// Chain represents a certificate chain as used in the `x5c` field of +// various objects within JOSE. +// +// It stores the certificates as a list of base64 encoded []byte +// sequence. By definition these values must PKIX encoded. +type Chain struct { + certificates [][]byte +} + +func (cc Chain) MarshalJSON() ([]byte, error) { + var buf bytes.Buffer + buf.WriteByte('[') + for i, cert := range cc.certificates { + if i > 0 { + buf.WriteByte(',') + } + buf.WriteByte('"') + buf.Write(cert) + buf.WriteByte('"') + } + buf.WriteByte(']') + return buf.Bytes(), nil +} + +func (cc *Chain) UnmarshalJSON(data []byte) error { + var tmp []string + if err := json.Unmarshal(data, &tmp); err != nil { + return fmt.Errorf(`failed to unmarshal certificate chain: %w`, err) + } + + certs := make([][]byte, len(tmp)) + for i, cert := range tmp { + certs[i] = []byte(cert) + } + cc.certificates = certs + return nil +} + +// Get returns the n-th ASN.1 DER + base64 encoded certificate +// stored. `false` will be returned in the second argument if +// the corresponding index is out of range. +func (cc *Chain) Get(index int) ([]byte, bool) { + if index < 0 || index > len(cc.certificates) { + return nil, false + } + + return cc.certificates[index], true +} + +// Len returns the number of certificates stored in this Chain +func (cc *Chain) Len() int { + return len(cc.certificates) +} + +var pemStart = []byte("----- BEGIN CERTIFICATE -----") +var pemEnd = []byte("----- END CERTIFICATE -----") + +func (cc *Chain) AddString(der string) error { + return cc.Add([]byte(der)) +} + +func (cc *Chain) Add(der []byte) error { + // We're going to be nice and remove marker lines if they + // give it to us + der = bytes.TrimPrefix(der, pemStart) + der = bytes.TrimSuffix(der, pemEnd) + der = bytes.TrimSpace(der) + cc.certificates = append(cc.certificates, der) + return nil +} diff --git a/vendor/github.com/lestrrat-go/jwx/v2/codecov.yml b/vendor/github.com/lestrrat-go/jwx/v2/codecov.yml new file mode 100644 index 0000000..130effd --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/codecov.yml @@ -0,0 +1,2 @@ +codecov: + allow_coverage_offsets: true diff --git a/vendor/github.com/lestrrat-go/jwx/format.go b/vendor/github.com/lestrrat-go/jwx/v2/format.go similarity index 100% rename from vendor/github.com/lestrrat-go/jwx/format.go rename to vendor/github.com/lestrrat-go/jwx/v2/format.go diff --git a/vendor/github.com/lestrrat-go/jwx/formatkind_string_gen.go b/vendor/github.com/lestrrat-go/jwx/v2/formatkind_string_gen.go similarity index 100% rename from vendor/github.com/lestrrat-go/jwx/formatkind_string_gen.go rename to vendor/github.com/lestrrat-go/jwx/v2/formatkind_string_gen.go diff --git a/vendor/github.com/lestrrat-go/jwx/internal/base64/base64.go b/vendor/github.com/lestrrat-go/jwx/v2/internal/base64/base64.go similarity index 93% rename from vendor/github.com/lestrrat-go/jwx/internal/base64/base64.go rename to vendor/github.com/lestrrat-go/jwx/v2/internal/base64/base64.go index f0a52c3..bc494bc 100644 --- a/vendor/github.com/lestrrat-go/jwx/internal/base64/base64.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/internal/base64/base64.go @@ -4,8 +4,7 @@ import ( "bytes" "encoding/base64" "encoding/binary" - - "github.com/pkg/errors" + "fmt" ) func Encode(src []byte) []byte { @@ -56,7 +55,7 @@ func Decode(src []byte) ([]byte, error) { dst := make([]byte, enc.DecodedLen(len(src))) n, err := enc.Decode(dst, src) if err != nil { - return nil, errors.Wrap(err, `failed to decode source`) + return nil, fmt.Errorf(`failed to decode source: %w`, err) } return dst[:n], nil } diff --git a/vendor/github.com/lestrrat-go/jwx/internal/ecutil/ecutil.go b/vendor/github.com/lestrrat-go/jwx/v2/internal/ecutil/ecutil.go similarity index 98% rename from vendor/github.com/lestrrat-go/jwx/internal/ecutil/ecutil.go rename to vendor/github.com/lestrrat-go/jwx/v2/internal/ecutil/ecutil.go index 1286669..e70f816 100644 --- a/vendor/github.com/lestrrat-go/jwx/internal/ecutil/ecutil.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/internal/ecutil/ecutil.go @@ -7,7 +7,7 @@ import ( "math/big" "sync" - "github.com/lestrrat-go/jwx/jwa" + "github.com/lestrrat-go/jwx/v2/jwa" ) // data for available curves. Some algorithms may be compiled in/out diff --git a/vendor/github.com/lestrrat-go/jwx/internal/iter/mapiter.go b/vendor/github.com/lestrrat-go/jwx/v2/internal/iter/mapiter.go similarity index 91% rename from vendor/github.com/lestrrat-go/jwx/internal/iter/mapiter.go rename to vendor/github.com/lestrrat-go/jwx/v2/internal/iter/mapiter.go index f7ddc11..c98fd46 100644 --- a/vendor/github.com/lestrrat-go/jwx/internal/iter/mapiter.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/internal/iter/mapiter.go @@ -2,9 +2,9 @@ package iter import ( "context" + "fmt" "github.com/lestrrat-go/iter/mapiter" - "github.com/pkg/errors" ) // MapVisitor is a specialized visitor for our purposes. @@ -30,7 +30,7 @@ func WalkMap(ctx context.Context, src mapiter.Source, visitor MapVisitor) error func AsMap(ctx context.Context, src mapiter.Source) (map[string]interface{}, error) { var m map[string]interface{} if err := mapiter.AsMap(ctx, src, &m); err != nil { - return nil, errors.Wrap(err, `mapiter.AsMap failed`) + return nil, fmt.Errorf(`mapiter.AsMap failed: %w`, err) } return m, nil } diff --git a/vendor/github.com/lestrrat-go/jwx/internal/json/goccy.go b/vendor/github.com/lestrrat-go/jwx/v2/internal/json/goccy.go similarity index 100% rename from vendor/github.com/lestrrat-go/jwx/internal/json/goccy.go rename to vendor/github.com/lestrrat-go/jwx/v2/internal/json/goccy.go diff --git a/vendor/github.com/lestrrat-go/jwx/internal/json/json.go b/vendor/github.com/lestrrat-go/jwx/v2/internal/json/json.go similarity index 86% rename from vendor/github.com/lestrrat-go/jwx/internal/json/json.go rename to vendor/github.com/lestrrat-go/jwx/v2/internal/json/json.go index 285b7e2..e8d6a2d 100644 --- a/vendor/github.com/lestrrat-go/jwx/internal/json/json.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/internal/json/json.go @@ -2,11 +2,12 @@ package json import ( "bytes" + "fmt" + "os" "sync" "sync/atomic" - "github.com/lestrrat-go/jwx/internal/base64" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/internal/base64" ) var muGlobalConfig sync.RWMutex @@ -29,12 +30,12 @@ func Unmarshal(b []byte, v interface{}) error { func AssignNextBytesToken(dst *[]byte, dec *Decoder) error { var val string if err := dec.Decode(&val); err != nil { - return errors.Wrap(err, `error reading next value`) + return fmt.Errorf(`error reading next value: %w`, err) } buf, err := base64.DecodeString(val) if err != nil { - return errors.Errorf(`expected base64 encoded []byte (%T)`, val) + return fmt.Errorf(`expected base64 encoded []byte (%T)`, val) } *dst = buf return nil @@ -43,7 +44,7 @@ func AssignNextBytesToken(dst *[]byte, dec *Decoder) error { func ReadNextStringToken(dec *Decoder) (string, error) { var val string if err := dec.Decode(&val); err != nil { - return "", errors.Wrap(err, `error reading next value`) + return "", fmt.Errorf(`error reading next value: %w`, err) } return val, nil } @@ -103,3 +104,10 @@ func NewDecodeCtx(r *Registry) DecodeCtx { func (dc *decodeCtx) Registry() *Registry { return dc.registry } + +func Dump(v interface{}) { + enc := NewEncoder(os.Stdout) + enc.SetIndent("", " ") + //nolint:errchkjson + _ = enc.Encode(v) +} diff --git a/vendor/github.com/lestrrat-go/jwx/internal/json/registry.go b/vendor/github.com/lestrrat-go/jwx/v2/internal/json/registry.go similarity index 84% rename from vendor/github.com/lestrrat-go/jwx/internal/json/registry.go rename to vendor/github.com/lestrrat-go/jwx/v2/internal/json/registry.go index 9f1800e..4830e86 100644 --- a/vendor/github.com/lestrrat-go/jwx/internal/json/registry.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/internal/json/registry.go @@ -1,10 +1,9 @@ package json import ( + "fmt" "reflect" "sync" - - "github.com/pkg/errors" ) type Registry struct { @@ -40,14 +39,14 @@ func (r *Registry) Decode(dec *Decoder, name string) (interface{}, error) { if typ, ok := r.data[name]; ok { ptr := reflect.New(typ).Interface() if err := dec.Decode(ptr); err != nil { - return nil, errors.Wrapf(err, `failed to decode field %s`, name) + return nil, fmt.Errorf(`failed to decode field %s: %w`, name, err) } return reflect.ValueOf(ptr).Elem().Interface(), nil } var decoded interface{} if err := dec.Decode(&decoded); err != nil { - return nil, errors.Wrapf(err, `failed to decode field %s`, name) + return nil, fmt.Errorf(`failed to decode field %s: %w`, name, err) } return decoded, nil } diff --git a/vendor/github.com/lestrrat-go/jwx/internal/json/stdlib.go b/vendor/github.com/lestrrat-go/jwx/v2/internal/json/stdlib.go similarity index 100% rename from vendor/github.com/lestrrat-go/jwx/internal/json/stdlib.go rename to vendor/github.com/lestrrat-go/jwx/v2/internal/json/stdlib.go diff --git a/vendor/github.com/lestrrat-go/jwx/internal/keyconv/keyconv.go b/vendor/github.com/lestrrat-go/jwx/v2/internal/keyconv/keyconv.go similarity index 72% rename from vendor/github.com/lestrrat-go/jwx/internal/keyconv/keyconv.go rename to vendor/github.com/lestrrat-go/jwx/v2/internal/keyconv/keyconv.go index 62b31de..807da1d 100644 --- a/vendor/github.com/lestrrat-go/jwx/internal/keyconv/keyconv.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/internal/keyconv/keyconv.go @@ -4,10 +4,10 @@ import ( "crypto" "crypto/ecdsa" "crypto/rsa" + "fmt" "github.com/lestrrat-go/blackmagic" - "github.com/lestrrat-go/jwx/jwk" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/jwk" "golang.org/x/crypto/ed25519" ) @@ -18,7 +18,7 @@ func RSAPrivateKey(dst, src interface{}) error { if jwkKey, ok := src.(jwk.Key); ok { var raw rsa.PrivateKey if err := jwkKey.Raw(&raw); err != nil { - return errors.Wrapf(err, `failed to produce rsa.PrivateKey from %T`, src) + return fmt.Errorf(`failed to produce rsa.PrivateKey from %T: %w`, src, err) } src = &raw } @@ -30,7 +30,7 @@ func RSAPrivateKey(dst, src interface{}) error { case *rsa.PrivateKey: ptr = src default: - return errors.Errorf(`expected rsa.PrivateKey or *rsa.PrivateKey, got %T`, src) + return fmt.Errorf(`expected rsa.PrivateKey or *rsa.PrivateKey, got %T`, src) } return blackmagic.AssignIfCompatible(dst, ptr) @@ -43,7 +43,7 @@ func RSAPublicKey(dst, src interface{}) error { if jwkKey, ok := src.(jwk.Key); ok { var raw rsa.PublicKey if err := jwkKey.Raw(&raw); err != nil { - return errors.Wrapf(err, `failed to produce rsa.PublicKey from %T`, src) + return fmt.Errorf(`failed to produce rsa.PublicKey from %T: %w`, src, err) } src = &raw } @@ -55,7 +55,7 @@ func RSAPublicKey(dst, src interface{}) error { case *rsa.PublicKey: ptr = src default: - return errors.Errorf(`expected rsa.PublicKey or *rsa.PublicKey, got %T`, src) + return fmt.Errorf(`expected rsa.PublicKey or *rsa.PublicKey, got %T`, src) } return blackmagic.AssignIfCompatible(dst, ptr) @@ -67,7 +67,7 @@ func ECDSAPrivateKey(dst, src interface{}) error { if jwkKey, ok := src.(jwk.Key); ok { var raw ecdsa.PrivateKey if err := jwkKey.Raw(&raw); err != nil { - return errors.Wrapf(err, `failed to produce ecdsa.PrivateKey from %T`, src) + return fmt.Errorf(`failed to produce ecdsa.PrivateKey from %T: %w`, src, err) } src = &raw } @@ -79,7 +79,7 @@ func ECDSAPrivateKey(dst, src interface{}) error { case *ecdsa.PrivateKey: ptr = src default: - return errors.Errorf(`expected ecdsa.PrivateKey or *ecdsa.PrivateKey, got %T`, src) + return fmt.Errorf(`expected ecdsa.PrivateKey or *ecdsa.PrivateKey, got %T`, src) } return blackmagic.AssignIfCompatible(dst, ptr) } @@ -90,7 +90,7 @@ func ECDSAPublicKey(dst, src interface{}) error { if jwkKey, ok := src.(jwk.Key); ok { var raw ecdsa.PublicKey if err := jwkKey.Raw(&raw); err != nil { - return errors.Wrapf(err, `failed to produce ecdsa.PublicKey from %T`, src) + return fmt.Errorf(`failed to produce ecdsa.PublicKey from %T: %w`, src, err) } src = &raw } @@ -102,7 +102,7 @@ func ECDSAPublicKey(dst, src interface{}) error { case *ecdsa.PublicKey: ptr = src default: - return errors.Errorf(`expected ecdsa.PublicKey or *ecdsa.PublicKey, got %T`, src) + return fmt.Errorf(`expected ecdsa.PublicKey or *ecdsa.PublicKey, got %T`, src) } return blackmagic.AssignIfCompatible(dst, ptr) } @@ -111,13 +111,13 @@ func ByteSliceKey(dst, src interface{}) error { if jwkKey, ok := src.(jwk.Key); ok { var raw []byte if err := jwkKey.Raw(&raw); err != nil { - return errors.Wrapf(err, `failed to produce []byte from %T`, src) + return fmt.Errorf(`failed to produce []byte from %T: %w`, src, err) } src = raw } if _, ok := src.([]byte); !ok { - return errors.Errorf(`expected []byte, got %T`, src) + return fmt.Errorf(`expected []byte, got %T`, src) } return blackmagic.AssignIfCompatible(dst, src) } @@ -126,7 +126,7 @@ func Ed25519PrivateKey(dst, src interface{}) error { if jwkKey, ok := src.(jwk.Key); ok { var raw ed25519.PrivateKey if err := jwkKey.Raw(&raw); err != nil { - return errors.Wrapf(err, `failed to produce ed25519.PrivateKey from %T`, src) + return fmt.Errorf(`failed to produce ed25519.PrivateKey from %T: %w`, src, err) } src = &raw } @@ -138,7 +138,7 @@ func Ed25519PrivateKey(dst, src interface{}) error { case *ed25519.PrivateKey: ptr = src default: - return errors.Errorf(`expected ed25519.PrivateKey or *ed25519.PrivateKey, got %T`, src) + return fmt.Errorf(`expected ed25519.PrivateKey or *ed25519.PrivateKey, got %T`, src) } return blackmagic.AssignIfCompatible(dst, ptr) } @@ -147,7 +147,7 @@ func Ed25519PublicKey(dst, src interface{}) error { if jwkKey, ok := src.(jwk.Key); ok { var raw ed25519.PublicKey if err := jwkKey.Raw(&raw); err != nil { - return errors.Wrapf(err, `failed to produce ed25519.PublicKey from %T`, src) + return fmt.Errorf(`failed to produce ed25519.PublicKey from %T: %w`, src, err) } src = &raw } @@ -161,17 +161,17 @@ func Ed25519PublicKey(dst, src interface{}) error { case *crypto.PublicKey: tmp, ok := (*src).(ed25519.PublicKey) if !ok { - return errors.New(`failed to retrieve ed25519.PublicKey out of *crypto.PublicKey`) + return fmt.Errorf(`failed to retrieve ed25519.PublicKey out of *crypto.PublicKey`) } ptr = &tmp case crypto.PublicKey: tmp, ok := src.(ed25519.PublicKey) if !ok { - return errors.New(`failed to retrieve ed25519.PublicKey out of crypto.PublicKey`) + return fmt.Errorf(`failed to retrieve ed25519.PublicKey out of crypto.PublicKey`) } ptr = &tmp default: - return errors.Errorf(`expected ed25519.PublicKey or *ed25519.PublicKey, got %T`, src) + return fmt.Errorf(`expected ed25519.PublicKey or *ed25519.PublicKey, got %T`, src) } return blackmagic.AssignIfCompatible(dst, ptr) } diff --git a/vendor/github.com/lestrrat-go/jwx/internal/pool/pool.go b/vendor/github.com/lestrrat-go/jwx/v2/internal/pool/pool.go similarity index 100% rename from vendor/github.com/lestrrat-go/jwx/internal/pool/pool.go rename to vendor/github.com/lestrrat-go/jwx/v2/internal/pool/pool.go diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jwa/README.md b/vendor/github.com/lestrrat-go/jwx/v2/jwa/README.md new file mode 100644 index 0000000..d62f292 --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwa/README.md @@ -0,0 +1,3 @@ +# JWA [![Go Reference](https://pkg.go.dev/badge/github.com/lestrrat-go/jwx/v2/jwa.svg)](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwa) + +Package [github.com/lestrrat-go/jwx/v2/jwa](./jwa) defines the various algorithm described in [RFC7518](https://tools.ietf.org/html/rfc7518) diff --git a/vendor/github.com/lestrrat-go/jwx/jwa/compression_gen.go b/vendor/github.com/lestrrat-go/jwx/v2/jwa/compression_gen.go similarity index 91% rename from vendor/github.com/lestrrat-go/jwx/jwa/compression_gen.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwa/compression_gen.go index 5eeb796..1649b4a 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwa/compression_gen.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwa/compression_gen.go @@ -6,8 +6,6 @@ import ( "fmt" "sort" "sync" - - "github.com/pkg/errors" ) // CompressionAlgorithm represents the compression algorithms as described in https://tools.ietf.org/html/rfc7518#section-7.3 @@ -55,12 +53,12 @@ func (v *CompressionAlgorithm) Accept(value interface{}) error { case string: s = x default: - return errors.Errorf(`invalid type for jwa.CompressionAlgorithm: %T`, value) + return fmt.Errorf(`invalid type for jwa.CompressionAlgorithm: %T`, value) } tmp = CompressionAlgorithm(s) } if _, ok := allCompressionAlgorithms[tmp]; !ok { - return errors.Errorf(`invalid jwa.CompressionAlgorithm value`) + return fmt.Errorf(`invalid jwa.CompressionAlgorithm value`) } *v = tmp diff --git a/vendor/github.com/lestrrat-go/jwx/jwa/content_encryption_gen.go b/vendor/github.com/lestrrat-go/jwx/v2/jwa/content_encryption_gen.go similarity index 93% rename from vendor/github.com/lestrrat-go/jwx/jwa/content_encryption_gen.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwa/content_encryption_gen.go index 8b2eb95..fe0e062 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwa/content_encryption_gen.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwa/content_encryption_gen.go @@ -6,8 +6,6 @@ import ( "fmt" "sort" "sync" - - "github.com/pkg/errors" ) // ContentEncryptionAlgorithm represents the various encryption algorithms as described in https://tools.ietf.org/html/rfc7518#section-5 @@ -63,12 +61,12 @@ func (v *ContentEncryptionAlgorithm) Accept(value interface{}) error { case string: s = x default: - return errors.Errorf(`invalid type for jwa.ContentEncryptionAlgorithm: %T`, value) + return fmt.Errorf(`invalid type for jwa.ContentEncryptionAlgorithm: %T`, value) } tmp = ContentEncryptionAlgorithm(s) } if _, ok := allContentEncryptionAlgorithms[tmp]; !ok { - return errors.Errorf(`invalid jwa.ContentEncryptionAlgorithm value`) + return fmt.Errorf(`invalid jwa.ContentEncryptionAlgorithm value`) } *v = tmp diff --git a/vendor/github.com/lestrrat-go/jwx/jwa/elliptic_gen.go b/vendor/github.com/lestrrat-go/jwx/v2/jwa/elliptic_gen.go similarity index 92% rename from vendor/github.com/lestrrat-go/jwx/jwa/elliptic_gen.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwa/elliptic_gen.go index acc045c..e899086 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwa/elliptic_gen.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwa/elliptic_gen.go @@ -6,8 +6,6 @@ import ( "fmt" "sort" "sync" - - "github.com/pkg/errors" ) // EllipticCurveAlgorithm represents the algorithms used for EC keys @@ -66,12 +64,12 @@ func (v *EllipticCurveAlgorithm) Accept(value interface{}) error { case string: s = x default: - return errors.Errorf(`invalid type for jwa.EllipticCurveAlgorithm: %T`, value) + return fmt.Errorf(`invalid type for jwa.EllipticCurveAlgorithm: %T`, value) } tmp = EllipticCurveAlgorithm(s) } if _, ok := allEllipticCurveAlgorithms[tmp]; !ok { - return errors.Errorf(`invalid jwa.EllipticCurveAlgorithm value`) + return fmt.Errorf(`invalid jwa.EllipticCurveAlgorithm value`) } *v = tmp diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jwa/jwa.go b/vendor/github.com/lestrrat-go/jwx/v2/jwa/jwa.go new file mode 100644 index 0000000..f9ce38e --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwa/jwa.go @@ -0,0 +1,61 @@ +//go:generate ../tools/cmd/genjwa.sh + +// Package jwa defines the various algorithm described in https://tools.ietf.org/html/rfc7518 +package jwa + +import "fmt" + +// KeyAlgorithm is a workaround for jwk.Key being able to contain different +// types of algorithms in its `alg` field. +// +// Previously the storage for the `alg` field was represented as a string, +// but this caused some users to wonder why the field was not typed appropriately +// like other fields. +// +// Ideally we would like to keep track of Signature Algorithms and +// Content Encryption Algorithms separately, and force the APIs to +// type-check at compile time, but this allows users to pass a value from a +// jwk.Key directly +type KeyAlgorithm interface { + String() string +} + +// InvalidKeyAlgorithm represents an algorithm that the library is not aware of. +type InvalidKeyAlgorithm string + +func (s InvalidKeyAlgorithm) String() string { + return string(s) +} + +func (InvalidKeyAlgorithm) Accept(_ interface{}) error { + return fmt.Errorf(`jwa.InvalidKeyAlgorithm does not support Accept() method calls`) +} + +// KeyAlgorithmFrom takes either a string, `jwa.SignatureAlgorithm` or `jwa.KeyEncryptionAlgorithm` +// and returns a `jwa.KeyAlgorithm`. +// +// If the value cannot be handled, it returns an `jwa.InvalidKeyAlgorithm` +// object instead of returning an error. This design choice was made to allow +// users to directly pass the return value to functions such as `jws.Sign()` +func KeyAlgorithmFrom(v interface{}) KeyAlgorithm { + switch v := v.(type) { + case SignatureAlgorithm: + return v + case KeyEncryptionAlgorithm: + return v + case string: + var salg SignatureAlgorithm + if err := salg.Accept(v); err == nil { + return salg + } + + var kealg KeyEncryptionAlgorithm + if err := kealg.Accept(v); err == nil { + return kealg + } + + return InvalidKeyAlgorithm(v) + default: + return InvalidKeyAlgorithm(fmt.Sprintf("%s", v)) + } +} diff --git a/vendor/github.com/lestrrat-go/jwx/jwa/key_encryption_gen.go b/vendor/github.com/lestrrat-go/jwx/v2/jwa/key_encryption_gen.go similarity index 95% rename from vendor/github.com/lestrrat-go/jwx/jwa/key_encryption_gen.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwa/key_encryption_gen.go index cecf859..80a97d9 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwa/key_encryption_gen.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwa/key_encryption_gen.go @@ -6,8 +6,6 @@ import ( "fmt" "sort" "sync" - - "github.com/pkg/errors" ) // KeyEncryptionAlgorithm represents the various encryption algorithms as described in https://tools.ietf.org/html/rfc7518#section-4.1 @@ -85,12 +83,12 @@ func (v *KeyEncryptionAlgorithm) Accept(value interface{}) error { case string: s = x default: - return errors.Errorf(`invalid type for jwa.KeyEncryptionAlgorithm: %T`, value) + return fmt.Errorf(`invalid type for jwa.KeyEncryptionAlgorithm: %T`, value) } tmp = KeyEncryptionAlgorithm(s) } if _, ok := allKeyEncryptionAlgorithms[tmp]; !ok { - return errors.Errorf(`invalid jwa.KeyEncryptionAlgorithm value`) + return fmt.Errorf(`invalid jwa.KeyEncryptionAlgorithm value`) } *v = tmp diff --git a/vendor/github.com/lestrrat-go/jwx/jwa/key_type_gen.go b/vendor/github.com/lestrrat-go/jwx/v2/jwa/key_type_gen.go similarity index 91% rename from vendor/github.com/lestrrat-go/jwx/jwa/key_type_gen.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwa/key_type_gen.go index 869afd4..a55da78 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwa/key_type_gen.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwa/key_type_gen.go @@ -6,8 +6,6 @@ import ( "fmt" "sort" "sync" - - "github.com/pkg/errors" ) // KeyType represents the key type ("kty") that are supported @@ -60,12 +58,12 @@ func (v *KeyType) Accept(value interface{}) error { case string: s = x default: - return errors.Errorf(`invalid type for jwa.KeyType: %T`, value) + return fmt.Errorf(`invalid type for jwa.KeyType: %T`, value) } tmp = KeyType(s) } if _, ok := allKeyTypes[tmp]; !ok { - return errors.Errorf(`invalid jwa.KeyType value`) + return fmt.Errorf(`invalid jwa.KeyType value`) } *v = tmp diff --git a/vendor/github.com/lestrrat-go/jwx/jwa/secp2561k.go b/vendor/github.com/lestrrat-go/jwx/v2/jwa/secp2561k.go similarity index 100% rename from vendor/github.com/lestrrat-go/jwx/jwa/secp2561k.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwa/secp2561k.go diff --git a/vendor/github.com/lestrrat-go/jwx/jwa/signature_gen.go b/vendor/github.com/lestrrat-go/jwx/v2/jwa/signature_gen.go similarity index 94% rename from vendor/github.com/lestrrat-go/jwx/jwa/signature_gen.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwa/signature_gen.go index e430503..40cb017 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwa/signature_gen.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwa/signature_gen.go @@ -6,8 +6,6 @@ import ( "fmt" "sort" "sync" - - "github.com/pkg/errors" ) // SignatureAlgorithm represents the various signature algorithms as described in https://tools.ietf.org/html/rfc7518#section-3.1 @@ -81,12 +79,12 @@ func (v *SignatureAlgorithm) Accept(value interface{}) error { case string: s = x default: - return errors.Errorf(`invalid type for jwa.SignatureAlgorithm: %T`, value) + return fmt.Errorf(`invalid type for jwa.SignatureAlgorithm: %T`, value) } tmp = SignatureAlgorithm(s) } if _, ok := allSignatureAlgorithms[tmp]; !ok { - return errors.Errorf(`invalid jwa.SignatureAlgorithm value`) + return fmt.Errorf(`invalid jwa.SignatureAlgorithm value`) } *v = tmp diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/README.md b/vendor/github.com/lestrrat-go/jwx/v2/jwe/README.md similarity index 92% rename from vendor/github.com/lestrrat-go/jwx/jwe/README.md rename to vendor/github.com/lestrrat-go/jwx/v2/jwe/README.md index 216c533..542172d 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwe/README.md +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwe/README.md @@ -1,4 +1,4 @@ -# JWE [![Go Reference](https://pkg.go.dev/badge/github.com/lestrrat-go/jwx/jwe.svg)](https://pkg.go.dev/github.com/lestrrat-go/jwx/jwe) +# JWE [![Go Reference](https://pkg.go.dev/badge/github.com/lestrrat-go/jwx/v2/jwe.svg)](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwe) Package jwe implements JWE as described in [RFC7516](https://tools.ietf.org/html/rfc7516) @@ -59,7 +59,7 @@ func ExampleEncrypt() { payload := []byte("Lorem Ipsum") - encrypted, err := jwe.Encrypt(payload, jwa.RSA1_5, &privkey.PublicKey, jwa.A128CBC_HS256, jwa.NoCompress) + encrypted, err := jwe.Encrypt(payload, jwe.WithKey(jwa.RSA1_5, &privkey.PublicKey), jwe.WithContentEncryption(jwa.A128CBC_HS256)) if err != nil { log.Printf("failed to encrypt payload: %s", err) return @@ -79,7 +79,7 @@ func ExampleDecrypt() { return } - decrypted, err := jwe.Decrypt(encrypted, jwa.RSA1_5, privkey) + decrypted, err := jwe.Decrypt(encrypted, jwe.WithKey(jwa.RSA1_5, privkey)) if err != nil { log.Printf("failed to decrypt: %s", err) return diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jwe/compress.go b/vendor/github.com/lestrrat-go/jwx/v2/jwe/compress.go new file mode 100644 index 0000000..0beba4a --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwe/compress.go @@ -0,0 +1,36 @@ +package jwe + +import ( + "bytes" + "compress/flate" + "fmt" + "io" + + "github.com/lestrrat-go/jwx/v2/internal/pool" +) + +func uncompress(plaintext []byte) ([]byte, error) { + return io.ReadAll(flate.NewReader(bytes.NewReader(plaintext))) +} + +func compress(plaintext []byte) ([]byte, error) { + buf := pool.GetBytesBuffer() + defer pool.ReleaseBytesBuffer(buf) + + w, _ := flate.NewWriter(buf, 1) + in := plaintext + for len(in) > 0 { + n, err := w.Write(in) + if err != nil { + return nil, fmt.Errorf(`failed to write to compression writer: %w`, err) + } + in = in[n:] + } + if err := w.Close(); err != nil { + return nil, fmt.Errorf(`failed to close compression writer: %w`, err) + } + + ret := make([]byte, buf.Len()) + copy(ret, buf.Bytes()) + return ret, nil +} diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/decrypt.go b/vendor/github.com/lestrrat-go/jwx/v2/jwe/decrypt.go similarity index 58% rename from vendor/github.com/lestrrat-go/jwx/jwe/decrypt.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwe/decrypt.go index 4fc2ef3..a3443e2 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwe/decrypt.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwe/decrypt.go @@ -7,23 +7,23 @@ import ( "crypto/rsa" "crypto/sha256" "crypto/sha512" + "fmt" "hash" "golang.org/x/crypto/pbkdf2" - "github.com/lestrrat-go/jwx/internal/keyconv" - "github.com/lestrrat-go/jwx/jwa" - "github.com/lestrrat-go/jwx/jwe/internal/cipher" - "github.com/lestrrat-go/jwx/jwe/internal/content_crypt" - "github.com/lestrrat-go/jwx/jwe/internal/keyenc" - "github.com/lestrrat-go/jwx/x25519" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/internal/keyconv" + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwe/internal/cipher" + "github.com/lestrrat-go/jwx/v2/jwe/internal/content_crypt" + "github.com/lestrrat-go/jwx/v2/jwe/internal/keyenc" + "github.com/lestrrat-go/jwx/v2/x25519" ) -// Decrypter is responsible for taking various components to decrypt a message. +// decrypter is responsible for taking various components to decrypt a message. // its operation is not concurrency safe. You must provide locking yourself //nolint:govet -type Decrypter struct { +type decrypter struct { aad []byte apu []byte apv []byte @@ -41,7 +41,7 @@ type Decrypter struct { keycount int } -// NewDecrypter Creates a new Decrypter instance. You must supply the +// newDecrypter Creates a new Decrypter instance. You must supply the // rest of parameters via their respective setter methods before // calling Decrypt(). // @@ -49,103 +49,103 @@ type Decrypter struct { // *rsa.PrivateKey, instead of jwk.Key) // // You should consider this object immutable once you assign values to it. -func NewDecrypter(keyalg jwa.KeyEncryptionAlgorithm, ctalg jwa.ContentEncryptionAlgorithm, privkey interface{}) *Decrypter { - return &Decrypter{ +func newDecrypter(keyalg jwa.KeyEncryptionAlgorithm, ctalg jwa.ContentEncryptionAlgorithm, privkey interface{}) *decrypter { + return &decrypter{ ctalg: ctalg, keyalg: keyalg, privkey: privkey, } } -func (d *Decrypter) AgreementPartyUInfo(apu []byte) *Decrypter { +func (d *decrypter) AgreementPartyUInfo(apu []byte) *decrypter { d.apu = apu return d } -func (d *Decrypter) AgreementPartyVInfo(apv []byte) *Decrypter { +func (d *decrypter) AgreementPartyVInfo(apv []byte) *decrypter { d.apv = apv return d } -func (d *Decrypter) AuthenticatedData(aad []byte) *Decrypter { +func (d *decrypter) AuthenticatedData(aad []byte) *decrypter { d.aad = aad return d } -func (d *Decrypter) ComputedAuthenticatedData(aad []byte) *Decrypter { +func (d *decrypter) ComputedAuthenticatedData(aad []byte) *decrypter { d.computedAad = aad return d } -func (d *Decrypter) ContentEncryptionAlgorithm(ctalg jwa.ContentEncryptionAlgorithm) *Decrypter { +func (d *decrypter) ContentEncryptionAlgorithm(ctalg jwa.ContentEncryptionAlgorithm) *decrypter { d.ctalg = ctalg return d } -func (d *Decrypter) InitializationVector(iv []byte) *Decrypter { +func (d *decrypter) InitializationVector(iv []byte) *decrypter { d.iv = iv return d } -func (d *Decrypter) KeyCount(keycount int) *Decrypter { +func (d *decrypter) KeyCount(keycount int) *decrypter { d.keycount = keycount return d } -func (d *Decrypter) KeyInitializationVector(keyiv []byte) *Decrypter { +func (d *decrypter) KeyInitializationVector(keyiv []byte) *decrypter { d.keyiv = keyiv return d } -func (d *Decrypter) KeySalt(keysalt []byte) *Decrypter { +func (d *decrypter) KeySalt(keysalt []byte) *decrypter { d.keysalt = keysalt return d } -func (d *Decrypter) KeyTag(keytag []byte) *Decrypter { +func (d *decrypter) KeyTag(keytag []byte) *decrypter { d.keytag = keytag return d } // PublicKey sets the public key to be used in decoding EC based encryptions. // The key must be in its "raw" format (i.e. *ecdsa.PublicKey, instead of jwk.Key) -func (d *Decrypter) PublicKey(pubkey interface{}) *Decrypter { +func (d *decrypter) PublicKey(pubkey interface{}) *decrypter { d.pubkey = pubkey return d } -func (d *Decrypter) Tag(tag []byte) *Decrypter { +func (d *decrypter) Tag(tag []byte) *decrypter { d.tag = tag return d } -func (d *Decrypter) ContentCipher() (content_crypt.Cipher, error) { +func (d *decrypter) ContentCipher() (content_crypt.Cipher, error) { if d.cipher == nil { switch d.ctalg { case jwa.A128GCM, jwa.A192GCM, jwa.A256GCM, jwa.A128CBC_HS256, jwa.A192CBC_HS384, jwa.A256CBC_HS512: cipher, err := cipher.NewAES(d.ctalg) if err != nil { - return nil, errors.Wrapf(err, `failed to build content cipher for %s`, d.ctalg) + return nil, fmt.Errorf(`failed to build content cipher for %s: %w`, d.ctalg, err) } d.cipher = cipher default: - return nil, errors.Errorf(`invalid content cipher algorithm (%s)`, d.ctalg) + return nil, fmt.Errorf(`invalid content cipher algorithm (%s)`, d.ctalg) } } return d.cipher, nil } -func (d *Decrypter) Decrypt(recipientKey, ciphertext []byte) (plaintext []byte, err error) { +func (d *decrypter) Decrypt(recipientKey, ciphertext []byte) (plaintext []byte, err error) { cek, keyerr := d.DecryptKey(recipientKey) if keyerr != nil { - err = errors.Wrap(keyerr, `failed to decrypt key`) + err = fmt.Errorf(`failed to decrypt key: %w`, keyerr) return } cipher, ciphererr := d.ContentCipher() if ciphererr != nil { - err = errors.Wrap(ciphererr, `failed to fetch content crypt cipher`) + err = fmt.Errorf(`failed to fetch content crypt cipher: %w`, ciphererr) return } @@ -156,14 +156,14 @@ func (d *Decrypter) Decrypt(recipientKey, ciphertext []byte) (plaintext []byte, plaintext, err = cipher.Decrypt(cek, d.iv, ciphertext, d.tag, computedAad) if err != nil { - err = errors.Wrap(err, `failed to decrypt payload`) + err = fmt.Errorf(`failed to decrypt payload: %w`, err) return } return plaintext, nil } -func (d *Decrypter) decryptSymmetricKey(recipientKey, cek []byte) ([]byte, error) { +func (d *decrypter) decryptSymmetricKey(recipientKey, cek []byte) ([]byte, error) { switch d.keyalg { case jwa.DIRECT: return cek, nil @@ -189,48 +189,48 @@ func (d *Decrypter) decryptSymmetricKey(recipientKey, cek []byte) ([]byte, error case jwa.A128KW, jwa.A192KW, jwa.A256KW: block, err := aes.NewCipher(cek) if err != nil { - return nil, errors.Wrap(err, `failed to create new AES cipher`) + return nil, fmt.Errorf(`failed to create new AES cipher: %w`, err) } jek, err := keyenc.Unwrap(block, recipientKey) if err != nil { - return nil, errors.Wrap(err, `failed to unwrap key`) + return nil, fmt.Errorf(`failed to unwrap key: %w`, err) } return jek, nil case jwa.A128GCMKW, jwa.A192GCMKW, jwa.A256GCMKW: if len(d.keyiv) != 12 { - return nil, errors.Errorf("GCM requires 96-bit iv, got %d", len(d.keyiv)*8) + return nil, fmt.Errorf("GCM requires 96-bit iv, got %d", len(d.keyiv)*8) } if len(d.keytag) != 16 { - return nil, errors.Errorf("GCM requires 128-bit tag, got %d", len(d.keytag)*8) + return nil, fmt.Errorf("GCM requires 128-bit tag, got %d", len(d.keytag)*8) } block, err := aes.NewCipher(cek) if err != nil { - return nil, errors.Wrap(err, `failed to create new AES cipher`) + return nil, fmt.Errorf(`failed to create new AES cipher: %w`, err) } aesgcm, err := cryptocipher.NewGCM(block) if err != nil { - return nil, errors.Wrap(err, `failed to create new GCM wrap`) + return nil, fmt.Errorf(`failed to create new GCM wrap: %w`, err) } ciphertext := recipientKey[:] ciphertext = append(ciphertext, d.keytag...) jek, err := aesgcm.Open(nil, d.keyiv, ciphertext, nil) if err != nil { - return nil, errors.Wrap(err, `failed to decode key`) + return nil, fmt.Errorf(`failed to decode key: %w`, err) } return jek, nil default: - return nil, errors.Errorf("decrypt key: unsupported algorithm %s", d.keyalg) + return nil, fmt.Errorf("decrypt key: unsupported algorithm %s", d.keyalg) } } -func (d *Decrypter) DecryptKey(recipientKey []byte) (cek []byte, err error) { +func (d *decrypter) DecryptKey(recipientKey []byte) (cek []byte, err error) { if d.keyalg.IsSymmetric() { var ok bool cek, ok = d.privkey.([]byte) if !ok { - return nil, errors.Errorf("decrypt key: []byte is required as the key to build %s key decrypter (got %T)", d.keyalg, d.privkey) + return nil, fmt.Errorf("decrypt key: []byte is required as the key to build %s key decrypter (got %T)", d.keyalg, d.privkey) } return d.decryptSymmetricKey(recipientKey, cek) @@ -238,42 +238,42 @@ func (d *Decrypter) DecryptKey(recipientKey []byte) (cek []byte, err error) { k, err := d.BuildKeyDecrypter() if err != nil { - return nil, errors.Wrap(err, `failed to build key decrypter`) + return nil, fmt.Errorf(`failed to build key decrypter: %w`, err) } cek, err = k.Decrypt(recipientKey) if err != nil { - return nil, errors.Wrap(err, `failed to decrypt key`) + return nil, fmt.Errorf(`failed to decrypt key: %w`, err) } return cek, nil } -func (d *Decrypter) BuildKeyDecrypter() (keyenc.Decrypter, error) { +func (d *decrypter) BuildKeyDecrypter() (keyenc.Decrypter, error) { cipher, err := d.ContentCipher() if err != nil { - return nil, errors.Wrap(err, `failed to fetch content crypt cipher`) + return nil, fmt.Errorf(`failed to fetch content crypt cipher: %w`, err) } switch alg := d.keyalg; alg { case jwa.RSA1_5: var privkey rsa.PrivateKey if err := keyconv.RSAPrivateKey(&privkey, d.privkey); err != nil { - return nil, errors.Wrapf(err, "*rsa.PrivateKey is required as the key to build %s key decrypter", alg) + return nil, fmt.Errorf(`*rsa.PrivateKey is required as the key to build %s key decrypter: %w`, alg, err) } return keyenc.NewRSAPKCS15Decrypt(alg, &privkey, cipher.KeySize()/2), nil case jwa.RSA_OAEP, jwa.RSA_OAEP_256: var privkey rsa.PrivateKey if err := keyconv.RSAPrivateKey(&privkey, d.privkey); err != nil { - return nil, errors.Wrapf(err, "*rsa.PrivateKey is required as the key to build %s key decrypter", alg) + return nil, fmt.Errorf(`*rsa.PrivateKey is required as the key to build %s key decrypter: %w`, alg, err) } return keyenc.NewRSAOAEPDecrypt(alg, &privkey) case jwa.A128KW, jwa.A192KW, jwa.A256KW: sharedkey, ok := d.privkey.([]byte) if !ok { - return nil, errors.Errorf("[]byte is required as the key to build %s key decrypter", alg) + return nil, fmt.Errorf("[]byte is required as the key to build %s key decrypter", alg) } return keyenc.NewAES(alg, sharedkey) @@ -284,17 +284,17 @@ func (d *Decrypter) BuildKeyDecrypter() (keyenc.Decrypter, error) { default: var pubkey ecdsa.PublicKey if err := keyconv.ECDSAPublicKey(&pubkey, d.pubkey); err != nil { - return nil, errors.Wrapf(err, "*ecdsa.PublicKey is required as the key to build %s key decrypter", alg) + return nil, fmt.Errorf(`*ecdsa.PublicKey is required as the key to build %s key decrypter: %w`, alg, err) } var privkey ecdsa.PrivateKey if err := keyconv.ECDSAPrivateKey(&privkey, d.privkey); err != nil { - return nil, errors.Wrapf(err, "*ecdsa.PrivateKey is required as the key to build %s key decrypter", alg) + return nil, fmt.Errorf(`*ecdsa.PrivateKey is required as the key to build %s key decrypter: %w`, alg, err) } return keyenc.NewECDHESDecrypt(alg, d.ctalg, &pubkey, d.apu, d.apv, &privkey), nil } default: - return nil, errors.Errorf(`unsupported algorithm for key decryption (%s)`, alg) + return nil, fmt.Errorf(`unsupported algorithm for key decryption (%s)`, alg) } } diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/headers.go b/vendor/github.com/lestrrat-go/jwx/v2/jwe/headers.go similarity index 77% rename from vendor/github.com/lestrrat-go/jwx/jwe/headers.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwe/headers.go index eacbfda..1145591 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwe/headers.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwe/headers.go @@ -2,13 +2,13 @@ package jwe import ( "context" + "fmt" - "github.com/lestrrat-go/jwx/internal/base64" - "github.com/lestrrat-go/jwx/internal/json" + "github.com/lestrrat-go/jwx/v2/internal/base64" + "github.com/lestrrat-go/jwx/v2/internal/json" "github.com/lestrrat-go/iter/mapiter" - "github.com/lestrrat-go/jwx/internal/iter" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/internal/iter" ) type isZeroer interface { @@ -64,7 +64,7 @@ func (h *stdHeaders) AsMap(ctx context.Context) (map[string]interface{}, error) func (h *stdHeaders) Clone(ctx context.Context) (Headers, error) { dst := NewHeaders() if err := h.Copy(ctx, dst); err != nil { - return nil, errors.Wrap(err, `failed to copy header contents to new object`) + return nil, fmt.Errorf(`failed to copy header contents to new object: %w`, err) } return dst, nil } @@ -74,7 +74,7 @@ func (h *stdHeaders) Copy(ctx context.Context, dst Headers) error { //nolint:forcetypeassert key := pair.Key.(string) if err := dst.Set(key, pair.Value); err != nil { - return errors.Wrapf(err, `failed to set header %q`, key) + return fmt.Errorf(`failed to set header %q: %w`, key, err) } } return nil @@ -85,13 +85,13 @@ func (h *stdHeaders) Merge(ctx context.Context, h2 Headers) (Headers, error) { if h != nil { if err := h.Copy(ctx, h3); err != nil { - return nil, errors.Wrap(err, `failed to copy headers from receiver`) + return nil, fmt.Errorf(`failed to copy headers from receiver: %w`, err) } } if h2 != nil { if err := h2.Copy(ctx, h3); err != nil { - return nil, errors.Wrap(err, `failed to copy headers from argument`) + return nil, fmt.Errorf(`failed to copy headers from argument: %w`, err) } } @@ -101,7 +101,7 @@ func (h *stdHeaders) Merge(ctx context.Context, h2 Headers) (Headers, error) { func (h *stdHeaders) Encode() ([]byte, error) { buf, err := json.Marshal(h) if err != nil { - return nil, errors.Wrap(err, `failed to marshal headers to JSON prior to encoding`) + return nil, fmt.Errorf(`failed to marshal headers to JSON prior to encoding: %w`, err) } return base64.Encode(buf), nil @@ -111,11 +111,11 @@ func (h *stdHeaders) Decode(buf []byte) error { // base64 json string -> json object representation of header decoded, err := base64.Decode(buf) if err != nil { - return errors.Wrap(err, "failed to unmarshal base64 encoded buffer") + return fmt.Errorf(`failed to unmarshal base64 encoded buffer: %w`, err) } if err := json.Unmarshal(decoded, h); err != nil { - return errors.Wrap(err, "failed to unmarshal buffer") + return fmt.Errorf(`failed to unmarshal buffer: %w`, err) } return nil diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/headers_gen.go b/vendor/github.com/lestrrat-go/jwx/v2/jwe/headers_gen.go similarity index 81% rename from vendor/github.com/lestrrat-go/jwx/jwe/headers_gen.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwe/headers_gen.go index d486502..61ce413 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwe/headers_gen.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwe/headers_gen.go @@ -5,15 +5,16 @@ package jwe import ( "bytes" "context" + "fmt" "sort" "sync" - "github.com/lestrrat-go/jwx/internal/base64" - "github.com/lestrrat-go/jwx/internal/json" - "github.com/lestrrat-go/jwx/internal/pool" - "github.com/lestrrat-go/jwx/jwa" - "github.com/lestrrat-go/jwx/jwk" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/cert" + "github.com/lestrrat-go/jwx/v2/internal/base64" + "github.com/lestrrat-go/jwx/v2/internal/json" + "github.com/lestrrat-go/jwx/v2/internal/pool" + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwk" ) const ( @@ -51,7 +52,7 @@ type Headers interface { JWKSetURL() string KeyID() string Type() string - X509CertChain() []string + X509CertChain() *cert.Chain X509CertThumbprint() string X509CertThumbprintS256() string X509URL() string @@ -86,7 +87,7 @@ type stdHeaders struct { jwkSetURL *string keyID *string typ *string - x509CertChain []string + x509CertChain *cert.Chain x509CertThumbprint *string x509CertThumbprintS256 *string x509URL *string @@ -194,7 +195,7 @@ func (h *stdHeaders) Type() string { return *(h.typ) } -func (h *stdHeaders) X509CertChain() []string { +func (h *stdHeaders) X509CertChain() *cert.Chain { h.mu.RLock() defer h.mu.RUnlock() return h.x509CertChain @@ -394,100 +395,100 @@ func (h *stdHeaders) setNoLock(name string, value interface{}) error { h.agreementPartyUInfo = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, AgreementPartyUInfoKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, AgreementPartyUInfoKey, value) case AgreementPartyVInfoKey: if v, ok := value.([]byte); ok { h.agreementPartyVInfo = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, AgreementPartyVInfoKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, AgreementPartyVInfoKey, value) case AlgorithmKey: if v, ok := value.(jwa.KeyEncryptionAlgorithm); ok { h.algorithm = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, AlgorithmKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, AlgorithmKey, value) case CompressionKey: if v, ok := value.(jwa.CompressionAlgorithm); ok { h.compression = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, CompressionKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, CompressionKey, value) case ContentEncryptionKey: if v, ok := value.(jwa.ContentEncryptionAlgorithm); ok { if v == "" { - return errors.New(`"enc" field cannot be an empty string`) + return fmt.Errorf(`"enc" field cannot be an empty string`) } h.contentEncryption = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, ContentEncryptionKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, ContentEncryptionKey, value) case ContentTypeKey: if v, ok := value.(string); ok { h.contentType = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, ContentTypeKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, ContentTypeKey, value) case CriticalKey: if v, ok := value.([]string); ok { h.critical = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, CriticalKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, CriticalKey, value) case EphemeralPublicKeyKey: if v, ok := value.(jwk.Key); ok { h.ephemeralPublicKey = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, EphemeralPublicKeyKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, EphemeralPublicKeyKey, value) case JWKKey: if v, ok := value.(jwk.Key); ok { h.jwk = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, JWKKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, JWKKey, value) case JWKSetURLKey: if v, ok := value.(string); ok { h.jwkSetURL = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, JWKSetURLKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, JWKSetURLKey, value) case KeyIDKey: if v, ok := value.(string); ok { h.keyID = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, KeyIDKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, KeyIDKey, value) case TypeKey: if v, ok := value.(string); ok { h.typ = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, TypeKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, TypeKey, value) case X509CertChainKey: - if v, ok := value.([]string); ok { + if v, ok := value.(*cert.Chain); ok { h.x509CertChain = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509CertChainKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509CertChainKey, value) case X509CertThumbprintKey: if v, ok := value.(string); ok { h.x509CertThumbprint = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509CertThumbprintKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509CertThumbprintKey, value) case X509CertThumbprintS256Key: if v, ok := value.(string); ok { h.x509CertThumbprintS256 = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509CertThumbprintS256Key, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509CertThumbprintS256Key, value) case X509URLKey: if v, ok := value.(string); ok { h.x509URL = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509URLKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509URLKey, value) default: if h.privateParams == nil { h.privateParams = map[string]interface{}{} @@ -561,7 +562,7 @@ LOOP: for { tok, err := dec.Token() if err != nil { - return errors.Wrap(err, `error reading token`) + return fmt.Errorf(`error reading token: %w`, err) } switch tok := tok.(type) { case json.Delim: @@ -570,95 +571,95 @@ LOOP: if tok == '}' { // End of object break LOOP } else if tok != '{' { - return errors.Errorf(`expected '{', but got '%c'`, tok) + return fmt.Errorf(`expected '{', but got '%c'`, tok) } case string: // Objects can only have string keys switch tok { case AgreementPartyUInfoKey: if err := json.AssignNextBytesToken(&h.agreementPartyUInfo, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, AgreementPartyUInfoKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, AgreementPartyUInfoKey, err) } case AgreementPartyVInfoKey: if err := json.AssignNextBytesToken(&h.agreementPartyVInfo, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, AgreementPartyVInfoKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, AgreementPartyVInfoKey, err) } case AlgorithmKey: var decoded jwa.KeyEncryptionAlgorithm if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, AlgorithmKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, AlgorithmKey, err) } h.algorithm = &decoded case CompressionKey: var decoded jwa.CompressionAlgorithm if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, CompressionKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, CompressionKey, err) } h.compression = &decoded case ContentEncryptionKey: var decoded jwa.ContentEncryptionAlgorithm if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, ContentEncryptionKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, ContentEncryptionKey, err) } h.contentEncryption = &decoded case ContentTypeKey: if err := json.AssignNextStringToken(&h.contentType, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, ContentTypeKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, ContentTypeKey, err) } case CriticalKey: var decoded []string if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, CriticalKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, CriticalKey, err) } h.critical = decoded case EphemeralPublicKeyKey: var buf json.RawMessage if err := dec.Decode(&buf); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, EphemeralPublicKeyKey) + return fmt.Errorf(`failed to decode value for key %s:%w`, EphemeralPublicKeyKey, err) } key, err := jwk.ParseKey(buf) if err != nil { - return errors.Wrapf(err, `failed to parse JWK for key %s`, EphemeralPublicKeyKey) + return fmt.Errorf(`failed to parse JWK for key %s: %w`, EphemeralPublicKeyKey, err) } h.ephemeralPublicKey = key case JWKKey: var buf json.RawMessage if err := dec.Decode(&buf); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, JWKKey) + return fmt.Errorf(`failed to decode value for key %s:%w`, JWKKey, err) } key, err := jwk.ParseKey(buf) if err != nil { - return errors.Wrapf(err, `failed to parse JWK for key %s`, JWKKey) + return fmt.Errorf(`failed to parse JWK for key %s: %w`, JWKKey, err) } h.jwk = key case JWKSetURLKey: if err := json.AssignNextStringToken(&h.jwkSetURL, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, JWKSetURLKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, JWKSetURLKey, err) } case KeyIDKey: if err := json.AssignNextStringToken(&h.keyID, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, KeyIDKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, KeyIDKey, err) } case TypeKey: if err := json.AssignNextStringToken(&h.typ, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, TypeKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, TypeKey, err) } case X509CertChainKey: - var decoded []string + var decoded cert.Chain if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertChainKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertChainKey, err) } - h.x509CertChain = decoded + h.x509CertChain = &decoded case X509CertThumbprintKey: if err := json.AssignNextStringToken(&h.x509CertThumbprint, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertThumbprintKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertThumbprintKey, err) } case X509CertThumbprintS256Key: if err := json.AssignNextStringToken(&h.x509CertThumbprintS256, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertThumbprintS256Key) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertThumbprintS256Key, err) } case X509URLKey: if err := json.AssignNextStringToken(&h.x509URL, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509URLKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509URLKey, err) } default: decoded, err := registry.Decode(dec, tok) @@ -668,7 +669,7 @@ LOOP: h.setNoLock(tok, decoded) } default: - return errors.Errorf(`invalid token %T`, tok) + return fmt.Errorf(`invalid token %T`, tok) } } return nil @@ -702,7 +703,7 @@ func (h stdHeaders) MarshalJSON() ([]byte, error) { buf.WriteRune('"') default: if err := enc.Encode(v); err != nil { - errors.Errorf(`failed to encode value for field %s`, f) + return nil, fmt.Errorf(`failed to encode value for field %s`, f) } buf.Truncate(buf.Len() - 1) } diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jwe/interface.go b/vendor/github.com/lestrrat-go/jwx/v2/jwe/interface.go new file mode 100644 index 0000000..99ecb40 --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwe/interface.go @@ -0,0 +1,159 @@ +package jwe + +import ( + "github.com/lestrrat-go/iter/mapiter" + "github.com/lestrrat-go/jwx/v2/internal/iter" + "github.com/lestrrat-go/jwx/v2/jwe/internal/keygen" +) + +// Recipient holds the encrypted key and hints to decrypt the key +type Recipient interface { + Headers() Headers + EncryptedKey() []byte + SetHeaders(Headers) error + SetEncryptedKey([]byte) error +} + +type stdRecipient struct { + // Comments on each field are taken from https://datatracker.ietf.org/doc/html/rfc7516 + // + // header + // The "header" member MUST be present and contain the value JWE Per- + // Recipient Unprotected Header when the JWE Per-Recipient + // Unprotected Header value is non-empty; otherwise, it MUST be + // absent. This value is represented as an unencoded JSON object, + // rather than as a string. These Header Parameter values are not + // integrity protected. + // + // At least one of the "header", "protected", and "unprotected" members + // MUST be present so that "alg" and "enc" Header Parameter values are + // conveyed for each recipient computation. + // + // JWX note: see Message.unprotectedHeaders + headers Headers + + // encrypted_key + // The "encrypted_key" member MUST be present and contain the value + // BASE64URL(JWE Encrypted Key) when the JWE Encrypted Key value is + // non-empty; otherwise, it MUST be absent. + encryptedKey []byte +} + +// Message contains the entire encrypted JWE message. You should not +// expect to use Message for anything other than inspecting the +// state of an encrypted message. This is because encryption is +// highly context sensitive, and once we parse the original payload +// into an object, we may not always be able to recreate the exact +// context in which the encryption happened. +// +// For example, it is totally valid for if the protected header's +// integrity was calculated using a non-standard line breaks: +// +// {"a dummy": +// "protected header"} +// +// Once parsed, though, we can only serialize the protected header as: +// +// {"a dummy":"protected header"} +// +// which would obviously result in a contradicting integrity value +// if we tried to re-calculate it from a parsed message. +//nolint:govet +type Message struct { + // Comments on each field are taken from https://datatracker.ietf.org/doc/html/rfc7516 + // + // protected + // The "protected" member MUST be present and contain the value + // BASE64URL(UTF8(JWE Protected Header)) when the JWE Protected + // Header value is non-empty; otherwise, it MUST be absent. These + // Header Parameter values are integrity protected. + protectedHeaders Headers + + // unprotected + // The "unprotected" member MUST be present and contain the value JWE + // Shared Unprotected Header when the JWE Shared Unprotected Header + // value is non-empty; otherwise, it MUST be absent. This value is + // represented as an unencoded JSON object, rather than as a string. + // These Header Parameter values are not integrity protected. + // + // JWX note: This field is NOT mutually exclusive with per-recipient + // headers within the implmentation because... it's too much work. + // It is _never_ populated (we don't provide a way to do this) upon encryption. + // When decrypting, if present its values are always merged with + // per-recipient header. + unprotectedHeaders Headers + + // iv + // The "iv" member MUST be present and contain the value + // BASE64URL(JWE Initialization Vector) when the JWE Initialization + // Vector value is non-empty; otherwise, it MUST be absent. + initializationVector []byte + + // aad + // The "aad" member MUST be present and contain the value + // BASE64URL(JWE AAD)) when the JWE AAD value is non-empty; + // otherwise, it MUST be absent. A JWE AAD value can be included to + // supply a base64url-encoded value to be integrity protected but not + // encrypted. + authenticatedData []byte + + // ciphertext + // The "ciphertext" member MUST be present and contain the value + // BASE64URL(JWE Ciphertext). + cipherText []byte + + // tag + // The "tag" member MUST be present and contain the value + // BASE64URL(JWE Authentication Tag) when the JWE Authentication Tag + // value is non-empty; otherwise, it MUST be absent. + tag []byte + + // recipients + // The "recipients" member value MUST be an array of JSON objects. + // Each object contains information specific to a single recipient. + // This member MUST be present with exactly one array element per + // recipient, even if some or all of the array element values are the + // empty JSON object "{}" (which can happen when all Header Parameter + // values are shared between all recipients and when no encrypted key + // is used, such as when doing Direct Encryption). + // + // Some Header Parameters, including the "alg" parameter, can be shared + // among all recipient computations. Header Parameters in the JWE + // Protected Header and JWE Shared Unprotected Header values are shared + // among all recipients. + // + // The Header Parameter values used when creating or validating per- + // recipient ciphertext and Authentication Tag values are the union of + // the three sets of Header Parameter values that may be present: (1) + // the JWE Protected Header represented in the "protected" member, (2) + // the JWE Shared Unprotected Header represented in the "unprotected" + // member, and (3) the JWE Per-Recipient Unprotected Header represented + // in the "header" member of the recipient's array element. The union + // of these sets of Header Parameters comprises the JOSE Header. The + // Header Parameter names in the three locations MUST be disjoint. + recipients []Recipient + + // TODO: Additional members can be present in both the JSON objects defined + // above; if not understood by implementations encountering them, they + // MUST be ignored. + // privateParams map[string]interface{} + + // These two fields below are not available for the public consumers of this object. + // rawProtectedHeaders stores the original protected header buffer + rawProtectedHeaders []byte + // storeProtectedHeaders is a hint to be used in UnmarshalJSON(). + // When this flag is true, UnmarshalJSON() will populate the + // rawProtectedHeaders field + storeProtectedHeaders bool +} + +// populater is an interface for things that may modify the +// JWE header. e.g. ByteWithECPrivateKey +type populater interface { + Populate(keygen.Setter) error +} + +type Visitor = iter.MapVisitor +type VisitorFunc = iter.MapVisitorFunc +type HeaderPair = mapiter.Pair +type Iterator = mapiter.Iterator diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/internal/aescbc/aescbc.go b/vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/aescbc/aescbc.go similarity index 89% rename from vendor/github.com/lestrrat-go/jwx/jwe/internal/aescbc/aescbc.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/aescbc/aescbc.go index 9163688..d38245f 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwe/internal/aescbc/aescbc.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/aescbc/aescbc.go @@ -9,8 +9,6 @@ import ( "encoding/binary" "fmt" "hash" - - "github.com/pkg/errors" ) const ( @@ -38,7 +36,7 @@ func unpad(buf []byte, n int) ([]byte, error) { // First, `buf` must be a multiple of `n` if rem != 0 { - return nil, errors.Errorf("input buffer must be multiple of block size %d", n) + return nil, fmt.Errorf("input buffer must be multiple of block size %d", n) } // Find the last byte, which is the encoded padding @@ -60,7 +58,7 @@ func unpad(buf []byte, n int) ([]byte, error) { // we also don't check against lbuf-i in range, because we have established expected <= lbuf for i := 1; i < expected; i++ { if buf[lbuf-i] != last { - return nil, errors.New(`invalid padding`) + return nil, fmt.Errorf(`invalid padding`) } } @@ -84,7 +82,7 @@ func New(key []byte, f BlockCipherFunc) (hmac *Hmac, err error) { bc, ciphererr := f(ekey) if ciphererr != nil { - err = errors.Wrap(ciphererr, `failed to execute block cipher function`) + err = fmt.Errorf(`failed to execute block cipher function: %w`, ciphererr) return } @@ -97,7 +95,7 @@ func New(key []byte, f BlockCipherFunc) (hmac *Hmac, err error) { case 32: hfunc = sha512.New default: - return nil, errors.Errorf("unsupported key size %d", keysize) + return nil, fmt.Errorf("unsupported key size %d", keysize) } return &Hmac{ @@ -133,7 +131,7 @@ func (c Hmac) ComputeAuthTag(aad, nonce, ciphertext []byte) ([]byte, error) { h := hmac.New(c.hash, c.integrityKey) if _, err := h.Write(buf); err != nil { - return nil, errors.Wrap(err, "failed to write ComputeAuthTag using Hmac") + return nil, fmt.Errorf(`failed to write ComputeAuthTag using Hmac: %w`, err) } s := h.Sum(nil) return s[:c.tagsize], nil @@ -182,7 +180,7 @@ func (c Hmac) Seal(dst, nonce, plaintext, data []byte) []byte { // Open fulfills the crypto.AEAD interface func (c Hmac) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { if len(ciphertext) < c.keysize { - return nil, errors.New("invalid ciphertext (too short)") + return nil, fmt.Errorf(`invalid ciphertext (too short)`) } tagOffset := len(ciphertext) - c.tagsize @@ -198,11 +196,11 @@ func (c Hmac) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { expectedTag, err := c.ComputeAuthTag(data, nonce, ciphertext[:tagOffset]) if err != nil { - return nil, errors.Wrap(err, `failed to compute auth tag`) + return nil, fmt.Errorf(`failed to compute auth tag: %w`, err) } if subtle.ConstantTimeCompare(expectedTag, tag) != 1 { - return nil, errors.New("invalid ciphertext (tag mismatch)") + return nil, fmt.Errorf(`invalid ciphertext (tag mismatch)`) } cbc := cipher.NewCBCDecrypter(c.blockCipher, nonce) @@ -211,7 +209,7 @@ func (c Hmac) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { plaintext, err := unpad(buf, c.blockCipher.BlockSize()) if err != nil { - return nil, errors.Wrap(err, `failed to generate plaintext from decrypted blocks`) + return nil, fmt.Errorf(`failed to generate plaintext from decrypted blocks: %w`, err) } ret := ensureSize(dst, len(plaintext)) out := ret[len(dst):] diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/internal/cipher/cipher.go b/vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/cipher/cipher.go similarity index 72% rename from vendor/github.com/lestrrat-go/jwx/jwe/internal/cipher/cipher.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/cipher/cipher.go index 4581f64..23f437e 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwe/internal/cipher/cipher.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/cipher/cipher.go @@ -5,10 +5,9 @@ import ( "crypto/cipher" "fmt" - "github.com/lestrrat-go/jwx/jwa" - "github.com/lestrrat-go/jwx/jwe/internal/aescbc" - "github.com/lestrrat-go/jwx/jwe/internal/keygen" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwe/internal/aescbc" + "github.com/lestrrat-go/jwx/v2/jwe/internal/keygen" ) var gcm = &gcmFetcher{} @@ -17,12 +16,12 @@ var cbc = &cbcFetcher{} func (f gcmFetcher) Fetch(key []byte) (cipher.AEAD, error) { aescipher, err := aes.NewCipher(key) if err != nil { - return nil, errors.Wrap(err, "cipher: failed to create AES cipher for GCM") + return nil, fmt.Errorf(`cipher: failed to create AES cipher for GCM: %w`, err) } aead, err := cipher.NewGCM(aescipher) if err != nil { - return nil, errors.Wrap(err, `failed to create GCM for cipher`) + return nil, fmt.Errorf(`failed to create GCM for cipher: %w`, err) } return aead, nil } @@ -30,7 +29,7 @@ func (f gcmFetcher) Fetch(key []byte) (cipher.AEAD, error) { func (f cbcFetcher) Fetch(key []byte) (cipher.AEAD, error) { aead, err := aescbc.New(key, aes.NewCipher) if err != nil { - return nil, errors.Wrap(err, "cipher: failed to create AES cipher for CBC") + return nil, fmt.Errorf(`cipher: failed to create AES cipher for CBC: %w`, err) } return aead, nil } @@ -73,7 +72,7 @@ func NewAES(alg jwa.ContentEncryptionAlgorithm) (*AesContentCipher, error) { keysize = tagsize * 2 fetcher = cbc default: - return nil, errors.Errorf("failed to create AES content cipher: invalid algorithm (%s)", alg) + return nil, fmt.Errorf("failed to create AES content cipher: invalid algorithm (%s)", alg) } return &AesContentCipher{ @@ -83,11 +82,11 @@ func NewAES(alg jwa.ContentEncryptionAlgorithm) (*AesContentCipher, error) { }, nil } -func (c AesContentCipher) Encrypt(cek, plaintext, aad []byte) (iv, ciphertext, tag []byte, err error) { +func (c AesContentCipher) Encrypt(cek, plaintext, aad []byte) (iv, ciphertxt, tag []byte, err error) { var aead cipher.AEAD aead, err = c.fetch.Fetch(cek) if err != nil { - return nil, nil, nil, errors.Wrap(err, "failed to fetch AEAD") + return nil, nil, nil, fmt.Errorf(`failed to fetch AEAD: %w`, err) } // Seal may panic (argh!), so protect ourselves from that @@ -97,9 +96,9 @@ func (c AesContentCipher) Encrypt(cek, plaintext, aad []byte) (iv, ciphertext, t case error: err = e default: - err = errors.Errorf("%s", e) + err = fmt.Errorf("%s", e) } - err = errors.Wrap(err, "failed to encrypt") + err = fmt.Errorf(`failed to encrypt: %w`, err) } }() @@ -110,7 +109,7 @@ func (c AesContentCipher) Encrypt(cek, plaintext, aad []byte) (iv, ciphertext, t bs, err = c.NonceGenerator.Generate() } if err != nil { - return nil, nil, nil, errors.Wrap(err, "failed to generate nonce") + return nil, nil, nil, fmt.Errorf(`failed to generate nonce: %w`, err) } iv = bs.Bytes() @@ -122,8 +121,8 @@ func (c AesContentCipher) Encrypt(cek, plaintext, aad []byte) (iv, ciphertext, t } tag = combined[tagoffset:] - ciphertext = make([]byte, tagoffset) - copy(ciphertext, combined[:tagoffset]) + ciphertxt = make([]byte, tagoffset) + copy(ciphertxt, combined[:tagoffset]) return } @@ -131,7 +130,7 @@ func (c AesContentCipher) Encrypt(cek, plaintext, aad []byte) (iv, ciphertext, t func (c AesContentCipher) Decrypt(cek, iv, ciphertxt, tag, aad []byte) (plaintext []byte, err error) { aead, err := c.fetch.Fetch(cek) if err != nil { - return nil, errors.Wrap(err, "failed to fetch AEAD data") + return nil, fmt.Errorf(`failed to fetch AEAD data: %w`, err) } // Open may panic (argh!), so protect ourselves from that @@ -141,9 +140,9 @@ func (c AesContentCipher) Decrypt(cek, iv, ciphertxt, tag, aad []byte) (plaintex case error: err = e default: - err = errors.Errorf("%s", e) + err = fmt.Errorf(`%s`, e) } - err = errors.Wrap(err, "failed to decrypt") + err = fmt.Errorf(`failed to decrypt: %w`, err) return } }() @@ -154,7 +153,7 @@ func (c AesContentCipher) Decrypt(cek, iv, ciphertxt, tag, aad []byte) (plaintex buf, aeaderr := aead.Open(nil, iv, combined, aad) if aeaderr != nil { - err = errors.Wrap(aeaderr, `aead.Open failed`) + err = fmt.Errorf(`aead.Open failed: %w`, aeaderr) return } plaintext = buf diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/internal/cipher/interface.go b/vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/cipher/interface.go similarity index 92% rename from vendor/github.com/lestrrat-go/jwx/jwe/internal/cipher/interface.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/cipher/interface.go index 8af8202..88b5007 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwe/internal/cipher/interface.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/cipher/interface.go @@ -3,7 +3,7 @@ package cipher import ( "crypto/cipher" - "github.com/lestrrat-go/jwx/jwe/internal/keygen" + "github.com/lestrrat-go/jwx/v2/jwe/internal/keygen" ) const ( diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/internal/concatkdf/concatkdf.go b/vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/concatkdf/concatkdf.go similarity index 84% rename from vendor/github.com/lestrrat-go/jwx/jwe/internal/concatkdf/concatkdf.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/concatkdf/concatkdf.go index 777c1a5..3691830 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwe/internal/concatkdf/concatkdf.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/concatkdf/concatkdf.go @@ -3,8 +3,7 @@ package concatkdf import ( "crypto" "encoding/binary" - - "github.com/pkg/errors" + "fmt" ) type KDF struct { @@ -48,13 +47,13 @@ func (k *KDF) Read(out []byte) (int, error) { h.Reset() if err := binary.Write(h, binary.BigEndian, round); err != nil { - return 0, errors.Wrap(err, "failed to write round using kdf") + return 0, fmt.Errorf(`failed to write round using kdf: %w`, err) } if _, err := h.Write(k.z); err != nil { - return 0, errors.Wrap(err, "failed to write z using kdf") + return 0, fmt.Errorf(`failed to write z using kdf: %w`, err) } if _, err := h.Write(k.otherinfo); err != nil { - return 0, errors.Wrap(err, "failed to write other info using kdf") + return 0, fmt.Errorf(`failed to write other info using kdf: %w`, err) } k.buf = append(k.buf, h.Sum(nil)...) diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/internal/content_crypt/content_crypt.go b/vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/content_crypt/content_crypt.go similarity index 74% rename from vendor/github.com/lestrrat-go/jwx/jwe/internal/content_crypt/content_crypt.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/content_crypt/content_crypt.go index b83af42..722e848 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwe/internal/content_crypt/content_crypt.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/content_crypt/content_crypt.go @@ -1,9 +1,10 @@ package content_crypt //nolint:golint import ( - "github.com/lestrrat-go/jwx/jwa" - "github.com/lestrrat-go/jwx/jwe/internal/cipher" - "github.com/pkg/errors" + "fmt" + + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwe/internal/cipher" ) func (c Generic) Algorithm() jwa.ContentEncryptionAlgorithm { @@ -13,7 +14,7 @@ func (c Generic) Algorithm() jwa.ContentEncryptionAlgorithm { func (c Generic) Encrypt(cek, plaintext, aad []byte) ([]byte, []byte, []byte, error) { iv, encrypted, tag, err := c.cipher.Encrypt(cek, plaintext, aad) if err != nil { - return nil, nil, nil, errors.Wrap(err, `failed to crypt content`) + return nil, nil, nil, fmt.Errorf(`failed to crypt content: %w`, err) } return iv, encrypted, tag, nil @@ -26,7 +27,7 @@ func (c Generic) Decrypt(cek, iv, ciphertext, tag, aad []byte) ([]byte, error) { func NewGeneric(alg jwa.ContentEncryptionAlgorithm) (*Generic, error) { c, err := cipher.NewAES(alg) if err != nil { - return nil, errors.Wrap(err, `aes crypt: failed to create content cipher`) + return nil, fmt.Errorf(`aes crypt: failed to create content cipher: %w`, err) } return &Generic{ diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/internal/content_crypt/interface.go b/vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/content_crypt/interface.go similarity index 81% rename from vendor/github.com/lestrrat-go/jwx/jwe/internal/content_crypt/interface.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/content_crypt/interface.go index b12fd31..abfaff3 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwe/internal/content_crypt/interface.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/content_crypt/interface.go @@ -1,8 +1,8 @@ package content_crypt //nolint:golint import ( - "github.com/lestrrat-go/jwx/jwa" - "github.com/lestrrat-go/jwx/jwe/internal/cipher" + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwe/internal/cipher" ) // Generic encrypts a message by applying all the necessary diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/internal/keyenc/interface.go b/vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/keyenc/interface.go similarity index 96% rename from vendor/github.com/lestrrat-go/jwx/jwe/internal/keyenc/interface.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/keyenc/interface.go index bc31ef1..70fe730 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwe/internal/keyenc/interface.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/keyenc/interface.go @@ -4,8 +4,8 @@ import ( "crypto/rsa" "hash" - "github.com/lestrrat-go/jwx/jwa" - "github.com/lestrrat-go/jwx/jwe/internal/keygen" + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwe/internal/keygen" ) // Encrypter is an interface for things that can encrypt keys diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/internal/keyenc/keyenc.go b/vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/keyenc/keyenc.go similarity index 81% rename from vendor/github.com/lestrrat-go/jwx/jwe/internal/keyenc/keyenc.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/keyenc/keyenc.go index 706816e..00b25e0 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwe/internal/keyenc/keyenc.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/keyenc/keyenc.go @@ -19,13 +19,12 @@ import ( "golang.org/x/crypto/curve25519" "golang.org/x/crypto/pbkdf2" - "github.com/lestrrat-go/jwx/internal/ecutil" - "github.com/lestrrat-go/jwx/jwa" - contentcipher "github.com/lestrrat-go/jwx/jwe/internal/cipher" - "github.com/lestrrat-go/jwx/jwe/internal/concatkdf" - "github.com/lestrrat-go/jwx/jwe/internal/keygen" - "github.com/lestrrat-go/jwx/x25519" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/internal/ecutil" + "github.com/lestrrat-go/jwx/v2/jwa" + contentcipher "github.com/lestrrat-go/jwx/v2/jwe/internal/cipher" + "github.com/lestrrat-go/jwx/v2/jwe/internal/concatkdf" + "github.com/lestrrat-go/jwx/v2/jwe/internal/keygen" + "github.com/lestrrat-go/jwx/v2/x25519" ) func NewNoop(alg jwa.KeyEncryptionAlgorithm, sharedkey []byte) (*Noop, error) { @@ -78,12 +77,12 @@ func (kw *AES) KeyID() string { func (kw *AES) Decrypt(enckey []byte) ([]byte, error) { block, err := aes.NewCipher(kw.sharedkey) if err != nil { - return nil, errors.Wrap(err, "failed to create cipher from shared key") + return nil, fmt.Errorf(`failed to create cipher from shared key: %w`, err) } cek, err := Unwrap(block, enckey) if err != nil { - return nil, errors.Wrap(err, "failed to unwrap data") + return nil, fmt.Errorf(`failed to unwrap data: %w`, err) } return cek, nil } @@ -92,11 +91,11 @@ func (kw *AES) Decrypt(enckey []byte) ([]byte, error) { func (kw *AES) Encrypt(cek []byte) (keygen.ByteSource, error) { block, err := aes.NewCipher(kw.sharedkey) if err != nil { - return nil, errors.Wrap(err, "failed to create cipher from shared key") + return nil, fmt.Errorf(`failed to create cipher from shared key: %w`, err) } encrypted, err := Wrap(block, cek) if err != nil { - return nil, errors.Wrap(err, `keywrap: failed to wrap key`) + return nil, fmt.Errorf(`keywrap: failed to wrap key: %w`, err) } return keygen.ByteKey(encrypted), nil } @@ -123,17 +122,17 @@ func (kw AESGCMEncrypt) KeyID() string { func (kw AESGCMEncrypt) Encrypt(cek []byte) (keygen.ByteSource, error) { block, err := aes.NewCipher(kw.sharedkey) if err != nil { - return nil, errors.Wrap(err, "failed to create cipher from shared key") + return nil, fmt.Errorf(`failed to create cipher from shared key: %w`, err) } aesgcm, err := cipher.NewGCM(block) if err != nil { - return nil, errors.Wrap(err, "failed to create gcm from cipher") + return nil, fmt.Errorf(`failed to create gcm from cipher: %w`, err) } iv := make([]byte, aesgcm.NonceSize()) _, err = io.ReadFull(rand.Reader, iv) if err != nil { - return nil, errors.Wrap(err, "failed to get random iv") + return nil, fmt.Errorf(`failed to get random iv: %w`, err) } encrypted := aesgcm.Seal(nil, iv, cek, nil) @@ -160,7 +159,7 @@ func NewPBES2Encrypt(alg jwa.KeyEncryptionAlgorithm, password []byte) (*PBES2Enc hashFunc = sha512.New keylen = 32 default: - return nil, errors.Errorf("unexpected key encryption algorithm %s", alg) + return nil, fmt.Errorf("unexpected key encryption algorithm %s", alg) } return &PBES2Encrypt{ algorithm: alg, @@ -187,7 +186,7 @@ func (kw PBES2Encrypt) Encrypt(cek []byte) (keygen.ByteSource, error) { salt := make([]byte, kw.keylen) _, err := io.ReadFull(rand.Reader, salt) if err != nil { - return nil, errors.Wrap(err, "failed to get random salt") + return nil, fmt.Errorf(`failed to get random salt: %w`, err) } fullsalt := []byte(kw.algorithm) @@ -197,11 +196,11 @@ func (kw PBES2Encrypt) Encrypt(cek []byte) (keygen.ByteSource, error) { block, err := aes.NewCipher(sharedkey) if err != nil { - return nil, errors.Wrap(err, "failed to create cipher from shared key") + return nil, fmt.Errorf(`failed to create cipher from shared key: %w`, err) } encrypted, err := Wrap(block, cek) if err != nil { - return nil, errors.Wrap(err, `keywrap: failed to wrap key`) + return nil, fmt.Errorf(`keywrap: failed to wrap key: %w`, err) } return keygen.ByteWithSaltAndCount{ ByteKey: encrypted, @@ -220,10 +219,10 @@ func NewECDHESEncrypt(alg jwa.KeyEncryptionAlgorithm, enc jwa.ContentEncryptionA case x25519.PublicKey: generator, err = keygen.NewX25519(alg, enc, keysize, key) default: - return nil, errors.Errorf("unexpected key type %T", keyif) + return nil, fmt.Errorf("unexpected key type %T", keyif) } if err != nil { - return nil, errors.Wrap(err, "failed to create key generator") + return nil, fmt.Errorf(`failed to create key generator: %w`, err) } return &ECDHESEncrypt{ algorithm: alg, @@ -249,12 +248,12 @@ func (kw ECDHESEncrypt) KeyID() string { func (kw ECDHESEncrypt) Encrypt(cek []byte) (keygen.ByteSource, error) { kg, err := kw.generator.Generate() if err != nil { - return nil, errors.Wrap(err, "failed to create key generator") + return nil, fmt.Errorf(`failed to create key generator: %w`, err) } bwpk, ok := kg.(keygen.ByteWithECPublicKey) if !ok { - return nil, errors.New("key generator generated invalid key (expected ByteWithECPrivateKey)") + return nil, fmt.Errorf(`key generator generated invalid key (expected ByteWithECPrivateKey)`) } if kw.algorithm == jwa.ECDH_ES { @@ -263,12 +262,12 @@ func (kw ECDHESEncrypt) Encrypt(cek []byte) (keygen.ByteSource, error) { block, err := aes.NewCipher(bwpk.Bytes()) if err != nil { - return nil, errors.Wrap(err, "failed to generate cipher from generated key") + return nil, fmt.Errorf(`failed to generate cipher from generated key: %w`, err) } jek, err := Wrap(block, cek) if err != nil { - return nil, errors.Wrap(err, "failed to wrap data") + return nil, fmt.Errorf(`failed to wrap data: %w`, err) } bwpk.ByteKey = keygen.ByteKey(jek) @@ -298,24 +297,24 @@ func DeriveZ(privkeyif interface{}, pubkeyif interface{}) ([]byte, error) { case x25519.PrivateKey: privkey, ok := privkeyif.(x25519.PrivateKey) if !ok { - return nil, errors.Errorf(`private key must be x25519.PrivateKey, was: %T`, privkeyif) + return nil, fmt.Errorf(`private key must be x25519.PrivateKey, was: %T`, privkeyif) } pubkey, ok := pubkeyif.(x25519.PublicKey) if !ok { - return nil, errors.Errorf(`public key must be x25519.PublicKey, was: %T`, pubkeyif) + return nil, fmt.Errorf(`public key must be x25519.PublicKey, was: %T`, pubkeyif) } return curve25519.X25519(privkey.Seed(), pubkey) default: privkey, ok := privkeyif.(*ecdsa.PrivateKey) if !ok { - return nil, errors.Errorf(`private key must be *ecdsa.PrivateKey, was: %T`, privkeyif) + return nil, fmt.Errorf(`private key must be *ecdsa.PrivateKey, was: %T`, privkeyif) } pubkey, ok := pubkeyif.(*ecdsa.PublicKey) if !ok { - return nil, errors.Errorf(`public key must be *ecdsa.PublicKey, was: %T`, pubkeyif) + return nil, fmt.Errorf(`public key must be *ecdsa.PublicKey, was: %T`, pubkeyif) } if !privkey.PublicKey.Curve.IsOnCurve(pubkey.X, pubkey.Y) { - return nil, errors.New(`public key must be on the same curve as private key`) + return nil, fmt.Errorf(`public key must be on the same curve as private key`) } z, _ := privkey.PublicKey.Curve.ScalarMult(pubkey.X, pubkey.Y, privkey.D.Bytes()) @@ -332,12 +331,12 @@ func DeriveECDHES(alg, apu, apv []byte, privkey interface{}, pubkey interface{}, binary.BigEndian.PutUint32(pubinfo, keysize*8) zBytes, err := DeriveZ(privkey, pubkey) if err != nil { - return nil, errors.Wrap(err, "unable to determine Z") + return nil, fmt.Errorf(`unable to determine Z: %w`, err) } kdf := concatkdf.New(crypto.SHA256, alg, zBytes, apu, apv, pubinfo, []byte{}) key := make([]byte, keysize) if _, err := kdf.Read(key); err != nil { - return nil, errors.Wrap(err, "failed to read kdf") + return nil, fmt.Errorf(`failed to read kdf: %w`, err) } return key, nil @@ -356,7 +355,7 @@ func (kw ECDHESDecrypt) Decrypt(enckey []byte) ([]byte, error) { // Create a content cipher from the content encryption algorithm c, err := contentcipher.NewAES(kw.contentalg) if err != nil { - return nil, errors.Wrapf(err, `failed to create content cipher for %s`, kw.contentalg) + return nil, fmt.Errorf(`failed to create content cipher for %s: %w`, kw.contentalg, err) } keysize = uint32(c.KeySize()) algBytes = []byte(kw.contentalg.String()) @@ -367,12 +366,12 @@ func (kw ECDHESDecrypt) Decrypt(enckey []byte) ([]byte, error) { case jwa.ECDH_ES_A256KW: keysize = 32 default: - return nil, errors.Errorf("invalid ECDH-ES key wrap algorithm (%s)", kw.keyalg) + return nil, fmt.Errorf("invalid ECDH-ES key wrap algorithm (%s)", kw.keyalg) } key, err := DeriveECDHES(algBytes, kw.apu, kw.apv, kw.privkey, kw.pubkey, keysize) if err != nil { - return nil, errors.Wrap(err, `failed to derive ECDHES encryption key`) + return nil, fmt.Errorf(`failed to derive ECDHES encryption key: %w`, err) } // ECDH-ES does not wrap keys @@ -382,7 +381,7 @@ func (kw ECDHESDecrypt) Decrypt(enckey []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { - return nil, errors.Wrap(err, "failed to create cipher for ECDH-ES key wrap") + return nil, fmt.Errorf(`failed to create cipher for ECDH-ES key wrap: %w`, err) } return Unwrap(block, enckey) @@ -393,7 +392,7 @@ func NewRSAOAEPEncrypt(alg jwa.KeyEncryptionAlgorithm, pubkey *rsa.PublicKey) (* switch alg { case jwa.RSA_OAEP, jwa.RSA_OAEP_256: default: - return nil, errors.Errorf("invalid RSA OAEP encrypt algorithm (%s)", alg) + return nil, fmt.Errorf("invalid RSA OAEP encrypt algorithm (%s)", alg) } return &RSAOAEPEncrypt{ alg: alg, @@ -406,7 +405,7 @@ func NewRSAPKCSEncrypt(alg jwa.KeyEncryptionAlgorithm, pubkey *rsa.PublicKey) (* switch alg { case jwa.RSA1_5: default: - return nil, errors.Errorf("invalid RSA PKCS encrypt algorithm (%s)", alg) + return nil, fmt.Errorf("invalid RSA PKCS encrypt algorithm (%s)", alg) } return &RSAPKCSEncrypt{ @@ -446,11 +445,11 @@ func (e RSAOAEPEncrypt) KeyID() string { // KeyEncrypt encrypts the content encryption key using RSA PKCS1v15 func (e RSAPKCSEncrypt) Encrypt(cek []byte) (keygen.ByteSource, error) { if e.alg != jwa.RSA1_5 { - return nil, errors.Errorf("invalid RSA PKCS encrypt algorithm (%s)", e.alg) + return nil, fmt.Errorf("invalid RSA PKCS encrypt algorithm (%s)", e.alg) } encrypted, err := rsa.EncryptPKCS1v15(rand.Reader, e.pubkey, cek) if err != nil { - return nil, errors.Wrap(err, "failed to encrypt using PKCS1v15") + return nil, fmt.Errorf(`failed to encrypt using PKCS1v15: %w`, err) } return keygen.ByteKey(encrypted), nil } @@ -464,11 +463,11 @@ func (e RSAOAEPEncrypt) Encrypt(cek []byte) (keygen.ByteSource, error) { case jwa.RSA_OAEP_256: hash = sha256.New() default: - return nil, errors.New("failed to generate key encrypter for RSA-OAEP: RSA_OAEP/RSA_OAEP_256 required") + return nil, fmt.Errorf(`failed to generate key encrypter for RSA-OAEP: RSA_OAEP/RSA_OAEP_256 required`) } encrypted, err := rsa.EncryptOAEP(hash, rand.Reader, e.pubkey, cek, []byte{}) if err != nil { - return nil, errors.Wrap(err, `failed to OAEP encrypt`) + return nil, fmt.Errorf(`failed to OAEP encrypt: %w`, err) } return keygen.ByteKey(encrypted), nil } @@ -518,7 +517,7 @@ func (d RSAPKCS15Decrypt) Decrypt(enckey []byte) ([]byte, error) { bk, err := d.generator.Generate() if err != nil { - return nil, errors.New("failed to generate key") + return nil, fmt.Errorf(`failed to generate key`) } cek := bk.Bytes() @@ -528,7 +527,7 @@ func (d RSAPKCS15Decrypt) Decrypt(enckey []byte) ([]byte, error) { // therefore deliberately ignoring errors here. err = rsa.DecryptPKCS1v15SessionKey(rand.Reader, d.privkey, enckey, cek) if err != nil { - return nil, errors.Wrap(err, "failed to decrypt via PKCS1v15") + return nil, fmt.Errorf(`failed to decrypt via PKCS1v15: %w`, err) } return cek, nil @@ -539,7 +538,7 @@ func NewRSAOAEPDecrypt(alg jwa.KeyEncryptionAlgorithm, privkey *rsa.PrivateKey) switch alg { case jwa.RSA_OAEP, jwa.RSA_OAEP_256: default: - return nil, errors.Errorf("invalid RSA OAEP decrypt algorithm (%s)", alg) + return nil, fmt.Errorf("invalid RSA OAEP decrypt algorithm (%s)", alg) } return &RSAOAEPDecrypt{ @@ -562,7 +561,7 @@ func (d RSAOAEPDecrypt) Decrypt(enckey []byte) ([]byte, error) { case jwa.RSA_OAEP_256: hash = sha256.New() default: - return nil, errors.New("failed to generate key encrypter for RSA-OAEP: RSA_OAEP/RSA_OAEP_256 required") + return nil, fmt.Errorf(`failed to generate key encrypter for RSA-OAEP: RSA_OAEP/RSA_OAEP_256 required`) } return rsa.DecryptOAEP(hash, rand.Reader, d.privkey, enckey, []byte{}) } @@ -581,7 +580,7 @@ const keywrapChunkLen = 8 func Wrap(kek cipher.Block, cek []byte) ([]byte, error) { if len(cek)%8 != 0 { - return nil, errors.New(`keywrap input must be 8 byte blocks`) + return nil, fmt.Errorf(`keywrap input must be 8 byte blocks`) } n := len(cek) / keywrapChunkLen @@ -620,7 +619,7 @@ func Wrap(kek cipher.Block, cek []byte) ([]byte, error) { func Unwrap(block cipher.Block, ciphertxt []byte) ([]byte, error) { if len(ciphertxt)%keywrapChunkLen != 0 { - return nil, errors.Errorf(`keyunwrap input must be %d byte blocks`, keywrapChunkLen) + return nil, fmt.Errorf(`keyunwrap input must be %d byte blocks`, keywrapChunkLen) } n := (len(ciphertxt) / keywrapChunkLen) - 1 @@ -649,7 +648,7 @@ func Unwrap(block cipher.Block, ciphertxt []byte) ([]byte, error) { } if subtle.ConstantTimeCompare(buffer[:keywrapChunkLen], keywrapDefaultIV) == 0 { - return nil, errors.New("key unwrap: failed to unwrap key") + return nil, fmt.Errorf(`key unwrap: failed to unwrap key`) } out := make([]byte, n*keywrapChunkLen) diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/internal/keygen/interface.go b/vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/keygen/interface.go similarity index 95% rename from vendor/github.com/lestrrat-go/jwx/jwe/internal/keygen/interface.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/keygen/interface.go index 0114abe..331a00e 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwe/internal/keygen/interface.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/keygen/interface.go @@ -3,8 +3,8 @@ package keygen import ( "crypto/ecdsa" - "github.com/lestrrat-go/jwx/jwa" - "github.com/lestrrat-go/jwx/x25519" + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/x25519" ) type Generator interface { diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/internal/keygen/keygen.go b/vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/keygen/keygen.go similarity index 80% rename from vendor/github.com/lestrrat-go/jwx/jwe/internal/keygen/keygen.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/keygen/keygen.go index f15e2a1..5b2d8a6 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwe/internal/keygen/keygen.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwe/internal/keygen/keygen.go @@ -5,16 +5,16 @@ import ( "crypto/ecdsa" "crypto/rand" "encoding/binary" + "fmt" "io" "golang.org/x/crypto/curve25519" - "github.com/lestrrat-go/jwx/internal/ecutil" - "github.com/lestrrat-go/jwx/jwa" - "github.com/lestrrat-go/jwx/jwe/internal/concatkdf" - "github.com/lestrrat-go/jwx/jwk" - "github.com/lestrrat-go/jwx/x25519" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/internal/ecutil" + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwe/internal/concatkdf" + "github.com/lestrrat-go/jwx/v2/jwk" + "github.com/lestrrat-go/jwx/v2/x25519" ) // Bytes returns the byte from this ByteKey @@ -49,7 +49,7 @@ func (g Random) Size() int { func (g Random) Generate() (ByteSource, error) { buf := make([]byte, g.keysize) if _, err := io.ReadFull(rand.Reader, buf); err != nil { - return nil, errors.Wrap(err, "failed to read from rand.Reader") + return nil, fmt.Errorf(`failed to read from rand.Reader: %w`, err) } return ByteKey(buf), nil } @@ -73,7 +73,7 @@ func (g Ecdhes) Size() int { func (g Ecdhes) Generate() (ByteSource, error) { priv, err := ecdsa.GenerateKey(g.pubkey.Curve, rand.Reader) if err != nil { - return nil, errors.Wrap(err, "failed to generate key for ECDH-ES") + return nil, fmt.Errorf(`failed to generate key for ECDH-ES: %w`, err) } var algorithm string @@ -92,7 +92,7 @@ func (g Ecdhes) Generate() (ByteSource, error) { kdf := concatkdf.New(crypto.SHA256, []byte(algorithm), zBytes, []byte{}, []byte{}, pubinfo, []byte{}) kek := make([]byte, g.keysize) if _, err := kdf.Read(kek); err != nil { - return nil, errors.Wrap(err, "failed to read kdf") + return nil, fmt.Errorf(`failed to read kdf: %w`, err) } return ByteWithECPublicKey{ @@ -120,7 +120,7 @@ func (g X25519) Size() int { func (g X25519) Generate() (ByteSource, error) { pub, priv, err := x25519.GenerateKey(rand.Reader) if err != nil { - return nil, errors.Wrap(err, "failed to generate key for X25519") + return nil, fmt.Errorf(`failed to generate key for X25519: %w`, err) } var algorithm string @@ -135,12 +135,12 @@ func (g X25519) Generate() (ByteSource, error) { zBytes, err := curve25519.X25519(priv.Seed(), g.pubkey) if err != nil { - return nil, errors.Wrap(err, "failed to compute Z") + return nil, fmt.Errorf(`failed to compute Z: %w`, err) } kdf := concatkdf.New(crypto.SHA256, []byte(algorithm), zBytes, []byte{}, []byte{}, pubinfo, []byte{}) kek := make([]byte, g.keysize) if _, err := kdf.Read(kek); err != nil { - return nil, errors.Wrap(err, "failed to read kdf") + return nil, fmt.Errorf(`failed to read kdf: %w`, err) } return ByteWithECPublicKey{ @@ -152,13 +152,13 @@ func (g X25519) Generate() (ByteSource, error) { // HeaderPopulate populates the header with the required EC-DSA public key // information ('epk' key) func (k ByteWithECPublicKey) Populate(h Setter) error { - key, err := jwk.New(k.PublicKey) + key, err := jwk.FromRaw(k.PublicKey) if err != nil { - return errors.Wrap(err, "failed to create JWK") + return fmt.Errorf(`failed to create JWK: %w`, err) } if err := h.Set("epk", key); err != nil { - return errors.Wrap(err, "failed to write header") + return fmt.Errorf(`failed to write header: %w`, err) } return nil } @@ -167,11 +167,11 @@ func (k ByteWithECPublicKey) Populate(h Setter) error { // parameters ('iv' and 'tag') func (k ByteWithIVAndTag) Populate(h Setter) error { if err := h.Set("iv", k.IV); err != nil { - return errors.Wrap(err, "failed to write header") + return fmt.Errorf(`failed to write header: %w`, err) } if err := h.Set("tag", k.Tag); err != nil { - return errors.Wrap(err, "failed to write header") + return fmt.Errorf(`failed to write header: %w`, err) } return nil @@ -181,11 +181,11 @@ func (k ByteWithIVAndTag) Populate(h Setter) error { // parameters ('p2s' and 'p2c') func (k ByteWithSaltAndCount) Populate(h Setter) error { if err := h.Set("p2c", k.Count); err != nil { - return errors.Wrap(err, "failed to write header") + return fmt.Errorf(`failed to write header: %w`, err) } if err := h.Set("p2s", k.Salt); err != nil { - return errors.Wrap(err, "failed to write header") + return fmt.Errorf(`failed to write header: %w`, err) } return nil diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jwe/io.go b/vendor/github.com/lestrrat-go/jwx/v2/jwe/io.go new file mode 100644 index 0000000..e101199 --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwe/io.go @@ -0,0 +1,42 @@ +// Automatically generated by internal/cmd/genreadfile/main.go. DO NOT EDIT + +package jwe + +import ( + "io/fs" + "os" +) + +type sysFS struct{} + +func (sysFS) Open(path string) (fs.File, error) { + return os.Open(path) +} + +func ReadFile(path string, options ...ReadFileOption) (*Message, error) { + var parseOptions []ParseOption + var readFileOptions []ReadFileOption + for _, option := range options { + if po, ok := option.(ParseOption); ok { + parseOptions = append(parseOptions, po) + } else { + readFileOptions = append(readFileOptions, option) + } + } + + var srcFS fs.FS = sysFS{} + for _, option := range options { + switch option.Ident() { + case identFS{}: + srcFS = option.Value().(fs.FS) + } + } + + f, err := srcFS.Open(path) + if err != nil { + return nil, err + } + + defer f.Close() + return ParseReader(f) +} diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jwe/jwe.go b/vendor/github.com/lestrrat-go/jwx/v2/jwe/jwe.go new file mode 100644 index 0000000..95a020d --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwe/jwe.go @@ -0,0 +1,793 @@ +//go:generate ../tools/cmd/genjwe.sh + +// Package jwe implements JWE as described in https://tools.ietf.org/html/rfc7516 +package jwe + +import ( + "bytes" + "context" + "crypto/ecdsa" + "crypto/rsa" + "fmt" + "io" + + "github.com/lestrrat-go/blackmagic" + "github.com/lestrrat-go/jwx/v2/internal/base64" + "github.com/lestrrat-go/jwx/v2/internal/json" + "github.com/lestrrat-go/jwx/v2/internal/keyconv" + "github.com/lestrrat-go/jwx/v2/jwk" + + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwe/internal/content_crypt" + "github.com/lestrrat-go/jwx/v2/jwe/internal/keyenc" + "github.com/lestrrat-go/jwx/v2/jwe/internal/keygen" + "github.com/lestrrat-go/jwx/v2/x25519" +) + +const ( + fmtInvalid = iota + fmtCompact + fmtJSON + fmtJSONPretty + fmtMax +) + +var _ = fmtInvalid +var _ = fmtMax + +var registry = json.NewRegistry() + +type recipientBuilder struct { + alg jwa.KeyEncryptionAlgorithm + key interface{} + headers Headers +} + +func (b *recipientBuilder) Build(cek []byte, calg jwa.ContentEncryptionAlgorithm, cc *content_crypt.Generic) (Recipient, []byte, error) { + // we need the raw key + rawKey := b.key + + var keyID string + if jwkKey, ok := b.key.(jwk.Key); ok { + // Meanwhile, grab the kid as well + keyID = jwkKey.KeyID() + + var raw interface{} + if err := jwkKey.Raw(&raw); err != nil { + return nil, nil, fmt.Errorf(`failed to retrieve raw key out of %T: %w`, b.key, err) + } + + rawKey = raw + } + + // First, create a key encryptor + var enc keyenc.Encrypter + switch b.alg { + case jwa.RSA1_5: + var pubkey rsa.PublicKey + if err := keyconv.RSAPublicKey(&pubkey, rawKey); err != nil { + return nil, nil, fmt.Errorf(`failed to generate public key from key (%T): %w`, rawKey, err) + } + + v, err := keyenc.NewRSAPKCSEncrypt(b.alg, &pubkey) + if err != nil { + return nil, nil, fmt.Errorf(`failed to create RSA PKCS encrypter: %w`, err) + } + enc = v + case jwa.RSA_OAEP, jwa.RSA_OAEP_256: + var pubkey rsa.PublicKey + if err := keyconv.RSAPublicKey(&pubkey, rawKey); err != nil { + return nil, nil, fmt.Errorf(`failed to generate public key from key (%T): %w`, rawKey, err) + } + + v, err := keyenc.NewRSAOAEPEncrypt(b.alg, &pubkey) + if err != nil { + return nil, nil, fmt.Errorf(`failed to create RSA OAEP encrypter: %w`, err) + } + enc = v + case jwa.A128KW, jwa.A192KW, jwa.A256KW, + jwa.A128GCMKW, jwa.A192GCMKW, jwa.A256GCMKW, + jwa.PBES2_HS256_A128KW, jwa.PBES2_HS384_A192KW, jwa.PBES2_HS512_A256KW: + sharedkey, ok := rawKey.([]byte) + if !ok { + return nil, nil, fmt.Errorf(`invalid key: []byte required (%T)`, rawKey) + } + + var err error + switch b.alg { + case jwa.A128KW, jwa.A192KW, jwa.A256KW: + enc, err = keyenc.NewAES(b.alg, sharedkey) + case jwa.PBES2_HS256_A128KW, jwa.PBES2_HS384_A192KW, jwa.PBES2_HS512_A256KW: + enc, err = keyenc.NewPBES2Encrypt(b.alg, sharedkey) + default: + enc, err = keyenc.NewAESGCMEncrypt(b.alg, sharedkey) + } + if err != nil { + return nil, nil, fmt.Errorf(`failed to create key wrap encrypter: %w`, err) + } + // NOTE: there was formerly a restriction, introduced + // in PR #26, which disallowed certain key/content + // algorithm combinations. This seemed bogus, and + // interop with the jose tool demonstrates it. + case jwa.ECDH_ES, jwa.ECDH_ES_A128KW, jwa.ECDH_ES_A192KW, jwa.ECDH_ES_A256KW: + var keysize int + switch b.alg { + case jwa.ECDH_ES: + // https://tools.ietf.org/html/rfc7518#page-15 + // In Direct Key Agreement mode, the output of the Concat KDF MUST be a + // key of the same length as that used by the "enc" algorithm. + keysize = cc.KeySize() + case jwa.ECDH_ES_A128KW: + keysize = 16 + case jwa.ECDH_ES_A192KW: + keysize = 24 + case jwa.ECDH_ES_A256KW: + keysize = 32 + } + + switch key := rawKey.(type) { + case x25519.PublicKey: + v, err := keyenc.NewECDHESEncrypt(b.alg, calg, keysize, rawKey) + if err != nil { + return nil, nil, fmt.Errorf(`failed to create ECDHS key wrap encrypter: %w`, err) + } + enc = v + default: + var pubkey ecdsa.PublicKey + if err := keyconv.ECDSAPublicKey(&pubkey, rawKey); err != nil { + return nil, nil, fmt.Errorf(`failed to generate public key from key (%T): %w`, key, err) + } + v, err := keyenc.NewECDHESEncrypt(b.alg, calg, keysize, &pubkey) + if err != nil { + return nil, nil, fmt.Errorf(`failed to create ECDHS key wrap encrypter: %w`, err) + } + enc = v + } + case jwa.DIRECT: + sharedkey, ok := rawKey.([]byte) + if !ok { + return nil, nil, fmt.Errorf("invalid key: []byte required") + } + enc, _ = keyenc.NewNoop(b.alg, sharedkey) + default: + return nil, nil, fmt.Errorf(`invalid key encryption algorithm (%s)`, b.alg) + } + + if keyID != "" { + enc.SetKeyID(keyID) + } + + r := NewRecipient() + if hdrs := b.headers; hdrs != nil { + _ = r.SetHeaders(hdrs) + } + + if err := r.Headers().Set(AlgorithmKey, b.alg); err != nil { + return nil, nil, fmt.Errorf(`failed to set header: %w`, err) + } + if v := enc.KeyID(); v != "" { + if err := r.Headers().Set(KeyIDKey, v); err != nil { + return nil, nil, fmt.Errorf(`failed to set header: %w`, err) + } + } + + var rawCEK []byte + enckey, err := enc.Encrypt(cek) + if err != nil { + return nil, nil, fmt.Errorf(`failed to encrypt key: %w`, err) + } + if enc.Algorithm() == jwa.ECDH_ES || enc.Algorithm() == jwa.DIRECT { + rawCEK = enckey.Bytes() + } else { + if err := r.SetEncryptedKey(enckey.Bytes()); err != nil { + return nil, nil, fmt.Errorf(`failed to set encrypted key: %w`, err) + } + } + + if hp, ok := enckey.(populater); ok { + if err := hp.Populate(r.Headers()); err != nil { + return nil, nil, fmt.Errorf(`failed to populate: %w`, err) + } + } + + return r, rawCEK, nil +} + +// Encrypt generates a JWE message for the given payload and returns +// it in serialized form, which can be in either compact or +// JSON format. Default is compact. +// +// You must pass at least one key to `jwe.Encrypt()` by using `jwe.WithKey()` +// option. +// +// jwe.Encrypt(payload, jwe.WithKey(alg, key)) +// jwe.Encrypt(payload, jws.WithJSON(), jws.WithKey(alg1, key1), jws.WithKey(alg2, key2)) +// +// Note that in the second example the `jws.WithJSON()` option is +// specified as well. This is because the compact serialization +// format does not support multiple recipients, and users must +// specifically ask for the JSON serialization format. +// +// Read the documentation for `jwe.WithKey()` to learn more about the +// possible values that can be used for `alg` and `key`. +// +// Look for options that return `jwe.EncryptOption` or `jws.EncryptDecryptOption` +// for a complete list of options that can be passed to this function. +func Encrypt(payload []byte, options ...EncryptOption) ([]byte, error) { + // default content encryption algorithm + calg := jwa.A256GCM + + // default compression is "none" + compression := jwa.NoCompress + + format := fmtCompact + + // builds each "recipient" with encrypted_key and headers + var builders []*recipientBuilder + + var protected Headers + var mergeProtected bool + var useRawCEK bool + for _, option := range options { + //nolint:forcetypeassert + switch option.Ident() { + case identKey{}: + data := option.Value().(*withKey) + v, ok := data.alg.(jwa.KeyEncryptionAlgorithm) + if !ok { + return nil, fmt.Errorf(`jwe.Encrypt: expected alg to be jwa.KeyEncryptionAlgorithm, but got %T`, data.alg) + } + + switch v { + case jwa.DIRECT, jwa.ECDH_ES: + useRawCEK = true + } + + builders = append(builders, &recipientBuilder{ + alg: v, + key: data.key, + headers: data.headers, + }) + case identContentEncryptionAlgorithm{}: + calg = option.Value().(jwa.ContentEncryptionAlgorithm) + case identCompress{}: + compression = option.Value().(jwa.CompressionAlgorithm) + case identMergeProtectedHeaders{}: + mergeProtected = option.Value().(bool) + case identProtectedHeaders{}: + v := option.Value().(Headers) + if !mergeProtected || protected == nil { + protected = v + } else { + ctx := context.TODO() + merged, err := protected.Merge(ctx, v) + if err != nil { + return nil, fmt.Errorf(`jwe.Encrypt: failed to merge headers: %w`, err) + } + protected = merged + } + case identSerialization{}: + format = option.Value().(int) + } + } + + // We need to have at least one builder + switch l := len(builders); { + case l == 0: + return nil, fmt.Errorf(`jwe.Encrypt: missing key encryption builders: use jwe.WithKey() to specify one`) + case l > 1: + if format == fmtCompact { + return nil, fmt.Errorf(`jwe.Encrypt: cannot use compact serialization when multiple recipients exist (check the number of WithKey() argument, or use WithJSON())`) + } + } + + if useRawCEK { + if len(builders) != 1 { + return nil, fmt.Errorf(`jwe.Encrypt: multiple recipients for ECDH-ES/DIRECT mode supported`) + } + } + + // There is exactly one content encrypter. + contentcrypt, err := content_crypt.NewGeneric(calg) + if err != nil { + return nil, fmt.Errorf(`jwe.Encrypt: failed to create AES encrypter: %w`, err) + } + + generator := keygen.NewRandom(contentcrypt.KeySize()) + bk, err := generator.Generate() + if err != nil { + return nil, fmt.Errorf(`jwe.Encrypt: failed to generate key: %w`, err) + } + cek := bk.Bytes() + + recipients := make([]Recipient, len(builders)) + for i, builder := range builders { + // some builders require hint from the contentcrypt object + r, rawCEK, err := builder.Build(cek, calg, contentcrypt) + if err != nil { + return nil, fmt.Errorf(`jwe.Encrypt: failed to create recipient #%d: %w`, i, err) + } + recipients[i] = r + + // Kinda feels weird, but if useRawCEK == true, we asserted earlier + // that len(builders) == 1, so this is OK + if useRawCEK { + cek = rawCEK + } + } + + if protected == nil { + protected = NewHeaders() + } + + if err := protected.Set(ContentEncryptionKey, calg); err != nil { + return nil, fmt.Errorf(`jwe.Encrypt: failed to set "enc" in protected header: %w`, err) + } + + if compression != jwa.NoCompress { + payload, err = compress(payload) + if err != nil { + return nil, fmt.Errorf(`jwe.Encrypt: failed to compress payload before encryption: %w`, err) + } + if err := protected.Set(CompressionKey, compression); err != nil { + return nil, fmt.Errorf(`jwe.Encrypt: failed to set "zip" in protected header: %w`, err) + } + } + + // If there's only one recipient, you want to include that in the + // protected header + if len(recipients) == 1 { + h, err := protected.Merge(context.TODO(), recipients[0].Headers()) + if err != nil { + return nil, fmt.Errorf(`jwe.Encrypt: failed to merge protected headers: %w`, err) + } + protected = h + } + + aad, err := protected.Encode() + if err != nil { + return nil, fmt.Errorf(`failed to base64 encode protected headers: %w`, err) + } + + iv, ciphertext, tag, err := contentcrypt.Encrypt(cek, payload, aad) + if err != nil { + return nil, fmt.Errorf(`failed to encrypt payload: %w`, err) + } + + msg := NewMessage() + + if err := msg.Set(CipherTextKey, ciphertext); err != nil { + return nil, fmt.Errorf(`failed to set %s: %w`, CipherTextKey, err) + } + if err := msg.Set(InitializationVectorKey, iv); err != nil { + return nil, fmt.Errorf(`failed to set %s: %w`, InitializationVectorKey, err) + } + if err := msg.Set(ProtectedHeadersKey, protected); err != nil { + return nil, fmt.Errorf(`failed to set %s: %w`, ProtectedHeadersKey, err) + } + if err := msg.Set(RecipientsKey, recipients); err != nil { + return nil, fmt.Errorf(`failed to set %s: %w`, RecipientsKey, err) + } + if err := msg.Set(TagKey, tag); err != nil { + return nil, fmt.Errorf(`failed to set %s: %w`, TagKey, err) + } + + switch format { + case fmtCompact: + return Compact(msg) + case fmtJSON: + return json.Marshal(msg) + case fmtJSONPretty: + return json.MarshalIndent(msg, "", " ") + default: + return nil, fmt.Errorf(`jwe.Encrypt: invalid serialization`) + } +} + +type decryptCtx struct { + msg *Message + aad []byte + computedAad []byte + keyProviders []KeyProvider + protectedHeaders Headers +} + +// Decrypt takes the key encryption algorithm and the corresponding +// key to decrypt the JWE message, and returns the decrypted payload. +// The JWE message can be either compact or full JSON format. +// +// `alg` accepts a `jwa.KeyAlgorithm` for convenience so you can directly pass +// the result of `(jwk.Key).Algorithm()`, but in practice it must be of type +// `jwa.KeyEncryptionAlgorithm` or otherwise it will cause an error. +// +// `key` must be a private key. It can be either in its raw format (e.g. *rsa.PrivateKey) or a jwk.Key +func Decrypt(buf []byte, options ...DecryptOption) ([]byte, error) { + var keyProviders []KeyProvider + var keyUsed interface{} + + var dst *Message + //nolint:forcetypeassert + for _, option := range options { + switch option.Ident() { + case identMessage{}: + dst = option.Value().(*Message) + case identKeyProvider{}: + keyProviders = append(keyProviders, option.Value().(KeyProvider)) + case identKeyUsed{}: + keyUsed = option.Value() + case identKey{}: + pair := option.Value().(*withKey) + alg, ok := pair.alg.(jwa.KeyEncryptionAlgorithm) + if !ok { + return nil, fmt.Errorf(`WithKey() option must be specified using jwa.KeyEncryptionAlgorithm (got %T)`, pair.alg) + } + keyProviders = append(keyProviders, &staticKeyProvider{ + alg: alg, + key: pair.key, + }) + } + } + + if len(keyProviders) < 1 { + return nil, fmt.Errorf(`jwe.Decrypt: no key providers have been provided (see jwe.WithKey(), jwe.WithKeySet(), and jwe.WithKeyProvider()`) + } + + msg, err := parseJSONOrCompact(buf, true) + if err != nil { + return nil, fmt.Errorf(`failed to parse buffer for Decrypt: %w`, err) + } + + // Process things that are common to the message + ctx := context.TODO() + h, err := msg.protectedHeaders.Clone(ctx) + if err != nil { + return nil, fmt.Errorf(`failed to copy protected headers: %w`, err) + } + h, err = h.Merge(ctx, msg.unprotectedHeaders) + if err != nil { + return nil, fmt.Errorf(`failed to merge headers for message decryption: %w`, err) + } + + var aad []byte + if aadContainer := msg.authenticatedData; aadContainer != nil { + aad = base64.Encode(aadContainer) + } + + var computedAad []byte + if len(msg.rawProtectedHeaders) > 0 { + computedAad = msg.rawProtectedHeaders + } else { + // this is probably not required once msg.Decrypt is deprecated + var err error + computedAad, err = msg.protectedHeaders.Encode() + if err != nil { + return nil, fmt.Errorf(`failed to encode protected headers: %w`, err) + } + } + + // for each recipient, attempt to match the key providers + // if we have no recipients, pretend like we only have one + recipients := msg.recipients + if len(recipients) == 0 { + r := NewRecipient() + if err := r.SetHeaders(msg.protectedHeaders); err != nil { + return nil, fmt.Errorf(`failed to set headers to recipient: %w`, err) + } + recipients = append(recipients, r) + } + + var dctx decryptCtx + + dctx.aad = aad + dctx.computedAad = computedAad + dctx.msg = msg + dctx.keyProviders = keyProviders + dctx.protectedHeaders = h + + var lastError error + for _, recipient := range recipients { + decrypted, err := dctx.try(ctx, recipient, keyUsed) + if err != nil { + lastError = err + continue + } + if dst != nil { + *dst = *msg + dst.rawProtectedHeaders = nil + dst.storeProtectedHeaders = false + } + return decrypted, nil + } + return nil, fmt.Errorf(`jwe.Decrypt: failed to decrypt any of the recipients (last error = %w)`, lastError) +} + +func (dctx *decryptCtx) try(ctx context.Context, recipient Recipient, keyUsed interface{}) ([]byte, error) { + var tried int + var lastError error + for i, kp := range dctx.keyProviders { + var sink algKeySink + if err := kp.FetchKeys(ctx, &sink, recipient, dctx.msg); err != nil { + return nil, fmt.Errorf(`key provider %d failed: %w`, i, err) + } + + for _, pair := range sink.list { + tried++ + // alg is converted here because pair.alg is of type jwa.KeyAlgorithm. + // this may seem ugly, but we're trying to avoid declaring separate + // structs for `alg jwa.KeyAlgorithm` and `alg jwa.SignatureAlgorithm` + //nolint:forcetypeassert + alg := pair.alg.(jwa.KeyEncryptionAlgorithm) + key := pair.key + + decrypted, err := dctx.decryptKey(ctx, alg, key, recipient) + if err != nil { + lastError = err + continue + } + + if keyUsed != nil { + if err := blackmagic.AssignIfCompatible(keyUsed, key); err != nil { + return nil, fmt.Errorf(`failed to assign used key (%T) to %T: %w`, key, keyUsed, err) + } + } + return decrypted, nil + } + } + return nil, fmt.Errorf(`jwe.Decrypt: tried %d keys, but failed to match any of the keys with recipient (last error = %s)`, tried, lastError) +} + +func (dctx *decryptCtx) decryptKey(ctx context.Context, alg jwa.KeyEncryptionAlgorithm, key interface{}, recipient Recipient) ([]byte, error) { + if jwkKey, ok := key.(jwk.Key); ok { + var raw interface{} + if err := jwkKey.Raw(&raw); err != nil { + return nil, fmt.Errorf(`failed to retrieve raw key from %T: %w`, key, err) + } + key = raw + } + + dec := newDecrypter(alg, dctx.msg.protectedHeaders.ContentEncryption(), key). + AuthenticatedData(dctx.aad). + ComputedAuthenticatedData(dctx.computedAad). + InitializationVector(dctx.msg.initializationVector). + Tag(dctx.msg.tag) + + if recipient.Headers().Algorithm() != alg { + // algorithms don't match + return nil, fmt.Errorf(`jwe.Decrypt: key and recipient algorithms do not match`) + } + + h2, err := dctx.protectedHeaders.Clone(ctx) + if err != nil { + return nil, fmt.Errorf(`jwe.Decrypt: failed to copy headers (1): %w`, err) + } + + h2, err = h2.Merge(ctx, recipient.Headers()) + if err != nil { + return nil, fmt.Errorf(`failed to copy headers (2): %w`, err) + } + + switch alg { + case jwa.ECDH_ES, jwa.ECDH_ES_A128KW, jwa.ECDH_ES_A192KW, jwa.ECDH_ES_A256KW: + epkif, ok := h2.Get(EphemeralPublicKeyKey) + if !ok { + return nil, fmt.Errorf(`failed to get 'epk' field`) + } + switch epk := epkif.(type) { + case jwk.ECDSAPublicKey: + var pubkey ecdsa.PublicKey + if err := epk.Raw(&pubkey); err != nil { + return nil, fmt.Errorf(`failed to get public key: %w`, err) + } + dec.PublicKey(&pubkey) + case jwk.OKPPublicKey: + var pubkey interface{} + if err := epk.Raw(&pubkey); err != nil { + return nil, fmt.Errorf(`failed to get public key: %w`, err) + } + dec.PublicKey(pubkey) + default: + return nil, fmt.Errorf("unexpected 'epk' type %T for alg %s", epkif, alg) + } + + if apu := h2.AgreementPartyUInfo(); len(apu) > 0 { + dec.AgreementPartyUInfo(apu) + } + + if apv := h2.AgreementPartyVInfo(); len(apv) > 0 { + dec.AgreementPartyVInfo(apv) + } + case jwa.A128GCMKW, jwa.A192GCMKW, jwa.A256GCMKW: + ivB64, ok := h2.Get(InitializationVectorKey) + if !ok { + return nil, fmt.Errorf(`failed to get 'iv' field`) + } + ivB64Str, ok := ivB64.(string) + if !ok { + return nil, fmt.Errorf("unexpected type for 'iv': %T", ivB64) + } + tagB64, ok := h2.Get(TagKey) + if !ok { + return nil, fmt.Errorf(`failed to get 'tag' field`) + } + tagB64Str, ok := tagB64.(string) + if !ok { + return nil, fmt.Errorf("unexpected type for 'tag': %T", tagB64) + } + iv, err := base64.DecodeString(ivB64Str) + if err != nil { + return nil, fmt.Errorf(`failed to b64-decode 'iv': %w`, err) + } + tag, err := base64.DecodeString(tagB64Str) + if err != nil { + return nil, fmt.Errorf(`failed to b64-decode 'tag': %w`, err) + } + dec.KeyInitializationVector(iv) + dec.KeyTag(tag) + case jwa.PBES2_HS256_A128KW, jwa.PBES2_HS384_A192KW, jwa.PBES2_HS512_A256KW: + saltB64, ok := h2.Get(SaltKey) + if !ok { + return nil, fmt.Errorf(`failed to get 'p2s' field`) + } + saltB64Str, ok := saltB64.(string) + if !ok { + return nil, fmt.Errorf("unexpected type for 'p2s': %T", saltB64) + } + + count, ok := h2.Get(CountKey) + if !ok { + return nil, fmt.Errorf(`failed to get 'p2c' field`) + } + countFlt, ok := count.(float64) + if !ok { + return nil, fmt.Errorf("unexpected type for 'p2c': %T", count) + } + salt, err := base64.DecodeString(saltB64Str) + if err != nil { + return nil, fmt.Errorf(`failed to b64-decode 'salt': %w`, err) + } + dec.KeySalt(salt) + dec.KeyCount(int(countFlt)) + } + + plaintext, err := dec.Decrypt(recipient.EncryptedKey(), dctx.msg.cipherText) + if err != nil { + return nil, fmt.Errorf(`jwe.Decrypt: decryption failed: %w`, err) + } + + if h2.Compression() == jwa.Deflate { + buf, err := uncompress(plaintext) + if err != nil { + return nil, fmt.Errorf(`jwe.Derypt: failed to uncompress payload: %w`, err) + } + plaintext = buf + } + + if plaintext == nil { + return nil, fmt.Errorf(`failed to find matching recipient`) + } + + return plaintext, nil +} + +// Parse parses the JWE message into a Message object. The JWE message +// can be either compact or full JSON format. +// +// Parse() currently does not take any options, but the API accepts it +// in anticipation of future addition. +func Parse(buf []byte, _ ...ParseOption) (*Message, error) { + return parseJSONOrCompact(buf, false) +} + +func parseJSONOrCompact(buf []byte, storeProtectedHeaders bool) (*Message, error) { + buf = bytes.TrimSpace(buf) + if len(buf) == 0 { + return nil, fmt.Errorf(`empty buffer`) + } + + if buf[0] == '{' { + return parseJSON(buf, storeProtectedHeaders) + } + return parseCompact(buf, storeProtectedHeaders) +} + +// ParseString is the same as Parse, but takes a string. +func ParseString(s string) (*Message, error) { + return Parse([]byte(s)) +} + +// ParseReader is the same as Parse, but takes an io.Reader. +func ParseReader(src io.Reader) (*Message, error) { + buf, err := io.ReadAll(src) + if err != nil { + return nil, fmt.Errorf(`failed to read from io.Reader: %w`, err) + } + return Parse(buf) +} + +func parseJSON(buf []byte, storeProtectedHeaders bool) (*Message, error) { + m := NewMessage() + m.storeProtectedHeaders = storeProtectedHeaders + if err := json.Unmarshal(buf, &m); err != nil { + return nil, fmt.Errorf(`failed to parse JSON: %w`, err) + } + return m, nil +} + +func parseCompact(buf []byte, storeProtectedHeaders bool) (*Message, error) { + parts := bytes.Split(buf, []byte{'.'}) + if len(parts) != 5 { + return nil, fmt.Errorf(`compact JWE format must have five parts (%d)`, len(parts)) + } + + hdrbuf, err := base64.Decode(parts[0]) + if err != nil { + return nil, fmt.Errorf(`failed to parse first part of compact form: %w`, err) + } + + protected := NewHeaders() + if err := json.Unmarshal(hdrbuf, protected); err != nil { + return nil, fmt.Errorf(`failed to parse header JSON: %w`, err) + } + + ivbuf, err := base64.Decode(parts[2]) + if err != nil { + return nil, fmt.Errorf(`failed to base64 decode iv: %w`, err) + } + + ctbuf, err := base64.Decode(parts[3]) + if err != nil { + return nil, fmt.Errorf(`failed to base64 decode content: %w`, err) + } + + tagbuf, err := base64.Decode(parts[4]) + if err != nil { + return nil, fmt.Errorf(`failed to base64 decode tag: %w`, err) + } + + m := NewMessage() + if err := m.Set(CipherTextKey, ctbuf); err != nil { + return nil, fmt.Errorf(`failed to set %s: %w`, CipherTextKey, err) + } + if err := m.Set(InitializationVectorKey, ivbuf); err != nil { + return nil, fmt.Errorf(`failed to set %s: %w`, InitializationVectorKey, err) + } + if err := m.Set(ProtectedHeadersKey, protected); err != nil { + return nil, fmt.Errorf(`failed to set %s: %w`, ProtectedHeadersKey, err) + } + + if err := m.makeDummyRecipient(string(parts[1]), protected); err != nil { + return nil, fmt.Errorf(`failed to setup recipient: %w`, err) + } + + if err := m.Set(TagKey, tagbuf); err != nil { + return nil, fmt.Errorf(`failed to set %s: %w`, TagKey, err) + } + + if storeProtectedHeaders { + // This is later used for decryption. + m.rawProtectedHeaders = parts[0] + } + + return m, nil +} + +// RegisterCustomField allows users to specify that a private field +// be decoded as an instance of the specified type. This option has +// a global effect. +// +// For example, suppose you have a custom field `x-birthday`, which +// you want to represent as a string formatted in RFC3339 in JSON, +// but want it back as `time.Time`. +// +// In that case you would register a custom field as follows +// +// jwe.RegisterCustomField(`x-birthday`, timeT) +// +// Then `hdr.Get("x-birthday")` will still return an `interface{}`, +// but you can convert its type to `time.Time` +// +// bdayif, _ := hdr.Get(`x-birthday`) +// bday := bdayif.(time.Time) +func RegisterCustomField(name string, object interface{}) { + registry.Register(name, object) +} diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jwe/key_provider.go b/vendor/github.com/lestrrat-go/jwx/v2/jwe/key_provider.go new file mode 100644 index 0000000..5302c39 --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwe/key_provider.go @@ -0,0 +1,161 @@ +package jwe + +import ( + "context" + "fmt" + "sync" + + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwk" +) + +// KeyProvider is responsible for providing key(s) to encrypt or decrypt a payload. +// Multiple `jwe.KeyProvider`s can be passed to `jwe.Encrypt()` or `jwe.Decrypt()` +// +// `jwe.Encrypt()` can only accept static key providers via `jwe.WithKey()`, +// while `jwe.Derypt()` can accept `jwe.WithKey()`, `jwe.WithKeySet()`, +// and `jwe.WithKeyProvider()`. +// +// Understanding how this works is crucial to learn how this package works. +// Here we will use `jwe.Decrypt()` as an example to show how the `KeyProvider` +// works. +// +// `jwe.Encrypt()` is straightforward: the content encryption key is encrypted +// using the provided keys, and JWS recipient objects are created for each. +// +// `jwe.Decrypt()` is a bit more involved, because there are cases you +// will want to compute/deduce/guess the keys that you would like to +// use for decryption. +// +// The first thing that `jwe.Decrypt()` needs to do is to collect the +// KeyProviders from the option list that the user provided (presented in pseudocode): +// +// keyProviders := filterKeyProviders(options) +// +// Then, remember that a JWE message may contain multiple recipients in the +// message. For each recipient, we call on the KeyProviders to give us +// the key(s) to use on this signature: +// +// for r in msg.Recipients { +// for kp in keyProviders { +// kp.FetcKeys(ctx, sink, r, msg) +// ... +// } +// } +// +// The `sink` argument passed to the KeyProvider is a temporary storage +// for the keys (either a jwk.Key or a "raw" key). The `KeyProvider` +// is responsible for sending keys into the `sink`. +// +// When called, the `KeyProvider` created by `jwe.WithKey()` sends the same key, +// `jwe.WithKeySet()` sends keys that matches a particular `kid` and `alg`, +// and finally `jwe.WithKeyProvider()` allows you to execute arbitrary +// logic to provide keys. If you are providing a custom `KeyProvider`, +// you should execute the necessary checks or retrieval of keys, and +// then send the key(s) to the sink: +// +// sink.Key(alg, key) +// +// These keys are then retrieved and tried for each signature, until +// a match is found: +// +// keys := sink.Keys() +// for key in keys { +// if decryptJWEKey(recipient.EncryptedKey(), key) { +// return OK +// } +// } +type KeyProvider interface { + FetchKeys(context.Context, KeySink, Recipient, *Message) error +} + +// KeySink is a data storage where `jwe.KeyProvider` objects should +// send their keys to. +type KeySink interface { + Key(jwa.KeyEncryptionAlgorithm, interface{}) +} + +type algKeyPair struct { + alg jwa.KeyAlgorithm + key interface{} +} + +type algKeySink struct { + mu sync.Mutex + list []algKeyPair +} + +func (s *algKeySink) Key(alg jwa.KeyEncryptionAlgorithm, key interface{}) { + s.mu.Lock() + s.list = append(s.list, algKeyPair{alg, key}) + s.mu.Unlock() +} + +type staticKeyProvider struct { + alg jwa.KeyEncryptionAlgorithm + key interface{} +} + +func (kp *staticKeyProvider) FetchKeys(_ context.Context, sink KeySink, _ Recipient, _ *Message) error { + sink.Key(kp.alg, kp.key) + return nil +} + +type keySetProvider struct { + set jwk.Set + requireKid bool +} + +func (kp *keySetProvider) selectKey(sink KeySink, key jwk.Key, _ Recipient, _ *Message) error { + if usage := key.KeyUsage(); usage != "" && usage != jwk.ForEncryption.String() { + return nil + } + + if v := key.Algorithm(); v.String() != "" { + var alg jwa.KeyEncryptionAlgorithm + if err := alg.Accept(v); err != nil { + return fmt.Errorf(`invalid key encryption algorithm %s: %w`, key.Algorithm(), err) + } + + sink.Key(alg, key) + return nil + } + + return nil +} + +func (kp *keySetProvider) FetchKeys(_ context.Context, sink KeySink, r Recipient, msg *Message) error { + if kp.requireKid { + var key jwk.Key + + wantedKid := r.Headers().KeyID() + if wantedKid == "" { + return fmt.Errorf(`failed to find matching key: no key ID ("kid") specified in token but multiple keys available in key set`) + } + // Otherwise we better be able to look up the key, baby. + v, ok := kp.set.LookupKeyID(wantedKid) + if !ok { + return fmt.Errorf(`failed to find key with key ID %q in key set`, wantedKid) + } + key = v + + return kp.selectKey(sink, key, r, msg) + } + + for i := 0; i < kp.set.Len(); i++ { + key, _ := kp.set.Key(i) + if err := kp.selectKey(sink, key, r, msg); err != nil { + continue + } + } + return nil +} + +// KeyProviderFunc is a type of KeyProvider that is implemented by +// a single function. You can use this to create ad-hoc `KeyProvider` +// instances. +type KeyProviderFunc func(context.Context, KeySink, Recipient, *Message) error + +func (kp KeyProviderFunc) FetchKeys(ctx context.Context, sink KeySink, r Recipient, msg *Message) error { + return kp(ctx, sink, r, msg) +} diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jwe/message.go b/vendor/github.com/lestrrat-go/jwx/v2/jwe/message.go new file mode 100644 index 0000000..0088082 --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwe/message.go @@ -0,0 +1,547 @@ +package jwe + +import ( + "context" + "fmt" + "sort" + "strings" + + "github.com/lestrrat-go/jwx/v2/internal/base64" + "github.com/lestrrat-go/jwx/v2/internal/json" + "github.com/lestrrat-go/jwx/v2/internal/pool" +) + +// NewRecipient creates a Recipient object +func NewRecipient() Recipient { + return &stdRecipient{ + headers: NewHeaders(), + } +} + +func (r *stdRecipient) SetHeaders(h Headers) error { + r.headers = h + return nil +} + +func (r *stdRecipient) SetEncryptedKey(v []byte) error { + r.encryptedKey = v + return nil +} + +func (r *stdRecipient) Headers() Headers { + return r.headers +} + +func (r *stdRecipient) EncryptedKey() []byte { + return r.encryptedKey +} + +type recipientMarshalProxy struct { + Headers Headers `json:"header"` + EncryptedKey string `json:"encrypted_key"` +} + +func (r *stdRecipient) UnmarshalJSON(buf []byte) error { + var proxy recipientMarshalProxy + proxy.Headers = NewHeaders() + if err := json.Unmarshal(buf, &proxy); err != nil { + return fmt.Errorf(`failed to unmarshal json into recipient: %w`, err) + } + + r.headers = proxy.Headers + decoded, err := base64.DecodeString(proxy.EncryptedKey) + if err != nil { + return fmt.Errorf(`failed to decode "encrypted_key": %w`, err) + } + r.encryptedKey = decoded + return nil +} + +func (r *stdRecipient) MarshalJSON() ([]byte, error) { + buf := pool.GetBytesBuffer() + defer pool.ReleaseBytesBuffer(buf) + + buf.WriteString(`{"header":`) + hdrbuf, err := r.headers.MarshalJSON() + if err != nil { + return nil, fmt.Errorf(`failed to marshal recipient header: %w`, err) + } + buf.Write(hdrbuf) + buf.WriteString(`,"encrypted_key":"`) + buf.WriteString(base64.EncodeToString(r.encryptedKey)) + buf.WriteString(`"}`) + + ret := make([]byte, buf.Len()) + copy(ret, buf.Bytes()) + return ret, nil +} + +// NewMessage creates a new message +func NewMessage() *Message { + return &Message{} +} + +func (m *Message) AuthenticatedData() []byte { + return m.authenticatedData +} + +func (m *Message) CipherText() []byte { + return m.cipherText +} + +func (m *Message) InitializationVector() []byte { + return m.initializationVector +} + +func (m *Message) Tag() []byte { + return m.tag +} + +func (m *Message) ProtectedHeaders() Headers { + return m.protectedHeaders +} + +func (m *Message) Recipients() []Recipient { + return m.recipients +} + +func (m *Message) UnprotectedHeaders() Headers { + return m.unprotectedHeaders +} + +const ( + AuthenticatedDataKey = "aad" + CipherTextKey = "ciphertext" + CountKey = "p2c" + InitializationVectorKey = "iv" + ProtectedHeadersKey = "protected" + RecipientsKey = "recipients" + SaltKey = "p2s" + TagKey = "tag" + UnprotectedHeadersKey = "unprotected" + HeadersKey = "header" + EncryptedKeyKey = "encrypted_key" +) + +func (m *Message) Set(k string, v interface{}) error { + switch k { + case AuthenticatedDataKey: + buf, ok := v.([]byte) + if !ok { + return fmt.Errorf(`invalid value %T for %s key`, v, AuthenticatedDataKey) + } + m.authenticatedData = buf + case CipherTextKey: + buf, ok := v.([]byte) + if !ok { + return fmt.Errorf(`invalid value %T for %s key`, v, CipherTextKey) + } + m.cipherText = buf + case InitializationVectorKey: + buf, ok := v.([]byte) + if !ok { + return fmt.Errorf(`invalid value %T for %s key`, v, InitializationVectorKey) + } + m.initializationVector = buf + case ProtectedHeadersKey: + cv, ok := v.(Headers) + if !ok { + return fmt.Errorf(`invalid value %T for %s key`, v, ProtectedHeadersKey) + } + m.protectedHeaders = cv + case RecipientsKey: + cv, ok := v.([]Recipient) + if !ok { + return fmt.Errorf(`invalid value %T for %s key`, v, RecipientsKey) + } + m.recipients = cv + case TagKey: + buf, ok := v.([]byte) + if !ok { + return fmt.Errorf(`invalid value %T for %s key`, v, TagKey) + } + m.tag = buf + case UnprotectedHeadersKey: + cv, ok := v.(Headers) + if !ok { + return fmt.Errorf(`invalid value %T for %s key`, v, UnprotectedHeadersKey) + } + m.unprotectedHeaders = cv + default: + if m.unprotectedHeaders == nil { + m.unprotectedHeaders = NewHeaders() + } + return m.unprotectedHeaders.Set(k, v) + } + return nil +} + +type messageMarshalProxy struct { + AuthenticatedData string `json:"aad,omitempty"` + CipherText string `json:"ciphertext"` + InitializationVector string `json:"iv,omitempty"` + ProtectedHeaders json.RawMessage `json:"protected"` + Recipients []json.RawMessage `json:"recipients,omitempty"` + Tag string `json:"tag,omitempty"` + UnprotectedHeaders Headers `json:"unprotected,omitempty"` + + // For flattened structure. Headers is NOT a Headers type, + // so that we can detect its presence by checking proxy.Headers != nil + Headers json.RawMessage `json:"header,omitempty"` + EncryptedKey string `json:"encrypted_key,omitempty"` +} + +type jsonKV struct { + Key string + Value string +} + +func (m *Message) MarshalJSON() ([]byte, error) { + // This is slightly convoluted, but we need to encode the + // protected headers, so we do it by hand + buf := pool.GetBytesBuffer() + defer pool.ReleaseBytesBuffer(buf) + enc := json.NewEncoder(buf) + + var fields []jsonKV + + if cipherText := m.CipherText(); len(cipherText) > 0 { + buf.Reset() + if err := enc.Encode(base64.EncodeToString(cipherText)); err != nil { + return nil, fmt.Errorf(`failed to encode %s field: %w`, CipherTextKey, err) + } + fields = append(fields, jsonKV{ + Key: CipherTextKey, + Value: strings.TrimSpace(buf.String()), + }) + } + + if iv := m.InitializationVector(); len(iv) > 0 { + buf.Reset() + if err := enc.Encode(base64.EncodeToString(iv)); err != nil { + return nil, fmt.Errorf(`failed to encode %s field: %w`, InitializationVectorKey, err) + } + fields = append(fields, jsonKV{ + Key: InitializationVectorKey, + Value: strings.TrimSpace(buf.String()), + }) + } + + var encodedProtectedHeaders []byte + if h := m.ProtectedHeaders(); h != nil { + v, err := h.Encode() + if err != nil { + return nil, fmt.Errorf(`failed to encode protected headers: %w`, err) + } + + encodedProtectedHeaders = v + if len(encodedProtectedHeaders) <= 2 { // '{}' + encodedProtectedHeaders = nil + } else { + fields = append(fields, jsonKV{ + Key: ProtectedHeadersKey, + Value: fmt.Sprintf("%q", encodedProtectedHeaders), + }) + } + } + + if aad := m.AuthenticatedData(); len(aad) > 0 { + aad = base64.Encode(aad) + if encodedProtectedHeaders != nil { + tmp := append(encodedProtectedHeaders, '.') + aad = append(tmp, aad...) + } + + buf.Reset() + if err := enc.Encode(aad); err != nil { + return nil, fmt.Errorf(`failed to encode %s field: %w`, AuthenticatedDataKey, err) + } + fields = append(fields, jsonKV{ + Key: AuthenticatedDataKey, + Value: strings.TrimSpace(buf.String()), + }) + } + + if recipients := m.Recipients(); len(recipients) > 0 { + if len(recipients) == 1 { // Use flattened format + if hdrs := recipients[0].Headers(); hdrs != nil { + buf.Reset() + if err := enc.Encode(hdrs); err != nil { + return nil, fmt.Errorf(`failed to encode %s field: %w`, HeadersKey, err) + } + fields = append(fields, jsonKV{ + Key: HeadersKey, + Value: strings.TrimSpace(buf.String()), + }) + } + + if ek := recipients[0].EncryptedKey(); len(ek) > 0 { + buf.Reset() + if err := enc.Encode(base64.EncodeToString(ek)); err != nil { + return nil, fmt.Errorf(`failed to encode %s field: %w`, EncryptedKeyKey, err) + } + fields = append(fields, jsonKV{ + Key: EncryptedKeyKey, + Value: strings.TrimSpace(buf.String()), + }) + } + } else { + buf.Reset() + if err := enc.Encode(recipients); err != nil { + return nil, fmt.Errorf(`failed to encode %s field: %w`, RecipientsKey, err) + } + fields = append(fields, jsonKV{ + Key: RecipientsKey, + Value: strings.TrimSpace(buf.String()), + }) + } + } + + if tag := m.Tag(); len(tag) > 0 { + buf.Reset() + if err := enc.Encode(base64.EncodeToString(tag)); err != nil { + return nil, fmt.Errorf(`failed to encode %s field: %w`, TagKey, err) + } + fields = append(fields, jsonKV{ + Key: TagKey, + Value: strings.TrimSpace(buf.String()), + }) + } + + if h := m.UnprotectedHeaders(); h != nil { + unprotected, err := json.Marshal(h) + if err != nil { + return nil, fmt.Errorf(`failed to encode unprotected headers: %w`, err) + } + + if len(unprotected) > 2 { + fields = append(fields, jsonKV{ + Key: UnprotectedHeadersKey, + Value: fmt.Sprintf("%q", unprotected), + }) + } + } + + sort.Slice(fields, func(i, j int) bool { + return fields[i].Key < fields[j].Key + }) + buf.Reset() + fmt.Fprintf(buf, `{`) + for i, kv := range fields { + if i > 0 { + fmt.Fprintf(buf, `,`) + } + fmt.Fprintf(buf, `%q:%s`, kv.Key, kv.Value) + } + fmt.Fprintf(buf, `}`) + + ret := make([]byte, buf.Len()) + copy(ret, buf.Bytes()) + return ret, nil +} + +func (m *Message) UnmarshalJSON(buf []byte) error { + var proxy messageMarshalProxy + proxy.UnprotectedHeaders = NewHeaders() + + if err := json.Unmarshal(buf, &proxy); err != nil { + return fmt.Errorf(`failed to unmashal JSON into message: %w`, err) + } + + // Get the string value + var protectedHeadersStr string + if err := json.Unmarshal(proxy.ProtectedHeaders, &protectedHeadersStr); err != nil { + return fmt.Errorf(`failed to decode protected headers (1): %w`, err) + } + + // It's now in _quoted_ base64 string. Decode it + protectedHeadersRaw, err := base64.DecodeString(protectedHeadersStr) + if err != nil { + return fmt.Errorf(`failed to base64 decoded protected headers buffer: %w`, err) + } + + h := NewHeaders() + if err := json.Unmarshal(protectedHeadersRaw, h); err != nil { + return fmt.Errorf(`failed to decode protected headers (2): %w`, err) + } + + // if this were a flattened message, we would see a "header" and "ciphertext" + // field. TODO: do both of these conditions need to meet, or just one? + if proxy.Headers != nil || len(proxy.EncryptedKey) > 0 { + recipient := NewRecipient() + hdrs := NewHeaders() + if err := json.Unmarshal(proxy.Headers, hdrs); err != nil { + return fmt.Errorf(`failed to decode headers field: %w`, err) + } + + if err := recipient.SetHeaders(hdrs); err != nil { + return fmt.Errorf(`failed to set new headers: %w`, err) + } + + if v := proxy.EncryptedKey; len(v) > 0 { + buf, err := base64.DecodeString(v) + if err != nil { + return fmt.Errorf(`failed to decode encrypted key: %w`, err) + } + if err := recipient.SetEncryptedKey(buf); err != nil { + return fmt.Errorf(`failed to set encrypted key: %w`, err) + } + } + + m.recipients = append(m.recipients, recipient) + } else { + for i, recipientbuf := range proxy.Recipients { + recipient := NewRecipient() + if err := json.Unmarshal(recipientbuf, recipient); err != nil { + return fmt.Errorf(`failed to decode recipient at index %d: %w`, i, err) + } + + m.recipients = append(m.recipients, recipient) + } + } + + if src := proxy.AuthenticatedData; len(src) > 0 { + v, err := base64.DecodeString(src) + if err != nil { + return fmt.Errorf(`failed to decode "aad": %w`, err) + } + m.authenticatedData = v + } + + if src := proxy.CipherText; len(src) > 0 { + v, err := base64.DecodeString(src) + if err != nil { + return fmt.Errorf(`failed to decode "ciphertext": %w`, err) + } + m.cipherText = v + } + + if src := proxy.InitializationVector; len(src) > 0 { + v, err := base64.DecodeString(src) + if err != nil { + return fmt.Errorf(`failed to decode "iv": %w`, err) + } + m.initializationVector = v + } + + if src := proxy.Tag; len(src) > 0 { + v, err := base64.DecodeString(src) + if err != nil { + return fmt.Errorf(`failed to decode "tag": %w`, err) + } + m.tag = v + } + + m.protectedHeaders = h + if m.storeProtectedHeaders { + // this is later used for decryption + m.rawProtectedHeaders = base64.Encode(protectedHeadersRaw) + } + + if iz, ok := proxy.UnprotectedHeaders.(isZeroer); ok { + if !iz.isZero() { + m.unprotectedHeaders = proxy.UnprotectedHeaders + } + } + + if len(m.recipients) == 0 { + if err := m.makeDummyRecipient(proxy.EncryptedKey, m.protectedHeaders); err != nil { + return fmt.Errorf(`failed to setup recipient: %w`, err) + } + } + + return nil +} + +func (m *Message) makeDummyRecipient(enckeybuf string, protected Headers) error { + // Recipients in this case should not contain the content encryption key, + // so move that out + hdrs, err := protected.Clone(context.TODO()) + if err != nil { + return fmt.Errorf(`failed to clone headers: %w`, err) + } + + if err := hdrs.Remove(ContentEncryptionKey); err != nil { + return fmt.Errorf(`failed to remove %#v from public header: %w`, ContentEncryptionKey, err) + } + + enckey, err := base64.DecodeString(enckeybuf) + if err != nil { + return fmt.Errorf(`failed to decode encrypted key: %w`, err) + } + + if err := m.Set(RecipientsKey, []Recipient{ + &stdRecipient{ + headers: hdrs, + encryptedKey: enckey, + }, + }); err != nil { + return fmt.Errorf(`failed to set %s: %w`, RecipientsKey, err) + } + return nil +} + +// Compact generates a JWE message in compact serialization format from a +// `*jwe.Message` object. The object contain exactly one recipient, or +// an error is returned. +// +// This function currently does not take any options, but the function +// signature contains `options` for possible future expansion of the API +func Compact(m *Message, _ ...CompactOption) ([]byte, error) { + if len(m.recipients) != 1 { + return nil, fmt.Errorf(`wrong number of recipients for compact serialization`) + } + + recipient := m.recipients[0] + + // The protected header must be a merge between the message-wide + // protected header AND the recipient header + + // There's something wrong if m.protectedHeaders is nil, but + // it could happen + if m.protectedHeaders == nil { + return nil, fmt.Errorf(`invalid protected header`) + } + + ctx := context.TODO() + hcopy, err := m.protectedHeaders.Clone(ctx) + if err != nil { + return nil, fmt.Errorf(`failed to copy protected header: %w`, err) + } + hcopy, err = hcopy.Merge(ctx, m.unprotectedHeaders) + if err != nil { + return nil, fmt.Errorf(`failed to merge unprotected header: %w`, err) + } + hcopy, err = hcopy.Merge(ctx, recipient.Headers()) + if err != nil { + return nil, fmt.Errorf(`failed to merge recipient header: %w`, err) + } + + protected, err := hcopy.Encode() + if err != nil { + return nil, fmt.Errorf(`failed to encode header: %w`, err) + } + + encryptedKey := base64.Encode(recipient.EncryptedKey()) + iv := base64.Encode(m.initializationVector) + cipher := base64.Encode(m.cipherText) + tag := base64.Encode(m.tag) + + buf := pool.GetBytesBuffer() + defer pool.ReleaseBytesBuffer(buf) + + buf.Grow(len(protected) + len(encryptedKey) + len(iv) + len(cipher) + len(tag) + 4) + buf.Write(protected) + buf.WriteByte('.') + buf.Write(encryptedKey) + buf.WriteByte('.') + buf.Write(iv) + buf.WriteByte('.') + buf.Write(cipher) + buf.WriteByte('.') + buf.Write(tag) + + result := make([]byte, buf.Len()) + copy(result, buf.Bytes()) + return result, nil +} diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jwe/options.go b/vendor/github.com/lestrrat-go/jwx/v2/jwe/options.go new file mode 100644 index 0000000..f31c635 --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwe/options.go @@ -0,0 +1,107 @@ +package jwe + +import ( + "context" + + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwk" + "github.com/lestrrat-go/option" +) + +// Specify contents of the protected header. Some fields such as +// "enc" and "zip" will be overwritten when encryption is performed. +// +// There is no equivalent for unprotected headers in this implementation +func WithProtectedHeaders(h Headers) EncryptOption { + cloned, _ := h.Clone(context.Background()) + return &encryptOption{option.New(identProtectedHeaders{}, cloned)} +} + +type withKey struct { + alg jwa.KeyAlgorithm + key interface{} + headers Headers +} + +type WithKeySuboption interface { + Option + withKeySuboption() +} + +type withKeySuboption struct { + Option +} + +func (*withKeySuboption) withKeySuboption() {} + +// WithPerRecipientHeaders is used to pass header values for each recipient. +// Note that these headers are by definition _unprotected_. +func WithPerRecipientHeaders(hdr Headers) WithKeySuboption { + return &withKeySuboption{option.New(identPerRecipientHeaders{}, hdr)} +} + +// WithKey is used to pass a static algorithm/key pair to either `jwe.Encrypt()` or `jwe.Decrypt()`. +// either a raw key or `jwk.Key` may be passed as `key`. +// +// The `alg` parameter is the identifier for the key encryption algorithm that should be used. +// It is of type `jwa.KeyAlgorithm` but in reality you can only pass `jwa.SignatureAlgorithm` +// types. It is this way so that the value in `(jwk.Key).Algorithm()` can be directly +// passed to the option. If you specify other algorithm types such as `jwa.ContentEncryptionAlgorithm`, +// then you will get an error when `jwe.Encrypt()` or `jwe.Decrypt()` is executed. +// +// Unlike `jwe.WithKeySet()`, the `kid` field does not need to match for the key +// to be tried. +func WithKey(alg jwa.KeyAlgorithm, key interface{}, options ...WithKeySuboption) EncryptDecryptOption { + var hdr Headers + for _, option := range options { + //nolint:forcetypeassert + switch option.Ident() { + case identPerRecipientHeaders{}: + hdr = option.Value().(Headers) + } + } + + return &encryptDecryptOption{option.New(identKey{}, &withKey{ + alg: alg, + key: key, + headers: hdr, + })} +} + +func WithKeySet(set jwk.Set, options ...WithKeySetSuboption) DecryptOption { + requireKid := true + for _, option := range options { + //nolint:forcetypeassert + switch option.Ident() { + case identRequireKid{}: + requireKid = option.Value().(bool) + } + } + + return WithKeyProvider(&keySetProvider{ + set: set, + requireKid: requireKid, + }) +} + +// WithJSON specifies that the result of `jwe.Encrypt()` is serialized in +// JSON format. +// +// If you pass multiple keys to `jwe.Encrypt()`, it will fail unless +// you also pass this option. +func WithJSON(options ...WithJSONSuboption) EncryptOption { + var pretty bool + for _, option := range options { + //nolint:forcetypeassert + switch option.Ident() { + case identPretty{}: + pretty = option.Value().(bool) + } + } + + format := fmtJSON + if pretty { + format = fmtJSONPretty + } + return &encryptOption{option.New(identSerialization{}, format)} +} diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jwe/options.yaml b/vendor/github.com/lestrrat-go/jwx/v2/jwe/options.yaml new file mode 100644 index 0000000..84f8966 --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwe/options.yaml @@ -0,0 +1,122 @@ +package_name: jwe +output: jwe/options_gen.go +interfaces: + - name: CompactOption + comment: | + CompactOption describes options that can be passed to `jwe.Compact` + - name: DecryptOption + comment: | + DecryptOption describes options that can be passed to `jwe.Decrypt` + - name: EncryptOption + comment: | + EncryptOption describes options that can be passed to `jwe.Encrypt` + - name: EncryptDecryptOption + methods: + - encryptOption + - decryptOption + comment: | + EncryptDecryptOption describes options that can be passed to either `jwe.Encrypt` or `jwe.Decrypt` + - name: WithJSONSuboption + concrete_type: withJSONSuboption + comment: | + JSONSuboption describes suboptions that can be passed to `jwe.WithJSON()` option + - name: WithKeySetSuboption + comment: | + WithKeySetSuboption is a suboption passed to the WithKeySet() option + - name: ParseOption + methods: + - readFileOption + comment: | + ReadFileOption is a type of `Option` that can be passed to `jwe.Parse` + - name: ReadFileOption + comment: | + ReadFileOption is a type of `Option` that can be passed to `jwe.ReadFile` +options: + - ident: Key + skip_option: true + - ident: Pretty + skip_option: true + - ident: ProtectedHeaders + skip_option: true + - ident: PerRecipientHeaders + skip_option: true + - ident: KeyProvider + interface: DecryptOption + argument_type: KeyProvider + - ident: Serialization + option_name: WithCompact + interface: EncryptOption + constant_value: fmtCompact + comment: | + WithCompact specifies that the result of `jwe.Encrypt()` is serialized in + compact format. + + By default `jwe.Encrypt()` will opt to use compact format, so you usually + do not need to specify this option other than to be explicit about it + - ident: Compress + interface: EncryptOption + argument_type: jwa.CompressionAlgorithm + comment: | + WithCompress specifies the compression algorithm to use when encrypting + a payload using `jwe.Encrypt` (Yes, we know it can only be "" or "DEF", + but the way the specification is written it could allow for more options, + and therefore this option takes an argument) + - ident: ContentEncryptionAlgorithm + interface: EncryptOption + option_name: WithContentEncryption + argument_type: jwa.ContentEncryptionAlgorithm + comment: | + WithContentEncryptionAlgorithm specifies the algorithm to encrypt the + JWE message content with. If not provided, `jwa.A256GCM` is used. + - ident: Message + interface: DecryptOption + argument_type: '*Message' + comment: | + WithMessage provides a message object to be populated by `jwe.Decrpt` + Using this option allows you to decrypt AND obtain the `jwe.Message` + in one go. + + Note that you should NOT be using the message object for anything other + than inspecting its contents. Particularly, do not expect the message + reliable when you call `Decrypt` on it. `(jwe.Message).Decrypt` is + slated to be deprecated in the next major version. + - ident: RequireKid + interface: WithKeySetSuboption + argument_type: bool + comment: | + WithrequiredKid specifies whether the keys in the jwk.Set should + only be matched if the target JWE message's Key ID and the Key ID + in the given key matches. + - ident: Pretty + interface: WithJSONSuboption + argument_type: bool + comment: | + WithPretty specifies whether the JSON output should be formatted and + indented + - ident: MergeProtectedHeaders + interface: EncryptOption + argument_type: bool + comment: | + WithMergeProtectedHeaders specify that when given multiple headers + as options to `jwe.Encrypt`, these headers should be merged instead + of overwritten + - ident: FS + interface: ReadFileOption + argument_type: fs.FS + comment: | + WithFS specifies the source `fs.FS` object to read the file from. + - ident: KeyUsed + interface: DecryptOption + argument_type: 'interface{}' + comment: | + WithKeyUsed allows you to specify the `jwe.Decrypt()` function to + return the key used for decryption. This may be useful when + you specify multiple key sources or if you pass a `jwk.Set` + and you want to know which key was successful at decrypting the + signature. + + `v` must be a pointer to an empty `interface{}`. Do not use + `jwk.Key` here unless you are 100% sure that all keys that you + have provided are instances of `jwk.Key` (remember that the + jwx API allows users to specify a raw key such as *rsa.PublicKey) + diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jwe/options_gen.go b/vendor/github.com/lestrrat-go/jwx/v2/jwe/options_gen.go new file mode 100644 index 0000000..41b4389 --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwe/options_gen.go @@ -0,0 +1,255 @@ +// This file is auto-generated by internal/cmd/genoptions/main.go. DO NOT EDIT + +package jwe + +import ( + "io/fs" + + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/option" +) + +type Option = option.Interface + +// CompactOption describes options that can be passed to `jwe.Compact` +type CompactOption interface { + Option + compactOption() +} + +type compactOption struct { + Option +} + +func (*compactOption) compactOption() {} + +// DecryptOption describes options that can be passed to `jwe.Decrypt` +type DecryptOption interface { + Option + decryptOption() +} + +type decryptOption struct { + Option +} + +func (*decryptOption) decryptOption() {} + +// EncryptDecryptOption describes options that can be passed to either `jwe.Encrypt` or `jwe.Decrypt` +type EncryptDecryptOption interface { + Option + encryptOption() + decryptOption() +} + +type encryptDecryptOption struct { + Option +} + +func (*encryptDecryptOption) encryptOption() {} + +func (*encryptDecryptOption) decryptOption() {} + +// EncryptOption describes options that can be passed to `jwe.Encrypt` +type EncryptOption interface { + Option + encryptOption() +} + +type encryptOption struct { + Option +} + +func (*encryptOption) encryptOption() {} + +// ReadFileOption is a type of `Option` that can be passed to `jwe.Parse` +type ParseOption interface { + Option + readFileOption() +} + +type parseOption struct { + Option +} + +func (*parseOption) readFileOption() {} + +// ReadFileOption is a type of `Option` that can be passed to `jwe.ReadFile` +type ReadFileOption interface { + Option + readFileOption() +} + +type readFileOption struct { + Option +} + +func (*readFileOption) readFileOption() {} + +// JSONSuboption describes suboptions that can be passed to `jwe.WithJSON()` option +type WithJSONSuboption interface { + Option + withJSONSuboption() +} + +type withJSONSuboption struct { + Option +} + +func (*withJSONSuboption) withJSONSuboption() {} + +// WithKeySetSuboption is a suboption passed to the WithKeySet() option +type WithKeySetSuboption interface { + Option + withKeySetSuboption() +} + +type withKeySetSuboption struct { + Option +} + +func (*withKeySetSuboption) withKeySetSuboption() {} + +type identCompress struct{} +type identContentEncryptionAlgorithm struct{} +type identFS struct{} +type identKey struct{} +type identKeyProvider struct{} +type identKeyUsed struct{} +type identMergeProtectedHeaders struct{} +type identMessage struct{} +type identPerRecipientHeaders struct{} +type identPretty struct{} +type identProtectedHeaders struct{} +type identRequireKid struct{} +type identSerialization struct{} + +func (identCompress) String() string { + return "WithCompress" +} + +func (identContentEncryptionAlgorithm) String() string { + return "WithContentEncryption" +} + +func (identFS) String() string { + return "WithFS" +} + +func (identKey) String() string { + return "WithKey" +} + +func (identKeyProvider) String() string { + return "WithKeyProvider" +} + +func (identKeyUsed) String() string { + return "WithKeyUsed" +} + +func (identMergeProtectedHeaders) String() string { + return "WithMergeProtectedHeaders" +} + +func (identMessage) String() string { + return "WithMessage" +} + +func (identPerRecipientHeaders) String() string { + return "WithPerRecipientHeaders" +} + +func (identPretty) String() string { + return "WithPretty" +} + +func (identProtectedHeaders) String() string { + return "WithProtectedHeaders" +} + +func (identRequireKid) String() string { + return "WithRequireKid" +} + +func (identSerialization) String() string { + return "WithCompact" +} + +// WithCompress specifies the compression algorithm to use when encrypting +// a payload using `jwe.Encrypt` (Yes, we know it can only be "" or "DEF", +// but the way the specification is written it could allow for more options, +// and therefore this option takes an argument) +func WithCompress(v jwa.CompressionAlgorithm) EncryptOption { + return &encryptOption{option.New(identCompress{}, v)} +} + +// WithContentEncryptionAlgorithm specifies the algorithm to encrypt the +// JWE message content with. If not provided, `jwa.A256GCM` is used. +func WithContentEncryption(v jwa.ContentEncryptionAlgorithm) EncryptOption { + return &encryptOption{option.New(identContentEncryptionAlgorithm{}, v)} +} + +// WithFS specifies the source `fs.FS` object to read the file from. +func WithFS(v fs.FS) ReadFileOption { + return &readFileOption{option.New(identFS{}, v)} +} + +func WithKeyProvider(v KeyProvider) DecryptOption { + return &decryptOption{option.New(identKeyProvider{}, v)} +} + +// WithKeyUsed allows you to specify the `jwe.Decrypt()` function to +// return the key used for decryption. This may be useful when +// you specify multiple key sources or if you pass a `jwk.Set` +// and you want to know which key was successful at decrypting the +// signature. +// +// `v` must be a pointer to an empty `interface{}`. Do not use +// `jwk.Key` here unless you are 100% sure that all keys that you +// have provided are instances of `jwk.Key` (remember that the +// jwx API allows users to specify a raw key such as *rsa.PublicKey) +func WithKeyUsed(v interface{}) DecryptOption { + return &decryptOption{option.New(identKeyUsed{}, v)} +} + +// WithMergeProtectedHeaders specify that when given multiple headers +// as options to `jwe.Encrypt`, these headers should be merged instead +// of overwritten +func WithMergeProtectedHeaders(v bool) EncryptOption { + return &encryptOption{option.New(identMergeProtectedHeaders{}, v)} +} + +// WithMessage provides a message object to be populated by `jwe.Decrpt` +// Using this option allows you to decrypt AND obtain the `jwe.Message` +// in one go. +// +// Note that you should NOT be using the message object for anything other +// than inspecting its contents. Particularly, do not expect the message +// reliable when you call `Decrypt` on it. `(jwe.Message).Decrypt` is +// slated to be deprecated in the next major version. +func WithMessage(v *Message) DecryptOption { + return &decryptOption{option.New(identMessage{}, v)} +} + +// WithPretty specifies whether the JSON output should be formatted and +// indented +func WithPretty(v bool) WithJSONSuboption { + return &withJSONSuboption{option.New(identPretty{}, v)} +} + +// WithrequiredKid specifies whether the keys in the jwk.Set should +// only be matched if the target JWE message's Key ID and the Key ID +// in the given key matches. +func WithRequireKid(v bool) WithKeySetSuboption { + return &withKeySetSuboption{option.New(identRequireKid{}, v)} +} + +// WithCompact specifies that the result of `jwe.Encrypt()` is serialized in +// compact format. +// +// By default `jwe.Encrypt()` will opt to use compact format, so you usually +// do not need to specify this option other than to be explicit about it +func WithCompact() EncryptOption { + return &encryptOption{option.New(identSerialization{}, fmtCompact)} +} diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jwk/README.md b/vendor/github.com/lestrrat-go/jwx/v2/jwk/README.md new file mode 100644 index 0000000..a5ded40 --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwk/README.md @@ -0,0 +1,223 @@ +# JWK [![Go Reference](https://pkg.go.dev/badge/github.com/lestrrat-go/jwx/v2/jwk.svg)](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk) + +Package jwk implements JWK as described in [RFC7517](https://tools.ietf.org/html/rfc7517). +If you are looking to use JWT wit JWKs, look no further than [github.com/lestrrat-go/jwx](../jwt). + +* Parse and work with RSA/EC/Symmetric/OKP JWK types + * Convert to and from JSON + * Convert to and from raw key types (e.g. *rsa.PrivateKey) +* Ability to keep a JWKS fresh using *jwk.AutoRefersh + +## Supported key types: + +| kty | Curve | Go Key Type | +|:----|:------------------------|:----------------------------------------------| +| RSA | N/A | rsa.PrivateKey / rsa.PublicKey (2) | +| EC | P-256
P-384
P-521
secp256k1 (1) | ecdsa.PrivateKey / ecdsa.PublicKey (2) | +| oct | N/A | []byte | +| OKP | Ed25519 (1) | ed25519.PrivateKey / ed25519.PublicKey (2) | +| | X25519 (1) | (jwx/)x25519.PrivateKey / x25519.PublicKey (2)| + +* Note 1: Experimental +* Note 2: Either value or pointers accepted (e.g. rsa.PrivateKey or *rsa.PrivateKey) + +# Documentation + +Please read the [API reference](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk), or +the how-to style documentation on how to use JWK can be found in the [docs directory](../docs/04-jwk.md). + +# Auto-Refresh a key during a long running process + + +```go +package examples_test + +import ( + "context" + "fmt" + "time" + + "github.com/lestrrat-go/jwx/v2/jwk" +) + +func ExampleJWK_Cache() { + ctx, cancel := context.WithCancel(context.Background()) + + const googleCerts = `https://www.googleapis.com/oauth2/v3/certs` + + // First, set up the `jwk.Cache` object. You need to pass it a + // `context.Context` object to control the lifecycle of the background fetching goroutine. + // + // Note that by default refreshes only happen very 15 minutes at the + // earliest. If you need to control this, use `jwk.WithRefreshWindow()` + c := jwk.NewCache(ctx) + + // Tell *jwk.Cache that we only want to refresh this JWKS + // when it needs to (based on Cache-Control or Expires header from + // the HTTP response). If the calculated minimum refresh interval is less + // than 15 minutes, don't go refreshing any earlier than 15 minutes. + c.Register(googleCerts, jwk.WithMinRefreshInterval(15*time.Minute)) + + // Refresh the JWKS once before getting into the main loop. + // This allows you to check if the JWKS is available before we start + // a long-running program + _, err := c.Refresh(ctx, googleCerts) + if err != nil { + fmt.Printf("failed to refresh google JWKS: %s\n", err) + return + } + + // Pretend that this is your program's main loop +MAIN: + for { + select { + case <-ctx.Done(): + break MAIN + default: + } + keyset, err := c.Get(ctx, googleCerts) + if err != nil { + fmt.Printf("failed to fetch google JWKS: %s\n", err) + return + } + _ = keyset + // The returned `keyset` will always be "reasonably" new. It is important that + // you always call `ar.Fetch()` before using the `keyset` as this is where the refreshing occurs. + // + // By "reasonably" we mean that we cannot guarantee that the keys will be refreshed + // immediately after it has been rotated in the remote source. But it should be close\ + // enough, and should you need to forcefully refresh the token using the `(jwk.Cache).Refresh()` method. + // + // If re-fetching the keyset fails, a cached version will be returned from the previous successful + // fetch upon calling `(jwk.Cache).Fetch()`. + + // Do interesting stuff with the keyset... but here, we just + // sleep for a bit + time.Sleep(time.Second) + + // Because we're a dummy program, we just cancel the loop now. + // If this were a real program, you prosumably loop forever + cancel() + } + // OUTPUT: +} +``` +source: [examples/jwk_cache_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwk_cache_example_test.go) + + +Parse and use a JWK key: + + +```go +package examples_test + +import ( + "bytes" + "context" + "crypto/ecdsa" + "crypto/elliptic" + "fmt" + "log" + + "github.com/lestrrat-go/jwx/v2/internal/json" + "github.com/lestrrat-go/jwx/v2/jwk" +) + +func ExampleJWK_Usage() { + // Use jwk.Cache if you intend to keep reuse the JWKS over and over + set, err := jwk.Fetch(context.Background(), "https://www.googleapis.com/oauth2/v3/certs") + if err != nil { + log.Printf("failed to parse JWK: %s", err) + return + } + + // Key sets can be serialized back to JSON + { + jsonbuf, err := json.Marshal(set) + if err != nil { + log.Printf("failed to marshal key set into JSON: %s", err) + return + } + log.Printf("%s", jsonbuf) + } + + for it := set.Iterate(context.Background()); it.Next(context.Background()); { + pair := it.Pair() + key := pair.Value.(jwk.Key) + + var rawkey interface{} // This is the raw key, like *rsa.PrivateKey or *ecdsa.PrivateKey + if err := key.Raw(&rawkey); err != nil { + log.Printf("failed to create public key: %s", err) + return + } + // Use rawkey for jws.Verify() or whatever. + _ = rawkey + + // You can create jwk.Key from a raw key, too + fromRawKey, err := jwk.FromRaw(rawkey) + if err != nil { + log.Printf("failed to acquire raw key from jwk.Key: %s", err) + return + } + + // Keys can be serialized back to JSON + jsonbuf, err := json.Marshal(key) + if err != nil { + log.Printf("failed to marshal key into JSON: %s", err) + return + } + + fromJSONKey, err := jwk.Parse(jsonbuf) + if err != nil { + log.Printf("failed to parse json: %s", err) + return + } + _ = fromJSONKey + _ = fromRawKey + } + // OUTPUT: +} + +//nolint:govet +func ExampleJWK_MarshalJSON() { + // to get the same values every time, we need to create a static source + // of "randomness" + rdr := bytes.NewReader([]byte("01234567890123456789012345678901234567890123456789ABCDEF")) + raw, err := ecdsa.GenerateKey(elliptic.P384(), rdr) + if err != nil { + fmt.Printf("failed to generate new ECDSA private key: %s\n", err) + return + } + + key, err := jwk.FromRaw(raw) + if err != nil { + fmt.Printf("failed to create ECDSA key: %s\n", err) + return + } + if _, ok := key.(jwk.ECDSAPrivateKey); !ok { + fmt.Printf("expected jwk.ECDSAPrivateKey, got %T\n", key) + return + } + + key.Set(jwk.KeyIDKey, "mykey") + + buf, err := json.MarshalIndent(key, "", " ") + if err != nil { + fmt.Printf("failed to marshal key into JSON: %s\n", err) + return + } + fmt.Printf("%s\n", buf) + + // OUTPUT: + // { + // "crv": "P-384", + // "d": "ODkwMTIzNDU2Nzg5MDEyMz7deMbyLt8g4cjcxozuIoygLLlAeoQ1AfM9TSvxkFHJ", + // "kid": "mykey", + // "kty": "EC", + // "x": "gvvRMqm1w5aHn7sVNA2QUJeOVcedUnmiug6VhU834gzS9k87crVwu9dz7uLOdoQl", + // "y": "7fVF7b6J_6_g6Wu9RuJw8geWxEi5ja9Gp2TSdELm5u2E-M7IF-bsxqcdOj3n1n7N" + // } +} +``` +source: [examples/jwk_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwk_example_test.go) + diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jwk/cache.go b/vendor/github.com/lestrrat-go/jwx/v2/jwk/cache.go new file mode 100644 index 0000000..1c9efae --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwk/cache.go @@ -0,0 +1,348 @@ +package jwk + +import ( + "context" + "fmt" + "io" + "net/http" + "time" + + "github.com/lestrrat-go/httprc" + "github.com/lestrrat-go/iter/arrayiter" + "github.com/lestrrat-go/iter/mapiter" +) + +type Transformer = httprc.Transformer +type HTTPClient = httprc.HTTPClient +type ErrSink = httprc.ErrSink +type Whitelist = httprc.Whitelist + +// Cache is a container that keeps track of Set object by their source URLs. +// The Set objects are stored in memory, and are refreshed automatically +// behind the scenes. +// +// Before retrieving the Set objects, the user must pre-register the +// URLs they intend to use by calling `Register()` +// +// c := jwk.NewCache(ctx) +// c.Register(url, options...) +// +// Once registered, you can call `Get()` to retrieve the Set object. +// +// All JWKS objects that are retrieved via this mechanism should be +// treated read-only, as they are shared among the consumers and this object. +type Cache struct { + cache *httprc.Cache +} + +// PostFetcher is an interface for objects that want to perform +// operations on the `Set` that was fetched. +type PostFetcher interface { + // PostFetch revceives the URL and the JWKS, after a successful + // fetch and parse. + // + // It should return a `Set`, optionally modified, to be stored + // in the cache for subsequent use + PostFetch(string, Set) (Set, error) +} + +// PostFetchFunc is a PostFetcher based on a functon. +type PostFetchFunc func(string, Set) (Set, error) + +func (f PostFetchFunc) PostFetch(u string, set Set) (Set, error) { + return f(u, set) +} + +// httprc.Transofmer that transforms the response into a JWKS +type jwksTransform struct { + postFetch PostFetcher + parseOptions []ParseOption +} + +// Default transform has no postFetch. This can be shared +// by multiple fetchers +var defaultTransform = &jwksTransform{} + +func (t *jwksTransform) Transform(u string, res *http.Response) (interface{}, error) { + buf, err := io.ReadAll(res.Body) + if err != nil { + return nil, fmt.Errorf(`failed to read response body status: %w`, err) + } + + set, err := Parse(buf, t.parseOptions...) + if err != nil { + return nil, fmt.Errorf(`failed to parse JWK set at %q: %w`, u, err) + } + + if pf := t.postFetch; pf != nil { + v, err := pf.PostFetch(u, set) + if err != nil { + return nil, fmt.Errorf(`failed to execute PostFetch: %w`, err) + } + set = v + } + + return set, nil +} + +// NewCache creates a new `jwk.Cache` object. +// +// Please refer to the documentation for `httprc.New` for more +// details. +func NewCache(ctx context.Context, options ...CacheOption) *Cache { + var hrcopts []httprc.CacheOption + for _, option := range options { + //nolint:forcetypeassert + switch option.Ident() { + case identRefreshWindow{}: + hrcopts = append(hrcopts, httprc.WithRefreshWindow(option.Value().(time.Duration))) + case identErrSink{}: + hrcopts = append(hrcopts, httprc.WithErrSink(option.Value().(ErrSink))) + } + } + + return &Cache{ + cache: httprc.NewCache(ctx, hrcopts...), + } +} + +// Register registers a URL to be managed by the cache. URLs must +// be registered before issuing `Get` +// +// This method is almost identical to `(httprc.Cache).Register`, except +// it accepts some extra options. +// +// Use `jwk.WithParser` to configure how the JWKS should be parsed, +// such as passing it extra options. +// +// Please refer to the documentation for `(httprc.Cache).Register` for more +// details. +func (c *Cache) Register(u string, options ...RegisterOption) error { + var hrropts []httprc.RegisterOption + var pf PostFetcher + var parseOptions []ParseOption + + // Note: we do NOT accept Transform option + for _, option := range options { + if parseOpt, ok := option.(ParseOption); ok { + parseOptions = append(parseOptions, parseOpt) + continue + } + + //nolint:forcetypeassert + switch option.Ident() { + case identHTTPClient{}: + hrropts = append(hrropts, httprc.WithHTTPClient(option.Value().(HTTPClient))) + case identRefreshInterval{}: + hrropts = append(hrropts, httprc.WithRefreshInterval(option.Value().(time.Duration))) + case identMinRefreshInterval{}: + hrropts = append(hrropts, httprc.WithMinRefreshInterval(option.Value().(time.Duration))) + case identFetchWhitelist{}: + hrropts = append(hrropts, httprc.WithWhitelist(option.Value().(httprc.Whitelist))) + case identPostFetcher{}: + pf = option.Value().(PostFetcher) + } + } + + var t *jwksTransform + if pf == nil && len(parseOptions) == 0 { + t = defaultTransform + } else { + // User-supplied PostFetcher is attached to the transformer + t = &jwksTransform{ + postFetch: pf, + parseOptions: parseOptions, + } + } + + // Set the transfomer at the end so that nobody can override it + hrropts = append(hrropts, httprc.WithTransformer(t)) + return c.cache.Register(u, hrropts...) +} + +// Get returns the stored JWK set (`Set`) from the cache. +// +// Please refer to the documentation for `(httprc.Cache).Get` for more +// details. +func (c *Cache) Get(ctx context.Context, u string) (Set, error) { + v, err := c.cache.Get(ctx, u) + if err != nil { + return nil, err + } + + set, ok := v.(Set) + if !ok { + return nil, fmt.Errorf(`cached object is not a Set (was %T)`, v) + } + return set, nil +} + +// Refresh is identical to Get(), except it always fetches the +// specified resource anew, and updates the cached content +// +// Please refer to the documentation for `(httprc.Cache).Refresh` for +// more details +func (c *Cache) Refresh(ctx context.Context, u string) (Set, error) { + v, err := c.cache.Refresh(ctx, u) + if err != nil { + return nil, err + } + + set, ok := v.(Set) + if !ok { + return nil, fmt.Errorf(`cached object is not a Set (was %T)`, v) + } + return set, nil +} + +// IsRegistered returns true if the given URL `u` has already been registered +// in the cache. +// +// Please refer to the documentation for `(httprc.Cache).IsRegistered` for more +// details. +func (c *Cache) IsRegistered(u string) bool { + return c.cache.IsRegistered(u) +} + +// Unregister removes the given URL `u` from the cache. +// +// Please refer to the documentation for `(httprc.Cache).Unregister` for more +// details. +func (c *Cache) Unregister(u string) error { + return c.cache.Unregister(u) +} + +func (c *Cache) Snapshot() *httprc.Snapshot { + return c.cache.Snapshot() +} + +// CachedSet is a thin shim over jwk.Cache that allows the user to cloack +// jwk.Cache as if it's a `jwk.Set`. Behind the scenes, the `jwk.Set` is +// retrieved from the `jwk.Cache` for every operation. +// +// Since `jwk.CachedSet` always deals with a cached version of the `jwk.Set`, +// all operations that mutate the object (such as AddKey(), RemoveKey(), et. al) +// are no-ops and return an error. +// +// Note that since this is a utility shim over `jwk.Cache`, you _will_ lose +// the ability to control the finer details (such as controlling how long to +// wait for in case of a fetch failure using `context.Context`) +type CachedSet struct { + cache *Cache + url string +} + +var _ Set = &CachedSet{} + +func NewCachedSet(cache *Cache, url string) Set { + return &CachedSet{ + cache: cache, + url: url, + } +} + +func (cs *CachedSet) cached() (Set, error) { + return cs.cache.Get(context.Background(), cs.url) +} + +// Add is a no-op for `jwk.CachedSet`, as the `jwk.Set` should be treated read-only +func (*CachedSet) AddKey(_ Key) error { + return fmt.Errorf(`(jwk.Cachedset).AddKey: jwk.CachedSet is immutable`) +} + +// Clear is a no-op for `jwk.CachedSet`, as the `jwk.Set` should be treated read-only +func (*CachedSet) Clear() error { + return fmt.Errorf(`(jwk.CachedSet).Clear: jwk.CachedSet is immutable`) +} + +// Set is a no-op for `jwk.CachedSet`, as the `jwk.Set` should be treated read-only +func (*CachedSet) Set(_ string, _ interface{}) error { + return fmt.Errorf(`(jwk.CachedSet).Set: jwk.CachedSet is immutable`) +} + +// Remove is a no-op for `jwk.CachedSet`, as the `jwk.Set` should be treated read-only +func (*CachedSet) Remove(_ string) error { + // TODO: Remove() should be renamed to Remove(string) error + return fmt.Errorf(`(jwk.CachedSet).Remove: jwk.CachedSet is immutable`) +} + +// RemoveKey is a no-op for `jwk.CachedSet`, as the `jwk.Set` should be treated read-only +func (*CachedSet) RemoveKey(_ Key) error { + return fmt.Errorf(`(jwk.CachedSet).RemoveKey: jwk.CachedSet is immutable`) +} + +func (cs *CachedSet) Clone() (Set, error) { + set, err := cs.cached() + if err != nil { + return nil, fmt.Errorf(`failed to get cached jwk.Set: %w`, err) + } + + return set.Clone() +} + +// Get returns the value of non-Key field stored in the jwk.Set +func (cs *CachedSet) Get(name string) (interface{}, bool) { + set, err := cs.cached() + if err != nil { + return nil, false + } + + return set.Get(name) +} + +// Key returns the Key at the specified index +func (cs *CachedSet) Key(idx int) (Key, bool) { + set, err := cs.cached() + if err != nil { + return nil, false + } + + return set.Key(idx) +} + +func (cs *CachedSet) Index(key Key) int { + set, err := cs.cached() + if err != nil { + return -1 + } + + return set.Index(key) +} + +func (cs *CachedSet) Keys(ctx context.Context) KeyIterator { + //nolint:contextcheck + set, err := cs.cached() + if err != nil { + return arrayiter.New(nil) + } + + return set.Keys(ctx) +} + +func (cs *CachedSet) Iterate(ctx context.Context) HeaderIterator { + //nolint:contextcheck + set, err := cs.cached() + if err != nil { + return mapiter.New(nil) + } + + return set.Iterate(ctx) +} + +func (cs *CachedSet) Len() int { + set, err := cs.cached() + if err != nil { + return -1 + } + + return set.Len() +} + +func (cs *CachedSet) LookupKeyID(kid string) (Key, bool) { + set, err := cs.cached() + if err != nil { + return nil, false + } + + return set.LookupKeyID(kid) +} diff --git a/vendor/github.com/lestrrat-go/jwx/jwk/ecdsa.go b/vendor/github.com/lestrrat-go/jwx/v2/jwk/ecdsa.go similarity index 82% rename from vendor/github.com/lestrrat-go/jwx/jwk/ecdsa.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwk/ecdsa.go index 19e0f88..67a14ba 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwk/ecdsa.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwk/ecdsa.go @@ -8,10 +8,9 @@ import ( "math/big" "github.com/lestrrat-go/blackmagic" - "github.com/lestrrat-go/jwx/internal/base64" - "github.com/lestrrat-go/jwx/internal/ecutil" - "github.com/lestrrat-go/jwx/jwa" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/internal/base64" + "github.com/lestrrat-go/jwx/v2/internal/ecutil" + "github.com/lestrrat-go/jwx/v2/jwa" ) func init() { @@ -25,11 +24,11 @@ func (k *ecdsaPublicKey) FromRaw(rawKey *ecdsa.PublicKey) error { defer k.mu.Unlock() if rawKey.X == nil { - return errors.Errorf(`invalid ecdsa.PublicKey`) + return fmt.Errorf(`invalid ecdsa.PublicKey`) } if rawKey.Y == nil { - return errors.Errorf(`invalid ecdsa.PublicKey`) + return fmt.Errorf(`invalid ecdsa.PublicKey`) } xbuf := ecutil.AllocECPointBuffer(rawKey.X, rawKey.Curve) @@ -46,7 +45,7 @@ func (k *ecdsaPublicKey) FromRaw(rawKey *ecdsa.PublicKey) error { if tmp, ok := ecutil.AlgorithmForCurve(rawKey.Curve); ok { crv = tmp } else { - return errors.Errorf(`invalid elliptic curve %s`, rawKey.Curve) + return fmt.Errorf(`invalid elliptic curve %s`, rawKey.Curve) } k.crv = &crv @@ -58,13 +57,13 @@ func (k *ecdsaPrivateKey) FromRaw(rawKey *ecdsa.PrivateKey) error { defer k.mu.Unlock() if rawKey.PublicKey.X == nil { - return errors.Errorf(`invalid ecdsa.PrivateKey`) + return fmt.Errorf(`invalid ecdsa.PrivateKey`) } if rawKey.PublicKey.Y == nil { - return errors.Errorf(`invalid ecdsa.PrivateKey`) + return fmt.Errorf(`invalid ecdsa.PrivateKey`) } if rawKey.D == nil { - return errors.Errorf(`invalid ecdsa.PrivateKey`) + return fmt.Errorf(`invalid ecdsa.PrivateKey`) } xbuf := ecutil.AllocECPointBuffer(rawKey.PublicKey.X, rawKey.Curve) @@ -85,7 +84,7 @@ func (k *ecdsaPrivateKey) FromRaw(rawKey *ecdsa.PrivateKey) error { if tmp, ok := ecutil.AlgorithmForCurve(rawKey.Curve); ok { crv = tmp } else { - return errors.Errorf(`invalid elliptic curve %s`, rawKey.Curve) + return fmt.Errorf(`invalid elliptic curve %s`, rawKey.Curve) } k.crv = &crv @@ -97,7 +96,7 @@ func buildECDSAPublicKey(alg jwa.EllipticCurveAlgorithm, xbuf, ybuf []byte) (*ec if tmp, ok := ecutil.CurveForAlgorithm(alg); ok { crv = tmp } else { - return nil, errors.Errorf(`invalid curve algorithm %s`, alg) + return nil, fmt.Errorf(`invalid curve algorithm %s`, alg) } var x, y big.Int @@ -114,7 +113,7 @@ func (k *ecdsaPublicKey) Raw(v interface{}) error { pubk, err := buildECDSAPublicKey(k.Crv(), k.x, k.y) if err != nil { - return errors.Wrap(err, `failed to build public key`) + return fmt.Errorf(`failed to build public key: %w`, err) } return blackmagic.AssignIfCompatible(v, pubk) @@ -126,7 +125,7 @@ func (k *ecdsaPrivateKey) Raw(v interface{}) error { pubk, err := buildECDSAPublicKey(k.Crv(), k.x, k.y) if err != nil { - return errors.Wrap(err, `failed to build public key`) + return fmt.Errorf(`failed to build public key: %w`, err) } var key ecdsa.PrivateKey @@ -141,7 +140,7 @@ func (k *ecdsaPrivateKey) Raw(v interface{}) error { func makeECDSAPublicKey(v interface { makePairs() []*HeaderPair }) (Key, error) { - newKey := NewECDSAPublicKey() + newKey := newECDSAPublicKey() // Iterate and copy everything except for the bits that should not be in the public key for _, pair := range v.makePairs() { @@ -152,7 +151,7 @@ func makeECDSAPublicKey(v interface { //nolint:forcetypeassert key := pair.Key.(string) if err := newKey.Set(key, pair.Value); err != nil { - return nil, errors.Wrapf(err, `failed to set field %q`, key) + return nil, fmt.Errorf(`failed to set field %q: %w`, key, err) } } } @@ -188,7 +187,7 @@ func (k ecdsaPublicKey) Thumbprint(hash crypto.Hash) ([]byte, error) { var key ecdsa.PublicKey if err := k.Raw(&key); err != nil { - return nil, errors.Wrap(err, `failed to materialize ecdsa.PublicKey for thumbprint generation`) + return nil, fmt.Errorf(`failed to materialize ecdsa.PublicKey for thumbprint generation: %w`, err) } xbuf := ecutil.AllocECPointBuffer(key.X, key.Curve) @@ -212,7 +211,7 @@ func (k ecdsaPrivateKey) Thumbprint(hash crypto.Hash) ([]byte, error) { var key ecdsa.PrivateKey if err := k.Raw(&key); err != nil { - return nil, errors.Wrap(err, `failed to materialize ecdsa.PrivateKey for thumbprint generation`) + return nil, fmt.Errorf(`failed to materialize ecdsa.PrivateKey for thumbprint generation: %w`, err) } xbuf := ecutil.AllocECPointBuffer(key.X, key.Curve) diff --git a/vendor/github.com/lestrrat-go/jwx/jwk/ecdsa_gen.go b/vendor/github.com/lestrrat-go/jwx/v2/jwk/ecdsa_gen.go similarity index 75% rename from vendor/github.com/lestrrat-go/jwx/jwk/ecdsa_gen.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwk/ecdsa_gen.go index e5d6374..92f56f5 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwk/ecdsa_gen.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwk/ecdsa_gen.go @@ -6,18 +6,17 @@ import ( "bytes" "context" "crypto/ecdsa" - "crypto/x509" "fmt" "sort" "sync" "github.com/lestrrat-go/iter/mapiter" - "github.com/lestrrat-go/jwx/internal/base64" - "github.com/lestrrat-go/jwx/internal/iter" - "github.com/lestrrat-go/jwx/internal/json" - "github.com/lestrrat-go/jwx/internal/pool" - "github.com/lestrrat-go/jwx/jwa" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/cert" + "github.com/lestrrat-go/jwx/v2/internal/base64" + "github.com/lestrrat-go/jwx/v2/internal/iter" + "github.com/lestrrat-go/jwx/v2/internal/json" + "github.com/lestrrat-go/jwx/v2/internal/pool" + "github.com/lestrrat-go/jwx/v2/jwa" ) const ( @@ -36,25 +35,24 @@ type ECDSAPublicKey interface { } type ecdsaPublicKey struct { - algorithm *string // https://tools.ietf.org/html/rfc7517#section-4.4 + algorithm *jwa.KeyAlgorithm // https://tools.ietf.org/html/rfc7517#section-4.4 crv *jwa.EllipticCurveAlgorithm keyID *string // https://tools.ietf.org/html/rfc7515#section-4.1.4 keyOps *KeyOperationList // https://tools.ietf.org/html/rfc7517#section-4.3 keyUsage *string // https://tools.ietf.org/html/rfc7517#section-4.2 x []byte - x509CertChain *CertificateChain // https://tools.ietf.org/html/rfc7515#section-4.1.6 - x509CertThumbprint *string // https://tools.ietf.org/html/rfc7515#section-4.1.7 - x509CertThumbprintS256 *string // https://tools.ietf.org/html/rfc7515#section-4.1.8 - x509URL *string // https://tools.ietf.org/html/rfc7515#section-4.1.5 + x509CertChain *cert.Chain // https://tools.ietf.org/html/rfc7515#section-4.1.6 + x509CertThumbprint *string // https://tools.ietf.org/html/rfc7515#section-4.1.7 + x509CertThumbprintS256 *string // https://tools.ietf.org/html/rfc7515#section-4.1.8 + x509URL *string // https://tools.ietf.org/html/rfc7515#section-4.1.5 y []byte privateParams map[string]interface{} mu *sync.RWMutex dc json.DecodeCtx } -func NewECDSAPublicKey() ECDSAPublicKey { - return newECDSAPublicKey() -} +var _ ECDSAPublicKey = &ecdsaPublicKey{} +var _ Key = &ecdsaPublicKey{} func newECDSAPublicKey() *ecdsaPublicKey { return &ecdsaPublicKey{ @@ -67,11 +65,11 @@ func (h ecdsaPublicKey) KeyType() jwa.KeyType { return jwa.EC } -func (h *ecdsaPublicKey) Algorithm() string { +func (h *ecdsaPublicKey) Algorithm() jwa.KeyAlgorithm { if h.algorithm != nil { return *(h.algorithm) } - return "" + return jwa.InvalidKeyAlgorithm("") } func (h *ecdsaPublicKey) Crv() jwa.EllipticCurveAlgorithm { @@ -106,11 +104,8 @@ func (h *ecdsaPublicKey) X() []byte { return h.x } -func (h *ecdsaPublicKey) X509CertChain() []*x509.Certificate { - if h.x509CertChain != nil { - return h.x509CertChain.Get() - } - return nil +func (h *ecdsaPublicKey) X509CertChain() *cert.Chain { + return h.x509CertChain } func (h *ecdsaPublicKey) X509CertThumbprint() string { @@ -163,7 +158,7 @@ func (h *ecdsaPublicKey) makePairs() []*HeaderPair { pairs = append(pairs, &HeaderPair{Key: ECDSAXKey, Value: h.x}) } if h.x509CertChain != nil { - pairs = append(pairs, &HeaderPair{Key: X509CertChainKey, Value: *(h.x509CertChain)}) + pairs = append(pairs, &HeaderPair{Key: X509CertChainKey, Value: h.x509CertChain}) } if h.x509CertThumbprint != nil { pairs = append(pairs, &HeaderPair{Key: X509CertThumbprintKey, Value: *(h.x509CertThumbprint)}) @@ -227,7 +222,7 @@ func (h *ecdsaPublicKey) Get(name string) (interface{}, bool) { if h.x509CertChain == nil { return nil, false } - return h.x509CertChain.Get(), true + return h.x509CertChain, true case X509CertThumbprintKey: if h.x509CertThumbprint == nil { return nil, false @@ -266,13 +261,15 @@ func (h *ecdsaPublicKey) setNoLock(name string, value interface{}) error { return nil case AlgorithmKey: switch v := value.(type) { - case string: - h.algorithm = &v + case string, jwa.SignatureAlgorithm, jwa.ContentEncryptionAlgorithm: + var tmp = jwa.KeyAlgorithmFrom(v) + h.algorithm = &tmp case fmt.Stringer: - tmp := v.String() + s := v.String() + var tmp = jwa.KeyAlgorithmFrom(s) h.algorithm = &tmp default: - return errors.Errorf(`invalid type for %s key: %T`, AlgorithmKey, value) + return fmt.Errorf(`invalid type for %s key: %T`, AlgorithmKey, value) } return nil case ECDSACrvKey: @@ -280,17 +277,17 @@ func (h *ecdsaPublicKey) setNoLock(name string, value interface{}) error { h.crv = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, ECDSACrvKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, ECDSACrvKey, value) case KeyIDKey: if v, ok := value.(string); ok { h.keyID = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, KeyIDKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, KeyIDKey, value) case KeyOpsKey: var acceptor KeyOperationList if err := acceptor.Accept(value); err != nil { - return errors.Wrapf(err, `invalid value for %s key`, KeyOpsKey) + return fmt.Errorf(`invalid value for %s key: %w`, KeyOpsKey, err) } h.keyOps = &acceptor return nil @@ -302,50 +299,49 @@ func (h *ecdsaPublicKey) setNoLock(name string, value interface{}) error { tmp := v.String() h.keyUsage = &tmp default: - return errors.Errorf(`invalid key usage type %s`, v) + return fmt.Errorf(`invalid key usage type %s`, v) } case string: h.keyUsage = &v default: - return errors.Errorf(`invalid key usage type %s`, v) + return fmt.Errorf(`invalid key usage type %s`, v) } case ECDSAXKey: if v, ok := value.([]byte); ok { h.x = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, ECDSAXKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, ECDSAXKey, value) case X509CertChainKey: - var acceptor CertificateChain - if err := acceptor.Accept(value); err != nil { - return errors.Wrapf(err, `invalid value for %s key`, X509CertChainKey) + if v, ok := value.(*cert.Chain); ok { + h.x509CertChain = v + return nil } - h.x509CertChain = &acceptor - return nil + return fmt.Errorf(`invalid value for %s key: %T`, X509CertChainKey, value) case X509CertThumbprintKey: if v, ok := value.(string); ok { h.x509CertThumbprint = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509CertThumbprintKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509CertThumbprintKey, value) case X509CertThumbprintS256Key: if v, ok := value.(string); ok { h.x509CertThumbprintS256 = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509CertThumbprintS256Key, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509CertThumbprintS256Key, value) case X509URLKey: if v, ok := value.(string); ok { h.x509URL = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509URLKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509URLKey, value) case ECDSAYKey: if v, ok := value.([]byte); ok { h.y = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, ECDSAYKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, ECDSAYKey, value) default: if h.privateParams == nil { h.privateParams = map[string]interface{}{} @@ -404,6 +400,8 @@ func (k *ecdsaPublicKey) SetDecodeCtx(dc json.DecodeCtx) { } func (h *ecdsaPublicKey) UnmarshalJSON(buf []byte) error { + h.mu.Lock() + defer h.mu.Unlock() h.algorithm = nil h.crv = nil h.keyID = nil @@ -420,7 +418,7 @@ LOOP: for { tok, err := dec.Token() if err != nil { - return errors.Wrap(err, `error reading token`) + return fmt.Errorf(`error reading token: %w`, err) } switch tok := tok.(type) { case json.Delim: @@ -429,67 +427,70 @@ LOOP: if tok == '}' { // End of object break LOOP } else if tok != '{' { - return errors.Errorf(`expected '{', but got '%c'`, tok) + return fmt.Errorf(`expected '{', but got '%c'`, tok) } case string: // Objects can only have string keys switch tok { case KeyTypeKey: val, err := json.ReadNextStringToken(dec) if err != nil { - return errors.Wrap(err, `error reading token`) + return fmt.Errorf(`error reading token: %w`, err) } if val != jwa.EC.String() { - return errors.Errorf(`invalid kty value for RSAPublicKey (%s)`, val) + return fmt.Errorf(`invalid kty value for RSAPublicKey (%s)`, val) } case AlgorithmKey: - if err := json.AssignNextStringToken(&h.algorithm, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, AlgorithmKey) + var s string + if err := dec.Decode(&s); err != nil { + return fmt.Errorf(`failed to decode value for key %s: %w`, AlgorithmKey, err) } + alg := jwa.KeyAlgorithmFrom(s) + h.algorithm = &alg case ECDSACrvKey: var decoded jwa.EllipticCurveAlgorithm if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, ECDSACrvKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, ECDSACrvKey, err) } h.crv = &decoded case KeyIDKey: if err := json.AssignNextStringToken(&h.keyID, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, KeyIDKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, KeyIDKey, err) } case KeyOpsKey: var decoded KeyOperationList if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, KeyOpsKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, KeyOpsKey, err) } h.keyOps = &decoded case KeyUsageKey: if err := json.AssignNextStringToken(&h.keyUsage, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, KeyUsageKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, KeyUsageKey, err) } case ECDSAXKey: if err := json.AssignNextBytesToken(&h.x, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, ECDSAXKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, ECDSAXKey, err) } case X509CertChainKey: - var decoded CertificateChain + var decoded cert.Chain if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertChainKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertChainKey, err) } h.x509CertChain = &decoded case X509CertThumbprintKey: if err := json.AssignNextStringToken(&h.x509CertThumbprint, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertThumbprintKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertThumbprintKey, err) } case X509CertThumbprintS256Key: if err := json.AssignNextStringToken(&h.x509CertThumbprintS256, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertThumbprintS256Key) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertThumbprintS256Key, err) } case X509URLKey: if err := json.AssignNextStringToken(&h.x509URL, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509URLKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509URLKey, err) } case ECDSAYKey: if err := json.AssignNextBytesToken(&h.y, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, ECDSAYKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, ECDSAYKey, err) } default: if dc := h.dc; dc != nil { @@ -506,20 +507,20 @@ LOOP: h.setNoLock(tok, decoded) continue } - return errors.Wrapf(err, `could not decode field %s`, tok) + return fmt.Errorf(`could not decode field %s: %w`, tok, err) } default: - return errors.Errorf(`invalid token %T`, tok) + return fmt.Errorf(`invalid token %T`, tok) } } if h.crv == nil { - return errors.Errorf(`required field crv is missing`) + return fmt.Errorf(`required field crv is missing`) } if h.x == nil { - return errors.Errorf(`required field x is missing`) + return fmt.Errorf(`required field x is missing`) } if h.y == nil { - return errors.Errorf(`required field y is missing`) + return fmt.Errorf(`required field y is missing`) } return nil } @@ -552,7 +553,7 @@ func (h ecdsaPublicKey) MarshalJSON() ([]byte, error) { buf.WriteRune('"') default: if err := enc.Encode(v); err != nil { - return nil, errors.Wrapf(err, `failed to encode value for field %s`, f) + return nil, fmt.Errorf(`failed to encode value for field %s: %w`, f, err) } buf.Truncate(buf.Len() - 1) } @@ -597,26 +598,25 @@ type ECDSAPrivateKey interface { } type ecdsaPrivateKey struct { - algorithm *string // https://tools.ietf.org/html/rfc7517#section-4.4 + algorithm *jwa.KeyAlgorithm // https://tools.ietf.org/html/rfc7517#section-4.4 crv *jwa.EllipticCurveAlgorithm d []byte keyID *string // https://tools.ietf.org/html/rfc7515#section-4.1.4 keyOps *KeyOperationList // https://tools.ietf.org/html/rfc7517#section-4.3 keyUsage *string // https://tools.ietf.org/html/rfc7517#section-4.2 x []byte - x509CertChain *CertificateChain // https://tools.ietf.org/html/rfc7515#section-4.1.6 - x509CertThumbprint *string // https://tools.ietf.org/html/rfc7515#section-4.1.7 - x509CertThumbprintS256 *string // https://tools.ietf.org/html/rfc7515#section-4.1.8 - x509URL *string // https://tools.ietf.org/html/rfc7515#section-4.1.5 + x509CertChain *cert.Chain // https://tools.ietf.org/html/rfc7515#section-4.1.6 + x509CertThumbprint *string // https://tools.ietf.org/html/rfc7515#section-4.1.7 + x509CertThumbprintS256 *string // https://tools.ietf.org/html/rfc7515#section-4.1.8 + x509URL *string // https://tools.ietf.org/html/rfc7515#section-4.1.5 y []byte privateParams map[string]interface{} mu *sync.RWMutex dc json.DecodeCtx } -func NewECDSAPrivateKey() ECDSAPrivateKey { - return newECDSAPrivateKey() -} +var _ ECDSAPrivateKey = &ecdsaPrivateKey{} +var _ Key = &ecdsaPrivateKey{} func newECDSAPrivateKey() *ecdsaPrivateKey { return &ecdsaPrivateKey{ @@ -629,11 +629,11 @@ func (h ecdsaPrivateKey) KeyType() jwa.KeyType { return jwa.EC } -func (h *ecdsaPrivateKey) Algorithm() string { +func (h *ecdsaPrivateKey) Algorithm() jwa.KeyAlgorithm { if h.algorithm != nil { return *(h.algorithm) } - return "" + return jwa.InvalidKeyAlgorithm("") } func (h *ecdsaPrivateKey) Crv() jwa.EllipticCurveAlgorithm { @@ -672,11 +672,8 @@ func (h *ecdsaPrivateKey) X() []byte { return h.x } -func (h *ecdsaPrivateKey) X509CertChain() []*x509.Certificate { - if h.x509CertChain != nil { - return h.x509CertChain.Get() - } - return nil +func (h *ecdsaPrivateKey) X509CertChain() *cert.Chain { + return h.x509CertChain } func (h *ecdsaPrivateKey) X509CertThumbprint() string { @@ -732,7 +729,7 @@ func (h *ecdsaPrivateKey) makePairs() []*HeaderPair { pairs = append(pairs, &HeaderPair{Key: ECDSAXKey, Value: h.x}) } if h.x509CertChain != nil { - pairs = append(pairs, &HeaderPair{Key: X509CertChainKey, Value: *(h.x509CertChain)}) + pairs = append(pairs, &HeaderPair{Key: X509CertChainKey, Value: h.x509CertChain}) } if h.x509CertThumbprint != nil { pairs = append(pairs, &HeaderPair{Key: X509CertThumbprintKey, Value: *(h.x509CertThumbprint)}) @@ -801,7 +798,7 @@ func (h *ecdsaPrivateKey) Get(name string) (interface{}, bool) { if h.x509CertChain == nil { return nil, false } - return h.x509CertChain.Get(), true + return h.x509CertChain, true case X509CertThumbprintKey: if h.x509CertThumbprint == nil { return nil, false @@ -840,13 +837,15 @@ func (h *ecdsaPrivateKey) setNoLock(name string, value interface{}) error { return nil case AlgorithmKey: switch v := value.(type) { - case string: - h.algorithm = &v + case string, jwa.SignatureAlgorithm, jwa.ContentEncryptionAlgorithm: + var tmp = jwa.KeyAlgorithmFrom(v) + h.algorithm = &tmp case fmt.Stringer: - tmp := v.String() + s := v.String() + var tmp = jwa.KeyAlgorithmFrom(s) h.algorithm = &tmp default: - return errors.Errorf(`invalid type for %s key: %T`, AlgorithmKey, value) + return fmt.Errorf(`invalid type for %s key: %T`, AlgorithmKey, value) } return nil case ECDSACrvKey: @@ -854,23 +853,23 @@ func (h *ecdsaPrivateKey) setNoLock(name string, value interface{}) error { h.crv = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, ECDSACrvKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, ECDSACrvKey, value) case ECDSADKey: if v, ok := value.([]byte); ok { h.d = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, ECDSADKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, ECDSADKey, value) case KeyIDKey: if v, ok := value.(string); ok { h.keyID = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, KeyIDKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, KeyIDKey, value) case KeyOpsKey: var acceptor KeyOperationList if err := acceptor.Accept(value); err != nil { - return errors.Wrapf(err, `invalid value for %s key`, KeyOpsKey) + return fmt.Errorf(`invalid value for %s key: %w`, KeyOpsKey, err) } h.keyOps = &acceptor return nil @@ -882,50 +881,49 @@ func (h *ecdsaPrivateKey) setNoLock(name string, value interface{}) error { tmp := v.String() h.keyUsage = &tmp default: - return errors.Errorf(`invalid key usage type %s`, v) + return fmt.Errorf(`invalid key usage type %s`, v) } case string: h.keyUsage = &v default: - return errors.Errorf(`invalid key usage type %s`, v) + return fmt.Errorf(`invalid key usage type %s`, v) } case ECDSAXKey: if v, ok := value.([]byte); ok { h.x = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, ECDSAXKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, ECDSAXKey, value) case X509CertChainKey: - var acceptor CertificateChain - if err := acceptor.Accept(value); err != nil { - return errors.Wrapf(err, `invalid value for %s key`, X509CertChainKey) + if v, ok := value.(*cert.Chain); ok { + h.x509CertChain = v + return nil } - h.x509CertChain = &acceptor - return nil + return fmt.Errorf(`invalid value for %s key: %T`, X509CertChainKey, value) case X509CertThumbprintKey: if v, ok := value.(string); ok { h.x509CertThumbprint = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509CertThumbprintKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509CertThumbprintKey, value) case X509CertThumbprintS256Key: if v, ok := value.(string); ok { h.x509CertThumbprintS256 = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509CertThumbprintS256Key, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509CertThumbprintS256Key, value) case X509URLKey: if v, ok := value.(string); ok { h.x509URL = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509URLKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509URLKey, value) case ECDSAYKey: if v, ok := value.([]byte); ok { h.y = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, ECDSAYKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, ECDSAYKey, value) default: if h.privateParams == nil { h.privateParams = map[string]interface{}{} @@ -986,6 +984,8 @@ func (k *ecdsaPrivateKey) SetDecodeCtx(dc json.DecodeCtx) { } func (h *ecdsaPrivateKey) UnmarshalJSON(buf []byte) error { + h.mu.Lock() + defer h.mu.Unlock() h.algorithm = nil h.crv = nil h.d = nil @@ -1003,7 +1003,7 @@ LOOP: for { tok, err := dec.Token() if err != nil { - return errors.Wrap(err, `error reading token`) + return fmt.Errorf(`error reading token: %w`, err) } switch tok := tok.(type) { case json.Delim: @@ -1012,71 +1012,74 @@ LOOP: if tok == '}' { // End of object break LOOP } else if tok != '{' { - return errors.Errorf(`expected '{', but got '%c'`, tok) + return fmt.Errorf(`expected '{', but got '%c'`, tok) } case string: // Objects can only have string keys switch tok { case KeyTypeKey: val, err := json.ReadNextStringToken(dec) if err != nil { - return errors.Wrap(err, `error reading token`) + return fmt.Errorf(`error reading token: %w`, err) } if val != jwa.EC.String() { - return errors.Errorf(`invalid kty value for RSAPublicKey (%s)`, val) + return fmt.Errorf(`invalid kty value for RSAPublicKey (%s)`, val) } case AlgorithmKey: - if err := json.AssignNextStringToken(&h.algorithm, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, AlgorithmKey) + var s string + if err := dec.Decode(&s); err != nil { + return fmt.Errorf(`failed to decode value for key %s: %w`, AlgorithmKey, err) } + alg := jwa.KeyAlgorithmFrom(s) + h.algorithm = &alg case ECDSACrvKey: var decoded jwa.EllipticCurveAlgorithm if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, ECDSACrvKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, ECDSACrvKey, err) } h.crv = &decoded case ECDSADKey: if err := json.AssignNextBytesToken(&h.d, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, ECDSADKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, ECDSADKey, err) } case KeyIDKey: if err := json.AssignNextStringToken(&h.keyID, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, KeyIDKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, KeyIDKey, err) } case KeyOpsKey: var decoded KeyOperationList if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, KeyOpsKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, KeyOpsKey, err) } h.keyOps = &decoded case KeyUsageKey: if err := json.AssignNextStringToken(&h.keyUsage, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, KeyUsageKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, KeyUsageKey, err) } case ECDSAXKey: if err := json.AssignNextBytesToken(&h.x, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, ECDSAXKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, ECDSAXKey, err) } case X509CertChainKey: - var decoded CertificateChain + var decoded cert.Chain if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertChainKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertChainKey, err) } h.x509CertChain = &decoded case X509CertThumbprintKey: if err := json.AssignNextStringToken(&h.x509CertThumbprint, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertThumbprintKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertThumbprintKey, err) } case X509CertThumbprintS256Key: if err := json.AssignNextStringToken(&h.x509CertThumbprintS256, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertThumbprintS256Key) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertThumbprintS256Key, err) } case X509URLKey: if err := json.AssignNextStringToken(&h.x509URL, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509URLKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509URLKey, err) } case ECDSAYKey: if err := json.AssignNextBytesToken(&h.y, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, ECDSAYKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, ECDSAYKey, err) } default: if dc := h.dc; dc != nil { @@ -1093,23 +1096,23 @@ LOOP: h.setNoLock(tok, decoded) continue } - return errors.Wrapf(err, `could not decode field %s`, tok) + return fmt.Errorf(`could not decode field %s: %w`, tok, err) } default: - return errors.Errorf(`invalid token %T`, tok) + return fmt.Errorf(`invalid token %T`, tok) } } if h.crv == nil { - return errors.Errorf(`required field crv is missing`) + return fmt.Errorf(`required field crv is missing`) } if h.d == nil { - return errors.Errorf(`required field d is missing`) + return fmt.Errorf(`required field d is missing`) } if h.x == nil { - return errors.Errorf(`required field x is missing`) + return fmt.Errorf(`required field x is missing`) } if h.y == nil { - return errors.Errorf(`required field y is missing`) + return fmt.Errorf(`required field y is missing`) } return nil } @@ -1142,7 +1145,7 @@ func (h ecdsaPrivateKey) MarshalJSON() ([]byte, error) { buf.WriteRune('"') default: if err := enc.Encode(v); err != nil { - return nil, errors.Wrapf(err, `failed to encode value for field %s`, f) + return nil, fmt.Errorf(`failed to encode value for field %s: %w`, f, err) } buf.Truncate(buf.Len() - 1) } diff --git a/vendor/github.com/lestrrat-go/jwx/jwk/es256k.go b/vendor/github.com/lestrrat-go/jwx/v2/jwk/es256k.go similarity index 65% rename from vendor/github.com/lestrrat-go/jwx/jwk/es256k.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwk/es256k.go index d42cabb..66f822b 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwk/es256k.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwk/es256k.go @@ -4,8 +4,8 @@ package jwk import ( "github.com/decred/dcrd/dcrec/secp256k1/v4" - "github.com/lestrrat-go/jwx/internal/ecutil" - "github.com/lestrrat-go/jwx/jwa" + "github.com/lestrrat-go/jwx/v2/internal/ecutil" + "github.com/lestrrat-go/jwx/v2/jwa" ) func init() { diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jwk/fetch.go b/vendor/github.com/lestrrat-go/jwx/v2/jwk/fetch.go new file mode 100644 index 0000000..daca177 --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwk/fetch.go @@ -0,0 +1,76 @@ +package jwk + +import ( + "context" + "fmt" + "io" + "os" + "strconv" + + "github.com/lestrrat-go/httprc" +) + +type Fetcher interface { + Fetch(context.Context, string, ...FetchOption) (Set, error) +} + +type FetchFunc func(context.Context, string, ...FetchOption) (Set, error) + +func (f FetchFunc) Fetch(ctx context.Context, u string, options ...FetchOption) (Set, error) { + return f(ctx, u, options...) +} + +var globalFetcher httprc.Fetcher + +func init() { + var nworkers int + v := os.Getenv(`JWK_FETCHER_WORKER_COUNT`) + if c, err := strconv.ParseInt(v, 10, 64); err == nil { + nworkers = int(c) + } + if nworkers < 1 { + nworkers = 3 + } + + globalFetcher = httprc.NewFetcher(context.Background(), httprc.WithFetcherWorkerCount(nworkers)) +} + +// Fetch fetches a JWK resource specified by a URL. The url must be +// pointing to a resource that is supported by `net/http`. +// +// If you are using the same `jwk.Set` for long periods of time during +// the lifecycle of your program, and would like to periodically refresh the +// contents of the object with the data at the remote resource, +// consider using `jwk.Cache`, which automatically refreshes +// jwk.Set objects asynchronously. +func Fetch(ctx context.Context, u string, options ...FetchOption) (Set, error) { + var hrfopts []httprc.FetchOption + var parseOptions []ParseOption + for _, option := range options { + if parseOpt, ok := option.(ParseOption); ok { + parseOptions = append(parseOptions, parseOpt) + continue + } + + //nolint:forcetypeassert + switch option.Ident() { + case identHTTPClient{}: + hrfopts = append(hrfopts, httprc.WithHTTPClient(option.Value().(HTTPClient))) + case identFetchWhitelist{}: + hrfopts = append(hrfopts, httprc.WithWhitelist(option.Value().(httprc.Whitelist))) + } + } + + res, err := globalFetcher.Fetch(ctx, u, hrfopts...) + if err != nil { + return nil, fmt.Errorf(`failed to fetch %q: %w`, u, err) + } + + buf, err := io.ReadAll(res.Body) + defer res.Body.Close() + if err != nil { + return nil, fmt.Errorf(`failed to read response body for %q: %w`, u, err) + } + + return Parse(buf, parseOptions...) +} diff --git a/vendor/github.com/lestrrat-go/jwx/jwk/interface.go b/vendor/github.com/lestrrat-go/jwx/v2/jwk/interface.go similarity index 70% rename from vendor/github.com/lestrrat-go/jwx/jwk/interface.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwk/interface.go index 9182f71..6dbc0db 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwk/interface.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwk/interface.go @@ -2,14 +2,12 @@ package jwk import ( "context" - "crypto/x509" - "net/http" "sync" "github.com/lestrrat-go/iter/arrayiter" "github.com/lestrrat-go/iter/mapiter" - "github.com/lestrrat-go/jwx/internal/iter" - "github.com/lestrrat-go/jwx/internal/json" + "github.com/lestrrat-go/jwx/v2/internal/iter" + "github.com/lestrrat-go/jwx/v2/internal/json" ) // KeyUsageType is used to denote what this key should be used for @@ -24,10 +22,6 @@ const ( ForEncryption KeyUsageType = "enc" ) -type CertificateChain struct { - certs []*x509.Certificate -} - type KeyOperation string type KeyOperationList []KeyOperation @@ -55,30 +49,25 @@ const ( // are stored in _both_ the resulting `jwk.Set` object and the `jwk.Key` object . // type Set interface { - // Add adds the specified key. If the key already exists in the set, it is - // not added. - // This method will be renamed to `AddKey(Key)` in a future major release. - Add(Key) bool + // AddKey adds the specified key. If the key already exists in the set, + // an error is returned. + AddKey(Key) error // Clear resets the list of keys associated with this set, emptying the - // internal list of `jwk.Key`s - // This method will be changed in the future to clear all contents in the - // `jwk.Set` instead of just the keys. - Clear() + // internal list of `jwk.Key`s, as well as clearing any other non-key + // fields + Clear() error // Get returns the key at index `idx`. If the index is out of range, // then the second return value is false. - // This method will be renamed to `Key(int)` in a future major release. - Get(int) (Key, bool) + Key(int) (Key, bool) - // Field returns the value of a private field in the key set. + // Get returns the value of a private field in the key set. // // For the purposes of a key set, any field other than the "keys" field is // considered to be a private field. In other words, you cannot use this // method to directly access the list of keys in the set - // - // This method will be renamed to `Get(string)` in a future major release. - Field(string) (interface{}, bool) + Get(string) (interface{}, bool) // Set sets the value of a single field. // @@ -87,8 +76,10 @@ type Set interface { // specify, and there is no way of knowing what type they could be. Set(string, interface{}) error - // Remove removes the field associated with the specified key. - // There is no way to remove the `kty` (key type). You will ALWAYS be left with one field in a jwk.Key. + // RemoveKey removes the specified non-key field from the set. + // Keys may not be removed using this method. + Remove(string) error + // Index returns the index where the given key exists, -1 otherwise Index(Key) int @@ -101,11 +92,14 @@ type Set interface { // need all of them, use `Iterate()` LookupKeyID(string) (Key, bool) - // Remove removes the key from the set. - Remove(Key) bool + // RemoveKey removes the key from the set. + RemoveKey(Key) error - // Iterate creates an iterator to iterate through all keys in the set. - Iterate(context.Context) KeyIterator + // Keys creates an iterator to iterate through all keys in the set. + Keys(context.Context) KeyIterator + + // Iterate creates an iterator to iterate through all fields other than the keys + Iterate(context.Context) HeaderIterator // Clone create a new set with identical keys. Keys themselves are not cloned. Clone() (Set, error) @@ -132,12 +126,6 @@ type PublicKeyer interface { PublicKey() (Key, error) } -// HTTPClient specifies the minimum interface that is required for our JWK -// fetching tools. -type HTTPClient interface { - Do(*http.Request) (*http.Response, error) -} - type DecodeCtx interface { json.DecodeCtx IgnoreParseError() bool @@ -146,15 +134,3 @@ type KeyWithDecodeCtx interface { SetDecodeCtx(DecodeCtx) DecodeCtx() DecodeCtx } - -type AutoRefreshError struct { - Error error - URL string -} - -// Whitelist is an interface for a set of URL whitelists. When provided -// to JWK fetching operations, urls are checked against this object, and -// the object must return true for urls to be fetched. -type Whitelist interface { - IsAllowed(string) bool -} diff --git a/vendor/github.com/lestrrat-go/jwx/jwk/interface_gen.go b/vendor/github.com/lestrrat-go/jwx/v2/jwk/interface_gen.go similarity index 91% rename from vendor/github.com/lestrrat-go/jwx/jwk/interface_gen.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwk/interface_gen.go index 1f94e54..aa93821 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwk/interface_gen.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwk/interface_gen.go @@ -5,9 +5,9 @@ package jwk import ( "context" "crypto" - "crypto/x509" - "github.com/lestrrat-go/jwx/jwa" + "github.com/lestrrat-go/jwx/v2/cert" + "github.com/lestrrat-go/jwx/v2/jwa" ) const ( @@ -95,13 +95,18 @@ type Key interface { // KeyOps returns `key_ops` of a JWK KeyOps() KeyOperationList // Algorithm returns `alg` of a JWK - Algorithm() string + + // Algorithm returns the value of the `alg` field + // + // This field may contain either `jwk.SignatureAlgorithm` or `jwk.KeyEncryptionAlgorithm`. + // This is why there exists a `jwa.KeyAlgorithm` type that encompases both types. + Algorithm() jwa.KeyAlgorithm // KeyID returns `kid` of a JWK KeyID() string // X509URL returns `x58` of a JWK X509URL() string // X509CertChain returns `x5c` of a JWK - X509CertChain() []*x509.Certificate + X509CertChain() *cert.Chain // X509CertThumbprint returns `x5t` of a JWK X509CertThumbprint() string // X509CertThumbprintS256 returns `x5t#S256` of a JWK diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jwk/io.go b/vendor/github.com/lestrrat-go/jwx/v2/jwk/io.go new file mode 100644 index 0000000..4e0d487 --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwk/io.go @@ -0,0 +1,42 @@ +// Automatically generated by internal/cmd/genreadfile/main.go. DO NOT EDIT + +package jwk + +import ( + "io/fs" + "os" +) + +type sysFS struct{} + +func (sysFS) Open(path string) (fs.File, error) { + return os.Open(path) +} + +func ReadFile(path string, options ...ReadFileOption) (Set, error) { + var parseOptions []ParseOption + var readFileOptions []ReadFileOption + for _, option := range options { + if po, ok := option.(ParseOption); ok { + parseOptions = append(parseOptions, po) + } else { + readFileOptions = append(readFileOptions, option) + } + } + + var srcFS fs.FS = sysFS{} + for _, option := range options { + switch option.Ident() { + case identFS{}: + srcFS = option.Value().(fs.FS) + } + } + + f, err := srcFS.Open(path) + if err != nil { + return nil, err + } + + defer f.Close() + return ParseReader(f, parseOptions...) +} diff --git a/vendor/github.com/lestrrat-go/jwx/jwk/jwk.go b/vendor/github.com/lestrrat-go/jwx/v2/jwk/jwk.go similarity index 62% rename from vendor/github.com/lestrrat-go/jwx/jwk/jwk.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwk/jwk.go index 0ef377c..a5070a2 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwk/jwk.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwk/jwk.go @@ -1,40 +1,38 @@ -//go:generate ./gen.sh +//go:generate ../tools/cmd/genjwk.sh // Package jwk implements JWK as described in https://tools.ietf.org/html/rfc7517 package jwk import ( "bytes" - "context" "crypto" "crypto/ecdsa" "crypto/ed25519" + "crypto/elliptic" "crypto/rsa" "crypto/x509" "encoding/pem" + "fmt" "io" - "io/ioutil" "math/big" - "net/http" - "github.com/lestrrat-go/backoff/v2" - "github.com/lestrrat-go/jwx/internal/base64" - "github.com/lestrrat-go/jwx/internal/json" - "github.com/lestrrat-go/jwx/jwa" - "github.com/lestrrat-go/jwx/x25519" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/internal/base64" + "github.com/lestrrat-go/jwx/v2/internal/ecutil" + "github.com/lestrrat-go/jwx/v2/internal/json" + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/x25519" ) var registry = json.NewRegistry() func bigIntToBytes(n *big.Int) ([]byte, error) { if n == nil { - return nil, errors.New(`invalid *big.Int value`) + return nil, fmt.Errorf(`invalid *big.Int value`) } return n.Bytes(), nil } -// New creates a jwk.Key from the given key (RSA/ECDSA/symmetric keys). +// FromRaw creates a jwk.Key from the given key (RSA/ECDSA/symmetric keys). // // The constructor auto-detects the type of key to be instantiated // based on the input type: @@ -43,9 +41,9 @@ func bigIntToBytes(n *big.Int) ([]byte, error) { // * "crypto/ecdsa".PrivateKey and "crypto/ecdsa".PublicKey creates an EC based key // * "crypto/ed25519".PrivateKey and "crypto/ed25519".PublicKey creates an OKP based key // * []byte creates a symmetric key -func New(key interface{}) (Key, error) { +func FromRaw(key interface{}) (Key, error) { if key == nil { - return nil, errors.New(`jwk.New requires a non-nil key`) + return nil, fmt.Errorf(`jwk.New requires a non-nil key`) } var ptr interface{} @@ -64,61 +62,61 @@ func New(key interface{}) (Key, error) { switch rawKey := ptr.(type) { case *rsa.PrivateKey: - k := NewRSAPrivateKey() + k := newRSAPrivateKey() if err := k.FromRaw(rawKey); err != nil { - return nil, errors.Wrapf(err, `failed to initialize %T from %T`, k, rawKey) + return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err) } return k, nil case *rsa.PublicKey: - k := NewRSAPublicKey() + k := newRSAPublicKey() if err := k.FromRaw(rawKey); err != nil { - return nil, errors.Wrapf(err, `failed to initialize %T from %T`, k, rawKey) + return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err) } return k, nil case *ecdsa.PrivateKey: - k := NewECDSAPrivateKey() + k := newECDSAPrivateKey() if err := k.FromRaw(rawKey); err != nil { - return nil, errors.Wrapf(err, `failed to initialize %T from %T`, k, rawKey) + return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err) } return k, nil case *ecdsa.PublicKey: - k := NewECDSAPublicKey() + k := newECDSAPublicKey() if err := k.FromRaw(rawKey); err != nil { - return nil, errors.Wrapf(err, `failed to initialize %T from %T`, k, rawKey) + return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err) } return k, nil case ed25519.PrivateKey: - k := NewOKPPrivateKey() + k := newOKPPrivateKey() if err := k.FromRaw(rawKey); err != nil { - return nil, errors.Wrapf(err, `failed to initialize %T from %T`, k, rawKey) + return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err) } return k, nil case ed25519.PublicKey: - k := NewOKPPublicKey() + k := newOKPPublicKey() if err := k.FromRaw(rawKey); err != nil { - return nil, errors.Wrapf(err, `failed to initialize %T from %T`, k, rawKey) + return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err) } return k, nil case x25519.PrivateKey: - k := NewOKPPrivateKey() + k := newOKPPrivateKey() if err := k.FromRaw(rawKey); err != nil { - return nil, errors.Wrapf(err, `failed to initialize %T from %T`, k, rawKey) + return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err) } return k, nil case x25519.PublicKey: - k := NewOKPPublicKey() + k := newOKPPublicKey() if err := k.FromRaw(rawKey); err != nil { - return nil, errors.Wrapf(err, `failed to initialize %T from %T`, k, rawKey) + return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err) } return k, nil case []byte: - k := NewSymmetricKey() + k := newSymmetricKey() if err := k.FromRaw(rawKey); err != nil { - return nil, errors.Wrapf(err, `failed to initialize %T from %T`, k, rawKey) + return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err) } return k, nil default: - return nil, errors.Errorf(`invalid key type '%T' for jwk.New`, key) + return nil, fmt.Errorf(`invalid key type '%T' for jwk.New`, key) } } @@ -136,15 +134,17 @@ func PublicSetOf(v Set) (Set, error) { n := v.Len() for i := 0; i < n; i++ { - k, ok := v.Get(i) + k, ok := v.Key(i) if !ok { - return nil, errors.New("key not found") + return nil, fmt.Errorf(`key not found`) } pubKey, err := PublicKeyOf(k) if err != nil { - return nil, errors.Wrapf(err, `failed to get public key of %T`, k) + return nil, fmt.Errorf(`failed to get public key of %T: %w`, k, err) + } + if err := newSet.AddKey(pubKey); err != nil { + return nil, fmt.Errorf(`failed to add key to public key set: %w`, err) } - newSet.Add(pubKey) } return newSet, nil @@ -160,13 +160,14 @@ func PublicSetOf(v Set) (Set, error) { // // If `v` is a raw key, the key is first converted to a `jwk.Key` func PublicKeyOf(v interface{}) (Key, error) { + // This should catch all jwk.Key instances if pk, ok := v.(PublicKeyer); ok { return pk.PublicKey() } - jk, err := New(v) + jk, err := FromRaw(v) if err != nil { - return nil, errors.Wrapf(err, `failed to convert key into JWK`) + return nil, fmt.Errorf(`failed to convert key into JWK: %w`, err) } return jk.PublicKey() @@ -183,12 +184,12 @@ func PublicRawKeyOf(v interface{}) (interface{}, error) { if pk, ok := v.(PublicKeyer); ok { pubk, err := pk.PublicKey() if err != nil { - return nil, errors.Wrapf(err, `failed to obtain public key from %T`, v) + return nil, fmt.Errorf(`failed to obtain public key from %T: %w`, v, err) } var raw interface{} if err := pubk.Raw(&raw); err != nil { - return nil, errors.Wrapf(err, `failed to obtain raw key from %T`, pubk) + return nil, fmt.Errorf(`failed to obtain raw key from %T: %w`, pubk, err) } return raw, nil } @@ -228,86 +229,133 @@ func PublicRawKeyOf(v interface{}) (interface{}, error) { case []byte: return x, nil default: - return nil, errors.Errorf(`invalid key type passed to PublicKeyOf (%T)`, v) + return nil, fmt.Errorf(`invalid key type passed to PublicKeyOf (%T)`, v) } } -// Fetch fetches a JWK resource specified by a URL. The url must be -// pointing to a resource that is supported by `net/http`. +const ( + pmPrivateKey = `PRIVATE KEY` + pmPublicKey = `PUBLIC KEY` +) + +// EncodeX509 encodes the key into a byte sequence in ASN.1 DER format +// suitable for to be PEM encoded. The key can be a jwk.Key or a raw key +// instance, but it must be one of the types supported by `x509` package. // -// If you are using the same `jwk.Set` for long periods of time during -// the lifecycle of your program, and would like to periodically refresh the -// contents of the object with the data at the remote resource, -// consider using `jwk.AutoRefresh`, which automatically refreshes -// jwk.Set objects asynchronously. +// This function will try to do the right thing depending on the key type +// (i.e. switch between `x509.MarshalPKCS1PRivateKey` and `x509.MarshalECPrivateKey`), +// but for public keys, it will always use `x509.MarshalPKIXPublicKey`. +// Please manually perform the encoding if you need more fine grained control // -// See the list of `jwk.FetchOption`s for various options to tweak the -// behavior, including providing alternate HTTP Clients, setting a backoff, -// and using whitelists. -func Fetch(ctx context.Context, urlstring string, options ...FetchOption) (Set, error) { - res, err := fetch(ctx, urlstring, options...) - if err != nil { - return nil, err - } - - defer res.Body.Close() - keyset, err := ParseReader(res.Body) - if err != nil { - return nil, errors.Wrap(err, `failed to parse JWK set`) - } - return keyset, nil -} - -func fetch(ctx context.Context, urlstring string, options ...FetchOption) (*http.Response, error) { - var wl Whitelist - var httpcl HTTPClient = http.DefaultClient - bo := backoff.Null() - for _, option := range options { - //nolint:forcetypeassert - switch option.Ident() { - case identHTTPClient{}: - httpcl = option.Value().(HTTPClient) - case identFetchBackoff{}: - bo = option.Value().(backoff.Policy) - case identFetchWhitelist{}: - wl = option.Value().(Whitelist) +// The first return value is the name that can be used for `(pem.Block).Type`. +// The second return value is the encoded byte sequence. +func EncodeX509(v interface{}) (string, []byte, error) { + // we can't import jwk, so just use the interface + if key, ok := v.(interface{ Raw(interface{}) error }); ok { + var raw interface{} + if err := key.Raw(&raw); err != nil { + return "", nil, fmt.Errorf(`failed to get raw key out of %T: %w`, key, err) } + + v = raw } - if wl != nil { - if !wl.IsAllowed(urlstring) { - return nil, errors.New(`url rejected by whitelist`) - } - } - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, urlstring, nil) - if err != nil { - return nil, errors.Wrap(err, "failed to new request to remote JWK") - } - - b := bo.Start(ctx) - var lastError error - for backoff.Continue(b) { - res, err := httpcl.Do(req) + // Try to convert it into a certificate + switch v := v.(type) { + case *rsa.PrivateKey: + return "RSA PRIVATE KEY", x509.MarshalPKCS1PrivateKey(v), nil + case *ecdsa.PrivateKey: + marshaled, err := x509.MarshalECPrivateKey(v) if err != nil { - lastError = errors.Wrap(err, "failed to fetch remote JWK") - continue + return "", nil, err } + return "ECDSA PRIVATE KEY", marshaled, nil + case ed25519.PrivateKey: + marshaled, err := x509.MarshalPKCS8PrivateKey(v) + if err != nil { + return "", nil, err + } + return pmPrivateKey, marshaled, nil + case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey: + marshaled, err := x509.MarshalPKIXPublicKey(v) + if err != nil { + return "", nil, err + } + return pmPublicKey, marshaled, nil + default: + return "", nil, fmt.Errorf(`unsupported type %T for ASN.1 DER encoding`, v) + } +} - if res.StatusCode != http.StatusOK { - lastError = errors.Errorf("failed to fetch remote JWK (status = %d)", res.StatusCode) - continue - } - return res, nil +// EncodePEM encodes the key into a PEM encoded ASN.1 DER format. +// The key can be a jwk.Key or a raw key instance, but it must be one of +// the types supported by `x509` package. +// +// Internally, it uses the same routine as `jwk.EncodeX509()`, and therefore +// the same caveats apply +func EncodePEM(v interface{}) ([]byte, error) { + typ, marshaled, err := EncodeX509(v) + if err != nil { + return nil, fmt.Errorf(`failed to encode key in x509: %w`, err) } - // It's possible for us to get here without populating lastError. - // e.g. what if we bailed out of `for backoff.Contineu(b)` without making - // a single request? or, <-ctx.Done() returned? - if lastError == nil { - lastError = errors.New(`fetching remote JWK did not complete`) + block := &pem.Block{ + Type: typ, + Bytes: marshaled, + } + return pem.EncodeToMemory(block), nil +} + +// DecodePEM decodes a key in PEM encoded ASN.1 DER format. +// and returns a raw key +func DecodePEM(src []byte) (interface{}, []byte, error) { + block, rest := pem.Decode(src) + if block == nil { + return nil, nil, fmt.Errorf(`failed to decode PEM data`) + } + + switch block.Type { + // Handle the semi-obvious cases + case "RSA PRIVATE KEY": + key, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return nil, nil, fmt.Errorf(`failed to parse PKCS1 private key: %w`, err) + } + return key, rest, nil + case "RSA PUBLIC KEY": + key, err := x509.ParsePKCS1PublicKey(block.Bytes) + if err != nil { + return nil, nil, fmt.Errorf(`failed to parse PKCS1 public key: %w`, err) + } + return key, rest, nil + case "EC PRIVATE KEY": + key, err := x509.ParseECPrivateKey(block.Bytes) + if err != nil { + return nil, nil, fmt.Errorf(`failed to parse EC private key: %w`, err) + } + return key, rest, nil + case pmPublicKey: + // XXX *could* return dsa.PublicKey + key, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return nil, nil, fmt.Errorf(`failed to parse PKIX public key: %w`, err) + } + return key, rest, nil + case pmPrivateKey: + key, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + return nil, nil, fmt.Errorf(`failed to parse PKCS8 private key: %w`, err) + } + return key, rest, nil + case "CERTIFICATE": + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return nil, nil, fmt.Errorf(`failed to parse certificate: %w`, err) + } + return cert.PublicKey, rest, nil + default: + return nil, nil, fmt.Errorf(`invalid PEM block type %s`, block.Type) } - return nil, lastError } // ParseRawKey is a combination of ParseKey and Raw. It parses a single JWK key, @@ -317,69 +365,16 @@ func fetch(ctx context.Context, urlstring string, options ...FetchOption) (*http func ParseRawKey(data []byte, rawkey interface{}) error { key, err := ParseKey(data) if err != nil { - return errors.Wrap(err, `failed to parse key`) + return fmt.Errorf(`failed to parse key: %w`, err) } if err := key.Raw(rawkey); err != nil { - return errors.Wrap(err, `failed to assign to raw key variable`) + return fmt.Errorf(`failed to assign to raw key variable: %w`, err) } return nil } -// parsePEMEncodedRawKey parses a key in PEM encoded ASN.1 DER format. It tires its -// best to determine the key type, but when it just can't, it will return -// an error -func parsePEMEncodedRawKey(src []byte) (interface{}, []byte, error) { - block, rest := pem.Decode(src) - if block == nil { - return nil, nil, errors.New(`failed to decode PEM data`) - } - - switch block.Type { - // Handle the semi-obvious cases - case "RSA PRIVATE KEY": - key, err := x509.ParsePKCS1PrivateKey(block.Bytes) - if err != nil { - return nil, nil, errors.Wrap(err, `failed to parse PKCS1 private key`) - } - return key, rest, nil - case "RSA PUBLIC KEY": - key, err := x509.ParsePKCS1PublicKey(block.Bytes) - if err != nil { - return nil, nil, errors.Wrap(err, `failed to parse PKCS1 public key`) - } - return key, rest, nil - case "EC PRIVATE KEY": - key, err := x509.ParseECPrivateKey(block.Bytes) - if err != nil { - return nil, nil, errors.Wrap(err, `failed to parse EC private key`) - } - return key, rest, nil - case "PUBLIC KEY": - // XXX *could* return dsa.PublicKey - key, err := x509.ParsePKIXPublicKey(block.Bytes) - if err != nil { - return nil, nil, errors.Wrap(err, `failed to parse PKIX public key`) - } - return key, rest, nil - case "PRIVATE KEY": - key, err := x509.ParsePKCS8PrivateKey(block.Bytes) - if err != nil { - return nil, nil, errors.Wrap(err, `failed to parse PKCS8 private key`) - } - return key, rest, nil - case "CERTIFICATE": - cert, err := x509.ParseCertificate(block.Bytes) - if err != nil { - return nil, nil, errors.Wrap(err, `failed to parse certificate`) - } - return cert.PublicKey, rest, nil - default: - return nil, nil, errors.Errorf(`invalid PEM block type %s`, block.Type) - } -} - type setDecodeCtx struct { json.DecodeCtx ignoreParseError bool @@ -420,16 +415,16 @@ func ParseKey(data []byte, options ...ParseOption) (Key, error) { } localReg.Register(pair.Name, pair.Value) case identIgnoreParseError{}: - return nil, errors.Errorf(`jwk.WithIgnoreParseError() cannot be used for ParseKey()`) + return nil, fmt.Errorf(`jwk.WithIgnoreParseError() cannot be used for ParseKey()`) } } if parsePEM { - raw, _, err := parsePEMEncodedRawKey(data) + raw, _, err := DecodePEM(data) if err != nil { - return nil, errors.Wrap(err, `failed to parse PEM encoded key`) + return nil, fmt.Errorf(`failed to parse PEM encoded key: %w`, err) } - return New(raw) + return FromRaw(raw) } var hint struct { @@ -438,7 +433,7 @@ func ParseKey(data []byte, options ...ParseOption) (Key, error) { } if err := json.Unmarshal(data, &hint); err != nil { - return nil, errors.Wrap(err, `failed to unmarshal JSON into key hint`) + return nil, fmt.Errorf(`failed to unmarshal JSON into key hint: %w`, err) } var key Key @@ -464,13 +459,13 @@ func ParseKey(data []byte, options ...ParseOption) (Key, error) { key = newOKPPublicKey() } default: - return nil, errors.Errorf(`invalid key type from JSON (%s)`, hint.Kty) + return nil, fmt.Errorf(`invalid key type from JSON (%s)`, hint.Kty) } if localReg != nil { dcKey, ok := key.(json.DecodeCtxContainer) if !ok { - return nil, errors.Errorf(`typed field was requested, but the key (%T) does not support DecodeCtx`, key) + return nil, fmt.Errorf(`typed field was requested, but the key (%T) does not support DecodeCtx`, key) } dc := json.NewDecodeCtx(localReg) dcKey.SetDecodeCtx(dc) @@ -478,7 +473,7 @@ func ParseKey(data []byte, options ...ParseOption) (Key, error) { } if err := json.Unmarshal(data, key); err != nil { - return nil, errors.Wrapf(err, `failed to unmarshal JSON into key (%T)`, key) + return nil, fmt.Errorf(`failed to unmarshal JSON into key (%T): %w`, key, err) } return key, nil @@ -523,15 +518,17 @@ func Parse(src []byte, options ...ParseOption) (Set, error) { if parsePEM { src = bytes.TrimSpace(src) for len(src) > 0 { - raw, rest, err := parsePEMEncodedRawKey(src) + raw, rest, err := DecodePEM(src) if err != nil { - return nil, errors.Wrap(err, `failed to parse PEM encoded key`) + return nil, fmt.Errorf(`failed to parse PEM encoded key: %w`, err) } - key, err := New(raw) + key, err := FromRaw(raw) if err != nil { - return nil, errors.Wrapf(err, `failed to create jwk.Key from %T`, raw) + return nil, fmt.Errorf(`failed to create jwk.Key from %T: %w`, raw, err) + } + if err := s.AddKey(key); err != nil { + return nil, fmt.Errorf(`failed to add jwk.Key to set: %w`, err) } - s.Add(key) src = bytes.TrimSpace(rest) } return s, nil @@ -540,7 +537,7 @@ func Parse(src []byte, options ...ParseOption) (Set, error) { if localReg != nil || ignoreParseError { dcKs, ok := s.(KeyWithDecodeCtx) if !ok { - return nil, errors.Errorf(`typed field was requested, but the key set (%T) does not support DecodeCtx`, s) + return nil, fmt.Errorf(`typed field was requested, but the key set (%T) does not support DecodeCtx`, s) } dc := &setDecodeCtx{ DecodeCtx: json.NewDecodeCtx(localReg), @@ -551,8 +548,9 @@ func Parse(src []byte, options ...ParseOption) (Set, error) { } if err := json.Unmarshal(src, s); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal JWK set") + return nil, fmt.Errorf(`failed to unmarshal JWK set: %w`, err) } + return s, nil } @@ -560,9 +558,9 @@ func Parse(src []byte, options ...ParseOption) (Set, error) { func ParseReader(src io.Reader, options ...ParseOption) (Set, error) { // meh, there's no way to tell if a stream has "ended" a single // JWKs except when we encounter an EOF, so just... ReadAll - buf, err := ioutil.ReadAll(src) + buf, err := io.ReadAll(src) if err != nil { - return nil, errors.Wrap(err, `failed to read from io.Reader`) + return nil, fmt.Errorf(`failed to read from io.Reader: %w`, err) } return Parse(buf, options...) @@ -576,7 +574,7 @@ func ParseString(s string, options ...ParseOption) (Set, error) { // AssignKeyID is a convenience function to automatically assign the "kid" // section of the key, if it already doesn't have one. It uses Key.Thumbprint // method with crypto.SHA256 as the default hashing algorithm -func AssignKeyID(key Key, options ...Option) error { +func AssignKeyID(key Key, options ...AssignKeyIDOption) error { if _, ok := key.Get(KeyIDKey); ok { return nil } @@ -592,11 +590,11 @@ func AssignKeyID(key Key, options ...Option) error { h, err := key.Thumbprint(hash) if err != nil { - return errors.Wrap(err, `failed to generate thumbprint`) + return fmt.Errorf(`failed to generate thumbprint: %w`, err) } if err := key.Set(KeyIDKey, base64.EncodeToString(h)); err != nil { - return errors.Wrap(err, `failed to set "kid"`) + return fmt.Errorf(`failed to set "kid": %w`, err) } return nil @@ -606,28 +604,28 @@ func cloneKey(src Key) (Key, error) { var dst Key switch src.(type) { case RSAPrivateKey: - dst = NewRSAPrivateKey() + dst = newRSAPrivateKey() case RSAPublicKey: - dst = NewRSAPublicKey() + dst = newRSAPublicKey() case ECDSAPrivateKey: - dst = NewECDSAPrivateKey() + dst = newECDSAPrivateKey() case ECDSAPublicKey: - dst = NewECDSAPublicKey() + dst = newECDSAPublicKey() case OKPPrivateKey: - dst = NewOKPPrivateKey() + dst = newOKPPrivateKey() case OKPPublicKey: - dst = NewOKPPublicKey() + dst = newOKPPublicKey() case SymmetricKey: - dst = NewSymmetricKey() + dst = newSymmetricKey() default: - return nil, errors.Errorf(`unknown key type %T`, src) + return nil, fmt.Errorf(`unknown key type %T`, src) } for _, pair := range src.makePairs() { //nolint:forcetypeassert key := pair.Key.(string) if err := dst.Set(key, pair.Value); err != nil { - return nil, errors.Wrapf(err, `failed to set %q`, key) + return nil, fmt.Errorf(`failed to set %q: %w`, key, err) } } return dst, nil @@ -646,19 +644,21 @@ func Pem(v interface{}) ([]byte, error) { switch v := v.(type) { case Key: set = NewSet() - set.Add(v) + if err := set.AddKey(v); err != nil { + return nil, fmt.Errorf(`failed to add key to set: %w`, err) + } case Set: set = v default: - return nil, errors.Errorf(`argument to Pem must be either jwk.Key or jwk.Set: %T`, v) + return nil, fmt.Errorf(`argument to Pem must be either jwk.Key or jwk.Set: %T`, v) } var ret []byte for i := 0; i < set.Len(); i++ { - key, _ := set.Get(i) + key, _ := set.Key(i) typ, buf, err := asnEncode(key) if err != nil { - return nil, errors.Wrapf(err, `failed to encode content for key #%d`, i) + return nil, fmt.Errorf(`failed to encode content for key #%d: %w`, i, err) } var block pem.Block @@ -674,25 +674,25 @@ func asnEncode(key Key) (string, []byte, error) { case RSAPrivateKey, ECDSAPrivateKey, OKPPrivateKey: var rawkey interface{} if err := key.Raw(&rawkey); err != nil { - return "", nil, errors.Wrap(err, `failed to get raw key from jwk.Key`) + return "", nil, fmt.Errorf(`failed to get raw key from jwk.Key: %w`, err) } buf, err := x509.MarshalPKCS8PrivateKey(rawkey) if err != nil { - return "", nil, errors.Wrap(err, `failed to marshal PKCS8`) + return "", nil, fmt.Errorf(`failed to marshal PKCS8: %w`, err) } - return "PRIVATE KEY", buf, nil + return pmPrivateKey, buf, nil case RSAPublicKey, ECDSAPublicKey, OKPPublicKey: var rawkey interface{} if err := key.Raw(&rawkey); err != nil { - return "", nil, errors.Wrap(err, `failed to get raw key from jwk.Key`) + return "", nil, fmt.Errorf(`failed to get raw key from jwk.Key: %w`, err) } buf, err := x509.MarshalPKIXPublicKey(rawkey) if err != nil { - return "", nil, errors.Wrap(err, `failed to marshal PKIX`) + return "", nil, fmt.Errorf(`failed to marshal PKIX: %w`, err) } - return "PUBLIC KEY", buf, nil + return pmPublicKey, buf, nil default: - return "", nil, errors.Errorf(`unsupported key type %T`, key) + return "", nil, fmt.Errorf(`unsupported key type %T`, key) } } @@ -717,3 +717,11 @@ func asnEncode(key Key) (string, []byte, error) { func RegisterCustomField(name string, object interface{}) { registry.Register(name, object) } + +func AvailableCurves() []elliptic.Curve { + return ecutil.AvailableCurves() +} + +func CurveForAlgorithm(alg jwa.EllipticCurveAlgorithm) (elliptic.Curve, bool) { + return ecutil.CurveForAlgorithm(alg) +} diff --git a/vendor/github.com/lestrrat-go/jwx/jwk/key_ops.go b/vendor/github.com/lestrrat-go/jwx/v2/jwk/key_ops.go similarity index 79% rename from vendor/github.com/lestrrat-go/jwx/jwk/key_ops.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwk/key_ops.go index 01435f3..26fc2f2 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwk/key_ops.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwk/key_ops.go @@ -1,6 +1,6 @@ package jwk -import "github.com/pkg/errors" +import "fmt" func (ops *KeyOperationList) Get() KeyOperationList { if ops == nil { @@ -19,7 +19,7 @@ func (ops *KeyOperationList) Accept(v interface{}) error { if es, ok := e.(string); ok { l[i] = es } else { - return errors.Errorf(`invalid list element type: expected string, got %T`, v) + return fmt.Errorf(`invalid list element type: expected string, got %T`, v) } } return ops.Accept(l) @@ -30,7 +30,7 @@ func (ops *KeyOperationList) Accept(v interface{}) error { case KeyOpSign, KeyOpVerify, KeyOpEncrypt, KeyOpDecrypt, KeyOpWrapKey, KeyOpUnwrapKey, KeyOpDeriveKey, KeyOpDeriveBits: list[i] = e default: - return errors.Errorf(`invalid keyoperation %v`, e) + return fmt.Errorf(`invalid keyoperation %v`, e) } } @@ -43,7 +43,7 @@ func (ops *KeyOperationList) Accept(v interface{}) error { case KeyOpSign, KeyOpVerify, KeyOpEncrypt, KeyOpDecrypt, KeyOpWrapKey, KeyOpUnwrapKey, KeyOpDeriveKey, KeyOpDeriveBits: list[i] = e default: - return errors.Errorf(`invalid keyoperation %v`, e) + return fmt.Errorf(`invalid keyoperation %v`, e) } } @@ -53,6 +53,6 @@ func (ops *KeyOperationList) Accept(v interface{}) error { *ops = x return nil default: - return errors.Errorf(`invalid value %T`, v) + return fmt.Errorf(`invalid value %T`, v) } } diff --git a/vendor/github.com/lestrrat-go/jwx/jwk/okp.go b/vendor/github.com/lestrrat-go/jwx/v2/jwk/okp.go similarity index 81% rename from vendor/github.com/lestrrat-go/jwx/jwk/okp.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwk/okp.go index 6d99bd6..2686ba5 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwk/okp.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwk/okp.go @@ -7,10 +7,9 @@ import ( "fmt" "github.com/lestrrat-go/blackmagic" - "github.com/lestrrat-go/jwx/internal/base64" - "github.com/lestrrat-go/jwx/jwa" - "github.com/lestrrat-go/jwx/x25519" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/internal/base64" + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/x25519" ) func (k *okpPublicKey) FromRaw(rawKeyIf interface{}) error { @@ -28,7 +27,7 @@ func (k *okpPublicKey) FromRaw(rawKeyIf interface{}) error { crv = jwa.X25519 k.crv = &crv default: - return errors.Errorf(`unknown key type %T`, rawKeyIf) + return fmt.Errorf(`unknown key type %T`, rawKeyIf) } return nil @@ -51,7 +50,7 @@ func (k *okpPrivateKey) FromRaw(rawKeyIf interface{}) error { crv = jwa.X25519 k.crv = &crv default: - return errors.Errorf(`unknown key type %T`, rawKeyIf) + return fmt.Errorf(`unknown key type %T`, rawKeyIf) } return nil @@ -64,7 +63,7 @@ func buildOKPPublicKey(alg jwa.EllipticCurveAlgorithm, xbuf []byte) (interface{} case jwa.X25519: return x25519.PublicKey(xbuf), nil default: - return nil, errors.Errorf(`invalid curve algorithm %s`, alg) + return nil, fmt.Errorf(`invalid curve algorithm %s`, alg) } } @@ -75,7 +74,7 @@ func (k *okpPublicKey) Raw(v interface{}) error { pubk, err := buildOKPPublicKey(k.Crv(), k.x) if err != nil { - return errors.Wrap(err, `failed to build public key`) + return fmt.Errorf(`failed to build public key: %w`, err) } return blackmagic.AssignIfCompatible(v, pubk) @@ -87,21 +86,21 @@ func buildOKPPrivateKey(alg jwa.EllipticCurveAlgorithm, xbuf []byte, dbuf []byte ret := ed25519.NewKeyFromSeed(dbuf) //nolint:forcetypeassert if !bytes.Equal(xbuf, ret.Public().(ed25519.PublicKey)) { - return nil, errors.Errorf(`invalid x value given d value`) + return nil, fmt.Errorf(`invalid x value given d value`) } return ret, nil case jwa.X25519: ret, err := x25519.NewKeyFromSeed(dbuf) if err != nil { - return nil, errors.Wrap(err, `unable to construct x25519 private key from seed`) + return nil, fmt.Errorf(`unable to construct x25519 private key from seed: %w`, err) } //nolint:forcetypeassert if !bytes.Equal(xbuf, ret.Public().(x25519.PublicKey)) { - return nil, errors.Errorf(`invalid x value given d value`) + return nil, fmt.Errorf(`invalid x value given d value`) } return ret, nil default: - return nil, errors.Errorf(`invalid curve algorithm %s`, alg) + return nil, fmt.Errorf(`invalid curve algorithm %s`, alg) } } @@ -111,7 +110,7 @@ func (k *okpPrivateKey) Raw(v interface{}) error { privk, err := buildOKPPrivateKey(k.Crv(), k.x, k.d) if err != nil { - return errors.Wrap(err, `failed to build public key`) + return fmt.Errorf(`failed to build public key: %w`, err) } return blackmagic.AssignIfCompatible(v, privk) @@ -120,7 +119,7 @@ func (k *okpPrivateKey) Raw(v interface{}) error { func makeOKPPublicKey(v interface { makePairs() []*HeaderPair }) (Key, error) { - newKey := NewOKPPublicKey() + newKey := newOKPPublicKey() // Iterate and copy everything except for the bits that should not be in the public key for _, pair := range v.makePairs() { @@ -131,7 +130,7 @@ func makeOKPPublicKey(v interface { //nolint:forcetypeassert key := pair.Key.(string) if err := newKey.Set(key, pair.Value); err != nil { - return nil, errors.Wrapf(err, `failed to set field %q`, key) + return nil, fmt.Errorf(`failed to set field %q: %w`, key, err) } } } diff --git a/vendor/github.com/lestrrat-go/jwx/jwk/okp_gen.go b/vendor/github.com/lestrrat-go/jwx/v2/jwk/okp_gen.go similarity index 75% rename from vendor/github.com/lestrrat-go/jwx/jwk/okp_gen.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwk/okp_gen.go index 022c285..ccad677 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwk/okp_gen.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwk/okp_gen.go @@ -5,18 +5,17 @@ package jwk import ( "bytes" "context" - "crypto/x509" "fmt" "sort" "sync" "github.com/lestrrat-go/iter/mapiter" - "github.com/lestrrat-go/jwx/internal/base64" - "github.com/lestrrat-go/jwx/internal/iter" - "github.com/lestrrat-go/jwx/internal/json" - "github.com/lestrrat-go/jwx/internal/pool" - "github.com/lestrrat-go/jwx/jwa" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/cert" + "github.com/lestrrat-go/jwx/v2/internal/base64" + "github.com/lestrrat-go/jwx/v2/internal/iter" + "github.com/lestrrat-go/jwx/v2/internal/json" + "github.com/lestrrat-go/jwx/v2/internal/pool" + "github.com/lestrrat-go/jwx/v2/jwa" ) const ( @@ -33,24 +32,23 @@ type OKPPublicKey interface { } type okpPublicKey struct { - algorithm *string // https://tools.ietf.org/html/rfc7517#section-4.4 + algorithm *jwa.KeyAlgorithm // https://tools.ietf.org/html/rfc7517#section-4.4 crv *jwa.EllipticCurveAlgorithm keyID *string // https://tools.ietf.org/html/rfc7515#section-4.1.4 keyOps *KeyOperationList // https://tools.ietf.org/html/rfc7517#section-4.3 keyUsage *string // https://tools.ietf.org/html/rfc7517#section-4.2 x []byte - x509CertChain *CertificateChain // https://tools.ietf.org/html/rfc7515#section-4.1.6 - x509CertThumbprint *string // https://tools.ietf.org/html/rfc7515#section-4.1.7 - x509CertThumbprintS256 *string // https://tools.ietf.org/html/rfc7515#section-4.1.8 - x509URL *string // https://tools.ietf.org/html/rfc7515#section-4.1.5 + x509CertChain *cert.Chain // https://tools.ietf.org/html/rfc7515#section-4.1.6 + x509CertThumbprint *string // https://tools.ietf.org/html/rfc7515#section-4.1.7 + x509CertThumbprintS256 *string // https://tools.ietf.org/html/rfc7515#section-4.1.8 + x509URL *string // https://tools.ietf.org/html/rfc7515#section-4.1.5 privateParams map[string]interface{} mu *sync.RWMutex dc json.DecodeCtx } -func NewOKPPublicKey() OKPPublicKey { - return newOKPPublicKey() -} +var _ OKPPublicKey = &okpPublicKey{} +var _ Key = &okpPublicKey{} func newOKPPublicKey() *okpPublicKey { return &okpPublicKey{ @@ -63,11 +61,11 @@ func (h okpPublicKey) KeyType() jwa.KeyType { return jwa.OKP } -func (h *okpPublicKey) Algorithm() string { +func (h *okpPublicKey) Algorithm() jwa.KeyAlgorithm { if h.algorithm != nil { return *(h.algorithm) } - return "" + return jwa.InvalidKeyAlgorithm("") } func (h *okpPublicKey) Crv() jwa.EllipticCurveAlgorithm { @@ -102,11 +100,8 @@ func (h *okpPublicKey) X() []byte { return h.x } -func (h *okpPublicKey) X509CertChain() []*x509.Certificate { - if h.x509CertChain != nil { - return h.x509CertChain.Get() - } - return nil +func (h *okpPublicKey) X509CertChain() *cert.Chain { + return h.x509CertChain } func (h *okpPublicKey) X509CertThumbprint() string { @@ -155,7 +150,7 @@ func (h *okpPublicKey) makePairs() []*HeaderPair { pairs = append(pairs, &HeaderPair{Key: OKPXKey, Value: h.x}) } if h.x509CertChain != nil { - pairs = append(pairs, &HeaderPair{Key: X509CertChainKey, Value: *(h.x509CertChain)}) + pairs = append(pairs, &HeaderPair{Key: X509CertChainKey, Value: h.x509CertChain}) } if h.x509CertThumbprint != nil { pairs = append(pairs, &HeaderPair{Key: X509CertThumbprintKey, Value: *(h.x509CertThumbprint)}) @@ -216,7 +211,7 @@ func (h *okpPublicKey) Get(name string) (interface{}, bool) { if h.x509CertChain == nil { return nil, false } - return h.x509CertChain.Get(), true + return h.x509CertChain, true case X509CertThumbprintKey: if h.x509CertThumbprint == nil { return nil, false @@ -250,13 +245,15 @@ func (h *okpPublicKey) setNoLock(name string, value interface{}) error { return nil case AlgorithmKey: switch v := value.(type) { - case string: - h.algorithm = &v + case string, jwa.SignatureAlgorithm, jwa.ContentEncryptionAlgorithm: + var tmp = jwa.KeyAlgorithmFrom(v) + h.algorithm = &tmp case fmt.Stringer: - tmp := v.String() + s := v.String() + var tmp = jwa.KeyAlgorithmFrom(s) h.algorithm = &tmp default: - return errors.Errorf(`invalid type for %s key: %T`, AlgorithmKey, value) + return fmt.Errorf(`invalid type for %s key: %T`, AlgorithmKey, value) } return nil case OKPCrvKey: @@ -264,17 +261,17 @@ func (h *okpPublicKey) setNoLock(name string, value interface{}) error { h.crv = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, OKPCrvKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, OKPCrvKey, value) case KeyIDKey: if v, ok := value.(string); ok { h.keyID = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, KeyIDKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, KeyIDKey, value) case KeyOpsKey: var acceptor KeyOperationList if err := acceptor.Accept(value); err != nil { - return errors.Wrapf(err, `invalid value for %s key`, KeyOpsKey) + return fmt.Errorf(`invalid value for %s key: %w`, KeyOpsKey, err) } h.keyOps = &acceptor return nil @@ -286,44 +283,43 @@ func (h *okpPublicKey) setNoLock(name string, value interface{}) error { tmp := v.String() h.keyUsage = &tmp default: - return errors.Errorf(`invalid key usage type %s`, v) + return fmt.Errorf(`invalid key usage type %s`, v) } case string: h.keyUsage = &v default: - return errors.Errorf(`invalid key usage type %s`, v) + return fmt.Errorf(`invalid key usage type %s`, v) } case OKPXKey: if v, ok := value.([]byte); ok { h.x = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, OKPXKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, OKPXKey, value) case X509CertChainKey: - var acceptor CertificateChain - if err := acceptor.Accept(value); err != nil { - return errors.Wrapf(err, `invalid value for %s key`, X509CertChainKey) + if v, ok := value.(*cert.Chain); ok { + h.x509CertChain = v + return nil } - h.x509CertChain = &acceptor - return nil + return fmt.Errorf(`invalid value for %s key: %T`, X509CertChainKey, value) case X509CertThumbprintKey: if v, ok := value.(string); ok { h.x509CertThumbprint = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509CertThumbprintKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509CertThumbprintKey, value) case X509CertThumbprintS256Key: if v, ok := value.(string); ok { h.x509CertThumbprintS256 = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509CertThumbprintS256Key, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509CertThumbprintS256Key, value) case X509URLKey: if v, ok := value.(string); ok { h.x509URL = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509URLKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509URLKey, value) default: if h.privateParams == nil { h.privateParams = map[string]interface{}{} @@ -380,6 +376,8 @@ func (k *okpPublicKey) SetDecodeCtx(dc json.DecodeCtx) { } func (h *okpPublicKey) UnmarshalJSON(buf []byte) error { + h.mu.Lock() + defer h.mu.Unlock() h.algorithm = nil h.crv = nil h.keyID = nil @@ -395,7 +393,7 @@ LOOP: for { tok, err := dec.Token() if err != nil { - return errors.Wrap(err, `error reading token`) + return fmt.Errorf(`error reading token: %w`, err) } switch tok := tok.(type) { case json.Delim: @@ -404,63 +402,66 @@ LOOP: if tok == '}' { // End of object break LOOP } else if tok != '{' { - return errors.Errorf(`expected '{', but got '%c'`, tok) + return fmt.Errorf(`expected '{', but got '%c'`, tok) } case string: // Objects can only have string keys switch tok { case KeyTypeKey: val, err := json.ReadNextStringToken(dec) if err != nil { - return errors.Wrap(err, `error reading token`) + return fmt.Errorf(`error reading token: %w`, err) } if val != jwa.OKP.String() { - return errors.Errorf(`invalid kty value for RSAPublicKey (%s)`, val) + return fmt.Errorf(`invalid kty value for RSAPublicKey (%s)`, val) } case AlgorithmKey: - if err := json.AssignNextStringToken(&h.algorithm, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, AlgorithmKey) + var s string + if err := dec.Decode(&s); err != nil { + return fmt.Errorf(`failed to decode value for key %s: %w`, AlgorithmKey, err) } + alg := jwa.KeyAlgorithmFrom(s) + h.algorithm = &alg case OKPCrvKey: var decoded jwa.EllipticCurveAlgorithm if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, OKPCrvKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, OKPCrvKey, err) } h.crv = &decoded case KeyIDKey: if err := json.AssignNextStringToken(&h.keyID, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, KeyIDKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, KeyIDKey, err) } case KeyOpsKey: var decoded KeyOperationList if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, KeyOpsKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, KeyOpsKey, err) } h.keyOps = &decoded case KeyUsageKey: if err := json.AssignNextStringToken(&h.keyUsage, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, KeyUsageKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, KeyUsageKey, err) } case OKPXKey: if err := json.AssignNextBytesToken(&h.x, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, OKPXKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, OKPXKey, err) } case X509CertChainKey: - var decoded CertificateChain + var decoded cert.Chain if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertChainKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertChainKey, err) } h.x509CertChain = &decoded case X509CertThumbprintKey: if err := json.AssignNextStringToken(&h.x509CertThumbprint, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertThumbprintKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertThumbprintKey, err) } case X509CertThumbprintS256Key: if err := json.AssignNextStringToken(&h.x509CertThumbprintS256, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertThumbprintS256Key) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertThumbprintS256Key, err) } case X509URLKey: if err := json.AssignNextStringToken(&h.x509URL, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509URLKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509URLKey, err) } default: if dc := h.dc; dc != nil { @@ -477,17 +478,17 @@ LOOP: h.setNoLock(tok, decoded) continue } - return errors.Wrapf(err, `could not decode field %s`, tok) + return fmt.Errorf(`could not decode field %s: %w`, tok, err) } default: - return errors.Errorf(`invalid token %T`, tok) + return fmt.Errorf(`invalid token %T`, tok) } } if h.crv == nil { - return errors.Errorf(`required field crv is missing`) + return fmt.Errorf(`required field crv is missing`) } if h.x == nil { - return errors.Errorf(`required field x is missing`) + return fmt.Errorf(`required field x is missing`) } return nil } @@ -520,7 +521,7 @@ func (h okpPublicKey) MarshalJSON() ([]byte, error) { buf.WriteRune('"') default: if err := enc.Encode(v); err != nil { - return nil, errors.Wrapf(err, `failed to encode value for field %s`, f) + return nil, fmt.Errorf(`failed to encode value for field %s: %w`, f, err) } buf.Truncate(buf.Len() - 1) } @@ -564,25 +565,24 @@ type OKPPrivateKey interface { } type okpPrivateKey struct { - algorithm *string // https://tools.ietf.org/html/rfc7517#section-4.4 + algorithm *jwa.KeyAlgorithm // https://tools.ietf.org/html/rfc7517#section-4.4 crv *jwa.EllipticCurveAlgorithm d []byte keyID *string // https://tools.ietf.org/html/rfc7515#section-4.1.4 keyOps *KeyOperationList // https://tools.ietf.org/html/rfc7517#section-4.3 keyUsage *string // https://tools.ietf.org/html/rfc7517#section-4.2 x []byte - x509CertChain *CertificateChain // https://tools.ietf.org/html/rfc7515#section-4.1.6 - x509CertThumbprint *string // https://tools.ietf.org/html/rfc7515#section-4.1.7 - x509CertThumbprintS256 *string // https://tools.ietf.org/html/rfc7515#section-4.1.8 - x509URL *string // https://tools.ietf.org/html/rfc7515#section-4.1.5 + x509CertChain *cert.Chain // https://tools.ietf.org/html/rfc7515#section-4.1.6 + x509CertThumbprint *string // https://tools.ietf.org/html/rfc7515#section-4.1.7 + x509CertThumbprintS256 *string // https://tools.ietf.org/html/rfc7515#section-4.1.8 + x509URL *string // https://tools.ietf.org/html/rfc7515#section-4.1.5 privateParams map[string]interface{} mu *sync.RWMutex dc json.DecodeCtx } -func NewOKPPrivateKey() OKPPrivateKey { - return newOKPPrivateKey() -} +var _ OKPPrivateKey = &okpPrivateKey{} +var _ Key = &okpPrivateKey{} func newOKPPrivateKey() *okpPrivateKey { return &okpPrivateKey{ @@ -595,11 +595,11 @@ func (h okpPrivateKey) KeyType() jwa.KeyType { return jwa.OKP } -func (h *okpPrivateKey) Algorithm() string { +func (h *okpPrivateKey) Algorithm() jwa.KeyAlgorithm { if h.algorithm != nil { return *(h.algorithm) } - return "" + return jwa.InvalidKeyAlgorithm("") } func (h *okpPrivateKey) Crv() jwa.EllipticCurveAlgorithm { @@ -638,11 +638,8 @@ func (h *okpPrivateKey) X() []byte { return h.x } -func (h *okpPrivateKey) X509CertChain() []*x509.Certificate { - if h.x509CertChain != nil { - return h.x509CertChain.Get() - } - return nil +func (h *okpPrivateKey) X509CertChain() *cert.Chain { + return h.x509CertChain } func (h *okpPrivateKey) X509CertThumbprint() string { @@ -694,7 +691,7 @@ func (h *okpPrivateKey) makePairs() []*HeaderPair { pairs = append(pairs, &HeaderPair{Key: OKPXKey, Value: h.x}) } if h.x509CertChain != nil { - pairs = append(pairs, &HeaderPair{Key: X509CertChainKey, Value: *(h.x509CertChain)}) + pairs = append(pairs, &HeaderPair{Key: X509CertChainKey, Value: h.x509CertChain}) } if h.x509CertThumbprint != nil { pairs = append(pairs, &HeaderPair{Key: X509CertThumbprintKey, Value: *(h.x509CertThumbprint)}) @@ -760,7 +757,7 @@ func (h *okpPrivateKey) Get(name string) (interface{}, bool) { if h.x509CertChain == nil { return nil, false } - return h.x509CertChain.Get(), true + return h.x509CertChain, true case X509CertThumbprintKey: if h.x509CertThumbprint == nil { return nil, false @@ -794,13 +791,15 @@ func (h *okpPrivateKey) setNoLock(name string, value interface{}) error { return nil case AlgorithmKey: switch v := value.(type) { - case string: - h.algorithm = &v + case string, jwa.SignatureAlgorithm, jwa.ContentEncryptionAlgorithm: + var tmp = jwa.KeyAlgorithmFrom(v) + h.algorithm = &tmp case fmt.Stringer: - tmp := v.String() + s := v.String() + var tmp = jwa.KeyAlgorithmFrom(s) h.algorithm = &tmp default: - return errors.Errorf(`invalid type for %s key: %T`, AlgorithmKey, value) + return fmt.Errorf(`invalid type for %s key: %T`, AlgorithmKey, value) } return nil case OKPCrvKey: @@ -808,23 +807,23 @@ func (h *okpPrivateKey) setNoLock(name string, value interface{}) error { h.crv = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, OKPCrvKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, OKPCrvKey, value) case OKPDKey: if v, ok := value.([]byte); ok { h.d = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, OKPDKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, OKPDKey, value) case KeyIDKey: if v, ok := value.(string); ok { h.keyID = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, KeyIDKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, KeyIDKey, value) case KeyOpsKey: var acceptor KeyOperationList if err := acceptor.Accept(value); err != nil { - return errors.Wrapf(err, `invalid value for %s key`, KeyOpsKey) + return fmt.Errorf(`invalid value for %s key: %w`, KeyOpsKey, err) } h.keyOps = &acceptor return nil @@ -836,44 +835,43 @@ func (h *okpPrivateKey) setNoLock(name string, value interface{}) error { tmp := v.String() h.keyUsage = &tmp default: - return errors.Errorf(`invalid key usage type %s`, v) + return fmt.Errorf(`invalid key usage type %s`, v) } case string: h.keyUsage = &v default: - return errors.Errorf(`invalid key usage type %s`, v) + return fmt.Errorf(`invalid key usage type %s`, v) } case OKPXKey: if v, ok := value.([]byte); ok { h.x = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, OKPXKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, OKPXKey, value) case X509CertChainKey: - var acceptor CertificateChain - if err := acceptor.Accept(value); err != nil { - return errors.Wrapf(err, `invalid value for %s key`, X509CertChainKey) + if v, ok := value.(*cert.Chain); ok { + h.x509CertChain = v + return nil } - h.x509CertChain = &acceptor - return nil + return fmt.Errorf(`invalid value for %s key: %T`, X509CertChainKey, value) case X509CertThumbprintKey: if v, ok := value.(string); ok { h.x509CertThumbprint = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509CertThumbprintKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509CertThumbprintKey, value) case X509CertThumbprintS256Key: if v, ok := value.(string); ok { h.x509CertThumbprintS256 = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509CertThumbprintS256Key, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509CertThumbprintS256Key, value) case X509URLKey: if v, ok := value.(string); ok { h.x509URL = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509URLKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509URLKey, value) default: if h.privateParams == nil { h.privateParams = map[string]interface{}{} @@ -932,6 +930,8 @@ func (k *okpPrivateKey) SetDecodeCtx(dc json.DecodeCtx) { } func (h *okpPrivateKey) UnmarshalJSON(buf []byte) error { + h.mu.Lock() + defer h.mu.Unlock() h.algorithm = nil h.crv = nil h.d = nil @@ -948,7 +948,7 @@ LOOP: for { tok, err := dec.Token() if err != nil { - return errors.Wrap(err, `error reading token`) + return fmt.Errorf(`error reading token: %w`, err) } switch tok := tok.(type) { case json.Delim: @@ -957,67 +957,70 @@ LOOP: if tok == '}' { // End of object break LOOP } else if tok != '{' { - return errors.Errorf(`expected '{', but got '%c'`, tok) + return fmt.Errorf(`expected '{', but got '%c'`, tok) } case string: // Objects can only have string keys switch tok { case KeyTypeKey: val, err := json.ReadNextStringToken(dec) if err != nil { - return errors.Wrap(err, `error reading token`) + return fmt.Errorf(`error reading token: %w`, err) } if val != jwa.OKP.String() { - return errors.Errorf(`invalid kty value for RSAPublicKey (%s)`, val) + return fmt.Errorf(`invalid kty value for RSAPublicKey (%s)`, val) } case AlgorithmKey: - if err := json.AssignNextStringToken(&h.algorithm, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, AlgorithmKey) + var s string + if err := dec.Decode(&s); err != nil { + return fmt.Errorf(`failed to decode value for key %s: %w`, AlgorithmKey, err) } + alg := jwa.KeyAlgorithmFrom(s) + h.algorithm = &alg case OKPCrvKey: var decoded jwa.EllipticCurveAlgorithm if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, OKPCrvKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, OKPCrvKey, err) } h.crv = &decoded case OKPDKey: if err := json.AssignNextBytesToken(&h.d, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, OKPDKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, OKPDKey, err) } case KeyIDKey: if err := json.AssignNextStringToken(&h.keyID, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, KeyIDKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, KeyIDKey, err) } case KeyOpsKey: var decoded KeyOperationList if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, KeyOpsKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, KeyOpsKey, err) } h.keyOps = &decoded case KeyUsageKey: if err := json.AssignNextStringToken(&h.keyUsage, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, KeyUsageKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, KeyUsageKey, err) } case OKPXKey: if err := json.AssignNextBytesToken(&h.x, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, OKPXKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, OKPXKey, err) } case X509CertChainKey: - var decoded CertificateChain + var decoded cert.Chain if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertChainKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertChainKey, err) } h.x509CertChain = &decoded case X509CertThumbprintKey: if err := json.AssignNextStringToken(&h.x509CertThumbprint, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertThumbprintKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertThumbprintKey, err) } case X509CertThumbprintS256Key: if err := json.AssignNextStringToken(&h.x509CertThumbprintS256, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertThumbprintS256Key) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertThumbprintS256Key, err) } case X509URLKey: if err := json.AssignNextStringToken(&h.x509URL, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509URLKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509URLKey, err) } default: if dc := h.dc; dc != nil { @@ -1034,20 +1037,20 @@ LOOP: h.setNoLock(tok, decoded) continue } - return errors.Wrapf(err, `could not decode field %s`, tok) + return fmt.Errorf(`could not decode field %s: %w`, tok, err) } default: - return errors.Errorf(`invalid token %T`, tok) + return fmt.Errorf(`invalid token %T`, tok) } } if h.crv == nil { - return errors.Errorf(`required field crv is missing`) + return fmt.Errorf(`required field crv is missing`) } if h.d == nil { - return errors.Errorf(`required field d is missing`) + return fmt.Errorf(`required field d is missing`) } if h.x == nil { - return errors.Errorf(`required field x is missing`) + return fmt.Errorf(`required field x is missing`) } return nil } @@ -1080,7 +1083,7 @@ func (h okpPrivateKey) MarshalJSON() ([]byte, error) { buf.WriteRune('"') default: if err := enc.Encode(v); err != nil { - return nil, errors.Wrapf(err, `failed to encode value for field %s`, f) + return nil, fmt.Errorf(`failed to encode value for field %s: %w`, f, err) } buf.Truncate(buf.Len() - 1) } diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jwk/options.go b/vendor/github.com/lestrrat-go/jwx/v2/jwk/options.go new file mode 100644 index 0000000..98fcc40 --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwk/options.go @@ -0,0 +1,38 @@ +package jwk + +import ( + "github.com/lestrrat-go/option" +) + +type identTypedField struct{} + +type typedFieldPair struct { + Name string + Value interface{} +} + +// WithTypedField allows a private field to be parsed into the object type of +// your choice. It works much like the RegisterCustomField, but the effect +// is only applicable to the jwt.Parse function call which receives this option. +// +// While this can be extremely useful, this option should be used with caution: +// There are many caveats that your entire team/user-base needs to be aware of, +// and therefore in general its use is discouraged. Only use it when you know +// what you are doing, and you document its use clearly for others. +// +// First and foremost, this is a "per-object" option. Meaning that given the same +// serialized format, it is possible to generate two objects whose internal +// representations may differ. That is, if you parse one _WITH_ the option, +// and the other _WITHOUT_, their internal representation may completely differ. +// This could potentially lead to problems. +// +// Second, specifying this option will slightly slow down the decoding process +// as it needs to consult multiple definitions sources (global and local), so +// be careful if you are decoding a large number of tokens, as the effects will stack up. +func WithTypedField(name string, object interface{}) ParseOption { + return &parseOption{ + option.New(identTypedField{}, + typedFieldPair{Name: name, Value: object}, + ), + } +} diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jwk/options.yaml b/vendor/github.com/lestrrat-go/jwx/v2/jwk/options.yaml new file mode 100644 index 0000000..3f7b6e2 --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwk/options.yaml @@ -0,0 +1,142 @@ +package_name: jwk +output: jwk/options_gen.go +interfaces: + - name: CacheOption + comment: | + CacheOption is a type of Option that can be passed to the + `jwk.Cache` object. + - name: AssignKeyIDOption + - name: FetchOption + methods: + - fetchOption + - parseOption + - registerOption + comment: | + FetchOption is a type of Option that can be passed to `jwk.Fetch()` + FetchOption also implements the `CacheOption`, and thus can + safely be passed to `(*jwk.Cache).Configure()` + - name: ParseOption + methods: + - fetchOption + - registerOption + - readFileOption + comment: | + ParseOption is a type of Option that can be passed to `jwk.Parse()` + ParseOption also implmentsthe `ReadFileOption` and `CacheOption`, + and thus safely be passed to `jwk.ReadFile` and `(*jwk.Cache).Configure()` + - name: ReadFileOption + comment: | + ReadFileOption is a type of `Option` that can be passed to `jwk.ReadFile` + - name: RegisterOption + comment: | + RegisterOption desribes options that can be passed to `(jwk.Cache).Register()` +options: + - ident: HTTPClient + interface: FetchOption + argument_type: HTTPClient + comment: | + WithHTTPClient allows users to specify the "net/http".Client object that + is used when fetching jwk.Set objects. + - ident: ThumbprintHash + interface: AssignKeyIDOption + argument_type: crypto.Hash + - ident: RefreshInterval + interface: RegisterOption + argument_type: time.Duration + comment: | + WithRefreshInterval specifies the static interval between refreshes + of jwk.Set objects controlled by jwk.Cache. + + Providing this option overrides the adaptive token refreshing based + on Cache-Control/Expires header (and jwk.WithMinRefreshInterval), + and refreshes will *always* happen in this interval. + - ident: MinRefreshInterval + interface: RegisterOption + argument_type: time.Duration + comment: | + WithMinRefreshInterval specifies the minimum refresh interval to be used + when using `jwk.Cache`. This value is ONLY used if you did not specify + a user-supplied static refresh interval via `WithRefreshInterval`. + + This value is used as a fallback value when tokens are refreshed. + + When we fetch the key from a remote URL, we first look at the max-age + directive from Cache-Control response header. If this value is present, + we compare the max-age value and the value specified by this option + and take the larger one. + + Next we check for the Expires header, and similarly if the header is + present, we compare it against the value specified by this option, + and take the larger one. + + Finally, if neither of the above headers are present, we use the + value specified by this option as the next refresh timing + + If unspecified, the minimum refresh interval is 1 hour + - ident: LocalRegistry + option_name: withLocalRegistry + interface: ParseOption + argument_type: '*json.Registry' + comment: This option is only available for internal code. Users don't get to play with it + - ident: PEM + interface: ParseOption + argument_type: bool + comment: WithPEM specifies that the input to `Parse()` is a PEM encoded key. + - ident: FetchWhitelist + interface: FetchOption + argument_type: Whitelist + comment: | + WithFetchWhitelist specifies the Whitelist object to use when + fetching JWKs from a remote source. This option can be passed + to both `jwk.Fetch()`, `jwk.NewCache()`, and `(*jwk.Cache).Configure()` + - ident: IgnoreParseError + interface: ParseOption + argument_type: bool + comment: | + WithIgnoreParseError is only applicable when used with `jwk.Parse()` + (i.e. to parse JWK sets). If passed to `jwk.ParseKey()`, the function + will return an error no matter what the input is. + + DO NOT USE WITHOUT EXHAUSTING ALL OTHER ROUTES FIRST. + + The option specifies that errors found during parsing of individual + keys are ignored. For example, if you had keys A, B, C where B is + invalid (e.g. it does not contain the required fields), then the + resulting JWKS will contain keys A and C only. + + This options exists as an escape hatch for those times when a + key in a JWKS that is irrelevant for your use case is causing + your JWKS parsing to fail, and you want to get to the rest of the + keys in the JWKS. + + Again, DO NOT USE unless you have exhausted all other routes. + When you use this option, you will not be able to tell if you are + using a faulty JWKS, except for when there are JSON syntax errors. + - ident: FS + interface: ReadFileOption + argument_type: fs.FS + comment: | + WithFS specifies the source `fs.FS` object to read the file from. + - ident: PostFetcher + interface: RegisterOption + argument_type: PostFetcher + comment: | + WithPostFetcher specifies the PostFetcher object to be used on the + jwk.Set object obtained in `jwk.Cache`. This option can be used + to, for example, modify the jwk.Set to give it key IDs or algorithm + names after it has been fetched and parsed, but before it is cached. + - ident: RefreshWindow + interface: CacheOption + argument_type: time.Duration + comment: | + WithRefreshWindow specifies the interval between checks for refreshes. + + See the documentation in `httprc.WithRefreshWindow` for more details. + - ident: ErrSink + interface: CacheOption + argument_type: ErrSink + comment: | + WithErrSink specifies the `httprc.ErrSink` object that handles errors + that occurred during the cache's execution. + + See the documentation in `httprc.WithErrSink` for more details. diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jwk/options_gen.go b/vendor/github.com/lestrrat-go/jwx/v2/jwk/options_gen.go new file mode 100644 index 0000000..17e23a7 --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwk/options_gen.go @@ -0,0 +1,274 @@ +// This file is auto-generated by internal/cmd/genoptions/main.go. DO NOT EDIT + +package jwk + +import ( + "crypto" + "io/fs" + "time" + + "github.com/lestrrat-go/jwx/v2/internal/json" + "github.com/lestrrat-go/option" +) + +type Option = option.Interface + +type AssignKeyIDOption interface { + Option + assignKeyIDOption() +} + +type assignKeyIDOption struct { + Option +} + +func (*assignKeyIDOption) assignKeyIDOption() {} + +// CacheOption is a type of Option that can be passed to the +// `jwk.Cache` object. +type CacheOption interface { + Option + cacheOption() +} + +type cacheOption struct { + Option +} + +func (*cacheOption) cacheOption() {} + +// FetchOption is a type of Option that can be passed to `jwk.Fetch()` +// FetchOption also implements the `CacheOption`, and thus can +// safely be passed to `(*jwk.Cache).Configure()` +type FetchOption interface { + Option + fetchOption() + parseOption() + registerOption() +} + +type fetchOption struct { + Option +} + +func (*fetchOption) fetchOption() {} + +func (*fetchOption) parseOption() {} + +func (*fetchOption) registerOption() {} + +// ParseOption is a type of Option that can be passed to `jwk.Parse()` +// ParseOption also implmentsthe `ReadFileOption` and `CacheOption`, +// and thus safely be passed to `jwk.ReadFile` and `(*jwk.Cache).Configure()` +type ParseOption interface { + Option + fetchOption() + registerOption() + readFileOption() +} + +type parseOption struct { + Option +} + +func (*parseOption) fetchOption() {} + +func (*parseOption) registerOption() {} + +func (*parseOption) readFileOption() {} + +// ReadFileOption is a type of `Option` that can be passed to `jwk.ReadFile` +type ReadFileOption interface { + Option + readFileOption() +} + +type readFileOption struct { + Option +} + +func (*readFileOption) readFileOption() {} + +// RegisterOption desribes options that can be passed to `(jwk.Cache).Register()` +type RegisterOption interface { + Option + registerOption() +} + +type registerOption struct { + Option +} + +func (*registerOption) registerOption() {} + +type identErrSink struct{} +type identFS struct{} +type identFetchWhitelist struct{} +type identHTTPClient struct{} +type identIgnoreParseError struct{} +type identLocalRegistry struct{} +type identMinRefreshInterval struct{} +type identPEM struct{} +type identPostFetcher struct{} +type identRefreshInterval struct{} +type identRefreshWindow struct{} +type identThumbprintHash struct{} + +func (identErrSink) String() string { + return "WithErrSink" +} + +func (identFS) String() string { + return "WithFS" +} + +func (identFetchWhitelist) String() string { + return "WithFetchWhitelist" +} + +func (identHTTPClient) String() string { + return "WithHTTPClient" +} + +func (identIgnoreParseError) String() string { + return "WithIgnoreParseError" +} + +func (identLocalRegistry) String() string { + return "withLocalRegistry" +} + +func (identMinRefreshInterval) String() string { + return "WithMinRefreshInterval" +} + +func (identPEM) String() string { + return "WithPEM" +} + +func (identPostFetcher) String() string { + return "WithPostFetcher" +} + +func (identRefreshInterval) String() string { + return "WithRefreshInterval" +} + +func (identRefreshWindow) String() string { + return "WithRefreshWindow" +} + +func (identThumbprintHash) String() string { + return "WithThumbprintHash" +} + +// WithErrSink specifies the `httprc.ErrSink` object that handles errors +// that occurred during the cache's execution. +// +// See the documentation in `httprc.WithErrSink` for more details. +func WithErrSink(v ErrSink) CacheOption { + return &cacheOption{option.New(identErrSink{}, v)} +} + +// WithFS specifies the source `fs.FS` object to read the file from. +func WithFS(v fs.FS) ReadFileOption { + return &readFileOption{option.New(identFS{}, v)} +} + +// WithFetchWhitelist specifies the Whitelist object to use when +// fetching JWKs from a remote source. This option can be passed +// to both `jwk.Fetch()`, `jwk.NewCache()`, and `(*jwk.Cache).Configure()` +func WithFetchWhitelist(v Whitelist) FetchOption { + return &fetchOption{option.New(identFetchWhitelist{}, v)} +} + +// WithHTTPClient allows users to specify the "net/http".Client object that +// is used when fetching jwk.Set objects. +func WithHTTPClient(v HTTPClient) FetchOption { + return &fetchOption{option.New(identHTTPClient{}, v)} +} + +// WithIgnoreParseError is only applicable when used with `jwk.Parse()` +// (i.e. to parse JWK sets). If passed to `jwk.ParseKey()`, the function +// will return an error no matter what the input is. +// +// DO NOT USE WITHOUT EXHAUSTING ALL OTHER ROUTES FIRST. +// +// The option specifies that errors found during parsing of individual +// keys are ignored. For example, if you had keys A, B, C where B is +// invalid (e.g. it does not contain the required fields), then the +// resulting JWKS will contain keys A and C only. +// +// This options exists as an escape hatch for those times when a +// key in a JWKS that is irrelevant for your use case is causing +// your JWKS parsing to fail, and you want to get to the rest of the +// keys in the JWKS. +// +// Again, DO NOT USE unless you have exhausted all other routes. +// When you use this option, you will not be able to tell if you are +// using a faulty JWKS, except for when there are JSON syntax errors. +func WithIgnoreParseError(v bool) ParseOption { + return &parseOption{option.New(identIgnoreParseError{}, v)} +} + +// This option is only available for internal code. Users don't get to play with it +func withLocalRegistry(v *json.Registry) ParseOption { + return &parseOption{option.New(identLocalRegistry{}, v)} +} + +// WithMinRefreshInterval specifies the minimum refresh interval to be used +// when using `jwk.Cache`. This value is ONLY used if you did not specify +// a user-supplied static refresh interval via `WithRefreshInterval`. +// +// This value is used as a fallback value when tokens are refreshed. +// +// When we fetch the key from a remote URL, we first look at the max-age +// directive from Cache-Control response header. If this value is present, +// we compare the max-age value and the value specified by this option +// and take the larger one. +// +// Next we check for the Expires header, and similarly if the header is +// present, we compare it against the value specified by this option, +// and take the larger one. +// +// Finally, if neither of the above headers are present, we use the +// value specified by this option as the next refresh timing +// +// If unspecified, the minimum refresh interval is 1 hour +func WithMinRefreshInterval(v time.Duration) RegisterOption { + return ®isterOption{option.New(identMinRefreshInterval{}, v)} +} + +// WithPEM specifies that the input to `Parse()` is a PEM encoded key. +func WithPEM(v bool) ParseOption { + return &parseOption{option.New(identPEM{}, v)} +} + +// WithPostFetcher specifies the PostFetcher object to be used on the +// jwk.Set object obtained in `jwk.Cache`. This option can be used +// to, for example, modify the jwk.Set to give it key IDs or algorithm +// names after it has been fetched and parsed, but before it is cached. +func WithPostFetcher(v PostFetcher) RegisterOption { + return ®isterOption{option.New(identPostFetcher{}, v)} +} + +// WithRefreshInterval specifies the static interval between refreshes +// of jwk.Set objects controlled by jwk.Cache. +// +// Providing this option overrides the adaptive token refreshing based +// on Cache-Control/Expires header (and jwk.WithMinRefreshInterval), +// and refreshes will *always* happen in this interval. +func WithRefreshInterval(v time.Duration) RegisterOption { + return ®isterOption{option.New(identRefreshInterval{}, v)} +} + +// WithRefreshWindow specifies the interval between checks for refreshes. +// +// See the documentation in `httprc.WithRefreshWindow` for more details. +func WithRefreshWindow(v time.Duration) CacheOption { + return &cacheOption{option.New(identRefreshWindow{}, v)} +} + +func WithThumbprintHash(v crypto.Hash) AssignKeyIDOption { + return &assignKeyIDOption{option.New(identThumbprintHash{}, v)} +} diff --git a/vendor/github.com/lestrrat-go/jwx/jwk/rsa.go b/vendor/github.com/lestrrat-go/jwx/v2/jwk/rsa.go similarity index 86% rename from vendor/github.com/lestrrat-go/jwx/jwk/rsa.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwk/rsa.go index 1a31caa..5de6b63 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwk/rsa.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwk/rsa.go @@ -8,9 +8,8 @@ import ( "math/big" "github.com/lestrrat-go/blackmagic" - "github.com/lestrrat-go/jwx/internal/base64" - "github.com/lestrrat-go/jwx/internal/pool" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/internal/base64" + "github.com/lestrrat-go/jwx/v2/internal/pool" ) func (k *rsaPrivateKey) FromRaw(rawKey *rsa.PrivateKey) error { @@ -19,7 +18,7 @@ func (k *rsaPrivateKey) FromRaw(rawKey *rsa.PrivateKey) error { d, err := bigIntToBytes(rawKey.D) if err != nil { - return errors.Wrap(err, `invalid rsa.PrivateKey`) + return fmt.Errorf(`invalid rsa.PrivateKey: %w`, err) } k.d = d @@ -59,7 +58,7 @@ func (k *rsaPrivateKey) FromRaw(rawKey *rsa.PrivateKey) error { // public key part n, e, err := rsaPublicKeyByteValuesFromRaw(&rawKey.PublicKey) if err != nil { - return errors.Wrap(err, `invalid rsa.PrivateKey`) + return fmt.Errorf(`invalid rsa.PrivateKey: %w`, err) } k.n = n k.e = e @@ -70,7 +69,7 @@ func (k *rsaPrivateKey) FromRaw(rawKey *rsa.PrivateKey) error { func rsaPublicKeyByteValuesFromRaw(rawKey *rsa.PublicKey) ([]byte, []byte, error) { n, err := bigIntToBytes(rawKey.N) if err != nil { - return nil, nil, errors.Wrap(err, `invalid rsa.PublicKey`) + return nil, nil, fmt.Errorf(`invalid rsa.PublicKey: %w`, err) } data := make([]byte, 8) @@ -90,7 +89,7 @@ func (k *rsaPublicKey) FromRaw(rawKey *rsa.PublicKey) error { n, e, err := rsaPublicKeyByteValuesFromRaw(rawKey) if err != nil { - return errors.Wrap(err, `invalid rsa.PrivateKey`) + return fmt.Errorf(`invalid rsa.PrivateKey: %w`, err) } k.n = n k.e = e @@ -131,7 +130,7 @@ func (k *rsaPrivateKey) Raw(v interface{}) error { pubk.n = k.n pubk.e = k.e if err := pubk.Raw(&key.PublicKey); err != nil { - return errors.Wrap(err, `failed to materialize RSA public key`) + return fmt.Errorf(`failed to materialize RSA public key: %w`, err) } key.D = &d @@ -175,7 +174,7 @@ func (k *rsaPublicKey) Raw(v interface{}) error { func makeRSAPublicKey(v interface { makePairs() []*HeaderPair }) (Key, error) { - newKey := NewRSAPublicKey() + newKey := newRSAPublicKey() // Iterate and copy everything except for the bits that should not be in the public key for _, pair := range v.makePairs() { @@ -186,7 +185,7 @@ func makeRSAPublicKey(v interface { //nolint:forcetypeassert key := pair.Key.(string) if err := newKey.Set(key, pair.Value); err != nil { - return nil, errors.Wrapf(err, `failed to set field %q`, key) + return nil, fmt.Errorf(`failed to set field %q: %w`, key, err) } } } @@ -210,7 +209,7 @@ func (k rsaPrivateKey) Thumbprint(hash crypto.Hash) ([]byte, error) { var key rsa.PrivateKey if err := k.Raw(&key); err != nil { - return nil, errors.Wrap(err, `failed to materialize RSA private key`) + return nil, fmt.Errorf(`failed to materialize RSA private key: %w`, err) } return rsaThumbprint(hash, &key.PublicKey) } @@ -221,7 +220,7 @@ func (k rsaPublicKey) Thumbprint(hash crypto.Hash) ([]byte, error) { var key rsa.PublicKey if err := k.Raw(&key); err != nil { - return nil, errors.Wrap(err, `failed to materialize RSA public key`) + return nil, fmt.Errorf(`failed to materialize RSA public key: %w`, err) } return rsaThumbprint(hash, &key) } @@ -238,7 +237,7 @@ func rsaThumbprint(hash crypto.Hash, key *rsa.PublicKey) ([]byte, error) { h := hash.New() if _, err := buf.WriteTo(h); err != nil { - return nil, errors.Wrap(err, "failed to write rsaThumbprint") + return nil, fmt.Errorf(`failed to write rsaThumbprint: %w`, err) } return h.Sum(nil), nil } diff --git a/vendor/github.com/lestrrat-go/jwx/jwk/rsa_gen.go b/vendor/github.com/lestrrat-go/jwx/v2/jwk/rsa_gen.go similarity index 75% rename from vendor/github.com/lestrrat-go/jwx/jwk/rsa_gen.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwk/rsa_gen.go index 383cb34..ce4e400 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwk/rsa_gen.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwk/rsa_gen.go @@ -6,18 +6,17 @@ import ( "bytes" "context" "crypto/rsa" - "crypto/x509" "fmt" "sort" "sync" "github.com/lestrrat-go/iter/mapiter" - "github.com/lestrrat-go/jwx/internal/base64" - "github.com/lestrrat-go/jwx/internal/iter" - "github.com/lestrrat-go/jwx/internal/json" - "github.com/lestrrat-go/jwx/internal/pool" - "github.com/lestrrat-go/jwx/jwa" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/cert" + "github.com/lestrrat-go/jwx/v2/internal/base64" + "github.com/lestrrat-go/jwx/v2/internal/iter" + "github.com/lestrrat-go/jwx/v2/internal/json" + "github.com/lestrrat-go/jwx/v2/internal/pool" + "github.com/lestrrat-go/jwx/v2/jwa" ) const ( @@ -39,24 +38,23 @@ type RSAPublicKey interface { } type rsaPublicKey struct { - algorithm *string // https://tools.ietf.org/html/rfc7517#section-4.4 + algorithm *jwa.KeyAlgorithm // https://tools.ietf.org/html/rfc7517#section-4.4 e []byte keyID *string // https://tools.ietf.org/html/rfc7515#section-4.1.4 keyOps *KeyOperationList // https://tools.ietf.org/html/rfc7517#section-4.3 keyUsage *string // https://tools.ietf.org/html/rfc7517#section-4.2 n []byte - x509CertChain *CertificateChain // https://tools.ietf.org/html/rfc7515#section-4.1.6 - x509CertThumbprint *string // https://tools.ietf.org/html/rfc7515#section-4.1.7 - x509CertThumbprintS256 *string // https://tools.ietf.org/html/rfc7515#section-4.1.8 - x509URL *string // https://tools.ietf.org/html/rfc7515#section-4.1.5 + x509CertChain *cert.Chain // https://tools.ietf.org/html/rfc7515#section-4.1.6 + x509CertThumbprint *string // https://tools.ietf.org/html/rfc7515#section-4.1.7 + x509CertThumbprintS256 *string // https://tools.ietf.org/html/rfc7515#section-4.1.8 + x509URL *string // https://tools.ietf.org/html/rfc7515#section-4.1.5 privateParams map[string]interface{} mu *sync.RWMutex dc json.DecodeCtx } -func NewRSAPublicKey() RSAPublicKey { - return newRSAPublicKey() -} +var _ RSAPublicKey = &rsaPublicKey{} +var _ Key = &rsaPublicKey{} func newRSAPublicKey() *rsaPublicKey { return &rsaPublicKey{ @@ -69,11 +67,11 @@ func (h rsaPublicKey) KeyType() jwa.KeyType { return jwa.RSA } -func (h *rsaPublicKey) Algorithm() string { +func (h *rsaPublicKey) Algorithm() jwa.KeyAlgorithm { if h.algorithm != nil { return *(h.algorithm) } - return "" + return jwa.InvalidKeyAlgorithm("") } func (h *rsaPublicKey) E() []byte { @@ -105,11 +103,8 @@ func (h *rsaPublicKey) N() []byte { return h.n } -func (h *rsaPublicKey) X509CertChain() []*x509.Certificate { - if h.x509CertChain != nil { - return h.x509CertChain.Get() - } - return nil +func (h *rsaPublicKey) X509CertChain() *cert.Chain { + return h.x509CertChain } func (h *rsaPublicKey) X509CertThumbprint() string { @@ -158,7 +153,7 @@ func (h *rsaPublicKey) makePairs() []*HeaderPair { pairs = append(pairs, &HeaderPair{Key: RSANKey, Value: h.n}) } if h.x509CertChain != nil { - pairs = append(pairs, &HeaderPair{Key: X509CertChainKey, Value: *(h.x509CertChain)}) + pairs = append(pairs, &HeaderPair{Key: X509CertChainKey, Value: h.x509CertChain}) } if h.x509CertThumbprint != nil { pairs = append(pairs, &HeaderPair{Key: X509CertThumbprintKey, Value: *(h.x509CertThumbprint)}) @@ -219,7 +214,7 @@ func (h *rsaPublicKey) Get(name string) (interface{}, bool) { if h.x509CertChain == nil { return nil, false } - return h.x509CertChain.Get(), true + return h.x509CertChain, true case X509CertThumbprintKey: if h.x509CertThumbprint == nil { return nil, false @@ -253,13 +248,15 @@ func (h *rsaPublicKey) setNoLock(name string, value interface{}) error { return nil case AlgorithmKey: switch v := value.(type) { - case string: - h.algorithm = &v + case string, jwa.SignatureAlgorithm, jwa.ContentEncryptionAlgorithm: + var tmp = jwa.KeyAlgorithmFrom(v) + h.algorithm = &tmp case fmt.Stringer: - tmp := v.String() + s := v.String() + var tmp = jwa.KeyAlgorithmFrom(s) h.algorithm = &tmp default: - return errors.Errorf(`invalid type for %s key: %T`, AlgorithmKey, value) + return fmt.Errorf(`invalid type for %s key: %T`, AlgorithmKey, value) } return nil case RSAEKey: @@ -267,17 +264,17 @@ func (h *rsaPublicKey) setNoLock(name string, value interface{}) error { h.e = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, RSAEKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, RSAEKey, value) case KeyIDKey: if v, ok := value.(string); ok { h.keyID = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, KeyIDKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, KeyIDKey, value) case KeyOpsKey: var acceptor KeyOperationList if err := acceptor.Accept(value); err != nil { - return errors.Wrapf(err, `invalid value for %s key`, KeyOpsKey) + return fmt.Errorf(`invalid value for %s key: %w`, KeyOpsKey, err) } h.keyOps = &acceptor return nil @@ -289,44 +286,43 @@ func (h *rsaPublicKey) setNoLock(name string, value interface{}) error { tmp := v.String() h.keyUsage = &tmp default: - return errors.Errorf(`invalid key usage type %s`, v) + return fmt.Errorf(`invalid key usage type %s`, v) } case string: h.keyUsage = &v default: - return errors.Errorf(`invalid key usage type %s`, v) + return fmt.Errorf(`invalid key usage type %s`, v) } case RSANKey: if v, ok := value.([]byte); ok { h.n = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, RSANKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, RSANKey, value) case X509CertChainKey: - var acceptor CertificateChain - if err := acceptor.Accept(value); err != nil { - return errors.Wrapf(err, `invalid value for %s key`, X509CertChainKey) + if v, ok := value.(*cert.Chain); ok { + h.x509CertChain = v + return nil } - h.x509CertChain = &acceptor - return nil + return fmt.Errorf(`invalid value for %s key: %T`, X509CertChainKey, value) case X509CertThumbprintKey: if v, ok := value.(string); ok { h.x509CertThumbprint = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509CertThumbprintKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509CertThumbprintKey, value) case X509CertThumbprintS256Key: if v, ok := value.(string); ok { h.x509CertThumbprintS256 = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509CertThumbprintS256Key, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509CertThumbprintS256Key, value) case X509URLKey: if v, ok := value.(string); ok { h.x509URL = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509URLKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509URLKey, value) default: if h.privateParams == nil { h.privateParams = map[string]interface{}{} @@ -383,6 +379,8 @@ func (k *rsaPublicKey) SetDecodeCtx(dc json.DecodeCtx) { } func (h *rsaPublicKey) UnmarshalJSON(buf []byte) error { + h.mu.Lock() + defer h.mu.Unlock() h.algorithm = nil h.e = nil h.keyID = nil @@ -398,7 +396,7 @@ LOOP: for { tok, err := dec.Token() if err != nil { - return errors.Wrap(err, `error reading token`) + return fmt.Errorf(`error reading token: %w`, err) } switch tok := tok.(type) { case json.Delim: @@ -407,61 +405,64 @@ LOOP: if tok == '}' { // End of object break LOOP } else if tok != '{' { - return errors.Errorf(`expected '{', but got '%c'`, tok) + return fmt.Errorf(`expected '{', but got '%c'`, tok) } case string: // Objects can only have string keys switch tok { case KeyTypeKey: val, err := json.ReadNextStringToken(dec) if err != nil { - return errors.Wrap(err, `error reading token`) + return fmt.Errorf(`error reading token: %w`, err) } if val != jwa.RSA.String() { - return errors.Errorf(`invalid kty value for RSAPublicKey (%s)`, val) + return fmt.Errorf(`invalid kty value for RSAPublicKey (%s)`, val) } case AlgorithmKey: - if err := json.AssignNextStringToken(&h.algorithm, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, AlgorithmKey) + var s string + if err := dec.Decode(&s); err != nil { + return fmt.Errorf(`failed to decode value for key %s: %w`, AlgorithmKey, err) } + alg := jwa.KeyAlgorithmFrom(s) + h.algorithm = &alg case RSAEKey: if err := json.AssignNextBytesToken(&h.e, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, RSAEKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, RSAEKey, err) } case KeyIDKey: if err := json.AssignNextStringToken(&h.keyID, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, KeyIDKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, KeyIDKey, err) } case KeyOpsKey: var decoded KeyOperationList if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, KeyOpsKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, KeyOpsKey, err) } h.keyOps = &decoded case KeyUsageKey: if err := json.AssignNextStringToken(&h.keyUsage, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, KeyUsageKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, KeyUsageKey, err) } case RSANKey: if err := json.AssignNextBytesToken(&h.n, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, RSANKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, RSANKey, err) } case X509CertChainKey: - var decoded CertificateChain + var decoded cert.Chain if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertChainKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertChainKey, err) } h.x509CertChain = &decoded case X509CertThumbprintKey: if err := json.AssignNextStringToken(&h.x509CertThumbprint, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertThumbprintKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertThumbprintKey, err) } case X509CertThumbprintS256Key: if err := json.AssignNextStringToken(&h.x509CertThumbprintS256, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertThumbprintS256Key) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertThumbprintS256Key, err) } case X509URLKey: if err := json.AssignNextStringToken(&h.x509URL, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509URLKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509URLKey, err) } default: if dc := h.dc; dc != nil { @@ -478,17 +479,17 @@ LOOP: h.setNoLock(tok, decoded) continue } - return errors.Wrapf(err, `could not decode field %s`, tok) + return fmt.Errorf(`could not decode field %s: %w`, tok, err) } default: - return errors.Errorf(`invalid token %T`, tok) + return fmt.Errorf(`invalid token %T`, tok) } } if h.e == nil { - return errors.Errorf(`required field e is missing`) + return fmt.Errorf(`required field e is missing`) } if h.n == nil { - return errors.Errorf(`required field n is missing`) + return fmt.Errorf(`required field n is missing`) } return nil } @@ -521,7 +522,7 @@ func (h rsaPublicKey) MarshalJSON() ([]byte, error) { buf.WriteRune('"') default: if err := enc.Encode(v); err != nil { - return nil, errors.Wrapf(err, `failed to encode value for field %s`, f) + return nil, fmt.Errorf(`failed to encode value for field %s: %w`, f, err) } buf.Truncate(buf.Len() - 1) } @@ -570,7 +571,7 @@ type RSAPrivateKey interface { } type rsaPrivateKey struct { - algorithm *string // https://tools.ietf.org/html/rfc7517#section-4.4 + algorithm *jwa.KeyAlgorithm // https://tools.ietf.org/html/rfc7517#section-4.4 d []byte dp []byte dq []byte @@ -582,18 +583,17 @@ type rsaPrivateKey struct { p []byte q []byte qi []byte - x509CertChain *CertificateChain // https://tools.ietf.org/html/rfc7515#section-4.1.6 - x509CertThumbprint *string // https://tools.ietf.org/html/rfc7515#section-4.1.7 - x509CertThumbprintS256 *string // https://tools.ietf.org/html/rfc7515#section-4.1.8 - x509URL *string // https://tools.ietf.org/html/rfc7515#section-4.1.5 + x509CertChain *cert.Chain // https://tools.ietf.org/html/rfc7515#section-4.1.6 + x509CertThumbprint *string // https://tools.ietf.org/html/rfc7515#section-4.1.7 + x509CertThumbprintS256 *string // https://tools.ietf.org/html/rfc7515#section-4.1.8 + x509URL *string // https://tools.ietf.org/html/rfc7515#section-4.1.5 privateParams map[string]interface{} mu *sync.RWMutex dc json.DecodeCtx } -func NewRSAPrivateKey() RSAPrivateKey { - return newRSAPrivateKey() -} +var _ RSAPrivateKey = &rsaPrivateKey{} +var _ Key = &rsaPrivateKey{} func newRSAPrivateKey() *rsaPrivateKey { return &rsaPrivateKey{ @@ -606,11 +606,11 @@ func (h rsaPrivateKey) KeyType() jwa.KeyType { return jwa.RSA } -func (h *rsaPrivateKey) Algorithm() string { +func (h *rsaPrivateKey) Algorithm() jwa.KeyAlgorithm { if h.algorithm != nil { return *(h.algorithm) } - return "" + return jwa.InvalidKeyAlgorithm("") } func (h *rsaPrivateKey) D() []byte { @@ -666,11 +666,8 @@ func (h *rsaPrivateKey) QI() []byte { return h.qi } -func (h *rsaPrivateKey) X509CertChain() []*x509.Certificate { - if h.x509CertChain != nil { - return h.x509CertChain.Get() - } - return nil +func (h *rsaPrivateKey) X509CertChain() *cert.Chain { + return h.x509CertChain } func (h *rsaPrivateKey) X509CertThumbprint() string { @@ -737,7 +734,7 @@ func (h *rsaPrivateKey) makePairs() []*HeaderPair { pairs = append(pairs, &HeaderPair{Key: RSAQIKey, Value: h.qi}) } if h.x509CertChain != nil { - pairs = append(pairs, &HeaderPair{Key: X509CertChainKey, Value: *(h.x509CertChain)}) + pairs = append(pairs, &HeaderPair{Key: X509CertChainKey, Value: h.x509CertChain}) } if h.x509CertThumbprint != nil { pairs = append(pairs, &HeaderPair{Key: X509CertThumbprintKey, Value: *(h.x509CertThumbprint)}) @@ -828,7 +825,7 @@ func (h *rsaPrivateKey) Get(name string) (interface{}, bool) { if h.x509CertChain == nil { return nil, false } - return h.x509CertChain.Get(), true + return h.x509CertChain, true case X509CertThumbprintKey: if h.x509CertThumbprint == nil { return nil, false @@ -862,13 +859,15 @@ func (h *rsaPrivateKey) setNoLock(name string, value interface{}) error { return nil case AlgorithmKey: switch v := value.(type) { - case string: - h.algorithm = &v + case string, jwa.SignatureAlgorithm, jwa.ContentEncryptionAlgorithm: + var tmp = jwa.KeyAlgorithmFrom(v) + h.algorithm = &tmp case fmt.Stringer: - tmp := v.String() + s := v.String() + var tmp = jwa.KeyAlgorithmFrom(s) h.algorithm = &tmp default: - return errors.Errorf(`invalid type for %s key: %T`, AlgorithmKey, value) + return fmt.Errorf(`invalid type for %s key: %T`, AlgorithmKey, value) } return nil case RSADKey: @@ -876,35 +875,35 @@ func (h *rsaPrivateKey) setNoLock(name string, value interface{}) error { h.d = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, RSADKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, RSADKey, value) case RSADPKey: if v, ok := value.([]byte); ok { h.dp = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, RSADPKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, RSADPKey, value) case RSADQKey: if v, ok := value.([]byte); ok { h.dq = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, RSADQKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, RSADQKey, value) case RSAEKey: if v, ok := value.([]byte); ok { h.e = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, RSAEKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, RSAEKey, value) case KeyIDKey: if v, ok := value.(string); ok { h.keyID = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, KeyIDKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, KeyIDKey, value) case KeyOpsKey: var acceptor KeyOperationList if err := acceptor.Accept(value); err != nil { - return errors.Wrapf(err, `invalid value for %s key`, KeyOpsKey) + return fmt.Errorf(`invalid value for %s key: %w`, KeyOpsKey, err) } h.keyOps = &acceptor return nil @@ -916,62 +915,61 @@ func (h *rsaPrivateKey) setNoLock(name string, value interface{}) error { tmp := v.String() h.keyUsage = &tmp default: - return errors.Errorf(`invalid key usage type %s`, v) + return fmt.Errorf(`invalid key usage type %s`, v) } case string: h.keyUsage = &v default: - return errors.Errorf(`invalid key usage type %s`, v) + return fmt.Errorf(`invalid key usage type %s`, v) } case RSANKey: if v, ok := value.([]byte); ok { h.n = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, RSANKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, RSANKey, value) case RSAPKey: if v, ok := value.([]byte); ok { h.p = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, RSAPKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, RSAPKey, value) case RSAQKey: if v, ok := value.([]byte); ok { h.q = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, RSAQKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, RSAQKey, value) case RSAQIKey: if v, ok := value.([]byte); ok { h.qi = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, RSAQIKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, RSAQIKey, value) case X509CertChainKey: - var acceptor CertificateChain - if err := acceptor.Accept(value); err != nil { - return errors.Wrapf(err, `invalid value for %s key`, X509CertChainKey) + if v, ok := value.(*cert.Chain); ok { + h.x509CertChain = v + return nil } - h.x509CertChain = &acceptor - return nil + return fmt.Errorf(`invalid value for %s key: %T`, X509CertChainKey, value) case X509CertThumbprintKey: if v, ok := value.(string); ok { h.x509CertThumbprint = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509CertThumbprintKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509CertThumbprintKey, value) case X509CertThumbprintS256Key: if v, ok := value.(string); ok { h.x509CertThumbprintS256 = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509CertThumbprintS256Key, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509CertThumbprintS256Key, value) case X509URLKey: if v, ok := value.(string); ok { h.x509URL = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509URLKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509URLKey, value) default: if h.privateParams == nil { h.privateParams = map[string]interface{}{} @@ -1040,6 +1038,8 @@ func (k *rsaPrivateKey) SetDecodeCtx(dc json.DecodeCtx) { } func (h *rsaPrivateKey) UnmarshalJSON(buf []byte) error { + h.mu.Lock() + defer h.mu.Unlock() h.algorithm = nil h.d = nil h.dp = nil @@ -1061,7 +1061,7 @@ LOOP: for { tok, err := dec.Token() if err != nil { - return errors.Wrap(err, `error reading token`) + return fmt.Errorf(`error reading token: %w`, err) } switch tok := tok.(type) { case json.Delim: @@ -1070,85 +1070,88 @@ LOOP: if tok == '}' { // End of object break LOOP } else if tok != '{' { - return errors.Errorf(`expected '{', but got '%c'`, tok) + return fmt.Errorf(`expected '{', but got '%c'`, tok) } case string: // Objects can only have string keys switch tok { case KeyTypeKey: val, err := json.ReadNextStringToken(dec) if err != nil { - return errors.Wrap(err, `error reading token`) + return fmt.Errorf(`error reading token: %w`, err) } if val != jwa.RSA.String() { - return errors.Errorf(`invalid kty value for RSAPublicKey (%s)`, val) + return fmt.Errorf(`invalid kty value for RSAPublicKey (%s)`, val) } case AlgorithmKey: - if err := json.AssignNextStringToken(&h.algorithm, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, AlgorithmKey) + var s string + if err := dec.Decode(&s); err != nil { + return fmt.Errorf(`failed to decode value for key %s: %w`, AlgorithmKey, err) } + alg := jwa.KeyAlgorithmFrom(s) + h.algorithm = &alg case RSADKey: if err := json.AssignNextBytesToken(&h.d, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, RSADKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, RSADKey, err) } case RSADPKey: if err := json.AssignNextBytesToken(&h.dp, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, RSADPKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, RSADPKey, err) } case RSADQKey: if err := json.AssignNextBytesToken(&h.dq, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, RSADQKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, RSADQKey, err) } case RSAEKey: if err := json.AssignNextBytesToken(&h.e, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, RSAEKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, RSAEKey, err) } case KeyIDKey: if err := json.AssignNextStringToken(&h.keyID, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, KeyIDKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, KeyIDKey, err) } case KeyOpsKey: var decoded KeyOperationList if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, KeyOpsKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, KeyOpsKey, err) } h.keyOps = &decoded case KeyUsageKey: if err := json.AssignNextStringToken(&h.keyUsage, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, KeyUsageKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, KeyUsageKey, err) } case RSANKey: if err := json.AssignNextBytesToken(&h.n, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, RSANKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, RSANKey, err) } case RSAPKey: if err := json.AssignNextBytesToken(&h.p, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, RSAPKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, RSAPKey, err) } case RSAQKey: if err := json.AssignNextBytesToken(&h.q, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, RSAQKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, RSAQKey, err) } case RSAQIKey: if err := json.AssignNextBytesToken(&h.qi, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, RSAQIKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, RSAQIKey, err) } case X509CertChainKey: - var decoded CertificateChain + var decoded cert.Chain if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertChainKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertChainKey, err) } h.x509CertChain = &decoded case X509CertThumbprintKey: if err := json.AssignNextStringToken(&h.x509CertThumbprint, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertThumbprintKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertThumbprintKey, err) } case X509CertThumbprintS256Key: if err := json.AssignNextStringToken(&h.x509CertThumbprintS256, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertThumbprintS256Key) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertThumbprintS256Key, err) } case X509URLKey: if err := json.AssignNextStringToken(&h.x509URL, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509URLKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509URLKey, err) } default: if dc := h.dc; dc != nil { @@ -1165,20 +1168,20 @@ LOOP: h.setNoLock(tok, decoded) continue } - return errors.Wrapf(err, `could not decode field %s`, tok) + return fmt.Errorf(`could not decode field %s: %w`, tok, err) } default: - return errors.Errorf(`invalid token %T`, tok) + return fmt.Errorf(`invalid token %T`, tok) } } if h.d == nil { - return errors.Errorf(`required field d is missing`) + return fmt.Errorf(`required field d is missing`) } if h.e == nil { - return errors.Errorf(`required field e is missing`) + return fmt.Errorf(`required field e is missing`) } if h.n == nil { - return errors.Errorf(`required field n is missing`) + return fmt.Errorf(`required field n is missing`) } return nil } @@ -1211,7 +1214,7 @@ func (h rsaPrivateKey) MarshalJSON() ([]byte, error) { buf.WriteRune('"') default: if err := enc.Encode(v); err != nil { - return nil, errors.Wrapf(err, `failed to encode value for field %s`, f) + return nil, fmt.Errorf(`failed to encode value for field %s: %w`, f, err) } buf.Truncate(buf.Len() - 1) } diff --git a/vendor/github.com/lestrrat-go/jwx/jwk/set.go b/vendor/github.com/lestrrat-go/jwx/v2/jwk/set.go similarity index 69% rename from vendor/github.com/lestrrat-go/jwx/jwk/set.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwk/set.go index d5e844a..6a26156 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwk/set.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwk/set.go @@ -7,9 +7,9 @@ import ( "sort" "github.com/lestrrat-go/iter/arrayiter" - "github.com/lestrrat-go/jwx/internal/json" - "github.com/lestrrat-go/jwx/internal/pool" - "github.com/pkg/errors" + "github.com/lestrrat-go/iter/mapiter" + "github.com/lestrrat-go/jwx/v2/internal/json" + "github.com/lestrrat-go/jwx/v2/internal/pool" ) const keysKey = `keys` // appease linter @@ -28,7 +28,7 @@ func (s *set) Set(n string, v interface{}) error { if n == keysKey { vl, ok := v.([]Key) if !ok { - return errors.Errorf(`value for field "keys" must be []jwk.Key`) + return fmt.Errorf(`value for field "keys" must be []jwk.Key`) } s.keys = vl return nil @@ -38,7 +38,7 @@ func (s *set) Set(n string, v interface{}) error { return nil } -func (s *set) Field(n string) (interface{}, bool) { +func (s *set) Get(n string) (interface{}, bool) { s.mu.RLock() defer s.mu.RUnlock() @@ -46,7 +46,7 @@ func (s *set) Field(n string) (interface{}, bool) { return v, ok } -func (s *set) Get(idx int) (Key, bool) { +func (s *set) Key(idx int) (Key, bool) { s.mu.RLock() defer s.mu.RUnlock() @@ -80,18 +80,26 @@ func (s *set) Index(key Key) int { return s.indexNL(key) } -func (s *set) Add(key Key) bool { +func (s *set) AddKey(key Key) error { s.mu.Lock() defer s.mu.Unlock() if i := s.indexNL(key); i > -1 { - return false + return fmt.Errorf(`(jwk.Set).AddKey: key already exists`) } s.keys = append(s.keys, key) - return true + return nil } -func (s *set) Remove(key Key) bool { +func (s *set) Remove(name string) error { + s.mu.Lock() + defer s.mu.Unlock() + + delete(s.privateParams, name) + return nil +} + +func (s *set) RemoveKey(key Key) error { s.mu.Lock() defer s.mu.Unlock() @@ -105,20 +113,22 @@ func (s *set) Remove(key Key) bool { default: s.keys = append(s.keys[:i], s.keys[i+1:]...) } - return true + return nil } } - return false + return fmt.Errorf(`(jwk.Set).RemoveKey: specified key does not exist in set`) } -func (s *set) Clear() { +func (s *set) Clear() error { s.mu.Lock() defer s.mu.Unlock() s.keys = nil + s.privateParams = make(map[string]interface{}) + return nil } -func (s *set) Iterate(ctx context.Context) KeyIterator { +func (s *set) Keys(ctx context.Context) KeyIterator { ch := make(chan *KeyPair, s.Len()) go iterate(ctx, s.keys, ch) return arrayiter.New(ch) @@ -159,7 +169,7 @@ func (s *set) MarshalJSON() ([]byte, error) { fmt.Fprintf(buf, `%q:`, field) if field != keysKey { if err := enc.Encode(s.privateParams[field]); err != nil { - return nil, errors.Wrapf(err, `failed to marshal field %q`, field) + return nil, fmt.Errorf(`failed to marshal field %q: %w`, field, err) } } else { buf.WriteByte('[') @@ -168,7 +178,7 @@ func (s *set) MarshalJSON() ([]byte, error) { buf.WriteByte(',') } if err := enc.Encode(k); err != nil { - return nil, errors.Wrapf(err, `failed to marshal key #%d`, i) + return nil, fmt.Errorf(`failed to marshal key #%d: %w`, i, err) } } buf.WriteByte(']') @@ -203,7 +213,7 @@ LOOP: for { tok, err := dec.Token() if err != nil { - return errors.Wrap(err, `error reading token`) + return fmt.Errorf(`error reading token: %w`, err) } switch tok := tok.(type) { @@ -213,7 +223,7 @@ LOOP: if tok == '}' { // End of object break LOOP } else if tok != '{' { - return errors.Errorf(`expected '{', but got '%c'`, tok) + return fmt.Errorf(`expected '{', but got '%c'`, tok) } case string: switch tok { @@ -221,14 +231,14 @@ LOOP: sawKeysField = true var list []json.RawMessage if err := dec.Decode(&list); err != nil { - return errors.Wrap(err, `failed to decode "keys"`) + return fmt.Errorf(`failed to decode "keys": %w`, err) } for i, keysrc := range list { key, err := ParseKey(keysrc, options...) if err != nil { if !ignoreParseError { - return errors.Wrapf(err, `failed to decode key #%d in "keys"`, i) + return fmt.Errorf(`failed to decode key #%d in "keys": %w`, i, err) } continue } @@ -237,7 +247,7 @@ LOOP: default: var v interface{} if err := dec.Decode(&v); err != nil { - return errors.Wrapf(err, `failed to decode value for key %q`, tok) + return fmt.Errorf(`failed to decode value for key %q: %w`, tok, err) } s.privateParams[tok] = v } @@ -252,7 +262,7 @@ LOOP: if !sawKeysField { key, err := ParseKey(data, options...) if err != nil { - return errors.Wrapf(err, `failed to parse sole key in key set`) + return fmt.Errorf(`failed to parse sole key in key set`) } s.keys = append(s.keys, key) } @@ -265,7 +275,7 @@ func (s *set) LookupKeyID(kid string) (Key, bool) { n := s.Len() for i := 0; i < n; i++ { - key, ok := s.Get(i) + key, ok := s.Key(i) if !ok { return nil, false } @@ -301,3 +311,31 @@ func (s *set) Clone() (Set, error) { } return s2, nil } + +func (s *set) makePairs() []*HeaderPair { + pairs := make([]*HeaderPair, 0, len(s.privateParams)) + for k, v := range s.privateParams { + pairs = append(pairs, &HeaderPair{Key: k, Value: v}) + } + sort.Slice(pairs, func(i, j int) bool { + //nolint:forcetypeassert + return pairs[i].Key.(string) < pairs[j].Key.(string) + }) + return pairs +} + +func (s *set) Iterate(ctx context.Context) HeaderIterator { + pairs := s.makePairs() + ch := make(chan *HeaderPair, len(pairs)) + go func(ctx context.Context, ch chan *HeaderPair, pairs []*HeaderPair) { + defer close(ch) + for _, pair := range pairs { + select { + case <-ctx.Done(): + return + case ch <- pair: + } + } + }(ctx, ch, pairs) + return mapiter.New(ch) +} diff --git a/vendor/github.com/lestrrat-go/jwx/jwk/symmetric.go b/vendor/github.com/lestrrat-go/jwx/v2/jwk/symmetric.go similarity index 79% rename from vendor/github.com/lestrrat-go/jwx/jwk/symmetric.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwk/symmetric.go index 8511cff..d2498e3 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwk/symmetric.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwk/symmetric.go @@ -5,8 +5,7 @@ import ( "fmt" "github.com/lestrrat-go/blackmagic" - "github.com/lestrrat-go/jwx/internal/base64" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/internal/base64" ) func (k *symmetricKey) FromRaw(rawKey []byte) error { @@ -14,7 +13,7 @@ func (k *symmetricKey) FromRaw(rawKey []byte) error { defer k.mu.Unlock() if len(rawKey) == 0 { - return errors.New(`non-empty []byte key required`) + return fmt.Errorf(`non-empty []byte key required`) } k.octets = rawKey @@ -37,7 +36,7 @@ func (k *symmetricKey) Thumbprint(hash crypto.Hash) ([]byte, error) { defer k.mu.RUnlock() var octets []byte if err := k.Raw(&octets); err != nil { - return nil, errors.Wrap(err, `failed to materialize symmetric key`) + return nil, fmt.Errorf(`failed to materialize symmetric key: %w`, err) } h := hash.New() @@ -48,13 +47,13 @@ func (k *symmetricKey) Thumbprint(hash crypto.Hash) ([]byte, error) { } func (k *symmetricKey) PublicKey() (Key, error) { - newKey := NewSymmetricKey() + newKey := newSymmetricKey() for _, pair := range k.makePairs() { //nolint:forcetypeassert key := pair.Key.(string) if err := newKey.Set(key, pair.Value); err != nil { - return nil, errors.Wrapf(err, `failed to set field %q`, key) + return nil, fmt.Errorf(`failed to set field %q: %w`, key, err) } } return newKey, nil diff --git a/vendor/github.com/lestrrat-go/jwx/jwk/symmetric_gen.go b/vendor/github.com/lestrrat-go/jwx/v2/jwk/symmetric_gen.go similarity index 75% rename from vendor/github.com/lestrrat-go/jwx/jwk/symmetric_gen.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwk/symmetric_gen.go index 60b0fd9..6a96519 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwk/symmetric_gen.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwk/symmetric_gen.go @@ -5,18 +5,17 @@ package jwk import ( "bytes" "context" - "crypto/x509" "fmt" "sort" "sync" "github.com/lestrrat-go/iter/mapiter" - "github.com/lestrrat-go/jwx/internal/base64" - "github.com/lestrrat-go/jwx/internal/iter" - "github.com/lestrrat-go/jwx/internal/json" - "github.com/lestrrat-go/jwx/internal/pool" - "github.com/lestrrat-go/jwx/jwa" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/cert" + "github.com/lestrrat-go/jwx/v2/internal/base64" + "github.com/lestrrat-go/jwx/v2/internal/iter" + "github.com/lestrrat-go/jwx/v2/internal/json" + "github.com/lestrrat-go/jwx/v2/internal/pool" + "github.com/lestrrat-go/jwx/v2/jwa" ) const ( @@ -30,23 +29,22 @@ type SymmetricKey interface { } type symmetricKey struct { - algorithm *string // https://tools.ietf.org/html/rfc7517#section-4.4 + algorithm *jwa.KeyAlgorithm // https://tools.ietf.org/html/rfc7517#section-4.4 keyID *string // https://tools.ietf.org/html/rfc7515#section-4.1.4 keyOps *KeyOperationList // https://tools.ietf.org/html/rfc7517#section-4.3 keyUsage *string // https://tools.ietf.org/html/rfc7517#section-4.2 octets []byte - x509CertChain *CertificateChain // https://tools.ietf.org/html/rfc7515#section-4.1.6 - x509CertThumbprint *string // https://tools.ietf.org/html/rfc7515#section-4.1.7 - x509CertThumbprintS256 *string // https://tools.ietf.org/html/rfc7515#section-4.1.8 - x509URL *string // https://tools.ietf.org/html/rfc7515#section-4.1.5 + x509CertChain *cert.Chain // https://tools.ietf.org/html/rfc7515#section-4.1.6 + x509CertThumbprint *string // https://tools.ietf.org/html/rfc7515#section-4.1.7 + x509CertThumbprintS256 *string // https://tools.ietf.org/html/rfc7515#section-4.1.8 + x509URL *string // https://tools.ietf.org/html/rfc7515#section-4.1.5 privateParams map[string]interface{} mu *sync.RWMutex dc json.DecodeCtx } -func NewSymmetricKey() SymmetricKey { - return newSymmetricKey() -} +var _ SymmetricKey = &symmetricKey{} +var _ Key = &symmetricKey{} func newSymmetricKey() *symmetricKey { return &symmetricKey{ @@ -59,11 +57,11 @@ func (h symmetricKey) KeyType() jwa.KeyType { return jwa.OctetSeq } -func (h *symmetricKey) Algorithm() string { +func (h *symmetricKey) Algorithm() jwa.KeyAlgorithm { if h.algorithm != nil { return *(h.algorithm) } - return "" + return jwa.InvalidKeyAlgorithm("") } func (h *symmetricKey) KeyID() string { @@ -91,11 +89,8 @@ func (h *symmetricKey) Octets() []byte { return h.octets } -func (h *symmetricKey) X509CertChain() []*x509.Certificate { - if h.x509CertChain != nil { - return h.x509CertChain.Get() - } - return nil +func (h *symmetricKey) X509CertChain() *cert.Chain { + return h.x509CertChain } func (h *symmetricKey) X509CertThumbprint() string { @@ -141,7 +136,7 @@ func (h *symmetricKey) makePairs() []*HeaderPair { pairs = append(pairs, &HeaderPair{Key: SymmetricOctetsKey, Value: h.octets}) } if h.x509CertChain != nil { - pairs = append(pairs, &HeaderPair{Key: X509CertChainKey, Value: *(h.x509CertChain)}) + pairs = append(pairs, &HeaderPair{Key: X509CertChainKey, Value: h.x509CertChain}) } if h.x509CertThumbprint != nil { pairs = append(pairs, &HeaderPair{Key: X509CertThumbprintKey, Value: *(h.x509CertThumbprint)}) @@ -197,7 +192,7 @@ func (h *symmetricKey) Get(name string) (interface{}, bool) { if h.x509CertChain == nil { return nil, false } - return h.x509CertChain.Get(), true + return h.x509CertChain, true case X509CertThumbprintKey: if h.x509CertThumbprint == nil { return nil, false @@ -231,13 +226,15 @@ func (h *symmetricKey) setNoLock(name string, value interface{}) error { return nil case AlgorithmKey: switch v := value.(type) { - case string: - h.algorithm = &v + case string, jwa.SignatureAlgorithm, jwa.ContentEncryptionAlgorithm: + var tmp = jwa.KeyAlgorithmFrom(v) + h.algorithm = &tmp case fmt.Stringer: - tmp := v.String() + s := v.String() + var tmp = jwa.KeyAlgorithmFrom(s) h.algorithm = &tmp default: - return errors.Errorf(`invalid type for %s key: %T`, AlgorithmKey, value) + return fmt.Errorf(`invalid type for %s key: %T`, AlgorithmKey, value) } return nil case KeyIDKey: @@ -245,11 +242,11 @@ func (h *symmetricKey) setNoLock(name string, value interface{}) error { h.keyID = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, KeyIDKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, KeyIDKey, value) case KeyOpsKey: var acceptor KeyOperationList if err := acceptor.Accept(value); err != nil { - return errors.Wrapf(err, `invalid value for %s key`, KeyOpsKey) + return fmt.Errorf(`invalid value for %s key: %w`, KeyOpsKey, err) } h.keyOps = &acceptor return nil @@ -261,44 +258,43 @@ func (h *symmetricKey) setNoLock(name string, value interface{}) error { tmp := v.String() h.keyUsage = &tmp default: - return errors.Errorf(`invalid key usage type %s`, v) + return fmt.Errorf(`invalid key usage type %s`, v) } case string: h.keyUsage = &v default: - return errors.Errorf(`invalid key usage type %s`, v) + return fmt.Errorf(`invalid key usage type %s`, v) } case SymmetricOctetsKey: if v, ok := value.([]byte); ok { h.octets = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, SymmetricOctetsKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, SymmetricOctetsKey, value) case X509CertChainKey: - var acceptor CertificateChain - if err := acceptor.Accept(value); err != nil { - return errors.Wrapf(err, `invalid value for %s key`, X509CertChainKey) + if v, ok := value.(*cert.Chain); ok { + h.x509CertChain = v + return nil } - h.x509CertChain = &acceptor - return nil + return fmt.Errorf(`invalid value for %s key: %T`, X509CertChainKey, value) case X509CertThumbprintKey: if v, ok := value.(string); ok { h.x509CertThumbprint = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509CertThumbprintKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509CertThumbprintKey, value) case X509CertThumbprintS256Key: if v, ok := value.(string); ok { h.x509CertThumbprintS256 = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509CertThumbprintS256Key, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509CertThumbprintS256Key, value) case X509URLKey: if v, ok := value.(string); ok { h.x509URL = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509URLKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509URLKey, value) default: if h.privateParams == nil { h.privateParams = map[string]interface{}{} @@ -353,6 +349,8 @@ func (k *symmetricKey) SetDecodeCtx(dc json.DecodeCtx) { } func (h *symmetricKey) UnmarshalJSON(buf []byte) error { + h.mu.Lock() + defer h.mu.Unlock() h.algorithm = nil h.keyID = nil h.keyOps = nil @@ -367,7 +365,7 @@ LOOP: for { tok, err := dec.Token() if err != nil { - return errors.Wrap(err, `error reading token`) + return fmt.Errorf(`error reading token: %w`, err) } switch tok := tok.(type) { case json.Delim: @@ -376,57 +374,60 @@ LOOP: if tok == '}' { // End of object break LOOP } else if tok != '{' { - return errors.Errorf(`expected '{', but got '%c'`, tok) + return fmt.Errorf(`expected '{', but got '%c'`, tok) } case string: // Objects can only have string keys switch tok { case KeyTypeKey: val, err := json.ReadNextStringToken(dec) if err != nil { - return errors.Wrap(err, `error reading token`) + return fmt.Errorf(`error reading token: %w`, err) } if val != jwa.OctetSeq.String() { - return errors.Errorf(`invalid kty value for RSAPublicKey (%s)`, val) + return fmt.Errorf(`invalid kty value for RSAPublicKey (%s)`, val) } case AlgorithmKey: - if err := json.AssignNextStringToken(&h.algorithm, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, AlgorithmKey) + var s string + if err := dec.Decode(&s); err != nil { + return fmt.Errorf(`failed to decode value for key %s: %w`, AlgorithmKey, err) } + alg := jwa.KeyAlgorithmFrom(s) + h.algorithm = &alg case KeyIDKey: if err := json.AssignNextStringToken(&h.keyID, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, KeyIDKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, KeyIDKey, err) } case KeyOpsKey: var decoded KeyOperationList if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, KeyOpsKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, KeyOpsKey, err) } h.keyOps = &decoded case KeyUsageKey: if err := json.AssignNextStringToken(&h.keyUsage, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, KeyUsageKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, KeyUsageKey, err) } case SymmetricOctetsKey: if err := json.AssignNextBytesToken(&h.octets, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, SymmetricOctetsKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, SymmetricOctetsKey, err) } case X509CertChainKey: - var decoded CertificateChain + var decoded cert.Chain if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertChainKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertChainKey, err) } h.x509CertChain = &decoded case X509CertThumbprintKey: if err := json.AssignNextStringToken(&h.x509CertThumbprint, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertThumbprintKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertThumbprintKey, err) } case X509CertThumbprintS256Key: if err := json.AssignNextStringToken(&h.x509CertThumbprintS256, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertThumbprintS256Key) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertThumbprintS256Key, err) } case X509URLKey: if err := json.AssignNextStringToken(&h.x509URL, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509URLKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509URLKey, err) } default: if dc := h.dc; dc != nil { @@ -443,14 +444,14 @@ LOOP: h.setNoLock(tok, decoded) continue } - return errors.Wrapf(err, `could not decode field %s`, tok) + return fmt.Errorf(`could not decode field %s: %w`, tok, err) } default: - return errors.Errorf(`invalid token %T`, tok) + return fmt.Errorf(`invalid token %T`, tok) } } if h.octets == nil { - return errors.Errorf(`required field k is missing`) + return fmt.Errorf(`required field k is missing`) } return nil } @@ -483,7 +484,7 @@ func (h symmetricKey) MarshalJSON() ([]byte, error) { buf.WriteRune('"') default: if err := enc.Encode(v); err != nil { - return nil, errors.Wrapf(err, `failed to encode value for field %s`, f) + return nil, fmt.Errorf(`failed to encode value for field %s: %w`, f, err) } buf.Truncate(buf.Len() - 1) } diff --git a/vendor/github.com/lestrrat-go/jwx/jwk/usage.go b/vendor/github.com/lestrrat-go/jwx/v2/jwk/usage.go similarity index 65% rename from vendor/github.com/lestrrat-go/jwx/jwk/usage.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwk/usage.go index 731a2ae..c218923 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwk/usage.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwk/usage.go @@ -1,6 +1,6 @@ package jwk -import "github.com/pkg/errors" +import "fmt" func (k KeyUsageType) String() string { return string(k) @@ -14,7 +14,7 @@ func (k *KeyUsageType) Accept(v interface{}) error { *k = v return nil default: - return errors.Errorf("invalid key usage type %s", v) + return fmt.Errorf("invalid key usage type %s", v) } case string: switch v { @@ -22,9 +22,9 @@ func (k *KeyUsageType) Accept(v interface{}) error { *k = KeyUsageType(v) return nil default: - return errors.Errorf("invalid key usage type %s", v) + return fmt.Errorf("invalid key usage type %s", v) } } - return errors.Errorf("invalid value for key usage type %s", v) + return fmt.Errorf("invalid value for key usage type %s", v) } diff --git a/vendor/github.com/lestrrat-go/jwx/jwk/whitelist.go b/vendor/github.com/lestrrat-go/jwx/v2/jwk/whitelist.go similarity index 100% rename from vendor/github.com/lestrrat-go/jwx/jwk/whitelist.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwk/whitelist.go diff --git a/vendor/github.com/lestrrat-go/jwx/jws/README.md b/vendor/github.com/lestrrat-go/jwx/v2/jws/README.md similarity index 70% rename from vendor/github.com/lestrrat-go/jwx/jws/README.md rename to vendor/github.com/lestrrat-go/jwx/v2/jws/README.md index f6e3dcb..470842e 100644 --- a/vendor/github.com/lestrrat-go/jwx/jws/README.md +++ b/vendor/github.com/lestrrat-go/jwx/v2/jws/README.md @@ -1,10 +1,10 @@ -# JWS [![Go Reference](https://pkg.go.dev/badge/github.com/lestrrat-go/jwx/jws.svg)](https://pkg.go.dev/github.com/lestrrat-go/jwx/jws) +# JWS [![Go Reference](https://pkg.go.dev/badge/github.com/lestrrat-go/jwx/v2/jws.svg)](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jws) Package jws implements JWS as described in [RFC7515](https://tools.ietf.org/html/rfc7515) and [RFC7797](https://tools.ietf.org/html/rfc7797) * Parse and generate compact or JSON serializations * Sign and verify arbitrary payload -* Use any of the keys supported in [github.com/lestrrat-go/jwx/jwk](../jwk) +* Use any of the keys supported in [github.com/lestrrat-go/jwx/v2/jwk](../jwk) * Add arbitrary fields in the JWS object * Ability to add/replace existing signature methods * Respect "b64" settings for RFC7797 @@ -45,8 +45,8 @@ import( "crypto/rsa" "log" - "github.com/lestrrat-go/jwx/jwa" - "github.com/lestrrat-go/jwx/jws" + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jws" ) func main() { @@ -56,7 +56,7 @@ func main() { return } - buf, err := jws.Sign([]byte("Lorem ipsum"), jwa.RS256, privkey) + buf, err := jws.Sign([]byte("Lorem ipsum"), jws.WithKey(jwa.RS256, privkey)) if err != nil { log.Printf("failed to created JWS message: %s", err) return @@ -64,7 +64,7 @@ func main() { // When you receive a JWS message, you can verify the signature // and grab the payload sent in the message in one go: - verified, err := jws.Verify(buf, jwa.RS256, &privkey.PublicKey) + verified, err := jws.Verify(buf, jws.WithKey(jwa.RS256, &privkey.PublicKey)) if err != nil { log.Printf("failed to verify message: %s", err) return @@ -78,34 +78,34 @@ func main() { ```go func ExampleMessage() { - // initialization for the following variables have been omitted. - // please see jws_example_test.go for details - var decodedPayload, decodedSig1, decodedSig2 []byte - var public1, protected1, public2, protected2 jws.Header + // initialization for the following variables have been omitted. + // please see jws_example_test.go for details + var decodedPayload, decodedSig1, decodedSig2 []byte + var public1, protected1, public2, protected2 jws.Header - // Construct a message. DO NOT use values that are base64 encoded - m := jws.NewMessage(). - SetPayload(decodedPayload). - AppendSignature( - jws.NewSignature(). - SetSignature(decodedSig1). - SetProtectedHeaders(public1). - SetPublicHeaders(protected1), - ). - AppendSignature( - jws.NewSignature(). - SetSignature(decodedSig2). - SetProtectedHeaders(public2). - SetPublicHeaders(protected2), - ) + // Construct a message. DO NOT use values that are base64 encoded + m := jws.NewMessage(). + SetPayload(decodedPayload). + AppendSignature( + jws.NewSignature(). + SetSignature(decodedSig1). + SetProtectedHeaders(public1). + SetPublicHeaders(protected1), + ). + AppendSignature( + jws.NewSignature(). + SetSignature(decodedSig2). + SetProtectedHeaders(public2). + SetPublicHeaders(protected2), + ) - buf, err := json.MarshalIndent(m, "", " ") - if err != nil { - fmt.Printf("%s\n", err) - return - } + buf, err := json.MarshalIndent(m, "", " ") + if err != nil { + fmt.Printf("%s\n", err) + return + } - _ = buf + _ = buf } ``` diff --git a/vendor/github.com/lestrrat-go/jwx/jws/ecdsa.go b/vendor/github.com/lestrrat-go/jwx/v2/jws/ecdsa.go similarity index 81% rename from vendor/github.com/lestrrat-go/jwx/jws/ecdsa.go rename to vendor/github.com/lestrrat-go/jwx/v2/jws/ecdsa.go index 2f35af5..aadb059 100644 --- a/vendor/github.com/lestrrat-go/jwx/jws/ecdsa.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jws/ecdsa.go @@ -8,10 +8,9 @@ import ( "fmt" "math/big" - "github.com/lestrrat-go/jwx/internal/keyconv" - "github.com/lestrrat-go/jwx/internal/pool" - "github.com/lestrrat-go/jwx/jwa" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/internal/keyconv" + "github.com/lestrrat-go/jwx/v2/internal/pool" + "github.com/lestrrat-go/jwx/v2/jwa" ) var ecdsaSigners map[jwa.SignatureAlgorithm]*ecdsaSigner @@ -55,12 +54,12 @@ func (es ecdsaSigner) Algorithm() jwa.SignatureAlgorithm { func (es *ecdsaSigner) Sign(payload []byte, key interface{}) ([]byte, error) { if key == nil { - return nil, errors.New(`missing private key while signing payload`) + return nil, fmt.Errorf(`missing private key while signing payload`) } h := es.hash.New() if _, err := h.Write(payload); err != nil { - return nil, errors.Wrap(err, "failed to write payload using ecdsa") + return nil, fmt.Errorf(`failed to write payload using ecdsa: %w`, err) } signer, ok := key.(crypto.Signer) @@ -86,7 +85,7 @@ func (es *ecdsaSigner) Sign(payload []byte, key interface{}) ([]byte, error) { S *big.Int } if _, err := asn1.Unmarshal(signed, &p); err != nil { - return nil, errors.Wrap(err, `failed to unmarshal ASN1 encoded signature`) + return nil, fmt.Errorf(`failed to unmarshal ASN1 encoded signature: %w`, err) } // Okay, this is silly, but hear me out. When we use the @@ -108,12 +107,12 @@ func (es *ecdsaSigner) Sign(payload []byte, key interface{}) ([]byte, error) { } else { var privkey ecdsa.PrivateKey if err := keyconv.ECDSAPrivateKey(&privkey, key); err != nil { - return nil, errors.Wrapf(err, `failed to retrieve ecdsa.PrivateKey out of %T`, key) + return nil, fmt.Errorf(`failed to retrieve ecdsa.PrivateKey out of %T: %w`, key, err) } curveBits = privkey.Curve.Params().BitSize rtmp, stmp, err := ecdsa.Sign(rand.Reader, &privkey, h.Sum(nil)) if err != nil { - return nil, errors.Wrap(err, "failed to sign payload using ecdsa") + return nil, fmt.Errorf(`failed to sign payload using ecdsa: %w`, err) } r = rtmp s = stmp @@ -153,7 +152,7 @@ func (v ecdsaVerifier) Algorithm() jwa.SignatureAlgorithm { func (v *ecdsaVerifier) Verify(payload []byte, signature []byte, key interface{}) error { if key == nil { - return errors.New(`missing public key while verifying payload`) + return fmt.Errorf(`missing public key while verifying payload`) } var pubkey ecdsa.PublicKey @@ -165,11 +164,11 @@ func (v *ecdsaVerifier) Verify(payload []byte, signature []byte, key interface{} case *ecdsa.PublicKey: pubkey = *cpub default: - return errors.Errorf(`failed to retrieve ecdsa.PublicKey out of crypto.Signer %T`, key) + return fmt.Errorf(`failed to retrieve ecdsa.PublicKey out of crypto.Signer %T`, key) } } else { if err := keyconv.ECDSAPublicKey(&pubkey, key); err != nil { - return errors.Wrapf(err, `failed to retrieve ecdsa.PublicKey out of %T`, key) + return fmt.Errorf(`failed to retrieve ecdsa.PublicKey out of %T: %w`, key, err) } } @@ -184,11 +183,11 @@ func (v *ecdsaVerifier) Verify(payload []byte, signature []byte, key interface{} h := v.hash.New() if _, err := h.Write(payload); err != nil { - return errors.Wrap(err, "failed to write payload using ecdsa") + return fmt.Errorf(`failed to write payload using ecdsa: %w`, err) } if !ecdsa.Verify(&pubkey, h.Sum(nil), r, s) { - return errors.New(`failed to verify signature using ecdsa`) + return fmt.Errorf(`failed to verify signature using ecdsa`) } return nil } diff --git a/vendor/github.com/lestrrat-go/jwx/jws/eddsa.go b/vendor/github.com/lestrrat-go/jwx/v2/jws/eddsa.go similarity index 69% rename from vendor/github.com/lestrrat-go/jwx/jws/eddsa.go rename to vendor/github.com/lestrrat-go/jwx/v2/jws/eddsa.go index 698148b..78c1a2d 100644 --- a/vendor/github.com/lestrrat-go/jwx/jws/eddsa.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jws/eddsa.go @@ -4,10 +4,10 @@ import ( "crypto" "crypto/ed25519" "crypto/rand" + "fmt" - "github.com/lestrrat-go/jwx/internal/keyconv" - "github.com/lestrrat-go/jwx/jwa" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/internal/keyconv" + "github.com/lestrrat-go/jwx/v2/jwa" ) type eddsaSigner struct{} @@ -22,7 +22,7 @@ func (s eddsaSigner) Algorithm() jwa.SignatureAlgorithm { func (s eddsaSigner) Sign(payload []byte, key interface{}) ([]byte, error) { if key == nil { - return nil, errors.New(`missing private key while signing payload`) + return nil, fmt.Errorf(`missing private key while signing payload`) } // The ed25519.PrivateKey object implements crypto.Signer, so we should @@ -33,7 +33,7 @@ func (s eddsaSigner) Sign(payload []byte, key interface{}) ([]byte, error) { // users gave us a pointer instead of non-pointer, etc. var privkey ed25519.PrivateKey if err := keyconv.Ed25519PrivateKey(&privkey, key); err != nil { - return nil, errors.Wrapf(err, `failed to retrieve ed25519.PrivateKey out of %T`, key) + return nil, fmt.Errorf(`failed to retrieve ed25519.PrivateKey out of %T: %w`, key, err) } signer = privkey } @@ -48,7 +48,7 @@ func newEdDSAVerifier() Verifier { func (v eddsaVerifier) Verify(payload, signature []byte, key interface{}) (err error) { if key == nil { - return errors.New(`missing public key while verifying payload`) + return fmt.Errorf(`missing public key while verifying payload`) } var pubkey ed25519.PublicKey @@ -57,16 +57,16 @@ func (v eddsaVerifier) Verify(payload, signature []byte, key interface{}) (err e v := signer.Public() pubkey, ok = v.(ed25519.PublicKey) if !ok { - return errors.Errorf(`expected crypto.Signer.Public() to return ed25519.PublicKey, but got %T`, v) + return fmt.Errorf(`expected crypto.Signer.Public() to return ed25519.PublicKey, but got %T`, v) } } else { if err := keyconv.Ed25519PublicKey(&pubkey, key); err != nil { - return errors.Wrapf(err, `failed to retrieve ed25519.PublicKey out of %T`, key) + return fmt.Errorf(`failed to retrieve ed25519.PublicKey out of %T: %w`, key, err) } } if !ed25519.Verify(pubkey, payload, signature) { - return errors.New(`failed to match EdDSA signature`) + return fmt.Errorf(`failed to match EdDSA signature`) } return nil diff --git a/vendor/github.com/lestrrat-go/jwx/jws/es256k.go b/vendor/github.com/lestrrat-go/jwx/v2/jws/es256k.go similarity index 74% rename from vendor/github.com/lestrrat-go/jwx/jws/es256k.go rename to vendor/github.com/lestrrat-go/jwx/v2/jws/es256k.go index fd5db88..d342df5 100644 --- a/vendor/github.com/lestrrat-go/jwx/jws/es256k.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jws/es256k.go @@ -3,7 +3,7 @@ package jws import ( - "github.com/lestrrat-go/jwx/jwa" + "github.com/lestrrat-go/jwx/v2/jwa" ) func init() { diff --git a/vendor/github.com/lestrrat-go/jwx/jws/headers.go b/vendor/github.com/lestrrat-go/jwx/v2/jws/headers.go similarity index 84% rename from vendor/github.com/lestrrat-go/jwx/jws/headers.go rename to vendor/github.com/lestrrat-go/jwx/v2/jws/headers.go index 9ca8f65..dce7289 100644 --- a/vendor/github.com/lestrrat-go/jwx/jws/headers.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jws/headers.go @@ -2,10 +2,10 @@ package jws import ( "context" + "fmt" "github.com/lestrrat-go/iter/mapiter" - "github.com/lestrrat-go/jwx/internal/iter" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/internal/iter" ) // Iterate returns a channel that successively returns all the @@ -39,7 +39,7 @@ func (h *stdHeaders) Copy(ctx context.Context, dst Headers) error { //nolint:forcetypeassert key := pair.Key.(string) if err := dst.Set(key, pair.Value); err != nil { - return errors.Wrapf(err, `failed to set header %q`, key) + return fmt.Errorf(`failed to set header %q: %w`, key, err) } } return nil @@ -53,13 +53,13 @@ func mergeHeaders(ctx context.Context, h1, h2 Headers) (Headers, error) { if h1 != nil { if err := h1.Copy(ctx, h3); err != nil { - return nil, errors.Wrap(err, `failed to copy headers from first Header`) + return nil, fmt.Errorf(`failed to copy headers from first Header: %w`, err) } } if h2 != nil { if err := h2.Copy(ctx, h3); err != nil { - return nil, errors.Wrap(err, `failed to copy headers from second Header`) + return nil, fmt.Errorf(`failed to copy headers from second Header: %w`, err) } } diff --git a/vendor/github.com/lestrrat-go/jwx/jws/headers_gen.go b/vendor/github.com/lestrrat-go/jwx/v2/jws/headers_gen.go similarity index 82% rename from vendor/github.com/lestrrat-go/jwx/jws/headers_gen.go rename to vendor/github.com/lestrrat-go/jwx/v2/jws/headers_gen.go index de624bc..fd892e2 100644 --- a/vendor/github.com/lestrrat-go/jwx/jws/headers_gen.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jws/headers_gen.go @@ -5,15 +5,16 @@ package jws import ( "bytes" "context" + "fmt" "sort" "sync" - "github.com/lestrrat-go/jwx/internal/base64" - "github.com/lestrrat-go/jwx/internal/json" - "github.com/lestrrat-go/jwx/internal/pool" - "github.com/lestrrat-go/jwx/jwa" - "github.com/lestrrat-go/jwx/jwk" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/cert" + "github.com/lestrrat-go/jwx/v2/internal/base64" + "github.com/lestrrat-go/jwx/v2/internal/json" + "github.com/lestrrat-go/jwx/v2/internal/pool" + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwk" ) const ( @@ -41,7 +42,7 @@ type Headers interface { JWKSetURL() string KeyID() string Type() string - X509CertChain() []string + X509CertChain() *cert.Chain X509CertThumbprint() string X509CertThumbprintS256() string X509URL() string @@ -68,7 +69,7 @@ type stdHeaders struct { jwkSetURL *string // https://tools.ietf.org/html/rfc7515#section-4.1.2 keyID *string // https://tools.ietf.org/html/rfc7515#section-4.1.4 typ *string // https://tools.ietf.org/html/rfc7515#section-4.1.9 - x509CertChain []string // https://tools.ietf.org/html/rfc7515#section-4.1.6 + x509CertChain *cert.Chain // https://tools.ietf.org/html/rfc7515#section-4.1.6 x509CertThumbprint *string // https://tools.ietf.org/html/rfc7515#section-4.1.7 x509CertThumbprintS256 *string // https://tools.ietf.org/html/rfc7515#section-4.1.8 x509URL *string // https://tools.ietf.org/html/rfc7515#section-4.1.5 @@ -141,7 +142,7 @@ func (h *stdHeaders) Type() string { return *(h.typ) } -func (h *stdHeaders) X509CertChain() []string { +func (h *stdHeaders) X509CertChain() *cert.Chain { h.mu.RLock() defer h.mu.RUnlock() return h.x509CertChain @@ -174,6 +175,22 @@ func (h *stdHeaders) X509URL() string { return *(h.x509URL) } +func (h *stdHeaders) clear() { + h.algorithm = nil + h.contentType = nil + h.critical = nil + h.jwk = nil + h.jwkSetURL = nil + h.keyID = nil + h.typ = nil + h.x509CertChain = nil + h.x509CertThumbprint = nil + h.x509CertThumbprintS256 = nil + h.x509URL = nil + h.privateParams = nil + h.raw = nil +} + func (h *stdHeaders) DecodeCtx() DecodeCtx { h.mu.RLock() defer h.mu.RUnlock() @@ -318,7 +335,7 @@ func (h *stdHeaders) setNoLock(name string, value interface{}) error { case AlgorithmKey: var acceptor jwa.SignatureAlgorithm if err := acceptor.Accept(value); err != nil { - return errors.Wrapf(err, `invalid value for %s key`, AlgorithmKey) + return fmt.Errorf(`invalid value for %s key: %w`, AlgorithmKey, err) } h.algorithm = &acceptor return nil @@ -327,61 +344,61 @@ func (h *stdHeaders) setNoLock(name string, value interface{}) error { h.contentType = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, ContentTypeKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, ContentTypeKey, value) case CriticalKey: if v, ok := value.([]string); ok { h.critical = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, CriticalKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, CriticalKey, value) case JWKKey: if v, ok := value.(jwk.Key); ok { h.jwk = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, JWKKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, JWKKey, value) case JWKSetURLKey: if v, ok := value.(string); ok { h.jwkSetURL = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, JWKSetURLKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, JWKSetURLKey, value) case KeyIDKey: if v, ok := value.(string); ok { h.keyID = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, KeyIDKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, KeyIDKey, value) case TypeKey: if v, ok := value.(string); ok { h.typ = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, TypeKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, TypeKey, value) case X509CertChainKey: - if v, ok := value.([]string); ok { + if v, ok := value.(*cert.Chain); ok { h.x509CertChain = v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509CertChainKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509CertChainKey, value) case X509CertThumbprintKey: if v, ok := value.(string); ok { h.x509CertThumbprint = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509CertThumbprintKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509CertThumbprintKey, value) case X509CertThumbprintS256Key: if v, ok := value.(string); ok { h.x509CertThumbprintS256 = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509CertThumbprintS256Key, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509CertThumbprintS256Key, value) case X509URLKey: if v, ok := value.(string); ok { h.x509URL = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, X509URLKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, X509URLKey, value) default: if h.privateParams == nil { h.privateParams = map[string]interface{}{} @@ -424,23 +441,15 @@ func (h *stdHeaders) Remove(key string) error { } func (h *stdHeaders) UnmarshalJSON(buf []byte) error { - h.algorithm = nil - h.contentType = nil - h.critical = nil - h.jwk = nil - h.jwkSetURL = nil - h.keyID = nil - h.typ = nil - h.x509CertChain = nil - h.x509CertThumbprint = nil - h.x509CertThumbprintS256 = nil - h.x509URL = nil + h.mu.Lock() + defer h.mu.Unlock() + h.clear() dec := json.NewDecoder(bytes.NewReader(buf)) LOOP: for { tok, err := dec.Token() if err != nil { - return errors.Wrap(err, `error reading token`) + return fmt.Errorf(`error reading token: %w`, err) } switch tok := tok.(type) { case json.Delim: @@ -449,65 +458,65 @@ LOOP: if tok == '}' { // End of object break LOOP } else if tok != '{' { - return errors.Errorf(`expected '{', but got '%c'`, tok) + return fmt.Errorf(`expected '{', but got '%c'`, tok) } case string: // Objects can only have string keys switch tok { case AlgorithmKey: var decoded jwa.SignatureAlgorithm if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, AlgorithmKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, AlgorithmKey, err) } h.algorithm = &decoded case ContentTypeKey: if err := json.AssignNextStringToken(&h.contentType, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, ContentTypeKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, ContentTypeKey, err) } case CriticalKey: var decoded []string if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, CriticalKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, CriticalKey, err) } h.critical = decoded case JWKKey: var buf json.RawMessage if err := dec.Decode(&buf); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, JWKKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, JWKKey, err) } key, err := jwk.ParseKey(buf) if err != nil { - return errors.Wrapf(err, `failed to parse JWK for key %s`, JWKKey) + return fmt.Errorf(`failed to parse JWK for key %s: %w`, JWKKey, err) } h.jwk = key case JWKSetURLKey: if err := json.AssignNextStringToken(&h.jwkSetURL, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, JWKSetURLKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, JWKSetURLKey, err) } case KeyIDKey: if err := json.AssignNextStringToken(&h.keyID, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, KeyIDKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, KeyIDKey, err) } case TypeKey: if err := json.AssignNextStringToken(&h.typ, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, TypeKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, TypeKey, err) } case X509CertChainKey: - var decoded []string + var decoded cert.Chain if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertChainKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertChainKey, err) } - h.x509CertChain = decoded + h.x509CertChain = &decoded case X509CertThumbprintKey: if err := json.AssignNextStringToken(&h.x509CertThumbprint, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertThumbprintKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertThumbprintKey, err) } case X509CertThumbprintS256Key: if err := json.AssignNextStringToken(&h.x509CertThumbprintS256, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509CertThumbprintS256Key) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertThumbprintS256Key, err) } case X509URLKey: if err := json.AssignNextStringToken(&h.x509URL, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, X509URLKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, X509URLKey, err) } default: decoded, err := registry.Decode(dec, tok) @@ -517,15 +526,10 @@ LOOP: h.setNoLock(tok, decoded) } default: - return errors.Errorf(`invalid token %T`, tok) - } - } - - if dc := h.dc; dc != nil { - if dc.CollectRaw() { - h.raw = buf + return fmt.Errorf(`invalid token %T`, tok) } } + h.raw = buf return nil } @@ -549,7 +553,7 @@ func (h stdHeaders) MarshalJSON() ([]byte, error) { buf.WriteRune('"') default: if err := enc.Encode(v); err != nil { - errors.Errorf(`failed to encode value for field %s`, p.Key) + return nil, fmt.Errorf(`failed to encode value for field %s: %w`, p.Key, err) } buf.Truncate(buf.Len() - 1) } diff --git a/vendor/github.com/lestrrat-go/jwx/jws/hmac.go b/vendor/github.com/lestrrat-go/jwx/v2/jws/hmac.go similarity index 76% rename from vendor/github.com/lestrrat-go/jwx/jws/hmac.go rename to vendor/github.com/lestrrat-go/jwx/v2/jws/hmac.go index e85471a..247ebc7 100644 --- a/vendor/github.com/lestrrat-go/jwx/jws/hmac.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jws/hmac.go @@ -4,11 +4,11 @@ import ( "crypto/hmac" "crypto/sha256" "crypto/sha512" + "fmt" "hash" - "github.com/lestrrat-go/jwx/internal/keyconv" - "github.com/lestrrat-go/jwx/jwa" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/internal/keyconv" + "github.com/lestrrat-go/jwx/v2/jwa" ) var hmacSignFuncs = map[jwa.SignatureAlgorithm]hmacSignFunc{} @@ -36,7 +36,7 @@ func makeHMACSignFunc(hfunc func() hash.Hash) hmacSignFunc { return func(payload []byte, key []byte) ([]byte, error) { h := hmac.New(hfunc, key) if _, err := h.Write(payload); err != nil { - return nil, errors.Wrap(err, "failed to write payload using hmac") + return nil, fmt.Errorf(`failed to write payload using hmac: %w`, err) } return h.Sum(nil), nil } @@ -49,11 +49,11 @@ func (s HMACSigner) Algorithm() jwa.SignatureAlgorithm { func (s HMACSigner) Sign(payload []byte, key interface{}) ([]byte, error) { var hmackey []byte if err := keyconv.ByteSliceKey(&hmackey, key); err != nil { - return nil, errors.Wrapf(err, `invalid key type %T. []byte is required`, key) + return nil, fmt.Errorf(`invalid key type %T. []byte is required: %w`, key, err) } if len(hmackey) == 0 { - return nil, errors.New(`missing key while signing payload`) + return nil, fmt.Errorf(`missing key while signing payload`) } return s.sign(payload, hmackey) @@ -67,11 +67,11 @@ func newHMACVerifier(alg jwa.SignatureAlgorithm) Verifier { func (v HMACVerifier) Verify(payload, signature []byte, key interface{}) (err error) { expected, err := v.signer.Sign(payload, key) if err != nil { - return errors.Wrap(err, `failed to generated signature`) + return fmt.Errorf(`failed to generated signature: %w`, err) } if !hmac.Equal(signature, expected) { - return errors.New(`failed to match hmac signature`) + return fmt.Errorf(`failed to match hmac signature`) } return nil } diff --git a/vendor/github.com/lestrrat-go/jwx/jws/interface.go b/vendor/github.com/lestrrat-go/jwx/v2/jws/interface.go similarity index 97% rename from vendor/github.com/lestrrat-go/jwx/jws/interface.go rename to vendor/github.com/lestrrat-go/jwx/v2/jws/interface.go index aeee42b..5fc1fcd 100644 --- a/vendor/github.com/lestrrat-go/jwx/jws/interface.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jws/interface.go @@ -2,8 +2,8 @@ package jws import ( "github.com/lestrrat-go/iter/mapiter" - "github.com/lestrrat-go/jwx/internal/iter" - "github.com/lestrrat-go/jwx/jwa" + "github.com/lestrrat-go/jwx/v2/internal/iter" + "github.com/lestrrat-go/jwx/v2/jwa" ) type DecodeCtx interface { diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jws/io.go b/vendor/github.com/lestrrat-go/jwx/v2/jws/io.go new file mode 100644 index 0000000..7bfd07f --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jws/io.go @@ -0,0 +1,42 @@ +// Automatically generated by internal/cmd/genreadfile/main.go. DO NOT EDIT + +package jws + +import ( + "io/fs" + "os" +) + +type sysFS struct{} + +func (sysFS) Open(path string) (fs.File, error) { + return os.Open(path) +} + +func ReadFile(path string, options ...ReadFileOption) (*Message, error) { + var parseOptions []ParseOption + var readFileOptions []ReadFileOption + for _, option := range options { + if po, ok := option.(ParseOption); ok { + parseOptions = append(parseOptions, po) + } else { + readFileOptions = append(readFileOptions, option) + } + } + + var srcFS fs.FS = sysFS{} + for _, option := range options { + switch option.Ident() { + case identFS{}: + srcFS = option.Value().(fs.FS) + } + } + + f, err := srcFS.Open(path) + if err != nil { + return nil, err + } + + defer f.Close() + return ParseReader(f) +} diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jws/jws.go b/vendor/github.com/lestrrat-go/jwx/v2/jws/jws.go new file mode 100644 index 0000000..77e21fc --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jws/jws.go @@ -0,0 +1,716 @@ +//go:generate ../tools/cmd/genjws.sh + +// Package jws implements the digital signature on JSON based data +// structures as described in https://tools.ietf.org/html/rfc7515 +// +// If you do not care about the details, the only things that you +// would need to use are the following functions: +// +// jws.Sign(payload, jws.WithKey(algorithm, key)) +// jws.Verify(serialized, jws.WithKey(algorithm, key)) +// +// To sign, simply use `jws.Sign`. `payload` is a []byte buffer that +// contains whatever data you want to sign. `alg` is one of the +// jwa.SignatureAlgorithm constants from package jwa. For RSA and +// ECDSA family of algorithms, you will need to prepare a private key. +// For HMAC family, you just need a []byte value. The `jws.Sign` +// function will return the encoded JWS message on success. +// +// To verify, use `jws.Verify`. It will parse the `encodedjws` buffer +// and verify the result using `algorithm` and `key`. Upon successful +// verification, the original payload is returned, so you can work on it. +package jws + +import ( + "bufio" + "bytes" + "context" + "crypto/ecdsa" + "crypto/ed25519" + "crypto/rsa" + "fmt" + "io" + "reflect" + "strings" + "sync" + "unicode" + "unicode/utf8" + + "github.com/lestrrat-go/blackmagic" + "github.com/lestrrat-go/jwx/v2/internal/base64" + "github.com/lestrrat-go/jwx/v2/internal/json" + "github.com/lestrrat-go/jwx/v2/internal/pool" + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwk" + "github.com/lestrrat-go/jwx/v2/x25519" +) + +var registry = json.NewRegistry() + +type payloadSigner struct { + signer Signer + key interface{} + protected Headers + public Headers +} + +func (s *payloadSigner) Sign(payload []byte) ([]byte, error) { + return s.signer.Sign(payload, s.key) +} + +func (s *payloadSigner) Algorithm() jwa.SignatureAlgorithm { + return s.signer.Algorithm() +} + +func (s *payloadSigner) ProtectedHeader() Headers { + return s.protected +} + +func (s *payloadSigner) PublicHeader() Headers { + return s.public +} + +var signers = make(map[jwa.SignatureAlgorithm]Signer) +var muSigner = &sync.Mutex{} + +func makeSigner(alg jwa.SignatureAlgorithm, key interface{}, public, protected Headers) (*payloadSigner, error) { + muSigner.Lock() + signer, ok := signers[alg] + if !ok { + v, err := NewSigner(alg) + if err != nil { + muSigner.Unlock() + return nil, fmt.Errorf(`failed to create payload signer: %w`, err) + } + signers[alg] = v + signer = v + } + muSigner.Unlock() + + return &payloadSigner{ + signer: signer, + key: key, + public: public, + protected: protected, + }, nil +} + +const ( + fmtInvalid = iota + fmtCompact + fmtJSON + fmtJSONPretty + fmtMax +) + +// silence linters +var _ = fmtInvalid +var _ = fmtMax + +// Sign generates a JWS message for the given payload and returns +// it in serialized form, which can be in either compact or +// JSON format. Default is compact. +// +// You must pass at least one key to `jws.Sign()` by using `jws.WithKey()` +// option. +// +// jws.Sign(payload, jws.WithKey(alg, key)) +// jws.Sign(payload, jws.WithJSON(), jws.WithKey(alg1, key1), jws.WithKey(alg2, key2)) +// +// Note that in the second example the `jws.WithJSON()` option is +// specified as well. This is because the compact serialization +// format does not support multiple signatures, and users must +// specifically ask for the JSON serialization format. +// +// Read the documentation for `jws.WithKey()` to learn more about the +// possible values that can be used for `alg` and `key`. +// +// If you want to use a detached payload, use `jws.WithDetachedPayload()` as +// one of the options. When you use this option, you must always set the +// first parameter (`payload`) to `nil`, or the function will return an error +// +// You may also wantt to look at how to pass protected headers to the +// signing process, as you will likely be required to set the `b64` field +// when using detached payload. +// +// Look for options that return `jws.SignOption` or `jws.SignVerifyOption` +// for a complete list of options that can be passed to this function. +func Sign(payload []byte, options ...SignOption) ([]byte, error) { + format := fmtCompact + var signers []*payloadSigner + var detached bool + for _, option := range options { + //nolint:forcetypeassert + switch option.Ident() { + case identSerialization{}: + format = option.Value().(int) + case identKey{}: + data := option.Value().(*withKey) + + alg, ok := data.alg.(jwa.SignatureAlgorithm) + if !ok { + return nil, fmt.Errorf(`jws.Sign: expected algorithm to be of type jwa.SignatureAlgorithm but got (%[1]q, %[1]T)`, data.alg) + } + signer, err := makeSigner(alg, data.key, data.public, data.protected) + if err != nil { + return nil, fmt.Errorf(`jws.Sign: failed to create signer: %w`, err) + } + signers = append(signers, signer) + case identDetachedPayload{}: + detached = true + if payload != nil { + return nil, fmt.Errorf(`jws.Sign: payload must be nil when jws.WithDetachedPayload() is specified`) + } + payload = option.Value().([]byte) + } + } + + lsigner := len(signers) + if lsigner == 0 { + return nil, fmt.Errorf(`jws.Sign: no signers available. Specify an alogirthm and akey using jws.WithKey()`) + } + + // Design note: while we could have easily set format = fmtJSON when + // lsigner > 1, I believe the decision to change serialization formats + // must be explicitly stated by the caller. Otherwise I'm pretty sure + // there would be people filing issues saying "I get JSON when I expcted + // compact serialization". + // + // Therefore, instead of making implicit format conversions, we force the + // user to spell it out as `jws.Sign(..., jws.WithJSON(), jws.WithKey(...), jws.WithKey(...))` + if format == fmtCompact && lsigner != 1 { + return nil, fmt.Errorf(`jws.Sign: cannot have multiple signers (keys) specified for compact serialization. Use only one jws.WithKey()`) + } + + // Create a Message object with all the bits and bobs, and we'll + // serialize it in the end + var result Message + + result.payload = payload + + result.signatures = make([]*Signature, 0, len(signers)) + for i, signer := range signers { + protected := signer.ProtectedHeader() + if protected == nil { + protected = NewHeaders() + } + + if err := protected.Set(AlgorithmKey, signer.Algorithm()); err != nil { + return nil, fmt.Errorf(`failed to set "alg" header: %w`, err) + } + + if key, ok := signer.key.(jwk.Key); ok { + if kid := key.KeyID(); kid != "" { + if err := protected.Set(KeyIDKey, kid); err != nil { + return nil, fmt.Errorf(`failed to set "kid" header: %w`, err) + } + } + } + sig := &Signature{ + headers: signer.PublicHeader(), + protected: protected, + // cheat. FIXXXXXXMEEEEEE + detached: detached, + } + _, _, err := sig.Sign(payload, signer.signer, signer.key) + if err != nil { + return nil, fmt.Errorf(`failed to generate signature for signer #%d (alg=%s): %w`, i, signer.Algorithm(), err) + } + + result.signatures = append(result.signatures, sig) + } + + switch format { + case fmtJSON: + return json.Marshal(result) + case fmtJSONPretty: + return json.MarshalIndent(result, "", " ") + case fmtCompact: + // Take the only signature object, and convert it into a Compact + // serialization format + var compactOpts []CompactOption + if detached { + compactOpts = append(compactOpts, WithDetached(detached)) + } + return Compact(&result, compactOpts...) + default: + return nil, fmt.Errorf(`jws.Sign: invalid serialization format`) + } +} + +var allowNoneWhitelist = jwk.WhitelistFunc(func(string) bool { + return false +}) + +// Verify checks if the given JWS message is verifiable using `alg` and `key`. +// `key` may be a "raw" key (e.g. rsa.PublicKey) or a jwk.Key +// +// If the verification is successful, `err` is nil, and the content of the +// payload that was signed is returned. If you need more fine-grained +// control of the verification process, manually generate a +// `Verifier` in `verify` subpackage, and call `Verify` method on it. +// If you need to access signatures and JOSE headers in a JWS message, +// use `Parse` function to get `Message` object. +func Verify(buf []byte, options ...VerifyOption) ([]byte, error) { + var dst *Message + var detachedPayload []byte + var keyProviders []KeyProvider + var keyUsed interface{} + + ctx := context.Background() + + //nolint:forcetypeassert + for _, option := range options { + switch option.Ident() { + case identMessage{}: + dst = option.Value().(*Message) + case identDetachedPayload{}: + detachedPayload = option.Value().([]byte) + case identKey{}: + pair := option.Value().(*withKey) + alg, ok := pair.alg.(jwa.SignatureAlgorithm) + if !ok { + return nil, fmt.Errorf(`WithKey() option must be specified using jwa.SignatureAlgorithm (got %T)`, pair.alg) + } + keyProviders = append(keyProviders, &staticKeyProvider{ + alg: alg, + key: pair.key, + }) + case identKeyProvider{}: + keyProviders = append(keyProviders, option.Value().(KeyProvider)) + case identKeyUsed{}: + keyUsed = option.Value() + case identContext{}: + ctx = option.Value().(context.Context) + default: + return nil, fmt.Errorf(`invalid jws.VerifyOption %q passed`, `With`+strings.TrimPrefix(fmt.Sprintf(`%T`, option.Ident()), `jws.ident`)) + } + } + + if len(keyProviders) < 1 { + return nil, fmt.Errorf(`jws.Verify: no key providers have been provided (see jws.WithKey(), jws.WithKeySet(), jws.WithVerifyAuto(), and jws.WithKeyProvider()`) + } + + msg, err := Parse(buf) + if err != nil { + return nil, fmt.Errorf(`failed to parse jws: %w`, err) + } + defer msg.clearRaw() + + if detachedPayload != nil { + if len(msg.payload) != 0 { + return nil, fmt.Errorf(`can't specify detached payload for JWS with payload`) + } + + msg.payload = detachedPayload + } + + // Pre-compute the base64 encoded version of payload + var payload string + if msg.b64 { + payload = base64.EncodeToString(msg.payload) + } else { + payload = string(msg.payload) + } + + verifyBuf := pool.GetBytesBuffer() + defer pool.ReleaseBytesBuffer(verifyBuf) + + for i, sig := range msg.signatures { + verifyBuf.Reset() + + var encodedProtectedHeader string + if rbp, ok := sig.protected.(interface{ rawBuffer() []byte }); ok { + if raw := rbp.rawBuffer(); raw != nil { + encodedProtectedHeader = base64.EncodeToString(raw) + } + } + + if encodedProtectedHeader == "" { + protected, err := json.Marshal(sig.protected) + if err != nil { + return nil, fmt.Errorf(`failed to marshal "protected" for signature #%d: %w`, i+1, err) + } + + encodedProtectedHeader = base64.EncodeToString(protected) + } + + verifyBuf.WriteString(encodedProtectedHeader) + verifyBuf.WriteByte('.') + verifyBuf.WriteString(payload) + + for i, kp := range keyProviders { + var sink algKeySink + if err := kp.FetchKeys(ctx, &sink, sig, msg); err != nil { + return nil, fmt.Errorf(`key provider %d failed: %w`, i, err) + } + + for _, pair := range sink.list { + // alg is converted here because pair.alg is of type jwa.KeyAlgorithm. + // this may seem ugly, but we're trying to avoid declaring separate + // structs for `alg jwa.KeyAlgorithm` and `alg jwa.SignatureAlgorithm` + //nolint:forcetypeassert + alg := pair.alg.(jwa.SignatureAlgorithm) + key := pair.key + verifier, err := NewVerifier(alg) + if err != nil { + return nil, fmt.Errorf(`failed to create verifier for algorithm %q: %w`, alg, err) + } + + if err := verifier.Verify(verifyBuf.Bytes(), sig.signature, key); err != nil { + continue + } + + if keyUsed != nil { + if err := blackmagic.AssignIfCompatible(keyUsed, key); err != nil { + return nil, fmt.Errorf(`failed to assign used key (%T) to %T: %w`, key, keyUsed, err) + } + } + + if dst != nil { + *(dst) = *msg + } + + return msg.payload, nil + } + } + } + return nil, fmt.Errorf(`could not verify message using any of the signatures or keys`) +} + +// get the value of b64 header field. +// If the field does not exist, returns true (default) +// Otherwise return the value specified by the header field. +func getB64Value(hdr Headers) bool { + b64raw, ok := hdr.Get("b64") + if !ok { + return true // default + } + + b64, ok := b64raw.(bool) // default + if !ok { + return false + } + return b64 +} + +// This is an "optimized" io.ReadAll(). It will attempt to read +// all of the contents from the reader IF the reader is of a certain +// concrete type. +func readAll(rdr io.Reader) ([]byte, bool) { + switch rdr.(type) { + case *bytes.Reader, *bytes.Buffer, *strings.Reader: + data, err := io.ReadAll(rdr) + if err != nil { + return nil, false + } + return data, true + default: + return nil, false + } +} + +// Parse parses contents from the given source and creates a jws.Message +// struct. The input can be in either compact or full JSON serialization. +// +// Parse() currently does not take any options, but the API accepts it +// in anticipation of future addition. +func Parse(src []byte, _ ...ParseOption) (*Message, error) { + for i := 0; i < len(src); i++ { + r := rune(src[i]) + if r >= utf8.RuneSelf { + r, _ = utf8.DecodeRune(src) + } + if !unicode.IsSpace(r) { + if r == '{' { + return parseJSON(src) + } + return parseCompact(src) + } + } + return nil, fmt.Errorf(`invalid byte sequence`) +} + +// Parse parses contents from the given source and creates a jws.Message +// struct. The input can be in either compact or full JSON serialization. +func ParseString(src string) (*Message, error) { + return Parse([]byte(src)) +} + +// Parse parses contents from the given source and creates a jws.Message +// struct. The input can be in either compact or full JSON serialization. +func ParseReader(src io.Reader) (*Message, error) { + if data, ok := readAll(src); ok { + return Parse(data) + } + + rdr := bufio.NewReader(src) + var first rune + for { + r, _, err := rdr.ReadRune() + if err != nil { + return nil, fmt.Errorf(`failed to read rune: %w`, err) + } + if !unicode.IsSpace(r) { + first = r + if err := rdr.UnreadRune(); err != nil { + return nil, fmt.Errorf(`failed to unread rune: %w`, err) + } + + break + } + } + + var parser func(io.Reader) (*Message, error) + if first == '{' { + parser = parseJSONReader + } else { + parser = parseCompactReader + } + + m, err := parser(rdr) + if err != nil { + return nil, fmt.Errorf(`failed to parse jws message: %w`, err) + } + + return m, nil +} + +func parseJSONReader(src io.Reader) (result *Message, err error) { + var m Message + if err := json.NewDecoder(src).Decode(&m); err != nil { + return nil, fmt.Errorf(`failed to unmarshal jws message: %w`, err) + } + return &m, nil +} + +func parseJSON(data []byte) (result *Message, err error) { + var m Message + if err := json.Unmarshal(data, &m); err != nil { + return nil, fmt.Errorf(`failed to unmarshal jws message: %w`, err) + } + return &m, nil +} + +// SplitCompact splits a JWT and returns its three parts +// separately: protected headers, payload and signature. +func SplitCompact(src []byte) ([]byte, []byte, []byte, error) { + parts := bytes.Split(src, []byte(".")) + if len(parts) < 3 { + return nil, nil, nil, fmt.Errorf(`invalid number of segments`) + } + return parts[0], parts[1], parts[2], nil +} + +// SplitCompactString splits a JWT and returns its three parts +// separately: protected headers, payload and signature. +func SplitCompactString(src string) ([]byte, []byte, []byte, error) { + parts := strings.Split(src, ".") + if len(parts) < 3 { + return nil, nil, nil, fmt.Errorf(`invalid number of segments`) + } + return []byte(parts[0]), []byte(parts[1]), []byte(parts[2]), nil +} + +// SplitCompactReader splits a JWT and returns its three parts +// separately: protected headers, payload and signature. +func SplitCompactReader(rdr io.Reader) ([]byte, []byte, []byte, error) { + if data, ok := readAll(rdr); ok { + return SplitCompact(data) + } + + var protected []byte + var payload []byte + var signature []byte + var periods int + var state int + + buf := make([]byte, 4096) + var sofar []byte + + for { + // read next bytes + n, err := rdr.Read(buf) + // return on unexpected read error + if err != nil && err != io.EOF { + return nil, nil, nil, fmt.Errorf(`unexpected end of input: %w`, err) + } + + // append to current buffer + sofar = append(sofar, buf[:n]...) + // loop to capture multiple '.' in current buffer + for loop := true; loop; { + var i = bytes.IndexByte(sofar, '.') + if i == -1 && err != io.EOF { + // no '.' found -> exit and read next bytes (outer loop) + loop = false + continue + } else if i == -1 && err == io.EOF { + // no '.' found -> process rest and exit + i = len(sofar) + loop = false + } else { + // '.' found + periods++ + } + + // Reaching this point means we have found a '.' or EOF and process the rest of the buffer + switch state { + case 0: + protected = sofar[:i] + state++ + case 1: + payload = sofar[:i] + state++ + case 2: + signature = sofar[:i] + } + // Shorten current buffer + if len(sofar) > i { + sofar = sofar[i+1:] + } + } + // Exit on EOF + if err == io.EOF { + break + } + } + if periods != 2 { + return nil, nil, nil, fmt.Errorf(`invalid number of segments`) + } + + return protected, payload, signature, nil +} + +// parseCompactReader parses a JWS value serialized via compact serialization. +func parseCompactReader(rdr io.Reader) (m *Message, err error) { + protected, payload, signature, err := SplitCompactReader(rdr) + if err != nil { + return nil, fmt.Errorf(`invalid compact serialization format: %w`, err) + } + return parse(protected, payload, signature) +} + +func parseCompact(data []byte) (m *Message, err error) { + protected, payload, signature, err := SplitCompact(data) + if err != nil { + return nil, fmt.Errorf(`invalid compact serialization format: %w`, err) + } + return parse(protected, payload, signature) +} + +func parse(protected, payload, signature []byte) (*Message, error) { + decodedHeader, err := base64.Decode(protected) + if err != nil { + return nil, fmt.Errorf(`failed to decode protected headers: %w`, err) + } + + hdr := NewHeaders() + if err := json.Unmarshal(decodedHeader, hdr); err != nil { + return nil, fmt.Errorf(`failed to parse JOSE headers: %w`, err) + } + + var decodedPayload []byte + b64 := getB64Value(hdr) + if !b64 { + decodedPayload = payload + } else { + v, err := base64.Decode(payload) + if err != nil { + return nil, fmt.Errorf(`failed to decode payload: %w`, err) + } + decodedPayload = v + } + + decodedSignature, err := base64.Decode(signature) + if err != nil { + return nil, fmt.Errorf(`failed to decode signature: %w`, err) + } + + var msg Message + msg.payload = decodedPayload + msg.signatures = append(msg.signatures, &Signature{ + protected: hdr, + signature: decodedSignature, + }) + msg.b64 = b64 + return &msg, nil +} + +// RegisterCustomField allows users to specify that a private field +// be decoded as an instance of the specified type. This option has +// a global effect. +// +// For example, suppose you have a custom field `x-birthday`, which +// you want to represent as a string formatted in RFC3339 in JSON, +// but want it back as `time.Time`. +// +// In that case you would register a custom field as follows +// +// jwe.RegisterCustomField(`x-birthday`, timeT) +// +// Then `hdr.Get("x-birthday")` will still return an `interface{}`, +// but you can convert its type to `time.Time` +// +// bdayif, _ := hdr.Get(`x-birthday`) +// bday := bdayif.(time.Time) +// +func RegisterCustomField(name string, object interface{}) { + registry.Register(name, object) +} + +// Helpers for signature verification +var rawKeyToKeyType = make(map[reflect.Type]jwa.KeyType) +var keyTypeToAlgorithms = make(map[jwa.KeyType][]jwa.SignatureAlgorithm) + +func init() { + rawKeyToKeyType[reflect.TypeOf([]byte(nil))] = jwa.OctetSeq + rawKeyToKeyType[reflect.TypeOf(ed25519.PublicKey(nil))] = jwa.OKP + rawKeyToKeyType[reflect.TypeOf(rsa.PublicKey{})] = jwa.RSA + rawKeyToKeyType[reflect.TypeOf((*rsa.PublicKey)(nil))] = jwa.RSA + rawKeyToKeyType[reflect.TypeOf(ecdsa.PublicKey{})] = jwa.EC + rawKeyToKeyType[reflect.TypeOf((*ecdsa.PublicKey)(nil))] = jwa.EC + + addAlgorithmForKeyType(jwa.OKP, jwa.EdDSA) + for _, alg := range []jwa.SignatureAlgorithm{jwa.HS256, jwa.HS384, jwa.HS512} { + addAlgorithmForKeyType(jwa.OctetSeq, alg) + } + for _, alg := range []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512} { + addAlgorithmForKeyType(jwa.RSA, alg) + } + for _, alg := range []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512} { + addAlgorithmForKeyType(jwa.EC, alg) + } +} + +func addAlgorithmForKeyType(kty jwa.KeyType, alg jwa.SignatureAlgorithm) { + keyTypeToAlgorithms[kty] = append(keyTypeToAlgorithms[kty], alg) +} + +// AlgorithmsForKey returns the possible signature algorithms that can +// be used for a given key. It only takes in consideration keys/algorithms +// for verification purposes, as this is the only usage where one may need +// dynamically figure out which method to use. +func AlgorithmsForKey(key interface{}) ([]jwa.SignatureAlgorithm, error) { + var kty jwa.KeyType + switch key := key.(type) { + case jwk.Key: + kty = key.KeyType() + case rsa.PublicKey, *rsa.PublicKey, rsa.PrivateKey, *rsa.PrivateKey: + kty = jwa.RSA + case ecdsa.PublicKey, *ecdsa.PublicKey, ecdsa.PrivateKey, *ecdsa.PrivateKey: + kty = jwa.EC + case ed25519.PublicKey, ed25519.PrivateKey, x25519.PublicKey, x25519.PrivateKey: + kty = jwa.OKP + case []byte: + kty = jwa.OctetSeq + default: + return nil, fmt.Errorf(`invalid key %T`, key) + } + + algs, ok := keyTypeToAlgorithms[kty] + if !ok { + return nil, fmt.Errorf(`invalid key type %q`, kty) + } + return algs, nil +} diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jws/key_provider.go b/vendor/github.com/lestrrat-go/jwx/v2/jws/key_provider.go new file mode 100644 index 0000000..02fcded --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jws/key_provider.go @@ -0,0 +1,253 @@ +package jws + +import ( + "context" + "fmt" + "net/url" + "sync" + + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwk" +) + +// KeyProvider is responsible for providing key(s) to sign or verify a payload. +// Multiple `jws.KeyProvider`s can be passed to `jws.Verify()` or `jws.Sign()` +// +// `jws.Sign()` can only accept static key providers via `jws.WithKey()`, +// while `jws.Verify()` can accept `jws.WithKey()`, `jws.WithKeySet()`, +// `jws.WithVerifyAuto()`, and `jws.WithKeyProvider()`. +// +// Understanding how this works is crucial to learn how this package works. +// +// `jws.Sign()` is straightforward: signatures are created for each +// provided key. +// +// `jws.Verify()` is a bit more involved, because there are cases you +// will want to compute/deduce/guess the keys that you would like to +// use for verification. +// +// The first thing that `jws.Verify()` does is to collect the +// KeyProviders from the option list that the user provided (presented in pseudocode): +// +// keyProviders := filterKeyProviders(options) +// +// Then, remember that a JWS message may contain multiple signatures in the +// message. For each signature, we call on the KeyProviders to give us +// the key(s) to use on this signature: +// +// for sig in msg.Signatures { +// for kp in keyProviders { +// kp.FetcKeys(ctx, sink, sig, msg) +// ... +// } +// } +// +// The `sink` argument passed to the KeyProvider is a temporary storage +// for the keys (either a jwk.Key or a "raw" key). The `KeyProvider` +// is responsible for sending keys into the `sink`. +// +// When called, the `KeyProvider` created by `jws.WithKey()` sends the same key, +// `jws.WithKeySet()` sends keys that matches a particular `kid` and `alg`, +// `jws.WithVerifyAuto()` fetchs a JWK from the `jku` URL, +// and finally `jws.WithKeyProvider()` allows you to execute arbitrary +// logic to provide keys. If you are providing a custom `KeyProvider`, +// you should execute the necessary checks or retrieval of keys, and +// then send the key(s) to the sink: +// +// sink.Key(alg, key) +// +// These keys are then retrieved and tried for each signature, until +// a match is found: +// +// keys := sink.Keys() +// for key in keys { +// if givenSignature == makeSignatre(key, payload, ...)) { +// return OK +// } +// } +type KeyProvider interface { + FetchKeys(context.Context, KeySink, *Signature, *Message) error +} + +// KeySink is a data storage where `jws.KeyProvider` objects should +// send their keys to. +type KeySink interface { + Key(jwa.SignatureAlgorithm, interface{}) +} + +type algKeyPair struct { + alg jwa.KeyAlgorithm + key interface{} +} + +type algKeySink struct { + mu sync.Mutex + list []algKeyPair +} + +func (s *algKeySink) Key(alg jwa.SignatureAlgorithm, key interface{}) { + s.mu.Lock() + s.list = append(s.list, algKeyPair{alg, key}) + s.mu.Unlock() +} + +type staticKeyProvider struct { + alg jwa.SignatureAlgorithm + key interface{} +} + +func (kp *staticKeyProvider) FetchKeys(_ context.Context, sink KeySink, _ *Signature, _ *Message) error { + sink.Key(kp.alg, kp.key) + return nil +} + +type keySetProvider struct { + set jwk.Set + requireKid bool // true if `kid` must be specified + useDefault bool // true if the first key should be used iff there's exactly one key in set + inferAlgorithm bool // true if the algorithm should be inferred from key type +} + +func (kp *keySetProvider) selectKey(sink KeySink, key jwk.Key, sig *Signature, _ *Message) error { + if usage := key.KeyUsage(); usage != "" && usage != jwk.ForSignature.String() { + return nil + } + + if v := key.Algorithm(); v.String() != "" { + var alg jwa.SignatureAlgorithm + if err := alg.Accept(v); err != nil { + return fmt.Errorf(`invalid signature algorithm %s: %w`, key.Algorithm(), err) + } + + sink.Key(alg, key) + return nil + } + + if kp.inferAlgorithm { + algs, err := AlgorithmsForKey(key) + if err != nil { + return fmt.Errorf(`failed to get a list of signature methods for key type %s: %w`, key.KeyType(), err) + } + + // bail out if the JWT has a `alg` field, and it doesn't match + if tokAlg := sig.ProtectedHeaders().Algorithm(); tokAlg != "" { + for _, alg := range algs { + if tokAlg == alg { + sink.Key(alg, key) + return nil + } + } + return fmt.Errorf(`algorithm in the message does not match any of the inferred algorithms`) + } + + // Yes, you get to try them all!!!!!!! + for _, alg := range algs { + sink.Key(alg, key) + } + return nil + } + return nil +} + +func (kp *keySetProvider) FetchKeys(_ context.Context, sink KeySink, sig *Signature, msg *Message) error { + if kp.requireKid { + var key jwk.Key + + wantedKid := sig.ProtectedHeaders().KeyID() + if wantedKid == "" { + // If the kid is NOT specified... kp.useDefault needs to be true, and the + // JWKs must have exactly one key in it + if !kp.useDefault { + return fmt.Errorf(`failed to find matching key: no key ID ("kid") specified in token`) + } else if kp.useDefault && kp.set.Len() > 1 { + return fmt.Errorf(`failed to find matching key: no key ID ("kid") specified in token but multiple keys available in key set`) + } + + // if we got here, then useDefault == true AND there is exactly + // one key in the set. + key, _ = kp.set.Key(0) + } else { + // Otherwise we better be able to look up the key, baby. + v, ok := kp.set.LookupKeyID(wantedKid) + if !ok { + return fmt.Errorf(`failed to find key with key ID %q in key set`, wantedKid) + } + key = v + } + + return kp.selectKey(sink, key, sig, msg) + } + + for i := 0; i < kp.set.Len(); i++ { + key, _ := kp.set.Key(i) + if err := kp.selectKey(sink, key, sig, msg); err != nil { + continue + } + } + return nil +} + +type jkuProvider struct { + fetcher jwk.Fetcher + options []jwk.FetchOption +} + +func (kp jkuProvider) FetchKeys(ctx context.Context, sink KeySink, sig *Signature, _ *Message) error { + kid := sig.ProtectedHeaders().KeyID() + if kid == "" { + return fmt.Errorf(`use of "jku" requires that the payload contain a "kid" field in the protected header`) + } + + // errors here can't be reliablly passed to the consumers. + // it's unfortunate, but if you need this control, you are + // going to have to write your own fetcher + u := sig.ProtectedHeaders().JWKSetURL() + if u == "" { + return fmt.Errorf(`use of "jku" field specified, but the field is empty`) + } + uo, err := url.Parse(u) + if err != nil { + return fmt.Errorf(`failed to parse "jku": %w`, err) + } + if uo.Scheme != "https" { + return fmt.Errorf(`url in "jku" must be HTTPS`) + } + + set, err := kp.fetcher.Fetch(ctx, u, kp.options...) + if err != nil { + return fmt.Errorf(`failed to fetch %q: %w`, u, err) + } + + key, ok := set.LookupKeyID(kid) + if !ok { + // It is not an error if the key with the kid doesn't exist + return nil + } + + algs, err := AlgorithmsForKey(key) + if err != nil { + return fmt.Errorf(`failed to get a list of signature methods for key type %s: %w`, key.KeyType(), err) + } + + hdrAlg := sig.ProtectedHeaders().Algorithm() + for _, alg := range algs { + // if we have a "alg" field in the JWS, we can only proceed if + // the inferred algorithm matches + if hdrAlg != "" && hdrAlg != alg { + continue + } + + sink.Key(alg, key) + break + } + return nil +} + +// KeyProviderFunc is a type of KeyProvider that is implemented by +// a single function. You can use this to create ad-hoc `KeyProvider` +// instances. +type KeyProviderFunc func(context.Context, KeySink, *Signature, *Message) error + +func (kp KeyProviderFunc) FetchKeys(ctx context.Context, sink KeySink, sig *Signature, msg *Message) error { + return kp(ctx, sink, sig, msg) +} diff --git a/vendor/github.com/lestrrat-go/jwx/jws/message.go b/vendor/github.com/lestrrat-go/jwx/v2/jws/message.go similarity index 72% rename from vendor/github.com/lestrrat-go/jwx/jws/message.go rename to vendor/github.com/lestrrat-go/jwx/v2/jws/message.go index 802b297..e028422 100644 --- a/vendor/github.com/lestrrat-go/jwx/jws/message.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jws/message.go @@ -3,20 +3,14 @@ package jws import ( "bytes" "context" + "fmt" - "github.com/lestrrat-go/jwx/internal/base64" - "github.com/lestrrat-go/jwx/internal/json" - "github.com/lestrrat-go/jwx/internal/pool" - "github.com/lestrrat-go/jwx/jwk" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/internal/base64" + "github.com/lestrrat-go/jwx/v2/internal/json" + "github.com/lestrrat-go/jwx/v2/internal/pool" + "github.com/lestrrat-go/jwx/v2/jwk" ) -type collectRawCtx struct{} - -func (collectRawCtx) CollectRaw() bool { - return true -} - func NewSignature() *Signature { return &Signature{} } @@ -66,7 +60,7 @@ func (s *Signature) UnmarshalJSON(data []byte) error { var sup signatureUnmarshalProbe sup.Header = NewHeaders() if err := json.Unmarshal(data, &sup); err != nil { - return errors.Wrap(err, `failed to unmarshal signature into temporary struct`) + return fmt.Errorf(`failed to unmarshal signature into temporary struct: %w`, err) } s.headers = sup.Header @@ -75,7 +69,7 @@ func (s *Signature) UnmarshalJSON(data []byte) error { if !bytes.HasPrefix(src, []byte{'{'}) { decoded, err := base64.Decode(src) if err != nil { - return errors.Wrap(err, `failed to base64 decode protected headers`) + return fmt.Errorf(`failed to base64 decode protected headers: %w`, err) } src = decoded } @@ -84,7 +78,7 @@ func (s *Signature) UnmarshalJSON(data []byte) error { //nolint:forcetypeassert prt.(*stdHeaders).SetDecodeCtx(s.DecodeCtx()) if err := json.Unmarshal(src, prt); err != nil { - return errors.Wrap(err, `failed to unmarshal protected headers`) + return fmt.Errorf(`failed to unmarshal protected headers: %w`, err) } //nolint:forcetypeassert prt.(*stdHeaders).SetDecodeCtx(nil) @@ -93,7 +87,7 @@ func (s *Signature) UnmarshalJSON(data []byte) error { decoded, err := base64.DecodeString(*sup.Signature) if err != nil { - return errors.Wrap(err, `failed to base decode signature`) + return fmt.Errorf(`failed to base decode signature: %w`, err) } s.signature = decoded return nil @@ -111,11 +105,11 @@ func (s *Signature) Sign(payload []byte, signer Signer, key interface{}) ([]byte hdrs, err := mergeHeaders(ctx, s.headers, s.protected) if err != nil { - return nil, nil, errors.Wrap(err, `failed to merge headers`) + return nil, nil, fmt.Errorf(`failed to merge headers: %w`, err) } if err := hdrs.Set(AlgorithmKey, signer.Algorithm()); err != nil { - return nil, nil, errors.Wrap(err, `failed to set "alg"`) + return nil, nil, fmt.Errorf(`failed to set "alg": %w`, err) } // If the key is a jwk.Key instance, obtain the raw key @@ -123,13 +117,13 @@ func (s *Signature) Sign(payload []byte, signer Signer, key interface{}) ([]byte // If we have a key ID specified by this jwk.Key, use that in the header if kid := jwkKey.KeyID(); kid != "" { if err := hdrs.Set(jwk.KeyIDKey, kid); err != nil { - return nil, nil, errors.Wrap(err, `set key ID from jwk.Key`) + return nil, nil, fmt.Errorf(`set key ID from jwk.Key: %w`, err) } } } hdrbuf, err := json.Marshal(hdrs) if err != nil { - return nil, nil, errors.Wrap(err, `failed to marshal headers`) + return nil, nil, fmt.Errorf(`failed to marshal headers: %w`, err) } buf := pool.GetBytesBuffer() @@ -147,7 +141,7 @@ func (s *Signature) Sign(payload []byte, signer Signer, key interface{}) ([]byte } else { if !s.detached { if bytes.Contains(payload, []byte{'.'}) { - return nil, nil, errors.New(`payload must not contain a "."`) + return nil, nil, fmt.Errorf(`payload must not contain a "."`) } } plen = len(payload) @@ -156,7 +150,7 @@ func (s *Signature) Sign(payload []byte, signer Signer, key interface{}) ([]byte signature, err := signer.Sign(buf.Bytes(), key) if err != nil { - return nil, nil, errors.Wrap(err, `failed to sign payload`) + return nil, nil, fmt.Errorf(`failed to sign payload: %w`, err) } s.signature = signature @@ -264,13 +258,13 @@ func (m *Message) UnmarshalJSON(buf []byte) error { var mup messageUnmarshalProbe mup.Header = NewHeaders() if err := json.Unmarshal(buf, &mup); err != nil { - return errors.Wrap(err, `failed to unmarshal into temporary structure`) + return fmt.Errorf(`failed to unmarshal into temporary structure: %w`, err) } b64 := true if mup.Signature == nil { // flattened signature is NOT present if len(mup.Signatures) == 0 { - return errors.New(`required field "signatures" not present`) + return fmt.Errorf(`required field "signatures" not present`) } m.signatures = make([]*Signature, 0, len(mup.Signatures)) @@ -278,7 +272,7 @@ func (m *Message) UnmarshalJSON(buf []byte) error { var sig Signature sig.SetDecodeCtx(m.DecodeCtx()) if err := json.Unmarshal(rawsig, &sig); err != nil { - return errors.Wrapf(err, `failed to unmarshal signature #%d`, i+1) + return fmt.Errorf(`failed to unmarshal signature #%d: %w`, i+1, err) } sig.SetDecodeCtx(nil) @@ -288,7 +282,7 @@ func (m *Message) UnmarshalJSON(buf []byte) error { } } else { if b64 != getB64Value(sig.protected) { - return errors.Errorf(`b64 value must be the same for all signatures`) + return fmt.Errorf(`b64 value must be the same for all signatures`) } } @@ -296,7 +290,7 @@ func (m *Message) UnmarshalJSON(buf []byte) error { } } else { // .signature is present, it's a flattened structure if len(mup.Signatures) != 0 { - return errors.New(`invalid format ("signatures" and "signature" keys cannot both be present)`) + return fmt.Errorf(`invalid format ("signatures" and "signature" keys cannot both be present)`) } var sig Signature @@ -304,13 +298,13 @@ func (m *Message) UnmarshalJSON(buf []byte) error { if src := mup.Protected; src != nil { decoded, err := base64.DecodeString(*src) if err != nil { - return errors.Wrap(err, `failed to base64 decode flattened protected headers`) + return fmt.Errorf(`failed to base64 decode flattened protected headers: %w`, err) } prt := NewHeaders() //nolint:forcetypeassert prt.(*stdHeaders).SetDecodeCtx(m.DecodeCtx()) if err := json.Unmarshal(decoded, prt); err != nil { - return errors.Wrap(err, `failed to unmarshal flattened protected headers`) + return fmt.Errorf(`failed to unmarshal flattened protected headers: %w`, err) } //nolint:forcetypeassert prt.(*stdHeaders).SetDecodeCtx(nil) @@ -319,7 +313,7 @@ func (m *Message) UnmarshalJSON(buf []byte) error { decoded, err := base64.DecodeString(*mup.Signature) if err != nil { - return errors.Wrap(err, `failed to base64 decode flattened signature`) + return fmt.Errorf(`failed to base64 decode flattened signature: %w`, err) } sig.signature = decoded @@ -333,7 +327,7 @@ func (m *Message) UnmarshalJSON(buf []byte) error { } else { decoded, err := base64.DecodeString(*mup.Payload) if err != nil { - return errors.Wrap(err, `failed to base64 decode payload`) + return fmt.Errorf(`failed to base64 decode payload: %w`, err) } m.payload = decoded } @@ -361,7 +355,7 @@ func (m Message) marshalFlattened() ([]byte, error) { if hdr := sig.headers; hdr != nil { hdrjs, err := hdr.MarshalJSON() if err != nil { - return nil, errors.Wrap(err, `failed to marshal "header" (flattened format)`) + return nil, fmt.Errorf(`failed to marshal "header" (flattened format): %w`, err) } buf.WriteString(`"header":`) buf.Write(hdrjs) @@ -378,7 +372,7 @@ func (m Message) marshalFlattened() ([]byte, error) { if protected := sig.protected; protected != nil { protectedbuf, err := protected.MarshalJSON() if err != nil { - return nil, errors.Wrap(err, `failed to marshal "protected" (flattened format)`) + return nil, fmt.Errorf(`failed to marshal "protected" (flattened format): %w`, err) } buf.WriteString(`,"protected":"`) buf.WriteString(base64.EncodeToString(protectedbuf)) @@ -412,7 +406,7 @@ func (m Message) marshalFull() ([]byte, error) { if hdr := sig.headers; hdr != nil { hdrbuf, err := hdr.MarshalJSON() if err != nil { - return nil, errors.Wrapf(err, `failed to marshal "header" for signature #%d`, i+1) + return nil, fmt.Errorf(`failed to marshal "header" for signature #%d: %w`, i+1, err) } buf.WriteString(`"header":`) buf.Write(hdrbuf) @@ -422,7 +416,7 @@ func (m Message) marshalFull() ([]byte, error) { if protected := sig.protected; protected != nil { protectedbuf, err := protected.MarshalJSON() if err != nil { - return nil, errors.Wrapf(err, `failed to marshal "protected" for signature #%d`, i+1) + return nil, fmt.Errorf(`failed to marshal "protected" for signature #%d: %w`, i+1, err) } if wrote { buf.WriteRune(',') @@ -446,3 +440,58 @@ func (m Message) marshalFull() ([]byte, error) { copy(ret, buf.Bytes()) return ret, nil } + +// Compact generates a JWS message in compact serialization format from +// `*jws.Message` object. The object contain exactly one signature, or +// an error is returned. +// +// If using a detached payload, the payload must already be stored in +// the `*jws.Message` object, and the `jws.WithDetached()` option +// must be passed to the function. +func Compact(msg *Message, options ...CompactOption) ([]byte, error) { + if l := len(msg.signatures); l != 1 { + return nil, fmt.Errorf(`jws.Compact: cannot serialize message with %d signatures (must be one)`, l) + } + + var detached bool + for _, option := range options { + //nolint:forcetypeassert + switch option.Ident() { + case identDetached{}: + detached = option.Value().(bool) + } + } + + s := msg.signatures[0] + // XXX check if this is correct + hdrs := s.ProtectedHeaders() + + hdrbuf, err := json.Marshal(hdrs) + if err != nil { + return nil, fmt.Errorf(`jws.Compress: failed to marshal headers: %w`, err) + } + + buf := pool.GetBytesBuffer() + defer pool.ReleaseBytesBuffer(buf) + + buf.WriteString(base64.EncodeToString(hdrbuf)) + buf.WriteByte('.') + + if !detached { + if getB64Value(hdrs) { + encoded := base64.EncodeToString(msg.payload) + buf.WriteString(encoded) + } else { + if bytes.Contains(msg.payload, []byte{'.'}) { + return nil, fmt.Errorf(`jws.Compress: payload must not contain a "."`) + } + buf.Write(msg.payload) + } + } + + buf.WriteByte('.') + buf.WriteString(base64.EncodeToString(s.signature)) + ret := make([]byte, buf.Len()) + copy(ret, buf.Bytes()) + return ret, nil +} diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jws/options.go b/vendor/github.com/lestrrat-go/jwx/v2/jws/options.go new file mode 100644 index 0000000..ea5972e --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jws/options.go @@ -0,0 +1,156 @@ +package jws + +import ( + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwk" + "github.com/lestrrat-go/option" +) + +type identHeaders struct{} + +// WithHeaders allows you to specify extra header values to include in the +// final JWS message +func WithHeaders(h Headers) SignOption { + return &signOption{option.New(identHeaders{}, h)} +} + +// WithJSON specifies that the result of `jws.Sign()` is serialized in +// JSON format. +// +// If you pass multiple keys to `jws.Sign()`, it will fail unless +// you also pass this option. +func WithJSON(options ...WithJSONSuboption) SignOption { + var pretty bool + for _, option := range options { + //nolint:forcetypeassert + switch option.Ident() { + case identPretty{}: + pretty = option.Value().(bool) + } + } + + format := fmtJSON + if pretty { + format = fmtJSONPretty + } + return &signOption{option.New(identSerialization{}, format)} +} + +type withKey struct { + alg jwa.KeyAlgorithm + key interface{} + protected Headers + public Headers +} + +// This exist as escape hatches to modify the header values after the fact +func (w *withKey) Protected(v Headers) Headers { + if w.protected == nil && v != nil { + w.protected = v + } + return w.protected +} + +// WithKey is used to pass a static algorithm/key pair to either `jws.Sign()` or `jws.Verify()`. +// +// The `alg` parameter is the identifier for the signature algorithm that should be used. +// It is of type `jwa.KeyAlgorithm` but in reality you can only pass `jwa.SignatureAlgorithm` +// types. It is this way so that the value in `(jwk.Key).Algorithm()` can be directly +// passed to the option. If you specify other algorithm types such as `jwa.ContentEncryptionAlgorithm`, +// then you will get an error when `jws.Sign()` or `jws.Verify()` is executed. +// +// The algorithm specified in the `alg` parameter must be able to support +// the type of key you provided, otherwise an error is returned. +// +// Any of the followin is accepted for the `key` parameter: +// * A "raw" key (e.g. rsa.PrivateKey, ecdsa.PrivateKey, etc) +// * A crypto.Signer +// * A jwk.Key +// +// A `crypto.Signer` is used when the private part of a key is +// kept in an inaccessible location, such as hardware. +// `crypto.Signer` is currently supported for RSA, ECDSA, and EdDSA +// family of algorithms. You may consider using `github.com/jwx-go/crypto-signer` +// if you would like to use keys stored in GCP/AWS KMS services. +// +// If the key is a jwk.Key and the key contains a key ID (`kid` field), +// then it is added to the protected header generated by the signature. +// +// `jws.WithKey()` can furher accept suboptions to change signing behavior +// when used with `jws.Sign()`. `jws.WithProtected()` and `jws.WithPublic()` +// can be passed to specify JWS headers that should be used whe signing. +// +// If the protected headers contain "b64" field, then the boolean value for the field +// is respected when serializing. That is, if you specify a header with +// `{"b64": false}`, then the payload is not base64 encoded. +// +// These suboptions are ignored whe the `jws.WithKey()` option is used with `jws.Verify()`. +func WithKey(alg jwa.KeyAlgorithm, key interface{}, options ...WithKeySuboption) SignVerifyOption { + // Implementation note: this option is shared between Sign() and + // Verify(). As such we don't create a KeyProvider here because + // if used in Sign() we would be doing something else. + var protected, public Headers + for _, option := range options { + //nolint:forcetypeassert + switch option.Ident() { + case identProtectedHeaders{}: + protected = option.Value().(Headers) + case identPublicHeaders{}: + public = option.Value().(Headers) + } + } + + return &signVerifyOption{ + option.New(identKey{}, &withKey{ + alg: alg, + key: key, + protected: protected, + public: public, + }), + } +} + +// WithKeySet specifies a JWKS (jwk.Set) to use for verification. +// +// By default both `alg` and `kid` fields in the JWS _and_ the +// key must match for a key in the JWKS to be considered to be used. +// +// The behavior can be tweaked by using the `jws.WithKeySetSuboption` +// suboption types. +func WithKeySet(set jwk.Set, options ...WithKeySetSuboption) VerifyOption { + requireKid := true + var useDefault, inferAlgorithm bool + for _, option := range options { + //nolint:forcetypeassert + switch option.Ident() { + case identRequireKid{}: + requireKid = option.Value().(bool) + case identUseDefault{}: + useDefault = option.Value().(bool) + case identInferAlgorithmFromKey{}: + inferAlgorithm = option.Value().(bool) + } + } + + return WithKeyProvider(&keySetProvider{ + set: set, + requireKid: requireKid, + useDefault: useDefault, + inferAlgorithm: inferAlgorithm, + }) +} + +func WithVerifyAuto(f jwk.Fetcher, options ...jwk.FetchOption) VerifyOption { + if f == nil { + f = jwk.FetchFunc(jwk.Fetch) + } + + // the option MUST start with a "disallow no whitelist" to force + // users provide a whitelist + options = append(append([]jwk.FetchOption(nil), jwk.WithFetchWhitelist(allowNoneWhitelist)), options...) + + return WithKeyProvider(jkuProvider{ + fetcher: f, + options: options, + }) +} diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jws/options.yaml b/vendor/github.com/lestrrat-go/jwx/v2/jws/options.yaml new file mode 100644 index 0000000..7e2fdfb --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jws/options.yaml @@ -0,0 +1,158 @@ +package_name: jws +output: jws/options_gen.go +interfaces: + - name: CompactOption + comment: | + CompactOption describes options that can be passed to `jws.Compact` + - name: VerifyOption + comment: | + VerifyOption describes options that can be passed to `jws.Verify` + - name: SignOption + comment: | + SignOption describes options that can be passed to `jws.Sign` + - name: SignVerifyOption + methods: + - signOption + - verifyOption + comment: | + SignVerifyOption describes options that can be passed to either `jws.Verify` or `jws.Sign` + - name: WithJSONSuboption + concrete_type: withJSONSuboption + comment: | + JSONSuboption describes suboptions that can be passed to `jws.WithJSON()` option + - name: WithKeySuboption + comment: | + WithKeySuboption describes option types that can be passed to the `jws.WithKey()` + option. + - name: WithKeySetSuboption + comment: | + WithKeySetSuboption is a suboption passed to the `jws.WithKeySet()` option + - name: ParseOption + methods: + - readFileOption + comment: | + ReadFileOption is a type of `Option` that can be passed to `jwe.Parse` + - name: ReadFileOption + comment: | + ReadFileOption is a type of `Option` that can be passed to `jws.ReadFile` +options: + - ident: Key + skip_option: true + - ident: Serialization + skip_option: true + - ident: Serialization + option_name: WithCompact + interface: SignOption + constant_value: fmtCompact + comment: | + WithCompact specifies that the result of `jws.Sign()` is serialized in + compact format. + + By default `jws.Sign()` will opt to use compact format, so you usually + do not need to specify this option other than to be explicit about it + - ident: Detached + interface: CompactOption + argument_type: bool + comment: | + WithDetached specifies that the `jws.Message` should be serialized in + JWS compact serialization with detached payload. The resulting octet + sequence will not contain the payload section. + - ident: DetachedPayload + interface: SignVerifyOption + argument_type: '[]byte' + comment: | + WithDetachedPayload can be used to both sign or verify a JWS message with a + detached payload. + + When this option is used for `jws.Sign()`, the first parameter (normally the payload) + must be set to `nil`. + + If you have to verify using this option, you should know exactly how and why this works. + - ident: Message + interface: VerifyOption + argument_type: '*Message' + comment: | + WithMessage can be passed to Verify() to obtain the jws.Message upon + a successful verification. + - ident: KeyUsed + interface: VerifyOption + argument_type: 'interface{}' + comment: | + WithKeyUsed allows you to specify the `jws.Verify()` function to + return the key used for verification. This may be useful when + you specify multiple key sources or if you pass a `jwk.Set` + and you want to know which key was successful at verifying the + signature. + + `v` must be a pointer to an empty `interface{}`. Do not use + `jwk.Key` here unless you are 100% sure that all keys that you + have provided are instances of `jwk.Key` (remember that the + jwx API allows users to specify a raw key such as *rsa.PublicKey) + - ident: InferAlgorithmFromKey + interface: WithKeySetSuboption + argument_type: bool + comment: | + WithInferAlgorithmFromKey specifies whether the JWS signing algorithm name + should be inferred by looking at the provided key, in case the JWS + message or the key does not have a proper `alg` header. + + Compared to providing explicit `alg` from the key this is slower, and + verification may fail to verify if some how our heuristics are wrong + or outdated. + + Also, automatic detection of signature verification methods are always + more vulnerable for potential attack vectors. + + It is highly recommended that you fix your key to contain a proper `alg` + header field instead of resorting to using this option, but sometimes + it just needs to happen. + - ident: UseDefault + interface: WithKeySetSuboption + argument_type: bool + comment: | + WithUseDefault specifies that if and only if a jwk.Key contains + exactly one jwk.Key, that tkey should be used. + (I think this should be removed) + - ident: RequireKid + interface: WithKeySetSuboption + argument_type: bool + comment: | + WithrequiredKid specifies whether the keys in the jwk.Set should + only be matched if the target JWS message's Key ID and the Key ID + in the given key matches. + - ident: Pretty + interface: WithJSONSuboption + argument_type: bool + comment: | + WithPretty specifies whether the JSON output should be formatted and + indented + - ident: KeyProvider + interface: VerifyOption + argument_type: KeyProvider + - ident: Context + interface: VerifyOption + argument_type: context.Context + - ident: ProtectedHeaders + interface: WithKeySuboption + argument_type: Headers + comment: | + WithProtected is used with `jws.WithKey()` option when used with `jws.Sign()` + to specify a protected header to be attached to the JWS signature. + + It has no effect if used when `jws.WithKey()` is passed to `jws.Verify()` + - ident: PublicHeaders + interface: WithKeySuboption + argument_type: Headers + comment: | + WithPublic is used with `jws.WithKey()` option when used with `jws.Sign()` + to specify a public header to be attached to the JWS signature. + + It has no effect if used when `jws.WithKey()` is passed to `jws.Verify()` + + `jws.Sign()` will result in an error if `jws.WithPublic()` is used + and the serialization format is compact serialization. + - ident: FS + interface: ReadFileOption + argument_type: fs.FS + comment: | + WithFS specifies the source `fs.FS` object to read the file from. diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jws/options_gen.go b/vendor/github.com/lestrrat-go/jwx/v2/jws/options_gen.go new file mode 100644 index 0000000..8587487 --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jws/options_gen.go @@ -0,0 +1,317 @@ +// This file is auto-generated by internal/cmd/genoptions/main.go. DO NOT EDIT + +package jws + +import ( + "context" + "io/fs" + + "github.com/lestrrat-go/option" +) + +type Option = option.Interface + +// CompactOption describes options that can be passed to `jws.Compact` +type CompactOption interface { + Option + compactOption() +} + +type compactOption struct { + Option +} + +func (*compactOption) compactOption() {} + +// ReadFileOption is a type of `Option` that can be passed to `jwe.Parse` +type ParseOption interface { + Option + readFileOption() +} + +type parseOption struct { + Option +} + +func (*parseOption) readFileOption() {} + +// ReadFileOption is a type of `Option` that can be passed to `jws.ReadFile` +type ReadFileOption interface { + Option + readFileOption() +} + +type readFileOption struct { + Option +} + +func (*readFileOption) readFileOption() {} + +// SignOption describes options that can be passed to `jws.Sign` +type SignOption interface { + Option + signOption() +} + +type signOption struct { + Option +} + +func (*signOption) signOption() {} + +// SignVerifyOption describes options that can be passed to either `jws.Verify` or `jws.Sign` +type SignVerifyOption interface { + Option + signOption() + verifyOption() +} + +type signVerifyOption struct { + Option +} + +func (*signVerifyOption) signOption() {} + +func (*signVerifyOption) verifyOption() {} + +// VerifyOption describes options that can be passed to `jws.Verify` +type VerifyOption interface { + Option + verifyOption() +} + +type verifyOption struct { + Option +} + +func (*verifyOption) verifyOption() {} + +// JSONSuboption describes suboptions that can be passed to `jws.WithJSON()` option +type WithJSONSuboption interface { + Option + withJSONSuboption() +} + +type withJSONSuboption struct { + Option +} + +func (*withJSONSuboption) withJSONSuboption() {} + +// WithKeySetSuboption is a suboption passed to the `jws.WithKeySet()` option +type WithKeySetSuboption interface { + Option + withKeySetSuboption() +} + +type withKeySetSuboption struct { + Option +} + +func (*withKeySetSuboption) withKeySetSuboption() {} + +// WithKeySuboption describes option types that can be passed to the `jws.WithKey()` +// option. +type WithKeySuboption interface { + Option + withKeySuboption() +} + +type withKeySuboption struct { + Option +} + +func (*withKeySuboption) withKeySuboption() {} + +type identContext struct{} +type identDetached struct{} +type identDetachedPayload struct{} +type identFS struct{} +type identInferAlgorithmFromKey struct{} +type identKey struct{} +type identKeyProvider struct{} +type identKeyUsed struct{} +type identMessage struct{} +type identPretty struct{} +type identProtectedHeaders struct{} +type identPublicHeaders struct{} +type identRequireKid struct{} +type identSerialization struct{} +type identUseDefault struct{} + +func (identContext) String() string { + return "WithContext" +} + +func (identDetached) String() string { + return "WithDetached" +} + +func (identDetachedPayload) String() string { + return "WithDetachedPayload" +} + +func (identFS) String() string { + return "WithFS" +} + +func (identInferAlgorithmFromKey) String() string { + return "WithInferAlgorithmFromKey" +} + +func (identKey) String() string { + return "WithKey" +} + +func (identKeyProvider) String() string { + return "WithKeyProvider" +} + +func (identKeyUsed) String() string { + return "WithKeyUsed" +} + +func (identMessage) String() string { + return "WithMessage" +} + +func (identPretty) String() string { + return "WithPretty" +} + +func (identProtectedHeaders) String() string { + return "WithProtectedHeaders" +} + +func (identPublicHeaders) String() string { + return "WithPublicHeaders" +} + +func (identRequireKid) String() string { + return "WithRequireKid" +} + +func (identSerialization) String() string { + return "WithSerialization" +} + +func (identUseDefault) String() string { + return "WithUseDefault" +} + +func WithContext(v context.Context) VerifyOption { + return &verifyOption{option.New(identContext{}, v)} +} + +// WithDetached specifies that the `jws.Message` should be serialized in +// JWS compact serialization with detached payload. The resulting octet +// sequence will not contain the payload section. +func WithDetached(v bool) CompactOption { + return &compactOption{option.New(identDetached{}, v)} +} + +// WithDetachedPayload can be used to both sign or verify a JWS message with a +// detached payload. +// +// When this option is used for `jws.Sign()`, the first parameter (normally the payload) +// must be set to `nil`. +// +// If you have to verify using this option, you should know exactly how and why this works. +func WithDetachedPayload(v []byte) SignVerifyOption { + return &signVerifyOption{option.New(identDetachedPayload{}, v)} +} + +// WithFS specifies the source `fs.FS` object to read the file from. +func WithFS(v fs.FS) ReadFileOption { + return &readFileOption{option.New(identFS{}, v)} +} + +// WithInferAlgorithmFromKey specifies whether the JWS signing algorithm name +// should be inferred by looking at the provided key, in case the JWS +// message or the key does not have a proper `alg` header. +// +// Compared to providing explicit `alg` from the key this is slower, and +// verification may fail to verify if some how our heuristics are wrong +// or outdated. +// +// Also, automatic detection of signature verification methods are always +// more vulnerable for potential attack vectors. +// +// It is highly recommended that you fix your key to contain a proper `alg` +// header field instead of resorting to using this option, but sometimes +// it just needs to happen. +func WithInferAlgorithmFromKey(v bool) WithKeySetSuboption { + return &withKeySetSuboption{option.New(identInferAlgorithmFromKey{}, v)} +} + +func WithKeyProvider(v KeyProvider) VerifyOption { + return &verifyOption{option.New(identKeyProvider{}, v)} +} + +// WithKeyUsed allows you to specify the `jws.Verify()` function to +// return the key used for verification. This may be useful when +// you specify multiple key sources or if you pass a `jwk.Set` +// and you want to know which key was successful at verifying the +// signature. +// +// `v` must be a pointer to an empty `interface{}`. Do not use +// `jwk.Key` here unless you are 100% sure that all keys that you +// have provided are instances of `jwk.Key` (remember that the +// jwx API allows users to specify a raw key such as *rsa.PublicKey) +func WithKeyUsed(v interface{}) VerifyOption { + return &verifyOption{option.New(identKeyUsed{}, v)} +} + +// WithMessage can be passed to Verify() to obtain the jws.Message upon +// a successful verification. +func WithMessage(v *Message) VerifyOption { + return &verifyOption{option.New(identMessage{}, v)} +} + +// WithPretty specifies whether the JSON output should be formatted and +// indented +func WithPretty(v bool) WithJSONSuboption { + return &withJSONSuboption{option.New(identPretty{}, v)} +} + +// WithProtected is used with `jws.WithKey()` option when used with `jws.Sign()` +// to specify a protected header to be attached to the JWS signature. +// +// It has no effect if used when `jws.WithKey()` is passed to `jws.Verify()` +func WithProtectedHeaders(v Headers) WithKeySuboption { + return &withKeySuboption{option.New(identProtectedHeaders{}, v)} +} + +// WithPublic is used with `jws.WithKey()` option when used with `jws.Sign()` +// to specify a public header to be attached to the JWS signature. +// +// It has no effect if used when `jws.WithKey()` is passed to `jws.Verify()` +// +// `jws.Sign()` will result in an error if `jws.WithPublic()` is used +// and the serialization format is compact serialization. +func WithPublicHeaders(v Headers) WithKeySuboption { + return &withKeySuboption{option.New(identPublicHeaders{}, v)} +} + +// WithrequiredKid specifies whether the keys in the jwk.Set should +// only be matched if the target JWS message's Key ID and the Key ID +// in the given key matches. +func WithRequireKid(v bool) WithKeySetSuboption { + return &withKeySetSuboption{option.New(identRequireKid{}, v)} +} + +// WithCompact specifies that the result of `jws.Sign()` is serialized in +// compact format. +// +// By default `jws.Sign()` will opt to use compact format, so you usually +// do not need to specify this option other than to be explicit about it +func WithCompact() SignOption { + return &signOption{option.New(identSerialization{}, fmtCompact)} +} + +// WithUseDefault specifies that if and only if a jwk.Key contains +// exactly one jwk.Key, that tkey should be used. +// (I think this should be removed) +func WithUseDefault(v bool) WithKeySetSuboption { + return &withKeySetSuboption{option.New(identUseDefault{}, v)} +} diff --git a/vendor/github.com/lestrrat-go/jwx/jws/rsa.go b/vendor/github.com/lestrrat-go/jwx/v2/jws/rsa.go similarity index 80% rename from vendor/github.com/lestrrat-go/jwx/jws/rsa.go rename to vendor/github.com/lestrrat-go/jwx/v2/jws/rsa.go index 2e3b02a..e239330 100644 --- a/vendor/github.com/lestrrat-go/jwx/jws/rsa.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jws/rsa.go @@ -4,10 +4,10 @@ import ( "crypto" "crypto/rand" "crypto/rsa" + "fmt" - "github.com/lestrrat-go/jwx/internal/keyconv" - "github.com/lestrrat-go/jwx/jwa" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/internal/keyconv" + "github.com/lestrrat-go/jwx/v2/jwa" ) var rsaSigners map[jwa.SignatureAlgorithm]*rsaSigner @@ -73,21 +73,21 @@ func (rs *rsaSigner) Algorithm() jwa.SignatureAlgorithm { func (rs *rsaSigner) Sign(payload []byte, key interface{}) ([]byte, error) { if key == nil { - return nil, errors.New(`missing private key while signing payload`) + return nil, fmt.Errorf(`missing private key while signing payload`) } signer, ok := key.(crypto.Signer) if !ok { var privkey rsa.PrivateKey if err := keyconv.RSAPrivateKey(&privkey, key); err != nil { - return nil, errors.Wrapf(err, `failed to retrieve rsa.PrivateKey out of %T`, key) + return nil, fmt.Errorf(`failed to retrieve rsa.PrivateKey out of %T: %w`, key, err) } signer = &privkey } h := rs.hash.New() if _, err := h.Write(payload); err != nil { - return nil, errors.Wrap(err, "failed to write payload to hash") + return nil, fmt.Errorf(`failed to write payload to hash: %w`, err) } if rs.pss { return signer.Sign(rand.Reader, h.Sum(nil), &rsa.PSSOptions{ @@ -110,7 +110,7 @@ func newRSAVerifier(alg jwa.SignatureAlgorithm) Verifier { func (rv *rsaVerifier) Verify(payload, signature []byte, key interface{}) error { if key == nil { - return errors.New(`missing public key while verifying payload`) + return fmt.Errorf(`missing public key while verifying payload`) } var pubkey rsa.PublicKey @@ -122,17 +122,17 @@ func (rv *rsaVerifier) Verify(payload, signature []byte, key interface{}) error case *rsa.PublicKey: pubkey = *cpub default: - return errors.Errorf(`failed to retrieve rsa.PublicKey out of crypto.Signer %T`, key) + return fmt.Errorf(`failed to retrieve rsa.PublicKey out of crypto.Signer %T`, key) } } else { if err := keyconv.RSAPublicKey(&pubkey, key); err != nil { - return errors.Wrapf(err, `failed to retrieve rsa.PublicKey out of %T`, key) + return fmt.Errorf(`failed to retrieve rsa.PublicKey out of %T: %w`, key, err) } } h := rv.hash.New() if _, err := h.Write(payload); err != nil { - return errors.Wrap(err, "failed to write payload to hash") + return fmt.Errorf(`failed to write payload to hash: %w`, err) } if rv.pss { diff --git a/vendor/github.com/lestrrat-go/jwx/jws/signer.go b/vendor/github.com/lestrrat-go/jwx/v2/jws/signer.go similarity index 93% rename from vendor/github.com/lestrrat-go/jwx/jws/signer.go rename to vendor/github.com/lestrrat-go/jwx/v2/jws/signer.go index 6065817..46e73eb 100644 --- a/vendor/github.com/lestrrat-go/jwx/jws/signer.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jws/signer.go @@ -1,8 +1,9 @@ package jws import ( - "github.com/lestrrat-go/jwx/jwa" - "github.com/pkg/errors" + "fmt" + + "github.com/lestrrat-go/jwx/v2/jwa" ) type SignerFactory interface { @@ -64,5 +65,5 @@ func NewSigner(alg jwa.SignatureAlgorithm) (Signer, error) { if ok { return f.Create() } - return nil, errors.Errorf(`unsupported signature algorithm "%s"`, alg) + return nil, fmt.Errorf(`unsupported signature algorithm "%s"`, alg) } diff --git a/vendor/github.com/lestrrat-go/jwx/jws/verifier.go b/vendor/github.com/lestrrat-go/jwx/v2/jws/verifier.go similarity index 93% rename from vendor/github.com/lestrrat-go/jwx/jws/verifier.go rename to vendor/github.com/lestrrat-go/jwx/v2/jws/verifier.go index 3a4173b..8093f87 100644 --- a/vendor/github.com/lestrrat-go/jwx/jws/verifier.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jws/verifier.go @@ -1,8 +1,9 @@ package jws import ( - "github.com/lestrrat-go/jwx/jwa" - "github.com/pkg/errors" + "fmt" + + "github.com/lestrrat-go/jwx/v2/jwa" ) type VerifierFactory interface { @@ -64,5 +65,5 @@ func NewVerifier(alg jwa.SignatureAlgorithm) (Verifier, error) { if ok { return f.Create() } - return nil, errors.Errorf(`unsupported signature algorithm "%s"`, alg) + return nil, fmt.Errorf(`unsupported signature algorithm "%s"`, alg) } diff --git a/vendor/github.com/lestrrat-go/jwx/jwt/README.md b/vendor/github.com/lestrrat-go/jwx/v2/jwt/README.md similarity index 94% rename from vendor/github.com/lestrrat-go/jwx/jwt/README.md rename to vendor/github.com/lestrrat-go/jwx/v2/jwt/README.md index 9e97439..103cafc 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwt/README.md +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwt/README.md @@ -1,4 +1,4 @@ -# JWT [![Go Reference](https://pkg.go.dev/badge/github.com/lestrrat-go/jwx/jwt.svg)](https://pkg.go.dev/github.com/lestrrat-go/jwx/jwt) +# JWT [![Go Reference](https://pkg.go.dev/badge/github.com/lestrrat-go/jwx/v2/jwt.svg)](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwt) Package jwt implements JSON Web Tokens as described in [RFC7519](https://tools.ietf.org/html/rfc7519). @@ -8,7 +8,7 @@ Package jwt implements JSON Web Tokens as described in [RFC7519](https://tools.i * Conversion to and from JSON * Generate signed tokens * Verify signed tokens -* Extra support for OpenID tokens via [github.com/lestrrat-go/jwx/jwt/openid](./jwt/openid) +* Extra support for OpenID tokens via [github.com/lestrrat-go/jwx/v2/jwt/openid](./jwt/openid) How-to style documentation can be found in the [docs directory](../docs). @@ -19,7 +19,7 @@ More examples are located in the examples directory ([jwt_example_test.go](../ex ## Verify a signed JWT ```go - token, err := jwt.Parse(payload, jwt.WithKeySet(keyset)) + token, err := jwt.Parse(payload, jwt.WithKey(alg, key)) if err != nil { fmt.Printf("failed to parse payload: %s\n", err) } @@ -32,7 +32,7 @@ func ExampleJWT() { const aLongLongTimeAgo = 233431200 t := jwt.New() - t.Set(jwt.SubjectKey, `https://github.com/lestrrat-go/jwx/jwt`) + t.Set(jwt.SubjectKey, `https://github.com/lestrrat-go/jwx/v2/jwt`) t.Set(jwt.AudienceKey, `Golang Users`) t.Set(jwt.IssuedAtKey, time.Unix(aLongLongTimeAgo, 0)) t.Set(`privateClaimKey`, `Hello, World!`) @@ -59,7 +59,7 @@ func ExampleJWT() { { // Signing a token (using raw rsa.PrivateKey) - signed, err := jwt.Sign(t, jwa.RS256, key) + signed, err := jwt.Sign(t, jwt.WithKey(jwa.RS256, key)) if err != nil { log.Printf("failed to sign token: %s", err) return @@ -75,7 +75,7 @@ func ExampleJWT() { return } - signed, err := jwt.Sign(t, jwa.RS256, jwkKey) + signed, err := jwt.Sign(t, jwt.WithKey(jwa.RS256, jwkKey)) if err != nil { log.Printf("failed to sign token: %s", err) return @@ -97,7 +97,7 @@ func Example_openid() { const aLongLongTimeAgo = 233431200 t := openid.New() - t.Set(jwt.SubjectKey, `https://github.com/lestrrat-go/jwx/jwt`) + t.Set(jwt.SubjectKey, `https://github.com/lestrrat-go/jwx/v2/jwt`) t.Set(jwt.AudienceKey, `Golang Users`) t.Set(jwt.IssuedAtKey, time.Unix(aLongLongTimeAgo, 0)) t.Set(`privateClaimKey`, `Hello, World!`) diff --git a/vendor/github.com/lestrrat-go/jwx/jwt/builder_gen.go b/vendor/github.com/lestrrat-go/jwx/v2/jwt/builder_gen.go similarity index 94% rename from vendor/github.com/lestrrat-go/jwx/jwt/builder_gen.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwt/builder_gen.go index 4ce5a42..a588bc6 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwt/builder_gen.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwt/builder_gen.go @@ -3,9 +3,8 @@ package jwt import ( + "fmt" "time" - - "github.com/pkg/errors" ) // Builder is a convenience wrapper around the New() constructor @@ -64,7 +63,7 @@ func (b *Builder) Build() (Token, error) { tok := New() for _, claim := range b.claims { if err := tok.Set(claim.Key.(string), claim.Value); err != nil { - return nil, errors.Wrapf(err, `failed to set claim %q`, claim.Key.(string)) + return nil, fmt.Errorf(`failed to set claim %q: %w`, claim.Key.(string), err) } } return tok, nil diff --git a/vendor/github.com/lestrrat-go/jwx/jwt/http.go b/vendor/github.com/lestrrat-go/jwx/v2/jwt/http.go similarity index 94% rename from vendor/github.com/lestrrat-go/jwx/jwt/http.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwt/http.go index aaaf27f..08b73bc 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwt/http.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwt/http.go @@ -1,13 +1,13 @@ package jwt import ( + "fmt" "net/http" "net/url" "strconv" "strings" - "github.com/lestrrat-go/jwx/internal/pool" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/internal/pool" ) // ParseHeader parses a JWT stored in a http.Header. @@ -18,7 +18,7 @@ func ParseHeader(hdr http.Header, name string, options ...ParseOption) (Token, e key := http.CanonicalHeaderKey(name) v := strings.TrimSpace(hdr.Get(key)) if v == "" { - return nil, errors.Errorf(`empty header (%s)`, key) + return nil, fmt.Errorf(`empty header (%s)`, key) } if key == "Authorization" { @@ -34,7 +34,7 @@ func ParseHeader(hdr http.Header, name string, options ...ParseOption) (Token, e func ParseForm(values url.Values, name string, options ...ParseOption) (Token, error) { v := strings.TrimSpace(values.Get(name)) if v == "" { - return nil, errors.Errorf(`empty value (%s)`, name) + return nil, fmt.Errorf(`empty value (%s)`, name) } return ParseString(v, options...) @@ -99,7 +99,7 @@ func ParseRequest(req *http.Request, options ...ParseOption) (Token, error) { if cl := req.ContentLength; cl > 0 { if err := req.ParseForm(); err != nil { - return nil, errors.Wrap(err, `failed to parse form`) + return nil, fmt.Errorf(`failed to parse form: %w`, err) } } @@ -184,5 +184,5 @@ func ParseRequest(req *http.Request, options ...ParseOption) (Token, error) { } } } - return nil, errors.New(b.String()) + return nil, fmt.Errorf(b.String()) } diff --git a/vendor/github.com/lestrrat-go/jwx/jwt/interface.go b/vendor/github.com/lestrrat-go/jwx/v2/jwt/interface.go similarity index 74% rename from vendor/github.com/lestrrat-go/jwx/jwt/interface.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwt/interface.go index 1e2c269..3a4352e 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwt/interface.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwt/interface.go @@ -2,8 +2,8 @@ package jwt import ( "github.com/lestrrat-go/iter/mapiter" - "github.com/lestrrat-go/jwx/internal/iter" - "github.com/lestrrat-go/jwx/internal/json" + "github.com/lestrrat-go/jwx/v2/internal/iter" + "github.com/lestrrat-go/jwx/v2/internal/json" ) type ClaimPair = mapiter.Pair diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jwt/internal/types/date.go b/vendor/github.com/lestrrat-go/jwx/v2/jwt/internal/types/date.go new file mode 100644 index 0000000..0878397 --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwt/internal/types/date.go @@ -0,0 +1,191 @@ +package types + +import ( + "fmt" + "strconv" + "strings" + "time" + + "github.com/lestrrat-go/jwx/v2/internal/json" +) + +const ( + DefaultPrecision uint32 = 0 // second level + MaxPrecision uint32 = 9 // nanosecond level +) + +var Pedantic uint32 +var ParsePrecision = DefaultPrecision +var FormatPrecision = DefaultPrecision + +// NumericDate represents the date format used in the 'nbf' claim +type NumericDate struct { + time.Time +} + +func (n *NumericDate) Get() time.Time { + if n == nil { + return (time.Time{}).UTC() + } + return n.Time +} + +func intToTime(v interface{}, t *time.Time) bool { + var n int64 + switch x := v.(type) { + case int64: + n = x + case int32: + n = int64(x) + case int16: + n = int64(x) + case int8: + n = int64(x) + case int: + n = int64(x) + default: + return false + } + + *t = time.Unix(n, 0) + return true +} + +func parseNumericString(x string) (time.Time, error) { + var t time.Time // empty time for empty return value + + // Only check for the escape hatch if it's the pedantic + // flag is off + if Pedantic != 1 { + // This is an escape hatch for non-conformant providers + // that gives us RFC3339 instead of epoch time + for _, r := range x { + // 0x30 = '0', 0x39 = '9', 0x2E = '.' + if (r >= 0x30 && r <= 0x39) || r == 0x2E { + continue + } + + // if it got here, then it probably isn't epoch time + tv, err := time.Parse(time.RFC3339, x) + if err != nil { + return t, fmt.Errorf(`value is not number of seconds since the epoch, and attempt to parse it as RFC3339 timestamp failed: %w`, err) + } + return tv, nil + } + } + + var fractional string + whole := x + if i := strings.IndexRune(x, '.'); i > 0 { + if ParsePrecision > 0 && len(x) > i+1 { + fractional = x[i+1:] // everything after the '.' + if int(ParsePrecision) < len(fractional) { + // Remove insignificant digits + fractional = fractional[:int(ParsePrecision)] + } + // Replace missing fractional diits with zeros + for len(fractional) < int(MaxPrecision) { + fractional = fractional + "0" + } + } + whole = x[:i] + } + n, err := strconv.ParseInt(whole, 10, 64) + if err != nil { + return t, fmt.Errorf(`failed to parse whole value %q: %w`, whole, err) + } + var nsecs int64 + if fractional != "" { + v, err := strconv.ParseInt(fractional, 10, 64) + if err != nil { + return t, fmt.Errorf(`failed to parse fractional value %q: %w`, fractional, err) + } + nsecs = v + } + + return time.Unix(n, nsecs).UTC(), nil +} + +func (n *NumericDate) Accept(v interface{}) error { + var t time.Time + switch x := v.(type) { + case float32: + tv, err := parseNumericString(fmt.Sprintf(`%.9f`, x)) + if err != nil { + return fmt.Errorf(`failed to accept float32 %.9f: %w`, x, err) + } + t = tv + case float64: + tv, err := parseNumericString(fmt.Sprintf(`%.9f`, x)) + if err != nil { + return fmt.Errorf(`failed to accept float32 %.9f: %w`, x, err) + } + t = tv + case json.Number: + tv, err := parseNumericString(x.String()) + if err != nil { + return fmt.Errorf(`failed to accept json.Number %q: %w`, x.String(), err) + } + t = tv + case string: + tv, err := parseNumericString(x) + if err != nil { + return fmt.Errorf(`failed to accept string %q: %w`, x, err) + } + t = tv + case time.Time: + t = x + default: + if !intToTime(v, &t) { + return fmt.Errorf(`invalid type %T`, v) + } + } + n.Time = t.UTC() + return nil +} + +func (n NumericDate) String() string { + if FormatPrecision == 0 { + return strconv.FormatInt(n.Unix(), 10) + } + + // This is cheating,but it's better (easier) than doing floating point math + // We basically munge with strings after formatting an integer balue + // for nanoseconds since epoch + s := strconv.FormatInt(n.UnixNano(), 10) + for len(s) < int(MaxPrecision) { + s = "0" + s + } + + slwhole := len(s) - int(MaxPrecision) + s = s[:slwhole] + "." + s[slwhole:slwhole+int(FormatPrecision)] + if s[0] == '.' { + s = "0" + s + } + + return s +} + +// MarshalJSON translates from internal representation to JSON NumericDate +// See https://tools.ietf.org/html/rfc7519#page-6 +func (n *NumericDate) MarshalJSON() ([]byte, error) { + if n.IsZero() { + return json.Marshal(nil) + } + + return json.Marshal(n.String()) +} + +func (n *NumericDate) UnmarshalJSON(data []byte) error { + var v interface{} + if err := json.Unmarshal(data, &v); err != nil { + return fmt.Errorf(`failed to unmarshal date: %w`, err) + } + + var n2 NumericDate + if err := n2.Accept(v); err != nil { + return fmt.Errorf(`invalid value for NumericDate: %w`, err) + } + *n = n2 + return nil +} diff --git a/vendor/github.com/lestrrat-go/jwx/jwt/internal/types/string.go b/vendor/github.com/lestrrat-go/jwx/v2/jwt/internal/types/string.go similarity index 72% rename from vendor/github.com/lestrrat-go/jwx/jwt/internal/types/string.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwt/internal/types/string.go index 9a27c68..eb67aef 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwt/internal/types/string.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwt/internal/types/string.go @@ -1,9 +1,9 @@ package types import ( - "github.com/lestrrat-go/jwx/internal/json" + "fmt" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/internal/json" ) type StringList []string @@ -25,11 +25,11 @@ func (l *StringList) Accept(v interface{}) error { list[i] = s continue } - return errors.Errorf(`invalid list element type %T`, e) + return fmt.Errorf(`invalid list element type %T`, e) } *l = list default: - return errors.Errorf(`invalid type: %T`, v) + return fmt.Errorf(`invalid type: %T`, v) } return nil } @@ -37,7 +37,7 @@ func (l *StringList) Accept(v interface{}) error { func (l *StringList) UnmarshalJSON(data []byte) error { var v interface{} if err := json.Unmarshal(data, &v); err != nil { - return errors.Wrap(err, `failed to unmarshal data`) + return fmt.Errorf(`failed to unmarshal data: %w`, err) } return l.Accept(v) } diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jwt/io.go b/vendor/github.com/lestrrat-go/jwx/v2/jwt/io.go new file mode 100644 index 0000000..ad5db4b --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwt/io.go @@ -0,0 +1,42 @@ +// Automatically generated by internal/cmd/genreadfile/main.go. DO NOT EDIT + +package jwt + +import ( + "io/fs" + "os" +) + +type sysFS struct{} + +func (sysFS) Open(path string) (fs.File, error) { + return os.Open(path) +} + +func ReadFile(path string, options ...ReadFileOption) (Token, error) { + var parseOptions []ParseOption + var readFileOptions []ReadFileOption + for _, option := range options { + if po, ok := option.(ParseOption); ok { + parseOptions = append(parseOptions, po) + } else { + readFileOptions = append(readFileOptions, option) + } + } + + var srcFS fs.FS = sysFS{} + for _, option := range options { + switch option.Ident() { + case identFS{}: + srcFS = option.Value().(fs.FS) + } + } + + f, err := srcFS.Open(path) + if err != nil { + return nil, err + } + + defer f.Close() + return ParseReader(f, parseOptions...) +} diff --git a/vendor/github.com/lestrrat-go/jwx/jwt/jwt.go b/vendor/github.com/lestrrat-go/jwx/v2/jwt/jwt.go similarity index 50% rename from vendor/github.com/lestrrat-go/jwx/jwt/jwt.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwt/jwt.go index 13c1539..5846a8f 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwt/jwt.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwt/jwt.go @@ -1,48 +1,83 @@ -//go:generate ./gen.sh +//go:generate ../tools/cmd/genjwt.sh // Package jwt implements JSON Web Tokens as described in https://tools.ietf.org/html/rfc7519 package jwt import ( "bytes" + "fmt" "io" - "io/ioutil" - "net/http" - "strings" "sync/atomic" - "github.com/lestrrat-go/backoff/v2" - "github.com/lestrrat-go/jwx" - "github.com/lestrrat-go/jwx/internal/json" - "github.com/lestrrat-go/jwx/jwe" - - "github.com/lestrrat-go/jwx/jwa" - "github.com/lestrrat-go/jwx/jwk" - "github.com/lestrrat-go/jwx/jws" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2" + "github.com/lestrrat-go/jwx/v2/internal/json" + "github.com/lestrrat-go/jwx/v2/jws" + "github.com/lestrrat-go/jwx/v2/jwt/internal/types" ) -const _jwt = `jwt` - // Settings controls global settings that are specific to JWTs. func Settings(options ...GlobalOption) { var flattenAudienceBool bool + var parsePedantic bool + var parsePrecision = types.MaxPrecision + 1 // illegal value, so we can detect nothing was set + var formatPrecision = types.MaxPrecision + 1 // illegal value, so we can detect nothing was set //nolint:forcetypeassert for _, option := range options { switch option.Ident() { case identFlattenAudience{}: flattenAudienceBool = option.Value().(bool) + case identNumericDateParsePedantic{}: + parsePedantic = option.Value().(bool) + case identNumericDateParsePrecision{}: + v := option.Value().(int) + // only accept this value if it's in our desired range + if v >= 0 && v <= int(types.MaxPrecision) { + parsePrecision = uint32(v) + } + case identNumericDateFormatPrecision{}: + v := option.Value().(int) + // only accept this value if it's in our desired range + if v >= 0 && v <= int(types.MaxPrecision) { + formatPrecision = uint32(v) + } } } - v := atomic.LoadUint32(&json.FlattenAudience) - if (v == 1) != flattenAudienceBool { - var newVal uint32 - if flattenAudienceBool { - newVal = 1 + if parsePrecision <= types.MaxPrecision { // remember we set default to max + 1 + v := atomic.LoadUint32(&types.ParsePrecision) + if v != parsePrecision { + atomic.CompareAndSwapUint32(&types.ParsePrecision, v, parsePrecision) + } + } + + if formatPrecision <= types.MaxPrecision { // remember we set default to max + 1 + v := atomic.LoadUint32(&types.FormatPrecision) + if v != formatPrecision { + atomic.CompareAndSwapUint32(&types.FormatPrecision, v, formatPrecision) + } + } + + { + v := atomic.LoadUint32(&types.Pedantic) + if (v == 1) != parsePedantic { + var newVal uint32 + if parsePedantic { + newVal = 1 + } + atomic.CompareAndSwapUint32(&types.Pedantic, v, newVal) + } + } + + { + v := atomic.LoadUint32(&json.FlattenAudience) + if (v == 1) != flattenAudienceBool { + var newVal uint32 + if flattenAudienceBool { + newVal = 1 + } + atomic.CompareAndSwapUint32(&json.FlattenAudience, v, newVal) } - atomic.CompareAndSwapUint32(&json.FlattenAudience, v, newVal) } } @@ -56,13 +91,12 @@ func ParseString(s string, options ...ParseOption) (Token, error) { // Parse parses the JWT token payload and creates a new `jwt.Token` object. // The token must be encoded in either JSON format or compact format. // -// This function can work with encrypted and/or signed tokens. Any combination -// of JWS and JWE may be applied to the token, but this function will only -// attempt to verify/decrypt up to 2 levels (i.e. JWS only, JWE only, JWS then -// JWE, or JWE then JWS) +// This function can only work with either raw JWT (JSON) and JWS (Compact or JSON). +// If you need JWE support on top of it, you will need to rollout your +// own workaround. // // If the token is signed and you want to verify the payload matches the signature, -// you must pass the jwt.WithVerify(alg, key) or jwt.WithKeySet(jwk.Set) option. +// you must pass the jwt.WithKey(alg, key) or jwt.WithKeySet(jwk.Set) option. // If you do not specify these parameters, no verification will be performed. // // During verification, if the JWS headers specify a key ID (`kid`), the @@ -83,35 +117,57 @@ func Parse(s []byte, options ...ParseOption) (Token, error) { return parseBytes(s, options...) } +// ParseInsecure is exactly the same as Parse(), but it disables +// signature verification and token validation. +// +// You cannot override `jwt.WithVerify()` or `jwt.WithValidate()` +// using this function. Providing these options would result in +// an error +func ParseInsecure(s []byte, options ...ParseOption) (Token, error) { + for _, option := range options { + switch option.Ident() { + case identVerify{}, identValidate{}: + return nil, fmt.Errorf(`jwt.ParseInsecure: jwt.WithVerify() and jwt.WithValidate() may not be specified`) + } + } + + options = append(options, WithVerify(false), WithValidate(false)) + return Parse(s, options...) +} + // ParseReader calls Parse against an io.Reader func ParseReader(src io.Reader, options ...ParseOption) (Token, error) { // We're going to need the raw bytes regardless. Read it. - data, err := ioutil.ReadAll(src) + data, err := io.ReadAll(src) if err != nil { - return nil, errors.Wrap(err, `failed to read from token data source`) + return nil, fmt.Errorf(`failed to read from token data source: %w`, err) } return parseBytes(data, options...) } type parseCtx struct { - decryptParams DecryptParameters - verifyParams VerifyParameters - keySet jwk.Set - keySetProvider KeySetProvider token Token validateOpts []ValidateOption - verifyAutoOpts []jws.VerifyOption + verifyOpts []jws.VerifyOption localReg *json.Registry - inferAlgorithm bool pedantic bool skipVerification bool - useDefault bool validate bool - verifyAuto bool } func parseBytes(data []byte, options ...ParseOption) (Token, error) { var ctx parseCtx + + // Validation is turned on by default. You need to specify + // jwt.WithValidate(false) if you want to disable it + ctx.validate = true + + // Verification is required (i.e., it is assumed that the incoming + // data is in JWS format) unless the user explicitly asks for + // it to be skipped. + verification := true + + var verifyOpts []Option for _, o := range options { if v, ok := o.(ValidateOption); ok { ctx.validateOpts = append(ctx.validateOpts, v) @@ -120,51 +176,42 @@ func parseBytes(data []byte, options ...ParseOption) (Token, error) { //nolint:forcetypeassert switch o.Ident() { - case identVerifyAuto{}: - ctx.verifyAuto = o.Value().(bool) - case identFetchWhitelist{}: - ctx.verifyAutoOpts = append(ctx.verifyAutoOpts, jws.WithFetchWhitelist(o.Value().(jwk.Whitelist))) - case identHTTPClient{}: - ctx.verifyAutoOpts = append(ctx.verifyAutoOpts, jws.WithHTTPClient(o.Value().(*http.Client))) - case identFetchBackoff{}: - ctx.verifyAutoOpts = append(ctx.verifyAutoOpts, jws.WithFetchBackoff(o.Value().(backoff.Policy))) - case identJWKSetFetcher{}: - ctx.verifyAutoOpts = append(ctx.verifyAutoOpts, jws.WithJWKSetFetcher(o.Value().(jws.JWKSetFetcher))) - case identVerify{}: - ctx.verifyParams = o.Value().(VerifyParameters) - case identDecrypt{}: - ctx.decryptParams = o.Value().(DecryptParameters) - case identKeySet{}: - ks, ok := o.Value().(jwk.Set) - if !ok { - return nil, errors.Errorf(`invalid JWK set passed via WithKeySet() option (%T)`, o.Value()) - } - ctx.keySet = ks + case identKey{}, identKeySet{}, identVerifyAuto{}, identKeyProvider{}: + verifyOpts = append(verifyOpts, o) case identToken{}: token, ok := o.Value().(Token) if !ok { - return nil, errors.Errorf(`invalid token passed via WithToken() option (%T)`, o.Value()) + return nil, fmt.Errorf(`invalid token passed via WithToken() option (%T)`, o.Value()) } ctx.token = token case identPedantic{}: ctx.pedantic = o.Value().(bool) - case identDefault{}: - ctx.useDefault = o.Value().(bool) case identValidate{}: ctx.validate = o.Value().(bool) + case identVerify{}: + verification = o.Value().(bool) case identTypedClaim{}: pair := o.Value().(claimPair) if ctx.localReg == nil { ctx.localReg = json.NewRegistry() } ctx.localReg.Register(pair.Name, pair.Value) - case identInferAlgorithmFromKey{}: - ctx.inferAlgorithm = o.Value().(bool) - case identKeySetProvider{}: - ctx.keySetProvider = o.Value().(KeySetProvider) } } + lvo := len(verifyOpts) + if lvo == 0 && verification { + return nil, fmt.Errorf(`jwt.Parse: no keys for verification are provided (use jwt.WithVerify(false) to explicitly skip)`) + } + + if lvo > 0 { + converted, err := toVerifyOptions(verifyOpts...) + if err != nil { + return nil, fmt.Errorf(`jwt.Parse: failed to convert options into jws.VerifyOption: %w`, err) + } + ctx.verifyOpts = converted + } + data = bytes.TrimSpace(data) return parse(&ctx, data) } @@ -176,151 +223,15 @@ const ( _JwsVerifySkipped ) +var _ = _JwsVerifyInvalid + func verifyJWS(ctx *parseCtx, payload []byte) ([]byte, int, error) { - if ctx.verifyAuto { - options := ctx.verifyAutoOpts - verified, err := jws.VerifyAuto(payload, options...) - return verified, _JwsVerifyDone, err - } - - // if we have a key set or a provider, use that - ks := ctx.keySet - p := ctx.keySetProvider - if ks != nil || p != nil { - return verifyJWSWithKeySet(ctx, payload) - } - - // We can't proceed without verification parameters - vp := ctx.verifyParams - if vp == nil { + if len(ctx.verifyOpts) == 0 { return nil, _JwsVerifySkipped, nil } - return verifyJWSWithParams(ctx, payload, vp.Algorithm(), vp.Key()) -} - -func verifyJWSWithKeySet(ctx *parseCtx, payload []byte) ([]byte, int, error) { - // First, get the JWS message - msg, err := jws.Parse(payload) - if err != nil { - return nil, _JwsVerifyInvalid, errors.Wrap(err, `failed to parse token data as JWS message`) - } - ks := ctx.keySet - if ks == nil { // the caller should have checked ctx.keySet || ctx.keySetProvider - if p := ctx.keySetProvider; p != nil { - // "trust" the payload, and parse it so that the provider can do its thing - ctx.skipVerification = true - tok, err := parse(ctx, msg.Payload()) - if err != nil { - return nil, _JwsVerifyInvalid, err - } - ctx.skipVerification = false - - v, err := p.KeySetFrom(tok) - if err != nil { - return nil, _JwsVerifyInvalid, errors.Wrap(err, `failed to obtain jwk.Set from KeySetProvider`) - } - ks = v - } - } - - // Bail out early if we don't even have a key in the set - if ks.Len() == 0 { - return nil, _JwsVerifyInvalid, errors.New(`empty keyset provided`) - } - - var key jwk.Key - - // Find the kid. we need the kid, unless the user explicitly - // specified to use the "default" (the first and only) key in the set - headers := msg.Signatures()[0].ProtectedHeaders() - kid := headers.KeyID() - if kid == "" { - // If the kid is NOT specified... ctx.useDefault needs to be true, and the - // JWKs must have exactly one key in it - if !ctx.useDefault { - return nil, _JwsVerifyInvalid, errors.New(`failed to find matching key: no key ID ("kid") specified in token`) - } else if ctx.useDefault && ks.Len() > 1 { - return nil, _JwsVerifyInvalid, errors.New(`failed to find matching key: no key ID ("kid") specified in token but multiple keys available in key set`) - } - - // if we got here, then useDefault == true AND there is exactly - // one key in the set. - key, _ = ks.Get(0) - } else { - // Otherwise we better be able to look up the key, baby. - v, ok := ks.LookupKeyID(kid) - if !ok { - return nil, _JwsVerifyInvalid, errors.Errorf(`failed to find key with key ID %q in key set`, kid) - } - key = v - } - - // We found a key with matching kid. Check fo the algorithm specified in the key. - // If we find an algorithm in the key, use that. - if v := key.Algorithm(); v != "" { - var alg jwa.SignatureAlgorithm - if err := alg.Accept(v); err != nil { - return nil, _JwsVerifyInvalid, errors.Wrapf(err, `invalid signature algorithm %s`, key.Algorithm()) - } - - // Okay, we have a valid algorithm, go go - return verifyJWSWithParams(ctx, payload, alg, key) - } - - if ctx.inferAlgorithm { - // Check whether the JWT headers specify a valid - // algorithm, use it if it's compatible. - algs, err := jws.AlgorithmsForKey(key) - if err != nil { - return nil, _JwsVerifyInvalid, errors.Wrapf(err, `failed to get a list of signature methods for key type %s`, key.KeyType()) - } - - for _, alg := range algs { - // bail out if the JWT has a `alg` field, and it doesn't match - if tokAlg := headers.Algorithm(); tokAlg != "" { - if tokAlg != alg { - continue - } - } - - return verifyJWSWithParams(ctx, payload, alg, key) - } - } - - return nil, _JwsVerifyInvalid, errors.New(`failed to match any of the keys`) -} - -func verifyJWSWithParams(ctx *parseCtx, payload []byte, alg jwa.SignatureAlgorithm, key interface{}) ([]byte, int, error) { - var m *jws.Message - var verifyOpts []jws.VerifyOption - if ctx.pedantic { - m = jws.NewMessage() - verifyOpts = []jws.VerifyOption{jws.WithMessage(m)} - } - v, err := jws.Verify(payload, alg, key, verifyOpts...) - if err != nil { - return nil, _JwsVerifyInvalid, errors.Wrap(err, `failed to verify jws signature`) - } - - if !ctx.pedantic { - return v, _JwsVerifyDone, nil - } - // This payload could be a JWT+JWS, in which case typ: JWT should be there - // If its JWT+(JWE or JWS or...)+JWS, then cty should be JWT - for _, sig := range m.Signatures() { - hdrs := sig.ProtectedHeaders() - if strings.ToLower(hdrs.Type()) == _jwt { - return v, _JwsVerifyDone, nil - } - - if strings.ToLower(hdrs.ContentType()) == _jwt { - return v, _JwsVerifyExpectNested, nil - } - } - - // Hmmm, it was a JWS and we got... nothing? - return nil, _JwsVerifyInvalid, errors.Errorf(`expected "typ" or "cty" fields, neither could be found`) + verified, err := jws.Verify(payload, ctx.verifyOpts...) + return verified, _JwsVerifyDone, err } // verify parameter exists to make sure that we don't accidentally skip @@ -338,7 +249,7 @@ OUTER: case jwx.JWT: if ctx.pedantic { if expectNested { - return nil, errors.Errorf(`expected nested encrypted/signed payload, got raw JWT`) + return nil, fmt.Errorf(`expected nested encrypted/signed payload, got raw JWT`) } } @@ -356,7 +267,7 @@ OUTER: // "Unknown" may include invalid JWTs, for example, those who lack "aud" // claim. We could be pedantic and reject these if ctx.pedantic { - return nil, errors.Errorf(`invalid JWT`) + return nil, fmt.Errorf(`invalid JWT`) } if i == 0 { @@ -405,44 +316,11 @@ OUTER: // No verification. m, err := jws.Parse(data) if err != nil { - return nil, errors.Wrap(err, `invalid jws message`) + return nil, fmt.Errorf(`invalid jws message: %w`, err) } payload = m.Payload() - case jwx.JWE: - dp := ctx.decryptParams - if dp == nil { - return nil, errors.Errorf(`jwt.Parse: cannot proceed with JWE encrypted payload without decryption parameters`) - } - - var m *jwe.Message - var decryptOpts []jwe.DecryptOption - if ctx.pedantic { - m = jwe.NewMessage() - decryptOpts = []jwe.DecryptOption{jwe.WithMessage(m)} - } - - v, err := jwe.Decrypt(data, dp.Algorithm(), dp.Key(), decryptOpts...) - if err != nil { - return nil, errors.Wrap(err, `failed to decrypt payload`) - } - - if !ctx.pedantic { - payload = v - continue - } - - if strings.ToLower(m.ProtectedHeaders().Type()) == _jwt { - payload = v - break OUTER - } - - if strings.ToLower(m.ProtectedHeaders().ContentType()) == _jwt { - expectNested = true - payload = v - continue OUTER - } default: - return nil, errors.Errorf(`unsupported format (layer: #%d)`, i+1) + return nil, fmt.Errorf(`unsupported format (layer: #%d)`, i+1) } expectNested = false } @@ -454,7 +332,7 @@ OUTER: if ctx.localReg != nil { dcToken, ok := ctx.token.(TokenWithDecodeCtx) if !ok { - return nil, errors.Errorf(`typed claim was requested, but the token (%T) does not support DecodeCtx`, ctx.token) + return nil, fmt.Errorf(`typed claim was requested, but the token (%T) does not support DecodeCtx`, ctx.token) } dc := json.NewDecodeCtx(ctx.localReg) dcToken.SetDecodeCtx(dc) @@ -462,7 +340,7 @@ OUTER: } if err := json.Unmarshal(payload, ctx.token); err != nil { - return nil, errors.Wrap(err, `failed to parse token`) + return nil, fmt.Errorf(`failed to parse token: %w`, err) } if ctx.validate { @@ -485,12 +363,31 @@ OUTER: // // The algorithm specified in the `alg` parameter must be able to support // the type of key you provided, otherwise an error is returned. +// For convenience `alg` is of type jwa.KeyAlgorithm so you can pass +// the return value of `(jwk.Key).Algorithm()` directly, but in practice +// it must be an instance of jwa.SignatureAlgorithm, otherwise an error +// is returned. // // The protected header will also automatically have the `typ` field set // to the literal value `JWT`, unless you provide a custom value for it // by jwt.WithHeaders option. -func Sign(t Token, alg jwa.SignatureAlgorithm, key interface{}, options ...SignOption) ([]byte, error) { - return NewSerializer().Sign(alg, key, options...).Serialize(t) +func Sign(t Token, options ...SignOption) ([]byte, error) { + var soptions []jws.SignOption + if l := len(options); l > 0 { + // we need to from SignOption to Option because ... reasons + // (todo: when go1.18 prevails, use type parameters + rawoptions := make([]Option, l) + for i, option := range options { + rawoptions[i] = option + } + + converted, err := toSignOptions(rawoptions...) + if err != nil { + return nil, fmt.Errorf(`jwt.Sign: failed to convert options into jws.SignOption: %w`, err) + } + soptions = converted + } + return NewSerializer().sign(soptions...).Serialize(t) } // Equal compares two JWT tokens. Do not use `reflect.Equal` or the like @@ -532,7 +429,7 @@ func (t *stdToken) Clone() (Token, error) { //nolint:forcetypeassert key := pair.Key.(string) if err := dst.Set(key, pair.Value); err != nil { - return nil, errors.Wrapf(err, `failed to set %s`, key) + return nil, fmt.Errorf(`failed to set %s: %w`, key, err) } } return dst, nil diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jwt/options.go b/vendor/github.com/lestrrat-go/jwx/v2/jwt/options.go new file mode 100644 index 0000000..a83f476 --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwt/options.go @@ -0,0 +1,297 @@ +package jwt + +import ( + "fmt" + "time" + + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwe" + "github.com/lestrrat-go/jwx/v2/jwk" + "github.com/lestrrat-go/jwx/v2/jws" + "github.com/lestrrat-go/option" +) + +type identKey struct{} +type identKeySet struct{} +type identTypedClaim struct{} +type identVerifyAuto struct{} + +func toSignOptions(options ...Option) ([]jws.SignOption, error) { + var soptions []jws.SignOption + for _, option := range options { + //nolint:forcetypeassert + switch option.Ident() { + case identKey{}: + wk := option.Value().(*withKey) // this always succeeds + var wksoptions []jws.WithKeySuboption + for _, subopt := range wk.options { + wksopt, ok := subopt.(jws.WithKeySuboption) + if !ok { + return nil, fmt.Errorf(`expected optional arguments in jwt.WithKey to be jws.WithKeySuboption, but got %T`, subopt) + } + wksoptions = append(wksoptions, wksopt) + } + + soptions = append(soptions, jws.WithKey(wk.alg, wk.key, wksoptions...)) + } + } + return soptions, nil +} + +func toEncryptOptions(options ...Option) ([]jwe.EncryptOption, error) { + var soptions []jwe.EncryptOption + for _, option := range options { + //nolint:forcetypeassert + switch option.Ident() { + case identKey{}: + wk := option.Value().(*withKey) // this always succeeds + var wksoptions []jwe.WithKeySuboption + for _, subopt := range wk.options { + wksopt, ok := subopt.(jwe.WithKeySuboption) + if !ok { + return nil, fmt.Errorf(`expected optional arguments in jwt.WithKey to be jwe.WithKeySuboption, but got %T`, subopt) + } + wksoptions = append(wksoptions, wksopt) + } + + soptions = append(soptions, jwe.WithKey(wk.alg, wk.key, wksoptions...)) + } + } + return soptions, nil +} + +func toVerifyOptions(options ...Option) ([]jws.VerifyOption, error) { + var voptions []jws.VerifyOption + for _, option := range options { + //nolint:forcetypeassert + switch option.Ident() { + case identKey{}: + wk := option.Value().(*withKey) // this always succeeds + var wksoptions []jws.WithKeySuboption + for _, subopt := range wk.options { + wksopt, ok := subopt.(jws.WithKeySuboption) + if !ok { + return nil, fmt.Errorf(`expected optional arguments in jwt.WithKey to be jws.WithKeySuboption, but got %T`, subopt) + } + wksoptions = append(wksoptions, wksopt) + } + + voptions = append(voptions, jws.WithKey(wk.alg, wk.key, wksoptions...)) + case identKeySet{}: + wks := option.Value().(*withKeySet) // this always succeeds + var wkssoptions []jws.WithKeySetSuboption + for _, subopt := range wks.options { + wkssopt, ok := subopt.(jws.WithKeySetSuboption) + if !ok { + return nil, fmt.Errorf(`expected optional arguments in jwt.WithKey to be jws.WithKeySetSuboption, but got %T`, subopt) + } + wkssoptions = append(wkssoptions, wkssopt) + } + + voptions = append(voptions, jws.WithKeySet(wks.set, wkssoptions...)) + case identVerifyAuto{}: + // this one doesn't need conversion. just get the stored option + voptions = append(voptions, option.Value().(jws.VerifyOption)) + case identKeyProvider{}: + kp, ok := option.Value().(jws.KeyProvider) + if !ok { + return nil, fmt.Errorf(`expected jws.KeyProvider, got %T`, option.Value()) + } + voptions = append(voptions, jws.WithKeyProvider(kp)) + } + } + return voptions, nil +} + +type withKey struct { + alg jwa.KeyAlgorithm + key interface{} + options []Option +} + +// WithKey is a multi-purpose option. It can be used for either jwt.Sign, jwt.Parse (and +// its siblings), and jwt.Serializer methods. +// +// It is the caller's responsibility to match the suboptions to the operation that they +// are performing. For example, you are not allowed to do this: +// +// jwt.Sign(token, jwt.WithKey(alg, key, jweOptions...)) +// +// In the above example, the creation of the option via `jwt.WithKey()` will work, but +// when `jwt.Sign()` is called, the fact that you passed JWE suboptions will be +// detected, and it will be an error. +func WithKey(alg jwa.KeyAlgorithm, key interface{}, suboptions ...Option) SignEncryptParseOption { + return &signEncryptParseOption{option.New(identKey{}, &withKey{ + alg: alg, + key: key, + options: suboptions, + })} +} + +type withKeySet struct { + set jwk.Set + options []interface{} +} + +// WithKeySet forces the Parse method to verify the JWT message +// using one of the keys in the given key set. +// +// Key IDs (`kid`) in the JWS message and the JWK in the given `jwk.Set` +// must match in order for the key to be a candidate to be used for +// verification. +// +// This is for security reasons. If you must disable it, you can do so by +// specifying `jws.WithRequireKid(false)` in the suboptions. But we don't +// recommend it unless you know exactly what the security implications are +// +// When using this option, keys MUST have a proper 'alg' field +// set. This is because we need to know the exact algorithm that +// you (the user) wants to use to verify the token. We do NOT +// trust the token's headers, because they can easily be tampered with. +// +// However, there _is_ a workaround if you do understand the risks +// of allowing a library to automatically choose a signature verification strategy, +// and you do not mind the verification process having to possibly +// attempt using multiple times before succeeding to verify. See +// `jws.InferAlgorithmFromKey` option +// +// If you have only one key in the set, and are sure you want to +// use that key, you can use the `jwt.WithDefaultKey` option. +func WithKeySet(set jwk.Set, options ...interface{}) ParseOption { + return &parseOption{option.New(identKeySet{}, &withKeySet{ + set: set, + options: options, + })} +} + +// WithIssuer specifies that expected issuer value. If not specified, +// the value of issuer is not verified at all. +func WithIssuer(s string) ValidateOption { + return WithValidator(issuerClaimValueIs(s)) +} + +// WithSubject specifies that expected subject value. If not specified, +// the value of subject is not verified at all. +func WithSubject(s string) ValidateOption { + return WithValidator(ClaimValueIs(SubjectKey, s)) +} + +// WithJwtID specifies that expected jti value. If not specified, +// the value of jti is not verified at all. +func WithJwtID(s string) ValidateOption { + return WithValidator(ClaimValueIs(JwtIDKey, s)) +} + +// WithAudience specifies that expected audience value. +// `Validate()` will return true if one of the values in the `aud` element +// matches this value. If not specified, the value of issuer is not +// verified at all. +func WithAudience(s string) ValidateOption { + return WithValidator(audienceClaimContainsString(s)) +} + +// WithClaimValue specifies the expected value for a given claim +func WithClaimValue(name string, v interface{}) ValidateOption { + return WithValidator(ClaimValueIs(name, v)) +} + +type claimPair struct { + Name string + Value interface{} +} + +// WithTypedClaim allows a private claim to be parsed into the object type of +// your choice. It works much like the RegisterCustomField, but the effect +// is only applicable to the jwt.Parse function call which receives this option. +// +// While this can be extremely useful, this option should be used with caution: +// There are many caveats that your entire team/user-base needs to be aware of, +// and therefore in general its use is discouraged. Only use it when you know +// what you are doing, and you document its use clearly for others. +// +// First and foremost, this is a "per-object" option. Meaning that given the same +// serialized format, it is possible to generate two objects whose internal +// representations may differ. That is, if you parse one _WITH_ the option, +// and the other _WITHOUT_, their internal representation may completely differ. +// This could potentially lead to problems. +// +// Second, specifying this option will slightly slow down the decoding process +// as it needs to consult multiple definitions sources (global and local), so +// be careful if you are decoding a large number of tokens, as the effects will stack up. +// +// Finally, this option will also NOT work unless the tokens themselves support such +// parsing mechanism. For example, while tokens obtained from `jwt.New()` and +// `openid.New()` will respect this option, if you provide your own custom +// token type, it will need to implement the TokenWithDecodeCtx interface. +func WithTypedClaim(name string, object interface{}) ParseOption { + return &parseOption{option.New(identTypedClaim{}, claimPair{Name: name, Value: object})} +} + +// WithRequiredClaim specifies that the claim identified the given name +// must exist in the token. Only the existence of the claim is checked: +// the actual value associated with that field is not checked. +func WithRequiredClaim(name string) ValidateOption { + return WithValidator(IsRequired(name)) +} + +// WithMaxDelta specifies that given two claims `c1` and `c2` that represent time, the difference in +// time.Duration must be less than equal to the value specified by `d`. If `c1` or `c2` is the +// empty string, the current time (as computed by `time.Now` or the object passed via +// `WithClock()`) is used for the comparison. +// +// `c1` and `c2` are also assumed to be required, therefore not providing either claim in the +// token will result in an error. +// +// Because there is no way of reliably knowing how to parse private claims, we currently only +// support `iat`, `exp`, and `nbf` claims. +// +// If the empty string is passed to c1 or c2, then the current time (as calculated by time.Now() or +// the clock object provided via WithClock()) is used. +// +// For example, in order to specify that `exp` - `iat` should be less than 10*time.Second, you would write +// +// jwt.Validate(token, jwt.WithMaxDelta(10*time.Second, jwt.ExpirationKey, jwt.IssuedAtKey)) +// +// If AcceptableSkew of 2 second is specified, the above will return valid for any value of +// `exp` - `iat` between 8 (10-2) and 12 (10+2). +func WithMaxDelta(dur time.Duration, c1, c2 string) ValidateOption { + return WithValidator(MaxDeltaIs(c1, c2, dur)) +} + +// WithMinDelta is almost exactly the same as WithMaxDelta, but force validation to fail if +// the difference between time claims are less than dur. +// +// For example, in order to specify that `exp` - `iat` should be greater than 10*time.Second, you would write +// +// jwt.Validate(token, jwt.WithMinDelta(10*time.Second, jwt.ExpirationKey, jwt.IssuedAtKey)) +// +// The validation would fail if the difference is less than 10 seconds. +// +func WithMinDelta(dur time.Duration, c1, c2 string) ValidateOption { + return WithValidator(MinDeltaIs(c1, c2, dur)) +} + +// WithVerifyAuto specifies that the JWS verification should be attempted +// by using the data available in the JWS message. Currently only verification +// method available is to use the keys available in the JWKS URL pointed +// in the `jku` field. +// +// The first argument should either be `nil`, or your custom jwk.Fetcher +// object, which tells how the JWKS should be fetched. Leaving it to +// `nil` is equivalent to specifying that `jwk.Fetch` should be used. +// +// You can further pass options to customize the fetching behavior. +// +// One notable difference in the option available via the `jwt` +// package and the `jws.Verify()` or `jwk.Fetch()` functions is that +// by default all fetching is disabled unless you explicitly whitelist urls. +// Therefore, when you use this option you WILL have to specify at least +// the `jwk.WithFetchWhitelist()` suboption: as: +// +// jwt.Parse(data, jwt.WithVerifyAuto(nil, jwk.WithFetchWhitelist(...))) +// +// See the list of available options that you can pass to `jwk.Fetch()` +// in the `jwk` package +func WithVerifyAuto(f jwk.Fetcher, options ...jwk.FetchOption) ParseOption { + return &parseOption{option.New(identVerifyAuto{}, jws.WithVerifyAuto(f, options...))} +} diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jwt/options.yaml b/vendor/github.com/lestrrat-go/jwx/v2/jwt/options.yaml new file mode 100644 index 0000000..2684ade --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwt/options.yaml @@ -0,0 +1,203 @@ +package_name: jwt +output: jwt/options_gen.go +interfaces: + - name: GlobalOption + comment: | + GlobalOption describes an Option that can be passed to `Settings()`. + - name: EncryptOption + comment: | + EncryptOption describes an Option that can be passed to (jwt.Serializer).Encrypt + - name: ParseOption + methods: + - parseOption + - readFileOption + comment: | + ParseOption describes an Option that can be passed to `jwt.Parse()`. + ParseOption also implements ReadFileOption, therefore it may be + safely pass them to `jwt.ReadFile()` + - name: SignOption + comment: | + SignOption describes an Option that can be passed to `jwt.Sign()` or + (jwt.Serializer).Sign + - name: SignEncryptParseOption + methods: + - parseOption + - encryptOption + - readFileOption + - signOption + comment: | + SignParseOption describes an Option that can be passed to both `jwt.Sign()` or + `jwt.Parse()` + - name: ValidateOption + methods: + - parseOption + - readFileOption + - validateOption + comment: | + ValidateOption describes an Option that can be passed to Validate(). + ValidateOption also implements ParseOption, therefore it may be + safely passed to `Parse()` (and thus `jwt.ReadFile()`) + - name: ReadFileOption + comment: | + ReadFileOption is a type of `Option` that can be passed to `jws.ReadFile` +options: + - ident: AcceptableSkew + interface: ValidateOption + argument_type: time.Duration + comment: | + WithAcceptableSkew specifies the duration in which exp and nbf + claims may differ by. This value should be positive + - ident: Clock + interface: ValidateOption + argument_type: Clock + comment: | + WithClock specifies the `Clock` to be used when verifying + exp and nbf claims. + - ident: Context + interface: ValidateOption + argument_type: context.Context + comment: | + WithContext allows you to specify a context.Context object to be used + with `jwt.Validate()` option. + + Please be aware that in the next major release of this library, + `jwt.Validate()`'s signature will change to include an explicit + `context.Context` object. + - ident: FlattenAudience + interface: GlobalOption + argument_type: bool + comment: | + WithFlattenAudience specifies if the "aud" claim should be flattened + to a single string upon the token being serialized to JSON. + + This is sometimes important when a JWT consumer does not understand that + the "aud" claim can actually take the form of an array of strings. + + The default value is `false`, which means that "aud" claims are always + rendered as a arrays of strings. This setting has a global effect, + and will change the behavior for all JWT serialization. + - ident: FormKey + interface: ParseOption + argument_type: string + comment: | + WithFormKey is used to specify header keys to search for tokens. + + While the type system allows this option to be passed to jwt.Parse() directly, + doing so will have no effect. Only use it for HTTP request parsing functions + - ident: HeaderKey + interface: ParseOption + argument_type: string + comment: | + WithHeaderKey is used to specify header keys to search for tokens. + + While the type system allows this option to be passed to `jwt.Parse()` directly, + doing so will have no effect. Only use it for HTTP request parsing functions + - ident: Token + interface: ParseOption + argument_type: Token + comment: | + WithToken specifies the token instance where the result JWT is stored + when parsing JWT tokensthat is used when parsing + - ident: Validate + interface: ParseOption + argument_type: bool + comment: | + WithValidate is passed to `Parse()` method to denote that the + validation of the JWT token should be performed (or not) after + a successful parsing of the incoming payload. + + This option is enabled by default. + + If you would like disable validation, + you must use `jwt.WithValidate(false)` or use `jwt.ParseInsecure()` + - ident: Verify + interface: ParseOption + argument_type: bool + comment: | + WithVerify is passed to `Parse()` method to denote that the + signature verification should be performed after a successful + deserialization of the incoming payload. + + This option is enabled by default. + + If you do not provide any verification key sources, `jwt.Parse()` + would return an error. + + If you would like to only parse the JWT payload and not verify it, + you must use `jwt.WithVerify(false)` or use `jwt.ParseInsecure()` + - ident: KeyProvider + interface: ParseOption + argument_type: jws.KeyProvider + comment: | + WithKeyProvider allows users to specify an object to provide keys to + sign/verify tokens using arbitrary code. Please read the documentation + for `jws.KeyProvider` in the `jws` package for details on how this works. + - ident: Pedantic + interface: ParseOption + argument_type: bool + comment: | + WithPedantic enables pedantic mode for parsing JWTs. Currently this only + applies to checking for the correct `typ` and/or `cty` when necessary. + - ident: EncryptOption + interface: EncryptOption + argument_type: jwe.EncryptOption + comment: | + WithEncryptOption provides an escape hatch for cases where extra options to + `(jws.Serializer).Encrypt()` must be specified when usng `jwt.Sign()`. Normally you do not + need to use this. + - ident: SignOption + interface: SignOption + argument_type: jws.SignOption + comment: | + WithSignOption provides an escape hatch for cases where extra options to + `jws.Sign()` must be specified when usng `jwt.Sign()`. Normally you do not + need to use this. + - ident: Validator + interface: ValidateOption + argument_type: Validator + comment: | + WithValidator validates the token with the given Validator. + + For example, in order to validate tokens that are only valid during August, you would write + + validator := jwt.ValidatorFunc(func(_ context.Context, t jwt.Token) error { + if time.Now().Month() != 8 { + return fmt.Errorf(`tokens are only valid during August!`) + } + return nil + }) + err := jwt.Validate(token, jwt.WithValidator(validator)) + - ident: FS + interface: ReadFileOption + argument_type: fs.FS + comment: | + WithFS specifies the source `fs.FS` object to read the file from. + - ident: NumericDateParsePrecision + interface: GlobalOption + argument_type: int + comment: | + WithNumericDateParsePrecision sets the precision up to which the + library uses to parse fractional dates found in the numeric date + fields. Default is 0 (second, no fractionals), max is 9 (nanosecond) + - ident: NumericDateFormatPrecision + interface: GlobalOption + argument_type: int + comment: | + WithNumericDateFormatPrecision sets the precision up to which the + library uses to format fractional dates found in the numeric date + fields. Default is 0 (second, no fractionals), max is 9 (nanosecond) + - ident: NumericDateParsePedantic + interface: GlobalOption + argument_type: bool + comment: | + WithNumericDateParsePedantic specifies if the parser should behave + in a pedantic manner when parsing numeric dates. Normally this library + attempts to interpret timestamps as a numeric value representing + number of seconds (with an optional fractional part), but if that fails + it tries to parse using a RFC3339 parser. This allows us to parse + payloads from non-comforming servers. + + However, when you set WithNumericDateParePedantic to `true`, the + RFC3339 parser is not tried, and we expect a numeric value strictly + + diff --git a/vendor/github.com/lestrrat-go/jwx/v2/jwt/options_gen.go b/vendor/github.com/lestrrat-go/jwx/v2/jwt/options_gen.go new file mode 100644 index 0000000..98cccf3 --- /dev/null +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwt/options_gen.go @@ -0,0 +1,372 @@ +// This file is auto-generated by internal/cmd/genoptions/main.go. DO NOT EDIT + +package jwt + +import ( + "context" + "io/fs" + "time" + + "github.com/lestrrat-go/jwx/v2/jwe" + "github.com/lestrrat-go/jwx/v2/jws" + "github.com/lestrrat-go/option" +) + +type Option = option.Interface + +// EncryptOption describes an Option that can be passed to (jwt.Serializer).Encrypt +type EncryptOption interface { + Option + encryptOption() +} + +type encryptOption struct { + Option +} + +func (*encryptOption) encryptOption() {} + +// GlobalOption describes an Option that can be passed to `Settings()`. +type GlobalOption interface { + Option + globalOption() +} + +type globalOption struct { + Option +} + +func (*globalOption) globalOption() {} + +// ParseOption describes an Option that can be passed to `jwt.Parse()`. +// ParseOption also implements ReadFileOption, therefore it may be +// safely pass them to `jwt.ReadFile()` +type ParseOption interface { + Option + parseOption() + readFileOption() +} + +type parseOption struct { + Option +} + +func (*parseOption) parseOption() {} + +func (*parseOption) readFileOption() {} + +// ReadFileOption is a type of `Option` that can be passed to `jws.ReadFile` +type ReadFileOption interface { + Option + readFileOption() +} + +type readFileOption struct { + Option +} + +func (*readFileOption) readFileOption() {} + +// SignParseOption describes an Option that can be passed to both `jwt.Sign()` or +// `jwt.Parse()` +type SignEncryptParseOption interface { + Option + parseOption() + encryptOption() + readFileOption() + signOption() +} + +type signEncryptParseOption struct { + Option +} + +func (*signEncryptParseOption) parseOption() {} + +func (*signEncryptParseOption) encryptOption() {} + +func (*signEncryptParseOption) readFileOption() {} + +func (*signEncryptParseOption) signOption() {} + +// SignOption describes an Option that can be passed to `jwt.Sign()` or +// (jwt.Serializer).Sign +type SignOption interface { + Option + signOption() +} + +type signOption struct { + Option +} + +func (*signOption) signOption() {} + +// ValidateOption describes an Option that can be passed to Validate(). +// ValidateOption also implements ParseOption, therefore it may be +// safely passed to `Parse()` (and thus `jwt.ReadFile()`) +type ValidateOption interface { + Option + parseOption() + readFileOption() + validateOption() +} + +type validateOption struct { + Option +} + +func (*validateOption) parseOption() {} + +func (*validateOption) readFileOption() {} + +func (*validateOption) validateOption() {} + +type identAcceptableSkew struct{} +type identClock struct{} +type identContext struct{} +type identEncryptOption struct{} +type identFS struct{} +type identFlattenAudience struct{} +type identFormKey struct{} +type identHeaderKey struct{} +type identKeyProvider struct{} +type identNumericDateFormatPrecision struct{} +type identNumericDateParsePedantic struct{} +type identNumericDateParsePrecision struct{} +type identPedantic struct{} +type identSignOption struct{} +type identToken struct{} +type identValidate struct{} +type identValidator struct{} +type identVerify struct{} + +func (identAcceptableSkew) String() string { + return "WithAcceptableSkew" +} + +func (identClock) String() string { + return "WithClock" +} + +func (identContext) String() string { + return "WithContext" +} + +func (identEncryptOption) String() string { + return "WithEncryptOption" +} + +func (identFS) String() string { + return "WithFS" +} + +func (identFlattenAudience) String() string { + return "WithFlattenAudience" +} + +func (identFormKey) String() string { + return "WithFormKey" +} + +func (identHeaderKey) String() string { + return "WithHeaderKey" +} + +func (identKeyProvider) String() string { + return "WithKeyProvider" +} + +func (identNumericDateFormatPrecision) String() string { + return "WithNumericDateFormatPrecision" +} + +func (identNumericDateParsePedantic) String() string { + return "WithNumericDateParsePedantic" +} + +func (identNumericDateParsePrecision) String() string { + return "WithNumericDateParsePrecision" +} + +func (identPedantic) String() string { + return "WithPedantic" +} + +func (identSignOption) String() string { + return "WithSignOption" +} + +func (identToken) String() string { + return "WithToken" +} + +func (identValidate) String() string { + return "WithValidate" +} + +func (identValidator) String() string { + return "WithValidator" +} + +func (identVerify) String() string { + return "WithVerify" +} + +// WithAcceptableSkew specifies the duration in which exp and nbf +// claims may differ by. This value should be positive +func WithAcceptableSkew(v time.Duration) ValidateOption { + return &validateOption{option.New(identAcceptableSkew{}, v)} +} + +// WithClock specifies the `Clock` to be used when verifying +// exp and nbf claims. +func WithClock(v Clock) ValidateOption { + return &validateOption{option.New(identClock{}, v)} +} + +// WithContext allows you to specify a context.Context object to be used +// with `jwt.Validate()` option. +// +// Please be aware that in the next major release of this library, +// `jwt.Validate()`'s signature will change to include an explicit +// `context.Context` object. +func WithContext(v context.Context) ValidateOption { + return &validateOption{option.New(identContext{}, v)} +} + +// WithEncryptOption provides an escape hatch for cases where extra options to +// `(jws.Serializer).Encrypt()` must be specified when usng `jwt.Sign()`. Normally you do not +// need to use this. +func WithEncryptOption(v jwe.EncryptOption) EncryptOption { + return &encryptOption{option.New(identEncryptOption{}, v)} +} + +// WithFS specifies the source `fs.FS` object to read the file from. +func WithFS(v fs.FS) ReadFileOption { + return &readFileOption{option.New(identFS{}, v)} +} + +// WithFlattenAudience specifies if the "aud" claim should be flattened +// to a single string upon the token being serialized to JSON. +// +// This is sometimes important when a JWT consumer does not understand that +// the "aud" claim can actually take the form of an array of strings. +// +// The default value is `false`, which means that "aud" claims are always +// rendered as a arrays of strings. This setting has a global effect, +// and will change the behavior for all JWT serialization. +func WithFlattenAudience(v bool) GlobalOption { + return &globalOption{option.New(identFlattenAudience{}, v)} +} + +// WithFormKey is used to specify header keys to search for tokens. +// +// While the type system allows this option to be passed to jwt.Parse() directly, +// doing so will have no effect. Only use it for HTTP request parsing functions +func WithFormKey(v string) ParseOption { + return &parseOption{option.New(identFormKey{}, v)} +} + +// WithHeaderKey is used to specify header keys to search for tokens. +// +// While the type system allows this option to be passed to `jwt.Parse()` directly, +// doing so will have no effect. Only use it for HTTP request parsing functions +func WithHeaderKey(v string) ParseOption { + return &parseOption{option.New(identHeaderKey{}, v)} +} + +// WithKeyProvider allows users to specify an object to provide keys to +// sign/verify tokens using arbitrary code. Please read the documentation +// for `jws.KeyProvider` in the `jws` package for details on how this works. +func WithKeyProvider(v jws.KeyProvider) ParseOption { + return &parseOption{option.New(identKeyProvider{}, v)} +} + +// WithNumericDateFormatPrecision sets the precision up to which the +// library uses to format fractional dates found in the numeric date +// fields. Default is 0 (second, no fractionals), max is 9 (nanosecond) +func WithNumericDateFormatPrecision(v int) GlobalOption { + return &globalOption{option.New(identNumericDateFormatPrecision{}, v)} +} + +// WithNumericDateParsePedantic specifies if the parser should behave +// in a pedantic manner when parsing numeric dates. Normally this library +// attempts to interpret timestamps as a numeric value representing +// number of seconds (with an optional fractional part), but if that fails +// it tries to parse using a RFC3339 parser. This allows us to parse +// payloads from non-comforming servers. +// +// However, when you set WithNumericDateParePedantic to `true`, the +// RFC3339 parser is not tried, and we expect a numeric value strictly +func WithNumericDateParsePedantic(v bool) GlobalOption { + return &globalOption{option.New(identNumericDateParsePedantic{}, v)} +} + +// WithNumericDateParsePrecision sets the precision up to which the +// library uses to parse fractional dates found in the numeric date +// fields. Default is 0 (second, no fractionals), max is 9 (nanosecond) +func WithNumericDateParsePrecision(v int) GlobalOption { + return &globalOption{option.New(identNumericDateParsePrecision{}, v)} +} + +// WithPedantic enables pedantic mode for parsing JWTs. Currently this only +// applies to checking for the correct `typ` and/or `cty` when necessary. +func WithPedantic(v bool) ParseOption { + return &parseOption{option.New(identPedantic{}, v)} +} + +// WithSignOption provides an escape hatch for cases where extra options to +// `jws.Sign()` must be specified when usng `jwt.Sign()`. Normally you do not +// need to use this. +func WithSignOption(v jws.SignOption) SignOption { + return &signOption{option.New(identSignOption{}, v)} +} + +// WithToken specifies the token instance where the result JWT is stored +// when parsing JWT tokensthat is used when parsing +func WithToken(v Token) ParseOption { + return &parseOption{option.New(identToken{}, v)} +} + +// WithValidate is passed to `Parse()` method to denote that the +// validation of the JWT token should be performed (or not) after +// a successful parsing of the incoming payload. +// +// This option is enabled by default. +// +// If you would like disable validation, +// you must use `jwt.WithValidate(false)` or use `jwt.ParseInsecure()` +func WithValidate(v bool) ParseOption { + return &parseOption{option.New(identValidate{}, v)} +} + +// WithValidator validates the token with the given Validator. +// +// For example, in order to validate tokens that are only valid during August, you would write +// +// validator := jwt.ValidatorFunc(func(_ context.Context, t jwt.Token) error { +// if time.Now().Month() != 8 { +// return fmt.Errorf(`tokens are only valid during August!`) +// } +// return nil +// }) +// err := jwt.Validate(token, jwt.WithValidator(validator)) +func WithValidator(v Validator) ValidateOption { + return &validateOption{option.New(identValidator{}, v)} +} + +// WithVerify is passed to `Parse()` method to denote that the +// signature verification should be performed after a successful +// deserialization of the incoming payload. +// +// This option is enabled by default. +// +// If you do not provide any verification key sources, `jwt.Parse()` +// would return an error. +// +// If you would like to only parse the JWT payload and not verify it, +// you must use `jwt.WithVerify(false)` or use `jwt.ParseInsecure()` +func WithVerify(v bool) ParseOption { + return &parseOption{option.New(identVerify{}, v)} +} diff --git a/vendor/github.com/lestrrat-go/jwx/jwt/serialize.go b/vendor/github.com/lestrrat-go/jwx/v2/jwt/serialize.go similarity index 51% rename from vendor/github.com/lestrrat-go/jwx/jwt/serialize.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwt/serialize.go index a3665ce..84f3e71 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwt/serialize.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwt/serialize.go @@ -3,11 +3,9 @@ package jwt import ( "fmt" - "github.com/lestrrat-go/jwx/internal/json" - "github.com/lestrrat-go/jwx/jwa" - "github.com/lestrrat-go/jwx/jwe" - "github.com/lestrrat-go/jwx/jws" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/internal/json" + "github.com/lestrrat-go/jwx/v2/jwe" + "github.com/lestrrat-go/jwx/v2/jws" ) type SerializeCtx interface { @@ -32,6 +30,16 @@ type SerializeStep interface { Serialize(SerializeCtx, interface{}) (interface{}, error) } +// errStep is always an error. used to indicate that a method like +// serializer.Sign or Encrypt already errored out on configuration +type errStep struct { + err error +} + +func (e errStep) Serialize(_ SerializeCtx, _ interface{}) (interface{}, error) { + return nil, e.err +} + // Serializer is a generic serializer for JWTs. Whereas other conveinience // functions can only do one thing (such as generate a JWS signed JWT), // Using this construct you can serialize the token however you want. @@ -79,12 +87,12 @@ type jsonSerializer struct{} func (jsonSerializer) Serialize(_ SerializeCtx, v interface{}) (interface{}, error) { token, ok := v.(Token) if !ok { - return nil, errors.Errorf(`invalid input: expected jwt.Token`) + return nil, fmt.Errorf(`invalid input: expected jwt.Token`) } buf, err := json.Marshal(token) if err != nil { - return nil, errors.Errorf(`failed to serialize as JSON`) + return nil, fmt.Errorf(`failed to serialize as JSON`) } return buf, nil } @@ -104,7 +112,7 @@ func setTypeOrCty(ctx SerializeCtx, hdrs genericHeader) error { // We are executed immediately after json marshaling if _, ok := hdrs.Get(typKey); !ok { if err := hdrs.Set(typKey, `JWT`); err != nil { - return errors.Wrapf(err, `failed to set %s key to "JWT"`, typKey) + return fmt.Errorf(`failed to set %s key to "JWT": %w`, typKey, err) } } } else { @@ -112,7 +120,7 @@ func setTypeOrCty(ctx SerializeCtx, hdrs genericHeader) error { // If this is part of a nested sequence, we should set cty = 'JWT' // https://datatracker.ietf.org/doc/html/rfc7519#section-5.2 if err := hdrs.Set(ctyKey, `JWT`); err != nil { - return errors.Wrapf(err, `failed to set %s key to "JWT"`, ctyKey) + return fmt.Errorf(`failed to set %s key to "JWT": %w`, ctyKey, err) } } } @@ -120,60 +128,65 @@ func setTypeOrCty(ctx SerializeCtx, hdrs genericHeader) error { } type jwsSerializer struct { - alg jwa.SignatureAlgorithm - key interface{} - options []SignOption + options []jws.SignOption } func (s *jwsSerializer) Serialize(ctx SerializeCtx, v interface{}) (interface{}, error) { payload, ok := v.([]byte) if !ok { - return nil, errors.New(`expected []byte as input`) + return nil, fmt.Errorf(`expected []byte as input`) } - var hdrs jws.Headers - //nolint:forcetypeassert for _, option := range s.options { - switch option.Ident() { - case identJwsHeaders{}: - hdrs = option.Value().(jws.Headers) + pc, ok := option.Value().(interface{ Protected(jws.Headers) jws.Headers }) + if !ok { + continue + } + hdrs := pc.Protected(jws.NewHeaders()) + if err := setTypeOrCty(ctx, hdrs); err != nil { + return nil, err // this is already wrapped } - } - if hdrs == nil { - hdrs = jws.NewHeaders() - } - - if err := setTypeOrCty(ctx, hdrs); err != nil { - return nil, err // this is already wrapped - } - - // JWTs MUST NOT use b64 = false - // https://datatracker.ietf.org/doc/html/rfc7797#section-7 - if v, ok := hdrs.Get("b64"); ok { - if bval, bok := v.(bool); bok { - if !bval { // b64 = false - return nil, errors.New(`b64 cannot be false for JWTs`) + // JWTs MUST NOT use b64 = false + // https://datatracker.ietf.org/doc/html/rfc7797#section-7 + if v, ok := hdrs.Get("b64"); ok { + if bval, bok := v.(bool); bok { + if !bval { // b64 = false + return nil, fmt.Errorf(`b64 cannot be false for JWTs`) + } } } } - return jws.Sign(payload, s.alg, s.key, jws.WithHeaders(hdrs)) + return jws.Sign(payload, s.options...) } -func (s *Serializer) Sign(alg jwa.SignatureAlgorithm, key interface{}, options ...SignOption) *Serializer { +func (s *Serializer) Sign(options ...SignOption) *Serializer { + var soptions []jws.SignOption + if l := len(options); l > 0 { + // we need to from SignOption to Option because ... reasons + // (todo: when go1.18 prevails, use type parameters + rawoptions := make([]Option, l) + for i, option := range options { + rawoptions[i] = option + } + + converted, err := toSignOptions(rawoptions...) + if err != nil { + return s.Step(errStep{fmt.Errorf(`(jwt.Serializer).Sign: failed to convert options into jws.SignOption: %w`, err)}) + } + soptions = converted + } + return s.sign(soptions...) +} + +func (s *Serializer) sign(options ...jws.SignOption) *Serializer { return s.Step(&jwsSerializer{ - alg: alg, - key: key, options: options, }) } type jweSerializer struct { - keyalg jwa.KeyEncryptionAlgorithm - key interface{} - contentalg jwa.ContentEncryptionAlgorithm - compressalg jwa.CompressionAlgorithm - options []EncryptOption + options []jwe.EncryptOption } func (s *jweSerializer) Serialize(ctx SerializeCtx, v interface{}) (interface{}, error) { @@ -182,32 +195,44 @@ func (s *jweSerializer) Serialize(ctx SerializeCtx, v interface{}) (interface{}, return nil, fmt.Errorf(`expected []byte as input`) } - var hdrs jwe.Headers - //nolint:forcetypeassert - for _, option := range s.options { - switch option.Ident() { - case identJweHeaders{}: - hdrs = option.Value().(jwe.Headers) - } - } - - if hdrs == nil { - hdrs = jwe.NewHeaders() - } - + hdrs := jwe.NewHeaders() if err := setTypeOrCty(ctx, hdrs); err != nil { return nil, err // this is already wrapped } - return jwe.Encrypt(payload, s.keyalg, s.key, s.contentalg, s.compressalg, jwe.WithProtectedHeaders(hdrs)) + + options := append([]jwe.EncryptOption{jwe.WithMergeProtectedHeaders(true), jwe.WithProtectedHeaders(hdrs)}, s.options...) + return jwe.Encrypt(payload, options...) } -func (s *Serializer) Encrypt(keyalg jwa.KeyEncryptionAlgorithm, key interface{}, contentalg jwa.ContentEncryptionAlgorithm, compressalg jwa.CompressionAlgorithm, options ...EncryptOption) *Serializer { +// Encrypt specifies the JWT to be serialized as an encrypted payload. +// +// One notable difference between this method and `jwe.Encrypt()` is that +// while `jwe.Encrypt()` OVERWRITES the previous headers when `jwe.WithProtectedHeaders()` +// is provided, this method MERGES them. This is due to the fact that we +// MUST add some extra headers to construct a proper JWE message. +// Be careful when you pass multiple `jwe.EncryptOption`s. +func (s *Serializer) Encrypt(options ...EncryptOption) *Serializer { + var eoptions []jwe.EncryptOption + if l := len(options); l > 0 { + // we need to from SignOption to Option because ... reasons + // (todo: when go1.18 prevails, use type parameters + rawoptions := make([]Option, l) + for i, option := range options { + rawoptions[i] = option + } + + converted, err := toEncryptOptions(rawoptions...) + if err != nil { + return s.Step(errStep{fmt.Errorf(`(jwt.Serializer).Encrypt: failed to convert options into jwe.EncryptOption: %w`, err)}) + } + eoptions = converted + } + return s.encrypt(eoptions...) +} + +func (s *Serializer) encrypt(options ...jwe.EncryptOption) *Serializer { return s.Step(&jweSerializer{ - keyalg: keyalg, - key: key, - contentalg: contentalg, - compressalg: compressalg, - options: options, + options: options, }) } @@ -225,14 +250,14 @@ func (s *Serializer) Serialize(t Token) ([]byte, error) { ctx.step = i v, err := step.Serialize(&ctx, payload) if err != nil { - return nil, errors.Wrapf(err, `failed to serialize token at step #%d`, i+1) + return nil, fmt.Errorf(`failed to serialize token at step #%d: %w`, i+1, err) } payload = v } res, ok := payload.([]byte) if !ok { - return nil, errors.New(`invalid serialization produced`) + return nil, fmt.Errorf(`invalid serialization produced`) } return res, nil diff --git a/vendor/github.com/lestrrat-go/jwx/jwt/token_gen.go b/vendor/github.com/lestrrat-go/jwx/v2/jwt/token_gen.go similarity index 87% rename from vendor/github.com/lestrrat-go/jwx/jwt/token_gen.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwt/token_gen.go index 448d8c4..4a09a3b 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwt/token_gen.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwt/token_gen.go @@ -5,17 +5,17 @@ package jwt import ( "bytes" "context" + "fmt" "sort" "sync" "time" "github.com/lestrrat-go/iter/mapiter" - "github.com/lestrrat-go/jwx/internal/base64" - "github.com/lestrrat-go/jwx/internal/iter" - "github.com/lestrrat-go/jwx/internal/json" - "github.com/lestrrat-go/jwx/internal/pool" - "github.com/lestrrat-go/jwx/jwt/internal/types" - "github.com/pkg/errors" + "github.com/lestrrat-go/jwx/v2/internal/base64" + "github.com/lestrrat-go/jwx/v2/internal/iter" + "github.com/lestrrat-go/jwx/v2/internal/json" + "github.com/lestrrat-go/jwx/v2/internal/pool" + "github.com/lestrrat-go/jwx/v2/jwt/internal/types" ) const ( @@ -210,21 +210,21 @@ func (t *stdToken) setNoLock(name string, value interface{}) error { case AudienceKey: var acceptor types.StringList if err := acceptor.Accept(value); err != nil { - return errors.Wrapf(err, `invalid value for %s key`, AudienceKey) + return fmt.Errorf(`invalid value for %s key: %w`, AudienceKey, err) } t.audience = acceptor return nil case ExpirationKey: var acceptor types.NumericDate if err := acceptor.Accept(value); err != nil { - return errors.Wrapf(err, `invalid value for %s key`, ExpirationKey) + return fmt.Errorf(`invalid value for %s key: %w`, ExpirationKey, err) } t.expiration = &acceptor return nil case IssuedAtKey: var acceptor types.NumericDate if err := acceptor.Accept(value); err != nil { - return errors.Wrapf(err, `invalid value for %s key`, IssuedAtKey) + return fmt.Errorf(`invalid value for %s key: %w`, IssuedAtKey, err) } t.issuedAt = &acceptor return nil @@ -233,17 +233,17 @@ func (t *stdToken) setNoLock(name string, value interface{}) error { t.issuer = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, IssuerKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, IssuerKey, value) case JwtIDKey: if v, ok := value.(string); ok { t.jwtID = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, JwtIDKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, JwtIDKey, value) case NotBeforeKey: var acceptor types.NumericDate if err := acceptor.Accept(value); err != nil { - return errors.Wrapf(err, `invalid value for %s key`, NotBeforeKey) + return fmt.Errorf(`invalid value for %s key: %w`, NotBeforeKey, err) } t.notBefore = &acceptor return nil @@ -252,7 +252,7 @@ func (t *stdToken) setNoLock(name string, value interface{}) error { t.subject = &v return nil } - return errors.Errorf(`invalid value for %s key: %T`, SubjectKey, value) + return fmt.Errorf(`invalid value for %s key: %T`, SubjectKey, value) default: if t.privateClaims == nil { t.privateClaims = map[string]interface{}{} @@ -388,7 +388,7 @@ LOOP: for { tok, err := dec.Token() if err != nil { - return errors.Wrap(err, `error reading token`) + return fmt.Errorf(`error reading token: %w`, err) } switch tok := tok.(type) { case json.Delim: @@ -397,45 +397,45 @@ LOOP: if tok == '}' { // End of object break LOOP } else if tok != '{' { - return errors.Errorf(`expected '{', but got '%c'`, tok) + return fmt.Errorf(`expected '{', but got '%c'`, tok) } case string: // Objects can only have string keys switch tok { case AudienceKey: var decoded types.StringList if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, AudienceKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, AudienceKey, err) } t.audience = decoded case ExpirationKey: var decoded types.NumericDate if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, ExpirationKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, ExpirationKey, err) } t.expiration = &decoded case IssuedAtKey: var decoded types.NumericDate if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, IssuedAtKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, IssuedAtKey, err) } t.issuedAt = &decoded case IssuerKey: if err := json.AssignNextStringToken(&t.issuer, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, IssuerKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, IssuerKey, err) } case JwtIDKey: if err := json.AssignNextStringToken(&t.jwtID, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, JwtIDKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, JwtIDKey, err) } case NotBeforeKey: var decoded types.NumericDate if err := dec.Decode(&decoded); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, NotBeforeKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, NotBeforeKey, err) } t.notBefore = &decoded case SubjectKey: if err := json.AssignNextStringToken(&t.subject, dec); err != nil { - return errors.Wrapf(err, `failed to decode value for key %s`, SubjectKey) + return fmt.Errorf(`failed to decode value for key %s: %w`, SubjectKey, err) } default: if dc := t.dc; dc != nil { @@ -452,18 +452,16 @@ LOOP: t.setNoLock(tok, decoded) continue } - return errors.Wrapf(err, `could not decode field %s`, tok) + return fmt.Errorf(`could not decode field %s: %w`, tok, err) } default: - return errors.Errorf(`invalid token %T`, tok) + return fmt.Errorf(`invalid token %T`, tok) } } return nil } func (t stdToken) MarshalJSON() ([]byte, error) { - t.mu.RLock() - defer t.mu.RUnlock() buf := pool.GetBytesBuffer() defer pool.ReleaseBytesBuffer(buf) buf.WriteByte('{') @@ -479,7 +477,7 @@ func (t stdToken) MarshalJSON() ([]byte, error) { switch f { case AudienceKey: if err := json.EncodeAudience(enc, pair.Value.([]string)); err != nil { - return nil, errors.Wrap(err, `failed to encode "aud"`) + return nil, fmt.Errorf(`failed to encode "aud": %w`, err) } continue case ExpirationKey, IssuedAtKey, NotBeforeKey: @@ -493,7 +491,7 @@ func (t stdToken) MarshalJSON() ([]byte, error) { buf.WriteRune('"') default: if err := enc.Encode(v); err != nil { - return nil, errors.Wrapf(err, `failed to marshal field %s`, f) + return nil, fmt.Errorf(`failed to marshal field %s: %w`, f, err) } buf.Truncate(buf.Len() - 1) } diff --git a/vendor/github.com/lestrrat-go/jwx/jwt/validate.go b/vendor/github.com/lestrrat-go/jwx/v2/jwt/validate.go similarity index 59% rename from vendor/github.com/lestrrat-go/jwx/jwt/validate.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwt/validate.go index 631e384..315035e 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwt/validate.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwt/validate.go @@ -2,10 +2,9 @@ package jwt import ( "context" + "fmt" "strconv" "time" - - "github.com/pkg/errors" ) type Clock interface { @@ -22,7 +21,7 @@ func isSupportedTimeClaim(c string) error { case ExpirationKey, IssuedAtKey, NotBeforeKey: return nil } - return NewValidationError(errors.Errorf(`unsupported time claim %s`, strconv.Quote(c))) + return NewValidationError(fmt.Errorf(`unsupported time claim %s`, strconv.Quote(c))) } func timeClaim(t Token, clock Clock, c string) time.Time { @@ -120,7 +119,7 @@ func MinDeltaIs(c1, c2 string, dur time.Duration) Validator { } } -func (iitr *isInTimeRange) Validate(ctx context.Context, t Token) error { +func (iitr *isInTimeRange) Validate(ctx context.Context, t Token) ValidationError { clock := ValidationCtxClock(ctx) // MUST be populated skew := ValidationCtxSkew(ctx) // MUST be populated // We don't check if the claims already exist, because we already did that @@ -130,11 +129,11 @@ func (iitr *isInTimeRange) Validate(ctx context.Context, t Token) error { if iitr.less { // t1 - t2 <= iitr.dur // t1 - t2 < iitr.dur + skew if t1.Sub(t2) > iitr.dur+skew { - return NewValidationError(errors.Errorf(`iitr between %s and %s exceeds %s (skew %s)`, iitr.c1, iitr.c2, iitr.dur, skew)) + return NewValidationError(fmt.Errorf(`iitr between %s and %s exceeds %s (skew %s)`, iitr.c1, iitr.c2, iitr.dur, skew)) } } else { if t1.Sub(t2) < iitr.dur-skew { - return NewValidationError(errors.Errorf(`iitr between %s and %s is less than %s (skew %s)`, iitr.c1, iitr.c2, iitr.dur, skew)) + return NewValidationError(fmt.Errorf(`iitr between %s and %s is less than %s (skew %s)`, iitr.c1, iitr.c2, iitr.dur, skew)) } } return nil @@ -143,6 +142,7 @@ func (iitr *isInTimeRange) Validate(ctx context.Context, t Token) error { type ValidationError interface { error isValidationError() + Unwrap() error } func NewValidationError(err error) ValidationError { @@ -155,41 +155,145 @@ type validationError struct { } func (validationError) isValidationError() {} +func (err *validationError) Unwrap() error { + return err.error +} -var errTokenExpired = NewValidationError(errors.New(`exp not satisfied`)) -var errInvalidIssuedAt = NewValidationError(errors.New(`iat not satisfied`)) -var errTokenNotYetValid = NewValidationError(errors.New(`nbf not satisfied`)) +type missingRequiredClaimError struct { + claim string +} + +func (err *missingRequiredClaimError) Error() string { + return fmt.Sprintf("%q not satisfied: required claim not found", err.claim) +} + +func (err *missingRequiredClaimError) Is(target error) bool { + _, ok := target.(*missingRequiredClaimError) + return ok +} + +func (err *missingRequiredClaimError) isValidationError() {} +func (*missingRequiredClaimError) Unwrap() error { return nil } + +type invalidAudienceError struct { + error +} + +func (err *invalidAudienceError) Is(target error) bool { + _, ok := target.(*invalidAudienceError) + return ok +} + +func (err *invalidAudienceError) isValidationError() {} +func (err *invalidAudienceError) Unwrap() error { + return err.error +} + +func (err *invalidAudienceError) Error() string { + if err.error == nil { + return `"aud" not satisfied` + } + return err.error.Error() +} + +type invalidIssuerError struct { + error +} + +func (err *invalidIssuerError) Is(target error) bool { + _, ok := target.(*invalidIssuerError) + return ok +} + +func (err *invalidIssuerError) isValidationError() {} +func (err *invalidIssuerError) Unwrap() error { + return err.error +} + +func (err *invalidIssuerError) Error() string { + if err.error == nil { + return `"iss" not satisfied` + } + return err.error.Error() +} + +var errTokenExpired = NewValidationError(fmt.Errorf(`"exp" not satisfied`)) +var errInvalidIssuedAt = NewValidationError(fmt.Errorf(`"iat" not satisfied`)) +var errTokenNotYetValid = NewValidationError(fmt.Errorf(`"nbf" not satisfied`)) +var errInvalidAudience = &invalidAudienceError{} +var errInvalidIssuer = &invalidIssuerError{} +var errRequiredClaim = &missingRequiredClaimError{} // ErrTokenExpired returns the immutable error used when `exp` claim -// is not satisfied -func ErrTokenExpired() error { +// is not satisfied. +// +// The return value should only be used for comparison using `errors.Is()` +func ErrTokenExpired() ValidationError { return errTokenExpired } // ErrInvalidIssuedAt returns the immutable error used when `iat` claim // is not satisfied -func ErrInvalidIssuedAt() error { +// +// The return value should only be used for comparison using `errors.Is()` +func ErrInvalidIssuedAt() ValidationError { return errInvalidIssuedAt } -func ErrTokenNotYetValid() error { +// ErrTokenNotYetValid returns the immutable error used when `nbf` claim +// is not satisfied +// +// The return value should only be used for comparison using `errors.Is()` +func ErrTokenNotYetValid() ValidationError { return errTokenNotYetValid } +// ErrInvalidAudience returns the immutable error used when `aud` claim +// is not satisfied +// +// The return value should only be used for comparison using `errors.Is()` +func ErrInvalidAudience() ValidationError { + return errInvalidAudience +} + +// ErrInvalidIssuer returns the immutable error used when `iss` claim +// is not satisfied +// +// The return value should only be used for comparison using `errors.Is()` +func ErrInvalidIssuer() ValidationError { + return errInvalidIssuer +} + +// ErrMissingRequiredClaim should not have been exported, and will be +// removed in a future release. Use `ErrRequiredClaim()` instead to get +// an error to be used in `errors.Is()` +// +// This function should not have been implemented as a constructor. +// but rather a means to retrieve an opaque and immutable error value +// that could be passed to `errors.Is()`. +func ErrMissingRequiredClaim(name string) ValidationError { + return &missingRequiredClaimError{claim: name} +} + +// ErrRequiredClaim returns the immutable error used when the claim +// specified by `jwt.IsRequired()` is not present. +// +// The return value should only be used for comparison using `errors.Is()` +func ErrRequiredClaim() ValidationError { + return errRequiredClaim +} + // Validator describes interface to validate a Token. type Validator interface { // Validate should return an error if a required conditions is not met. - // This method will be changed in the next major release to return - // jwt.ValidationError instead of error to force users to return - // a validation error even for user-specified validators - Validate(context.Context, Token) error + Validate(context.Context, Token) ValidationError } // ValidatorFunc is a type of Validator that does not have any // state, that is implemented as a function -type ValidatorFunc func(context.Context, Token) error +type ValidatorFunc func(context.Context, Token) ValidationError -func (vf ValidatorFunc) Validate(ctx context.Context, tok Token) error { +func (vf ValidatorFunc) Validate(ctx context.Context, tok Token) ValidationError { return vf(ctx, tok) } @@ -228,7 +332,7 @@ func IsExpirationValid() Validator { return ValidatorFunc(isExpirationValid) } -func isExpirationValid(ctx context.Context, t Token) error { +func isExpirationValid(ctx context.Context, t Token) ValidationError { if tv := t.Expiration(); !tv.IsZero() && tv.Unix() != 0 { clock := ValidationCtxClock(ctx) // MUST be populated now := clock.Now().Truncate(time.Second) @@ -252,7 +356,7 @@ func IsIssuedAtValid() Validator { return ValidatorFunc(isIssuedAtValid) } -func isIssuedAtValid(ctx context.Context, t Token) error { +func isIssuedAtValid(ctx context.Context, t Token) ValidationError { if tv := t.IssuedAt(); !tv.IsZero() && tv.Unix() != 0 { clock := ValidationCtxClock(ctx) // MUST be populated now := clock.Now().Truncate(time.Second) @@ -276,7 +380,7 @@ func IsNbfValid() Validator { return ValidatorFunc(isNbfValid) } -func isNbfValid(ctx context.Context, t Token) error { +func isNbfValid(ctx context.Context, t Token) ValidationError { if tv := t.NotBefore(); !tv.IsZero() && tv.Unix() != 0 { clock := ValidationCtxClock(ctx) // MUST be populated now := clock.Now().Truncate(time.Second) @@ -291,8 +395,9 @@ func isNbfValid(ctx context.Context, t Token) error { } type claimContainsString struct { - name string - value string + name string + value string + makeErr func(error) ValidationError } // ClaimContainsString can be used to check if the claim called `name`, which is @@ -300,8 +405,9 @@ type claimContainsString struct { // implementation this will probably only work for `aud` fields. func ClaimContainsString(name, value string) Validator { return claimContainsString{ - name: name, - value: value, + name: name, + value: value, + makeErr: NewValidationError, } } @@ -312,7 +418,7 @@ func IsValidationError(err error) bool { return true default: switch err.(type) { - case *validationError: + case *validationError, *invalidAudienceError, *invalidIssuerError, *missingRequiredClaimError: return true default: return false @@ -320,33 +426,43 @@ func IsValidationError(err error) bool { } } -func (ccs claimContainsString) Validate(_ context.Context, t Token) error { +func (ccs claimContainsString) Validate(_ context.Context, t Token) ValidationError { v, ok := t.Get(ccs.name) if !ok { - return NewValidationError(errors.Errorf(`claim %q not found`, ccs.name)) + return ccs.makeErr(fmt.Errorf(`claim %q not found`, ccs.name)) } list, ok := v.([]string) if !ok { - return NewValidationError(errors.Errorf(`claim %q must be a []string (got %T)`, ccs.name, v)) + return ccs.makeErr(fmt.Errorf(`claim %q must be a []string (got %T)`, ccs.name, v)) } - var found bool for _, v := range list { if v == ccs.value { - found = true - break + return nil } } - if !found { - return NewValidationError(errors.Errorf(`%s not satisfied`, ccs.name)) + return ccs.makeErr(fmt.Errorf(`%q not satisfied`, ccs.name)) +} + +func makeInvalidAudienceError(err error) ValidationError { + return &invalidAudienceError{error: err} +} + +// audienceClaimContainsString can be used to check if the audience claim, which is +// expected to be a list of strings, contains `value`. +func audienceClaimContainsString(value string) Validator { + return claimContainsString{ + name: AudienceKey, + value: value, + makeErr: makeInvalidAudienceError, } - return nil } type claimValueIs struct { - name string - value interface{} + name string + value interface{} + makeErr func(error) ValidationError } // ClaimValueIs creates a Validator that checks if the value of claim `name` @@ -354,20 +470,38 @@ type claimValueIs struct { // and therefore complex comparisons may fail using this code. If you // need to do more, use a custom Validator. func ClaimValueIs(name string, value interface{}) Validator { - return &claimValueIs{name: name, value: value} + return &claimValueIs{ + name: name, + value: value, + makeErr: NewValidationError, + } } -func (cv *claimValueIs) Validate(_ context.Context, t Token) error { +func (cv *claimValueIs) Validate(_ context.Context, t Token) ValidationError { v, ok := t.Get(cv.name) if !ok { - return NewValidationError(errors.Errorf(`%q not satisfied: claim %q does not exist`, cv.name, cv.name)) + return cv.makeErr(fmt.Errorf(`%q not satisfied: claim %q does not exist`, cv.name, cv.name)) } if v != cv.value { - return NewValidationError(errors.Errorf(`%q not satisfied: values do not match`, cv.name)) + return cv.makeErr(fmt.Errorf(`%q not satisfied: values do not match`, cv.name)) } return nil } +func makeIssuerClaimError(err error) ValidationError { + return &invalidIssuerError{error: err} +} + +// issuerClaimValueIs creates a Validator that checks if the issuer claim +// matches `value`. +func issuerClaimValueIs(value string) Validator { + return &claimValueIs{ + name: IssuerKey, + value: value, + makeErr: makeIssuerClaimError, + } +} + // IsRequired creates a Validator that checks if the required claim `name` // exists in the token func IsRequired(name string) Validator { @@ -376,10 +510,11 @@ func IsRequired(name string) Validator { type isRequired string -func (ir isRequired) Validate(_ context.Context, t Token) error { - _, ok := t.Get(string(ir)) +func (ir isRequired) Validate(_ context.Context, t Token) ValidationError { + name := string(ir) + _, ok := t.Get(name) if !ok { - return NewValidationError(errors.Errorf(`required claim %q was not found`, string(ir))) + return &missingRequiredClaimError{claim: name} } return nil } diff --git a/vendor/github.com/lestrrat-go/jwx/jwx.go b/vendor/github.com/lestrrat-go/jwx/v2/jwx.go similarity index 88% rename from vendor/github.com/lestrrat-go/jwx/jwx.go rename to vendor/github.com/lestrrat-go/jwx/v2/jwx.go index 24c9653..7e4358f 100644 --- a/vendor/github.com/lestrrat-go/jwx/jwx.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/jwx.go @@ -1,4 +1,5 @@ -//go:generate ./gen.sh +//go:generate ./tools/cmd/genreadfile.sh +//go:generate ./tools/cmd/genoptions.sh //go:generate stringer -type=FormatKind //go:generate mv formatkind_string.go formatkind_string_gen.go @@ -14,15 +15,15 @@ // Examples are stored in a separate Go module (to avoid adding // dependencies to this module), and thus does not appear in the // online documentation for this module. -// You can find the examples in Github at https://github.com/lestrrat-go/jwx/examples +// You can find the examples in Github at https://github.com/lestrrat-go/jwx/tree/v2/examples // -// You can find more high level documentation at Github (https://github.com/lestrrat-go/jwx) +// You can find more high level documentation at Github (https://github.com/lestrrat-go/jwx/tree/v2) // // FAQ style documentation can be found in the repository (https://github.com/lestrrat-go/jwx/tree/develop/v2/docs) package jwx import ( - "github.com/lestrrat-go/jwx/internal/json" + "github.com/lestrrat-go/jwx/v2/internal/json" ) // DecoderSettings gives you a access to configure the "encoding/json".Decoder diff --git a/vendor/github.com/lestrrat-go/jwx/options.go b/vendor/github.com/lestrrat-go/jwx/v2/options.go similarity index 100% rename from vendor/github.com/lestrrat-go/jwx/options.go rename to vendor/github.com/lestrrat-go/jwx/v2/options.go diff --git a/vendor/github.com/lestrrat-go/jwx/x25519/x25519.go b/vendor/github.com/lestrrat-go/jwx/v2/x25519/x25519.go similarity index 95% rename from vendor/github.com/lestrrat-go/jwx/x25519/x25519.go rename to vendor/github.com/lestrrat-go/jwx/v2/x25519/x25519.go index 4ea70af..0f9e32c 100644 --- a/vendor/github.com/lestrrat-go/jwx/x25519/x25519.go +++ b/vendor/github.com/lestrrat-go/jwx/v2/x25519/x25519.go @@ -4,11 +4,10 @@ import ( "bytes" "crypto" cryptorand "crypto/rand" + "fmt" "io" "golang.org/x/crypto/curve25519" - - "github.com/pkg/errors" ) // This mirrors ed25519's structure for private/public "keys". jwx @@ -81,12 +80,12 @@ func (priv PrivateKey) Seed() []byte { func NewKeyFromSeed(seed []byte) (PrivateKey, error) { privateKey := make([]byte, PrivateKeySize) if len(seed) != SeedSize { - return nil, errors.Errorf("unexpected seed size: %d", len(seed)) + return nil, fmt.Errorf("unexpected seed size: %d", len(seed)) } copy(privateKey, seed) public, err := curve25519.X25519(seed, curve25519.Basepoint) if err != nil { - return nil, errors.Wrap(err, "failed to compute public key") + return nil, fmt.Errorf(`failed to compute public key: %w`, err) } copy(privateKey[SeedSize:], public) diff --git a/vendor/github.com/pkg/errors/.gitignore b/vendor/github.com/pkg/errors/.gitignore deleted file mode 100644 index daf913b..0000000 --- a/vendor/github.com/pkg/errors/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test -*.prof diff --git a/vendor/github.com/pkg/errors/.travis.yml b/vendor/github.com/pkg/errors/.travis.yml deleted file mode 100644 index 9159de0..0000000 --- a/vendor/github.com/pkg/errors/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: go -go_import_path: github.com/pkg/errors -go: - - 1.11.x - - 1.12.x - - 1.13.x - - tip - -script: - - make check diff --git a/vendor/github.com/pkg/errors/LICENSE b/vendor/github.com/pkg/errors/LICENSE deleted file mode 100644 index 835ba3e..0000000 --- a/vendor/github.com/pkg/errors/LICENSE +++ /dev/null @@ -1,23 +0,0 @@ -Copyright (c) 2015, Dave Cheney -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. - -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 HOLDER 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/github.com/pkg/errors/Makefile b/vendor/github.com/pkg/errors/Makefile deleted file mode 100644 index ce9d7cd..0000000 --- a/vendor/github.com/pkg/errors/Makefile +++ /dev/null @@ -1,44 +0,0 @@ -PKGS := github.com/pkg/errors -SRCDIRS := $(shell go list -f '{{.Dir}}' $(PKGS)) -GO := go - -check: test vet gofmt misspell unconvert staticcheck ineffassign unparam - -test: - $(GO) test $(PKGS) - -vet: | test - $(GO) vet $(PKGS) - -staticcheck: - $(GO) get honnef.co/go/tools/cmd/staticcheck - staticcheck -checks all $(PKGS) - -misspell: - $(GO) get github.com/client9/misspell/cmd/misspell - misspell \ - -locale GB \ - -error \ - *.md *.go - -unconvert: - $(GO) get github.com/mdempsky/unconvert - unconvert -v $(PKGS) - -ineffassign: - $(GO) get github.com/gordonklaus/ineffassign - find $(SRCDIRS) -name '*.go' | xargs ineffassign - -pedantic: check errcheck - -unparam: - $(GO) get mvdan.cc/unparam - unparam ./... - -errcheck: - $(GO) get github.com/kisielk/errcheck - errcheck $(PKGS) - -gofmt: - @echo Checking code is gofmted - @test -z "$(shell gofmt -s -l -d -e $(SRCDIRS) | tee /dev/stderr)" diff --git a/vendor/github.com/pkg/errors/README.md b/vendor/github.com/pkg/errors/README.md deleted file mode 100644 index 54dfdcb..0000000 --- a/vendor/github.com/pkg/errors/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) [![Sourcegraph](https://sourcegraph.com/github.com/pkg/errors/-/badge.svg)](https://sourcegraph.com/github.com/pkg/errors?badge) - -Package errors provides simple error handling primitives. - -`go get github.com/pkg/errors` - -The traditional error handling idiom in Go is roughly akin to -```go -if err != nil { - return err -} -``` -which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error. - -## Adding context to an error - -The errors.Wrap function returns a new error that adds context to the original error. For example -```go -_, err := ioutil.ReadAll(r) -if err != nil { - return errors.Wrap(err, "read failed") -} -``` -## Retrieving the cause of an error - -Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`. -```go -type causer interface { - Cause() error -} -``` -`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example: -```go -switch err := errors.Cause(err).(type) { -case *MyError: - // handle specifically -default: - // unknown error -} -``` - -[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors). - -## Roadmap - -With the upcoming [Go2 error proposals](https://go.googlesource.com/proposal/+/master/design/go2draft.md) this package is moving into maintenance mode. The roadmap for a 1.0 release is as follows: - -- 0.9. Remove pre Go 1.9 and Go 1.10 support, address outstanding pull requests (if possible) -- 1.0. Final release. - -## Contributing - -Because of the Go2 errors changes, this package is not accepting proposals for new functionality. With that said, we welcome pull requests, bug fixes and issue reports. - -Before sending a PR, please discuss your change by raising an issue. - -## License - -BSD-2-Clause diff --git a/vendor/github.com/pkg/errors/appveyor.yml b/vendor/github.com/pkg/errors/appveyor.yml deleted file mode 100644 index a932ead..0000000 --- a/vendor/github.com/pkg/errors/appveyor.yml +++ /dev/null @@ -1,32 +0,0 @@ -version: build-{build}.{branch} - -clone_folder: C:\gopath\src\github.com\pkg\errors -shallow_clone: true # for startup speed - -environment: - GOPATH: C:\gopath - -platform: - - x64 - -# http://www.appveyor.com/docs/installed-software -install: - # some helpful output for debugging builds - - go version - - go env - # pre-installed MinGW at C:\MinGW is 32bit only - # but MSYS2 at C:\msys64 has mingw64 - - set PATH=C:\msys64\mingw64\bin;%PATH% - - gcc --version - - g++ --version - -build_script: - - go install -v ./... - -test_script: - - set PATH=C:\gopath\bin;%PATH% - - go test -v ./... - -#artifacts: -# - path: '%GOPATH%\bin\*.exe' -deploy: off diff --git a/vendor/github.com/pkg/errors/errors.go b/vendor/github.com/pkg/errors/errors.go deleted file mode 100644 index 161aea2..0000000 --- a/vendor/github.com/pkg/errors/errors.go +++ /dev/null @@ -1,288 +0,0 @@ -// Package errors provides simple error handling primitives. -// -// The traditional error handling idiom in Go is roughly akin to -// -// if err != nil { -// return err -// } -// -// which when applied recursively up the call stack results in error reports -// without context or debugging information. The errors package allows -// programmers to add context to the failure path in their code in a way -// that does not destroy the original value of the error. -// -// Adding context to an error -// -// The errors.Wrap function returns a new error that adds context to the -// original error by recording a stack trace at the point Wrap is called, -// together with the supplied message. For example -// -// _, err := ioutil.ReadAll(r) -// if err != nil { -// return errors.Wrap(err, "read failed") -// } -// -// If additional control is required, the errors.WithStack and -// errors.WithMessage functions destructure errors.Wrap into its component -// operations: annotating an error with a stack trace and with a message, -// respectively. -// -// Retrieving the cause of an error -// -// Using errors.Wrap constructs a stack of errors, adding context to the -// preceding error. Depending on the nature of the error it may be necessary -// to reverse the operation of errors.Wrap to retrieve the original error -// for inspection. Any error value which implements this interface -// -// type causer interface { -// Cause() error -// } -// -// can be inspected by errors.Cause. errors.Cause will recursively retrieve -// the topmost error that does not implement causer, which is assumed to be -// the original cause. For example: -// -// switch err := errors.Cause(err).(type) { -// case *MyError: -// // handle specifically -// default: -// // unknown error -// } -// -// Although the causer interface is not exported by this package, it is -// considered a part of its stable public interface. -// -// Formatted printing of errors -// -// All error values returned from this package implement fmt.Formatter and can -// be formatted by the fmt package. The following verbs are supported: -// -// %s print the error. If the error has a Cause it will be -// printed recursively. -// %v see %s -// %+v extended format. Each Frame of the error's StackTrace will -// be printed in detail. -// -// Retrieving the stack trace of an error or wrapper -// -// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are -// invoked. This information can be retrieved with the following interface: -// -// type stackTracer interface { -// StackTrace() errors.StackTrace -// } -// -// The returned errors.StackTrace type is defined as -// -// type StackTrace []Frame -// -// The Frame type represents a call site in the stack trace. Frame supports -// the fmt.Formatter interface that can be used for printing information about -// the stack trace of this error. For example: -// -// if err, ok := err.(stackTracer); ok { -// for _, f := range err.StackTrace() { -// fmt.Printf("%+s:%d\n", f, f) -// } -// } -// -// Although the stackTracer interface is not exported by this package, it is -// considered a part of its stable public interface. -// -// See the documentation for Frame.Format for more details. -package errors - -import ( - "fmt" - "io" -) - -// New returns an error with the supplied message. -// New also records the stack trace at the point it was called. -func New(message string) error { - return &fundamental{ - msg: message, - stack: callers(), - } -} - -// Errorf formats according to a format specifier and returns the string -// as a value that satisfies error. -// Errorf also records the stack trace at the point it was called. -func Errorf(format string, args ...interface{}) error { - return &fundamental{ - msg: fmt.Sprintf(format, args...), - stack: callers(), - } -} - -// fundamental is an error that has a message and a stack, but no caller. -type fundamental struct { - msg string - *stack -} - -func (f *fundamental) Error() string { return f.msg } - -func (f *fundamental) Format(s fmt.State, verb rune) { - switch verb { - case 'v': - if s.Flag('+') { - io.WriteString(s, f.msg) - f.stack.Format(s, verb) - return - } - fallthrough - case 's': - io.WriteString(s, f.msg) - case 'q': - fmt.Fprintf(s, "%q", f.msg) - } -} - -// WithStack annotates err with a stack trace at the point WithStack was called. -// If err is nil, WithStack returns nil. -func WithStack(err error) error { - if err == nil { - return nil - } - return &withStack{ - err, - callers(), - } -} - -type withStack struct { - error - *stack -} - -func (w *withStack) Cause() error { return w.error } - -// Unwrap provides compatibility for Go 1.13 error chains. -func (w *withStack) Unwrap() error { return w.error } - -func (w *withStack) Format(s fmt.State, verb rune) { - switch verb { - case 'v': - if s.Flag('+') { - fmt.Fprintf(s, "%+v", w.Cause()) - w.stack.Format(s, verb) - return - } - fallthrough - case 's': - io.WriteString(s, w.Error()) - case 'q': - fmt.Fprintf(s, "%q", w.Error()) - } -} - -// Wrap returns an error annotating err with a stack trace -// at the point Wrap is called, and the supplied message. -// If err is nil, Wrap returns nil. -func Wrap(err error, message string) error { - if err == nil { - return nil - } - err = &withMessage{ - cause: err, - msg: message, - } - return &withStack{ - err, - callers(), - } -} - -// Wrapf returns an error annotating err with a stack trace -// at the point Wrapf is called, and the format specifier. -// If err is nil, Wrapf returns nil. -func Wrapf(err error, format string, args ...interface{}) error { - if err == nil { - return nil - } - err = &withMessage{ - cause: err, - msg: fmt.Sprintf(format, args...), - } - return &withStack{ - err, - callers(), - } -} - -// WithMessage annotates err with a new message. -// If err is nil, WithMessage returns nil. -func WithMessage(err error, message string) error { - if err == nil { - return nil - } - return &withMessage{ - cause: err, - msg: message, - } -} - -// WithMessagef annotates err with the format specifier. -// If err is nil, WithMessagef returns nil. -func WithMessagef(err error, format string, args ...interface{}) error { - if err == nil { - return nil - } - return &withMessage{ - cause: err, - msg: fmt.Sprintf(format, args...), - } -} - -type withMessage struct { - cause error - msg string -} - -func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } -func (w *withMessage) Cause() error { return w.cause } - -// Unwrap provides compatibility for Go 1.13 error chains. -func (w *withMessage) Unwrap() error { return w.cause } - -func (w *withMessage) Format(s fmt.State, verb rune) { - switch verb { - case 'v': - if s.Flag('+') { - fmt.Fprintf(s, "%+v\n", w.Cause()) - io.WriteString(s, w.msg) - return - } - fallthrough - case 's', 'q': - io.WriteString(s, w.Error()) - } -} - -// Cause returns the underlying cause of the error, if possible. -// An error value has a cause if it implements the following -// interface: -// -// type causer interface { -// Cause() error -// } -// -// If the error does not implement Cause, the original error will -// be returned. If the error is nil, nil will be returned without further -// investigation. -func Cause(err error) error { - type causer interface { - Cause() error - } - - for err != nil { - cause, ok := err.(causer) - if !ok { - break - } - err = cause.Cause() - } - return err -} diff --git a/vendor/github.com/pkg/errors/go113.go b/vendor/github.com/pkg/errors/go113.go deleted file mode 100644 index be0d10d..0000000 --- a/vendor/github.com/pkg/errors/go113.go +++ /dev/null @@ -1,38 +0,0 @@ -// +build go1.13 - -package errors - -import ( - stderrors "errors" -) - -// Is reports whether any error in err's chain matches target. -// -// The chain consists of err itself followed by the sequence of errors obtained by -// repeatedly calling Unwrap. -// -// An error is considered to match a target if it is equal to that target or if -// it implements a method Is(error) bool such that Is(target) returns true. -func Is(err, target error) bool { return stderrors.Is(err, target) } - -// As finds the first error in err's chain that matches target, and if so, sets -// target to that error value and returns true. -// -// The chain consists of err itself followed by the sequence of errors obtained by -// repeatedly calling Unwrap. -// -// An error matches target if the error's concrete value is assignable to the value -// pointed to by target, or if the error has a method As(interface{}) bool such that -// As(target) returns true. In the latter case, the As method is responsible for -// setting target. -// -// As will panic if target is not a non-nil pointer to either a type that implements -// error, or to any interface type. As returns false if err is nil. -func As(err error, target interface{}) bool { return stderrors.As(err, target) } - -// Unwrap returns the result of calling the Unwrap method on err, if err's -// type contains an Unwrap method returning error. -// Otherwise, Unwrap returns nil. -func Unwrap(err error) error { - return stderrors.Unwrap(err) -} diff --git a/vendor/github.com/pkg/errors/stack.go b/vendor/github.com/pkg/errors/stack.go deleted file mode 100644 index 779a834..0000000 --- a/vendor/github.com/pkg/errors/stack.go +++ /dev/null @@ -1,177 +0,0 @@ -package errors - -import ( - "fmt" - "io" - "path" - "runtime" - "strconv" - "strings" -) - -// Frame represents a program counter inside a stack frame. -// For historical reasons if Frame is interpreted as a uintptr -// its value represents the program counter + 1. -type Frame uintptr - -// pc returns the program counter for this frame; -// multiple frames may have the same PC value. -func (f Frame) pc() uintptr { return uintptr(f) - 1 } - -// file returns the full path to the file that contains the -// function for this Frame's pc. -func (f Frame) file() string { - fn := runtime.FuncForPC(f.pc()) - if fn == nil { - return "unknown" - } - file, _ := fn.FileLine(f.pc()) - return file -} - -// line returns the line number of source code of the -// function for this Frame's pc. -func (f Frame) line() int { - fn := runtime.FuncForPC(f.pc()) - if fn == nil { - return 0 - } - _, line := fn.FileLine(f.pc()) - return line -} - -// name returns the name of this function, if known. -func (f Frame) name() string { - fn := runtime.FuncForPC(f.pc()) - if fn == nil { - return "unknown" - } - return fn.Name() -} - -// Format formats the frame according to the fmt.Formatter interface. -// -// %s source file -// %d source line -// %n function name -// %v equivalent to %s:%d -// -// Format accepts flags that alter the printing of some verbs, as follows: -// -// %+s function name and path of source file relative to the compile time -// GOPATH separated by \n\t (\n\t) -// %+v equivalent to %+s:%d -func (f Frame) Format(s fmt.State, verb rune) { - switch verb { - case 's': - switch { - case s.Flag('+'): - io.WriteString(s, f.name()) - io.WriteString(s, "\n\t") - io.WriteString(s, f.file()) - default: - io.WriteString(s, path.Base(f.file())) - } - case 'd': - io.WriteString(s, strconv.Itoa(f.line())) - case 'n': - io.WriteString(s, funcname(f.name())) - case 'v': - f.Format(s, 's') - io.WriteString(s, ":") - f.Format(s, 'd') - } -} - -// MarshalText formats a stacktrace Frame as a text string. The output is the -// same as that of fmt.Sprintf("%+v", f), but without newlines or tabs. -func (f Frame) MarshalText() ([]byte, error) { - name := f.name() - if name == "unknown" { - return []byte(name), nil - } - return []byte(fmt.Sprintf("%s %s:%d", name, f.file(), f.line())), nil -} - -// StackTrace is stack of Frames from innermost (newest) to outermost (oldest). -type StackTrace []Frame - -// Format formats the stack of Frames according to the fmt.Formatter interface. -// -// %s lists source files for each Frame in the stack -// %v lists the source file and line number for each Frame in the stack -// -// Format accepts flags that alter the printing of some verbs, as follows: -// -// %+v Prints filename, function, and line number for each Frame in the stack. -func (st StackTrace) Format(s fmt.State, verb rune) { - switch verb { - case 'v': - switch { - case s.Flag('+'): - for _, f := range st { - io.WriteString(s, "\n") - f.Format(s, verb) - } - case s.Flag('#'): - fmt.Fprintf(s, "%#v", []Frame(st)) - default: - st.formatSlice(s, verb) - } - case 's': - st.formatSlice(s, verb) - } -} - -// formatSlice will format this StackTrace into the given buffer as a slice of -// Frame, only valid when called with '%s' or '%v'. -func (st StackTrace) formatSlice(s fmt.State, verb rune) { - io.WriteString(s, "[") - for i, f := range st { - if i > 0 { - io.WriteString(s, " ") - } - f.Format(s, verb) - } - io.WriteString(s, "]") -} - -// stack represents a stack of program counters. -type stack []uintptr - -func (s *stack) Format(st fmt.State, verb rune) { - switch verb { - case 'v': - switch { - case st.Flag('+'): - for _, pc := range *s { - f := Frame(pc) - fmt.Fprintf(st, "\n%+v", f) - } - } - } -} - -func (s *stack) StackTrace() StackTrace { - f := make([]Frame, len(*s)) - for i := 0; i < len(f); i++ { - f[i] = Frame((*s)[i]) - } - return f -} - -func callers() *stack { - const depth = 32 - var pcs [depth]uintptr - n := runtime.Callers(3, pcs[:]) - var st stack = pcs[0:n] - return &st -} - -// funcname removes the path prefix component of a function's name reported by func.Name(). -func funcname(name string) string { - i := strings.LastIndex(name, "/") - name = name[i+1:] - i = strings.Index(name, ".") - return name[i+1:] -} diff --git a/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go b/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go index cec819d..d50826d 100644 --- a/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go +++ b/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go @@ -17,7 +17,6 @@ // developer tools, which will then be able to consume both Go 1.7 and // Go 1.8 export data files, so they will work before and after the // Go update. (See discussion at https://golang.org/issue/15651.) -// package gcexportdata // import "golang.org/x/tools/go/gcexportdata" import ( diff --git a/vendor/golang.org/x/tools/go/gcexportdata/importer.go b/vendor/golang.org/x/tools/go/gcexportdata/importer.go index efe221e..fe6ed93 100644 --- a/vendor/golang.org/x/tools/go/gcexportdata/importer.go +++ b/vendor/golang.org/x/tools/go/gcexportdata/importer.go @@ -22,7 +22,6 @@ import ( // version-skew problems described in the documentation of this package, // or to control the FileSet or access the imports map populated during // package loading. -// func NewImporter(fset *token.FileSet, imports map[string]*types.Package) types.ImporterFrom { return importer{fset, imports} } diff --git a/vendor/golang.org/x/tools/go/internal/gcimporter/bexport.go b/vendor/golang.org/x/tools/go/internal/gcimporter/bexport.go index 0a3cdb9..196cb3f 100644 --- a/vendor/golang.org/x/tools/go/internal/gcimporter/bexport.go +++ b/vendor/golang.org/x/tools/go/internal/gcimporter/bexport.go @@ -35,16 +35,18 @@ import ( const debugFormat = false // default: false // Current export format version. Increase with each format change. +// // Note: The latest binary (non-indexed) export format is at version 6. -// This exporter is still at level 4, but it doesn't matter since -// the binary importer can handle older versions just fine. -// 6: package height (CL 105038) -- NOT IMPLEMENTED HERE -// 5: improved position encoding efficiency (issue 20080, CL 41619) -- NOT IMPLEMEMTED HERE -// 4: type name objects support type aliases, uses aliasTag -// 3: Go1.8 encoding (same as version 2, aliasTag defined but never used) -// 2: removed unused bool in ODCL export (compiler only) -// 1: header format change (more regular), export package for _ struct fields -// 0: Go1.7 encoding +// This exporter is still at level 4, but it doesn't matter since +// the binary importer can handle older versions just fine. +// +// 6: package height (CL 105038) -- NOT IMPLEMENTED HERE +// 5: improved position encoding efficiency (issue 20080, CL 41619) -- NOT IMPLEMENTED HERE +// 4: type name objects support type aliases, uses aliasTag +// 3: Go1.8 encoding (same as version 2, aliasTag defined but never used) +// 2: removed unused bool in ODCL export (compiler only) +// 1: header format change (more regular), export package for _ struct fields +// 0: Go1.7 encoding const exportVersion = 4 // trackAllTypes enables cycle tracking for all types, not just named diff --git a/vendor/golang.org/x/tools/go/internal/gcimporter/gcimporter.go b/vendor/golang.org/x/tools/go/internal/gcimporter/gcimporter.go index 3ab6683..493bfa0 100644 --- a/vendor/golang.org/x/tools/go/internal/gcimporter/gcimporter.go +++ b/vendor/golang.org/x/tools/go/internal/gcimporter/gcimporter.go @@ -45,7 +45,6 @@ var pkgExts = [...]string{".a", ".o"} // the build.Default build.Context). A relative srcDir is interpreted // relative to the current working directory. // If no file was found, an empty filename is returned. -// func FindPkg(path, srcDir string) (filename, id string) { if path == "" { return @@ -109,7 +108,6 @@ func FindPkg(path, srcDir string) (filename, id string) { // If packages[id] contains the completely imported package, that package // can be used directly, and there is no need to call this function (but // there is also no harm but for extra time used). -// func ImportData(packages map[string]*types.Package, filename, id string, data io.Reader) (pkg *types.Package, err error) { // support for parser error handling defer func() { @@ -133,7 +131,6 @@ func ImportData(packages map[string]*types.Package, filename, id string, data io // Import imports a gc-generated package given its import path and srcDir, adds // the corresponding package object to the packages map, and returns the object. // The packages map must contain all packages already imported. -// func Import(packages map[string]*types.Package, path, srcDir string, lookup func(path string) (io.ReadCloser, error)) (pkg *types.Package, err error) { var rc io.ReadCloser var filename, id string @@ -348,8 +345,9 @@ func (p *parser) expectKeyword(keyword string) { // ---------------------------------------------------------------------------- // Qualified and unqualified names -// PackageId = string_lit . +// parsePackageID parses a PackageId: // +// PackageId = string_lit . func (p *parser) parsePackageID() string { id, err := strconv.Unquote(p.expect(scanner.String)) if err != nil { @@ -363,13 +361,16 @@ func (p *parser) parsePackageID() string { return id } -// PackageName = ident . +// parsePackageName parse a PackageName: // +// PackageName = ident . func (p *parser) parsePackageName() string { return p.expect(scanner.Ident) } -// dotIdentifier = ( ident | '·' ) { ident | int | '·' } . +// parseDotIdent parses a dotIdentifier: +// +// dotIdentifier = ( ident | '·' ) { ident | int | '·' } . func (p *parser) parseDotIdent() string { ident := "" if p.tok != scanner.Int { @@ -386,8 +387,9 @@ func (p *parser) parseDotIdent() string { return ident } -// QualifiedName = "@" PackageId "." ( "?" | dotIdentifier ) . +// parseQualifiedName parses a QualifiedName: // +// QualifiedName = "@" PackageId "." ( "?" | dotIdentifier ) . func (p *parser) parseQualifiedName() (id, name string) { p.expect('@') id = p.parsePackageID() @@ -410,7 +412,6 @@ func (p *parser) parseQualifiedName() (id, name string) { // id identifies a package, usually by a canonical package path like // "encoding/json" but possibly by a non-canonical import path like // "./json". -// func (p *parser) getPkg(id, name string) *types.Package { // package unsafe is not in the packages maps - handle explicitly if id == "unsafe" { @@ -446,7 +447,6 @@ func (p *parser) getPkg(id, name string) *types.Package { // parseExportedName is like parseQualifiedName, but // the package id is resolved to an imported *types.Package. -// func (p *parser) parseExportedName() (pkg *types.Package, name string) { id, name := p.parseQualifiedName() pkg = p.getPkg(id, "") @@ -456,8 +456,9 @@ func (p *parser) parseExportedName() (pkg *types.Package, name string) { // ---------------------------------------------------------------------------- // Types -// BasicType = identifier . +// parseBasicType parses a BasicType: // +// BasicType = identifier . func (p *parser) parseBasicType() types.Type { id := p.expect(scanner.Ident) obj := types.Universe.Lookup(id) @@ -468,8 +469,9 @@ func (p *parser) parseBasicType() types.Type { return nil } -// ArrayType = "[" int_lit "]" Type . +// parseArrayType parses an ArrayType: // +// ArrayType = "[" int_lit "]" Type . func (p *parser) parseArrayType(parent *types.Package) types.Type { // "[" already consumed and lookahead known not to be "]" lit := p.expect(scanner.Int) @@ -482,8 +484,9 @@ func (p *parser) parseArrayType(parent *types.Package) types.Type { return types.NewArray(elem, n) } -// MapType = "map" "[" Type "]" Type . +// parseMapType parses a MapType: // +// MapType = "map" "[" Type "]" Type . func (p *parser) parseMapType(parent *types.Package) types.Type { p.expectKeyword("map") p.expect('[') @@ -493,7 +496,9 @@ func (p *parser) parseMapType(parent *types.Package) types.Type { return types.NewMap(key, elem) } -// Name = identifier | "?" | QualifiedName . +// parseName parses a Name: +// +// Name = identifier | "?" | QualifiedName . // // For unqualified and anonymous names, the returned package is the parent // package unless parent == nil, in which case the returned package is the @@ -505,7 +510,6 @@ func (p *parser) parseMapType(parent *types.Package) types.Type { // it doesn't exist yet) unless materializePkg is set (which creates an // unnamed package with valid package path). In the latter case, a // subsequent import clause is expected to provide a name for the package. -// func (p *parser) parseName(parent *types.Package, materializePkg bool) (pkg *types.Package, name string) { pkg = parent if pkg == nil { @@ -539,8 +543,9 @@ func deref(typ types.Type) types.Type { return typ } -// Field = Name Type [ string_lit ] . +// parseField parses a Field: // +// Field = Name Type [ string_lit ] . func (p *parser) parseField(parent *types.Package) (*types.Var, string) { pkg, name := p.parseName(parent, true) @@ -583,9 +588,10 @@ func (p *parser) parseField(parent *types.Package) (*types.Var, string) { return types.NewField(token.NoPos, pkg, name, typ, anonymous), tag } -// StructType = "struct" "{" [ FieldList ] "}" . -// FieldList = Field { ";" Field } . +// parseStructType parses a StructType: // +// StructType = "struct" "{" [ FieldList ] "}" . +// FieldList = Field { ";" Field } . func (p *parser) parseStructType(parent *types.Package) types.Type { var fields []*types.Var var tags []string @@ -610,8 +616,9 @@ func (p *parser) parseStructType(parent *types.Package) types.Type { return types.NewStruct(fields, tags) } -// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] . +// parseParameter parses a Parameter: // +// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] . func (p *parser) parseParameter() (par *types.Var, isVariadic bool) { _, name := p.parseName(nil, false) // remove gc-specific parameter numbering @@ -635,9 +642,10 @@ func (p *parser) parseParameter() (par *types.Var, isVariadic bool) { return } -// Parameters = "(" [ ParameterList ] ")" . -// ParameterList = { Parameter "," } Parameter . +// parseParameters parses a Parameters: // +// Parameters = "(" [ ParameterList ] ")" . +// ParameterList = { Parameter "," } Parameter . func (p *parser) parseParameters() (list []*types.Var, isVariadic bool) { p.expect('(') for p.tok != ')' && p.tok != scanner.EOF { @@ -658,9 +666,10 @@ func (p *parser) parseParameters() (list []*types.Var, isVariadic bool) { return } -// Signature = Parameters [ Result ] . -// Result = Type | Parameters . +// parseSignature parses a Signature: // +// Signature = Parameters [ Result ] . +// Result = Type | Parameters . func (p *parser) parseSignature(recv *types.Var) *types.Signature { params, isVariadic := p.parseParameters() @@ -677,14 +686,15 @@ func (p *parser) parseSignature(recv *types.Var) *types.Signature { return types.NewSignature(recv, types.NewTuple(params...), types.NewTuple(results...), isVariadic) } -// InterfaceType = "interface" "{" [ MethodList ] "}" . -// MethodList = Method { ";" Method } . -// Method = Name Signature . +// parseInterfaceType parses an InterfaceType: +// +// InterfaceType = "interface" "{" [ MethodList ] "}" . +// MethodList = Method { ";" Method } . +// Method = Name Signature . // // The methods of embedded interfaces are always "inlined" // by the compiler and thus embedded interfaces are never // visible in the export data. -// func (p *parser) parseInterfaceType(parent *types.Package) types.Type { var methods []*types.Func @@ -705,8 +715,9 @@ func (p *parser) parseInterfaceType(parent *types.Package) types.Type { return newInterface(methods, nil).Complete() } -// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type . +// parseChanType parses a ChanType: // +// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type . func (p *parser) parseChanType(parent *types.Package) types.Type { dir := types.SendRecv if p.tok == scanner.Ident { @@ -724,17 +735,18 @@ func (p *parser) parseChanType(parent *types.Package) types.Type { return types.NewChan(dir, elem) } -// Type = -// BasicType | TypeName | ArrayType | SliceType | StructType | -// PointerType | FuncType | InterfaceType | MapType | ChanType | -// "(" Type ")" . +// parseType parses a Type: // -// BasicType = ident . -// TypeName = ExportedName . -// SliceType = "[" "]" Type . -// PointerType = "*" Type . -// FuncType = "func" Signature . +// Type = +// BasicType | TypeName | ArrayType | SliceType | StructType | +// PointerType | FuncType | InterfaceType | MapType | ChanType | +// "(" Type ")" . // +// BasicType = ident . +// TypeName = ExportedName . +// SliceType = "[" "]" Type . +// PointerType = "*" Type . +// FuncType = "func" Signature . func (p *parser) parseType(parent *types.Package) types.Type { switch p.tok { case scanner.Ident: @@ -786,16 +798,18 @@ func (p *parser) parseType(parent *types.Package) types.Type { // ---------------------------------------------------------------------------- // Declarations -// ImportDecl = "import" PackageName PackageId . +// parseImportDecl parses an ImportDecl: // +// ImportDecl = "import" PackageName PackageId . func (p *parser) parseImportDecl() { p.expectKeyword("import") name := p.parsePackageName() p.getPkg(p.parsePackageID(), name) } -// int_lit = [ "+" | "-" ] { "0" ... "9" } . +// parseInt parses an int_lit: // +// int_lit = [ "+" | "-" ] { "0" ... "9" } . func (p *parser) parseInt() string { s := "" switch p.tok { @@ -808,8 +822,9 @@ func (p *parser) parseInt() string { return s + p.expect(scanner.Int) } -// number = int_lit [ "p" int_lit ] . +// parseNumber parses a number: // +// number = int_lit [ "p" int_lit ] . func (p *parser) parseNumber() (typ *types.Basic, val constant.Value) { // mantissa mant := constant.MakeFromLiteral(p.parseInt(), token.INT, 0) @@ -844,13 +859,14 @@ func (p *parser) parseNumber() (typ *types.Basic, val constant.Value) { return } -// ConstDecl = "const" ExportedName [ Type ] "=" Literal . -// Literal = bool_lit | int_lit | float_lit | complex_lit | rune_lit | string_lit . -// bool_lit = "true" | "false" . -// complex_lit = "(" float_lit "+" float_lit "i" ")" . -// rune_lit = "(" int_lit "+" int_lit ")" . -// string_lit = `"` { unicode_char } `"` . +// parseConstDecl parses a ConstDecl: // +// ConstDecl = "const" ExportedName [ Type ] "=" Literal . +// Literal = bool_lit | int_lit | float_lit | complex_lit | rune_lit | string_lit . +// bool_lit = "true" | "false" . +// complex_lit = "(" float_lit "+" float_lit "i" ")" . +// rune_lit = "(" int_lit "+" int_lit ")" . +// string_lit = `"` { unicode_char } `"` . func (p *parser) parseConstDecl() { p.expectKeyword("const") pkg, name := p.parseExportedName() @@ -920,8 +936,9 @@ func (p *parser) parseConstDecl() { pkg.Scope().Insert(types.NewConst(token.NoPos, pkg, name, typ0, val)) } -// TypeDecl = "type" ExportedName Type . +// parseTypeDecl parses a TypeDecl: // +// TypeDecl = "type" ExportedName Type . func (p *parser) parseTypeDecl() { p.expectKeyword("type") pkg, name := p.parseExportedName() @@ -939,8 +956,9 @@ func (p *parser) parseTypeDecl() { } } -// VarDecl = "var" ExportedName Type . +// parseVarDecl parses a VarDecl: // +// VarDecl = "var" ExportedName Type . func (p *parser) parseVarDecl() { p.expectKeyword("var") pkg, name := p.parseExportedName() @@ -948,9 +966,10 @@ func (p *parser) parseVarDecl() { pkg.Scope().Insert(types.NewVar(token.NoPos, pkg, name, typ)) } -// Func = Signature [ Body ] . -// Body = "{" ... "}" . +// parseFunc parses a Func: // +// Func = Signature [ Body ] . +// Body = "{" ... "}" . func (p *parser) parseFunc(recv *types.Var) *types.Signature { sig := p.parseSignature(recv) if p.tok == '{' { @@ -967,9 +986,10 @@ func (p *parser) parseFunc(recv *types.Var) *types.Signature { return sig } -// MethodDecl = "func" Receiver Name Func . -// Receiver = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" . +// parseMethodDecl parses a MethodDecl: // +// MethodDecl = "func" Receiver Name Func . +// Receiver = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" . func (p *parser) parseMethodDecl() { // "func" already consumed p.expect('(') @@ -992,8 +1012,9 @@ func (p *parser) parseMethodDecl() { base.AddMethod(types.NewFunc(token.NoPos, pkg, name, sig)) } -// FuncDecl = "func" ExportedName Func . +// parseFuncDecl parses a FuncDecl: // +// FuncDecl = "func" ExportedName Func . func (p *parser) parseFuncDecl() { // "func" already consumed pkg, name := p.parseExportedName() @@ -1001,8 +1022,9 @@ func (p *parser) parseFuncDecl() { pkg.Scope().Insert(types.NewFunc(token.NoPos, pkg, name, typ)) } -// Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" . +// parseDecl parses a Decl: // +// Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" . func (p *parser) parseDecl() { if p.tok == scanner.Ident { switch p.lit { @@ -1029,9 +1051,10 @@ func (p *parser) parseDecl() { // ---------------------------------------------------------------------------- // Export -// Export = "PackageClause { Decl } "$$" . -// PackageClause = "package" PackageName [ "safe" ] "\n" . +// parseExport parses an Export: // +// Export = "PackageClause { Decl } "$$" . +// PackageClause = "package" PackageName [ "safe" ] "\n" . func (p *parser) parseExport() *types.Package { p.expectKeyword("package") name := p.parsePackageName() diff --git a/vendor/golang.org/x/tools/go/internal/gcimporter/iexport.go b/vendor/golang.org/x/tools/go/internal/gcimporter/iexport.go index 2095534..9a4ff32 100644 --- a/vendor/golang.org/x/tools/go/internal/gcimporter/iexport.go +++ b/vendor/golang.org/x/tools/go/internal/gcimporter/iexport.go @@ -251,7 +251,10 @@ func (p *iexporter) stringOff(s string) uint64 { // pushDecl adds n to the declaration work queue, if not already present. func (p *iexporter) pushDecl(obj types.Object) { // Package unsafe is known to the compiler and predeclared. - assert(obj.Pkg() != types.Unsafe) + // Caller should not ask us to do export it. + if obj.Pkg() == types.Unsafe { + panic("cannot export package unsafe") + } if _, ok := p.declIndex[obj]; ok { return diff --git a/vendor/golang.org/x/tools/go/internal/gcimporter/iimport.go b/vendor/golang.org/x/tools/go/internal/gcimporter/iimport.go index 84cfb80..28b91b8 100644 --- a/vendor/golang.org/x/tools/go/internal/gcimporter/iimport.go +++ b/vendor/golang.org/x/tools/go/internal/gcimporter/iimport.go @@ -53,7 +53,7 @@ const ( ) type ident struct { - pkg string + pkg *types.Package name string } @@ -100,7 +100,9 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data if !debug { defer func() { if e := recover(); e != nil { - if version > currentVersion { + if bundle { + err = fmt.Errorf("%v", e) + } else if version > currentVersion { err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e) } else { err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e) @@ -461,7 +463,7 @@ func (r *importReader) obj(name string) { // To handle recursive references to the typeparam within its // bound, save the partial type in tparamIndex before reading the bounds. - id := ident{r.currPkg.Name(), name} + id := ident{r.currPkg, name} r.p.tparamIndex[id] = t var implicit bool if r.p.version >= iexportVersionGo1_18 { @@ -777,7 +779,7 @@ func (r *importReader) doType(base *types.Named) (res types.Type) { errorf("unexpected type param type") } pkg, name := r.qualifiedIdent() - id := ident{pkg.Name(), name} + id := ident{pkg, name} if t, ok := r.p.tparamIndex[id]; ok { // We're already in the process of importing this typeparam. return t diff --git a/vendor/golang.org/x/tools/go/packages/doc.go b/vendor/golang.org/x/tools/go/packages/doc.go index 4bfe28a..da4ab89 100644 --- a/vendor/golang.org/x/tools/go/packages/doc.go +++ b/vendor/golang.org/x/tools/go/packages/doc.go @@ -67,7 +67,6 @@ Most tools should pass their command-line arguments (after any flags) uninterpreted to the loader, so that the loader can interpret them according to the conventions of the underlying build system. See the Example function for typical usage. - */ package packages // import "golang.org/x/tools/go/packages" diff --git a/vendor/golang.org/x/tools/go/packages/golist.go b/vendor/golang.org/x/tools/go/packages/golist.go index 0e1e7f1..5053399 100644 --- a/vendor/golang.org/x/tools/go/packages/golist.go +++ b/vendor/golang.org/x/tools/go/packages/golist.go @@ -26,7 +26,6 @@ import ( "golang.org/x/tools/go/internal/packagesdriver" "golang.org/x/tools/internal/gocommand" "golang.org/x/tools/internal/packagesinternal" - "golang.org/x/xerrors" ) // debug controls verbose logging. @@ -393,6 +392,8 @@ type jsonPackage struct { CompiledGoFiles []string IgnoredGoFiles []string IgnoredOtherFiles []string + EmbedPatterns []string + EmbedFiles []string CFiles []string CgoFiles []string CXXFiles []string @@ -444,7 +445,11 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse // Run "go list" for complete // information on the specified packages. - buf, err := state.invokeGo("list", golistargs(state.cfg, words)...) + goVersion, err := state.getGoVersion() + if err != nil { + return nil, err + } + buf, err := state.invokeGo("list", golistargs(state.cfg, words, goVersion)...) if err != nil { return nil, err } @@ -565,6 +570,8 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse GoFiles: absJoin(p.Dir, p.GoFiles, p.CgoFiles), CompiledGoFiles: absJoin(p.Dir, p.CompiledGoFiles), OtherFiles: absJoin(p.Dir, otherFiles(p)...), + EmbedFiles: absJoin(p.Dir, p.EmbedFiles), + EmbedPatterns: absJoin(p.Dir, p.EmbedPatterns), IgnoredFiles: absJoin(p.Dir, p.IgnoredGoFiles, p.IgnoredOtherFiles), forTest: p.ForTest, depsErrors: p.DepsErrors, @@ -805,17 +812,83 @@ func absJoin(dir string, fileses ...[]string) (res []string) { return res } -func golistargs(cfg *Config, words []string) []string { +func jsonFlag(cfg *Config, goVersion int) string { + if goVersion < 19 { + return "-json" + } + var fields []string + added := make(map[string]bool) + addFields := func(fs ...string) { + for _, f := range fs { + if !added[f] { + added[f] = true + fields = append(fields, f) + } + } + } + addFields("Name", "ImportPath", "Error") // These fields are always needed + if cfg.Mode&NeedFiles != 0 || cfg.Mode&NeedTypes != 0 { + addFields("Dir", "GoFiles", "IgnoredGoFiles", "IgnoredOtherFiles", "CFiles", + "CgoFiles", "CXXFiles", "MFiles", "HFiles", "FFiles", "SFiles", + "SwigFiles", "SwigCXXFiles", "SysoFiles") + if cfg.Tests { + addFields("TestGoFiles", "XTestGoFiles") + } + } + if cfg.Mode&NeedTypes != 0 { + // CompiledGoFiles seems to be required for the test case TestCgoNoSyntax, + // even when -compiled isn't passed in. + // TODO(#52435): Should we make the test ask for -compiled, or automatically + // request CompiledGoFiles in certain circumstances? + addFields("Dir", "CompiledGoFiles") + } + if cfg.Mode&NeedCompiledGoFiles != 0 { + addFields("Dir", "CompiledGoFiles", "Export") + } + if cfg.Mode&NeedImports != 0 { + // When imports are requested, DepOnly is used to distinguish between packages + // explicitly requested and transitive imports of those packages. + addFields("DepOnly", "Imports", "ImportMap") + if cfg.Tests { + addFields("TestImports", "XTestImports") + } + } + if cfg.Mode&NeedDeps != 0 { + addFields("DepOnly") + } + if usesExportData(cfg) { + // Request Dir in the unlikely case Export is not absolute. + addFields("Dir", "Export") + } + if cfg.Mode&needInternalForTest != 0 { + addFields("ForTest") + } + if cfg.Mode&needInternalDepsErrors != 0 { + addFields("DepsErrors") + } + if cfg.Mode&NeedModule != 0 { + addFields("Module") + } + if cfg.Mode&NeedEmbedFiles != 0 { + addFields("EmbedFiles") + } + if cfg.Mode&NeedEmbedPatterns != 0 { + addFields("EmbedPatterns") + } + return "-json=" + strings.Join(fields, ",") +} + +func golistargs(cfg *Config, words []string, goVersion int) []string { const findFlags = NeedImports | NeedTypes | NeedSyntax | NeedTypesInfo fullargs := []string{ - "-e", "-json", + "-e", jsonFlag(cfg, goVersion), fmt.Sprintf("-compiled=%t", cfg.Mode&(NeedCompiledGoFiles|NeedSyntax|NeedTypes|NeedTypesInfo|NeedTypesSizes) != 0), fmt.Sprintf("-test=%t", cfg.Tests), fmt.Sprintf("-export=%t", usesExportData(cfg)), fmt.Sprintf("-deps=%t", cfg.Mode&NeedImports != 0), // go list doesn't let you pass -test and -find together, // probably because you'd just get the TestMain. - fmt.Sprintf("-find=%t", !cfg.Tests && cfg.Mode&findFlags == 0), + fmt.Sprintf("-find=%t", !cfg.Tests && cfg.Mode&findFlags == 0 && !usesExportData(cfg)), } fullargs = append(fullargs, cfg.BuildFlags...) fullargs = append(fullargs, "--") @@ -879,7 +952,7 @@ func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer, if !ok { // Catastrophic error: // - context cancellation - return nil, xerrors.Errorf("couldn't run 'go': %w", err) + return nil, fmt.Errorf("couldn't run 'go': %w", err) } // Old go version? diff --git a/vendor/golang.org/x/tools/go/packages/loadmode_string.go b/vendor/golang.org/x/tools/go/packages/loadmode_string.go index 7ea37e7..5c080d2 100644 --- a/vendor/golang.org/x/tools/go/packages/loadmode_string.go +++ b/vendor/golang.org/x/tools/go/packages/loadmode_string.go @@ -15,7 +15,7 @@ var allModes = []LoadMode{ NeedCompiledGoFiles, NeedImports, NeedDeps, - NeedExportsFile, + NeedExportFile, NeedTypes, NeedSyntax, NeedTypesInfo, @@ -28,7 +28,7 @@ var modeStrings = []string{ "NeedCompiledGoFiles", "NeedImports", "NeedDeps", - "NeedExportsFile", + "NeedExportFile", "NeedTypes", "NeedSyntax", "NeedTypesInfo", diff --git a/vendor/golang.org/x/tools/go/packages/packages.go b/vendor/golang.org/x/tools/go/packages/packages.go index 1b5424e..a93dc6a 100644 --- a/vendor/golang.org/x/tools/go/packages/packages.go +++ b/vendor/golang.org/x/tools/go/packages/packages.go @@ -39,9 +39,6 @@ import ( // Load may return more information than requested. type LoadMode int -// TODO(matloob): When a V2 of go/packages is released, rename NeedExportsFile to -// NeedExportFile to make it consistent with the Package field it's adding. - const ( // NeedName adds Name and PkgPath. NeedName LoadMode = 1 << iota @@ -59,8 +56,8 @@ const ( // NeedDeps adds the fields requested by the LoadMode in the packages in Imports. NeedDeps - // NeedExportsFile adds ExportFile. - NeedExportsFile + // NeedExportFile adds ExportFile. + NeedExportFile // NeedTypes adds Types, Fset, and IllTyped. NeedTypes @@ -74,12 +71,25 @@ const ( // NeedTypesSizes adds TypesSizes. NeedTypesSizes + // needInternalDepsErrors adds the internal deps errors field for use by gopls. + needInternalDepsErrors + + // needInternalForTest adds the internal forTest field. + // Tests must also be set on the context for this field to be populated. + needInternalForTest + // typecheckCgo enables full support for type checking cgo. Requires Go 1.15+. // Modifies CompiledGoFiles and Types, and has no effect on its own. typecheckCgo // NeedModule adds Module. NeedModule + + // NeedEmbedFiles adds EmbedFiles. + NeedEmbedFiles + + // NeedEmbedPatterns adds EmbedPatterns. + NeedEmbedPatterns ) const ( @@ -102,6 +112,9 @@ const ( // Deprecated: LoadAllSyntax exists for historical compatibility // and should not be used. Please directly specify the needed fields using the Need values. LoadAllSyntax = LoadSyntax | NeedDeps + + // Deprecated: NeedExportsFile is a historical misspelling of NeedExportFile. + NeedExportsFile = NeedExportFile ) // A Config specifies details about how packages should be loaded. @@ -296,6 +309,14 @@ type Package struct { // including assembly, C, C++, Fortran, Objective-C, SWIG, and so on. OtherFiles []string + // EmbedFiles lists the absolute file paths of the package's files + // embedded with go:embed. + EmbedFiles []string + + // EmbedPatterns lists the absolute file patterns of the package's + // files embedded with go:embed. + EmbedPatterns []string + // IgnoredFiles lists source files that are not part of the package // using the current build configuration but that might be part of // the package using other build configurations. @@ -389,6 +410,8 @@ func init() { config.(*Config).modFlag = value } packagesinternal.TypecheckCgo = int(typecheckCgo) + packagesinternal.DepsErrors = int(needInternalDepsErrors) + packagesinternal.ForTest = int(needInternalForTest) } // An Error describes a problem with a package's metadata, syntax, or types. @@ -431,6 +454,8 @@ type flatPackage struct { GoFiles []string `json:",omitempty"` CompiledGoFiles []string `json:",omitempty"` OtherFiles []string `json:",omitempty"` + EmbedFiles []string `json:",omitempty"` + EmbedPatterns []string `json:",omitempty"` IgnoredFiles []string `json:",omitempty"` ExportFile string `json:",omitempty"` Imports map[string]string `json:",omitempty"` @@ -454,6 +479,8 @@ func (p *Package) MarshalJSON() ([]byte, error) { GoFiles: p.GoFiles, CompiledGoFiles: p.CompiledGoFiles, OtherFiles: p.OtherFiles, + EmbedFiles: p.EmbedFiles, + EmbedPatterns: p.EmbedPatterns, IgnoredFiles: p.IgnoredFiles, ExportFile: p.ExportFile, } @@ -481,6 +508,8 @@ func (p *Package) UnmarshalJSON(b []byte) error { GoFiles: flat.GoFiles, CompiledGoFiles: flat.CompiledGoFiles, OtherFiles: flat.OtherFiles, + EmbedFiles: flat.EmbedFiles, + EmbedPatterns: flat.EmbedPatterns, ExportFile: flat.ExportFile, } if len(flat.Imports) > 0 { @@ -614,7 +643,7 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) { needsrc := ((ld.Mode&(NeedSyntax|NeedTypesInfo) != 0 && (rootIndex >= 0 || ld.Mode&NeedDeps != 0)) || // ... or if we need types and the exportData is invalid. We fall back to (incompletely) // typechecking packages from source if they fail to compile. - (ld.Mode&NeedTypes|NeedTypesInfo != 0 && exportDataInvalid)) && pkg.PkgPath != "unsafe" + (ld.Mode&(NeedTypes|NeedTypesInfo) != 0 && exportDataInvalid)) && pkg.PkgPath != "unsafe" lpkg := &loaderPackage{ Package: pkg, needtypes: needtypes, @@ -752,13 +781,19 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) { ld.pkgs[i].OtherFiles = nil ld.pkgs[i].IgnoredFiles = nil } + if ld.requestedMode&NeedEmbedFiles == 0 { + ld.pkgs[i].EmbedFiles = nil + } + if ld.requestedMode&NeedEmbedPatterns == 0 { + ld.pkgs[i].EmbedPatterns = nil + } if ld.requestedMode&NeedCompiledGoFiles == 0 { ld.pkgs[i].CompiledGoFiles = nil } if ld.requestedMode&NeedImports == 0 { ld.pkgs[i].Imports = nil } - if ld.requestedMode&NeedExportsFile == 0 { + if ld.requestedMode&NeedExportFile == 0 { ld.pkgs[i].ExportFile = "" } if ld.requestedMode&NeedTypes == 0 { @@ -1053,7 +1088,6 @@ func (ld *loader) parseFile(filename string) (*ast.File, error) { // // Because files are scanned in parallel, the token.Pos // positions of the resulting ast.Files are not ordered. -// func (ld *loader) parseFiles(filenames []string) ([]*ast.File, []error) { var wg sync.WaitGroup n := len(filenames) @@ -1097,7 +1131,6 @@ func (ld *loader) parseFiles(filenames []string) ([]*ast.File, []error) { // sameFile returns true if x and y have the same basename and denote // the same file. -// func sameFile(x, y string) bool { if x == y { // It could be the case that y doesn't exist. @@ -1210,8 +1243,13 @@ func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error if err != nil { return nil, fmt.Errorf("reading %s: %v", lpkg.ExportFile, err) } + if _, ok := view["go.shape"]; ok { + // Account for the pseudopackage "go.shape" that gets + // created by generic code. + viewLen++ + } if viewLen != len(view) { - log.Fatalf("Unexpected package creation during export data loading") + log.Panicf("golang.org/x/tools/go/packages: unexpected new packages during load of %s", lpkg.PkgPath) } lpkg.Types = tpkg @@ -1222,17 +1260,8 @@ func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error // impliedLoadMode returns loadMode with its dependencies. func impliedLoadMode(loadMode LoadMode) LoadMode { - if loadMode&NeedTypesInfo != 0 && loadMode&NeedImports == 0 { - // If NeedTypesInfo, go/packages needs to do typechecking itself so it can - // associate type info with the AST. To do so, we need the export data - // for dependencies, which means we need to ask for the direct dependencies. - // NeedImports is used to ask for the direct dependencies. - loadMode |= NeedImports - } - - if loadMode&NeedDeps != 0 && loadMode&NeedImports == 0 { - // With NeedDeps we need to load at least direct dependencies. - // NeedImports is used to ask for the direct dependencies. + if loadMode&(NeedDeps|NeedTypes|NeedTypesInfo) != 0 { + // All these things require knowing the import graph. loadMode |= NeedImports } @@ -1240,5 +1269,5 @@ func impliedLoadMode(loadMode LoadMode) LoadMode { } func usesExportData(cfg *Config) bool { - return cfg.Mode&NeedExportsFile != 0 || cfg.Mode&NeedTypes != 0 && cfg.Mode&NeedDeps == 0 + return cfg.Mode&NeedExportFile != 0 || cfg.Mode&NeedTypes != 0 && cfg.Mode&NeedDeps == 0 } diff --git a/vendor/golang.org/x/tools/internal/gocommand/invoke.go b/vendor/golang.org/x/tools/internal/gocommand/invoke.go index f753368..67256dc 100644 --- a/vendor/golang.org/x/tools/internal/gocommand/invoke.go +++ b/vendor/golang.org/x/tools/internal/gocommand/invoke.go @@ -264,8 +264,10 @@ func cmdDebugStr(cmd *exec.Cmd) string { env := make(map[string]string) for _, kv := range cmd.Env { split := strings.SplitN(kv, "=", 2) - k, v := split[0], split[1] - env[k] = v + if len(split) == 2 { + k, v := split[0], split[1] + env[k] = v + } } var args []string diff --git a/vendor/golang.org/x/tools/internal/packagesinternal/packages.go b/vendor/golang.org/x/tools/internal/packagesinternal/packages.go index 9702094..d9950b1 100644 --- a/vendor/golang.org/x/tools/internal/packagesinternal/packages.go +++ b/vendor/golang.org/x/tools/internal/packagesinternal/packages.go @@ -23,6 +23,8 @@ var GetGoCmdRunner = func(config interface{}) *gocommand.Runner { return nil } var SetGoCmdRunner = func(config interface{}, runner *gocommand.Runner) {} var TypecheckCgo int +var DepsErrors int // must be set as a LoadMode to call GetDepsErrors +var ForTest int // must be set as a LoadMode to call GetForTest var SetModFlag = func(config interface{}, value string) {} var SetModFile = func(config interface{}, value string) {} diff --git a/vendor/golang.org/x/tools/internal/typeparams/common.go b/vendor/golang.org/x/tools/internal/typeparams/common.go index ab6b30b..25a1426 100644 --- a/vendor/golang.org/x/tools/internal/typeparams/common.go +++ b/vendor/golang.org/x/tools/internal/typeparams/common.go @@ -16,11 +16,10 @@ // Additionally, this package contains common utilities for working with the // new generic constructs, to supplement the standard library APIs. Notably, // the StructuralTerms API computes a minimal representation of the structural -// restrictions on a type parameter. In the future, this API may be available -// from go/types. +// restrictions on a type parameter. // -// See the example/README.md for a more detailed guide on how to update tools -// to support generics. +// An external version of these APIs is available in the +// golang.org/x/exp/typeparams module. package typeparams import ( @@ -121,15 +120,15 @@ func OriginMethod(fn *types.Func) *types.Func { // // For example, consider the following type declarations: // -// type Interface[T any] interface { -// Accept(T) -// } +// type Interface[T any] interface { +// Accept(T) +// } // -// type Container[T any] struct { -// Element T -// } +// type Container[T any] struct { +// Element T +// } // -// func (c Container[T]) Accept(t T) { c.Element = t } +// func (c Container[T]) Accept(t T) { c.Element = t } // // In this case, GenericAssignableTo reports that instantiations of Container // are assignable to the corresponding instantiation of Interface. diff --git a/vendor/golang.org/x/tools/internal/typeparams/coretype.go b/vendor/golang.org/x/tools/internal/typeparams/coretype.go new file mode 100644 index 0000000..993135e --- /dev/null +++ b/vendor/golang.org/x/tools/internal/typeparams/coretype.go @@ -0,0 +1,122 @@ +// Copyright 2022 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 typeparams + +import ( + "go/types" +) + +// CoreType returns the core type of T or nil if T does not have a core type. +// +// See https://go.dev/ref/spec#Core_types for the definition of a core type. +func CoreType(T types.Type) types.Type { + U := T.Underlying() + if _, ok := U.(*types.Interface); !ok { + return U // for non-interface types, + } + + terms, err := _NormalTerms(U) + if len(terms) == 0 || err != nil { + // len(terms) -> empty type set of interface. + // err != nil => U is invalid, exceeds complexity bounds, or has an empty type set. + return nil // no core type. + } + + U = terms[0].Type().Underlying() + var identical int // i in [0,identical) => Identical(U, terms[i].Type().Underlying()) + for identical = 1; identical < len(terms); identical++ { + if !types.Identical(U, terms[identical].Type().Underlying()) { + break + } + } + + if identical == len(terms) { + // https://go.dev/ref/spec#Core_types + // "There is a single type U which is the underlying type of all types in the type set of T" + return U + } + ch, ok := U.(*types.Chan) + if !ok { + return nil // no core type as identical < len(terms) and U is not a channel. + } + // https://go.dev/ref/spec#Core_types + // "the type chan E if T contains only bidirectional channels, or the type chan<- E or + // <-chan E depending on the direction of the directional channels present." + for chans := identical; chans < len(terms); chans++ { + curr, ok := terms[chans].Type().Underlying().(*types.Chan) + if !ok { + return nil + } + if !types.Identical(ch.Elem(), curr.Elem()) { + return nil // channel elements are not identical. + } + if ch.Dir() == types.SendRecv { + // ch is bidirectional. We can safely always use curr's direction. + ch = curr + } else if curr.Dir() != types.SendRecv && ch.Dir() != curr.Dir() { + // ch and curr are not bidirectional and not the same direction. + return nil + } + } + return ch +} + +// _NormalTerms returns a slice of terms representing the normalized structural +// type restrictions of a type, if any. +// +// For all types other than *types.TypeParam, *types.Interface, and +// *types.Union, this is just a single term with Tilde() == false and +// Type() == typ. For *types.TypeParam, *types.Interface, and *types.Union, see +// below. +// +// Structural type restrictions of a type parameter are created via +// non-interface types embedded in its constraint interface (directly, or via a +// chain of interface embeddings). For example, in the declaration type +// T[P interface{~int; m()}] int the structural restriction of the type +// parameter P is ~int. +// +// With interface embedding and unions, the specification of structural type +// restrictions may be arbitrarily complex. For example, consider the +// following: +// +// type A interface{ ~string|~[]byte } +// +// type B interface{ int|string } +// +// type C interface { ~string|~int } +// +// type T[P interface{ A|B; C }] int +// +// In this example, the structural type restriction of P is ~string|int: A|B +// expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int, +// which when intersected with C (~string|~int) yields ~string|int. +// +// _NormalTerms computes these expansions and reductions, producing a +// "normalized" form of the embeddings. A structural restriction is normalized +// if it is a single union containing no interface terms, and is minimal in the +// sense that removing any term changes the set of types satisfying the +// constraint. It is left as a proof for the reader that, modulo sorting, there +// is exactly one such normalized form. +// +// Because the minimal representation always takes this form, _NormalTerms +// returns a slice of tilde terms corresponding to the terms of the union in +// the normalized structural restriction. An error is returned if the type is +// invalid, exceeds complexity bounds, or has an empty type set. In the latter +// case, _NormalTerms returns ErrEmptyTypeSet. +// +// _NormalTerms makes no guarantees about the order of terms, except that it +// is deterministic. +func _NormalTerms(typ types.Type) ([]*Term, error) { + switch typ := typ.(type) { + case *TypeParam: + return StructuralTerms(typ) + case *Union: + return UnionTermSet(typ) + case *types.Interface: + return InterfaceTermSet(typ) + default: + return []*Term{NewTerm(false, typ)}, nil + } +} diff --git a/vendor/golang.org/x/tools/internal/typeparams/normalize.go b/vendor/golang.org/x/tools/internal/typeparams/normalize.go index 090f142..9c631b6 100644 --- a/vendor/golang.org/x/tools/internal/typeparams/normalize.go +++ b/vendor/golang.org/x/tools/internal/typeparams/normalize.go @@ -24,20 +24,22 @@ var ErrEmptyTypeSet = errors.New("empty type set") // Structural type restrictions of a type parameter are created via // non-interface types embedded in its constraint interface (directly, or via a // chain of interface embeddings). For example, in the declaration -// type T[P interface{~int; m()}] int +// +// type T[P interface{~int; m()}] int +// // the structural restriction of the type parameter P is ~int. // // With interface embedding and unions, the specification of structural type // restrictions may be arbitrarily complex. For example, consider the // following: // -// type A interface{ ~string|~[]byte } +// type A interface{ ~string|~[]byte } // -// type B interface{ int|string } +// type B interface{ int|string } // -// type C interface { ~string|~int } +// type C interface { ~string|~int } // -// type T[P interface{ A|B; C }] int +// type T[P interface{ A|B; C }] int // // In this example, the structural type restriction of P is ~string|int: A|B // expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int, diff --git a/vendor/golang.org/x/tools/internal/typeparams/termlist.go b/vendor/golang.org/x/tools/internal/typeparams/termlist.go index 10857d5..933106a 100644 --- a/vendor/golang.org/x/tools/internal/typeparams/termlist.go +++ b/vendor/golang.org/x/tools/internal/typeparams/termlist.go @@ -97,15 +97,6 @@ func (xl termlist) norm() termlist { return rl } -// If the type set represented by xl is specified by a single (non-𝓤) term, -// structuralType returns that type. Otherwise it returns nil. -func (xl termlist) structuralType() types.Type { - if nl := xl.norm(); len(nl) == 1 { - return nl[0].typ // if nl.isAll() then typ is nil, which is ok - } - return nil -} - // union returns the union xl ∪ yl. func (xl termlist) union(yl termlist) termlist { return append(xl, yl...).norm() diff --git a/vendor/modules.txt b/vendor/modules.txt index fd6aef1..265cd48 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -4,7 +4,7 @@ github.com/DATA-DOG/go-sqlmock # github.com/andybalholm/brotli v1.0.4 ## explicit; go 1.12 github.com/andybalholm/brotli -# github.com/brianvoe/gofakeit/v6 v6.15.0 +# github.com/brianvoe/gofakeit/v6 v6.16.0 ## explicit; go 1.17 github.com/brianvoe/gofakeit/v6 github.com/brianvoe/gofakeit/v6/data @@ -65,41 +65,42 @@ github.com/kballard/go-shellquote github.com/klauspost/compress/flate github.com/klauspost/compress/gzip github.com/klauspost/compress/zlib -# github.com/lestrrat-go/backoff/v2 v2.0.8 -## explicit; go 1.16 -github.com/lestrrat-go/backoff/v2 # github.com/lestrrat-go/blackmagic v1.0.1 ## explicit; go 1.16 github.com/lestrrat-go/blackmagic # github.com/lestrrat-go/httpcc v1.0.1 ## explicit; go 1.16 github.com/lestrrat-go/httpcc +# github.com/lestrrat-go/httprc v1.0.1 +## explicit; go 1.17 +github.com/lestrrat-go/httprc # github.com/lestrrat-go/iter v1.0.2 ## explicit; go 1.13 github.com/lestrrat-go/iter/arrayiter github.com/lestrrat-go/iter/mapiter -# github.com/lestrrat-go/jwx v1.2.25 -## explicit; go 1.15 -github.com/lestrrat-go/jwx -github.com/lestrrat-go/jwx/internal/base64 -github.com/lestrrat-go/jwx/internal/ecutil -github.com/lestrrat-go/jwx/internal/iter -github.com/lestrrat-go/jwx/internal/json -github.com/lestrrat-go/jwx/internal/keyconv -github.com/lestrrat-go/jwx/internal/pool -github.com/lestrrat-go/jwx/jwa -github.com/lestrrat-go/jwx/jwe -github.com/lestrrat-go/jwx/jwe/internal/aescbc -github.com/lestrrat-go/jwx/jwe/internal/cipher -github.com/lestrrat-go/jwx/jwe/internal/concatkdf -github.com/lestrrat-go/jwx/jwe/internal/content_crypt -github.com/lestrrat-go/jwx/jwe/internal/keyenc -github.com/lestrrat-go/jwx/jwe/internal/keygen -github.com/lestrrat-go/jwx/jwk -github.com/lestrrat-go/jwx/jws -github.com/lestrrat-go/jwx/jwt -github.com/lestrrat-go/jwx/jwt/internal/types -github.com/lestrrat-go/jwx/x25519 +# github.com/lestrrat-go/jwx/v2 v2.0.2 +## explicit; go 1.16 +github.com/lestrrat-go/jwx/v2 +github.com/lestrrat-go/jwx/v2/cert +github.com/lestrrat-go/jwx/v2/internal/base64 +github.com/lestrrat-go/jwx/v2/internal/ecutil +github.com/lestrrat-go/jwx/v2/internal/iter +github.com/lestrrat-go/jwx/v2/internal/json +github.com/lestrrat-go/jwx/v2/internal/keyconv +github.com/lestrrat-go/jwx/v2/internal/pool +github.com/lestrrat-go/jwx/v2/jwa +github.com/lestrrat-go/jwx/v2/jwe +github.com/lestrrat-go/jwx/v2/jwe/internal/aescbc +github.com/lestrrat-go/jwx/v2/jwe/internal/cipher +github.com/lestrrat-go/jwx/v2/jwe/internal/concatkdf +github.com/lestrrat-go/jwx/v2/jwe/internal/content_crypt +github.com/lestrrat-go/jwx/v2/jwe/internal/keyenc +github.com/lestrrat-go/jwx/v2/jwe/internal/keygen +github.com/lestrrat-go/jwx/v2/jwk +github.com/lestrrat-go/jwx/v2/jws +github.com/lestrrat-go/jwx/v2/jwt +github.com/lestrrat-go/jwx/v2/jwt/internal/types +github.com/lestrrat-go/jwx/v2/x25519 # github.com/lestrrat-go/option v1.0.0 ## explicit; go 1.16 github.com/lestrrat-go/option @@ -124,9 +125,6 @@ github.com/pelletier/go-toml/v2/internal/tracker # github.com/philhofer/fwd v1.1.1 ## explicit github.com/philhofer/fwd -# github.com/pkg/errors v0.9.1 -## explicit -github.com/pkg/errors # github.com/pmezard/go-difflib v1.0.0 ## explicit github.com/pmezard/go-difflib/difflib @@ -208,7 +206,7 @@ golang.org/x/crypto/curve25519 golang.org/x/crypto/curve25519/internal/field golang.org/x/crypto/ed25519 golang.org/x/crypto/pbkdf2 -# golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 +# golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 ## explicit; go 1.17 golang.org/x/mod/semver # golang.org/x/net v0.0.0-20220607020251-c690dde0001d @@ -236,7 +234,7 @@ golang.org/x/text/message golang.org/x/text/message/catalog golang.org/x/text/transform golang.org/x/text/unicode/norm -# golang.org/x/tools v0.1.10 +# golang.org/x/tools v0.1.11 ## explicit; go 1.17 golang.org/x/tools/go/gcexportdata golang.org/x/tools/go/internal/gcimporter @@ -325,7 +323,7 @@ modernc.org/token # source.toby3d.me/toby3d/form v0.3.0 ## explicit; go 1.18 source.toby3d.me/toby3d/form -# source.toby3d.me/toby3d/middleware v0.9.1 +# source.toby3d.me/toby3d/middleware v0.9.2 ## explicit; go 1.17 source.toby3d.me/toby3d/middleware # willnorris.com/go/microformats v1.1.1 diff --git a/vendor/source.toby3d.me/toby3d/middleware/jwt.go b/vendor/source.toby3d.me/toby3d/middleware/jwt.go index 7f5db9c..755be87 100644 --- a/vendor/source.toby3d.me/toby3d/middleware/jwt.go +++ b/vendor/source.toby3d.me/toby3d/middleware/jwt.go @@ -4,8 +4,8 @@ import ( "errors" "fmt" - "github.com/lestrrat-go/jwx/jwa" - "github.com/lestrrat-go/jwx/jwt" + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwt" http "github.com/valyala/fasthttp" ) @@ -292,9 +292,7 @@ func JWTWithConfig(config JWTConfig) Interceptor { } func (config *JWTConfig) defaultParseToken(auth []byte, ctx *http.RequestCtx) (interface{}, error) { - token, err := jwt.Parse( - auth, jwt.WithVerify(config.SigningMethod, config.SigningKey), jwt.WithValidate(true), - ) + token, err := jwt.Parse(auth, jwt.WithKey(config.SigningMethod, config.SigningKey), jwt.WithVerify(true)) if err != nil { return nil, fmt.Errorf("cannot parse JWT token: %w", err) }