...
1
21
22
23
24
25 package hmac
26
27 import (
28 "crypto/hmac"
29 "crypto/sha512"
30 "encoding/base64"
31 "fmt"
32 "hash"
33 "strings"
34 "sync"
35
36 "github.com/ory/x/errorsx"
37
38 "github.com/pkg/errors"
39
40 "github.com/ory/fosite"
41 )
42
43
44 type HMACStrategy struct {
45 TokenEntropy int
46 GlobalSecret []byte
47 RotatedGlobalSecrets [][]byte
48 Hash func() hash.Hash
49 sync.Mutex
50 }
51
52 const (
53
54 minimumEntropy = 32
55
56
57 minimumSecretLength = 32
58 )
59
60 var b64 = base64.URLEncoding.WithPadding(base64.NoPadding)
61
62
63
64 func (c *HMACStrategy) Generate() (string, string, error) {
65 c.Lock()
66 defer c.Unlock()
67
68 if len(c.GlobalSecret) < minimumSecretLength {
69 return "", "", errors.Errorf("secret for signing HMAC-SHA512/256 is expected to be 32 byte long, got %d byte", len(c.GlobalSecret))
70 }
71
72 var signingKey [32]byte
73 copy(signingKey[:], c.GlobalSecret)
74
75 if c.TokenEntropy < minimumEntropy {
76 c.TokenEntropy = minimumEntropy
77 }
78
79
80
81
82
83
84
85
86 tokenKey, err := RandomBytes(c.TokenEntropy)
87 if err != nil {
88 return "", "", errorsx.WithStack(err)
89 }
90
91 signature := c.generateHMAC(tokenKey, &signingKey)
92
93 encodedSignature := b64.EncodeToString(signature)
94 encodedToken := fmt.Sprintf("%s.%s", b64.EncodeToString(tokenKey), encodedSignature)
95 return encodedToken, encodedSignature, nil
96 }
97
98
99 func (c *HMACStrategy) Validate(token string) (err error) {
100 var keys [][]byte
101
102 if len(c.GlobalSecret) > 0 {
103 keys = append(keys, c.GlobalSecret)
104 }
105
106 if len(c.RotatedGlobalSecrets) > 0 {
107 keys = append(keys, c.RotatedGlobalSecrets...)
108 }
109
110 for _, key := range keys {
111 if err = c.validate(key, token); err == nil {
112 return nil
113 } else if errors.Is(err, fosite.ErrTokenSignatureMismatch) {
114 } else {
115 return err
116 }
117 }
118
119 if err == nil {
120 return errors.New("a secret for signing HMAC-SHA512/256 is expected to be defined, but none were")
121 }
122
123 return err
124 }
125
126 func (c *HMACStrategy) validate(secret []byte, token string) error {
127 if len(secret) < minimumSecretLength {
128 return errors.Errorf("secret for signing HMAC-SHA512/256 is expected to be 32 byte long, got %d byte", len(secret))
129 }
130
131 var signingKey [32]byte
132 copy(signingKey[:], secret)
133
134 split := strings.Split(token, ".")
135 if len(split) != 2 {
136 return errorsx.WithStack(fosite.ErrInvalidTokenFormat)
137 }
138
139 tokenKey := split[0]
140 tokenSignature := split[1]
141 if tokenKey == "" || tokenSignature == "" {
142 return errorsx.WithStack(fosite.ErrInvalidTokenFormat)
143 }
144
145 decodedTokenSignature, err := b64.DecodeString(tokenSignature)
146 if err != nil {
147 return errorsx.WithStack(err)
148 }
149
150 decodedTokenKey, err := b64.DecodeString(tokenKey)
151 if err != nil {
152 return errorsx.WithStack(err)
153 }
154
155 expectedMAC := c.generateHMAC(decodedTokenKey, &signingKey)
156 if !hmac.Equal(expectedMAC, decodedTokenSignature) {
157
158 return errorsx.WithStack(fosite.ErrTokenSignatureMismatch)
159 }
160
161 return nil
162 }
163
164 func (c *HMACStrategy) Signature(token string) string {
165 split := strings.Split(token, ".")
166
167 if len(split) != 2 {
168 return ""
169 }
170
171 return split[1]
172 }
173
174 func (c *HMACStrategy) generateHMAC(data []byte, key *[32]byte) []byte {
175 hasher := c.Hash
176 if hasher == nil {
177 hasher = sha512.New512_256
178 }
179 h := hmac.New(hasher, key[:])
180
181 _, err := h.Write(data)
182 if err != nil {
183 panic(err)
184 }
185 return h.Sum(nil)
186 }
187
View as plain text