Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

statement_test.gno

5.51 Kb · 147 lines
  1package wesh
  2
  3import (
  4	"encoding/hex"
  5	"testing"
  6
  7	"gno.land/p/nt/uassert/v0"
  8)
  9
 10// Fixtures. The signatures were produced with Go's crypto/ed25519 over the
 11// exact statement text this package builds, using the account key whose seed
 12// is 32 x 0x42, the same key as testAccountPKHex.
 13const (
 14	testChainID = "gnoland-1"
 15	testGnoAddr = "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5"
 16
 17	testDev1Hex = "0707070707070707070707070707070707070707070707070707070707070707"
 18	testDev2Hex = "0909090909090909090909090909090909090909090909090909090909090909"
 19
 20	bindRev1Sig = "4cd53e6096994d2801de1b16cfd1b2d2aa34ff35b265c53fa176cfb18c949be7" +
 21		"0078161fe2afaf503cf808b2f52a321e15a625f56b1b2d0aaaf8c460f6d64702"
 22
 23	dev0Sig = "19d0bd2517ca6308b089196528e6c27359117b80e1972c71c25434a6f260d62a" +
 24		"703a28c2f973d89056e7c691b4f879d38e1b016745508b357d217eb54c984305"
 25	dev0Digest = "003570bf50d50213f9f3e9c76a8271435a8f2e4b7c24010a8e8383dedd0ed2a6"
 26
 27	dev1Sig = "c26b338045373f7504a0d251c2126e31ff3330f08463672a82467daf4dedca51" +
 28		"1da4681715a6c3afe4115622cfcca9ec3f44d5a35f9973c3b9856cc06859810c"
 29	dev1Digest = "35a8219e60f6cd375e1378879de8921f9d554f054cd8a70ea2d3914b20b77bbb"
 30)
 31
 32func TestBindStatementIsCanonical(t *testing.T) {
 33	got := BindStatement(testChainID, testGnoAddr, mustHex(t, testAccountPKHex), mustHex(t, testSeedHex), 1)
 34	want := "gno.wesh/v0:bind\n" + testChainID + "\n" + testGnoAddr + "\n" +
 35		testAccountPKHex + "\n" + testSeedHex + "\n1"
 36	uassert.Equal(t, want, got)
 37}
 38
 39func TestVerifyBind(t *testing.T) {
 40	pk := mustHex(t, testAccountPKHex)
 41	seed := mustHex(t, testSeedHex)
 42	sig := mustHex(t, bindRev1Sig)
 43
 44	uassert.NoError(t, VerifyBind(testChainID, testGnoAddr, pk, seed, 1, sig),
 45		"a signature made by the account key verifies")
 46}
 47
 48// TestVerifyBindRejectsEveryTamperedField is the heart of the scheme: each
 49// field is in the signed text precisely so that changing it invalidates the
 50// signature. One case per field, so a regression names the field it broke.
 51func TestVerifyBindRejectsEveryTamperedField(t *testing.T) {
 52	pk := mustHex(t, testAccountPKHex)
 53	seed := mustHex(t, testSeedHex)
 54	sig := mustHex(t, bindRev1Sig)
 55
 56	cases := []struct {
 57		name    string
 58		chainID string
 59		addr    string
 60		payload []byte
 61		rev     int
 62	}{
 63		{"replayed on another chain", "test7", testGnoAddr, seed, 1},
 64		{"claimed from another gno address", testChainID, "g1manfred47kzduec920z88wfr64ylksmdcedlf5", seed, 1},
 65		{"a different seed swapped in", testChainID, testGnoAddr, repeat(0xEE, 32), 1},
 66		{"an older revision replayed", testChainID, testGnoAddr, seed, 0},
 67		{"a newer revision forged", testChainID, testGnoAddr, seed, 2},
 68	}
 69	for _, tc := range cases {
 70		err := VerifyBind(tc.chainID, tc.addr, pk, tc.payload, tc.rev, sig)
 71		uassert.ErrorIs(t, err, ErrBadSignature, tc.name)
 72	}
 73}
 74
 75func TestVerifyBindRejectsAnotherAccountsKey(t *testing.T) {
 76	err := VerifyBind(testChainID, testGnoAddr, repeat(0x33, 32), mustHex(t, testSeedHex), 1, mustHex(t, bindRev1Sig))
 77	uassert.ErrorIs(t, err, ErrBadSignature)
 78
 79	err = VerifyBind(testChainID, testGnoAddr, repeat(0x33, 31), mustHex(t, testSeedHex), 1, mustHex(t, bindRev1Sig))
 80	uassert.ErrorIs(t, err, ErrBadAccountPKLen)
 81}
 82
 83func TestGenesisDigest(t *testing.T) {
 84	uassert.Equal(t, DigestLen, len(GenesisDigest()))
 85	uassert.Equal(t,
 86		"0000000000000000000000000000000000000000000000000000000000000000",
 87		hex.EncodeToString(GenesisDigest()))
 88}
 89
 90// TestVerifyDeviceChain walks a three-entry sigchain: add, add, revoke. Each
 91// entry chains to the digest of the previous one, which is what stops an entry
 92// being reordered or silently dropped.
 93func TestVerifyDeviceChain(t *testing.T) {
 94	pk := mustHex(t, testAccountPKHex)
 95
 96	d0, err := VerifyDevice(testChainID, pk, 0, GenesisDigest(), OpAdd, mustHex(t, testDev1Hex), mustHex(t, dev0Sig))
 97	uassert.NoError(t, err, "first entry")
 98	uassert.Equal(t, dev0Digest, hex.EncodeToString(d0), "digest the next entry must chain to")
 99
100	d1, err := VerifyDevice(testChainID, pk, 1, d0, OpAdd, mustHex(t, testDev2Hex), mustHex(t, dev1Sig))
101	uassert.NoError(t, err, "second entry")
102	uassert.Equal(t, dev1Digest, hex.EncodeToString(d1))
103}
104
105func TestVerifyDeviceRejectsABrokenChain(t *testing.T) {
106	pk := mustHex(t, testAccountPKHex)
107	dev1 := mustHex(t, testDev1Hex)
108	dev2 := mustHex(t, testDev2Hex)
109
110	cases := []struct {
111		name    string
112		seq     int
113		prev    []byte
114		op      string
115		device  []byte
116		sig     string
117		wantErr error
118	}{
119		{"wrong previous digest", 1, GenesisDigest(), OpAdd, dev2, dev1Sig, ErrBadSignature},
120		{"wrong sequence number", 2, mustHexRaw(dev0Digest), OpAdd, dev2, dev1Sig, ErrBadSignature},
121		{"operation flipped to revoke", 0, GenesisDigest(), OpRevoke, dev1, dev0Sig, ErrBadSignature},
122		{"a different device substituted", 0, GenesisDigest(), OpAdd, dev2, dev0Sig, ErrBadSignature},
123		{"unknown operation", 0, GenesisDigest(), "delete", dev1, dev0Sig, ErrBadOp},
124		{"negative sequence", -1, GenesisDigest(), OpAdd, dev1, dev0Sig, ErrBadSeq},
125		{"short previous digest", 0, repeat(0, 31), OpAdd, dev1, dev0Sig, ErrBadPrevLen},
126		{"short device key", 0, GenesisDigest(), OpAdd, repeat(7, 31), dev0Sig, ErrBadDevicePKLen},
127	}
128	for _, tc := range cases {
129		_, err := VerifyDevice(testChainID, pk, tc.seq, tc.prev, tc.op, tc.device, mustHexRaw(tc.sig))
130		uassert.ErrorIs(t, err, tc.wantErr, tc.name)
131	}
132}
133
134func TestValidOp(t *testing.T) {
135	uassert.True(t, ValidOp(OpAdd))
136	uassert.True(t, ValidOp(OpRevoke))
137	uassert.False(t, ValidOp("Add"), "operations are case-sensitive")
138	uassert.False(t, ValidOp(""))
139}
140
141func mustHexRaw(s string) []byte {
142	b, err := hex.DecodeString(s)
143	if err != nil {
144		panic("bad hex fixture: " + s)
145	}
146	return b
147}