biscuit

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 18, 2022 License: Apache-2.0 Imports: 15 Imported by: 1

README

biscuit-go

biscuit-go is an implementation of Biscuit in Go. It aims to be fully compatible with other existing implementations, so that tokens issued by, for example, the Rust version, could be validated by this library and vice versa.

Documentation and specifications

Usage

Create a biscuit
rng := rand.Reader
publicRoot, privateRoot, _ := ed25519.GenerateKey(rng)
builder := biscuit.NewBuilder(privateRoot)

fact1, err := parser.FromStringFact(`right("/a/file1.txt", "read")`)
if err != nil {
    panic(fmt.Errorf("failed to parse authority facts: %v", err))
}

err := builder.AddAuthorityFact(fact1)
if err != nil {
    panic(fmt.Errorf("failed to add authority facts: %v", err))
}

// ... add more authority facts, rules, caveats...

b, err := builder.Build()
if err != nil {
    panic(fmt.Errorf("failed to build biscuit: %v", err))
}
token, err := b.Serialize()
if err != nil {
    panic(fmt.Errorf("failed to serialize biscuit: %v", err))
}

// token is now a []byte, ready to be shared
Attenuate a biscuit
b, err = biscuit.Unmarshal(token)
if err != nil {
    panic(fmt.Errorf("failed to deserialize biscuit: %v", err))
}

// Attenuate the biscuit by appending a new block to it
blockBuilder := b.CreateBlock()
blockBuilder.AddFact(biscuit.Fact{/* ... */})

// ... add more facts, rules, caveats...

attenuatedBiscuit, err := b.Append(rng, blockBuilder.Build())
if err != nil {
    panic(fmt.Errorf("failed to append: %v", err))
}
attenuatedToken, err := b.Serialize()
if err != nil {
    panic(fmt.Errorf("failed to serialize biscuit: %v", err))
}

// token is now a []byte attenuation of the original token, and ready to be shared
Verify a biscuit
b, err := biscuit.Unmarshal(token)
if err != nil {
    panic(fmt.Errorf("failed to deserialize token: %v", err))
}

authorizer, err := b.Authorizer(publicRoot)
if err != nil {
    panic(fmt.Errorf("failed to verify token and create authorizer: %v", err))
}

fact1, err := parser.FromStringFact(`resource("/a/file1.txt")`)
if err != nil {
    panic(fmt.Errorf("failed to parse authority facts: %v", err))
}

auhorizer.AddFact(fact1)

// ... add more ambient facts, rules, caveats...

authorizer.AddPolicy(biscuit.DefaultAllowPolicy)

if err := authorizer.Authorize(); err != nil {
    fmt.Printf("failed authorizing token: %v\n", err)
} else {
    fmt.Println("success authorizing token")
}
Using biscuit-go grammar

To ease adding facts, rules, or caveats, a simple grammar and a parser are available, allowing to declare biscuit elements as plain strings. See GRAMMAR reference for the complete syntax.

p := parser.New()
b.AddFact(p.Must().Fact(`resource("/a/file1.txt")`))
b.AddRule(p.Must().Rule(`
    can_read($file) 
        <- resource($file)
        $file.starts_with("/a/")
`))

Examples

License

Licensed under Apache License, Version 2.0.

Documentation

Index

Examples

Constants

View Source
const (
	PolicyKindAllow = iota
	PolicyKindDeny
)
View Source
const MaxSchemaVersion uint32 = 2

Variables

View Source
var (
	ErrMissingSymbols   = errors.New("biscuit: missing symbols")
	ErrPolicyDenied     = errors.New("biscuit: denied by policy")
	ErrNoMatchingPolicy = errors.New("biscuit: denied by no matching policies")
)
View Source
var (
	// ErrSymbolTableOverlap is returned when multiple blocks declare the same symbols
	ErrSymbolTableOverlap = errors.New("biscuit: symbol table overlap")
	// ErrInvalidAuthorityIndex occurs when an authority block index is not 0
	ErrInvalidAuthorityIndex = errors.New("biscuit: invalid authority index")
	// ErrInvalidAuthorityFact occurs when an authority fact is an ambient fact
	ErrInvalidAuthorityFact = errors.New("biscuit: invalid authority fact")
	// ErrInvalidBlockFact occurs when a block fact provides an authority or ambient fact
	ErrInvalidBlockFact = errors.New("biscuit: invalid block fact")
	// ErrInvalidBlockRule occurs when a block rule generate an authority or ambient fact
	ErrInvalidBlockRule = errors.New("biscuit: invalid block rule")
	// ErrEmptyKeys is returned when verifying a biscuit having no keys
	ErrEmptyKeys = errors.New("biscuit: empty keys")
	// ErrUnknownPublicKey is returned when verifying a biscuit with the wrong public key
	ErrUnknownPublicKey = errors.New("biscuit: unknown public key")

	ErrInvalidSignature = errors.New("biscuit: invalid signature")

	ErrInvalidSignatureSize = errors.New("biscuit: invalid signature size")

	ErrInvalidKeySize = errors.New("biscuit: invalid key size")

	UnsupportedAlgorithm = errors.New("biscuit: unsupported signature algorithm")
)
View Source
var (
	ErrDuplicateFact     = errors.New("biscuit: fact already exists")
	ErrInvalidBlockIndex = errors.New("biscuit: invalid block index")
)
View Source
var (
	// DefaultAllowPolicy allows the biscuit to verify sucessfully as long as all its rules generate some facts.
	DefaultAllowPolicy = Policy{Kind: PolicyKindAllow, Queries: []Rule{{Head: Predicate{Name: "true"}}}}
	// DefaultDenyPolicy makes the biscuit verification fail in all cases.
	DefaultDenyPolicy = Policy{Kind: PolicyKindDeny, Queries: []Rule{{Head: Predicate{Name: "true"}}}}
)
View Source
var ErrFactNotFound = errors.New("biscuit: fact not found")

Functions

func WihtRandom

func WihtRandom(rng io.Reader) builderOption

func WithSymbols

func WithSymbols(symbols *datalog.SymbolTable) builderOption

Types

type Authorizer

type Authorizer interface {
	AddFact(fact Fact)
	AddRule(rule Rule)
	AddCheck(check Check)
	AddPolicy(policy Policy)
	Authorize() error
	Query(rule Rule) (FactSet, error)
	Biscuit() *Biscuit
	Reset()
	PrintWorld() string
	LoadPolicies([]byte) error
	SerializePolicies() ([]byte, error)
}

func NewVerifier

func NewVerifier(b *Biscuit) (Authorizer, error)

type BinaryOp

type BinaryOp binaryOpType
const (
	BinaryUndefined BinaryOp = iota
	BinaryLessThan
	BinaryLessOrEqual
	BinaryGreaterThan
	BinaryGreaterOrEqual
	BinaryEqual
	BinaryContains
	BinaryPrefix
	BinarySuffix
	BinaryRegex
	BinaryAdd
	BinarySub
	BinaryMul
	BinaryDiv
	BinaryAnd
	BinaryOr
	BinaryIntersection
	BinaryUnion
)

func (BinaryOp) Type

func (BinaryOp) Type() OpType

type Biscuit

type Biscuit struct {
	// contains filtered or unexported fields
}

Biscuit represents a valid Biscuit token It contains multiple `Block` elements, the associated symbol table, and a serialized version of this data

Example
rng := rand.Reader
publicRoot, privateRoot, _ := ed25519.GenerateKey(rng)
builder := biscuit.NewBuilder(privateRoot)

fact1, err := parser.FromStringFact(`right("/a/file1.txt", "read")`)
if err != nil {
	panic(fmt.Errorf("failed to parse authority facts: %v", err))
}
err = builder.AddAuthorityFact(fact1)
if err != nil {
	panic(fmt.Errorf("failed to add authority facts: %v", err))
}

fact2, err := parser.FromStringFact(`right("/a/file1.txt", "write")`)
if err != nil {
	panic(fmt.Errorf("failed to parse authority facts: %v", err))
}
err = builder.AddAuthorityFact(fact2)
if err != nil {
	panic(fmt.Errorf("failed to add authority facts: %v", err))
}

fact3, err := parser.FromStringFact(`right("/a/file2.txt", "read")`)
if err != nil {
	panic(fmt.Errorf("failed to parse authority facts: %v", err))
}
err = builder.AddAuthorityFact(fact3)
if err != nil {
	panic(fmt.Errorf("failed to add authority facts: %v", err))
}

fact4, err := parser.FromStringFact(`right("/a/file3.txt", "write")`)
if err != nil {
	panic(fmt.Errorf("failed to parse authority facts: %v", err))
}
err = builder.AddAuthorityFact(fact4)
if err != nil {
	panic(fmt.Errorf("failed to add authority facts: %v", err))
}

b, err := builder.Build()
if err != nil {
	panic(fmt.Errorf("failed to build biscuit: %v", err))
}

token, err := b.Serialize()
if err != nil {
	panic(fmt.Errorf("failed to serialize biscuit: %v", err))
}

fmt.Printf("Token1 length: %d\n", len(token))

deser, err := biscuit.Unmarshal(token)
if err != nil {
	panic(fmt.Errorf("failed to deserialize biscuit: %v", err))
}

blockBuilder := deser.CreateBlock()

check, err := parser.FromStringCheck(`check if resource($file), operation($permission), ["read"].contains($permission)`)
if err != nil {
	panic(fmt.Errorf("failed to parse check: %v", err))
}
err = blockBuilder.AddCheck(check)
if err != nil {
	panic(fmt.Errorf("failed to add block check: %v", err))
}

b2, err := deser.Append(rng, blockBuilder.Build())
if err != nil {
	panic(fmt.Errorf("failed to append: %v", err))
}

token2, err := b2.Serialize()
if err != nil {
	panic(fmt.Errorf("failed to serialize biscuit: %v", err))
}

fmt.Printf("Token2 length: %d\n", len(token2))

// Verify
b2, err = biscuit.Unmarshal(token2)
if err != nil {
	panic(fmt.Errorf("failed to deserialize token: %v", err))
}

v1, err := b2.Authorizer(publicRoot)
if err != nil {
	panic(fmt.Errorf("failed to create verifier: %v", err))
}

vfact1, err := parser.FromStringFact(`resource("/a/file1.txt")`)
if err != nil {
	panic(fmt.Errorf("failed to parse verifier fact: %v", err))
}
v1.AddFact(vfact1)

vfact2, err := parser.FromStringFact(`operation("read")`)
if err != nil {
	panic(fmt.Errorf("failed to parse verifier fact: %v", err))
}
v1.AddFact(vfact2)

policy, err := parser.FromStringPolicy(`allow if resource("/a/file1.txt")`)
if err != nil {
	panic(fmt.Errorf("failed to parse verifier policy: %v", err))
}
v1.AddPolicy(policy)

if err := v1.Authorize(); err != nil {
	fmt.Println(v1.PrintWorld())
	fmt.Println("forbidden to read /a/file1.txt")
} else {
	//fmt.Println(v1.PrintWorld())

	fmt.Println("allowed to read /a/file1.txt")
}

v1, _ = b2.Authorizer(publicRoot)

vfact1, err = parser.FromStringFact(`resource("/a/file1.txt")`)
if err != nil {
	panic(fmt.Errorf("failed to parse verifier fact: %v", err))
}
v1.AddFact(vfact1)

vfact2, err = parser.FromStringFact(`operation("write")`)
if err != nil {
	panic(fmt.Errorf("failed to parse verifier fact: %v", err))
}
v1.AddFact(vfact2)

policy, err = parser.FromStringPolicy(`allow if resource("/a/file1.txt")`)
if err != nil {
	panic(fmt.Errorf("failed to parse verifier policy: %v", err))
}
v1.AddPolicy(policy)

if err := v1.Authorize(); err != nil {
	fmt.Println("forbidden to write /a/file1.txt")
} else {
	fmt.Println("allowed to write /a/file1.txt")
}
Output:

Token1 length: 260
Token2 length: 446
allowed to read /a/file1.txt
forbidden to write /a/file1.txt

func New

func New(rng io.Reader, root ed25519.PrivateKey, baseSymbols *datalog.SymbolTable, authority *Block) (*Biscuit, error)

func Unmarshal

func Unmarshal(serialized []byte) (*Biscuit, error)

func (*Biscuit) Append

func (b *Biscuit) Append(rng io.Reader, block *Block) (*Biscuit, error)

func (*Biscuit) Authorizer

func (b *Biscuit) Authorizer(root ed25519.PublicKey) (Authorizer, error)

Checks the signature and creates an Authorizer The Authorizer can then test the authorizaion policies and accept or refuse the request

func (*Biscuit) BlockCount

func (b *Biscuit) BlockCount() int

func (*Biscuit) Checks

func (b *Biscuit) Checks() [][]datalog.Check

func (*Biscuit) CreateBlock

func (b *Biscuit) CreateBlock() BlockBuilder

func (*Biscuit) GetBlockID

func (b *Biscuit) GetBlockID(fact Fact) (int, error)

GetBlockID returns the first block index containing a fact starting from the authority block and then each block in the order they were added. ErrFactNotFound is returned when no block contains the fact.

func (*Biscuit) RevocationIds

func (b *Biscuit) RevocationIds() [][]byte

func (*Biscuit) Seal

func (b *Biscuit) Seal(rng io.Reader) (*Biscuit, error)

func (*Biscuit) Serialize

func (b *Biscuit) Serialize() ([]byte, error)

func (*Biscuit) String

func (b *Biscuit) String() string

type Block

type Block struct {
	// contains filtered or unexported fields
}

func (*Block) String

func (b *Block) String(symbols *datalog.SymbolTable) string

type BlockBuilder

type BlockBuilder interface {
	AddFact(fact Fact) error
	AddRule(rule Rule) error
	AddCheck(check Check) error
	SetContext(string)
	Build() *Block
}

func NewBlockBuilder

func NewBlockBuilder(baseSymbols *datalog.SymbolTable) BlockBuilder

type Bool

type Bool bool

func (Bool) String

func (b Bool) String() string

func (Bool) Type

func (b Bool) Type() TermType

type Builder

type Builder interface {
	AddAuthorityFact(fact Fact) error
	AddAuthorityRule(rule Rule) error
	AddAuthorityCheck(check Check) error
	Build() (*Biscuit, error)
}

func NewBuilder

func NewBuilder(root ed25519.PrivateKey, opts ...builderOption) Builder

type Bytes

type Bytes []byte

func (Bytes) String

func (a Bytes) String() string

func (Bytes) Type

func (a Bytes) Type() TermType

type Check

type Check struct {
	Queries []Rule
}

type Date

type Date time.Time

func (Date) String

func (a Date) String() string

func (Date) Type

func (a Date) Type() TermType

type Expression

type Expression []Op

type Fact

type Fact struct {
	Predicate
}

func (Fact) String

func (f Fact) String() string

type FactSet

type FactSet []Fact

func (FactSet) String

func (fs FactSet) String() string

type Integer

type Integer int64

func (Integer) String

func (a Integer) String() string

func (Integer) Type

func (a Integer) Type() TermType

type Op

type Op interface {
	Type() OpType
	// contains filtered or unexported methods
}

type OpType

type OpType byte
const (
	OpTypeValue OpType = iota
	OpTypeUnary
	OpTypeBinary
)

type Policy

type Policy struct {
	Queries []Rule
	Kind    PolicyKind
}

type PolicyKind

type PolicyKind byte

type Predicate

type Predicate struct {
	Name string
	IDs  []Term
}

func (Predicate) String

func (p Predicate) String() string

type Rule

type Rule struct {
	Head        Predicate
	Body        []Predicate
	Expressions []Expression
}

type Set

type Set []Term

func (Set) String

func (a Set) String() string

func (Set) Type

func (a Set) Type() TermType

type String

type String string

func (String) String

func (a String) String() string

func (String) Type

func (a String) Type() TermType

type Term

type Term interface {
	Type() TermType
	String() string
	// contains filtered or unexported methods
}

type TermType

type TermType byte
const (
	TermTypeSymbol TermType = iota
	TermTypeVariable
	TermTypeInteger
	TermTypeString
	TermTypeDate
	TermTypeBytes
	TermTypeBool
	TermTypeSet
)

type UnaryOp

type UnaryOp unaryOpType
const (
	UnaryUndefined UnaryOp = iota
	UnaryNegate
	UnaryParens
	UnaryLength
)

func (UnaryOp) Type

func (UnaryOp) Type() OpType

type Unmarshaler

type Unmarshaler struct {
	Symbols *datalog.SymbolTable
}

func (*Unmarshaler) Unmarshal

func (u *Unmarshaler) Unmarshal(serialized []byte) (*Biscuit, error)

type Value

type Value struct {
	Term Term
}

func (Value) Type

func (v Value) Type() OpType

type Variable

type Variable string

func (Variable) String

func (a Variable) String() string

func (Variable) Type

func (a Variable) Type() TermType

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL