delegation

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Jan 23, 2025 License: Apache-2.0, MIT Imports: 24 Imported by: 0

Documentation

Overview

Package delegation implements the UCAN delegation specification with an immutable Token type as well as methods to convert the Token to and from the envelope-enclosed, signed and DAG-CBOR-encoded form that should most commonly be used for transport and storage.

Index

Examples

Constants

View Source
const Tag = "ucan/dlg@1.0.0-rc.1"

Tag is the string used as a key within the SigPayload that identifies that the TokenPayload is a delegation.

Variables

View Source
var ErrDelegationNotFound = fmt.Errorf("delegation not found")

ErrDelegationNotFound is returned if a delegation token is not found

Functions

This section is empty.

Types

type Bundle

type Bundle struct {
	Cid     cid.Cid
	Decoded *Token
	Sealed  []byte
}

Bundle carries together a decoded delegation with its Cid and raw signed data.

type Loader

type Loader interface {
	// GetDelegation returns the delegation.Token matching the given CID.
	// If not found, ErrDelegationNotFound is returned.
	GetDelegation(cid cid.Cid) (*Token, error)
}

Loader is a delegation token loader.

type Option

type Option func(*Token) error

Option is a type that allows optional fields to be set during the creation of a Token.

func WithEncryptedMetaBytes

func WithEncryptedMetaBytes(key string, val, encryptionKey []byte) Option

WithEncryptedMetaBytes adds a key/value pair in the "meta" field. The []byte value is encrypted with the given key. The ciphertext will be 40 bytes larger than the plaintext due to encryption overhead.

func WithEncryptedMetaString

func WithEncryptedMetaString(key, val string, encryptionKey []byte) Option

WithEncryptedMetaString adds a key/value pair in the "meta" field. The string value is encrypted with the given key. The ciphertext will be 40 bytes larger than the plaintext due to encryption overhead.

func WithExpiration

func WithExpiration(exp time.Time) Option

WithExpiration set's the Token's optional "expiration" field to the value of the provided time.Time.

func WithExpirationIn

func WithExpirationIn(exp time.Duration) Option

WithExpirationIn set's the Token's optional "expiration" field to Now() plus the given duration.

func WithMeta

func WithMeta(key string, val any) Option

WithMeta adds a key/value pair in the "meta" field.

WithMeta can be used multiple times in the same call. Accepted types for the value are: bool, string, int, int32, int64, []byte, and ipld.Node.

func WithNonce

func WithNonce(nonce []byte) Option

WithNonce sets the Token's nonce with the given value. If this option is not used, a random 12-byte nonce is generated for this required field.

func WithNotBefore

func WithNotBefore(nbf time.Time) Option

WithNotBefore set's the Token's optional "notBefore" field to the value of the provided time.Time.

func WithNotBeforeIn

func WithNotBeforeIn(nbf time.Duration) Option

WithNotBeforeIn set's the Token's optional "notBefore" field to the value of the provided time.Time.

type Token

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

Token is an immutable type that holds the fields of a UCAN delegation.

func Decode

func Decode(b []byte, decFn codec.Decoder) (*Token, error)

Decode unmarshals the input data using the format specified by the provided codec.Decoder into a Token.

An error is returned if the conversion fails, or if the resulting Token is invalid.

func DecodeReader

func DecodeReader(r io.Reader, decFn codec.Decoder) (*Token, error)

DecodeReader is the same as Decode, but accept an io.Reader.

func FromDagCbor

func FromDagCbor(data []byte) (*Token, error)

FromDagCbor unmarshals the input data into a Token.

An error is returned if the conversion fails, or if the resulting Token is invalid.

func FromDagCborReader

func FromDagCborReader(r io.Reader) (*Token, error)

FromDagCborReader is the same as FromDagCbor, but accept an io.Reader.

func FromDagJson

func FromDagJson(data []byte) (*Token, error)

FromDagJson unmarshals the input data into a Token.

An error is returned if the conversion fails, or if the resulting Token is invalid.

func FromDagJsonReader

func FromDagJsonReader(r io.Reader) (*Token, error)

FromDagJsonReader is the same as FromDagJson, but accept an io.Reader.

func FromIPLD

func FromIPLD(node datamodel.Node) (*Token, error)

FromIPLD decode the given IPLD representation into a Token.

func FromSealed

func FromSealed(data []byte) (*Token, cid.Cid, error)

FromSealed decodes the provided binary data from the DAG-CBOR format, verifies that the envelope's signature is correct based on the public key taken from the issuer (iss) field and calculates the CID of the incoming data.

Example

The following example demonstrates how to get a delegation.Token from a DAG-CBOR []byte.

package main

import (
	"encoding/base64"
	"encoding/hex"
	"fmt"

	"github.com/ucan-wg/go-ucan/token/delegation"
	"github.com/ucan-wg/go-ucan/token/internal/envelope"
)

func main() {
	const cborBase64 = "glhAmnAkgfjAx4SA5pzJmtaHRJtTGNpF1y6oqb4yhGoM2H2EUGbBYT4rVDjMKBgCjhdGHjipm00L8iR5SsQh3sIEBaJhaEQ07QFxc3VjYW4vZGxnQDEuMC4wLXJjLjGoY2F1ZHg4ZGlkOmtleTp6Nk1rcTVZbWJKY1RyUEV4TkRpMjZpbXJUQ3BLaGVwakJGQlNIcXJCRE4yQXJQa3ZjY21kaC9mb28vYmFyY2V4cPZjaXNzeDhkaWQ6a2V5Ono2TWtwem4ybjNaR1QyVmFxTUdTUUMzdHptelY0VFM5UzcxaUZzRFhFMVdub05IMmNwb2yDg2I9PWcuc3RhdHVzZWRyYWZ0g2NhbGxpLnJldmlld2Vyg2RsaWtlZi5lbWFpbG0qQGV4YW1wbGUuY29tg2NhbnllLnRhZ3OCYm9ygoNiPT1hLmRuZXdzg2I9PWEuZXByZXNzY3N1Yng4ZGlkOmtleTp6Nk1rdEExdUJkQ3BxNHVKQnFFOWpqTWlMeXhaQmc5YTZ4Z1BQS0pqTXFzczZaYzJkbWV0YaBlbm9uY2VMAAECAwQFBgcICQoL"

	cborBytes, err := base64.StdEncoding.DecodeString(cborBase64)
	printThenPanicOnErr(err)

	tkn, c, err := delegation.FromSealed(cborBytes)
	printThenPanicOnErr(err)

	fmt.Println("CID (base58BTC):", envelope.CIDToBase58BTC(c))
	fmt.Println("Issuer (iss):", tkn.Issuer().String())
	fmt.Println("Audience (aud):", tkn.Audience().String())
	fmt.Println("Subject (sub):", tkn.Subject().String())
	fmt.Println("Command (cmd):", tkn.Command().String())
	fmt.Println("Policy (pol):", tkn.Policy().String())
	fmt.Println("Nonce (nonce):", hex.EncodeToString(tkn.Nonce()))
	fmt.Println("Meta (meta):", tkn.Meta().String())
	fmt.Println("NotBefore (nbf):", tkn.NotBefore())
	fmt.Println("Expiration (exp):", tkn.Expiration())

}

func printThenPanicOnErr(err error) {
	if err != nil {
		panic(err)
	}
}
Output:

CID (base58BTC): zdpuAw26pFuvZa2Z9YAtpZZnWN6VmnRFr7Z8LVY5c7RVWoxGY
Issuer (iss): did:key:z6Mkpzn2n3ZGT2VaqMGSQC3tzmzV4TS9S71iFsDXE1WnoNH2
Audience (aud): did:key:z6Mkq5YmbJcTrPExNDi26imrTCpKhepjBFBSHqrBDN2ArPkv
Subject (sub): did:key:z6MktA1uBdCpq4uJBqE9jjMiLyxZBg9a6xgPPKJjMqss6Zc2
Command (cmd): /foo/bar
Policy (pol): [
  ["==", ".status", "draft"],
  ["all", ".reviewer",
    ["like", ".email", "*@example.com"]
  ],
  ["any", ".tags",
    ["or", [
      ["==", ".", "news"],
      ["==", ".", "press"]
    ]]
  ]
]
Nonce (nonce): 000102030405060708090a0b
Meta (meta): {}
NotBefore (nbf): <nil>
Expiration (exp): <nil>

func FromSealedReader

func FromSealedReader(r io.Reader) (*Token, cid.Cid, error)

FromSealedReader is the same as Unseal but accepts an io.Reader.

func New

func New(iss did.DID, aud did.DID, cmd command.Command, pol policy.Policy, sub did.DID, opts ...Option) (*Token, error)

New creates a validated delegation Token from the provided parameters and options. This is typically used to delegate a given power to another agent.

You can read it as "(issuer) allows (audience) to perform (cmd+pol) on (subject)".

Example

The following example shows how to create a delegation.Token with distinct DIDs for issuer (iss), audience (aud) and subject (sub).

fmt.Println("issDid:", didtest.PersonaBob.DID().String())

// The command defines the shape of the arguments that will be evaluated against the policy
cmd := command.MustParse("/foo/bar")

// The policy defines what is allowed to do.
pol := policy.MustConstruct(
	policy.Equal(".status", literal.String("draft")),
	policy.All(".reviewer",
		policy.Like(".email", "*@example.com"),
	),
	policy.Any(".tags", policy.Or(
		policy.Equal(".", literal.String("news")),
		policy.Equal(".", literal.String("press")),
	)),
)

tkn, err := delegation.New(didtest.PersonaBob.DID(), didtest.PersonaCarol.DID(), cmd, pol, didtest.PersonaAlice.DID(),
	delegation.WithExpirationIn(time.Hour),
	delegation.WithNotBeforeIn(time.Minute),
	delegation.WithMeta("foo", "bar"),
	delegation.WithMeta("baz", 123),
)
printThenPanicOnErr(err)

// "Seal", meaning encode and wrap into a signed envelope.
data, id, err := tkn.ToSealed(didtest.PersonaBob.PrivKey())
printThenPanicOnErr(err)

printCIDAndSealed(id, data)

// Example output:
//
// issDid: did:key:z6MkvJPmEZZYbgiw1ouT1oouTsTFBHJSts9ophVsNgcRmYxU
//
// CID (base58BTC): zdpuAsqfZkgg2jgZyob23sq1J9xwtf9PHgt1PsskVCMq7Vvxk
//
// DAG-CBOR (base64) out: lhAOnjc0bPptlI5MxRBrIK3YmAP1CxKfXOPkz6MHt/UJCx2gCN+6gXZX2N+BIJvmy8XmAO5sT2GYimiV7HlJH1AA6JhaEQ07QFxc3VjYW4vZGxnQDEuMC4wLXJjLjGpY2F1ZHg4ZGlkOmtleTp6Nk1rZ3VwY2hoNUh3dUhhaFM3WXN5RThiTHVhMU1yOHAyaUtOUmh5dlN2UkFzOW5jY21kaC9mb28vYmFyY2V4cBpnROP/Y2lzc3g4ZGlkOmtleTp6Nk1rdkpQbUVaWlliZ2l3MW91VDFvb3VUc1RGQkhKU3RzOW9waFZzTmdjUm1ZeFVjbmJmGmdE1itjcG9sg4NiPT1nLnN0YXR1c2VkcmFmdINjYWxsaS5yZXZpZXdlcoNkbGlrZWYuZW1haWxtKkBleGFtcGxlLmNvbYNjYW55ZS50YWdzgmJvcoKDYj09YS5kbmV3c4NiPT1hLmVwcmVzc2NzdWJ4OGRpZDprZXk6ejZNa3V1a2syc2tEWExRbjdOSzNFaDlqTW5kWWZ2REJ4eGt0Z3BpZEpBcWI3TTNwZG1ldGGiY2Jhehh7Y2Zvb2NiYXJlbm9uY2VMv+Diy6GExIuM1eX4
// Converted to DAG-JSON out:
//	[
//		{
//			"/": {
//				"bytes": "5rvl8uKmDVGvAVSt4m/0MGiXl9dZwljJJ9m2qHCoIB617l26UvMxyH5uvN9hM7ozfVATiq4mLhoGgm9IGnEEAg"
//			}
//		},
//		{
//			"h": {
//				"/": {
//					"bytes": "NO0BcQ"
//				}
//			},
//			"ucan/dlg@1.0.0-rc.1": {
//				"aud": "did:key:z6Mkq5YmbJcTrPExNDi26imrTCpKhepjBFBSHqrBDN2ArPkv",
//				"cmd": "/foo/bar",
//				"exp": 1728933098,
//				"iss": "did:key:z6MkhVFznPeR572rTK51UjoTNpnF8cxuWfPm9oBMPr7y8ABe",
//				"meta": {
//					"baz": 123,
//					"foo": "bar"
//				},
//				"nbf": 1728929558,
//				"nonce": {
//					"/": {
//						"bytes": "u0HMgJ5Y+M84I/66"
//					}
//				},
//				"pol": [
//					[
//						"==",
//						".status",
//						"draft"
//					],
//					[
//						"all",
//						".reviewer",
//						[
//							"like",
//							".email",
//							"*@example.com"
//						]
//					],
//					[
//						"any",
//						".tags",
//						[
//							"or",
//							[
//								[
//									"==",
//									".",
//									"news"
//								],
//								[
//									"==",
//									".",
//									"press"
//								]
//							]
//						]
//					]
//				],
//				"sub": "did:key:z6MktA1uBdCpq4uJBqE9jjMiLyxZBg9a6xgPPKJjMqss6Zc2"
//			}
//		}
//	]
Output:

func Powerline

func Powerline(iss did.DID, aud did.DID, cmd command.Command, pol policy.Policy, opts ...Option) (*Token, error)

Powerline creates a validated UCAN delegation Token from the provided parameters and options.

Powerline is a pattern for automatically delegating all future delegations to another agent regardless of Subject. This is a very powerful pattern, use it only if you understand it. Powerline delegations MUST NOT be used as the root delegation to a resource

A very common use case for Powerline is providing a stable DID across multiple agents (e.g. representing a user with multiple devices). This enables the automatic sharing of authority across their devices without needing to share keys or set up a threshold scheme. It is also flexible, since a Powerline delegation MAY be revoked.

You can read it as "(issuer) allows (audience) to perform (cmd+pol) on anything".

func Root

func Root(iss did.DID, aud did.DID, cmd command.Command, pol policy.Policy, opts ...Option) (*Token, error)

Root creates a validated UCAN delegation Token from the provided parameters and options. This is typically used to create and give a power to an agent.

You can read it as "(issuer) allows (audience) to perform (cmd+pol) on itself".

Example

The following example shows how to create a UCAN root delegation.Token - a delegation.Token with the subject (sub) set to the value of issuer (iss).

// The command defines the shape of the arguments that will be evaluated against the policy
cmd := command.MustParse("/foo/bar")

// The policy defines what is allowed to do.
pol := policy.MustConstruct(
	policy.Equal(".status", literal.String("draft")),
	policy.All(".reviewer",
		policy.Like(".email", "*@example.com"),
	),
	policy.Any(".tags", policy.Or(
		policy.Equal(".", literal.String("news")),
		policy.Equal(".", literal.String("press")),
	)),
)

tkn, err := delegation.Root(didtest.PersonaAlice.DID(), didtest.PersonaBob.DID(), cmd, pol,
	delegation.WithExpirationIn(time.Hour),
	delegation.WithNotBeforeIn(time.Minute),
	delegation.WithMeta("foo", "bar"),
	delegation.WithMeta("baz", 123),
)
printThenPanicOnErr(err)

// "Seal", meaning encode and wrap into a signed envelope.
data, id, err := tkn.ToSealed(didtest.PersonaAlice.PrivKey())
printThenPanicOnErr(err)

printCIDAndSealed(id, data)

// Example output:
//
// issDid: did:key:z6MknWJqz17Y4AfsXSJUFKomuBR4GTkViM7kJYutzTMkCyFF
//
// CID (base58BTC): zdpuAkwYz8nY7uU8j3F6wVTfFY1VEoExwvUAYBEwRWfTozddE
//
// DAG-CBOR (base64) out: glhAVpW67FJ+myNi+azvnw2jivuiqXTuMrDZI2Qdaa8jE1Oi3mkjnm7DyqSQGADcomcuDslMWKmJ+OIyvbPG5PtSA6JhaEQ07QFxc3VjYW4vZGxnQDEuMC4wLXJjLjGpY2F1ZHg4ZGlkOmtleTp6Nk1rdkpQbUVaWlliZ2l3MW91VDFvb3VUc1RGQkhKU3RzOW9waFZzTmdjUm1ZeFVjY21kaC9mb28vYmFyY2V4cBpnROVoY2lzc3g4ZGlkOmtleTp6Nk1rdXVrazJza0RYTFFuN05LM0VoOWpNbmRZZnZEQnh4a3RncGlkSkFxYjdNM3BjbmJmGmdE15RjcG9sg4NiPT1nLnN0YXR1c2VkcmFmdINjYWxsaS5yZXZpZXdlcoNkbGlrZWYuZW1haWxtKkBleGFtcGxlLmNvbYNjYW55ZS50YWdzgmJvcoKDYj09YS5kbmV3c4NiPT1hLmVwcmVzc2NzdWJ4OGRpZDprZXk6ejZNa3V1a2syc2tEWExRbjdOSzNFaDlqTW5kWWZ2REJ4eGt0Z3BpZEpBcWI3TTNwZG1ldGGiY2Jhehh7Y2Zvb2NiYXJlbm9uY2VMwzDc03WBciJIGPWG
//
// Converted to DAG-JSON out:
//	[
//		{
//			"/": {
//				"bytes": "VpW67FJ+myNi+azvnw2jivuiqXTuMrDZI2Qdaa8jE1Oi3mkjnm7DyqSQGADcomcuDslMWKmJ+OIyvbPG5PtSAw"
//			}
//		},
//		{
//			"h": {
//				"/": {
//					"bytes": "NO0BcQ"
//				}
//			},
//			"ucan/dlg@1.0.0-rc.1": {
//				"aud": "did:key:z6MkvJPmEZZYbgiw1ouT1oouTsTFBHJSts9ophVsNgcRmYxU",
//				"cmd": "/foo/bar",
//				"exp": 1732568424,
//				"iss": "did:key:z6Mkuukk2skDXLQn7NK3Eh9jMndYfvDBxxktgpidJAqb7M3p",
//				"meta": {
//					"baz": 123,
//					"foo": "bar"
//				},
//				"nbf": 1732564884,
//				"nonce": {
//					"/": {
//						"bytes": "wzDc03WBciJIGPWG"
//					}
//				},
//				"pol": [
//					[
//						"==",
//						".status",
//						"draft"
//					],
//					[
//						"all",
//						".reviewer",
//						[
//							"like",
//							".email",
//							"*@example.com"
//						]
//					],
//					[
//						"any",
//						".tags",
//						[
//							"or",
//							[
//								[
//									"==",
//									".",
//									"news"
//								],
//								[
//									"==",
//									".",
//									"press"
//								]
//							]
//						]
//					]
//				],
//				"sub": "did:key:z6Mkuukk2skDXLQn7NK3Eh9jMndYfvDBxxktgpidJAqb7M3p"
//			}
//		}
//	]
Output:

func (*Token) Audience

func (t *Token) Audience() did.DID

Audience returns the did.DID representing the Token's audience.

func (*Token) Command

func (t *Token) Command() command.Command

Command returns the capability's command.Command.

func (*Token) Encode

func (t *Token) Encode(privKey crypto.PrivKey, encFn codec.Encoder) ([]byte, error)

Encode marshals a Token to the format specified by the provided codec.Encoder.

func (*Token) EncodeWriter

func (t *Token) EncodeWriter(w io.Writer, privKey crypto.PrivKey, encFn codec.Encoder) error

EncodeWriter is the same as Encode, but accepts an io.Writer.

func (*Token) Expiration

func (t *Token) Expiration() *time.Time

Expiration returns the time at which the Token expires.

func (*Token) IsValidAt

func (t *Token) IsValidAt(ti time.Time) bool

IsValidNow verifies that the token can be used at the given time, based on expiration or "not before" fields. This does NOT do any other kind of verifications.

func (*Token) IsValidNow

func (t *Token) IsValidNow() bool

IsValidNow verifies that the token can be used at the current time, based on expiration or "not before" fields. This does NOT do any other kind of verifications.

func (*Token) Issuer

func (t *Token) Issuer() did.DID

Issuer returns the did.DID representing the Token's issuer.

func (*Token) Meta

func (t *Token) Meta() meta.ReadOnly

Meta returns the Token's metadata.

func (*Token) Nonce

func (t *Token) Nonce() []byte

Nonce returns the random Nonce encapsulated in this Token.

func (*Token) NotBefore

func (t *Token) NotBefore() *time.Time

NotBefore returns the time at which the Token becomes "active".

func (*Token) Policy

func (t *Token) Policy() policy.Policy

Policy returns the capability's policy.Policy.

func (*Token) String

func (t *Token) String() string

func (*Token) Subject

func (t *Token) Subject() did.DID

Subject returns the did.DID representing the Token's subject.

This field may be did.Undef for delegations that are [Powerlined] but must be equal to the value returned by the Issuer method for root tokens.

func (*Token) ToDagCbor

func (t *Token) ToDagCbor(privKey crypto.PrivKey) ([]byte, error)

ToDagCbor marshals the Token to the DAG-CBOR format.

func (*Token) ToDagCborWriter

func (t *Token) ToDagCborWriter(w io.Writer, privKey crypto.PrivKey) error

ToDagCborWriter is the same as ToDagCbor, but it accepts an io.Writer.

func (*Token) ToDagJson

func (t *Token) ToDagJson(privKey crypto.PrivKey) ([]byte, error)

ToDagJson marshals the Token to the DAG-JSON format.

func (*Token) ToDagJsonWriter

func (t *Token) ToDagJsonWriter(w io.Writer, privKey crypto.PrivKey) error

ToDagJsonWriter is the same as ToDagJson, but it accepts an io.Writer.

func (*Token) ToSealed

func (t *Token) ToSealed(privKey crypto.PrivKey) ([]byte, cid.Cid, error)

ToSealed wraps the delegation token in an envelope, generates the signature, encodes the result to DAG-CBOR and calculates the CID of the resulting binary data.

func (*Token) ToSealedWriter

func (t *Token) ToSealedWriter(w io.Writer, privKey crypto.PrivKey) (cid.Cid, error)

ToSealedWriter is the same as ToSealed but accepts an io.Writer.

Directories

Path Synopsis
Package delegationtest provides a set of pre-built delegation tokens for a variety of test cases.
Package delegationtest provides a set of pre-built delegation tokens for a variety of test cases.

Jump to

Keyboard shortcuts

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