forked from cmars/macaroon
-
Notifications
You must be signed in to change notification settings - Fork 0
/
marshal.go
179 lines (166 loc) · 4.6 KB
/
marshal.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
package macaroon
import (
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
)
// field names, as defined in libmacaroons
const (
fieldLocation = "location"
fieldIdentifier = "identifier"
fieldSignature = "signature"
fieldCaveatId = "cid"
fieldVerificationId = "vid"
fieldCaveatLocation = "cl"
)
var (
fieldLocationBytes = []byte("location")
fieldIdentifierBytes = []byte("identifier")
fieldSignatureBytes = []byte("signature")
fieldCaveatIdBytes = []byte("cid")
fieldVerificationIdBytes = []byte("vid")
fieldCaveatLocationBytes = []byte("cl")
)
// macaroonJSON defines the JSON format for macaroons.
type macaroonJSON struct {
Caveats []caveatJSON `json:"caveats"`
Location string `json:"location"`
Identifier string `json:"identifier"`
Signature string `json:"signature"` // hex-encoded
}
// caveatJSON defines the JSON format for caveats within a macaroon.
type caveatJSON struct {
CID string `json:"cid"`
VID string `json:"vid,omitempty"`
Location string `json:"cl,omitempty"`
}
// MarshalJSON implements json.Marshaler.
func (m *Macaroon) MarshalJSON() ([]byte, error) {
mjson := macaroonJSON{
Location: m.Location(),
Identifier: m.dataStr(m.id),
Signature: hex.EncodeToString(m.sig),
Caveats: make([]caveatJSON, len(m.caveats)),
}
for i, cav := range m.caveats {
mjson.Caveats[i] = caveatJSON{
Location: m.dataStr(cav.location),
CID: m.dataStr(cav.caveatId),
VID: base64.StdEncoding.EncodeToString(m.dataBytes(cav.verificationId)),
}
}
data, err := json.Marshal(mjson)
if err != nil {
return nil, fmt.Errorf("cannot marshal json data: %v", err)
}
return data, nil
}
// UnmarshalJSON implements json.Unmarshaler.
func (m *Macaroon) UnmarshalJSON(jsonData []byte) error {
var mjson macaroonJSON
err := json.Unmarshal(jsonData, &mjson)
if err != nil {
return fmt.Errorf("cannot unmarshal json data: %v", err)
}
if err := m.init(mjson.Identifier, mjson.Location); err != nil {
return err
}
m.sig, err = hex.DecodeString(mjson.Signature)
if err != nil {
return fmt.Errorf("cannot decode macaroon signature %q: %v", m.sig, err)
}
m.caveats = m.caveats[:0]
for _, cav := range mjson.Caveats {
vid, err := base64.StdEncoding.DecodeString(cav.VID)
if err != nil {
return fmt.Errorf("cannot decode verification id %q: %v", cav.VID, err)
}
if _, err := m.appendCaveat(cav.CID, vid, cav.Location); err != nil {
return err
}
}
return nil
}
// MarshalBinary implements encoding.BinaryMarshaler.
func (m *Macaroon) MarshalBinary() ([]byte, error) {
data := make([]byte, len(m.data), len(m.data)+len(m.sig))
copy(data, m.data)
data, _, ok := rawAppendPacket(data, fieldSignature, m.sig)
if !ok {
panic("cannot append signature")
}
return data, nil
}
// The binary format of a macaroon is as follows.
// Each identifier repesents a packet.
//
// location
// identifier
// (
// caveatId?
// verificationId?
// caveatLocation?
// )*
// signature
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
func (m *Macaroon) UnmarshalBinary(data []byte) error {
m.data = append([]byte(nil), data...)
var err error
var start int
start, m.location, err = m.expectPacket(0, fieldLocation)
if err != nil {
return err
}
start, m.id, err = m.expectPacket(start, fieldIdentifier)
if err != nil {
return err
}
var cav caveat
for {
p, err := m.parsePacket(start)
if err != nil {
return err
}
start += p.len()
switch field := string(m.fieldName(p)); field {
case fieldSignature:
// At the end of the caveats we find the signature.
if cav.caveatId.len() != 0 {
m.caveats = append(m.caveats, cav)
}
// Remove the signature from data.
m.data = m.data[0:p.start]
m.sig = append([]byte(nil), m.dataBytes(p)...)
return nil
case fieldCaveatId:
if cav.caveatId.len() != 0 {
m.caveats = append(m.caveats, cav)
}
cav.caveatId = p
case fieldVerificationId:
if cav.verificationId.len() != 0 {
return fmt.Errorf("repeated field %q in caveat", fieldVerificationId)
}
cav.verificationId = p
case fieldCaveatLocation:
if cav.location.len() != 0 {
return fmt.Errorf("repeated field %q in caveat", fieldLocation)
}
cav.location = p
default:
return fmt.Errorf("unexpected field %q", field)
}
}
return nil
}
func (m *Macaroon) expectPacket(start int, kind string) (int, packet, error) {
p, err := m.parsePacket(start)
if err != nil {
return 0, packet{}, err
}
if field := string(m.fieldName(p)); field != kind {
return 0, packet{}, fmt.Errorf("unexpected field %q; expected %s", field, kind)
}
return start + p.len(), p, nil
}