feat(invocation): validate delegation proof chain
This commit is contained in:
committed by
Michael Muré
parent
89e4d5d419
commit
0f70557309
32
token/invocation/errors.go
Normal file
32
token/invocation/errors.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package invocation
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrDelegationExpired is returned if one of the delegations in the
|
||||||
|
// proof chain has expired.
|
||||||
|
ErrDelegationExpired = errors.New("delegation in proof chain has expired")
|
||||||
|
|
||||||
|
// ErrDelegationInactive is returned if one of the delegations in the
|
||||||
|
// proof chain is not yet active.
|
||||||
|
ErrDelegationInactive = errors.New("delegation in proof chain not yet active")
|
||||||
|
|
||||||
|
// ErrLastNotRoot is returned if the last delegation token in the proof
|
||||||
|
// chain is not a root delegation token.
|
||||||
|
ErrLastNotRoot = errors.New("the last delegation token in proof chain must be a root token")
|
||||||
|
|
||||||
|
// ErrMissingDelegation
|
||||||
|
ErrMissingDelegation = errors.New("loader missing delegation for proof chain")
|
||||||
|
|
||||||
|
// ErrNoProof is returned when no delegations were provided to prove
|
||||||
|
// that the invocation should be executed.
|
||||||
|
ErrNoProof = errors.New("at least one delegation must be provided to validate the invocation")
|
||||||
|
|
||||||
|
// ErrNotIssuedToInvoder is returned if the first delegation token's
|
||||||
|
// Audience DID is not the Invoker's Issuer DID.
|
||||||
|
ErrNotIssuedToInvoker = errors.New("first delegation token is not issued to invoker")
|
||||||
|
|
||||||
|
// ErrBrokenChain is returned when the Audience of each delegation is
|
||||||
|
// not the Issuer of the previous one.
|
||||||
|
ErrBrokenChain = errors.New("delegation proof chain is broken")
|
||||||
|
)
|
||||||
@@ -3,6 +3,45 @@
|
|||||||
// from the [envelope]-enclosed, signed and DAG-CBOR-encoded form that
|
// from the [envelope]-enclosed, signed and DAG-CBOR-encoded form that
|
||||||
// should most commonly be used for transport and storage.
|
// should most commonly be used for transport and storage.
|
||||||
//
|
//
|
||||||
|
// # Invocation token validation
|
||||||
|
//
|
||||||
|
// Per the specification, invocation Tokens must be validated before the
|
||||||
|
// command is executed. This validation can happen in multiple stages:
|
||||||
|
//
|
||||||
|
// 1. When the invocation is unsealed from its containing envelope:
|
||||||
|
// a. The envelope can be decoded.
|
||||||
|
// b. The envelope contains a Signature, VarsigHeader and Payload.
|
||||||
|
// c. The Payload contains an iss field that contains a valid did:key.
|
||||||
|
// d. The a public key can be extracted from the did:key.
|
||||||
|
// e. The public key type is supported by go-ucan.
|
||||||
|
// f. The Signature can be decoded per the VarsigHeader.
|
||||||
|
// g. The SigPayload can be verified using the Signature and public key.
|
||||||
|
// h. The field key of the TokenPayload matches the expected tag.
|
||||||
|
// 2. When the invocation is created or passes step one:
|
||||||
|
// a. The Issuer field is not empty.
|
||||||
|
// b. The Subject field is not empty
|
||||||
|
// c. The Command field is not empty and the Command is not a wildcard.
|
||||||
|
// d. The Policy field is present (but may be empty).
|
||||||
|
// e. The Arguments field is present (but may be empty).
|
||||||
|
// 3. When an unsealed invocation passes steps one and two for execution:
|
||||||
|
// a. The invocation can not be expired.
|
||||||
|
// b. Invoked at should not be in the future.
|
||||||
|
// 4. When the proof chain is being validated:
|
||||||
|
// a. There must be at least one delegation in the proof chain.
|
||||||
|
// b. All referenced delegations must be available.
|
||||||
|
// c. The first proof must be issued to the Invoker (audience DID).
|
||||||
|
// d. The token must not be expired (expiration in the future or absent).
|
||||||
|
// e. The token must be active (nbf in the past or absent).
|
||||||
|
// f. The Issuer of each delegation must be the Audience in the next
|
||||||
|
// one.
|
||||||
|
// g. The last token must be a root delegation.
|
||||||
|
// h. The Subject of each delegation must equal the invocation's
|
||||||
|
// Audience field.
|
||||||
|
// i. The command of each delegation must "allow" the one before it.
|
||||||
|
// 5. If steps 1-4 pass:
|
||||||
|
// a. The policy must "match" the arguments.
|
||||||
|
// b. The nonce (if present) is not reused.
|
||||||
|
//
|
||||||
// [envelope]: https://github.com/ucan-wg/spec#envelope
|
// [envelope]: https://github.com/ucan-wg/spec#envelope
|
||||||
// [invocation]: https://github.com/ucan-wg/invocation
|
// [invocation]: https://github.com/ucan-wg/invocation
|
||||||
package invocation
|
package invocation
|
||||||
@@ -104,16 +143,71 @@ type DelegationLoader interface {
|
|||||||
GetDelegation(cid cid.Cid) (*delegation.Token, error)
|
GetDelegation(cid cid.Cid) (*delegation.Token, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Token) ExecutionAllowed(loader DelegationLoader) bool {
|
func (t *Token) ExecutionAllowed(loader DelegationLoader) (bool, error) {
|
||||||
return t.executionAllowed(loader, t.arguments)
|
return t.executionAllowed(loader, t.arguments)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Token) ExecutionAllowedWithArgsHook(loader DelegationLoader, hook func(*args.Args) *args.Args) bool {
|
func (t *Token) ExecutionAllowedWithArgsHook(loader DelegationLoader, hook func(*args.Args) *args.Args) (bool, error) {
|
||||||
return t.executionAllowed(loader, hook(t.arguments))
|
return t.executionAllowed(loader, hook(t.arguments))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Token) executionAllowed(loader DelegationLoader, arguments *args.Args) bool {
|
func (t *Token) executionAllowed(loader DelegationLoader, arguments *args.Args) (bool, error) {
|
||||||
panic("TODO")
|
// There must be at least one delegation referenced - 4a
|
||||||
|
if len(t.proof) < 1 {
|
||||||
|
return false, ErrNoProof
|
||||||
|
}
|
||||||
|
|
||||||
|
type chainer interface {
|
||||||
|
Issuer() did.DID
|
||||||
|
Subject() did.DID // TODO: if the invocation token's Audience is nil, copy the subject into it
|
||||||
|
Command() command.Command
|
||||||
|
}
|
||||||
|
|
||||||
|
// This starts as the invocation token but will be the root delegation
|
||||||
|
// after the for loop below completes
|
||||||
|
var lastChainer chainer = t
|
||||||
|
|
||||||
|
for i, dlgCid := range t.proof {
|
||||||
|
// The token must be present - 4b
|
||||||
|
dlg, err := loader.GetDelegation(dlgCid)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("%w: need %s", ErrMissingDelegation, dlgCid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// No tokens in the proof chain may be expired - 4d
|
||||||
|
if dlg.Expiration() != nil && dlg.Expiration().Before(time.Now()) {
|
||||||
|
return false, fmt.Errorf("%w: CID is %s", ErrDelegationExpired, dlgCid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// No tokens in the proof chain may be inactive - 4e
|
||||||
|
if dlg.NotBefore() != nil && dlg.NotBefore().After(time.Now()) {
|
||||||
|
return false, fmt.Errorf("%w: CID is %s", ErrDelegationInactive, dlgCid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// First proof must have the invoker's Issuer as the Audience - 4c
|
||||||
|
if i == 0 && dlg.Audience() != t.Issuer() {
|
||||||
|
return false, fmt.Errorf("%w: expected %s, got %s", ErrNotIssuedToInvoker, t.issuer, dlg.Audience())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tokens must form a chain with current issuer equal to the
|
||||||
|
// next audience - 4f
|
||||||
|
if lastChainer.Issuer() != dlg.Audience() {
|
||||||
|
return false, fmt.Errorf("%w: expected %s, got %s", ErrBrokenChain, lastChainer.Issuer(), dlg.Audience())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Checking the subject consistency can happen here - 4h
|
||||||
|
// TODO: Checking the command equivalence or attenuation can happen here - 4i
|
||||||
|
|
||||||
|
lastChainer = dlg
|
||||||
|
}
|
||||||
|
|
||||||
|
// The last prf value must be a root delegation (have the issuer field
|
||||||
|
// match the Subject field) - 4g
|
||||||
|
if lastChainer.Issuer() != lastChainer.Subject() {
|
||||||
|
return false, fmt.Errorf("%w: expected %s, got %s", ErrLastNotRoot, lastChainer.Subject(), lastChainer.Issuer())
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Issuer returns the did.DID representing the Token's issuer.
|
// Issuer returns the did.DID representing the Token's issuer.
|
||||||
|
|||||||
334
token/invocation/invocation_test.go
Normal file
334
token/invocation/invocation_test.go
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
package invocation_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ipfs/go-cid"
|
||||||
|
"github.com/libp2p/go-libp2p/core/crypto"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/ucan-wg/go-ucan/did"
|
||||||
|
"github.com/ucan-wg/go-ucan/pkg/args"
|
||||||
|
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||||
|
"github.com/ucan-wg/go-ucan/pkg/policy"
|
||||||
|
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||||
|
"github.com/ucan-wg/go-ucan/token/invocation"
|
||||||
|
"github.com/ucan-wg/go-ucan/token/invocation/invocationtest"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
rootPrivKeyCfg = "CAESQNMjVXyblHUrtmvdzjfkdrUlLkxh/cHZMFirki7dJLFjW7cCs74VxjO8Wh04f8Xg0uqyKw7N0wTUBiPy2h4hGPQ="
|
||||||
|
rootDIDStr = "did:key:z6MkkdH136Kee5akqx1DiK3hE3WGx4SKmHo11tYw2cWmjjZV"
|
||||||
|
rootTknCIDStr = "bafyreibk6lkgd32zldpqqqsdn7diyosokelrhder5lic4ujadtxl5blkei"
|
||||||
|
|
||||||
|
rootOnlyTknCIDStr = "bafyreidyknlfnsah63jujdkhe5vvjil2yoznlgoaezeq62qqhghaxzpfya"
|
||||||
|
|
||||||
|
dlg0PrivKeyCfg = "CAESQFIb3aD0lZzidUzTtwdpHtCyx1VJxe+Uq4x/S+XQFDDgz/NZIi/TR3rhgUn550RSBOSxNmw0QnR0FOPmAB7SXAg="
|
||||||
|
dlg0DIDStr = "did:key:z6MktT1f5LXQ2MFSUwwTTY9DDU2QdBzZA11V5bjou4YDQY6K"
|
||||||
|
dlg0TknCIDStr = "bafyreihocbstcdvgyeczjoijiyo2bdppyplm2aglqumwtutyapd2zlp2bi"
|
||||||
|
|
||||||
|
expiredTknDagJson = `[{"/":{"bytes":"U4IH1Q52UrdOyOHkHtXSkH0uf5ouk10k/LTOMz3UvP2k1kqv9/rbGXUwhQCy6JP3s8hc+U3h/lBXYFYzIlULAw"}},{"h":{"/":{"bytes":"NO0BcQ"}},"ucan/dlg@1.0.0-rc.1":{"aud":"did:key:z6MkqTkYrRV6WpFCWRcEHBXnTV3cVqzMtVZREhJKAAswrrGq","cmd":"/seg0","exp":1731439015,"iss":"did:key:z6MktT1f5LXQ2MFSUwwTTY9DDU2QdBzZA11V5bjou4YDQY6K","nonce":{"/":{"bytes":"AAECAwQFBgcICQoL"}},"pol":[],"sub":"did:key:z6MkkdH136Kee5akqx1DiK3hE3WGx4SKmHo11tYw2cWmjjZV"}}]`
|
||||||
|
expiredDlg0TknCIDStr = "bafyreigezbrohmjmzv5nxdw2nhjabnrvs52vcv4uvbuscnbyr2jzep2vru"
|
||||||
|
|
||||||
|
inactiveDlg0TknCIDStr = "bafyreiffflxvtfiv722i3lomrqcogc6nncxai3voxeltvrphpuwecdfbgq"
|
||||||
|
|
||||||
|
dlg1PrivKeyCfg = "CAESQPHkXp4OatPxpJ1veMAxEYzP4rd3/sPUz9PRQuJaDKTco5DJTdJC5iXxjCe1VDYAmRwlJOBvBdbsvS3qFgV6zoI="
|
||||||
|
dlg1DIDStr = "did:key:z6MkqTkYrRV6WpFCWRcEHBXnTV3cVqzMtVZREhJKAAswrrGq"
|
||||||
|
dlg1TknCIDStr = "bafyreihjbkvom3tdivzolh6aieuwamz4x6bu3dh667bxytdc5vzo37obo4"
|
||||||
|
|
||||||
|
// dlg2PrivKeyCfg = "CAESQJryJWe3uGt+7BTH2doqsN+2H83rNXv7Yw0tMoKE+lBKTqByESll668G1Jiy9gW/8hm9/jVcrFD9Y1BWyokfNBE="
|
||||||
|
// dlg2DIDStr = "did:key:z6MkjkBk9hzMhcr6rYMXHPEsXy1LAWK5dHniNdbaEPojGXr8"
|
||||||
|
|
||||||
|
// dlg3PrivKeyCfg = "CAESQPrLMlX2p+Dgz70YCyVXbHAJfVMT7lLUYAbuZ1rBKQmBLiD7WJ4Spc4VFZAsJ7HUnkneJWNTk/FFaN2z3pb/OZI="
|
||||||
|
// dlg3DIDStr = "did:key:z6MkhZKy9X4sLtbH1fGQmPVMjXmBAEQ3vAN6DRSfebSBCqpu"
|
||||||
|
|
||||||
|
invPrivKeyCfg = "CAESQHJW8WYTZDRzxjLBjrFN35raIGvVsPoXAJB/5X+J8miboVWVLZFyQmxCAIXOMpwLqWW7R2I98qsCGvxgEJZ5qgY="
|
||||||
|
invDIDStr = "did:key:z6MkqK3NgTnZZo77iptQdU9proJn1ozMmcTSKR98t8sZzJAq"
|
||||||
|
invTknCIDStr = ""
|
||||||
|
|
||||||
|
missingPrivKeyCfg = "CAESQMjRvrEIjpPYRQKmkAGw/pV0XgE958rYa4vlnKJjl1zz/sdnGnyV1xKLJk8D39edyjhHWyqcpgFnozQK62SG16k="
|
||||||
|
missingDIDStr = "bafyreigwypmw6eul6vadi6g6lnfbsfo2zck7gfzsbjoroqs3djhnzzc7mm"
|
||||||
|
missingTknCIDStr = "did:key:z6MkwboxFsH3kEuehBZ5fLkRmxi68yv1u38swA4r9Jm2VRma"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestToken_ExecutionAllowed(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
t.Run("passes - only root", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
args := invocationtest.EmptyArguments
|
||||||
|
prf := invocationtest.Proof(t, rootOnlyTknCIDStr)
|
||||||
|
testPasses(t, []string{"seg0"}, args, prf)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("passes - valid chain", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
args := invocationtest.EmptyArguments
|
||||||
|
prf := invocationtest.Proof(t, dlg1TknCIDStr, dlg0TknCIDStr, rootTknCIDStr)
|
||||||
|
testPasses(t, []string{"seg0"}, args, prf)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("fails - no proof", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
args := args.New()
|
||||||
|
testFails(t, invocation.ErrNoProof, []string{"seg0"}, args, []cid.Cid{})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("fails - missing referenced delegation", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
args := invocationtest.EmptyArguments
|
||||||
|
prf := invocationtest.Proof(t, missingDIDStr, rootTknCIDStr)
|
||||||
|
testFails(t, invocation.ErrMissingDelegation, []string{"seg0"}, args, prf)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("fails - referenced delegation expired", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
args := invocationtest.EmptyArguments
|
||||||
|
prf := invocationtest.Proof(t, dlg1TknCIDStr, expiredDlg0TknCIDStr, rootTknCIDStr)
|
||||||
|
testFails(t, invocation.ErrDelegationExpired, []string{"seg0"}, args, prf)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("fails - referenced delegation inactive", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
args := invocationtest.EmptyArguments
|
||||||
|
prf := invocationtest.Proof(t, dlg1TknCIDStr, inactiveDlg0TknCIDStr, rootTknCIDStr)
|
||||||
|
testFails(t, invocation.ErrDelegationInactive, []string{"seg0"}, args, prf)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("fails - last (or only) delegation not root", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
args := args.New()
|
||||||
|
prf := invocationtest.Proof(t, dlg1TknCIDStr)
|
||||||
|
testFails(t, invocation.ErrLastNotRoot, []string{"seg0"}, args, prf)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("fails - broken chain", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
args := invocationtest.EmptyArguments
|
||||||
|
prf := invocationtest.Proof(t, dlg1TknCIDStr, rootTknCIDStr)
|
||||||
|
testFails(t, invocation.ErrBrokenChain, []string{"seg0"}, args, prf)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("fails - first not issued to invoker", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
args := invocationtest.EmptyArguments
|
||||||
|
prf := invocationtest.Proof(t, dlg0TknCIDStr, rootTknCIDStr)
|
||||||
|
testFails(t, invocation.ErrNotIssuedToInvoker, []string{"seg0"}, args, prf)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func test(t *testing.T, cmdSegs []string, args *args.Args, prf []cid.Cid, opts ...invocation.Option) (bool, error) {
|
||||||
|
ldr := newTestDelegationLoader(t)
|
||||||
|
tkn := testInvocation(t, invPrivKeyCfg, rootDIDStr, rootDIDStr, cmdSegs, args, prf, opts...)
|
||||||
|
|
||||||
|
return tkn.ExecutionAllowed(ldr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFails(t *testing.T, expErr error, cmdSegs []string, args *args.Args, prf []cid.Cid, opts ...invocation.Option) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
ok, err := test(t, cmdSegs, args, prf, opts...)
|
||||||
|
require.ErrorIs(t, err, expErr)
|
||||||
|
assert.False(t, ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testPasses(t *testing.T, cmdSegs []string, args *args.Args, prf []cid.Cid, opts ...invocation.Option) {
|
||||||
|
ok, err := test(t, cmdSegs, args, prf, opts...)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.True(t, ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ invocation.DelegationLoader = (*testDelegationLoader)(nil)
|
||||||
|
|
||||||
|
type testDelegationLoader struct {
|
||||||
|
tkns map[cid.Cid]*delegation.Token
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestDelegationLoader(t *testing.T) *testDelegationLoader {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
cmdSegs := []string{"seg0"}
|
||||||
|
ldr := &testDelegationLoader{
|
||||||
|
tkns: map[cid.Cid]*delegation.Token{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// aud, err := did.Parse(dlg0DIDStr)
|
||||||
|
// require.NoError(t, err)
|
||||||
|
// cmd := command.New(cmdSegs...)
|
||||||
|
|
||||||
|
// pol, err := policy.FromDagJson("[]")
|
||||||
|
// require.NoError(t, err)
|
||||||
|
|
||||||
|
// rootDlg, err := delegation.Root(rootPrivKey, aud, cmd, pol)
|
||||||
|
// require.NoError(t, err)
|
||||||
|
// _, rootCID, err := rootDlg.ToSealed(rootPrivKey)
|
||||||
|
// require.NoError(t, err)
|
||||||
|
|
||||||
|
// Do not add this one to the loader
|
||||||
|
_, missingCID := testDelegation(t, missingPrivKeyCfg, dlg1DIDStr, rootDIDStr, cmdSegs, "[]")
|
||||||
|
t.Log("Missing CID", missingCID)
|
||||||
|
|
||||||
|
rootTkn, rootDlgCID := testDelegation(t, rootPrivKeyCfg, dlg0DIDStr, rootDIDStr, cmdSegs, "[]")
|
||||||
|
t.Log("Root chain CID:", rootDlgCID)
|
||||||
|
ldr.tkns[rootDlgCID] = rootTkn
|
||||||
|
|
||||||
|
rootOnlyTkn, rootOnlyDlgCID := testDelegation(t, rootPrivKeyCfg, invDIDStr, rootDIDStr, cmdSegs, "[]")
|
||||||
|
t.Log("Root only chain CID:", rootOnlyDlgCID)
|
||||||
|
ldr.tkns[rootOnlyDlgCID] = rootOnlyTkn
|
||||||
|
|
||||||
|
dlg0Tkn, dlg0CID := testDelegation(t, dlg0PrivKeyCfg, dlg1DIDStr, rootDIDStr, cmdSegs, "[]")
|
||||||
|
t.Log("Dlg0 CID:", dlg0CID)
|
||||||
|
ldr.tkns[dlg0CID] = dlg0Tkn
|
||||||
|
|
||||||
|
// exp := time.Now().Add(time.Second)
|
||||||
|
|
||||||
|
// expiredDlg0Tkn, expiredDlg0CID := testDelegation(t, dlg0PrivKeyCfg, dlg1DIDStr, rootDIDStr, cmdSegs, "[]", delegation.WithExpiration(exp))
|
||||||
|
// t.Log("Expired Dlg0 CID:", expiredDlg0CID)
|
||||||
|
|
||||||
|
// dlg0PrivKeyEnc, err := crypto.ConfigDecodeKey(dlg0PrivKeyCfg)
|
||||||
|
// require.NoError(t, err)
|
||||||
|
// dlg0PrivKey, err := crypto.UnmarshalPrivateKey(dlg0PrivKeyEnc)
|
||||||
|
// require.NoError(t, err)
|
||||||
|
|
||||||
|
// dlg0DagJson, err := expiredDlg0Tkn.ToDagJson(dlg0PrivKey)
|
||||||
|
// require.NoError(t, err)
|
||||||
|
|
||||||
|
// t.Log("Expired token DAGJSON:", string(dlg0DagJson))
|
||||||
|
|
||||||
|
expiredDlg0Tkn, err := delegation.FromDagJson([]byte(expiredTknDagJson))
|
||||||
|
require.NoError(t, err)
|
||||||
|
expiredDlg0TknCID, err := cid.Parse(expiredDlg0TknCIDStr)
|
||||||
|
ldr.tkns[expiredDlg0TknCID] = expiredDlg0Tkn
|
||||||
|
|
||||||
|
nbf, err := time.Parse(time.RFC3339, "2500-01-01T00:00:00Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
inactiveDlg0Tkn, inactiveDlg0CID := testDelegation(t, dlg0PrivKeyCfg, dlg1DIDStr, rootDIDStr, cmdSegs, "[]", delegation.WithNotBefore(nbf))
|
||||||
|
t.Log("Inactive Dlg0 CID:", inactiveDlg0CID)
|
||||||
|
ldr.tkns[inactiveDlg0CID] = inactiveDlg0Tkn
|
||||||
|
|
||||||
|
dlg1Tkn, dlg1CID := testDelegation(t, dlg1PrivKeyCfg, invDIDStr, rootDIDStr, cmdSegs, "[]")
|
||||||
|
t.Log("Dlg1 CID:", dlg1CID)
|
||||||
|
ldr.tkns[dlg1CID] = dlg1Tkn
|
||||||
|
|
||||||
|
// for i := 0; i < 5; i++ {
|
||||||
|
// privKey, id, err := did.GenerateEd25519()
|
||||||
|
// require.NoError(t, err)
|
||||||
|
|
||||||
|
// privKeyEnc, err := crypto.MarshalPrivateKey(privKey)
|
||||||
|
// require.NoError(t, err)
|
||||||
|
|
||||||
|
// t.Log("PrivKey:", crypto.ConfigEncodeKey(privKeyEnc), "DID:", id)
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
t.Log("Delegation loader length:", len(ldr.tkns))
|
||||||
|
|
||||||
|
return ldr
|
||||||
|
}
|
||||||
|
|
||||||
|
var ErrUnknownDelegationTokenCID = errors.New("unknown delegation token CID")
|
||||||
|
|
||||||
|
func (l *testDelegationLoader) GetDelegation(c cid.Cid) (*delegation.Token, error) {
|
||||||
|
tkn, ok := l.tkns[c]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("%w: %s", ErrUnknownDelegationTokenCID, c.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return tkn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDelegation(t *testing.T, privKeyCfg string, audStr string, subStr string, cmdSegs []string, polDAGJSON string, opts ...delegation.Option) (*delegation.Token, cid.Cid) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
privKey, aud, sub, cmd := parseTokenArgs(t, privKeyCfg, audStr, subStr, cmdSegs)
|
||||||
|
|
||||||
|
pol, err := policy.FromDagJson(polDAGJSON)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
opts = append(
|
||||||
|
opts,
|
||||||
|
delegation.WithNonce([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b}),
|
||||||
|
delegation.WithSubject(sub),
|
||||||
|
)
|
||||||
|
|
||||||
|
tkn, err := delegation.New(privKey, aud, cmd, pol, opts...)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, id, err := tkn.ToSealed(privKey)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return tkn, id
|
||||||
|
}
|
||||||
|
|
||||||
|
func testInvocation(t *testing.T, privKeyCfg string, audStr string, subStr string, cmdSegs []string, args *args.Args, prf []cid.Cid, opts ...invocation.Option) *invocation.Token {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
privKey, _, sub, cmd := parseTokenArgs(t, privKeyCfg, audStr, subStr, cmdSegs)
|
||||||
|
|
||||||
|
iss, err := did.FromPrivKey(privKey)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
tkn, err := invocation.New(iss, sub, cmd, prf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return tkn
|
||||||
|
}
|
||||||
|
|
||||||
|
func testProof(t *testing.T, cidStrs ...string) []cid.Cid {
|
||||||
|
var prf []cid.Cid
|
||||||
|
|
||||||
|
for cidStr := range cidStrs {
|
||||||
|
id, err := cid.Parse(cidStr)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
prf = append(prf, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return prf
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTokenArgs(
|
||||||
|
t *testing.T,
|
||||||
|
privKeyCfg string,
|
||||||
|
audStr string,
|
||||||
|
subStr string,
|
||||||
|
cmdSegs []string,
|
||||||
|
) (
|
||||||
|
privKey crypto.PrivKey,
|
||||||
|
aud did.DID,
|
||||||
|
sub did.DID,
|
||||||
|
cmd command.Command,
|
||||||
|
) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
privKeyEnc, err := crypto.ConfigDecodeKey(privKeyCfg)
|
||||||
|
require.NoError(t, err)
|
||||||
|
privKey, err = crypto.UnmarshalPrivateKey(privKeyEnc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
aud, err = did.Parse(audStr)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
sub, err = did.Parse(subStr)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
cmd = command.New(cmdSegs...)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
39
token/invocation/invocationtest/field.go
Normal file
39
token/invocation/invocationtest/field.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package invocationtest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ipfs/go-cid"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/ucan-wg/go-ucan/pkg/args"
|
||||||
|
"github.com/ucan-wg/go-ucan/pkg/policy"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
EmptyArguments = args.New()
|
||||||
|
EmptyPolicy = emptyPolicy()
|
||||||
|
)
|
||||||
|
|
||||||
|
func emptyPolicy() policy.Policy {
|
||||||
|
pol, err := policy.FromDagJson("[]")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pol
|
||||||
|
}
|
||||||
|
|
||||||
|
func Proof(t *testing.T, cidStrs ...string) []cid.Cid {
|
||||||
|
// t.Helper()
|
||||||
|
|
||||||
|
prf := make([]cid.Cid, len(cidStrs))
|
||||||
|
|
||||||
|
for i, cidStr := range cidStrs {
|
||||||
|
id, err := cid.Parse(cidStr)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
prf[i] = id
|
||||||
|
}
|
||||||
|
|
||||||
|
return prf
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user