2
0
mirror of https://github.com/edouardparis/lntop synced 2024-11-17 21:26:12 +00:00
lntop/vendor/gopkg.in/macaroon.v2/marshal.go

240 lines
6.3 KiB
Go
Raw Normal View History

2019-03-15 16:02:16 +00:00
package macaroon
import (
"encoding/base64"
"encoding/json"
"fmt"
)
// Version specifies the version of a macaroon.
// In version 1, the macaroon id and all caveats
// must be UTF-8-compatible strings, and the
// size of any part of the macaroon may not exceed
// approximately 64K. In version 2,
// all field may be arbitrary binary blobs.
type Version uint16
const (
// V1 specifies version 1 macaroons.
V1 Version = 1
// V2 specifies version 2 macaroons.
V2 Version = 2
// LatestVersion holds the latest supported version.
LatestVersion = V2
)
// String returns a string representation of the version;
// for example V1 formats as "v1".
func (v Version) String() string {
return fmt.Sprintf("v%d", v)
}
// Version returns the version of the macaroon.
func (m *Macaroon) Version() Version {
return m.version
}
// MarshalJSON implements json.Marshaler by marshaling the
// macaroon in JSON format. The serialisation format is determined
// by the macaroon's version.
func (m *Macaroon) MarshalJSON() ([]byte, error) {
switch m.version {
case V1:
return m.marshalJSONV1()
case V2:
return m.marshalJSONV2()
default:
return nil, fmt.Errorf("unknown version %v", m.version)
}
}
// UnmarshalJSON implements json.Unmarshaller by unmarshaling
// the given macaroon in JSON format. It accepts both V1 and V2
// forms encoded forms, and also a base64-encoded JSON string
// containing the binary-marshaled macaroon.
//
// After unmarshaling, the macaroon's version will reflect
// the version that it was unmarshaled as.
func (m *Macaroon) UnmarshalJSON(data []byte) error {
if data[0] == '"' {
// It's a string, so it must be a base64-encoded binary form.
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
data, err := Base64Decode([]byte(s))
if err != nil {
return err
}
if err := m.UnmarshalBinary(data); err != nil {
return err
}
return nil
}
// Not a string; try to unmarshal into both kinds of macaroon object.
// This assumes that neither format has any fields in common.
// For subsequent versions we may need to change this approach.
type MacaroonJSONV1 macaroonJSONV1
type MacaroonJSONV2 macaroonJSONV2
var both struct {
*MacaroonJSONV1
*MacaroonJSONV2
}
if err := json.Unmarshal(data, &both); err != nil {
return err
}
switch {
case both.MacaroonJSONV1 != nil && both.MacaroonJSONV2 != nil:
return fmt.Errorf("cannot determine macaroon encoding version")
case both.MacaroonJSONV1 != nil:
if err := m.initJSONV1((*macaroonJSONV1)(both.MacaroonJSONV1)); err != nil {
return err
}
m.version = V1
case both.MacaroonJSONV2 != nil:
if err := m.initJSONV2((*macaroonJSONV2)(both.MacaroonJSONV2)); err != nil {
return err
}
m.version = V2
default:
return fmt.Errorf("invalid JSON macaroon encoding")
}
return nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
// It accepts both V1 and V2 binary encodings.
func (m *Macaroon) UnmarshalBinary(data []byte) error {
// Copy the data to avoid retaining references to it
// in the internal data structures.
data = append([]byte(nil), data...)
_, err := m.parseBinary(data)
return err
}
// parseBinary parses the macaroon in binary format
// from the given data and returns where the parsed data ends.
//
// It retains references to data.
func (m *Macaroon) parseBinary(data []byte) ([]byte, error) {
if len(data) == 0 {
return nil, fmt.Errorf("empty macaroon data")
}
v := data[0]
if v == 2 {
// Version 2 binary format.
data, err := m.parseBinaryV2(data)
if err != nil {
return nil, fmt.Errorf("unmarshal v2: %v", err)
}
m.version = V2
return data, nil
}
if isASCIIHex(v) {
// It's a hex digit - version 1 binary format
data, err := m.parseBinaryV1(data)
if err != nil {
return nil, fmt.Errorf("unmarshal v1: %v", err)
}
m.version = V1
return data, nil
}
return nil, fmt.Errorf("cannot determine data format of binary-encoded macaroon")
}
// MarshalBinary implements encoding.BinaryMarshaler by
// formatting the macaroon according to the version specified
// by MarshalAs.
func (m *Macaroon) MarshalBinary() ([]byte, error) {
return m.appendBinary(nil)
}
// appendBinary appends the binary-formatted macaroon to
// the given data, formatting it according to the macaroon's
// version.
func (m *Macaroon) appendBinary(data []byte) ([]byte, error) {
switch m.version {
case V1:
return m.appendBinaryV1(data)
case V2:
return m.appendBinaryV2(data), nil
default:
return nil, fmt.Errorf("bad macaroon version %v", m.version)
}
}
// Slice defines a collection of macaroons. By convention, the
// first macaroon in the slice is a primary macaroon and the rest
// are discharges for its third party caveats.
type Slice []*Macaroon
// MarshalBinary implements encoding.BinaryMarshaler.
func (s Slice) MarshalBinary() ([]byte, error) {
var data []byte
var err error
for _, m := range s {
data, err = m.appendBinary(data)
if err != nil {
return nil, fmt.Errorf("failed to marshal macaroon %q: %v", m.Id(), err)
}
}
return data, nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
// It accepts all known binary encodings for the data - all the
// embedded macaroons need not be encoded in the same format.
func (s *Slice) UnmarshalBinary(data []byte) error {
// Prevent the internal data structures from holding onto the
// slice by copying it first.
data = append([]byte(nil), data...)
*s = (*s)[:0]
for len(data) > 0 {
var m Macaroon
rest, err := m.parseBinary(data)
if err != nil {
return fmt.Errorf("cannot unmarshal macaroon: %v", err)
}
*s = append(*s, &m)
data = rest
}
return nil
}
const (
padded = 1 << iota
stdEncoding
)
var codecs = [4]*base64.Encoding{
0: base64.RawURLEncoding,
padded: base64.URLEncoding,
stdEncoding: base64.RawStdEncoding,
stdEncoding | padded: base64.StdEncoding,
}
// Base64Decode base64-decodes the given data.
// It accepts both standard and URL encodings, both
// padded and unpadded.
func Base64Decode(data []byte) ([]byte, error) {
encoding := 0
if len(data) > 0 && data[len(data)-1] == '=' {
encoding |= padded
}
for _, b := range data {
if b == '/' || b == '+' {
encoding |= stdEncoding
break
}
}
codec := codecs[encoding]
buf := make([]byte, codec.DecodedLen(len(data)))
n, err := codec.Decode(buf, data)
if err == nil {
return buf[0:n], nil
}
return nil, err
}