github.com/storacha/go-ucanto@v0.7.2/validator/lib_test.go (about)

     1  package validator
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/ipfs/go-cid"
    11  	"github.com/ipld/go-ipld-prime"
    12  	cidlink "github.com/ipld/go-ipld-prime/linking/cid"
    13  	"github.com/ipld/go-ipld-prime/node/basicnode"
    14  	"github.com/storacha/go-ucanto/core/dag/blockstore"
    15  	"github.com/storacha/go-ucanto/core/delegation"
    16  	"github.com/storacha/go-ucanto/core/invocation"
    17  	"github.com/storacha/go-ucanto/core/ipld/block"
    18  	"github.com/storacha/go-ucanto/core/ipld/codec/cbor"
    19  	"github.com/storacha/go-ucanto/core/ipld/hash/sha256"
    20  	"github.com/storacha/go-ucanto/core/result/failure"
    21  	"github.com/storacha/go-ucanto/core/schema"
    22  	"github.com/storacha/go-ucanto/did"
    23  	"github.com/storacha/go-ucanto/principal"
    24  	"github.com/storacha/go-ucanto/principal/ed25519/verifier"
    25  	"github.com/storacha/go-ucanto/principal/signer"
    26  	"github.com/storacha/go-ucanto/testing/fixtures"
    27  	"github.com/storacha/go-ucanto/testing/helpers"
    28  	"github.com/storacha/go-ucanto/ucan"
    29  	udm "github.com/storacha/go-ucanto/ucan/datamodel/ucan"
    30  	"github.com/stretchr/testify/require"
    31  )
    32  
    33  type storeAddCaveats struct {
    34  	Link   ipld.Link
    35  	Origin ipld.Link
    36  }
    37  
    38  func (c storeAddCaveats) ToIPLD() (ipld.Node, error) {
    39  	np := basicnode.Prototype.Any
    40  	nb := np.NewBuilder()
    41  	ma, _ := nb.BeginMap(2)
    42  	if c != (storeAddCaveats{}) {
    43  		ma.AssembleKey().AssignString("link")
    44  		ma.AssembleValue().AssignLink(c.Link)
    45  		if c.Origin != nil {
    46  			ma.AssembleKey().AssignString("origin")
    47  			ma.AssembleValue().AssignLink(c.Origin)
    48  		}
    49  	}
    50  	ma.Finish()
    51  	return nb.Build(), nil
    52  }
    53  
    54  var storeAddTyp = helpers.Must(ipld.LoadSchemaBytes([]byte(`
    55  	type StoreAddCaveats struct {
    56  		link Link
    57  		origin optional Link
    58  	}
    59  `)))
    60  
    61  var storeAdd = NewCapability(
    62  	"store/add",
    63  	schema.DIDString(),
    64  	schema.Struct[storeAddCaveats](storeAddTyp.TypeByName("StoreAddCaveats"), nil),
    65  	func(claimed, delegated ucan.Capability[storeAddCaveats]) failure.Failure {
    66  		if claimed.With() != delegated.With() {
    67  			err := fmt.Errorf("Expected 'with: \"%s\"' instead got '%s'", delegated.With(), claimed.With())
    68  			return failure.FromError(err)
    69  		}
    70  		if delegated.Nb().Link != nil && delegated.Nb().Link != claimed.Nb().Link {
    71  			var err error
    72  			if claimed.Nb().Link == nil {
    73  				err = fmt.Errorf("Link violates imposed %s constraint", delegated.Nb().Link)
    74  			} else {
    75  				err = fmt.Errorf("Link %s violates imposed %s constraint", claimed.Nb().Link, delegated.Nb().Link)
    76  			}
    77  			return failure.FromError(err)
    78  		}
    79  		return nil
    80  	},
    81  )
    82  
    83  var testLink = cidlink.Link{Cid: cid.MustParse("bafkqaaa")}
    84  var validateAuthOk = func(ctx context.Context, auth Authorization[any]) Revoked { return nil }
    85  var parseEdPrincipal = func(str string) (principal.Verifier, error) {
    86  	return verifier.Parse(str)
    87  }
    88  
    89  func TestAccess(t *testing.T) {
    90  	t.Run("authorized", func(t *testing.T) {
    91  		t.Run("self-issued invocation", func(t *testing.T) {
    92  			inv, err := storeAdd.Invoke(
    93  				fixtures.Alice,
    94  				fixtures.Bob,
    95  				fixtures.Alice.DID().String(),
    96  				storeAddCaveats{Link: testLink},
    97  			)
    98  			require.NoError(t, err)
    99  
   100  			vctx := NewValidationContext(
   101  				fixtures.Service.Verifier(),
   102  				storeAdd,
   103  				IsSelfIssued,
   104  				validateAuthOk,
   105  				ProofUnavailable,
   106  				parseEdPrincipal,
   107  				FailDIDKeyResolution,
   108  				NotExpiredNotTooEarly,
   109  			)
   110  
   111  			a, x := Access(t.Context(), inv, vctx)
   112  			require.NoError(t, x)
   113  			require.Equal(t, storeAdd.Can(), a.Capability().Can())
   114  			require.Equal(t, fixtures.Alice.DID().String(), a.Capability().With())
   115  			require.Equal(t, fixtures.Alice.DID(), a.Issuer().DID())
   116  			require.Equal(t, fixtures.Bob.DID(), a.Audience().DID())
   117  		})
   118  
   119  		t.Run("delegated invocation", func(t *testing.T) {
   120  			dlg, err := storeAdd.Delegate(
   121  				fixtures.Alice,
   122  				fixtures.Bob,
   123  				fixtures.Alice.DID().String(),
   124  				storeAddCaveats{},
   125  			)
   126  			require.NoError(t, err)
   127  
   128  			inv, err := storeAdd.Invoke(
   129  				fixtures.Bob,
   130  				fixtures.Service,
   131  				fixtures.Alice.DID().String(),
   132  				storeAddCaveats{Link: testLink},
   133  				delegation.WithProof(delegation.FromDelegation(dlg)),
   134  			)
   135  			require.NoError(t, err)
   136  
   137  			vctx := NewValidationContext(
   138  				fixtures.Service.Verifier(),
   139  				storeAdd,
   140  				IsSelfIssued,
   141  				validateAuthOk,
   142  				ProofUnavailable,
   143  				parseEdPrincipal,
   144  				FailDIDKeyResolution,
   145  				NotExpiredNotTooEarly,
   146  			)
   147  
   148  			a, x := Access(t.Context(), inv, vctx)
   149  			require.NoError(t, x)
   150  			require.Equal(t, storeAdd.Can(), a.Capability().Can())
   151  			require.Equal(t, fixtures.Alice.DID().String(), a.Capability().With())
   152  			require.Equal(t, fixtures.Bob.DID(), a.Issuer().DID())
   153  			require.Equal(t, fixtures.Service.DID(), a.Audience().DID())
   154  		})
   155  
   156  		t.Run("delegation chain", func(t *testing.T) {
   157  			alice2bob, err := storeAdd.Delegate(
   158  				fixtures.Alice,
   159  				fixtures.Bob,
   160  				fixtures.Alice.DID().String(),
   161  				storeAddCaveats{},
   162  			)
   163  			require.NoError(t, err)
   164  
   165  			bob2mallory, err := storeAdd.Delegate(
   166  				fixtures.Bob,
   167  				fixtures.Mallory,
   168  				fixtures.Alice.DID().String(),
   169  				storeAddCaveats{},
   170  				delegation.WithProof(delegation.FromDelegation(alice2bob)),
   171  			)
   172  			require.NoError(t, err)
   173  
   174  			inv, err := storeAdd.Invoke(
   175  				fixtures.Mallory,
   176  				fixtures.Service,
   177  				fixtures.Alice.DID().String(),
   178  				storeAddCaveats{Link: testLink},
   179  				delegation.WithProof(delegation.FromDelegation(bob2mallory)),
   180  			)
   181  			require.NoError(t, err)
   182  
   183  			vctx := NewValidationContext(
   184  				fixtures.Service.Verifier(),
   185  				storeAdd,
   186  				IsSelfIssued,
   187  				validateAuthOk,
   188  				ProofUnavailable,
   189  				parseEdPrincipal,
   190  				FailDIDKeyResolution,
   191  				NotExpiredNotTooEarly,
   192  			)
   193  
   194  			a, x := Access(t.Context(), inv, vctx)
   195  			require.NoError(t, x)
   196  			require.Equal(t, storeAdd.Can(), a.Capability().Can())
   197  			require.Equal(t, fixtures.Alice.DID().String(), a.Capability().With())
   198  			require.Equal(t, fixtures.Mallory.DID(), a.Issuer().DID())
   199  			require.Equal(t, fixtures.Service.DID(), a.Audience().DID())
   200  
   201  			require.Equal(t, storeAdd.Can(), a.Proofs()[0].Capability().Can())
   202  			require.Equal(t, fixtures.Alice.DID().String(), a.Proofs()[0].Capability().With())
   203  			require.Equal(t, fixtures.Bob.DID(), a.Proofs()[0].Issuer().DID())
   204  			require.Equal(t, fixtures.Mallory.DID(), a.Proofs()[0].Audience().DID())
   205  
   206  			require.Equal(t, storeAdd.Can(), a.Proofs()[0].Proofs()[0].Capability().Can())
   207  			require.Equal(t, fixtures.Alice.DID().String(), a.Proofs()[0].Proofs()[0].Capability().With())
   208  			require.Equal(t, fixtures.Alice.DID(), a.Proofs()[0].Proofs()[0].Issuer().DID())
   209  			require.Equal(t, fixtures.Bob.DID(), a.Proofs()[0].Proofs()[0].Audience().DID())
   210  		})
   211  
   212  		t.Run("resolve external proof", func(t *testing.T) {
   213  			dlg, err := storeAdd.Delegate(
   214  				fixtures.Alice,
   215  				fixtures.Bob,
   216  				fixtures.Alice.DID().String(),
   217  				storeAddCaveats{},
   218  			)
   219  			require.NoError(t, err)
   220  
   221  			inv, err := storeAdd.Invoke(
   222  				fixtures.Bob,
   223  				fixtures.Service,
   224  				fixtures.Alice.DID().String(),
   225  				storeAddCaveats{Link: testLink},
   226  				delegation.WithProof(delegation.FromDelegation(dlg)),
   227  			)
   228  			require.NoError(t, err)
   229  
   230  			vctx := NewValidationContext(
   231  				fixtures.Service.Verifier(),
   232  				storeAdd,
   233  				IsSelfIssued,
   234  				validateAuthOk,
   235  				func(ctx context.Context, p ucan.Link) (delegation.Delegation, UnavailableProof) {
   236  					if p == dlg.Link() {
   237  						return dlg, nil
   238  					}
   239  					return nil, NewUnavailableProofError(p, fmt.Errorf("no proof resolver configured"))
   240  				},
   241  				parseEdPrincipal,
   242  				FailDIDKeyResolution,
   243  				NotExpiredNotTooEarly,
   244  			)
   245  
   246  			a, x := Access(t.Context(), inv, vctx)
   247  			require.NoError(t, x)
   248  			require.Equal(t, storeAdd.Can(), a.Capability().Can())
   249  			require.Equal(t, fixtures.Alice.DID().String(), a.Capability().With())
   250  			require.Equal(t, fixtures.Bob.DID(), a.Issuer().DID())
   251  			require.Equal(t, fixtures.Service.DID(), a.Audience().DID())
   252  
   253  			require.Equal(t, storeAdd.Can(), a.Proofs()[0].Capability().Can())
   254  			require.Equal(t, fixtures.Alice.DID().String(), a.Proofs()[0].Capability().With())
   255  			require.Equal(t, fixtures.Alice.DID(), a.Proofs()[0].Issuer().DID())
   256  			require.Equal(t, fixtures.Bob.DID(), a.Proofs()[0].Audience().DID())
   257  		})
   258  
   259  		t.Run("expiration and not valid before can be ignored", func(t *testing.T) {
   260  			exp := ucan.Now() - 5
   261  			nbf := ucan.Now() + 500
   262  			inv, err := storeAdd.Invoke(
   263  				fixtures.Alice,
   264  				fixtures.Service,
   265  				fixtures.Alice.DID().String(),
   266  				storeAddCaveats{Link: testLink},
   267  				delegation.WithExpiration(exp),
   268  				delegation.WithNotBefore(nbf),
   269  			)
   270  			require.NoError(t, err)
   271  
   272  			vctx := NewValidationContext(
   273  				fixtures.Service.Verifier(),
   274  				storeAdd,
   275  				IsSelfIssued,
   276  				validateAuthOk,
   277  				ProofUnavailable,
   278  				parseEdPrincipal,
   279  				FailDIDKeyResolution,
   280  				func(dlg delegation.Delegation) InvalidProof {
   281  					return nil
   282  				},
   283  			)
   284  
   285  			a, x := Access(t.Context(), inv, vctx)
   286  			require.NoError(t, x)
   287  			require.Equal(t, storeAdd.Can(), a.Capability().Can())
   288  			require.Equal(t, fixtures.Alice.DID().String(), a.Capability().With())
   289  			require.Equal(t, fixtures.Alice.DID(), a.Issuer().DID())
   290  			require.Equal(t, fixtures.Service.DID(), a.Audience().DID())
   291  		})
   292  	})
   293  
   294  	t.Run("unauthorized", func(t *testing.T) {
   295  		t.Run("expired invocation", func(t *testing.T) {
   296  			exp := ucan.Now() - 5
   297  			inv, err := storeAdd.Invoke(
   298  				fixtures.Alice,
   299  				fixtures.Service,
   300  				fixtures.Alice.DID().String(),
   301  				storeAddCaveats{Link: testLink},
   302  				delegation.WithExpiration(exp),
   303  			)
   304  			require.NoError(t, err)
   305  
   306  			vctx := NewValidationContext(
   307  				fixtures.Service.Verifier(),
   308  				storeAdd,
   309  				IsSelfIssued,
   310  				validateAuthOk,
   311  				ProofUnavailable,
   312  				parseEdPrincipal,
   313  				FailDIDKeyResolution,
   314  				NotExpiredNotTooEarly,
   315  			)
   316  
   317  			a, x := Access(t.Context(), inv, vctx)
   318  			require.Nil(t, a)
   319  			require.Error(t, x)
   320  			require.Equal(t, x.Name(), "Unauthorized")
   321  			msg := strings.Join([]string{
   322  				fmt.Sprintf("Claim %s is not authorized", storeAdd),
   323  				fmt.Sprintf("  - Proof %s has expired on %s", inv.Link(), time.Unix(int64(exp), 0).Format(time.RFC3339)),
   324  			}, "\n")
   325  			require.Equal(t, msg, x.Error())
   326  		})
   327  
   328  		t.Run("not valid before", func(t *testing.T) {
   329  			nbf := ucan.Now() + 500
   330  			inv, err := storeAdd.Invoke(
   331  				fixtures.Alice,
   332  				fixtures.Service,
   333  				fixtures.Alice.DID().String(),
   334  				storeAddCaveats{Link: testLink},
   335  				delegation.WithNotBefore(nbf),
   336  			)
   337  			require.NoError(t, err)
   338  
   339  			vctx := NewValidationContext(
   340  				fixtures.Service.Verifier(),
   341  				storeAdd,
   342  				IsSelfIssued,
   343  				validateAuthOk,
   344  				ProofUnavailable,
   345  				parseEdPrincipal,
   346  				FailDIDKeyResolution,
   347  				NotExpiredNotTooEarly,
   348  			)
   349  
   350  			a, x := Access(t.Context(), inv, vctx)
   351  			require.Nil(t, a)
   352  			require.Error(t, x)
   353  			require.Equal(t, x.Name(), "Unauthorized")
   354  			msg := strings.Join([]string{
   355  				fmt.Sprintf("Claim %s is not authorized", storeAdd),
   356  				fmt.Sprintf("  - Proof %s is not valid before %s", inv.Link(), time.Unix(int64(nbf), 0).Format(time.RFC3339)),
   357  			}, "\n")
   358  			require.Equal(t, msg, x.Error())
   359  		})
   360  
   361  		t.Run("invalid signature", func(t *testing.T) {
   362  			inv, err := storeAdd.Invoke(
   363  				fixtures.Alice,
   364  				fixtures.Service,
   365  				fixtures.Alice.DID().String(),
   366  				storeAddCaveats{Link: testLink},
   367  			)
   368  			require.NoError(t, err)
   369  
   370  			inv.Data().Model().S = fixtures.Bob.Sign(inv.Root().Bytes()).Bytes()
   371  
   372  			vctx := NewValidationContext(
   373  				fixtures.Service.Verifier(),
   374  				storeAdd,
   375  				IsSelfIssued,
   376  				validateAuthOk,
   377  				ProofUnavailable,
   378  				parseEdPrincipal,
   379  				FailDIDKeyResolution,
   380  				NotExpiredNotTooEarly,
   381  			)
   382  
   383  			a, x := Access(t.Context(), inv, vctx)
   384  			require.Nil(t, a)
   385  			require.Error(t, x)
   386  			require.Equal(t, x.Name(), "Unauthorized")
   387  			msg := strings.Join([]string{
   388  				fmt.Sprintf("Claim %s is not authorized", storeAdd),
   389  				fmt.Sprintf("  - Proof %s does not have a valid signature from %s", inv.Link(), fixtures.Alice.DID()),
   390  			}, "\n")
   391  			require.Equal(t, msg, x.Error())
   392  		})
   393  
   394  		t.Run("unknown capability", func(t *testing.T) {
   395  			inv, err := invocation.Invoke(
   396  				fixtures.Alice,
   397  				fixtures.Service,
   398  				ucan.NewCapability(
   399  					"store/write",
   400  					fixtures.Alice.DID().String(),
   401  					ucan.NoCaveats{},
   402  				),
   403  			)
   404  			require.NoError(t, err)
   405  
   406  			vctx := NewValidationContext(
   407  				fixtures.Service.Verifier(),
   408  				storeAdd,
   409  				IsSelfIssued,
   410  				validateAuthOk,
   411  				ProofUnavailable,
   412  				parseEdPrincipal,
   413  				FailDIDKeyResolution,
   414  				NotExpiredNotTooEarly,
   415  			)
   416  
   417  			a, x := Access(t.Context(), inv, vctx)
   418  			require.Nil(t, a)
   419  			require.Error(t, x)
   420  			require.Equal(t, x.Name(), "Unauthorized")
   421  			msg := strings.Join([]string{
   422  				fmt.Sprintf("Claim %s is not authorized", storeAdd),
   423  				"  - No matching delegated capability found",
   424  				"  - Encountered unknown capabilities",
   425  				fmt.Sprintf("    - {\"can\":\"store/write\",\"with\":\"%s\",\"nb\":{}}", fixtures.Alice.DID()),
   426  			}, "\n")
   427  			require.Equal(t, msg, x.Error())
   428  		})
   429  	})
   430  
   431  	t.Run("invalid claim", func(t *testing.T) {
   432  		t.Run("no proofs", func(t *testing.T) {
   433  			inv, err := storeAdd.Invoke(
   434  				fixtures.Alice,
   435  				fixtures.Bob,
   436  				fixtures.Bob.DID().String(),
   437  				storeAddCaveats{Link: testLink},
   438  			)
   439  			require.NoError(t, err)
   440  
   441  			vctx := NewValidationContext(
   442  				fixtures.Service.Verifier(),
   443  				storeAdd,
   444  				IsSelfIssued,
   445  				validateAuthOk,
   446  				ProofUnavailable,
   447  				parseEdPrincipal,
   448  				FailDIDKeyResolution,
   449  				NotExpiredNotTooEarly,
   450  			)
   451  
   452  			a, x := Access(t.Context(), inv, vctx)
   453  			require.Nil(t, a)
   454  			require.Error(t, x)
   455  			require.Equal(t, x.Name(), "Unauthorized")
   456  			msg := strings.Join([]string{
   457  				fmt.Sprintf("Claim %s is not authorized", storeAdd),
   458  				fmt.Sprintf(`  - Capability {"can":"%s","with":"%s","nb":{"Link":{"/":"%s"},"Origin":null}} is not authorized because:`, storeAdd.Can(), fixtures.Bob.DID(), testLink),
   459  				fmt.Sprintf("    - Capability can not be (self) issued by '%s'", fixtures.Alice.DID()),
   460  				"    - Delegated capability not found",
   461  			}, "\n")
   462  			require.Equal(t, msg, x.Error())
   463  		})
   464  
   465  		t.Run("expired", func(t *testing.T) {
   466  			exp := ucan.Now() - 5
   467  			dlg, err := storeAdd.Delegate(
   468  				fixtures.Alice,
   469  				fixtures.Bob,
   470  				fixtures.Alice.DID().String(),
   471  				storeAddCaveats{},
   472  				delegation.WithExpiration(exp),
   473  			)
   474  			require.NoError(t, err)
   475  
   476  			inv, err := storeAdd.Invoke(
   477  				fixtures.Bob,
   478  				fixtures.Service,
   479  				fixtures.Alice.DID().String(),
   480  				storeAddCaveats{Link: testLink},
   481  				delegation.WithProof(delegation.FromDelegation(dlg)),
   482  			)
   483  			require.NoError(t, err)
   484  
   485  			vctx := NewValidationContext(
   486  				fixtures.Service.Verifier(),
   487  				storeAdd,
   488  				IsSelfIssued,
   489  				validateAuthOk,
   490  				ProofUnavailable,
   491  				parseEdPrincipal,
   492  				FailDIDKeyResolution,
   493  				NotExpiredNotTooEarly,
   494  			)
   495  
   496  			a, x := Access(t.Context(), inv, vctx)
   497  			require.Nil(t, a)
   498  			require.Error(t, x)
   499  			require.Equal(t, x.Name(), "Unauthorized")
   500  			msg := strings.Join([]string{
   501  				fmt.Sprintf("Claim %s is not authorized", storeAdd),
   502  				fmt.Sprintf(`  - Capability {"can":"%s","with":"%s","nb":{"Link":{"/":"%s"},"Origin":null}} is not authorized because:`, storeAdd.Can(), fixtures.Alice.DID(), testLink),
   503  				fmt.Sprintf("    - Capability can not be (self) issued by '%s'", fixtures.Bob.DID()),
   504  				fmt.Sprintf("    - Capability can not be derived from prf: %s because:", dlg.Link()),
   505  				fmt.Sprintf("      - Proof %s has expired on %s", dlg.Link(), time.Unix(int64(exp), 0).Format(time.RFC3339)),
   506  			}, "\n")
   507  			require.Equal(t, msg, x.Error())
   508  		})
   509  
   510  		t.Run("not valid before", func(t *testing.T) {
   511  			nbf := ucan.Now() + 60*60
   512  			dlg, err := storeAdd.Delegate(
   513  				fixtures.Alice,
   514  				fixtures.Bob,
   515  				fixtures.Alice.DID().String(),
   516  				storeAddCaveats{},
   517  				delegation.WithNotBefore(nbf),
   518  			)
   519  			require.NoError(t, err)
   520  
   521  			inv, err := storeAdd.Invoke(
   522  				fixtures.Bob,
   523  				fixtures.Service,
   524  				fixtures.Alice.DID().String(),
   525  				storeAddCaveats{Link: testLink},
   526  				delegation.WithProof(delegation.FromDelegation(dlg)),
   527  			)
   528  			require.NoError(t, err)
   529  
   530  			vctx := NewValidationContext(
   531  				fixtures.Service.Verifier(),
   532  				storeAdd,
   533  				IsSelfIssued,
   534  				validateAuthOk,
   535  				ProofUnavailable,
   536  				parseEdPrincipal,
   537  				FailDIDKeyResolution,
   538  				NotExpiredNotTooEarly,
   539  			)
   540  
   541  			a, x := Access(t.Context(), inv, vctx)
   542  			require.Nil(t, a)
   543  			require.Error(t, x)
   544  			require.Equal(t, x.Name(), "Unauthorized")
   545  			msg := strings.Join([]string{
   546  				fmt.Sprintf("Claim %s is not authorized", storeAdd),
   547  				fmt.Sprintf(`  - Capability {"can":"%s","with":"%s","nb":{"Link":{"/":"%s"},"Origin":null}} is not authorized because:`, storeAdd.Can(), fixtures.Alice.DID(), testLink),
   548  				fmt.Sprintf("    - Capability can not be (self) issued by '%s'", fixtures.Bob.DID()),
   549  				fmt.Sprintf("    - Capability can not be derived from prf: %s because:", dlg.Link()),
   550  				fmt.Sprintf("      - Proof %s is not valid before %s", dlg.Link(), time.Unix(int64(nbf), 0).Format(time.RFC3339)),
   551  			}, "\n")
   552  			require.Equal(t, msg, x.Error())
   553  		})
   554  
   555  		t.Run("invalid signature", func(t *testing.T) {
   556  			// In order to mess up the signature we need to reach deep in UCAN library
   557  			// to create a UCAN model, manually setting the signature to something bad
   558  			// and then encode it as the root block of the delegation.
   559  			nb, _ := storeAddCaveats{Link: testLink}.ToIPLD()
   560  			exp := ucan.Now() + 30
   561  			model := udm.UCANModel{
   562  				V:   "0.9.1",
   563  				S:   fixtures.Alice.Sign([]byte{}).Bytes(),
   564  				Iss: fixtures.Alice.DID().Bytes(),
   565  				Aud: fixtures.Bob.DID().Bytes(),
   566  				Att: []udm.CapabilityModel{
   567  					{
   568  						Can:  storeAdd.Can(),
   569  						With: fixtures.Alice.DID().String(),
   570  						Nb:   nb,
   571  					},
   572  				},
   573  				Exp: &exp,
   574  			}
   575  
   576  			rt, err := block.Encode(&model, udm.Type(), cbor.Codec, sha256.Hasher)
   577  			require.NoError(t, err)
   578  
   579  			bs, err := blockstore.NewBlockStore(blockstore.WithBlocks([]block.Block{rt}))
   580  			require.NoError(t, err)
   581  
   582  			dlg, err := delegation.NewDelegation(rt, bs)
   583  			require.NoError(t, err)
   584  
   585  			inv, err := storeAdd.Invoke(
   586  				fixtures.Bob,
   587  				fixtures.Service,
   588  				fixtures.Alice.DID().String(),
   589  				storeAddCaveats{Link: testLink},
   590  				delegation.WithProof(delegation.FromDelegation(dlg)),
   591  			)
   592  			require.NoError(t, err)
   593  
   594  			vctx := NewValidationContext(
   595  				fixtures.Service.Verifier(),
   596  				storeAdd,
   597  				IsSelfIssued,
   598  				validateAuthOk,
   599  				ProofUnavailable,
   600  				parseEdPrincipal,
   601  				FailDIDKeyResolution,
   602  				NotExpiredNotTooEarly,
   603  			)
   604  
   605  			a, x := Access(t.Context(), inv, vctx)
   606  			require.Nil(t, a)
   607  			require.Error(t, x)
   608  			require.Equal(t, x.Name(), "Unauthorized")
   609  			msg := strings.Join([]string{
   610  				fmt.Sprintf("Claim %s is not authorized", storeAdd),
   611  				fmt.Sprintf(`  - Capability {"can":"%s","with":"%s","nb":{"Link":{"/":"%s"},"Origin":null}} is not authorized because:`, storeAdd.Can(), fixtures.Alice.DID(), testLink),
   612  				fmt.Sprintf("    - Capability can not be (self) issued by '%s'", fixtures.Bob.DID()),
   613  				fmt.Sprintf("    - Capability can not be derived from prf: %s because:", dlg.Link()),
   614  				fmt.Sprintf("      - Proof %s does not have a valid signature from %s", dlg.Link(), fixtures.Alice.DID()),
   615  			}, "\n")
   616  			require.Equal(t, msg, x.Error())
   617  		})
   618  
   619  		t.Run("unknown capability", func(t *testing.T) {
   620  			dlg, err := delegation.Delegate(
   621  				fixtures.Alice,
   622  				fixtures.Bob,
   623  				[]ucan.Capability[ucan.NoCaveats]{
   624  					ucan.NewCapability("store/pin", fixtures.Alice.DID().String(), ucan.NoCaveats{}),
   625  				},
   626  			)
   627  			require.NoError(t, err)
   628  
   629  			inv, err := storeAdd.Invoke(
   630  				fixtures.Bob,
   631  				fixtures.Service,
   632  				fixtures.Alice.DID().String(),
   633  				storeAddCaveats{Link: testLink},
   634  				delegation.WithProof(delegation.FromDelegation(dlg)),
   635  			)
   636  			require.NoError(t, err)
   637  
   638  			vctx := NewValidationContext(
   639  				fixtures.Service.Verifier(),
   640  				storeAdd,
   641  				IsSelfIssued,
   642  				validateAuthOk,
   643  				ProofUnavailable,
   644  				parseEdPrincipal,
   645  				FailDIDKeyResolution,
   646  				NotExpiredNotTooEarly,
   647  			)
   648  
   649  			a, x := Access(t.Context(), inv, vctx)
   650  			require.Nil(t, a)
   651  			require.Error(t, x)
   652  			require.Equal(t, x.Name(), "Unauthorized")
   653  			msg := strings.Join([]string{
   654  				fmt.Sprintf("Claim %s is not authorized", storeAdd),
   655  				fmt.Sprintf(`  - Capability {"can":"%s","with":"%s","nb":{"Link":{"/":"%s"},"Origin":null}} is not authorized because:`, storeAdd.Can(), fixtures.Alice.DID(), testLink),
   656  				fmt.Sprintf("    - Capability can not be (self) issued by '%s'", fixtures.Bob.DID()),
   657  				"    - Delegated capability not found",
   658  				"    - Encountered unknown capabilities",
   659  				fmt.Sprintf(`      - {"can":"store/pin","with":"%s","nb":{}}`, fixtures.Alice.DID()),
   660  			}, "\n")
   661  			require.Equal(t, msg, x.Error())
   662  		})
   663  
   664  		t.Run("malformed capability", func(t *testing.T) {
   665  			badDID := fmt.Sprintf("bib:%s", fixtures.Alice.DID().String()[4:])
   666  			dlg, err := storeAdd.Delegate(
   667  				fixtures.Alice,
   668  				fixtures.Bob,
   669  				badDID,
   670  				storeAddCaveats{},
   671  			)
   672  			require.NoError(t, err)
   673  
   674  			inv, err := storeAdd.Invoke(
   675  				fixtures.Bob,
   676  				fixtures.Service,
   677  				fixtures.Alice.DID().String(),
   678  				storeAddCaveats{Link: testLink},
   679  				delegation.WithProof(delegation.FromDelegation(dlg)),
   680  			)
   681  			require.NoError(t, err)
   682  
   683  			vctx := NewValidationContext(
   684  				fixtures.Service.Verifier(),
   685  				storeAdd,
   686  				IsSelfIssued,
   687  				validateAuthOk,
   688  				ProofUnavailable,
   689  				parseEdPrincipal,
   690  				FailDIDKeyResolution,
   691  				NotExpiredNotTooEarly,
   692  			)
   693  
   694  			a, x := Access(t.Context(), inv, vctx)
   695  			require.Nil(t, a)
   696  			require.Error(t, x)
   697  			require.Equal(t, x.Name(), "Unauthorized")
   698  			msg := strings.Join([]string{
   699  				fmt.Sprintf("Claim %s is not authorized", storeAdd),
   700  				fmt.Sprintf(`  - Capability {"can":"%s","with":"%s","nb":{"Link":{"/":"%s"},"Origin":null}} is not authorized because:`, storeAdd.Can(), fixtures.Alice.DID(), testLink),
   701  				fmt.Sprintf("    - Capability can not be (self) issued by '%s'", fixtures.Bob.DID()),
   702  				fmt.Sprintf(`    - Cannot derive {"can":"%s","with":"%s","nb":{"Link":{"/":"%s"},"Origin":null}} from delegated capabilities:`, storeAdd.Can(), fixtures.Alice.DID(), testLink),
   703  				fmt.Sprintf(`      - Encountered malformed '%s' capability: {"can":"%s","with":"%s","nb":{}}`, storeAdd.Can(), storeAdd.Can(), badDID),
   704  				fmt.Sprintf(`        - Expected a "did:" but got "%s" instead`, badDID),
   705  			}, "\n")
   706  			require.Equal(t, msg, x.Error())
   707  		})
   708  
   709  		t.Run("unavailable proof", func(t *testing.T) {
   710  			dlg, err := storeAdd.Delegate(
   711  				fixtures.Alice,
   712  				fixtures.Bob,
   713  				fixtures.Alice.DID().String(),
   714  				storeAddCaveats{},
   715  			)
   716  			require.NoError(t, err)
   717  
   718  			inv, err := storeAdd.Invoke(
   719  				fixtures.Bob,
   720  				fixtures.Service,
   721  				fixtures.Alice.DID().String(),
   722  				storeAddCaveats{Link: testLink},
   723  				delegation.WithProof(delegation.FromLink(dlg.Link())),
   724  			)
   725  			require.NoError(t, err)
   726  
   727  			vctx := NewValidationContext(
   728  				fixtures.Service.Verifier(),
   729  				storeAdd,
   730  				IsSelfIssued,
   731  				validateAuthOk,
   732  				ProofUnavailable,
   733  				parseEdPrincipal,
   734  				FailDIDKeyResolution,
   735  				NotExpiredNotTooEarly,
   736  			)
   737  
   738  			a, x := Access(t.Context(), inv, vctx)
   739  			require.Nil(t, a)
   740  			require.Error(t, x)
   741  			require.Equal(t, x.Name(), "Unauthorized")
   742  			msg := strings.Join([]string{
   743  				fmt.Sprintf("Claim %s is not authorized", storeAdd),
   744  				fmt.Sprintf(`  - Capability {"can":"%s","with":"%s","nb":{"Link":{"/":"%s"},"Origin":null}} is not authorized because:`, storeAdd.Can(), fixtures.Alice.DID(), testLink),
   745  				fmt.Sprintf("    - Capability can not be (self) issued by '%s'", fixtures.Bob.DID()),
   746  				fmt.Sprintf(`    - Capability can not be derived from prf: %s because:`, dlg.Link()),
   747  				fmt.Sprintf(`      - Linked proof "%s" is not included and could not be resolved`, dlg.Link()),
   748  				`        - Proof resolution failed with: no proof resolver configured`,
   749  			}, "\n")
   750  			require.Equal(t, msg, x.Error())
   751  		})
   752  
   753  		t.Run("invalid audience", func(t *testing.T) {
   754  			dlg, err := storeAdd.Delegate(
   755  				fixtures.Alice,
   756  				fixtures.Bob,
   757  				fixtures.Alice.DID().String(),
   758  				storeAddCaveats{},
   759  			)
   760  			require.NoError(t, err)
   761  
   762  			inv, err := storeAdd.Invoke(
   763  				fixtures.Mallory,
   764  				fixtures.Service,
   765  				fixtures.Alice.DID().String(),
   766  				storeAddCaveats{Link: testLink},
   767  				delegation.WithProof(delegation.FromDelegation(dlg)),
   768  			)
   769  			require.NoError(t, err)
   770  
   771  			vctx := NewValidationContext(
   772  				fixtures.Service.Verifier(),
   773  				storeAdd,
   774  				IsSelfIssued,
   775  				validateAuthOk,
   776  				ProofUnavailable,
   777  				parseEdPrincipal,
   778  				FailDIDKeyResolution,
   779  				NotExpiredNotTooEarly,
   780  			)
   781  
   782  			a, x := Access(t.Context(), inv, vctx)
   783  			require.Nil(t, a)
   784  			require.Error(t, x)
   785  			require.Equal(t, x.Name(), "Unauthorized")
   786  			msg := strings.Join([]string{
   787  				fmt.Sprintf("Claim %s is not authorized", storeAdd),
   788  				fmt.Sprintf(`  - Capability {"can":"%s","with":"%s","nb":{"Link":{"/":"%s"},"Origin":null}} is not authorized because:`, storeAdd.Can(), fixtures.Alice.DID(), testLink),
   789  				fmt.Sprintf("    - Capability can not be (self) issued by '%s'", fixtures.Mallory.DID()),
   790  				fmt.Sprintf(`    - Capability can not be derived from prf: %s because:`, dlg.Link()),
   791  				fmt.Sprintf(`      - Delegation audience is '%s' instead of '%s'`, fixtures.Bob.DID(), fixtures.Mallory.DID()),
   792  			}, "\n")
   793  			require.Equal(t, msg, x.Error())
   794  		})
   795  
   796  		t.Run("invalid claim", func(t *testing.T) {
   797  			dlg, err := storeAdd.Delegate(
   798  				fixtures.Alice,
   799  				fixtures.Bob,
   800  				fixtures.Mallory.DID().String(),
   801  				storeAddCaveats{},
   802  			)
   803  			require.NoError(t, err)
   804  
   805  			nb := storeAddCaveats{Link: testLink}
   806  			inv, err := storeAdd.Invoke(
   807  				fixtures.Bob,
   808  				fixtures.Service,
   809  				fixtures.Alice.DID().String(),
   810  				nb,
   811  				delegation.WithProof(delegation.FromDelegation(dlg)),
   812  			)
   813  			require.NoError(t, err)
   814  
   815  			vctx := NewValidationContext(
   816  				fixtures.Service.Verifier(),
   817  				storeAdd,
   818  				IsSelfIssued,
   819  				validateAuthOk,
   820  				ProofUnavailable,
   821  				parseEdPrincipal,
   822  				FailDIDKeyResolution,
   823  				NotExpiredNotTooEarly,
   824  			)
   825  
   826  			a, x := Access(t.Context(), inv, vctx)
   827  			require.Nil(t, a)
   828  			require.Error(t, x)
   829  			require.Equal(t, x.Name(), "Unauthorized")
   830  			msg := strings.Join([]string{
   831  				fmt.Sprintf("Claim %s is not authorized", storeAdd),
   832  				fmt.Sprintf(`  - Capability {"can":"%s","with":"%s","nb":{"Link":{"/":"%s"},"Origin":null}} is not authorized because:`, storeAdd.Can(), fixtures.Alice.DID(), testLink),
   833  				fmt.Sprintf("    - Capability can not be (self) issued by '%s'", fixtures.Bob.DID()),
   834  				fmt.Sprintf(`    - Cannot derive {"can":"%s","with":"%s","nb":{"Link":{"/":"%s"},"Origin":null}} from delegated capabilities:`, storeAdd.Can(), fixtures.Alice.DID(), testLink),
   835  				fmt.Sprintf(`      - Constraint violation: Expected 'with: "%s"' instead got '%s'`, fixtures.Mallory.DID(), fixtures.Alice.DID()),
   836  			}, "\n")
   837  			require.Equal(t, msg, x.Error())
   838  		})
   839  
   840  		t.Run("invalid sub delegation", func(t *testing.T) {
   841  			prf, err := storeAdd.Delegate(
   842  				fixtures.Alice,
   843  				fixtures.Bob,
   844  				fixtures.Service.DID().String(),
   845  				storeAddCaveats{},
   846  			)
   847  			require.NoError(t, err)
   848  
   849  			dlg, err := storeAdd.Delegate(
   850  				fixtures.Bob,
   851  				fixtures.Mallory,
   852  				fixtures.Service.DID().String(),
   853  				storeAddCaveats{},
   854  				delegation.WithProof(delegation.FromDelegation(prf)),
   855  			)
   856  			require.NoError(t, err)
   857  
   858  			nb := storeAddCaveats{Link: testLink}
   859  			inv, err := storeAdd.Invoke(
   860  				fixtures.Mallory,
   861  				fixtures.Service,
   862  				fixtures.Service.DID().String(),
   863  				nb,
   864  				delegation.WithProof(delegation.FromDelegation(dlg)),
   865  			)
   866  			require.NoError(t, err)
   867  
   868  			vctx := NewValidationContext(
   869  				fixtures.Service.Verifier(),
   870  				storeAdd,
   871  				IsSelfIssued,
   872  				validateAuthOk,
   873  				ProofUnavailable,
   874  				parseEdPrincipal,
   875  				FailDIDKeyResolution,
   876  				NotExpiredNotTooEarly,
   877  			)
   878  
   879  			cstr := fmt.Sprintf(`{"can":"%s","with":"%s","nb":{"Link":{"/":"%s"},"Origin":null}}`, storeAdd.Can(), fixtures.Service.DID(), testLink)
   880  			a, x := Access(t.Context(), inv, vctx)
   881  			require.Nil(t, a)
   882  			require.Error(t, x)
   883  			require.Equal(t, x.Name(), "Unauthorized")
   884  			msg := strings.Join([]string{
   885  				fmt.Sprintf("Claim %s is not authorized", storeAdd),
   886  				fmt.Sprintf(`  - Capability %s is not authorized because:`, cstr),
   887  				fmt.Sprintf("    - Capability can not be (self) issued by '%s'", fixtures.Mallory.DID()),
   888  				fmt.Sprintf(`    - Capability %s is not authorized because:`, cstr),
   889  				fmt.Sprintf(`      - Capability can not be (self) issued by '%s'`, fixtures.Bob.DID()),
   890  				fmt.Sprintf(`      - Capability %s is not authorized because:`, cstr),
   891  				fmt.Sprintf(`        - Capability can not be (self) issued by '%s'`, fixtures.Alice.DID()),
   892  				"        - Delegated capability not found",
   893  			}, "\n")
   894  			require.Equal(t, msg, x.Error())
   895  		})
   896  
   897  		t.Run("principal alignment", func(t *testing.T) {
   898  			prf, err := storeAdd.Delegate(
   899  				fixtures.Alice,
   900  				fixtures.Bob,
   901  				fixtures.Alice.DID().String(),
   902  				storeAddCaveats{},
   903  			)
   904  			require.NoError(t, err)
   905  
   906  			nb := storeAddCaveats{Link: testLink}
   907  			inv, err := storeAdd.Invoke(
   908  				fixtures.Mallory,
   909  				fixtures.Service,
   910  				fixtures.Alice.DID().String(),
   911  				nb,
   912  				delegation.WithProof(delegation.FromDelegation(prf)),
   913  			)
   914  			require.NoError(t, err)
   915  
   916  			vctx := NewValidationContext(
   917  				fixtures.Service.Verifier(),
   918  				storeAdd,
   919  				IsSelfIssued,
   920  				validateAuthOk,
   921  				ProofUnavailable,
   922  				parseEdPrincipal,
   923  				FailDIDKeyResolution,
   924  				NotExpiredNotTooEarly,
   925  			)
   926  
   927  			cstr := fmt.Sprintf(`{"can":"%s","with":"%s","nb":{"Link":{"/":"%s"},"Origin":null}}`, storeAdd.Can(), fixtures.Alice.DID(), testLink)
   928  			a, x := Access(t.Context(), inv, vctx)
   929  			require.Nil(t, a)
   930  			require.Error(t, x)
   931  			require.Equal(t, x.Name(), "Unauthorized")
   932  			msg := strings.Join([]string{
   933  				fmt.Sprintf("Claim %s is not authorized", storeAdd),
   934  				fmt.Sprintf(`  - Capability %s is not authorized because:`, cstr),
   935  				fmt.Sprintf("    - Capability can not be (self) issued by '%s'", fixtures.Mallory.DID()),
   936  				fmt.Sprintf(`    - Capability can not be derived from prf: %s because:`, prf.Link()),
   937  				fmt.Sprintf(`      - Delegation audience is '%s' instead of '%s'`, fixtures.Bob.DID(), fixtures.Mallory.DID()),
   938  			}, "\n")
   939  			require.Equal(t, msg, x.Error())
   940  		})
   941  
   942  		t.Run("invalid delegation chain", func(t *testing.T) {
   943  			space := fixtures.Alice
   944  
   945  			prf, err := storeAdd.Delegate(
   946  				space,
   947  				fixtures.Service,
   948  				space.DID().String(),
   949  				storeAddCaveats{},
   950  			)
   951  			require.NoError(t, err)
   952  
   953  			nb := storeAddCaveats{Link: testLink}
   954  			inv, err := storeAdd.Invoke(
   955  				fixtures.Bob,
   956  				fixtures.Service,
   957  				space.DID().String(),
   958  				nb,
   959  				delegation.WithProof(delegation.FromDelegation(prf)),
   960  			)
   961  			require.NoError(t, err)
   962  
   963  			vctx := NewValidationContext(
   964  				fixtures.Service.Verifier(),
   965  				storeAdd,
   966  				IsSelfIssued,
   967  				validateAuthOk,
   968  				ProofUnavailable,
   969  				parseEdPrincipal,
   970  				FailDIDKeyResolution,
   971  				NotExpiredNotTooEarly,
   972  			)
   973  
   974  			cstr := fmt.Sprintf(`{"can":"%s","with":"%s","nb":{"Link":{"/":"%s"},"Origin":null}}`, storeAdd.Can(), space.DID(), testLink)
   975  			a, x := Access(t.Context(), inv, vctx)
   976  			require.Nil(t, a)
   977  			require.Error(t, x)
   978  			require.Equal(t, x.Name(), "Unauthorized")
   979  			msg := strings.Join([]string{
   980  				fmt.Sprintf("Claim %s is not authorized", storeAdd),
   981  				fmt.Sprintf(`  - Capability %s is not authorized because:`, cstr),
   982  				fmt.Sprintf("    - Capability can not be (self) issued by '%s'", fixtures.Bob.DID()),
   983  				fmt.Sprintf(`    - Capability can not be derived from prf: %s because:`, prf.Link()),
   984  				fmt.Sprintf(`      - Delegation audience is '%s' instead of '%s'`, fixtures.Service.DID(), fixtures.Bob.DID()),
   985  			}, "\n")
   986  			require.Equal(t, msg, x.Error())
   987  		})
   988  	})
   989  }
   990  
   991  func TestClaim(t *testing.T) {
   992  	t.Run("without a proof", func(t *testing.T) {
   993  		dlg, err := storeAdd.Delegate(
   994  			fixtures.Alice,
   995  			fixtures.Service,
   996  			fixtures.Alice.DID().String(),
   997  			storeAddCaveats{},
   998  		)
   999  		require.NoError(t, err)
  1000  
  1001  		vctx := NewValidationContext(
  1002  			fixtures.Service.Verifier(),
  1003  			storeAdd,
  1004  			IsSelfIssued,
  1005  			validateAuthOk,
  1006  			ProofUnavailable,
  1007  			parseEdPrincipal,
  1008  			FailDIDKeyResolution,
  1009  			NotExpiredNotTooEarly,
  1010  		)
  1011  
  1012  		a, x := Claim(t.Context(), storeAdd, []delegation.Proof{delegation.FromLink(dlg.Link())}, vctx)
  1013  		require.Nil(t, a)
  1014  		require.Error(t, x)
  1015  		require.Equal(t, x.Name(), "Unauthorized")
  1016  		msg := strings.Join([]string{
  1017  			fmt.Sprintf("Claim %s is not authorized", storeAdd),
  1018  			fmt.Sprintf(`  - Linked proof "%s" is not included and could not be resolved`, dlg.Link()),
  1019  			`    - Proof resolution failed with: no proof resolver configured`,
  1020  		}, "\n")
  1021  		require.Equal(t, msg, x.Error())
  1022  	})
  1023  
  1024  	t.Run("mismatched signature", func(t *testing.T) {
  1025  		svcdid, err := did.Parse("did:web:w3.storage")
  1026  		require.NoError(t, err)
  1027  
  1028  		old, err := signer.Wrap(fixtures.Alice, svcdid)
  1029  		require.NoError(t, err)
  1030  
  1031  		new, err := signer.Wrap(fixtures.Bob, svcdid)
  1032  		require.NoError(t, err)
  1033  
  1034  		dlg, err := storeAdd.Delegate(
  1035  			old,
  1036  			old,
  1037  			old.DID().String(),
  1038  			storeAddCaveats{Link: testLink},
  1039  		)
  1040  		require.NoError(t, err)
  1041  
  1042  		vctx := NewValidationContext(
  1043  			new.Verifier(),
  1044  			storeAdd,
  1045  			IsSelfIssued,
  1046  			validateAuthOk,
  1047  			ProofUnavailable,
  1048  			parseEdPrincipal,
  1049  			FailDIDKeyResolution,
  1050  			NotExpiredNotTooEarly,
  1051  		)
  1052  
  1053  		a, x := Claim(t.Context(), storeAdd, []delegation.Proof{delegation.FromDelegation(dlg)}, vctx)
  1054  		require.Nil(t, a)
  1055  		require.Error(t, x)
  1056  		require.Equal(t, x.Name(), "Unauthorized")
  1057  		msg := strings.Join([]string{
  1058  			fmt.Sprintf("Claim %s is not authorized", storeAdd),
  1059  			fmt.Sprintf(`  - Proof %s issued by %s does not have a valid signature from %s`, dlg.Link(), new.DID(), new.DID()),
  1060  			`    ℹ️ Issuer probably signed with a different key, which got rotated, invalidating delegations that were issued with prior keys`,
  1061  		}, "\n")
  1062  		require.Equal(t, msg, x.Error())
  1063  	})
  1064  }
  1065  
  1066  func TestIsSelfIssued(t *testing.T) {
  1067  	cap := ucan.NewCapability("upload/add", fixtures.Alice.DID().String(), struct{}{})
  1068  
  1069  	canIssue := IsSelfIssued(cap, fixtures.Alice.DID())
  1070  	if canIssue == false {
  1071  		t.Fatal("capability self issued by alice")
  1072  	}
  1073  
  1074  	canIssue = IsSelfIssued(cap, fixtures.Bob.DID())
  1075  	if canIssue == true {
  1076  		t.Fatal("capability not self issued by bob")
  1077  	}
  1078  }