PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/p/moul/x/wesh/v0

Package
Open in gnoweb ↗

Overview

Kind
Pure package
Name
v0
Namespace
moul / x / wesh
Files
12 (README)(gnomod.toml)
Exported functions
n/a — not supported for pure packages by the node (vm/qfuncs)
Module
gno.land/p/moul/x/wesh/v0
gno
0.9

Files (12)

  • README.mdmarkdown
  • gnomod.tomltoml
  • hmac.gnogno
  • link.gnogno
  • rendezvous.gnogno
  • statement.gnogno
  • wesh.gnogno
  • hmac_test.gnogno
  • link_test.gnogno
  • rendezvous_test.gnogno
  • statement_test.gnogno
  • wesh_test.gnogno
  • statement.gnogno
    1package wesh23import (4	"crypto/ed25519"5	"crypto/sha256"6	"encoding/hex"7	"errors"8

    Functions

    not supported for pure packages by the node (vm/qfuncs)

    Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

    "strconv"
    9 "strings"
    10)
    11
    12// Statement kinds. A signature is only ever valid for the kind it was made
    13// under, so a binding signature can never be replayed as a device statement.
    14const (
    15 kindBind = "gno.wesh/v0:bind"
    16 kindDevice = "gno.wesh/v0:device"
    17)
    18
    19// Device sigchain operations.
    20const (
    21 OpAdd = "add"
    22 OpRevoke = "revoke"
    23)
    24
    25var (
    26 ErrBadOp = errors.New("wesh: operation must be add or revoke")
    27 ErrBadSignature = errors.New("wesh: signature does not verify under the account key")
    28 ErrBadSeq = errors.New("wesh: sequence number must not be negative")
    29 ErrBadPrevLen = errors.New("wesh: previous digest must be 32 bytes")
    30)
    31
    32// DigestLen is the length of a statement digest (SHA-256).
    33const DigestLen = 32
    34
    35// ValidOp reports whether op is a known sigchain operation.
    36func ValidOp(op string) bool { return op == OpAdd || op == OpRevoke }
    37
    38// BindStatement returns the canonical text an account signs, with its Wesh
    39// account private key, to claim a directory entry.
    40//
    41// The signature is what makes a directory entry meaningful. Without it anyone
    42// could publish anyone else's account key next to a seed of their choosing and
    43// harvest the contact requests that followed. With it, a directory entry is a
    44// statement by the account itself, verifiable by anyone, replayable nowhere:
    45//
    46// - the kind prefix stops a signature made for one purpose being reused for
    47// another;
    48// - chainID stops a binding signed for a testnet being replayed on mainnet;
    49// - gnoAddr binds the Wesh identity to exactly one gno account, so a leaked
    50// signature cannot be used to claim the identity from a different address;
    51// - revision is monotonic, so an old, superseded binding cannot be replayed
    52// to roll a rotation back.
    53//
    54// payload is the seed for a publicly published identity, or the seed
    55// commitment from [SeedCommitment] for one that keeps its rendezvous point off
    56// chain. The two are indistinguishable on the wire, which is deliberate: an
    57// observer cannot tell a committed identity from a published one without the
    58// realm's own mode flag.
    59//
    60// Fields are newline-framed and hex-encoded, never concatenated raw: with
    61// plain concatenation a different split of the same bytes would produce the
    62// same statement.
    63func BindStatement(chainID, gnoAddr string, accountPK, payload []byte, revision int) string {
    64 var b strings.Builder
    65 b.WriteString(kindBind)
    66 b.WriteString("\n")
    67 b.WriteString(chainID)
    68 b.WriteString("\n")
    69 b.WriteString(gnoAddr)
    70 b.WriteString("\n")
    71 b.WriteString(hex.EncodeToString(accountPK))
    72 b.WriteString("\n")
    73 b.WriteString(hex.EncodeToString(payload))
    74 b.WriteString("\n")
    75 b.WriteString(strconv.Itoa(revision))
    76 return b.String()
    77}
    78
    79// DeviceStatement returns the canonical text an account signs to append one
    80// entry to its device sigchain.
    81//
    82// This is the piece the Wesh protocol has no answer for. Its own documentation
    83// says a device, once linked, can never be revoked: the account metadata log is
    84// append-only and no authority can mark an entry void. A chain cannot undo
    85// that (the ratchets are already out there and forward secrecy is a local
    86// property), but it can host the record that was missing, so a revocation
    87// becomes publicly visible, ordered, and attributable to the account key.
    88//
    89// prev is the digest of the preceding statement, or [GenesisDigest] for seq 0.
    90// Chaining each entry to its predecessor means the log cannot be reordered or
    91// have an entry quietly dropped: any gap changes every digest after it.
    92func DeviceStatement(chainID string, accountPK []byte, seq int, prev []byte, op string, devicePK []byte) string {
    93 var b strings.Builder
    94 b.WriteString(kindDevice)
    95 b.WriteString("\n")
    96 b.WriteString(chainID)
    97 b.WriteString("\n")
    98 b.WriteString(hex.EncodeToString(accountPK))
    99 b.WriteString("\n")
    100 b.WriteString(strconv.Itoa(seq))
    101 b.WriteString("\n")
    102 b.WriteString(hex.EncodeToString(prev))
    103 b.WriteString("\n")
    104 b.WriteString(op)
    105 b.WriteString("\n")
    106 b.WriteString(hex.EncodeToString(devicePK))
    107 return b.String()
    108}
    109
    110// StatementDigest returns SHA-256 of a canonical statement. It is the value
    111// the next sigchain entry chains to.
    112func StatementDigest(statement string) []byte {
    113 sum := sha256.Sum256([]byte(statement))
    114 return sum[:]
    115}
    116
    117// GenesisDigest is the all-zero digest that the first sigchain entry chains to.
    118func GenesisDigest() []byte { return make([]byte, DigestLen) }
    119
    120// VerifyBind checks a binding signature against the account key.
    121func VerifyBind(chainID, gnoAddr string, accountPK, payload []byte, revision int, sig []byte) error {
    122 if len(accountPK) != AccountPKLen {
    123 return ErrBadAccountPKLen
    124 }
    125 stmt := BindStatement(chainID, gnoAddr, accountPK, payload, revision)
    126 if !ed25519.Verify(accountPK, []byte(stmt), sig) {
    127 return ErrBadSignature
    128 }
    129 return nil
    130}
    131
    132// VerifyDevice checks a sigchain entry's shape and its signature, and returns
    133// the digest the next entry must chain to.
    134func VerifyDevice(chainID string, accountPK []byte, seq int, prev []byte, op string, devicePK, sig []byte) ([]byte, error) {
    135 if len(accountPK) != AccountPKLen {
    136 return nil, ErrBadAccountPKLen
    137 }
    138 if len(devicePK) != DevicePKLen {
    139 return nil, ErrBadDevicePKLen
    140 }
    141 if seq < 0 {
    142 return nil, ErrBadSeq
    143 }
    144 if len(prev) != DigestLen {
    145 return nil, ErrBadPrevLen
    146 }
    147 if !ValidOp(op) {
    148 return nil, ErrBadOp
    149 }
    150 stmt := DeviceStatement(chainID, accountPK, seq, prev, op, devicePK)
    151 if !ed25519.Verify(accountPK, []byte(stmt), sig) {
    152 return nil, ErrBadSignature
    153 }
    154 return StatementDigest(stmt), nil
    155}
    156