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