feat(cid): calculate the CID for decoded and newly created Tokeners
This commit is contained in:
@@ -6,11 +6,13 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ipfs/go-cid"
|
||||||
"github.com/libp2p/go-libp2p/core/crypto"
|
"github.com/libp2p/go-libp2p/core/crypto"
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/capability/command"
|
"github.com/ucan-wg/go-ucan/capability/command"
|
||||||
"github.com/ucan-wg/go-ucan/capability/policy"
|
"github.com/ucan-wg/go-ucan/capability/policy"
|
||||||
"github.com/ucan-wg/go-ucan/did"
|
"github.com/ucan-wg/go-ucan/did"
|
||||||
|
"github.com/ucan-wg/go-ucan/internal/envelope"
|
||||||
"github.com/ucan-wg/go-ucan/pkg/meta"
|
"github.com/ucan-wg/go-ucan/pkg/meta"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -33,6 +35,8 @@ type Token struct {
|
|||||||
notBefore *time.Time
|
notBefore *time.Time
|
||||||
// The timestamp at which the Invocation becomes invalid
|
// The timestamp at which the Invocation becomes invalid
|
||||||
expiration *time.Time
|
expiration *time.Time
|
||||||
|
// The CID of the Token when enclosed in an Envelope and encoded to DAG-CBOR
|
||||||
|
cid cid.Cid
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a validated Token from the provided parameters and options.
|
// New creates a validated Token from the provided parameters and options.
|
||||||
@@ -69,6 +73,18 @@ func New(privKey crypto.PrivKey, aud did.DID, cmd command.Command, pol policy.Po
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cbor, err := tkn.ToDagCbor(privKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := envelope.CIDFromBytes(cbor)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tkn.cid = id
|
||||||
|
|
||||||
return tkn, nil
|
return tkn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,6 +148,12 @@ func (t *Token) Expiration() *time.Time {
|
|||||||
return t.expiration
|
return t.expiration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CID returns the content identifier of the Token model when enclosed
|
||||||
|
// in an Envelope and encoded to DAG-CBOR.
|
||||||
|
func (t *Token) CID() cid.Cid {
|
||||||
|
return t.cid
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Token) validate() error {
|
func (t *Token) validate() error {
|
||||||
var errs error
|
var errs error
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/libp2p/go-libp2p/core/crypto"
|
"github.com/libp2p/go-libp2p/core/crypto"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"gotest.tools/v3/golden"
|
"gotest.tools/v3/golden"
|
||||||
|
|
||||||
@@ -13,6 +14,7 @@ import (
|
|||||||
"github.com/ucan-wg/go-ucan/capability/policy"
|
"github.com/ucan-wg/go-ucan/capability/policy"
|
||||||
"github.com/ucan-wg/go-ucan/delegation"
|
"github.com/ucan-wg/go-ucan/delegation"
|
||||||
"github.com/ucan-wg/go-ucan/did"
|
"github.com/ucan-wg/go-ucan/did"
|
||||||
|
"github.com/ucan-wg/go-ucan/internal/envelope"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -64,6 +66,9 @@ const (
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
`
|
`
|
||||||
|
|
||||||
|
newCID = "zdpuAn9JgGPvnt2WCmTaKktZdbuvcVGTg9bUT5kQaufwUtZ6e"
|
||||||
|
rootCID = "zdpuAkgGmUp5JrXvehGuuw9JA8DLQKDaxtK3R8brDQQVC2i5X"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConstructors(t *testing.T) {
|
func TestConstructors(t *testing.T) {
|
||||||
@@ -101,6 +106,7 @@ func TestConstructors(t *testing.T) {
|
|||||||
t.Log(string(data))
|
t.Log(string(data))
|
||||||
|
|
||||||
golden.Assert(t, string(data), "new.dagjson")
|
golden.Assert(t, string(data), "new.dagjson")
|
||||||
|
assert.Equal(t, newCID, envelope.CIDToBase58BTC(dlg.CID()))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Root", func(t *testing.T) {
|
t.Run("Root", func(t *testing.T) {
|
||||||
@@ -120,6 +126,7 @@ func TestConstructors(t *testing.T) {
|
|||||||
t.Log(string(data))
|
t.Log(string(data))
|
||||||
|
|
||||||
golden.Assert(t, string(data), "root.dagjson")
|
golden.Assert(t, string(data), "root.dagjson")
|
||||||
|
assert.Equal(t, rootCID, envelope.CIDToBase58BTC(dlg.CID()))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package delegation
|
|||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"github.com/ipfs/go-cid"
|
||||||
"github.com/ipld/go-ipld-prime"
|
"github.com/ipld/go-ipld-prime"
|
||||||
"github.com/ipld/go-ipld-prime/codec"
|
"github.com/ipld/go-ipld-prime/codec"
|
||||||
"github.com/ipld/go-ipld-prime/codec/dagcbor"
|
"github.com/ipld/go-ipld-prime/codec/dagcbor"
|
||||||
@@ -100,19 +101,19 @@ func (t *Token) ToIPLD(privKey crypto.PrivKey) (datamodel.Node, error) {
|
|||||||
//
|
//
|
||||||
// An error is returned if the conversion fails, or if the resulting
|
// An error is returned if the conversion fails, or if the resulting
|
||||||
// View is invalid.
|
// View is invalid.
|
||||||
func Decode(b []byte, decFn codec.Decoder) (*Token, error) {
|
func Decode(b []byte, decFn codec.Decoder) (*Token, cid.Cid, error) {
|
||||||
node, err := ipld.Decode(b, decFn)
|
node, err := ipld.Decode(b, decFn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, cid.Undef, err
|
||||||
}
|
}
|
||||||
return FromIPLD(node)
|
return FromIPLD(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeReader is the same as Decode, but accept an io.Reader.
|
// DecodeReader is the same as Decode, but accept an io.Reader.
|
||||||
func DecodeReader(r io.Reader, decFn codec.Decoder) (*Token, error) {
|
func DecodeReader(r io.Reader, decFn codec.Decoder) (*Token, cid.Cid, error) {
|
||||||
node, err := ipld.DecodeStreaming(r, decFn)
|
node, err := ipld.DecodeStreaming(r, decFn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, cid.Undef, err
|
||||||
}
|
}
|
||||||
return FromIPLD(node)
|
return FromIPLD(node)
|
||||||
}
|
}
|
||||||
@@ -121,12 +122,22 @@ func DecodeReader(r io.Reader, decFn codec.Decoder) (*Token, error) {
|
|||||||
//
|
//
|
||||||
// An error is returned if the conversion fails, or if the resulting
|
// An error is returned if the conversion fails, or if the resulting
|
||||||
// View is invalid.
|
// View is invalid.
|
||||||
func FromDagCbor(data []byte) (*Token, error) {
|
func FromDagCbor(data []byte) (*Token, cid.Cid, error) {
|
||||||
return Decode(data, dagcbor.Decode)
|
pay, id, err := envelope.FromDagCbor[*tokenPayloadModel](data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, cid.Undef, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tkn, err := tokenFromModel(*pay)
|
||||||
|
if err != nil {
|
||||||
|
return nil, cid.Undef, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tkn, id, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromDagCborReader is the same as FromDagCbor, but accept an io.Reader.
|
// FromDagCborReader is the same as FromDagCbor, but accept an io.Reader.
|
||||||
func FromDagCborReader(r io.Reader) (*Token, error) {
|
func FromDagCborReader(r io.Reader) (*Token, cid.Cid, error) {
|
||||||
return DecodeReader(r, dagcbor.Decode)
|
return DecodeReader(r, dagcbor.Decode)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,12 +145,12 @@ func FromDagCborReader(r io.Reader) (*Token, error) {
|
|||||||
//
|
//
|
||||||
// An error is returned if the conversion fails, or if the resulting
|
// An error is returned if the conversion fails, or if the resulting
|
||||||
// View is invalid.
|
// View is invalid.
|
||||||
func FromDagJson(data []byte) (*Token, error) {
|
func FromDagJson(data []byte) (*Token, cid.Cid, error) {
|
||||||
return Decode(data, dagjson.Decode)
|
return Decode(data, dagjson.Decode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromDagJsonReader is the same as FromDagJson, but accept an io.Reader.
|
// FromDagJsonReader is the same as FromDagJson, but accept an io.Reader.
|
||||||
func FromDagJsonReader(r io.Reader) (*Token, error) {
|
func FromDagJsonReader(r io.Reader) (*Token, cid.Cid, error) {
|
||||||
return DecodeReader(r, dagjson.Decode)
|
return DecodeReader(r, dagjson.Decode)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,11 +158,16 @@ func FromDagJsonReader(r io.Reader) (*Token, error) {
|
|||||||
//
|
//
|
||||||
// An error is returned if the conversion fails, or if the resulting
|
// An error is returned if the conversion fails, or if the resulting
|
||||||
// View is invalid.
|
// View is invalid.
|
||||||
func FromIPLD(node datamodel.Node) (*Token, error) {
|
func FromIPLD(node datamodel.Node) (*Token, cid.Cid, error) {
|
||||||
tkn, _, err := envelope.FromIPLD[*tokenPayloadModel](node) // TODO add CID to view
|
pay, id, err := envelope.FromIPLD[*tokenPayloadModel](node) // TODO add CID to view
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, cid.Undef, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return tokenFromModel(*tkn)
|
tkn, err := tokenFromModel(*pay)
|
||||||
|
if err != nil {
|
||||||
|
return nil, cid.Undef, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tkn, id, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,43 +10,14 @@ import (
|
|||||||
"gotest.tools/v3/golden"
|
"gotest.tools/v3/golden"
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/delegation"
|
"github.com/ucan-wg/go-ucan/delegation"
|
||||||
|
"github.com/ucan-wg/go-ucan/internal/envelope"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed delegation.ipldsch
|
//go:embed delegation.ipldsch
|
||||||
var schemaBytes []byte
|
var schemaBytes []byte
|
||||||
|
|
||||||
func TestSchemaRoundTrip(t *testing.T) {
|
func TestSchemaRoundTrip(t *testing.T) {
|
||||||
// const delegationJson = `
|
t.Parallel()
|
||||||
// {
|
|
||||||
// "aud":"did:key:def456",
|
|
||||||
// "cmd":"/foo/bar",
|
|
||||||
// "exp":123456,
|
|
||||||
// "iss":"did:key:abc123",
|
|
||||||
// "meta":{
|
|
||||||
// "bar":"baaar",
|
|
||||||
// "foo":"fooo"
|
|
||||||
// },
|
|
||||||
// "nbf":123456,
|
|
||||||
// "nonce":{
|
|
||||||
// "/":{
|
|
||||||
// "bytes":"c3VwZXItcmFuZG9t"
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// "pol":[
|
|
||||||
// ["==", ".status", "draft"],
|
|
||||||
// ["all", ".reviewer", [
|
|
||||||
// ["like", ".email", "*@example.com"]]
|
|
||||||
// ],
|
|
||||||
// ["any", ".tags", [
|
|
||||||
// ["or", [
|
|
||||||
// ["==", ".", "news"],
|
|
||||||
// ["==", ".", "press"]]
|
|
||||||
// ]]
|
|
||||||
// ]
|
|
||||||
// ],
|
|
||||||
// "sub":""
|
|
||||||
// }
|
|
||||||
// `
|
|
||||||
|
|
||||||
delegationJson := golden.Get(t, "new.dagjson")
|
delegationJson := golden.Get(t, "new.dagjson")
|
||||||
privKey := privKey(t, issuerPrivKeyCfg)
|
privKey := privKey(t, issuerPrivKeyCfg)
|
||||||
@@ -54,20 +25,22 @@ func TestSchemaRoundTrip(t *testing.T) {
|
|||||||
// format: dagJson --> PayloadModel --> dagCbor --> PayloadModel --> dagJson
|
// format: dagJson --> PayloadModel --> dagCbor --> PayloadModel --> dagJson
|
||||||
// function: DecodeDagJson() EncodeDagCbor() DecodeDagCbor() EncodeDagJson()
|
// function: DecodeDagJson() EncodeDagCbor() DecodeDagCbor() EncodeDagJson()
|
||||||
|
|
||||||
p1, err := delegation.FromDagJson([]byte(delegationJson))
|
p1, id1, err := delegation.FromDagJson([]byte(delegationJson))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, newCID, envelope.CIDToBase58BTC(id1))
|
||||||
|
|
||||||
cborBytes, err := p1.ToDagCbor(privKey)
|
cborBytes, err := p1.ToDagCbor(privKey)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
fmt.Println("cborBytes length", len(cborBytes))
|
fmt.Println("cborBytes length", len(cborBytes))
|
||||||
fmt.Println("cbor", string(cborBytes))
|
fmt.Println("cbor", string(cborBytes))
|
||||||
|
|
||||||
p2, err := delegation.FromDagCbor(cborBytes)
|
p2, id2, err := delegation.FromDagCbor(cborBytes)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
fmt.Println("read Cbor", p2)
|
fmt.Println("read Cbor", p2)
|
||||||
|
|
||||||
readJson, err := p2.ToDagJson(privKey)
|
readJson, err := p2.ToDagJson(privKey)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, newCID, envelope.CIDToBase58BTC(id2))
|
||||||
fmt.Println("readJson length", len(readJson))
|
fmt.Println("readJson length", len(readJson))
|
||||||
fmt.Println("json: ", string(readJson))
|
fmt.Println("json: ", string(readJson))
|
||||||
|
|
||||||
|
|||||||
49
internal/envelope/cid.go
Normal file
49
internal/envelope/cid.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package envelope
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ipfs/go-cid"
|
||||||
|
"github.com/ipld/go-ipld-prime"
|
||||||
|
"github.com/ipld/go-ipld-prime/codec/dagcbor"
|
||||||
|
"github.com/libp2p/go-libp2p/core/crypto"
|
||||||
|
"github.com/multiformats/go-multibase"
|
||||||
|
"github.com/multiformats/go-multicodec"
|
||||||
|
"github.com/multiformats/go-multihash"
|
||||||
|
)
|
||||||
|
|
||||||
|
var b58BTCEnc = multibase.MustNewEncoder(multibase.Base58BTC)
|
||||||
|
|
||||||
|
// CIDToBase56BTC is a utility method to convert a CIDv1 to the canonical
|
||||||
|
// string representation used by UCAN.
|
||||||
|
func CIDToBase58BTC(id cid.Cid) string {
|
||||||
|
return id.Encode(b58BTCEnc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CID returns the UCAN content identifier a Tokener.
|
||||||
|
func CID(privKey crypto.PrivKey, token Tokener) (cid.Cid, error) {
|
||||||
|
data, err := ToDagCbor(privKey, token)
|
||||||
|
if err != nil {
|
||||||
|
return cid.Undef, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return CIDFromBytes(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CIDFromBytes returns the UCAN content identifier for an arbitrary slice
|
||||||
|
// of bytes.
|
||||||
|
func CIDFromBytes(b []byte) (cid.Cid, error) {
|
||||||
|
return cid.V1Builder{
|
||||||
|
Codec: uint64(multicodec.DagCbor),
|
||||||
|
MhType: multihash.SHA2_256,
|
||||||
|
MhLength: 0,
|
||||||
|
}.Sum(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CIDFromIPLD returns the UCAN content identifier for an ipld.Node.
|
||||||
|
func CIDFromIPLD(node ipld.Node) (cid.Cid, error) {
|
||||||
|
data, err := ipld.Encode(node, dagcbor.Encode)
|
||||||
|
if err != nil {
|
||||||
|
return cid.Undef, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return CIDFromBytes(data)
|
||||||
|
}
|
||||||
25
internal/envelope/cid_test.go
Normal file
25
internal/envelope/cid_test.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package envelope_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/multiformats/go-multicodec"
|
||||||
|
"github.com/multiformats/go-multihash"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/ucan-wg/go-ucan/internal/envelope"
|
||||||
|
"gotest.tools/v3/golden"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCid(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
expData := golden.Get(t, "example.dagcbor")
|
||||||
|
expHash, err := multihash.Sum(expData, uint64(multicodec.Sha2_256), -1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
id, err := envelope.CID(examplePrivKey(t), newExample(t))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, exampleCID, envelope.CIDToBase58BTC(id))
|
||||||
|
assert.Equal(t, expHash, id.Hash())
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
exampleCID = "zdpuAyw6R5HvKSPzztuzXNYFx3ZGoMHMuAsXL6u3xLGQriRXQ"
|
||||||
exampleDID = "did:key:z6MkpuK2Amsu1RqcLGgmHHQHhvmeXCCBVsM4XFSg2cCyg4Nh"
|
exampleDID = "did:key:z6MkpuK2Amsu1RqcLGgmHHQHhvmeXCCBVsM4XFSg2cCyg4Nh"
|
||||||
exampleGreeting = "world"
|
exampleGreeting = "world"
|
||||||
examplePrivKeyCfg = "CAESQP9v2uqECTuIi45dyg3znQvsryvf2IXmOF/6aws6aCehm0FVrj0zHR5RZSDxWNjcpcJqsGym3sjCungX9Zt5oA4="
|
examplePrivKeyCfg = "CAESQP9v2uqECTuIi45dyg3znQvsryvf2IXmOF/6aws6aCehm0FVrj0zHR5RZSDxWNjcpcJqsGym3sjCungX9Zt5oA4="
|
||||||
|
|||||||
@@ -90,7 +90,24 @@ func DecodeReader[T Tokener](r io.Reader, decFn codec.Decoder) (T, cid.Cid, erro
|
|||||||
// An error is returned if the conversion fails, or if the resulting
|
// An error is returned if the conversion fails, or if the resulting
|
||||||
// Tokener is invalid.
|
// Tokener is invalid.
|
||||||
func FromDagCbor[T Tokener](b []byte) (T, cid.Cid, error) {
|
func FromDagCbor[T Tokener](b []byte) (T, cid.Cid, error) {
|
||||||
return Decode[T](b, dagcbor.Decode)
|
undef := *new(T)
|
||||||
|
|
||||||
|
node, err := ipld.Decode(b, dagcbor.Decode)
|
||||||
|
if err != nil {
|
||||||
|
return undef, cid.Undef, err
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := CIDFromBytes(b)
|
||||||
|
if err != nil {
|
||||||
|
return undef, cid.Undef, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tkn, err := fromIPLD[T](node)
|
||||||
|
if err != nil {
|
||||||
|
return undef, cid.Undef, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tkn, id, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromDagCborReader is the same as FromDagCbor, but accept an io.Reader.
|
// FromDagCborReader is the same as FromDagCbor, but accept an io.Reader.
|
||||||
@@ -118,29 +135,45 @@ func FromDagJsonReader[T Tokener](r io.Reader) (T, cid.Cid, error) {
|
|||||||
func FromIPLD[T Tokener](node datamodel.Node) (T, cid.Cid, error) {
|
func FromIPLD[T Tokener](node datamodel.Node) (T, cid.Cid, error) {
|
||||||
undef := *new(T)
|
undef := *new(T)
|
||||||
|
|
||||||
signatureNode, err := node.LookupByIndex(0)
|
id, err := CIDFromIPLD(node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return undef, cid.Undef, err
|
return undef, cid.Undef, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tkn, err := fromIPLD[T](node)
|
||||||
|
if err != nil {
|
||||||
|
return undef, cid.Undef, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tkn, id, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fromIPLD[T Tokener](node datamodel.Node) (T, error) {
|
||||||
|
undef := *new(T)
|
||||||
|
|
||||||
|
signatureNode, err := node.LookupByIndex(0)
|
||||||
|
if err != nil {
|
||||||
|
return undef, err
|
||||||
|
}
|
||||||
|
|
||||||
signature, err := signatureNode.AsBytes()
|
signature, err := signatureNode.AsBytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return undef, cid.Undef, err
|
return undef, err
|
||||||
}
|
}
|
||||||
|
|
||||||
sigPayloadNode, err := node.LookupByIndex(1)
|
sigPayloadNode, err := node.LookupByIndex(1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return undef, cid.Undef, err
|
return undef, err
|
||||||
}
|
}
|
||||||
|
|
||||||
varsigHeaderNode, err := sigPayloadNode.LookupByString(varsigHeaderKey)
|
varsigHeaderNode, err := sigPayloadNode.LookupByString(varsigHeaderKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return undef, cid.Undef, err
|
return undef, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenPayloadNode, err := sigPayloadNode.LookupByString(undef.Tag())
|
tokenPayloadNode, err := sigPayloadNode.LookupByString(undef.Tag())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return undef, cid.Undef, err
|
return undef, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// This needs to be done before converting this node to it's schema
|
// This needs to be done before converting this node to it's schema
|
||||||
@@ -148,7 +181,7 @@ func FromIPLD[T Tokener](node datamodel.Node) (T, cid.Cid, error) {
|
|||||||
// to use the wire name).
|
// to use the wire name).
|
||||||
issuerNode, err := tokenPayloadNode.LookupByString("iss")
|
issuerNode, err := tokenPayloadNode.LookupByString("iss")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return undef, cid.Undef, err
|
return undef, err
|
||||||
}
|
}
|
||||||
// ^^^
|
// ^^^
|
||||||
|
|
||||||
@@ -160,7 +193,7 @@ func FromIPLD[T Tokener](node datamodel.Node) (T, cid.Cid, error) {
|
|||||||
|
|
||||||
err = nb.AssignNode(tokenPayloadNode)
|
err = nb.AssignNode(tokenPayloadNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return undef, cid.Undef, err
|
return undef, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenPayloadNode = nb.Build()
|
tokenPayloadNode = nb.Build()
|
||||||
@@ -168,12 +201,12 @@ func FromIPLD[T Tokener](node datamodel.Node) (T, cid.Cid, error) {
|
|||||||
|
|
||||||
tokenPayload := bindnode.Unwrap(tokenPayloadNode)
|
tokenPayload := bindnode.Unwrap(tokenPayloadNode)
|
||||||
if tokenPayload == nil {
|
if tokenPayload == nil {
|
||||||
return undef, cid.Undef, errors.New("failed to Unwrap the TokenPayload")
|
return undef, errors.New("failed to Unwrap the TokenPayload")
|
||||||
}
|
}
|
||||||
|
|
||||||
tkn, ok := tokenPayload.(T)
|
tkn, ok := tokenPayload.(T)
|
||||||
if !ok {
|
if !ok {
|
||||||
return undef, cid.Undef, errors.New("failed to assert the TokenPayload type as *token.Token")
|
return undef, errors.New("failed to assert the TokenPayload type as *token.Token")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the issuer's DID contains a public key with a type that
|
// Check that the issuer's DID contains a public key with a type that
|
||||||
@@ -181,45 +214,45 @@ func FromIPLD[T Tokener](node datamodel.Node) (T, cid.Cid, error) {
|
|||||||
// vvv
|
// vvv
|
||||||
issuer, err := issuerNode.AsString()
|
issuer, err := issuerNode.AsString()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return undef, cid.Undef, err
|
return undef, err
|
||||||
}
|
}
|
||||||
|
|
||||||
issuerDID, err := did.Parse(issuer)
|
issuerDID, err := did.Parse(issuer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return undef, cid.Undef, err
|
return undef, err
|
||||||
}
|
}
|
||||||
|
|
||||||
issuerPubKey, err := issuerDID.PubKey()
|
issuerPubKey, err := issuerDID.PubKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return undef, cid.Undef, err
|
return undef, err
|
||||||
}
|
}
|
||||||
|
|
||||||
issuerVarsigHeader, err := varsig.Encode(issuerPubKey.Type())
|
issuerVarsigHeader, err := varsig.Encode(issuerPubKey.Type())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return undef, cid.Undef, err
|
return undef, err
|
||||||
}
|
}
|
||||||
|
|
||||||
varsigHeader, err := varsigHeaderNode.AsBytes()
|
varsigHeader, err := varsigHeaderNode.AsBytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return undef, cid.Undef, err
|
return undef, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if string(varsigHeader) != string(issuerVarsigHeader) {
|
if string(varsigHeader) != string(issuerVarsigHeader) {
|
||||||
return undef, cid.Undef, errors.New("the VarsigHeader key type doesn't match the issuer's key type")
|
return undef, errors.New("the VarsigHeader key type doesn't match the issuer's key type")
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := ipld.Encode(sigPayloadNode, dagcbor.Encode)
|
data, err := ipld.Encode(sigPayloadNode, dagcbor.Encode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return undef, cid.Undef, err
|
return undef, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ok, err = issuerPubKey.Verify(data, signature)
|
ok, err = issuerPubKey.Verify(data, signature)
|
||||||
if err != nil || !ok {
|
if err != nil || !ok {
|
||||||
return undef, cid.Undef, errors.New("failed to verify the token's signature")
|
return undef, errors.New("failed to verify the token's signature")
|
||||||
}
|
}
|
||||||
// ^^^
|
// ^^^
|
||||||
|
|
||||||
return tkn, cid.Undef, nil
|
return tkn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode marshals a Tokener to the format specified by the provided
|
// Encode marshals a Tokener to the format specified by the provided
|
||||||
|
|||||||
@@ -59,35 +59,69 @@ func TestEncode(t *testing.T) {
|
|||||||
func TestRoundtrip(t *testing.T) {
|
func TestRoundtrip(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
t.Run("via FromDagCbor/ToDagCbor", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
dataIn := golden.Get(t, exampleDAGCBORFilename)
|
||||||
|
|
||||||
|
tkn, id, err := envelope.FromDagCbor[*Example](dataIn)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, exampleGreeting, tkn.Hello)
|
||||||
|
assert.Equal(t, exampleDID, tkn.Issuer)
|
||||||
|
assert.Equal(t, exampleCID, envelope.CIDToBase58BTC(id))
|
||||||
|
|
||||||
|
dataOut, err := envelope.ToDagCbor(examplePrivKey(t), newExample(t))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, dataIn, dataOut)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("via FromDagCborReader/ToDagCborWriter", func(t *testing.T) {
|
t.Run("via FromDagCborReader/ToDagCborWriter", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
data := golden.Get(t, exampleDAGCBORFilename)
|
data := golden.Get(t, exampleDAGCBORFilename)
|
||||||
|
|
||||||
tkn, _, err := envelope.FromDagCborReader[*Example](bytes.NewReader(data))
|
tkn, id, err := envelope.FromDagCborReader[*Example](bytes.NewReader(data))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, exampleGreeting, tkn.Hello)
|
assert.Equal(t, exampleGreeting, tkn.Hello)
|
||||||
assert.Equal(t, exampleDID, tkn.Issuer)
|
assert.Equal(t, exampleDID, tkn.Issuer)
|
||||||
|
assert.Equal(t, exampleCID, envelope.CIDToBase58BTC(id))
|
||||||
|
|
||||||
w := &bytes.Buffer{}
|
w := &bytes.Buffer{}
|
||||||
require.NoError(t, envelope.ToDagCborWriter(w, examplePrivKey(t), newExample(t)))
|
require.NoError(t, envelope.ToDagCborWriter(w, examplePrivKey(t), newExample(t)))
|
||||||
assert.Equal(t, data, w.Bytes())
|
assert.Equal(t, data, w.Bytes())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("via FromDagCbor/ToDagCbor", func(t *testing.T) {
|
t.Run("via FromDagJson/ToDagJson", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
dataIn := golden.Get(t, exampleDAGCBORFilename)
|
dataIn := golden.Get(t, exampleDAGJSONFilename)
|
||||||
|
|
||||||
tkn, _, err := envelope.FromDagCbor[*Example](dataIn)
|
tkn, id, err := envelope.FromDagJson[*Example](dataIn)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, exampleGreeting, tkn.Hello)
|
assert.Equal(t, exampleGreeting, tkn.Hello)
|
||||||
assert.Equal(t, exampleDID, tkn.Issuer)
|
assert.Equal(t, exampleDID, tkn.Issuer)
|
||||||
|
assert.Equal(t, exampleCID, envelope.CIDToBase58BTC(id))
|
||||||
|
|
||||||
dataOut, err := envelope.ToDagCbor(examplePrivKey(t), newExample(t))
|
dataOut, err := envelope.ToDagJson(examplePrivKey(t), newExample(t))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, dataIn, dataOut)
|
assert.Equal(t, dataIn, dataOut)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("via FromDagJsonReader/ToDagJsonrWriter", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
data := golden.Get(t, exampleDAGJSONFilename)
|
||||||
|
|
||||||
|
tkn, id, err := envelope.FromDagJsonReader[*Example](bytes.NewReader(data))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, exampleGreeting, tkn.Hello)
|
||||||
|
assert.Equal(t, exampleDID, tkn.Issuer)
|
||||||
|
assert.Equal(t, exampleCID, envelope.CIDToBase58BTC(id))
|
||||||
|
|
||||||
|
w := &bytes.Buffer{}
|
||||||
|
require.NoError(t, envelope.ToDagJsonWriter(w, examplePrivKey(t), newExample(t)))
|
||||||
|
assert.Equal(t, data, w.Bytes())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFromIPLD_with_invalid_signature(t *testing.T) {
|
func TestFromIPLD_with_invalid_signature(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user