security

package
v0.0.0-...-efa0d0c Latest Latest
Warning

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

Go to latest
Published: Aug 1, 2016 License: BSD-3-Clause Imports: 28 Imported by: 0

Documentation

Overview

Package security defines types and utilities associated with security.

Concept: https://vanadium.github.io/concepts/security.html
Tutorial: (forthcoming)

The primitives and APIs defined in this package enable bi-directional, end-to-end authentication between communicating parties; authorization based on that authentication; and secrecy and integrity of all communication.

Overview

The Vanadium security model is centered around the concepts of principals and blessings.

A principal in the Vanadium framework is a public and private key pair. Every RPC is executed on behalf of a principal.

A blessing is a binding of a human-readable name to a principal, valid under some caveats, given by another principal. A principal can have multiple blessings bound to it. For instance, a television principal may have a blessing from the manufacturer (e.g., popularcorp:products:tv) as well as from the owner (e.g., alice:devices:hometv). Principals are authorized for operations based on the blessings bound to them.

A principal can "bless" another principal by binding an extension of one of its own blessings to the other principal. This enables delegation of authority. For example, a principal with the blessing "johndoe" can delegate to his phone by blessing the phone as "johndoe:phone", which in-turn can delegate to the headset by blessing it as "johndoe:phone:headset".

Caveats can be added to a blessing in order to restrict the contexts in which it can be used. Amongst other things, caveats can restrict the duration of use and the set of peers that can be communicated with using a blessing.

Navigating the interfaces

Godoc renders all interfaces in this package in alphabetical order. However, we recommend the following order in order to introduce yourself to the API:

  • Principal
  • Blessings
  • BlessingStore
  • BlessingRoots
  • NewCaveat
  • ThirdPartyCaveat
  • NewPublicKeyCaveat

Examples

A principal can decide to name itself anything it wants:

// (in process A)
var p1 Principal
alice, _ := p1.BlessSelf("alice")

This "alice" blessing can be presented to to another principal (typically a remote process), but that other principal will not recognize this "self-proclaimed" authority:

// (in process B)
var p2 Principal
ctx, call := GetContextAndCall() // current context and security state
names, rejected := RemoteBlessingNames(ctx, call)
fmt.Printf("%v %v", names, rejected) // Will print [] ["alice": "..."]

However, p2 can decide to trust the roots of the "alice" blessing and then it will be able to recognize her delegates as well:

// (in process B)
AddToRoots(p2, call.RemoteBlessings())
names, rejected := RemoteBlessingNames(ctx, call)
fmt.Printf("%v %v", names, rejected) // Will print ["alice"] []

Furthermore, p2 can seek a blessing from "alice":

// (in process A)
call := GetCall() // Call under which p2 is seeking a blessing from alice, call.LocalPrincipal = p1
key2 := call.RemoteBlessings().PublicKey()
onlyFor10Minutes := NewExpiryCaveat(time.Now().Add(10*time.Minute))
aliceFriend, _ := p1.Bless(key2, alice, "friend", onlyFor10Minutes)
SendBlessingToProcessB(aliceFriend)

p2 can then add this blessing to its store such that this blessing will be presented to "alice" (and her delegates) anytime p2 communicates with it in the future:

// (in process B)
p2.BlessingStore().Set(aliceFriend, "alice")

p2 can also choose to present multiple blessings to some servers:

// (in process B)
charlieFriend := ReceiveBlessingFromSomeWhere()
union, _ := UnionOfBlessings(aliceFriend, charlieFriend)
p2.BlessingStore().Set(union, "alice:mom")

Thus, when communicating with a "server" that presents the blessing "alice:mom", p2 will declare that he is both "alice's friend" and "charlie's friend" and the server may authorize actions based on this fact.

p2 may also choose that it wants to present these two blessings when acting as a "server", (i.e., when it does not know who the peer is):

// (in process B)
default, _ := UnionOfBlessings(aliceFriend, charlieFriend)
p2.BlessingStore().SetDefault(default)

Index

Constants

View Source
const AllPrincipals = BlessingPattern("...") // Glob pattern that matches all blessings.

TODO(ataly, ashankar): The semantics of AllPrincipals breaks monotonicity in AccessLists with NotIn clauses. For instance, the AccessList "In: {AllPrincipals}, NotIn: {"foo"} matches the principal that presents no recognizable blessings ([]) however does not match the principal that presents "foo" as the only recognizable blessings (["foo"]) We need to sort this out.

View Source
const ChainSeparator = ":" // ChainSeparator joins blessing names to form a blessing chain name.
View Source
const NoExtension = BlessingPattern("$")

NoExtension is an optional terminator for a blessing pattern indicating that the pattern cannot match any extensions of the blessing from that point onwards.

View Source
const SHA1Hash = Hash("SHA1") // SHA1 cryptographic hash function defined in RFC3174.
View Source
const SHA256Hash = Hash("SHA256") // SHA256 cryptographic hash function defined  in FIPS 180-4.
View Source
const SHA384Hash = Hash("SHA384") // SHA384 cryptographic hash function defined in FIPS 180-2.
View Source
const SHA512Hash = Hash("SHA512") // SHA512 cryptographic hash function defined in FIPS 180-2.
View Source
const SignatureForBlessingCertificates = "B1" // Signature.Purpose used by a Principal when signing Certificates for creating blessings.
View Source
const SignatureForDischarge = "D1" // Signature.Purpose used by a Principal when signing discharges for public-key based third-party caveats.
View Source
const SignatureForMessageSigning = "S1" // Signature.Purpose used by a Principal to sign arbitrary messages.

Variables

View Source
var (
	ErrCaveatNotRegistered           = verror.Register("v.io/v23/security.CaveatNotRegistered", verror.NoRetry, "{1:}{2:} no validation function registered for caveat id {3}")
	ErrCaveatParamAny                = verror.Register("v.io/v23/security.CaveatParamAny", verror.NoRetry, "{1:}{2:} caveat {3} uses illegal param type any")
	ErrCaveatParamTypeMismatch       = verror.Register("v.io/v23/security.CaveatParamTypeMismatch", verror.NoRetry, "{1:}{2:} bad param type: caveat {3} got {4}, want {5}")
	ErrCaveatParamCoding             = verror.Register("v.io/v23/security.CaveatParamCoding", verror.NoRetry, "{1:}{2:} unable to encode/decode caveat param(type={4}) for caveat {3}: {5}")
	ErrCaveatValidation              = verror.Register("v.io/v23/security.CaveatValidation", verror.NoRetry, "{1:}{2:} caveat validation failed: {3}")
	ErrConstCaveatValidation         = verror.Register("v.io/v23/security.ConstCaveatValidation", verror.NoRetry, "{1:}{2:} false const caveat always fails validation")
	ErrExpiryCaveatValidation        = verror.Register("v.io/v23/security.ExpiryCaveatValidation", verror.NoRetry, "{1:}{2:} now({3}) is after expiry({4})")
	ErrMethodCaveatValidation        = verror.Register("v.io/v23/security.MethodCaveatValidation", verror.NoRetry, "{1:}{2:} method {3} not in list {4}")
	ErrPeerBlessingsCaveatValidation = verror.Register("v.io/v23/security.PeerBlessingsCaveatValidation", verror.NoRetry, "{1:}{2:} patterns in peer blessings caveat {4} not matched by the peer {3}")
	ErrUnrecognizedRoot              = verror.Register("v.io/v23/security.UnrecognizedRoot", verror.NoRetry, "{1:}{2:} unrecognized public key {3} in root certificate{:4}")
	ErrAuthorizationFailed           = verror.Register("v.io/v23/security.AuthorizationFailed", verror.NoRetry, "{1:}{2:} principal with blessings {3} (rejected {4}) is not authorized by principal with blessings {5}")
	ErrInvalidSigningBlessingCaveat  = verror.Register("v.io/v23/security.InvalidSigningBlessingCaveat", verror.NoRetry, "{1:}{2:} blessing has caveat with UUID {3} which makes it unsuitable for signing -- please use blessings with just Expiry caveats")
	ErrPublicKeyNotAllowed           = verror.Register("v.io/v23/security.PublicKeyNotAllowed", verror.NoRetry, "{1:}{2:} peer has public key {3}, not the authorized public key {4}")
	ErrEndpointAuthorizationFailed   = verror.Register("v.io/v23/security.EndpointAuthorizationFailed", verror.NoRetry, "{1:}{2:} blessings in endpoint {3} not matched by blessings presented: {4} (rejected {5})")
)
View Source
var ConstCaveat = CaveatDescriptor{
	ParamType: vdl.BoolType,
}

ConstCaveat represents a caveat that either always validates or never validates.

View Source
var ExpiryCaveat = CaveatDescriptor{
	Id: uniqueid.Id{
		166,
		76,
		45,
		1,
		25,
		251,
		163,
		52,
		128,
		113,
		254,
		235,
		47,
		48,
		128,
		0,
	},
	ParamType: __VDLType_struct_22,
}

ExpiryCaveat represents a caveat that validates iff the current time is no later the specified time.Time.

View Source
var MethodCaveat = CaveatDescriptor{
	Id: uniqueid.Id{
		84,
		166,
		118,
		57,
		129,
		55,
		24,
		126,
		205,
		178,
		109,
		45,
		105,
		186,
		0,
		3,
	},
	ParamType: __VDLType_list_23,
}

MethodCaveat represents a caveat that validates iff the method being invoked is included in this list. An empty list implies that the caveat is invalid.

View Source
var PeerBlessingsCaveat = CaveatDescriptor{
	Id: uniqueid.Id{
		5,
		119,
		248,
		86,
		76,
		142,
		95,
		254,
		255,
		142,
		43,
		31,
		77,
		109,
		128,
		0,
	},
	ParamType: __VDLType_list_13,
}

PeerBlessingsCaveat represents a caveat that validates iff the peer being communicated with (local end of the call) has a blessing name matching at least one of the patterns in the list. An empty list implies that the caveat is invalid.

View Source
var PublicKeyThirdPartyCaveat = CaveatDescriptor{
	Id: uniqueid.Id{
		121,
		114,
		206,
		23,
		74,
		123,
		169,
		63,
		121,
		84,
		125,
		118,
		156,
		145,
		128,
		0,
	},
	ParamType: __VDLType_struct_6,
}

Functions

func AddToRoots

func AddToRoots(p Principal, blessings Blessings) error

AddToRoots marks the root principals of all blessing chains represented by 'blessings' as an authority on blessing chains beginning at that root name in p.BlessingRoots().

For example, if blessings represents the blessing chains ["alice:friend:spouse", "charlie:family:daughter"] then AddToRoots(blessing) will mark the root public key of the chain "alice:friend:bob" as the authority on all blessings that match the pattern "alice", and root public key of the chain "charlie:family:daughter" as an authority on all blessings that match the pattern "charlie".

This is a convenience function over extracting the names and public keys of the roots of blessings and invoking p.Roots().Add(...).

func BlessingNames

func BlessingNames(principal Principal, blessings Blessings) []string

BlessingNames returns the set of human-readable blessing names encapsulated in blessings.

The returned names are guaranteed to be rooted in principal.Roots, though caveats may not be validated.

The blessings must be bound to principal. There is intentionally no API to obtain blessing names bound to other principals by ignoring caveats. This is to prevent accidental authorization based on potentially invalid names (since caveats are not validated).

func DefaultBlessingNames

func DefaultBlessingNames(p Principal) (names []string)

DefaultBlessingNames returns the blessing names of the Default Blessings of the provided Principal.

func JoinPatternName

func JoinPatternName(pattern BlessingPattern, name string) string

JoinPatternName embeds the specified pattern into a name.

func LocalBlessingNames

func LocalBlessingNames(ctx *context.T, call Call) []string

LocalBlessingNames returns the set of human-readable blessing names encapsulated in the blessings object presented by the local end of the call.

This is just a convenience function over:

BlessingNames(call.LocalPrincipal(), call.LocalBlessings())

func NewErrAuthorizationFailed

func NewErrAuthorizationFailed(ctx *context.T, remote []string, remoteErr []RejectedBlessing, local []string) error

NewErrAuthorizationFailed returns an error with the ErrAuthorizationFailed ID.

func NewErrCaveatNotRegistered

func NewErrCaveatNotRegistered(ctx *context.T, id uniqueid.Id) error

NewErrCaveatNotRegistered returns an error with the ErrCaveatNotRegistered ID.

func NewErrCaveatParamAny

func NewErrCaveatParamAny(ctx *context.T, id uniqueid.Id) error

NewErrCaveatParamAny returns an error with the ErrCaveatParamAny ID.

func NewErrCaveatParamCoding

func NewErrCaveatParamCoding(ctx *context.T, id uniqueid.Id, typ *vdl.Type, err error) error

NewErrCaveatParamCoding returns an error with the ErrCaveatParamCoding ID.

func NewErrCaveatParamTypeMismatch

func NewErrCaveatParamTypeMismatch(ctx *context.T, id uniqueid.Id, got *vdl.Type, want *vdl.Type) error

NewErrCaveatParamTypeMismatch returns an error with the ErrCaveatParamTypeMismatch ID.

func NewErrCaveatValidation

func NewErrCaveatValidation(ctx *context.T, err error) error

NewErrCaveatValidation returns an error with the ErrCaveatValidation ID.

func NewErrConstCaveatValidation

func NewErrConstCaveatValidation(ctx *context.T) error

NewErrConstCaveatValidation returns an error with the ErrConstCaveatValidation ID.

func NewErrEndpointAuthorizationFailed

func NewErrEndpointAuthorizationFailed(ctx *context.T, endpoint string, remote []string, rejected []RejectedBlessing) error

NewErrEndpointAuthorizationFailed returns an error with the ErrEndpointAuthorizationFailed ID.

func NewErrExpiryCaveatValidation

func NewErrExpiryCaveatValidation(ctx *context.T, currentTime time.Time, expiryTime time.Time) error

NewErrExpiryCaveatValidation returns an error with the ErrExpiryCaveatValidation ID.

func NewErrInvalidSigningBlessingCaveat

func NewErrInvalidSigningBlessingCaveat(ctx *context.T, id uniqueid.Id) error

NewErrInvalidSigningBlessingCaveat returns an error with the ErrInvalidSigningBlessingCaveat ID.

func NewErrMethodCaveatValidation

func NewErrMethodCaveatValidation(ctx *context.T, invokedMethod string, permittedMethods []string) error

NewErrMethodCaveatValidation returns an error with the ErrMethodCaveatValidation ID.

func NewErrPeerBlessingsCaveatValidation

func NewErrPeerBlessingsCaveatValidation(ctx *context.T, peerBlessings []string, permittedPatterns []BlessingPattern) error

NewErrPeerBlessingsCaveatValidation returns an error with the ErrPeerBlessingsCaveatValidation ID.

func NewErrPublicKeyNotAllowed

func NewErrPublicKeyNotAllowed(ctx *context.T, got string, want string) error

NewErrPublicKeyNotAllowed returns an error with the ErrPublicKeyNotAllowed ID.

func NewErrUnrecognizedRoot

func NewErrUnrecognizedRoot(ctx *context.T, rootKey string, details error) error

NewErrUnrecognizedRoot returns an error with the ErrUnrecognizedRoot ID.

func RegisterCaveatValidator

func RegisterCaveatValidator(c CaveatDescriptor, validator interface{})

RegisterCaveatValidator associates a CaveatDescriptor with the implementation of the validation function.

The validation function must act as if the caveat was obtained from the remote end of the call. In particular, if the caveat is a third-party caveat then 'call.RemoteDischarges()' must be used to validate it.

This function must be called at most once per c.ID, and will panic on duplicate registrations.

func VDLReadWireDischarge

func VDLReadWireDischarge(dec vdl.Decoder, x *WireDischarge) error

func WireBlessingsFromNative

func WireBlessingsFromNative(wire *WireBlessings, native Blessings) error

func WireBlessingsToNative

func WireBlessingsToNative(wire WireBlessings, native *Blessings) error

func WireDischargeFromNative

func WireDischargeFromNative(wire *WireDischarge, native Discharge) error

func WireDischargeToNative

func WireDischargeToNative(wire WireDischarge, native *Discharge) error

Types

type Authorizer

type Authorizer interface {
	Authorize(ctx *context.T, call Call) error
}

Authorizer is the interface for performing authorization checks.

func AllowEveryone

func AllowEveryone() Authorizer

AllowEveryone returns an Authorizer which implements a policy of always allowing access - irrespective of any parameters of the call or the blessings of the caller.

func DefaultAuthorizer

func DefaultAuthorizer() Authorizer

DefaultAuthorizer returns an Authorizer that implements a "reasonably secure" authorization policy that can be used whenever in doubt.

It has the conservative policy that requires one end of the RPC to have a blessing that is extended from the blessing presented by the other end.

func EndpointAuthorizer

func EndpointAuthorizer() Authorizer

EndpointAuthorizer authorizes principals iff they present blessings that match those specified in call.RemoteEndpoint().

func PublicKeyAuthorizer

func PublicKeyAuthorizer(key PublicKey) Authorizer

PublicKeyAuthorizer only authorizes principals with a specific public key.

Normally, authorizations in Vanadium should be based on blessing names and not public keys, since the former are resilient to key rotations and process replication. However, in rare circumstances it may be possible that blessing names cannot be used (for example, if the local end does not recognize the remote end's blessing root), and the PublicKey might be usable instead.

type BlessingPattern

type BlessingPattern string

BlessingPattern is a pattern that is matched by specific blessings.

A pattern can either be a blessing (slash-separated human-readable string) or a blessing ending in "/$". A pattern ending in "/$" is matched exactly by the blessing specified by the pattern string with the "/$" suffix stripped out. For example, the pattern "a/b/c/$" is matched by exactly by the blessing "a/b/c".

A pattern not ending in "/$" is more permissive, and is also matched by blessings that are extensions of the pattern (including the pattern itself). For example, the pattern "a/b/c" is matched by the blessings "a/b/c", "a/b/c/x", "a/b/c/x/y", etc.

TODO(ataly, ashankar): Define a formal BNF grammar for blessings and blessing patterns.

func DefaultBlessingPatterns

func DefaultBlessingPatterns(p Principal) (patterns []BlessingPattern)

DefaultBlessingPatterns returns the BlessingPatterns of the Default Blessings of the provided Principal.

func SplitPatternName

func SplitPatternName(origName string) (BlessingPattern, string)

SplitPatternName takes an object name and parses out the server blessing pattern. It returns the pattern specified, and the name with the pattern removed.

func (BlessingPattern) IsValid

func (p BlessingPattern) IsValid() bool

IsValid returns true iff the BlessingPattern is well formed, as per the rules described in documentation for the BlessingPattern type.

func (BlessingPattern) MakeNonExtendable

func (p BlessingPattern) MakeNonExtendable() BlessingPattern

MakeNonExtendable returns a pattern that is matched exactly by the blessing specified by the given pattern string.

For example:

onlyAlice := BlessingPattern("google:alice").MakeNonExtendable()
onlyAlice.MatchedBy("google:alice")  // Returns true
onlyAlice.MatchedBy("google")  // Returns false
onlyAlice.MatchedBy("google:alice:bob")  // Returns false

func (BlessingPattern) MatchedBy

func (p BlessingPattern) MatchedBy(blessings ...string) bool

MatchedBy returns true iff one of the presented blessings matches p as per the rules described in documentation for the BlessingPattern type.

func (BlessingPattern) PrefixPatterns

func (p BlessingPattern) PrefixPatterns() []BlessingPattern

PrefixPatterns returns a set of BlessingPatterns that are matched by blessings that either directly match the provided pattern or can be extended to match the provided pattern.

For example: BlessingPattern("google:alice:friend").PrefixPatterns() returns

["google:$", "google:alice:$", "google:alice:friend"]

BlessingPattern("google:alice:friend:$").PrefixPatterns() returns

["google:$", "google:alice:$", "google:alice:friend:$"]

The returned set of BlessingPatterns are ordered by the number of ":"-separated components in the pattern.

func (BlessingPattern) VDLIsZero

func (x BlessingPattern) VDLIsZero() bool

func (*BlessingPattern) VDLRead

func (x *BlessingPattern) VDLRead(dec vdl.Decoder) error

func (BlessingPattern) VDLWrite

func (x BlessingPattern) VDLWrite(enc vdl.Encoder) error

type BlessingRoots

type BlessingRoots interface {
	// Add marks 'root' (a DER-encoded public key) as an authoritative key
	// for blessings that match 'pattern'.
	//
	// Multiple keys can be added for the same pattern, in which
	// case all those keys are considered authoritative for
	// blessings that match the pattern.
	Add(root []byte, pattern BlessingPattern) error

	// Recognized returns nil iff the provided (DER-encoded) root public
	// key as an authority on a pattern that is matched by blessing.
	Recognized(root []byte, blessing string) error

	// Dump returns the set of recognized roots as a map from
	// blessing patterns to the set of authoritative keys for that
	// pattern.
	Dump() map[BlessingPattern][]PublicKey

	// DebugString returns a human-readable string description of the roots.
	// This description is detailed and lists out all the roots. Use
	// fmt.Sprintf("%v", ...) for a more succinct description.
	DebugString() string
}

BlessingRoots hosts the set of authoritative public keys for roots of blessings.

See also: https://vanadium.github.io/glossary.html#blessing-root

type BlessingStore

type BlessingStore interface {
	// Set marks the set of blessings to be shared with peers.
	//
	// Set(b, pattern) marks the intention to reveal b to peers
	// who present blessings of their own matching pattern.
	//
	// If multiple calls to Set are made with the same pattern, the
	// last call prevails.
	//
	// Set(Blessings{}, pattern) can be used to remove the blessings
	// previously associated with the pattern (by a prior call to Set).
	//
	// It is an error to call Set with "blessings" whose public key does
	// not match the PublicKey of the principal for which this store hosts
	// blessings.
	//
	// Set returns the Blessings object which was previously associated
	// with the pattern.
	Set(blessings Blessings, forPeers BlessingPattern) (Blessings, error)

	// ForPeer returns the set of blessings that have been previously
	// added to the store with an intent of being shared with peers
	// that have at least one of the provided blessings.
	//
	// If no peerBlessings are provided then blessings marked for all peers
	// (i.e., added with the AllPrincipals pattern) is returned.
	//
	// Returns the zero value if there are no matching blessings in the store.
	ForPeer(peerBlessings ...string) Blessings

	// SetDefault sets up the Blessings made available on a subsequent call
	// to Default.
	//
	// It is an error to call SetDefault with a blessings whose public key
	// does not match the PublicKey of the principal for which this store
	// hosts blessings.
	SetDefault(blessings Blessings) error

	// Default returns the blessings to be shared with peers for which no
	// other information is available in order to select blessings from the
	// store.
	//
	// For example, Default can be used by servers to identify themselves
	// to clients before the client has identified itself.
	//
	// Default returns the blessings provided to the last call to
	// SetDefault and a channel which will be closed when the
	// Default changes (i.e., SetDefault is called again).
	//
	// Returns the zero value if there is no usable blessings.
	Default() (Blessings, <-chan struct{})

	// PublicKey returns the public key of the Principal for which
	// this store hosts blessings.
	PublicKey() PublicKey

	// PeerBlessings returns all the blessings that the BlessingStore
	// currently holds for various peers.
	PeerBlessings() map[BlessingPattern]Blessings

	// CacheDischarge inserts the discharge for the provided impetus and caveat into the cache.
	CacheDischarge(discharge Discharge, caveat Caveat, impetus DischargeImpetus)

	// ClearDischarges clears the input discharges from the BlessingStore's
	// discharge cache.
	ClearDischarges(discharges ...Discharge)

	// Discharge takes a caveat and DischargeImpetus and returns a cached discharge
	// and the time at which it was cached.  Zero values are returned if no
	// corresponding cached discharge can be found.  Note that in certain upgrade
	// situations a zero cacheTime may be returned if the real cache time cannot
	// be determined.
	Discharge(caveat Caveat, impetus DischargeImpetus) (discharge Discharge, cacheTime time.Time)

	// DebugString return a human-readable string description of the store.
	// This description is detailed and lists out the contents of the store.
	// Use fmt.Sprintf("%v", ...) for a more succinct description.
	DebugString() string
}

BlessingStore is the interface for storing blessings bound to a principal and managing the subset of blessings to be presented to particular peers. BlessingStore implementations may also cache Discharges for third-party caveats on blessings, allowing unexpired Discharges to be reused.

type Blessings

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

Blessings encapsulates all cryptographic operations required to prove that a set of (human-readable) blessing names are bound to a principal in a specific call.

Blessings objects are meant to be presented to other principals to authenticate and authorize actions. The functions 'LocalBlessingNames', 'SigningBlessingNames' and 'RemoteBlessingNames' defined in this package can be used to uncover the blessing names encapsulated in these objects.

Blessings objects are immutable and multiple goroutines may invoke methods on them simultaneously.

See also: https://vanadium.github.io/glossary.html#blessing

func NamelessBlessing

func NamelessBlessing(pk PublicKey) (Blessings, error)

NamelessBlessing returns a blessings object that has no names, only the provided public key.

func RootBlessings

func RootBlessings(b Blessings) []Blessings

RootBlessings returns the blessings of the roots of b. In other words, RootBlessings returns the blessings of the identity providers encapsulated in b.

In particular:

AddToRoots(p, b)

is equivalent to:

for _, root := range RootBlessings(b) {
    AddToRoots(p, root)
}

Why would you use the latter? Only to share roots with another process, without revealing your complete blessings and using fewer bytes.

func SigningBlessings

func SigningBlessings(blessings Blessings) Blessings

SigningBlessings returns a blessings object that encapsulates the subset of names of the provided blessings object that can be used to sign data at rest.

The names of the returned blessings object can be obtained using the 'SigningNames' function.

func UnionOfBlessings

func UnionOfBlessings(blessings ...Blessings) (Blessings, error)

UnionOfBlessings returns a Blessings object that carries the union of the provided blessings.

All provided Blessings must have the same PublicKey.

UnionOfBlessings with no arguments returns (nil, nil).

func (Blessings) CouldHaveNames

func (b Blessings) CouldHaveNames(names []string) bool

CouldHaveNames returns true iff the blessings 'b' encapsulates the provided set of blessing names.

This check ignores all caveats on the blessing name and the recognition status of its blessing root.

func (Blessings) Equivalent

func (b Blessings) Equivalent(blessings Blessings) bool

Equivalent returns true if 'b' and 'blessings' can be used interchangeably, i.e., 'b' will be authorized wherever 'blessings' is and vice-versa.

func (Blessings) Expiry

func (b Blessings) Expiry() time.Time

Expiry returns the time at which b will no longer be valid, or the zero value of time.Time if the blessing does not expire.

func (Blessings) IsZero

func (b Blessings) IsZero() bool

IsZero returns true if b represents the zero value of blessings (an empty set).

func (Blessings) PublicKey

func (b Blessings) PublicKey() PublicKey

PublicKey returns the public key of the principal to which blessings obtained from this object are bound.

Can return nil if b is the zero value.

func (Blessings) String

func (b Blessings) String() string

func (Blessings) ThirdPartyCaveats

func (b Blessings) ThirdPartyCaveats() []Caveat

ThirdPartyCaveats returns the set of third-party restrictions on the scope of the blessings (i.e., the subset of Caveats for which ThirdPartyDetails will be non-nil).

func (Blessings) UniqueID

func (b Blessings) UniqueID() []byte

UniqueID returns an identifier of the set of blessings. Two blessings objects with the same UniqueID are interchangeable for any authorization decisions.

The converse is not guaranteed at this time. Two interchangeable blessings objects may in theory have different UniqueIDs.

type Call

type Call interface {
	// Timestamp returns the time at which the authorization state is to be checked.
	Timestamp() time.Time
	// Method returns the method being invoked.
	Method() string
	// MethodTags returns the tags attached to the method, typically through the
	// interface specification in VDL.
	MethodTags() []*vdl.Value
	// Suffix returns the object name suffix for the request.
	Suffix() string
	// LocalDischarges specify discharges for third-party caveats presented by
	// the local end of the call. It maps a third-party caveat identifier to the
	// corresponding discharge.
	LocalDischarges() map[string]Discharge
	// RemoteDischarges specify discharges for third-party caveats presented by
	// the remote end of the call. It maps a third-party caveat identifier to the
	// corresponding discharge.
	RemoteDischarges() map[string]Discharge
	// LocalPrincipal returns the principal used to authenticate to the remote end.
	LocalPrincipal() Principal
	// LocalBlessings returns the blessings (bound to the local end)
	// provided to the remote end for authentication.
	LocalBlessings() Blessings
	// RemoteBlessings returns the blessings (bound to the remote end)
	// provided to the local end during authentication.
	RemoteBlessings() Blessings
	// LocalEndpoint() returns the Endpoint of the principal at the local
	// end of communication.
	LocalEndpoint() naming.Endpoint
	// RemoteEndpoint() returns the Endpoint of the principal at the remote end
	// of communication.
	RemoteEndpoint() naming.Endpoint
}

Call defines the state available for authorizing a principal.

func NewCall

func NewCall(params *CallParams) Call

NewCall creates a Call.

type CallParams

type CallParams struct {
	Timestamp  time.Time    // Time at which the authorization is to be checked.
	Method     string       // Method being invoked.
	MethodTags []*vdl.Value // Method tags, typically specified in VDL.
	Suffix     string       // Object suffix on which the method is being invoked.

	LocalPrincipal Principal       // Principal at the local end of a request.
	LocalBlessings Blessings       // Blessings presented to the remote end.
	LocalEndpoint  naming.Endpoint // Endpoint of local end of communication.

	RemoteBlessings  Blessings            // Blessings presented by the remote end.
	RemoteDischarges map[string]Discharge // Map of third-party caveat identifiers to corresponding discharges shared by the remote end.
	LocalDischarges  map[string]Discharge // Map of third-party caveat identifiers to corresponding discharges shared by the local end.
	RemoteEndpoint   naming.Endpoint      // Endpoint of the remote end of communication (as seen by the local end)
}

CallParams is used to create Call objects using the NewCall function.

func (*CallParams) Copy

func (p *CallParams) Copy(c Call)

Copy fills in p with a copy of the values in c.

type Caveat

type Caveat struct {
	Id       uniqueid.Id // The identifier of the caveat validation function.
	ParamVom []byte      // VOM-encoded bytes of the parameters to be provided to the validation function.
}

Caveat is a condition on the validity of a blessing/discharge.

These conditions are provided when asking a principal to create a blessing/discharge and are verified when extracting blessings (Blessings.ForName in the Go API).

Given a Hash, the message digest of a caveat is: Hash(Hash(Id), Hash(ParamVom))

func NewCaveat

func NewCaveat(c CaveatDescriptor, param interface{}) (Caveat, error)

NewCaveat returns a Caveat that requires validation by the validation function correponding to c and uses the provided parameters.

func NewExpiryCaveat

func NewExpiryCaveat(t time.Time) (Caveat, error)

NewExpiryCaveat returns a Caveat that validates iff the current time is before t.

func NewMethodCaveat

func NewMethodCaveat(method string, additionalMethods ...string) (Caveat, error)

NewMethodCaveat returns a Caveat that validates iff the method being invoked by the peer is listed in an argument to this function.

func NewPublicKeyCaveat

func NewPublicKeyCaveat(discharger PublicKey, location string, requirements ThirdPartyRequirements, caveat Caveat, additionalCaveats ...Caveat) (Caveat, error)

NewPublicKeyCaveat returns a third-party caveat, i.e., the returned Caveat will be valid only when a discharge signed by discharger is issued.

Location specifies the expected address at which the third-party service is found (and which issues discharges).

The discharger will validate all provided caveats (caveat, additionalCaveats) before issuing a discharge.

func UnconstrainedUse

func UnconstrainedUse() Caveat

UnconstrainedUse returns a Caveat implementation that never fails to validate. This is useful only for providing unconstrained blessings/discharges to another principal.

func (Caveat) String

func (c Caveat) String() string

func (*Caveat) ThirdPartyDetails

func (c *Caveat) ThirdPartyDetails() ThirdPartyCaveat

ThirdPartyDetails returns nil if c is not a third party caveat, or details about the third party otherwise.

func (Caveat) VDLIsZero

func (x Caveat) VDLIsZero() bool

func (*Caveat) VDLRead

func (x *Caveat) VDLRead(dec vdl.Decoder) error

func (Caveat) VDLWrite

func (x Caveat) VDLWrite(enc vdl.Encoder) error

func (*Caveat) Validate

func (c *Caveat) Validate(ctx *context.T, call Call) error

Validate tests if 'c' is satisfied under 'call', returning nil if it is or an error otherwise.

It assumes that 'c' was found on a credential obtained from the remote end of the call. In particular, if 'c' is a third-party caveat then it uses the call.RemoteDischarges() to validate it.

type CaveatDescriptor

type CaveatDescriptor struct {
	Id        uniqueid.Id // The identifier of the caveat validation function.
	ParamType *vdl.Type   // The type of the parameter expected by the validation function.
}

CaveatDescriptor defines an association between a caveat validation function (addressed by globally unique identifier) and the data needed by the validation function.

For a validator to be invoked, a validation function must be registered with the validator description in the language that the function is defined in.

func (CaveatDescriptor) VDLIsZero

func (x CaveatDescriptor) VDLIsZero() bool

func (*CaveatDescriptor) VDLRead

func (x *CaveatDescriptor) VDLRead(dec vdl.Decoder) error

func (CaveatDescriptor) VDLWrite

func (x CaveatDescriptor) VDLWrite(enc vdl.Encoder) error

type Certificate

type Certificate struct {
	Extension string    // Human-readable string extension bound to PublicKey.
	PublicKey []byte    // DER-encoded PKIX public key.
	Caveats   []Caveat  // Caveats on the binding of Name to PublicKey.
	Signature Signature // Signature by the blessing principal that binds the extension to the public key.
}

Certificate represents the cryptographic proof of the binding of extensions of a blessing held by one principal to another (represented by a public key) under specific caveats.

For example, if a principal P1 has a blessing "alice", then it can extend it with a Certificate to generate the blessing "alice/friend" for another principal P2.

func (Certificate) VDLIsZero

func (x Certificate) VDLIsZero() bool

func (*Certificate) VDLRead

func (x *Certificate) VDLRead(dec vdl.Decoder) error

func (Certificate) VDLWrite

func (x Certificate) VDLWrite(enc vdl.Encoder) error

type Discharge

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

Discharge represents a "proof" required for satisfying a ThirdPartyCaveat.

A discharge may have caveats of its own (including ThirdPartyCaveats) that restrict the context in which the discharge is usable.

Discharge objects are immutable and multiple goroutines may invoke methods on a Discharge simultaneously.

See also: https://vanadium.github.io/glossary.html#discharge

func (Discharge) Equivalent

func (d Discharge) Equivalent(discharge Discharge) bool

Equivalent returns true if 'd' and 'discharge' can be used interchangeably, i.e. any authorizations that are enabled by 'd' will be enabled by 'discharge' and vice versa.

func (Discharge) Expiry

func (d Discharge) Expiry() time.Time

Expiry returns the time at which d will no longer be valid, or the zero value of time.Time if the discharge does not expire.

func (Discharge) ID

func (d Discharge) ID() string

ID returns the identifier for the third-party caveat that d is a discharge for.

func (Discharge) ThirdPartyCaveats

func (d Discharge) ThirdPartyCaveats() []ThirdPartyCaveat

ThirdPartyCaveats returns the set of third-party caveats on the scope of the discharge.

func (Discharge) VDLIsZero

func (d Discharge) VDLIsZero() bool

VDLIsZero implements the vdl.IsZeroer interface, and returns true if d represents an empty discharge.

type DischargeImpetus

type DischargeImpetus struct {
	Server    []BlessingPattern // The client intends to use the discharge to communicate with a server that has a blessing matching one of the patterns in this set.
	Method    string            // Name of the method being invoked by the client.
	Arguments []*vom.RawBytes   // Arguments to the method invocation.
}

DischargeImpetus encapsulates the motivation for a discharge being sought.

These values are reported by a principal that is requesting a Discharge for a third-party caveat on one of its blessings. The third-party issues discharges cannot safely assume that all these values are provided, or that they are provided honestly.

Implementations of services that issue discharges are encouraged to add caveats to the discharge that bind the discharge to the impetus, thereby rendering the discharge unsuable for any other purpose.

func (DischargeImpetus) VDLIsZero

func (x DischargeImpetus) VDLIsZero() bool

func (*DischargeImpetus) VDLRead

func (x *DischargeImpetus) VDLRead(dec vdl.Decoder) error

func (DischargeImpetus) VDLWrite

func (x DischargeImpetus) VDLWrite(enc vdl.Encoder) error

type Hash

type Hash string

Hash identifies a cryptographic hash function approved for use in signature algorithms.

func (Hash) VDLIsZero

func (x Hash) VDLIsZero() bool

func (*Hash) VDLRead

func (x *Hash) VDLRead(dec vdl.Decoder) error

func (Hash) VDLWrite

func (x Hash) VDLWrite(enc vdl.Encoder) error

type Principal

type Principal interface {
	// Bless binds extensions of blessings held by this principal to
	// another principal (represented by its public key).
	//
	// For example, a principal with the blessings "google:alice"
	// and "v23:alice" can bind the blessings "google:alice:friend"
	// and "v23:alice:friend" to another principal using:
	//   Bless(<other principal>, <google:alice, v23:alice>, "friend", ...)
	//
	// To discourage unconstrained delegation of authority, the interface
	// requires at least one caveat to be provided. If unconstrained delegation
	// is desired, the UnconstrainedUse function can be used to produce
	// this argument.
	//
	// with.PublicKey must be the same as the principal's public key.
	Bless(key PublicKey, with Blessings, extension string, caveat Caveat, additionalCaveats ...Caveat) (Blessings, error)

	// BlessSelf creates a blessing with the provided name for this principal.
	BlessSelf(name string, caveats ...Caveat) (Blessings, error)

	// Sign uses the private key of the principal to sign message.
	Sign(message []byte) (Signature, error)

	// MintDischarge generates a discharge for 'tp'.
	//
	// It assumes that it is okay to generate a discharge, i.e., any
	// restrictions encoded within 'tp' are satisfied.
	//
	// The returned discharge will be usable only if the provided caveats
	// are met when using the discharge.
	MintDischarge(forThirdPartyCaveat, caveatOnDischarge Caveat, additionalCaveatsOnDischarge ...Caveat) (Discharge, error)

	// PublicKey returns the public key counterpart of the private key held
	// by the Principal.
	PublicKey() PublicKey

	// BlessingStore provides access to the BlessingStore containing blessings
	// that have been granted to this principal.
	BlessingStore() BlessingStore

	// Roots returns the set of recognized authorities (identified by their
	// public keys) on blessings that match specific patterns
	Roots() BlessingRoots
}

Principal represents an entity capable of making or receiving RPCs. Principals have a unique (public, private) key pair, have (zero or more) blessings bound to them and can bless other principals.

Multiple goroutines may invoke methods on a Principal simultaneously.

See also: https://vanadium.github.io/glossary.html#principal

func CreatePrincipal

func CreatePrincipal(signer Signer, store BlessingStore, roots BlessingRoots) (Principal, error)

CreatePrincipal returns a Principal that uses 'signer' for all private key operations, 'store' for storing blessings bound to the Principal and 'roots' for the set of authoritative public keys on blessings recognized by this Principal.

If provided 'roots' is nil then the Principal does not trust any public keys and all subsequent 'AddToRoots' operations fail.

It returns an error if store.PublicKey does not match signer.PublicKey.

NOTE: v.io/x/ref/lib/testutil/security provides utility methods for creating principals for testing purposes.

type PublicKey

type PublicKey interface {
	encoding.BinaryMarshaler
	fmt.Stringer
	// contains filtered or unexported methods
}

PublicKey represents a public key using an unspecified algorithm.

MarshalBinary returns the DER-encoded PKIX representation of the public key, while UnmarshalPublicKey creates a PublicKey object from the marshaled bytes.

String returns a human-readable representation of the public key.

func NewECDSAPublicKey

func NewECDSAPublicKey(key *ecdsa.PublicKey) PublicKey

NewECDSAPublicKey creates a PublicKey object that uses the ECDSA algorithm and the provided ECDSA public key.

func UnmarshalPublicKey

func UnmarshalPublicKey(bytes []byte) (PublicKey, error)

UnmarshalPublicKey returns a PublicKey object from the DER-encoded PKIX represntation of it (typically obtianed via PublicKey.MarshalBinary).

type PublicKeyDischarge

type PublicKeyDischarge struct {
	ThirdPartyCaveatId string    // Id of the third party caveat for which this discharge was issued.
	Caveats            []Caveat  // Caveats on the use of this discharge.
	Signature          Signature // Signature of the content hash of this discharge by the discharger.
}

PublicKeyDischarge represents a discharge for third party caveats that require a signature from a third-party's public key.

The message digest of this structure is computed as follows: hash(hash(ThirdPartyCaveatId), hash(Caveats[0]), hash(Caveats[1]), ...), where hash is a cryptographic hash function with a security strength equivalent to the strength of the public key of the principal issuing the discharge.

func (*PublicKeyDischarge) String

func (d *PublicKeyDischarge) String() string

func (PublicKeyDischarge) VDLIsZero

func (x PublicKeyDischarge) VDLIsZero() bool

func (*PublicKeyDischarge) VDLRead

func (x *PublicKeyDischarge) VDLRead(dec vdl.Decoder) error

func (PublicKeyDischarge) VDLWrite

func (x PublicKeyDischarge) VDLWrite(enc vdl.Encoder) error

type RejectedBlessing

type RejectedBlessing struct {
	Blessing string
	Err      error
}

RejectedBlessing describes why a blessing failed validation.

func RemoteBlessingNames

func RemoteBlessingNames(ctx *context.T, call Call) ([]string, []RejectedBlessing)

RemoteBlessingNames returns the validated set of human-readable blessing names encapsulated in the blessings object presented by the remote end of a call.

The blessing names are guaranteed to:

(1) Satisfy all the caveats associated with them, in the context of the call. (2) Be rooted in call.LocalPrincipal.Roots.

Caveats are considered satisfied for the 'call' if the CaveatValidator implementation can be found in the address space of the caller and Validate returns nil.

RemoteBlessingNames also returns the RejectedBlessings for each blessing name that cannot be validated.

func SigningBlessingNames

func SigningBlessingNames(ctx *context.T, p Principal, blessings Blessings) ([]string, []RejectedBlessing)

SigningBlessingNames returns the validated set of human-readable blessing names encapsulated in the provided signing blessings object, as determined by the provided principal.

This function also returns the RejectedBlessings for each blessing name that cannot be validated. TODO(ataly): While the principal is encapsulated inside context.T, we can't extract it due to an import cycle issue. Therefore at the moment we have the principal separately passed to this function. We should clean this up.

func (RejectedBlessing) String

func (i RejectedBlessing) String() string

func (RejectedBlessing) VDLIsZero

func (x RejectedBlessing) VDLIsZero() bool

func (*RejectedBlessing) VDLRead

func (x *RejectedBlessing) VDLRead(dec vdl.Decoder) error

func (RejectedBlessing) VDLWrite

func (x RejectedBlessing) VDLWrite(enc vdl.Encoder) error

type Signature

type Signature struct {
	// Purpose of the signature. Can be used to prevent type attacks.
	// (See Section 4.2 of http://www-users.cs.york.ac.uk/~jac/PublishedPapers/reviewV1_1997.pdf for example).
	// The actual signature (R, S values for ECDSA keys) is produced by signing: Hash(Hash(message), Hash(Purpose)).
	Purpose []byte
	// Cryptographic hash function applied to the message before computing the signature.
	Hash Hash
	// Pair of integers that make up an ECDSA signature.
	R []byte
	S []byte
}

Signature represents a digital signature.

func (Signature) VDLIsZero

func (x Signature) VDLIsZero() bool

func (*Signature) VDLRead

func (x *Signature) VDLRead(dec vdl.Decoder) error

func (Signature) VDLWrite

func (x Signature) VDLWrite(enc vdl.Encoder) error

func (*Signature) Verify

func (sig *Signature) Verify(key PublicKey, message []byte) bool

Verify returns true iff sig is a valid signature for a message.

type Signer

type Signer interface {
	// Sign signs an arbitrary length message using the private key associated
	// with this Signer.
	//
	// The provided purpose is used to avoid "type attacks", wherein an honest
	// entity is cheated into interpreting a field in a message as one with a
	// type other than the intended one.
	Sign(purpose, message []byte) (Signature, error)

	// PublicKey returns the public key corresponding to the Signer's private key.
	PublicKey() PublicKey
}

Signer is the interface for signing arbitrary length messages using private keys.

Multiple goroutines may invoke methods on a Signer simultaneously.

func NewECDSASigner

func NewECDSASigner(key *ecdsa.PublicKey, sign func(data []byte) (r, s *big.Int, err error)) Signer

NewECDSASigner creates a Signer that uses the provided function to sign messages.

func NewInMemoryECDSASigner

func NewInMemoryECDSASigner(key *ecdsa.PrivateKey) Signer

NewInMemoryECDSASigner creates a Signer that uses the provided ECDSA private key to sign messages. This private key is kept in the clear in the memory of the running process.

type ThirdPartyCaveat

type ThirdPartyCaveat interface {
	// ID returns a cryptographically unique identifier for the ThirdPartCaveat.
	ID() string

	// Location returns the Vanadium object name of the discharging third-party.
	Location() string

	// Requirements lists the information that the third-party requires
	// in order to issue a discharge.
	Requirements() ThirdPartyRequirements

	// Dischargeable validates all restrictions encoded within the third-party
	// caveat under the current call and returns nil iff they have been satisfied,
	// and thus ensures that it is okay to generate a discharge for this
	// ThirdPartyCaveat.
	//
	// It assumes that the ThirdPartCaveat was obtained from the remote end of
	// call.
	Dischargeable(ctx *context.T, call Call) error
}

ThirdPartyCaveat is a restriction on the applicability of a blessing that is considered satisfied only when accompanied with a specific "discharge" from the third-party specified in the caveat. (The first two parties are the ones presenting a blessing and the one making authorization decisions based on the blessing presented).

Multiple goroutines may invoke methods on a ThirdPartyCaveat simultaneously.

See also: https://vanadium.github.io/glossary.html#third-party-caveat

type ThirdPartyRequirements

type ThirdPartyRequirements struct {
	ReportServer    bool // The blessings presented by the server of an IPC call.
	ReportMethod    bool // The name of the method being invoked.
	ReportArguments bool // Arguments to the method being invoked.
}

ThirdPartyRequirements specifies the information required by the third-party that will issue discharges for third-party caveats.

These requirements are typically used to construct a DischargeImpetus, which will be sent to the third-party.

func (ThirdPartyRequirements) VDLIsZero

func (x ThirdPartyRequirements) VDLIsZero() bool

func (*ThirdPartyRequirements) VDLRead

func (x *ThirdPartyRequirements) VDLRead(dec vdl.Decoder) error

func (ThirdPartyRequirements) VDLWrite

func (x ThirdPartyRequirements) VDLWrite(enc vdl.Encoder) error

type WireBlessings

type WireBlessings struct {
	// CertificateChains is an array of chains of certificates that bind
	// a blessing to the public key in the last certificate of the chain.
	CertificateChains [][]Certificate
}

WireBlessings encapsulates wire format of a set of blessings and the corresponding cryptographic proof that binds them to a principal (identified by a public key).

This structure is the "wire" format for sending and receiving blessings in RPCs or marshaling to persistent storage. Typically, languages will provide a factory function that converts this wire representation to a more usable object to inspect and manipulate these blessings.

func MarshalBlessings

func MarshalBlessings(b Blessings) WireBlessings

TODO(ashankar): Get rid of this function? It allows users to mess with the integrity of 'b'.

func (WireBlessings) VDLIsZero

func (x WireBlessings) VDLIsZero() bool

func (*WireBlessings) VDLRead

func (x *WireBlessings) VDLRead(dec vdl.Decoder) error

func (WireBlessings) VDLWrite

func (x WireBlessings) VDLWrite(enc vdl.Encoder) error

type WireDischarge

type WireDischarge interface {
	// Index returns the field index.
	Index() int
	// Interface returns the field value as an interface.
	Interface() interface{}
	// Name returns the field name.
	Name() string

	VDLIsZero() bool
	VDLWrite(vdl.Encoder) error
	// contains filtered or unexported methods
}

WireDischarge represents any single field of the WireDischarge union type.

WireDischarge encapsulates the wire format of a third-party caveat Discharge.

type WireDischargePublicKey

type WireDischargePublicKey struct{ Value PublicKeyDischarge } // Discharge for PublicKeyThirdPartyCaveat

WireDischargePublicKey represents field PublicKey of the WireDischarge union type.

func (WireDischargePublicKey) Index

func (x WireDischargePublicKey) Index() int

func (WireDischargePublicKey) Interface

func (x WireDischargePublicKey) Interface() interface{}

func (WireDischargePublicKey) Name

func (x WireDischargePublicKey) Name() string

func (WireDischargePublicKey) VDLIsZero

func (x WireDischargePublicKey) VDLIsZero() bool

func (WireDischargePublicKey) VDLWrite

func (x WireDischargePublicKey) VDLWrite(enc vdl.Encoder) error

Directories

Path Synopsis
Package access defines types and interfaces for dynamic access control.
Package access defines types and interfaces for dynamic access control.
internal
Package internal provides a VDL specification for a service used in the unittest of the access package.
Package internal provides a VDL specification for a service used in the unittest of the access package.

Jump to

Keyboard shortcuts

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