Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add verifySignature native function #22

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions examples/gno.land/p/demo/gnorkle/gnorkle/instance.gno
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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")
}
}
10 changes: 10 additions & 0 deletions examples/gno.land/p/demo/gnorkle/message/parse.gno
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
3 changes: 3 additions & 0 deletions examples/gno.land/p/demo/gnorkle/message/type.gno
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
5 changes: 5 additions & 0 deletions examples/gno.land/r/gnoland/ghverify/contract.gno
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ func RequestVerification(githubHandle string) {
); err != nil {
panic(err)
}
std.Emit(
"verification_requested",
"from", gnoAddress,
"handle", githubHandle,
)
}

// GnorkleEntrypoint is the entrypoint to the gnorkle oracle handler.
Expand Down
42 changes: 42 additions & 0 deletions gnovm/stdlibs/generated.go
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,48 @@ var nativeFuncs = [...]NativeFunc{
))
},
},
{
"std",
"verifySignature",
[]gno.FieldTypeExpr{
{Name: gno.N("p0"), Type: gno.X("string")},
{Name: gno.N("p1"), Type: gno.X("string")},
{Name: gno.N("p2"), Type: gno.X("string")},
},
[]gno.FieldTypeExpr{
{Name: gno.N("r0"), Type: gno.X("bool")},
{Name: gno.N("r1"), Type: gno.X("string")},
},
false,
func(m *gno.Machine) {
b := m.LastBlock()
var (
p0 string
rp0 = reflect.ValueOf(&p0).Elem()
p1 string
rp1 = reflect.ValueOf(&p1).Elem()
p2 string
rp2 = reflect.ValueOf(&p2).Elem()
)

gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0)
gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1)
gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 2, "")).TV, rp2)

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(),
))
},
},
{
"strconv",
"Itoa",
Expand Down
5 changes: 5 additions & 0 deletions gnovm/stdlibs/std/native.gno
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ func DecodeBech32(addr Address) (prefix string, bz [20]byte, ok bool) {
return decodeBech32(string(addr))
}

func VerifySignature(pubKeySigner string, signature string, msg string) (bool,string) {
return verifySignature(pubKeySigner, msg, signature)
}

// Variations which don't use named types.
func origSend() (denoms []string, amounts []int64)
func origCaller() string
Expand All @@ -65,3 +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,string)
18 changes: 18 additions & 0 deletions gnovm/stdlibs/std/native.go
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -150,6 +152,22 @@ func X_encodeBech32(prefix string, bytes [20]byte) string {
return b32
}

func X_verifySignature(pubKeySigner string, msg string, signature string) (bool, string) {
key, err := crypto.PubKeyFromBech32(pubKeySigner)
if err != nil {
panic(err) // should not happen
}

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) {
prefix, bz, err := bech32.Decode(addr)
if err != nil || len(bz) != 20 {
Expand Down
31 changes: 31 additions & 0 deletions gnovm/stdlibs/std/native_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ import (

"github.com/stretchr/testify/assert"

"github.com/gnolang/gno/gnovm/pkg/gnoenv"
gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
"github.com/gnolang/gno/tm2/pkg/crypto"
"github.com/gnolang/gno/tm2/pkg/crypto/keys"
)

const DefaultAccount_Seed = "source bonus chronic canvas draft south burst lottery vacant surface solve popular case indicate oppose farm nothing bullet exhibit title speed wink action roast"

func TestPrevRealmIsOrigin(t *testing.T) {
var (
user = gno.DerivePkgAddr("user1.gno").Bech32()
Expand Down Expand Up @@ -192,3 +196,30 @@ func TestPrevRealmIsOrigin(t *testing.T) {
})
}
}

func TestVerify(t *testing.T) {
kb, _ := keys.NewKeyBaseFromDir(gnoenv.HomeDir())
pass := "hardPass"
info, err := kb.CreateAccount("user", DefaultAccount_Seed, pass, pass, 0, 0)
assert.NoError(t, err)

publicKey := info.GetPubKey().String() // gpub1pgfj7ard9eg82cjtv4u4xetrwqer2dntxyfzxz3pqfzjcj8wph4wl0x7tqu3k7geuqsz2d45eddys0hgf0xd7dr2dupnqukpghs
goodMessage := "Verification Ok"
maliciousMessage := "Malicious Message"
signature, _, err := kb.Sign("user", pass, []byte(goodMessage))
assert.NoError(t, err)
signatureValid, signer := X_verifySignature(publicKey, goodMessage, string(signature))

if !signatureValid {
t.Error("verify failed")
}

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")
}
}
Loading