snapshot of https://github.com/cortezaproject/corteza.git (2024.9.x, 2026-09-18; blobs > 131072B omitted)
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
This commit is contained in:
35
server/vendor/github.com/lestrrat-go/jwx/.gitignore
generated
vendored
Normal file
35
server/vendor/github.com/lestrrat-go/jwx/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
# 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
|
||||
|
||||
# IDE
|
||||
.idea
|
||||
.vscode
|
||||
.DS_Store
|
||||
*~
|
||||
|
||||
coverage.out
|
||||
|
||||
# I redirect my test output to files named "out" way too often
|
||||
out
|
||||
102
server/vendor/github.com/lestrrat-go/jwx/.golangci.yml
generated
vendored
Normal file
102
server/vendor/github.com/lestrrat-go/jwx/.golangci.yml
generated
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
run:
|
||||
|
||||
linters-settings:
|
||||
govet:
|
||||
enable-all: true
|
||||
disable:
|
||||
- shadow
|
||||
- fieldalignment
|
||||
|
||||
linters:
|
||||
enable-all: true
|
||||
disable:
|
||||
- contextcheck
|
||||
- cyclop
|
||||
- depguard
|
||||
- deadcode # deprecated
|
||||
- dupl
|
||||
- exhaustive
|
||||
- exhaustivestruct
|
||||
- exhaustruct
|
||||
- errorlint
|
||||
- funlen
|
||||
- gci
|
||||
- gochecknoglobals
|
||||
- gochecknoinits
|
||||
- gocognit
|
||||
- gocritic
|
||||
- gocyclo
|
||||
- godot
|
||||
- godox
|
||||
- goerr113
|
||||
- gofumpt
|
||||
- golint #deprecated
|
||||
- gomnd
|
||||
- gomoddirectives # I think it's broken
|
||||
- gosec
|
||||
- gosmopolitan
|
||||
- govet
|
||||
- interfacer # deprecated
|
||||
- interfacebloat
|
||||
- ifshort
|
||||
- ireturn # No, I _LIKE_ returning interfaces
|
||||
- lll
|
||||
- maintidx # Do this in code review
|
||||
- maligned # deprecated
|
||||
- makezero
|
||||
- nonamedreturns
|
||||
- nakedret
|
||||
- nestif
|
||||
- nlreturn
|
||||
- nosnakecase # deprecated
|
||||
- paralleltest
|
||||
- scopelint # deprecated
|
||||
- structcheck # deprecated
|
||||
- tagliatelle
|
||||
- testpackage
|
||||
- thelper # Tests are fine
|
||||
- varcheck # deprecated
|
||||
- varnamelen # Short names are ok
|
||||
- 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: internal/jwxtest/jwxtest.go
|
||||
linters:
|
||||
- errcheck
|
||||
- errchkjson
|
||||
- forcetypeassert
|
||||
- path: /*_test.go
|
||||
linters:
|
||||
- errcheck
|
||||
- errchkjson
|
||||
- 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
|
||||
|
||||
767
server/vendor/github.com/lestrrat-go/jwx/Changes
generated
vendored
Normal file
767
server/vendor/github.com/lestrrat-go/jwx/Changes
generated
vendored
Normal file
@@ -0,0 +1,767 @@
|
||||
Changes
|
||||
=======
|
||||
|
||||
v1.2.29 07 Mar 2024
|
||||
* [jwe] Added `jwe.Settings(jwe.WithMaxDecompressBufferSize(int64))` to specify the
|
||||
maximum size of a decompressed JWE payload. The default value is 10MB. If you
|
||||
are compressing payloads greater than this, you need to explicitly set it.
|
||||
|
||||
Unlike in v2, there is no way to set this globally. Please use v2 if this is required.
|
||||
|
||||
v1.2.28 09 Jan 2024
|
||||
[Security Fixes]
|
||||
* [jws] JWS messages formated in full JSON format (i.e. not the compact format, which
|
||||
consists of three base64 strings concatenated with a '.') with missing "protected"
|
||||
headers could cause a panic, thereby introducing a possiblity of a DoS.
|
||||
|
||||
This has been fixed so that the `jws.Parse` function succeeds in parsing a JWS message
|
||||
lacking a protected header. Calling `jws.Verify` on this same JWS message will result
|
||||
in a failed verification attempt. Note that this behavior will differ slightly when
|
||||
parsing JWS messages in compact form, which result in an error.
|
||||
|
||||
v1.2.27 - 03 Dec 2023
|
||||
[Security]
|
||||
* [jwe] A large number in p2c parameter for PBKDF2 based encryptions could cause a DoS attack,
|
||||
similar to https://nvd.nist.gov/vuln/detail/CVE-2022-36083. All users should upgrade, as
|
||||
unlike v2, v1 attempts to decrypt JWEs on JWTs by default.
|
||||
[GHSA-7f9x-gw85-8grf]
|
||||
|
||||
[Bug Fixes]
|
||||
* [jwk] jwk.Set(jwk.KeyOpsKey, <jwk.KeyOperation>) now works (previously, either
|
||||
Set(.., <string>) or Set(..., []jwk.KeyOperation{...}) worked, but not a single
|
||||
jwk.KeyOperation
|
||||
|
||||
v1.2.26 - 14 Jun 2023
|
||||
[Security]
|
||||
* Potential Padding Oracle Attack Vulnerability and Timing Attack Vulnerability
|
||||
for JWE AES-CBC encrypted payloads affecting all v2 releases up to v2.0.10,
|
||||
all v1 releases up to v1.2.25, and all v0 releases up to v0.9.2 have been reported by
|
||||
@shogo82148.
|
||||
|
||||
Please note that v0 versions will NOT receive fixes.
|
||||
This release fixes these vulnerabilities for the v1 series.
|
||||
|
||||
[Miscellaneous]
|
||||
* JWE tests now only run algorithms that are supported by the underlying
|
||||
`jose` tool
|
||||
|
||||
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 presence 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.
|
||||
22
server/vendor/github.com/lestrrat-go/jwx/LICENSE
generated
vendored
Normal file
22
server/vendor/github.com/lestrrat-go/jwx/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 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
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
81
server/vendor/github.com/lestrrat-go/jwx/Makefile
generated
vendored
Normal file
81
server/vendor/github.com/lestrrat-go/jwx/Makefile
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
.PHONY: generate realclean cover viewcover test lint check_diffs imports tidy
|
||||
generate:
|
||||
@go 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:
|
||||
rm coverage.out
|
||||
|
||||
test-cmd:
|
||||
go test -v -race $(TESTOPTS)
|
||||
|
||||
test:
|
||||
$(MAKE) TESTOPTS=./... test-cmd
|
||||
$(MAKE) -f $(PWD)/Makefile -C examples test-cmd
|
||||
$(MAKE) -f $(PWD)/Makefile -C bench/performance test-cmd
|
||||
|
||||
cover-cmd:
|
||||
$(MAKE) test-cmd
|
||||
$(MAKE) -f $(PWD)/Makefile -C examples TESTOPTS= test-cmd
|
||||
$(MAKE) -f $(PWD)/Makefile -C bench/performance TESTOPTS= test-cmd
|
||||
$(MAKE) -f $(PWD)/Makefile -C cmd/jwx TESTOPTS= test-cmd
|
||||
@# This is NOT cheating. tools to generate code, and tools to
|
||||
@# run tests don't need to be included in the final result.
|
||||
@cat coverage.out.tmp | grep -v "internal/jose" | grep -v "internal/jwxtest" | grep -v "internal/cmd" > coverage.out
|
||||
@rm coverage.out.tmp
|
||||
|
||||
cover:
|
||||
$(MAKE) cover-stdlib
|
||||
|
||||
cover-stdlib:
|
||||
$(MAKE) cover-cmd TESTOPTS="-coverpkg=./... -coverprofile=coverage.out.tmp ./..."
|
||||
|
||||
cover-goccy:
|
||||
$(MAKE) cover-cmd TESTOPTS="-tags jwx_goccy -coverpkg=./... -coverprofile=coverage.out.tmp ./..."
|
||||
|
||||
cover-es256k:
|
||||
$(MAKE) cover-cmd TESTOPTS="-tags jwx_es256k -coverpkg=./... -coverprofile=coverage.out.tmp ./..."
|
||||
|
||||
cover-all:
|
||||
$(MAKE) cover-cmd TESTOPTS="-tags jwx_goccy,jwx_es256k -coverpkg=./... -coverprofile=coverage.out.tmp ./..."
|
||||
|
||||
smoke-cmd:
|
||||
$(MAKE) test-cmd
|
||||
$(MAKE) -f $(PWD)/Makefile -C examples test-cmd
|
||||
$(MAKE) -f $(PWD)/Makefile -C bench/performance test-cmd
|
||||
$(MAKE) -f $(PWD)/Makefile -C cmd/jwx test-cmd
|
||||
|
||||
smoke:
|
||||
$(MAKE) smoke-stdlib
|
||||
|
||||
smoke-stdlib:
|
||||
$(MAKE) smoke-cmd TESTOPTS="-short ./..."
|
||||
|
||||
smoke-goccy:
|
||||
$(MAKE) smoke-cmd TESTOPTS="-short -tags jwx_goccy ./..."
|
||||
|
||||
smoke-es256k:
|
||||
$(MAKE) smoke-cmd TESTOPTS="-short -tags jwx_es256k ./..."
|
||||
|
||||
smoke-all:
|
||||
$(MAKE) smoke-cmd TESTOPTS="-short -tags jwx_goccy,jwx_es256k ./..."
|
||||
|
||||
viewcover:
|
||||
go tool cover -html=coverage.out
|
||||
|
||||
lint:
|
||||
golangci-lint run ./...
|
||||
|
||||
check_diffs:
|
||||
./scripts/check-diff.sh
|
||||
|
||||
imports:
|
||||
goimports -w ./
|
||||
|
||||
tidy:
|
||||
./scripts/tidy.sh
|
||||
|
||||
134
server/vendor/github.com/lestrrat-go/jwx/README.md
generated
vendored
Normal file
134
server/vendor/github.com/lestrrat-go/jwx/README.md
generated
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
# github.com/lestrrat-go/jwx  [](https://pkg.go.dev/github.com/lestrrat-go/jwx) [](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.
|
||||
|
||||
95
server/vendor/github.com/lestrrat-go/jwx/format.go
generated
vendored
Normal file
95
server/vendor/github.com/lestrrat-go/jwx/format.go
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
package jwx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type FormatKind int
|
||||
|
||||
const (
|
||||
UnknownFormat FormatKind = iota
|
||||
JWE
|
||||
JWS
|
||||
JWK
|
||||
JWKS
|
||||
JWT
|
||||
)
|
||||
|
||||
type formatHint struct {
|
||||
Payload json.RawMessage `json:"payload"` // Only in JWS
|
||||
Signatures json.RawMessage `json:"signatures"` // Only in JWS
|
||||
Ciphertext json.RawMessage `json:"ciphertext"` // Only in JWE
|
||||
KeyType json.RawMessage `json:"kty"` // Only in JWK
|
||||
Keys json.RawMessage `json:"keys"` // Only in JWKS
|
||||
Audience json.RawMessage `json:"aud"` // Only in JWT
|
||||
}
|
||||
|
||||
// GuessFormat is used to guess the format the given payload is in
|
||||
// using heuristics. See the type FormatKind for a full list of
|
||||
// possible types.
|
||||
//
|
||||
// This may be useful in determining your next action when you may
|
||||
// encounter a payload that could either be a JWE, JWS, or a plain JWT.
|
||||
//
|
||||
// Because JWTs are almost always JWS signed, you may be thrown off
|
||||
// if you pass what you think is a JWT payload to this function.
|
||||
// If the function is in the "Compact" format, it means it's a JWS
|
||||
// signed message, and its payload is the JWT. Therefore this function
|
||||
// will reuturn JWS, not JWT.
|
||||
//
|
||||
// This function requires an extra parsing of the payload, and therefore
|
||||
// may be inefficient if you call it every time before parsing.
|
||||
func GuessFormat(payload []byte) FormatKind {
|
||||
// The check against kty, keys, and aud are something this library
|
||||
// made up. for the distinctions between JWE and JWS, we used
|
||||
// https://datatracker.ietf.org/doc/html/rfc7516#section-9.
|
||||
//
|
||||
// The above RFC described several ways to distinguish between
|
||||
// a JWE and JWS JSON, but we're only using one of them
|
||||
|
||||
payload = bytes.TrimSpace(payload)
|
||||
if len(payload) <= 0 {
|
||||
return UnknownFormat
|
||||
}
|
||||
|
||||
if payload[0] != '{' {
|
||||
// Compact format. It's probably a JWS or JWE
|
||||
sep := []byte{'.'} // I want to const this :/
|
||||
|
||||
// Note: this counts the number of occurrences of the
|
||||
// separator, but the RFC talks about the number of segments.
|
||||
// number of '.' == segments - 1, so that's why we have 2 and 4 here
|
||||
switch count := bytes.Count(payload, sep); count {
|
||||
case 2:
|
||||
return JWS
|
||||
case 4:
|
||||
return JWE
|
||||
default:
|
||||
return UnknownFormat
|
||||
}
|
||||
}
|
||||
|
||||
// If we got here, we probably have JSON.
|
||||
var h formatHint
|
||||
if err := json.Unmarshal(payload, &h); err != nil {
|
||||
return UnknownFormat
|
||||
}
|
||||
|
||||
if h.Audience != nil {
|
||||
return JWT
|
||||
}
|
||||
if h.KeyType != nil {
|
||||
return JWK
|
||||
}
|
||||
if h.Keys != nil {
|
||||
return JWKS
|
||||
}
|
||||
if h.Ciphertext != nil {
|
||||
return JWE
|
||||
}
|
||||
if h.Signatures != nil && h.Payload != nil {
|
||||
return JWS
|
||||
}
|
||||
return UnknownFormat
|
||||
}
|
||||
28
server/vendor/github.com/lestrrat-go/jwx/formatkind_string_gen.go
generated
vendored
Normal file
28
server/vendor/github.com/lestrrat-go/jwx/formatkind_string_gen.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
// Code generated by "stringer -type=FormatKind"; DO NOT EDIT.
|
||||
|
||||
package jwx
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[UnknownFormat-0]
|
||||
_ = x[JWE-1]
|
||||
_ = x[JWS-2]
|
||||
_ = x[JWK-3]
|
||||
_ = x[JWKS-4]
|
||||
_ = x[JWT-5]
|
||||
}
|
||||
|
||||
const _FormatKind_name = "UnknownFormatJWEJWSJWKJWKSJWT"
|
||||
|
||||
var _FormatKind_index = [...]uint8{0, 13, 16, 19, 22, 26, 29}
|
||||
|
||||
func (i FormatKind) String() string {
|
||||
if i < 0 || i >= FormatKind(len(_FormatKind_index)-1) {
|
||||
return "FormatKind(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _FormatKind_name[_FormatKind_index[i]:_FormatKind_index[i+1]]
|
||||
}
|
||||
14
server/vendor/github.com/lestrrat-go/jwx/gen.sh
generated
vendored
Normal file
14
server/vendor/github.com/lestrrat-go/jwx/gen.sh
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
#!/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
|
||||
66
server/vendor/github.com/lestrrat-go/jwx/internal/base64/base64.go
generated
vendored
Normal file
66
server/vendor/github.com/lestrrat-go/jwx/internal/base64/base64.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
package base64
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func Encode(src []byte) []byte {
|
||||
enc := base64.RawURLEncoding
|
||||
dst := make([]byte, enc.EncodedLen(len(src)))
|
||||
enc.Encode(dst, src)
|
||||
return dst
|
||||
}
|
||||
|
||||
func EncodeToStringStd(src []byte) string {
|
||||
return base64.StdEncoding.EncodeToString(src)
|
||||
}
|
||||
|
||||
func EncodeToString(src []byte) string {
|
||||
return base64.RawURLEncoding.EncodeToString(src)
|
||||
}
|
||||
|
||||
func EncodeUint64ToString(v uint64) string {
|
||||
data := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(data, v)
|
||||
|
||||
i := 0
|
||||
for ; i < len(data); i++ {
|
||||
if data[i] != 0x0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return EncodeToString(data[i:])
|
||||
}
|
||||
|
||||
func Decode(src []byte) ([]byte, error) {
|
||||
var enc *base64.Encoding
|
||||
|
||||
var isRaw = !bytes.HasSuffix(src, []byte{'='})
|
||||
var isURL = !bytes.ContainsAny(src, "+/")
|
||||
switch {
|
||||
case isRaw && isURL:
|
||||
enc = base64.RawURLEncoding
|
||||
case isURL:
|
||||
enc = base64.URLEncoding
|
||||
case isRaw:
|
||||
enc = base64.RawStdEncoding
|
||||
default:
|
||||
enc = base64.StdEncoding
|
||||
}
|
||||
|
||||
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 dst[:n], nil
|
||||
}
|
||||
|
||||
func DecodeString(src string) ([]byte, error) {
|
||||
return Decode([]byte(src))
|
||||
}
|
||||
110
server/vendor/github.com/lestrrat-go/jwx/internal/ecutil/ecutil.go
generated
vendored
Normal file
110
server/vendor/github.com/lestrrat-go/jwx/internal/ecutil/ecutil.go
generated
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
// Package ecutil defines tools that help with elliptic curve related
|
||||
// computation
|
||||
package ecutil
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"math/big"
|
||||
"sync"
|
||||
|
||||
"github.com/lestrrat-go/jwx/jwa"
|
||||
)
|
||||
|
||||
// data for available curves. Some algorithms may be compiled in/out
|
||||
var curveToAlg = map[elliptic.Curve]jwa.EllipticCurveAlgorithm{}
|
||||
var algToCurve = map[jwa.EllipticCurveAlgorithm]elliptic.Curve{}
|
||||
var availableAlgs []jwa.EllipticCurveAlgorithm
|
||||
var availableCrvs []elliptic.Curve
|
||||
|
||||
func RegisterCurve(crv elliptic.Curve, alg jwa.EllipticCurveAlgorithm) {
|
||||
curveToAlg[crv] = alg
|
||||
algToCurve[alg] = crv
|
||||
availableAlgs = append(availableAlgs, alg)
|
||||
availableCrvs = append(availableCrvs, crv)
|
||||
}
|
||||
|
||||
func IsAvailable(alg jwa.EllipticCurveAlgorithm) bool {
|
||||
_, ok := algToCurve[alg]
|
||||
return ok
|
||||
}
|
||||
|
||||
func AvailableAlgorithms() []jwa.EllipticCurveAlgorithm {
|
||||
return availableAlgs
|
||||
}
|
||||
|
||||
func AvailableCurves() []elliptic.Curve {
|
||||
return availableCrvs
|
||||
}
|
||||
|
||||
func AlgorithmForCurve(crv elliptic.Curve) (jwa.EllipticCurveAlgorithm, bool) {
|
||||
v, ok := curveToAlg[crv]
|
||||
return v, ok
|
||||
}
|
||||
|
||||
func CurveForAlgorithm(alg jwa.EllipticCurveAlgorithm) (elliptic.Curve, bool) {
|
||||
v, ok := algToCurve[alg]
|
||||
return v, ok
|
||||
}
|
||||
|
||||
const (
|
||||
// size of buffer that needs to be allocated for EC521 curve
|
||||
ec521BufferSize = 66 // (521 / 8) + 1
|
||||
)
|
||||
|
||||
var ecpointBufferPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
// In most cases the curve bit size will be less than this length
|
||||
// so allocate the maximum, and keep reusing
|
||||
buf := make([]byte, 0, ec521BufferSize)
|
||||
return &buf
|
||||
},
|
||||
}
|
||||
|
||||
func getCrvFixedBuffer(size int) []byte {
|
||||
//nolint:forcetypeassert
|
||||
buf := *(ecpointBufferPool.Get().(*[]byte))
|
||||
if size > ec521BufferSize && cap(buf) < size {
|
||||
buf = append(buf, make([]byte, size-cap(buf))...)
|
||||
}
|
||||
return buf[:size]
|
||||
}
|
||||
|
||||
// ReleaseECPointBuffer releases the []byte buffer allocated.
|
||||
func ReleaseECPointBuffer(buf []byte) {
|
||||
buf = buf[:cap(buf)]
|
||||
buf[0] = 0x0
|
||||
for i := 1; i < len(buf); i *= 2 {
|
||||
copy(buf[i:], buf[:i])
|
||||
}
|
||||
buf = buf[:0]
|
||||
ecpointBufferPool.Put(&buf)
|
||||
}
|
||||
|
||||
// AllocECPointBuffer allocates a buffer for the given point in the given
|
||||
// curve. This buffer should be released using the ReleaseECPointBuffer
|
||||
// function.
|
||||
func AllocECPointBuffer(v *big.Int, crv elliptic.Curve) []byte {
|
||||
// We need to create a buffer that fits the entire curve.
|
||||
// If the curve size is 66, that fits in 9 bytes. If the curve
|
||||
// size is 64, it fits in 8 bytes.
|
||||
bits := crv.Params().BitSize
|
||||
|
||||
// For most common cases we know before hand what the byte length
|
||||
// is going to be. optimize
|
||||
var inBytes int
|
||||
switch bits {
|
||||
case 224, 256, 384: // TODO: use constant?
|
||||
inBytes = bits / 8
|
||||
case 521:
|
||||
inBytes = ec521BufferSize
|
||||
default:
|
||||
inBytes = bits / 8
|
||||
if (bits % 8) != 0 {
|
||||
inBytes++
|
||||
}
|
||||
}
|
||||
|
||||
buf := getCrvFixedBuffer(inBytes)
|
||||
v.FillBytes(buf)
|
||||
return buf
|
||||
}
|
||||
36
server/vendor/github.com/lestrrat-go/jwx/internal/iter/mapiter.go
generated
vendored
Normal file
36
server/vendor/github.com/lestrrat-go/jwx/internal/iter/mapiter.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
package iter
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/lestrrat-go/iter/mapiter"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// MapVisitor is a specialized visitor for our purposes.
|
||||
// Whereas mapiter.Visitor supports any type of key, this
|
||||
// visitor assumes the key is a string
|
||||
type MapVisitor interface {
|
||||
Visit(string, interface{}) error
|
||||
}
|
||||
|
||||
type MapVisitorFunc func(string, interface{}) error
|
||||
|
||||
func (fn MapVisitorFunc) Visit(s string, v interface{}) error {
|
||||
return fn(s, v)
|
||||
}
|
||||
|
||||
func WalkMap(ctx context.Context, src mapiter.Source, visitor MapVisitor) error {
|
||||
return mapiter.Walk(ctx, src, mapiter.VisitorFunc(func(k, v interface{}) error {
|
||||
//nolint:forcetypeassert
|
||||
return visitor.Visit(k.(string), v)
|
||||
}))
|
||||
}
|
||||
|
||||
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 m, nil
|
||||
}
|
||||
50
server/vendor/github.com/lestrrat-go/jwx/internal/json/goccy.go
generated
vendored
Normal file
50
server/vendor/github.com/lestrrat-go/jwx/internal/json/goccy.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
// +build jwx_goccy
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/goccy/go-json"
|
||||
)
|
||||
|
||||
type Decoder = json.Decoder
|
||||
type Delim = json.Delim
|
||||
type Encoder = json.Encoder
|
||||
type Marshaler = json.Marshaler
|
||||
type Number = json.Number
|
||||
type RawMessage = json.RawMessage
|
||||
type Unmarshaler = json.Unmarshaler
|
||||
|
||||
func Engine() string {
|
||||
return "github.com/goccy/go-json"
|
||||
}
|
||||
|
||||
// NewDecoder respects the values specified in DecoderSettings,
|
||||
// and creates a Decoder that has certain features turned on/off
|
||||
func NewDecoder(r io.Reader) *json.Decoder {
|
||||
dec := json.NewDecoder(r)
|
||||
|
||||
muGlobalConfig.RLock()
|
||||
if useNumber {
|
||||
dec.UseNumber()
|
||||
}
|
||||
muGlobalConfig.RUnlock()
|
||||
|
||||
return dec
|
||||
}
|
||||
|
||||
// NewEncoder is just a proxy for "encoding/json".NewEncoder
|
||||
func NewEncoder(w io.Writer) *json.Encoder {
|
||||
return json.NewEncoder(w)
|
||||
}
|
||||
|
||||
// Marshal is just a proxy for "encoding/json".Marshal
|
||||
func Marshal(v interface{}) ([]byte, error) {
|
||||
return json.Marshal(v)
|
||||
}
|
||||
|
||||
// MarshalIndent is just a proxy for "encoding/json".MarshalIndent
|
||||
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
|
||||
return json.MarshalIndent(v, prefix, indent)
|
||||
}
|
||||
105
server/vendor/github.com/lestrrat-go/jwx/internal/json/json.go
generated
vendored
Normal file
105
server/vendor/github.com/lestrrat-go/jwx/internal/json/json.go
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/lestrrat-go/jwx/internal/base64"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var muGlobalConfig sync.RWMutex
|
||||
var useNumber bool
|
||||
|
||||
// Sets the global configuration for json decoding
|
||||
func DecoderSettings(inUseNumber bool) {
|
||||
muGlobalConfig.Lock()
|
||||
useNumber = inUseNumber
|
||||
muGlobalConfig.Unlock()
|
||||
}
|
||||
|
||||
// Unmarshal respects the values specified in DecoderSettings,
|
||||
// and uses a Decoder that has certain features turned on/off
|
||||
func Unmarshal(b []byte, v interface{}) error {
|
||||
dec := NewDecoder(bytes.NewReader(b))
|
||||
return dec.Decode(v)
|
||||
}
|
||||
|
||||
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`)
|
||||
}
|
||||
|
||||
buf, err := base64.DecodeString(val)
|
||||
if err != nil {
|
||||
return errors.Errorf(`expected base64 encoded []byte (%T)`, val)
|
||||
}
|
||||
*dst = buf
|
||||
return nil
|
||||
}
|
||||
|
||||
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 val, nil
|
||||
}
|
||||
|
||||
func AssignNextStringToken(dst **string, dec *Decoder) error {
|
||||
val, err := ReadNextStringToken(dec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*dst = &val
|
||||
return nil
|
||||
}
|
||||
|
||||
// FlattenAudience is a flag to specify if we should flatten the "aud"
|
||||
// entry to a string when there's only one entry.
|
||||
// In jwx < 1.1.8 we just dumped everything as an array of strings,
|
||||
// but apparently AWS Cognito doesn't handle this well.
|
||||
//
|
||||
// So now we have the ability to dump "aud" as a string if there's
|
||||
// only one entry, but we need to retain the old behavior so that
|
||||
// we don't accidentally break somebody else's code. (e.g. messing
|
||||
// up how signatures are calculated)
|
||||
var FlattenAudience uint32
|
||||
|
||||
func EncodeAudience(enc *Encoder, aud []string) error {
|
||||
var val interface{}
|
||||
if len(aud) == 1 && atomic.LoadUint32(&FlattenAudience) == 1 {
|
||||
val = aud[0]
|
||||
} else {
|
||||
val = aud
|
||||
}
|
||||
return enc.Encode(val)
|
||||
}
|
||||
|
||||
// DecodeCtx is an interface for objects that needs that extra something
|
||||
// when decoding JSON into an object.
|
||||
type DecodeCtx interface {
|
||||
Registry() *Registry
|
||||
}
|
||||
|
||||
// DecodeCtxContainer is used to differentiate objects that can carry extra
|
||||
// decoding hints and those who can't.
|
||||
type DecodeCtxContainer interface {
|
||||
DecodeCtx() DecodeCtx
|
||||
SetDecodeCtx(DecodeCtx)
|
||||
}
|
||||
|
||||
// stock decodeCtx. should cover 80% of the cases
|
||||
type decodeCtx struct {
|
||||
registry *Registry
|
||||
}
|
||||
|
||||
func NewDecodeCtx(r *Registry) DecodeCtx {
|
||||
return &decodeCtx{registry: r}
|
||||
}
|
||||
|
||||
func (dc *decodeCtx) Registry() *Registry {
|
||||
return dc.registry
|
||||
}
|
||||
53
server/vendor/github.com/lestrrat-go/jwx/internal/json/registry.go
generated
vendored
Normal file
53
server/vendor/github.com/lestrrat-go/jwx/internal/json/registry.go
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type Registry struct {
|
||||
mu *sync.RWMutex
|
||||
data map[string]reflect.Type
|
||||
}
|
||||
|
||||
func NewRegistry() *Registry {
|
||||
return &Registry{
|
||||
mu: &sync.RWMutex{},
|
||||
data: make(map[string]reflect.Type),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Registry) Register(name string, object interface{}) {
|
||||
if object == nil {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
delete(r.data, name)
|
||||
return
|
||||
}
|
||||
|
||||
typ := reflect.TypeOf(object)
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.data[name] = typ
|
||||
}
|
||||
|
||||
func (r *Registry) Decode(dec *Decoder, name string) (interface{}, error) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
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 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 decoded, nil
|
||||
}
|
||||
49
server/vendor/github.com/lestrrat-go/jwx/internal/json/stdlib.go
generated
vendored
Normal file
49
server/vendor/github.com/lestrrat-go/jwx/internal/json/stdlib.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
//go:build !jwx_goccy
|
||||
// +build !jwx_goccy
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
type Decoder = json.Decoder
|
||||
type Delim = json.Delim
|
||||
type Encoder = json.Encoder
|
||||
type Marshaler = json.Marshaler
|
||||
type Number = json.Number
|
||||
type RawMessage = json.RawMessage
|
||||
type Unmarshaler = json.Unmarshaler
|
||||
|
||||
func Engine() string {
|
||||
return "encoding/json"
|
||||
}
|
||||
|
||||
// NewDecoder respects the values specified in DecoderSettings,
|
||||
// and creates a Decoder that has certain features turned on/off
|
||||
func NewDecoder(r io.Reader) *json.Decoder {
|
||||
dec := json.NewDecoder(r)
|
||||
|
||||
muGlobalConfig.RLock()
|
||||
if useNumber {
|
||||
dec.UseNumber()
|
||||
}
|
||||
muGlobalConfig.RUnlock()
|
||||
|
||||
return dec
|
||||
}
|
||||
|
||||
func NewEncoder(w io.Writer) *json.Encoder {
|
||||
return json.NewEncoder(w)
|
||||
}
|
||||
|
||||
// Marshal is just a proxy for "encoding/json".Marshal
|
||||
func Marshal(v interface{}) ([]byte, error) {
|
||||
return json.Marshal(v)
|
||||
}
|
||||
|
||||
// MarshalIndent is just a proxy for "encoding/json".MarshalIndent
|
||||
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
|
||||
return json.MarshalIndent(v, prefix, indent)
|
||||
}
|
||||
177
server/vendor/github.com/lestrrat-go/jwx/internal/keyconv/keyconv.go
generated
vendored
Normal file
177
server/vendor/github.com/lestrrat-go/jwx/internal/keyconv/keyconv.go
generated
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
package keyconv
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
|
||||
"github.com/lestrrat-go/blackmagic"
|
||||
"github.com/lestrrat-go/jwx/jwk"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
)
|
||||
|
||||
// RSAPrivateKey assigns src to dst.
|
||||
// `dst` should be a pointer to a rsa.PrivateKey.
|
||||
// `src` may be rsa.PrivateKey, *rsa.PrivateKey, or a jwk.Key
|
||||
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)
|
||||
}
|
||||
src = &raw
|
||||
}
|
||||
|
||||
var ptr *rsa.PrivateKey
|
||||
switch src := src.(type) {
|
||||
case rsa.PrivateKey:
|
||||
ptr = &src
|
||||
case *rsa.PrivateKey:
|
||||
ptr = src
|
||||
default:
|
||||
return errors.Errorf(`expected rsa.PrivateKey or *rsa.PrivateKey, got %T`, src)
|
||||
}
|
||||
|
||||
return blackmagic.AssignIfCompatible(dst, ptr)
|
||||
}
|
||||
|
||||
// RSAPublicKey assigns src to dst
|
||||
// `dst` should be a pointer to a non-zero rsa.PublicKey.
|
||||
// `src` may be rsa.PublicKey, *rsa.PublicKey, or a jwk.Key
|
||||
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)
|
||||
}
|
||||
src = &raw
|
||||
}
|
||||
|
||||
var ptr *rsa.PublicKey
|
||||
switch src := src.(type) {
|
||||
case rsa.PublicKey:
|
||||
ptr = &src
|
||||
case *rsa.PublicKey:
|
||||
ptr = src
|
||||
default:
|
||||
return errors.Errorf(`expected rsa.PublicKey or *rsa.PublicKey, got %T`, src)
|
||||
}
|
||||
|
||||
return blackmagic.AssignIfCompatible(dst, ptr)
|
||||
}
|
||||
|
||||
// ECDSAPrivateKey assigns src to dst, converting its type from a
|
||||
// non-pointer to a pointer
|
||||
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)
|
||||
}
|
||||
src = &raw
|
||||
}
|
||||
|
||||
var ptr *ecdsa.PrivateKey
|
||||
switch src := src.(type) {
|
||||
case ecdsa.PrivateKey:
|
||||
ptr = &src
|
||||
case *ecdsa.PrivateKey:
|
||||
ptr = src
|
||||
default:
|
||||
return errors.Errorf(`expected ecdsa.PrivateKey or *ecdsa.PrivateKey, got %T`, src)
|
||||
}
|
||||
return blackmagic.AssignIfCompatible(dst, ptr)
|
||||
}
|
||||
|
||||
// ECDSAPublicKey assigns src to dst, converting its type from a
|
||||
// non-pointer to a pointer
|
||||
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)
|
||||
}
|
||||
src = &raw
|
||||
}
|
||||
|
||||
var ptr *ecdsa.PublicKey
|
||||
switch src := src.(type) {
|
||||
case ecdsa.PublicKey:
|
||||
ptr = &src
|
||||
case *ecdsa.PublicKey:
|
||||
ptr = src
|
||||
default:
|
||||
return errors.Errorf(`expected ecdsa.PublicKey or *ecdsa.PublicKey, got %T`, src)
|
||||
}
|
||||
return blackmagic.AssignIfCompatible(dst, ptr)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
src = raw
|
||||
}
|
||||
|
||||
if _, ok := src.([]byte); !ok {
|
||||
return errors.Errorf(`expected []byte, got %T`, src)
|
||||
}
|
||||
return blackmagic.AssignIfCompatible(dst, src)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
src = &raw
|
||||
}
|
||||
|
||||
var ptr *ed25519.PrivateKey
|
||||
switch src := src.(type) {
|
||||
case ed25519.PrivateKey:
|
||||
ptr = &src
|
||||
case *ed25519.PrivateKey:
|
||||
ptr = src
|
||||
default:
|
||||
return errors.Errorf(`expected ed25519.PrivateKey or *ed25519.PrivateKey, got %T`, src)
|
||||
}
|
||||
return blackmagic.AssignIfCompatible(dst, ptr)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
src = &raw
|
||||
}
|
||||
|
||||
var ptr *ed25519.PublicKey
|
||||
switch src := src.(type) {
|
||||
case ed25519.PublicKey:
|
||||
ptr = &src
|
||||
case *ed25519.PublicKey:
|
||||
ptr = src
|
||||
case *crypto.PublicKey:
|
||||
tmp, ok := (*src).(ed25519.PublicKey)
|
||||
if !ok {
|
||||
return errors.New(`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`)
|
||||
}
|
||||
ptr = &tmp
|
||||
default:
|
||||
return errors.Errorf(`expected ed25519.PublicKey or *ed25519.PublicKey, got %T`, src)
|
||||
}
|
||||
return blackmagic.AssignIfCompatible(dst, ptr)
|
||||
}
|
||||
61
server/vendor/github.com/lestrrat-go/jwx/internal/pool/pool.go
generated
vendored
Normal file
61
server/vendor/github.com/lestrrat-go/jwx/internal/pool/pool.go
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
package pool
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/big"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var bytesBufferPool = sync.Pool{
|
||||
New: allocBytesBuffer,
|
||||
}
|
||||
|
||||
func allocBytesBuffer() interface{} {
|
||||
return &bytes.Buffer{}
|
||||
}
|
||||
|
||||
func GetBytesBuffer() *bytes.Buffer {
|
||||
//nolint:forcetypeassert
|
||||
return bytesBufferPool.Get().(*bytes.Buffer)
|
||||
}
|
||||
|
||||
func ReleaseBytesBuffer(b *bytes.Buffer) {
|
||||
b.Reset()
|
||||
bytesBufferPool.Put(b)
|
||||
}
|
||||
|
||||
var bigIntPool = sync.Pool{
|
||||
New: allocBigInt,
|
||||
}
|
||||
|
||||
func allocBigInt() interface{} {
|
||||
return &big.Int{}
|
||||
}
|
||||
|
||||
func GetBigInt() *big.Int {
|
||||
//nolint:forcetypeassert
|
||||
return bigIntPool.Get().(*big.Int)
|
||||
}
|
||||
|
||||
func ReleaseBigInt(i *big.Int) {
|
||||
bigIntPool.Put(i.SetInt64(0))
|
||||
}
|
||||
|
||||
var keyToErrorMapPool = sync.Pool{
|
||||
New: allocKeyToErrorMap,
|
||||
}
|
||||
|
||||
func allocKeyToErrorMap() interface{} {
|
||||
return make(map[string]error)
|
||||
}
|
||||
|
||||
func GetKeyToErrorMap() map[string]error {
|
||||
//nolint:forcetypeassert
|
||||
return keyToErrorMapPool.Get().(map[string]error)
|
||||
}
|
||||
|
||||
func ReleaseKeyToErrorMap(m map[string]error) {
|
||||
for key := range m {
|
||||
delete(m, key)
|
||||
}
|
||||
}
|
||||
3
server/vendor/github.com/lestrrat-go/jwx/jwa/README.md
generated
vendored
Normal file
3
server/vendor/github.com/lestrrat-go/jwx/jwa/README.md
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# JWA [](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)
|
||||
73
server/vendor/github.com/lestrrat-go/jwx/jwa/compression_gen.go
generated
vendored
Normal file
73
server/vendor/github.com/lestrrat-go/jwx/jwa/compression_gen.go
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
// this file was auto-generated by internal/cmd/gentypes/main.go: DO NOT EDIT
|
||||
|
||||
package jwa
|
||||
|
||||
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
|
||||
type CompressionAlgorithm string
|
||||
|
||||
// Supported values for CompressionAlgorithm
|
||||
const (
|
||||
Deflate CompressionAlgorithm = "DEF" // DEFLATE (RFC 1951)
|
||||
NoCompress CompressionAlgorithm = "" // No compression
|
||||
)
|
||||
|
||||
var allCompressionAlgorithms = map[CompressionAlgorithm]struct{}{
|
||||
Deflate: {},
|
||||
NoCompress: {},
|
||||
}
|
||||
|
||||
var listCompressionAlgorithmOnce sync.Once
|
||||
var listCompressionAlgorithm []CompressionAlgorithm
|
||||
|
||||
// CompressionAlgorithms returns a list of all available values for CompressionAlgorithm
|
||||
func CompressionAlgorithms() []CompressionAlgorithm {
|
||||
listCompressionAlgorithmOnce.Do(func() {
|
||||
listCompressionAlgorithm = make([]CompressionAlgorithm, 0, len(allCompressionAlgorithms))
|
||||
for v := range allCompressionAlgorithms {
|
||||
listCompressionAlgorithm = append(listCompressionAlgorithm, v)
|
||||
}
|
||||
sort.Slice(listCompressionAlgorithm, func(i, j int) bool {
|
||||
return string(listCompressionAlgorithm[i]) < string(listCompressionAlgorithm[j])
|
||||
})
|
||||
})
|
||||
return listCompressionAlgorithm
|
||||
}
|
||||
|
||||
// Accept is used when conversion from values given by
|
||||
// outside sources (such as JSON payloads) is required
|
||||
func (v *CompressionAlgorithm) Accept(value interface{}) error {
|
||||
var tmp CompressionAlgorithm
|
||||
if x, ok := value.(CompressionAlgorithm); ok {
|
||||
tmp = x
|
||||
} else {
|
||||
var s string
|
||||
switch x := value.(type) {
|
||||
case fmt.Stringer:
|
||||
s = x.String()
|
||||
case string:
|
||||
s = x
|
||||
default:
|
||||
return errors.Errorf(`invalid type for jwa.CompressionAlgorithm: %T`, value)
|
||||
}
|
||||
tmp = CompressionAlgorithm(s)
|
||||
}
|
||||
if _, ok := allCompressionAlgorithms[tmp]; !ok {
|
||||
return errors.Errorf(`invalid jwa.CompressionAlgorithm value`)
|
||||
}
|
||||
|
||||
*v = tmp
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the string representation of a CompressionAlgorithm
|
||||
func (v CompressionAlgorithm) String() string {
|
||||
return string(v)
|
||||
}
|
||||
81
server/vendor/github.com/lestrrat-go/jwx/jwa/content_encryption_gen.go
generated
vendored
Normal file
81
server/vendor/github.com/lestrrat-go/jwx/jwa/content_encryption_gen.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
// this file was auto-generated by internal/cmd/gentypes/main.go: DO NOT EDIT
|
||||
|
||||
package jwa
|
||||
|
||||
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
|
||||
type ContentEncryptionAlgorithm string
|
||||
|
||||
// Supported values for ContentEncryptionAlgorithm
|
||||
const (
|
||||
A128CBC_HS256 ContentEncryptionAlgorithm = "A128CBC-HS256" // AES-CBC + HMAC-SHA256 (128)
|
||||
A128GCM ContentEncryptionAlgorithm = "A128GCM" // AES-GCM (128)
|
||||
A192CBC_HS384 ContentEncryptionAlgorithm = "A192CBC-HS384" // AES-CBC + HMAC-SHA384 (192)
|
||||
A192GCM ContentEncryptionAlgorithm = "A192GCM" // AES-GCM (192)
|
||||
A256CBC_HS512 ContentEncryptionAlgorithm = "A256CBC-HS512" // AES-CBC + HMAC-SHA512 (256)
|
||||
A256GCM ContentEncryptionAlgorithm = "A256GCM" // AES-GCM (256)
|
||||
)
|
||||
|
||||
var allContentEncryptionAlgorithms = map[ContentEncryptionAlgorithm]struct{}{
|
||||
A128CBC_HS256: {},
|
||||
A128GCM: {},
|
||||
A192CBC_HS384: {},
|
||||
A192GCM: {},
|
||||
A256CBC_HS512: {},
|
||||
A256GCM: {},
|
||||
}
|
||||
|
||||
var listContentEncryptionAlgorithmOnce sync.Once
|
||||
var listContentEncryptionAlgorithm []ContentEncryptionAlgorithm
|
||||
|
||||
// ContentEncryptionAlgorithms returns a list of all available values for ContentEncryptionAlgorithm
|
||||
func ContentEncryptionAlgorithms() []ContentEncryptionAlgorithm {
|
||||
listContentEncryptionAlgorithmOnce.Do(func() {
|
||||
listContentEncryptionAlgorithm = make([]ContentEncryptionAlgorithm, 0, len(allContentEncryptionAlgorithms))
|
||||
for v := range allContentEncryptionAlgorithms {
|
||||
listContentEncryptionAlgorithm = append(listContentEncryptionAlgorithm, v)
|
||||
}
|
||||
sort.Slice(listContentEncryptionAlgorithm, func(i, j int) bool {
|
||||
return string(listContentEncryptionAlgorithm[i]) < string(listContentEncryptionAlgorithm[j])
|
||||
})
|
||||
})
|
||||
return listContentEncryptionAlgorithm
|
||||
}
|
||||
|
||||
// Accept is used when conversion from values given by
|
||||
// outside sources (such as JSON payloads) is required
|
||||
func (v *ContentEncryptionAlgorithm) Accept(value interface{}) error {
|
||||
var tmp ContentEncryptionAlgorithm
|
||||
if x, ok := value.(ContentEncryptionAlgorithm); ok {
|
||||
tmp = x
|
||||
} else {
|
||||
var s string
|
||||
switch x := value.(type) {
|
||||
case fmt.Stringer:
|
||||
s = x.String()
|
||||
case string:
|
||||
s = x
|
||||
default:
|
||||
return errors.Errorf(`invalid type for jwa.ContentEncryptionAlgorithm: %T`, value)
|
||||
}
|
||||
tmp = ContentEncryptionAlgorithm(s)
|
||||
}
|
||||
if _, ok := allContentEncryptionAlgorithms[tmp]; !ok {
|
||||
return errors.Errorf(`invalid jwa.ContentEncryptionAlgorithm value`)
|
||||
}
|
||||
|
||||
*v = tmp
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the string representation of a ContentEncryptionAlgorithm
|
||||
func (v ContentEncryptionAlgorithm) String() string {
|
||||
return string(v)
|
||||
}
|
||||
84
server/vendor/github.com/lestrrat-go/jwx/jwa/elliptic_gen.go
generated
vendored
Normal file
84
server/vendor/github.com/lestrrat-go/jwx/jwa/elliptic_gen.go
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
// this file was auto-generated by internal/cmd/gentypes/main.go: DO NOT EDIT
|
||||
|
||||
package jwa
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// EllipticCurveAlgorithm represents the algorithms used for EC keys
|
||||
type EllipticCurveAlgorithm string
|
||||
|
||||
// Supported values for EllipticCurveAlgorithm
|
||||
const (
|
||||
Ed25519 EllipticCurveAlgorithm = "Ed25519"
|
||||
Ed448 EllipticCurveAlgorithm = "Ed448"
|
||||
InvalidEllipticCurve EllipticCurveAlgorithm = "P-invalid"
|
||||
P256 EllipticCurveAlgorithm = "P-256"
|
||||
P384 EllipticCurveAlgorithm = "P-384"
|
||||
P521 EllipticCurveAlgorithm = "P-521"
|
||||
X25519 EllipticCurveAlgorithm = "X25519"
|
||||
X448 EllipticCurveAlgorithm = "X448"
|
||||
)
|
||||
|
||||
var allEllipticCurveAlgorithms = map[EllipticCurveAlgorithm]struct{}{
|
||||
Ed25519: {},
|
||||
Ed448: {},
|
||||
P256: {},
|
||||
P384: {},
|
||||
P521: {},
|
||||
X25519: {},
|
||||
X448: {},
|
||||
}
|
||||
|
||||
var listEllipticCurveAlgorithmOnce sync.Once
|
||||
var listEllipticCurveAlgorithm []EllipticCurveAlgorithm
|
||||
|
||||
// EllipticCurveAlgorithms returns a list of all available values for EllipticCurveAlgorithm
|
||||
func EllipticCurveAlgorithms() []EllipticCurveAlgorithm {
|
||||
listEllipticCurveAlgorithmOnce.Do(func() {
|
||||
listEllipticCurveAlgorithm = make([]EllipticCurveAlgorithm, 0, len(allEllipticCurveAlgorithms))
|
||||
for v := range allEllipticCurveAlgorithms {
|
||||
listEllipticCurveAlgorithm = append(listEllipticCurveAlgorithm, v)
|
||||
}
|
||||
sort.Slice(listEllipticCurveAlgorithm, func(i, j int) bool {
|
||||
return string(listEllipticCurveAlgorithm[i]) < string(listEllipticCurveAlgorithm[j])
|
||||
})
|
||||
})
|
||||
return listEllipticCurveAlgorithm
|
||||
}
|
||||
|
||||
// Accept is used when conversion from values given by
|
||||
// outside sources (such as JSON payloads) is required
|
||||
func (v *EllipticCurveAlgorithm) Accept(value interface{}) error {
|
||||
var tmp EllipticCurveAlgorithm
|
||||
if x, ok := value.(EllipticCurveAlgorithm); ok {
|
||||
tmp = x
|
||||
} else {
|
||||
var s string
|
||||
switch x := value.(type) {
|
||||
case fmt.Stringer:
|
||||
s = x.String()
|
||||
case string:
|
||||
s = x
|
||||
default:
|
||||
return errors.Errorf(`invalid type for jwa.EllipticCurveAlgorithm: %T`, value)
|
||||
}
|
||||
tmp = EllipticCurveAlgorithm(s)
|
||||
}
|
||||
if _, ok := allEllipticCurveAlgorithms[tmp]; !ok {
|
||||
return errors.Errorf(`invalid jwa.EllipticCurveAlgorithm value`)
|
||||
}
|
||||
|
||||
*v = tmp
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the string representation of a EllipticCurveAlgorithm
|
||||
func (v EllipticCurveAlgorithm) String() string {
|
||||
return string(v)
|
||||
}
|
||||
14
server/vendor/github.com/lestrrat-go/jwx/jwa/gen.sh
generated
vendored
Normal file
14
server/vendor/github.com/lestrrat-go/jwx/jwa/gen.sh
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
#!/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
|
||||
4
server/vendor/github.com/lestrrat-go/jwx/jwa/jwa.go
generated
vendored
Normal file
4
server/vendor/github.com/lestrrat-go/jwx/jwa/jwa.go
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
//go:generate ./gen.sh
|
||||
|
||||
// Package jwa defines the various algorithm described in https://tools.ietf.org/html/rfc7518
|
||||
package jwa
|
||||
112
server/vendor/github.com/lestrrat-go/jwx/jwa/key_encryption_gen.go
generated
vendored
Normal file
112
server/vendor/github.com/lestrrat-go/jwx/jwa/key_encryption_gen.go
generated
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
// this file was auto-generated by internal/cmd/gentypes/main.go: DO NOT EDIT
|
||||
|
||||
package jwa
|
||||
|
||||
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
|
||||
type KeyEncryptionAlgorithm string
|
||||
|
||||
// Supported values for KeyEncryptionAlgorithm
|
||||
const (
|
||||
A128GCMKW KeyEncryptionAlgorithm = "A128GCMKW" // AES-GCM key wrap (128)
|
||||
A128KW KeyEncryptionAlgorithm = "A128KW" // AES key wrap (128)
|
||||
A192GCMKW KeyEncryptionAlgorithm = "A192GCMKW" // AES-GCM key wrap (192)
|
||||
A192KW KeyEncryptionAlgorithm = "A192KW" // AES key wrap (192)
|
||||
A256GCMKW KeyEncryptionAlgorithm = "A256GCMKW" // AES-GCM key wrap (256)
|
||||
A256KW KeyEncryptionAlgorithm = "A256KW" // AES key wrap (256)
|
||||
DIRECT KeyEncryptionAlgorithm = "dir" // Direct encryption
|
||||
ECDH_ES KeyEncryptionAlgorithm = "ECDH-ES" // ECDH-ES
|
||||
ECDH_ES_A128KW KeyEncryptionAlgorithm = "ECDH-ES+A128KW" // ECDH-ES + AES key wrap (128)
|
||||
ECDH_ES_A192KW KeyEncryptionAlgorithm = "ECDH-ES+A192KW" // ECDH-ES + AES key wrap (192)
|
||||
ECDH_ES_A256KW KeyEncryptionAlgorithm = "ECDH-ES+A256KW" // ECDH-ES + AES key wrap (256)
|
||||
PBES2_HS256_A128KW KeyEncryptionAlgorithm = "PBES2-HS256+A128KW" // PBES2 + HMAC-SHA256 + AES key wrap (128)
|
||||
PBES2_HS384_A192KW KeyEncryptionAlgorithm = "PBES2-HS384+A192KW" // PBES2 + HMAC-SHA384 + AES key wrap (192)
|
||||
PBES2_HS512_A256KW KeyEncryptionAlgorithm = "PBES2-HS512+A256KW" // PBES2 + HMAC-SHA512 + AES key wrap (256)
|
||||
RSA1_5 KeyEncryptionAlgorithm = "RSA1_5" // RSA-PKCS1v1.5
|
||||
RSA_OAEP KeyEncryptionAlgorithm = "RSA-OAEP" // RSA-OAEP-SHA1
|
||||
RSA_OAEP_256 KeyEncryptionAlgorithm = "RSA-OAEP-256" // RSA-OAEP-SHA256
|
||||
)
|
||||
|
||||
var allKeyEncryptionAlgorithms = map[KeyEncryptionAlgorithm]struct{}{
|
||||
A128GCMKW: {},
|
||||
A128KW: {},
|
||||
A192GCMKW: {},
|
||||
A192KW: {},
|
||||
A256GCMKW: {},
|
||||
A256KW: {},
|
||||
DIRECT: {},
|
||||
ECDH_ES: {},
|
||||
ECDH_ES_A128KW: {},
|
||||
ECDH_ES_A192KW: {},
|
||||
ECDH_ES_A256KW: {},
|
||||
PBES2_HS256_A128KW: {},
|
||||
PBES2_HS384_A192KW: {},
|
||||
PBES2_HS512_A256KW: {},
|
||||
RSA1_5: {},
|
||||
RSA_OAEP: {},
|
||||
RSA_OAEP_256: {},
|
||||
}
|
||||
|
||||
var listKeyEncryptionAlgorithmOnce sync.Once
|
||||
var listKeyEncryptionAlgorithm []KeyEncryptionAlgorithm
|
||||
|
||||
// KeyEncryptionAlgorithms returns a list of all available values for KeyEncryptionAlgorithm
|
||||
func KeyEncryptionAlgorithms() []KeyEncryptionAlgorithm {
|
||||
listKeyEncryptionAlgorithmOnce.Do(func() {
|
||||
listKeyEncryptionAlgorithm = make([]KeyEncryptionAlgorithm, 0, len(allKeyEncryptionAlgorithms))
|
||||
for v := range allKeyEncryptionAlgorithms {
|
||||
listKeyEncryptionAlgorithm = append(listKeyEncryptionAlgorithm, v)
|
||||
}
|
||||
sort.Slice(listKeyEncryptionAlgorithm, func(i, j int) bool {
|
||||
return string(listKeyEncryptionAlgorithm[i]) < string(listKeyEncryptionAlgorithm[j])
|
||||
})
|
||||
})
|
||||
return listKeyEncryptionAlgorithm
|
||||
}
|
||||
|
||||
// Accept is used when conversion from values given by
|
||||
// outside sources (such as JSON payloads) is required
|
||||
func (v *KeyEncryptionAlgorithm) Accept(value interface{}) error {
|
||||
var tmp KeyEncryptionAlgorithm
|
||||
if x, ok := value.(KeyEncryptionAlgorithm); ok {
|
||||
tmp = x
|
||||
} else {
|
||||
var s string
|
||||
switch x := value.(type) {
|
||||
case fmt.Stringer:
|
||||
s = x.String()
|
||||
case string:
|
||||
s = x
|
||||
default:
|
||||
return errors.Errorf(`invalid type for jwa.KeyEncryptionAlgorithm: %T`, value)
|
||||
}
|
||||
tmp = KeyEncryptionAlgorithm(s)
|
||||
}
|
||||
if _, ok := allKeyEncryptionAlgorithms[tmp]; !ok {
|
||||
return errors.Errorf(`invalid jwa.KeyEncryptionAlgorithm value`)
|
||||
}
|
||||
|
||||
*v = tmp
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the string representation of a KeyEncryptionAlgorithm
|
||||
func (v KeyEncryptionAlgorithm) String() string {
|
||||
return string(v)
|
||||
}
|
||||
|
||||
// IsSymmetric returns true if the algorithm is a symmetric type
|
||||
func (v KeyEncryptionAlgorithm) IsSymmetric() bool {
|
||||
switch v {
|
||||
case A128GCMKW, A128KW, A192GCMKW, A192KW, A256GCMKW, A256KW, DIRECT, PBES2_HS256_A128KW, PBES2_HS384_A192KW, PBES2_HS512_A256KW:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
78
server/vendor/github.com/lestrrat-go/jwx/jwa/key_type_gen.go
generated
vendored
Normal file
78
server/vendor/github.com/lestrrat-go/jwx/jwa/key_type_gen.go
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
// this file was auto-generated by internal/cmd/gentypes/main.go: DO NOT EDIT
|
||||
|
||||
package jwa
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// KeyType represents the key type ("kty") that are supported
|
||||
type KeyType string
|
||||
|
||||
// Supported values for KeyType
|
||||
const (
|
||||
EC KeyType = "EC" // Elliptic Curve
|
||||
InvalidKeyType KeyType = "" // Invalid KeyType
|
||||
OKP KeyType = "OKP" // Octet string key pairs
|
||||
OctetSeq KeyType = "oct" // Octet sequence (used to represent symmetric keys)
|
||||
RSA KeyType = "RSA" // RSA
|
||||
)
|
||||
|
||||
var allKeyTypes = map[KeyType]struct{}{
|
||||
EC: {},
|
||||
OKP: {},
|
||||
OctetSeq: {},
|
||||
RSA: {},
|
||||
}
|
||||
|
||||
var listKeyTypeOnce sync.Once
|
||||
var listKeyType []KeyType
|
||||
|
||||
// KeyTypes returns a list of all available values for KeyType
|
||||
func KeyTypes() []KeyType {
|
||||
listKeyTypeOnce.Do(func() {
|
||||
listKeyType = make([]KeyType, 0, len(allKeyTypes))
|
||||
for v := range allKeyTypes {
|
||||
listKeyType = append(listKeyType, v)
|
||||
}
|
||||
sort.Slice(listKeyType, func(i, j int) bool {
|
||||
return string(listKeyType[i]) < string(listKeyType[j])
|
||||
})
|
||||
})
|
||||
return listKeyType
|
||||
}
|
||||
|
||||
// Accept is used when conversion from values given by
|
||||
// outside sources (such as JSON payloads) is required
|
||||
func (v *KeyType) Accept(value interface{}) error {
|
||||
var tmp KeyType
|
||||
if x, ok := value.(KeyType); ok {
|
||||
tmp = x
|
||||
} else {
|
||||
var s string
|
||||
switch x := value.(type) {
|
||||
case fmt.Stringer:
|
||||
s = x.String()
|
||||
case string:
|
||||
s = x
|
||||
default:
|
||||
return errors.Errorf(`invalid type for jwa.KeyType: %T`, value)
|
||||
}
|
||||
tmp = KeyType(s)
|
||||
}
|
||||
if _, ok := allKeyTypes[tmp]; !ok {
|
||||
return errors.Errorf(`invalid jwa.KeyType value`)
|
||||
}
|
||||
|
||||
*v = tmp
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the string representation of a KeyType
|
||||
func (v KeyType) String() string {
|
||||
return string(v)
|
||||
}
|
||||
10
server/vendor/github.com/lestrrat-go/jwx/jwa/secp2561k.go
generated
vendored
Normal file
10
server/vendor/github.com/lestrrat-go/jwx/jwa/secp2561k.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// +build jwx_es256k
|
||||
|
||||
package jwa
|
||||
|
||||
// This constant is only available if compiled with jwx_es256k build tag
|
||||
const Secp256k1 EllipticCurveAlgorithm = "secp256k1"
|
||||
|
||||
func init() {
|
||||
allEllipticCurveAlgorithms[Secp256k1] = struct{}{}
|
||||
}
|
||||
99
server/vendor/github.com/lestrrat-go/jwx/jwa/signature_gen.go
generated
vendored
Normal file
99
server/vendor/github.com/lestrrat-go/jwx/jwa/signature_gen.go
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
// this file was auto-generated by internal/cmd/gentypes/main.go: DO NOT EDIT
|
||||
|
||||
package jwa
|
||||
|
||||
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
|
||||
type SignatureAlgorithm string
|
||||
|
||||
// Supported values for SignatureAlgorithm
|
||||
const (
|
||||
ES256 SignatureAlgorithm = "ES256" // ECDSA using P-256 and SHA-256
|
||||
ES256K SignatureAlgorithm = "ES256K" // ECDSA using secp256k1 and SHA-256
|
||||
ES384 SignatureAlgorithm = "ES384" // ECDSA using P-384 and SHA-384
|
||||
ES512 SignatureAlgorithm = "ES512" // ECDSA using P-521 and SHA-512
|
||||
EdDSA SignatureAlgorithm = "EdDSA" // EdDSA signature algorithms
|
||||
HS256 SignatureAlgorithm = "HS256" // HMAC using SHA-256
|
||||
HS384 SignatureAlgorithm = "HS384" // HMAC using SHA-384
|
||||
HS512 SignatureAlgorithm = "HS512" // HMAC using SHA-512
|
||||
NoSignature SignatureAlgorithm = "none"
|
||||
PS256 SignatureAlgorithm = "PS256" // RSASSA-PSS using SHA256 and MGF1-SHA256
|
||||
PS384 SignatureAlgorithm = "PS384" // RSASSA-PSS using SHA384 and MGF1-SHA384
|
||||
PS512 SignatureAlgorithm = "PS512" // RSASSA-PSS using SHA512 and MGF1-SHA512
|
||||
RS256 SignatureAlgorithm = "RS256" // RSASSA-PKCS-v1.5 using SHA-256
|
||||
RS384 SignatureAlgorithm = "RS384" // RSASSA-PKCS-v1.5 using SHA-384
|
||||
RS512 SignatureAlgorithm = "RS512" // RSASSA-PKCS-v1.5 using SHA-512
|
||||
)
|
||||
|
||||
var allSignatureAlgorithms = map[SignatureAlgorithm]struct{}{
|
||||
ES256: {},
|
||||
ES256K: {},
|
||||
ES384: {},
|
||||
ES512: {},
|
||||
EdDSA: {},
|
||||
HS256: {},
|
||||
HS384: {},
|
||||
HS512: {},
|
||||
NoSignature: {},
|
||||
PS256: {},
|
||||
PS384: {},
|
||||
PS512: {},
|
||||
RS256: {},
|
||||
RS384: {},
|
||||
RS512: {},
|
||||
}
|
||||
|
||||
var listSignatureAlgorithmOnce sync.Once
|
||||
var listSignatureAlgorithm []SignatureAlgorithm
|
||||
|
||||
// SignatureAlgorithms returns a list of all available values for SignatureAlgorithm
|
||||
func SignatureAlgorithms() []SignatureAlgorithm {
|
||||
listSignatureAlgorithmOnce.Do(func() {
|
||||
listSignatureAlgorithm = make([]SignatureAlgorithm, 0, len(allSignatureAlgorithms))
|
||||
for v := range allSignatureAlgorithms {
|
||||
listSignatureAlgorithm = append(listSignatureAlgorithm, v)
|
||||
}
|
||||
sort.Slice(listSignatureAlgorithm, func(i, j int) bool {
|
||||
return string(listSignatureAlgorithm[i]) < string(listSignatureAlgorithm[j])
|
||||
})
|
||||
})
|
||||
return listSignatureAlgorithm
|
||||
}
|
||||
|
||||
// Accept is used when conversion from values given by
|
||||
// outside sources (such as JSON payloads) is required
|
||||
func (v *SignatureAlgorithm) Accept(value interface{}) error {
|
||||
var tmp SignatureAlgorithm
|
||||
if x, ok := value.(SignatureAlgorithm); ok {
|
||||
tmp = x
|
||||
} else {
|
||||
var s string
|
||||
switch x := value.(type) {
|
||||
case fmt.Stringer:
|
||||
s = x.String()
|
||||
case string:
|
||||
s = x
|
||||
default:
|
||||
return errors.Errorf(`invalid type for jwa.SignatureAlgorithm: %T`, value)
|
||||
}
|
||||
tmp = SignatureAlgorithm(s)
|
||||
}
|
||||
if _, ok := allSignatureAlgorithms[tmp]; !ok {
|
||||
return errors.Errorf(`invalid jwa.SignatureAlgorithm value`)
|
||||
}
|
||||
|
||||
*v = tmp
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the string representation of a SignatureAlgorithm
|
||||
func (v SignatureAlgorithm) String() string {
|
||||
return string(v)
|
||||
}
|
||||
94
server/vendor/github.com/lestrrat-go/jwx/jwe/README.md
generated
vendored
Normal file
94
server/vendor/github.com/lestrrat-go/jwx/jwe/README.md
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
# JWE [](https://pkg.go.dev/github.com/lestrrat-go/jwx/jwe)
|
||||
|
||||
Package jwe implements JWE as described in [RFC7516](https://tools.ietf.org/html/rfc7516)
|
||||
|
||||
* Encrypt and Decrypt arbitrary data
|
||||
* Content compression and decompression
|
||||
* Add arbitrary fields in the JWE header object
|
||||
|
||||
How-to style documentation can be found in the [docs directory](../docs).
|
||||
|
||||
Examples are located in the examples directory ([jwe_example_test.go](../examples/jwe_example_test.go))
|
||||
|
||||
Supported key encryption algorithm:
|
||||
|
||||
| Algorithm | Supported? | Constant in [jwa](../jwa) |
|
||||
|:-----------------------------------------|:-----------|:-------------------------|
|
||||
| RSA-PKCS1v1.5 | YES | jwa.RSA1_5 |
|
||||
| RSA-OAEP-SHA1 | YES | jwa.RSA_OAEP |
|
||||
| RSA-OAEP-SHA256 | YES | jwa.RSA_OAEP_256 |
|
||||
| AES key wrap (128) | YES | jwa.A128KW |
|
||||
| AES key wrap (192) | YES | jwa.A192KW |
|
||||
| AES key wrap (256) | YES | jwa.A256KW |
|
||||
| Direct encryption | YES (1) | jwa.DIRECT |
|
||||
| ECDH-ES | YES (1) | jwa.ECDH_ES |
|
||||
| ECDH-ES + AES key wrap (128) | YES | jwa.ECDH_ES_A128KW |
|
||||
| ECDH-ES + AES key wrap (192) | YES | jwa.ECDH_ES_A192KW |
|
||||
| ECDH-ES + AES key wrap (256) | YES | jwa.ECDH_ES_A256KW |
|
||||
| AES-GCM key wrap (128) | YES | jwa.A128GCMKW |
|
||||
| AES-GCM key wrap (192) | YES | jwa.A192GCMKW |
|
||||
| AES-GCM key wrap (256) | YES | jwa.A256GCMKW |
|
||||
| PBES2 + HMAC-SHA256 + AES key wrap (128) | YES | jwa.PBES2_HS256_A128KW |
|
||||
| PBES2 + HMAC-SHA384 + AES key wrap (192) | YES | jwa.PBES2_HS384_A192KW |
|
||||
| PBES2 + HMAC-SHA512 + AES key wrap (256) | YES | jwa.PBES2_HS512_A256KW |
|
||||
|
||||
* Note 1: Single-recipient only
|
||||
|
||||
Supported content encryption algorithm:
|
||||
|
||||
| Algorithm | Supported? | Constant in [jwa](../jwa) |
|
||||
|:----------------------------|:-----------|:--------------------------|
|
||||
| AES-CBC + HMAC-SHA256 (128) | YES | jwa.A128CBC_HS256 |
|
||||
| AES-CBC + HMAC-SHA384 (192) | YES | jwa.A192CBC_HS384 |
|
||||
| AES-CBC + HMAC-SHA512 (256) | YES | jwa.A256CBC_HS512 |
|
||||
| AES-GCM (128) | YES | jwa.A128GCM |
|
||||
| AES-GCM (192) | YES | jwa.A192GCM |
|
||||
| AES-GCM (256) | YES | jwa.A256GCM |
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
## Encrypt data
|
||||
|
||||
```go
|
||||
func ExampleEncrypt() {
|
||||
privkey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
log.Printf("failed to generate private key: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
payload := []byte("Lorem Ipsum")
|
||||
|
||||
encrypted, err := jwe.Encrypt(payload, jwa.RSA1_5, &privkey.PublicKey, jwa.A128CBC_HS256, jwa.NoCompress)
|
||||
if err != nil {
|
||||
log.Printf("failed to encrypt payload: %s", err)
|
||||
return
|
||||
}
|
||||
_ = encrypted
|
||||
// OUTPUT:
|
||||
}
|
||||
```
|
||||
|
||||
## Decrypt data
|
||||
|
||||
```go
|
||||
func ExampleDecrypt() {
|
||||
privkey, encrypted, err := exampleGenPayload()
|
||||
if err != nil {
|
||||
log.Printf("failed to generate encrypted payload: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
decrypted, err := jwe.Decrypt(encrypted, jwa.RSA1_5, privkey)
|
||||
if err != nil {
|
||||
log.Printf("failed to decrypt: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
if string(decrypted) != "Lorem Ipsum" {
|
||||
log.Printf("WHAT?!")
|
||||
return
|
||||
}
|
||||
// OUTPUT:
|
||||
}
|
||||
```
|
||||
68
server/vendor/github.com/lestrrat-go/jwx/jwe/compress.go
generated
vendored
Normal file
68
server/vendor/github.com/lestrrat-go/jwx/jwe/compress.go
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
package jwe
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/flate"
|
||||
"io"
|
||||
|
||||
"github.com/lestrrat-go/jwx/internal/pool"
|
||||
"github.com/lestrrat-go/jwx/jwa"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func uncompress(src []byte, maxBufferSize int64) ([]byte, error) {
|
||||
var dst bytes.Buffer
|
||||
r := flate.NewReader(bytes.NewReader(src))
|
||||
defer r.Close()
|
||||
var buf [16384]byte
|
||||
var sofar int64
|
||||
for {
|
||||
n, readErr := r.Read(buf[:])
|
||||
sofar += int64(n)
|
||||
if sofar > maxBufferSize {
|
||||
return nil, errors.New(`compressed payload exceeds maximum allowed size`)
|
||||
}
|
||||
if readErr != nil {
|
||||
// if we have a read error, and it's not EOF, then we need to stop
|
||||
if readErr != io.EOF {
|
||||
return nil, errors.Wrap(readErr, `failed to read inflated data`)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := dst.Write(buf[:n]); err != nil {
|
||||
return nil, errors.Wrap(err, `failed to write inflated data`)
|
||||
}
|
||||
|
||||
if readErr != nil {
|
||||
// if it got here, then readErr == io.EOF, we're done
|
||||
//nolint:nilerr
|
||||
return dst.Bytes(), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
301
server/vendor/github.com/lestrrat-go/jwx/jwe/decrypt.go
generated
vendored
Normal file
301
server/vendor/github.com/lestrrat-go/jwx/jwe/decrypt.go
generated
vendored
Normal file
@@ -0,0 +1,301 @@
|
||||
package jwe
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
cryptocipher "crypto/cipher"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"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"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
aad []byte
|
||||
apu []byte
|
||||
apv []byte
|
||||
computedAad []byte
|
||||
iv []byte
|
||||
keyiv []byte
|
||||
keysalt []byte
|
||||
keytag []byte
|
||||
tag []byte
|
||||
privkey interface{}
|
||||
pubkey interface{}
|
||||
ctalg jwa.ContentEncryptionAlgorithm
|
||||
keyalg jwa.KeyEncryptionAlgorithm
|
||||
cipher content_crypt.Cipher
|
||||
keycount int
|
||||
}
|
||||
|
||||
// NewDecrypter Creates a new Decrypter instance. You must supply the
|
||||
// rest of parameters via their respective setter methods before
|
||||
// calling Decrypt().
|
||||
//
|
||||
// privkey must be a private key in its "raw" format (i.e. something like
|
||||
// *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{
|
||||
ctalg: ctalg,
|
||||
keyalg: keyalg,
|
||||
privkey: privkey,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Decrypter) AgreementPartyUInfo(apu []byte) *Decrypter {
|
||||
d.apu = apu
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *Decrypter) AgreementPartyVInfo(apv []byte) *Decrypter {
|
||||
d.apv = apv
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *Decrypter) AuthenticatedData(aad []byte) *Decrypter {
|
||||
d.aad = aad
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *Decrypter) ComputedAuthenticatedData(aad []byte) *Decrypter {
|
||||
d.computedAad = aad
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *Decrypter) ContentEncryptionAlgorithm(ctalg jwa.ContentEncryptionAlgorithm) *Decrypter {
|
||||
d.ctalg = ctalg
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *Decrypter) InitializationVector(iv []byte) *Decrypter {
|
||||
d.iv = iv
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *Decrypter) KeyCount(keycount int) *Decrypter {
|
||||
d.keycount = keycount
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *Decrypter) KeyInitializationVector(keyiv []byte) *Decrypter {
|
||||
d.keyiv = keyiv
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *Decrypter) KeySalt(keysalt []byte) *Decrypter {
|
||||
d.keysalt = keysalt
|
||||
return d
|
||||
}
|
||||
|
||||
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 {
|
||||
d.pubkey = pubkey
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *Decrypter) Tag(tag []byte) *Decrypter {
|
||||
d.tag = tag
|
||||
return d
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
d.cipher = cipher
|
||||
default:
|
||||
return nil, errors.Errorf(`invalid content cipher algorithm (%s)`, d.ctalg)
|
||||
}
|
||||
}
|
||||
|
||||
return d.cipher, nil
|
||||
}
|
||||
|
||||
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`)
|
||||
return
|
||||
}
|
||||
|
||||
cipher, ciphererr := d.ContentCipher()
|
||||
if ciphererr != nil {
|
||||
err = errors.Wrap(ciphererr, `failed to fetch content crypt cipher`)
|
||||
return
|
||||
}
|
||||
|
||||
computedAad := d.computedAad
|
||||
if d.aad != nil {
|
||||
computedAad = append(append(computedAad, '.'), d.aad...)
|
||||
}
|
||||
|
||||
plaintext, err = cipher.Decrypt(cek, d.iv, ciphertext, d.tag, computedAad)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, `failed to decrypt payload`)
|
||||
return
|
||||
}
|
||||
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
func (d *Decrypter) decryptSymmetricKey(recipientKey, cek []byte) ([]byte, error) {
|
||||
switch d.keyalg {
|
||||
case jwa.DIRECT:
|
||||
return cek, nil
|
||||
case jwa.PBES2_HS256_A128KW, jwa.PBES2_HS384_A192KW, jwa.PBES2_HS512_A256KW:
|
||||
var hashFunc func() hash.Hash
|
||||
var keylen int
|
||||
switch d.keyalg {
|
||||
case jwa.PBES2_HS256_A128KW:
|
||||
hashFunc = sha256.New
|
||||
keylen = 16
|
||||
case jwa.PBES2_HS384_A192KW:
|
||||
hashFunc = sha512.New384
|
||||
keylen = 24
|
||||
case jwa.PBES2_HS512_A256KW:
|
||||
hashFunc = sha512.New
|
||||
keylen = 32
|
||||
}
|
||||
salt := []byte(d.keyalg)
|
||||
salt = append(salt, byte(0))
|
||||
salt = append(salt, d.keysalt...)
|
||||
cek = pbkdf2.Key(cek, salt, d.keycount, keylen, hashFunc)
|
||||
fallthrough
|
||||
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`)
|
||||
}
|
||||
|
||||
jek, err := keyenc.Unwrap(block, recipientKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, `failed to unwrap key`)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
if len(d.keytag) != 16 {
|
||||
return nil, errors.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`)
|
||||
}
|
||||
aesgcm, err := cryptocipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, `failed to create new GCM wrap`)
|
||||
}
|
||||
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 jek, nil
|
||||
default:
|
||||
return nil, errors.Errorf("decrypt key: unsupported algorithm %s", d.keyalg)
|
||||
}
|
||||
}
|
||||
|
||||
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 d.decryptSymmetricKey(recipientKey, cek)
|
||||
}
|
||||
|
||||
k, err := d.BuildKeyDecrypter()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, `failed to build key decrypter`)
|
||||
}
|
||||
|
||||
cek, err = k.Decrypt(recipientKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, `failed to decrypt key`)
|
||||
}
|
||||
|
||||
return cek, nil
|
||||
}
|
||||
|
||||
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`)
|
||||
}
|
||||
|
||||
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 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 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 keyenc.NewAES(alg, sharedkey)
|
||||
case jwa.ECDH_ES, jwa.ECDH_ES_A128KW, jwa.ECDH_ES_A192KW, jwa.ECDH_ES_A256KW:
|
||||
switch d.pubkey.(type) {
|
||||
case x25519.PublicKey:
|
||||
return keyenc.NewECDHESDecrypt(alg, d.ctalg, d.pubkey, d.apu, d.apv, d.privkey), nil
|
||||
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)
|
||||
}
|
||||
|
||||
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 keyenc.NewECDHESDecrypt(alg, d.ctalg, &pubkey, d.apu, d.apv, &privkey), nil
|
||||
}
|
||||
default:
|
||||
return nil, errors.Errorf(`unsupported algorithm for key decryption (%s)`, alg)
|
||||
}
|
||||
}
|
||||
145
server/vendor/github.com/lestrrat-go/jwx/jwe/encrypt.go
generated
vendored
Normal file
145
server/vendor/github.com/lestrrat-go/jwx/jwe/encrypt.go
generated
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
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
|
||||
}
|
||||
14
server/vendor/github.com/lestrrat-go/jwx/jwe/gen.sh
generated
vendored
Normal file
14
server/vendor/github.com/lestrrat-go/jwx/jwe/gen.sh
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
#!/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
|
||||
122
server/vendor/github.com/lestrrat-go/jwx/jwe/headers.go
generated
vendored
Normal file
122
server/vendor/github.com/lestrrat-go/jwx/jwe/headers.go
generated
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
package jwe
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/lestrrat-go/jwx/internal/base64"
|
||||
"github.com/lestrrat-go/jwx/internal/json"
|
||||
|
||||
"github.com/lestrrat-go/iter/mapiter"
|
||||
"github.com/lestrrat-go/jwx/internal/iter"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type isZeroer interface {
|
||||
isZero() bool
|
||||
}
|
||||
|
||||
func (h *stdHeaders) isZero() bool {
|
||||
return h.agreementPartyUInfo == nil &&
|
||||
h.agreementPartyVInfo == nil &&
|
||||
h.algorithm == nil &&
|
||||
h.compression == nil &&
|
||||
h.contentEncryption == nil &&
|
||||
h.contentType == nil &&
|
||||
h.critical == nil &&
|
||||
h.ephemeralPublicKey == 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 &&
|
||||
len(h.privateParams) == 0
|
||||
}
|
||||
|
||||
// Iterate returns a channel that successively returns all the
|
||||
// header name and values.
|
||||
func (h *stdHeaders) Iterate(ctx context.Context) Iterator {
|
||||
pairs := h.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)
|
||||
}
|
||||
|
||||
func (h *stdHeaders) Walk(ctx context.Context, visitor Visitor) error {
|
||||
return iter.WalkMap(ctx, h, visitor)
|
||||
}
|
||||
|
||||
func (h *stdHeaders) AsMap(ctx context.Context) (map[string]interface{}, error) {
|
||||
return iter.AsMap(ctx, h)
|
||||
}
|
||||
|
||||
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 dst, nil
|
||||
}
|
||||
|
||||
func (h *stdHeaders) Copy(_ context.Context, dst Headers) error {
|
||||
for _, pair := range h.makePairs() {
|
||||
//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 nil
|
||||
}
|
||||
|
||||
func (h *stdHeaders) Merge(ctx context.Context, h2 Headers) (Headers, error) {
|
||||
h3 := NewHeaders()
|
||||
|
||||
if h != nil {
|
||||
if err := h.Copy(ctx, h3); err != nil {
|
||||
return nil, errors.Wrap(err, `failed to copy headers from receiver`)
|
||||
}
|
||||
}
|
||||
|
||||
if h2 != nil {
|
||||
if err := h2.Copy(ctx, h3); err != nil {
|
||||
return nil, errors.Wrap(err, `failed to copy headers from argument`)
|
||||
}
|
||||
}
|
||||
|
||||
return h3, nil
|
||||
}
|
||||
|
||||
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 base64.Encode(buf), nil
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(decoded, h); err != nil {
|
||||
return errors.Wrap(err, "failed to unmarshal buffer")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
714
server/vendor/github.com/lestrrat-go/jwx/jwe/headers_gen.go
generated
vendored
Normal file
714
server/vendor/github.com/lestrrat-go/jwx/jwe/headers_gen.go
generated
vendored
Normal file
@@ -0,0 +1,714 @@
|
||||
// This file is auto-generated by jwe/internal/cmd/genheaders/main.go. DO NOT EDIT
|
||||
|
||||
package jwe
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"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"
|
||||
)
|
||||
|
||||
const (
|
||||
AgreementPartyUInfoKey = "apu"
|
||||
AgreementPartyVInfoKey = "apv"
|
||||
AlgorithmKey = "alg"
|
||||
CompressionKey = "zip"
|
||||
ContentEncryptionKey = "enc"
|
||||
ContentTypeKey = "cty"
|
||||
CriticalKey = "crit"
|
||||
EphemeralPublicKeyKey = "epk"
|
||||
JWKKey = "jwk"
|
||||
JWKSetURLKey = "jku"
|
||||
KeyIDKey = "kid"
|
||||
TypeKey = "typ"
|
||||
X509CertChainKey = "x5c"
|
||||
X509CertThumbprintKey = "x5t"
|
||||
X509CertThumbprintS256Key = "x5t#S256"
|
||||
X509URLKey = "x5u"
|
||||
)
|
||||
|
||||
// Headers describe a standard Header set.
|
||||
type Headers interface {
|
||||
json.Marshaler
|
||||
json.Unmarshaler
|
||||
AgreementPartyUInfo() []byte
|
||||
AgreementPartyVInfo() []byte
|
||||
Algorithm() jwa.KeyEncryptionAlgorithm
|
||||
Compression() jwa.CompressionAlgorithm
|
||||
ContentEncryption() jwa.ContentEncryptionAlgorithm
|
||||
ContentType() string
|
||||
Critical() []string
|
||||
EphemeralPublicKey() jwk.Key
|
||||
JWK() jwk.Key
|
||||
JWKSetURL() string
|
||||
KeyID() string
|
||||
Type() string
|
||||
X509CertChain() []string
|
||||
X509CertThumbprint() string
|
||||
X509CertThumbprintS256() string
|
||||
X509URL() string
|
||||
Iterate(ctx context.Context) Iterator
|
||||
Walk(ctx context.Context, v Visitor) error
|
||||
AsMap(ctx context.Context) (map[string]interface{}, error)
|
||||
Get(string) (interface{}, bool)
|
||||
Set(string, interface{}) error
|
||||
Remove(string) error
|
||||
Encode() ([]byte, error)
|
||||
Decode([]byte) error
|
||||
// PrivateParams returns the map containing the non-standard ('private') parameters
|
||||
// in the associated header. WARNING: DO NOT USE PrivateParams()
|
||||
// IF YOU HAVE CONCURRENT CODE ACCESSING THEM. Use AsMap() to
|
||||
// get a copy of the entire header instead
|
||||
PrivateParams() map[string]interface{}
|
||||
Clone(context.Context) (Headers, error)
|
||||
Copy(context.Context, Headers) error
|
||||
Merge(context.Context, Headers) (Headers, error)
|
||||
}
|
||||
|
||||
type stdHeaders struct {
|
||||
agreementPartyUInfo []byte
|
||||
agreementPartyVInfo []byte
|
||||
algorithm *jwa.KeyEncryptionAlgorithm
|
||||
compression *jwa.CompressionAlgorithm
|
||||
contentEncryption *jwa.ContentEncryptionAlgorithm
|
||||
contentType *string
|
||||
critical []string
|
||||
ephemeralPublicKey jwk.Key
|
||||
jwk jwk.Key
|
||||
jwkSetURL *string
|
||||
keyID *string
|
||||
typ *string
|
||||
x509CertChain []string
|
||||
x509CertThumbprint *string
|
||||
x509CertThumbprintS256 *string
|
||||
x509URL *string
|
||||
privateParams map[string]interface{}
|
||||
mu *sync.RWMutex
|
||||
}
|
||||
|
||||
func NewHeaders() Headers {
|
||||
return &stdHeaders{
|
||||
mu: &sync.RWMutex{},
|
||||
privateParams: map[string]interface{}{},
|
||||
}
|
||||
}
|
||||
|
||||
func (h *stdHeaders) AgreementPartyUInfo() []byte {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
return h.agreementPartyUInfo
|
||||
}
|
||||
|
||||
func (h *stdHeaders) AgreementPartyVInfo() []byte {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
return h.agreementPartyVInfo
|
||||
}
|
||||
|
||||
func (h *stdHeaders) Algorithm() jwa.KeyEncryptionAlgorithm {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.algorithm == nil {
|
||||
return ""
|
||||
}
|
||||
return *(h.algorithm)
|
||||
}
|
||||
|
||||
func (h *stdHeaders) Compression() jwa.CompressionAlgorithm {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.compression == nil {
|
||||
return jwa.NoCompress
|
||||
}
|
||||
return *(h.compression)
|
||||
}
|
||||
|
||||
func (h *stdHeaders) ContentEncryption() jwa.ContentEncryptionAlgorithm {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.contentEncryption == nil {
|
||||
return ""
|
||||
}
|
||||
return *(h.contentEncryption)
|
||||
}
|
||||
|
||||
func (h *stdHeaders) ContentType() string {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.contentType == nil {
|
||||
return ""
|
||||
}
|
||||
return *(h.contentType)
|
||||
}
|
||||
|
||||
func (h *stdHeaders) Critical() []string {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
return h.critical
|
||||
}
|
||||
|
||||
func (h *stdHeaders) EphemeralPublicKey() jwk.Key {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
return h.ephemeralPublicKey
|
||||
}
|
||||
|
||||
func (h *stdHeaders) JWK() jwk.Key {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
return h.jwk
|
||||
}
|
||||
|
||||
func (h *stdHeaders) JWKSetURL() string {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.jwkSetURL == nil {
|
||||
return ""
|
||||
}
|
||||
return *(h.jwkSetURL)
|
||||
}
|
||||
|
||||
func (h *stdHeaders) KeyID() string {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.keyID == nil {
|
||||
return ""
|
||||
}
|
||||
return *(h.keyID)
|
||||
}
|
||||
|
||||
func (h *stdHeaders) Type() string {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.typ == nil {
|
||||
return ""
|
||||
}
|
||||
return *(h.typ)
|
||||
}
|
||||
|
||||
func (h *stdHeaders) X509CertChain() []string {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
return h.x509CertChain
|
||||
}
|
||||
|
||||
func (h *stdHeaders) X509CertThumbprint() string {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.x509CertThumbprint == nil {
|
||||
return ""
|
||||
}
|
||||
return *(h.x509CertThumbprint)
|
||||
}
|
||||
|
||||
func (h *stdHeaders) X509CertThumbprintS256() string {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.x509CertThumbprintS256 == nil {
|
||||
return ""
|
||||
}
|
||||
return *(h.x509CertThumbprintS256)
|
||||
}
|
||||
|
||||
func (h *stdHeaders) X509URL() string {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.x509URL == nil {
|
||||
return ""
|
||||
}
|
||||
return *(h.x509URL)
|
||||
}
|
||||
|
||||
func (h *stdHeaders) makePairs() []*HeaderPair {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
var pairs []*HeaderPair
|
||||
if h.agreementPartyUInfo != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: AgreementPartyUInfoKey, Value: h.agreementPartyUInfo})
|
||||
}
|
||||
if h.agreementPartyVInfo != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: AgreementPartyVInfoKey, Value: h.agreementPartyVInfo})
|
||||
}
|
||||
if h.algorithm != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: AlgorithmKey, Value: *(h.algorithm)})
|
||||
}
|
||||
if h.compression != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: CompressionKey, Value: *(h.compression)})
|
||||
}
|
||||
if h.contentEncryption != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: ContentEncryptionKey, Value: *(h.contentEncryption)})
|
||||
}
|
||||
if h.contentType != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: ContentTypeKey, Value: *(h.contentType)})
|
||||
}
|
||||
if h.critical != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: CriticalKey, Value: h.critical})
|
||||
}
|
||||
if h.ephemeralPublicKey != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: EphemeralPublicKeyKey, Value: h.ephemeralPublicKey})
|
||||
}
|
||||
if h.jwk != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: JWKKey, Value: h.jwk})
|
||||
}
|
||||
if h.jwkSetURL != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: JWKSetURLKey, Value: *(h.jwkSetURL)})
|
||||
}
|
||||
if h.keyID != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: KeyIDKey, Value: *(h.keyID)})
|
||||
}
|
||||
if h.typ != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: TypeKey, Value: *(h.typ)})
|
||||
}
|
||||
if h.x509CertChain != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: X509CertChainKey, Value: h.x509CertChain})
|
||||
}
|
||||
if h.x509CertThumbprint != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: X509CertThumbprintKey, Value: *(h.x509CertThumbprint)})
|
||||
}
|
||||
if h.x509CertThumbprintS256 != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: X509CertThumbprintS256Key, Value: *(h.x509CertThumbprintS256)})
|
||||
}
|
||||
if h.x509URL != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: X509URLKey, Value: *(h.x509URL)})
|
||||
}
|
||||
for k, v := range h.privateParams {
|
||||
pairs = append(pairs, &HeaderPair{Key: k, Value: v})
|
||||
}
|
||||
return pairs
|
||||
}
|
||||
|
||||
func (h *stdHeaders) PrivateParams() map[string]interface{} {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
return h.privateParams
|
||||
}
|
||||
|
||||
func (h *stdHeaders) Get(name string) (interface{}, bool) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
switch name {
|
||||
case AgreementPartyUInfoKey:
|
||||
if h.agreementPartyUInfo == nil {
|
||||
return nil, false
|
||||
}
|
||||
return h.agreementPartyUInfo, true
|
||||
case AgreementPartyVInfoKey:
|
||||
if h.agreementPartyVInfo == nil {
|
||||
return nil, false
|
||||
}
|
||||
return h.agreementPartyVInfo, true
|
||||
case AlgorithmKey:
|
||||
if h.algorithm == nil {
|
||||
return nil, false
|
||||
}
|
||||
return *(h.algorithm), true
|
||||
case CompressionKey:
|
||||
if h.compression == nil {
|
||||
return nil, false
|
||||
}
|
||||
return *(h.compression), true
|
||||
case ContentEncryptionKey:
|
||||
if h.contentEncryption == nil {
|
||||
return nil, false
|
||||
}
|
||||
return *(h.contentEncryption), true
|
||||
case ContentTypeKey:
|
||||
if h.contentType == nil {
|
||||
return nil, false
|
||||
}
|
||||
return *(h.contentType), true
|
||||
case CriticalKey:
|
||||
if h.critical == nil {
|
||||
return nil, false
|
||||
}
|
||||
return h.critical, true
|
||||
case EphemeralPublicKeyKey:
|
||||
if h.ephemeralPublicKey == nil {
|
||||
return nil, false
|
||||
}
|
||||
return h.ephemeralPublicKey, true
|
||||
case JWKKey:
|
||||
if h.jwk == nil {
|
||||
return nil, false
|
||||
}
|
||||
return h.jwk, true
|
||||
case JWKSetURLKey:
|
||||
if h.jwkSetURL == nil {
|
||||
return nil, false
|
||||
}
|
||||
return *(h.jwkSetURL), true
|
||||
case KeyIDKey:
|
||||
if h.keyID == nil {
|
||||
return nil, false
|
||||
}
|
||||
return *(h.keyID), true
|
||||
case TypeKey:
|
||||
if h.typ == nil {
|
||||
return nil, false
|
||||
}
|
||||
return *(h.typ), true
|
||||
case X509CertChainKey:
|
||||
if h.x509CertChain == nil {
|
||||
return nil, false
|
||||
}
|
||||
return h.x509CertChain, true
|
||||
case X509CertThumbprintKey:
|
||||
if h.x509CertThumbprint == nil {
|
||||
return nil, false
|
||||
}
|
||||
return *(h.x509CertThumbprint), true
|
||||
case X509CertThumbprintS256Key:
|
||||
if h.x509CertThumbprintS256 == nil {
|
||||
return nil, false
|
||||
}
|
||||
return *(h.x509CertThumbprintS256), true
|
||||
case X509URLKey:
|
||||
if h.x509URL == nil {
|
||||
return nil, false
|
||||
}
|
||||
return *(h.x509URL), true
|
||||
default:
|
||||
v, ok := h.privateParams[name]
|
||||
return v, ok
|
||||
}
|
||||
}
|
||||
|
||||
func (h *stdHeaders) Set(name string, value interface{}) error {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
return h.setNoLock(name, value)
|
||||
}
|
||||
|
||||
func (h *stdHeaders) setNoLock(name string, value interface{}) error {
|
||||
switch name {
|
||||
case AgreementPartyUInfoKey:
|
||||
if v, ok := value.([]byte); ok {
|
||||
h.agreementPartyUInfo = v
|
||||
return nil
|
||||
}
|
||||
return errors.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)
|
||||
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)
|
||||
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)
|
||||
case ContentEncryptionKey:
|
||||
if v, ok := value.(jwa.ContentEncryptionAlgorithm); ok {
|
||||
if v == "" {
|
||||
return errors.New(`"enc" field cannot be an empty string`)
|
||||
}
|
||||
h.contentEncryption = &v
|
||||
return nil
|
||||
}
|
||||
return errors.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)
|
||||
case CriticalKey:
|
||||
if v, ok := value.([]string); ok {
|
||||
h.critical = v
|
||||
return nil
|
||||
}
|
||||
return errors.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)
|
||||
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)
|
||||
case JWKSetURLKey:
|
||||
if v, ok := value.(string); ok {
|
||||
h.jwkSetURL = &v
|
||||
return nil
|
||||
}
|
||||
return errors.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)
|
||||
case TypeKey:
|
||||
if v, ok := value.(string); ok {
|
||||
h.typ = &v
|
||||
return nil
|
||||
}
|
||||
return errors.Errorf(`invalid value for %s key: %T`, TypeKey, value)
|
||||
case X509CertChainKey:
|
||||
if v, ok := value.([]string); ok {
|
||||
h.x509CertChain = v
|
||||
return nil
|
||||
}
|
||||
return errors.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)
|
||||
case X509CertThumbprintS256Key:
|
||||
if v, ok := value.(string); ok {
|
||||
h.x509CertThumbprintS256 = &v
|
||||
return nil
|
||||
}
|
||||
return errors.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)
|
||||
default:
|
||||
if h.privateParams == nil {
|
||||
h.privateParams = map[string]interface{}{}
|
||||
}
|
||||
h.privateParams[name] = value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *stdHeaders) Remove(key string) error {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
switch key {
|
||||
case AgreementPartyUInfoKey:
|
||||
h.agreementPartyUInfo = nil
|
||||
case AgreementPartyVInfoKey:
|
||||
h.agreementPartyVInfo = nil
|
||||
case AlgorithmKey:
|
||||
h.algorithm = nil
|
||||
case CompressionKey:
|
||||
h.compression = nil
|
||||
case ContentEncryptionKey:
|
||||
h.contentEncryption = nil
|
||||
case ContentTypeKey:
|
||||
h.contentType = nil
|
||||
case CriticalKey:
|
||||
h.critical = nil
|
||||
case EphemeralPublicKeyKey:
|
||||
h.ephemeralPublicKey = nil
|
||||
case JWKKey:
|
||||
h.jwk = nil
|
||||
case JWKSetURLKey:
|
||||
h.jwkSetURL = nil
|
||||
case KeyIDKey:
|
||||
h.keyID = nil
|
||||
case TypeKey:
|
||||
h.typ = nil
|
||||
case X509CertChainKey:
|
||||
h.x509CertChain = nil
|
||||
case X509CertThumbprintKey:
|
||||
h.x509CertThumbprint = nil
|
||||
case X509CertThumbprintS256Key:
|
||||
h.x509CertThumbprintS256 = nil
|
||||
case X509URLKey:
|
||||
h.x509URL = nil
|
||||
default:
|
||||
delete(h.privateParams, key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *stdHeaders) UnmarshalJSON(buf []byte) error {
|
||||
h.agreementPartyUInfo = nil
|
||||
h.agreementPartyVInfo = nil
|
||||
h.algorithm = nil
|
||||
h.compression = nil
|
||||
h.contentEncryption = nil
|
||||
h.contentType = nil
|
||||
h.critical = nil
|
||||
h.ephemeralPublicKey = 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
|
||||
dec := json.NewDecoder(bytes.NewReader(buf))
|
||||
LOOP:
|
||||
for {
|
||||
tok, err := dec.Token()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, `error reading token`)
|
||||
}
|
||||
switch tok := tok.(type) {
|
||||
case json.Delim:
|
||||
// Assuming we're doing everything correctly, we should ONLY
|
||||
// get either '{' or '}' here.
|
||||
if tok == '}' { // End of object
|
||||
break LOOP
|
||||
} else if tok != '{' {
|
||||
return errors.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)
|
||||
}
|
||||
case AgreementPartyVInfoKey:
|
||||
if err := json.AssignNextBytesToken(&h.agreementPartyVInfo, dec); err != nil {
|
||||
return errors.Wrapf(err, `failed to decode value for key %s`, AgreementPartyVInfoKey)
|
||||
}
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
case CriticalKey:
|
||||
var decoded []string
|
||||
if err := dec.Decode(&decoded); err != nil {
|
||||
return errors.Wrapf(err, `failed to decode value for key %s`, CriticalKey)
|
||||
}
|
||||
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)
|
||||
}
|
||||
key, err := jwk.ParseKey(buf)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, `failed to parse JWK for key %s`, EphemeralPublicKeyKey)
|
||||
}
|
||||
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)
|
||||
}
|
||||
key, err := jwk.ParseKey(buf)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, `failed to parse JWK for key %s`, JWKKey)
|
||||
}
|
||||
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)
|
||||
}
|
||||
case KeyIDKey:
|
||||
if err := json.AssignNextStringToken(&h.keyID, dec); err != nil {
|
||||
return errors.Wrapf(err, `failed to decode value for key %s`, KeyIDKey)
|
||||
}
|
||||
case TypeKey:
|
||||
if err := json.AssignNextStringToken(&h.typ, dec); err != nil {
|
||||
return errors.Wrapf(err, `failed to decode value for key %s`, TypeKey)
|
||||
}
|
||||
case X509CertChainKey:
|
||||
var decoded []string
|
||||
if err := dec.Decode(&decoded); err != nil {
|
||||
return errors.Wrapf(err, `failed to decode value for key %s`, X509CertChainKey)
|
||||
}
|
||||
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)
|
||||
}
|
||||
case X509CertThumbprintS256Key:
|
||||
if err := json.AssignNextStringToken(&h.x509CertThumbprintS256, dec); err != nil {
|
||||
return errors.Wrapf(err, `failed to decode value for key %s`, X509CertThumbprintS256Key)
|
||||
}
|
||||
case X509URLKey:
|
||||
if err := json.AssignNextStringToken(&h.x509URL, dec); err != nil {
|
||||
return errors.Wrapf(err, `failed to decode value for key %s`, X509URLKey)
|
||||
}
|
||||
default:
|
||||
decoded, err := registry.Decode(dec, tok)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.setNoLock(tok, decoded)
|
||||
}
|
||||
default:
|
||||
return errors.Errorf(`invalid token %T`, tok)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h stdHeaders) MarshalJSON() ([]byte, error) {
|
||||
data := make(map[string]interface{})
|
||||
fields := make([]string, 0, 16)
|
||||
for _, pair := range h.makePairs() {
|
||||
fields = append(fields, pair.Key.(string))
|
||||
data[pair.Key.(string)] = pair.Value
|
||||
}
|
||||
|
||||
sort.Strings(fields)
|
||||
buf := pool.GetBytesBuffer()
|
||||
defer pool.ReleaseBytesBuffer(buf)
|
||||
buf.WriteByte('{')
|
||||
enc := json.NewEncoder(buf)
|
||||
for i, f := range fields {
|
||||
if i > 0 {
|
||||
buf.WriteRune(',')
|
||||
}
|
||||
buf.WriteRune('"')
|
||||
buf.WriteString(f)
|
||||
buf.WriteString(`":`)
|
||||
v := data[f]
|
||||
switch v := v.(type) {
|
||||
case []byte:
|
||||
buf.WriteRune('"')
|
||||
buf.WriteString(base64.EncodeToString(v))
|
||||
buf.WriteRune('"')
|
||||
default:
|
||||
if err := enc.Encode(v); err != nil {
|
||||
errors.Errorf(`failed to encode value for field %s`, f)
|
||||
}
|
||||
buf.Truncate(buf.Len() - 1)
|
||||
}
|
||||
}
|
||||
buf.WriteByte('}')
|
||||
ret := make([]byte, buf.Len())
|
||||
copy(ret, buf.Bytes())
|
||||
return ret, nil
|
||||
}
|
||||
102
server/vendor/github.com/lestrrat-go/jwx/jwe/interface.go
generated
vendored
Normal file
102
server/vendor/github.com/lestrrat-go/jwx/jwe/interface.go
generated
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
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)
|
||||
}
|
||||
240
server/vendor/github.com/lestrrat-go/jwx/jwe/internal/aescbc/aescbc.go
generated
vendored
Normal file
240
server/vendor/github.com/lestrrat-go/jwx/jwe/internal/aescbc/aescbc.go
generated
vendored
Normal file
@@ -0,0 +1,240 @@
|
||||
package aescbc
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"crypto/subtle"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"hash"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
NonceSize = 16
|
||||
)
|
||||
|
||||
func pad(buf []byte, n int) []byte {
|
||||
rem := n - len(buf)%n
|
||||
if rem == 0 {
|
||||
return buf
|
||||
}
|
||||
|
||||
newbuf := make([]byte, len(buf)+rem)
|
||||
copy(newbuf, buf)
|
||||
|
||||
for i := len(buf); i < len(newbuf); i++ {
|
||||
newbuf[i] = byte(rem)
|
||||
}
|
||||
return newbuf
|
||||
}
|
||||
|
||||
// ref. https://github.com/golang/go/blob/c3db64c0f45e8f2d75c5b59401e0fc925701b6f4/src/crypto/tls/conn.go#L279-L324
|
||||
//
|
||||
// extractPadding returns, in constant time, the length of the padding to remove
|
||||
// from the end of payload. It also returns a byte which is equal to 255 if the
|
||||
// padding was valid and 0 otherwise. See RFC 2246, Section 6.2.3.2.
|
||||
func extractPadding(payload []byte) (toRemove int, good byte) {
|
||||
if len(payload) < 1 {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
paddingLen := payload[len(payload)-1]
|
||||
t := uint(len(payload)) - uint(paddingLen)
|
||||
// if len(payload) > paddingLen then the MSB of t is zero
|
||||
good = byte(int32(^t) >> 31)
|
||||
|
||||
// The maximum possible padding length plus the actual length field
|
||||
toCheck := 256
|
||||
// The length of the padded data is public, so we can use an if here
|
||||
if toCheck > len(payload) {
|
||||
toCheck = len(payload)
|
||||
}
|
||||
|
||||
for i := 1; i <= toCheck; i++ {
|
||||
t := uint(paddingLen) - uint(i)
|
||||
// if i <= paddingLen then the MSB of t is zero
|
||||
mask := byte(int32(^t) >> 31)
|
||||
b := payload[len(payload)-i]
|
||||
good &^= mask&paddingLen ^ mask&b
|
||||
}
|
||||
|
||||
// We AND together the bits of good and replicate the result across
|
||||
// all the bits.
|
||||
good &= good << 4
|
||||
good &= good << 2
|
||||
good &= good << 1
|
||||
good = uint8(int8(good) >> 7)
|
||||
|
||||
// Zero the padding length on error. This ensures any unchecked bytes
|
||||
// are included in the MAC. Otherwise, an attacker that could
|
||||
// distinguish MAC failures from padding failures could mount an attack
|
||||
// similar to POODLE in SSL 3.0: given a good ciphertext that uses a
|
||||
// full block's worth of padding, replace the final block with another
|
||||
// block. If the MAC check passed but the padding check failed, the
|
||||
// last byte of that block decrypted to the block size.
|
||||
//
|
||||
// See also macAndPaddingGood logic below.
|
||||
paddingLen &= good
|
||||
|
||||
toRemove = int(paddingLen)
|
||||
return
|
||||
}
|
||||
|
||||
type Hmac struct {
|
||||
blockCipher cipher.Block
|
||||
hash func() hash.Hash
|
||||
keysize int
|
||||
tagsize int
|
||||
integrityKey []byte
|
||||
}
|
||||
|
||||
type BlockCipherFunc func([]byte) (cipher.Block, error)
|
||||
|
||||
func New(key []byte, f BlockCipherFunc) (hmac *Hmac, err error) {
|
||||
keysize := len(key) / 2
|
||||
ikey := key[:keysize]
|
||||
ekey := key[keysize:]
|
||||
|
||||
bc, ciphererr := f(ekey)
|
||||
if ciphererr != nil {
|
||||
err = errors.Wrap(ciphererr, `failed to execute block cipher function`)
|
||||
return
|
||||
}
|
||||
|
||||
var hfunc func() hash.Hash
|
||||
switch keysize {
|
||||
case 16:
|
||||
hfunc = sha256.New
|
||||
case 24:
|
||||
hfunc = sha512.New384
|
||||
case 32:
|
||||
hfunc = sha512.New
|
||||
default:
|
||||
return nil, errors.Errorf("unsupported key size %d", keysize)
|
||||
}
|
||||
|
||||
return &Hmac{
|
||||
blockCipher: bc,
|
||||
hash: hfunc,
|
||||
integrityKey: ikey,
|
||||
keysize: keysize,
|
||||
tagsize: keysize, // NonceSize,
|
||||
// While investigating GH #207, I stumbled upon another problem where
|
||||
// the computed tags don't match on decrypt. After poking through the
|
||||
// code using a bunch of debug statements, I've finally found out that
|
||||
// tagsize = keysize makes the whole thing work.
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NonceSize fulfills the crypto.AEAD interface
|
||||
func (c Hmac) NonceSize() int {
|
||||
return NonceSize
|
||||
}
|
||||
|
||||
// Overhead fulfills the crypto.AEAD interface
|
||||
func (c Hmac) Overhead() int {
|
||||
return c.blockCipher.BlockSize() + c.tagsize
|
||||
}
|
||||
|
||||
func (c Hmac) ComputeAuthTag(aad, nonce, ciphertext []byte) ([]byte, error) {
|
||||
buf := make([]byte, len(aad)+len(nonce)+len(ciphertext)+8)
|
||||
n := 0
|
||||
n += copy(buf, aad)
|
||||
n += copy(buf[n:], nonce)
|
||||
n += copy(buf[n:], ciphertext)
|
||||
binary.BigEndian.PutUint64(buf[n:], uint64(len(aad)*8))
|
||||
|
||||
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")
|
||||
}
|
||||
s := h.Sum(nil)
|
||||
return s[:c.tagsize], nil
|
||||
}
|
||||
|
||||
func ensureSize(dst []byte, n int) []byte {
|
||||
// if the dst buffer has enough length just copy the relevant parts to it.
|
||||
// Otherwise create a new slice that's big enough, and operate on that
|
||||
// Note: I think go-jose has a bug in that it checks for cap(), but not len().
|
||||
ret := dst
|
||||
if diff := n - len(dst); diff > 0 {
|
||||
// dst is not big enough
|
||||
ret = make([]byte, n)
|
||||
copy(ret, dst)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Seal fulfills the crypto.AEAD interface
|
||||
func (c Hmac) Seal(dst, nonce, plaintext, data []byte) []byte {
|
||||
ctlen := len(plaintext)
|
||||
ciphertext := make([]byte, ctlen+c.Overhead())[:ctlen]
|
||||
copy(ciphertext, plaintext)
|
||||
ciphertext = pad(ciphertext, c.blockCipher.BlockSize())
|
||||
|
||||
cbc := cipher.NewCBCEncrypter(c.blockCipher, nonce)
|
||||
cbc.CryptBlocks(ciphertext, ciphertext)
|
||||
|
||||
authtag, err := c.ComputeAuthTag(data, nonce, ciphertext)
|
||||
if err != nil {
|
||||
// Hmac implements cipher.AEAD interface. Seal can't return error.
|
||||
// But currently it never reach here because of Hmac.ComputeAuthTag doesn't return error.
|
||||
panic(fmt.Errorf("failed to seal on hmac: %v", err))
|
||||
}
|
||||
|
||||
retlen := len(dst) + len(ciphertext) + len(authtag)
|
||||
|
||||
ret := ensureSize(dst, retlen)
|
||||
out := ret[len(dst):]
|
||||
n := copy(out, ciphertext)
|
||||
copy(out[n:], authtag)
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// 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)")
|
||||
}
|
||||
|
||||
tagOffset := len(ciphertext) - c.tagsize
|
||||
if tagOffset%c.blockCipher.BlockSize() != 0 {
|
||||
return nil, fmt.Errorf(
|
||||
"invalid ciphertext (invalid length: %d %% %d != 0)",
|
||||
tagOffset,
|
||||
c.blockCipher.BlockSize(),
|
||||
)
|
||||
}
|
||||
tag := ciphertext[tagOffset:]
|
||||
ciphertext = ciphertext[:tagOffset]
|
||||
|
||||
expectedTag, err := c.ComputeAuthTag(data, nonce, ciphertext[:tagOffset])
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, `failed to compute auth tag`)
|
||||
}
|
||||
|
||||
if subtle.ConstantTimeCompare(expectedTag, tag) != 1 {
|
||||
return nil, errors.New("invalid ciphertext (tag mismatch)")
|
||||
}
|
||||
|
||||
cbc := cipher.NewCBCDecrypter(c.blockCipher, nonce)
|
||||
buf := make([]byte, tagOffset)
|
||||
cbc.CryptBlocks(buf, ciphertext)
|
||||
|
||||
toRemove, good := extractPadding(buf)
|
||||
cmp := subtle.ConstantTimeCompare(expectedTag, tag) & int(good)
|
||||
if cmp != 1 {
|
||||
return nil, errors.New("invalid ciphertext")
|
||||
}
|
||||
|
||||
plaintext := buf[:len(buf)-toRemove]
|
||||
ret := ensureSize(dst, len(plaintext))
|
||||
out := ret[len(dst):]
|
||||
copy(out, plaintext)
|
||||
return ret, nil
|
||||
}
|
||||
162
server/vendor/github.com/lestrrat-go/jwx/jwe/internal/cipher/cipher.go
generated
vendored
Normal file
162
server/vendor/github.com/lestrrat-go/jwx/jwe/internal/cipher/cipher.go
generated
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
package cipher
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"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"
|
||||
)
|
||||
|
||||
var gcm = &gcmFetcher{}
|
||||
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")
|
||||
}
|
||||
|
||||
aead, err := cipher.NewGCM(aescipher)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, `failed to create GCM for cipher`)
|
||||
}
|
||||
return aead, nil
|
||||
}
|
||||
|
||||
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 aead, nil
|
||||
}
|
||||
|
||||
func (c AesContentCipher) KeySize() int {
|
||||
return c.keysize
|
||||
}
|
||||
|
||||
func (c AesContentCipher) TagSize() int {
|
||||
return c.tagsize
|
||||
}
|
||||
|
||||
func NewAES(alg jwa.ContentEncryptionAlgorithm) (*AesContentCipher, error) {
|
||||
var keysize int
|
||||
var tagsize int
|
||||
var fetcher Fetcher
|
||||
switch alg {
|
||||
case jwa.A128GCM:
|
||||
keysize = 16
|
||||
tagsize = 16
|
||||
fetcher = gcm
|
||||
case jwa.A192GCM:
|
||||
keysize = 24
|
||||
tagsize = 16
|
||||
fetcher = gcm
|
||||
case jwa.A256GCM:
|
||||
keysize = 32
|
||||
tagsize = 16
|
||||
fetcher = gcm
|
||||
case jwa.A128CBC_HS256:
|
||||
tagsize = 16
|
||||
keysize = tagsize * 2
|
||||
fetcher = cbc
|
||||
case jwa.A192CBC_HS384:
|
||||
tagsize = 24
|
||||
keysize = tagsize * 2
|
||||
fetcher = cbc
|
||||
case jwa.A256CBC_HS512:
|
||||
tagsize = 32
|
||||
keysize = tagsize * 2
|
||||
fetcher = cbc
|
||||
default:
|
||||
return nil, errors.Errorf("failed to create AES content cipher: invalid algorithm (%s)", alg)
|
||||
}
|
||||
|
||||
return &AesContentCipher{
|
||||
keysize: keysize,
|
||||
tagsize: tagsize,
|
||||
fetch: fetcher,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c AesContentCipher) Encrypt(cek, plaintext, aad []byte) (iv, ciphertext, 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")
|
||||
}
|
||||
|
||||
// Seal may panic (argh!), so protect ourselves from that
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
switch e := e.(type) {
|
||||
case error:
|
||||
err = e
|
||||
default:
|
||||
err = errors.Errorf("%s", e)
|
||||
}
|
||||
err = errors.Wrap(err, "failed to encrypt")
|
||||
}
|
||||
}()
|
||||
|
||||
var bs keygen.ByteSource
|
||||
if c.NonceGenerator == nil {
|
||||
bs, err = keygen.NewRandom(aead.NonceSize()).Generate()
|
||||
} else {
|
||||
bs, err = c.NonceGenerator.Generate()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrap(err, "failed to generate nonce")
|
||||
}
|
||||
iv = bs.Bytes()
|
||||
|
||||
combined := aead.Seal(nil, iv, plaintext, aad)
|
||||
tagoffset := len(combined) - c.TagSize()
|
||||
|
||||
if tagoffset < 0 {
|
||||
panic(fmt.Sprintf("tag offset is less than 0 (combined len = %d, tagsize = %d)", len(combined), c.TagSize()))
|
||||
}
|
||||
|
||||
tag = combined[tagoffset:]
|
||||
ciphertext = make([]byte, tagoffset)
|
||||
copy(ciphertext, combined[:tagoffset])
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
// Open may panic (argh!), so protect ourselves from that
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
switch e := e.(type) {
|
||||
case error:
|
||||
err = e
|
||||
default:
|
||||
err = errors.Errorf("%s", e)
|
||||
}
|
||||
err = errors.Wrap(err, "failed to decrypt")
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
combined := make([]byte, len(ciphertxt)+len(tag))
|
||||
copy(combined, ciphertxt)
|
||||
copy(combined[len(ciphertxt):], tag)
|
||||
|
||||
buf, aeaderr := aead.Open(nil, iv, combined, aad)
|
||||
if aeaderr != nil {
|
||||
err = errors.Wrap(aeaderr, `aead.Open failed`)
|
||||
return
|
||||
}
|
||||
plaintext = buf
|
||||
return
|
||||
}
|
||||
34
server/vendor/github.com/lestrrat-go/jwx/jwe/internal/cipher/interface.go
generated
vendored
Normal file
34
server/vendor/github.com/lestrrat-go/jwx/jwe/internal/cipher/interface.go
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
package cipher
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
|
||||
"github.com/lestrrat-go/jwx/jwe/internal/keygen"
|
||||
)
|
||||
|
||||
const (
|
||||
TagSize = 16
|
||||
)
|
||||
|
||||
// ContentCipher knows how to encrypt/decrypt the content given a content
|
||||
// encryption key and other data
|
||||
type ContentCipher interface {
|
||||
KeySize() int
|
||||
Encrypt(cek, aad, plaintext []byte) ([]byte, []byte, []byte, error)
|
||||
Decrypt(cek, iv, aad, ciphertext, tag []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
type Fetcher interface {
|
||||
Fetch([]byte) (cipher.AEAD, error)
|
||||
}
|
||||
|
||||
type gcmFetcher struct{}
|
||||
type cbcFetcher struct{}
|
||||
|
||||
// AesContentCipher represents a cipher based on AES
|
||||
type AesContentCipher struct {
|
||||
NonceGenerator keygen.Generator
|
||||
fetch Fetcher
|
||||
keysize int
|
||||
tagsize int
|
||||
}
|
||||
67
server/vendor/github.com/lestrrat-go/jwx/jwe/internal/concatkdf/concatkdf.go
generated
vendored
Normal file
67
server/vendor/github.com/lestrrat-go/jwx/jwe/internal/concatkdf/concatkdf.go
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
package concatkdf
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type KDF struct {
|
||||
buf []byte
|
||||
otherinfo []byte
|
||||
z []byte
|
||||
hash crypto.Hash
|
||||
}
|
||||
|
||||
func ndata(src []byte) []byte {
|
||||
buf := make([]byte, 4+len(src))
|
||||
binary.BigEndian.PutUint32(buf, uint32(len(src)))
|
||||
copy(buf[4:], src)
|
||||
return buf
|
||||
}
|
||||
|
||||
func New(hash crypto.Hash, alg, Z, apu, apv, pubinfo, privinfo []byte) *KDF {
|
||||
algbuf := ndata(alg)
|
||||
apubuf := ndata(apu)
|
||||
apvbuf := ndata(apv)
|
||||
|
||||
concat := make([]byte, len(algbuf)+len(apubuf)+len(apvbuf)+len(pubinfo)+len(privinfo))
|
||||
n := copy(concat, algbuf)
|
||||
n += copy(concat[n:], apubuf)
|
||||
n += copy(concat[n:], apvbuf)
|
||||
n += copy(concat[n:], pubinfo)
|
||||
copy(concat[n:], privinfo)
|
||||
|
||||
return &KDF{
|
||||
hash: hash,
|
||||
otherinfo: concat,
|
||||
z: Z,
|
||||
}
|
||||
}
|
||||
|
||||
func (k *KDF) Read(out []byte) (int, error) {
|
||||
var round uint32 = 1
|
||||
h := k.hash.New()
|
||||
|
||||
for len(out) > len(k.buf) {
|
||||
h.Reset()
|
||||
|
||||
if err := binary.Write(h, binary.BigEndian, round); err != nil {
|
||||
return 0, errors.Wrap(err, "failed to write round using kdf")
|
||||
}
|
||||
if _, err := h.Write(k.z); err != nil {
|
||||
return 0, errors.Wrap(err, "failed to write z using kdf")
|
||||
}
|
||||
if _, err := h.Write(k.otherinfo); err != nil {
|
||||
return 0, errors.Wrap(err, "failed to write other info using kdf")
|
||||
}
|
||||
|
||||
k.buf = append(k.buf, h.Sum(nil)...)
|
||||
round++
|
||||
}
|
||||
|
||||
n := copy(out, k.buf[:len(out)])
|
||||
k.buf = k.buf[len(out):]
|
||||
return n, nil
|
||||
}
|
||||
42
server/vendor/github.com/lestrrat-go/jwx/jwe/internal/content_crypt/content_crypt.go
generated
vendored
Normal file
42
server/vendor/github.com/lestrrat-go/jwx/jwe/internal/content_crypt/content_crypt.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
package content_crypt //nolint:golint
|
||||
|
||||
import (
|
||||
"github.com/lestrrat-go/jwx/jwa"
|
||||
"github.com/lestrrat-go/jwx/jwe/internal/cipher"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (c Generic) Algorithm() jwa.ContentEncryptionAlgorithm {
|
||||
return c.alg
|
||||
}
|
||||
|
||||
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 iv, encrypted, tag, nil
|
||||
}
|
||||
|
||||
func (c Generic) Decrypt(cek, iv, ciphertext, tag, aad []byte) ([]byte, error) {
|
||||
return c.cipher.Decrypt(cek, iv, ciphertext, tag, aad)
|
||||
}
|
||||
|
||||
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 &Generic{
|
||||
alg: alg,
|
||||
cipher: c,
|
||||
keysize: c.KeySize(),
|
||||
tagsize: 16,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c Generic) KeySize() int {
|
||||
return c.keysize
|
||||
}
|
||||
20
server/vendor/github.com/lestrrat-go/jwx/jwe/internal/content_crypt/interface.go
generated
vendored
Normal file
20
server/vendor/github.com/lestrrat-go/jwx/jwe/internal/content_crypt/interface.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
package content_crypt //nolint:golint
|
||||
|
||||
import (
|
||||
"github.com/lestrrat-go/jwx/jwa"
|
||||
"github.com/lestrrat-go/jwx/jwe/internal/cipher"
|
||||
)
|
||||
|
||||
// Generic encrypts a message by applying all the necessary
|
||||
// modifications to the keys and the contents
|
||||
type Generic struct {
|
||||
alg jwa.ContentEncryptionAlgorithm
|
||||
keysize int
|
||||
tagsize int
|
||||
cipher cipher.ContentCipher
|
||||
}
|
||||
|
||||
type Cipher interface {
|
||||
Decrypt([]byte, []byte, []byte, []byte, []byte) ([]byte, error)
|
||||
KeySize() int
|
||||
}
|
||||
106
server/vendor/github.com/lestrrat-go/jwx/jwe/internal/keyenc/interface.go
generated
vendored
Normal file
106
server/vendor/github.com/lestrrat-go/jwx/jwe/internal/keyenc/interface.go
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
package keyenc
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"hash"
|
||||
|
||||
"github.com/lestrrat-go/jwx/jwa"
|
||||
"github.com/lestrrat-go/jwx/jwe/internal/keygen"
|
||||
)
|
||||
|
||||
// Encrypter is an interface for things that can encrypt keys
|
||||
type Encrypter interface {
|
||||
Algorithm() jwa.KeyEncryptionAlgorithm
|
||||
Encrypt([]byte) (keygen.ByteSource, error)
|
||||
// KeyID returns the key id for this Encrypter. This exists so that
|
||||
// you can pass in a Encrypter to MultiEncrypt, you can rest assured
|
||||
// that the generated key will have the proper key ID.
|
||||
KeyID() string
|
||||
|
||||
SetKeyID(string)
|
||||
}
|
||||
|
||||
// Decrypter is an interface for things that can decrypt keys
|
||||
type Decrypter interface {
|
||||
Algorithm() jwa.KeyEncryptionAlgorithm
|
||||
Decrypt([]byte) ([]byte, error)
|
||||
}
|
||||
|
||||
type Noop struct {
|
||||
alg jwa.KeyEncryptionAlgorithm
|
||||
keyID string
|
||||
sharedkey []byte
|
||||
}
|
||||
|
||||
// AES encrypts content encryption keys using AES key wrap.
|
||||
// Contrary to what the name implies, it also decrypt encrypted keys
|
||||
type AES struct {
|
||||
alg jwa.KeyEncryptionAlgorithm
|
||||
keyID string
|
||||
sharedkey []byte
|
||||
}
|
||||
|
||||
// AESGCM encrypts content encryption keys using AES-GCM key wrap.
|
||||
type AESGCMEncrypt struct {
|
||||
algorithm jwa.KeyEncryptionAlgorithm
|
||||
keyID string
|
||||
sharedkey []byte
|
||||
}
|
||||
|
||||
// ECDHESEncrypt encrypts content encryption keys using ECDH-ES.
|
||||
type ECDHESEncrypt struct {
|
||||
algorithm jwa.KeyEncryptionAlgorithm
|
||||
keyID string
|
||||
generator keygen.Generator
|
||||
}
|
||||
|
||||
// ECDHESDecrypt decrypts keys using ECDH-ES.
|
||||
type ECDHESDecrypt struct {
|
||||
keyalg jwa.KeyEncryptionAlgorithm
|
||||
contentalg jwa.ContentEncryptionAlgorithm
|
||||
apu []byte
|
||||
apv []byte
|
||||
privkey interface{}
|
||||
pubkey interface{}
|
||||
}
|
||||
|
||||
// RSAOAEPEncrypt encrypts keys using RSA OAEP algorithm
|
||||
type RSAOAEPEncrypt struct {
|
||||
alg jwa.KeyEncryptionAlgorithm
|
||||
pubkey *rsa.PublicKey
|
||||
keyID string
|
||||
}
|
||||
|
||||
// RSAOAEPDecrypt decrypts keys using RSA OAEP algorithm
|
||||
type RSAOAEPDecrypt struct {
|
||||
alg jwa.KeyEncryptionAlgorithm
|
||||
privkey *rsa.PrivateKey
|
||||
}
|
||||
|
||||
// RSAPKCS15Decrypt decrypts keys using RSA PKCS1v15 algorithm
|
||||
type RSAPKCS15Decrypt struct {
|
||||
alg jwa.KeyEncryptionAlgorithm
|
||||
privkey *rsa.PrivateKey
|
||||
generator keygen.Generator
|
||||
}
|
||||
|
||||
// RSAPKCSEncrypt encrypts keys using RSA PKCS1v15 algorithm
|
||||
type RSAPKCSEncrypt struct {
|
||||
alg jwa.KeyEncryptionAlgorithm
|
||||
pubkey *rsa.PublicKey
|
||||
keyID string
|
||||
}
|
||||
|
||||
// DirectDecrypt does no encryption (Note: Unimplemented)
|
||||
type DirectDecrypt struct {
|
||||
Key []byte
|
||||
}
|
||||
|
||||
// PBES2Encrypt encrypts keys with PBES2 / PBKDF2 password
|
||||
type PBES2Encrypt struct {
|
||||
algorithm jwa.KeyEncryptionAlgorithm
|
||||
hashFunc func() hash.Hash
|
||||
keylen int
|
||||
keyID string
|
||||
password []byte
|
||||
}
|
||||
661
server/vendor/github.com/lestrrat-go/jwx/jwe/internal/keyenc/keyenc.go
generated
vendored
Normal file
661
server/vendor/github.com/lestrrat-go/jwx/jwe/internal/keyenc/keyenc.go
generated
vendored
Normal file
@@ -0,0 +1,661 @@
|
||||
package keyenc
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"crypto/subtle"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
func NewNoop(alg jwa.KeyEncryptionAlgorithm, sharedkey []byte) (*Noop, error) {
|
||||
return &Noop{
|
||||
alg: alg,
|
||||
sharedkey: sharedkey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (kw *Noop) Algorithm() jwa.KeyEncryptionAlgorithm {
|
||||
return kw.alg
|
||||
}
|
||||
|
||||
func (kw *Noop) SetKeyID(v string) {
|
||||
kw.keyID = v
|
||||
}
|
||||
|
||||
func (kw *Noop) KeyID() string {
|
||||
return kw.keyID
|
||||
}
|
||||
|
||||
func (kw *Noop) Encrypt(_ []byte) (keygen.ByteSource, error) {
|
||||
return keygen.ByteKey(kw.sharedkey), nil
|
||||
}
|
||||
|
||||
// NewAES creates a key-wrap encrypter using AES.
|
||||
// Although the name suggests otherwise, this does the decryption as well.
|
||||
func NewAES(alg jwa.KeyEncryptionAlgorithm, sharedkey []byte) (*AES, error) {
|
||||
return &AES{
|
||||
alg: alg,
|
||||
sharedkey: sharedkey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Algorithm returns the key encryption algorithm being used
|
||||
func (kw *AES) Algorithm() jwa.KeyEncryptionAlgorithm {
|
||||
return kw.alg
|
||||
}
|
||||
|
||||
func (kw *AES) SetKeyID(v string) {
|
||||
kw.keyID = v
|
||||
}
|
||||
|
||||
// KeyID returns the key ID associated with this encrypter
|
||||
func (kw *AES) KeyID() string {
|
||||
return kw.keyID
|
||||
}
|
||||
|
||||
// Decrypt decrypts the encrypted key using AES key unwrap
|
||||
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")
|
||||
}
|
||||
|
||||
cek, err := Unwrap(block, enckey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unwrap data")
|
||||
}
|
||||
return cek, nil
|
||||
}
|
||||
|
||||
// KeyEncrypt encrypts the given content encryption key
|
||||
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")
|
||||
}
|
||||
encrypted, err := Wrap(block, cek)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, `keywrap: failed to wrap key`)
|
||||
}
|
||||
return keygen.ByteKey(encrypted), nil
|
||||
}
|
||||
|
||||
func NewAESGCMEncrypt(alg jwa.KeyEncryptionAlgorithm, sharedkey []byte) (*AESGCMEncrypt, error) {
|
||||
return &AESGCMEncrypt{
|
||||
algorithm: alg,
|
||||
sharedkey: sharedkey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (kw AESGCMEncrypt) Algorithm() jwa.KeyEncryptionAlgorithm {
|
||||
return kw.algorithm
|
||||
}
|
||||
|
||||
func (kw *AESGCMEncrypt) SetKeyID(v string) {
|
||||
kw.keyID = v
|
||||
}
|
||||
|
||||
func (kw AESGCMEncrypt) KeyID() string {
|
||||
return kw.keyID
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
aesgcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create gcm from cipher")
|
||||
}
|
||||
|
||||
iv := make([]byte, aesgcm.NonceSize())
|
||||
_, err = io.ReadFull(rand.Reader, iv)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get random iv")
|
||||
}
|
||||
|
||||
encrypted := aesgcm.Seal(nil, iv, cek, nil)
|
||||
tag := encrypted[len(encrypted)-aesgcm.Overhead():]
|
||||
ciphertext := encrypted[:len(encrypted)-aesgcm.Overhead()]
|
||||
return keygen.ByteWithIVAndTag{
|
||||
ByteKey: ciphertext,
|
||||
IV: iv,
|
||||
Tag: tag,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewPBES2Encrypt(alg jwa.KeyEncryptionAlgorithm, password []byte) (*PBES2Encrypt, error) {
|
||||
var hashFunc func() hash.Hash
|
||||
var keylen int
|
||||
switch alg {
|
||||
case jwa.PBES2_HS256_A128KW:
|
||||
hashFunc = sha256.New
|
||||
keylen = 16
|
||||
case jwa.PBES2_HS384_A192KW:
|
||||
hashFunc = sha512.New384
|
||||
keylen = 24
|
||||
case jwa.PBES2_HS512_A256KW:
|
||||
hashFunc = sha512.New
|
||||
keylen = 32
|
||||
default:
|
||||
return nil, errors.Errorf("unexpected key encryption algorithm %s", alg)
|
||||
}
|
||||
return &PBES2Encrypt{
|
||||
algorithm: alg,
|
||||
password: password,
|
||||
hashFunc: hashFunc,
|
||||
keylen: keylen,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (kw PBES2Encrypt) Algorithm() jwa.KeyEncryptionAlgorithm {
|
||||
return kw.algorithm
|
||||
}
|
||||
|
||||
func (kw *PBES2Encrypt) SetKeyID(v string) {
|
||||
kw.keyID = v
|
||||
}
|
||||
|
||||
func (kw PBES2Encrypt) KeyID() string {
|
||||
return kw.keyID
|
||||
}
|
||||
|
||||
func (kw PBES2Encrypt) Encrypt(cek []byte) (keygen.ByteSource, error) {
|
||||
count := 10000
|
||||
salt := make([]byte, kw.keylen)
|
||||
_, err := io.ReadFull(rand.Reader, salt)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get random salt")
|
||||
}
|
||||
|
||||
fullsalt := []byte(kw.algorithm)
|
||||
fullsalt = append(fullsalt, byte(0))
|
||||
fullsalt = append(fullsalt, salt...)
|
||||
sharedkey := pbkdf2.Key(kw.password, fullsalt, count, kw.keylen, kw.hashFunc)
|
||||
|
||||
block, err := aes.NewCipher(sharedkey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create cipher from shared key")
|
||||
}
|
||||
encrypted, err := Wrap(block, cek)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, `keywrap: failed to wrap key`)
|
||||
}
|
||||
return keygen.ByteWithSaltAndCount{
|
||||
ByteKey: encrypted,
|
||||
Salt: salt,
|
||||
Count: count,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewECDHESEncrypt creates a new key encrypter based on ECDH-ES
|
||||
func NewECDHESEncrypt(alg jwa.KeyEncryptionAlgorithm, enc jwa.ContentEncryptionAlgorithm, keysize int, keyif interface{}) (*ECDHESEncrypt, error) {
|
||||
var generator keygen.Generator
|
||||
var err error
|
||||
switch key := keyif.(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
generator, err = keygen.NewEcdhes(alg, enc, keysize, key)
|
||||
case x25519.PublicKey:
|
||||
generator, err = keygen.NewX25519(alg, enc, keysize, key)
|
||||
default:
|
||||
return nil, errors.Errorf("unexpected key type %T", keyif)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create key generator")
|
||||
}
|
||||
return &ECDHESEncrypt{
|
||||
algorithm: alg,
|
||||
generator: generator,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Algorithm returns the key encryption algorithm being used
|
||||
func (kw ECDHESEncrypt) Algorithm() jwa.KeyEncryptionAlgorithm {
|
||||
return kw.algorithm
|
||||
}
|
||||
|
||||
func (kw *ECDHESEncrypt) SetKeyID(v string) {
|
||||
kw.keyID = v
|
||||
}
|
||||
|
||||
// KeyID returns the key ID associated with this encrypter
|
||||
func (kw ECDHESEncrypt) KeyID() string {
|
||||
return kw.keyID
|
||||
}
|
||||
|
||||
// KeyEncrypt encrypts the content encryption key using ECDH-ES
|
||||
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")
|
||||
}
|
||||
|
||||
bwpk, ok := kg.(keygen.ByteWithECPublicKey)
|
||||
if !ok {
|
||||
return nil, errors.New("key generator generated invalid key (expected ByteWithECPrivateKey)")
|
||||
}
|
||||
|
||||
if kw.algorithm == jwa.ECDH_ES {
|
||||
return bwpk, nil
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(bwpk.Bytes())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to generate cipher from generated key")
|
||||
}
|
||||
|
||||
jek, err := Wrap(block, cek)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to wrap data")
|
||||
}
|
||||
|
||||
bwpk.ByteKey = keygen.ByteKey(jek)
|
||||
|
||||
return bwpk, nil
|
||||
}
|
||||
|
||||
// NewECDHESDecrypt creates a new key decrypter using ECDH-ES
|
||||
func NewECDHESDecrypt(keyalg jwa.KeyEncryptionAlgorithm, contentalg jwa.ContentEncryptionAlgorithm, pubkey interface{}, apu, apv []byte, privkey interface{}) *ECDHESDecrypt {
|
||||
return &ECDHESDecrypt{
|
||||
keyalg: keyalg,
|
||||
contentalg: contentalg,
|
||||
apu: apu,
|
||||
apv: apv,
|
||||
privkey: privkey,
|
||||
pubkey: pubkey,
|
||||
}
|
||||
}
|
||||
|
||||
// Algorithm returns the key encryption algorithm being used
|
||||
func (kw ECDHESDecrypt) Algorithm() jwa.KeyEncryptionAlgorithm {
|
||||
return kw.keyalg
|
||||
}
|
||||
|
||||
func DeriveZ(privkeyif interface{}, pubkeyif interface{}) ([]byte, error) {
|
||||
switch privkeyif.(type) {
|
||||
case x25519.PrivateKey:
|
||||
privkey, ok := privkeyif.(x25519.PrivateKey)
|
||||
if !ok {
|
||||
return nil, errors.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 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)
|
||||
}
|
||||
pubkey, ok := pubkeyif.(*ecdsa.PublicKey)
|
||||
if !ok {
|
||||
return nil, errors.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`)
|
||||
}
|
||||
|
||||
z, _ := privkey.PublicKey.Curve.ScalarMult(pubkey.X, pubkey.Y, privkey.D.Bytes())
|
||||
zBytes := ecutil.AllocECPointBuffer(z, privkey.Curve)
|
||||
defer ecutil.ReleaseECPointBuffer(zBytes)
|
||||
zCopy := make([]byte, len(zBytes))
|
||||
copy(zCopy, zBytes)
|
||||
return zCopy, nil
|
||||
}
|
||||
}
|
||||
|
||||
func DeriveECDHES(alg, apu, apv []byte, privkey interface{}, pubkey interface{}, keysize uint32) ([]byte, error) {
|
||||
pubinfo := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(pubinfo, keysize*8)
|
||||
zBytes, err := DeriveZ(privkey, pubkey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to determine Z")
|
||||
}
|
||||
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 key, nil
|
||||
}
|
||||
|
||||
// Decrypt decrypts the encrypted key using ECDH-ES
|
||||
func (kw ECDHESDecrypt) Decrypt(enckey []byte) ([]byte, error) {
|
||||
var algBytes []byte
|
||||
var keysize uint32
|
||||
|
||||
// Use keyalg except for when jwa.ECDH_ES
|
||||
algBytes = []byte(kw.keyalg.String())
|
||||
|
||||
switch kw.keyalg {
|
||||
case jwa.ECDH_ES:
|
||||
// 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)
|
||||
}
|
||||
keysize = uint32(c.KeySize())
|
||||
algBytes = []byte(kw.contentalg.String())
|
||||
case jwa.ECDH_ES_A128KW:
|
||||
keysize = 16
|
||||
case jwa.ECDH_ES_A192KW:
|
||||
keysize = 24
|
||||
case jwa.ECDH_ES_A256KW:
|
||||
keysize = 32
|
||||
default:
|
||||
return nil, errors.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`)
|
||||
}
|
||||
|
||||
// ECDH-ES does not wrap keys
|
||||
if kw.keyalg == jwa.ECDH_ES {
|
||||
return key, nil
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create cipher for ECDH-ES key wrap")
|
||||
}
|
||||
|
||||
return Unwrap(block, enckey)
|
||||
}
|
||||
|
||||
// NewRSAOAEPEncrypt creates a new key encrypter using RSA OAEP
|
||||
func NewRSAOAEPEncrypt(alg jwa.KeyEncryptionAlgorithm, pubkey *rsa.PublicKey) (*RSAOAEPEncrypt, error) {
|
||||
switch alg {
|
||||
case jwa.RSA_OAEP, jwa.RSA_OAEP_256:
|
||||
default:
|
||||
return nil, errors.Errorf("invalid RSA OAEP encrypt algorithm (%s)", alg)
|
||||
}
|
||||
return &RSAOAEPEncrypt{
|
||||
alg: alg,
|
||||
pubkey: pubkey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewRSAPKCSEncrypt creates a new key encrypter using PKCS1v15
|
||||
func NewRSAPKCSEncrypt(alg jwa.KeyEncryptionAlgorithm, pubkey *rsa.PublicKey) (*RSAPKCSEncrypt, error) {
|
||||
switch alg {
|
||||
case jwa.RSA1_5:
|
||||
default:
|
||||
return nil, errors.Errorf("invalid RSA PKCS encrypt algorithm (%s)", alg)
|
||||
}
|
||||
|
||||
return &RSAPKCSEncrypt{
|
||||
alg: alg,
|
||||
pubkey: pubkey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Algorithm returns the key encryption algorithm being used
|
||||
func (e RSAPKCSEncrypt) Algorithm() jwa.KeyEncryptionAlgorithm {
|
||||
return e.alg
|
||||
}
|
||||
|
||||
func (e *RSAPKCSEncrypt) SetKeyID(v string) {
|
||||
e.keyID = v
|
||||
}
|
||||
|
||||
// KeyID returns the key ID associated with this encrypter
|
||||
func (e RSAPKCSEncrypt) KeyID() string {
|
||||
return e.keyID
|
||||
}
|
||||
|
||||
// Algorithm returns the key encryption algorithm being used
|
||||
func (e RSAOAEPEncrypt) Algorithm() jwa.KeyEncryptionAlgorithm {
|
||||
return e.alg
|
||||
}
|
||||
|
||||
func (e *RSAOAEPEncrypt) SetKeyID(v string) {
|
||||
e.keyID = v
|
||||
}
|
||||
|
||||
// KeyID returns the key ID associated with this encrypter
|
||||
func (e RSAOAEPEncrypt) KeyID() string {
|
||||
return e.keyID
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
encrypted, err := rsa.EncryptPKCS1v15(rand.Reader, e.pubkey, cek)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to encrypt using PKCS1v15")
|
||||
}
|
||||
return keygen.ByteKey(encrypted), nil
|
||||
}
|
||||
|
||||
// KeyEncrypt encrypts the content encryption key using RSA OAEP
|
||||
func (e RSAOAEPEncrypt) Encrypt(cek []byte) (keygen.ByteSource, error) {
|
||||
var hash hash.Hash
|
||||
switch e.alg {
|
||||
case jwa.RSA_OAEP:
|
||||
hash = sha1.New()
|
||||
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")
|
||||
}
|
||||
encrypted, err := rsa.EncryptOAEP(hash, rand.Reader, e.pubkey, cek, []byte{})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, `failed to OAEP encrypt`)
|
||||
}
|
||||
return keygen.ByteKey(encrypted), nil
|
||||
}
|
||||
|
||||
// NewRSAPKCS15Decrypt creates a new decrypter using RSA PKCS1v15
|
||||
func NewRSAPKCS15Decrypt(alg jwa.KeyEncryptionAlgorithm, privkey *rsa.PrivateKey, keysize int) *RSAPKCS15Decrypt {
|
||||
generator := keygen.NewRandom(keysize * 2)
|
||||
return &RSAPKCS15Decrypt{
|
||||
alg: alg,
|
||||
privkey: privkey,
|
||||
generator: generator,
|
||||
}
|
||||
}
|
||||
|
||||
// Algorithm returns the key encryption algorithm being used
|
||||
func (d RSAPKCS15Decrypt) Algorithm() jwa.KeyEncryptionAlgorithm {
|
||||
return d.alg
|
||||
}
|
||||
|
||||
// Decrypt decrypts the encrypted key using RSA PKCS1v1.5
|
||||
func (d RSAPKCS15Decrypt) Decrypt(enckey []byte) ([]byte, error) {
|
||||
// Hey, these notes and workarounds were stolen from go-jose
|
||||
defer func() {
|
||||
// DecryptPKCS1v15SessionKey sometimes panics on an invalid payload
|
||||
// because of an index out of bounds error, which we want to ignore.
|
||||
// This has been fixed in Go 1.3.1 (released 2014/08/13), the recover()
|
||||
// only exists for preventing crashes with unpatched versions.
|
||||
// See: https://groups.google.com/forum/#!topic/golang-dev/7ihX6Y6kx9k
|
||||
// See: https://code.google.com/p/go/source/detail?r=58ee390ff31602edb66af41ed10901ec95904d33
|
||||
_ = recover()
|
||||
}()
|
||||
|
||||
// Perform some input validation.
|
||||
expectedlen := d.privkey.PublicKey.N.BitLen() / 8
|
||||
if expectedlen != len(enckey) {
|
||||
// Input size is incorrect, the encrypted payload should always match
|
||||
// the size of the public modulus (e.g. using a 2048 bit key will
|
||||
// produce 256 bytes of output). Reject this since it's invalid input.
|
||||
return nil, fmt.Errorf(
|
||||
"input size for key decrypt is incorrect (expected %d, got %d)",
|
||||
expectedlen,
|
||||
len(enckey),
|
||||
)
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
bk, err := d.generator.Generate()
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to generate key")
|
||||
}
|
||||
cek := bk.Bytes()
|
||||
|
||||
// When decrypting an RSA-PKCS1v1.5 payload, we must take precautions to
|
||||
// prevent chosen-ciphertext attacks as described in RFC 3218, "Preventing
|
||||
// the Million Message Attack on Cryptographic Message Syntax". We are
|
||||
// therefore 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 cek, nil
|
||||
}
|
||||
|
||||
// NewRSAOAEPDecrypt creates a new key decrypter using RSA OAEP
|
||||
func NewRSAOAEPDecrypt(alg jwa.KeyEncryptionAlgorithm, privkey *rsa.PrivateKey) (*RSAOAEPDecrypt, error) {
|
||||
switch alg {
|
||||
case jwa.RSA_OAEP, jwa.RSA_OAEP_256:
|
||||
default:
|
||||
return nil, errors.Errorf("invalid RSA OAEP decrypt algorithm (%s)", alg)
|
||||
}
|
||||
|
||||
return &RSAOAEPDecrypt{
|
||||
alg: alg,
|
||||
privkey: privkey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Algorithm returns the key encryption algorithm being used
|
||||
func (d RSAOAEPDecrypt) Algorithm() jwa.KeyEncryptionAlgorithm {
|
||||
return d.alg
|
||||
}
|
||||
|
||||
// Decrypt decrypts the encrypted key using RSA OAEP
|
||||
func (d RSAOAEPDecrypt) Decrypt(enckey []byte) ([]byte, error) {
|
||||
var hash hash.Hash
|
||||
switch d.alg {
|
||||
case jwa.RSA_OAEP:
|
||||
hash = sha1.New()
|
||||
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 rsa.DecryptOAEP(hash, rand.Reader, d.privkey, enckey, []byte{})
|
||||
}
|
||||
|
||||
// Decrypt for DirectDecrypt does not do anything other than
|
||||
// return a copy of the embedded key
|
||||
func (d DirectDecrypt) Decrypt() ([]byte, error) {
|
||||
cek := make([]byte, len(d.Key))
|
||||
copy(cek, d.Key)
|
||||
return cek, nil
|
||||
}
|
||||
|
||||
var keywrapDefaultIV = []byte{0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6}
|
||||
|
||||
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`)
|
||||
}
|
||||
|
||||
n := len(cek) / keywrapChunkLen
|
||||
r := make([][]byte, n)
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
r[i] = make([]byte, keywrapChunkLen)
|
||||
copy(r[i], cek[i*keywrapChunkLen:])
|
||||
}
|
||||
|
||||
buffer := make([]byte, keywrapChunkLen*2)
|
||||
tBytes := make([]byte, keywrapChunkLen)
|
||||
copy(buffer, keywrapDefaultIV)
|
||||
|
||||
for t := 0; t < 6*n; t++ {
|
||||
copy(buffer[keywrapChunkLen:], r[t%n])
|
||||
|
||||
kek.Encrypt(buffer, buffer)
|
||||
|
||||
binary.BigEndian.PutUint64(tBytes, uint64(t+1))
|
||||
|
||||
for i := 0; i < keywrapChunkLen; i++ {
|
||||
buffer[i] = buffer[i] ^ tBytes[i]
|
||||
}
|
||||
copy(r[t%n], buffer[keywrapChunkLen:])
|
||||
}
|
||||
|
||||
out := make([]byte, (n+1)*keywrapChunkLen)
|
||||
copy(out, buffer[:keywrapChunkLen])
|
||||
for i := range r {
|
||||
copy(out[(i+1)*8:], r[i])
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
n := (len(ciphertxt) / keywrapChunkLen) - 1
|
||||
r := make([][]byte, n)
|
||||
|
||||
for i := range r {
|
||||
r[i] = make([]byte, keywrapChunkLen)
|
||||
copy(r[i], ciphertxt[(i+1)*keywrapChunkLen:])
|
||||
}
|
||||
|
||||
buffer := make([]byte, keywrapChunkLen*2)
|
||||
tBytes := make([]byte, keywrapChunkLen)
|
||||
copy(buffer[:keywrapChunkLen], ciphertxt[:keywrapChunkLen])
|
||||
|
||||
for t := 6*n - 1; t >= 0; t-- {
|
||||
binary.BigEndian.PutUint64(tBytes, uint64(t+1))
|
||||
|
||||
for i := 0; i < keywrapChunkLen; i++ {
|
||||
buffer[i] = buffer[i] ^ tBytes[i]
|
||||
}
|
||||
copy(buffer[keywrapChunkLen:], r[t%n])
|
||||
|
||||
block.Decrypt(buffer, buffer)
|
||||
|
||||
copy(r[t%n], buffer[keywrapChunkLen:])
|
||||
}
|
||||
|
||||
if subtle.ConstantTimeCompare(buffer[:keywrapChunkLen], keywrapDefaultIV) == 0 {
|
||||
return nil, errors.New("key unwrap: failed to unwrap key")
|
||||
}
|
||||
|
||||
out := make([]byte, n*keywrapChunkLen)
|
||||
for i := range r {
|
||||
copy(out[i*keywrapChunkLen:], r[i])
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
73
server/vendor/github.com/lestrrat-go/jwx/jwe/internal/keygen/interface.go
generated
vendored
Normal file
73
server/vendor/github.com/lestrrat-go/jwx/jwe/internal/keygen/interface.go
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
package keygen
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
|
||||
"github.com/lestrrat-go/jwx/jwa"
|
||||
"github.com/lestrrat-go/jwx/x25519"
|
||||
)
|
||||
|
||||
type Generator interface {
|
||||
Size() int
|
||||
Generate() (ByteSource, error)
|
||||
}
|
||||
|
||||
// StaticKeyGenerate uses a static byte buffer to provide keys.
|
||||
type Static []byte
|
||||
|
||||
// RandomKeyGenerate generates random keys
|
||||
type Random struct {
|
||||
keysize int
|
||||
}
|
||||
|
||||
// EcdhesKeyGenerate generates keys using ECDH-ES algorithm / EC-DSA curve
|
||||
type Ecdhes struct {
|
||||
pubkey *ecdsa.PublicKey
|
||||
keysize int
|
||||
algorithm jwa.KeyEncryptionAlgorithm
|
||||
enc jwa.ContentEncryptionAlgorithm
|
||||
}
|
||||
|
||||
// X25519KeyGenerate generates keys using ECDH-ES algorithm / X25519 curve
|
||||
type X25519 struct {
|
||||
algorithm jwa.KeyEncryptionAlgorithm
|
||||
enc jwa.ContentEncryptionAlgorithm
|
||||
keysize int
|
||||
pubkey x25519.PublicKey
|
||||
}
|
||||
|
||||
// ByteKey is a generated key that only has the key's byte buffer
|
||||
// as its instance data. If a key needs to do more, such as providing
|
||||
// values to be set in a JWE header, that key type wraps a ByteKey
|
||||
type ByteKey []byte
|
||||
|
||||
// ByteWithECPublicKey holds the EC private key that generated
|
||||
// the key along with the key itself. This is required to set the
|
||||
// proper values in the JWE headers
|
||||
type ByteWithECPublicKey struct {
|
||||
ByteKey
|
||||
PublicKey interface{}
|
||||
}
|
||||
|
||||
type ByteWithIVAndTag struct {
|
||||
ByteKey
|
||||
IV []byte
|
||||
Tag []byte
|
||||
}
|
||||
|
||||
type ByteWithSaltAndCount struct {
|
||||
ByteKey
|
||||
Salt []byte
|
||||
Count int
|
||||
}
|
||||
|
||||
// ByteSource is an interface for things that return a byte sequence.
|
||||
// This is used for KeyGenerator so that the result of computations can
|
||||
// carry more than just the generate byte sequence.
|
||||
type ByteSource interface {
|
||||
Bytes() []byte
|
||||
}
|
||||
|
||||
type Setter interface {
|
||||
Set(string, interface{}) error
|
||||
}
|
||||
192
server/vendor/github.com/lestrrat-go/jwx/jwe/internal/keygen/keygen.go
generated
vendored
Normal file
192
server/vendor/github.com/lestrrat-go/jwx/jwe/internal/keygen/keygen.go
generated
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
package keygen
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"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"
|
||||
)
|
||||
|
||||
// Bytes returns the byte from this ByteKey
|
||||
func (k ByteKey) Bytes() []byte {
|
||||
return []byte(k)
|
||||
}
|
||||
|
||||
// Size returns the size of the key
|
||||
func (g Static) Size() int {
|
||||
return len(g)
|
||||
}
|
||||
|
||||
// Generate returns the key
|
||||
func (g Static) Generate() (ByteSource, error) {
|
||||
buf := make([]byte, g.Size())
|
||||
copy(buf, g)
|
||||
return ByteKey(buf), nil
|
||||
}
|
||||
|
||||
// NewRandom creates a new Generator that returns
|
||||
// random bytes
|
||||
func NewRandom(n int) Random {
|
||||
return Random{keysize: n}
|
||||
}
|
||||
|
||||
// Size returns the key size
|
||||
func (g Random) Size() int {
|
||||
return g.keysize
|
||||
}
|
||||
|
||||
// Generate generates a random new key
|
||||
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 ByteKey(buf), nil
|
||||
}
|
||||
|
||||
// NewEcdhes creates a new key generator using ECDH-ES
|
||||
func NewEcdhes(alg jwa.KeyEncryptionAlgorithm, enc jwa.ContentEncryptionAlgorithm, keysize int, pubkey *ecdsa.PublicKey) (*Ecdhes, error) {
|
||||
return &Ecdhes{
|
||||
algorithm: alg,
|
||||
enc: enc,
|
||||
keysize: keysize,
|
||||
pubkey: pubkey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Size returns the key size associated with this generator
|
||||
func (g Ecdhes) Size() int {
|
||||
return g.keysize
|
||||
}
|
||||
|
||||
// Generate generates new keys using ECDH-ES
|
||||
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")
|
||||
}
|
||||
|
||||
var algorithm string
|
||||
if g.algorithm == jwa.ECDH_ES {
|
||||
algorithm = g.enc.String()
|
||||
} else {
|
||||
algorithm = g.algorithm.String()
|
||||
}
|
||||
|
||||
pubinfo := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(pubinfo, uint32(g.keysize)*8)
|
||||
|
||||
z, _ := priv.PublicKey.Curve.ScalarMult(g.pubkey.X, g.pubkey.Y, priv.D.Bytes())
|
||||
zBytes := ecutil.AllocECPointBuffer(z, priv.PublicKey.Curve)
|
||||
defer ecutil.ReleaseECPointBuffer(zBytes)
|
||||
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 ByteWithECPublicKey{
|
||||
PublicKey: &priv.PublicKey,
|
||||
ByteKey: ByteKey(kek),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewX25519 creates a new key generator using ECDH-ES
|
||||
func NewX25519(alg jwa.KeyEncryptionAlgorithm, enc jwa.ContentEncryptionAlgorithm, keysize int, pubkey x25519.PublicKey) (*X25519, error) {
|
||||
return &X25519{
|
||||
algorithm: alg,
|
||||
enc: enc,
|
||||
keysize: keysize,
|
||||
pubkey: pubkey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Size returns the key size associated with this generator
|
||||
func (g X25519) Size() int {
|
||||
return g.keysize
|
||||
}
|
||||
|
||||
// Generate generates new keys using ECDH-ES
|
||||
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")
|
||||
}
|
||||
|
||||
var algorithm string
|
||||
if g.algorithm == jwa.ECDH_ES {
|
||||
algorithm = g.enc.String()
|
||||
} else {
|
||||
algorithm = g.algorithm.String()
|
||||
}
|
||||
|
||||
pubinfo := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(pubinfo, uint32(g.keysize)*8)
|
||||
|
||||
zBytes, err := curve25519.X25519(priv.Seed(), g.pubkey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to compute Z")
|
||||
}
|
||||
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 ByteWithECPublicKey{
|
||||
PublicKey: pub,
|
||||
ByteKey: ByteKey(kek),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create JWK")
|
||||
}
|
||||
|
||||
if err := h.Set("epk", key); err != nil {
|
||||
return errors.Wrap(err, "failed to write header")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HeaderPopulate populates the header with the required AES GCM
|
||||
// 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")
|
||||
}
|
||||
|
||||
if err := h.Set("tag", k.Tag); err != nil {
|
||||
return errors.Wrap(err, "failed to write header")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HeaderPopulate populates the header with the required PBES2
|
||||
// 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")
|
||||
}
|
||||
|
||||
if err := h.Set("p2s", k.Salt); err != nil {
|
||||
return errors.Wrap(err, "failed to write header")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
23
server/vendor/github.com/lestrrat-go/jwx/jwe/io.go
generated
vendored
Normal file
23
server/vendor/github.com/lestrrat-go/jwx/jwe/io.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
// 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)
|
||||
}
|
||||
388
server/vendor/github.com/lestrrat-go/jwx/jwe/jwe.go
generated
vendored
Normal file
388
server/vendor/github.com/lestrrat-go/jwx/jwe/jwe.go
generated
vendored
Normal file
@@ -0,0 +1,388 @@
|
||||
//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 pllaintext 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
|
||||
maxDecompressBufferSize int64
|
||||
}
|
||||
|
||||
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
|
||||
//
|
||||
// The decrypted payload must be smaller than the amount specified by the
|
||||
// `jwe.WithMaxDecompressBufferSize` setting, which defaults to 10MB.
|
||||
//
|
||||
// jwe.Decrypt(..., jwe.WithMaxDecompressBufferSize(250*1024))
|
||||
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
|
||||
// in v1 the default value is hardcoded. Use v2 if you want to change this value globally
|
||||
var maxDecompressBufferSize int64 = 10 * 1024 * 1024
|
||||
//nolint:forcetypeassert
|
||||
for _, option := range options {
|
||||
switch option.Ident() {
|
||||
case identMessage{}:
|
||||
dst = option.Value().(*Message)
|
||||
case identPostParser{}:
|
||||
postParse = option.Value().(PostParser)
|
||||
case identMaxDecompressBufferSize{}:
|
||||
maxDecompressBufferSize = option.Value().(int64)
|
||||
}
|
||||
}
|
||||
|
||||
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`)
|
||||
}
|
||||
}
|
||||
ctx.maxDecompressBufferSize = maxDecompressBufferSize
|
||||
|
||||
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)
|
||||
}
|
||||
654
server/vendor/github.com/lestrrat-go/jwx/jwe/message.go
generated
vendored
Normal file
654
server/vendor/github.com/lestrrat-go/jwx/jwe/message.go
generated
vendored
Normal file
@@ -0,0 +1,654 @@
|
||||
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)
|
||||
}
|
||||
// in v1, this number is hardcoded to 10000. Use v2 if you need to
|
||||
// finetune this value
|
||||
if countFlt > 10000 {
|
||||
return nil, errors.Errorf("invalid value for 'p2c'")
|
||||
}
|
||||
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, dctx.maxDecompressBufferSize)
|
||||
if err != nil {
|
||||
lastError = errors.Wrap(err, `failed to uncompress payload`)
|
||||
plaintext = nil
|
||||
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
|
||||
}
|
||||
96
server/vendor/github.com/lestrrat-go/jwx/jwe/options.go
generated
vendored
Normal file
96
server/vendor/github.com/lestrrat-go/jwx/jwe/options.go
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
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 identMaxDecompressBufferSize struct{}
|
||||
|
||||
type DecryptOption interface {
|
||||
Option
|
||||
decryptOption()
|
||||
}
|
||||
|
||||
type decryptOption struct {
|
||||
Option
|
||||
}
|
||||
|
||||
func (*decryptOption) decryptOption() {}
|
||||
|
||||
// WithMaxDecompressBufferSize specifies the maximum buffer size for used when
|
||||
// decompressing the payload of a JWE message. If a JWE payload is compressed,
|
||||
// and the size of the decompressed payload exceeds this amount, and error is
|
||||
// returned. The default value is 10MB.
|
||||
func WithMaxDecompressBufferSize(size int64) DecryptOption {
|
||||
return &decryptOption{option.New(identMaxDecompressBufferSize{}, size)}
|
||||
}
|
||||
|
||||
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)}
|
||||
}
|
||||
94
server/vendor/github.com/lestrrat-go/jwx/jwe/serializer.go
generated
vendored
Normal file
94
server/vendor/github.com/lestrrat-go/jwx/jwe/serializer.go
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
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)
|
||||
}
|
||||
274
server/vendor/github.com/lestrrat-go/jwx/jwk/README.md
generated
vendored
Normal file
274
server/vendor/github.com/lestrrat-go/jwx/jwk/README.md
generated
vendored
Normal file
@@ -0,0 +1,274 @@
|
||||
# JWK [](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<br>P-384<br>P-521<br>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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
85
server/vendor/github.com/lestrrat-go/jwx/jwk/certchain.go
generated
vendored
Normal file
85
server/vendor/github.com/lestrrat-go/jwx/jwk/certchain.go
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
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
|
||||
}
|
||||
229
server/vendor/github.com/lestrrat-go/jwx/jwk/ecdsa.go
generated
vendored
Normal file
229
server/vendor/github.com/lestrrat-go/jwx/jwk/ecdsa.go
generated
vendored
Normal file
@@ -0,0 +1,229 @@
|
||||
package jwk
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"fmt"
|
||||
"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"
|
||||
)
|
||||
|
||||
func init() {
|
||||
ecutil.RegisterCurve(elliptic.P256(), jwa.P256)
|
||||
ecutil.RegisterCurve(elliptic.P384(), jwa.P384)
|
||||
ecutil.RegisterCurve(elliptic.P521(), jwa.P521)
|
||||
}
|
||||
|
||||
func (k *ecdsaPublicKey) FromRaw(rawKey *ecdsa.PublicKey) error {
|
||||
k.mu.Lock()
|
||||
defer k.mu.Unlock()
|
||||
|
||||
if rawKey.X == nil {
|
||||
return errors.Errorf(`invalid ecdsa.PublicKey`)
|
||||
}
|
||||
|
||||
if rawKey.Y == nil {
|
||||
return errors.Errorf(`invalid ecdsa.PublicKey`)
|
||||
}
|
||||
|
||||
xbuf := ecutil.AllocECPointBuffer(rawKey.X, rawKey.Curve)
|
||||
ybuf := ecutil.AllocECPointBuffer(rawKey.Y, rawKey.Curve)
|
||||
defer ecutil.ReleaseECPointBuffer(xbuf)
|
||||
defer ecutil.ReleaseECPointBuffer(ybuf)
|
||||
|
||||
k.x = make([]byte, len(xbuf))
|
||||
copy(k.x, xbuf)
|
||||
k.y = make([]byte, len(ybuf))
|
||||
copy(k.y, ybuf)
|
||||
|
||||
var crv jwa.EllipticCurveAlgorithm
|
||||
if tmp, ok := ecutil.AlgorithmForCurve(rawKey.Curve); ok {
|
||||
crv = tmp
|
||||
} else {
|
||||
return errors.Errorf(`invalid elliptic curve %s`, rawKey.Curve)
|
||||
}
|
||||
k.crv = &crv
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *ecdsaPrivateKey) FromRaw(rawKey *ecdsa.PrivateKey) error {
|
||||
k.mu.Lock()
|
||||
defer k.mu.Unlock()
|
||||
|
||||
if rawKey.PublicKey.X == nil {
|
||||
return errors.Errorf(`invalid ecdsa.PrivateKey`)
|
||||
}
|
||||
if rawKey.PublicKey.Y == nil {
|
||||
return errors.Errorf(`invalid ecdsa.PrivateKey`)
|
||||
}
|
||||
if rawKey.D == nil {
|
||||
return errors.Errorf(`invalid ecdsa.PrivateKey`)
|
||||
}
|
||||
|
||||
xbuf := ecutil.AllocECPointBuffer(rawKey.PublicKey.X, rawKey.Curve)
|
||||
ybuf := ecutil.AllocECPointBuffer(rawKey.PublicKey.Y, rawKey.Curve)
|
||||
dbuf := ecutil.AllocECPointBuffer(rawKey.D, rawKey.Curve)
|
||||
defer ecutil.ReleaseECPointBuffer(xbuf)
|
||||
defer ecutil.ReleaseECPointBuffer(ybuf)
|
||||
defer ecutil.ReleaseECPointBuffer(dbuf)
|
||||
|
||||
k.x = make([]byte, len(xbuf))
|
||||
copy(k.x, xbuf)
|
||||
k.y = make([]byte, len(ybuf))
|
||||
copy(k.y, ybuf)
|
||||
k.d = make([]byte, len(dbuf))
|
||||
copy(k.d, dbuf)
|
||||
|
||||
var crv jwa.EllipticCurveAlgorithm
|
||||
if tmp, ok := ecutil.AlgorithmForCurve(rawKey.Curve); ok {
|
||||
crv = tmp
|
||||
} else {
|
||||
return errors.Errorf(`invalid elliptic curve %s`, rawKey.Curve)
|
||||
}
|
||||
k.crv = &crv
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildECDSAPublicKey(alg jwa.EllipticCurveAlgorithm, xbuf, ybuf []byte) (*ecdsa.PublicKey, error) {
|
||||
var crv elliptic.Curve
|
||||
if tmp, ok := ecutil.CurveForAlgorithm(alg); ok {
|
||||
crv = tmp
|
||||
} else {
|
||||
return nil, errors.Errorf(`invalid curve algorithm %s`, alg)
|
||||
}
|
||||
|
||||
var x, y big.Int
|
||||
x.SetBytes(xbuf)
|
||||
y.SetBytes(ybuf)
|
||||
|
||||
return &ecdsa.PublicKey{Curve: crv, X: &x, Y: &y}, nil
|
||||
}
|
||||
|
||||
// Raw returns the EC-DSA public key represented by this JWK
|
||||
func (k *ecdsaPublicKey) Raw(v interface{}) error {
|
||||
k.mu.RLock()
|
||||
defer k.mu.RUnlock()
|
||||
|
||||
pubk, err := buildECDSAPublicKey(k.Crv(), k.x, k.y)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, `failed to build public key`)
|
||||
}
|
||||
|
||||
return blackmagic.AssignIfCompatible(v, pubk)
|
||||
}
|
||||
|
||||
func (k *ecdsaPrivateKey) Raw(v interface{}) error {
|
||||
k.mu.RLock()
|
||||
defer k.mu.RUnlock()
|
||||
|
||||
pubk, err := buildECDSAPublicKey(k.Crv(), k.x, k.y)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, `failed to build public key`)
|
||||
}
|
||||
|
||||
var key ecdsa.PrivateKey
|
||||
var d big.Int
|
||||
d.SetBytes(k.d)
|
||||
key.D = &d
|
||||
key.PublicKey = *pubk
|
||||
|
||||
return blackmagic.AssignIfCompatible(v, &key)
|
||||
}
|
||||
|
||||
func makeECDSAPublicKey(v interface {
|
||||
makePairs() []*HeaderPair
|
||||
}) (Key, error) {
|
||||
newKey := NewECDSAPublicKey()
|
||||
|
||||
// Iterate and copy everything except for the bits that should not be in the public key
|
||||
for _, pair := range v.makePairs() {
|
||||
switch pair.Key {
|
||||
case ECDSADKey:
|
||||
continue
|
||||
default:
|
||||
//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 newKey, nil
|
||||
}
|
||||
|
||||
func (k *ecdsaPrivateKey) PublicKey() (Key, error) {
|
||||
return makeECDSAPublicKey(k)
|
||||
}
|
||||
|
||||
func (k *ecdsaPublicKey) PublicKey() (Key, error) {
|
||||
return makeECDSAPublicKey(k)
|
||||
}
|
||||
|
||||
func ecdsaThumbprint(hash crypto.Hash, crv, x, y string) []byte {
|
||||
h := hash.New()
|
||||
fmt.Fprint(h, `{"crv":"`)
|
||||
fmt.Fprint(h, crv)
|
||||
fmt.Fprint(h, `","kty":"EC","x":"`)
|
||||
fmt.Fprint(h, x)
|
||||
fmt.Fprint(h, `","y":"`)
|
||||
fmt.Fprint(h, y)
|
||||
fmt.Fprint(h, `"}`)
|
||||
return h.Sum(nil)
|
||||
}
|
||||
|
||||
// Thumbprint returns the JWK thumbprint using the indicated
|
||||
// hashing algorithm, according to RFC 7638
|
||||
func (k ecdsaPublicKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
|
||||
k.mu.RLock()
|
||||
defer k.mu.RUnlock()
|
||||
|
||||
var key ecdsa.PublicKey
|
||||
if err := k.Raw(&key); err != nil {
|
||||
return nil, errors.Wrap(err, `failed to materialize ecdsa.PublicKey for thumbprint generation`)
|
||||
}
|
||||
|
||||
xbuf := ecutil.AllocECPointBuffer(key.X, key.Curve)
|
||||
ybuf := ecutil.AllocECPointBuffer(key.Y, key.Curve)
|
||||
defer ecutil.ReleaseECPointBuffer(xbuf)
|
||||
defer ecutil.ReleaseECPointBuffer(ybuf)
|
||||
|
||||
return ecdsaThumbprint(
|
||||
hash,
|
||||
key.Curve.Params().Name,
|
||||
base64.EncodeToString(xbuf),
|
||||
base64.EncodeToString(ybuf),
|
||||
), nil
|
||||
}
|
||||
|
||||
// Thumbprint returns the JWK thumbprint using the indicated
|
||||
// hashing algorithm, according to RFC 7638
|
||||
func (k ecdsaPrivateKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
|
||||
k.mu.RLock()
|
||||
defer k.mu.RUnlock()
|
||||
|
||||
var key ecdsa.PrivateKey
|
||||
if err := k.Raw(&key); err != nil {
|
||||
return nil, errors.Wrap(err, `failed to materialize ecdsa.PrivateKey for thumbprint generation`)
|
||||
}
|
||||
|
||||
xbuf := ecutil.AllocECPointBuffer(key.X, key.Curve)
|
||||
ybuf := ecutil.AllocECPointBuffer(key.Y, key.Curve)
|
||||
defer ecutil.ReleaseECPointBuffer(xbuf)
|
||||
defer ecutil.ReleaseECPointBuffer(ybuf)
|
||||
|
||||
return ecdsaThumbprint(
|
||||
hash,
|
||||
key.Curve.Params().Name,
|
||||
base64.EncodeToString(xbuf),
|
||||
base64.EncodeToString(ybuf),
|
||||
), nil
|
||||
}
|
||||
1178
server/vendor/github.com/lestrrat-go/jwx/jwk/ecdsa_gen.go
generated
vendored
Normal file
1178
server/vendor/github.com/lestrrat-go/jwx/jwk/ecdsa_gen.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
13
server/vendor/github.com/lestrrat-go/jwx/jwk/es256k.go
generated
vendored
Normal file
13
server/vendor/github.com/lestrrat-go/jwx/jwk/es256k.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// +build jwx_es256k
|
||||
|
||||
package jwk
|
||||
|
||||
import (
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/lestrrat-go/jwx/internal/ecutil"
|
||||
"github.com/lestrrat-go/jwx/jwa"
|
||||
)
|
||||
|
||||
func init() {
|
||||
ecutil.RegisterCurve(secp256k1.S256(), jwa.Secp256k1)
|
||||
}
|
||||
14
server/vendor/github.com/lestrrat-go/jwx/jwk/gen.sh
generated
vendored
Normal file
14
server/vendor/github.com/lestrrat-go/jwx/jwk/gen.sh
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
#!/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
|
||||
159
server/vendor/github.com/lestrrat-go/jwx/jwk/interface.go
generated
vendored
Normal file
159
server/vendor/github.com/lestrrat-go/jwx/jwk/interface.go
generated
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
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"
|
||||
)
|
||||
|
||||
// KeyUsageType is used to denote what this key should be used for
|
||||
type KeyUsageType string
|
||||
|
||||
const (
|
||||
// ForSignature is the value used in the headers to indicate that
|
||||
// this key should be used for signatures
|
||||
ForSignature KeyUsageType = "sig"
|
||||
// ForEncryption is the value used in the headers to indicate that
|
||||
// this key should be used for encrypting
|
||||
ForEncryption KeyUsageType = "enc"
|
||||
)
|
||||
|
||||
type CertificateChain struct {
|
||||
certs []*x509.Certificate
|
||||
}
|
||||
|
||||
type KeyOperation string
|
||||
type KeyOperationList []KeyOperation
|
||||
|
||||
const (
|
||||
KeyOpSign KeyOperation = "sign" // (compute digital signature or MAC)
|
||||
KeyOpVerify KeyOperation = "verify" // (verify digital signature or MAC)
|
||||
KeyOpEncrypt KeyOperation = "encrypt" // (encrypt content)
|
||||
KeyOpDecrypt KeyOperation = "decrypt" // (decrypt content and validate decryption, if applicable)
|
||||
KeyOpWrapKey KeyOperation = "wrapKey" // (encrypt key)
|
||||
KeyOpUnwrapKey KeyOperation = "unwrapKey" // (decrypt key and validate decryption, if applicable)
|
||||
KeyOpDeriveKey KeyOperation = "deriveKey" // (derive key)
|
||||
KeyOpDeriveBits KeyOperation = "deriveBits" // (derive bits not to be used as a key)
|
||||
)
|
||||
|
||||
// Set represents JWKS object, a collection of jwk.Key objects.
|
||||
//
|
||||
// Sets can be safely converted to and from JSON using the standard
|
||||
// `"encoding/json".Marshal` and `"encoding/json".Unmarshal`. However,
|
||||
// if you do not know if the payload contains a single JWK or a JWK set,
|
||||
// consider using `jwk.Parse()` to always get a `jwk.Set` out of it.
|
||||
//
|
||||
// Since v1.2.12, JWK sets with private parameters can be parsed as well.
|
||||
// Such private parameters can be accessed via the `Field()` method.
|
||||
// If a resource contains a single JWK instead of a JWK set, private parameters
|
||||
// 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
|
||||
|
||||
// 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()
|
||||
|
||||
// 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)
|
||||
|
||||
// Field 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)
|
||||
|
||||
// Set sets the value of a single field.
|
||||
//
|
||||
// This method, which takes an `interface{}`, exists because
|
||||
// these objects can contain extra _arbitrary_ fields that users can
|
||||
// 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.
|
||||
// Index returns the index where the given key exists, -1 otherwise
|
||||
Index(Key) int
|
||||
|
||||
// Len returns the number of keys in the set
|
||||
Len() int
|
||||
|
||||
// LookupKeyID returns the first key matching the given key id.
|
||||
// The second return value is false if there are no keys matching the key id.
|
||||
// The set *may* contain multiple keys with the same key id. If you
|
||||
// need all of them, use `Iterate()`
|
||||
LookupKeyID(string) (Key, bool)
|
||||
|
||||
// Remove removes the key from the set.
|
||||
Remove(Key) bool
|
||||
|
||||
// Iterate creates an iterator to iterate through all keys in the set.
|
||||
Iterate(context.Context) KeyIterator
|
||||
|
||||
// Clone create a new set with identical keys. Keys themselves are not cloned.
|
||||
Clone() (Set, error)
|
||||
}
|
||||
|
||||
type set struct {
|
||||
keys []Key
|
||||
mu sync.RWMutex
|
||||
dc DecodeCtx
|
||||
privateParams map[string]interface{}
|
||||
}
|
||||
|
||||
type HeaderVisitor = iter.MapVisitor
|
||||
type HeaderVisitorFunc = iter.MapVisitorFunc
|
||||
type HeaderPair = mapiter.Pair
|
||||
type HeaderIterator = mapiter.Iterator
|
||||
type KeyPair = arrayiter.Pair
|
||||
type KeyIterator = arrayiter.Iterator
|
||||
|
||||
type PublicKeyer interface {
|
||||
// PublicKey creates the corresponding PublicKey type for this object.
|
||||
// All fields are copied onto the new public key, except for those that are not allowed.
|
||||
// Returned value must not be the receiver itself.
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
111
server/vendor/github.com/lestrrat-go/jwx/jwk/interface_gen.go
generated
vendored
Normal file
111
server/vendor/github.com/lestrrat-go/jwx/jwk/interface_gen.go
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
// This file is auto-generated. DO NOT EDIT
|
||||
|
||||
package jwk
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
|
||||
"github.com/lestrrat-go/jwx/jwa"
|
||||
)
|
||||
|
||||
const (
|
||||
KeyTypeKey = "kty"
|
||||
KeyUsageKey = "use"
|
||||
KeyOpsKey = "key_ops"
|
||||
AlgorithmKey = "alg"
|
||||
KeyIDKey = "kid"
|
||||
X509URLKey = "x58"
|
||||
X509CertChainKey = "x5c"
|
||||
X509CertThumbprintKey = "x5t"
|
||||
X509CertThumbprintS256Key = "x5t#S256"
|
||||
)
|
||||
|
||||
// Key defines the minimal interface for each of the
|
||||
// key types. Their use and implementation differ significantly
|
||||
// between each key types, so you should use type assertions
|
||||
// to perform more specific tasks with each key
|
||||
type Key interface {
|
||||
// Get returns the value of a single field. The second boolean return value
|
||||
// will be false if the field is not stored in the source
|
||||
//
|
||||
// This method, which returns an `interface{}`, exists because
|
||||
// these objects can contain extra _arbitrary_ fields that users can
|
||||
// specify, and there is no way of knowing what type they could be
|
||||
Get(string) (interface{}, bool)
|
||||
|
||||
// Set sets the value of a single field. Note that certain fields,
|
||||
// notably "kty", cannot be altered, but will not return an error
|
||||
//
|
||||
// This method, which takes an `interface{}`, exists because
|
||||
// these objects can contain extra _arbitrary_ fields that users can
|
||||
// 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.
|
||||
Remove(string) error
|
||||
|
||||
// Raw creates the corresponding raw key. For example,
|
||||
// EC types would create *ecdsa.PublicKey or *ecdsa.PrivateKey,
|
||||
// and OctetSeq types create a []byte key.
|
||||
//
|
||||
// If you do not know the exact type of a jwk.Key before attempting
|
||||
// to obtain the raw key, you can simply pass a pointer to an
|
||||
// empty interface as the first argument.
|
||||
//
|
||||
// If you already know the exact type, it is recommended that you
|
||||
// pass a pointer to the zero value of the actual key type (e.g. &rsa.PrivateKey)
|
||||
// for efficiency.
|
||||
Raw(interface{}) error
|
||||
|
||||
// Thumbprint returns the JWK thumbprint using the indicated
|
||||
// hashing algorithm, according to RFC 7638
|
||||
Thumbprint(crypto.Hash) ([]byte, error)
|
||||
|
||||
// Iterate returns an iterator that returns all keys and values.
|
||||
// See github.com/lestrrat-go/iter for a description of the iterator.
|
||||
Iterate(ctx context.Context) HeaderIterator
|
||||
|
||||
// Walk is a utility tool that allows a visitor to iterate all keys and values
|
||||
Walk(context.Context, HeaderVisitor) error
|
||||
|
||||
// AsMap is a utility tool that returns a new map that contains the same fields as the source
|
||||
AsMap(context.Context) (map[string]interface{}, error)
|
||||
|
||||
// PrivateParams returns the non-standard elements in the source structure
|
||||
// WARNING: DO NOT USE PrivateParams() IF YOU HAVE CONCURRENT CODE ACCESSING THEM.
|
||||
// Use `AsMap()` to get a copy of the entire header, or use `Iterate()` instead
|
||||
PrivateParams() map[string]interface{}
|
||||
|
||||
// Clone creates a new instance of the same type
|
||||
Clone() (Key, error)
|
||||
|
||||
// PublicKey creates the corresponding PublicKey type for this object.
|
||||
// All fields are copied onto the new public key, except for those that are not allowed.
|
||||
//
|
||||
// If the key is already a public key, it returns a new copy minus the disallowed fields as above.
|
||||
PublicKey() (Key, error)
|
||||
|
||||
// KeyType returns the `kid` of a JWK
|
||||
KeyType() jwa.KeyType
|
||||
// KeyUsage returns `use` of a JWK
|
||||
KeyUsage() string
|
||||
// KeyOps returns `key_ops` of a JWK
|
||||
KeyOps() KeyOperationList
|
||||
// Algorithm returns `alg` of a JWK
|
||||
Algorithm() string
|
||||
// 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
|
||||
// X509CertThumbprint returns `x5t` of a JWK
|
||||
X509CertThumbprint() string
|
||||
// X509CertThumbprintS256 returns `x5t#S256` of a JWK
|
||||
X509CertThumbprintS256() string
|
||||
|
||||
makePairs() []*HeaderPair
|
||||
}
|
||||
29
server/vendor/github.com/lestrrat-go/jwx/jwk/io.go
generated
vendored
Normal file
29
server/vendor/github.com/lestrrat-go/jwx/jwk/io.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
// 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...)
|
||||
}
|
||||
717
server/vendor/github.com/lestrrat-go/jwx/jwk/jwk.go
generated
vendored
Normal file
717
server/vendor/github.com/lestrrat-go/jwx/jwk/jwk.go
generated
vendored
Normal file
@@ -0,0 +1,717 @@
|
||||
//go:generate ./gen.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/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"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"
|
||||
)
|
||||
|
||||
var registry = json.NewRegistry()
|
||||
|
||||
func bigIntToBytes(n *big.Int) ([]byte, error) {
|
||||
if n == nil {
|
||||
return nil, errors.New(`invalid *big.Int value`)
|
||||
}
|
||||
return n.Bytes(), nil
|
||||
}
|
||||
|
||||
// New 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:
|
||||
//
|
||||
// - "crypto/rsa".PrivateKey and "crypto/rsa".PublicKey creates an RSA based key
|
||||
// - "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) {
|
||||
if key == nil {
|
||||
return nil, errors.New(`jwk.New requires a non-nil key`)
|
||||
}
|
||||
|
||||
var ptr interface{}
|
||||
switch v := key.(type) {
|
||||
case rsa.PrivateKey:
|
||||
ptr = &v
|
||||
case rsa.PublicKey:
|
||||
ptr = &v
|
||||
case ecdsa.PrivateKey:
|
||||
ptr = &v
|
||||
case ecdsa.PublicKey:
|
||||
ptr = &v
|
||||
default:
|
||||
ptr = v
|
||||
}
|
||||
|
||||
switch rawKey := ptr.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
k := NewRSAPrivateKey()
|
||||
if err := k.FromRaw(rawKey); err != nil {
|
||||
return nil, errors.Wrapf(err, `failed to initialize %T from %T`, k, rawKey)
|
||||
}
|
||||
return k, nil
|
||||
case *rsa.PublicKey:
|
||||
k := NewRSAPublicKey()
|
||||
if err := k.FromRaw(rawKey); err != nil {
|
||||
return nil, errors.Wrapf(err, `failed to initialize %T from %T`, k, rawKey)
|
||||
}
|
||||
return k, nil
|
||||
case *ecdsa.PrivateKey:
|
||||
k := NewECDSAPrivateKey()
|
||||
if err := k.FromRaw(rawKey); err != nil {
|
||||
return nil, errors.Wrapf(err, `failed to initialize %T from %T`, k, rawKey)
|
||||
}
|
||||
return k, nil
|
||||
case *ecdsa.PublicKey:
|
||||
k := NewECDSAPublicKey()
|
||||
if err := k.FromRaw(rawKey); err != nil {
|
||||
return nil, errors.Wrapf(err, `failed to initialize %T from %T`, k, rawKey)
|
||||
}
|
||||
return k, nil
|
||||
case ed25519.PrivateKey:
|
||||
k := NewOKPPrivateKey()
|
||||
if err := k.FromRaw(rawKey); err != nil {
|
||||
return nil, errors.Wrapf(err, `failed to initialize %T from %T`, k, rawKey)
|
||||
}
|
||||
return k, nil
|
||||
case ed25519.PublicKey:
|
||||
k := NewOKPPublicKey()
|
||||
if err := k.FromRaw(rawKey); err != nil {
|
||||
return nil, errors.Wrapf(err, `failed to initialize %T from %T`, k, rawKey)
|
||||
}
|
||||
return k, nil
|
||||
case x25519.PrivateKey:
|
||||
k := NewOKPPrivateKey()
|
||||
if err := k.FromRaw(rawKey); err != nil {
|
||||
return nil, errors.Wrapf(err, `failed to initialize %T from %T`, k, rawKey)
|
||||
}
|
||||
return k, nil
|
||||
case x25519.PublicKey:
|
||||
k := NewOKPPublicKey()
|
||||
if err := k.FromRaw(rawKey); err != nil {
|
||||
return nil, errors.Wrapf(err, `failed to initialize %T from %T`, k, rawKey)
|
||||
}
|
||||
return k, nil
|
||||
case []byte:
|
||||
k := NewSymmetricKey()
|
||||
if err := k.FromRaw(rawKey); err != nil {
|
||||
return nil, errors.Wrapf(err, `failed to initialize %T from %T`, k, rawKey)
|
||||
}
|
||||
return k, nil
|
||||
default:
|
||||
return nil, errors.Errorf(`invalid key type '%T' for jwk.New`, key)
|
||||
}
|
||||
}
|
||||
|
||||
// PublicSetOf returns a new jwk.Set consisting of
|
||||
// public keys of the keys contained in the set.
|
||||
//
|
||||
// This is useful when you are generating a set of private keys, and
|
||||
// you want to generate the corresponding public versions for the
|
||||
// users to verify with.
|
||||
//
|
||||
// Be aware that all fields will be copied onto the new public key. It is the caller's
|
||||
// responsibility to remove any fields, if necessary.
|
||||
func PublicSetOf(v Set) (Set, error) {
|
||||
newSet := NewSet()
|
||||
|
||||
n := v.Len()
|
||||
for i := 0; i < n; i++ {
|
||||
k, ok := v.Get(i)
|
||||
if !ok {
|
||||
return nil, errors.New("key not found")
|
||||
}
|
||||
pubKey, err := PublicKeyOf(k)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, `failed to get public key of %T`, k)
|
||||
}
|
||||
newSet.Add(pubKey)
|
||||
}
|
||||
|
||||
return newSet, nil
|
||||
}
|
||||
|
||||
// PublicKeyOf returns the corresponding public version of the jwk.Key.
|
||||
// If `v` is a SymmetricKey, then the same value is returned.
|
||||
// If `v` is already a public key, the key itself is returned.
|
||||
//
|
||||
// If `v` is a private key type that has a `PublicKey()` method, be aware
|
||||
// that all fields will be copied onto the new public key. It is the caller's
|
||||
// responsibility to remove any fields, if necessary
|
||||
//
|
||||
// If `v` is a raw key, the key is first converted to a `jwk.Key`
|
||||
func PublicKeyOf(v interface{}) (Key, error) {
|
||||
if pk, ok := v.(PublicKeyer); ok {
|
||||
return pk.PublicKey()
|
||||
}
|
||||
|
||||
jk, err := New(v)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, `failed to convert key into JWK`)
|
||||
}
|
||||
|
||||
return jk.PublicKey()
|
||||
}
|
||||
|
||||
// PublicRawKeyOf returns the corresponding public key of the given
|
||||
// value `v` (e.g. given *rsa.PrivateKey, *rsa.PublicKey is returned)
|
||||
// If `v` is already a public key, the key itself is returned.
|
||||
//
|
||||
// The returned value will always be a pointer to the public key,
|
||||
// except when a []byte (e.g. symmetric key, ed25519 key) is passed to `v`.
|
||||
// In this case, the same []byte value is returned.
|
||||
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)
|
||||
}
|
||||
|
||||
var raw interface{}
|
||||
if err := pubk.Raw(&raw); err != nil {
|
||||
return nil, errors.Wrapf(err, `failed to obtain raw key from %T`, pubk)
|
||||
}
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
// This may be a silly idea, but if the user gave us a non-pointer value...
|
||||
var ptr interface{}
|
||||
switch v := v.(type) {
|
||||
case rsa.PrivateKey:
|
||||
ptr = &v
|
||||
case rsa.PublicKey:
|
||||
ptr = &v
|
||||
case ecdsa.PrivateKey:
|
||||
ptr = &v
|
||||
case ecdsa.PublicKey:
|
||||
ptr = &v
|
||||
default:
|
||||
ptr = v
|
||||
}
|
||||
|
||||
switch x := ptr.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
return &x.PublicKey, nil
|
||||
case *rsa.PublicKey:
|
||||
return x, nil
|
||||
case *ecdsa.PrivateKey:
|
||||
return &x.PublicKey, nil
|
||||
case *ecdsa.PublicKey:
|
||||
return x, nil
|
||||
case ed25519.PrivateKey:
|
||||
return x.Public(), nil
|
||||
case ed25519.PublicKey:
|
||||
return x, nil
|
||||
case x25519.PrivateKey:
|
||||
return x.Public(), nil
|
||||
case x25519.PublicKey:
|
||||
return x, nil
|
||||
case []byte:
|
||||
return x, nil
|
||||
default:
|
||||
return nil, errors.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`.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
lastError = errors.Wrap(err, "failed to fetch remote JWK")
|
||||
continue
|
||||
}
|
||||
|
||||
if res.StatusCode != http.StatusOK {
|
||||
lastError = errors.Errorf("failed to fetch remote JWK (status = %d)", res.StatusCode)
|
||||
continue
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// 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`)
|
||||
}
|
||||
return nil, lastError
|
||||
}
|
||||
|
||||
// ParseRawKey is a combination of ParseKey and Raw. It parses a single JWK key,
|
||||
// and assigns the "raw" key to the given parameter. The key must either be
|
||||
// a pointer to an empty interface, or a pointer to the actual raw key type
|
||||
// such as *rsa.PrivateKey, *ecdsa.PublicKey, *[]byte, etc.
|
||||
func ParseRawKey(data []byte, rawkey interface{}) error {
|
||||
key, err := ParseKey(data)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, `failed to parse key`)
|
||||
}
|
||||
|
||||
if err := key.Raw(rawkey); err != nil {
|
||||
return errors.Wrap(err, `failed to assign to raw key variable`)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (ctx *setDecodeCtx) IgnoreParseError() bool {
|
||||
return ctx.ignoreParseError
|
||||
}
|
||||
|
||||
// ParseKey parses a single key JWK. Unlike `jwk.Parse` this method will
|
||||
// report failure if you attempt to pass a JWK set. Only use this function
|
||||
// when you know that the data is a single JWK.
|
||||
//
|
||||
// Given a WithPEM(true) option, this function assumes that the given input
|
||||
// is PEM encoded ASN.1 DER format key.
|
||||
//
|
||||
// Note that a successful parsing of any type of key does NOT necessarily
|
||||
// guarantee a valid key. For example, no checks against expiration dates
|
||||
// are performed for certificate expiration, no checks against missing
|
||||
// parameters are performed, etc.
|
||||
func ParseKey(data []byte, options ...ParseOption) (Key, error) {
|
||||
var parsePEM bool
|
||||
var localReg *json.Registry
|
||||
for _, option := range options {
|
||||
//nolint:forcetypeassert
|
||||
switch option.Ident() {
|
||||
case identPEM{}:
|
||||
parsePEM = option.Value().(bool)
|
||||
case identLocalRegistry{}:
|
||||
// in reality you can only pass either withLocalRegistry or
|
||||
// WithTypedField, but since withLocalRegistry is used only by us,
|
||||
// we skip checking
|
||||
localReg = option.Value().(*json.Registry)
|
||||
case identTypedField{}:
|
||||
pair := option.Value().(typedFieldPair)
|
||||
if localReg == nil {
|
||||
localReg = json.NewRegistry()
|
||||
}
|
||||
localReg.Register(pair.Name, pair.Value)
|
||||
case identIgnoreParseError{}:
|
||||
return nil, errors.Errorf(`jwk.WithIgnoreParseError() cannot be used for ParseKey()`)
|
||||
}
|
||||
}
|
||||
|
||||
if parsePEM {
|
||||
raw, _, err := parsePEMEncodedRawKey(data)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, `failed to parse PEM encoded key`)
|
||||
}
|
||||
return New(raw)
|
||||
}
|
||||
|
||||
var hint struct {
|
||||
Kty string `json:"kty"`
|
||||
D json.RawMessage `json:"d"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &hint); err != nil {
|
||||
return nil, errors.Wrap(err, `failed to unmarshal JSON into key hint`)
|
||||
}
|
||||
|
||||
var key Key
|
||||
switch jwa.KeyType(hint.Kty) {
|
||||
case jwa.RSA:
|
||||
if len(hint.D) > 0 {
|
||||
key = newRSAPrivateKey()
|
||||
} else {
|
||||
key = newRSAPublicKey()
|
||||
}
|
||||
case jwa.EC:
|
||||
if len(hint.D) > 0 {
|
||||
key = newECDSAPrivateKey()
|
||||
} else {
|
||||
key = newECDSAPublicKey()
|
||||
}
|
||||
case jwa.OctetSeq:
|
||||
key = newSymmetricKey()
|
||||
case jwa.OKP:
|
||||
if len(hint.D) > 0 {
|
||||
key = newOKPPrivateKey()
|
||||
} else {
|
||||
key = newOKPPublicKey()
|
||||
}
|
||||
default:
|
||||
return nil, errors.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)
|
||||
}
|
||||
dc := json.NewDecodeCtx(localReg)
|
||||
dcKey.SetDecodeCtx(dc)
|
||||
defer func() { dcKey.SetDecodeCtx(nil) }()
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, key); err != nil {
|
||||
return nil, errors.Wrapf(err, `failed to unmarshal JSON into key (%T)`, key)
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// Parse parses JWK from the incoming []byte.
|
||||
//
|
||||
// For JWK sets, this is a convenience function. You could just as well
|
||||
// call `json.Unmarshal` against an empty set created by `jwk.NewSet()`
|
||||
// to parse a JSON buffer into a `jwk.Set`.
|
||||
//
|
||||
// This function exists because many times the user does not know before hand
|
||||
// if a JWK(s) resource at a remote location contains a single JWK key or
|
||||
// a JWK set, and `jwk.Parse()` can handle either case, returning a JWK Set
|
||||
// even if the data only contains a single JWK key
|
||||
//
|
||||
// If you are looking for more information on how JWKs are parsed, or if
|
||||
// you know for sure that you have a single key, please see the documentation
|
||||
// for `jwk.ParseKey()`.
|
||||
func Parse(src []byte, options ...ParseOption) (Set, error) {
|
||||
var parsePEM bool
|
||||
var localReg *json.Registry
|
||||
var ignoreParseError bool
|
||||
for _, option := range options {
|
||||
//nolint:forcetypeassert
|
||||
switch option.Ident() {
|
||||
case identPEM{}:
|
||||
parsePEM = option.Value().(bool)
|
||||
case identIgnoreParseError{}:
|
||||
ignoreParseError = option.Value().(bool)
|
||||
case identTypedField{}:
|
||||
pair := option.Value().(typedFieldPair)
|
||||
if localReg == nil {
|
||||
localReg = json.NewRegistry()
|
||||
}
|
||||
localReg.Register(pair.Name, pair.Value)
|
||||
}
|
||||
}
|
||||
|
||||
s := NewSet()
|
||||
|
||||
if parsePEM {
|
||||
src = bytes.TrimSpace(src)
|
||||
for len(src) > 0 {
|
||||
raw, rest, err := parsePEMEncodedRawKey(src)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, `failed to parse PEM encoded key`)
|
||||
}
|
||||
key, err := New(raw)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, `failed to create jwk.Key from %T`, raw)
|
||||
}
|
||||
s.Add(key)
|
||||
src = bytes.TrimSpace(rest)
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
dc := &setDecodeCtx{
|
||||
DecodeCtx: json.NewDecodeCtx(localReg),
|
||||
ignoreParseError: ignoreParseError,
|
||||
}
|
||||
dcKs.SetDecodeCtx(dc)
|
||||
defer func() { dcKs.SetDecodeCtx(nil) }()
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(src, s); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal JWK set")
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// ParseReader parses a JWK set from the incoming byte buffer.
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, `failed to read from io.Reader`)
|
||||
}
|
||||
|
||||
return Parse(buf, options...)
|
||||
}
|
||||
|
||||
// ParseString parses a JWK set from the incoming string.
|
||||
func ParseString(s string, options ...ParseOption) (Set, error) {
|
||||
return Parse([]byte(s), options...)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if _, ok := key.Get(KeyIDKey); ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
hash := crypto.SHA256
|
||||
for _, option := range options {
|
||||
//nolint:forcetypeassert
|
||||
switch option.Ident() {
|
||||
case identThumbprintHash{}:
|
||||
hash = option.Value().(crypto.Hash)
|
||||
}
|
||||
}
|
||||
|
||||
h, err := key.Thumbprint(hash)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, `failed to generate thumbprint`)
|
||||
}
|
||||
|
||||
if err := key.Set(KeyIDKey, base64.EncodeToString(h)); err != nil {
|
||||
return errors.Wrap(err, `failed to set "kid"`)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func cloneKey(src Key) (Key, error) {
|
||||
var dst Key
|
||||
switch src.(type) {
|
||||
case RSAPrivateKey:
|
||||
dst = NewRSAPrivateKey()
|
||||
case RSAPublicKey:
|
||||
dst = NewRSAPublicKey()
|
||||
case ECDSAPrivateKey:
|
||||
dst = NewECDSAPrivateKey()
|
||||
case ECDSAPublicKey:
|
||||
dst = NewECDSAPublicKey()
|
||||
case OKPPrivateKey:
|
||||
dst = NewOKPPrivateKey()
|
||||
case OKPPublicKey:
|
||||
dst = NewOKPPublicKey()
|
||||
case SymmetricKey:
|
||||
dst = NewSymmetricKey()
|
||||
default:
|
||||
return nil, errors.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 dst, nil
|
||||
}
|
||||
|
||||
// Pem serializes the given jwk.Key in PEM encoded ASN.1 DER format,
|
||||
// using either PKCS8 for private keys and PKIX for public keys.
|
||||
// If you need to encode using PKCS1 or SEC1, you must do it yourself.
|
||||
// The argument to this function must be of type jwk.Key or jwk.Set
|
||||
//
|
||||
// Currently only EC (including Ed25519) and RSA keys (and jwk.Set
|
||||
// comprised of these key types) are supported.
|
||||
func Pem(v interface{}) ([]byte, error) {
|
||||
var set Set
|
||||
switch v := v.(type) {
|
||||
case Key:
|
||||
set = NewSet()
|
||||
set.Add(v)
|
||||
case Set:
|
||||
set = v
|
||||
default:
|
||||
return nil, errors.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)
|
||||
typ, buf, err := asnEncode(key)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, `failed to encode content for key #%d`, i)
|
||||
}
|
||||
|
||||
var block pem.Block
|
||||
block.Type = typ
|
||||
block.Bytes = buf
|
||||
ret = append(ret, pem.EncodeToMemory(&block)...)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func asnEncode(key Key) (string, []byte, error) {
|
||||
switch key := key.(type) {
|
||||
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`)
|
||||
}
|
||||
buf, err := x509.MarshalPKCS8PrivateKey(rawkey)
|
||||
if err != nil {
|
||||
return "", nil, errors.Wrap(err, `failed to marshal PKCS8`)
|
||||
}
|
||||
return "PRIVATE KEY", 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`)
|
||||
}
|
||||
buf, err := x509.MarshalPKIXPublicKey(rawkey)
|
||||
if err != nil {
|
||||
return "", nil, errors.Wrap(err, `failed to marshal PKIX`)
|
||||
}
|
||||
return "PUBLIC KEY", buf, nil
|
||||
default:
|
||||
return "", nil, errors.Errorf(`unsupported key type %T`, key)
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
//
|
||||
// jwk.RegisterCustomField(`x-birthday`, timeT)
|
||||
//
|
||||
// Then `key.Get("x-birthday")` will still return an `interface{}`,
|
||||
// but you can convert its type to `time.Time`
|
||||
//
|
||||
// bdayif, _ := key.Get(`x-birthday`)
|
||||
// bday := bdayif.(time.Time)
|
||||
func RegisterCustomField(name string, object interface{}) {
|
||||
registry.Register(name, object)
|
||||
}
|
||||
60
server/vendor/github.com/lestrrat-go/jwx/jwk/key_ops.go
generated
vendored
Normal file
60
server/vendor/github.com/lestrrat-go/jwx/jwk/key_ops.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
package jwk
|
||||
|
||||
import "github.com/pkg/errors"
|
||||
|
||||
func (ops *KeyOperationList) Get() KeyOperationList {
|
||||
if ops == nil {
|
||||
return nil
|
||||
}
|
||||
return *ops
|
||||
}
|
||||
|
||||
func (ops *KeyOperationList) Accept(v interface{}) error {
|
||||
switch x := v.(type) {
|
||||
case string:
|
||||
return ops.Accept([]string{x})
|
||||
case KeyOperation:
|
||||
return ops.Accept([]KeyOperation{x})
|
||||
case []interface{}:
|
||||
l := make([]string, len(x))
|
||||
for i, e := range x {
|
||||
if es, ok := e.(string); ok {
|
||||
l[i] = es
|
||||
} else {
|
||||
return errors.Errorf(`invalid list element type: expected string, got %T`, v)
|
||||
}
|
||||
}
|
||||
return ops.Accept(l)
|
||||
case []string:
|
||||
list := make(KeyOperationList, len(x))
|
||||
for i, e := range x {
|
||||
switch e := KeyOperation(e); e {
|
||||
case KeyOpSign, KeyOpVerify, KeyOpEncrypt, KeyOpDecrypt, KeyOpWrapKey, KeyOpUnwrapKey, KeyOpDeriveKey, KeyOpDeriveBits:
|
||||
list[i] = e
|
||||
default:
|
||||
return errors.Errorf(`invalid keyoperation %v`, e)
|
||||
}
|
||||
}
|
||||
|
||||
*ops = list
|
||||
return nil
|
||||
case []KeyOperation:
|
||||
list := make(KeyOperationList, len(x))
|
||||
for i, e := range x {
|
||||
switch e {
|
||||
case KeyOpSign, KeyOpVerify, KeyOpEncrypt, KeyOpDecrypt, KeyOpWrapKey, KeyOpUnwrapKey, KeyOpDeriveKey, KeyOpDeriveBits:
|
||||
list[i] = e
|
||||
default:
|
||||
return errors.Errorf(`invalid keyoperation %v`, e)
|
||||
}
|
||||
}
|
||||
|
||||
*ops = list
|
||||
return nil
|
||||
case KeyOperationList:
|
||||
*ops = x
|
||||
return nil
|
||||
default:
|
||||
return errors.Errorf(`invalid value %T`, v)
|
||||
}
|
||||
}
|
||||
184
server/vendor/github.com/lestrrat-go/jwx/jwk/okp.go
generated
vendored
Normal file
184
server/vendor/github.com/lestrrat-go/jwx/jwk/okp.go
generated
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
package jwk
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/ed25519"
|
||||
"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"
|
||||
)
|
||||
|
||||
func (k *okpPublicKey) FromRaw(rawKeyIf interface{}) error {
|
||||
k.mu.Lock()
|
||||
defer k.mu.Unlock()
|
||||
|
||||
var crv jwa.EllipticCurveAlgorithm
|
||||
switch rawKey := rawKeyIf.(type) {
|
||||
case ed25519.PublicKey:
|
||||
k.x = rawKey
|
||||
crv = jwa.Ed25519
|
||||
k.crv = &crv
|
||||
case x25519.PublicKey:
|
||||
k.x = rawKey
|
||||
crv = jwa.X25519
|
||||
k.crv = &crv
|
||||
default:
|
||||
return errors.Errorf(`unknown key type %T`, rawKeyIf)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *okpPrivateKey) FromRaw(rawKeyIf interface{}) error {
|
||||
k.mu.Lock()
|
||||
defer k.mu.Unlock()
|
||||
|
||||
var crv jwa.EllipticCurveAlgorithm
|
||||
switch rawKey := rawKeyIf.(type) {
|
||||
case ed25519.PrivateKey:
|
||||
k.d = rawKey.Seed()
|
||||
k.x = rawKey.Public().(ed25519.PublicKey) //nolint:forcetypeassert
|
||||
crv = jwa.Ed25519
|
||||
k.crv = &crv
|
||||
case x25519.PrivateKey:
|
||||
k.d = rawKey.Seed()
|
||||
k.x = rawKey.Public().(x25519.PublicKey) //nolint:forcetypeassert
|
||||
crv = jwa.X25519
|
||||
k.crv = &crv
|
||||
default:
|
||||
return errors.Errorf(`unknown key type %T`, rawKeyIf)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildOKPPublicKey(alg jwa.EllipticCurveAlgorithm, xbuf []byte) (interface{}, error) {
|
||||
switch alg {
|
||||
case jwa.Ed25519:
|
||||
return ed25519.PublicKey(xbuf), nil
|
||||
case jwa.X25519:
|
||||
return x25519.PublicKey(xbuf), nil
|
||||
default:
|
||||
return nil, errors.Errorf(`invalid curve algorithm %s`, alg)
|
||||
}
|
||||
}
|
||||
|
||||
// Raw returns the EC-DSA public key represented by this JWK
|
||||
func (k *okpPublicKey) Raw(v interface{}) error {
|
||||
k.mu.RLock()
|
||||
defer k.mu.RUnlock()
|
||||
|
||||
pubk, err := buildOKPPublicKey(k.Crv(), k.x)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, `failed to build public key`)
|
||||
}
|
||||
|
||||
return blackmagic.AssignIfCompatible(v, pubk)
|
||||
}
|
||||
|
||||
func buildOKPPrivateKey(alg jwa.EllipticCurveAlgorithm, xbuf []byte, dbuf []byte) (interface{}, error) {
|
||||
switch alg {
|
||||
case jwa.Ed25519:
|
||||
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 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`)
|
||||
}
|
||||
//nolint:forcetypeassert
|
||||
if !bytes.Equal(xbuf, ret.Public().(x25519.PublicKey)) {
|
||||
return nil, errors.Errorf(`invalid x value given d value`)
|
||||
}
|
||||
return ret, nil
|
||||
default:
|
||||
return nil, errors.Errorf(`invalid curve algorithm %s`, alg)
|
||||
}
|
||||
}
|
||||
|
||||
func (k *okpPrivateKey) Raw(v interface{}) error {
|
||||
k.mu.RLock()
|
||||
defer k.mu.RUnlock()
|
||||
|
||||
privk, err := buildOKPPrivateKey(k.Crv(), k.x, k.d)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, `failed to build public key`)
|
||||
}
|
||||
|
||||
return blackmagic.AssignIfCompatible(v, privk)
|
||||
}
|
||||
|
||||
func makeOKPPublicKey(v interface {
|
||||
makePairs() []*HeaderPair
|
||||
}) (Key, error) {
|
||||
newKey := NewOKPPublicKey()
|
||||
|
||||
// Iterate and copy everything except for the bits that should not be in the public key
|
||||
for _, pair := range v.makePairs() {
|
||||
switch pair.Key {
|
||||
case OKPDKey:
|
||||
continue
|
||||
default:
|
||||
//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 newKey, nil
|
||||
}
|
||||
|
||||
func (k *okpPrivateKey) PublicKey() (Key, error) {
|
||||
return makeOKPPublicKey(k)
|
||||
}
|
||||
|
||||
func (k *okpPublicKey) PublicKey() (Key, error) {
|
||||
return makeOKPPublicKey(k)
|
||||
}
|
||||
|
||||
func okpThumbprint(hash crypto.Hash, crv, x string) []byte {
|
||||
h := hash.New()
|
||||
fmt.Fprint(h, `{"crv":"`)
|
||||
fmt.Fprint(h, crv)
|
||||
fmt.Fprint(h, `","kty":"OKP","x":"`)
|
||||
fmt.Fprint(h, x)
|
||||
fmt.Fprint(h, `"}`)
|
||||
return h.Sum(nil)
|
||||
}
|
||||
|
||||
// Thumbprint returns the JWK thumbprint using the indicated
|
||||
// hashing algorithm, according to RFC 7638 / 8037
|
||||
func (k okpPublicKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
|
||||
k.mu.RLock()
|
||||
defer k.mu.RUnlock()
|
||||
|
||||
return okpThumbprint(
|
||||
hash,
|
||||
k.Crv().String(),
|
||||
base64.EncodeToString(k.x),
|
||||
), nil
|
||||
}
|
||||
|
||||
// Thumbprint returns the JWK thumbprint using the indicated
|
||||
// hashing algorithm, according to RFC 7638 / 8037
|
||||
func (k okpPrivateKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
|
||||
k.mu.RLock()
|
||||
defer k.mu.RUnlock()
|
||||
|
||||
return okpThumbprint(
|
||||
hash,
|
||||
k.Crv().String(),
|
||||
base64.EncodeToString(k.x),
|
||||
), nil
|
||||
}
|
||||
1116
server/vendor/github.com/lestrrat-go/jwx/jwk/okp_gen.go
generated
vendored
Normal file
1116
server/vendor/github.com/lestrrat-go/jwx/jwk/okp_gen.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
197
server/vendor/github.com/lestrrat-go/jwx/jwk/option.go
generated
vendored
Normal file
197
server/vendor/github.com/lestrrat-go/jwx/jwk/option.go
generated
vendored
Normal file
@@ -0,0 +1,197 @@
|
||||
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)}
|
||||
}
|
||||
654
server/vendor/github.com/lestrrat-go/jwx/jwk/refresh.go
generated
vendored
Normal file
654
server/vendor/github.com/lestrrat-go/jwx/jwk/refresh.go
generated
vendored
Normal file
@@ -0,0 +1,654 @@
|
||||
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
|
||||
}
|
||||
244
server/vendor/github.com/lestrrat-go/jwx/jwk/rsa.go
generated
vendored
Normal file
244
server/vendor/github.com/lestrrat-go/jwx/jwk/rsa.go
generated
vendored
Normal file
@@ -0,0 +1,244 @@
|
||||
package jwk
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rsa"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"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"
|
||||
)
|
||||
|
||||
func (k *rsaPrivateKey) FromRaw(rawKey *rsa.PrivateKey) error {
|
||||
k.mu.Lock()
|
||||
defer k.mu.Unlock()
|
||||
|
||||
d, err := bigIntToBytes(rawKey.D)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, `invalid rsa.PrivateKey`)
|
||||
}
|
||||
k.d = d
|
||||
|
||||
l := len(rawKey.Primes)
|
||||
|
||||
if l < 0 /* I know, I'm being paranoid */ || l > 2 {
|
||||
return fmt.Errorf(`invalid number of primes in rsa.PrivateKey: need 0 to 2, but got %d`, len(rawKey.Primes))
|
||||
}
|
||||
|
||||
if l > 0 {
|
||||
p, err := bigIntToBytes(rawKey.Primes[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf(`invalid rsa.PrivateKey: %w`, err)
|
||||
}
|
||||
k.p = p
|
||||
}
|
||||
|
||||
if l > 1 {
|
||||
q, err := bigIntToBytes(rawKey.Primes[1])
|
||||
if err != nil {
|
||||
return fmt.Errorf(`invalid rsa.PrivateKey: %w`, err)
|
||||
}
|
||||
k.q = q
|
||||
}
|
||||
|
||||
// dp, dq, qi are optional values
|
||||
if v, err := bigIntToBytes(rawKey.Precomputed.Dp); err == nil {
|
||||
k.dp = v
|
||||
}
|
||||
if v, err := bigIntToBytes(rawKey.Precomputed.Dq); err == nil {
|
||||
k.dq = v
|
||||
}
|
||||
if v, err := bigIntToBytes(rawKey.Precomputed.Qinv); err == nil {
|
||||
k.qi = v
|
||||
}
|
||||
|
||||
// public key part
|
||||
n, e, err := rsaPublicKeyByteValuesFromRaw(&rawKey.PublicKey)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, `invalid rsa.PrivateKey`)
|
||||
}
|
||||
k.n = n
|
||||
k.e = e
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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`)
|
||||
}
|
||||
|
||||
data := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(data, uint64(rawKey.E))
|
||||
i := 0
|
||||
for ; i < len(data); i++ {
|
||||
if data[i] != 0x0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n, data[i:], nil
|
||||
}
|
||||
|
||||
func (k *rsaPublicKey) FromRaw(rawKey *rsa.PublicKey) error {
|
||||
k.mu.Lock()
|
||||
defer k.mu.Unlock()
|
||||
|
||||
n, e, err := rsaPublicKeyByteValuesFromRaw(rawKey)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, `invalid rsa.PrivateKey`)
|
||||
}
|
||||
k.n = n
|
||||
k.e = e
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *rsaPrivateKey) Raw(v interface{}) error {
|
||||
k.mu.RLock()
|
||||
defer k.mu.RUnlock()
|
||||
|
||||
var d, q, p big.Int // note: do not use from sync.Pool
|
||||
|
||||
d.SetBytes(k.d)
|
||||
q.SetBytes(k.q)
|
||||
p.SetBytes(k.p)
|
||||
|
||||
// optional fields
|
||||
var dp, dq, qi *big.Int
|
||||
if len(k.dp) > 0 {
|
||||
dp = &big.Int{} // note: do not use from sync.Pool
|
||||
dp.SetBytes(k.dp)
|
||||
}
|
||||
|
||||
if len(k.dq) > 0 {
|
||||
dq = &big.Int{} // note: do not use from sync.Pool
|
||||
dq.SetBytes(k.dq)
|
||||
}
|
||||
|
||||
if len(k.qi) > 0 {
|
||||
qi = &big.Int{} // note: do not use from sync.Pool
|
||||
qi.SetBytes(k.qi)
|
||||
}
|
||||
|
||||
var key rsa.PrivateKey
|
||||
|
||||
pubk := newRSAPublicKey()
|
||||
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`)
|
||||
}
|
||||
|
||||
key.D = &d
|
||||
key.Primes = []*big.Int{&p, &q}
|
||||
|
||||
if dp != nil {
|
||||
key.Precomputed.Dp = dp
|
||||
}
|
||||
if dq != nil {
|
||||
key.Precomputed.Dq = dq
|
||||
}
|
||||
if qi != nil {
|
||||
key.Precomputed.Qinv = qi
|
||||
}
|
||||
key.Precomputed.CRTValues = []rsa.CRTValue{}
|
||||
|
||||
return blackmagic.AssignIfCompatible(v, &key)
|
||||
}
|
||||
|
||||
// Raw takes the values stored in the Key object, and creates the
|
||||
// corresponding *rsa.PublicKey object.
|
||||
func (k *rsaPublicKey) Raw(v interface{}) error {
|
||||
k.mu.RLock()
|
||||
defer k.mu.RUnlock()
|
||||
|
||||
var key rsa.PublicKey
|
||||
|
||||
n := pool.GetBigInt()
|
||||
e := pool.GetBigInt()
|
||||
defer pool.ReleaseBigInt(e)
|
||||
|
||||
n.SetBytes(k.n)
|
||||
e.SetBytes(k.e)
|
||||
|
||||
key.N = n
|
||||
key.E = int(e.Int64())
|
||||
|
||||
return blackmagic.AssignIfCompatible(v, &key)
|
||||
}
|
||||
|
||||
func makeRSAPublicKey(v interface {
|
||||
makePairs() []*HeaderPair
|
||||
}) (Key, error) {
|
||||
newKey := NewRSAPublicKey()
|
||||
|
||||
// Iterate and copy everything except for the bits that should not be in the public key
|
||||
for _, pair := range v.makePairs() {
|
||||
switch pair.Key {
|
||||
case RSADKey, RSADPKey, RSADQKey, RSAPKey, RSAQKey, RSAQIKey:
|
||||
continue
|
||||
default:
|
||||
//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 newKey, nil
|
||||
}
|
||||
|
||||
func (k *rsaPrivateKey) PublicKey() (Key, error) {
|
||||
return makeRSAPublicKey(k)
|
||||
}
|
||||
|
||||
func (k *rsaPublicKey) PublicKey() (Key, error) {
|
||||
return makeRSAPublicKey(k)
|
||||
}
|
||||
|
||||
// Thumbprint returns the JWK thumbprint using the indicated
|
||||
// hashing algorithm, according to RFC 7638
|
||||
func (k rsaPrivateKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
|
||||
k.mu.RLock()
|
||||
defer k.mu.RUnlock()
|
||||
|
||||
var key rsa.PrivateKey
|
||||
if err := k.Raw(&key); err != nil {
|
||||
return nil, errors.Wrap(err, `failed to materialize RSA private key`)
|
||||
}
|
||||
return rsaThumbprint(hash, &key.PublicKey)
|
||||
}
|
||||
|
||||
func (k rsaPublicKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
|
||||
k.mu.RLock()
|
||||
defer k.mu.RUnlock()
|
||||
|
||||
var key rsa.PublicKey
|
||||
if err := k.Raw(&key); err != nil {
|
||||
return nil, errors.Wrap(err, `failed to materialize RSA public key`)
|
||||
}
|
||||
return rsaThumbprint(hash, &key)
|
||||
}
|
||||
|
||||
func rsaThumbprint(hash crypto.Hash, key *rsa.PublicKey) ([]byte, error) {
|
||||
buf := pool.GetBytesBuffer()
|
||||
defer pool.ReleaseBytesBuffer(buf)
|
||||
|
||||
buf.WriteString(`{"e":"`)
|
||||
buf.WriteString(base64.EncodeUint64ToString(uint64(key.E)))
|
||||
buf.WriteString(`","kty":"RSA","n":"`)
|
||||
buf.WriteString(base64.EncodeToString(key.N.Bytes()))
|
||||
buf.WriteString(`"}`)
|
||||
|
||||
h := hash.New()
|
||||
if _, err := buf.WriteTo(h); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to write rsaThumbprint")
|
||||
}
|
||||
return h.Sum(nil), nil
|
||||
}
|
||||
1247
server/vendor/github.com/lestrrat-go/jwx/jwk/rsa_gen.go
generated
vendored
Normal file
1247
server/vendor/github.com/lestrrat-go/jwx/jwk/rsa_gen.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
301
server/vendor/github.com/lestrrat-go/jwx/jwk/set.go
generated
vendored
Normal file
301
server/vendor/github.com/lestrrat-go/jwx/jwk/set.go
generated
vendored
Normal file
@@ -0,0 +1,301 @@
|
||||
package jwk
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"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"
|
||||
)
|
||||
|
||||
const keysKey = `keys` // appease linter
|
||||
|
||||
// NewSet creates and empty `jwk.Set` object
|
||||
func NewSet() Set {
|
||||
return &set{
|
||||
privateParams: make(map[string]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *set) Set(n string, v interface{}) error {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
if n == keysKey {
|
||||
vl, ok := v.([]Key)
|
||||
if !ok {
|
||||
return errors.Errorf(`value for field "keys" must be []jwk.Key`)
|
||||
}
|
||||
s.keys = vl
|
||||
return nil
|
||||
}
|
||||
|
||||
s.privateParams[n] = v
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *set) Field(n string) (interface{}, bool) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
v, ok := s.privateParams[n]
|
||||
return v, ok
|
||||
}
|
||||
|
||||
func (s *set) Get(idx int) (Key, bool) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
if idx >= 0 && idx < len(s.keys) {
|
||||
return s.keys[idx], true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (s *set) Len() int {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return len(s.keys)
|
||||
}
|
||||
|
||||
// indexNL is Index(), but without the locking
|
||||
func (s *set) indexNL(key Key) int {
|
||||
for i, k := range s.keys {
|
||||
if k == key {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func (s *set) Index(key Key) int {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return s.indexNL(key)
|
||||
}
|
||||
|
||||
func (s *set) Add(key Key) bool {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if i := s.indexNL(key); i > -1 {
|
||||
return false
|
||||
}
|
||||
s.keys = append(s.keys, key)
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *set) Remove(key Key) bool {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
for i, k := range s.keys {
|
||||
if k == key {
|
||||
switch i {
|
||||
case 0:
|
||||
s.keys = s.keys[1:]
|
||||
case len(s.keys) - 1:
|
||||
s.keys = s.keys[:i]
|
||||
default:
|
||||
s.keys = append(s.keys[:i], s.keys[i+1:]...)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *set) Clear() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.keys = nil
|
||||
}
|
||||
|
||||
func (s *set) Iterate(ctx context.Context) KeyIterator {
|
||||
ch := make(chan *KeyPair, s.Len())
|
||||
go iterate(ctx, s.keys, ch)
|
||||
return arrayiter.New(ch)
|
||||
}
|
||||
|
||||
func iterate(ctx context.Context, keys []Key, ch chan *KeyPair) {
|
||||
defer close(ch)
|
||||
|
||||
for i, key := range keys {
|
||||
pair := &KeyPair{Index: i, Value: key}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case ch <- pair:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *set) MarshalJSON() ([]byte, error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
buf := pool.GetBytesBuffer()
|
||||
defer pool.ReleaseBytesBuffer(buf)
|
||||
enc := json.NewEncoder(buf)
|
||||
|
||||
fields := []string{keysKey}
|
||||
for k := range s.privateParams {
|
||||
fields = append(fields, k)
|
||||
}
|
||||
sort.Strings(fields)
|
||||
|
||||
buf.WriteByte('{')
|
||||
for i, field := range fields {
|
||||
if i > 0 {
|
||||
buf.WriteByte(',')
|
||||
}
|
||||
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)
|
||||
}
|
||||
} else {
|
||||
buf.WriteByte('[')
|
||||
for j, k := range s.keys {
|
||||
if j > 0 {
|
||||
buf.WriteByte(',')
|
||||
}
|
||||
if err := enc.Encode(k); err != nil {
|
||||
return nil, errors.Wrapf(err, `failed to marshal key #%d`, i)
|
||||
}
|
||||
}
|
||||
buf.WriteByte(']')
|
||||
}
|
||||
}
|
||||
buf.WriteByte('}')
|
||||
|
||||
ret := make([]byte, buf.Len())
|
||||
copy(ret, buf.Bytes())
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (s *set) UnmarshalJSON(data []byte) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.privateParams = make(map[string]interface{})
|
||||
s.keys = nil
|
||||
|
||||
var options []ParseOption
|
||||
var ignoreParseError bool
|
||||
if dc := s.dc; dc != nil {
|
||||
if localReg := dc.Registry(); localReg != nil {
|
||||
options = append(options, withLocalRegistry(localReg))
|
||||
}
|
||||
ignoreParseError = dc.IgnoreParseError()
|
||||
}
|
||||
|
||||
var sawKeysField bool
|
||||
dec := json.NewDecoder(bytes.NewReader(data))
|
||||
LOOP:
|
||||
for {
|
||||
tok, err := dec.Token()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, `error reading token`)
|
||||
}
|
||||
|
||||
switch tok := tok.(type) {
|
||||
case json.Delim:
|
||||
// Assuming we're doing everything correctly, we should ONLY
|
||||
// get either '{' or '}' here.
|
||||
if tok == '}' { // End of object
|
||||
break LOOP
|
||||
} else if tok != '{' {
|
||||
return errors.Errorf(`expected '{', but got '%c'`, tok)
|
||||
}
|
||||
case string:
|
||||
switch tok {
|
||||
case "keys":
|
||||
sawKeysField = true
|
||||
var list []json.RawMessage
|
||||
if err := dec.Decode(&list); err != nil {
|
||||
return errors.Wrap(err, `failed to decode "keys"`)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
continue
|
||||
}
|
||||
s.keys = append(s.keys, key)
|
||||
}
|
||||
default:
|
||||
var v interface{}
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return errors.Wrapf(err, `failed to decode value for key %q`, tok)
|
||||
}
|
||||
s.privateParams[tok] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is really silly, but we can only detect the
|
||||
// lack of the "keys" field after going through the
|
||||
// entire object once
|
||||
// Not checking for len(s.keys) == 0, because it could be
|
||||
// an empty key set
|
||||
if !sawKeysField {
|
||||
key, err := ParseKey(data, options...)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, `failed to parse sole key in key set`)
|
||||
}
|
||||
s.keys = append(s.keys, key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *set) LookupKeyID(kid string) (Key, bool) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
n := s.Len()
|
||||
for i := 0; i < n; i++ {
|
||||
key, ok := s.Get(i)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
if key.KeyID() == kid {
|
||||
return key, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (s *set) DecodeCtx() DecodeCtx {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return s.dc
|
||||
}
|
||||
|
||||
func (s *set) SetDecodeCtx(dc DecodeCtx) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.dc = dc
|
||||
}
|
||||
|
||||
func (s *set) Clone() (Set, error) {
|
||||
s2 := &set{}
|
||||
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
s2.keys = make([]Key, len(s.keys))
|
||||
|
||||
copy(s2.keys, s.keys)
|
||||
return s2, nil
|
||||
}
|
||||
61
server/vendor/github.com/lestrrat-go/jwx/jwk/symmetric.go
generated
vendored
Normal file
61
server/vendor/github.com/lestrrat-go/jwx/jwk/symmetric.go
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
package jwk
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"fmt"
|
||||
|
||||
"github.com/lestrrat-go/blackmagic"
|
||||
"github.com/lestrrat-go/jwx/internal/base64"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (k *symmetricKey) FromRaw(rawKey []byte) error {
|
||||
k.mu.Lock()
|
||||
defer k.mu.Unlock()
|
||||
|
||||
if len(rawKey) == 0 {
|
||||
return errors.New(`non-empty []byte key required`)
|
||||
}
|
||||
|
||||
k.octets = rawKey
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Raw returns the octets for this symmetric key.
|
||||
// Since this is a symmetric key, this just calls Octets
|
||||
func (k *symmetricKey) Raw(v interface{}) error {
|
||||
k.mu.RLock()
|
||||
defer k.mu.RUnlock()
|
||||
return blackmagic.AssignIfCompatible(v, k.octets)
|
||||
}
|
||||
|
||||
// Thumbprint returns the JWK thumbprint using the indicated
|
||||
// hashing algorithm, according to RFC 7638
|
||||
func (k *symmetricKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
|
||||
k.mu.RLock()
|
||||
defer k.mu.RUnlock()
|
||||
var octets []byte
|
||||
if err := k.Raw(&octets); err != nil {
|
||||
return nil, errors.Wrap(err, `failed to materialize symmetric key`)
|
||||
}
|
||||
|
||||
h := hash.New()
|
||||
fmt.Fprint(h, `{"k":"`)
|
||||
fmt.Fprint(h, base64.EncodeToString(octets))
|
||||
fmt.Fprint(h, `","kty":"oct"}`)
|
||||
return h.Sum(nil), nil
|
||||
}
|
||||
|
||||
func (k *symmetricKey) PublicKey() (Key, error) {
|
||||
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 newKey, nil
|
||||
}
|
||||
519
server/vendor/github.com/lestrrat-go/jwx/jwk/symmetric_gen.go
generated
vendored
Normal file
519
server/vendor/github.com/lestrrat-go/jwx/jwk/symmetric_gen.go
generated
vendored
Normal file
@@ -0,0 +1,519 @@
|
||||
// This file is auto-generated by jwk/internal/cmd/genheader/main.go. DO NOT EDIT
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
const (
|
||||
SymmetricOctetsKey = "k"
|
||||
)
|
||||
|
||||
type SymmetricKey interface {
|
||||
Key
|
||||
FromRaw([]byte) error
|
||||
Octets() []byte
|
||||
}
|
||||
|
||||
type symmetricKey struct {
|
||||
algorithm *string // 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
|
||||
privateParams map[string]interface{}
|
||||
mu *sync.RWMutex
|
||||
dc json.DecodeCtx
|
||||
}
|
||||
|
||||
func NewSymmetricKey() SymmetricKey {
|
||||
return newSymmetricKey()
|
||||
}
|
||||
|
||||
func newSymmetricKey() *symmetricKey {
|
||||
return &symmetricKey{
|
||||
mu: &sync.RWMutex{},
|
||||
privateParams: make(map[string]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (h symmetricKey) KeyType() jwa.KeyType {
|
||||
return jwa.OctetSeq
|
||||
}
|
||||
|
||||
func (h *symmetricKey) Algorithm() string {
|
||||
if h.algorithm != nil {
|
||||
return *(h.algorithm)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (h *symmetricKey) KeyID() string {
|
||||
if h.keyID != nil {
|
||||
return *(h.keyID)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (h *symmetricKey) KeyOps() KeyOperationList {
|
||||
if h.keyOps != nil {
|
||||
return *(h.keyOps)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *symmetricKey) KeyUsage() string {
|
||||
if h.keyUsage != nil {
|
||||
return *(h.keyUsage)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
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) X509CertThumbprint() string {
|
||||
if h.x509CertThumbprint != nil {
|
||||
return *(h.x509CertThumbprint)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (h *symmetricKey) X509CertThumbprintS256() string {
|
||||
if h.x509CertThumbprintS256 != nil {
|
||||
return *(h.x509CertThumbprintS256)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (h *symmetricKey) X509URL() string {
|
||||
if h.x509URL != nil {
|
||||
return *(h.x509URL)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (h *symmetricKey) makePairs() []*HeaderPair {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
|
||||
var pairs []*HeaderPair
|
||||
pairs = append(pairs, &HeaderPair{Key: "kty", Value: jwa.OctetSeq})
|
||||
if h.algorithm != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: AlgorithmKey, Value: *(h.algorithm)})
|
||||
}
|
||||
if h.keyID != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: KeyIDKey, Value: *(h.keyID)})
|
||||
}
|
||||
if h.keyOps != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: KeyOpsKey, Value: *(h.keyOps)})
|
||||
}
|
||||
if h.keyUsage != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: KeyUsageKey, Value: *(h.keyUsage)})
|
||||
}
|
||||
if h.octets != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: SymmetricOctetsKey, Value: h.octets})
|
||||
}
|
||||
if h.x509CertChain != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: X509CertChainKey, Value: *(h.x509CertChain)})
|
||||
}
|
||||
if h.x509CertThumbprint != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: X509CertThumbprintKey, Value: *(h.x509CertThumbprint)})
|
||||
}
|
||||
if h.x509CertThumbprintS256 != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: X509CertThumbprintS256Key, Value: *(h.x509CertThumbprintS256)})
|
||||
}
|
||||
if h.x509URL != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: X509URLKey, Value: *(h.x509URL)})
|
||||
}
|
||||
for k, v := range h.privateParams {
|
||||
pairs = append(pairs, &HeaderPair{Key: k, Value: v})
|
||||
}
|
||||
return pairs
|
||||
}
|
||||
|
||||
func (h *symmetricKey) PrivateParams() map[string]interface{} {
|
||||
return h.privateParams
|
||||
}
|
||||
|
||||
func (h *symmetricKey) Get(name string) (interface{}, bool) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
switch name {
|
||||
case KeyTypeKey:
|
||||
return h.KeyType(), true
|
||||
case AlgorithmKey:
|
||||
if h.algorithm == nil {
|
||||
return nil, false
|
||||
}
|
||||
return *(h.algorithm), true
|
||||
case KeyIDKey:
|
||||
if h.keyID == nil {
|
||||
return nil, false
|
||||
}
|
||||
return *(h.keyID), true
|
||||
case KeyOpsKey:
|
||||
if h.keyOps == nil {
|
||||
return nil, false
|
||||
}
|
||||
return *(h.keyOps), true
|
||||
case KeyUsageKey:
|
||||
if h.keyUsage == nil {
|
||||
return nil, false
|
||||
}
|
||||
return *(h.keyUsage), true
|
||||
case SymmetricOctetsKey:
|
||||
if h.octets == nil {
|
||||
return nil, false
|
||||
}
|
||||
return h.octets, true
|
||||
case X509CertChainKey:
|
||||
if h.x509CertChain == nil {
|
||||
return nil, false
|
||||
}
|
||||
return h.x509CertChain.Get(), true
|
||||
case X509CertThumbprintKey:
|
||||
if h.x509CertThumbprint == nil {
|
||||
return nil, false
|
||||
}
|
||||
return *(h.x509CertThumbprint), true
|
||||
case X509CertThumbprintS256Key:
|
||||
if h.x509CertThumbprintS256 == nil {
|
||||
return nil, false
|
||||
}
|
||||
return *(h.x509CertThumbprintS256), true
|
||||
case X509URLKey:
|
||||
if h.x509URL == nil {
|
||||
return nil, false
|
||||
}
|
||||
return *(h.x509URL), true
|
||||
default:
|
||||
v, ok := h.privateParams[name]
|
||||
return v, ok
|
||||
}
|
||||
}
|
||||
|
||||
func (h *symmetricKey) Set(name string, value interface{}) error {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
return h.setNoLock(name, value)
|
||||
}
|
||||
|
||||
func (h *symmetricKey) setNoLock(name string, value interface{}) error {
|
||||
switch name {
|
||||
case "kty":
|
||||
return nil
|
||||
case AlgorithmKey:
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
h.algorithm = &v
|
||||
case fmt.Stringer:
|
||||
tmp := v.String()
|
||||
h.algorithm = &tmp
|
||||
default:
|
||||
return errors.Errorf(`invalid type for %s key: %T`, AlgorithmKey, value)
|
||||
}
|
||||
return nil
|
||||
case KeyIDKey:
|
||||
if v, ok := value.(string); ok {
|
||||
h.keyID = &v
|
||||
return nil
|
||||
}
|
||||
return errors.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)
|
||||
}
|
||||
h.keyOps = &acceptor
|
||||
return nil
|
||||
case KeyUsageKey:
|
||||
switch v := value.(type) {
|
||||
case KeyUsageType:
|
||||
switch v {
|
||||
case ForSignature, ForEncryption:
|
||||
tmp := v.String()
|
||||
h.keyUsage = &tmp
|
||||
default:
|
||||
return errors.Errorf(`invalid key usage type %s`, v)
|
||||
}
|
||||
case string:
|
||||
h.keyUsage = &v
|
||||
default:
|
||||
return errors.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)
|
||||
case X509CertChainKey:
|
||||
var acceptor CertificateChain
|
||||
if err := acceptor.Accept(value); err != nil {
|
||||
return errors.Wrapf(err, `invalid value for %s key`, X509CertChainKey)
|
||||
}
|
||||
h.x509CertChain = &acceptor
|
||||
return nil
|
||||
case X509CertThumbprintKey:
|
||||
if v, ok := value.(string); ok {
|
||||
h.x509CertThumbprint = &v
|
||||
return nil
|
||||
}
|
||||
return errors.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)
|
||||
case X509URLKey:
|
||||
if v, ok := value.(string); ok {
|
||||
h.x509URL = &v
|
||||
return nil
|
||||
}
|
||||
return errors.Errorf(`invalid value for %s key: %T`, X509URLKey, value)
|
||||
default:
|
||||
if h.privateParams == nil {
|
||||
h.privateParams = map[string]interface{}{}
|
||||
}
|
||||
h.privateParams[name] = value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *symmetricKey) Remove(key string) error {
|
||||
k.mu.Lock()
|
||||
defer k.mu.Unlock()
|
||||
switch key {
|
||||
case AlgorithmKey:
|
||||
k.algorithm = nil
|
||||
case KeyIDKey:
|
||||
k.keyID = nil
|
||||
case KeyOpsKey:
|
||||
k.keyOps = nil
|
||||
case KeyUsageKey:
|
||||
k.keyUsage = nil
|
||||
case SymmetricOctetsKey:
|
||||
k.octets = nil
|
||||
case X509CertChainKey:
|
||||
k.x509CertChain = nil
|
||||
case X509CertThumbprintKey:
|
||||
k.x509CertThumbprint = nil
|
||||
case X509CertThumbprintS256Key:
|
||||
k.x509CertThumbprintS256 = nil
|
||||
case X509URLKey:
|
||||
k.x509URL = nil
|
||||
default:
|
||||
delete(k.privateParams, key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *symmetricKey) Clone() (Key, error) {
|
||||
return cloneKey(k)
|
||||
}
|
||||
|
||||
func (k *symmetricKey) DecodeCtx() json.DecodeCtx {
|
||||
k.mu.RLock()
|
||||
defer k.mu.RUnlock()
|
||||
return k.dc
|
||||
}
|
||||
|
||||
func (k *symmetricKey) SetDecodeCtx(dc json.DecodeCtx) {
|
||||
k.mu.Lock()
|
||||
defer k.mu.Unlock()
|
||||
k.dc = dc
|
||||
}
|
||||
|
||||
func (h *symmetricKey) UnmarshalJSON(buf []byte) error {
|
||||
h.algorithm = nil
|
||||
h.keyID = nil
|
||||
h.keyOps = nil
|
||||
h.keyUsage = nil
|
||||
h.octets = nil
|
||||
h.x509CertChain = nil
|
||||
h.x509CertThumbprint = nil
|
||||
h.x509CertThumbprintS256 = nil
|
||||
h.x509URL = nil
|
||||
dec := json.NewDecoder(bytes.NewReader(buf))
|
||||
LOOP:
|
||||
for {
|
||||
tok, err := dec.Token()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, `error reading token`)
|
||||
}
|
||||
switch tok := tok.(type) {
|
||||
case json.Delim:
|
||||
// Assuming we're doing everything correctly, we should ONLY
|
||||
// get either '{' or '}' here.
|
||||
if tok == '}' { // End of object
|
||||
break LOOP
|
||||
} else if tok != '{' {
|
||||
return errors.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`)
|
||||
}
|
||||
if val != jwa.OctetSeq.String() {
|
||||
return errors.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)
|
||||
}
|
||||
case KeyIDKey:
|
||||
if err := json.AssignNextStringToken(&h.keyID, dec); err != nil {
|
||||
return errors.Wrapf(err, `failed to decode value for key %s`, KeyIDKey)
|
||||
}
|
||||
case KeyOpsKey:
|
||||
var decoded KeyOperationList
|
||||
if err := dec.Decode(&decoded); err != nil {
|
||||
return errors.Wrapf(err, `failed to decode value for key %s`, KeyOpsKey)
|
||||
}
|
||||
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)
|
||||
}
|
||||
case SymmetricOctetsKey:
|
||||
if err := json.AssignNextBytesToken(&h.octets, dec); err != nil {
|
||||
return errors.Wrapf(err, `failed to decode value for key %s`, SymmetricOctetsKey)
|
||||
}
|
||||
case X509CertChainKey:
|
||||
var decoded CertificateChain
|
||||
if err := dec.Decode(&decoded); err != nil {
|
||||
return errors.Wrapf(err, `failed to decode value for key %s`, X509CertChainKey)
|
||||
}
|
||||
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)
|
||||
}
|
||||
case X509CertThumbprintS256Key:
|
||||
if err := json.AssignNextStringToken(&h.x509CertThumbprintS256, dec); err != nil {
|
||||
return errors.Wrapf(err, `failed to decode value for key %s`, X509CertThumbprintS256Key)
|
||||
}
|
||||
case X509URLKey:
|
||||
if err := json.AssignNextStringToken(&h.x509URL, dec); err != nil {
|
||||
return errors.Wrapf(err, `failed to decode value for key %s`, X509URLKey)
|
||||
}
|
||||
default:
|
||||
if dc := h.dc; dc != nil {
|
||||
if localReg := dc.Registry(); localReg != nil {
|
||||
decoded, err := localReg.Decode(dec, tok)
|
||||
if err == nil {
|
||||
h.setNoLock(tok, decoded)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
decoded, err := registry.Decode(dec, tok)
|
||||
if err == nil {
|
||||
h.setNoLock(tok, decoded)
|
||||
continue
|
||||
}
|
||||
return errors.Wrapf(err, `could not decode field %s`, tok)
|
||||
}
|
||||
default:
|
||||
return errors.Errorf(`invalid token %T`, tok)
|
||||
}
|
||||
}
|
||||
if h.octets == nil {
|
||||
return errors.Errorf(`required field k is missing`)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h symmetricKey) MarshalJSON() ([]byte, error) {
|
||||
data := make(map[string]interface{})
|
||||
fields := make([]string, 0, 9)
|
||||
for _, pair := range h.makePairs() {
|
||||
fields = append(fields, pair.Key.(string))
|
||||
data[pair.Key.(string)] = pair.Value
|
||||
}
|
||||
|
||||
sort.Strings(fields)
|
||||
buf := pool.GetBytesBuffer()
|
||||
defer pool.ReleaseBytesBuffer(buf)
|
||||
buf.WriteByte('{')
|
||||
enc := json.NewEncoder(buf)
|
||||
for i, f := range fields {
|
||||
if i > 0 {
|
||||
buf.WriteRune(',')
|
||||
}
|
||||
buf.WriteRune('"')
|
||||
buf.WriteString(f)
|
||||
buf.WriteString(`":`)
|
||||
v := data[f]
|
||||
switch v := v.(type) {
|
||||
case []byte:
|
||||
buf.WriteRune('"')
|
||||
buf.WriteString(base64.EncodeToString(v))
|
||||
buf.WriteRune('"')
|
||||
default:
|
||||
if err := enc.Encode(v); err != nil {
|
||||
return nil, errors.Wrapf(err, `failed to encode value for field %s`, f)
|
||||
}
|
||||
buf.Truncate(buf.Len() - 1)
|
||||
}
|
||||
}
|
||||
buf.WriteByte('}')
|
||||
ret := make([]byte, buf.Len())
|
||||
copy(ret, buf.Bytes())
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (h *symmetricKey) Iterate(ctx context.Context) HeaderIterator {
|
||||
pairs := h.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)
|
||||
}
|
||||
|
||||
func (h *symmetricKey) Walk(ctx context.Context, visitor HeaderVisitor) error {
|
||||
return iter.WalkMap(ctx, h, visitor)
|
||||
}
|
||||
|
||||
func (h *symmetricKey) AsMap(ctx context.Context) (map[string]interface{}, error) {
|
||||
return iter.AsMap(ctx, h)
|
||||
}
|
||||
30
server/vendor/github.com/lestrrat-go/jwx/jwk/usage.go
generated
vendored
Normal file
30
server/vendor/github.com/lestrrat-go/jwx/jwk/usage.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
package jwk
|
||||
|
||||
import "github.com/pkg/errors"
|
||||
|
||||
func (k KeyUsageType) String() string {
|
||||
return string(k)
|
||||
}
|
||||
|
||||
func (k *KeyUsageType) Accept(v interface{}) error {
|
||||
switch v := v.(type) {
|
||||
case KeyUsageType:
|
||||
switch v {
|
||||
case ForSignature, ForEncryption:
|
||||
*k = v
|
||||
return nil
|
||||
default:
|
||||
return errors.Errorf("invalid key usage type %s", v)
|
||||
}
|
||||
case string:
|
||||
switch v {
|
||||
case ForSignature.String(), ForEncryption.String():
|
||||
*k = KeyUsageType(v)
|
||||
return nil
|
||||
default:
|
||||
return errors.Errorf("invalid key usage type %s", v)
|
||||
}
|
||||
}
|
||||
|
||||
return errors.Errorf("invalid value for key usage type %s", v)
|
||||
}
|
||||
69
server/vendor/github.com/lestrrat-go/jwx/jwk/whitelist.go
generated
vendored
Normal file
69
server/vendor/github.com/lestrrat-go/jwx/jwk/whitelist.go
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
package jwk
|
||||
|
||||
import "regexp"
|
||||
|
||||
// InsecureWhitelist allows any URLs to be fetched. This is the default
|
||||
// behavior of `jwk.Fetch()`, but this exists to allow other libraries
|
||||
// (such as jws, via jws.VerifyAuto) and users to be able to explicitly
|
||||
// state that they intend to not check the URLs that are being fetched
|
||||
type InsecureWhitelist struct{}
|
||||
|
||||
func (InsecureWhitelist) IsAllowed(string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// RegexpWhitelist is a jwk.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 jwk.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 jwk.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)
|
||||
}
|
||||
111
server/vendor/github.com/lestrrat-go/jwx/jws/README.md
generated
vendored
Normal file
111
server/vendor/github.com/lestrrat-go/jwx/jws/README.md
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
# JWS [](https://pkg.go.dev/github.com/lestrrat-go/jwx/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)
|
||||
* Add arbitrary fields in the JWS object
|
||||
* Ability to add/replace existing signature methods
|
||||
* Respect "b64" settings for RFC7797
|
||||
|
||||
How-to style documentation can be found in the [docs directory](../docs).
|
||||
|
||||
Examples are located in the examples directory ([jws_example_test.go](../examples/jws_example_test.go))
|
||||
|
||||
Supported signature algorithms:
|
||||
|
||||
| Algorithm | Supported? | Constant in [jwa](../jwa) |
|
||||
|:----------------------------------------|:-----------|:-------------------------|
|
||||
| HMAC using SHA-256 | YES | jwa.HS256 |
|
||||
| HMAC using SHA-384 | YES | jwa.HS384 |
|
||||
| HMAC using SHA-512 | YES | jwa.HS512 |
|
||||
| RSASSA-PKCS-v1.5 using SHA-256 | YES | jwa.RS256 |
|
||||
| RSASSA-PKCS-v1.5 using SHA-384 | YES | jwa.RS384 |
|
||||
| RSASSA-PKCS-v1.5 using SHA-512 | YES | jwa.RS512 |
|
||||
| ECDSA using P-256 and SHA-256 | YES | jwa.ES256 |
|
||||
| ECDSA using P-384 and SHA-384 | YES | jwa.ES384 |
|
||||
| ECDSA using P-521 and SHA-512 | YES | jwa.ES512 |
|
||||
| ECDSA using secp256k1 and SHA-256 (2) | YES | jwa.ES256K |
|
||||
| RSASSA-PSS using SHA256 and MGF1-SHA256 | YES | jwa.PS256 |
|
||||
| RSASSA-PSS using SHA384 and MGF1-SHA384 | YES | jwa.PS384 |
|
||||
| RSASSA-PSS using SHA512 and MGF1-SHA512 | YES | jwa.PS512 |
|
||||
| EdDSA (1) | YES | jwa.EdDSA |
|
||||
|
||||
* Note 1: Experimental
|
||||
* Note 2: Experimental, and must be toggled using `-tags jwx_es256k` build tag
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
## Sign and verify arbitrary data
|
||||
|
||||
```go
|
||||
import(
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"log"
|
||||
|
||||
"github.com/lestrrat-go/jwx/jwa"
|
||||
"github.com/lestrrat-go/jwx/jws"
|
||||
)
|
||||
|
||||
func main() {
|
||||
privkey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
log.Printf("failed to generate private key: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
buf, err := jws.Sign([]byte("Lorem ipsum"), jwa.RS256, privkey)
|
||||
if err != nil {
|
||||
log.Printf("failed to created JWS message: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
log.Printf("failed to verify message: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("signed message verified! -> %s", verified)
|
||||
}
|
||||
```
|
||||
|
||||
## Programatically manipulate `jws.Message`
|
||||
|
||||
```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
|
||||
|
||||
// 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
|
||||
}
|
||||
```
|
||||
|
||||
194
server/vendor/github.com/lestrrat-go/jwx/jws/ecdsa.go
generated
vendored
Normal file
194
server/vendor/github.com/lestrrat-go/jwx/jws/ecdsa.go
generated
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
package jws
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"encoding/asn1"
|
||||
"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"
|
||||
)
|
||||
|
||||
var ecdsaSigners map[jwa.SignatureAlgorithm]*ecdsaSigner
|
||||
var ecdsaVerifiers map[jwa.SignatureAlgorithm]*ecdsaVerifier
|
||||
|
||||
func init() {
|
||||
algs := map[jwa.SignatureAlgorithm]crypto.Hash{
|
||||
jwa.ES256: crypto.SHA256,
|
||||
jwa.ES384: crypto.SHA384,
|
||||
jwa.ES512: crypto.SHA512,
|
||||
jwa.ES256K: crypto.SHA256,
|
||||
}
|
||||
ecdsaSigners = make(map[jwa.SignatureAlgorithm]*ecdsaSigner)
|
||||
ecdsaVerifiers = make(map[jwa.SignatureAlgorithm]*ecdsaVerifier)
|
||||
|
||||
for alg, hash := range algs {
|
||||
ecdsaSigners[alg] = &ecdsaSigner{
|
||||
alg: alg,
|
||||
hash: hash,
|
||||
}
|
||||
ecdsaVerifiers[alg] = &ecdsaVerifier{
|
||||
alg: alg,
|
||||
hash: hash,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newECDSASigner(alg jwa.SignatureAlgorithm) Signer {
|
||||
return ecdsaSigners[alg]
|
||||
}
|
||||
|
||||
// ecdsaSigners are immutable.
|
||||
type ecdsaSigner struct {
|
||||
alg jwa.SignatureAlgorithm
|
||||
hash crypto.Hash
|
||||
}
|
||||
|
||||
func (es ecdsaSigner) Algorithm() jwa.SignatureAlgorithm {
|
||||
return es.alg
|
||||
}
|
||||
|
||||
func (es *ecdsaSigner) Sign(payload []byte, key interface{}) ([]byte, error) {
|
||||
if key == nil {
|
||||
return nil, errors.New(`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")
|
||||
}
|
||||
|
||||
signer, ok := key.(crypto.Signer)
|
||||
if ok {
|
||||
switch key.(type) {
|
||||
case ecdsa.PrivateKey, *ecdsa.PrivateKey:
|
||||
// if it's a ecdsa.PrivateKey, it's more efficient to
|
||||
// go through the non-crypto.Signer route. Set ok to false
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
|
||||
var r, s *big.Int
|
||||
var curveBits int
|
||||
if ok {
|
||||
signed, err := signer.Sign(rand.Reader, h.Sum(nil), es.hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var p struct {
|
||||
R *big.Int
|
||||
S *big.Int
|
||||
}
|
||||
if _, err := asn1.Unmarshal(signed, &p); err != nil {
|
||||
return nil, errors.Wrap(err, `failed to unmarshal ASN1 encoded signature`)
|
||||
}
|
||||
|
||||
// Okay, this is silly, but hear me out. When we use the
|
||||
// crypto.Signer interface, the PrivateKey is hidden.
|
||||
// But we need some information about the key (it's bit size).
|
||||
//
|
||||
// So while silly, we're going to have to make another call
|
||||
// here and fetch the Public key.
|
||||
// This probably means that this should be cached some where.
|
||||
cpub := signer.Public()
|
||||
pubkey, ok := cpub.(*ecdsa.PublicKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(`expected *ecdsa.PublicKey, got %T`, pubkey)
|
||||
}
|
||||
curveBits = pubkey.Curve.Params().BitSize
|
||||
|
||||
r = p.R
|
||||
s = p.S
|
||||
} 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)
|
||||
}
|
||||
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")
|
||||
}
|
||||
r = rtmp
|
||||
s = stmp
|
||||
}
|
||||
|
||||
keyBytes := curveBits / 8
|
||||
// Curve bits do not need to be a multiple of 8.
|
||||
if curveBits%8 > 0 {
|
||||
keyBytes++
|
||||
}
|
||||
|
||||
rBytes := r.Bytes()
|
||||
rBytesPadded := make([]byte, keyBytes)
|
||||
copy(rBytesPadded[keyBytes-len(rBytes):], rBytes)
|
||||
|
||||
sBytes := s.Bytes()
|
||||
sBytesPadded := make([]byte, keyBytes)
|
||||
copy(sBytesPadded[keyBytes-len(sBytes):], sBytes)
|
||||
|
||||
out := append(rBytesPadded, sBytesPadded...)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ecdsaVerifiers are immutable.
|
||||
type ecdsaVerifier struct {
|
||||
alg jwa.SignatureAlgorithm
|
||||
hash crypto.Hash
|
||||
}
|
||||
|
||||
func newECDSAVerifier(alg jwa.SignatureAlgorithm) Verifier {
|
||||
return ecdsaVerifiers[alg]
|
||||
}
|
||||
|
||||
func (v ecdsaVerifier) Algorithm() jwa.SignatureAlgorithm {
|
||||
return v.alg
|
||||
}
|
||||
|
||||
func (v *ecdsaVerifier) Verify(payload []byte, signature []byte, key interface{}) error {
|
||||
if key == nil {
|
||||
return errors.New(`missing public key while verifying payload`)
|
||||
}
|
||||
|
||||
var pubkey ecdsa.PublicKey
|
||||
if cs, ok := key.(crypto.Signer); ok {
|
||||
cpub := cs.Public()
|
||||
switch cpub := cpub.(type) {
|
||||
case ecdsa.PublicKey:
|
||||
pubkey = cpub
|
||||
case *ecdsa.PublicKey:
|
||||
pubkey = *cpub
|
||||
default:
|
||||
return errors.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)
|
||||
}
|
||||
}
|
||||
|
||||
r := pool.GetBigInt()
|
||||
s := pool.GetBigInt()
|
||||
defer pool.ReleaseBigInt(r)
|
||||
defer pool.ReleaseBigInt(s)
|
||||
|
||||
n := len(signature) / 2
|
||||
r.SetBytes(signature[:n])
|
||||
s.SetBytes(signature[n:])
|
||||
|
||||
h := v.hash.New()
|
||||
if _, err := h.Write(payload); err != nil {
|
||||
return errors.Wrap(err, "failed to write payload using ecdsa")
|
||||
}
|
||||
|
||||
if !ecdsa.Verify(&pubkey, h.Sum(nil), r, s) {
|
||||
return errors.New(`failed to verify signature using ecdsa`)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
73
server/vendor/github.com/lestrrat-go/jwx/jws/eddsa.go
generated
vendored
Normal file
73
server/vendor/github.com/lestrrat-go/jwx/jws/eddsa.go
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
package jws
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
|
||||
"github.com/lestrrat-go/jwx/internal/keyconv"
|
||||
"github.com/lestrrat-go/jwx/jwa"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type eddsaSigner struct{}
|
||||
|
||||
func newEdDSASigner() Signer {
|
||||
return &eddsaSigner{}
|
||||
}
|
||||
|
||||
func (s eddsaSigner) Algorithm() jwa.SignatureAlgorithm {
|
||||
return jwa.EdDSA
|
||||
}
|
||||
|
||||
func (s eddsaSigner) Sign(payload []byte, key interface{}) ([]byte, error) {
|
||||
if key == nil {
|
||||
return nil, errors.New(`missing private key while signing payload`)
|
||||
}
|
||||
|
||||
// The ed25519.PrivateKey object implements crypto.Signer, so we should
|
||||
// simply accept a crypto.Signer here.
|
||||
signer, ok := key.(crypto.Signer)
|
||||
if !ok {
|
||||
// This fallback exists for cases when jwk.Key was passed, or
|
||||
// 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)
|
||||
}
|
||||
signer = privkey
|
||||
}
|
||||
return signer.Sign(rand.Reader, payload, crypto.Hash(0))
|
||||
}
|
||||
|
||||
type eddsaVerifier struct{}
|
||||
|
||||
func newEdDSAVerifier() Verifier {
|
||||
return &eddsaVerifier{}
|
||||
}
|
||||
|
||||
func (v eddsaVerifier) Verify(payload, signature []byte, key interface{}) (err error) {
|
||||
if key == nil {
|
||||
return errors.New(`missing public key while verifying payload`)
|
||||
}
|
||||
|
||||
var pubkey ed25519.PublicKey
|
||||
signer, ok := key.(crypto.Signer)
|
||||
if ok {
|
||||
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)
|
||||
}
|
||||
} else {
|
||||
if err := keyconv.Ed25519PublicKey(&pubkey, key); err != nil {
|
||||
return errors.Wrapf(err, `failed to retrieve ed25519.PublicKey out of %T`, key)
|
||||
}
|
||||
}
|
||||
|
||||
if !ed25519.Verify(pubkey, payload, signature) {
|
||||
return errors.New(`failed to match EdDSA signature`)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
12
server/vendor/github.com/lestrrat-go/jwx/jws/es256k.go
generated
vendored
Normal file
12
server/vendor/github.com/lestrrat-go/jwx/jws/es256k.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
//go:build jwx_es256k
|
||||
// +build jwx_es256k
|
||||
|
||||
package jws
|
||||
|
||||
import (
|
||||
"github.com/lestrrat-go/jwx/jwa"
|
||||
)
|
||||
|
||||
func init() {
|
||||
addAlgorithmForKeyType(jwa.EC, jwa.ES256K)
|
||||
}
|
||||
14
server/vendor/github.com/lestrrat-go/jwx/jws/gen.sh
generated
vendored
Normal file
14
server/vendor/github.com/lestrrat-go/jwx/jws/gen.sh
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
#!/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
|
||||
71
server/vendor/github.com/lestrrat-go/jwx/jws/headers.go
generated
vendored
Normal file
71
server/vendor/github.com/lestrrat-go/jwx/jws/headers.go
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
package jws
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/lestrrat-go/iter/mapiter"
|
||||
"github.com/lestrrat-go/jwx/internal/iter"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Iterate returns a channel that successively returns all the
|
||||
// header name and values.
|
||||
func (h *stdHeaders) Iterate(ctx context.Context) Iterator {
|
||||
pairs := h.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)
|
||||
}
|
||||
|
||||
func (h *stdHeaders) Walk(ctx context.Context, visitor Visitor) error {
|
||||
return iter.WalkMap(ctx, h, visitor)
|
||||
}
|
||||
|
||||
func (h *stdHeaders) AsMap(ctx context.Context) (map[string]interface{}, error) {
|
||||
return iter.AsMap(ctx, h)
|
||||
}
|
||||
|
||||
func (h *stdHeaders) Copy(_ context.Context, dst Headers) error {
|
||||
for _, pair := range h.makePairs() {
|
||||
//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 nil
|
||||
}
|
||||
|
||||
// mergeHeaders merges two headers, and works even if the first Header
|
||||
// object is nil. This is not exported because ATM it felt like this
|
||||
// function is not frequently used, and MergeHeaders seemed a clunky name
|
||||
func mergeHeaders(ctx context.Context, h1, h2 Headers) (Headers, error) {
|
||||
h3 := NewHeaders()
|
||||
|
||||
if h1 != nil {
|
||||
if err := h1.Copy(ctx, h3); err != nil {
|
||||
return nil, errors.Wrap(err, `failed to copy headers from first Header`)
|
||||
}
|
||||
}
|
||||
|
||||
if h2 != nil {
|
||||
if err := h2.Copy(ctx, h3); err != nil {
|
||||
return nil, errors.Wrap(err, `failed to copy headers from second Header`)
|
||||
}
|
||||
}
|
||||
|
||||
return h3, nil
|
||||
}
|
||||
|
||||
func (h *stdHeaders) Merge(ctx context.Context, h2 Headers) (Headers, error) {
|
||||
return mergeHeaders(ctx, h, h2)
|
||||
}
|
||||
561
server/vendor/github.com/lestrrat-go/jwx/jws/headers_gen.go
generated
vendored
Normal file
561
server/vendor/github.com/lestrrat-go/jwx/jws/headers_gen.go
generated
vendored
Normal file
@@ -0,0 +1,561 @@
|
||||
// This file is auto-generated by jws/internal/cmd/genheaders/main.go. DO NOT EDIT
|
||||
|
||||
package jws
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"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"
|
||||
)
|
||||
|
||||
const (
|
||||
AlgorithmKey = "alg"
|
||||
ContentTypeKey = "cty"
|
||||
CriticalKey = "crit"
|
||||
JWKKey = "jwk"
|
||||
JWKSetURLKey = "jku"
|
||||
KeyIDKey = "kid"
|
||||
TypeKey = "typ"
|
||||
X509CertChainKey = "x5c"
|
||||
X509CertThumbprintKey = "x5t"
|
||||
X509CertThumbprintS256Key = "x5t#S256"
|
||||
X509URLKey = "x5u"
|
||||
)
|
||||
|
||||
// Headers describe a standard Header set.
|
||||
type Headers interface {
|
||||
json.Marshaler
|
||||
json.Unmarshaler
|
||||
Algorithm() jwa.SignatureAlgorithm
|
||||
ContentType() string
|
||||
Critical() []string
|
||||
JWK() jwk.Key
|
||||
JWKSetURL() string
|
||||
KeyID() string
|
||||
Type() string
|
||||
X509CertChain() []string
|
||||
X509CertThumbprint() string
|
||||
X509CertThumbprintS256() string
|
||||
X509URL() string
|
||||
Iterate(ctx context.Context) Iterator
|
||||
Walk(context.Context, Visitor) error
|
||||
AsMap(context.Context) (map[string]interface{}, error)
|
||||
Copy(context.Context, Headers) error
|
||||
Merge(context.Context, Headers) (Headers, error)
|
||||
Get(string) (interface{}, bool)
|
||||
Set(string, interface{}) error
|
||||
Remove(string) error
|
||||
|
||||
// PrivateParams returns the non-standard elements in the source structure
|
||||
// WARNING: DO NOT USE PrivateParams() IF YOU HAVE CONCURRENT CODE ACCESSING THEM.
|
||||
// Use AsMap() to get a copy of the entire header instead
|
||||
PrivateParams() map[string]interface{}
|
||||
}
|
||||
|
||||
type stdHeaders struct {
|
||||
algorithm *jwa.SignatureAlgorithm // https://tools.ietf.org/html/rfc7515#section-4.1.1
|
||||
contentType *string // https://tools.ietf.org/html/rfc7515#section-4.1.10
|
||||
critical []string // https://tools.ietf.org/html/rfc7515#section-4.1.11
|
||||
jwk jwk.Key // https://tools.ietf.org/html/rfc7515#section-4.1.3
|
||||
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
|
||||
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 DecodeCtx
|
||||
raw []byte // stores the raw version of the header so it can be used later
|
||||
}
|
||||
|
||||
func NewHeaders() Headers {
|
||||
return &stdHeaders{
|
||||
mu: &sync.RWMutex{},
|
||||
}
|
||||
}
|
||||
|
||||
func (h *stdHeaders) Algorithm() jwa.SignatureAlgorithm {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.algorithm == nil {
|
||||
return ""
|
||||
}
|
||||
return *(h.algorithm)
|
||||
}
|
||||
|
||||
func (h *stdHeaders) ContentType() string {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.contentType == nil {
|
||||
return ""
|
||||
}
|
||||
return *(h.contentType)
|
||||
}
|
||||
|
||||
func (h *stdHeaders) Critical() []string {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
return h.critical
|
||||
}
|
||||
|
||||
func (h *stdHeaders) JWK() jwk.Key {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
return h.jwk
|
||||
}
|
||||
|
||||
func (h *stdHeaders) JWKSetURL() string {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.jwkSetURL == nil {
|
||||
return ""
|
||||
}
|
||||
return *(h.jwkSetURL)
|
||||
}
|
||||
|
||||
func (h *stdHeaders) KeyID() string {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.keyID == nil {
|
||||
return ""
|
||||
}
|
||||
return *(h.keyID)
|
||||
}
|
||||
|
||||
func (h *stdHeaders) Type() string {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.typ == nil {
|
||||
return ""
|
||||
}
|
||||
return *(h.typ)
|
||||
}
|
||||
|
||||
func (h *stdHeaders) X509CertChain() []string {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
return h.x509CertChain
|
||||
}
|
||||
|
||||
func (h *stdHeaders) X509CertThumbprint() string {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.x509CertThumbprint == nil {
|
||||
return ""
|
||||
}
|
||||
return *(h.x509CertThumbprint)
|
||||
}
|
||||
|
||||
func (h *stdHeaders) X509CertThumbprintS256() string {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.x509CertThumbprintS256 == nil {
|
||||
return ""
|
||||
}
|
||||
return *(h.x509CertThumbprintS256)
|
||||
}
|
||||
|
||||
func (h *stdHeaders) X509URL() string {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.x509URL == nil {
|
||||
return ""
|
||||
}
|
||||
return *(h.x509URL)
|
||||
}
|
||||
|
||||
func (h *stdHeaders) DecodeCtx() DecodeCtx {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
return h.dc
|
||||
}
|
||||
|
||||
func (h *stdHeaders) SetDecodeCtx(dc DecodeCtx) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
h.dc = dc
|
||||
}
|
||||
|
||||
func (h *stdHeaders) rawBuffer() []byte {
|
||||
return h.raw
|
||||
}
|
||||
|
||||
func (h *stdHeaders) makePairs() []*HeaderPair {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
var pairs []*HeaderPair
|
||||
if h.algorithm != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: AlgorithmKey, Value: *(h.algorithm)})
|
||||
}
|
||||
if h.contentType != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: ContentTypeKey, Value: *(h.contentType)})
|
||||
}
|
||||
if h.critical != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: CriticalKey, Value: h.critical})
|
||||
}
|
||||
if h.jwk != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: JWKKey, Value: h.jwk})
|
||||
}
|
||||
if h.jwkSetURL != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: JWKSetURLKey, Value: *(h.jwkSetURL)})
|
||||
}
|
||||
if h.keyID != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: KeyIDKey, Value: *(h.keyID)})
|
||||
}
|
||||
if h.typ != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: TypeKey, Value: *(h.typ)})
|
||||
}
|
||||
if h.x509CertChain != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: X509CertChainKey, Value: h.x509CertChain})
|
||||
}
|
||||
if h.x509CertThumbprint != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: X509CertThumbprintKey, Value: *(h.x509CertThumbprint)})
|
||||
}
|
||||
if h.x509CertThumbprintS256 != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: X509CertThumbprintS256Key, Value: *(h.x509CertThumbprintS256)})
|
||||
}
|
||||
if h.x509URL != nil {
|
||||
pairs = append(pairs, &HeaderPair{Key: X509URLKey, Value: *(h.x509URL)})
|
||||
}
|
||||
for k, v := range h.privateParams {
|
||||
pairs = append(pairs, &HeaderPair{Key: k, Value: v})
|
||||
}
|
||||
sort.Slice(pairs, func(i, j int) bool {
|
||||
return pairs[i].Key.(string) < pairs[j].Key.(string)
|
||||
})
|
||||
return pairs
|
||||
}
|
||||
|
||||
func (h *stdHeaders) PrivateParams() map[string]interface{} {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
return h.privateParams
|
||||
}
|
||||
|
||||
func (h *stdHeaders) Get(name string) (interface{}, bool) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
switch name {
|
||||
case AlgorithmKey:
|
||||
if h.algorithm == nil {
|
||||
return nil, false
|
||||
}
|
||||
return *(h.algorithm), true
|
||||
case ContentTypeKey:
|
||||
if h.contentType == nil {
|
||||
return nil, false
|
||||
}
|
||||
return *(h.contentType), true
|
||||
case CriticalKey:
|
||||
if h.critical == nil {
|
||||
return nil, false
|
||||
}
|
||||
return h.critical, true
|
||||
case JWKKey:
|
||||
if h.jwk == nil {
|
||||
return nil, false
|
||||
}
|
||||
return h.jwk, true
|
||||
case JWKSetURLKey:
|
||||
if h.jwkSetURL == nil {
|
||||
return nil, false
|
||||
}
|
||||
return *(h.jwkSetURL), true
|
||||
case KeyIDKey:
|
||||
if h.keyID == nil {
|
||||
return nil, false
|
||||
}
|
||||
return *(h.keyID), true
|
||||
case TypeKey:
|
||||
if h.typ == nil {
|
||||
return nil, false
|
||||
}
|
||||
return *(h.typ), true
|
||||
case X509CertChainKey:
|
||||
if h.x509CertChain == nil {
|
||||
return nil, false
|
||||
}
|
||||
return h.x509CertChain, true
|
||||
case X509CertThumbprintKey:
|
||||
if h.x509CertThumbprint == nil {
|
||||
return nil, false
|
||||
}
|
||||
return *(h.x509CertThumbprint), true
|
||||
case X509CertThumbprintS256Key:
|
||||
if h.x509CertThumbprintS256 == nil {
|
||||
return nil, false
|
||||
}
|
||||
return *(h.x509CertThumbprintS256), true
|
||||
case X509URLKey:
|
||||
if h.x509URL == nil {
|
||||
return nil, false
|
||||
}
|
||||
return *(h.x509URL), true
|
||||
default:
|
||||
v, ok := h.privateParams[name]
|
||||
return v, ok
|
||||
}
|
||||
}
|
||||
|
||||
func (h *stdHeaders) Set(name string, value interface{}) error {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
return h.setNoLock(name, value)
|
||||
}
|
||||
|
||||
func (h *stdHeaders) setNoLock(name string, value interface{}) error {
|
||||
switch name {
|
||||
case AlgorithmKey:
|
||||
var acceptor jwa.SignatureAlgorithm
|
||||
if err := acceptor.Accept(value); err != nil {
|
||||
return errors.Wrapf(err, `invalid value for %s key`, AlgorithmKey)
|
||||
}
|
||||
h.algorithm = &acceptor
|
||||
return nil
|
||||
case ContentTypeKey:
|
||||
if v, ok := value.(string); ok {
|
||||
h.contentType = &v
|
||||
return nil
|
||||
}
|
||||
return errors.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)
|
||||
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)
|
||||
case JWKSetURLKey:
|
||||
if v, ok := value.(string); ok {
|
||||
h.jwkSetURL = &v
|
||||
return nil
|
||||
}
|
||||
return errors.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)
|
||||
case TypeKey:
|
||||
if v, ok := value.(string); ok {
|
||||
h.typ = &v
|
||||
return nil
|
||||
}
|
||||
return errors.Errorf(`invalid value for %s key: %T`, TypeKey, value)
|
||||
case X509CertChainKey:
|
||||
if v, ok := value.([]string); ok {
|
||||
h.x509CertChain = v
|
||||
return nil
|
||||
}
|
||||
return errors.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)
|
||||
case X509CertThumbprintS256Key:
|
||||
if v, ok := value.(string); ok {
|
||||
h.x509CertThumbprintS256 = &v
|
||||
return nil
|
||||
}
|
||||
return errors.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)
|
||||
default:
|
||||
if h.privateParams == nil {
|
||||
h.privateParams = map[string]interface{}{}
|
||||
}
|
||||
h.privateParams[name] = value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *stdHeaders) Remove(key string) error {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
switch key {
|
||||
case AlgorithmKey:
|
||||
h.algorithm = nil
|
||||
case ContentTypeKey:
|
||||
h.contentType = nil
|
||||
case CriticalKey:
|
||||
h.critical = nil
|
||||
case JWKKey:
|
||||
h.jwk = nil
|
||||
case JWKSetURLKey:
|
||||
h.jwkSetURL = nil
|
||||
case KeyIDKey:
|
||||
h.keyID = nil
|
||||
case TypeKey:
|
||||
h.typ = nil
|
||||
case X509CertChainKey:
|
||||
h.x509CertChain = nil
|
||||
case X509CertThumbprintKey:
|
||||
h.x509CertThumbprint = nil
|
||||
case X509CertThumbprintS256Key:
|
||||
h.x509CertThumbprintS256 = nil
|
||||
case X509URLKey:
|
||||
h.x509URL = nil
|
||||
default:
|
||||
delete(h.privateParams, key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
dec := json.NewDecoder(bytes.NewReader(buf))
|
||||
LOOP:
|
||||
for {
|
||||
tok, err := dec.Token()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, `error reading token`)
|
||||
}
|
||||
switch tok := tok.(type) {
|
||||
case json.Delim:
|
||||
// Assuming we're doing everything correctly, we should ONLY
|
||||
// get either '{' or '}' here.
|
||||
if tok == '}' { // End of object
|
||||
break LOOP
|
||||
} else if tok != '{' {
|
||||
return errors.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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
case CriticalKey:
|
||||
var decoded []string
|
||||
if err := dec.Decode(&decoded); err != nil {
|
||||
return errors.Wrapf(err, `failed to decode value for key %s`, CriticalKey)
|
||||
}
|
||||
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)
|
||||
}
|
||||
key, err := jwk.ParseKey(buf)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, `failed to parse JWK for key %s`, JWKKey)
|
||||
}
|
||||
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)
|
||||
}
|
||||
case KeyIDKey:
|
||||
if err := json.AssignNextStringToken(&h.keyID, dec); err != nil {
|
||||
return errors.Wrapf(err, `failed to decode value for key %s`, KeyIDKey)
|
||||
}
|
||||
case TypeKey:
|
||||
if err := json.AssignNextStringToken(&h.typ, dec); err != nil {
|
||||
return errors.Wrapf(err, `failed to decode value for key %s`, TypeKey)
|
||||
}
|
||||
case X509CertChainKey:
|
||||
var decoded []string
|
||||
if err := dec.Decode(&decoded); err != nil {
|
||||
return errors.Wrapf(err, `failed to decode value for key %s`, X509CertChainKey)
|
||||
}
|
||||
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)
|
||||
}
|
||||
case X509CertThumbprintS256Key:
|
||||
if err := json.AssignNextStringToken(&h.x509CertThumbprintS256, dec); err != nil {
|
||||
return errors.Wrapf(err, `failed to decode value for key %s`, X509CertThumbprintS256Key)
|
||||
}
|
||||
case X509URLKey:
|
||||
if err := json.AssignNextStringToken(&h.x509URL, dec); err != nil {
|
||||
return errors.Wrapf(err, `failed to decode value for key %s`, X509URLKey)
|
||||
}
|
||||
default:
|
||||
decoded, err := registry.Decode(dec, tok)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
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 nil
|
||||
}
|
||||
|
||||
func (h stdHeaders) MarshalJSON() ([]byte, error) {
|
||||
buf := pool.GetBytesBuffer()
|
||||
defer pool.ReleaseBytesBuffer(buf)
|
||||
buf.WriteByte('{')
|
||||
enc := json.NewEncoder(buf)
|
||||
for i, p := range h.makePairs() {
|
||||
if i > 0 {
|
||||
buf.WriteRune(',')
|
||||
}
|
||||
buf.WriteRune('"')
|
||||
buf.WriteString(p.Key.(string))
|
||||
buf.WriteString(`":`)
|
||||
v := p.Value
|
||||
switch v := v.(type) {
|
||||
case []byte:
|
||||
buf.WriteRune('"')
|
||||
buf.WriteString(base64.EncodeToString(v))
|
||||
buf.WriteRune('"')
|
||||
default:
|
||||
if err := enc.Encode(v); err != nil {
|
||||
errors.Errorf(`failed to encode value for field %s`, p.Key)
|
||||
}
|
||||
buf.Truncate(buf.Len() - 1)
|
||||
}
|
||||
}
|
||||
buf.WriteByte('}')
|
||||
ret := make([]byte, buf.Len())
|
||||
copy(ret, buf.Bytes())
|
||||
return ret, nil
|
||||
}
|
||||
77
server/vendor/github.com/lestrrat-go/jwx/jws/hmac.go
generated
vendored
Normal file
77
server/vendor/github.com/lestrrat-go/jwx/jws/hmac.go
generated
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
package jws
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"hash"
|
||||
|
||||
"github.com/lestrrat-go/jwx/internal/keyconv"
|
||||
"github.com/lestrrat-go/jwx/jwa"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var hmacSignFuncs = map[jwa.SignatureAlgorithm]hmacSignFunc{}
|
||||
|
||||
func init() {
|
||||
algs := map[jwa.SignatureAlgorithm]func() hash.Hash{
|
||||
jwa.HS256: sha256.New,
|
||||
jwa.HS384: sha512.New384,
|
||||
jwa.HS512: sha512.New,
|
||||
}
|
||||
|
||||
for alg, h := range algs {
|
||||
hmacSignFuncs[alg] = makeHMACSignFunc(h)
|
||||
}
|
||||
}
|
||||
|
||||
func newHMACSigner(alg jwa.SignatureAlgorithm) Signer {
|
||||
return &HMACSigner{
|
||||
alg: alg,
|
||||
sign: hmacSignFuncs[alg], // we know this will succeed
|
||||
}
|
||||
}
|
||||
|
||||
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 h.Sum(nil), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s HMACSigner) Algorithm() jwa.SignatureAlgorithm {
|
||||
return s.alg
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
if len(hmackey) == 0 {
|
||||
return nil, errors.New(`missing key while signing payload`)
|
||||
}
|
||||
|
||||
return s.sign(payload, hmackey)
|
||||
}
|
||||
|
||||
func newHMACVerifier(alg jwa.SignatureAlgorithm) Verifier {
|
||||
s := newHMACSigner(alg)
|
||||
return &HMACVerifier{signer: s}
|
||||
}
|
||||
|
||||
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`)
|
||||
}
|
||||
|
||||
if !hmac.Equal(signature, expected) {
|
||||
return errors.New(`failed to match hmac signature`)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
106
server/vendor/github.com/lestrrat-go/jwx/jws/interface.go
generated
vendored
Normal file
106
server/vendor/github.com/lestrrat-go/jwx/jws/interface.go
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
package jws
|
||||
|
||||
import (
|
||||
"github.com/lestrrat-go/iter/mapiter"
|
||||
"github.com/lestrrat-go/jwx/internal/iter"
|
||||
"github.com/lestrrat-go/jwx/jwa"
|
||||
)
|
||||
|
||||
type DecodeCtx interface {
|
||||
CollectRaw() bool
|
||||
}
|
||||
|
||||
// Message represents a full JWS encoded message. Flattened serialization
|
||||
// is not supported as a struct, but rather it's represented as a
|
||||
// Message struct with only one `signature` element.
|
||||
//
|
||||
// Do not expect to use the Message object to verify or construct a
|
||||
// signed payload with. You should only use this when you want to actually
|
||||
// programmatically view the contents of the full JWS payload.
|
||||
//
|
||||
// As of this version, there is one big incompatibility when using Message
|
||||
// objects to convert between compact and JSON representations.
|
||||
// The protected header is sometimes encoded differently from the original
|
||||
// message and the JSON serialization that we use in Go.
|
||||
//
|
||||
// For example, the protected header `eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9`
|
||||
// decodes to
|
||||
//
|
||||
// {"typ":"JWT",
|
||||
// "alg":"HS256"}
|
||||
//
|
||||
// However, when we parse this into a message, we create a jws.Header object,
|
||||
// which, when we marshal into a JSON object again, becomes
|
||||
//
|
||||
// {"typ":"JWT","alg":"HS256"}
|
||||
//
|
||||
// Notice that serialization lacks a line break and a space between `"JWT",`
|
||||
// and `"alg"`. This causes a problem when verifying the signatures AFTER
|
||||
// a compact JWS message has been unmarshaled into a jws.Message.
|
||||
//
|
||||
// jws.Verify() doesn't go through this step, and therefore this does not
|
||||
// manifest itself. However, you may see this discrepancy when you manually
|
||||
// go through these conversions, and/or use the `jwx` tool like so:
|
||||
//
|
||||
// jwx jws parse message.jws | jwx jws verify --key somekey.jwk --stdin
|
||||
//
|
||||
// In this scenario, the first `jwx jws parse` outputs a parsed jws.Message
|
||||
// which is marshaled into JSON. At this point the message's protected
|
||||
// headers and the signatures don't match.
|
||||
//
|
||||
// To sign and verify, use the appropriate `Sign()` and `Verify()` functions.
|
||||
type Message struct {
|
||||
dc DecodeCtx
|
||||
payload []byte
|
||||
signatures []*Signature
|
||||
b64 bool // true if payload should be base64 encoded
|
||||
}
|
||||
|
||||
type Signature struct {
|
||||
dc DecodeCtx
|
||||
headers Headers // Unprotected Headers
|
||||
protected Headers // Protected Headers
|
||||
signature []byte // Signature
|
||||
detached bool
|
||||
}
|
||||
|
||||
type Visitor = iter.MapVisitor
|
||||
type VisitorFunc = iter.MapVisitorFunc
|
||||
type HeaderPair = mapiter.Pair
|
||||
type Iterator = mapiter.Iterator
|
||||
|
||||
// Signer generates the signature for a given payload.
|
||||
type Signer interface {
|
||||
// Sign creates a signature for the given payload.
|
||||
// The scond argument is the key used for signing the payload, and is usually
|
||||
// the private key type associated with the signature method. For example,
|
||||
// for `jwa.RSXXX` and `jwa.PSXXX` types, you need to pass the
|
||||
// `*"crypto/rsa".PrivateKey` type.
|
||||
// Check the documentation for each signer for details
|
||||
Sign([]byte, interface{}) ([]byte, error)
|
||||
|
||||
Algorithm() jwa.SignatureAlgorithm
|
||||
}
|
||||
|
||||
type hmacSignFunc func([]byte, []byte) ([]byte, error)
|
||||
|
||||
// HMACSigner uses crypto/hmac to sign the payloads.
|
||||
type HMACSigner struct {
|
||||
alg jwa.SignatureAlgorithm
|
||||
sign hmacSignFunc
|
||||
}
|
||||
|
||||
type Verifier interface {
|
||||
// Verify checks whether the payload and signature are valid for
|
||||
// the given key.
|
||||
// `key` is the key used for verifying the payload, and is usually
|
||||
// the public key associated with the signature method. For example,
|
||||
// for `jwa.RSXXX` and `jwa.PSXXX` types, you need to pass the
|
||||
// `*"crypto/rsa".PublicKey` type.
|
||||
// Check the documentation for each verifier for details
|
||||
Verify(payload []byte, signature []byte, key interface{}) error
|
||||
}
|
||||
|
||||
type HMACVerifier struct {
|
||||
signer Signer
|
||||
}
|
||||
23
server/vendor/github.com/lestrrat-go/jwx/jws/io.go
generated
vendored
Normal file
23
server/vendor/github.com/lestrrat-go/jwx/jws/io.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
// 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)
|
||||
}
|
||||
960
server/vendor/github.com/lestrrat-go/jwx/jws/jws.go
generated
vendored
Normal file
960
server/vendor/github.com/lestrrat-go/jwx/jws/jws.go
generated
vendored
Normal file
@@ -0,0 +1,960 @@
|
||||
//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
|
||||
}
|
||||
460
server/vendor/github.com/lestrrat-go/jwx/jws/message.go
generated
vendored
Normal file
460
server/vendor/github.com/lestrrat-go/jwx/jws/message.go
generated
vendored
Normal file
@@ -0,0 +1,460 @@
|
||||
package jws
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type collectRawCtx struct{}
|
||||
|
||||
func (collectRawCtx) CollectRaw() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func NewSignature() *Signature {
|
||||
return &Signature{}
|
||||
}
|
||||
|
||||
func (s *Signature) DecodeCtx() DecodeCtx {
|
||||
return s.dc
|
||||
}
|
||||
|
||||
func (s *Signature) SetDecodeCtx(dc DecodeCtx) {
|
||||
s.dc = dc
|
||||
}
|
||||
|
||||
func (s Signature) PublicHeaders() Headers {
|
||||
return s.headers
|
||||
}
|
||||
|
||||
func (s *Signature) SetPublicHeaders(v Headers) *Signature {
|
||||
s.headers = v
|
||||
return s
|
||||
}
|
||||
|
||||
func (s Signature) ProtectedHeaders() Headers {
|
||||
return s.protected
|
||||
}
|
||||
|
||||
func (s *Signature) SetProtectedHeaders(v Headers) *Signature {
|
||||
s.protected = v
|
||||
return s
|
||||
}
|
||||
|
||||
func (s Signature) Signature() []byte {
|
||||
return s.signature
|
||||
}
|
||||
|
||||
func (s *Signature) SetSignature(v []byte) *Signature {
|
||||
s.signature = v
|
||||
return s
|
||||
}
|
||||
|
||||
type signatureUnmarshalProbe struct {
|
||||
Header Headers `json:"header,omitempty"`
|
||||
Protected *string `json:"protected,omitempty"`
|
||||
Signature *string `json:"signature,omitempty"`
|
||||
}
|
||||
|
||||
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`)
|
||||
}
|
||||
|
||||
s.headers = sup.Header
|
||||
if buf := sup.Protected; buf != nil {
|
||||
src := []byte(*buf)
|
||||
if !bytes.HasPrefix(src, []byte{'{'}) {
|
||||
decoded, err := base64.Decode(src)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, `failed to base64 decode protected headers`)
|
||||
}
|
||||
src = decoded
|
||||
}
|
||||
|
||||
prt := NewHeaders()
|
||||
//nolint:forcetypeassert
|
||||
prt.(*stdHeaders).SetDecodeCtx(s.DecodeCtx())
|
||||
if err := json.Unmarshal(src, prt); err != nil {
|
||||
return errors.Wrap(err, `failed to unmarshal protected headers`)
|
||||
}
|
||||
//nolint:forcetypeassert
|
||||
prt.(*stdHeaders).SetDecodeCtx(nil)
|
||||
s.protected = prt
|
||||
}
|
||||
|
||||
if sup.Signature != nil {
|
||||
decoded, err := base64.DecodeString(*sup.Signature)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, `failed to base decode signature`)
|
||||
}
|
||||
s.signature = decoded
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sign populates the signature field, with a signature generated by
|
||||
// given the signer object and payload.
|
||||
//
|
||||
// The first return value is the raw signature in binary format.
|
||||
// The second return value s the full three-segment signature
|
||||
// (e.g. "eyXXXX.XXXXX.XXXX")
|
||||
func (s *Signature) Sign(payload []byte, signer Signer, key interface{}) ([]byte, []byte, error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
hdrs, err := mergeHeaders(ctx, s.headers, s.protected)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, `failed to merge headers`)
|
||||
}
|
||||
|
||||
if err := hdrs.Set(AlgorithmKey, signer.Algorithm()); err != nil {
|
||||
return nil, nil, errors.Wrap(err, `failed to set "alg"`)
|
||||
}
|
||||
|
||||
// If the key is a jwk.Key instance, obtain the raw key
|
||||
if jwkKey, ok := key.(jwk.Key); ok {
|
||||
// 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`)
|
||||
}
|
||||
}
|
||||
}
|
||||
hdrbuf, err := json.Marshal(hdrs)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, `failed to marshal headers`)
|
||||
}
|
||||
|
||||
buf := pool.GetBytesBuffer()
|
||||
defer pool.ReleaseBytesBuffer(buf)
|
||||
|
||||
buf.WriteString(base64.EncodeToString(hdrbuf))
|
||||
buf.WriteByte('.')
|
||||
|
||||
var plen int
|
||||
b64 := getB64Value(hdrs)
|
||||
if b64 {
|
||||
encoded := base64.EncodeToString(payload)
|
||||
plen = len(encoded)
|
||||
buf.WriteString(encoded)
|
||||
} else {
|
||||
if !s.detached {
|
||||
if bytes.Contains(payload, []byte{'.'}) {
|
||||
return nil, nil, errors.New(`payload must not contain a "."`)
|
||||
}
|
||||
}
|
||||
plen = len(payload)
|
||||
buf.Write(payload)
|
||||
}
|
||||
|
||||
signature, err := signer.Sign(buf.Bytes(), key)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, `failed to sign payload`)
|
||||
}
|
||||
s.signature = signature
|
||||
|
||||
// Detached payload, this should be removed from the end result
|
||||
if s.detached {
|
||||
buf.Truncate(buf.Len() - plen)
|
||||
}
|
||||
|
||||
buf.WriteByte('.')
|
||||
buf.WriteString(base64.EncodeToString(signature))
|
||||
ret := make([]byte, buf.Len())
|
||||
copy(ret, buf.Bytes())
|
||||
|
||||
return signature, ret, nil
|
||||
}
|
||||
|
||||
func NewMessage() *Message {
|
||||
return &Message{}
|
||||
}
|
||||
|
||||
// Clears the internal raw buffer that was accumulated during
|
||||
// the verify phase
|
||||
func (m *Message) clearRaw() {
|
||||
for _, sig := range m.signatures {
|
||||
if protected := sig.protected; protected != nil {
|
||||
if cr, ok := protected.(*stdHeaders); ok {
|
||||
cr.raw = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Message) SetDecodeCtx(dc DecodeCtx) {
|
||||
m.dc = dc
|
||||
}
|
||||
|
||||
func (m *Message) DecodeCtx() DecodeCtx {
|
||||
return m.dc
|
||||
}
|
||||
|
||||
// Payload returns the decoded payload
|
||||
func (m Message) Payload() []byte {
|
||||
return m.payload
|
||||
}
|
||||
|
||||
func (m *Message) SetPayload(v []byte) *Message {
|
||||
m.payload = v
|
||||
return m
|
||||
}
|
||||
|
||||
func (m Message) Signatures() []*Signature {
|
||||
return m.signatures
|
||||
}
|
||||
|
||||
func (m *Message) AppendSignature(v *Signature) *Message {
|
||||
m.signatures = append(m.signatures, v)
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *Message) ClearSignatures() *Message {
|
||||
m.signatures = nil
|
||||
return m
|
||||
}
|
||||
|
||||
// LookupSignature looks up a particular signature entry using
|
||||
// the `kid` value
|
||||
func (m Message) LookupSignature(kid string) []*Signature {
|
||||
var sigs []*Signature
|
||||
for _, sig := range m.signatures {
|
||||
if hdr := sig.PublicHeaders(); hdr != nil {
|
||||
hdrKeyID := hdr.KeyID()
|
||||
if hdrKeyID == kid {
|
||||
sigs = append(sigs, sig)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if hdr := sig.ProtectedHeaders(); hdr != nil {
|
||||
hdrKeyID := hdr.KeyID()
|
||||
if hdrKeyID == kid {
|
||||
sigs = append(sigs, sig)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return sigs
|
||||
}
|
||||
|
||||
// This struct is used to first probe for the structure of the
|
||||
// incoming JSON object. We then decide how to parse it
|
||||
// from the fields that are populated.
|
||||
type messageUnmarshalProbe struct {
|
||||
Payload *string `json:"payload"`
|
||||
Signatures []json.RawMessage `json:"signatures,omitempty"`
|
||||
Header Headers `json:"header,omitempty"`
|
||||
Protected *string `json:"protected,omitempty"`
|
||||
Signature *string `json:"signature,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Message) UnmarshalJSON(buf []byte) error {
|
||||
m.payload = nil
|
||||
m.signatures = nil
|
||||
m.b64 = true
|
||||
|
||||
var mup messageUnmarshalProbe
|
||||
mup.Header = NewHeaders()
|
||||
if err := json.Unmarshal(buf, &mup); err != nil {
|
||||
return errors.Wrap(err, `failed to unmarshal into temporary structure`)
|
||||
}
|
||||
|
||||
b64 := true
|
||||
if mup.Signature == nil { // flattened signature is NOT present
|
||||
if len(mup.Signatures) == 0 {
|
||||
return errors.New(`required field "signatures" not present`)
|
||||
}
|
||||
|
||||
m.signatures = make([]*Signature, 0, len(mup.Signatures))
|
||||
for i, rawsig := range mup.Signatures {
|
||||
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)
|
||||
}
|
||||
sig.SetDecodeCtx(nil)
|
||||
|
||||
if sig.protected == nil {
|
||||
// Instead of barfing on a nil protected header, use an empty header
|
||||
sig.protected = NewHeaders()
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
if !getB64Value(sig.protected) {
|
||||
b64 = false
|
||||
}
|
||||
} else {
|
||||
if b64 != getB64Value(sig.protected) {
|
||||
return errors.Errorf(`b64 value must be the same for all signatures`)
|
||||
}
|
||||
}
|
||||
|
||||
m.signatures = append(m.signatures, &sig)
|
||||
}
|
||||
} 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)`)
|
||||
}
|
||||
|
||||
var sig Signature
|
||||
sig.headers = mup.Header
|
||||
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`)
|
||||
}
|
||||
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`)
|
||||
}
|
||||
//nolint:forcetypeassert
|
||||
prt.(*stdHeaders).SetDecodeCtx(nil)
|
||||
sig.protected = prt
|
||||
}
|
||||
|
||||
if sig.protected == nil {
|
||||
// Instead of barfing on a nil protected header, use an empty header
|
||||
sig.protected = NewHeaders()
|
||||
}
|
||||
|
||||
decoded, err := base64.DecodeString(*mup.Signature)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, `failed to base64 decode flattened signature`)
|
||||
}
|
||||
sig.signature = decoded
|
||||
|
||||
m.signatures = []*Signature{&sig}
|
||||
b64 = getB64Value(sig.protected)
|
||||
}
|
||||
|
||||
if mup.Payload != nil {
|
||||
if !b64 { // NOT base64 encoded
|
||||
m.payload = []byte(*mup.Payload)
|
||||
} else {
|
||||
decoded, err := base64.DecodeString(*mup.Payload)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, `failed to base64 decode payload`)
|
||||
}
|
||||
m.payload = decoded
|
||||
}
|
||||
}
|
||||
m.b64 = b64
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m Message) MarshalJSON() ([]byte, error) {
|
||||
if len(m.signatures) == 1 {
|
||||
return m.marshalFlattened()
|
||||
}
|
||||
return m.marshalFull()
|
||||
}
|
||||
|
||||
func (m Message) marshalFlattened() ([]byte, error) {
|
||||
buf := pool.GetBytesBuffer()
|
||||
defer pool.ReleaseBytesBuffer(buf)
|
||||
|
||||
sig := m.signatures[0]
|
||||
|
||||
buf.WriteRune('{')
|
||||
var wrote bool
|
||||
|
||||
if hdr := sig.headers; hdr != nil {
|
||||
hdrjs, err := hdr.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, `failed to marshal "header" (flattened format)`)
|
||||
}
|
||||
buf.WriteString(`"header":`)
|
||||
buf.Write(hdrjs)
|
||||
wrote = true
|
||||
}
|
||||
|
||||
if wrote {
|
||||
buf.WriteRune(',')
|
||||
}
|
||||
buf.WriteString(`"payload":"`)
|
||||
buf.WriteString(base64.EncodeToString(m.payload))
|
||||
buf.WriteRune('"')
|
||||
|
||||
if protected := sig.protected; protected != nil {
|
||||
protectedbuf, err := protected.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, `failed to marshal "protected" (flattened format)`)
|
||||
}
|
||||
buf.WriteString(`,"protected":"`)
|
||||
buf.WriteString(base64.EncodeToString(protectedbuf))
|
||||
buf.WriteRune('"')
|
||||
}
|
||||
|
||||
buf.WriteString(`,"signature":"`)
|
||||
buf.WriteString(base64.EncodeToString(sig.signature))
|
||||
buf.WriteRune('"')
|
||||
buf.WriteRune('}')
|
||||
|
||||
ret := make([]byte, buf.Len())
|
||||
copy(ret, buf.Bytes())
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (m Message) marshalFull() ([]byte, error) {
|
||||
buf := pool.GetBytesBuffer()
|
||||
defer pool.ReleaseBytesBuffer(buf)
|
||||
|
||||
buf.WriteString(`{"payload":"`)
|
||||
buf.WriteString(base64.EncodeToString(m.payload))
|
||||
buf.WriteString(`","signatures":[`)
|
||||
for i, sig := range m.signatures {
|
||||
if i > 0 {
|
||||
buf.WriteRune(',')
|
||||
}
|
||||
|
||||
buf.WriteRune('{')
|
||||
var wrote bool
|
||||
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)
|
||||
}
|
||||
buf.WriteString(`"header":`)
|
||||
buf.Write(hdrbuf)
|
||||
wrote = true
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
if wrote {
|
||||
buf.WriteRune(',')
|
||||
}
|
||||
buf.WriteString(`"protected":"`)
|
||||
buf.WriteString(base64.EncodeToString(protectedbuf))
|
||||
buf.WriteRune('"')
|
||||
wrote = true
|
||||
}
|
||||
|
||||
if wrote {
|
||||
buf.WriteRune(',')
|
||||
}
|
||||
buf.WriteString(`"signature":"`)
|
||||
buf.WriteString(base64.EncodeToString(sig.signature))
|
||||
buf.WriteString(`"}`)
|
||||
}
|
||||
buf.WriteString(`]}`)
|
||||
|
||||
ret := make([]byte, buf.Len())
|
||||
copy(ret, buf.Bytes())
|
||||
return ret, nil
|
||||
}
|
||||
119
server/vendor/github.com/lestrrat-go/jwx/jws/option.go
generated
vendored
Normal file
119
server/vendor/github.com/lestrrat-go/jwx/jws/option.go
generated
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
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)}
|
||||
}
|
||||
142
server/vendor/github.com/lestrrat-go/jwx/jws/rsa.go
generated
vendored
Normal file
142
server/vendor/github.com/lestrrat-go/jwx/jws/rsa.go
generated
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
package jws
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
|
||||
"github.com/lestrrat-go/jwx/internal/keyconv"
|
||||
"github.com/lestrrat-go/jwx/jwa"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var rsaSigners map[jwa.SignatureAlgorithm]*rsaSigner
|
||||
var rsaVerifiers map[jwa.SignatureAlgorithm]*rsaVerifier
|
||||
|
||||
func init() {
|
||||
algs := map[jwa.SignatureAlgorithm]struct {
|
||||
Hash crypto.Hash
|
||||
PSS bool
|
||||
}{
|
||||
jwa.RS256: {
|
||||
Hash: crypto.SHA256,
|
||||
},
|
||||
jwa.RS384: {
|
||||
Hash: crypto.SHA384,
|
||||
},
|
||||
jwa.RS512: {
|
||||
Hash: crypto.SHA512,
|
||||
},
|
||||
jwa.PS256: {
|
||||
Hash: crypto.SHA256,
|
||||
PSS: true,
|
||||
},
|
||||
jwa.PS384: {
|
||||
Hash: crypto.SHA384,
|
||||
PSS: true,
|
||||
},
|
||||
jwa.PS512: {
|
||||
Hash: crypto.SHA512,
|
||||
PSS: true,
|
||||
},
|
||||
}
|
||||
|
||||
rsaSigners = make(map[jwa.SignatureAlgorithm]*rsaSigner)
|
||||
rsaVerifiers = make(map[jwa.SignatureAlgorithm]*rsaVerifier)
|
||||
for alg, item := range algs {
|
||||
rsaSigners[alg] = &rsaSigner{
|
||||
alg: alg,
|
||||
hash: item.Hash,
|
||||
pss: item.PSS,
|
||||
}
|
||||
rsaVerifiers[alg] = &rsaVerifier{
|
||||
alg: alg,
|
||||
hash: item.Hash,
|
||||
pss: item.PSS,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type rsaSigner struct {
|
||||
alg jwa.SignatureAlgorithm
|
||||
hash crypto.Hash
|
||||
pss bool
|
||||
}
|
||||
|
||||
func newRSASigner(alg jwa.SignatureAlgorithm) Signer {
|
||||
return rsaSigners[alg]
|
||||
}
|
||||
|
||||
func (rs *rsaSigner) Algorithm() jwa.SignatureAlgorithm {
|
||||
return rs.alg
|
||||
}
|
||||
|
||||
func (rs *rsaSigner) Sign(payload []byte, key interface{}) ([]byte, error) {
|
||||
if key == nil {
|
||||
return nil, errors.New(`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)
|
||||
}
|
||||
signer = &privkey
|
||||
}
|
||||
|
||||
h := rs.hash.New()
|
||||
if _, err := h.Write(payload); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to write payload to hash")
|
||||
}
|
||||
if rs.pss {
|
||||
return signer.Sign(rand.Reader, h.Sum(nil), &rsa.PSSOptions{
|
||||
Hash: rs.hash,
|
||||
SaltLength: rsa.PSSSaltLengthEqualsHash,
|
||||
})
|
||||
}
|
||||
return signer.Sign(rand.Reader, h.Sum(nil), rs.hash)
|
||||
}
|
||||
|
||||
type rsaVerifier struct {
|
||||
alg jwa.SignatureAlgorithm
|
||||
hash crypto.Hash
|
||||
pss bool
|
||||
}
|
||||
|
||||
func newRSAVerifier(alg jwa.SignatureAlgorithm) Verifier {
|
||||
return rsaVerifiers[alg]
|
||||
}
|
||||
|
||||
func (rv *rsaVerifier) Verify(payload, signature []byte, key interface{}) error {
|
||||
if key == nil {
|
||||
return errors.New(`missing public key while verifying payload`)
|
||||
}
|
||||
|
||||
var pubkey rsa.PublicKey
|
||||
if cs, ok := key.(crypto.Signer); ok {
|
||||
cpub := cs.Public()
|
||||
switch cpub := cpub.(type) {
|
||||
case rsa.PublicKey:
|
||||
pubkey = cpub
|
||||
case *rsa.PublicKey:
|
||||
pubkey = *cpub
|
||||
default:
|
||||
return errors.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)
|
||||
}
|
||||
}
|
||||
|
||||
h := rv.hash.New()
|
||||
if _, err := h.Write(payload); err != nil {
|
||||
return errors.Wrap(err, "failed to write payload to hash")
|
||||
}
|
||||
|
||||
if rv.pss {
|
||||
return rsa.VerifyPSS(&pubkey, rv.hash, h.Sum(nil), signature, nil)
|
||||
}
|
||||
return rsa.VerifyPKCS1v15(&pubkey, rv.hash, h.Sum(nil), signature)
|
||||
}
|
||||
68
server/vendor/github.com/lestrrat-go/jwx/jws/signer.go
generated
vendored
Normal file
68
server/vendor/github.com/lestrrat-go/jwx/jws/signer.go
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
package jws
|
||||
|
||||
import (
|
||||
"github.com/lestrrat-go/jwx/jwa"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type SignerFactory interface {
|
||||
Create() (Signer, error)
|
||||
}
|
||||
type SignerFactoryFn func() (Signer, error)
|
||||
|
||||
func (fn SignerFactoryFn) Create() (Signer, error) {
|
||||
return fn()
|
||||
}
|
||||
|
||||
var signerDB map[jwa.SignatureAlgorithm]SignerFactory
|
||||
|
||||
// RegisterSigner is used to register a factory object that creates
|
||||
// Signer objects based on the given algorithm.
|
||||
//
|
||||
// For example, if you would like to provide a custom signer for
|
||||
// jwa.EdDSA, use this function to register a `SignerFactory`
|
||||
// (probably in your `init()`)
|
||||
func RegisterSigner(alg jwa.SignatureAlgorithm, f SignerFactory) {
|
||||
signerDB[alg] = f
|
||||
}
|
||||
|
||||
func init() {
|
||||
signerDB = make(map[jwa.SignatureAlgorithm]SignerFactory)
|
||||
|
||||
for _, alg := range []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512} {
|
||||
RegisterSigner(alg, func(alg jwa.SignatureAlgorithm) SignerFactory {
|
||||
return SignerFactoryFn(func() (Signer, error) {
|
||||
return newRSASigner(alg), nil
|
||||
})
|
||||
}(alg))
|
||||
}
|
||||
|
||||
for _, alg := range []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512, jwa.ES256K} {
|
||||
RegisterSigner(alg, func(alg jwa.SignatureAlgorithm) SignerFactory {
|
||||
return SignerFactoryFn(func() (Signer, error) {
|
||||
return newECDSASigner(alg), nil
|
||||
})
|
||||
}(alg))
|
||||
}
|
||||
|
||||
for _, alg := range []jwa.SignatureAlgorithm{jwa.HS256, jwa.HS384, jwa.HS512} {
|
||||
RegisterSigner(alg, func(alg jwa.SignatureAlgorithm) SignerFactory {
|
||||
return SignerFactoryFn(func() (Signer, error) {
|
||||
return newHMACSigner(alg), nil
|
||||
})
|
||||
}(alg))
|
||||
}
|
||||
|
||||
RegisterSigner(jwa.EdDSA, SignerFactoryFn(func() (Signer, error) {
|
||||
return newEdDSASigner(), nil
|
||||
}))
|
||||
}
|
||||
|
||||
// NewSigner creates a signer that signs payloads using the given signature algorithm.
|
||||
func NewSigner(alg jwa.SignatureAlgorithm) (Signer, error) {
|
||||
f, ok := signerDB[alg]
|
||||
if ok {
|
||||
return f.Create()
|
||||
}
|
||||
return nil, errors.Errorf(`unsupported signature algorithm "%s"`, alg)
|
||||
}
|
||||
68
server/vendor/github.com/lestrrat-go/jwx/jws/verifier.go
generated
vendored
Normal file
68
server/vendor/github.com/lestrrat-go/jwx/jws/verifier.go
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
package jws
|
||||
|
||||
import (
|
||||
"github.com/lestrrat-go/jwx/jwa"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type VerifierFactory interface {
|
||||
Create() (Verifier, error)
|
||||
}
|
||||
type VerifierFactoryFn func() (Verifier, error)
|
||||
|
||||
func (fn VerifierFactoryFn) Create() (Verifier, error) {
|
||||
return fn()
|
||||
}
|
||||
|
||||
var verifierDB map[jwa.SignatureAlgorithm]VerifierFactory
|
||||
|
||||
// RegisterVerifier is used to register a factory object that creates
|
||||
// Verifier objects based on the given algorithm.
|
||||
//
|
||||
// For example, if you would like to provide a custom verifier for
|
||||
// jwa.EdDSA, use this function to register a `VerifierFactory`
|
||||
// (probably in your `init()`)
|
||||
func RegisterVerifier(alg jwa.SignatureAlgorithm, f VerifierFactory) {
|
||||
verifierDB[alg] = f
|
||||
}
|
||||
|
||||
func init() {
|
||||
verifierDB = make(map[jwa.SignatureAlgorithm]VerifierFactory)
|
||||
|
||||
for _, alg := range []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512} {
|
||||
RegisterVerifier(alg, func(alg jwa.SignatureAlgorithm) VerifierFactory {
|
||||
return VerifierFactoryFn(func() (Verifier, error) {
|
||||
return newRSAVerifier(alg), nil
|
||||
})
|
||||
}(alg))
|
||||
}
|
||||
|
||||
for _, alg := range []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512, jwa.ES256K} {
|
||||
RegisterVerifier(alg, func(alg jwa.SignatureAlgorithm) VerifierFactory {
|
||||
return VerifierFactoryFn(func() (Verifier, error) {
|
||||
return newECDSAVerifier(alg), nil
|
||||
})
|
||||
}(alg))
|
||||
}
|
||||
|
||||
for _, alg := range []jwa.SignatureAlgorithm{jwa.HS256, jwa.HS384, jwa.HS512} {
|
||||
RegisterVerifier(alg, func(alg jwa.SignatureAlgorithm) VerifierFactory {
|
||||
return VerifierFactoryFn(func() (Verifier, error) {
|
||||
return newHMACVerifier(alg), nil
|
||||
})
|
||||
}(alg))
|
||||
}
|
||||
|
||||
RegisterVerifier(jwa.EdDSA, VerifierFactoryFn(func() (Verifier, error) {
|
||||
return newEdDSAVerifier(), nil
|
||||
}))
|
||||
}
|
||||
|
||||
// NewVerifier creates a verifier that signs payloads using the given signature algorithm.
|
||||
func NewVerifier(alg jwa.SignatureAlgorithm) (Verifier, error) {
|
||||
f, ok := verifierDB[alg]
|
||||
if ok {
|
||||
return f.Create()
|
||||
}
|
||||
return nil, errors.Errorf(`unsupported signature algorithm "%s"`, alg)
|
||||
}
|
||||
224
server/vendor/github.com/lestrrat-go/jwx/jwt/README.md
generated
vendored
Normal file
224
server/vendor/github.com/lestrrat-go/jwx/jwt/README.md
generated
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
# JWT [](https://pkg.go.dev/github.com/lestrrat-go/jwx/jwt)
|
||||
|
||||
Package jwt implements JSON Web Tokens as described in [RFC7519](https://tools.ietf.org/html/rfc7519).
|
||||
|
||||
* Convenience methods for oft-used keys ("aud", "sub", "iss", etc)
|
||||
* Convenience functions to extract/parse from http.Request, http.Header, url.Values
|
||||
* Ability to Get/Set arbitrary keys
|
||||
* 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)
|
||||
|
||||
How-to style documentation can be found in the [docs directory](../docs).
|
||||
|
||||
More examples are located in the examples directory ([jwt_example_test.go](../examples/jwt_example_test.go))
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
## Verify a signed JWT
|
||||
|
||||
```go
|
||||
token, err := jwt.Parse(payload, jwt.WithKeySet(keyset))
|
||||
if err != nil {
|
||||
fmt.Printf("failed to parse payload: %s\n", err)
|
||||
}
|
||||
```
|
||||
|
||||
## Token Usage
|
||||
|
||||
```go
|
||||
func ExampleJWT() {
|
||||
const aLongLongTimeAgo = 233431200
|
||||
|
||||
t := jwt.New()
|
||||
t.Set(jwt.SubjectKey, `https://github.com/lestrrat-go/jwx/jwt`)
|
||||
t.Set(jwt.AudienceKey, `Golang Users`)
|
||||
t.Set(jwt.IssuedAtKey, time.Unix(aLongLongTimeAgo, 0))
|
||||
t.Set(`privateClaimKey`, `Hello, World!`)
|
||||
|
||||
buf, err := json.MarshalIndent(t, "", " ")
|
||||
if err != nil {
|
||||
fmt.Printf("failed to generate JSON: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", buf)
|
||||
fmt.Printf("aud -> '%s'\n", t.Audience())
|
||||
fmt.Printf("iat -> '%s'\n", t.IssuedAt().Format(time.RFC3339))
|
||||
if v, ok := t.Get(`privateClaimKey`); ok {
|
||||
fmt.Printf("privateClaimKey -> '%s'\n", v)
|
||||
}
|
||||
fmt.Printf("sub -> '%s'\n", t.Subject())
|
||||
|
||||
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
log.Printf("failed to generate private key: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
{
|
||||
// Signing a token (using raw rsa.PrivateKey)
|
||||
signed, err := jwt.Sign(t, jwa.RS256, key)
|
||||
if err != nil {
|
||||
log.Printf("failed to sign token: %s", err)
|
||||
return
|
||||
}
|
||||
_ = signed
|
||||
}
|
||||
|
||||
{
|
||||
// Signing a token (using JWK)
|
||||
jwkKey, err := jwk.New(key)
|
||||
if err != nil {
|
||||
log.Printf("failed to create JWK key: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
signed, err := jwt.Sign(t, jwa.RS256, jwkKey)
|
||||
if err != nil {
|
||||
log.Printf("failed to sign token: %s", err)
|
||||
return
|
||||
}
|
||||
_ = signed
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## OpenID Claims
|
||||
|
||||
`jwt` package can work with token types other than the default one.
|
||||
For OpenID claims, use the token created by `openid.New()`, or
|
||||
use the `jwt.WithToken(openid.New())`. If you need to use other specialized
|
||||
claims, use `jwt.WithToken()` to specify the exact token type
|
||||
|
||||
```go
|
||||
func Example_openid() {
|
||||
const aLongLongTimeAgo = 233431200
|
||||
|
||||
t := openid.New()
|
||||
t.Set(jwt.SubjectKey, `https://github.com/lestrrat-go/jwx/jwt`)
|
||||
t.Set(jwt.AudienceKey, `Golang Users`)
|
||||
t.Set(jwt.IssuedAtKey, time.Unix(aLongLongTimeAgo, 0))
|
||||
t.Set(`privateClaimKey`, `Hello, World!`)
|
||||
|
||||
addr := openid.NewAddress()
|
||||
addr.Set(openid.AddressPostalCodeKey, `105-0011`)
|
||||
addr.Set(openid.AddressCountryKey, `日本`)
|
||||
addr.Set(openid.AddressRegionKey, `東京都`)
|
||||
addr.Set(openid.AddressLocalityKey, `港区`)
|
||||
addr.Set(openid.AddressStreetAddressKey, `芝公園 4-2-8`)
|
||||
t.Set(openid.AddressKey, addr)
|
||||
|
||||
buf, err := json.MarshalIndent(t, "", " ")
|
||||
if err != nil {
|
||||
fmt.Printf("failed to generate JSON: %s\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("%s\n", buf)
|
||||
|
||||
t2, err := jwt.Parse(buf, jwt.WithToken(openid.New()))
|
||||
if err != nil {
|
||||
fmt.Printf("failed to parse JSON: %s\n", err)
|
||||
return
|
||||
}
|
||||
if _, ok := t2.(openid.Token); !ok {
|
||||
fmt.Printf("using jwt.WithToken(openid.New()) creates an openid.Token instance")
|
||||
return
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# FAQ
|
||||
|
||||
## Why is `jwt.Token` an interface?
|
||||
|
||||
In this package, `jwt.Token` is an interface. This is not an arbitrary choice: there are actual reason for the type being an interface.
|
||||
|
||||
We understand that if you are migrating from another library this may be a deal breaker, but we hope you can at least appreciate the fact that this was not done arbitrarily, and that there were real technical trade offs that were evaluated.
|
||||
|
||||
### No uninitialized tokens
|
||||
|
||||
First and foremost, by making it an interface, you cannot use an uninitialized token:
|
||||
|
||||
```go
|
||||
var token1 jwt.Token // this is nil, you can't just start using this
|
||||
if err := json.Unmarshal(data, &token1); err != nil { // so you can't do this
|
||||
...
|
||||
}
|
||||
|
||||
// But you _can_ do this, and we _want_ you to do this so the object is properly initialized
|
||||
token2 = jwt.New()
|
||||
if err := json.Unmarshal(data, &token2); err != nil { // actually, in practice you should use jwt.Parse()
|
||||
....
|
||||
}
|
||||
```
|
||||
|
||||
### But why does it need to be initialized?
|
||||
|
||||
There are several reasons, but one of the reasons is that I'm using a sync.Mutex to avoid races. We want this to be properly initialized.
|
||||
|
||||
The other reason is that we support custom claims out of the box. The `map[string]interface{}` container is initialized during new. This is important when checking for equality using reflect-y methods (akin to `reflect.DeepEqual`), because if you allowed zero values, you could end up with "empty" tokens, that actually differ. Consider the following:
|
||||
|
||||
```go
|
||||
// assume jwt.Token was s struct, not an interface
|
||||
token1 := jwt.Token{ privateClaims: make(map[string]interface{}) }
|
||||
token2 := jwt.Token{ privateClaims: nil }
|
||||
```
|
||||
|
||||
These are semantically equivalent, but users would need to be aware of this difference when comparing values. By forcing the user to use a constructor, we can force a uniform empty state.
|
||||
|
||||
### Standard way to store values
|
||||
|
||||
Unlike some other libraries, this library allows you to store standard claims and non-standard claims in the same token.
|
||||
|
||||
You _want_ to store standard claims in a properly typed field, which we do for fields like "iss", "nbf", etc.
|
||||
But for non-standard claims, there is just no way of doing this, so we _have_ to use a container like `map[string]interface{}`
|
||||
|
||||
This means that if you allow direct access to these fields via a struct, you will have two different ways to access the claims, which is confusing:
|
||||
|
||||
```go
|
||||
tok.Issuer = ...
|
||||
tok.PrivateClaims["foo"] = ...
|
||||
```
|
||||
|
||||
So we want to hide where this data is stored, and use a standard method like `Set()` and `Get()` to store all the values.
|
||||
At this point you are effectively going to hide the implementation detail from the user, so you end up with a struct like below, which is fundamentally not so different from providing just an interface{}:
|
||||
|
||||
```go
|
||||
type Token struct {
|
||||
// unexported fields
|
||||
}
|
||||
|
||||
func (tok *Token) Set(...) { ... }
|
||||
```
|
||||
|
||||
### Use of pointers to store values
|
||||
|
||||
We wanted to differentiate the state between a claim being uninitialized, and a claim being initialized to empty.
|
||||
|
||||
So we use pointers to store values:
|
||||
|
||||
```go
|
||||
type stdToken struct {
|
||||
....
|
||||
issuer *string // if nil, uninitialized. if &(""), initialized to empty
|
||||
}
|
||||
```
|
||||
|
||||
This is fine for us, but we doubt that this would be something users would want to do.
|
||||
This is a subtle difference, but cluttering up the API with slight variations of the same type (i.e. pointers vs non-pointers) seemed like a bad idea to us.
|
||||
|
||||
```go
|
||||
token.Issuer = &issuer // want to avoid this
|
||||
|
||||
token.Set(jwt.IssuerKey, "foobar") // so this is what we picked
|
||||
```
|
||||
|
||||
This way users no longer need to care how the data is internally stored.
|
||||
|
||||
### Allow more than one type of token through the same interface
|
||||
|
||||
`dgrijalva/jwt-go` does this in a different way, but we felt that it would be more intuitive for all tokens to follow a single interface so there is fewer type conversions required.
|
||||
|
||||
See the `openid` token for an example.
|
||||
71
server/vendor/github.com/lestrrat-go/jwx/jwt/builder_gen.go
generated
vendored
Normal file
71
server/vendor/github.com/lestrrat-go/jwx/jwt/builder_gen.go
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
// This file is auto-generated by jwt/internal/cmd/gentoken/main.go. DO NOT EDIT
|
||||
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Builder is a convenience wrapper around the New() constructor
|
||||
// and the Set() methods to assign values to Token claims.
|
||||
// Users can successively call Claim() on the Builder, and have it
|
||||
// construct the Token when Build() is called. This alleviates the
|
||||
// need for the user to check for the return value of every single
|
||||
// Set() method call.
|
||||
// Note that each call to Claim() overwrites the value set from the
|
||||
// previous call.
|
||||
type Builder struct {
|
||||
claims []*ClaimPair
|
||||
}
|
||||
|
||||
func NewBuilder() *Builder {
|
||||
return &Builder{}
|
||||
}
|
||||
|
||||
func (b *Builder) Claim(name string, value interface{}) *Builder {
|
||||
b.claims = append(b.claims, &ClaimPair{Key: name, Value: value})
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Builder) Audience(v []string) *Builder {
|
||||
return b.Claim(AudienceKey, v)
|
||||
}
|
||||
|
||||
func (b *Builder) Expiration(v time.Time) *Builder {
|
||||
return b.Claim(ExpirationKey, v)
|
||||
}
|
||||
|
||||
func (b *Builder) IssuedAt(v time.Time) *Builder {
|
||||
return b.Claim(IssuedAtKey, v)
|
||||
}
|
||||
|
||||
func (b *Builder) Issuer(v string) *Builder {
|
||||
return b.Claim(IssuerKey, v)
|
||||
}
|
||||
|
||||
func (b *Builder) JwtID(v string) *Builder {
|
||||
return b.Claim(JwtIDKey, v)
|
||||
}
|
||||
|
||||
func (b *Builder) NotBefore(v time.Time) *Builder {
|
||||
return b.Claim(NotBeforeKey, v)
|
||||
}
|
||||
|
||||
func (b *Builder) Subject(v string) *Builder {
|
||||
return b.Claim(SubjectKey, v)
|
||||
}
|
||||
|
||||
// Build creates a new token based on the claims that the builder has received
|
||||
// so far. If a claim cannot be set, then the method returns a nil Token with
|
||||
// a en error as a second return value
|
||||
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 tok, nil
|
||||
}
|
||||
9
server/vendor/github.com/lestrrat-go/jwx/jwt/gen.sh
generated
vendored
Normal file
9
server/vendor/github.com/lestrrat-go/jwx/jwt/gen.sh
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
#!/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
|
||||
188
server/vendor/github.com/lestrrat-go/jwx/jwt/http.go
generated
vendored
Normal file
188
server/vendor/github.com/lestrrat-go/jwx/jwt/http.go
generated
vendored
Normal file
@@ -0,0 +1,188 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/lestrrat-go/jwx/internal/pool"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// ParseHeader parses a JWT stored in a http.Header.
|
||||
//
|
||||
// For the header "Authorization", it will strip the prefix "Bearer " and will
|
||||
// treat the remaining value as a JWT.
|
||||
func ParseHeader(hdr http.Header, name string, options ...ParseOption) (Token, error) {
|
||||
key := http.CanonicalHeaderKey(name)
|
||||
v := strings.TrimSpace(hdr.Get(key))
|
||||
if v == "" {
|
||||
return nil, errors.Errorf(`empty header (%s)`, key)
|
||||
}
|
||||
|
||||
if key == "Authorization" {
|
||||
// Authorization header is an exception. We strip the "Bearer " from
|
||||
// the prefix
|
||||
v = strings.TrimSpace(strings.TrimPrefix(v, "Bearer"))
|
||||
}
|
||||
|
||||
return ParseString(v, options...)
|
||||
}
|
||||
|
||||
// ParseForm parses a JWT stored in a url.Value.
|
||||
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 ParseString(v, options...)
|
||||
}
|
||||
|
||||
// ParseRequest searches a http.Request object for a JWT token.
|
||||
//
|
||||
// Specifying WithHeaderKey() will tell it to search under a specific
|
||||
// header key. Specifying WithFormKey() will tell it to search under
|
||||
// a specific form field.
|
||||
//
|
||||
// By default, "Authorization" header will be searched.
|
||||
//
|
||||
// If WithHeaderKey() is used, you must explicitly re-enable searching for "Authorization" header.
|
||||
//
|
||||
// # searches for "Authorization"
|
||||
// jwt.ParseRequest(req)
|
||||
//
|
||||
// # searches for "x-my-token" ONLY.
|
||||
// jwt.ParseRequest(req, jwt.WithHeaderKey("x-my-token"))
|
||||
//
|
||||
// # searches for "Authorization" AND "x-my-token"
|
||||
// jwt.ParseRequest(req, jwt.WithHeaderKey("Authorization"), jwt.WithHeaderKey("x-my-token"))
|
||||
func ParseRequest(req *http.Request, options ...ParseOption) (Token, error) {
|
||||
var hdrkeys []string
|
||||
var formkeys []string
|
||||
var parseOptions []ParseOption
|
||||
for _, option := range options {
|
||||
//nolint:forcetypeassert
|
||||
switch option.Ident() {
|
||||
case identHeaderKey{}:
|
||||
hdrkeys = append(hdrkeys, option.Value().(string))
|
||||
case identFormKey{}:
|
||||
formkeys = append(formkeys, option.Value().(string))
|
||||
default:
|
||||
parseOptions = append(parseOptions, option)
|
||||
}
|
||||
}
|
||||
if len(hdrkeys) == 0 {
|
||||
hdrkeys = append(hdrkeys, "Authorization")
|
||||
}
|
||||
|
||||
mhdrs := pool.GetKeyToErrorMap()
|
||||
defer pool.ReleaseKeyToErrorMap(mhdrs)
|
||||
mfrms := pool.GetKeyToErrorMap()
|
||||
defer pool.ReleaseKeyToErrorMap(mfrms)
|
||||
|
||||
for _, hdrkey := range hdrkeys {
|
||||
// Check presence via a direct map lookup
|
||||
if _, ok := req.Header[http.CanonicalHeaderKey(hdrkey)]; !ok {
|
||||
// if non-existent, not error
|
||||
continue
|
||||
}
|
||||
|
||||
tok, err := ParseHeader(req.Header, hdrkey, parseOptions...)
|
||||
if err != nil {
|
||||
mhdrs[hdrkey] = err
|
||||
continue
|
||||
}
|
||||
return tok, nil
|
||||
}
|
||||
|
||||
if cl := req.ContentLength; cl > 0 {
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return nil, errors.Wrap(err, `failed to parse form`)
|
||||
}
|
||||
}
|
||||
|
||||
for _, formkey := range formkeys {
|
||||
// Check presence via a direct map lookup
|
||||
if _, ok := req.Form[formkey]; !ok {
|
||||
// if non-existent, not error
|
||||
continue
|
||||
}
|
||||
|
||||
tok, err := ParseForm(req.Form, formkey, parseOptions...)
|
||||
if err != nil {
|
||||
mfrms[formkey] = err
|
||||
continue
|
||||
}
|
||||
return tok, nil
|
||||
}
|
||||
|
||||
// Everything below is a preulde to error reporting.
|
||||
var triedHdrs strings.Builder
|
||||
for i, hdrkey := range hdrkeys {
|
||||
if i > 0 {
|
||||
triedHdrs.WriteString(", ")
|
||||
}
|
||||
triedHdrs.WriteString(strconv.Quote(hdrkey))
|
||||
}
|
||||
|
||||
var triedForms strings.Builder
|
||||
for i, formkey := range formkeys {
|
||||
if i > 0 {
|
||||
triedForms.WriteString(", ")
|
||||
}
|
||||
triedForms.WriteString(strconv.Quote(formkey))
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
b.WriteString(`failed to find a valid token in any location of the request (tried: [header keys: `)
|
||||
b.WriteString(triedHdrs.String())
|
||||
b.WriteByte(']')
|
||||
if triedForms.Len() > 0 {
|
||||
b.WriteString(", form keys: [")
|
||||
b.WriteString(triedForms.String())
|
||||
b.WriteByte(']')
|
||||
}
|
||||
b.WriteByte(')')
|
||||
|
||||
lmhdrs := len(mhdrs)
|
||||
lmfrms := len(mfrms)
|
||||
if lmhdrs > 0 || lmfrms > 0 {
|
||||
b.WriteString(". Additionally, errors were encountered during attempts to parse")
|
||||
|
||||
if lmhdrs > 0 {
|
||||
b.WriteString(" headers: (")
|
||||
count := 0
|
||||
for hdrkey, err := range mhdrs {
|
||||
if count > 0 {
|
||||
b.WriteString(", ")
|
||||
}
|
||||
b.WriteString("[header key: ")
|
||||
b.WriteString(strconv.Quote(hdrkey))
|
||||
b.WriteString(", error: ")
|
||||
b.WriteString(strconv.Quote(err.Error()))
|
||||
b.WriteString("]")
|
||||
count++
|
||||
}
|
||||
b.WriteString(")")
|
||||
}
|
||||
|
||||
if lmfrms > 0 {
|
||||
count := 0
|
||||
b.WriteString(" forms: (")
|
||||
for formkey, err := range mfrms {
|
||||
if count > 0 {
|
||||
b.WriteString(", ")
|
||||
}
|
||||
b.WriteString("[form key: ")
|
||||
b.WriteString(strconv.Quote(formkey))
|
||||
b.WriteString(", error: ")
|
||||
b.WriteString(strconv.Quote(err.Error()))
|
||||
b.WriteString("]")
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, errors.New(b.String())
|
||||
}
|
||||
14
server/vendor/github.com/lestrrat-go/jwx/jwt/interface.go
generated
vendored
Normal file
14
server/vendor/github.com/lestrrat-go/jwx/jwt/interface.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"github.com/lestrrat-go/iter/mapiter"
|
||||
"github.com/lestrrat-go/jwx/internal/iter"
|
||||
"github.com/lestrrat-go/jwx/internal/json"
|
||||
)
|
||||
|
||||
type ClaimPair = mapiter.Pair
|
||||
type Iterator = mapiter.Iterator
|
||||
type Visitor = iter.MapVisitor
|
||||
type VisitorFunc = iter.MapVisitorFunc
|
||||
type DecodeCtx = json.DecodeCtx
|
||||
type TokenWithDecodeCtx = json.DecodeCtxContainer
|
||||
98
server/vendor/github.com/lestrrat-go/jwx/jwt/internal/types/date.go
generated
vendored
Normal file
98
server/vendor/github.com/lestrrat-go/jwx/jwt/internal/types/date.go
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
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
|
||||
}
|
||||
43
server/vendor/github.com/lestrrat-go/jwx/jwt/internal/types/string.go
generated
vendored
Normal file
43
server/vendor/github.com/lestrrat-go/jwx/jwt/internal/types/string.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/lestrrat-go/jwx/internal/json"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type StringList []string
|
||||
|
||||
func (l StringList) Get() []string {
|
||||
return []string(l)
|
||||
}
|
||||
|
||||
func (l *StringList) Accept(v interface{}) error {
|
||||
switch x := v.(type) {
|
||||
case string:
|
||||
*l = StringList([]string{x})
|
||||
case []string:
|
||||
*l = StringList(x)
|
||||
case []interface{}:
|
||||
list := make(StringList, len(x))
|
||||
for i, e := range x {
|
||||
if s, ok := e.(string); ok {
|
||||
list[i] = s
|
||||
continue
|
||||
}
|
||||
return errors.Errorf(`invalid list element type %T`, e)
|
||||
}
|
||||
*l = list
|
||||
default:
|
||||
return errors.Errorf(`invalid type: %T`, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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 l.Accept(v)
|
||||
}
|
||||
29
server/vendor/github.com/lestrrat-go/jwx/jwt/io.go
generated
vendored
Normal file
29
server/vendor/github.com/lestrrat-go/jwx/jwt/io.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
// 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...)
|
||||
}
|
||||
560
server/vendor/github.com/lestrrat-go/jwx/jwt/jwt.go
generated
vendored
Normal file
560
server/vendor/github.com/lestrrat-go/jwx/jwt/jwt.go
generated
vendored
Normal file
@@ -0,0 +1,560 @@
|
||||
//go:generate ./gen.sh
|
||||
|
||||
// Package jwt implements JSON Web Tokens as described in https://tools.ietf.org/html/rfc7519
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"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"
|
||||
)
|
||||
|
||||
const _jwt = `jwt`
|
||||
|
||||
// Settings controls global settings that are specific to JWTs.
|
||||
func Settings(options ...GlobalOption) {
|
||||
var flattenAudienceBool bool
|
||||
|
||||
//nolint:forcetypeassert
|
||||
for _, option := range options {
|
||||
switch option.Ident() {
|
||||
case identFlattenAudience{}:
|
||||
flattenAudienceBool = option.Value().(bool)
|
||||
}
|
||||
}
|
||||
|
||||
v := atomic.LoadUint32(&json.FlattenAudience)
|
||||
if (v == 1) != flattenAudienceBool {
|
||||
var newVal uint32
|
||||
if flattenAudienceBool {
|
||||
newVal = 1
|
||||
}
|
||||
atomic.CompareAndSwapUint32(&json.FlattenAudience, v, newVal)
|
||||
}
|
||||
}
|
||||
|
||||
var registry = json.NewRegistry()
|
||||
|
||||
// ParseString calls Parse against a string
|
||||
func ParseString(s string, options ...ParseOption) (Token, error) {
|
||||
return parseBytes([]byte(s), options...)
|
||||
}
|
||||
|
||||
// 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)
|
||||
//
|
||||
// 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.
|
||||
// If you do not specify these parameters, no verification will be performed.
|
||||
//
|
||||
// During verification, if the JWS headers specify a key ID (`kid`), the
|
||||
// key used for verification must match the specified ID. If you are somehow
|
||||
// using a key without a `kid` (which is highly unlikely if you are working
|
||||
// with a JWT from a well know provider), you can workaround this by modifying
|
||||
// the `jwk.Key` and setting the `kid` header.
|
||||
//
|
||||
// If you also want to assert the validity of the JWT itself (i.e. expiration
|
||||
// and such), use the `Validate()` function on the returned token, or pass the
|
||||
// `WithValidate(true)` option. Validate options can also be passed to
|
||||
// `Parse`
|
||||
//
|
||||
// This function takes both ParseOption and ValidateOption types:
|
||||
// ParseOptions control the parsing behavior, and ValidateOptions are
|
||||
// passed to `Validate()` when `jwt.WithValidate` is specified.
|
||||
func Parse(s []byte, options ...ParseOption) (Token, error) {
|
||||
return parseBytes(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)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, `failed to read from token data source`)
|
||||
}
|
||||
return parseBytes(data, options...)
|
||||
}
|
||||
|
||||
type parseCtx struct {
|
||||
decryptParams DecryptParameters
|
||||
verifyParams VerifyParameters
|
||||
keySet jwk.Set
|
||||
keySetProvider KeySetProvider
|
||||
token Token
|
||||
validateOpts []ValidateOption
|
||||
verifyAutoOpts []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
|
||||
for _, o := range options {
|
||||
if v, ok := o.(ValidateOption); ok {
|
||||
ctx.validateOpts = append(ctx.validateOpts, v)
|
||||
continue
|
||||
}
|
||||
|
||||
//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 identToken{}:
|
||||
token, ok := o.Value().(Token)
|
||||
if !ok {
|
||||
return nil, errors.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 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)
|
||||
}
|
||||
}
|
||||
|
||||
data = bytes.TrimSpace(data)
|
||||
return parse(&ctx, data)
|
||||
}
|
||||
|
||||
const (
|
||||
_JwsVerifyInvalid = iota
|
||||
_JwsVerifyDone
|
||||
_JwsVerifyExpectNested
|
||||
_JwsVerifySkipped
|
||||
)
|
||||
|
||||
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 {
|
||||
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
|
||||
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`)
|
||||
}
|
||||
|
||||
// verify parameter exists to make sure that we don't accidentally skip
|
||||
// over verification just because alg == "" or key == nil or something.
|
||||
func parse(ctx *parseCtx, data []byte) (Token, error) {
|
||||
payload := data
|
||||
const maxDecodeLevels = 2
|
||||
|
||||
// If cty = `JWT`, we expect this to be a nested structure
|
||||
var expectNested bool
|
||||
|
||||
OUTER:
|
||||
for i := 0; i < maxDecodeLevels; i++ {
|
||||
switch kind := jwx.GuessFormat(payload); kind {
|
||||
case jwx.JWT:
|
||||
if ctx.pedantic {
|
||||
if expectNested {
|
||||
return nil, errors.Errorf(`expected nested encrypted/signed payload, got raw JWT`)
|
||||
}
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
// We were NOT enveloped in other formats
|
||||
if !ctx.skipVerification {
|
||||
if _, _, err := verifyJWS(ctx, payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break OUTER
|
||||
case jwx.UnknownFormat:
|
||||
// "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`)
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
// We were NOT enveloped in other formats
|
||||
if !ctx.skipVerification {
|
||||
if _, _, err := verifyJWS(ctx, payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
break OUTER
|
||||
case jwx.JWS:
|
||||
// Food for thought: This is going to break if you have multiple layers of
|
||||
// JWS enveloping using different keys. It is highly unlikely use case,
|
||||
// but it might happen.
|
||||
|
||||
// skipVerification should only be set to true by us. It's used
|
||||
// when we just want to parse the JWT out of a payload
|
||||
if !ctx.skipVerification {
|
||||
// nested return value means:
|
||||
// false (next envelope _may_ need to be processed)
|
||||
// true (next envelope MUST be processed)
|
||||
v, state, err := verifyJWS(ctx, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if state != _JwsVerifySkipped {
|
||||
payload = v
|
||||
|
||||
// We only check for cty and typ if the pedantic flag is enabled
|
||||
if !ctx.pedantic {
|
||||
continue
|
||||
}
|
||||
|
||||
if state == _JwsVerifyExpectNested {
|
||||
expectNested = true
|
||||
continue OUTER
|
||||
}
|
||||
|
||||
// if we're not nested, we found our target. bail out of this loop
|
||||
break OUTER
|
||||
}
|
||||
}
|
||||
|
||||
// No verification.
|
||||
m, err := jws.Parse(data)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, `invalid jws message`)
|
||||
}
|
||||
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)
|
||||
}
|
||||
expectNested = false
|
||||
}
|
||||
|
||||
if ctx.token == nil {
|
||||
ctx.token = New()
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
dc := json.NewDecodeCtx(ctx.localReg)
|
||||
dcToken.SetDecodeCtx(dc)
|
||||
defer func() { dcToken.SetDecodeCtx(nil) }()
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(payload, ctx.token); err != nil {
|
||||
return nil, errors.Wrap(err, `failed to parse token`)
|
||||
}
|
||||
|
||||
if ctx.validate {
|
||||
if err := Validate(ctx.token, ctx.validateOpts...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return ctx.token, nil
|
||||
}
|
||||
|
||||
// Sign is a convenience function to create a signed JWT token serialized in
|
||||
// compact form.
|
||||
//
|
||||
// It accepts either a raw key (e.g. rsa.PrivateKey, ecdsa.PrivateKey, etc)
|
||||
// or a jwk.Key, and the name of the algorithm that should be used to sign
|
||||
// the token.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
|
||||
// Equal compares two JWT tokens. Do not use `reflect.Equal` or the like
|
||||
// to compare tokens as they will also compare extra detail such as
|
||||
// sync.Mutex objects used to control concurrent access.
|
||||
//
|
||||
// The comparison for values is currently done using a simple equality ("=="),
|
||||
// except for time.Time, which uses time.Equal after dropping the monotonic
|
||||
// clock and truncating the values to 1 second accuracy.
|
||||
//
|
||||
// if both t1 and t2 are nil, returns true
|
||||
func Equal(t1, t2 Token) bool {
|
||||
if t1 == nil && t2 == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
// we already checked for t1 == t2 == nil, so safe to do this
|
||||
if t1 == nil || t2 == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
j1, err := json.Marshal(t1)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
j2, err := json.Marshal(t2)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return bytes.Equal(j1, j2)
|
||||
}
|
||||
|
||||
func (t *stdToken) Clone() (Token, error) {
|
||||
dst := New()
|
||||
|
||||
for _, pair := range t.makePairs() {
|
||||
//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 dst, 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
|
||||
//
|
||||
// jwt.RegisterCustomField(`x-birthday`, timeT)
|
||||
//
|
||||
// Then `token.Get("x-birthday")` will still return an `interface{}`,
|
||||
// but you can convert its type to `time.Time`
|
||||
//
|
||||
// bdayif, _ := token.Get(`x-birthday`)
|
||||
// bday := bdayif.(time.Time)
|
||||
func RegisterCustomField(name string, object interface{}) {
|
||||
registry.Register(name, object)
|
||||
}
|
||||
536
server/vendor/github.com/lestrrat-go/jwx/jwt/options.go
generated
vendored
Normal file
536
server/vendor/github.com/lestrrat-go/jwx/jwt/options.go
generated
vendored
Normal file
@@ -0,0 +1,536 @@
|
||||
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)
|
||||
}
|
||||
239
server/vendor/github.com/lestrrat-go/jwx/jwt/serialize.go
generated
vendored
Normal file
239
server/vendor/github.com/lestrrat-go/jwx/jwt/serialize.go
generated
vendored
Normal file
@@ -0,0 +1,239 @@
|
||||
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"
|
||||
)
|
||||
|
||||
type SerializeCtx interface {
|
||||
Step() int
|
||||
Nested() bool
|
||||
}
|
||||
|
||||
type serializeCtx struct {
|
||||
step int
|
||||
nested bool
|
||||
}
|
||||
|
||||
func (ctx *serializeCtx) Step() int {
|
||||
return ctx.step
|
||||
}
|
||||
|
||||
func (ctx *serializeCtx) Nested() bool {
|
||||
return ctx.nested
|
||||
}
|
||||
|
||||
type SerializeStep interface {
|
||||
Serialize(SerializeCtx, interface{}) (interface{}, error)
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// By default the serializer only marshals the token into a JSON payload.
|
||||
// You must set up the rest of the steps that should be taken by the
|
||||
// serializer.
|
||||
//
|
||||
// For example, to marshal the token into JSON, then apply JWS and JWE
|
||||
// in that order, you would do:
|
||||
//
|
||||
// serialized, err := jwt.NewSerialer().
|
||||
// Sign(jwa.RS256, key).
|
||||
// Encrypt(jwa.RSA_OAEP, key.PublicKey).
|
||||
// Serialize(token)
|
||||
//
|
||||
// The `jwt.Sign()` function is equivalent to
|
||||
//
|
||||
// serialized, err := jwt.NewSerializer().
|
||||
// Sign(...args...).
|
||||
// Serialize(token)
|
||||
type Serializer struct {
|
||||
steps []SerializeStep
|
||||
}
|
||||
|
||||
// NewSerializer creates a new empty serializer.
|
||||
func NewSerializer() *Serializer {
|
||||
return &Serializer{}
|
||||
}
|
||||
|
||||
// Reset clears all of the registered steps.
|
||||
func (s *Serializer) Reset() *Serializer {
|
||||
s.steps = nil
|
||||
return s
|
||||
}
|
||||
|
||||
// Step adds a new Step to the serialization process
|
||||
func (s *Serializer) Step(step SerializeStep) *Serializer {
|
||||
s.steps = append(s.steps, step)
|
||||
return s
|
||||
}
|
||||
|
||||
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`)
|
||||
}
|
||||
|
||||
buf, err := json.Marshal(token)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf(`failed to serialize as JSON`)
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
type genericHeader interface {
|
||||
Get(string) (interface{}, bool)
|
||||
Set(string, interface{}) error
|
||||
}
|
||||
|
||||
func setTypeOrCty(ctx SerializeCtx, hdrs genericHeader) error {
|
||||
// cty and typ are common between JWE/JWS, so we don't use
|
||||
// the constants in jws/jwe package here
|
||||
const typKey = `typ`
|
||||
const ctyKey = `cty`
|
||||
|
||||
if ctx.Step() == 1 {
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ctx.Nested() {
|
||||
// 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 nil
|
||||
}
|
||||
|
||||
type jwsSerializer struct {
|
||||
alg jwa.SignatureAlgorithm
|
||||
key interface{}
|
||||
options []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`)
|
||||
}
|
||||
|
||||
var hdrs jws.Headers
|
||||
//nolint:forcetypeassert
|
||||
for _, option := range s.options {
|
||||
switch option.Ident() {
|
||||
case identJwsHeaders{}:
|
||||
hdrs = option.Value().(jws.Headers)
|
||||
}
|
||||
}
|
||||
|
||||
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`)
|
||||
}
|
||||
}
|
||||
}
|
||||
return jws.Sign(payload, s.alg, s.key, jws.WithHeaders(hdrs))
|
||||
}
|
||||
|
||||
func (s *Serializer) Sign(alg jwa.SignatureAlgorithm, key interface{}, options ...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
|
||||
}
|
||||
|
||||
func (s *jweSerializer) Serialize(ctx SerializeCtx, v interface{}) (interface{}, error) {
|
||||
payload, ok := v.([]byte)
|
||||
if !ok {
|
||||
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()
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
func (s *Serializer) Encrypt(keyalg jwa.KeyEncryptionAlgorithm, key interface{}, contentalg jwa.ContentEncryptionAlgorithm, compressalg jwa.CompressionAlgorithm, options ...EncryptOption) *Serializer {
|
||||
return s.Step(&jweSerializer{
|
||||
keyalg: keyalg,
|
||||
key: key,
|
||||
contentalg: contentalg,
|
||||
compressalg: compressalg,
|
||||
options: options,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Serializer) Serialize(t Token) ([]byte, error) {
|
||||
steps := make([]SerializeStep, len(s.steps)+1)
|
||||
steps[0] = jsonSerializer{}
|
||||
for i, step := range s.steps {
|
||||
steps[i+1] = step
|
||||
}
|
||||
|
||||
var ctx serializeCtx
|
||||
ctx.nested = len(s.steps) > 1
|
||||
var payload interface{} = t
|
||||
for i, step := range steps {
|
||||
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)
|
||||
}
|
||||
payload = v
|
||||
}
|
||||
|
||||
res, ok := payload.([]byte)
|
||||
if !ok {
|
||||
return nil, errors.New(`invalid serialization produced`)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user