diff --git a/examples/gno.land/p/demo/gnorkle/gnorkle/instance.gno b/examples/gno.land/p/demo/gnorkle/gnorkle/instance.gno index 22746d569a8..11f3e0fe7fc 100644 --- a/examples/gno.land/p/demo/gnorkle/gnorkle/instance.gno +++ b/examples/gno.land/p/demo/gnorkle/gnorkle/instance.gno @@ -113,7 +113,30 @@ func (i *Instance) HandleMessage(msg string, postHandler PostMessageHandler) (st switch funcType { case message.FuncTypeRequest: return i.GetFeedDefinitions(caller) + case message.FuncTypeRelay: + publicKey, signature, relayedMsg := message.ParseRelayedMessage(msg) + i.assertSignatureIsValid(publicKey, signature, relayedMsg) + + funcType, relayedMsg := message.ParseFunc(relayedMsg) + + id, relayedMsg := message.ParseID(relayedMsg) + if err := assertValidID(id); err != nil { + return "", err + } + + feedWithWhitelist, err := i.getFeedWithWhitelist(id) + if err != nil { + return "", err + } + + if err := feedWithWhitelist.Ingest(funcType, relayedMsg, caller); err != nil { + return "", err + } + + if postHandler != nil { + postHandler.Handle(i, funcType, feedWithWhitelist) + } default: id, msg := message.ParseID(msg) if err := assertValidID(id); err != nil { @@ -237,3 +260,15 @@ func (i *Instance) GetFeedDefinitions(forAddress string) (string, error) { buf.WriteString("]") return buf.String(), nil } + +func (i *Instance) assertSignatureIsValid(publicKey, signature, msg string,) { + validSignature, signer := std.VerifySignature(publicKey, signature, msg) + if !validSignature { + panic("invalid signature") + } + + // For the moment just accept general whitelisted addressed not feed specific ones + if !addressIsWhitelisted(&i.whitelist, nil, signer, nil) { + panic("address is not whitelisted") + } +} \ No newline at end of file diff --git a/examples/gno.land/p/demo/gnorkle/message/parse.gno b/examples/gno.land/p/demo/gnorkle/message/parse.gno index 633dcc38b66..54ddf72e105 100644 --- a/examples/gno.land/p/demo/gnorkle/message/parse.gno +++ b/examples/gno.land/p/demo/gnorkle/message/parse.gno @@ -23,3 +23,13 @@ func parseFirstToken(rawMsg string) (string, string) { return msgParts[0], msgParts[1] } + +// ParseRelayedMessage parses a relayed message and returns: +// the signing public key +// the signature (commonly hexadecimal) +// The relayed message (Containing the messageFunction != Relay) +func ParseRelayedMessage(rawMsg string) ( string, string, string) { + publicKey, remainder := parseFirstToken(rawMsg) + signature, remainder := parseFirstToken(remainder) + return publicKey, signature, remainder +} diff --git a/examples/gno.land/p/demo/gnorkle/message/type.gno b/examples/gno.land/p/demo/gnorkle/message/type.gno index a80e568ea24..8e7c8481563 100644 --- a/examples/gno.land/p/demo/gnorkle/message/type.gno +++ b/examples/gno.land/p/demo/gnorkle/message/type.gno @@ -12,4 +12,7 @@ const ( // FuncTypeRequest means the agent is requesting feed definitions for all those // that it is whitelisted to provide data for. FuncTypeRequest FuncType = "request" + // FuncTypeRelay means a non whitelisted-user is relaying a message from a whitelisted agent + // the relayer user should provide a publicKey, the message to handle and a valid signature + FuncTypeRelay FuncType = "relay" ) diff --git a/gnovm/stdlibs/generated.go b/gnovm/stdlibs/generated.go index 9775bc877cc..9091e9d34ba 100644 --- a/gnovm/stdlibs/generated.go +++ b/gnovm/stdlibs/generated.go @@ -731,6 +731,7 @@ var nativeFuncs = [...]NativeFunc{ }, []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("bool")}, + {Name: gno.N("r1"), Type: gno.X("string")}, }, false, func(m *gno.Machine) { @@ -748,13 +749,18 @@ var nativeFuncs = [...]NativeFunc{ gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1) gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 2, "")).TV, rp2) - r0 := libs_std.X_verifySignature(p0, p1, p2) + r0, r1 := libs_std.X_verifySignature(p0, p1, p2) m.PushValue(gno.Go2GnoValue( m.Alloc, m.Store, reflect.ValueOf(&r0).Elem(), )) + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r1).Elem(), + )) }, }, { diff --git a/gnovm/stdlibs/std/native.gno b/gnovm/stdlibs/std/native.gno index a46fb567dd5..cbc29b6b167 100644 --- a/gnovm/stdlibs/std/native.gno +++ b/gnovm/stdlibs/std/native.gno @@ -56,7 +56,7 @@ func DecodeBech32(addr Address) (prefix string, bz [20]byte, ok bool) { return decodeBech32(string(addr)) } -func VerifySignature(pubKeySigner string,msg string, signature string) bool { +func VerifySignature(pubKeySigner string, signature string, msg string) (bool,string) { return verifySignature(pubKeySigner, msg, signature) } @@ -69,4 +69,4 @@ func getRealm(height int) (address string, pkgPath string) func derivePkgAddr(pkgPath string) string func encodeBech32(prefix string, bz [20]byte) string func decodeBech32(addr string) (prefix string, bz [20]byte, ok bool) -func verifySignature(pubKeySigner string,msg string, signature string) bool +func verifySignature(pubKeySigner string,msg string, signature string) (bool,string) diff --git a/gnovm/stdlibs/std/native.go b/gnovm/stdlibs/std/native.go index a11d2df6e22..9acb3a7e2d9 100644 --- a/gnovm/stdlibs/std/native.go +++ b/gnovm/stdlibs/std/native.go @@ -1,6 +1,8 @@ package std import ( + "encoding/hex" + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" "github.com/gnolang/gno/tm2/pkg/bech32" "github.com/gnolang/gno/tm2/pkg/crypto" @@ -150,12 +152,20 @@ func X_encodeBech32(prefix string, bytes [20]byte) string { return b32 } -func X_verifySignature(pubKeySigner string, msg string, signature string) bool { +func X_verifySignature(pubKeySigner string, msg string, signature string) (bool, string) { key, err := crypto.PubKeyFromBech32(pubKeySigner) if err != nil { panic(err) // should not happen } - return key.VerifyBytes([]byte(msg), []byte(signature)) + + decodedData, err := hex.DecodeString(signature) + if err != nil { + panic(err) // should not happen + } + + validSignature := key.VerifyBytes([]byte(msg), decodedData) + + return validSignature, key.Address().String() } func X_decodeBech32(addr string) (prefix string, bytes [20]byte, ok bool) { diff --git a/gnovm/stdlibs/std/native_test.go b/gnovm/stdlibs/std/native_test.go index aa41346378c..79c89ba3441 100644 --- a/gnovm/stdlibs/std/native_test.go +++ b/gnovm/stdlibs/std/native_test.go @@ -208,12 +208,18 @@ func TestVerify(t *testing.T) { maliciousMessage := "Malicious Message" signature, _, err := kb.Sign("user", pass, []byte(goodMessage)) assert.NoError(t, err) + signatureValid, signer := X_verifySignature(publicKey, goodMessage, string(signature)) - if !X_verifySignature(publicKey, goodMessage, string(signature)) { + if !signatureValid { t.Error("verify failed") } - if X_verifySignature(publicKey, maliciousMessage, string(signature)) { + if signer != info.GetAddress().String() { + t.Error("signer is not equal to address") + } + + signatureValid, _ = X_verifySignature(publicKey, maliciousMessage, string(signature)) + if signatureValid { t.Error("verify worked on malicious message") } }