refactor(envelope): more tests/docs and functions not a type

This commit is contained in:
Steve Moyer
2024-09-18 07:50:02 -04:00
parent 40488dfc3d
commit 70dc12d68e
10 changed files with 596 additions and 6 deletions

2
go.mod
View File

@@ -13,11 +13,13 @@ require (
github.com/multiformats/go-multicodec v0.9.0
github.com/multiformats/go-varint v0.0.7
github.com/stretchr/testify v1.9.0
gotest.tools/v3 v3.5.1
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect

12
go.sum
View File

@@ -2,6 +2,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
@@ -25,8 +27,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/libp2p/go-libp2p v0.36.2 h1:BbqRkDaGC3/5xfaJakLV/BrpjlAuYqSB0lRvtzL3B/U=
github.com/libp2p/go-libp2p v0.36.2/go.mod h1:XO3joasRE4Eup8yCTTP/+kX+g92mOgRaadk46LmPhHY=
github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8=
github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
github.com/libp2p/go-libp2p v0.36.3 h1:NHz30+G7D8Y8YmznrVZZla0ofVANrvBl2c+oARfMeDQ=
github.com/libp2p/go-libp2p v0.36.3/go.mod h1:4Y5vFyCUiJuluEPmpnKYf6WFx5ViKPUYs/ixe9ANFZ8=
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
@@ -37,6 +39,8 @@ github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aG
github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI=
github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=
github.com/multiformats/go-multiaddr v0.13.0 h1:BCBzs61E3AGHcYYTv8dqRH43ZfyrqM8RXVPT8t13tLQ=
github.com/multiformats/go-multiaddr v0.13.0/go.mod h1:sBXrNzucqkFJhvKOiwwLyqamGa/P5EIXNPLovyhQCII=
github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g=
github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk=
github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg=
@@ -67,6 +71,8 @@ github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvS
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -83,5 +89,7 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=

View File

@@ -0,0 +1,136 @@
package envelope_test
import (
_ "embed"
"encoding/base64"
"fmt"
"sync"
"testing"
"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/codec/dagcbor"
"github.com/ipld/go-ipld-prime/datamodel"
"github.com/ipld/go-ipld-prime/fluent/qp"
"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"
"github.com/ucan-wg/go-ucan/internal/envelope"
"gotest.tools/v3/golden"
)
const (
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"
invalidSignatureStr = "PZV6A2aI7n+MlyADqcqmWhkuyNrgUCDz+qSLSnI9bpasOwOhKUTx95m5Nu5CO/INa1LqzHGioD9+PVf6qdtTBK"
exampleDAGCBORFilename = "example.dagcbor"
exampleDAGJSONFilename = "example.dagjson"
)
//go:embed testdata/example.ipldsch
var schemaBytes []byte
var (
once sync.Once
ts *schema.TypeSystem
err error
)
func mustLoadSchema() *schema.TypeSystem {
once.Do(func() {
ts, err = ipld.LoadSchemaBytes(schemaBytes)
})
if err != nil {
panic(fmt.Errorf("failed to load IPLD schema: %s", err))
}
return ts
}
func exampleType() schema.Type {
return mustLoadSchema().TypeByName(exampleTypeName)
}
var _ envelope.Tokener = (*Example)(nil)
type Example struct {
Hello string
Issuer string
}
func newExample(t *testing.T) *Example {
t.Helper()
return &Example{
Hello: exampleGreeting,
Issuer: exampleDID,
}
}
func (e *Example) Prototype() schema.TypedPrototype {
return bindnode.Prototype(e, exampleType())
}
func (*Example) Tag() string {
return exampleTag
}
func exampleGoldenNode(t *testing.T) datamodel.Node {
t.Helper()
cbor := golden.Get(t, exampleDAGCBORFilename)
node, err := ipld.Decode(cbor, dagcbor.Decode)
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)
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 {
t.Helper()
invalidSig, err := base64.RawStdEncoding.DecodeString(invalidSignatureStr)
require.NoError(t, err)
envelNode := exampleGoldenNode(t)
sigPayloadNode, err := envelNode.LookupByIndex(1)
require.NoError(t, err)
node, err := qp.BuildList(basicnode.Prototype.Any, 2, func(la datamodel.ListAssembler) {
qp.ListEntry(la, qp.Bytes(invalidSig))
qp.ListEntry(la, qp.Node(sigPayloadNode))
})
require.NoError(t, err)
return node
}

330
internal/envelope/ipld.go Normal file
View File

@@ -0,0 +1,330 @@
// Package envelope provides functions that convert between wire-format
// encoding of a [UCAN] token's [Envelope] and the Go type representing
// 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
// cryptographic signature can be generated.
//
// Decoding functions in this package likewise perform the signature
// verification using a public key extracted from the TokenPayload as
// described by requirement two below.
//
// Types that wish to be marshaled and unmarshaled from the using
// is package have two requirements.
//
// 1. The type must implement the Tokener interface.
//
// 2. The IPLD Representation of the type must include an "iss"
// field when the TokenPayload is extracted from the Envelope.
// This field must contain the string representation of a
// "did:key" so that a public key can be extracted from the
//
// [Envelope]:
// [TokenPayload]:
// [UCAN]: https://ucan.xyz
package envelope
import (
"errors"
"fmt"
"io"
"strings"
"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/ipld/go-ipld-prime/fluent/qp"
"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-ucan/did"
"github.com/ucan-wg/go-ucan/internal/varsig"
)
type Tokener interface {
Prototype() schema.TypedPrototype
Tag() string
}
// Decode unmarshals the input data using the format specified by the
// provided codec.Decoder into a Tokener.
//
// An error is returned if the conversion fails, or if the resulting
// Tokener is invalid.
func Decode[T Tokener](b []byte, decFn codec.Decoder) (T, error) {
node, err := ipld.Decode(b, decFn)
if err != nil {
return *new(T), err
}
return FromIPLD[T](node)
}
// DecodeReader is the same as Decode, but accept an io.Reader.
func DecodeReader[T Tokener](r io.Reader, decFn codec.Decoder) (T, error) {
node, err := ipld.DecodeStreaming(r, decFn)
if err != nil {
return *new(T), err
}
return FromIPLD[T](node)
}
// FromDagCbor unmarshals the input data into a Tokener.
//
// An error is returned if the conversion fails, or if the resulting
// Tokener is invalid.
func FromDagCbor[T Tokener](b []byte) (T, error) {
return Decode[T](b, dagcbor.Decode)
}
// FromDagCborReader is the same as FromDagCbor, but accept an io.Reader.
func FromDagCborReader[T Tokener](r io.Reader) (T, error) {
return DecodeReader[T](r, dagcbor.Decode)
}
// FromDagJson unmarshals the input data into a Tokener.
//
// An error is returned if the conversion fails, or if the resulting
// Tokener is invalid.
func FromDagJson[T Tokener](b []byte) (T, error) {
return Decode[T](b, dagjson.Decode)
}
// FromDagJsonReader is the same as FromDagJson, but accept an io.Reader.
func FromDagJsonReader[T Tokener](r io.Reader) (T, error) {
return DecodeReader[T](r, dagjson.Decode)
}
// FromIPLD unwraps a Tokener from the provided IPLD datamodel.Node.
//
// An error is returned if the conversion fails, or if the resulting
// Tokener is invalid.
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()
if err != nil {
return undef, err
}
sigPayloadNode, err := node.LookupByIndex(1)
if err != nil {
return undef, err
}
// Normally we could look up the VarsigHeader and TokenPayload using
// node.LookupByString() - this works for the "h" key used for the
// VarsigHeader but not for the TokenPayload's key (tag) as all we
// know is that it starts with "ucan/" and as explained below, must
// decode to a schema.TypedNode for the representation provided by the
// token.Prototype().
// vvv
mi := sigPayloadNode.MapIterator()
if mi == nil {
return undef, fmt.Errorf("the SigPayload node is not a map: %s", sigPayloadNode.Kind().String())
}
var (
varsigHeaderNode datamodel.Node
tokenPayloadNode datamodel.Node
tag string
)
keyCount := 0
for !mi.Done() {
k, v, err := mi.Next()
if err != nil {
return undef, err
}
kStr, err := k.AsString()
if err != nil {
return undef, fmt.Errorf("the SigPayload keys are not strings: %w", err)
}
keyCount++
if kStr == "h" {
varsigHeaderNode = v
continue
}
if strings.HasPrefix(kStr, "ucan/") {
tokenPayloadNode = v
tag = kStr
}
}
if keyCount != 2 {
return undef, fmt.Errorf("the SigPayload map should have exactly two keys: %d", keyCount)
}
if undef.Tag() != tag {
return undef, fmt.Errorf("the TokenPayload tag doesn't match the Tokener tag: expected %s, got %s", undef.Tag(), tag)
}
// This needs to be done before converting this node to it's schema
// representation (afterwards, the field might be renamed os it's safer
// to use the wire name).
issuerNode, err := tokenPayloadNode.LookupByString("iss")
if err != nil {
return undef, err
}
// ^^^
// Replaces the datamodel.Node in tokenPayloadNode with a
// schema.TypedNode so that we can cast it to a *token.Token after
// unwrapping it.
// vvv
nb := undef.Prototype().Representation().NewBuilder()
err = nb.AssignNode(tokenPayloadNode)
if err != nil {
return undef, err
}
tokenPayloadNode = nb.Build()
// ^^^
tokenPayload := bindnode.Unwrap(tokenPayloadNode)
if tokenPayload == nil {
return undef, errors.New("failed to Unwrap the TokenPayload")
}
tkn, ok := tokenPayload.(T)
if !ok {
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
// matches the VarsigHeader and then verify the SigPayload.
// vvv
issuer, err := issuerNode.AsString()
if err != nil {
return undef, err
}
issuerDID, err := did.Parse(issuer)
if err != nil {
return undef, err
}
issuerPubKey, err := issuerDID.PubKey()
if err != nil {
return undef, err
}
issuerVarsigHeader, err := varsig.Encode(issuerPubKey.Type())
if err != nil {
return undef, err
}
varsigHeader, err := varsigHeaderNode.AsBytes()
if err != nil {
return undef, err
}
if string(varsigHeader) != string(issuerVarsigHeader) {
return undef, errors.New("the VarsigHeader key type doesn't match the issuer's key type")
}
data, err := ipld.Encode(sigPayloadNode, dagcbor.Encode)
if err != nil {
return undef, err
}
ok, err = issuerPubKey.Verify(data, signature)
if err != nil || !ok {
return undef, errors.New("failed to verify the token's signature")
}
// ^^^
return tkn, nil
}
// 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) {
node, err := ToIPLD(privKey, token)
if err != nil {
return nil, err
}
return ipld.Encode(node, encFn)
}
// 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 {
node, err := ToIPLD(privKey, token)
if err != nil {
return err
}
return ipld.EncodeStreaming(w, node, encFn)
}
// ToDagCbor marshals the Tokener to the DAG-CBOR format.
func ToDagCbor(privKey crypto.PrivKey, 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 {
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) {
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 {
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) {
tokenPayloadNode := bindnode.Wrap(token, token.Prototype().Type()).Representation()
varsigHeader, err := varsig.Encode(privKey.Type())
if err != nil {
return nil, err
}
sigPayloadNode, err := qp.BuildMap(basicnode.Prototype.Any, 2, func(ma datamodel.MapAssembler) {
qp.MapEntry(ma, "h", qp.Bytes(varsigHeader))
qp.MapEntry(ma, token.Tag(), qp.Node(tokenPayloadNode))
})
data, err := ipld.Encode(sigPayloadNode, dagcbor.Encode)
if err != nil {
return nil, err
}
signature, err := privKey.Sign(data)
if err != nil {
return nil, err
}
return qp.BuildList(basicnode.Prototype.Any, 2, func(la datamodel.ListAssembler) {
qp.ListEntry(la, qp.Bytes(signature))
qp.ListEntry(la, qp.Node(sigPayloadNode))
})
}

View File

@@ -0,0 +1,100 @@
package envelope_test
import (
"bytes"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/ucan-wg/go-ucan/internal/envelope"
"gotest.tools/v3/golden"
)
func TestDecode(t *testing.T) {
t.Parallel()
t.Run("via FromDagCbor", func(t *testing.T) {
t.Parallel()
data := golden.Get(t, "example.dagcbor")
tkn, err := envelope.FromDagCbor[*Example](data)
require.NoError(t, err)
assert.Equal(t, exampleGreeting, tkn.Hello)
assert.Equal(t, exampleDID, tkn.Issuer)
})
t.Run("via FromDagJson", func(t *testing.T) {
t.Parallel()
data := golden.Get(t, "example.dagjson")
tkn, err := envelope.FromDagJson[*Example](data)
require.NoError(t, err)
assert.Equal(t, exampleGreeting, tkn.Hello)
assert.Equal(t, exampleDID, tkn.Issuer)
})
}
func TestEncode(t *testing.T) {
t.Parallel()
t.Run("via ToDagCbor", func(t *testing.T) {
t.Parallel()
data, err := envelope.ToDagCbor(examplePrivKey(t), newExample(t))
require.NoError(t, err)
golden.AssertBytes(t, data, exampleDAGCBORFilename)
})
t.Run("via ToDagJson", func(t *testing.T) {
t.Parallel()
data, err := envelope.ToDagJson(examplePrivKey(t), newExample(t))
require.NoError(t, err)
golden.Assert(t, string(data), exampleDAGJSONFilename)
})
}
func TestRoundtrip(t *testing.T) {
t.Parallel()
t.Run("via FromDagCborReader/ToDagCborWriter", func(t *testing.T) {
t.Parallel()
data := golden.Get(t, exampleDAGCBORFilename)
tkn, err := envelope.FromDagCborReader[*Example](bytes.NewReader(data))
require.NoError(t, err)
assert.Equal(t, exampleGreeting, tkn.Hello)
assert.Equal(t, exampleDID, tkn.Issuer)
w := &bytes.Buffer{}
require.NoError(t, envelope.ToDagCborWriter(w, examplePrivKey(t), newExample(t)))
assert.Equal(t, data, w.Bytes())
})
t.Run("via FromDagCbor/ToDagCbor", func(t *testing.T) {
t.Parallel()
dataIn := golden.Get(t, exampleDAGCBORFilename)
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))
require.NoError(t, err)
assert.Equal(t, dataIn, dataOut)
})
}
func TestFromIPLD_with_invalid_signature(t *testing.T) {
t.Parallel()
node := invalidNodeFromGolden(t)
tkn, err := envelope.FromIPLD[*Example](node)
assert.Nil(t, tkn)
require.EqualError(t, err, "failed to verify the token's signature")
}

View File

@@ -0,0 +1 @@
X@|úŸÀ½â–ðõ+ÁŠ­!µ.®ÿhéÍúGO- ü¬”jÉssY¨quëiþ“ä°¬Íuý#ò¼’ç˜ c¢ahD4íqxucan/example@v1.0.0-rc.1¢cissx8did:key:z6MkpuK2Amsu1RqcLGgmHHQHhvmeXCCBVsM4XFSg2cCyg4Nhehelloeworld

View File

@@ -0,0 +1 @@
[{"/":{"bytes":"fPqfwL3iFpbw9SvBiq0DIbUurv9o6c36R08tC/yslGrJcwV51ghzWahxdetpEf6T5LCszXX9I/K8khvnmAxjAg"}},{"h":{"/":{"bytes":"NO0BcQ"}},"ucan/example@v1.0.0-rc.1":{"hello":"world","iss":"did:key:z6MkpuK2Amsu1RqcLGgmHHQHhvmeXCCBVsM4XFSg2cCyg4Nh"}}]

View File

@@ -0,0 +1,6 @@
type DID string
type Example struct {
hello String
issuer DID (rename "iss")
}

View File

@@ -24,7 +24,6 @@
package varsig
import (
"encoding/base64"
"encoding/binary"
"errors"
"fmt"
@@ -130,5 +129,5 @@ func header(vals ...multicodec.Code) string {
buf = binary.AppendUvarint(buf, uint64(val))
}
return base64.RawStdEncoding.EncodeToString(buf)
return string(buf)
}

View File

@@ -21,7 +21,14 @@ func TestDecode(t *testing.T) {
}
func ExampleDecode() {
keyType, _ := varsig.Decode([]byte("NIUkEoACcQ"))
hdr, err := base64.RawStdEncoding.DecodeString("NIUkEoACcQ")
if err != nil {
fmt.Println(err.Error())
return
}
keyType, _ := varsig.Decode(hdr)
fmt.Println(keyType.String())
// Output:
// RSA
@@ -37,7 +44,7 @@ func TestEncode(t *testing.T) {
func ExampleEncode() {
header, _ := varsig.Encode(pb.KeyType_RSA)
fmt.Println(string(header))
fmt.Println(base64.RawStdEncoding.EncodeToString(header))
// Output:
// NIUkEoACcQ