You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lntop/vendor/gopkg.in/macaroon.v2/macaroon.go

400 lines
12 KiB
Go

// The macaroon package implements macaroons as described in
// the paper "Macaroons: Cookies with Contextual Caveats for
// Decentralized Authorization in the Cloud"
// (http://theory.stanford.edu/~ataly/Papers/macaroons.pdf)
//
// See the macaroon bakery packages at http://godoc.org/gopkg.in/macaroon-bakery.v2
// for higher level services and operations that use macaroons.
package macaroon
import (
"bytes"
"crypto/hmac"
"crypto/rand"
"fmt"
"io"
"unicode/utf8"
)
// Macaroon holds a macaroon.
// See Fig. 7 of http://theory.stanford.edu/~ataly/Papers/macaroons.pdf
// for a description of the data contained within.
// Macaroons are mutable objects - use Clone as appropriate
// to avoid unwanted mutation.
type Macaroon struct {
location string
id []byte
caveats []Caveat
sig [hashLen]byte
version Version
}
// Equal reports whether m has exactly the same content as m1.
func (m *Macaroon) Equal(m1 *Macaroon) bool {
if m == m1 || m == nil || m1 == nil {
return m == m1
}
if m.location != m1.location ||
!bytes.Equal(m.id, m1.id) ||
m.sig != m1.sig ||
m.version != m1.version ||
len(m.caveats) != len(m1.caveats) {
return false
}
for i, c := range m.caveats {
if !c.Equal(m1.caveats[i]) {
return false
}
}
return true
}
// Caveat holds a first party or third party caveat.
type Caveat struct {
// Id holds the id of the caveat. For first
// party caveats this holds the condition;
// for third party caveats this holds the encrypted
// third party caveat.
Id []byte
// VerificationId holds the verification id. If this is
// non-empty, it's a third party caveat.
VerificationId []byte
// For third-party caveats, Location holds the
// ocation hint. Note that this is not signature checked
// as part of the caveat, so should only
// be used as a hint.
Location string
}
// Equal reports whether c is equal to c1.
func (c Caveat) Equal(c1 Caveat) bool {
return bytes.Equal(c.Id, c1.Id) &&
bytes.Equal(c.VerificationId, c1.VerificationId) &&
c.Location == c1.Location
}
// isThirdParty reports whether the caveat must be satisfied
// by some third party (if not, it's a first person caveat).
func (cav *Caveat) isThirdParty() bool {
return len(cav.VerificationId) > 0
}
// New returns a new macaroon with the given root key,
// identifier, location and version.
func New(rootKey, id []byte, loc string, version Version) (*Macaroon, error) {
var m Macaroon
if version < V2 {
if !utf8.Valid(id) {
return nil, fmt.Errorf("invalid id for %v macaroon", id)
}
// TODO check id length too.
}
if version < V1 || version > LatestVersion {
return nil, fmt.Errorf("invalid version %v", version)
}
m.version = version
m.init(append([]byte(nil), id...), loc, version)
derivedKey := makeKey(rootKey)
m.sig = *keyedHash(derivedKey, m.id)
return &m, nil
}
// init initializes the macaroon. It retains a reference to id.
func (m *Macaroon) init(id []byte, loc string, vers Version) {
m.location = loc
m.id = append([]byte(nil), id...)
m.version = vers
}
// SetLocation sets the location associated with the macaroon.
// Note that the location is not included in the macaroon's
// hash chain, so this does not change the signature.
func (m *Macaroon) SetLocation(loc string) {
m.location = loc
}
// Clone returns a copy of the receiving macaroon.
func (m *Macaroon) Clone() *Macaroon {
m1 := *m
// Ensure that if any caveats are appended to the new
// macaroon, it will copy the caveats.
m1.caveats = m1.caveats[0:len(m1.caveats):len(m1.caveats)]
return &m1
}
// Location returns the macaroon's location hint. This is
// not verified as part of the macaroon.
func (m *Macaroon) Location() string {
return m.location
}
// Id returns the id of the macaroon. This can hold
// arbitrary information.
func (m *Macaroon) Id() []byte {
return append([]byte(nil), m.id...)
}
// Signature returns the macaroon's signature.
func (m *Macaroon) Signature() []byte {
// sig := m.sig
// return sig[:]
// Work around https://github.com/golang/go/issues/9537
sig := new([hashLen]byte)
*sig = m.sig
return sig[:]
}
// Caveats returns the macaroon's caveats.
// This method will probably change, and it's important not to change the returned caveat.
func (m *Macaroon) Caveats() []Caveat {
return m.caveats[0:len(m.caveats):len(m.caveats)]
}
// appendCaveat appends a caveat without modifying the macaroon's signature.
func (m *Macaroon) appendCaveat(caveatId, verificationId []byte, loc string) {
if len(verificationId) == 0 {
// Ensure that an empty vid is always represented by nil,
// so that marshalers don't procuce spurious zero-length
// vid fields which can confuse some verifiers.
verificationId = nil
}
m.caveats = append(m.caveats, Caveat{
Id: caveatId,
VerificationId: verificationId,
Location: loc,
})
}
func (m *Macaroon) addCaveat(caveatId, verificationId []byte, loc string) error {
if m.version < V2 {
if !utf8.Valid(caveatId) {
return fmt.Errorf("invalid caveat id for %v macaroon", m.version)
}
// TODO check caveat length too.
}
m.appendCaveat(caveatId, verificationId, loc)
if len(verificationId) == 0 {
m.sig = *keyedHash(&m.sig, caveatId)
} else {
m.sig = *keyedHash2(&m.sig, verificationId, caveatId)
}
return nil
}
func keyedHash2(key *[keyLen]byte, d1, d2 []byte) *[hashLen]byte {
var data [hashLen * 2]byte
copy(data[0:], keyedHash(key, d1)[:])
copy(data[hashLen:], keyedHash(key, d2)[:])
return keyedHash(key, data[:])
}
// Bind prepares the macaroon for being used to discharge the
// macaroon with the given signature sig. This must be
// used before it is used in the discharges argument to Verify.
func (m *Macaroon) Bind(sig []byte) {
m.sig = *bindForRequest(sig, &m.sig)
}
// AddFirstPartyCaveat adds a caveat that will be verified
// by the target service.
func (m *Macaroon) AddFirstPartyCaveat(condition []byte) error {
m.addCaveat(condition, nil, "")
return nil
}
// AddThirdPartyCaveat adds a third-party caveat to the macaroon,
// using the given shared root key, caveat id and location hint.
// The caveat id should encode the root key in some
// way, either by encrypting it with a key known to the third party
// or by holding a reference to it stored in the third party's
// storage.
func (m *Macaroon) AddThirdPartyCaveat(rootKey, caveatId []byte, loc string) error {
return m.addThirdPartyCaveatWithRand(rootKey, caveatId, loc, rand.Reader)
}
// addThirdPartyCaveatWithRand adds a third-party caveat to the macaroon, using
// the given source of randomness for encrypting the caveat id.
func (m *Macaroon) addThirdPartyCaveatWithRand(rootKey, caveatId []byte, loc string, r io.Reader) error {
derivedKey := makeKey(rootKey)
verificationId, err := encrypt(&m.sig, derivedKey, r)
if err != nil {
return err
}
m.addCaveat(caveatId, verificationId, loc)
return nil
}
var zeroKey [hashLen]byte
// bindForRequest binds the given macaroon
// to the given signature of its parent macaroon.
func bindForRequest(rootSig []byte, dischargeSig *[hashLen]byte) *[hashLen]byte {
if bytes.Equal(rootSig, dischargeSig[:]) {
return dischargeSig
}
return keyedHash2(&zeroKey, rootSig, dischargeSig[:])
}
// Verify verifies that the receiving macaroon is valid.
// The root key must be the same that the macaroon was originally
// minted with. The check function is called to verify each
// first-party caveat - it should return an error if the
// condition is not met.
//
// The discharge macaroons should be provided in discharges.
//
// Verify returns nil if the verification succeeds.
func (m *Macaroon) Verify(rootKey []byte, check func(caveat string) error, discharges []*Macaroon) error {
var vctx verificationContext
vctx.init(rootKey, m, discharges, check)
return vctx.verify(m, rootKey)
}
// VerifySignature verifies the signature of the given macaroon with respect
// to the root key, but it does not validate any first-party caveats. Instead
// it returns all the applicable first party caveats on success.
//
// The caller is responsible for checking the returned first party caveat
// conditions.
func (m *Macaroon) VerifySignature(rootKey []byte, discharges []*Macaroon) ([]string, error) {
n := len(m.caveats)
for _, dm := range discharges {
n += len(dm.caveats)
}
conds := make([]string, 0, n)
var vctx verificationContext
vctx.init(rootKey, m, discharges, func(cond string) error {
conds = append(conds, cond)
return nil
})
err := vctx.verify(m, rootKey)
if err != nil {
return nil, err
}
return conds, nil
}
// TraceVerify verifies the signature of the macaroon without checking
// any of the first party caveats, and returns a slice of Traces holding
// the operations used when verifying the macaroons.
//
// Each element in the returned slice corresponds to the
// operation for one of the argument macaroons, with m at index 0,
// and discharges at 1 onwards.
func (m *Macaroon) TraceVerify(rootKey []byte, discharges []*Macaroon) ([]Trace, error) {
var vctx verificationContext
vctx.init(rootKey, m, discharges, func(string) error { return nil })
vctx.traces = make([]Trace, len(discharges)+1)
err := vctx.verify(m, rootKey)
return vctx.traces, err
}
type verificationContext struct {
used []bool
discharges []*Macaroon
rootSig *[hashLen]byte
traces []Trace
check func(caveat string) error
}
func (vctx *verificationContext) init(rootKey []byte, root *Macaroon, discharges []*Macaroon, check func(caveat string) error) {
*vctx = verificationContext{
discharges: discharges,
used: make([]bool, len(discharges)),
rootSig: &root.sig,
check: check,
}
}
func (vctx *verificationContext) verify(root *Macaroon, rootKey []byte) error {
vctx.traceRootKey(0, rootKey)
vctx.trace(0, TraceMakeKey, rootKey, nil)
derivedKey := makeKey(rootKey)
if err := vctx.verify0(root, 0, derivedKey); err != nil {
vctx.trace(0, TraceFail, nil, nil)
return err
}
for i, wasUsed := range vctx.used {
if !wasUsed {
vctx.trace(i+1, TraceFail, nil, nil)
return fmt.Errorf("discharge macaroon %q was not used", vctx.discharges[i].Id())
}
}
return nil
}
func (vctx *verificationContext) verify0(m *Macaroon, index int, rootKey *[hashLen]byte) error {
vctx.trace(index, TraceHash, m.id, nil)
caveatSig := keyedHash(rootKey, m.id)
for i, cav := range m.caveats {
if cav.isThirdParty() {
cavKey, err := decrypt(caveatSig, cav.VerificationId)
if err != nil {
return fmt.Errorf("failed to decrypt caveat %d signature: %v", i, err)
}
dm, di, err := vctx.findDischarge(cav.Id)
if err != nil {
return err
}
vctx.traceRootKey(di+1, cavKey[:])
if err := vctx.verify0(dm, di+1, cavKey); err != nil {
vctx.trace(di+1, TraceFail, nil, nil)
return err
}
vctx.trace(index, TraceHash, cav.VerificationId, cav.Id)
caveatSig = keyedHash2(caveatSig, cav.VerificationId, cav.Id)
} else {
vctx.trace(index, TraceHash, cav.Id, nil)
caveatSig = keyedHash(caveatSig, cav.Id)
if err := vctx.check(string(cav.Id)); err != nil {
return err
}
}
}
if index > 0 {
vctx.trace(index, TraceBind, vctx.rootSig[:], caveatSig[:])
caveatSig = bindForRequest(vctx.rootSig[:], caveatSig)
}
// TODO perhaps we should actually do this check before doing
// all the potentially expensive caveat checks.
if !hmac.Equal(caveatSig[:], m.sig[:]) {
return fmt.Errorf("signature mismatch after caveat verification")
}
return nil
}
func (vctx *verificationContext) findDischarge(id []byte) (dm *Macaroon, index int, err error) {
for di, dm := range vctx.discharges {
if !bytes.Equal(dm.id, id) {
continue
}
// Don't use a discharge macaroon more than once.
// It's important that we do this check here rather than after
// verify as it prevents potentially infinite recursion.
if vctx.used[di] {
return nil, 0, fmt.Errorf("discharge macaroon %q was used more than once", dm.Id())
}
vctx.used[di] = true
return dm, di, nil
}
return nil, 0, fmt.Errorf("cannot find discharge macaroon for caveat %x", id)
}
func (vctx *verificationContext) trace(index int, op TraceOpKind, data1, data2 []byte) {
if vctx.traces != nil {
vctx.traces[index].Ops = append(vctx.traces[index].Ops, TraceOp{
Kind: op,
Data1: data1,
Data2: data2,
})
}
}
func (vctx *verificationContext) traceRootKey(index int, rootKey []byte) {
if vctx.traces != nil {
vctx.traces[index].RootKey = rootKey[:]
}
}