From 7cead1bf8d52a245e2a92634988e0537eb7fad30 Mon Sep 17 00:00:00 2001 From: Steve Moyer Date: Fri, 13 Sep 2024 12:01:51 -0400 Subject: [PATCH] chore(envelope): refactor to decode token.Token to schema.TypedNode --- go.mod | 18 +- go.sum | 105 ++-------- internal/envelope/envelope.go | 219 +++++++++++---------- internal/envelope/envelope_test.go | 191 +++++++++++------- internal/envelope/testdata/example.cbor | Bin 318 -> 0 bytes internal/envelope/testdata/example.dagcbor | 1 + internal/envelope/testdata/example.dagjson | 1 + internal/envelope/testdata/example.json | 1 - internal/token/conversion.go | 24 +++ internal/token/doc.go | 33 ++++ internal/token/errors.go | 2 + internal/token/schema_test.go | 1 + internal/token/token.go | 11 +- internal/token/token.ipldsch | 8 +- internal/token/token_gen.go | 4 +- internal/token/token_test.go | 33 +++- internal/tools/tools.go | 2 +- 17 files changed, 363 insertions(+), 291 deletions(-) delete mode 100644 internal/envelope/testdata/example.cbor create mode 100644 internal/envelope/testdata/example.dagcbor create mode 100644 internal/envelope/testdata/example.dagjson delete mode 100644 internal/envelope/testdata/example.json create mode 100644 internal/token/conversion.go diff --git a/go.mod b/go.mod index ef87dc8..f501a85 100644 --- a/go.mod +++ b/go.mod @@ -1,18 +1,19 @@ module github.com/ucan-wg/go-ucan -go 1.21 +go 1.22.0 -toolchain go1.22.1 +toolchain go1.22.4 require ( github.com/gobwas/glob v0.2.3 github.com/ipfs/go-cid v0.4.1 github.com/ipld/go-ipld-prime v0.21.0 - github.com/launchdarkly/go-options v1.3.0 github.com/libp2p/go-libp2p v0.36.2 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/selesy/go-options v0.0.0-20240912020512-ed2658318e52 github.com/stretchr/testify v1.9.0 gotest.tools/v3 v3.5.1 ) @@ -20,22 +21,21 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect - github.com/fatih/structtag v1.0.0 // indirect + github.com/fatih/structtag v1.2.0 // indirect github.com/google/go-cmp v0.6.0 // 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 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 golang.org/x/crypto v0.25.0 // indirect - golang.org/x/mod v0.19.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.22.0 // indirect - golang.org/x/tools v0.23.0 // indirect + golang.org/x/mod v0.21.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/tools v0.25.0 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.3.0 // indirect diff --git a/go.sum b/go.sum index 52b874d..04d054d 100644 --- a/go.sum +++ b/go.sum @@ -1,41 +1,22 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 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.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/fatih/structtag v1.0.0 h1:pTHj65+u3RKWYPSGaU290FpI/dXxTaHdVwVwbcPKmEc= -github.com/fatih/structtag v1.0.0/go.mod h1:IKitwq45uXL/yqi5mYghiD3w9H6eTOvI9vnk8tXMphA= +github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= +github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= github.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH9C2E= @@ -48,8 +29,6 @@ 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/launchdarkly/go-options v1.3.0 h1:djG7Jnq5Ypixj4oqPlQOaZZMM54R9DhESyUheNl7Fow= -github.com/launchdarkly/go-options v1.3.0/go.mod h1:M4XaM6jqyeCwQFRKtU4OGepTeWvUF9rDhcggjLLTPvE= 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.2 h1:BbqRkDaGC3/5xfaJakLV/BrpjlAuYqSB0lRvtzL3B/U= @@ -72,14 +51,6 @@ github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7B github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4= @@ -87,6 +58,8 @@ github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/selesy/go-options v0.0.0-20240912020512-ed2658318e52 h1:poNWlojS+o3229ZuatLMzK9wFiLuLxo7O170Edggs0o= +github.com/selesy/go-options v0.0.0-20240912020512-ed2658318e52/go.mod h1:Cn8TrnJWCWd3dAmejFTpLN8tNVNKNoVVlZzL8ux5EWQ= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= @@ -94,88 +67,34 @@ github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hg github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ= github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= -golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= -golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= +golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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= diff --git a/internal/envelope/envelope.go b/internal/envelope/envelope.go index 0d58bbb..b63eada 100644 --- a/internal/envelope/envelope.go +++ b/internal/envelope/envelope.go @@ -3,24 +3,26 @@ package envelope import ( "bytes" "errors" + "fmt" + "strings" - "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/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" crypto "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/crypto/pb" - "github.com/multiformats/go-multibase" - "github.com/multiformats/go-multicodec" - "github.com/multiformats/go-multihash" - "github.com/multiformats/go-varint" + "github.com/ucan-wg/go-ucan/did" "github.com/ucan-wg/go-ucan/internal/token" "github.com/ucan-wg/go-ucan/internal/varsig" ) -// [Envelope] is a signed enclosure for types implementing Tokener. +// [Envelope] is a signed enclosure for a UCAN v1 Token. +// +// While the types and functions in this package are not exported, +// the names used for types, fields, variables, etc generally use the +// names from the specification // // [Envelope]: https://github.com/ucan-wg/spec#envelope type Envelope struct { @@ -29,8 +31,8 @@ type Envelope struct { } // New creates an Envelope containing a VarsigHeader and Signature for -// the data resulting from wrapping the provided Tokener in and IPLD -// datamodel.Node and encoding it using DAG-CBOR +// the data resulting from wrapping the provided Token in an IPLD +// datamodel.Node and encoding it using DAG-CBOR. func New(privKey crypto.PrivKey, token *token.Token, tag string) (*Envelope, error) { sigPayload, err := newSigPayload(privKey.Type(), token, tag) if err != nil { @@ -56,7 +58,7 @@ func New(privKey crypto.PrivKey, token *token.Token, tag string) (*Envelope, err // Wrap is syntactic sugar for creating an Envelope and wrapping it as an // IPLD datamodel.Node in a single operation. // -// Since the Envelope itself isn't returned, us this method only when +// Since the Envelope itself isn't returned, use this method only when // the IPLD datamodel.Node is used directly. If the Envelope is also // required, use New followed by Envelope.Wrap to avoid the need to // unwrap the newly created datamodel.Node. @@ -69,7 +71,13 @@ func Wrap(privKey crypto.PrivKey, token *token.Token, tag string) (datamodel.Nod return env.Wrap() } -func Unwrap(node datamodel.Node, tag string) (*Envelope, error) { +// Unwrap attempts to crate an Envelope from a datamodel.Node +// +// There are lots of ways that this can fail and therefore there are +// an almost excessive number of check included here and while +// attempting to extract the token.Token from one of the inner IPLD +// nodes. +func Unwrap(node datamodel.Node) (*Envelope, error) { signatureNode, err := node.LookupByIndex(0) if err != nil { return nil, err @@ -85,7 +93,7 @@ func Unwrap(node datamodel.Node, tag string) (*Envelope, error) { return nil, err } - sigPayload, err := unwrapSigPayload(sigPayloadNode, tag) + sigPayload, err := unwrapSigPayload(sigPayloadNode) if err != nil { return nil, err } @@ -96,35 +104,20 @@ func Unwrap(node datamodel.Node, tag string) (*Envelope, error) { }, nil } -func (e *Envelope) CID() (cid.Cid, error) { - node, err := e.Wrap() - if err != nil { - return cid.Undef, nil - } - - cbor, err := ipld.Encode(node, dagcbor.Encode) - if err != nil { - return cid.Undef, nil - } - - data := varint.ToUvarint(uint64(multicodec.DagCbor)) - data = append(data, cbor...) - - hash, err := multihash.Sum(data, uint64(multicodec.Sha2_256), -1) - if err != nil { - return cid.Undef, err - } - - return cid.NewCidV1(multibase.Base58BTC, hash), nil -} - -// Signature is an accessor that returns the cryptographic signature -// that was created when the Envelope was created or unwrapped. +// Signature returns the cryptographic signature of the Envelope's +// SigPayload. func (e *Envelope) Signature() []byte { return e.signature } -func (e *Envelope) Token() *token.Token { +// Tag returns the key that's used to reference the TokenPayload within +// this Envelope. +func (e *Envelope) Tag() string { + return e.sigPayload.tag +} + +// TokenPayload returns the *token.Token enclosed within this Envelope. +func (e *Envelope) TokenPayload() *token.Token { return e.sigPayload.tokenPayload } @@ -142,11 +135,17 @@ func (e *Envelope) VarsigHeader() []byte { // data created by encoding the SigPayload as DAG-CBOR and the public // key passed as the only argument. // -// Note that for Delegation and Invocation Tokeners, the public key -// is retrieved from the DID's method specific identifier. +// Note that for Delegation and Invocation tokens, the public key +// is retrieved from the DID's method specific identifier for the +// Issuer field. // // [Envelope]: https://github.com/ucan-wg/spec#envelope -func (e *Envelope) Verify(pubKey crypto.PubKey) (bool, error) { +func (e *Envelope) Verify() (bool, error) { + pubKey, err := did.ToPubKey(e.sigPayload.tokenPayload.Issuer) + if err != nil { + return false, err + } + cbor, err := e.sigPayload.cbor() if err != nil { return false, err @@ -157,35 +156,15 @@ func (e *Envelope) Verify(pubKey crypto.PubKey) (bool, error) { // Wrap encodes the Envelope as an IPLD datamodel.Node. func (e *Envelope) Wrap() (datamodel.Node, error) { - sn := bindnode.Wrap(&e.signature, nil) - spn, err := e.sigPayload.wrap() if err != nil { return nil, err } - np := basicnode.Prototype.Any - - lb := np.NewBuilder() - - la, err := lb.BeginList(2) - if err != nil { - return nil, err - } - - if err = la.AssembleValue().AssignNode(sn); err != nil { - return nil, err - } - - if err := la.AssembleValue().AssignNode(spn); err != nil { - return nil, err - } - - if err := la.Finish(); err != nil { - return nil, err - } - - return lb.Build(), nil + return qp.BuildList(basicnode.Prototype.Any, 2, func(la datamodel.ListAssembler) { + qp.ListEntry(la, qp.Bytes(e.signature)) + qp.ListEntry(la, qp.Node(spn)) + }) } // @@ -213,35 +192,90 @@ func newSigPayload(keyType pb.KeyType, token *token.Token, tag string) (*sigPayl }, nil } -func unwrapSigPayload(node datamodel.Node, tag string) (*sigPayload, error) { - tokenPayloadNode, err := node.LookupByString(tag) - if err != nil { - return nil, err +func unwrapSigPayload(node datamodel.Node) (*sigPayload, error) { + // 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 := node.MapIterator() + if mi == nil { + return nil, fmt.Errorf("the SigPayload node is not a map: %s", node.Kind().String()) } - tokenPayload := bindnode.Unwrap(tokenPayloadNode) - if tokenPayload == nil { - return nil, errors.New("unexpected type") // TODO + var ( + hdrNode datamodel.Node + tknNode datamodel.Node + tag string + ) + + keyCount := 0 + + for !mi.Done() { + k, v, err := mi.Next() + if err != nil { + return nil, err + } + + kStr, err := k.AsString() + if err != nil { + return nil, fmt.Errorf("the SigPayload keys are not strings: %w", err) + } + + keyCount++ + + if kStr == "h" { + hdrNode = v + + continue + } + + if strings.HasPrefix(kStr, "ucan/") { + tknNode = v + tag = kStr + } } - token, ok := tokenPayload.(*token.Token) - if !ok { - return nil, errors.New("unexpected type") // TODO + if keyCount != 2 { + return nil, fmt.Errorf("the SigPayload map should have exactly two keys: %d", keyCount) } + // ^^^ + + // 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 := token.Prototype().Representation().NewBuilder() - headerNode, err := node.LookupByString("h") + err := nb.AssignNode(tknNode) if err != nil { return nil, err } - header, err := headerNode.AsBytes() + tknNode = nb.Build() + // ^^^ + + tokenPayload := bindnode.Unwrap(tknNode) + if tokenPayload == nil { + return nil, errors.New("failed to Unwrap the TokenPayload") + } + + tkn, ok := tokenPayload.(*token.Token) + if !ok { + return nil, errors.New("failed to assert the TokenPayload type as *token.Token") + } + + hdr, err := hdrNode.AsBytes() if err != nil { return nil, err } return &sigPayload{ - varsigHeader: header, - tokenPayload: token, + varsigHeader: hdr, + tokenPayload: tkn, + tag: tag, }, nil } @@ -262,29 +296,8 @@ func (sp *sigPayload) cbor() ([]byte, error) { func (sp *sigPayload) wrap() (datamodel.Node, error) { tpn := bindnode.Wrap(sp.tokenPayload, token.Prototype().Type()) - np := basicnode.Prototype.Any - mb := np.NewBuilder() - - ma, err := mb.BeginMap(2) - if err != nil { - return nil, err - } - - ha, err := ma.AssembleEntry("h") - if err != nil { - return nil, err - } - ha.AssignBytes(sp.varsigHeader) - - ta, err := ma.AssembleEntry(sp.tag) - if err != nil { - return nil, err - } - ta.AssignNode(tpn.Representation()) - - if err := ma.Finish(); err != nil { - return nil, err - } - - return mb.Build(), nil + return qp.BuildMap(basicnode.Prototype.Any, 2, func(ma datamodel.MapAssembler) { + qp.MapEntry(ma, "h", qp.Bytes(sp.varsigHeader)) + qp.MapEntry(ma, sp.tag, qp.Node(tpn.Representation())) + }) } diff --git a/internal/envelope/envelope_test.go b/internal/envelope/envelope_test.go index aa13605..d396b0e 100644 --- a/internal/envelope/envelope_test.go +++ b/internal/envelope/envelope_test.go @@ -1,16 +1,16 @@ package envelope_test import ( - "bytes" - "crypto/rand" - "crypto/rsa" - _ "embed" "encoding/base64" "testing" + "github.com/ipld/go-ipld-prime" "github.com/ipld/go-ipld-prime/codec/dagcbor" "github.com/ipld/go-ipld-prime/codec/dagjson" - "github.com/libp2p/go-libp2p/core/crypto" + "github.com/ipld/go-ipld-prime/datamodel" + "github.com/ipld/go-ipld-prime/fluent/qp" + "github.com/ipld/go-ipld-prime/node/basicnode" + crypto "github.com/libp2p/go-libp2p/core/crypto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/ucan-wg/go-ucan/did" @@ -20,134 +20,181 @@ import ( ) const ( - exampleSignature = "G9EFlDm5csIZR+byd5qMFxuaN/gsZmPSeoecW2PqWW8+wYWna9zx0peX1g7mUdo4ZTLTTr8LJSxuF1JFOJR0EsjgM0c8OHuX0WpSv8U+KSNxonbZpZqO8lyI/kW4crl/k9QrWMXtyHLEOS1OD3q9SsNGsf62fk1AMH9W+D2JVBVWdWAYFVXVkXQ+RbJi21lWYc9v/JtHSJbbuCbwhRqEsTBdhcYnyfFLcgLZvR9vqM636gA3ebRjZGZJOiAvxwdTOzlVxtw/552pAx8Od3hRGc5xdG5jGu2/OwIn9UMoXPQl7pMUYqk1nfqN3C7kDelIaQlgoAGyfssepB1tMRH/KA" - exampleTag = "ucan/example@v1.0.0-rc.1" - exampleVarsigHeader = "NIUkEoACcQ" - invalidSignature = "a5BocvMSlifrDzWN7MQpDZ4cEciwe+b9twdQ7d5EZ/LlW3w1VIjk34ci8LqmzMCMwqJsoBqevArUMNS86RrDOLZEl+71+nSf1GJ9fK/E2o7ONSPTQt1wALH1xhJ4S/h5o8v0sWP/PWBvolSfMpro9lN1xCi9zC4iuFmizqdjOd3Ba3txHD5DGAculWBiob3N1mjkXZPbQYEQteCoLwSNDCmmHCE7VpRUkoi832N7UVHlu1FFucENB31qBWZQ+JTj8/oV56Do+LbhrDDiabNkTxulwQ7u+hdKA30vA6FWaA6QW+UE2/mCEKM5wvVAohLPZsapGXP6LoEcbBM3O758dx" - priCfg = "CAASqAkwggSkAgEAAoIBAQDq39Aou82MEteoEz+iKpu7zwJc0dZfomAfB4Zpnl8+WhUOhZyveHDD9lr/UCc/fcN5ufeyZxutDRvIXcmUGG5DNTVRZ10ywT/wN8KO+x/hZ5QIxBAsCFukcyHbAPseLYpAK0J0HNnQhtF6cQcQkuThCnZH/Ofj42d7snuztbBUxwjButvHYHiWwolcJUeb99HCpGwtJYjkp004roFBqjkLayP4AWHrnW4mtCY0rw86gRCT60N1XBZ9zXKw+LJeuQg3RUgZqBL6hvVIs1LAY5ie0LSXVkdjg9bmV7j5SKJBgk9ABoKvt35/KSWA5HW/6g3y/UITCD2DQDrTFv8xzDIvAgMBAAECggEAFoGr6LtWTv3fPHPbvSZoFe8YQty4tiFRJKgL8UMDzW3EZsfW49metKh+v8hmemcKvDddzPKkbEi9SM3z6wUMS9Rlb4+AFsT94370Xc8ilu7d+JkRE6cZYQDHVb0aUyH6BXwfuhCprpm8qQb7rlLlK8tc2jkZ33SDDg9kWywl4XmiDabKm0fOJd68KGuO5FNpCfpipqG3ok/FYuKlSqpCz+7QH2p3z2eVGTa/uIbDMNxkFBoIuhEJT43eDR2elPOrSL9+AYgBPVrzcJoRRxZFVvDDQ+RIwI/A62DvZ3IpFEyzEk2VZwWpLWYKnAUElcSegxx22K7S4BAaWjL86I6cAQKBgQDr8Y1NLYKYffHJAkmWE/ssGcih5C06gOo9WInBU8ZroY4LAhzKLstS0yKsM0OtRSFhZsmCdR3M/IHO94c3KsXu+KtA7r9AG+58LMpmob1mvyGsXIowHFMAd95s3FDd/HOE10tMyrIE1c7eWLfII+s6yGo8MS0WHXOSFBlioukGbwKBgQD+1v3vC+iUNP0FMhVxnhGgONyUb0X+AEw9GOLeCpsugZqRXnharYSiTjGPjwPaT2YBVkyaraoX6VwK+ys3RCngUg4s9IeHUYsR6Xa0oheAlME0ZDtuzi5+lDo+Zpsg8vepgx6v//bKvUcJb+9YDcKlMfQgFnSb3bAwUN9Ru79wQQKBgD7Z/9wZTXq1whzbwSJ7fCNJUwrdL7cv9DYXScr4OBkf1ijUjTrGsF8F42yf011q1vONYAyiiie69BFgGuL1P/jiwSvw7X10c1kczWX9m+is7ZlupVkfknTDebriDaC0yUkP2P1B2Z40HoFYfMyR1O25yaLzLqF/gvPc6s49u3l9AoGBAOZ9d2EdKTfbEToAyYpgyFpc84zBc9G/XTUpbBAeEasnh7CRfFOvezX9eS/5zydGBuGQt2pzRlOoOhqof7bVzPZZ4P5iEK6gXyNNQJMxxAYFBRYozeRzUXQlBuTnksljWAMWV8whu4o1VanAdv7yOymEm+Ply4QqJzAcBU/8erLBAoGBAJ6120n/Q8B7kNW/tZvJ4Xi0u/kSEodNQ9TpF44SB32bn/aWwfe7qFS1x+3omO7XWcx3FLUUPhITQjmQcbNa2yWY0UZoqnzkHhDmJeG2PUILEMCHSLCKQHS+PNJxqEWvwQe2mX/gJIjf14U9983hgLnOL7gH9sVYf9M9yA8NVlem" + exampleDID = "did:key:z6MkpuK2Amsu1RqcLGgmHHQHhvmeXCCBVsM4XFSg2cCyg4Nh" + examplePrivKeyCfg = "CAESQP9v2uqECTuIi45dyg3znQvsryvf2IXmOF/6aws6aCehm0FVrj0zHR5RZSDxWNjcpcJqsGym3sjCungX9Zt5oA4=" + exampleSignatureStr = "PZV6A2aI7n+MlyADqcqmWhkuyNrgUCDz+qSLSnI9bpasOwOhKUTx95m5Nu5CO/INa1LqzHGioD9+PVf6qdtTBg" + exampleTag = "ucan/example@v1.0.0-rc.1" + exampleVarsigHeaderStr = "NO0BcQ" + + invalidSignatureStr = "PZV6A2aI7n+MlyADqcqmWhkuyNrgUCDz+qSLSnI9bpasOwOhKUTx95m5Nu5CO/INa1LqzHGioD9+PVf6qdtTBK" + + exampleDAGCBORFilename = "example.dagcbor" + exampleDAGJSONFilename = "example.dagjson" ) func TestNew(t *testing.T) { t.Parallel() - exampleSignature, err := base64.RawStdEncoding.DecodeString(exampleSignature) + envel := exampleEnvelope(t) + assert.NotZero(t, envel) + + assert.Equal(t, exampleSignature(t), envel.Signature()) + assert.Equal(t, exampleTag, envel.Tag()) + assert.Equal(t, exampleVarsigHeader(t), envel.VarsigHeader()) + assert.EqualValues(t, exampleGoldenTokenPayload(t), envel.TokenPayload()) +} + +func TestWrap(t *testing.T) { + t.Parallel() + + node, err := envelope.Wrap(examplePrivKey(t), exampleToken(t), exampleTag) require.NoError(t, err) - varsigHeader, err := base64.RawStdEncoding.DecodeString(exampleVarsigHeader) + + cbor, err := ipld.Encode(node, dagcbor.Encode) require.NoError(t, err) - env := exampleEnvelope(t) - assert.Equal(t, exampleSignature, env.Signature()) - assert.Equal(t, varsigHeader, env.VarsigHeader()) + golden.AssertBytes(t, cbor, exampleDAGCBORFilename) + + json, err := ipld.Encode(node, dagjson.Encode) + require.NoError(t, err) - tkn := env.Token() - assert.IsType(t, (*token.Token)(nil), tkn) - // TODO + golden.Assert(t, string(json), exampleDAGJSONFilename) } -func TestWrap(t *testing.T) { +func TestEnvelope_Verify(t *testing.T) { t.Parallel() - envNode, err := envelope.Wrap(rsaPrivateKey(t), exampleToken(t), exampleTag) - assert.NoError(t, err) - assert.NotNil(t, envNode) + t.Run("valid signature by issuer", func(t *testing.T) { + t.Parallel() - buf := &bytes.Buffer{} - require.NoError(t, dagcbor.Encode(envNode, buf)) + envel := exampleEnvelope(t) + ok, err := envel.Verify() + require.NoError(t, err) + assert.True(t, ok) + }) - golden.AssertBytes(t, buf.Bytes(), "example.cbor") + t.Run("invalid signature by wrong issuer", func(t *testing.T) { + t.Parallel() + + envel, err := envelope.Unwrap(invalidNodeFromGolden(t)) + require.NoError(t, err) - // TODO: use golden file + ok, _ := envel.Verify() + assert.False(t, ok) + }) } func TestEnvelope_Wrap(t *testing.T) { t.Parallel() - env := exampleEnvelope(t) + envel := exampleEnvelope(t) - envNode, err := env.Wrap() + node, err := envel.Wrap() require.NoError(t, err) - buf := &bytes.Buffer{} - require.NoError(t, dagjson.Encode(envNode, buf)) + cbor, err := ipld.Encode(node, dagcbor.Encode) + require.NoError(t, err) - golden.AssertBytes(t, buf.Bytes(), "example.json") + assert.Equal(t, golden.Get(t, exampleDAGCBORFilename), cbor) +} - t.Log(buf.String()) +func exampleGoldenEnvelope(t *testing.T) *envelope.Envelope { + t.Helper() - env1, err := envelope.Unwrap(envNode, exampleTag) + envel, err := envelope.Unwrap(exampleGoldenNode(t)) require.NoError(t, err) - assert.NotNil(t, env1) - t.Log(string(env1.Signature())) - assert.Equal(t, env.Signature(), env1.Signature()) - assert.Equal(t, env.VarsigHeader(), env1.VarsigHeader()) - assert.Equal(t, env.Token(), env1.Token()) + return envel +} + +func exampleGoldenNode(t *testing.T) datamodel.Node { + t.Helper() + + cbor := golden.Get(t, exampleDAGCBORFilename) - // t.Fail() + node, err := ipld.Decode(cbor, dagcbor.Decode) + require.NoError(t, err) + + return node } -func TestEnvelope_Verify(t *testing.T) { - t.Parallel() +func exampleGoldenTokenPayload(t *testing.T) *token.Token { + t.Helper() - t.Run("true with correct public key", func(t *testing.T) { - t.Parallel() + return exampleGoldenEnvelope(t).TokenPayload() +} - env := exampleEnvelope(t) - ok, err := env.Verify(rsaPublicKey(t)) - require.NoError(t, err) - require.True(t, ok) - }) +func examplePrivKey(t *testing.T) crypto.PrivKey { + t.Helper() - t.Run("false with wrong public key", func(t *testing.T) { - t.Parallel() + privKeyEnc, err := crypto.ConfigDecodeKey(examplePrivKeyCfg) + require.NoError(t, err) - _, pub, err := crypto.GenerateRSAKeyPair(2048, rand.Reader) - require.NoError(t, err) + privKey, err := crypto.UnmarshalPrivateKey(privKeyEnc) + require.NoError(t, err) - env := exampleEnvelope(t) - ok, err := env.Verify(pub) - assert.ErrorIs(t, err, rsa.ErrVerification) - assert.False(t, ok) - }) + return privKey } func exampleEnvelope(t *testing.T) *envelope.Envelope { t.Helper() - env, err := envelope.New(rsaPrivateKey(t), exampleToken(t), exampleTag) + envel, err := envelope.New(examplePrivKey(t), exampleToken(t), exampleTag) require.NoError(t, err) - t.Log("exampleEnvelope.Signature", base64.RawStdEncoding.EncodeToString(env.Signature())) + return envel +} + +func examplePubKey(t *testing.T) crypto.PubKey { + t.Helper() - return env + return examplePrivKey(t).GetPublic() } -func exampleToken(t *testing.T) *token.Token { +func exampleSignature(t *testing.T) []byte { t.Helper() - id, err := did.Parse("did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK") + sig, err := base64.RawStdEncoding.DecodeString(exampleSignatureStr) require.NoError(t, err) - _ = id // TODO: - tkn, err := token.New() // TODO: fields + return sig +} + +func exampleToken(t *testing.T) *token.Token { + t.Helper() + + id, err := did.FromPubKey(examplePubKey(t)) require.NoError(t, err) - return tkn + return &token.Token{ + Issuer: id.String(), + } } -func rsaPrivateKey(t *testing.T) crypto.PrivKey { +func exampleVarsigHeader(t *testing.T) []byte { t.Helper() - priEnc, err := crypto.ConfigDecodeKey(priCfg) - require.NoError(t, err) - pri, err := crypto.UnmarshalPrivateKey(priEnc) + hdr, err := base64.RawStdEncoding.DecodeString(exampleVarsigHeaderStr) require.NoError(t, err) - return pri + return hdr } -func rsaPublicKey(t *testing.T) crypto.PubKey { +func invalidNodeFromGolden(t *testing.T) datamodel.Node { t.Helper() - return rsaPrivateKey(t).GetPublic() + 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 } diff --git a/internal/envelope/testdata/example.cbor b/internal/envelope/testdata/example.cbor deleted file mode 100644 index 64bf364530dc46ef7ceeb6f0d2839700adf3b44a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 318 zcmV-E0m1%)SpfhW(FK$_xpKl8N9OW(nv544nm70?W@FNNhn!nu>RE3-!G))5-0{+v zm(~vEQQA0VGSg1K3neUW7g9wylynlv;4?=&ID41TYEr+&J}Dz{qITJ(nvU{Zi2g;m za=Cw#)GJuU?Z|S(IW0~Pdc8`+MzQ|3eoa6ye^&TCiBuIY|<>Hs%+v}0suNje}e#|Kk8IaS8o zKj)pP10N1|cu^V7add8D8tuP30w?uDC|vX)U6V{>(4_LBRLF8}}l diff --git a/internal/envelope/testdata/example.dagcbor b/internal/envelope/testdata/example.dagcbor new file mode 100644 index 0000000..613cba2 --- /dev/null +++ b/internal/envelope/testdata/example.dagcbor @@ -0,0 +1 @@ +‚X@=•zfˆîŒ— ©Ê¦Z.ÈÚàP óú¤‹Jr=n–¬;¡)Dñ÷™¹6îB;ò kRêÌq¢ ?~=Wú©ÛS¢ahD4íqxucan/example@v1.0.0-rc.1¤ccmd`cexpöcissx8did:key:z6MkpuK2Amsu1RqcLGgmHHQHhvmeXCCBVsM4XFSg2cCyg4Nhcsubö \ No newline at end of file diff --git a/internal/envelope/testdata/example.dagjson b/internal/envelope/testdata/example.dagjson new file mode 100644 index 0000000..4577a11 --- /dev/null +++ b/internal/envelope/testdata/example.dagjson @@ -0,0 +1 @@ +[{"/":{"bytes":"PZV6A2aI7n+MlyADqcqmWhkuyNrgUCDz+qSLSnI9bpasOwOhKUTx95m5Nu5CO/INa1LqzHGioD9+PVf6qdtTBg"}},{"h":{"/":{"bytes":"NO0BcQ"}},"ucan/example@v1.0.0-rc.1":{"cmd":"","exp":null,"iss":"did:key:z6MkpuK2Amsu1RqcLGgmHHQHhvmeXCCBVsM4XFSg2cCyg4Nh","sub":null}}] \ No newline at end of file diff --git a/internal/envelope/testdata/example.json b/internal/envelope/testdata/example.json deleted file mode 100644 index cbda4b0..0000000 --- a/internal/envelope/testdata/example.json +++ /dev/null @@ -1 +0,0 @@ -[{"/":{"bytes":"G9EFlDm5csIZR+byd5qMFxuaN/gsZmPSeoecW2PqWW8+wYWna9zx0peX1g7mUdo4ZTLTTr8LJSxuF1JFOJR0EsjgM0c8OHuX0WpSv8U+KSNxonbZpZqO8lyI/kW4crl/k9QrWMXtyHLEOS1OD3q9SsNGsf62fk1AMH9W+D2JVBVWdWAYFVXVkXQ+RbJi21lWYc9v/JtHSJbbuCbwhRqEsTBdhcYnyfFLcgLZvR9vqM636gA3ebRjZGZJOiAvxwdTOzlVxtw/552pAx8Od3hRGc5xdG5jGu2/OwIn9UMoXPQl7pMUYqk1nfqN3C7kDelIaQlgoAGyfssepB1tMRH/KA"}},{"h":{"/":{"bytes":"NIUkEoACcQ"}},"ucan/example@v1.0.0-rc.1":{"cmd":"","exp":null,"iss":"","sub":null}}] \ No newline at end of file diff --git a/internal/token/conversion.go b/internal/token/conversion.go new file mode 100644 index 0000000..0e63b9a --- /dev/null +++ b/internal/token/conversion.go @@ -0,0 +1,24 @@ +package token + +import ( + "github.com/ipld/go-ipld-prime/datamodel" +) + +func ToIPLDMapStringAny(m map[string]datamodel.Node) Map__String__Any { + keys := make([]string, len(m)) + i := 0 + + for k := range m { + keys[i] = k + i++ + } + + return Map__String__Any{ + Keys: keys, + Values: m, + } +} + +func FromIPLDMapStringAny(m Map__String__Any) map[string]datamodel.Node { + return m.Values +} diff --git a/internal/token/doc.go b/internal/token/doc.go index e69de29..33a2850 100644 --- a/internal/token/doc.go +++ b/internal/token/doc.go @@ -0,0 +1,33 @@ +// Package token provides a generic model of the [TokenPayload] required +// within an Envelope. +// +// # Field requirements +// +// While the Token object represents the wire format of both a UCAN +// Delegation token and a UCAN Invocation token, the delegation and +// invocation packages are, respectively, responsible for making sure +// required fields are included when creating a new Token or when +// validating the contents of an Envelope as it's received from +// another party. The following table shows the current (as of +// 2024-09-11) relationship between optional and nullable fields in +// the delegation and invocation views and the payload model: +// +// | Name | Delegation | Invocation | Token | +// | | Required | Nullable | Required | Nullable | | +// | ----- | -------- | -------- | -------- | -------- | -------- | +// | iss | Yes | No | Yes | No | | +// | aud | Yes | No | No | N/A | Optional | +// | sub | Yes | Yes | Yes | No | Nullable | +// | cmd | Yes | No | Yes | No | | +// | pol | Yes | No | X | X | Optional | +// | nonce | Yes | No | No | N/A | Optional | +// | meta | No | N/A | No | N/A | Optional | +// | nbf | No | N/A | X | X | Optional | +// | exp | Yes | Yes | Yes | Yes | | +// | args | X | X | Yes | No | Optional | +// | prf | X | X | Yes | No | Optional | +// | iat | X | X | No | N/A | Optional | +// | cause | X | X | No | N/A | Optional | +// +// [TokenPayload]: https://github.com/ucan-wg/spec?tab=readme-ov-file#envelope +package token diff --git a/internal/token/errors.go b/internal/token/errors.go index 4dfbcb2..949b221 100644 --- a/internal/token/errors.go +++ b/internal/token/errors.go @@ -7,3 +7,5 @@ var ErrFailedSchemaLoad = errors.New("failed to load IPLD Schema") var ErrNoSchemaType = errors.New("schema does not contain type") var ErrNodeNotToken = errors.New("IPLD node is not a Token") + +var ErrMissingRequiredDID = errors.New("a required DID is missing") diff --git a/internal/token/schema_test.go b/internal/token/schema_test.go index 06b13e7..3058e42 100644 --- a/internal/token/schema_test.go +++ b/internal/token/schema_test.go @@ -77,6 +77,7 @@ func TestSchemaRoundTrip(t *testing.T) { func BenchmarkSchemaLoad(b *testing.B) { b.ReportAllocs() + for i := 0; i < b.N; i++ { _, _ = ipld.LoadSchemaBytes(schemaBytes) } diff --git a/internal/token/token.go b/internal/token/token.go index e11cd01..197738b 100644 --- a/internal/token/token.go +++ b/internal/token/token.go @@ -7,10 +7,12 @@ import ( //go:generate go run ../cmd/token/... -func New() (*Token, error) { - return &Token{}, nil -} - +// Unwrap creates a Token from an arbitrary IPLD node or returns an +// error if at least the required model fields are not present. +// +// It is the responsibility of the Delegation and Invocation views +// to further validate the presence of the required fields and the +// content as needed. func Unwrap(node datamodel.Node) (*Token, error) { iface := bindnode.Unwrap(node) if iface == nil { @@ -25,6 +27,7 @@ func Unwrap(node datamodel.Node) (*Token, error) { return tkn, nil } +// Wrap creates an IPLD node representing the Token. func (t *Token) Wrap() datamodel.Node { return bindnode.Wrap(t, tokenType()) } diff --git a/internal/token/token.ipldsch b/internal/token/token.ipldsch index 3f919ce..596f8ff 100644 --- a/internal/token/token.ipldsch +++ b/internal/token/token.ipldsch @@ -8,7 +8,7 @@ type DID string # Field requirements: # # | Name | Delegation | Invocation | Token | -# | | Required | Nullable | Required | Nullable | | +# | | Required | Nullable | Required | Nullable | | # | ----- | -------- | -------- | -------- | -------- | -------- | # | iss | Yes | No | Yes | No | | # | aud | Yes | No | No | N/A | Optional | @@ -18,7 +18,7 @@ type DID string # | nonce | Yes | No | No | N/A | Optional | # | meta | No | N/A | No | N/A | Optional | # | nbf | No | N/A | X | X | Optional | -# | exp | Yes | Yes | Yes | Yes | | +# | exp | Yes | Yes | Yes | Yes | Nullable | # | args | X | X | Yes | No | Optional | # | prf | X | X | Yes | No | Optional | # | iat | X | X | No | N/A | Optional | @@ -40,10 +40,10 @@ type Token struct { policy optional Any (rename "pol") # The invocation's arguments - args optional {String: Any} + arguments optional {String: Any} (rename "args") # Delegations that prove the chain of authority - prf optional [CID] + Proofs optional [CID] (rename "prf") # A unique, random nonce nonce optional Bytes diff --git a/internal/token/token_gen.go b/internal/token/token_gen.go index 4692df0..472e3d0 100644 --- a/internal/token/token_gen.go +++ b/internal/token/token_gen.go @@ -20,8 +20,8 @@ type Token struct { Subject *string Command string Policy *datamodel.Node - Args *Map__String__Any - Prf *List__CID + Arguments *Map__String__Any + Proofs *List__CID Nonce *[]uint8 Meta *Map__String__Any NotBefore *int diff --git a/internal/token/token_test.go b/internal/token/token_test.go index d0c3609..5eadaa3 100644 --- a/internal/token/token_test.go +++ b/internal/token/token_test.go @@ -12,8 +12,7 @@ import ( func TestEncode(t *testing.T) { t.Parallel() - tkn, err := token.New() - require.NoError(t, err) + tkn := &token.Token{} node := tkn.Wrap() @@ -24,3 +23,33 @@ func TestEncode(t *testing.T) { t.Fail() } + +func TestPrototype(t *testing.T) { + t.Parallel() + + tkn := &token.Token{ + Issuer: "blah", + } + n1 := tkn.Wrap() + json, err := ipld.Encode(n1, dagjson.Encode) + require.NoError(t, err) + + t.Log(string(json)) + + n2, err := ipld.Decode(json, dagjson.Decode) + require.NoError(t, err) + + nb := token.Prototype().Representation().NewBuilder() + require.NoError(t, nb.AssignNode(n2)) + + n3 := nb.Build() + + tkn2, err := token.Unwrap(n3) + require.NoError(t, err) + + t.Log(tkn2) + + require.Equal(t, tkn, tkn2) + + t.Fail() +} diff --git a/internal/tools/tools.go b/internal/tools/tools.go index eaeb0e9..481024b 100644 --- a/internal/tools/tools.go +++ b/internal/tools/tools.go @@ -3,5 +3,5 @@ package tools import ( - _ "github.com/launchdarkly/go-options" + _ "github.com/selesy/go-options" )