-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathjwsallow.go
More file actions
79 lines (72 loc) · 2.7 KB
/
Copy pathjwsallow.go
File metadata and controls
79 lines (72 loc) · 2.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package httpsign
import (
"fmt"
"github.com/lestrrat-go/jwx/v4/jwa"
)
// JWSAlgAllowlist is a non-empty set of permitted jwa signature algorithms for foreign JWS verify.
// Pass nil to NewJWSVerifier / NewJWSVerifierWithAlg to skip alg policy (lazy / tests).
// Prefer a real allowlist whenever key selection (e.g. keyid) can be attacker-influenced.
type JWSAlgAllowlist struct {
algs map[string]struct{}
}
// resolveRegisteredJWSAlg requires alg to be in the jwx signature-algorithm registry and
// rejects empty / "none". Returns the canonical registry value so handcrafted
// jwa.NewSignatureAlgorithm("HS256") is normalized to jwa.HS256() for key checks.
// Unregistered strings (e.g. "NONE", "hs256") are rejected — jwx SignerFor/VerifierFor
// may still accept them; we do not.
func resolveRegisteredJWSAlg(alg jwa.SignatureAlgorithm) (jwa.SignatureAlgorithm, error) {
name := alg.String()
if name == "" {
return jwa.EmptySignatureAlgorithm(), fmt.Errorf("JWS algorithm must not be empty")
}
canonical, ok := jwa.LookupSignatureAlgorithm(name)
if !ok {
return jwa.EmptySignatureAlgorithm(), fmt.Errorf("unknown or unregistered JWS algorithm %q", name)
}
if canonical == jwa.NoSignature() {
return jwa.EmptySignatureAlgorithm(), fmt.Errorf("the NONE signing algorithm is expressly disallowed")
}
return canonical, nil
}
// NewJWSAlgAllowlist builds an allowlist. Rejects an empty list, unregistered algs, and none.
func NewJWSAlgAllowlist(algs ...jwa.SignatureAlgorithm) (*JWSAlgAllowlist, error) {
if len(algs) == 0 {
return nil, fmt.Errorf("JWS algorithm allowlist must not be empty")
}
set := make(map[string]struct{}, len(algs))
for _, alg := range algs {
canonical, err := resolveRegisteredJWSAlg(alg)
if err != nil {
return nil, err
}
set[canonical.String()] = struct{}{}
}
return &JWSAlgAllowlist{algs: set}, nil
}
// Contains reports whether alg is permitted. A nil receiver does not contain any alg
// (callers should treat nil allowlist as “skip policy” before calling Contains).
// Comparison is by algorithm name string; constructors resolve to registry values first.
// Legacy "EdDSA" and RFC 9864 "Ed25519" are treated as equivalent.
func (a *JWSAlgAllowlist) Contains(alg jwa.SignatureAlgorithm) bool {
if a == nil {
return false
}
if _, ok := a.algs[alg.String()]; ok {
return true
}
if !isEd25519JWSAlg(alg) {
return false
}
_, legacy := a.algs[jwa.EdDSA().String()]
_, modern := a.algs[jwa.EdDSAEd25519().String()]
return legacy || modern
}
func checkJWSAlgAllowed(allowed *JWSAlgAllowlist, alg jwa.SignatureAlgorithm) error {
if allowed == nil {
return nil
}
if !allowed.Contains(alg) {
return fmt.Errorf("JWS algorithm %s is not in the allowlist", alg)
}
return nil
}