refactor(delegate): calculate CID in methods that explicitly state that they include that function

This commit is contained in:
Steve Moyer
2024-09-24 06:46:48 -04:00
parent 043c9b160d
commit b14671009c
7 changed files with 384 additions and 145 deletions

View File

@@ -15,10 +15,77 @@ import (
"github.com/ucan-wg/go-ucan/internal/envelope"
)
// Seal 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) Seal(privKey crypto.PrivKey) ([]byte, cid.Cid, error) {
data, err := t.ToDagCbor(privKey)
if err != nil {
return nil, cid.Undef, err
}
id, err := envelope.CIDFromBytes(data)
if err != nil {
return nil, cid.Undef, err
}
return data, id, nil
}
// SealWriter is the same as Seal but accepts an io.Writer.
func (t *Token) SealWriter(w io.Writer, privKey crypto.PrivKey) (cid.Cid, error) {
cidWriter := envelope.NewCIDWriter(w)
if err := t.ToDagCborWriter(cidWriter, privKey); err != nil {
return cid.Undef, err
}
return cidWriter.CID()
}
// Unseal decodes the provided binary data from the DAG-CBOR format,
// verifies that the envelope's signature is correct based on the public
// key taken from the issuer (iss) field and calculates the CID of the
// incoming data.
func Unseal(data []byte) (*Token, error) {
tkn, err := FromDagCbor(data)
if err != nil {
return nil, err
}
id, err := envelope.CIDFromBytes(data)
if err != nil {
return nil, err
}
tkn.cid = id
return tkn, nil
}
// UnsealReader is the same as Unseal but accepts an io.Reader.
func UnsealReader(r io.Reader) (*Token, error) {
cidReader := envelope.NewCIDReader(r)
tkn, err := FromDagCborReader(cidReader)
if err != nil {
return nil, err
}
id, err := cidReader.CID()
if err != nil {
return nil, err
}
tkn.cid = id
return tkn, nil
}
// Encode marshals a View to the format specified by the provided
// codec.Encoder.
func (t *Token) Encode(privKey crypto.PrivKey, encFn codec.Encoder) ([]byte, error) {
node, err := t.ToIPLD(privKey)
node, err := t.toIPLD(privKey)
if err != nil {
return nil, err
}
@@ -28,7 +95,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 {
node, err := t.ToIPLD(privKey)
node, err := t.toIPLD(privKey)
if err != nil {
return err
}
@@ -56,9 +123,81 @@ func (t *Token) ToDagJsonWriter(w io.Writer, privKey crypto.PrivKey) error {
return t.EncodeWriter(w, privKey, dagjson.Encode)
}
// ToIPLD wraps the View in an IPLD datamodel.Node.
func (t *Token) ToIPLD(privKey crypto.PrivKey) (datamodel.Node, error) {
// Decode unmarshals the input data using the format specified by the
// provided codec.Decoder into a View.
//
// An error is returned if the conversion fails, or if the resulting
// View is invalid.
func Decode(b []byte, decFn codec.Decoder) (*Token, error) {
node, err := ipld.Decode(b, decFn)
if err != nil {
return nil, err
}
return fromIPLD(node)
}
// DecodeReader is the same as Decode, but accept an io.Reader.
func DecodeReader(r io.Reader, decFn codec.Decoder) (*Token, error) {
node, err := ipld.DecodeStreaming(r, decFn)
if err != nil {
return nil, err
}
return fromIPLD(node)
}
// FromDagCbor unmarshals the input data into a View.
//
// An error is returned if the conversion fails, or if the resulting
// View is invalid.
func FromDagCbor(data []byte) (*Token, error) {
pay, err := envelope.FromDagCbor[*tokenPayloadModel](data)
if err != nil {
return nil, err
}
tkn, err := tokenFromModel(*pay)
if err != nil {
return nil, err
}
return tkn, err
}
// FromDagCborReader is the same as FromDagCbor, but accept an io.Reader.
func FromDagCborReader(r io.Reader) (*Token, error) {
return DecodeReader(r, dagcbor.Decode)
}
// FromDagJson unmarshals the input data into a View.
//
// An error is returned if the conversion fails, or if the resulting
// View is invalid.
func FromDagJson(data []byte) (*Token, error) {
return Decode(data, dagjson.Decode)
}
// FromDagJsonReader is the same as FromDagJson, but accept an io.Reader.
func FromDagJsonReader(r io.Reader) (*Token, error) {
return DecodeReader(r, dagjson.Decode)
}
func fromIPLD(node datamodel.Node) (*Token, error) {
pay, err := envelope.FromIPLD[*tokenPayloadModel](node)
if err != nil {
return nil, err
}
tkn, err := tokenFromModel(*pay)
if err != nil {
return nil, err
}
return tkn, err
}
func (t *Token) toIPLD(privKey crypto.PrivKey) (datamodel.Node, error) {
var sub *string
if t.subject != did.Undef {
s := t.subject.String()
sub = &s
@@ -95,79 +234,3 @@ func (t *Token) ToIPLD(privKey crypto.PrivKey) (datamodel.Node, error) {
return envelope.ToIPLD(privKey, model)
}
// Decode unmarshals the input data using the format specified by the
// provided codec.Decoder into a View.
//
// An error is returned if the conversion fails, or if the resulting
// View is invalid.
func Decode(b []byte, decFn codec.Decoder) (*Token, cid.Cid, error) {
node, err := ipld.Decode(b, decFn)
if err != nil {
return nil, cid.Undef, err
}
return FromIPLD(node)
}
// DecodeReader is the same as Decode, but accept an io.Reader.
func DecodeReader(r io.Reader, decFn codec.Decoder) (*Token, cid.Cid, error) {
node, err := ipld.DecodeStreaming(r, decFn)
if err != nil {
return nil, cid.Undef, err
}
return FromIPLD(node)
}
// FromDagCbor unmarshals the input data into a View.
//
// An error is returned if the conversion fails, or if the resulting
// View is invalid.
func FromDagCbor(data []byte) (*Token, cid.Cid, error) {
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.
func FromDagCborReader(r io.Reader) (*Token, cid.Cid, error) {
return DecodeReader(r, dagcbor.Decode)
}
// FromDagJson unmarshals the input data into a View.
//
// An error is returned if the conversion fails, or if the resulting
// View is invalid.
func FromDagJson(data []byte) (*Token, cid.Cid, error) {
return Decode(data, dagjson.Decode)
}
// FromDagJsonReader is the same as FromDagJson, but accept an io.Reader.
func FromDagJsonReader(r io.Reader) (*Token, cid.Cid, error) {
return DecodeReader(r, dagjson.Decode)
}
// FromIPLD unwraps a View from the provided IPLD datamodel.Node
//
// An error is returned if the conversion fails, or if the resulting
// View is invalid.
func FromIPLD(node datamodel.Node) (*Token, cid.Cid, error) {
pay, id, err := envelope.FromIPLD[*tokenPayloadModel](node) // TODO add CID to view
if err != nil {
return nil, cid.Undef, err
}
tkn, err := tokenFromModel(*pay)
if err != nil {
return nil, cid.Undef, err
}
return tkn, id, err
}

View File

@@ -1,11 +1,13 @@
package delegation_test
import (
"bytes"
_ "embed"
"fmt"
"testing"
"github.com/ipld/go-ipld-prime"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gotest.tools/v3/golden"
@@ -22,29 +24,62 @@ func TestSchemaRoundTrip(t *testing.T) {
delegationJson := golden.Get(t, "new.dagjson")
privKey := privKey(t, issuerPrivKeyCfg)
// format: dagJson --> PayloadModel --> dagCbor --> PayloadModel --> dagJson
// function: DecodeDagJson() EncodeDagCbor() DecodeDagCbor() EncodeDagJson()
t.Run("via buffers", func(t *testing.T) {
t.Parallel()
p1, id1, err := delegation.FromDagJson([]byte(delegationJson))
require.NoError(t, err)
require.Equal(t, newCID, envelope.CIDToBase58BTC(id1))
// format: dagJson --> PayloadModel --> dagCbor --> PayloadModel --> dagJson
// function: DecodeDagJson() Seal() Unseal() EncodeDagJson()
cborBytes, err := p1.ToDagCbor(privKey)
require.NoError(t, err)
fmt.Println("cborBytes length", len(cborBytes))
fmt.Println("cbor", string(cborBytes))
p1, err := delegation.FromDagJson([]byte(delegationJson))
require.NoError(t, err)
p2, id2, err := delegation.FromDagCbor(cborBytes)
require.NoError(t, err)
fmt.Println("read Cbor", p2)
cborBytes, id, err := p1.Seal(privKey)
require.NoError(t, err)
assert.Equal(t, newCID, envelope.CIDToBase58BTC(id))
fmt.Println("cborBytes length", len(cborBytes))
fmt.Println("cbor", string(cborBytes))
readJson, err := p2.ToDagJson(privKey)
require.NoError(t, err)
require.Equal(t, newCID, envelope.CIDToBase58BTC(id2))
fmt.Println("readJson length", len(readJson))
fmt.Println("json: ", string(readJson))
p2, err := delegation.Unseal(cborBytes)
require.NoError(t, err)
assert.Equal(t, id, p2.CID())
fmt.Println("read Cbor", p2)
require.JSONEq(t, string(delegationJson), string(readJson))
readJson, err := p2.ToDagJson(privKey)
require.NoError(t, err)
fmt.Println("readJson length", len(readJson))
fmt.Println("json: ", string(readJson))
assert.JSONEq(t, string(delegationJson), string(readJson))
})
t.Run("via streaming", func(t *testing.T) {
t.Parallel()
buf := bytes.NewBuffer(delegationJson)
// format: dagJson --> PayloadModel --> dagCbor --> PayloadModel --> dagJson
// function: DecodeDagJson() Seal() Unseal() EncodeDagJson()
p1, err := delegation.FromDagJsonReader(buf)
require.NoError(t, err)
cborBytes := &bytes.Buffer{}
id, err := p1.SealWriter(cborBytes, privKey)
t.Log(len(id.Bytes()), id.Bytes())
require.NoError(t, err)
assert.Equal(t, newCID, envelope.CIDToBase58BTC(id))
// buf = bytes.NewBuffer(cborBytes.Bytes())
p2, err := delegation.UnsealReader(cborBytes)
require.NoError(t, err)
t.Log(len(p2.CID().Bytes()), p2.CID().Bytes())
assert.Equal(t, envelope.CIDToBase58BTC(id), envelope.CIDToBase58BTC(p2.CID()))
readJson := &bytes.Buffer{}
require.NoError(t, p2.ToDagJsonWriter(readJson, privKey))
assert.JSONEq(t, string(delegationJson), readJson.String())
})
}
func BenchmarkSchemaLoad(b *testing.B) {

2
go.mod
View File

@@ -10,6 +10,7 @@ require (
github.com/libp2p/go-libp2p v0.36.3
github.com/multiformats/go-multibase v0.2.0
github.com/multiformats/go-multicodec v0.9.0
github.com/multiformats/go-multihash v0.2.3
github.com/multiformats/go-varint v0.0.7
github.com/stretchr/testify v1.9.0
gotest.tools/v3 v3.5.1
@@ -24,7 +25,6 @@ require (
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/multiformats/go-base32 v0.1.0 // indirect
github.com/multiformats/go-base36 v0.2.0 // indirect
github.com/multiformats/go-multihash v0.2.3 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/polydawn/refmt v0.89.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect

View File

@@ -1,9 +1,11 @@
package envelope
import (
"crypto/sha256"
"hash"
"io"
"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"
@@ -38,12 +40,89 @@ func CIDFromBytes(b []byte) (cid.Cid, error) {
}.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
var _ io.Reader = (*CIDReader)(nil)
// CIDReader wraps an io.Reader and includes a hash.Hash that is updated
// as data is read from the child io.Reader.
type CIDReader struct {
hash hash.Hash
r io.Reader
err error
}
// NewCIDReader initializes a hash.Hash to calculate the CID's hash and
// and returns a wrapped io.Reader.
func NewCIDReader(r io.Reader) *CIDReader {
hash := sha256.New()
hash.Reset()
return &CIDReader{
hash: hash,
r: r,
}
}
// CID returns the UCAN-formatted cid.Cid created from the hash calculated
// as bytes are read from the inner io.Reader.
func (r *CIDReader) CID() (cid.Cid, error) {
if r.err != nil {
return cid.Undef, r.err // TODO: Wrap to say it's an error during streaming?
}
return CIDFromBytes(data)
return cidFromHash(r.hash)
}
// Read implements io.Reader.
func (r *CIDReader) Read(p []byte) (n int, err error) {
n, err = r.r.Read(p)
if err != nil && err != io.EOF {
r.err = err
return
}
_, _ = r.hash.Write(p[:n])
return
}
var _ io.Writer = (*CIDWriter)(nil)
type CIDWriter struct {
hash hash.Hash
w io.Writer
err error
}
func NewCIDWriter(w io.Writer) *CIDWriter {
hash := sha256.New()
hash.Reset()
return &CIDWriter{
hash: hash,
w: w,
}
}
func (w *CIDWriter) CID() (cid.Cid, error) {
return cidFromHash(w.hash)
}
func (w *CIDWriter) Write(p []byte) (n int, err error) {
if _, err = w.hash.Write(p); err != nil {
w.err = err
return
}
return w.w.Write(p)
}
func cidFromHash(hash hash.Hash) (cid.Cid, error) {
mh, err := multihash.Encode(hash.Sum(nil), multihash.SHA2_256)
if err != nil {
return cid.Undef, err
}
return cid.NewCidV1(uint64(multicodec.DagCbor), mh), nil
}

View File

@@ -1,8 +1,10 @@
package envelope_test
import (
"io"
"testing"
"github.com/ipfs/go-cid"
"github.com/multiformats/go-multicodec"
"github.com/multiformats/go-multihash"
"github.com/stretchr/testify/assert"
@@ -23,3 +25,58 @@ func TestCid(t *testing.T) {
assert.Equal(t, exampleCID, envelope.CIDToBase58BTC(id))
assert.Equal(t, expHash, id.Hash())
}
func TestStreaming(t *testing.T) {
t.Parallel()
expData := []byte("this is a test")
expCID, err := cid.V1Builder{
Codec: uint64(multicodec.DagCbor),
MhType: multihash.SHA2_256,
MhLength: 0,
}.Sum(expData)
require.NoError(t, err)
t.Run("CIDReader()", func(t *testing.T) {
t.Parallel()
r, w := io.Pipe() //nolint:varnamelen
cidReader := envelope.NewCIDReader(r)
go func() {
_, err := w.Write(expData)
assert.NoError(t, err)
assert.NoError(t, w.Close())
}()
actData, err := io.ReadAll(cidReader)
require.NoError(t, err)
assert.Equal(t, expData, actData)
actCID, err := cidReader.CID()
require.NoError(t, err)
assert.Equal(t, expCID, actCID)
})
t.Run("CIDWriter", func(t *testing.T) {
t.Parallel()
r, w := io.Pipe() //nolint:varnamelen
cidWriter := envelope.NewCIDWriter(w)
go func() {
_, err := cidWriter.Write(expData)
assert.NoError(t, err)
assert.NoError(t, w.Close())
}()
actData, err := io.ReadAll(r)
require.NoError(t, err)
assert.Equal(t, expData, actData)
actCID, err := cidWriter.CID()
require.NoError(t, err)
assert.Equal(t, expCID, actCID)
})
}

View File

@@ -8,8 +8,7 @@
//
// Decoding functions in this package likewise perform the signature
// verification using a public key extracted from the TokenPayload as
// described by requirement two below. Additionally, the decode functions
// also return the CID for the verified Envelope.
// described by requirement two below.
//
// Types that wish to be marshaled and unmarshaled from the using
// is package have two requirements.
@@ -30,7 +29,6 @@ import (
"errors"
"io"
"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"
@@ -66,20 +64,20 @@ type Tokener interface {
//
// 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, cid.Cid, error) {
func Decode[T Tokener](b []byte, decFn codec.Decoder) (T, error) {
node, err := ipld.Decode(b, decFn)
if err != nil {
return *new(T), cid.Undef, err
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, cid.Cid, error) {
func DecodeReader[T Tokener](r io.Reader, decFn codec.Decoder) (T, error) {
node, err := ipld.DecodeStreaming(r, decFn)
if err != nil {
return *new(T), cid.Undef, err
return *new(T), err
}
return FromIPLD[T](node)
@@ -89,29 +87,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
// Tokener is invalid.
func FromDagCbor[T Tokener](b []byte) (T, cid.Cid, error) {
func FromDagCbor[T Tokener](b []byte) (T, error) {
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
return undef, err
}
tkn, err := fromIPLD[T](node)
if err != nil {
return undef, cid.Undef, err
return undef, err
}
return tkn, id, nil
return tkn, nil
}
// FromDagCborReader is the same as FromDagCbor, but accept an io.Reader.
func FromDagCborReader[T Tokener](r io.Reader) (T, cid.Cid, error) {
func FromDagCborReader[T Tokener](r io.Reader) (T, error) {
return DecodeReader[T](r, dagcbor.Decode)
}
@@ -119,12 +112,12 @@ func FromDagCborReader[T Tokener](r io.Reader) (T, cid.Cid, error) {
//
// An error is returned if the conversion fails, or if the resulting
// Tokener is invalid.
func FromDagJson[T Tokener](b []byte) (T, cid.Cid, error) {
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, cid.Cid, error) {
func FromDagJsonReader[T Tokener](r io.Reader) (T, error) {
return DecodeReader[T](r, dagjson.Decode)
}
@@ -132,20 +125,15 @@ func FromDagJsonReader[T Tokener](r io.Reader) (T, cid.Cid, error) {
//
// An error is returned if the conversion fails, or if the resulting
// Tokener is invalid.
func FromIPLD[T Tokener](node datamodel.Node) (T, cid.Cid, error) {
func FromIPLD[T Tokener](node datamodel.Node) (T, error) {
undef := *new(T)
id, err := CIDFromIPLD(node)
if err != nil {
return undef, cid.Undef, err
}
tkn, err := fromIPLD[T](node)
if err != nil {
return undef, cid.Undef, err
return undef, err
}
return tkn, id, nil
return tkn, nil
}
func fromIPLD[T Tokener](node datamodel.Node) (T, error) {

View File

@@ -2,6 +2,7 @@ package envelope_test
import (
"bytes"
"crypto/sha256"
"testing"
"github.com/stretchr/testify/assert"
@@ -18,7 +19,7 @@ func TestDecode(t *testing.T) {
data := golden.Get(t, "example.dagcbor")
tkn, _, err := envelope.FromDagCbor[*Example](data)
tkn, err := envelope.FromDagCbor[*Example](data)
require.NoError(t, err)
assert.Equal(t, exampleGreeting, tkn.Hello)
assert.Equal(t, exampleDID, tkn.Issuer)
@@ -29,7 +30,7 @@ func TestDecode(t *testing.T) {
data := golden.Get(t, "example.dagjson")
tkn, _, err := envelope.FromDagJson[*Example](data)
tkn, err := envelope.FromDagJson[*Example](data)
require.NoError(t, err)
assert.Equal(t, exampleGreeting, tkn.Hello)
assert.Equal(t, exampleDID, tkn.Issuer)
@@ -64,11 +65,10 @@ func TestRoundtrip(t *testing.T) {
dataIn := golden.Get(t, exampleDAGCBORFilename)
tkn, id, err := envelope.FromDagCbor[*Example](dataIn)
tkn, 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)
@@ -80,11 +80,10 @@ func TestRoundtrip(t *testing.T) {
data := golden.Get(t, exampleDAGCBORFilename)
tkn, id, err := envelope.FromDagCborReader[*Example](bytes.NewReader(data))
tkn, err := envelope.FromDagCborReader[*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.ToDagCborWriter(w, examplePrivKey(t), newExample(t)))
@@ -96,11 +95,10 @@ func TestRoundtrip(t *testing.T) {
dataIn := golden.Get(t, exampleDAGJSONFilename)
tkn, id, err := envelope.FromDagJson[*Example](dataIn)
tkn, err := envelope.FromDagJson[*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.ToDagJson(examplePrivKey(t), newExample(t))
require.NoError(t, err)
@@ -112,11 +110,10 @@ func TestRoundtrip(t *testing.T) {
data := golden.Get(t, exampleDAGJSONFilename)
tkn, id, err := envelope.FromDagJsonReader[*Example](bytes.NewReader(data))
tkn, 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)))
@@ -128,7 +125,27 @@ func TestFromIPLD_with_invalid_signature(t *testing.T) {
t.Parallel()
node := invalidNodeFromGolden(t)
tkn, _, err := envelope.FromIPLD[*Example](node)
tkn, err := envelope.FromIPLD[*Example](node)
assert.Nil(t, tkn)
require.EqualError(t, err, "failed to verify the token's signature")
}
func TestHash(t *testing.T) {
t.Parallel()
msg := []byte("this is a test")
hash1 := sha256.Sum256(msg)
hasher := sha256.New()
for _, b := range msg {
hasher.Write([]byte{b})
}
hash2 := hasher.Sum(nil)
hash3 := hasher.Sum(nil)
require.Equal(t, hash1[:], hash2)
require.Equal(t, hash1[:], hash3)
}