lsat

package
v0.0.2 Latest Latest
Warning

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

Go to latest
Published: Dec 7, 2023 License: MIT Imports: 29 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// GRPCErrCode is the error code we receive from a gRPC call if the
	// server expects a payment.
	GRPCErrCode = codes.Internal

	// GRPCErrCodeNew is the new error code we received for a "402 payment
	// required" error with version google.golang.org/grpc v1.41.0. This
	// error should not really be returned by any aperture instance since
	// we never deployed any version with google.golang.org/grpc v1.41.0 in
	// the wild as there were other issues with it as well. So this is just
	// here for completeness and shouldn't be used.
	GRPCErrCodeNew = codes.Unknown

	// GRPCErrMessage is the error message we receive from a gRPC call in
	// conjunction with the GRPCErrCode to signal the client that a payment
	// is required to access the service.
	GRPCErrMessage = "payment required"

	// AuthHeader is is the HTTP response header that contains the payment
	// challenge.
	AuthHeader = "WWW-Authenticate"

	// DefaultMaxCostSats is the default maximum amount in satoshis that we
	// are going to pay for an LSAT automatically. Does not include routing
	// fees.
	DefaultMaxCostSats = 1000

	// DefaultMaxRoutingFeeSats is the default maximum routing fee in
	// satoshis that we are going to pay to acquire an LSAT token.
	DefaultMaxRoutingFeeSats = 10

	// PaymentTimeout is the maximum time we allow a payment to take before
	// we stop waiting for it.
	PaymentTimeout = 60 * time.Second
)
View Source
const (
	// HeaderAuthorization is the HTTP header field name that is used to
	// send the LSAT by REST clients.
	HeaderAuthorization = "Authorization"

	// HeaderMacaroonMD is the HTTP header field name that is used to send
	// the LSAT by certain REST and gRPC clients.
	HeaderMacaroonMD = "Grpc-Metadata-Macaroon"

	// HeaderMacaroon is the HTTP header field name that is used to send the
	// LSAT by our own gRPC clients.
	HeaderMacaroon = "Macaroon"
)
View Source
const (
	// LatestVersion is the latest version used for minting new LSATs.
	LatestVersion = 0

	// SecretSize is the size in bytes of a LSAT's secret, also known as
	// the root key of the macaroon.
	SecretSize = 32

	// TokenIDSize is the size in bytes of an LSAT's ID encoded in its
	// macaroon identifier.
	TokenIDSize = 32
)
View Source
const (
	// CondServices is the condition used for a services caveat.
	CondServices = "services"

	// CondCapabilitiesSuffix is the condition suffix used for a service's
	// capabilities caveat. For example, the condition of a capabilities
	// caveat for a service named `loop` would be `loop_capabilities`.
	CondCapabilitiesSuffix = "_capabilities"

	// CondTimeoutSuffix is the condition suffix used for a service's
	// timeout caveat.
	CondTimeoutSuffix = "_valid_until"
)
View Source
const (
	// PreimageKey is the key used for a payment preimage caveat.
	PreimageKey = "preimage"
)
View Source
const Subsystem = "LSAT"

Subsystem defines the sub system name of this package.

Variables

View Source
var (
	// ErrNoServices is an error returned when we attempt to decode the
	// services included in a caveat.
	ErrNoServices = errors.New("no services found")

	// ErrInvalidService is an error returned when we attempt to decode a
	// service with an invalid format.
	ErrInvalidService = errors.New("service must be of the form " +
		"\"name:tier\"")
)
View Source
var (
	// ErrInvalidCaveat is an error returned when we attempt to decode a
	// caveat with an invalid format.
	ErrInvalidCaveat = errors.New("caveat must be of the form " +
		"\"condition=value\"")
)
View Source
var (
	// ErrNoToken is the error returned when the store doesn't contain a
	// token yet.
	ErrNoToken = errors.New("no token in store")
)
View Source
var (

	// ErrUnknownVersion is an error returned when attempting to decode an
	// LSAT identifier with an unknown version.
	ErrUnknownVersion = errors.New("unknown LSAT version")
)
View Source
var (
	// KeyTokenID is the key under which we store the client's token ID in
	// the request context.
	KeyTokenID = ContextKey{"tokenid"}
)

Functions

func AddFirstPartyCaveats

func AddFirstPartyCaveats(m *macaroon.Macaroon, caveats ...Caveat) error

AddFirstPartyCaveats adds a set of caveats as first-party caveats to a macaroon.

func AddToContext

func AddToContext(ctx context.Context, key ContextKey,
	value interface{}) context.Context

AddToContext adds the given value to the context for easy retrieval later on.

func EncodeCaveat

func EncodeCaveat(c Caveat) string

EncodeCaveat encodes a caveat into its string representation.

func EncodeIdentifier

func EncodeIdentifier(w io.Writer, id *Identifier) error

EncodeIdentifier encodes an LSAT's identifier according to its version.

func FromContext

func FromContext(ctx context.Context, key ContextKey) interface{}

FromContext tries to extract a value from the given context.

func FromHeader

func FromHeader(header *http.Header) (*macaroon.Macaroon, lntypes.Preimage, error)

FromHeader tries to extract authentication information from HTTP headers. There are two supported formats that can be sent in three different header fields:

  1. Authorization: LSAT <macBase64>:<preimageHex>
  2. Grpc-Metadata-Macaroon: <macHex>
  3. Macaroon: <macHex>

If only the macaroon is sent in header 2 or three then it is expected to have a caveat with the preimage attached to it.

func HasCaveat

func HasCaveat(m *macaroon.Macaroon, cond string) (string, bool)

HasCaveat checks whether the given macaroon has a caveat with the given condition, and if so, returns its value. If multiple caveats with the same condition exist, then the value of the last one is returned.

func IsPaymentRequired

func IsPaymentRequired(err error) bool

IsPaymentRequired inspects an error to find out if it's the specific gRPC error returned by the server to indicate a payment is required to access the service.

func SetHeader

func SetHeader(header *http.Header, mac *macaroon.Macaroon,
	preimage fmt.Stringer) error

SetHeader sets the provided authentication elements as the default/standard HTTP header for the LSAT protocol.

func UseLogger

func UseLogger(logger btclog.Logger)

UseLogger uses a specified Logger to output package logging info. This should be used in preference to SetLogWriter if the caller is also using btclog.

func VerifyCaveats

func VerifyCaveats(caveats []Caveat, satisfiers ...Satisfier) error

VerifyCaveats determines whether every relevant caveat of an LSAT holds true. A caveat is considered relevant if a satisfier is provided for it, which is what we'll use as their evaluation.

NOTE: The caveats provided should be in the same order as in the LSAT to ensure the correctness of each satisfier's SatisfyPrevious.

Types

type Caveat

type Caveat struct {
	// Condition serves as a way to identify a caveat and how to satisfy it.
	Condition string

	// Value is what will be used to satisfy a caveat. This can be as
	// flexible as needed, as long as it can be encoded into a string.
	Value string
}

Caveat is a predicate that can be applied to an LSAT in order to restrict its use in some form. Caveats are evaluated during LSAT verification after the LSAT's signature is verified. The predicate of each caveat must hold true in order to successfully validate an LSAT.

func DecodeCaveat

func DecodeCaveat(s string) (Caveat, error)

DecodeCaveat decodes a caveat from its string representation.

func NewCapabilitiesCaveat

func NewCapabilitiesCaveat(serviceName string, capabilities string) Caveat

NewCapabilitiesCaveat creates a new capabilities caveat for the given service.

func NewCaveat

func NewCaveat(condition string, value string) Caveat

NewCaveat construct a new caveat with the given condition and value.

func NewServicesCaveat

func NewServicesCaveat(services ...Service) (Caveat, error)

NewServicesCaveat creates a new services caveat with the provided caveats.

func NewTimeoutCaveat

func NewTimeoutCaveat(serviceName string, numSeconds int64,
	now func() time.Time) Caveat

NewTimeoutCaveat creates a new caveat that will result in a macaroon being valid for numSeconds after the current time.

func (Caveat) String

func (c Caveat) String() string

String returns a user-friendly view of a caveat.

type ClientInterceptor

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

ClientInterceptor is a gRPC client interceptor that can handle LSAT authentication challenges with embedded payment requests. It uses a connection to lnd to automatically pay for an authentication token.

func NewInterceptor

func NewInterceptor(lnd *lndclient.LndServices, store Store,
	rpcCallTimeout time.Duration, maxCost,
	maxFee btcutil.Amount, allowInsecure bool) *ClientInterceptor

NewInterceptor creates a new gRPC client interceptor that uses the provided lnd connection to automatically acquire and pay for LSAT tokens, unless the indicated store already contains a usable token.

func (*ClientInterceptor) StreamInterceptor

func (i *ClientInterceptor) StreamInterceptor(ctx context.Context,
	desc *grpc.StreamDesc, cc *grpc.ClientConn, method string,
	streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream,
	error)

StreamInterceptor is an interceptor method that can be used directly by gRPC for streaming calls. If the store contains a token, it is attached as credentials to every stream establishment call before patching it through. The response error is also intercepted for every initial stream initiation. If there is an error returned and it is indicating a payment challenge, a token is acquired and paid for automatically. The original request is then repeated back to the server, now with the new token attached.

func (*ClientInterceptor) UnaryInterceptor

func (i *ClientInterceptor) UnaryInterceptor(ctx context.Context, method string,
	req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker,
	opts ...grpc.CallOption) error

UnaryInterceptor is an interceptor method that can be used directly by gRPC for unary calls. If the store contains a token, it is attached as credentials to every call before patching it through. The response error is also intercepted for every call. If there is an error returned and it is indicating a payment challenge, a token is acquired and paid for automatically. The original request is then repeated back to the server, now with the new token attached.

type ContextKey

type ContextKey struct {
	Name string
}

ContextKey is the type that we use to identify LSAT specific values in the request context. We wrap the string inside a struct because of this comment in the context API: "The provided key must be comparable and should not be of type string or any other built-in type to avoid collisions between packages using context."

type FileStore

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

FileStore is an implementation of the Store interface that files to save the serialized tokens. There is always just one current token that is either pending or fully paid.

func NewFileStore

func NewFileStore(storeDir string) (*FileStore, error)

NewFileStore creates a new file based token store, creating its file in the provided directory. If the directory does not exist, it will be created.

func (*FileStore) AllTokens

func (f *FileStore) AllTokens() (map[string]*Token, error)

AllTokens returns all tokens that the store has knowledge of, even if they might be expired. The tokens are mapped by their identifying attribute like file name or storage key.

NOTE: This is part of the Store interface.

func (*FileStore) CurrentToken

func (f *FileStore) CurrentToken() (*Token, error)

CurrentToken returns the token that is currently contained in the store or an error if there is none.

NOTE: This is part of the Store interface.

func (*FileStore) RemovePendingToken

func (f *FileStore) RemovePendingToken() error

RemovePendingToken removes a pending token from the store or returns ErrNoToken if there is no pending token.

func (*FileStore) StoreToken

func (f *FileStore) StoreToken(newToken *Token) error

StoreToken saves a token to the store, overwriting any old token if there is one.

NOTE: This is part of the Store interface.

type Identifier

type Identifier struct {
	// Version is the version of an LSAT. Having a version allows us to
	// introduce new fields to the identifier in a backwards-compatible
	// manner.
	Version uint16

	// PaymentHash is the payment hash linked to an LSAT. Verification of
	// an LSAT depends on a valid payment, which is enforced by ensuring a
	// preimage is provided that hashes to our payment hash.
	PaymentHash lntypes.Hash

	// TokenID is the unique identifier of an LSAT.
	TokenID TokenID
}

Identifier contains the static identifying details of an LSAT. This is intended to be used as the identifier of the macaroon within an LSAT.

func DecodeIdentifier

func DecodeIdentifier(r io.Reader) (*Identifier, error)

DecodeIdentifier decodes an LSAT's identifier according to its version.

type MacaroonCredential

type MacaroonCredential struct {
	*macaroon.Macaroon

	// AllowInsecure specifies if the macaroon is allowed to be sent over
	// insecure transport channels. This should only ever be set to true if
	// the insecure connection is proxied through tor and the destination
	// address is an onion service.
	AllowInsecure bool
}

MacaroonCredential wraps a macaroon to implement the credentials.PerRPCCredentials interface.

func NewMacaroonCredential

func NewMacaroonCredential(m *macaroon.Macaroon,
	allowInsecure bool) MacaroonCredential

NewMacaroonCredential returns a copy of the passed macaroon wrapped in a MacaroonCredential struct which implements PerRPCCredentials.

func (MacaroonCredential) GetRequestMetadata

func (m MacaroonCredential) GetRequestMetadata(_ context.Context,
	_ ...string) (map[string]string, error)

GetRequestMetadata implements the PerRPCCredentials interface. This method is required in order to pass the wrapped macaroon into the gRPC context. With this, the macaroon will be available within the request handling scope of the ultimate gRPC server implementation.

func (MacaroonCredential) RequireTransportSecurity

func (m MacaroonCredential) RequireTransportSecurity() bool

RequireTransportSecurity implements the PerRPCCredentials interface.

type Satisfier

type Satisfier struct {
	// Condition is the condition of the caveat we'll attempt to satisfy.
	Condition string

	// SatisfyPrevious ensures a caveat is in accordance with a previous one
	// with the same condition. This is needed since caveats of the same
	// condition can be used multiple times as long as they enforce more
	// permissions than the previous.
	//
	// For example, we have a caveat that only allows us to use an LSAT for
	// 7 more days. We can add another caveat that only allows for 3 more
	// days of use and lend it to another party.
	SatisfyPrevious func(previous Caveat, current Caveat) error

	// SatisfyFinal satisfies the final caveat of an LSAT. If multiple
	// caveats with the same condition exist, this will only be executed
	// once all previous caveats are also satisfied.
	SatisfyFinal func(Caveat) error
}

Satisfier provides a generic interface to satisfy a caveat based on its condition.

func NewCapabilitiesSatisfier

func NewCapabilitiesSatisfier(service string,
	targetCapability string) Satisfier

NewCapabilitiesSatisfier implements a satisfier to determine whether the target capability for a service is authorized for a given LSAT.

func NewServicesSatisfier

func NewServicesSatisfier(targetService string) Satisfier

NewServicesSatisfier implements a satisfier to determine whether the target service is authorized for a given LSAT.

TODO(wilmer): Add tier verification?

func NewTimeoutSatisfier

func NewTimeoutSatisfier(service string, now func() time.Time) Satisfier

NewTimeoutSatisfier checks if an LSAT is expired or not. The Satisfier takes a service name to set as the condition prefix and currentTimestamp to compare against the expiration(s) in the caveats. The expiration time is retrieved from the caveat values themselves. The satisfier will also make sure that each subsequent caveat of the same condition only has increasingly strict expirations.

type ServerInterceptor

type ServerInterceptor struct{}

ServerInterceptor is a gRPC server interceptor that extracts the token ID from the request context if an LSAT is present.

func (*ServerInterceptor) StreamInterceptor

func (i *ServerInterceptor) StreamInterceptor(srv interface{},
	ss grpc.ServerStream, _ *grpc.StreamServerInfo,
	handler grpc.StreamHandler) error

StreamInterceptor is an stream gRPC server interceptor that inspects incoming streams for authentication tokens. If an LSAT authentication token is found in the initial stream establishment request, its token ID is extracted and treated as client ID. The extracted ID is then attached to the request context in a format that is easy to extract by request handlers.

func (*ServerInterceptor) UnaryInterceptor

func (i *ServerInterceptor) UnaryInterceptor(ctx context.Context,
	req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (
	resp interface{}, err error)

UnaryInterceptor is an unary gRPC server interceptor that inspects incoming calls for authentication tokens. If an LSAT authentication token is found in the request, its token ID is extracted and treated as client ID. The extracted ID is then attached to the request context in a format that is easy to extract by request handlers.

type Service

type Service struct {
	// Name is the name of the LSAT-enabled service.
	Name string

	// Tier is the tier of the LSAT-enabled service.
	Tier ServiceTier

	// Price of service LSAT in satoshis.
	Price int64
}

Service contains the details of an LSAT-enabled service.

type ServiceTier

type ServiceTier uint8

ServiceTier represents the different possible tiers of an LSAT-enabled service.

const (
	// BaseTier is the base tier of an LSAT-enabled service. This tier
	// should be used for any new LSATs that are not part of a service tier
	// upgrade.
	BaseTier ServiceTier = iota
)

type Store

type Store interface {
	// CurrentToken returns the token that is currently contained in the
	// store or an error if there is none.
	CurrentToken() (*Token, error)

	// AllTokens returns all tokens that the store has knowledge of, even
	// if they might be expired. The tokens are mapped by their identifying
	// attribute like file name or storage key.
	AllTokens() (map[string]*Token, error)

	// StoreToken saves a token to the store. Old tokens should be kept for
	// accounting purposes but marked as invalid somehow.
	StoreToken(*Token) error

	// RemovePendingToken removes a pending token from the store or returns
	// ErrNoToken if there is no pending token.
	RemovePendingToken() error
}

Store is an interface that allows users to store and retrieve an LSAT token.

type Token

type Token struct {
	// PaymentHash is the hash of the LSAT invoice that needs to be paid.
	// Knowing the preimage to this hash is seen as proof of payment by the
	// authentication server.
	PaymentHash lntypes.Hash

	// Preimage is the proof of payment indicating that the token has been
	// paid for if set. If the preimage is empty, the payment might still
	// be in transit.
	Preimage lntypes.Preimage

	// AmountPaid is the total amount in msat that the user paid to get the
	// token. This does not include routing fees.
	AmountPaid lnwire.MilliSatoshi

	// RoutingFeePaid is the total amount in msat that the user paid in
	// routing fee to get the token.
	RoutingFeePaid lnwire.MilliSatoshi

	// TimeCreated is the moment when this token was created.
	TimeCreated time.Time
	// contains filtered or unexported fields
}

Token is the main type to store an LSAT token in.

func (*Token) BaseMacaroon

func (t *Token) BaseMacaroon() *macaroon.Macaroon

BaseMacaroon returns the base macaroon as received from the authentication server.

func (*Token) IsValid

func (t *Token) IsValid() bool

IsValid returns true if the timestamp contained in the base macaroon is not yet expired.

func (*Token) PaidMacaroon

func (t *Token) PaidMacaroon() (*macaroon.Macaroon, error)

PaidMacaroon returns the base macaroon with the proof of payment (preimage) added as a first-party-caveat.

type TokenID

type TokenID [TokenIDSize]byte

TokenID is the type that stores the token identifier of an LSAT token.

func MakeIDFromString

func MakeIDFromString(newID string) (TokenID, error)

MakeIDFromString parses the hex encoded string and parses it into a token ID.

func (*TokenID) String

func (t *TokenID) String() string

String returns the hex encoded representation of the token ID as a string.

Jump to

Keyboard shortcuts

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