Files
ucan/delegate.go
2024-09-09 08:55:14 -04:00

125 lines
3.3 KiB
Go

package ucan
import (
"crypto/rand"
"time"
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/ucan-wg/go-ucan/v1/capability/command"
"github.com/ucan-wg/go-ucan/v1/capability/policy"
"github.com/ucan-wg/go-ucan/v1/delegation"
"github.com/ucan-wg/go-ucan/v1/did"
"github.com/ucan-wg/go-ucan/v1/internal/envelope"
"github.com/ucan-wg/go-ucan/v1/issue"
)
const (
DefaultExpiration = 30 * 24 * time.Hour
DefaultNonceLength = 32
)
//go:generate -command options go run github.com/launchdarkly/go-options
//go:generate options -type=authorityConfig -option=AuthorityOption -prefix=With -output=authority_options.go -cmp=false -new=false -imports=time
type authorityConfig struct {
expiration time.Duration
nonceLength int
}
type Authority struct {
*authorityConfig
privKey crypto.PrivKey
did did.DID // TODO
}
func NewAuthority(privKey crypto.PrivKey, opts ...AuthorityOption) (*Authority, error) {
cfg := &authorityConfig{
expiration: DefaultExpiration,
nonceLength: DefaultNonceLength,
}
if err := applyAuthorityConfigOptions(cfg, opts...); err != nil {
return nil, err
}
id, err := did.FromPubKey(privKey.GetPublic())
if err != nil {
return nil, err
}
return &Authority{
authorityConfig: cfg,
privKey: privKey,
did: id,
}, nil
}
func (a *Authority) DID() did.DID {
return a.did
}
func (a *Authority) Expiration() time.Duration {
return a.expiration
}
func (a *Authority) NonceLength() int {
return a.nonceLength
}
func (a *Authority) Delegate(aud did.DID, prf []delegation.Token, cmd *command.Command, pol *policy.Policy, exp *time.Time, opts ...delegation.Option) (*envelope.Envelope[*delegation.Token], error) {
nonce, err := a.Nonce()
if err != nil {
return nil, err
}
tkn, err := delegation.New(a.DID(), aud, prf, cmd, pol, nil, nonce, opts...)
if err != nil {
return nil, err
}
return envelope.New(a.privKey, tkn)
}
func (a *Authority) Nonce() ([]byte, error) {
nonce := make([]byte, a.nonceLength)
if _, err := rand.Read(nonce); err != nil {
return nil, err
}
return nonce, nil
}
// Issue creates a root UCAN token that can later be delegated.
//
// A subject is required when creating a root UCAN delegation as root
// UCAN delegation tokens should never be "Powerlined" per the
// specification. Therefore, the inclusion of the WithSubject or WithPowerline
// options will result in an error.
//
// Issuing a root UCAN delegation token
// should be a relatively rare occurrence, so this method is not
// available via an Authority.
func Issue(privKey crypto.PrivKey, sub did.DID, cmd *command.Command, pol *policy.Policy, exp *time.Time, opts ...issue.Option) (*envelope.Envelope[*delegation.Token], error) { // TODO: cmd as pointer?
delOpts, err := issue.ToDelegateOptions(sub, opts...)
if err != nil {
return nil, err
}
authority, err := NewAuthority(privKey)
if err != nil {
return nil, err
}
return authority.Delegate(authority.DID(), nil, cmd, pol, nil, delOpts...)
}
func Delegate(privKey crypto.PrivKey, aud did.DID, prf []delegation.Token, cmd *command.Command, pol *policy.Policy, exp *time.Time, opts ...delegation.Option) (*envelope.Envelope[*delegation.Token], error) {
authority, err := NewAuthority(privKey)
if err != nil {
return nil, err
}
return authority.Delegate(aud, prf, cmd, pol, exp, opts...)
}