← Back to Realms

Realm

gno.land/r/samcrew/enckeys_v1

Overview

Realm Path
gno.land/r/samcrew/enckeys_v1
Exported Functions
6
State Entries
4
Source Files
3
Total Package Entries
17

Exported Functions

6 exported functions

State

4 state entries

Source Code

FILES
enckeys.gno
go
1// Package enckeys_v1 is an ownerless public encryption-key directory.
2// Keys are announcements by accounts, not attestations of identity, custody,
3// recovery strength or successful decryption. There is no fee, admin or pause.
4package enckeys_v1
5
6import (
7	"chain"
8	"chain/runtime"
9	"crypto/bech32"
10	"encoding/base64"
11	"encoding/hex"
12	"strconv"
13
14	"gno.land/p/samcrew/avl"
15)
16
17const SuiteXWingV1 = "xwing-v1"
18const PublicKeyBytes = 1216
19
20type keyRecord struct {
21	Generation uint64
22	PublicKey  []byte
23	Operation  [16]byte
24	Height     int64
25	Revoked    bool
26}
27
28var keys = avl.NewTree()
29
30// RegisterKey replaces the caller's announcement using an optimistic lock.
31// expectedGeneration is zero only for first registration. A revoked generation
32// is retained forever; re-registering cannot reuse an old identity generation.
33func RegisterKey(cur realm, suite string, expectedGeneration uint64, publicKey []byte, opID [16]byte) uint64 {
34	caller := user(cur)
35	checkSuite(suite)
36	if len(publicKey) != PublicKeyBytes {
37		panic("enckeys: bad public key length")
38	}
39	old := current(caller)
40	checkUpdate(old, expectedGeneration, opID)
41	rec := &keyRecord{Generation: expectedGeneration + 1, PublicKey: clone(publicKey), Operation: opID, Height: runtime.ChainHeight()}
42	keys.Set(string(caller), rec)
43	emit(caller, rec, "registered")
44	return rec.Generation
45}
46
47// RevokeKey removes the active key but retains the generation and receipt.
48// Historical announcements and wraps remain public chain history.
49func RevokeKey(cur realm, suite string, expectedGeneration uint64, opID [16]byte) uint64 {
50	caller := user(cur)
51	checkSuite(suite)
52	old := current(caller)
53	checkUpdate(old, expectedGeneration, opID)
54	if old == nil || old.Revoked {
55		panic("enckeys: no active key")
56	}
57	rec := &keyRecord{Generation: expectedGeneration + 1, Operation: opID, Height: runtime.ChainHeight(), Revoked: true}
58	keys.Set(string(caller), rec)
59	emit(caller, rec, "revoked")
60	return rec.Generation
61}
62
63// GetKey returns a defensive copy; generation is still returned after revoke.
64func GetKey(addr address, suite string) (publicKey []byte, generation uint64, active bool) {
65	checkSuite(suite)
66	rec := current(CanonicalAddress(addr))
67	if rec == nil {
68		return nil, 0, false
69	}
70	return clone(rec.PublicKey), rec.Generation, !rec.Revoked
71}
72
73// KeyJSON serializes all chain integers as decimal strings. op_id and height
74// identify the last successful mutation for recovery from an unknown outcome.
75func KeyJSON(addr address, suite string) string {
76	checkSuite(suite)
77	who := CanonicalAddress(addr)
78	rec := current(who)
79	prefix := `{"address":` + strconv.Quote(string(who)) + `,"suite":"xwing-v1"`
80	if rec == nil {
81		return prefix + `,"generation":"0","active":false,"public_key":"","op_id":"","height":"0"}`
82	}
83	return prefix + `,"generation":` + strconv.Quote(strconv.FormatUint(rec.Generation, 10)) + `,"active":` + strconv.FormatBool(!rec.Revoked) + `,"public_key":` + strconv.Quote(base64.StdEncoding.EncodeToString(rec.PublicKey)) + `,"op_id":` + strconv.Quote(hex.EncodeToString(rec.Operation[:])) + `,"height":` + strconv.Quote(strconv.FormatInt(rec.Height, 10)) + `}`
84}
85
86func Render(path string) string {
87	if path == "" {
88		return "# Encryption key directory\n\nAccount announcements for the xwing-v1 suite. Revocation does not erase chain history.\n"
89	}
90	return "````json\n" + KeyJSON(address(path), SuiteXWingV1) + "\n````"
91}
92
93// CanonicalAddress accepts the chain's four checksum/case spellings but always
94// returns the lowercase original bech32 spelling that realm callers use.
95func CanonicalAddress(addr address) address {
96	if len(string(addr)) != 40 {
97		panic("enckeys: invalid address")
98	}
99	hrp, raw, err := bech32.DecodeToBase256(string(addr))
100	if err != nil || hrp != "g" || len(raw) != 20 {
101		panic("enckeys: invalid address")
102	}
103	out, err := bech32.EncodeFromBase256("g", raw)
104	if err != nil {
105		panic("enckeys: invalid address")
106	}
107	return address(out)
108}
109
110func user(cur realm) address {
111	if !cur.IsCurrent() {
112		panic("enckeys: stale realm")
113	}
114	if !cur.Previous().IsUserCall() {
115		panic("enckeys: call directly from your account")
116	}
117	assertNoCoins()
118	return cur.Previous().Address()
119}
120func checkSuite(suite string) {
121	if suite != SuiteXWingV1 {
122		panic("enckeys: unknown suite")
123	}
124}
125func current(addr address) *keyRecord {
126	v, ok := keys.Get(string(addr))
127	if !ok {
128		return nil
129	}
130	return v.(*keyRecord)
131}
132func checkUpdate(old *keyRecord, expected uint64, op [16]byte) {
133	generation := uint64(0)
134	if old != nil {
135		generation = old.Generation
136	}
137	if generation != expected {
138		panic("enckeys: stale generation")
139	}
140	if expected == ^uint64(0) {
141		panic("enckeys: generation exhausted")
142	}
143	if op == ([16]byte{}) {
144		panic("enckeys: empty operation id")
145	}
146	if old != nil && old.Operation == op {
147		panic("enckeys: operation id reused")
148	}
149}
150func clone(b []byte) []byte { return append([]byte(nil), b...) }
151func emit(addr address, rec *keyRecord, action string) {
152	chain.Emit("EncryptionKey", "address", string(addr), "suite", SuiteXWingV1, "generation", strconv.FormatUint(rec.Generation, 10), "action", action, "op_id", hex.EncodeToString(rec.Operation[:]))
153}
154

Raw Package Data

Raw JSON data