// Copyright (C) 2019 Storj Labs, Inc. // See LICENSE for copying information. package macaroon import ( "crypto/hmac" "crypto/rand" "crypto/sha256" "crypto/subtle" ) // Macaroon is a struct that determine contextual caveats and authorization. type Macaroon struct { head []byte caveats [][]byte tail []byte } // NewUnrestricted creates Macaroon with random Head and generated Tail. func NewUnrestricted(secret []byte) (*Macaroon, error) { head, err := NewSecret() if err != nil { return nil, err } return &Macaroon{ head: head, tail: sign(secret, head), }, nil } func sign(secret []byte, data []byte) []byte { signer := hmac.New(sha256.New, secret) _, err := signer.Write(data) if err != nil { // Error skipped because sha256 does not return error panic(err) } return signer.Sum(nil) } // NewSecret generates cryptographically random 32 bytes. func NewSecret() (secret []byte, err error) { secret = make([]byte, 32) _, err = rand.Read(secret) if err != nil { return nil, err } return secret, nil } // AddFirstPartyCaveat creates signed macaroon with appended caveat. func (m *Macaroon) AddFirstPartyCaveat(c []byte) (macaroon *Macaroon, err error) { macaroon = m.Copy() macaroon.caveats = append(macaroon.caveats, c) macaroon.tail = sign(macaroon.tail, c) return macaroon, nil } // Validate reconstructs with all caveats from the secret and compares tails, // returning true if the tails match. func (m *Macaroon) Validate(secret []byte) (ok bool) { tail := sign(secret, m.head) for _, cav := range m.caveats { tail = sign(tail, cav) } return subtle.ConstantTimeCompare(tail, m.tail) == 1 } // Tails returns all ancestor tails up to and including the current tail. func (m *Macaroon) Tails(secret []byte) [][]byte { tails := make([][]byte, 0, len(m.caveats)+1) tail := sign(secret, m.head) tails = append(tails, tail) for _, cav := range m.caveats { tail = sign(tail, cav) tails = append(tails, tail) } return tails } // Head returns copy of macaroon head. func (m *Macaroon) Head() (head []byte) { if len(m.head) == 0 { return nil } return append([]byte(nil), m.head...) } // CaveatLen returns the number of caveats this macaroon has. func (m *Macaroon) CaveatLen() int { return len(m.caveats) } // Caveats returns copy of macaroon caveats. func (m *Macaroon) Caveats() (caveats [][]byte) { if len(m.caveats) == 0 { return nil } caveats = make([][]byte, 0, len(m.caveats)) for _, cav := range m.caveats { caveats = append(caveats, append([]byte(nil), cav...)) } return caveats } // Tail returns copy of macaroon tail. func (m *Macaroon) Tail() (tail []byte) { if len(m.tail) == 0 { return nil } return append([]byte(nil), m.tail...) } // Copy return copy of macaroon. func (m *Macaroon) Copy() *Macaroon { return &Macaroon{ head: m.Head(), caveats: m.Caveats(), tail: m.Tail(), } }