mirror of
https://github.com/edouardparis/lntop
synced 2024-11-16 00:12:44 +00:00
134 lines
3.2 KiB
Go
134 lines
3.2 KiB
Go
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
|
|
}
|