diff --git a/gnovm/stdlibs/generated.go b/gnovm/stdlibs/generated.go index 0af38950ce1..9091e9d34ba 100644 --- a/gnovm/stdlibs/generated.go +++ b/gnovm/stdlibs/generated.go @@ -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", diff --git a/gnovm/stdlibs/std/native.gno b/gnovm/stdlibs/std/native.gno index 22a16fc18d1..c252d140da7 100644 --- a/gnovm/stdlibs/std/native.gno +++ b/gnovm/stdlibs/std/native.gno @@ -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 @@ -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) diff --git a/gnovm/stdlibs/std/native.go b/gnovm/stdlibs/std/native.go index f185a52f249..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,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 { diff --git a/gnovm/stdlibs/std/native_test.go b/gnovm/stdlibs/std/native_test.go index 851785575d7..3ac267a071f 100644 --- a/gnovm/stdlibs/std/native_test.go +++ b/gnovm/stdlibs/std/native_test.go @@ -1,14 +1,19 @@ package std import ( + "encoding/hex" "testing" "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() @@ -192,3 +197,31 @@ 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) + signatureHex := hex.EncodeToString(signature) + signatureValid, signer := X_verifySignature(publicKey, goodMessage, signatureHex) + + if !signatureValid { + t.Error("verify failed") + } + + if signer != info.GetAddress().String() { + t.Error("signer is not equal to address") + } + + signatureValid, _ = X_verifySignature(publicKey, maliciousMessage, signatureHex) + if signatureValid { + t.Error("verify worked on malicious message") + } +}