From 56594d161865c85594df4cf701c3c47b0f414534 Mon Sep 17 00:00:00 2001 From: Will Scott Date: Sun, 31 Jul 2022 14:20:22 +0200 Subject: [PATCH] add end-to-end test / ipld schema for signing/validation --- client/findproviders.go | 3 ++ client/provide.go | 95 +++++++++++++++++++++++++++-------------- go.mod | 6 +-- go.sum | 6 +++ test/provide_test.go | 54 +++++++++++++++++++++++ 5 files changed, 130 insertions(+), 34 deletions(-) create mode 100644 test/provide_test.go diff --git a/client/findproviders.go b/client/findproviders.go index 360e441..df9d762 100644 --- a/client/findproviders.go +++ b/client/findproviders.go @@ -145,6 +145,9 @@ func ParseNodeAddresses(n *proto.Peer) []peer.AddrInfo { } infos = append(infos, peer.AddrInfo{ID: peerID, Addrs: []multiaddr.Multiaddr{ma}}) } + if len(n.Multiaddresses) == 0 { + infos = append(infos, peer.AddrInfo{ID: peerID}) + } return infos } diff --git a/client/provide.go b/client/provide.go index c7b142e..62c7e69 100644 --- a/client/provide.go +++ b/client/provide.go @@ -5,15 +5,18 @@ import ( "context" "crypto/sha256" "errors" + "fmt" "time" "github.com/ipfs/go-cid" "github.com/ipfs/go-delegated-routing/gen/proto" "github.com/ipld/edelweiss/values" + "github.com/ipld/go-ipld-prime" "github.com/ipld/go-ipld-prime/codec/dagjson" "github.com/ipld/go-ipld-prime/node/bindnode" "github.com/libp2p/go-libp2p-core/crypto" "github.com/libp2p/go-libp2p-core/peer" + "github.com/multiformats/go-multiaddr" "github.com/multiformats/go-multicodec" "github.com/polydawn/refmt/cbor" ) @@ -99,11 +102,41 @@ func parseProtocol(tp *proto.TransferProtocol) TransferProtocol { type ProvideRequest struct { Key cid.Cid Provider - TTL time.Duration - signature struct { - At time.Time - Bytes []byte + Timestamp int64 + AdvisoryTTL time.Duration + Signature []byte +} + +var provideSchema, _ = ipld.LoadSchemaBytes([]byte(` + type ProvideRequest struct { + Key &Any + Provider Provider + Timestamp Int + AdvisoryTTL Int + Signature Bytes + } + type Provider struct { + Peer Peer + ProviderProto [TransferProtocol] + } + type Peer struct { + ID String + Multiaddresses [Bytes] + } + type TransferProtocol struct { + Codec Int + Payload Bytes + } + `)) + +func bytesToMA(b []byte) (interface{}, error) { + return multiaddr.NewMultiaddrBytes(b) +} +func maToBytes(iface interface{}) ([]byte, error) { + if ma, ok := iface.(multiaddr.Multiaddr); ok { + return ma.Bytes(), nil } + return nil, fmt.Errorf("did not get expected MA type") } // Sign a provide request @@ -111,13 +144,8 @@ func (pr *ProvideRequest) Sign(key crypto.PrivKey) error { if pr.IsSigned() { return errors.New("already Signed") } - pr.signature = struct { - At time.Time - Bytes []byte - }{ - At: time.Now(), - Bytes: []byte{}, - } + pr.Timestamp = time.Now().Unix() + pr.Signature = []byte{} sid, err := peer.IDFromPrivateKey(key) if err != nil { @@ -127,7 +155,12 @@ func (pr *ProvideRequest) Sign(key crypto.PrivKey) error { return errors.New("not the correct signing key") } - node := bindnode.Wrap(pr, nil) + ma, _ := multiaddr.NewMultiaddr("/") + opts := []bindnode.Option{ + bindnode.TypedBytesConverter(&ma, bytesToMA, maToBytes), + } + + node := bindnode.Wrap(pr, provideSchema.TypeByName("ProvideRequest"), opts...) nodeRepr := node.Representation() outBuf := bytes.NewBuffer(nil) if err = dagjson.Encode(nodeRepr, outBuf); err != nil { @@ -138,7 +171,7 @@ func (pr *ProvideRequest) Sign(key crypto.PrivKey) error { if err != nil { return err } - pr.signature.Bytes = sig + pr.Signature = sig return nil } @@ -146,13 +179,18 @@ func (pr *ProvideRequest) Verify() error { if !pr.IsSigned() { return errors.New("not signed") } - sig := pr.signature.Bytes - pr.signature.Bytes = []byte{} + sig := pr.Signature + pr.Signature = []byte{} defer func() { - pr.signature.Bytes = sig + pr.Signature = sig }() - node := bindnode.Wrap(pr, nil) + ma, _ := multiaddr.NewMultiaddr("/") + opts := []bindnode.Option{ + bindnode.TypedBytesConverter(&ma, bytesToMA, maToBytes), + } + + node := bindnode.Wrap(pr, provideSchema.TypeByName("ProvideRequest"), opts...) nodeRepr := node.Representation() outBuf := bytes.NewBuffer(nil) if err := dagjson.Encode(nodeRepr, outBuf); err != nil { @@ -178,21 +216,16 @@ func (pr *ProvideRequest) Verify() error { // IsSigned indicates if the ProvideRequest has been signed func (pr *ProvideRequest) IsSigned() bool { - return pr.signature.Bytes != nil + return pr.Signature != nil } func ParseProvideRequest(req *proto.ProvideRequest) (*ProvideRequest, error) { pr := ProvideRequest{ - Key: cid.Cid(req.Key), - Provider: parseProvider(&req.Provider), - TTL: time.Duration(req.AdvisoryTTL), - signature: struct { - At time.Time - Bytes []byte - }{ - At: time.Unix(int64(req.Timestamp), 0), - Bytes: req.Signature, - }, + Key: cid.Cid(req.Key), + Provider: parseProvider(&req.Provider), + AdvisoryTTL: time.Duration(req.AdvisoryTTL), + Timestamp: int64(req.Timestamp), + Signature: req.Signature, } if err := pr.Verify(); err != nil { @@ -225,9 +258,9 @@ func (fp *Client) Provide(ctx context.Context, req *ProvideRequest) (<-chan Prov ch0, err := fp.client.Provide_Async(ctx, &proto.ProvideRequest{ Key: proto.LinkToAny(req.Key), Provider: *req.Provider.ToProto(), - Timestamp: values.Int(req.signature.At.Unix()), - AdvisoryTTL: values.Int(req.TTL), - Signature: req.signature.Bytes, + Timestamp: values.Int(req.Timestamp), + AdvisoryTTL: values.Int(req.AdvisoryTTL), + Signature: req.Signature, }) if err != nil { return nil, err diff --git a/go.mod b/go.mod index 939ae89..1bba53b 100644 --- a/go.mod +++ b/go.mod @@ -7,12 +7,12 @@ require ( github.com/ipfs/go-ipns v0.1.2 github.com/ipfs/go-log/v2 v2.5.1 github.com/ipld/edelweiss v0.1.5 - github.com/ipld/go-ipld-prime v0.17.0 + github.com/ipld/go-ipld-prime v0.17.1-0.20220627233435-adf99676901e github.com/libp2p/go-libp2p-core v0.16.1 github.com/libp2p/go-libp2p-record v0.1.3 github.com/multiformats/go-multiaddr v0.5.0 github.com/multiformats/go-multicodec v0.5.0 - github.com/multiformats/go-multihash v0.1.0 + github.com/multiformats/go-multihash v0.2.0 github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e ) @@ -42,7 +42,7 @@ require ( go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.19.1 // indirect - golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf // indirect + golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect lukechampine.com/blake3 v1.1.6 // indirect ) diff --git a/go.sum b/go.sum index 9e545c7..6f6ca97 100644 --- a/go.sum +++ b/go.sum @@ -103,6 +103,8 @@ github.com/ipld/edelweiss v0.1.5/go.mod h1:IVSfo5e7vJrTKKRjR1lrtfgc2UbEMvvatNycf github.com/ipld/go-ipld-prime v0.9.0/go.mod h1:KvBLMr4PX1gWptgkzRjVZCrLmSGcZCb/jioOQwCqZN8= github.com/ipld/go-ipld-prime v0.17.0 h1:+U2peiA3aQsE7mrXjD2nYZaZrCcakoz2Wge8K42Ld8g= github.com/ipld/go-ipld-prime v0.17.0/go.mod h1:aYcKm5TIvGfY8P3QBKz/2gKcLxzJ1zDaD+o0bOowhgs= +github.com/ipld/go-ipld-prime v0.17.1-0.20220627233435-adf99676901e h1:p5qepdt1UEk6UadNwNBFDlm/uC+GwSmdVB4wqyt2JLA= +github.com/ipld/go-ipld-prime v0.17.1-0.20220627233435-adf99676901e/go.mod h1:735yXW548CKrLwVCYXzqx90p5deRJMVVxM9eJ4Qe+qE= github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY= github.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= @@ -205,6 +207,8 @@ github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUj github.com/multiformats/go-multihash v0.0.15/go.mod h1:D6aZrWNLFTV/ynMpKsNtB40mJzmCl4jb1alC0OvHiHg= github.com/multiformats/go-multihash v0.1.0 h1:CgAgwqk3//SVEw3T+6DqI4mWMyRuDwZtOWcJT0q9+EA= github.com/multiformats/go-multihash v0.1.0/go.mod h1:RJlXsxt6vHGaia+S8We0ErjhojtKzPP2AH4+kYM7k84= +github.com/multiformats/go-multihash v0.2.0 h1:oytJb9ZA1OUW0r0f9ea18GiaPOo4SXyc7p2movyUuo4= +github.com/multiformats/go-multihash v0.2.0/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc= github.com/multiformats/go-varint v0.0.2/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY= @@ -296,6 +300,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf h1:B2n+Zi5QeYRDAEodEu72OS36gmTWjgpXr2+cWcBW90o= golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= diff --git a/test/provide_test.go b/test/provide_test.go new file mode 100644 index 0000000..71f40bc --- /dev/null +++ b/test/provide_test.go @@ -0,0 +1,54 @@ +package test + +import ( + "context" + "crypto/rand" + "testing" + "time" + + "github.com/ipfs/go-cid" + "github.com/ipfs/go-delegated-routing/client" + "github.com/libp2p/go-libp2p-core/crypto" + "github.com/libp2p/go-libp2p-core/peer" + "github.com/multiformats/go-multihash" +) + +func TestProvideRoundtrip(t *testing.T) { + priv, _, err := crypto.GenerateEd25519Key(rand.Reader) + if err != nil { + t.Fatal(err) + } + pID, err := peer.IDFromPrivateKey(priv) + if err != nil { + t.Fatal(err) + } + + c, s := createClientAndServer(t, testDelegatedRoutingService{}) + defer s.Close() + + testMH, _ := multihash.Encode([]byte("test"), multihash.IDENTITY) + testCid := cid.NewCidV1(cid.Raw, testMH) + req := client.ProvideRequest{ + Key: testCid, + Provider: client.Provider{ + Peer: peer.AddrInfo{ID: pID}, + }, + AdvisoryTTL: time.Hour, + } + rc, err := c.Provide(context.Background(), &req) + if err == nil { + t.Fatal("should get sync error on unsigned provide request.") + } + + if err = req.Sign(priv); err != nil { + t.Fatal(err) + } + if rc, err = c.Provide(context.Background(), &req); err != nil { + t.Fatal(err) + } + + res := <-rc + if res.Err != nil { + t.Fatal(err) + } +}