-
Notifications
You must be signed in to change notification settings - Fork 24
/
packet-v1.go
133 lines (119 loc) · 3.18 KB
/
packet-v1.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
package macaroon
import (
"bytes"
"fmt"
)
// field names, as defined in libmacaroons
const (
fieldNameLocation = "location"
fieldNameIdentifier = "identifier"
fieldNameSignature = "signature"
fieldNameCaveatId = "cid"
fieldNameVerificationId = "vid"
fieldNameCaveatLocation = "cl"
)
// maxPacketV1Len is the maximum allowed length of a packet in the v1 macaroon
// serialization format.
const maxPacketV1Len = 0xffff
// The original macaroon binary encoding is made from a sequence
// of "packets", each of which has a field name and some data.
// The encoding is:
//
// - four ascii hex digits holding the entire packet size (including
// the digits themselves).
//
// - the field name, followed by an ascii space.
//
// - the raw data
//
// - a newline (\n) character
//
// The packet struct below holds a reference into Macaroon.data.
type packetV1 struct {
// ftype holds the field name of the packet.
fieldName []byte
// data holds the packet's data.
data []byte
// len holds the total length in bytes
// of the packet, including any header.
totalLen int
}
// parsePacket parses the packet at the start of the
// given data.
func parsePacketV1(data []byte) (packetV1, error) {
if len(data) < 6 {
return packetV1{}, fmt.Errorf("packet too short")
}
plen, ok := parseSizeV1(data)
if !ok {
return packetV1{}, fmt.Errorf("cannot parse size")
}
if plen > len(data) {
return packetV1{}, fmt.Errorf("packet size too big")
}
if plen < 4 {
return packetV1{}, fmt.Errorf("packet size too small")
}
data = data[4:plen]
i := bytes.IndexByte(data, ' ')
if i <= 0 {
return packetV1{}, fmt.Errorf("cannot parse field name")
}
fieldName := data[0:i]
if data[len(data)-1] != '\n' {
return packetV1{}, fmt.Errorf("no terminating newline found")
}
return packetV1{
fieldName: fieldName,
data: data[i+1 : len(data)-1],
totalLen: plen,
}, nil
}
// appendPacketV1 appends a packet with the given field name
// and data to the given buffer. If the field and data were
// too long to be encoded, it returns nil, false; otherwise
// it returns the appended buffer.
func appendPacketV1(buf []byte, field string, data []byte) ([]byte, bool) {
plen := packetV1Size(field, data)
if plen > maxPacketV1Len {
return nil, false
}
buf = appendSizeV1(buf, plen)
buf = append(buf, field...)
buf = append(buf, ' ')
buf = append(buf, data...)
buf = append(buf, '\n')
return buf, true
}
func packetV1Size(field string, data []byte) int {
return 4 + len(field) + 1 + len(data) + 1
}
var hexDigits = []byte("0123456789abcdef")
func appendSizeV1(data []byte, size int) []byte {
return append(data,
hexDigits[size>>12],
hexDigits[(size>>8)&0xf],
hexDigits[(size>>4)&0xf],
hexDigits[size&0xf],
)
}
func parseSizeV1(data []byte) (int, bool) {
d0, ok0 := asciiHex(data[0])
d1, ok1 := asciiHex(data[1])
d2, ok2 := asciiHex(data[2])
d3, ok3 := asciiHex(data[3])
return d0<<12 + d1<<8 + d2<<4 + d3, ok0 && ok1 && ok2 && ok3
}
func asciiHex(b byte) (int, bool) {
switch {
case b >= '0' && b <= '9':
return int(b) - '0', true
case b >= 'a' && b <= 'f':
return int(b) - 'a' + 0xa, true
}
return 0, false
}
func isASCIIHex(b byte) bool {
_, ok := asciiHex(b)
return ok
}