| 1 | package sites |
| 2 | |
| 3 | import ( |
| 4 | "strings" |
| 5 | |
| 6 | "fiatjaf.com/nostr" |
| 7 | ) |
| 8 | |
| 9 | const bech32Charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" |
| 10 | |
| 11 | func encodeNpub(key nostr.PubKey) string { |
| 12 | data, _ := convertBits(key[:], 8, 5, true) |
| 13 | return bech32Encode("npub" , data) |
| 14 | } |
| 15 | |
| 16 | func 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 | |
| 30 | func 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 | |
| 46 | func 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 | |
| 69 | func 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 | |
| 91 | func 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 | |
| 102 | func bech32Verify(prefix string, values []byte) bool { |
| 103 | return bech32Polymod(append(bech32Expand(prefix), values...)) == 1 |
| 104 | } |
| 105 | func 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 | } |
| 116 | func 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 | } |