forked from cmars/macaroon
-
Notifications
You must be signed in to change notification settings - Fork 0
/
macaroon.go
271 lines (248 loc) · 7.91 KB
/
macaroon.go
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
// WARNING
//
// The macaroon package is deprecated at this import path.
// Please use gopkg.in/macaroon.v1 instead.
package macaroon
import (
"bytes"
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"fmt"
"io"
)
// Macaroon holds a macaroon.
// See Fig. 7 of http://theory.stanford.edu/~ataly/Papers/macaroons.pdf
// for a description of the data contained within.
// Macaroons are mutable objects - use Clone as appropriate
// to avoid unwanted mutation.
type Macaroon struct {
// data holds the binary-marshalled form
// of the macaroon data.
data []byte
location packet
id packet
caveats []caveat
sig []byte
}
// caveat holds a first person or third party caveat.
type caveat struct {
location packet
caveatId packet
verificationId packet
}
type Caveat struct {
Id string
Location string
}
// isThirdParty reports whether the caveat must be satisfied
// by some third party (if not, it's a first person caveat).
func (cav *caveat) isThirdParty() bool {
return cav.verificationId.len() > 0
}
// New returns a new macaroon with the given root key,
// identifier and location.
func New(rootKey []byte, id, loc string) (*Macaroon, error) {
var m Macaroon
if err := m.init(id, loc); err != nil {
return nil, err
}
m.sig = keyedHash(rootKey, m.dataBytes(m.id))
return &m, nil
}
func (m *Macaroon) init(id, loc string) error {
var ok bool
m.location, ok = m.appendPacket(fieldLocation, []byte(loc))
if !ok {
return fmt.Errorf("macaroon location too big")
}
m.id, ok = m.appendPacket(fieldIdentifier, []byte(id))
if !ok {
return fmt.Errorf("macaroon identifier too big")
}
return nil
}
// Clone returns a copy of the receiving macaroon.
func (m *Macaroon) Clone() *Macaroon {
m1 := *m
// Ensure that if any data is appended to the new
// macaroon, it will copy data and caveats.
m1.data = m1.data[0:len(m1.data):len(m1.data)]
m1.caveats = m1.caveats[0:len(m1.caveats):len(m1.caveats)]
m1.sig = append([]byte(nil), m.sig...)
return &m1
}
// Location returns the macaroon's location hint. This is
// not verified as part of the macaroon.
func (m *Macaroon) Location() string {
return m.dataStr(m.location)
}
// Id returns the id of the macaroon. This can hold
// arbitrary information.
func (m *Macaroon) Id() string {
return m.dataStr(m.id)
}
// Signature returns the macaroon's signature.
func (m *Macaroon) Signature() []byte {
return append([]byte(nil), m.sig...)
}
// Caveats returns the macaroon's caveats.
// This method will probably change, and it's important not to change the returned caveat.
func (m *Macaroon) Caveats() []Caveat {
caveats := make([]Caveat, len(m.caveats))
for i, cav := range m.caveats {
caveats[i] = Caveat{
Id: m.dataStr(cav.caveatId),
Location: m.dataStr(cav.location),
}
}
return caveats
}
// appendCaveat appends a caveat without modifying the macaroon's signature.
func (m *Macaroon) appendCaveat(caveatId string, verificationId []byte, loc string) (*caveat, error) {
var cav caveat
var ok bool
if caveatId != "" {
cav.caveatId, ok = m.appendPacket(fieldCaveatId, []byte(caveatId))
if !ok {
return nil, fmt.Errorf("caveat identifier too big")
}
}
if len(verificationId) > 0 {
cav.verificationId, ok = m.appendPacket(fieldVerificationId, verificationId)
if !ok {
return nil, fmt.Errorf("caveat verification id too big")
}
}
if loc != "" {
cav.location, ok = m.appendPacket(fieldCaveatLocation, []byte(loc))
if !ok {
return nil, fmt.Errorf("caveat location too big")
}
}
m.caveats = append(m.caveats, cav)
return &m.caveats[len(m.caveats)-1], nil
}
func (m *Macaroon) addCaveat(caveatId string, verificationId []byte, loc string) error {
cav, err := m.appendCaveat(caveatId, verificationId, loc)
if err != nil {
return err
}
sig := keyedHasher(m.sig)
sig.Write(m.dataBytes(cav.verificationId))
sig.Write(m.dataBytes(cav.caveatId))
m.sig = sig.Sum(m.sig[:0])
return nil
}
// Bind prepares the macaroon for being used to discharge the
// macaroon with the given rootSig. This must be
// used before it is used in the discharges argument to Verify.
func (m *Macaroon) Bind(rootSig []byte) {
m.sig = bindForRequest(rootSig, m.sig)
}
// AddFirstPartyCaveat adds a caveat that will be verified
// by the target service.
func (m *Macaroon) AddFirstPartyCaveat(caveatId string) error {
return m.addCaveat(caveatId, nil, "")
}
// AddThirdPartyCaveat adds a third-party caveat to the macaroon,
// using the given shared root key, caveat id and location hint.
// The caveat id should encode the root key in some
// way, either by encrypting it with a key known to the third party
// or by holding a reference to it stored in the third party's
// storage.
func (m *Macaroon) AddThirdPartyCaveat(rootKey []byte, caveatId string, loc string) error {
return m.addThirdPartyCaveatWithRand(rootKey, caveatId, loc, rand.Reader)
}
func (m *Macaroon) addThirdPartyCaveatWithRand(rootKey []byte, caveatId string, loc string, r io.Reader) error {
verificationId, err := encrypt(m.sig, rootKey, r)
if err != nil {
return err
}
return m.addCaveat(caveatId, verificationId, loc)
}
// bndForRequest binds the given macaroon
// to the given signature of its parent macaroon.
func bindForRequest(rootSig, dischargeSig []byte) []byte {
if bytes.Equal(rootSig, dischargeSig) {
return rootSig
}
sig := sha256.New()
sig.Write(rootSig)
sig.Write(dischargeSig)
return sig.Sum(nil)
}
// Verify verifies that the receiving macaroon is valid.
// The root key must be the same that the macaroon was originally
// minted with. The check function is called to verify each
// first-party caveat - it should return an error if the
// condition is not met.
//
// The discharge macaroons should be provided in discharges.
//
// Verify returns true if the verification succeeds; if returns
// (false, nil) if the verification fails, and (false, err) if
// the verification cannot be asserted (but may not be false).
//
// TODO(rog) is there a possible DOS attack that can cause this
// function to infinitely recurse?
func (m *Macaroon) Verify(rootKey []byte, check func(caveat string) error, discharges []*Macaroon) error {
// TODO(rog) consider distinguishing between classes of
// check error - some errors may be resolved by minting
// a new macaroon; others may not.
return m.verify(m.sig, rootKey, check, discharges)
}
func (m *Macaroon) verify(rootSig []byte, rootKey []byte, check func(caveat string) error, discharges []*Macaroon) error {
if len(rootSig) == 0 {
rootSig = m.sig
}
caveatSig := keyedHash(rootKey, m.dataBytes(m.id))
for i, cav := range m.caveats {
if cav.isThirdParty() {
cavKey, err := decrypt(caveatSig, m.dataBytes(cav.verificationId))
if err != nil {
return fmt.Errorf("failed to decrypt caveat %d signature: %v", i, err)
}
// We choose an arbitrary error from one of the
// possible discharge macaroon verifications
// if there's more than one discharge macaroon
// with the required id.
var verifyErr error
found := false
for _, dm := range discharges {
if !bytes.Equal(dm.dataBytes(dm.id), m.dataBytes(cav.caveatId)) {
continue
}
found = true
verifyErr = dm.verify(rootSig, cavKey, check, discharges)
if verifyErr == nil {
break
}
}
if !found {
return fmt.Errorf("cannot find discharge macaroon for caveat %q", m.dataBytes(cav.caveatId))
}
if verifyErr != nil {
return verifyErr
}
} else {
if err := check(string(m.dataBytes(cav.caveatId))); err != nil {
return err
}
}
sig := keyedHasher(caveatSig)
sig.Write(m.dataBytes(cav.verificationId))
sig.Write(m.dataBytes(cav.caveatId))
caveatSig = sig.Sum(caveatSig[:0])
}
// TODO perhaps we should actually do this check before doing
// all the potentially expensive caveat checks.
boundSig := bindForRequest(rootSig, caveatSig)
if !hmac.Equal(boundSig, m.sig) {
return fmt.Errorf("signature mismatch after caveat verification")
}
return nil
}
type Verifier interface {
Verify(m *Macaroon, rootKey []byte) (bool, error)
}