Integrate go-varsig and go-did-it
- go-varsig provides a varsig V1 implementation - go-did-it provides a complete and extensible DID implementation
This commit is contained in:
@@ -16,7 +16,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
"github.com/MetaMask/go-did-it"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/ucan-wg/go-ucan/pkg/meta"
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy"
|
||||
@@ -102,7 +103,7 @@ func Root(iss did.DID, aud did.DID, cmd command.Command, pol policy.Policy, opts
|
||||
//
|
||||
// You can read it as "(issuer) allows (audience) to perform (cmd+pol) on anything".
|
||||
func Powerline(iss did.DID, aud did.DID, cmd command.Command, pol policy.Policy, opts ...Option) (*Token, error) {
|
||||
return New(iss, aud, cmd, pol, did.Undef, opts...)
|
||||
return New(iss, aud, cmd, pol, nil, opts...)
|
||||
}
|
||||
|
||||
// Issuer returns the did.DID representing the Token's issuer.
|
||||
@@ -156,12 +157,12 @@ func (t *Token) Expiration() *time.Time {
|
||||
|
||||
// IsRoot tells if the token is a root delegation.
|
||||
func (t *Token) IsRoot() bool {
|
||||
return t.issuer == t.subject
|
||||
return t.issuer.Equal(t.subject)
|
||||
}
|
||||
|
||||
// IsPowerline tells if the token is a powerline delegation.
|
||||
func (t *Token) IsPowerline() bool {
|
||||
return t.subject == did.Undef
|
||||
return t.subject == nil
|
||||
}
|
||||
|
||||
// IsValidNow verifies that the token can be used at the current time, based on expiration or "not before" fields.
|
||||
@@ -189,7 +190,7 @@ func (t *Token) String() string {
|
||||
switch {
|
||||
case t.issuer == t.subject:
|
||||
kind = " (root delegation)"
|
||||
case t.subject == did.Undef:
|
||||
case t.subject == nil:
|
||||
kind = " (powerline delegation)"
|
||||
default:
|
||||
kind = " (normal delegation)"
|
||||
@@ -212,7 +213,7 @@ func (t *Token) validate() error {
|
||||
var errs error
|
||||
|
||||
requiredDID := func(id did.DID, fieldname string) {
|
||||
if !id.Defined() {
|
||||
if id == nil {
|
||||
errs = errors.Join(errs, fmt.Errorf(`a valid did is required for %s: %s`, fieldname, id.String()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,28 @@
|
||||
package delegation_test
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"gotest.tools/v3/golden"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/did/didtest"
|
||||
"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/internal/didtest"
|
||||
)
|
||||
|
||||
//go:embed testdata/new.dagjson
|
||||
var newDagJson []byte
|
||||
|
||||
//go:embed testdata/powerline.dagjson
|
||||
var powerlineDagJson []byte
|
||||
|
||||
//go:embed testdata/root.dagjson
|
||||
var rootDagJson []byte
|
||||
|
||||
const (
|
||||
nonce = "6roDhGi0kiNriQAz7J3d+bOeoI/tj8ENikmQNbtjnD0"
|
||||
|
||||
@@ -63,12 +72,10 @@ func TestConstructors(t *testing.T) {
|
||||
data, err := tkn.ToDagJson(didtest.PersonaAlice.PrivKey())
|
||||
require.NoError(t, err)
|
||||
|
||||
golden.Assert(t, string(data), "new.dagjson")
|
||||
require.Equal(t, newDagJson, data)
|
||||
})
|
||||
|
||||
t.Run("Root", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tkn, err := delegation.Root(didtest.PersonaAlice.DID(), didtest.PersonaBob.DID(), cmd, pol,
|
||||
delegation.WithNonce([]byte(nonce)),
|
||||
delegation.WithExpiration(exp),
|
||||
@@ -83,12 +90,10 @@ func TestConstructors(t *testing.T) {
|
||||
data, err := tkn.ToDagJson(didtest.PersonaAlice.PrivKey())
|
||||
require.NoError(t, err)
|
||||
|
||||
golden.Assert(t, string(data), "root.dagjson")
|
||||
require.Equal(t, rootDagJson, data)
|
||||
})
|
||||
|
||||
t.Run("Powerline", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tkn, err := delegation.Powerline(didtest.PersonaAlice.DID(), didtest.PersonaBob.DID(), cmd, pol,
|
||||
delegation.WithNonce([]byte(nonce)),
|
||||
delegation.WithExpiration(exp),
|
||||
@@ -103,7 +108,7 @@ func TestConstructors(t *testing.T) {
|
||||
data, err := tkn.ToDagJson(didtest.PersonaAlice.PrivKey())
|
||||
require.NoError(t, err)
|
||||
|
||||
golden.Assert(t, string(data), "powerline.dagjson")
|
||||
require.Equal(t, powerlineDagJson, data)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -9,16 +9,17 @@ import (
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/MetaMask/go-did-it"
|
||||
didkeyctl "github.com/MetaMask/go-did-it/controller/did-key"
|
||||
"github.com/MetaMask/go-did-it/crypto"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/libp2p/go-libp2p/core/crypto"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
"github.com/ucan-wg/go-ucan/did/didtest"
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy"
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy/policytest"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation/delegationtest"
|
||||
"github.com/ucan-wg/go-ucan/token/internal/didtest"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -30,7 +31,7 @@ const (
|
||||
var constantNonce = []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b}
|
||||
|
||||
type newDelegationParams struct {
|
||||
privKey crypto.PrivKey // iss
|
||||
privKey crypto.PrivateKeySigningBytes // iss
|
||||
aud did.DID
|
||||
cmd command.Command
|
||||
pol policy.Policy
|
||||
@@ -157,10 +158,7 @@ func (g *generator) chainPersonas(personas []didtest.Persona, acc acc, vari vari
|
||||
func (g *generator) createDelegation(params newDelegationParams, name string, vari variant) (cid.Cid, error) {
|
||||
vari.variant(¶ms)
|
||||
|
||||
issDID, err := did.FromPrivKey(params.privKey)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
issDID := didkeyctl.FromPrivateKey(params.privKey)
|
||||
|
||||
tkn, err := delegation.New(issDID, params.aud, params.cmd, params.pol, params.sub, params.opts...)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/ucan-wg/go-ucan/did/didtest"
|
||||
"github.com/ucan-wg/go-ucan/token/internal/didtest"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
_ "github.com/MetaMask/go-did-it/verifiers/did-key"
|
||||
"github.com/ipfs/go-cid"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
|
||||
@@ -9,161 +9,161 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
TokenAliceBobCID = cid.MustParse("bafyreicidrwvmac5lvjypucgityrtjsknojraio7ujjli4r5eyby66wjzm")
|
||||
TokenAliceBobCID = cid.MustParse("bafyreifa35rjstdm37cjudzs72ab22rnh5blny725khtapox63fnsj6pbe")
|
||||
TokenAliceBobSealed = mustGetBundle(TokenAliceBobCID).Sealed
|
||||
TokenAliceBobBundle = mustGetBundle(TokenAliceBobCID)
|
||||
TokenAliceBob = mustGetBundle(TokenAliceBobCID).Decoded
|
||||
)
|
||||
|
||||
var (
|
||||
TokenBobCarolCID = cid.MustParse("bafyreihxv2uhq43oxllzs2xfvxst7wtvvvl7pohb2chcz6hjvfv2ntea5u")
|
||||
TokenBobCarolCID = cid.MustParse("bafyreiaysaafvfplhjsjywaqfletlr2sziui3sekkczwp2srszdoezwc7u")
|
||||
TokenBobCarolSealed = mustGetBundle(TokenBobCarolCID).Sealed
|
||||
TokenBobCarolBundle = mustGetBundle(TokenBobCarolCID)
|
||||
TokenBobCarol = mustGetBundle(TokenBobCarolCID).Decoded
|
||||
)
|
||||
|
||||
var (
|
||||
TokenCarolDanCID = cid.MustParse("bafyreihclsgiroazq3heqdswvj2cafwqbpboicq7immo65scl7ahktpsdq")
|
||||
TokenCarolDanCID = cid.MustParse("bafyreibwpzxcvrere7g5riv3dhza4xibrvulxcvj2nwxu6ozp572o2sbfa")
|
||||
TokenCarolDanSealed = mustGetBundle(TokenCarolDanCID).Sealed
|
||||
TokenCarolDanBundle = mustGetBundle(TokenCarolDanCID)
|
||||
TokenCarolDan = mustGetBundle(TokenCarolDanCID).Decoded
|
||||
)
|
||||
|
||||
var (
|
||||
TokenDanErinCID = cid.MustParse("bafyreicja6ihewy64p3ake56xukotafjlkh4uqep2qhj52en46zzfwby3e")
|
||||
TokenDanErinCID = cid.MustParse("bafyreichxodkcokcbvomxhmlf3g3j3zokruxvca63itynf7ib3hsmi4hla")
|
||||
TokenDanErinSealed = mustGetBundle(TokenDanErinCID).Sealed
|
||||
TokenDanErinBundle = mustGetBundle(TokenDanErinCID)
|
||||
TokenDanErin = mustGetBundle(TokenDanErinCID).Decoded
|
||||
)
|
||||
|
||||
var (
|
||||
TokenErinFrankCID = cid.MustParse("bafyreicjlx3lobxm6hl5s4htd4ydwkkqeiou6rft4rnvulfdyoew565vka")
|
||||
TokenErinFrankCID = cid.MustParse("bafyreigg434khwxqyfasnk63pi542uwtbrlpfd5sgjfrddqqcba3tardlu")
|
||||
TokenErinFrankSealed = mustGetBundle(TokenErinFrankCID).Sealed
|
||||
TokenErinFrankBundle = mustGetBundle(TokenErinFrankCID)
|
||||
TokenErinFrank = mustGetBundle(TokenErinFrankCID).Decoded
|
||||
)
|
||||
|
||||
var (
|
||||
TokenCarolDan_InvalidExpandedCommandCID = cid.MustParse("bafyreid3m3pk53gqgp5rlzqhvpedbwsqbidqlp4yz64vknwbzj7bxrmsr4")
|
||||
TokenCarolDan_InvalidExpandedCommandCID = cid.MustParse("bafyreifxdve23izhlg7fhzqh32i6wxtw33rqidm3cb72pghxlovnensjwe")
|
||||
TokenCarolDan_InvalidExpandedCommandSealed = mustGetBundle(TokenCarolDan_InvalidExpandedCommandCID).Sealed
|
||||
TokenCarolDan_InvalidExpandedCommandBundle = mustGetBundle(TokenCarolDan_InvalidExpandedCommandCID)
|
||||
TokenCarolDan_InvalidExpandedCommand = mustGetBundle(TokenCarolDan_InvalidExpandedCommandCID).Decoded
|
||||
)
|
||||
|
||||
var (
|
||||
TokenDanErin_InvalidExpandedCommandCID = cid.MustParse("bafyreifn4sy5onwajx3kqvot5mib6m6xarzrqjozqbzgmzpmc5ox3g2uzm")
|
||||
TokenDanErin_InvalidExpandedCommandCID = cid.MustParse("bafyreieblevkqh2xnbr4x5mosvwv7rboat7ip6ucqvefb2pomrb4qjg4wu")
|
||||
TokenDanErin_InvalidExpandedCommandSealed = mustGetBundle(TokenDanErin_InvalidExpandedCommandCID).Sealed
|
||||
TokenDanErin_InvalidExpandedCommandBundle = mustGetBundle(TokenDanErin_InvalidExpandedCommandCID)
|
||||
TokenDanErin_InvalidExpandedCommand = mustGetBundle(TokenDanErin_InvalidExpandedCommandCID).Decoded
|
||||
)
|
||||
|
||||
var (
|
||||
TokenErinFrank_InvalidExpandedCommandCID = cid.MustParse("bafyreidmpgd36jznmq42bs34o4qi3fcbrsh4idkg6ejahudejzwb76fwxe")
|
||||
TokenErinFrank_InvalidExpandedCommandCID = cid.MustParse("bafyreia2ckukamhpeqpcdhevr75ym66vqem432dx4dwbdrdkh63pke3r3i")
|
||||
TokenErinFrank_InvalidExpandedCommandSealed = mustGetBundle(TokenErinFrank_InvalidExpandedCommandCID).Sealed
|
||||
TokenErinFrank_InvalidExpandedCommandBundle = mustGetBundle(TokenErinFrank_InvalidExpandedCommandCID)
|
||||
TokenErinFrank_InvalidExpandedCommand = mustGetBundle(TokenErinFrank_InvalidExpandedCommandCID).Decoded
|
||||
)
|
||||
|
||||
var (
|
||||
TokenCarolDan_ValidAttenuatedCommandCID = cid.MustParse("bafyreiekhtm237vyapk3c6voeb5lnz54crebqdqi3x4wn4u4cbrrhzsqfe")
|
||||
TokenCarolDan_ValidAttenuatedCommandCID = cid.MustParse("bafyreibftsq3mhjd5itg6jbos4doccwspvmnilpuduniv2el7mnl45z33y")
|
||||
TokenCarolDan_ValidAttenuatedCommandSealed = mustGetBundle(TokenCarolDan_ValidAttenuatedCommandCID).Sealed
|
||||
TokenCarolDan_ValidAttenuatedCommandBundle = mustGetBundle(TokenCarolDan_ValidAttenuatedCommandCID)
|
||||
TokenCarolDan_ValidAttenuatedCommand = mustGetBundle(TokenCarolDan_ValidAttenuatedCommandCID).Decoded
|
||||
)
|
||||
|
||||
var (
|
||||
TokenDanErin_ValidAttenuatedCommandCID = cid.MustParse("bafyreicrvzqferyy7rgo75l5rn6r2nl7zyeexxjmu3dm4ff7rn2coblj4y")
|
||||
TokenDanErin_ValidAttenuatedCommandCID = cid.MustParse("bafyreig5gsifqn3afnyaoqtjhnud2w7tnrda57eiel3d45vbp47hjyayey")
|
||||
TokenDanErin_ValidAttenuatedCommandSealed = mustGetBundle(TokenDanErin_ValidAttenuatedCommandCID).Sealed
|
||||
TokenDanErin_ValidAttenuatedCommandBundle = mustGetBundle(TokenDanErin_ValidAttenuatedCommandCID)
|
||||
TokenDanErin_ValidAttenuatedCommand = mustGetBundle(TokenDanErin_ValidAttenuatedCommandCID).Decoded
|
||||
)
|
||||
|
||||
var (
|
||||
TokenErinFrank_ValidAttenuatedCommandCID = cid.MustParse("bafyreie6fhspk53kplcc2phla3e7z7fzldlbmmpuwk6nbow5q6s2zjmw2q")
|
||||
TokenErinFrank_ValidAttenuatedCommandCID = cid.MustParse("bafyreiex2qyrw3xmye4fk5wdf6ukea3ojbokq77k6y566hupj6cicszi3e")
|
||||
TokenErinFrank_ValidAttenuatedCommandSealed = mustGetBundle(TokenErinFrank_ValidAttenuatedCommandCID).Sealed
|
||||
TokenErinFrank_ValidAttenuatedCommandBundle = mustGetBundle(TokenErinFrank_ValidAttenuatedCommandCID)
|
||||
TokenErinFrank_ValidAttenuatedCommand = mustGetBundle(TokenErinFrank_ValidAttenuatedCommandCID).Decoded
|
||||
)
|
||||
|
||||
var (
|
||||
TokenCarolDan_InvalidSubjectCID = cid.MustParse("bafyreifgksz6756if42tnc6rqsnbaa2u3fdrveo7ek44lnj2d64d5sw26u")
|
||||
TokenCarolDan_InvalidSubjectCID = cid.MustParse("bafyreie7snyknubaeh4ruig7wxvos54b7skmwsnegf6k23bsffs5btbiiq")
|
||||
TokenCarolDan_InvalidSubjectSealed = mustGetBundle(TokenCarolDan_InvalidSubjectCID).Sealed
|
||||
TokenCarolDan_InvalidSubjectBundle = mustGetBundle(TokenCarolDan_InvalidSubjectCID)
|
||||
TokenCarolDan_InvalidSubject = mustGetBundle(TokenCarolDan_InvalidSubjectCID).Decoded
|
||||
)
|
||||
|
||||
var (
|
||||
TokenDanErin_InvalidSubjectCID = cid.MustParse("bafyreibdwew5nypsxrm4fq73wu6hw3lgwwiolj3bi33xdrbgcf3ogm6fty")
|
||||
TokenDanErin_InvalidSubjectCID = cid.MustParse("bafyreicrfmlw4nqqk5t7j6r7ct3c7jlxeasyetb6fq3556vmtihcnbi54i")
|
||||
TokenDanErin_InvalidSubjectSealed = mustGetBundle(TokenDanErin_InvalidSubjectCID).Sealed
|
||||
TokenDanErin_InvalidSubjectBundle = mustGetBundle(TokenDanErin_InvalidSubjectCID)
|
||||
TokenDanErin_InvalidSubject = mustGetBundle(TokenDanErin_InvalidSubjectCID).Decoded
|
||||
)
|
||||
|
||||
var (
|
||||
TokenErinFrank_InvalidSubjectCID = cid.MustParse("bafyreicr364mj3n7x4iyhcksxypelktcqkkw3ptg7ggxtqegw3p3mr6zc4")
|
||||
TokenErinFrank_InvalidSubjectCID = cid.MustParse("bafyreietx2sjsm72sbgjnosdnejwlqc5dadlolh2bjl6ifxdllslup7ecm")
|
||||
TokenErinFrank_InvalidSubjectSealed = mustGetBundle(TokenErinFrank_InvalidSubjectCID).Sealed
|
||||
TokenErinFrank_InvalidSubjectBundle = mustGetBundle(TokenErinFrank_InvalidSubjectCID)
|
||||
TokenErinFrank_InvalidSubject = mustGetBundle(TokenErinFrank_InvalidSubjectCID).Decoded
|
||||
)
|
||||
|
||||
var (
|
||||
TokenCarolDan_InvalidExpiredCID = cid.MustParse("bafyreifyzm5jkx2sfu5awyndg3dn5zlg7sq5hssgfatafk62kiiilapnqe")
|
||||
TokenCarolDan_InvalidExpiredCID = cid.MustParse("bafyreifhdiem6r5fh3wwaa6uszqe5uashfwaastqjyvzssbiju4mravx3y")
|
||||
TokenCarolDan_InvalidExpiredSealed = mustGetBundle(TokenCarolDan_InvalidExpiredCID).Sealed
|
||||
TokenCarolDan_InvalidExpiredBundle = mustGetBundle(TokenCarolDan_InvalidExpiredCID)
|
||||
TokenCarolDan_InvalidExpired = mustGetBundle(TokenCarolDan_InvalidExpiredCID).Decoded
|
||||
)
|
||||
|
||||
var (
|
||||
TokenDanErin_InvalidExpiredCID = cid.MustParse("bafyreihhnisabmkofuk3qaw37leijxqjaz5or6v2cufjxwzdkvuvv2dzbq")
|
||||
TokenDanErin_InvalidExpiredCID = cid.MustParse("bafyreiff7fqutstzz2ep5gzs2mqnt4rka7ezscrzpijyc4jjeopqm7oxxq")
|
||||
TokenDanErin_InvalidExpiredSealed = mustGetBundle(TokenDanErin_InvalidExpiredCID).Sealed
|
||||
TokenDanErin_InvalidExpiredBundle = mustGetBundle(TokenDanErin_InvalidExpiredCID)
|
||||
TokenDanErin_InvalidExpired = mustGetBundle(TokenDanErin_InvalidExpiredCID).Decoded
|
||||
)
|
||||
|
||||
var (
|
||||
TokenErinFrank_InvalidExpiredCID = cid.MustParse("bafyreigeokaziviwm5kzmkpwesj3gta5k7zrd62x4a746fnrnkhvatwbna")
|
||||
TokenErinFrank_InvalidExpiredCID = cid.MustParse("bafyreigxfwcynzsy4v4po36s3mz5nf6mg4kpptw6rpld3sc3c3mthq4noa")
|
||||
TokenErinFrank_InvalidExpiredSealed = mustGetBundle(TokenErinFrank_InvalidExpiredCID).Sealed
|
||||
TokenErinFrank_InvalidExpiredBundle = mustGetBundle(TokenErinFrank_InvalidExpiredCID)
|
||||
TokenErinFrank_InvalidExpired = mustGetBundle(TokenErinFrank_InvalidExpiredCID).Decoded
|
||||
)
|
||||
|
||||
var (
|
||||
TokenCarolDan_InvalidInactiveCID = cid.MustParse("bafyreicea5y2nvlitvxijkupeavtg23i7ktjk3uejnaquguurzptiabk4u")
|
||||
TokenCarolDan_InvalidInactiveCID = cid.MustParse("bafyreifw4j2mxg4bovuhihvnhqgln7m57lvajt6fza4x4jc7sncyh66wxm")
|
||||
TokenCarolDan_InvalidInactiveSealed = mustGetBundle(TokenCarolDan_InvalidInactiveCID).Sealed
|
||||
TokenCarolDan_InvalidInactiveBundle = mustGetBundle(TokenCarolDan_InvalidInactiveCID)
|
||||
TokenCarolDan_InvalidInactive = mustGetBundle(TokenCarolDan_InvalidInactiveCID).Decoded
|
||||
)
|
||||
|
||||
var (
|
||||
TokenDanErin_InvalidInactiveCID = cid.MustParse("bafyreifsgqzkmxj2vexuts3z766mwcjreiisjg2jykyzf7tbj5sclutpvq")
|
||||
TokenDanErin_InvalidInactiveCID = cid.MustParse("bafyreigrrfefw4wvjitiheonigeldnem2loo4yp5cghdskteunuxyozkiq")
|
||||
TokenDanErin_InvalidInactiveSealed = mustGetBundle(TokenDanErin_InvalidInactiveCID).Sealed
|
||||
TokenDanErin_InvalidInactiveBundle = mustGetBundle(TokenDanErin_InvalidInactiveCID)
|
||||
TokenDanErin_InvalidInactive = mustGetBundle(TokenDanErin_InvalidInactiveCID).Decoded
|
||||
)
|
||||
|
||||
var (
|
||||
TokenErinFrank_InvalidInactiveCID = cid.MustParse("bafyreifbfegon24c6dndiqyktahzs65vhyasrygbw7nhsvojn6distsdre")
|
||||
TokenErinFrank_InvalidInactiveCID = cid.MustParse("bafyreicdlvnc7iinl2eay3xc3yvtmwezifvfxywjud3defssstusngt6du")
|
||||
TokenErinFrank_InvalidInactiveSealed = mustGetBundle(TokenErinFrank_InvalidInactiveCID).Sealed
|
||||
TokenErinFrank_InvalidInactiveBundle = mustGetBundle(TokenErinFrank_InvalidInactiveCID)
|
||||
TokenErinFrank_InvalidInactive = mustGetBundle(TokenErinFrank_InvalidInactiveCID).Decoded
|
||||
)
|
||||
|
||||
var (
|
||||
TokenCarolDan_ValidExamplePolicyCID = cid.MustParse("bafyreibtfrp2njnkjrcuhxd4ebaecmpcql5knek2h2j2fjzu2sij2tv6ei")
|
||||
TokenCarolDan_ValidExamplePolicyCID = cid.MustParse("bafyreidvi5y42befcnb2qud23yt5memf4itn2v6xmw7gvdpihbnanvgqly")
|
||||
TokenCarolDan_ValidExamplePolicySealed = mustGetBundle(TokenCarolDan_ValidExamplePolicyCID).Sealed
|
||||
TokenCarolDan_ValidExamplePolicyBundle = mustGetBundle(TokenCarolDan_ValidExamplePolicyCID)
|
||||
TokenCarolDan_ValidExamplePolicy = mustGetBundle(TokenCarolDan_ValidExamplePolicyCID).Decoded
|
||||
)
|
||||
|
||||
var (
|
||||
TokenDanErin_ValidExamplePolicyCID = cid.MustParse("bafyreidxfwbkzujpu7ivulkc7b6ff4cpbzrkeklmxqvyhhmkmym5b45e2e")
|
||||
TokenDanErin_ValidExamplePolicyCID = cid.MustParse("bafyreid3mr53fjsntyyfbbzjx2n32d7atj3v4ztfufdkamrodm6kcjww64")
|
||||
TokenDanErin_ValidExamplePolicySealed = mustGetBundle(TokenDanErin_ValidExamplePolicyCID).Sealed
|
||||
TokenDanErin_ValidExamplePolicyBundle = mustGetBundle(TokenDanErin_ValidExamplePolicyCID)
|
||||
TokenDanErin_ValidExamplePolicy = mustGetBundle(TokenDanErin_ValidExamplePolicyCID).Decoded
|
||||
)
|
||||
|
||||
var (
|
||||
TokenErinFrank_ValidExamplePolicyCID = cid.MustParse("bafyreiatkvtvgakqcrdk6vgrv7tbq5rbeiqct52ep4plcftp2agffjyvp4")
|
||||
TokenErinFrank_ValidExamplePolicyCID = cid.MustParse("bafyreiegytubdwqnl3gd7dddfmqhr4kyo4sb5446kjyqvvolk2wmwum6ae")
|
||||
TokenErinFrank_ValidExamplePolicySealed = mustGetBundle(TokenErinFrank_ValidExamplePolicyCID).Sealed
|
||||
TokenErinFrank_ValidExamplePolicyBundle = mustGetBundle(TokenErinFrank_ValidExamplePolicyCID)
|
||||
TokenErinFrank_ValidExamplePolicy = mustGetBundle(TokenErinFrank_ValidExamplePolicyCID).Decoded
|
||||
|
||||
@@ -13,11 +13,11 @@ import (
|
||||
"github.com/ipld/go-ipld-prime/codec/dagcbor"
|
||||
"github.com/ipld/go-ipld-prime/codec/dagjson"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/did/didtest"
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy"
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||
"github.com/ucan-wg/go-ucan/token/internal/didtest"
|
||||
"github.com/ucan-wg/go-ucan/token/internal/envelope"
|
||||
)
|
||||
|
||||
@@ -57,37 +57,37 @@ func ExampleNew() {
|
||||
|
||||
// Example output:
|
||||
//
|
||||
// issDid: did:key:z6MkvJPmEZZYbgiw1ouT1oouTsTFBHJSts9ophVsNgcRmYxU
|
||||
// issDid: did:key:z6Mkf4WtCwPDtamsZvBJA4eSVcE7vZuRPy5Skm4HaoQv81i1
|
||||
//
|
||||
// CID (base58BTC): zdpuAsqfZkgg2jgZyob23sq1J9xwtf9PHgt1PsskVCMq7Vvxk
|
||||
// CID (base58BTC): zdpuB1bzMdGAtzBej9ZBJbW3ppsjP2Cf9KZ8xMjdhUjaf3vz1
|
||||
//
|
||||
// DAG-CBOR (base64) out: lhAOnjc0bPptlI5MxRBrIK3YmAP1CxKfXOPkz6MHt/UJCx2gCN+6gXZX2N+BIJvmy8XmAO5sT2GYimiV7HlJH1AA6JhaEQ07QFxc3VjYW4vZGxnQDEuMC4wLXJjLjGpY2F1ZHg4ZGlkOmtleTp6Nk1rZ3VwY2hoNUh3dUhhaFM3WXN5RThiTHVhMU1yOHAyaUtOUmh5dlN2UkFzOW5jY21kaC9mb28vYmFyY2V4cBpnROP/Y2lzc3g4ZGlkOmtleTp6Nk1rdkpQbUVaWlliZ2l3MW91VDFvb3VUc1RGQkhKU3RzOW9waFZzTmdjUm1ZeFVjbmJmGmdE1itjcG9sg4NiPT1nLnN0YXR1c2VkcmFmdINjYWxsaS5yZXZpZXdlcoNkbGlrZWYuZW1haWxtKkBleGFtcGxlLmNvbYNjYW55ZS50YWdzgmJvcoKDYj09YS5kbmV3c4NiPT1hLmVwcmVzc2NzdWJ4OGRpZDprZXk6ejZNa3V1a2syc2tEWExRbjdOSzNFaDlqTW5kWWZ2REJ4eGt0Z3BpZEpBcWI3TTNwZG1ldGGiY2Jhehh7Y2Zvb2NiYXJlbm9uY2VMv+Diy6GExIuM1eX4
|
||||
// DAG-CBOR (base64) out: glhAQhZTTb4U1rin/oLFre618Ol5/leaP758T6EkHYfQaNmFYad7yVTer8bbM1Zp2LxrV3eEYeWkHeL3F0V3zWC/CaJhaEg0Ae0B7QETcXN1Y2FuL2RsZ0AxLjAuMC1yYy4xqWNhdWR4OGRpZDprZXk6ejZNa2pXRlU4SHRmTXF1V241TUVROXIyMnpXcXlDTjF2YXpHdzZnNnB6Y2l6aU5WY2NtZGgvZm9vL2JhcmNleHAaaItoW2Npc3N4OGRpZDprZXk6ejZNa2Y0V3RDd1BEdGFtc1p2QkpBNGVTVmNFN3ZadVJQeTVTa200SGFvUXY4MWkxY25iZhpoi1qHY3BvbIODYj09Zy5zdGF0dXNlZHJhZnSDY2FsbGkucmV2aWV3ZXKDZGxpa2VmLmVtYWlsbSpAZXhhbXBsZS5jb22DY2FueWUudGFnc4Jib3KCg2I9PWEuZG5ld3ODYj09YS5lcHJlc3Njc3VieDhkaWQ6a2V5Ono2TWtuVXoxbVNqNHB2UzZhVVVIZWtDSGRVUHY3SEJoRHlEQlpRMlczVnVqYzVxQ2RtZXRhomNiYXoYe2Nmb29jYmFyZW5vbmNlTHa1D4DJ78LvscA/hg==
|
||||
// Converted to DAG-JSON out:
|
||||
// [
|
||||
// {
|
||||
// "/": {
|
||||
// "bytes": "5rvl8uKmDVGvAVSt4m/0MGiXl9dZwljJJ9m2qHCoIB617l26UvMxyH5uvN9hM7ozfVATiq4mLhoGgm9IGnEEAg"
|
||||
// "bytes": "QhZTTb4U1rin/oLFre618Ol5/leaP758T6EkHYfQaNmFYad7yVTer8bbM1Zp2LxrV3eEYeWkHeL3F0V3zWC/CQ"
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "h": {
|
||||
// "/": {
|
||||
// "bytes": "NO0BcQ"
|
||||
// "bytes": "NAHtAe0BE3E"
|
||||
// }
|
||||
// },
|
||||
// "ucan/dlg@1.0.0-rc.1": {
|
||||
// "aud": "did:key:z6Mkq5YmbJcTrPExNDi26imrTCpKhepjBFBSHqrBDN2ArPkv",
|
||||
// "aud": "did:key:z6MkjWFU8HtfMquWn5MEQ9r22zWqyCN1vazGw6g6pzciziNV",
|
||||
// "cmd": "/foo/bar",
|
||||
// "exp": 1728933098,
|
||||
// "iss": "did:key:z6MkhVFznPeR572rTK51UjoTNpnF8cxuWfPm9oBMPr7y8ABe",
|
||||
// "exp": 1753966683,
|
||||
// "iss": "did:key:z6Mkf4WtCwPDtamsZvBJA4eSVcE7vZuRPy5Skm4HaoQv81i1",
|
||||
// "meta": {
|
||||
// "baz": 123,
|
||||
// "foo": "bar"
|
||||
// },
|
||||
// "nbf": 1728929558,
|
||||
// "nbf": 1753963143,
|
||||
// "nonce": {
|
||||
// "/": {
|
||||
// "bytes": "u0HMgJ5Y+M84I/66"
|
||||
// "bytes": "drUPgMnvwu+xwD+G"
|
||||
// }
|
||||
// },
|
||||
// "pol": [
|
||||
@@ -125,7 +125,7 @@ func ExampleNew() {
|
||||
// ]
|
||||
// ]
|
||||
// ],
|
||||
// "sub": "did:key:z6MktA1uBdCpq4uJBqE9jjMiLyxZBg9a6xgPPKJjMqss6Zc2"
|
||||
// "sub": "did:key:z6MknUz1mSj4pvS6aUUHekCHdUPv7HBhDyDBZQ2W3Vujc5qC"
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
@@ -166,38 +166,36 @@ func ExampleRoot() {
|
||||
|
||||
// Example output:
|
||||
//
|
||||
// issDid: did:key:z6MknWJqz17Y4AfsXSJUFKomuBR4GTkViM7kJYutzTMkCyFF
|
||||
//
|
||||
// CID (base58BTC): zdpuAkwYz8nY7uU8j3F6wVTfFY1VEoExwvUAYBEwRWfTozddE
|
||||
//
|
||||
// DAG-CBOR (base64) out: glhAVpW67FJ+myNi+azvnw2jivuiqXTuMrDZI2Qdaa8jE1Oi3mkjnm7DyqSQGADcomcuDslMWKmJ+OIyvbPG5PtSA6JhaEQ07QFxc3VjYW4vZGxnQDEuMC4wLXJjLjGpY2F1ZHg4ZGlkOmtleTp6Nk1rdkpQbUVaWlliZ2l3MW91VDFvb3VUc1RGQkhKU3RzOW9waFZzTmdjUm1ZeFVjY21kaC9mb28vYmFyY2V4cBpnROVoY2lzc3g4ZGlkOmtleTp6Nk1rdXVrazJza0RYTFFuN05LM0VoOWpNbmRZZnZEQnh4a3RncGlkSkFxYjdNM3BjbmJmGmdE15RjcG9sg4NiPT1nLnN0YXR1c2VkcmFmdINjYWxsaS5yZXZpZXdlcoNkbGlrZWYuZW1haWxtKkBleGFtcGxlLmNvbYNjYW55ZS50YWdzgmJvcoKDYj09YS5kbmV3c4NiPT1hLmVwcmVzc2NzdWJ4OGRpZDprZXk6ejZNa3V1a2syc2tEWExRbjdOSzNFaDlqTW5kWWZ2REJ4eGt0Z3BpZEpBcWI3TTNwZG1ldGGiY2Jhehh7Y2Zvb2NiYXJlbm9uY2VMwzDc03WBciJIGPWG
|
||||
// DAG-CBOR (base64) out: glhATIXb2wnBByq/llaQ4RLoWTxheAwamCNo2sKL8SQJbq0EVRvdfUQDKNpuMVkvtyUR6tUdZlKv1BcXjfGEaF2XAKJhaEg0Ae0B7QETcXN1Y2FuL2RsZ0AxLjAuMC1yYy4xqWNhdWR4OGRpZDprZXk6ejZNa2Y0V3RDd1BEdGFtc1p2QkpBNGVTVmNFN3ZadVJQeTVTa200SGFvUXY4MWkxY2NtZGgvZm9vL2JhcmNleHAaaItnfWNpc3N4OGRpZDprZXk6ejZNa25VejFtU2o0cHZTNmFVVUhla0NIZFVQdjdIQmhEeURCWlEyVzNWdWpjNXFDY25iZhpoi1mpY3BvbIODYj09Zy5zdGF0dXNlZHJhZnSDY2FsbGkucmV2aWV3ZXKDZGxpa2VmLmVtYWlsbSpAZXhhbXBsZS5jb22DY2FueWUudGFnc4Jib3KCg2I9PWEuZG5ld3ODYj09YS5lcHJlc3Njc3VieDhkaWQ6a2V5Ono2TWtuVXoxbVNqNHB2UzZhVVVIZWtDSGRVUHY3SEJoRHlEQlpRMlczVnVqYzVxQ2RtZXRhomNiYXoYe2Nmb29jYmFyZW5vbmNlTGxwbjHKcevt5dZ0Xg==
|
||||
//
|
||||
// Converted to DAG-JSON out:
|
||||
// [
|
||||
// {
|
||||
// "/": {
|
||||
// "bytes": "VpW67FJ+myNi+azvnw2jivuiqXTuMrDZI2Qdaa8jE1Oi3mkjnm7DyqSQGADcomcuDslMWKmJ+OIyvbPG5PtSAw"
|
||||
// "bytes": "TIXb2wnBByq/llaQ4RLoWTxheAwamCNo2sKL8SQJbq0EVRvdfUQDKNpuMVkvtyUR6tUdZlKv1BcXjfGEaF2XAA"
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "h": {
|
||||
// "/": {
|
||||
// "bytes": "NO0BcQ"
|
||||
// "bytes": "NAHtAe0BE3E"
|
||||
// }
|
||||
// },
|
||||
// "ucan/dlg@1.0.0-rc.1": {
|
||||
// "aud": "did:key:z6MkvJPmEZZYbgiw1ouT1oouTsTFBHJSts9ophVsNgcRmYxU",
|
||||
// "aud": "did:key:z6Mkf4WtCwPDtamsZvBJA4eSVcE7vZuRPy5Skm4HaoQv81i1",
|
||||
// "cmd": "/foo/bar",
|
||||
// "exp": 1732568424,
|
||||
// "iss": "did:key:z6Mkuukk2skDXLQn7NK3Eh9jMndYfvDBxxktgpidJAqb7M3p",
|
||||
// "exp": 1753966461,
|
||||
// "iss": "did:key:z6MknUz1mSj4pvS6aUUHekCHdUPv7HBhDyDBZQ2W3Vujc5qC",
|
||||
// "meta": {
|
||||
// "baz": 123,
|
||||
// "foo": "bar"
|
||||
// },
|
||||
// "nbf": 1732564884,
|
||||
// "nbf": 1753962921,
|
||||
// "nonce": {
|
||||
// "/": {
|
||||
// "bytes": "wzDc03WBciJIGPWG"
|
||||
// "bytes": "bHBuMcpx6+3l1nRe"
|
||||
// }
|
||||
// },
|
||||
// "pol": [
|
||||
@@ -235,7 +233,7 @@ func ExampleRoot() {
|
||||
// ]
|
||||
// ]
|
||||
// ],
|
||||
// "sub": "did:key:z6Mkuukk2skDXLQn7NK3Eh9jMndYfvDBxxktgpidJAqb7M3p"
|
||||
// "sub": "did:key:z6MknUz1mSj4pvS6aUUHekCHdUPv7HBhDyDBZQ2W3Vujc5qC"
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
@@ -244,7 +242,7 @@ func ExampleRoot() {
|
||||
// The following example demonstrates how to get a delegation.Token from
|
||||
// a DAG-CBOR []byte.
|
||||
func ExampleFromSealed() {
|
||||
const cborBase64 = "glhAmnAkgfjAx4SA5pzJmtaHRJtTGNpF1y6oqb4yhGoM2H2EUGbBYT4rVDjMKBgCjhdGHjipm00L8iR5SsQh3sIEBaJhaEQ07QFxc3VjYW4vZGxnQDEuMC4wLXJjLjGoY2F1ZHg4ZGlkOmtleTp6Nk1rcTVZbWJKY1RyUEV4TkRpMjZpbXJUQ3BLaGVwakJGQlNIcXJCRE4yQXJQa3ZjY21kaC9mb28vYmFyY2V4cPZjaXNzeDhkaWQ6a2V5Ono2TWtwem4ybjNaR1QyVmFxTUdTUUMzdHptelY0VFM5UzcxaUZzRFhFMVdub05IMmNwb2yDg2I9PWcuc3RhdHVzZWRyYWZ0g2NhbGxpLnJldmlld2Vyg2RsaWtlZi5lbWFpbG0qQGV4YW1wbGUuY29tg2NhbnllLnRhZ3OCYm9ygoNiPT1hLmRuZXdzg2I9PWEuZXByZXNzY3N1Yng4ZGlkOmtleTp6Nk1rdEExdUJkQ3BxNHVKQnFFOWpqTWlMeXhaQmc5YTZ4Z1BQS0pqTXFzczZaYzJkbWV0YaBlbm9uY2VMAAECAwQFBgcICQoL"
|
||||
const cborBase64 = "glhACBuW/rjVKyBPUVPxexsafwBe7y84k0yzywq3hQW2rs2TNmWA5wexAQ+jTkSQ07zhmQRA/wytBfqWkx24+sjlD6JhaEg0Ae0B7QETcXN1Y2FuL2RsZ0AxLjAuMC1yYy4xp2NhdWR4OGRpZDprZXk6ejZNa2pXRlU4SHRmTXF1V241TUVROXIyMnpXcXlDTjF2YXpHdzZnNnB6Y2l6aU5WY2NtZGgvZm9vL2JhcmNleHD2Y2lzc3g4ZGlkOmtleTp6Nk1rZjRXdEN3UER0YW1zWnZCSkE0ZVNWY0U3dlp1UlB5NVNrbTRIYW9RdjgxaTFjcG9sg4NiPT1nLnN0YXR1c2VkcmFmdINjYWxsaS5yZXZpZXdlcoNkbGlrZWYuZW1haWxtKkBleGFtcGxlLmNvbYNjYW55ZS50YWdzgmJvcoKDYj09YS5kbmV3c4NiPT1hLmVwcmVzc2NzdWJ4OGRpZDprZXk6ejZNa25VejFtU2o0cHZTNmFVVUhla0NIZFVQdjdIQmhEeURCWlEyVzNWdWpjNXFDZW5vbmNlTDVJDkO3LVTKnhxKKw=="
|
||||
|
||||
cborBytes, err := base64.StdEncoding.DecodeString(cborBase64)
|
||||
printThenPanicOnErr(err)
|
||||
@@ -264,10 +262,10 @@ func ExampleFromSealed() {
|
||||
fmt.Println("Expiration (exp):", tkn.Expiration())
|
||||
|
||||
// Output:
|
||||
// CID (base58BTC): zdpuAw26pFuvZa2Z9YAtpZZnWN6VmnRFr7Z8LVY5c7RVWoxGY
|
||||
// Issuer (iss): did:key:z6Mkpzn2n3ZGT2VaqMGSQC3tzmzV4TS9S71iFsDXE1WnoNH2
|
||||
// Audience (aud): did:key:z6Mkq5YmbJcTrPExNDi26imrTCpKhepjBFBSHqrBDN2ArPkv
|
||||
// Subject (sub): did:key:z6MktA1uBdCpq4uJBqE9jjMiLyxZBg9a6xgPPKJjMqss6Zc2
|
||||
// CID (base58BTC): zdpuAsgmtmC849BEApGCJm2fTSzwtHiAqQnuJCMBBBCbFiadd
|
||||
// Issuer (iss): did:key:z6Mkf4WtCwPDtamsZvBJA4eSVcE7vZuRPy5Skm4HaoQv81i1
|
||||
// Audience (aud): did:key:z6MkjWFU8HtfMquWn5MEQ9r22zWqyCN1vazGw6g6pzciziNV
|
||||
// Subject (sub): did:key:z6MknUz1mSj4pvS6aUUHekCHdUPv7HBhDyDBZQ2W3Vujc5qC
|
||||
// Command (cmd): /foo/bar
|
||||
// Policy (pol): [
|
||||
// ["==", ".status", "draft"],
|
||||
@@ -281,7 +279,7 @@ func ExampleFromSealed() {
|
||||
// ]]
|
||||
// ]
|
||||
// ]
|
||||
// Nonce (nonce): 000102030405060708090a0b
|
||||
// Nonce (nonce): 35490e43b72d54ca9e1c4a2b
|
||||
// Meta (meta): {}
|
||||
// NotBefore (nbf): <nil>
|
||||
// Expiration (exp): <nil>
|
||||
|
||||
@@ -1,25 +1,23 @@
|
||||
package delegation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/MetaMask/go-did-it/crypto"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipld/go-ipld-prime"
|
||||
"github.com/ipld/go-ipld-prime/codec"
|
||||
"github.com/ipld/go-ipld-prime/codec/dagcbor"
|
||||
"github.com/ipld/go-ipld-prime/codec/dagjson"
|
||||
"github.com/ipld/go-ipld-prime/datamodel"
|
||||
"github.com/libp2p/go-libp2p/core/crypto"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
"github.com/ucan-wg/go-ucan/token/internal/envelope"
|
||||
)
|
||||
|
||||
// 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 (t *Token) ToSealed(privKey crypto.PrivKey) ([]byte, cid.Cid, error) {
|
||||
func (t *Token) ToSealed(privKey crypto.PrivateKeySigningBytes) ([]byte, cid.Cid, error) {
|
||||
data, err := t.ToDagCbor(privKey)
|
||||
if err != nil {
|
||||
return nil, cid.Undef, err
|
||||
@@ -34,7 +32,7 @@ func (t *Token) ToSealed(privKey crypto.PrivKey) ([]byte, cid.Cid, error) {
|
||||
}
|
||||
|
||||
// ToSealedWriter is the same as ToSealed but accepts an io.Writer.
|
||||
func (t *Token) ToSealedWriter(w io.Writer, privKey crypto.PrivKey) (cid.Cid, error) {
|
||||
func (t *Token) ToSealedWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes) (cid.Cid, error) {
|
||||
cidWriter := envelope.NewCIDWriter(w)
|
||||
|
||||
if err := t.ToDagCborWriter(cidWriter, privKey); err != nil {
|
||||
@@ -81,7 +79,7 @@ func FromSealedReader(r io.Reader) (*Token, cid.Cid, error) {
|
||||
|
||||
// Encode marshals a Token to the format specified by the provided
|
||||
// codec.Encoder.
|
||||
func (t *Token) Encode(privKey crypto.PrivKey, encFn codec.Encoder) ([]byte, error) {
|
||||
func (t *Token) Encode(privKey crypto.PrivateKeySigningBytes, encFn codec.Encoder) ([]byte, error) {
|
||||
node, err := t.toIPLD(privKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -91,7 +89,7 @@ func (t *Token) Encode(privKey crypto.PrivKey, encFn codec.Encoder) ([]byte, err
|
||||
}
|
||||
|
||||
// EncodeWriter is the same as Encode, but accepts an io.Writer.
|
||||
func (t *Token) EncodeWriter(w io.Writer, privKey crypto.PrivKey, encFn codec.Encoder) error {
|
||||
func (t *Token) EncodeWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes, encFn codec.Encoder) error {
|
||||
node, err := t.toIPLD(privKey)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -101,22 +99,22 @@ func (t *Token) EncodeWriter(w io.Writer, privKey crypto.PrivKey, encFn codec.En
|
||||
}
|
||||
|
||||
// ToDagCbor marshals the Token to the DAG-CBOR format.
|
||||
func (t *Token) ToDagCbor(privKey crypto.PrivKey) ([]byte, error) {
|
||||
func (t *Token) ToDagCbor(privKey crypto.PrivateKeySigningBytes) ([]byte, error) {
|
||||
return t.Encode(privKey, dagcbor.Encode)
|
||||
}
|
||||
|
||||
// ToDagCborWriter is the same as ToDagCbor, but it accepts an io.Writer.
|
||||
func (t *Token) ToDagCborWriter(w io.Writer, privKey crypto.PrivKey) error {
|
||||
func (t *Token) ToDagCborWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes) error {
|
||||
return t.EncodeWriter(w, privKey, dagcbor.Encode)
|
||||
}
|
||||
|
||||
// ToDagJson marshals the Token to the DAG-JSON format.
|
||||
func (t *Token) ToDagJson(privKey crypto.PrivKey) ([]byte, error) {
|
||||
func (t *Token) ToDagJson(privKey crypto.PrivateKeySigningBytes) ([]byte, error) {
|
||||
return t.Encode(privKey, dagjson.Encode)
|
||||
}
|
||||
|
||||
// ToDagJsonWriter is the same as ToDagJson, but it accepts an io.Writer.
|
||||
func (t *Token) ToDagJsonWriter(w io.Writer, privKey crypto.PrivKey) error {
|
||||
func (t *Token) ToDagJsonWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes) error {
|
||||
return t.EncodeWriter(w, privKey, dagjson.Encode)
|
||||
}
|
||||
|
||||
@@ -193,18 +191,9 @@ func FromIPLD(node datamodel.Node) (*Token, error) {
|
||||
return tkn, err
|
||||
}
|
||||
|
||||
func (t *Token) toIPLD(privKey crypto.PrivKey) (datamodel.Node, error) {
|
||||
// sanity check that privKey and issuer are matching
|
||||
issPub, err := t.issuer.PubKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !issPub.Equals(privKey.GetPublic()) {
|
||||
return nil, fmt.Errorf("private key doesn't match the issuer")
|
||||
}
|
||||
|
||||
func (t *Token) toIPLD(privKey crypto.PrivateKeySigningBytes) (datamodel.Node, error) {
|
||||
var sub *string
|
||||
if t.subject != did.Undef {
|
||||
if t.subject != nil {
|
||||
s := t.subject.String()
|
||||
sub = &s
|
||||
}
|
||||
|
||||
@@ -14,10 +14,10 @@ import (
|
||||
"github.com/ucan-wg/go-ucan/token/internal/envelope"
|
||||
)
|
||||
|
||||
// [Tag] is the string used as a key within the SigPayload that identifies
|
||||
// Tag is the string used as a key within the SigPayload that identifies
|
||||
// that the TokenPayload is a delegation.
|
||||
//
|
||||
// [Tag]: https://github.com/ucan-wg/delegation/tree/v1_ipld#type-tag
|
||||
// See: https://github.com/ucan-wg/delegation/tree/v1_ipld#type-tag
|
||||
const Tag = "ucan/dlg@1.0.0-rc.1"
|
||||
|
||||
// TODO: update the above Tag URL once the delegation specification is merged.
|
||||
|
||||
@@ -8,10 +8,9 @@ import (
|
||||
"github.com/ipld/go-ipld-prime"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gotest.tools/v3/golden"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/did/didtest"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||
"github.com/ucan-wg/go-ucan/token/internal/didtest"
|
||||
"github.com/ucan-wg/go-ucan/token/internal/envelope"
|
||||
)
|
||||
|
||||
@@ -21,7 +20,6 @@ var schemaBytes []byte
|
||||
func TestSchemaRoundTrip(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
delegationJson := golden.Get(t, "new.dagjson")
|
||||
privKey := didtest.PersonaAlice.PrivKey()
|
||||
|
||||
t.Run("via buffers", func(t *testing.T) {
|
||||
@@ -30,7 +28,7 @@ func TestSchemaRoundTrip(t *testing.T) {
|
||||
// format: dagJson --> PayloadModel --> dagCbor --> PayloadModel --> dagJson
|
||||
// function: DecodeDagJson() Seal() Unseal() EncodeDagJson()
|
||||
|
||||
p1, err := delegation.FromDagJson(delegationJson)
|
||||
p1, err := delegation.FromDagJson(newDagJson)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, newCID, err := p1.ToSealed(privKey)
|
||||
@@ -47,13 +45,13 @@ func TestSchemaRoundTrip(t *testing.T) {
|
||||
readJson, err := p2.ToDagJson(privKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.JSONEq(t, string(delegationJson), string(readJson))
|
||||
assert.JSONEq(t, string(newDagJson), string(readJson))
|
||||
})
|
||||
|
||||
t.Run("via streaming", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
buf := bytes.NewBuffer(delegationJson)
|
||||
buf := bytes.NewBuffer(newDagJson)
|
||||
|
||||
// format: dagJson --> PayloadModel --> dagCbor --> PayloadModel --> dagJson
|
||||
// function: DecodeDagJson() Seal() Unseal() EncodeDagJson()
|
||||
@@ -77,17 +75,7 @@ func TestSchemaRoundTrip(t *testing.T) {
|
||||
readJson := &bytes.Buffer{}
|
||||
require.NoError(t, p2.ToDagJsonWriter(readJson, privKey))
|
||||
|
||||
assert.JSONEq(t, string(delegationJson), readJson.String())
|
||||
})
|
||||
|
||||
t.Run("fails with wrong PrivKey", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
p1, err := delegation.FromDagJson(delegationJson)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, err = p1.ToSealed(didtest.PersonaBob.PrivKey())
|
||||
require.EqualError(t, err, "private key doesn't match the issuer")
|
||||
assert.JSONEq(t, string(newDagJson), readJson.String())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -99,11 +87,10 @@ func BenchmarkSchemaLoad(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkRoundTrip(b *testing.B) {
|
||||
delegationJson := golden.Get(b, "new.dagjson")
|
||||
privKey := didtest.PersonaAlice.PrivKey()
|
||||
|
||||
b.Run("via buffers", func(b *testing.B) {
|
||||
p1, _ := delegation.FromDagJson(delegationJson)
|
||||
p1, _ := delegation.FromDagJson(newDagJson)
|
||||
cborBytes, _, _ := p1.ToSealed(privKey)
|
||||
p2, _, _ := delegation.FromSealed(cborBytes)
|
||||
|
||||
@@ -112,7 +99,7 @@ func BenchmarkRoundTrip(b *testing.B) {
|
||||
b.Run("FromDagJson", func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = delegation.FromDagJson(delegationJson)
|
||||
_, _ = delegation.FromDagJson(newDagJson)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -139,7 +126,7 @@ func BenchmarkRoundTrip(b *testing.B) {
|
||||
})
|
||||
|
||||
b.Run("via streaming", func(b *testing.B) {
|
||||
p1, _ := delegation.FromDagJsonReader(bytes.NewReader(delegationJson))
|
||||
p1, _ := delegation.FromDagJsonReader(bytes.NewReader(newDagJson))
|
||||
cborBuf := &bytes.Buffer{}
|
||||
_, _ = p1.ToSealedWriter(cborBuf, privKey)
|
||||
cborBytes := cborBuf.Bytes()
|
||||
@@ -149,7 +136,7 @@ func BenchmarkRoundTrip(b *testing.B) {
|
||||
|
||||
b.Run("FromDagJsonReader", func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
reader := bytes.NewReader(delegationJson)
|
||||
reader := bytes.NewReader(newDagJson)
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = reader.Seek(0, 0)
|
||||
_, _ = delegation.FromDagJsonReader(reader)
|
||||
|
||||
2
token/delegation/testdata/new.dagjson
vendored
2
token/delegation/testdata/new.dagjson
vendored
@@ -1 +1 @@
|
||||
[{"/":{"bytes":"YJsl8EMLnXSFE/nKKjMxz9bHHo+Y7QeLEzukEzW1TB+m53TTiY1aOt+qUO8JaTcOKsOHt/a4Vn+YiOd5CkLdAQ"}},{"h":{"/":{"bytes":"NO0BcQ"}},"ucan/dlg@1.0.0-rc.1":{"aud":"did:key:z6MkvJPmEZZYbgiw1ouT1oouTsTFBHJSts9ophVsNgcRmYxU","cmd":"/foo/bar","exp":7258118400,"iss":"did:key:z6Mkuukk2skDXLQn7NK3Eh9jMndYfvDBxxktgpidJAqb7M3p","meta":{"bar":"barr","foo":"fooo"},"nonce":{"/":{"bytes":"NnJvRGhHaTBraU5yaVFBejdKM2QrYk9lb0kvdGo4RU5pa21RTmJ0am5EMA"}},"pol":[["==",".status","draft"],["all",".reviewer",["like",".email","*@example.com"]],["any",".tags",["or",[["==",".","news"],["==",".","press"]]]]],"sub":"did:key:z6Mkgupchh5HwuHahS7YsyE8bLua1Mr8p2iKNRhyvSvRAs9n"}}]
|
||||
[{"/":{"bytes":"7Df/QlkgvQ/j1sr2NUFryQbTxo16rsOPfDPX8evBmA0hV6Omv+sScM8HC30KHr/kXCl+DzxDg+/EOwiZ9ApNBQ"}},{"h":{"/":{"bytes":"NAHtAe0BE3E"}},"ucan/dlg@1.0.0-rc.1":{"aud":"did:key:z6Mkf4WtCwPDtamsZvBJA4eSVcE7vZuRPy5Skm4HaoQv81i1","cmd":"/foo/bar","exp":7258118400,"iss":"did:key:z6MknUz1mSj4pvS6aUUHekCHdUPv7HBhDyDBZQ2W3Vujc5qC","meta":{"bar":"barr","foo":"fooo"},"nonce":{"/":{"bytes":"NnJvRGhHaTBraU5yaVFBejdKM2QrYk9lb0kvdGo4RU5pa21RTmJ0am5EMA"}},"pol":[["==",".status","draft"],["all",".reviewer",["like",".email","*@example.com"]],["any",".tags",["or",[["==",".","news"],["==",".","press"]]]]],"sub":"did:key:z6MkjWFU8HtfMquWn5MEQ9r22zWqyCN1vazGw6g6pzciziNV"}}]
|
||||
2
token/delegation/testdata/powerline.dagjson
vendored
2
token/delegation/testdata/powerline.dagjson
vendored
@@ -1 +1 @@
|
||||
[{"/":{"bytes":"i3YkPDvNSU4V8XYEluZhLH0b+NDcW/6+PtPSUHC17cmXXqgelG0K4EzWQQkS9UsYCHfkZSCn9NjGSXYMMFhaAQ"}},{"h":{"/":{"bytes":"NO0BcQ"}},"ucan/dlg@1.0.0-rc.1":{"aud":"did:key:z6MkvJPmEZZYbgiw1ouT1oouTsTFBHJSts9ophVsNgcRmYxU","cmd":"/foo/bar","exp":7258118400,"iss":"did:key:z6Mkuukk2skDXLQn7NK3Eh9jMndYfvDBxxktgpidJAqb7M3p","meta":{"bar":"barr","foo":"fooo"},"nonce":{"/":{"bytes":"NnJvRGhHaTBraU5yaVFBejdKM2QrYk9lb0kvdGo4RU5pa21RTmJ0am5EMA"}},"pol":[["==",".status","draft"],["all",".reviewer",["like",".email","*@example.com"]],["any",".tags",["or",[["==",".","news"],["==",".","press"]]]]]}}]
|
||||
[{"/":{"bytes":"pcdEo4gSPlBWE1kQUUjFOQNRLvXDw6BTLmgoIrU/o1JCAeSYiiIQFrHB1KIm0phZfMHqp5O6k6QxHuldxUARBQ"}},{"h":{"/":{"bytes":"NAHtAe0BE3E"}},"ucan/dlg@1.0.0-rc.1":{"aud":"did:key:z6Mkf4WtCwPDtamsZvBJA4eSVcE7vZuRPy5Skm4HaoQv81i1","cmd":"/foo/bar","exp":7258118400,"iss":"did:key:z6MknUz1mSj4pvS6aUUHekCHdUPv7HBhDyDBZQ2W3Vujc5qC","meta":{"bar":"barr","foo":"fooo"},"nonce":{"/":{"bytes":"NnJvRGhHaTBraU5yaVFBejdKM2QrYk9lb0kvdGo4RU5pa21RTmJ0am5EMA"}},"pol":[["==",".status","draft"],["all",".reviewer",["like",".email","*@example.com"]],["any",".tags",["or",[["==",".","news"],["==",".","press"]]]]]}}]
|
||||
2
token/delegation/testdata/root.dagjson
vendored
2
token/delegation/testdata/root.dagjson
vendored
@@ -1 +1 @@
|
||||
[{"/":{"bytes":"BBabgnWqd+cjwG1td0w9BudNocmUwoR89RMZTqZHk3osCXEI/bOkko0zTvlusaE4EMBBeSzZDKzjvunLBfdiBg"}},{"h":{"/":{"bytes":"NO0BcQ"}},"ucan/dlg@1.0.0-rc.1":{"aud":"did:key:z6MkvJPmEZZYbgiw1ouT1oouTsTFBHJSts9ophVsNgcRmYxU","cmd":"/foo/bar","exp":7258118400,"iss":"did:key:z6Mkuukk2skDXLQn7NK3Eh9jMndYfvDBxxktgpidJAqb7M3p","meta":{"bar":"barr","foo":"fooo"},"nonce":{"/":{"bytes":"NnJvRGhHaTBraU5yaVFBejdKM2QrYk9lb0kvdGo4RU5pa21RTmJ0am5EMA"}},"pol":[["==",".status","draft"],["all",".reviewer",["like",".email","*@example.com"]],["any",".tags",["or",[["==",".","news"],["==",".","press"]]]]],"sub":"did:key:z6Mkuukk2skDXLQn7NK3Eh9jMndYfvDBxxktgpidJAqb7M3p"}}]
|
||||
[{"/":{"bytes":"jS5WzBU0JybWoQAJjJTgxke5U7vbwOJTYbe3bipYbmhz3Lcrl1B/68dK4xhWSv6wc3zZqXFJ7/Kw2jDLCOZWDA"}},{"h":{"/":{"bytes":"NAHtAe0BE3E"}},"ucan/dlg@1.0.0-rc.1":{"aud":"did:key:z6Mkf4WtCwPDtamsZvBJA4eSVcE7vZuRPy5Skm4HaoQv81i1","cmd":"/foo/bar","exp":7258118400,"iss":"did:key:z6MknUz1mSj4pvS6aUUHekCHdUPv7HBhDyDBZQ2W3Vujc5qC","meta":{"bar":"barr","foo":"fooo"},"nonce":{"/":{"bytes":"NnJvRGhHaTBraU5yaVFBejdKM2QrYk9lb0kvdGo4RU5pa21RTmJ0am5EMA"}},"pol":[["==",".status","draft"],["all",".reviewer",["like",".email","*@example.com"]],["any",".tags",["or",[["==",".","news"],["==",".","press"]]]]],"sub":"did:key:z6MknUz1mSj4pvS6aUUHekCHdUPv7HBhDyDBZQ2W3Vujc5qC"}}]
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/MetaMask/go-did-it/crypto"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipld/go-ipld-prime/codec"
|
||||
"github.com/libp2p/go-libp2p/core/crypto"
|
||||
)
|
||||
|
||||
type Token interface {
|
||||
@@ -23,21 +23,21 @@ type Token interface {
|
||||
type Marshaller interface {
|
||||
// ToSealed wraps the token in an envelope, generates the signature, encodes
|
||||
// the result to DAG-CBOR and calculates the CID of the resulting binary data.
|
||||
ToSealed(privKey crypto.PrivKey) ([]byte, cid.Cid, error)
|
||||
ToSealed(privKey crypto.PrivateKeySigningBytes) ([]byte, cid.Cid, error)
|
||||
// ToSealedWriter is the same as ToSealed but accepts an io.Writer.
|
||||
ToSealedWriter(w io.Writer, privKey crypto.PrivKey) (cid.Cid, error)
|
||||
ToSealedWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes) (cid.Cid, error)
|
||||
// Encode marshals a Token to the format specified by the provided codec.Encoder.
|
||||
Encode(privKey crypto.PrivKey, encFn codec.Encoder) ([]byte, error)
|
||||
Encode(privKey crypto.PrivateKeySigningBytes, encFn codec.Encoder) ([]byte, error)
|
||||
// EncodeWriter is the same as Encode, but accepts an io.Writer.
|
||||
EncodeWriter(w io.Writer, privKey crypto.PrivKey, encFn codec.Encoder) error
|
||||
EncodeWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes, encFn codec.Encoder) error
|
||||
// ToDagCbor marshals the Token to the DAG-CBOR format.
|
||||
ToDagCbor(privKey crypto.PrivKey) ([]byte, error)
|
||||
ToDagCbor(privKey crypto.PrivateKeySigningBytes) ([]byte, error)
|
||||
// ToDagCborWriter is the same as ToDagCbor, but it accepts an io.Writer.
|
||||
ToDagCborWriter(w io.Writer, privKey crypto.PrivKey) error
|
||||
ToDagCborWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes) error
|
||||
// ToDagJson marshals the Token to the DAG-JSON format.
|
||||
ToDagJson(privKey crypto.PrivKey) ([]byte, error)
|
||||
ToDagJson(privKey crypto.PrivateKeySigningBytes) ([]byte, error)
|
||||
// ToDagJsonWriter is the same as ToDagJson, but it accepts an io.Writer.
|
||||
ToDagJsonWriter(w io.Writer, privKey crypto.PrivKey) error
|
||||
ToDagJsonWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes) error
|
||||
}
|
||||
|
||||
// Bundle carries together a decoded token with its Cid and raw signed data.
|
||||
|
||||
139
token/internal/didtest/crypto.go
Normal file
139
token/internal/didtest/crypto.go
Normal file
@@ -0,0 +1,139 @@
|
||||
// Package didtest provides Personas that can be used for testing. Each
|
||||
// Persona has a name, crypto.PrivKey and associated crypto.PubKey and
|
||||
// did.DID.
|
||||
package didtest
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
|
||||
"github.com/MetaMask/go-did-it"
|
||||
didkeyctl "github.com/MetaMask/go-did-it/controller/did-key"
|
||||
"github.com/MetaMask/go-did-it/crypto"
|
||||
"github.com/MetaMask/go-did-it/crypto/ed25519"
|
||||
)
|
||||
|
||||
const (
|
||||
// all are ed25519 as base64
|
||||
alicePrivKeyB64 = "zth/9cTSUVwlLzfEWwLCcOkaEmjrRGPOI6mOJksWAYZ3Toe7ymxAzDeiseyxbmEpJ81qYM3dZ8XrXqgonnTTEw=="
|
||||
bobPrivKeyB64 = "+p1REV3MkUnLhUMbFe9RcSsmo33TT/FO85yaV+c6fiYJCBsdiwfMwodlkzSAG3sHQIuZj8qnJ678oJucYy7WEg=="
|
||||
carolPrivKeyB64 = "aSu3vTwE7z3pXaTaAhVLeizuqnZUJZQHTCSLMLxyZh5LDoZQn80uoQgMEdsbOhR+zIqrjBn5WviGurDkKYVfug=="
|
||||
danPrivKeyB64 = "s1zM1av6og3o0UMNbEs/RyezS7Nk/jbSYL2Z+xPEw9Cho/KuEAa75Sf4yJHclLwpKXNucbrZ2scE8Iy8K05KWQ=="
|
||||
erinPrivKeyB64 = "+qHpaAR3iivWMEl+pkXmq+uJeHtqFiY++XOXtZ9Tu/WPABCO+eRFrTCLJykJEzAPGFmkJF8HQ7DMwOH7Ry3Aqw=="
|
||||
frankPrivKeyB64 = "4k/1N0+Fq73DxmNbGis9PY2KgKxWmtDWhmi1E6sBLuGd7DS0TWjCn1Xa3lXkY49mFszMjhWC+V6DCBf7R68u4Q=="
|
||||
)
|
||||
|
||||
// Persona is a generic participant used for cryptographic testing.
|
||||
type Persona int
|
||||
|
||||
// The provided Personas were selected from the first few generic
|
||||
// participants listed in this [table].
|
||||
//
|
||||
// [table]: https://en.wikipedia.org/wiki/Alice_and_Bob#Cryptographic_systems
|
||||
const (
|
||||
PersonaAlice Persona = iota + 1
|
||||
PersonaBob
|
||||
PersonaCarol
|
||||
PersonaDan
|
||||
PersonaErin
|
||||
PersonaFrank
|
||||
)
|
||||
|
||||
var privKeys map[Persona]crypto.PrivateKeySigningBytes
|
||||
|
||||
func init() {
|
||||
privKeys = make(map[Persona]crypto.PrivateKeySigningBytes, 6)
|
||||
for persona, pB64 := range privKeyB64() {
|
||||
privBytes, err := base64.StdEncoding.DecodeString(pB64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
privKey, err := ed25519.PrivateKeyFromBytes(privBytes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
privKeys[persona] = privKey
|
||||
}
|
||||
}
|
||||
|
||||
// DID returns a did.DID based on the Persona's Ed25519 public key.
|
||||
func (p Persona) DID() did.DID {
|
||||
return didkeyctl.FromPrivateKey(p.PrivKey())
|
||||
}
|
||||
|
||||
// Name returns the username of the Persona.
|
||||
func (p Persona) Name() string {
|
||||
name, ok := map[Persona]string{
|
||||
PersonaAlice: "Alice",
|
||||
PersonaBob: "Bob",
|
||||
PersonaCarol: "Carol",
|
||||
PersonaDan: "Dan",
|
||||
PersonaErin: "Erin",
|
||||
PersonaFrank: "Frank",
|
||||
}[p]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Unknown persona: %v", p))
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
// PrivKey returns the Ed25519 private key for the Persona.
|
||||
func (p Persona) PrivKey() crypto.PrivateKeySigningBytes {
|
||||
res, ok := privKeys[p]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Unknown persona: %v", p))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (p Persona) PrivKeyConfig() string {
|
||||
res, ok := privKeyB64()[p]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Unknown persona: %v", p))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// PubKey returns the Ed25519 public key for the Persona.
|
||||
func (p Persona) PubKey() crypto.PublicKey {
|
||||
return p.PrivKey().Public()
|
||||
}
|
||||
|
||||
func privKeyB64() map[Persona]string {
|
||||
return map[Persona]string{
|
||||
PersonaAlice: alicePrivKeyB64,
|
||||
PersonaBob: bobPrivKeyB64,
|
||||
PersonaCarol: carolPrivKeyB64,
|
||||
PersonaDan: danPrivKeyB64,
|
||||
PersonaErin: erinPrivKeyB64,
|
||||
PersonaFrank: frankPrivKeyB64,
|
||||
}
|
||||
}
|
||||
|
||||
// Personas returns an (alphabetically) ordered list of the defined
|
||||
// Persona values.
|
||||
func Personas() []Persona {
|
||||
return []Persona{
|
||||
PersonaAlice,
|
||||
PersonaBob,
|
||||
PersonaCarol,
|
||||
PersonaDan,
|
||||
PersonaErin,
|
||||
PersonaFrank,
|
||||
}
|
||||
}
|
||||
|
||||
// DidToName retrieve the persona's name from its DID.
|
||||
func DidToName(d did.DID) string {
|
||||
return map[did.DID]string{
|
||||
PersonaAlice.DID(): "Alice",
|
||||
PersonaBob.DID(): "Bob",
|
||||
PersonaCarol.DID(): "Carol",
|
||||
PersonaDan.DID(): "Dan",
|
||||
PersonaErin.DID(): "Erin",
|
||||
PersonaFrank.DID(): "Frank",
|
||||
}[d]
|
||||
}
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/multiformats/go-multihash"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gotest.tools/v3/golden"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/token/internal/envelope"
|
||||
)
|
||||
@@ -17,11 +16,11 @@ import (
|
||||
func TestCidFromBytes(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
expData := golden.Get(t, "example.dagcbor")
|
||||
expData := exampleDagCbor
|
||||
expHash, err := multihash.Sum(expData, uint64(multicodec.Sha2_256), -1)
|
||||
require.NoError(t, err)
|
||||
|
||||
data, err := envelope.ToDagCbor(examplePrivKey(t), newExample(t))
|
||||
data, err := envelope.ToDagCbor(examplePrivKey(t), newExample())
|
||||
require.NoError(t, err)
|
||||
|
||||
id, err := envelope.CIDFromBytes(data)
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/MetaMask/go-did-it/crypto"
|
||||
"github.com/MetaMask/go-did-it/crypto/ed25519"
|
||||
"github.com/ipld/go-ipld-prime"
|
||||
"github.com/ipld/go-ipld-prime/codec/dagcbor"
|
||||
"github.com/ipld/go-ipld-prime/datamodel"
|
||||
@@ -14,32 +16,30 @@ import (
|
||||
"github.com/ipld/go-ipld-prime/node/basicnode"
|
||||
"github.com/ipld/go-ipld-prime/node/bindnode"
|
||||
"github.com/ipld/go-ipld-prime/schema"
|
||||
"github.com/libp2p/go-libp2p/core/crypto"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gotest.tools/v3/golden"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/token/internal/envelope"
|
||||
)
|
||||
|
||||
const (
|
||||
exampleCID = "zdpuAyw6R5HvKSPzztuzXNYFx3ZGoMHMuAsXL6u3xLGQriRXQ"
|
||||
exampleDID = "did:key:z6MkpuK2Amsu1RqcLGgmHHQHhvmeXCCBVsM4XFSg2cCyg4Nh"
|
||||
exampleGreeting = "world"
|
||||
examplePrivKeyCfg = "CAESQP9v2uqECTuIi45dyg3znQvsryvf2IXmOF/6aws6aCehm0FVrj0zHR5RZSDxWNjcpcJqsGym3sjCungX9Zt5oA4="
|
||||
exampleSignatureStr = "PZV6A2aI7n+MlyADqcqmWhkuyNrgUCDz+qSLSnI9bpasOwOhKUTx95m5Nu5CO/INa1LqzHGioD9+PVf6qdtTBg"
|
||||
exampleTag = "ucan/example@v1.0.0-rc.1"
|
||||
exampleTypeName = "Example"
|
||||
exampleVarsigHeaderStr = "NO0BcQ"
|
||||
exampleCID = "zdpuAn4jksvc1gc9PLDqHw2NoFq8CBkRVTTo2xFuW2JUPS5DY"
|
||||
exampleDID = "did:key:z6MkuqvEtTW9L1E91CY3GmL83muetLAA2h8A5fUHjJgqq2Ab"
|
||||
exampleGreeting = "world"
|
||||
examplePrivKeyB64 = "V4hh1lcFV43Y6vyOBEVOFTwl1XS/DR0F/kYcz5i6W/DkrUTG8yx09lOwSf36NCHPKSFYv/T1R3WKjNfndgVucA=="
|
||||
exampleTag = "ucan/example@v1.0.0-rc.1"
|
||||
|
||||
invalidSignatureStr = "PZV6A2aI7n+MlyADqcqmWhkuyNrgUCDz+qSLSnI9bpasOwOhKUTx95m5Nu5CO/INa1LqzHGioD9+PVf6qdtTBK"
|
||||
|
||||
exampleDAGCBORFilename = "example.dagcbor"
|
||||
exampleDAGJSONFilename = "example.dagjson"
|
||||
)
|
||||
|
||||
//go:embed testdata/example.ipldsch
|
||||
var schemaBytes []byte
|
||||
|
||||
//go:embed testdata/example.dagcbor
|
||||
var exampleDagCbor []byte
|
||||
|
||||
//go:embed testdata/example.dagjson
|
||||
var exampleDagJson []byte
|
||||
|
||||
var (
|
||||
once sync.Once
|
||||
ts *schema.TypeSystem
|
||||
@@ -59,7 +59,7 @@ func mustLoadSchema() *schema.TypeSystem {
|
||||
}
|
||||
|
||||
func exampleType() schema.Type {
|
||||
return mustLoadSchema().TypeByName(exampleTypeName)
|
||||
return mustLoadSchema().TypeByName("Example")
|
||||
}
|
||||
|
||||
var _ envelope.Tokener = (*Example)(nil)
|
||||
@@ -69,9 +69,7 @@ type Example struct {
|
||||
Issuer string
|
||||
}
|
||||
|
||||
func newExample(t *testing.T) *Example {
|
||||
t.Helper()
|
||||
|
||||
func newExample() *Example {
|
||||
return &Example{
|
||||
Hello: exampleGreeting,
|
||||
Issuer: exampleDID,
|
||||
@@ -86,45 +84,30 @@ func (*Example) Tag() string {
|
||||
return exampleTag
|
||||
}
|
||||
|
||||
func exampleGoldenNode(t *testing.T) datamodel.Node {
|
||||
func examplePrivKey(t *testing.T) crypto.PrivateKeySigningBytes {
|
||||
t.Helper()
|
||||
|
||||
cbor := golden.Get(t, exampleDAGCBORFilename)
|
||||
|
||||
node, err := ipld.Decode(cbor, dagcbor.Decode)
|
||||
privBytes, err := base64.StdEncoding.DecodeString(examplePrivKeyB64)
|
||||
require.NoError(t, err)
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
func examplePrivKey(t *testing.T) crypto.PrivKey {
|
||||
t.Helper()
|
||||
|
||||
privKeyEnc, err := crypto.ConfigDecodeKey(examplePrivKeyCfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
privKey, err := crypto.UnmarshalPrivateKey(privKeyEnc)
|
||||
privKey, err := ed25519.PrivateKeyFromBytes(privBytes)
|
||||
require.NoError(t, err)
|
||||
|
||||
return privKey
|
||||
}
|
||||
|
||||
func exampleSignature(t *testing.T) []byte {
|
||||
t.Helper()
|
||||
|
||||
sig, err := base64.RawStdEncoding.DecodeString(exampleSignatureStr)
|
||||
require.NoError(t, err)
|
||||
|
||||
return sig
|
||||
}
|
||||
|
||||
func invalidNodeFromGolden(t *testing.T) datamodel.Node {
|
||||
// nodeWithInvalidSignature creates an IPLD node of a token, with an invalid signature
|
||||
func nodeWithInvalidSignature(t *testing.T) datamodel.Node {
|
||||
t.Helper()
|
||||
|
||||
invalidSig, err := base64.RawStdEncoding.DecodeString(invalidSignatureStr)
|
||||
require.NoError(t, err)
|
||||
|
||||
envelNode := exampleGoldenNode(t)
|
||||
cbor := exampleDagCbor
|
||||
|
||||
envelNode, err := ipld.Decode(cbor, dagcbor.Decode)
|
||||
require.NoError(t, err)
|
||||
|
||||
sigPayloadNode, err := envelNode.LookupByIndex(1)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// a verified [TokenPayload].
|
||||
//
|
||||
// Encoding functions in this package require a private key as a
|
||||
// parameter so the VarsigHeader can be set and so that a
|
||||
// parameter so the VarsigBytes can be set and so that a
|
||||
// cryptographic signature can be generated.
|
||||
//
|
||||
// Decoding functions in this package likewise perform the signature
|
||||
@@ -40,10 +40,10 @@ import (
|
||||
"github.com/ipld/go-ipld-prime/node/basicnode"
|
||||
"github.com/ipld/go-ipld-prime/node/bindnode"
|
||||
"github.com/ipld/go-ipld-prime/schema"
|
||||
"github.com/libp2p/go-libp2p/core/crypto"
|
||||
"github.com/ucan-wg/go-varsig"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
"github.com/ucan-wg/go-ucan/token/internal/varsig"
|
||||
"github.com/MetaMask/go-did-it"
|
||||
"github.com/MetaMask/go-did-it/crypto"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -132,7 +132,7 @@ func FromIPLD[T Tokener](node datamodel.Node) (T, error) {
|
||||
}
|
||||
|
||||
// This needs to be done before converting this node to its schema
|
||||
// representation (afterwards, the field might be renamed os it's safer
|
||||
// representation (afterwards, the field might be renamed, so it's safer
|
||||
// to use the wire name).
|
||||
issuerNode, err := info.tokenPayloadNode.LookupByString("iss")
|
||||
if err != nil {
|
||||
@@ -162,7 +162,7 @@ func FromIPLD[T Tokener](node datamodel.Node) (T, error) {
|
||||
}
|
||||
|
||||
// Check that the issuer's DID contains a public key with a type that
|
||||
// matches the VarsigHeader and then verify the SigPayload.
|
||||
// matches the VarsigBytes and then verify the SigPayload.
|
||||
issuer, err := issuerNode.AsString()
|
||||
if err != nil {
|
||||
return zero, err
|
||||
@@ -173,28 +173,36 @@ func FromIPLD[T Tokener](node datamodel.Node) (T, error) {
|
||||
return zero, err
|
||||
}
|
||||
|
||||
issuerPubKey, err := issuerDID.PubKey()
|
||||
// TODO: pass resolution options
|
||||
issuerDoc, err := issuerDID.Document()
|
||||
if err != nil {
|
||||
return zero, err
|
||||
}
|
||||
|
||||
issuerVarsigHeader, err := varsig.Encode(issuerPubKey.Type())
|
||||
vsig, err := varsig.Decode(info.VarsigBytes)
|
||||
if err != nil {
|
||||
return zero, fmt.Errorf("failed to decode varsig: %w", err)
|
||||
}
|
||||
|
||||
var data []byte
|
||||
|
||||
switch vsig.PayloadEncoding() {
|
||||
case varsig.PayloadEncodingDAGCBOR:
|
||||
// TODO: can we use the already serialized CBOR data here, instead of encoding again the payload?
|
||||
data, err = ipld.Encode(info.sigPayloadNode, dagcbor.Encode)
|
||||
case varsig.PayloadEncodingDAGJSON:
|
||||
data, err = ipld.Encode(info.sigPayloadNode, dagjson.Encode)
|
||||
default:
|
||||
return zero, errors.New("unsupported payload encoding")
|
||||
}
|
||||
if err != nil {
|
||||
return zero, err
|
||||
}
|
||||
|
||||
if string(info.VarsigHeader) != string(issuerVarsigHeader) {
|
||||
return zero, errors.New("the VarsigHeader key type doesn't match the issuer's key type")
|
||||
}
|
||||
// TODO: use CapabilityDelegation() or CapabilityInvocation()
|
||||
|
||||
// TODO: can we use the already serialized CBOR data here, instead of encoding again the payload?
|
||||
data, err := ipld.Encode(info.sigPayloadNode, dagcbor.Encode)
|
||||
if err != nil {
|
||||
return zero, err
|
||||
}
|
||||
|
||||
ok, err = issuerPubKey.Verify(data, info.Signature)
|
||||
if err != nil || !ok {
|
||||
ok, _ = did.TryAllVerifyBytes(issuerDoc.CapabilityDelegation(), data, info.Signature, crypto.WithVarsig(vsig))
|
||||
if !ok {
|
||||
return zero, errors.New("failed to verify the token's signature")
|
||||
}
|
||||
|
||||
@@ -203,7 +211,7 @@ func FromIPLD[T Tokener](node datamodel.Node) (T, error) {
|
||||
|
||||
// Encode marshals a Tokener to the format specified by the provided
|
||||
// codec.Encoder.
|
||||
func Encode(privKey crypto.PrivKey, token Tokener, encFn codec.Encoder) ([]byte, error) {
|
||||
func Encode(privKey crypto.PrivateKeySigningBytes, token Tokener, encFn codec.Encoder) ([]byte, error) {
|
||||
node, err := ToIPLD(privKey, token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -214,7 +222,7 @@ func Encode(privKey crypto.PrivKey, token Tokener, encFn codec.Encoder) ([]byte,
|
||||
|
||||
// EncodeWriter is the same as Encode but outputs to an io.Writer instead
|
||||
// of encoding into a []byte.
|
||||
func EncodeWriter(w io.Writer, privKey crypto.PrivKey, token Tokener, encFn codec.Encoder) error {
|
||||
func EncodeWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes, token Tokener, encFn codec.Encoder) error {
|
||||
node, err := ToIPLD(privKey, token)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -224,38 +232,36 @@ func EncodeWriter(w io.Writer, privKey crypto.PrivKey, token Tokener, encFn code
|
||||
}
|
||||
|
||||
// ToDagCbor marshals the Tokener to the DAG-CBOR format.
|
||||
func ToDagCbor(privKey crypto.PrivKey, token Tokener) ([]byte, error) {
|
||||
func ToDagCbor(privKey crypto.PrivateKeySigningBytes, token Tokener) ([]byte, error) {
|
||||
return Encode(privKey, token, dagcbor.Encode)
|
||||
}
|
||||
|
||||
// ToDagCborWriter is the same as ToDagCbor but outputs to an io.Writer
|
||||
// instead of encoding into a []byte.
|
||||
func ToDagCborWriter(w io.Writer, privKey crypto.PrivKey, token Tokener) error {
|
||||
func ToDagCborWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes, token Tokener) error {
|
||||
return EncodeWriter(w, privKey, token, dagcbor.Encode)
|
||||
}
|
||||
|
||||
// ToDagJson marshals the Tokener to the DAG-JSON format.
|
||||
func ToDagJson(privKey crypto.PrivKey, token Tokener) ([]byte, error) {
|
||||
func ToDagJson(privKey crypto.PrivateKeySigningBytes, token Tokener) ([]byte, error) {
|
||||
return Encode(privKey, token, dagjson.Encode)
|
||||
}
|
||||
|
||||
// ToDagJsonWriter is the same as ToDagJson but outputs to an io.Writer
|
||||
// instead of encoding into a []byte.
|
||||
func ToDagJsonWriter(w io.Writer, privKey crypto.PrivKey, token Tokener) error {
|
||||
func ToDagJsonWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes, token Tokener) error {
|
||||
return EncodeWriter(w, privKey, token, dagjson.Encode)
|
||||
}
|
||||
|
||||
// ToIPLD wraps the Tokener in an IPLD datamodel.Node.
|
||||
func ToIPLD(privKey crypto.PrivKey, token Tokener) (datamodel.Node, error) {
|
||||
func ToIPLD(privKey crypto.PrivateKeySigningBytes, token Tokener) (datamodel.Node, error) {
|
||||
tokenPayloadNode := bindnode.Wrap(token, token.Prototype().Type()).Representation()
|
||||
|
||||
varsigHeader, err := varsig.Encode(privKey.Type())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts := []crypto.SigningOption{crypto.WithPayloadEncoding(varsig.PayloadEncodingDAGCBOR)}
|
||||
vsig := privKey.Varsig(opts...)
|
||||
|
||||
sigPayloadNode, err := qp.BuildMap(basicnode.Prototype.Any, 2, func(ma datamodel.MapAssembler) {
|
||||
qp.MapEntry(ma, VarsigHeaderKey, qp.Bytes(varsigHeader))
|
||||
qp.MapEntry(ma, VarsigHeaderKey, qp.Bytes(vsig.Encode()))
|
||||
qp.MapEntry(ma, token.Tag(), qp.Node(tokenPayloadNode))
|
||||
})
|
||||
|
||||
@@ -264,7 +270,7 @@ func ToIPLD(privKey crypto.PrivKey, token Tokener) (datamodel.Node, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
signature, err := privKey.Sign(data)
|
||||
signature, err := privKey.SignToBytes(data, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -315,7 +321,7 @@ func FindTag(node datamodel.Node) (string, error) {
|
||||
type Info struct {
|
||||
Tag string
|
||||
Signature []byte
|
||||
VarsigHeader []byte
|
||||
VarsigBytes []byte
|
||||
sigPayloadNode datamodel.Node // private, we don't want to expose that
|
||||
tokenPayloadNode datamodel.Node // private, we don't want to expose that
|
||||
}
|
||||
@@ -367,7 +373,7 @@ func Inspect(node datamodel.Node) (Info, error) {
|
||||
switch {
|
||||
case key == VarsigHeaderKey:
|
||||
foundVarsigHeader = true
|
||||
res.VarsigHeader, err = v.AsBytes()
|
||||
res.VarsigBytes, err = v.AsBytes()
|
||||
if err != nil {
|
||||
return Info{}, err
|
||||
}
|
||||
|
||||
@@ -3,15 +3,16 @@ package envelope_test
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
_ "embed"
|
||||
"encoding/base64"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
_ "github.com/MetaMask/go-did-it/verifiers/did-key"
|
||||
"github.com/ipld/go-ipld-prime"
|
||||
"github.com/ipld/go-ipld-prime/codec/dagcbor"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gotest.tools/v3/golden"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/token/internal/envelope"
|
||||
)
|
||||
@@ -22,9 +23,7 @@ func TestDecode(t *testing.T) {
|
||||
t.Run("via FromDagCbor", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data := golden.Get(t, "example.dagcbor")
|
||||
|
||||
tkn, err := envelope.FromDagCbor[*Example](data)
|
||||
tkn, err := envelope.FromDagCbor[*Example](exampleDagCbor)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, exampleGreeting, tkn.Hello)
|
||||
assert.Equal(t, exampleDID, tkn.Issuer)
|
||||
@@ -33,9 +32,7 @@ func TestDecode(t *testing.T) {
|
||||
t.Run("via FromDagJson", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data := golden.Get(t, "example.dagjson")
|
||||
|
||||
tkn, err := envelope.FromDagJson[*Example](data)
|
||||
tkn, err := envelope.FromDagJson[*Example](exampleDagJson)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, exampleGreeting, tkn.Hello)
|
||||
assert.Equal(t, exampleDID, tkn.Issuer)
|
||||
@@ -48,17 +45,17 @@ func TestEncode(t *testing.T) {
|
||||
t.Run("via ToDagCbor", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data, err := envelope.ToDagCbor(examplePrivKey(t), newExample(t))
|
||||
data, err := envelope.ToDagCbor(examplePrivKey(t), newExample())
|
||||
require.NoError(t, err)
|
||||
golden.AssertBytes(t, data, exampleDAGCBORFilename)
|
||||
require.Equal(t, exampleDagCbor, data)
|
||||
})
|
||||
|
||||
t.Run("via ToDagJson", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data, err := envelope.ToDagJson(examplePrivKey(t), newExample(t))
|
||||
data, err := envelope.ToDagJson(examplePrivKey(t), newExample())
|
||||
require.NoError(t, err)
|
||||
golden.Assert(t, string(data), exampleDAGJSONFilename)
|
||||
require.Equal(t, exampleDagJson, data)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -68,14 +65,14 @@ func TestRoundtrip(t *testing.T) {
|
||||
t.Run("via FromDagCbor/ToDagCbor", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dataIn := golden.Get(t, exampleDAGCBORFilename)
|
||||
dataIn := exampleDagCbor
|
||||
|
||||
tkn, err := envelope.FromDagCbor[*Example](dataIn)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, exampleGreeting, tkn.Hello)
|
||||
assert.Equal(t, exampleDID, tkn.Issuer)
|
||||
|
||||
dataOut, err := envelope.ToDagCbor(examplePrivKey(t), newExample(t))
|
||||
dataOut, err := envelope.ToDagCbor(examplePrivKey(t), newExample())
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, dataIn, dataOut)
|
||||
})
|
||||
@@ -83,7 +80,7 @@ func TestRoundtrip(t *testing.T) {
|
||||
t.Run("via FromDagCborReader/ToDagCborWriter", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data := golden.Get(t, exampleDAGCBORFilename)
|
||||
data := exampleDagCbor
|
||||
|
||||
tkn, err := envelope.FromDagCborReader[*Example](bytes.NewReader(data))
|
||||
require.NoError(t, err)
|
||||
@@ -91,21 +88,21 @@ func TestRoundtrip(t *testing.T) {
|
||||
assert.Equal(t, exampleDID, tkn.Issuer)
|
||||
|
||||
w := &bytes.Buffer{}
|
||||
require.NoError(t, envelope.ToDagCborWriter(w, examplePrivKey(t), newExample(t)))
|
||||
require.NoError(t, envelope.ToDagCborWriter(w, examplePrivKey(t), newExample()))
|
||||
assert.Equal(t, data, w.Bytes())
|
||||
})
|
||||
|
||||
t.Run("via FromDagJson/ToDagJson", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dataIn := golden.Get(t, exampleDAGJSONFilename)
|
||||
dataIn := exampleDagJson
|
||||
|
||||
tkn, err := envelope.FromDagJson[*Example](dataIn)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, exampleGreeting, tkn.Hello)
|
||||
assert.Equal(t, exampleDID, tkn.Issuer)
|
||||
|
||||
dataOut, err := envelope.ToDagJson(examplePrivKey(t), newExample(t))
|
||||
dataOut, err := envelope.ToDagJson(examplePrivKey(t), newExample())
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, dataIn, dataOut)
|
||||
})
|
||||
@@ -113,7 +110,7 @@ func TestRoundtrip(t *testing.T) {
|
||||
t.Run("via FromDagJsonReader/ToDagJsonrWriter", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data := golden.Get(t, exampleDAGJSONFilename)
|
||||
data := exampleDagJson
|
||||
|
||||
tkn, err := envelope.FromDagJsonReader[*Example](bytes.NewReader(data))
|
||||
require.NoError(t, err)
|
||||
@@ -121,7 +118,7 @@ func TestRoundtrip(t *testing.T) {
|
||||
assert.Equal(t, exampleDID, tkn.Issuer)
|
||||
|
||||
w := &bytes.Buffer{}
|
||||
require.NoError(t, envelope.ToDagJsonWriter(w, examplePrivKey(t), newExample(t)))
|
||||
require.NoError(t, envelope.ToDagJsonWriter(w, examplePrivKey(t), newExample()))
|
||||
assert.Equal(t, data, w.Bytes())
|
||||
})
|
||||
}
|
||||
@@ -129,7 +126,7 @@ func TestRoundtrip(t *testing.T) {
|
||||
func TestFromIPLD_with_invalid_signature(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
node := invalidNodeFromGolden(t)
|
||||
node := nodeWithInvalidSignature(t)
|
||||
tkn, err := envelope.FromIPLD[*Example](node)
|
||||
assert.Nil(t, tkn)
|
||||
require.EqualError(t, err, "failed to verify the token's signature")
|
||||
@@ -158,18 +155,17 @@ func TestHash(t *testing.T) {
|
||||
func TestInspect(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data := golden.Get(t, "example.dagcbor")
|
||||
node, err := ipld.Decode(data, dagcbor.Decode)
|
||||
node, err := ipld.Decode(exampleDagCbor, dagcbor.Decode)
|
||||
require.NoError(t, err)
|
||||
|
||||
expSig, err := base64.RawStdEncoding.DecodeString("fPqfwL3iFpbw9SvBiq0DIbUurv9o6c36R08tC/yslGrJcwV51ghzWahxdetpEf6T5LCszXX9I/K8khvnmAxjAg")
|
||||
expSig, err := base64.RawStdEncoding.DecodeString("+xUwgl/5VZcTxx6iePmkrIaZAlxuelHTbeQ5lQIgIV3ZgHS+Jf5BUERB0fvmFfiIfa5A3yMPfEA/7rswYsRRCg")
|
||||
require.NoError(t, err)
|
||||
|
||||
info, err := envelope.Inspect(node)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expSig, info.Signature)
|
||||
assert.Equal(t, "ucan/example@v1.0.0-rc.1", info.Tag)
|
||||
assert.Equal(t, []byte{0x34, 0xed, 0x1, 0x71}, info.VarsigHeader)
|
||||
assert.Equal(t, []byte{0x34, 0x1, 0xed, 0x1, 0xed, 0x1, 0x13, 0x71}, info.VarsigBytes)
|
||||
}
|
||||
|
||||
func FuzzInspect(f *testing.F) {
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
‚X@|úŸÀ½â–ðõ+ÁŠ!µ.®ÿhéÍúGO-ü¬”jÉsyÖsY¨quëiþ“ä°¬Íuý#ò¼’ç˜c¢ahD4íqxucan/example@v1.0.0-rc.1¢cissx8did:key:z6MkpuK2Amsu1RqcLGgmHHQHhvmeXCCBVsM4XFSg2cCyg4Nhehelloeworld
|
||||
‚X@û0‚_ùU—Ç¢xù¤¬†™\nzQÓmä9• !]Ù€t¾%þAPDAÑûæøˆ}®@ß#|@?î»0bÄQ
|
||||
¢ahH4ííqxucan/example@v1.0.0-rc.1¢cissx8did:key:z6MkuqvEtTW9L1E91CY3GmL83muetLAA2h8A5fUHjJgqq2Abehelloeworld
|
||||
@@ -1 +1 @@
|
||||
[{"/":{"bytes":"fPqfwL3iFpbw9SvBiq0DIbUurv9o6c36R08tC/yslGrJcwV51ghzWahxdetpEf6T5LCszXX9I/K8khvnmAxjAg"}},{"h":{"/":{"bytes":"NO0BcQ"}},"ucan/example@v1.0.0-rc.1":{"hello":"world","iss":"did:key:z6MkpuK2Amsu1RqcLGgmHHQHhvmeXCCBVsM4XFSg2cCyg4Nh"}}]
|
||||
[{"/":{"bytes":"+xUwgl/5VZcTxx6iePmkrIaZAlxuelHTbeQ5lQIgIV3ZgHS+Jf5BUERB0fvmFfiIfa5A3yMPfEA/7rswYsRRCg"}},{"h":{"/":{"bytes":"NAHtAe0BE3E"}},"ucan/example@v1.0.0-rc.1":{"hello":"world","iss":"did:key:z6MkuqvEtTW9L1E91CY3GmL83muetLAA2h8A5fUHjJgqq2Ab"}}]
|
||||
@@ -4,13 +4,14 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
"github.com/MetaMask/go-did-it"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy/limits"
|
||||
)
|
||||
|
||||
func OptionalDID(s *string) (did.DID, error) {
|
||||
if s == nil {
|
||||
return did.Undef, nil
|
||||
return nil, nil
|
||||
}
|
||||
return did.Parse(*s)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy/limits"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
// Package varsig implements the portion of the [varsig specification]
|
||||
// that's needed for the UCAN [Envelope].
|
||||
//
|
||||
// While the [Envelope] specification has a field that's labelled
|
||||
// "VarsigHeader", this field is actually the prefix, header and segments
|
||||
// of the body excluding the signature itself (which is a different field
|
||||
// in the [Envelope]).
|
||||
//
|
||||
// Given that [go-ucan] supports a limited number of public key types,
|
||||
// and that the signature isn't part of the resulting field, the values
|
||||
// that are used are constants. Note that for key types that are fully
|
||||
// specified in the [did:key], the [VarsigHeader] field isn't technically
|
||||
// needed and could theoretically conflict with the DID.
|
||||
//
|
||||
// Treating these values as constants has no impact when issuing or
|
||||
// delegating tokens. When decoding tokens, simply matching the strings
|
||||
// will allow us to detect errors but won't provide as much detail (e.g.
|
||||
// we can't indicate that the signature was incorrectly generated from
|
||||
// a DAG-JSON encoding.)
|
||||
//
|
||||
// [varsig specification]: https://github.com/ChainAgnostic/varsig
|
||||
// [Envelope]:https://github.com/ucan-wg/spec#envelope
|
||||
// [go-ucan]: https://github.com/ucan-wg/go-ucan
|
||||
package varsig
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/crypto/pb"
|
||||
"github.com/multiformats/go-multicodec"
|
||||
)
|
||||
|
||||
const (
|
||||
Prefix = 0x34
|
||||
)
|
||||
|
||||
// ErrUnknownHeader is returned when it's not possible to decode the
|
||||
// provided string into a libp2p public key type.
|
||||
var ErrUnknownHeader = errors.New("could not decode unknown header")
|
||||
|
||||
// ErrUnknownKeyType is returned when value provided is not a valid
|
||||
// libp2p public key type.
|
||||
var ErrUnknownKeyType = errors.New("could not encode unsupported key type")
|
||||
|
||||
var (
|
||||
decMap = headerToKeyType()
|
||||
encMap = keyTypeToHeader()
|
||||
)
|
||||
|
||||
// Decode returns either the pb.KeyType associated with the provided Header
|
||||
// or an error.
|
||||
//
|
||||
// Currently, only the four key types supported by the [go-libp2p/core/crypto]
|
||||
// library are supported.
|
||||
//
|
||||
// [go-libp2p/core/crypto]: github.com/libp2p/go-libp2p/core/crypto
|
||||
func Decode(header []byte) (pb.KeyType, error) {
|
||||
keyType, ok := decMap[string(header)]
|
||||
if !ok {
|
||||
return -1, fmt.Errorf("%w: %s", ErrUnknownHeader, header)
|
||||
}
|
||||
|
||||
return keyType, nil
|
||||
}
|
||||
|
||||
// Encode returns either the header associated with the provided pb.KeyType
|
||||
// or an error indicating the header was unknown.
|
||||
//
|
||||
// Currently, only the four key types supported by the [go-libp2p/core/crypto]
|
||||
// library are supported.
|
||||
//
|
||||
// [go-libp2p/core/crypto]: github.com/libp2p/go-libp2p/core/crypto
|
||||
func Encode(keyType pb.KeyType) ([]byte, error) {
|
||||
header, ok := encMap[keyType]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: %s", ErrUnknownKeyType, keyType.String())
|
||||
}
|
||||
|
||||
return []byte(header), nil
|
||||
}
|
||||
|
||||
func keyTypeToHeader() map[pb.KeyType]string {
|
||||
const rsaSigLen = 0x100
|
||||
|
||||
return map[pb.KeyType]string{
|
||||
pb.KeyType_RSA: header(
|
||||
Prefix,
|
||||
multicodec.RsaPub,
|
||||
multicodec.Sha2_256,
|
||||
rsaSigLen,
|
||||
multicodec.DagCbor,
|
||||
),
|
||||
pb.KeyType_Ed25519: header(
|
||||
Prefix,
|
||||
multicodec.Ed25519Pub,
|
||||
multicodec.DagCbor,
|
||||
),
|
||||
pb.KeyType_Secp256k1: header(
|
||||
Prefix,
|
||||
multicodec.Secp256k1Pub,
|
||||
multicodec.Sha2_256,
|
||||
multicodec.DagCbor,
|
||||
),
|
||||
pb.KeyType_ECDSA: header(
|
||||
Prefix,
|
||||
multicodec.Es256,
|
||||
multicodec.Sha2_256,
|
||||
multicodec.DagCbor,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func headerToKeyType() map[string]pb.KeyType {
|
||||
out := make(map[string]pb.KeyType, len(encMap))
|
||||
|
||||
for keyType, header := range encMap {
|
||||
out[header] = keyType
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func header(vals ...multicodec.Code) string {
|
||||
var buf []byte
|
||||
|
||||
for _, val := range vals {
|
||||
buf = binary.AppendUvarint(buf, uint64(val))
|
||||
}
|
||||
|
||||
return string(buf)
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package varsig_test
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/crypto/pb"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/token/internal/varsig"
|
||||
)
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
notAHeader := base64.RawStdEncoding.EncodeToString([]byte("not a header"))
|
||||
keyType, err := varsig.Decode([]byte(notAHeader))
|
||||
assert.Equal(t, pb.KeyType(-1), keyType)
|
||||
assert.ErrorIs(t, err, varsig.ErrUnknownHeader)
|
||||
}
|
||||
|
||||
func ExampleDecode() {
|
||||
hdr, err := base64.RawStdEncoding.DecodeString("NIUkEoACcQ")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
keyType, _ := varsig.Decode(hdr)
|
||||
fmt.Println(keyType.String())
|
||||
// Output:
|
||||
// RSA
|
||||
}
|
||||
|
||||
func TestEncode(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
header, err := varsig.Encode(pb.KeyType(99))
|
||||
assert.Nil(t, header)
|
||||
assert.ErrorIs(t, err, varsig.ErrUnknownKeyType)
|
||||
}
|
||||
|
||||
func ExampleEncode() {
|
||||
header, _ := varsig.Encode(pb.KeyType_RSA)
|
||||
fmt.Println(base64.RawStdEncoding.EncodeToString(header))
|
||||
|
||||
// Output:
|
||||
// NIUkEoACcQ
|
||||
}
|
||||
@@ -4,13 +4,13 @@ import "errors"
|
||||
|
||||
// Loading errors
|
||||
var (
|
||||
// ErrMissingDelegation
|
||||
// ErrMissingDelegation is returned when a loader can't find a delegation
|
||||
ErrMissingDelegation = errors.New("loader missing delegation for proof chain")
|
||||
)
|
||||
|
||||
// Time bound errors
|
||||
var (
|
||||
// ErrTokenExpired is returned if a token is invalid at execution time
|
||||
// ErrTokenInvalidNow is returned if a token is invalid at execution time
|
||||
ErrTokenInvalidNow = errors.New("token has expired")
|
||||
)
|
||||
|
||||
|
||||
@@ -7,14 +7,15 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/MetaMask/go-did-it"
|
||||
didkeyctl "github.com/MetaMask/go-did-it/controller/did-key"
|
||||
"github.com/MetaMask/go-did-it/crypto/ed25519"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipld/go-ipld-prime"
|
||||
"github.com/ipld/go-ipld-prime/codec/dagcbor"
|
||||
"github.com/ipld/go-ipld-prime/codec/dagjson"
|
||||
"github.com/ipld/go-ipld-prime/node/basicnode"
|
||||
"github.com/libp2p/go-libp2p/core/crypto"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/ucan-wg/go-ucan/token/invocation"
|
||||
)
|
||||
@@ -60,18 +61,18 @@ func ExampleNew() {
|
||||
fmt.Println(json)
|
||||
|
||||
// Expected CID and DAG-JSON output:
|
||||
// CID: bafyreid2n5q45vk4osned7k5huocbe3mxbisonh5vujepqftc5ftr543ae
|
||||
// CID: bafyreicgyuf6g7geoupyzpo3lgvyvsyhtc6tvnhiajkoo7lmy6woavkhra
|
||||
// Token (pretty DAG-JSON):
|
||||
// [
|
||||
// {
|
||||
// "/": {
|
||||
// "bytes": "gvyL7kdSkgmaDpDU/Qj9ohRwxYLCHER52HFMSFEqQqEcQC9qr4JCPP1f/WybvGGuVzYiA0Hx4JO+ohNz8BxUAA"
|
||||
// "bytes": "nWYKygq0TTooQGdQsfQWwMOCuX7+KYNRSyW8h4AcRoTIJHsYyG/mpU3iBhAdsAGhi8Brnm+/Z0tC2KoC/KUlDA"
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "h": {
|
||||
// "/": {
|
||||
// "bytes": "NO0BcQ"
|
||||
// "bytes": "NAHtAe0BE3E"
|
||||
// }
|
||||
// },
|
||||
// "ucan/inv@1.0.0-rc.1": {
|
||||
@@ -91,8 +92,8 @@ func ExampleNew() {
|
||||
// "uri": "https://example.com/blog/posts"
|
||||
// },
|
||||
// "cmd": "/crud/create",
|
||||
// "exp": 1729788921,
|
||||
// "iss": "did:key:z6MkhniGGyP88eZrq2dpMvUPdS2RQMhTUAWzcu6kVGUvEtCJ",
|
||||
// "exp": 1753965273,
|
||||
// "iss": "did:key:z6MknZNRLJ8zD2x5QhFuPsXYKAJ8PJpCe76bC5p7bGZXC5HD",
|
||||
// "meta": {
|
||||
// "env": "development",
|
||||
// "tags": [
|
||||
@@ -103,7 +104,7 @@ func ExampleNew() {
|
||||
// },
|
||||
// "nonce": {
|
||||
// "/": {
|
||||
// "bytes": "2xXPoZwWln1TfXIp"
|
||||
// "bytes": "y4kwaLuEHOPBUgrl"
|
||||
// }
|
||||
// },
|
||||
// "prf": [
|
||||
@@ -117,7 +118,7 @@ func ExampleNew() {
|
||||
// "/": "bafyreibkb66tpo2ixqx3fe5hmekkbuasrod6olt5bwm5u5pi726mduuwlq"
|
||||
// }
|
||||
// ],
|
||||
// "sub": "did:key:z6MktWuvPvBe5UyHnDGuEdw8aJ5qrhhwLG6jy7cQYM6ckP6P"
|
||||
// "sub": "did:key:z6MkqEBN9zEGU9Euh9toLGFkTaSguXPavv55yS9m3VnwWeMW"
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
@@ -144,18 +145,22 @@ func prettyDAGJSON(data []byte) (string, error) {
|
||||
return out.String(), nil
|
||||
}
|
||||
|
||||
func setupExampleNew() (privKey crypto.PrivKey, iss, sub did.DID, cmd command.Command, args map[string]any, prf []cid.Cid, meta map[string]any, errs error) {
|
||||
func setupExampleNew() (privKey ed25519.PrivateKey, iss, sub did.DID, cmd command.Command, args map[string]any, prf []cid.Cid, meta map[string]any, errs error) {
|
||||
var err error
|
||||
|
||||
privKey, iss, err = did.GenerateEd25519()
|
||||
_, privKey, err = ed25519.GenerateKeyPair()
|
||||
if err != nil {
|
||||
errs = errors.Join(errs, fmt.Errorf("failed to generate Issuer identity: %w", err))
|
||||
errs = errors.Join(errs, fmt.Errorf("failed to generate Ed25519 keypair: %w", err))
|
||||
return
|
||||
}
|
||||
iss = didkeyctl.FromPrivateKey(privKey)
|
||||
|
||||
_, sub, err = did.GenerateEd25519()
|
||||
_, privKeySub, err := ed25519.GenerateKeyPair()
|
||||
if err != nil {
|
||||
errs = errors.Join(errs, fmt.Errorf("failed to generate Subject identity: %w", err))
|
||||
errs = errors.Join(errs, fmt.Errorf("failed to generate Ed25519 keypair: %w", err))
|
||||
return
|
||||
}
|
||||
sub = didkeyctl.FromPrivateKey(privKeySub)
|
||||
|
||||
cmd, err = command.Parse("/crud/create")
|
||||
if err != nil {
|
||||
|
||||
@@ -14,9 +14,9 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/MetaMask/go-did-it"
|
||||
"github.com/ipfs/go-cid"
|
||||
|
||||
"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/meta"
|
||||
@@ -169,7 +169,7 @@ func (t *Token) Arguments() args.ReadOnly {
|
||||
return t.arguments.ReadOnly()
|
||||
}
|
||||
|
||||
// Proof() returns the ordered list of cid.Cid which reference the
|
||||
// Proof returns the ordered list of cid.Cid which reference the
|
||||
// delegation Tokens that authorize this invocation.
|
||||
// Ordering is from the leaf Delegation (with aud matching the invocation's iss)
|
||||
// to the root delegation.
|
||||
@@ -210,7 +210,7 @@ func (t *Token) IsValidNow() bool {
|
||||
return t.IsValidAt(time.Now())
|
||||
}
|
||||
|
||||
// IsValidNow verifies that the token can be used at the given time, based on expiration or "not before" fields.
|
||||
// IsValidAt 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 (t *Token) IsValidAt(ti time.Time) bool {
|
||||
if t.expiration != nil && ti.After(*t.expiration) {
|
||||
@@ -241,7 +241,7 @@ func (t *Token) validate() error {
|
||||
var errs error
|
||||
|
||||
requiredDID := func(id did.DID, fieldname string) {
|
||||
if !id.Defined() {
|
||||
if id == nil {
|
||||
errs = errors.Join(errs, fmt.Errorf(`a valid did is required for %s: %s`, fieldname, id.String()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
package invocation_test
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"testing"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/did/didtest"
|
||||
"github.com/ucan-wg/go-ucan/pkg/args"
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy/policytest"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation/delegationtest"
|
||||
"github.com/ucan-wg/go-ucan/token/internal/didtest"
|
||||
"github.com/ucan-wg/go-ucan/token/invocation"
|
||||
)
|
||||
|
||||
//go:embed testdata/new.dagjson
|
||||
var newDagJson []byte
|
||||
|
||||
const (
|
||||
missingPrivKeyCfg = "CAESQMjRvrEIjpPYRQKmkAGw/pV0XgE958rYa4vlnKJjl1zz/sdnGnyV1xKLJk8D39edyjhHWyqcpgFnozQK62SG16k="
|
||||
missingTknCIDStr = "bafyreigwypmw6eul6vadi6g6lnfbsfo2zck7gfzsbjoroqs3djhnzzc7mm"
|
||||
missingDIDStr = "did:key:z6MkwboxFsH3kEuehBZ5fLkRmxi68yv1u38swA4r9Jm2VRma"
|
||||
missingTknCIDStr = "bafyreigwypmw6eul6vadi6g6lnfbsfo2zck7gfzsbjoroqs3djhnzzc7mm"
|
||||
)
|
||||
|
||||
var emptyArguments = args.New()
|
||||
|
||||
@@ -1,25 +1,23 @@
|
||||
package invocation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/MetaMask/go-did-it/crypto"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipld/go-ipld-prime"
|
||||
"github.com/ipld/go-ipld-prime/codec"
|
||||
"github.com/ipld/go-ipld-prime/codec/dagcbor"
|
||||
"github.com/ipld/go-ipld-prime/codec/dagjson"
|
||||
"github.com/ipld/go-ipld-prime/datamodel"
|
||||
"github.com/libp2p/go-libp2p/core/crypto"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
"github.com/ucan-wg/go-ucan/token/internal/envelope"
|
||||
)
|
||||
|
||||
// ToSealed wraps the invocation token in an envelope, generates the
|
||||
// signature, encodes the result to DAG-CBOR and calculates the CID of
|
||||
// the resulting binary data.
|
||||
func (t *Token) ToSealed(privKey crypto.PrivKey) ([]byte, cid.Cid, error) {
|
||||
func (t *Token) ToSealed(privKey crypto.PrivateKeySigningBytes) ([]byte, cid.Cid, error) {
|
||||
data, err := t.ToDagCbor(privKey)
|
||||
if err != nil {
|
||||
return nil, cid.Undef, err
|
||||
@@ -34,7 +32,7 @@ func (t *Token) ToSealed(privKey crypto.PrivKey) ([]byte, cid.Cid, error) {
|
||||
}
|
||||
|
||||
// ToSealedWriter is the same as ToSealed but accepts an io.Writer.
|
||||
func (t *Token) ToSealedWriter(w io.Writer, privKey crypto.PrivKey) (cid.Cid, error) {
|
||||
func (t *Token) ToSealedWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes) (cid.Cid, error) {
|
||||
cidWriter := envelope.NewCIDWriter(w)
|
||||
|
||||
if err := t.ToDagCborWriter(cidWriter, privKey); err != nil {
|
||||
@@ -81,7 +79,7 @@ func FromSealedReader(r io.Reader) (*Token, cid.Cid, error) {
|
||||
|
||||
// Encode marshals a Token to the format specified by the provided
|
||||
// codec.Encoder.
|
||||
func (t *Token) Encode(privKey crypto.PrivKey, encFn codec.Encoder) ([]byte, error) {
|
||||
func (t *Token) Encode(privKey crypto.PrivateKeySigningBytes, encFn codec.Encoder) ([]byte, error) {
|
||||
node, err := t.toIPLD(privKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -91,7 +89,7 @@ func (t *Token) Encode(privKey crypto.PrivKey, encFn codec.Encoder) ([]byte, err
|
||||
}
|
||||
|
||||
// EncodeWriter is the same as Encode, but accepts an io.Writer.
|
||||
func (t *Token) EncodeWriter(w io.Writer, privKey crypto.PrivKey, encFn codec.Encoder) error {
|
||||
func (t *Token) EncodeWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes, encFn codec.Encoder) error {
|
||||
node, err := t.toIPLD(privKey)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -101,22 +99,22 @@ func (t *Token) EncodeWriter(w io.Writer, privKey crypto.PrivKey, encFn codec.En
|
||||
}
|
||||
|
||||
// ToDagCbor marshals the Token to the DAG-CBOR format.
|
||||
func (t *Token) ToDagCbor(privKey crypto.PrivKey) ([]byte, error) {
|
||||
func (t *Token) ToDagCbor(privKey crypto.PrivateKeySigningBytes) ([]byte, error) {
|
||||
return t.Encode(privKey, dagcbor.Encode)
|
||||
}
|
||||
|
||||
// ToDagCborWriter is the same as ToDagCbor, but it accepts an io.Writer.
|
||||
func (t *Token) ToDagCborWriter(w io.Writer, privKey crypto.PrivKey) error {
|
||||
func (t *Token) ToDagCborWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes) error {
|
||||
return t.EncodeWriter(w, privKey, dagcbor.Encode)
|
||||
}
|
||||
|
||||
// ToDagJson marshals the Token to the DAG-JSON format.
|
||||
func (t *Token) ToDagJson(privKey crypto.PrivKey) ([]byte, error) {
|
||||
func (t *Token) ToDagJson(privKey crypto.PrivateKeySigningBytes) ([]byte, error) {
|
||||
return t.Encode(privKey, dagjson.Encode)
|
||||
}
|
||||
|
||||
// ToDagJsonWriter is the same as ToDagJson, but it accepts an io.Writer.
|
||||
func (t *Token) ToDagJsonWriter(w io.Writer, privKey crypto.PrivKey) error {
|
||||
func (t *Token) ToDagJsonWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes) error {
|
||||
return t.EncodeWriter(w, privKey, dagjson.Encode)
|
||||
}
|
||||
|
||||
@@ -193,19 +191,10 @@ func FromIPLD(node datamodel.Node) (*Token, error) {
|
||||
return tkn, err
|
||||
}
|
||||
|
||||
func (t *Token) toIPLD(privKey crypto.PrivKey) (datamodel.Node, error) {
|
||||
// sanity check that privKey and issuer are matching
|
||||
issPub, err := t.issuer.PubKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !issPub.Equals(privKey.GetPublic()) {
|
||||
return nil, fmt.Errorf("private key doesn't match the issuer")
|
||||
}
|
||||
|
||||
func (t *Token) toIPLD(privKey crypto.PrivateKeySigningBytes) (datamodel.Node, error) {
|
||||
var aud *string
|
||||
|
||||
if t.audience != did.Undef {
|
||||
if t.audience != nil {
|
||||
a := t.audience.String()
|
||||
aud = &a
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ package invocation
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/MetaMask/go-did-it"
|
||||
"github.com/ipfs/go-cid"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
"github.com/ucan-wg/go-ucan/pkg/args"
|
||||
)
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ func (t *Token) verifyProofs(delegations []*delegation.Token) error {
|
||||
cmd := t.command
|
||||
iss := t.issuer
|
||||
sub := t.subject
|
||||
if t.audience.Defined() {
|
||||
if t.audience != nil {
|
||||
sub = t.audience
|
||||
}
|
||||
|
||||
@@ -68,13 +68,13 @@ func (t *Token) verifyProofs(delegations []*delegation.Token) error {
|
||||
dlg := delegations[i]
|
||||
|
||||
// The Subject of each delegation must equal the invocation's Subject (or Audience if defined). - 4f
|
||||
if dlg.Subject() != sub {
|
||||
if !dlg.Subject().Equal(sub) {
|
||||
return fmt.Errorf("%w: delegation %s, expected %s, got %s", ErrWrongSub, dlgCid, sub, dlg.Subject())
|
||||
}
|
||||
|
||||
// The first proof must be issued to the Invoker (audience DID). - 4c
|
||||
// The Issuer of each delegation must be the Audience in the next one. - 4d
|
||||
if dlg.Audience() != iss {
|
||||
if !dlg.Audience().Equal(iss) {
|
||||
return fmt.Errorf("%w: delegation %s, expected %s, got %s", ErrBrokenChain, dlgCid, iss, dlg.Audience())
|
||||
}
|
||||
iss = dlg.Issuer()
|
||||
@@ -87,7 +87,7 @@ func (t *Token) verifyProofs(delegations []*delegation.Token) error {
|
||||
}
|
||||
|
||||
// The last prf value must be a root delegation (have the issuer field match the Subject field) - 4e
|
||||
if last := delegations[len(delegations)-1]; last.Issuer() != last.Subject() {
|
||||
if last := delegations[len(delegations)-1]; !last.Issuer().Equal(last.Subject()) {
|
||||
return fmt.Errorf("%w: expected %s, got %s", ErrLastNotRoot, last.Subject(), last.Issuer())
|
||||
}
|
||||
|
||||
|
||||
@@ -2,27 +2,26 @@ package invocation_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/crypto"
|
||||
"github.com/MetaMask/go-did-it/crypto"
|
||||
"github.com/MetaMask/go-did-it/crypto/ed25519"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gotest.tools/v3/golden"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/did/didtest"
|
||||
"github.com/ucan-wg/go-ucan/token/internal/envelope"
|
||||
"github.com/ucan-wg/go-ucan/token/invocation"
|
||||
)
|
||||
|
||||
const (
|
||||
issuerPrivKeyCfg = "CAESQK45xBfqIxRp7ZdRdck3tIJZKocCqvANQc925dCJhFwO7DJNA2j94zkF0TNx5mpXV0s6utfkFdHddWTaPVU6yZc="
|
||||
newCID = "zdpuAqY6Zypg4UnpbSUgDvYGneyFaTKaZevzxgSxV4rmv3Fpp"
|
||||
issuerPrivKeyCfg = "BeAgktAj8irGgWjp4PGk/fV67e5CcML/KRmmHSldco3etP5lRiuYQ+VVO/39ol3XXruJC8deSuBxoEXzgdYpYw=="
|
||||
newCID = "zdpuB1NjhETofEUp5iYzoHjSc2KKgZvSoT6FBaLMoVzzsxiR1"
|
||||
)
|
||||
|
||||
func TestSchemaRoundTrip(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
invocationJson := golden.Get(t, "new.dagjson")
|
||||
privKey := privKey(t, issuerPrivKeyCfg)
|
||||
|
||||
t.Run("via buffers", func(t *testing.T) {
|
||||
@@ -31,7 +30,7 @@ func TestSchemaRoundTrip(t *testing.T) {
|
||||
// format: dagJson --> PayloadModel --> dagCbor --> PayloadModel --> dagJson
|
||||
// function: DecodeDagJson() Seal() Unseal() EncodeDagJson()
|
||||
|
||||
p1, err := invocation.FromDagJson(invocationJson)
|
||||
p1, err := invocation.FromDagJson(newDagJson)
|
||||
require.NoError(t, err)
|
||||
|
||||
cborBytes, id, err := p1.ToSealed(privKey)
|
||||
@@ -45,13 +44,13 @@ func TestSchemaRoundTrip(t *testing.T) {
|
||||
readJson, err := p2.ToDagJson(privKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.JSONEq(t, string(invocationJson), string(readJson))
|
||||
assert.JSONEq(t, string(newDagJson), string(readJson))
|
||||
})
|
||||
|
||||
t.Run("via streaming", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
buf := bytes.NewBuffer(invocationJson)
|
||||
buf := bytes.NewBuffer(newDagJson)
|
||||
|
||||
// format: dagJson --> PayloadModel --> dagCbor --> PayloadModel --> dagJson
|
||||
// function: DecodeDagJson() Seal() Unseal() EncodeDagJson()
|
||||
@@ -71,25 +70,15 @@ func TestSchemaRoundTrip(t *testing.T) {
|
||||
readJson := &bytes.Buffer{}
|
||||
require.NoError(t, p2.ToDagJsonWriter(readJson, privKey))
|
||||
|
||||
assert.JSONEq(t, string(invocationJson), readJson.String())
|
||||
})
|
||||
|
||||
t.Run("fails with wrong PrivKey", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
p1, err := invocation.FromDagJson(invocationJson)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, err = p1.ToSealed(didtest.PersonaBob.PrivKey())
|
||||
require.EqualError(t, err, "private key doesn't match the issuer")
|
||||
assert.JSONEq(t, string(newDagJson), readJson.String())
|
||||
})
|
||||
}
|
||||
|
||||
func privKey(t require.TestingT, privKeyCfg string) crypto.PrivKey {
|
||||
privKeyMar, err := crypto.ConfigDecodeKey(privKeyCfg)
|
||||
func privKey(t require.TestingT, privKeyCfg string) crypto.PrivateKeySigningBytes {
|
||||
privBytes, err := base64.StdEncoding.DecodeString(privKeyCfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
privKey, err := crypto.UnmarshalPrivateKey(privKeyMar)
|
||||
privKey, err := ed25519.PrivateKeyFromBytes(privBytes)
|
||||
require.NoError(t, err)
|
||||
|
||||
return privKey
|
||||
|
||||
2
token/invocation/testdata/new.dagjson
vendored
2
token/invocation/testdata/new.dagjson
vendored
@@ -1 +1 @@
|
||||
[{"/":{"bytes":"o/vTvTs8SEkD9QL/eNhhW0fAng/SGBouywCbUnOfsF2RFHxaV02KTCyzgDxlJLZ2XN/Vk5igLmlKL3QIXMaeCQ"}},{"h":{"/":{"bytes":"NO0BcQ"}},"ucan/inv@1.0.0-rc.1":{"args":{"headers":{"Content-Type":"application/json"},"payload":{"body":"UCAN is great","draft":true,"title":"UCAN for Fun and Profit","topics":["authz","journal"]},"uri":"https://example.com/blog/posts"},"cmd":"/crud/create","exp":1730812145,"iss":"did:key:z6MkvMGkN5nbUQLBVqJhr13Zdqyh9rR1VuF16PuZbfocBxpv","meta":{"env":"development","tags":["blog","post","pr#123"]},"nonce":{"/":{"bytes":"q1AH6MJrqoTH6av7"}},"prf":[{"/":"bafyreigx3qxd2cndpe66j2mdssj773ecv7tqd7wovcnz5raguw6lj7sjoe"},{"/":"bafyreib34ira254zdqgehz6f2bhwme2ja2re3ltcalejv4x4tkcveujvpa"},{"/":"bafyreibkb66tpo2ixqx3fe5hmekkbuasrod6olt5bwm5u5pi726mduuwlq"}],"sub":"did:key:z6MkuFj35aiTL7YQiVMobuSeUQju92g7wZzufS3HAc6NFFcQ"}}]
|
||||
[{"/":{"bytes":"tRKNRahqwdyR6OpytuGIdcYI7HxXvKI5I594zznCLbN2C6WP5f8FIfIQlo0Nnqg4xFgKjJGAbIEVqeCZdib1Dw"}},{"h":{"/":{"bytes":"NAHtAe0BE3E"}},"ucan/inv@1.0.0-rc.1":{"args":{"headers":{"Content-Type":"application/json"},"payload":{"body":"UCAN is great","draft":true,"title":"UCAN for Fun and Profit","topics":["authz","journal"]},"uri":"https://example.com/blog/posts"},"cmd":"/crud/create","exp":1753965668,"iss":"did:key:z6MkuScdGeTmbWubyoWWpPmX9wkwdZAshkTcLKb1bf4Cyj8N","meta":{"env":"development","tags":["blog","post","pr#123"]},"nonce":{"/":{"bytes":"BBR5znl7VpRof4ac"}},"prf":[{"/":"bafyreigx3qxd2cndpe66j2mdssj773ecv7tqd7wovcnz5raguw6lj7sjoe"},{"/":"bafyreib34ira254zdqgehz6f2bhwme2ja2re3ltcalejv4x4tkcveujvpa"},{"/":"bafyreibkb66tpo2ixqx3fe5hmekkbuasrod6olt5bwm5u5pi726mduuwlq"}],"sub":"did:key:z6MkuQU8kqxCAUeurotHyrnMgkMUBtJN8ozYxkwctnop4zzB"}}]
|
||||
Reference in New Issue
Block a user