consul/vendor/github.com/SermoDigital/jose/jws/jws_validate.go

204 lines
5.1 KiB
Go

package jws
import (
"fmt"
"github.com/SermoDigital/jose/crypto"
)
// VerifyCallback is a callback function that can be used to access header
// parameters to lookup needed information. For example, looking
// up the "kid" parameter.
// The return slice must be a slice of keys used in the verification
// of the JWS.
type VerifyCallback func(JWS) ([]interface{}, error)
// VerifyCallback validates the current JWS' signature as-is. It
// accepts a callback function that can be used to access header
// parameters to lookup needed information. For example, looking
// up the "kid" parameter.
// The return slice must be a slice of keys used in the verification
// of the JWS.
func (j *jws) VerifyCallback(fn VerifyCallback, methods []crypto.SigningMethod, o *SigningOpts) error {
keys, err := fn(j)
if err != nil {
return err
}
return j.VerifyMulti(keys, methods, o)
}
// IsMultiError returns true if the given error is type *MultiError.
func IsMultiError(err error) bool {
_, ok := err.(*MultiError)
return ok
}
// MultiError is a slice of errors.
type MultiError []error
// Errors implements the error interface.
func (m *MultiError) Error() string {
var s string
var n int
for _, err := range *m {
if err != nil {
if n == 0 {
s = err.Error()
}
n++
}
}
switch n {
case 0:
return ""
case 1:
return s
case 2:
return s + " and 1 other error"
}
return fmt.Sprintf("%s (and %d other errors)", s, n-1)
}
// Any means any of the JWS signatures need to verify.
// Refer to verifyMulti for more information.
const Any int = 0
// VerifyMulti verifies the current JWS as-is. Since it's meant to be
// called after parsing a stream of bytes into a JWS, it doesn't do any
// internal parsing like the Sign, Flat, Compact, or General methods do.
func (j *jws) VerifyMulti(keys []interface{}, methods []crypto.SigningMethod, o *SigningOpts) error {
// Catch a simple mistake. Parameter o is irrelevant in this scenario.
if len(keys) == 1 &&
len(methods) == 1 &&
len(j.sb) == 1 {
return j.Verify(keys[0], methods[0])
}
if len(j.sb) != len(methods) {
return ErrNotEnoughMethods
}
if len(keys) < 1 ||
len(keys) > 1 && len(keys) != len(j.sb) {
return ErrNotEnoughKeys
}
// TODO do this better.
if len(keys) == 1 {
k := keys[0]
keys = make([]interface{}, len(methods))
for i := range keys {
keys[i] = k
}
}
var o2 SigningOpts
if o == nil {
o = new(SigningOpts)
}
var m MultiError
for i := range j.sb {
err := j.sb[i].verify(j.plcache, keys[i], methods[i])
if err != nil {
m = append(m, err)
} else {
o2.Inc()
if o.Needs(i) {
o.ptr++
o2.Append(i)
}
}
}
err := o.Validate(&o2)
if err != nil {
m = append(m, err)
}
if len(m) == 0 {
return nil
}
return &m
}
// SigningOpts is a struct which holds options for validating
// JWS signatures.
// Number represents the cumulative which signatures need to verify
// in order for the JWS to be considered valid.
// Leave 'Number' empty or set it to the constant 'Any' if any number of
// valid signatures (greater than one) should verify the JWS.
//
// Use the indices of the signatures that need to verify in order
// for the JWS to be considered valid if specific signatures need
// to verify in order for the JWS to be considered valid.
//
// Note:
// The JWS spec requires *at least* one
// signature to verify in order for the JWS to be considered valid.
type SigningOpts struct {
// Minimum of signatures which need to verify.
Number int
// Indices of specific signatures which need to verify.
Indices []int
ptr int
_ struct{}
}
// Append appends x to s' Indices member.
func (s *SigningOpts) Append(x int) {
s.Indices = append(s.Indices, x)
}
// Needs returns true if x resides inside s' Indices member
// for the given index. It's used to match two SigningOpts Indices members.
func (s *SigningOpts) Needs(x int) bool {
return s.ptr < len(s.Indices) && s.Indices[s.ptr] == x
}
// Inc increments s' Number member by one.
func (s *SigningOpts) Inc() { s.Number++ }
// Validate returns any errors found while validating the
// provided SigningOpts. The receiver validates |have|.
// It'll return an error if the passed SigningOpts' Number member is less
// than s' or if the passed SigningOpts' Indices slice isn't equal to s'.
func (s *SigningOpts) Validate(have *SigningOpts) error {
if have.Number < s.Number ||
(s.Indices != nil &&
!eq(s.Indices, have.Indices)) {
return ErrNotEnoughValidSignatures
}
return nil
}
func eq(a, b []int) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}
// Verify verifies the current JWS as-is. Refer to verifyMulti
// for more information.
func (j *jws) Verify(key interface{}, method crypto.SigningMethod) error {
if len(j.sb) < 1 {
return ErrCannotValidate
}
return j.sb[0].verify(j.plcache, key, method)
}
func (s *sigHead) verify(pl []byte, key interface{}, method crypto.SigningMethod) error {
if s.method.Alg() != method.Alg() || s.method.Hasher() != method.Hasher() {
return ErrMismatchedAlgorithms
}
return method.Verify(format(s.Protected, pl), s.Signature, key)
}