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:
14
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/.gitignore
generated
vendored
Normal file
14
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
||||
.glide/
|
||||
7
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/.golangci.yml
generated
vendored
Normal file
7
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/.golangci.yml
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
issues:
|
||||
exclude-rules:
|
||||
- path: /*_example_test.go
|
||||
linters:
|
||||
- errcheck
|
||||
- forbidigo
|
||||
|
||||
8
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/Changes
generated
vendored
Normal file
8
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/Changes
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
v2.0.8 - 28 Feb 2021
|
||||
* Fix possible goroutine leak (#30)
|
||||
|
||||
v2.0.7 - 26 Jan 2021
|
||||
* Cosmetic go.mod / go.sum changes
|
||||
|
||||
v2.0.6 - 25 Jan 2021
|
||||
* Add jitter to constant backoff
|
||||
21
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/LICENSE
generated
vendored
Normal file
21
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 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.
|
||||
183
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/README.md
generated
vendored
Normal file
183
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/README.md
generated
vendored
Normal file
@@ -0,0 +1,183 @@
|
||||
# backoff  [](https://pkg.go.dev/github.com/lestrrat-go/backoff/v2)
|
||||
|
||||
Idiomatic backoff for Go
|
||||
|
||||
This library is an implementation of backoff algorithm for retrying operations
|
||||
in an idiomatic Go way. It respects `context.Context` natively, and the critical
|
||||
notifications are done through *channel operations*, allowing you to write code
|
||||
that is both more explicit and flexibile.
|
||||
|
||||
For a longer discussion, [please read this article](https://medium.com/@lestrrat/yak-shaving-with-backoff-libraries-in-go-80240f0aa30c)
|
||||
|
||||
# IMPORT
|
||||
|
||||
```go
|
||||
import "github.com/lestrrat-go/backoff/v2"
|
||||
```
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
```go
|
||||
func ExampleExponential() {
|
||||
p := backoff.Exponential(
|
||||
backoff.WithMinInterval(time.Second),
|
||||
backoff.WithMaxInterval(time.Minute),
|
||||
backoff.WithJitterFactor(0.05),
|
||||
)
|
||||
|
||||
flakyFunc := func(a int) (int, error) {
|
||||
// silly function that only succeeds if the current call count is
|
||||
// divisible by either 3 or 5 but not both
|
||||
switch {
|
||||
case a%15 == 0:
|
||||
return 0, errors.New(`invalid`)
|
||||
case a%3 == 0 || a%5 == 0:
|
||||
return a, nil
|
||||
}
|
||||
return 0, errors.New(`invalid`)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
retryFunc := func(v int) (int, error) {
|
||||
b := p.Start(ctx)
|
||||
for backoff.Continue(b) {
|
||||
x, err := flakyFunc(v)
|
||||
if err == nil {
|
||||
return x, nil
|
||||
}
|
||||
}
|
||||
return 0, errors.New(`failed to get value`)
|
||||
}
|
||||
|
||||
retryFunc(15)
|
||||
}
|
||||
```
|
||||
|
||||
# POLICIES
|
||||
|
||||
Policy objects describe a backoff policy, and are factories to create backoff Controller objects.
|
||||
Controller objects does the actual coordination.
|
||||
Create a new controller for each invocation of a backoff enabled operation.
|
||||
This way the controller object is protected from concurrent access (if you have one) and can easily be discarded
|
||||
|
||||
## Null
|
||||
|
||||
A null policy means there's no backoff.
|
||||
|
||||
For example, if you were to support both using and not using a backoff in your code you can say
|
||||
|
||||
```go
|
||||
var p backoff.Policy
|
||||
if useBackoff {
|
||||
p = backoff.Exponential(...)
|
||||
} else {
|
||||
p = backoff.Null()
|
||||
}
|
||||
c := p.Start(ctx)
|
||||
for backoff.Continue(c) {
|
||||
if err := doSomething(); err != nil {
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
Instead of
|
||||
|
||||
```go
|
||||
if useBackoff {
|
||||
p := backoff.Exponential(...)
|
||||
c := p.Start(ctx)
|
||||
for backoff.Continue(c) {
|
||||
if err := doSomething(); err != nil {
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if err := doSomething(); err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Constant
|
||||
|
||||
A constant policy implements are backoff where the intervals are always the same
|
||||
|
||||
## Exponential
|
||||
|
||||
This is the most "common" of the backoffs. Intervals between calls are spaced out such that as you keep retrying, the intervals keep increasing.
|
||||
|
||||
# FAQ
|
||||
|
||||
## I'm getting "package github.com/lestrrat-go/backoff/v2: no Go files in /go/src/github.com/lestrrat-go/backoff/v2"
|
||||
|
||||
You are using Go in GOPATH mode, which was the way before [Go Modules](https://blog.golang.org/using-go-modules) were introduced in Go 1.11 (Aug 2018).
|
||||
GOPATH has slowly been phased out, and in Go 1.14 onwards, Go Modules pretty much Just Work.
|
||||
Go 1.16 introduced more visible changes that forces users to be aware of the existance of go.mod files.
|
||||
|
||||
The short answer when you you get the above error is: **Migrate to using Go Modules**.
|
||||
This is simple: All you need to do is to include a go.mod (and go.sum) file to your library or app.
|
||||
|
||||
For example, if you have previously been doing this:
|
||||
|
||||
```
|
||||
git clone git@github.com:myusername/myawesomeproject.git
|
||||
cd myawesomeproject
|
||||
go get ./...
|
||||
```
|
||||
|
||||
First include go.mod and go.sum in your repository:
|
||||
|
||||
```
|
||||
git clone git@github.com:myusername/myawesomeproject.git
|
||||
cd myawesomeproject
|
||||
go mod init
|
||||
go mod tidy
|
||||
git add go.mod go.sum
|
||||
git commit -m "Add go.mod and go.sum" -a
|
||||
git push
|
||||
```
|
||||
|
||||
Then from subsequent calls:
|
||||
|
||||
```
|
||||
git clone git@github.com:myusername/myawesomeproject.git
|
||||
cd myawesomeproject
|
||||
go build # or go test, or go run, or whatever.
|
||||
```
|
||||
|
||||
This will tell go to respect tags, and will automatically pick up the latest version of github.com/lestrrat-go/backoff
|
||||
|
||||
If you really can't do this, then the quick and dirty workaround is to just copy the files over to /v2 directory of this library
|
||||
|
||||
```
|
||||
BACKOFF=github.com/lestrrat-go/backoff
|
||||
go get github.com/lestrrat-go/backoff
|
||||
if [[ if ! -d "$GOPATH/src/$BACKOFF/v2" ]]; then
|
||||
mkdir "$GOPATH/src/$BACKOFF/v2" # just to make sure it exists
|
||||
fi
|
||||
cp "$GOPATH/src/$BACKOFF/*.go" "$GOPATH/src/$BACKOFF/v2"
|
||||
|
||||
git clone git@github.com:myusername/myawesomeproject.git
|
||||
cd myawesomeproject
|
||||
go get ./...
|
||||
```
|
||||
|
||||
## Why won't you just add the files in /v2?
|
||||
|
||||
Because it's hard to maintain multiple sources of truth. Sorry, I don't get paid to do this.
|
||||
I will not hold anything against you if you decided to fork this to your repository, and move files to your own /v2 directory.
|
||||
Then, if you have a go.mod in your app, you can just do
|
||||
|
||||
```
|
||||
go mod edit -replace github.com/lestrrat-go/backoff/v2=github.com/myusername/myawesomemfork/v2
|
||||
```
|
||||
|
||||
Oh, wait, then you already have go.mod, so this is a non-issue.
|
||||
|
||||
...Yeah, just migrate to using go modules, please?
|
||||
|
||||
31
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/backoff.go
generated
vendored
Normal file
31
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/backoff.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
package backoff
|
||||
|
||||
// Null creates a new NullPolicy object
|
||||
func Null() Policy {
|
||||
return NewNull()
|
||||
}
|
||||
|
||||
// Constant creates a new ConstantPolicy object
|
||||
func Constant(options ...Option) Policy {
|
||||
return NewConstantPolicy(options...)
|
||||
}
|
||||
|
||||
// Constant creates a new ExponentialPolicy object
|
||||
func Exponential(options ...ExponentialOption) Policy {
|
||||
return NewExponentialPolicy(options...)
|
||||
}
|
||||
|
||||
// Continue is a convenience function to check when we can fire
|
||||
// the next invocation of the desired backoff code
|
||||
//
|
||||
// for backoff.Continue(c) {
|
||||
// ... your code ...
|
||||
// }
|
||||
func Continue(c Controller) bool {
|
||||
select {
|
||||
case <-c.Done():
|
||||
return false
|
||||
case _, ok := <-c.Next():
|
||||
return ok
|
||||
}
|
||||
}
|
||||
66
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/constant.go
generated
vendored
Normal file
66
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/constant.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
package backoff
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ConstantInterval struct {
|
||||
interval time.Duration
|
||||
jitter jitter
|
||||
}
|
||||
|
||||
func NewConstantInterval(options ...ConstantOption) *ConstantInterval {
|
||||
jitterFactor := 0.0
|
||||
interval := time.Minute
|
||||
var rng Random
|
||||
|
||||
for _, option := range options {
|
||||
switch option.Ident() {
|
||||
case identInterval{}:
|
||||
interval = option.Value().(time.Duration)
|
||||
case identJitterFactor{}:
|
||||
jitterFactor = option.Value().(float64)
|
||||
case identRNG{}:
|
||||
rng = option.Value().(Random)
|
||||
}
|
||||
}
|
||||
|
||||
return &ConstantInterval{
|
||||
interval: interval,
|
||||
jitter: newJitter(jitterFactor, rng),
|
||||
}
|
||||
}
|
||||
|
||||
func (g *ConstantInterval) Next() time.Duration {
|
||||
return time.Duration(g.jitter.apply(float64(g.interval)))
|
||||
}
|
||||
|
||||
type ConstantPolicy struct {
|
||||
cOptions []ControllerOption
|
||||
igOptions []ConstantOption
|
||||
}
|
||||
|
||||
func NewConstantPolicy(options ...Option) *ConstantPolicy {
|
||||
var cOptions []ControllerOption
|
||||
var igOptions []ConstantOption
|
||||
|
||||
for _, option := range options {
|
||||
switch opt := option.(type) {
|
||||
case ControllerOption:
|
||||
cOptions = append(cOptions, opt)
|
||||
default:
|
||||
igOptions = append(igOptions, opt.(ConstantOption))
|
||||
}
|
||||
}
|
||||
|
||||
return &ConstantPolicy{
|
||||
cOptions: cOptions,
|
||||
igOptions: igOptions,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ConstantPolicy) Start(ctx context.Context) Controller {
|
||||
ig := NewConstantInterval(p.igOptions...)
|
||||
return newController(ctx, ig, p.cOptions...)
|
||||
}
|
||||
99
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/controller.go
generated
vendored
Normal file
99
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/controller.go
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
package backoff
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type controller struct {
|
||||
ctx context.Context
|
||||
cancel func()
|
||||
ig IntervalGenerator
|
||||
maxRetries int
|
||||
mu *sync.RWMutex
|
||||
next chan struct{} // user-facing channel
|
||||
resetTimer chan time.Duration
|
||||
retries int
|
||||
timer *time.Timer
|
||||
}
|
||||
|
||||
func newController(ctx context.Context, ig IntervalGenerator, options ...ControllerOption) *controller {
|
||||
cctx, cancel := context.WithCancel(ctx) // DO NOT fire this cancel here
|
||||
|
||||
maxRetries := 10
|
||||
for _, option := range options {
|
||||
switch option.Ident() {
|
||||
case identMaxRetries{}:
|
||||
maxRetries = option.Value().(int)
|
||||
}
|
||||
}
|
||||
|
||||
c := &controller{
|
||||
cancel: cancel,
|
||||
ctx: cctx,
|
||||
ig: ig,
|
||||
maxRetries: maxRetries,
|
||||
mu: &sync.RWMutex{},
|
||||
next: make(chan struct{}, 1),
|
||||
resetTimer: make(chan time.Duration, 1),
|
||||
timer: time.NewTimer(ig.Next()),
|
||||
}
|
||||
|
||||
// enqueue a single fake event so the user gets to retry once
|
||||
c.next <- struct{}{}
|
||||
|
||||
go c.loop()
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *controller) loop() {
|
||||
for {
|
||||
select {
|
||||
case <-c.ctx.Done():
|
||||
return
|
||||
case d := <-c.resetTimer:
|
||||
if !c.timer.Stop() {
|
||||
select {
|
||||
case <-c.timer.C:
|
||||
default:
|
||||
}
|
||||
}
|
||||
c.timer.Reset(d)
|
||||
case <-c.timer.C:
|
||||
select {
|
||||
case <-c.ctx.Done():
|
||||
return
|
||||
case c.next <- struct{}{}:
|
||||
}
|
||||
if c.maxRetries > 0 {
|
||||
c.retries++
|
||||
}
|
||||
|
||||
if !c.check() {
|
||||
c.cancel()
|
||||
return
|
||||
}
|
||||
c.resetTimer <- c.ig.Next()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *controller) check() bool {
|
||||
if c.maxRetries > 0 && c.retries >= c.maxRetries {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *controller) Done() <-chan struct{} {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
return c.ctx.Done()
|
||||
}
|
||||
|
||||
func (c *controller) Next() <-chan struct{} {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
return c.next
|
||||
}
|
||||
6
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/doc.go
generated
vendored
Normal file
6
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/doc.go
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
// Package backoff implments backoff algorithms for retrying operations.
|
||||
//
|
||||
// Users first create an appropriate `Policy` object, and when the operation
|
||||
// that needs retrying is about to start, they kick the actual backoff
|
||||
//
|
||||
package backoff
|
||||
107
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/exponential.go
generated
vendored
Normal file
107
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/exponential.go
generated
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
package backoff
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ExponentialInterval struct {
|
||||
current float64
|
||||
maxInterval float64
|
||||
minInterval float64
|
||||
multiplier float64
|
||||
jitter jitter
|
||||
}
|
||||
|
||||
const (
|
||||
defaultMaxInterval = float64(time.Minute)
|
||||
defaultMinInterval = float64(500 * time.Millisecond)
|
||||
defaultMultiplier = 1.5
|
||||
)
|
||||
|
||||
func NewExponentialInterval(options ...ExponentialOption) *ExponentialInterval {
|
||||
jitterFactor := 0.0
|
||||
maxInterval := defaultMaxInterval
|
||||
minInterval := defaultMinInterval
|
||||
multiplier := defaultMultiplier
|
||||
var rng Random
|
||||
|
||||
for _, option := range options {
|
||||
switch option.Ident() {
|
||||
case identJitterFactor{}:
|
||||
jitterFactor = option.Value().(float64)
|
||||
case identMaxInterval{}:
|
||||
maxInterval = float64(option.Value().(time.Duration))
|
||||
case identMinInterval{}:
|
||||
minInterval = float64(option.Value().(time.Duration))
|
||||
case identMultiplier{}:
|
||||
multiplier = option.Value().(float64)
|
||||
case identRNG{}:
|
||||
rng = option.Value().(Random)
|
||||
}
|
||||
}
|
||||
|
||||
if minInterval > maxInterval {
|
||||
minInterval = maxInterval
|
||||
}
|
||||
if multiplier <= 1 {
|
||||
multiplier = defaultMultiplier
|
||||
}
|
||||
|
||||
return &ExponentialInterval{
|
||||
maxInterval: maxInterval,
|
||||
minInterval: minInterval,
|
||||
multiplier: multiplier,
|
||||
jitter: newJitter(jitterFactor, rng),
|
||||
}
|
||||
}
|
||||
|
||||
func (g *ExponentialInterval) Next() time.Duration {
|
||||
var next float64
|
||||
if g.current == 0 {
|
||||
next = g.minInterval
|
||||
} else {
|
||||
next = g.current * g.multiplier
|
||||
}
|
||||
|
||||
if next > g.maxInterval {
|
||||
next = g.maxInterval
|
||||
}
|
||||
if next < g.minInterval {
|
||||
next = g.minInterval
|
||||
}
|
||||
|
||||
// Apply jitter *AFTER* we calculate the base interval
|
||||
next = g.jitter.apply(next)
|
||||
g.current = next
|
||||
return time.Duration(next)
|
||||
}
|
||||
|
||||
type ExponentialPolicy struct {
|
||||
cOptions []ControllerOption
|
||||
igOptions []ExponentialOption
|
||||
}
|
||||
|
||||
func NewExponentialPolicy(options ...ExponentialOption) *ExponentialPolicy {
|
||||
var cOptions []ControllerOption
|
||||
var igOptions []ExponentialOption
|
||||
|
||||
for _, option := range options {
|
||||
switch opt := option.(type) {
|
||||
case ControllerOption:
|
||||
cOptions = append(cOptions, opt)
|
||||
default:
|
||||
igOptions = append(igOptions, opt)
|
||||
}
|
||||
}
|
||||
|
||||
return &ExponentialPolicy{
|
||||
cOptions: cOptions,
|
||||
igOptions: igOptions,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ExponentialPolicy) Start(ctx context.Context) Controller {
|
||||
ig := NewExponentialInterval(p.igOptions...)
|
||||
return newController(ctx, ig, p.cOptions...)
|
||||
}
|
||||
30
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/interface.go
generated
vendored
Normal file
30
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/interface.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
package backoff
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/lestrrat-go/option"
|
||||
)
|
||||
|
||||
type Option = option.Interface
|
||||
|
||||
type Controller interface {
|
||||
Done() <-chan struct{}
|
||||
Next() <-chan struct{}
|
||||
}
|
||||
|
||||
type IntervalGenerator interface {
|
||||
Next() time.Duration
|
||||
}
|
||||
|
||||
// Policy is an interface for the backoff policies that this package
|
||||
// implements. Users must create a controller object from this
|
||||
// policy to actually do anything with it
|
||||
type Policy interface {
|
||||
Start(context.Context) Controller
|
||||
}
|
||||
|
||||
type Random interface {
|
||||
Float64() float64
|
||||
}
|
||||
59
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/jitter.go
generated
vendored
Normal file
59
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/jitter.go
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
package backoff
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
type jitter interface {
|
||||
apply(interval float64) float64
|
||||
}
|
||||
|
||||
func newJitter(jitterFactor float64, rng Random) jitter {
|
||||
if jitterFactor <= 0 || jitterFactor >= 1 {
|
||||
return newNopJitter()
|
||||
}
|
||||
return newRandomJitter(jitterFactor, rng)
|
||||
}
|
||||
|
||||
type nopJitter struct{}
|
||||
|
||||
func newNopJitter() *nopJitter {
|
||||
return &nopJitter{}
|
||||
}
|
||||
|
||||
func (j *nopJitter) apply(interval float64) float64 {
|
||||
return interval
|
||||
}
|
||||
|
||||
type randomJitter struct {
|
||||
jitterFactor float64
|
||||
rng Random
|
||||
}
|
||||
|
||||
func newRandomJitter(jitterFactor float64, rng Random) *randomJitter {
|
||||
if rng == nil {
|
||||
// if we have a jitter factor, and no RNG is provided, create one.
|
||||
// This is definitely not "secure", but well, if you care enough,
|
||||
// you would provide one
|
||||
rng = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
}
|
||||
|
||||
return &randomJitter{
|
||||
jitterFactor: jitterFactor,
|
||||
rng: rng,
|
||||
}
|
||||
}
|
||||
|
||||
func (j *randomJitter) apply(interval float64) float64 {
|
||||
jitterDelta := interval * j.jitterFactor
|
||||
jitterMin := interval - jitterDelta
|
||||
jitterMax := interval + jitterDelta
|
||||
|
||||
// Get a random value from the range [minInterval, maxInterval].
|
||||
// The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then
|
||||
// we want a 33% chance for selecting either 1, 2 or 3.
|
||||
//
|
||||
// see also: https://github.com/cenkalti/backoff/blob/c2975ffa541a1caeca5f76c396cb8c3e7b3bb5f8/exponential.go#L154-L157
|
||||
return jitterMin + j.rng.Float64()*(jitterMax-jitterMin+1)
|
||||
}
|
||||
51
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/null.go
generated
vendored
Normal file
51
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/null.go
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
package backoff
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// NullPolicy does not do any backoff. It allows the caller
|
||||
// to execute the desired code once, and no more
|
||||
type NullPolicy struct{}
|
||||
|
||||
func NewNull() *NullPolicy {
|
||||
return &NullPolicy{}
|
||||
}
|
||||
|
||||
func (p *NullPolicy) Start(ctx context.Context) Controller {
|
||||
return newNullController(ctx)
|
||||
}
|
||||
|
||||
type nullController struct {
|
||||
mu *sync.RWMutex
|
||||
ctx context.Context
|
||||
next chan struct{}
|
||||
}
|
||||
|
||||
func newNullController(ctx context.Context) *nullController {
|
||||
cctx, cancel := context.WithCancel(ctx)
|
||||
c := &nullController{
|
||||
mu: &sync.RWMutex{},
|
||||
ctx: cctx,
|
||||
next: make(chan struct{}), // NO BUFFER
|
||||
}
|
||||
go func(ch chan struct{}, cancel func()) {
|
||||
ch <- struct{}{}
|
||||
close(ch)
|
||||
cancel()
|
||||
}(c.next, cancel)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *nullController) Done() <-chan struct{} {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
return c.ctx.Done()
|
||||
}
|
||||
|
||||
func (c *nullController) Next() <-chan struct{} {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
return c.next
|
||||
}
|
||||
127
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/options.go
generated
vendored
Normal file
127
extra/server-discovery/vendor/github.com/lestrrat-go/backoff/v2/options.go
generated
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
package backoff
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/lestrrat-go/option"
|
||||
)
|
||||
|
||||
type identInterval struct{}
|
||||
type identJitterFactor struct{}
|
||||
type identMaxInterval struct{}
|
||||
type identMaxRetries struct{}
|
||||
type identMinInterval struct{}
|
||||
type identMultiplier struct{}
|
||||
type identRNG struct{}
|
||||
|
||||
// ControllerOption is an option that may be passed to Policy objects,
|
||||
// but are ultimately passed down to the Controller objects.
|
||||
// (Normally you do not have to care about the distinction)
|
||||
type ControllerOption interface {
|
||||
ConstantOption
|
||||
ExponentialOption
|
||||
CommonOption
|
||||
controllerOption()
|
||||
}
|
||||
|
||||
type controllerOption struct {
|
||||
Option
|
||||
}
|
||||
|
||||
func (*controllerOption) exponentialOption() {}
|
||||
func (*controllerOption) controllerOption() {}
|
||||
func (*controllerOption) constantOption() {}
|
||||
|
||||
// ConstantOption is an option that is used by the Constant policy.
|
||||
type ConstantOption interface {
|
||||
Option
|
||||
constantOption()
|
||||
}
|
||||
|
||||
type constantOption struct {
|
||||
Option
|
||||
}
|
||||
|
||||
func (*constantOption) constantOption() {}
|
||||
|
||||
// ExponentialOption is an option that is used by the Exponential policy.
|
||||
type ExponentialOption interface {
|
||||
Option
|
||||
exponentialOption()
|
||||
}
|
||||
|
||||
type exponentialOption struct {
|
||||
Option
|
||||
}
|
||||
|
||||
func (*exponentialOption) exponentialOption() {}
|
||||
|
||||
// CommonOption is an option that can be passed to any of the backoff policies.
|
||||
type CommonOption interface {
|
||||
ExponentialOption
|
||||
ConstantOption
|
||||
}
|
||||
|
||||
type commonOption struct {
|
||||
Option
|
||||
}
|
||||
|
||||
func (*commonOption) constantOption() {}
|
||||
func (*commonOption) exponentialOption() {}
|
||||
|
||||
// WithMaxRetries specifies the maximum number of attempts that can be made
|
||||
// by the backoff policies. By default each policy tries up to 10 times.
|
||||
//
|
||||
// If you would like to retry forever, specify "0" and pass to the constructor
|
||||
// of each policy.
|
||||
//
|
||||
// This option can be passed to all policy constructors except for NullPolicy
|
||||
func WithMaxRetries(v int) ControllerOption {
|
||||
return &controllerOption{option.New(identMaxRetries{}, v)}
|
||||
}
|
||||
|
||||
// WithInterval specifies the constant interval used in ConstantPolicy and
|
||||
// ConstantInterval.
|
||||
// The default value is 1 minute.
|
||||
func WithInterval(v time.Duration) ConstantOption {
|
||||
return &constantOption{option.New(identInterval{}, v)}
|
||||
}
|
||||
|
||||
// WithMaxInterval specifies the maximum duration used in exponential backoff
|
||||
// The default value is 1 minute.
|
||||
func WithMaxInterval(v time.Duration) ExponentialOption {
|
||||
return &exponentialOption{option.New(identMaxInterval{}, v)}
|
||||
}
|
||||
|
||||
// WithMinInterval specifies the minimum duration used in exponential backoff.
|
||||
// The default value is 500ms.
|
||||
func WithMinInterval(v time.Duration) ExponentialOption {
|
||||
return &exponentialOption{option.New(identMinInterval{}, v)}
|
||||
}
|
||||
|
||||
// WithMultiplier specifies the factor in which the backoff intervals are
|
||||
// increased. By default this value is set to 1.5, which means that for
|
||||
// every iteration a 50% increase in the interval for every iteration
|
||||
// (up to the value specified by WithMaxInterval). this value must be greater
|
||||
// than 1.0. If the value is less than equal to 1.0, the default value
|
||||
// of 1.5 is used.
|
||||
func WithMultiplier(v float64) ExponentialOption {
|
||||
return &exponentialOption{option.New(identMultiplier{}, v)}
|
||||
}
|
||||
|
||||
// WithJitterFactor enables some randomness (jittering) in the computation of
|
||||
// the backoff intervals. This value must be between 0.0 < v < 1.0. If a
|
||||
// value outside of this range is specified, the value will be silently
|
||||
// ignored and jittering is disabled.
|
||||
//
|
||||
// This option can be passed to ExponentialPolicy or ConstantPolicy constructor
|
||||
func WithJitterFactor(v float64) CommonOption {
|
||||
return &commonOption{option.New(identJitterFactor{}, v)}
|
||||
}
|
||||
|
||||
// WithRNG specifies the random number generator used for jittering.
|
||||
// If not provided one will be created, but if you want a truly random
|
||||
// jittering, make sure to provide one that you explicitly initialized
|
||||
func WithRNG(v Random) CommonOption {
|
||||
return &commonOption{option.New(identRNG{}, v)}
|
||||
}
|
||||
15
extra/server-discovery/vendor/github.com/lestrrat-go/blackmagic/.gitignore
generated
vendored
Normal file
15
extra/server-discovery/vendor/github.com/lestrrat-go/blackmagic/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
21
extra/server-discovery/vendor/github.com/lestrrat-go/blackmagic/LICENSE
generated
vendored
Normal file
21
extra/server-discovery/vendor/github.com/lestrrat-go/blackmagic/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 lestrrat-go
|
||||
|
||||
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.
|
||||
3
extra/server-discovery/vendor/github.com/lestrrat-go/blackmagic/README.md
generated
vendored
Normal file
3
extra/server-discovery/vendor/github.com/lestrrat-go/blackmagic/README.md
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# blackmagic
|
||||
|
||||
Reflect-based black magic. YMMV, and use with caution
|
||||
90
extra/server-discovery/vendor/github.com/lestrrat-go/blackmagic/blackmagic.go
generated
vendored
Normal file
90
extra/server-discovery/vendor/github.com/lestrrat-go/blackmagic/blackmagic.go
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
package blackmagic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// AssignField is a convenience function to assign a value to
|
||||
// an optional struct field. In Go, an optional struct field is
|
||||
// usually denoted by a pointer to T instead of T:
|
||||
//
|
||||
// type Object struct {
|
||||
// Optional *T
|
||||
// }
|
||||
//
|
||||
// This gets a bit cumbersome when you want to assign literals
|
||||
// or you do not want to worry about taking the address of a
|
||||
// variable.
|
||||
//
|
||||
// Object.Optional = &"foo" // doesn't compile!
|
||||
//
|
||||
// Instead you can use this function to do it in one line:
|
||||
//
|
||||
// blackmagic.AssignOptionalField(&Object.Optionl, "foo")
|
||||
func AssignOptionalField(dst, src interface{}) error {
|
||||
dstRV := reflect.ValueOf(dst)
|
||||
srcRV := reflect.ValueOf(src)
|
||||
if dstRV.Kind() != reflect.Pointer || dstRV.Elem().Kind() != reflect.Pointer {
|
||||
return fmt.Errorf(`dst must be a pointer to a field that is turn a pointer of src (%T)`, src)
|
||||
}
|
||||
|
||||
if !dstRV.Elem().CanSet() {
|
||||
return fmt.Errorf(`dst (%T) is not assignable`, dstRV.Elem().Interface())
|
||||
}
|
||||
if !reflect.PtrTo(srcRV.Type()).AssignableTo(dstRV.Elem().Type()) {
|
||||
return fmt.Errorf(`cannot assign src (%T) to dst (%T)`, src, dst)
|
||||
}
|
||||
|
||||
ptr := reflect.New(srcRV.Type())
|
||||
ptr.Elem().Set(srcRV)
|
||||
dstRV.Elem().Set(ptr)
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssignIfCompatible is a convenience function to safely
|
||||
// assign arbitrary values. dst must be a pointer to an
|
||||
// empty interface, or it must be a pointer to a compatible
|
||||
// variable type that can hold src.
|
||||
func AssignIfCompatible(dst, src interface{}) error {
|
||||
orv := reflect.ValueOf(src) // save this value for error reporting
|
||||
result := orv
|
||||
|
||||
// t can be a pointer or a slice, and the code will slightly change
|
||||
// depending on this
|
||||
var isPtr bool
|
||||
var isSlice bool
|
||||
switch result.Kind() {
|
||||
case reflect.Ptr:
|
||||
isPtr = true
|
||||
case reflect.Slice:
|
||||
isSlice = true
|
||||
}
|
||||
|
||||
rv := reflect.ValueOf(dst)
|
||||
if rv.Kind() != reflect.Ptr {
|
||||
return fmt.Errorf(`destination argument to AssignIfCompatible() must be a pointer: %T`, dst)
|
||||
}
|
||||
|
||||
actualDst := rv.Elem()
|
||||
switch actualDst.Kind() {
|
||||
case reflect.Interface:
|
||||
// If it's an interface, we can just assign the pointer to the interface{}
|
||||
default:
|
||||
// If it's a pointer to the struct we're looking for, we need to set
|
||||
// the de-referenced struct
|
||||
if !isSlice && isPtr {
|
||||
result = result.Elem()
|
||||
}
|
||||
}
|
||||
if !result.Type().AssignableTo(actualDst.Type()) {
|
||||
return fmt.Errorf(`argument to AssignIfCompatible() must be compatible with %T (was %T)`, orv.Interface(), dst)
|
||||
}
|
||||
|
||||
if !actualDst.CanSet() {
|
||||
return fmt.Errorf(`argument to AssignIfCompatible() must be settable`)
|
||||
}
|
||||
actualDst.Set(result)
|
||||
|
||||
return nil
|
||||
}
|
||||
15
extra/server-discovery/vendor/github.com/lestrrat-go/httpcc/.gitignore
generated
vendored
Normal file
15
extra/server-discovery/vendor/github.com/lestrrat-go/httpcc/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
21
extra/server-discovery/vendor/github.com/lestrrat-go/httpcc/LICENSE
generated
vendored
Normal file
21
extra/server-discovery/vendor/github.com/lestrrat-go/httpcc/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 lestrrat-go
|
||||
|
||||
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.
|
||||
35
extra/server-discovery/vendor/github.com/lestrrat-go/httpcc/README.md
generated
vendored
Normal file
35
extra/server-discovery/vendor/github.com/lestrrat-go/httpcc/README.md
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
httpcc
|
||||
======
|
||||
|
||||
Parses HTTP/1.1 Cache-Control header, and returns a struct that is convenient
|
||||
for the end-user to do what they will with.
|
||||
|
||||
# Parsing the HTTP Request
|
||||
|
||||
```go
|
||||
dir, err := httpcc.ParseRequest(req.Header.Get(`Cache-Control`))
|
||||
// dir.MaxAge() uint64, bool
|
||||
// dir.MaxStale() uint64, bool
|
||||
// dir.MinFresh() uint64, bool
|
||||
// dir.NoCache() bool
|
||||
// dir.NoStore() bool
|
||||
// dir.NoTransform() bool
|
||||
// dir.OnlyIfCached() bool
|
||||
// dir.Extensions() map[string]string
|
||||
```
|
||||
|
||||
# Parsing the HTTP Response
|
||||
|
||||
```go
|
||||
directives, err := httpcc.ParseResponse(res.Header.Get(`Cache-Control`))
|
||||
// dir.MaxAge() uint64, bool
|
||||
// dir.MustRevalidate() bool
|
||||
// dir.NoCache() []string
|
||||
// dir.NoStore() bool
|
||||
// dir.NoTransform() bool
|
||||
// dir.Public() bool
|
||||
// dir.Private() bool
|
||||
// dir.SMaxAge() uint64, bool
|
||||
// dir.Extensions() map[string]string
|
||||
```
|
||||
|
||||
117
extra/server-discovery/vendor/github.com/lestrrat-go/httpcc/directives.go
generated
vendored
Normal file
117
extra/server-discovery/vendor/github.com/lestrrat-go/httpcc/directives.go
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
package httpcc
|
||||
|
||||
type RequestDirective struct {
|
||||
maxAge *uint64
|
||||
maxStale *uint64
|
||||
minFresh *uint64
|
||||
noCache bool
|
||||
noStore bool
|
||||
noTransform bool
|
||||
onlyIfCached bool
|
||||
extensions map[string]string
|
||||
}
|
||||
|
||||
func (d *RequestDirective) MaxAge() (uint64, bool) {
|
||||
if v := d.maxAge; v != nil {
|
||||
return *v, true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func (d *RequestDirective) MaxStale() (uint64, bool) {
|
||||
if v := d.maxStale; v != nil {
|
||||
return *v, true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func (d *RequestDirective) MinFresh() (uint64, bool) {
|
||||
if v := d.minFresh; v != nil {
|
||||
return *v, true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func (d *RequestDirective) NoCache() bool {
|
||||
return d.noCache
|
||||
}
|
||||
|
||||
func (d *RequestDirective) NoStore() bool {
|
||||
return d.noStore
|
||||
}
|
||||
|
||||
func (d *RequestDirective) NoTransform() bool {
|
||||
return d.noTransform
|
||||
}
|
||||
|
||||
func (d *RequestDirective) OnlyIfCached() bool {
|
||||
return d.onlyIfCached
|
||||
}
|
||||
|
||||
func (d *RequestDirective) Extensions() map[string]string {
|
||||
return d.extensions
|
||||
}
|
||||
|
||||
func (d *RequestDirective) Extension(s string) string {
|
||||
return d.extensions[s]
|
||||
}
|
||||
|
||||
type ResponseDirective struct {
|
||||
maxAge *uint64
|
||||
noCache []string
|
||||
noStore bool
|
||||
noTransform bool
|
||||
public bool
|
||||
private []string
|
||||
proxyRevalidate bool
|
||||
sMaxAge *uint64
|
||||
extensions map[string]string
|
||||
}
|
||||
|
||||
func (d *ResponseDirective) MaxAge() (uint64, bool) {
|
||||
if v := d.maxAge; v != nil {
|
||||
return *v, true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func (d *ResponseDirective) NoCache() []string {
|
||||
return d.noCache
|
||||
}
|
||||
|
||||
func (d *ResponseDirective) NoStore() bool {
|
||||
return d.noStore
|
||||
}
|
||||
|
||||
func (d *ResponseDirective) NoTransform() bool {
|
||||
return d.noTransform
|
||||
}
|
||||
|
||||
func (d *ResponseDirective) Public() bool {
|
||||
return d.public
|
||||
}
|
||||
|
||||
func (d *ResponseDirective) Private() []string {
|
||||
return d.private
|
||||
}
|
||||
|
||||
func (d *ResponseDirective) ProxyRevalidate() bool {
|
||||
return d.proxyRevalidate
|
||||
}
|
||||
|
||||
func (d *ResponseDirective) SMaxAge() (uint64, bool) {
|
||||
if v := d.sMaxAge; v != nil {
|
||||
return *v, true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func (d *ResponseDirective) Extensions() map[string]string {
|
||||
return d.extensions
|
||||
}
|
||||
|
||||
func (d *ResponseDirective) Extension(s string) string {
|
||||
return d.extensions[s]
|
||||
}
|
||||
|
||||
|
||||
310
extra/server-discovery/vendor/github.com/lestrrat-go/httpcc/httpcc.go
generated
vendored
Normal file
310
extra/server-discovery/vendor/github.com/lestrrat-go/httpcc/httpcc.go
generated
vendored
Normal file
@@ -0,0 +1,310 @@
|
||||
package httpcc
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const (
|
||||
// Request Cache-Control directives
|
||||
MaxAge = "max-age" // used in response as well
|
||||
MaxStale = "max-stale"
|
||||
MinFresh = "min-fresh"
|
||||
NoCache = "no-cache" // used in response as well
|
||||
NoStore = "no-store" // used in response as well
|
||||
NoTransform = "no-transform" // used in response as well
|
||||
OnlyIfCached = "only-if-cached"
|
||||
|
||||
// Response Cache-Control directive
|
||||
MustRevalidate = "must-revalidate"
|
||||
Public = "public"
|
||||
Private = "private"
|
||||
ProxyRevalidate = "proxy-revalidate"
|
||||
SMaxAge = "s-maxage"
|
||||
)
|
||||
|
||||
type TokenPair struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
type TokenValuePolicy int
|
||||
|
||||
const (
|
||||
NoArgument TokenValuePolicy = iota
|
||||
TokenOnly
|
||||
QuotedStringOnly
|
||||
AnyTokenValue
|
||||
)
|
||||
|
||||
type directiveValidator interface {
|
||||
Validate(string) TokenValuePolicy
|
||||
}
|
||||
type directiveValidatorFn func(string) TokenValuePolicy
|
||||
|
||||
func (fn directiveValidatorFn) Validate(ccd string) TokenValuePolicy {
|
||||
return fn(ccd)
|
||||
}
|
||||
|
||||
func responseDirectiveValidator(s string) TokenValuePolicy {
|
||||
switch s {
|
||||
case MustRevalidate, NoStore, NoTransform, Public, ProxyRevalidate:
|
||||
return NoArgument
|
||||
case NoCache, Private:
|
||||
return QuotedStringOnly
|
||||
case MaxAge, SMaxAge:
|
||||
return TokenOnly
|
||||
default:
|
||||
return AnyTokenValue
|
||||
}
|
||||
}
|
||||
|
||||
func requestDirectiveValidator(s string) TokenValuePolicy {
|
||||
switch s {
|
||||
case MaxAge, MaxStale, MinFresh:
|
||||
return TokenOnly
|
||||
case NoCache, NoStore, NoTransform, OnlyIfCached:
|
||||
return NoArgument
|
||||
default:
|
||||
return AnyTokenValue
|
||||
}
|
||||
}
|
||||
|
||||
// ParseRequestDirective parses a single token.
|
||||
func ParseRequestDirective(s string) (*TokenPair, error) {
|
||||
return parseDirective(s, directiveValidatorFn(requestDirectiveValidator))
|
||||
}
|
||||
|
||||
func ParseResponseDirective(s string) (*TokenPair, error) {
|
||||
return parseDirective(s, directiveValidatorFn(responseDirectiveValidator))
|
||||
}
|
||||
|
||||
func parseDirective(s string, ccd directiveValidator) (*TokenPair, error) {
|
||||
s = strings.TrimSpace(s)
|
||||
|
||||
i := strings.IndexByte(s, '=')
|
||||
if i == -1 {
|
||||
return &TokenPair{Name: s}, nil
|
||||
}
|
||||
|
||||
pair := &TokenPair{Name: strings.TrimSpace(s[:i])}
|
||||
|
||||
if len(s) <= i {
|
||||
// `key=` feels like it's a parse error, but it's HTTP...
|
||||
// for now, return as if nothing happened.
|
||||
return pair, nil
|
||||
}
|
||||
|
||||
v := strings.TrimSpace(s[i+1:])
|
||||
switch ccd.Validate(pair.Name) {
|
||||
case TokenOnly:
|
||||
if v[0] == '"' {
|
||||
return nil, fmt.Errorf(`invalid value for %s (quoted string not allowed)`, pair.Name)
|
||||
}
|
||||
case QuotedStringOnly: // quoted-string only
|
||||
if v[0] != '"' {
|
||||
return nil, fmt.Errorf(`invalid value for %s (bare token not allowed)`, pair.Name)
|
||||
}
|
||||
tmp, err := strconv.Unquote(v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`malformed quoted string in token`)
|
||||
}
|
||||
v = tmp
|
||||
case AnyTokenValue:
|
||||
if v[0] == '"' {
|
||||
tmp, err := strconv.Unquote(v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`malformed quoted string in token`)
|
||||
}
|
||||
v = tmp
|
||||
}
|
||||
case NoArgument:
|
||||
if len(v) > 0 {
|
||||
return nil, fmt.Errorf(`received argument to directive %s`, pair.Name)
|
||||
}
|
||||
}
|
||||
|
||||
pair.Value = v
|
||||
return pair, nil
|
||||
}
|
||||
|
||||
func ParseResponseDirectives(s string) ([]*TokenPair, error) {
|
||||
return parseDirectives(s, ParseResponseDirective)
|
||||
}
|
||||
|
||||
func ParseRequestDirectives(s string) ([]*TokenPair, error) {
|
||||
return parseDirectives(s, ParseRequestDirective)
|
||||
}
|
||||
|
||||
func parseDirectives(s string, p func(string) (*TokenPair, error)) ([]*TokenPair, error) {
|
||||
scanner := bufio.NewScanner(strings.NewReader(s))
|
||||
scanner.Split(scanCommaSeparatedWords)
|
||||
|
||||
var tokens []*TokenPair
|
||||
for scanner.Scan() {
|
||||
tok, err := p(scanner.Text())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to parse token #%d: %w`, len(tokens)+1, err)
|
||||
}
|
||||
tokens = append(tokens, tok)
|
||||
}
|
||||
return tokens, nil
|
||||
}
|
||||
|
||||
// isSpace reports whether the character is a Unicode white space character.
|
||||
// We avoid dependency on the unicode package, but check validity of the implementation
|
||||
// in the tests.
|
||||
func isSpace(r rune) bool {
|
||||
if r <= '\u00FF' {
|
||||
// Obvious ASCII ones: \t through \r plus space. Plus two Latin-1 oddballs.
|
||||
switch r {
|
||||
case ' ', '\t', '\n', '\v', '\f', '\r':
|
||||
return true
|
||||
case '\u0085', '\u00A0':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
// High-valued ones.
|
||||
if '\u2000' <= r && r <= '\u200a' {
|
||||
return true
|
||||
}
|
||||
switch r {
|
||||
case '\u1680', '\u2028', '\u2029', '\u202f', '\u205f', '\u3000':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func scanCommaSeparatedWords(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
||||
// Skip leading spaces.
|
||||
start := 0
|
||||
for width := 0; start < len(data); start += width {
|
||||
var r rune
|
||||
r, width = utf8.DecodeRune(data[start:])
|
||||
if !isSpace(r) {
|
||||
break
|
||||
}
|
||||
}
|
||||
// Scan until we find a comma. Keep track of consecutive whitespaces
|
||||
// so we remove them from the end result
|
||||
var ws int
|
||||
for width, i := 0, start; i < len(data); i += width {
|
||||
var r rune
|
||||
r, width = utf8.DecodeRune(data[i:])
|
||||
switch {
|
||||
case isSpace(r):
|
||||
ws++
|
||||
case r == ',':
|
||||
return i + width, data[start : i-ws], nil
|
||||
default:
|
||||
ws = 0
|
||||
}
|
||||
}
|
||||
|
||||
// If we're at EOF, we have a final, non-empty, non-terminated word. Return it.
|
||||
if atEOF && len(data) > start {
|
||||
return len(data), data[start : len(data)-ws], nil
|
||||
}
|
||||
|
||||
// Request more data.
|
||||
return start, nil, nil
|
||||
}
|
||||
|
||||
// ParseRequest parses the content of `Cache-Control` header of an HTTP Request.
|
||||
func ParseRequest(v string) (*RequestDirective, error) {
|
||||
var dir RequestDirective
|
||||
tokens, err := ParseRequestDirectives(v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to parse tokens: %w`, err)
|
||||
}
|
||||
|
||||
for _, token := range tokens {
|
||||
name := strings.ToLower(token.Name)
|
||||
switch name {
|
||||
case MaxAge:
|
||||
iv, err := strconv.ParseUint(token.Value, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to parse max-age: %w`, err)
|
||||
}
|
||||
dir.maxAge = &iv
|
||||
case MaxStale:
|
||||
iv, err := strconv.ParseUint(token.Value, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to parse max-stale: %w`, err)
|
||||
}
|
||||
dir.maxStale = &iv
|
||||
case MinFresh:
|
||||
iv, err := strconv.ParseUint(token.Value, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to parse min-fresh: %w`, err)
|
||||
}
|
||||
dir.minFresh = &iv
|
||||
case NoCache:
|
||||
dir.noCache = true
|
||||
case NoStore:
|
||||
dir.noStore = true
|
||||
case NoTransform:
|
||||
dir.noTransform = true
|
||||
case OnlyIfCached:
|
||||
dir.onlyIfCached = true
|
||||
default:
|
||||
dir.extensions[token.Name] = token.Value
|
||||
}
|
||||
}
|
||||
return &dir, nil
|
||||
}
|
||||
|
||||
// ParseResponse parses the content of `Cache-Control` header of an HTTP Response.
|
||||
func ParseResponse(v string) (*ResponseDirective, error) {
|
||||
tokens, err := ParseResponseDirectives(v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to parse tokens: %w`, err)
|
||||
}
|
||||
|
||||
var dir ResponseDirective
|
||||
dir.extensions = make(map[string]string)
|
||||
for _, token := range tokens {
|
||||
name := strings.ToLower(token.Name)
|
||||
switch name {
|
||||
case MaxAge:
|
||||
iv, err := strconv.ParseUint(token.Value, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to parse max-age: %w`, err)
|
||||
}
|
||||
dir.maxAge = &iv
|
||||
case NoCache:
|
||||
scanner := bufio.NewScanner(strings.NewReader(token.Value))
|
||||
scanner.Split(scanCommaSeparatedWords)
|
||||
for scanner.Scan() {
|
||||
dir.noCache = append(dir.noCache, scanner.Text())
|
||||
}
|
||||
case NoStore:
|
||||
dir.noStore = true
|
||||
case NoTransform:
|
||||
dir.noTransform = true
|
||||
case Public:
|
||||
dir.public = true
|
||||
case Private:
|
||||
scanner := bufio.NewScanner(strings.NewReader(token.Value))
|
||||
scanner.Split(scanCommaSeparatedWords)
|
||||
for scanner.Scan() {
|
||||
dir.private = append(dir.private, scanner.Text())
|
||||
}
|
||||
case ProxyRevalidate:
|
||||
dir.proxyRevalidate = true
|
||||
case SMaxAge:
|
||||
iv, err := strconv.ParseUint(token.Value, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to parse s-maxage: %w`, err)
|
||||
}
|
||||
dir.sMaxAge = &iv
|
||||
default:
|
||||
dir.extensions[token.Name] = token.Value
|
||||
}
|
||||
}
|
||||
return &dir, nil
|
||||
}
|
||||
21
extra/server-discovery/vendor/github.com/lestrrat-go/iter/LICENSE
generated
vendored
Normal file
21
extra/server-discovery/vendor/github.com/lestrrat-go/iter/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 lestrrat-go
|
||||
|
||||
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.
|
||||
192
extra/server-discovery/vendor/github.com/lestrrat-go/iter/arrayiter/arrayiter.go
generated
vendored
Normal file
192
extra/server-discovery/vendor/github.com/lestrrat-go/iter/arrayiter/arrayiter.go
generated
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
package arrayiter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func Iterate(ctx context.Context, a interface{}) (Iterator, error) {
|
||||
arv := reflect.ValueOf(a)
|
||||
|
||||
switch arv.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
default:
|
||||
return nil, fmt.Errorf(`argument must be an array/slice (%s)`, arv.Type())
|
||||
}
|
||||
|
||||
ch := make(chan *Pair)
|
||||
go func(ctx context.Context, ch chan *Pair, arv reflect.Value) {
|
||||
defer close(ch)
|
||||
|
||||
for i := 0; i < arv.Len(); i++ {
|
||||
value := arv.Index(i)
|
||||
pair := &Pair{
|
||||
Index: i,
|
||||
Value: value.Interface(),
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case ch <- pair:
|
||||
}
|
||||
}
|
||||
}(ctx, ch, arv)
|
||||
|
||||
return New(ch), nil
|
||||
}
|
||||
|
||||
// Source represents a array that knows how to create an iterator
|
||||
type Source interface {
|
||||
Iterate(context.Context) Iterator
|
||||
}
|
||||
|
||||
// Pair represents a single pair of key and value from a array
|
||||
type Pair struct {
|
||||
Index int
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// Iterator iterates through keys and values of a array
|
||||
type Iterator interface {
|
||||
Next(context.Context) bool
|
||||
Pair() *Pair
|
||||
}
|
||||
|
||||
type iter struct {
|
||||
ch chan *Pair
|
||||
mu sync.RWMutex
|
||||
next *Pair
|
||||
}
|
||||
|
||||
// Visitor represents an object that handles each pair in a array
|
||||
type Visitor interface {
|
||||
Visit(int, interface{}) error
|
||||
}
|
||||
|
||||
// VisitorFunc is a type of Visitor based on a function
|
||||
type VisitorFunc func(int, interface{}) error
|
||||
|
||||
func (fn VisitorFunc) Visit(s int, v interface{}) error {
|
||||
return fn(s, v)
|
||||
}
|
||||
|
||||
func New(ch chan *Pair) Iterator {
|
||||
return &iter{
|
||||
ch: ch,
|
||||
}
|
||||
}
|
||||
|
||||
// Next returns true if there are more items to read from the iterator
|
||||
func (i *iter) Next(ctx context.Context) bool {
|
||||
i.mu.RLock()
|
||||
if i.ch == nil {
|
||||
i.mu.RUnlock()
|
||||
return false
|
||||
}
|
||||
i.mu.RUnlock()
|
||||
|
||||
i.mu.Lock()
|
||||
defer i.mu.Unlock()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
i.ch = nil
|
||||
return false
|
||||
case v, ok := <-i.ch:
|
||||
if !ok {
|
||||
i.ch = nil
|
||||
return false
|
||||
}
|
||||
i.next = v
|
||||
return true
|
||||
}
|
||||
|
||||
//nolint:govet
|
||||
return false // never reached
|
||||
}
|
||||
|
||||
// Pair returns the currently buffered Pair. Calling Next() will reset its value
|
||||
func (i *iter) Pair() *Pair {
|
||||
i.mu.RLock()
|
||||
defer i.mu.RUnlock()
|
||||
return i.next
|
||||
}
|
||||
|
||||
// Walk walks through each element in the array
|
||||
func Walk(ctx context.Context, s Source, v Visitor) error {
|
||||
for i := s.Iterate(ctx); i.Next(ctx); {
|
||||
pair := i.Pair()
|
||||
if err := v.Visit(pair.Index, pair.Value); err != nil {
|
||||
return fmt.Errorf(`failed to visit index %d: %w`, pair.Index, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func AsArray(ctx context.Context, s interface{}, v interface{}) error {
|
||||
var iter Iterator
|
||||
switch reflect.ValueOf(s).Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
x, err := Iterate(ctx, s)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`failed to iterate over array/slice type: %w`, err)
|
||||
}
|
||||
iter = x
|
||||
default:
|
||||
ssrc, ok := s.(Source)
|
||||
if !ok {
|
||||
return fmt.Errorf(`cannot iterate over %T: not a arrayiter.Source type`, s)
|
||||
}
|
||||
iter = ssrc.Iterate(ctx)
|
||||
}
|
||||
|
||||
dst := reflect.ValueOf(v)
|
||||
|
||||
// dst MUST be a pointer to a array type
|
||||
if kind := dst.Kind(); kind != reflect.Ptr {
|
||||
return fmt.Errorf(`dst must be a pointer to a array (%s)`, dst.Type())
|
||||
}
|
||||
|
||||
dst = dst.Elem()
|
||||
switch dst.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
default:
|
||||
return fmt.Errorf(`dst must be a pointer to an array or slice (%s)`, dst.Type())
|
||||
}
|
||||
|
||||
var pairs []*Pair
|
||||
for iter.Next(ctx) {
|
||||
pair := iter.Pair()
|
||||
pairs = append(pairs, pair)
|
||||
}
|
||||
|
||||
switch dst.Kind() {
|
||||
case reflect.Array:
|
||||
if len(pairs) < dst.Len() {
|
||||
return fmt.Errorf(`dst array does not have enough space for elements (%d, want %d)`, dst.Len(), len(pairs))
|
||||
}
|
||||
case reflect.Slice:
|
||||
if dst.IsNil() {
|
||||
dst.Set(reflect.MakeSlice(dst.Type(), len(pairs), len(pairs)))
|
||||
}
|
||||
}
|
||||
|
||||
// dst must be assignable
|
||||
if !dst.CanSet() {
|
||||
return fmt.Errorf(`dst is not writeable`)
|
||||
}
|
||||
|
||||
elemtyp := dst.Type().Elem()
|
||||
for _, pair := range pairs {
|
||||
rvvalue := reflect.ValueOf(pair.Value)
|
||||
|
||||
if !rvvalue.Type().AssignableTo(elemtyp) {
|
||||
return fmt.Errorf(`cannot assign key of type %s to map key of type %s`, rvvalue.Type(), elemtyp)
|
||||
}
|
||||
|
||||
dst.Index(pair.Index).Set(rvvalue)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
195
extra/server-discovery/vendor/github.com/lestrrat-go/iter/mapiter/mapiter.go
generated
vendored
Normal file
195
extra/server-discovery/vendor/github.com/lestrrat-go/iter/mapiter/mapiter.go
generated
vendored
Normal file
@@ -0,0 +1,195 @@
|
||||
package mapiter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Iterate creates an iterator from arbitrary map types. This is not
|
||||
// the most efficient tool, but it's the quickest way to create an
|
||||
// iterator for maps.
|
||||
// Also, note that you cannot make any assumptions on the order of
|
||||
// pairs being returned.
|
||||
func Iterate(ctx context.Context, m interface{}) (Iterator, error) {
|
||||
mrv := reflect.ValueOf(m)
|
||||
|
||||
if mrv.Kind() != reflect.Map {
|
||||
return nil, fmt.Errorf(`argument must be a map (%s)`, mrv.Type())
|
||||
}
|
||||
|
||||
ch := make(chan *Pair)
|
||||
go func(ctx context.Context, ch chan *Pair, mrv reflect.Value) {
|
||||
defer close(ch)
|
||||
for _, key := range mrv.MapKeys() {
|
||||
value := mrv.MapIndex(key)
|
||||
pair := &Pair{
|
||||
Key: key.Interface(),
|
||||
Value: value.Interface(),
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case ch <- pair:
|
||||
}
|
||||
}
|
||||
}(ctx, ch, mrv)
|
||||
|
||||
return New(ch), nil
|
||||
}
|
||||
|
||||
// Source represents a map that knows how to create an iterator
|
||||
type Source interface {
|
||||
Iterate(context.Context) Iterator
|
||||
}
|
||||
|
||||
// Pair represents a single pair of key and value from a map
|
||||
type Pair struct {
|
||||
Key interface{}
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// Iterator iterates through keys and values of a map
|
||||
type Iterator interface {
|
||||
Next(context.Context) bool
|
||||
Pair() *Pair
|
||||
}
|
||||
|
||||
type iter struct {
|
||||
ch chan *Pair
|
||||
mu sync.RWMutex
|
||||
next *Pair
|
||||
}
|
||||
|
||||
// Visitor represents an object that handles each pair in a map
|
||||
type Visitor interface {
|
||||
Visit(interface{}, interface{}) error
|
||||
}
|
||||
|
||||
// VisitorFunc is a type of Visitor based on a function
|
||||
type VisitorFunc func(interface{}, interface{}) error
|
||||
|
||||
func (fn VisitorFunc) Visit(s interface{}, v interface{}) error {
|
||||
return fn(s, v)
|
||||
}
|
||||
|
||||
func New(ch chan *Pair) Iterator {
|
||||
return &iter{
|
||||
ch: ch,
|
||||
}
|
||||
}
|
||||
|
||||
// Next returns true if there are more items to read from the iterator
|
||||
func (i *iter) Next(ctx context.Context) bool {
|
||||
i.mu.RLock()
|
||||
if i.ch == nil {
|
||||
i.mu.RUnlock()
|
||||
return false
|
||||
}
|
||||
i.mu.RUnlock()
|
||||
|
||||
i.mu.Lock()
|
||||
defer i.mu.Unlock()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
i.ch = nil
|
||||
return false
|
||||
case v, ok := <-i.ch:
|
||||
if !ok {
|
||||
i.ch = nil
|
||||
return false
|
||||
}
|
||||
i.next = v
|
||||
return true
|
||||
}
|
||||
|
||||
//nolint:govet
|
||||
return false // never reached
|
||||
}
|
||||
|
||||
// Pair returns the currently buffered Pair. Calling Next() will reset its value
|
||||
func (i *iter) Pair() *Pair {
|
||||
i.mu.RLock()
|
||||
defer i.mu.RUnlock()
|
||||
return i.next
|
||||
}
|
||||
|
||||
// Walk walks through each element in the map
|
||||
func Walk(ctx context.Context, s Source, v Visitor) error {
|
||||
for i := s.Iterate(ctx); i.Next(ctx); {
|
||||
pair := i.Pair()
|
||||
if err := v.Visit(pair.Key, pair.Value); err != nil {
|
||||
return fmt.Errorf(`failed to visit key %s: %w`, pair.Key, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AsMap returns the values obtained from the source as a map
|
||||
func AsMap(ctx context.Context, s interface{}, v interface{}) error {
|
||||
var iter Iterator
|
||||
switch reflect.ValueOf(s).Kind() {
|
||||
case reflect.Map:
|
||||
x, err := Iterate(ctx, s)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`failed to iterate over map type: %w`, err)
|
||||
}
|
||||
iter = x
|
||||
default:
|
||||
ssrc, ok := s.(Source)
|
||||
if !ok {
|
||||
return fmt.Errorf(`cannot iterate over %T: not a mapiter.Source type`, s)
|
||||
}
|
||||
iter = ssrc.Iterate(ctx)
|
||||
}
|
||||
|
||||
dst := reflect.ValueOf(v)
|
||||
|
||||
// dst MUST be a pointer to a map type
|
||||
if kind := dst.Kind(); kind != reflect.Ptr {
|
||||
return fmt.Errorf(`dst must be a pointer to a map (%s)`, dst.Type())
|
||||
}
|
||||
|
||||
dst = dst.Elem()
|
||||
if dst.Kind() != reflect.Map {
|
||||
return fmt.Errorf(`dst must be a pointer to a map (%s)`, dst.Type())
|
||||
}
|
||||
|
||||
if dst.IsNil() {
|
||||
dst.Set(reflect.MakeMap(dst.Type()))
|
||||
}
|
||||
|
||||
// dst must be assignable
|
||||
if !dst.CanSet() {
|
||||
return fmt.Errorf(`dst is not writeable`)
|
||||
}
|
||||
|
||||
keytyp := dst.Type().Key()
|
||||
valtyp := dst.Type().Elem()
|
||||
|
||||
for iter.Next(ctx) {
|
||||
pair := iter.Pair()
|
||||
|
||||
rvkey := reflect.ValueOf(pair.Key)
|
||||
rvvalue := reflect.ValueOf(pair.Value)
|
||||
|
||||
if !rvkey.Type().AssignableTo(keytyp) {
|
||||
return fmt.Errorf(`cannot assign key of type %s to map key of type %s`, rvkey.Type(), keytyp)
|
||||
}
|
||||
|
||||
switch rvvalue.Kind() {
|
||||
// we can only check if we can assign to rvvalue to valtyp if it's non-nil
|
||||
case reflect.Invalid:
|
||||
rvvalue = reflect.New(valtyp).Elem()
|
||||
default:
|
||||
if !rvvalue.Type().AssignableTo(valtyp) {
|
||||
return fmt.Errorf(`cannot assign value of type %s to map value of type %s`, rvvalue.Type(), valtyp)
|
||||
}
|
||||
}
|
||||
|
||||
dst.SetMapIndex(rvkey, rvvalue)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
35
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/.gitignore
generated
vendored
Normal file
35
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/.golangci.yml
generated
vendored
Normal file
102
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/Changes
generated
vendored
Normal file
767
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/LICENSE
generated
vendored
Normal file
22
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/Makefile
generated
vendored
Normal file
81
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/README.md
generated
vendored
Normal file
134
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/format.go
generated
vendored
Normal file
95
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/formatkind_string_gen.go
generated
vendored
Normal file
28
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/gen.sh
generated
vendored
Normal file
14
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/internal/base64/base64.go
generated
vendored
Normal file
66
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/internal/ecutil/ecutil.go
generated
vendored
Normal file
110
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/internal/iter/mapiter.go
generated
vendored
Normal file
36
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/internal/json/goccy.go
generated
vendored
Normal file
50
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/internal/json/json.go
generated
vendored
Normal file
105
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/internal/json/registry.go
generated
vendored
Normal file
53
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/internal/json/stdlib.go
generated
vendored
Normal file
49
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/internal/keyconv/keyconv.go
generated
vendored
Normal file
177
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/internal/pool/pool.go
generated
vendored
Normal file
61
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwa/README.md
generated
vendored
Normal file
3
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwa/compression_gen.go
generated
vendored
Normal file
73
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwa/content_encryption_gen.go
generated
vendored
Normal file
81
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwa/elliptic_gen.go
generated
vendored
Normal file
84
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwa/gen.sh
generated
vendored
Normal file
14
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwa/jwa.go
generated
vendored
Normal file
4
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwa/key_encryption_gen.go
generated
vendored
Normal file
112
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwa/key_type_gen.go
generated
vendored
Normal file
78
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwa/secp2561k.go
generated
vendored
Normal file
10
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwa/signature_gen.go
generated
vendored
Normal file
99
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwe/README.md
generated
vendored
Normal file
94
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwe/compress.go
generated
vendored
Normal file
68
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwe/decrypt.go
generated
vendored
Normal file
301
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwe/encrypt.go
generated
vendored
Normal file
145
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwe/gen.sh
generated
vendored
Normal file
14
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwe/headers.go
generated
vendored
Normal file
122
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwe/headers_gen.go
generated
vendored
Normal file
714
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwe/interface.go
generated
vendored
Normal file
102
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwe/internal/aescbc/aescbc.go
generated
vendored
Normal file
240
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwe/internal/cipher/cipher.go
generated
vendored
Normal file
162
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwe/internal/cipher/interface.go
generated
vendored
Normal file
34
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwe/internal/concatkdf/concatkdf.go
generated
vendored
Normal file
67
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwe/internal/content_crypt/content_crypt.go
generated
vendored
Normal file
42
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwe/internal/content_crypt/interface.go
generated
vendored
Normal file
20
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwe/internal/keyenc/interface.go
generated
vendored
Normal file
106
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwe/internal/keyenc/keyenc.go
generated
vendored
Normal file
661
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwe/internal/keygen/interface.go
generated
vendored
Normal file
73
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwe/internal/keygen/keygen.go
generated
vendored
Normal file
192
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwe/io.go
generated
vendored
Normal file
23
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwe/jwe.go
generated
vendored
Normal file
388
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwe/message.go
generated
vendored
Normal file
654
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwe/options.go
generated
vendored
Normal file
96
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwe/serializer.go
generated
vendored
Normal file
94
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwk/README.md
generated
vendored
Normal file
274
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwk/certchain.go
generated
vendored
Normal file
85
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwk/ecdsa.go
generated
vendored
Normal file
229
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwk/ecdsa_gen.go
generated
vendored
Normal file
1178
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwk/es256k.go
generated
vendored
Normal file
13
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwk/gen.sh
generated
vendored
Normal file
14
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwk/interface.go
generated
vendored
Normal file
159
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwk/interface_gen.go
generated
vendored
Normal file
111
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwk/io.go
generated
vendored
Normal file
29
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwk/jwk.go
generated
vendored
Normal file
717
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwk/key_ops.go
generated
vendored
Normal file
60
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwk/okp.go
generated
vendored
Normal file
184
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwk/okp_gen.go
generated
vendored
Normal file
1116
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwk/option.go
generated
vendored
Normal file
197
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwk/refresh.go
generated
vendored
Normal file
654
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwk/rsa.go
generated
vendored
Normal file
244
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwk/rsa_gen.go
generated
vendored
Normal file
1247
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwk/set.go
generated
vendored
Normal file
301
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwk/symmetric.go
generated
vendored
Normal file
61
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwk/symmetric_gen.go
generated
vendored
Normal file
519
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwk/usage.go
generated
vendored
Normal file
30
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jwk/whitelist.go
generated
vendored
Normal file
69
extra/server-discovery/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
extra/server-discovery/vendor/github.com/lestrrat-go/jwx/jws/README.md
generated
vendored
Normal file
111
extra/server-discovery/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
|
||||
}
|
||||
```
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user