1package macaroon
2
3import (
4	"bytes"
5	"fmt"
6)
7
8// field names, as defined in libmacaroons
9const (
10	fieldNameLocation       = "location"
11	fieldNameIdentifier     = "identifier"
12	fieldNameSignature      = "signature"
13	fieldNameCaveatId       = "cid"
14	fieldNameVerificationId = "vid"
15	fieldNameCaveatLocation = "cl"
16)
17
18// maxPacketV1Len is the maximum allowed length of a packet in the v1 macaroon
19// serialization format.
20const maxPacketV1Len = 0xffff
21
22// The original macaroon binary encoding is made from a sequence
23// of "packets", each of which has a field name and some data.
24// The encoding is:
25//
26// - four ascii hex digits holding the entire packet size (including
27// the digits themselves).
28//
29// - the field name, followed by an ascii space.
30//
31// - the raw data
32//
33// - a newline (\n) character
34//
35// The packet struct below holds a reference into Macaroon.data.
36type packetV1 struct {
37	// ftype holds the field name of the packet.
38	fieldName []byte
39
40	// data holds the packet's data.
41	data []byte
42
43	// len holds the total length in bytes
44	// of the packet, including any header.
45	totalLen int
46}
47
48// parsePacket parses the packet at the start of the
49// given data.
50func parsePacketV1(data []byte) (packetV1, error) {
51	if len(data) < 6 {
52		return packetV1{}, fmt.Errorf("packet too short")
53	}
54	plen, ok := parseSizeV1(data)
55	if !ok {
56		return packetV1{}, fmt.Errorf("cannot parse size")
57	}
58	if plen > len(data) {
59		return packetV1{}, fmt.Errorf("packet size too big")
60	}
61	if plen < 4 {
62		return packetV1{}, fmt.Errorf("packet size too small")
63	}
64	data = data[4:plen]
65	i := bytes.IndexByte(data, ' ')
66	if i <= 0 {
67		return packetV1{}, fmt.Errorf("cannot parse field name")
68	}
69	fieldName := data[0:i]
70	if data[len(data)-1] != '\n' {
71		return packetV1{}, fmt.Errorf("no terminating newline found")
72	}
73	return packetV1{
74		fieldName: fieldName,
75		data:      data[i+1 : len(data)-1],
76		totalLen:  plen,
77	}, nil
78}
79
80// appendPacketV1 appends a packet with the given field name
81// and data to the given buffer. If the field and data were
82// too long to be encoded, it returns nil, false; otherwise
83// it returns the appended buffer.
84func appendPacketV1(buf []byte, field string, data []byte) ([]byte, bool) {
85	plen := packetV1Size(field, data)
86	if plen > maxPacketV1Len {
87		return nil, false
88	}
89	buf = appendSizeV1(buf, plen)
90	buf = append(buf, field...)
91	buf = append(buf, ' ')
92	buf = append(buf, data...)
93	buf = append(buf, '\n')
94	return buf, true
95}
96
97func packetV1Size(field string, data []byte) int {
98	return 4 + len(field) + 1 + len(data) + 1
99}
100
101var hexDigits = []byte("0123456789abcdef")
102
103func appendSizeV1(data []byte, size int) []byte {
104	return append(data,
105		hexDigits[size>>12],
106		hexDigits[(size>>8)&0xf],
107		hexDigits[(size>>4)&0xf],
108		hexDigits[size&0xf],
109	)
110}
111
112func parseSizeV1(data []byte) (int, bool) {
113	d0, ok0 := asciiHex(data[0])
114	d1, ok1 := asciiHex(data[1])
115	d2, ok2 := asciiHex(data[2])
116	d3, ok3 := asciiHex(data[3])
117	return d0<<12 + d1<<8 + d2<<4 + d3, ok0 && ok1 && ok2 && ok3
118}
119
120func asciiHex(b byte) (int, bool) {
121	switch {
122	case b >= '0' && b <= '9':
123		return int(b) - '0', true
124	case b >= 'a' && b <= 'f':
125		return int(b) - 'a' + 0xa, true
126	}
127	return 0, false
128}
129
130func isASCIIHex(b byte) bool {
131	_, ok := asciiHex(b)
132	return ok
133}
134