tinyrelayfile
tinyrelay/internal/sites/bech32.go/

internal/sites/bech32.go raw
1package sites
2
3import (
4 "strings"
5
6 "fiatjaf.com/nostr"
7)
8
9const bech32Charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
10
11func encodeNpub(key nostr.PubKey) string {
12 data, _ := convertBits(key[:], 8, 5, true)
13 return bech32Encode("npub", data)
14}
15
16func decodeNpub(value string) (nostr.PubKey, bool) {
17 prefix, data, ok := bech32Decode(value)
18 if !ok || prefix != "npub" {
19 return nostr.PubKey{}, false
20 }
21 raw, ok := convertBits(data, 5, 8, false)
22 if !ok || len(raw) != 32 {
23 return nostr.PubKey{}, false
24 }
25 var key nostr.PubKey
26 copy(key[:], raw)
27 return key, true
28}
29
30func bech32Encode(prefix string, data []byte) string {
31 values := make([]byte, len(data))
32 for i, value := range data {
33 values[i] = value
34 }
35 checksum := bech32Checksum(prefix, values)
36 all := append(values, checksum...)
37 var out strings.Builder
38 out.WriteString(prefix)
39 out.WriteByte('1')
40 for _, value := range all {
41 out.WriteByte(bech32Charset[value])
42 }
43 return out.String()
44}
45
46func bech32Decode(value string) (string, []byte, bool) {
47 if value != strings.ToLower(value) || len(value) < 8 {
48 return "", nil, false
49 }
50 sep := strings.LastIndexByte(value, '1')
51 if sep < 1 || sep+7 > len(value) {
52 return "", nil, false
53 }
54 prefix, encoded := value[:sep], value[sep+1:]
55 values := make([]byte, len(encoded))
56 for i, char := range encoded {
57 pos := strings.IndexByte(bech32Charset, byte(char))
58 if pos < 0 {
59 return "", nil, false
60 }
61 values[i] = byte(pos)
62 }
63 if !bech32Verify(prefix, values) {
64 return "", nil, false
65 }
66 return prefix, values[:len(values)-6], true
67}
68
69func convertBits(data []byte, from, to byte, pad bool) ([]byte, bool) {
70 var acc uint
71 bits := byte(0)
72 max := uint((1 << to) - 1)
73 out := make([]byte, 0, len(data)*int(from)/int(to)+1)
74 for _, value := range data {
75 acc = (acc << from) | uint(value)
76 bits += from
77 for bits >= to {
78 bits -= to
79 out = append(out, byte((acc>>bits)&max))
80 }
81 }
82 if pad && bits > 0 {
83 out = append(out, byte((acc<<uint(to-bits))&max))
84 }
85 if !pad && (bits >= from || ((acc<<uint(to-bits))&max) != 0) {
86 return nil, false
87 }
88 return out, true
89}
90
91func bech32Checksum(prefix string, values []byte) []byte {
92 all := append(bech32Expand(prefix), values...)
93 all = append(all, 0, 0, 0, 0, 0, 0)
94 polymod := bech32Polymod(all) ^ 1
95 out := make([]byte, 6)
96 for i := range out {
97 out[i] = byte((polymod >> uint(5*(5-i))) & 31)
98 }
99 return out
100}
101
102func bech32Verify(prefix string, values []byte) bool {
103 return bech32Polymod(append(bech32Expand(prefix), values...)) == 1
104}
105func bech32Expand(prefix string) []byte {
106 out := make([]byte, 0, len(prefix)*2+1)
107 for _, c := range prefix {
108 out = append(out, byte(c)>>5)
109 }
110 out = append(out, 0)
111 for _, c := range prefix {
112 out = append(out, byte(c)&31)
113 }
114 return out
115}
116func bech32Polymod(values []byte) uint32 {
117 generators := [...]uint32{0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3}
118 var chk uint32 = 1
119 for _, value := range values {
120 top := chk >> 25
121 chk = (chk&0x1ffffff)<<5 ^ uint32(value)
122 for i, generator := range generators {
123 if top>>uint(i)&1 != 0 {
124 chk ^= generator
125 }
126 }
127 }
128 return chk
129}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs