main.mx raw
1 package main
2
3 import (
4 "runtime"
5
6 "git.smesh.lol/musiquay/web/common/helpers"
7 "git.smesh.lol/musiquay/web/common/jsbridge/ext"
8 )
9
10 // Method name strings built from byte constants. Package var initializers run
11 // before main (moxie has no init()); push keeps them heap-allocated instead of
12 // placing them in the data section, which wasm32 relocations have corrupted.
13 var (
14 _true string
15 _false string
16 mGetPublicKey string
17 mSignEvent string
18 mGetRelays string
19 mNip44Encrypt string
20 mNip44Decrypt string
21 mGetSharedSecret string
22 mNip04Encrypt string
23 mNip04Decrypt string
24 mGetVaultStatus string
25 mLockVault string
26 mListIdentities string
27 mSwitchIdentity string
28 mAddIdentity string
29 mRemoveIdentity string
30 mNsecLogin string
31 mGetPermissions string
32 mSetPermission string
33 mPromptResponse string
34 mGetMnemonic string
35 mIsHD string
36 mResetExtension string
37 mValidateMnemonic string
38 mGenerateMnemonic string
39 mNwcList string
40 mUnlockVault string
41 mCreateVault string
42 mExportVault string
43 mImportVault string
44 mCreateHDVault string
45 mRestoreHDVault string
46 mDeriveIdentity string
47 mProbeAccount string
48 mNwcAdd string
49 mNwcRemove string
50 mNwcBuildRequest string
51 mNwcParseResponse string
52 mEcdhWithSecret string
53 mSignWithSecret string
54 mPubkeyFromSecret string
55 mLastUnlockError string
56 )
57
58 // --- Response builders: byte constants only ---
59
60 func jsonTrue() (s string) { return jsonResult(_true) }
61 func jsonFalse() (s string) { return jsonResult(_false) }
62
63 func jsonResult(val string) (s string) {
64 b := push([]byte(nil), '{', '"', 'r', 'e', 's', 'u', 'l', 't', '"', ':')
65 b = b | val
66 b = b | "}"
67 return string(b)
68 }
69
70 func jsonResultStr(s string) (sv string) {
71 return jsonResult(helpers.JsonString(s))
72 }
73
74 func jsonErr(msg string) (s string) {
75 b := push([]byte(nil), '{', '"', 'e', 'r', 'r', 'o', 'r', '"', ':')
76 b = b | helpers.JsonString(msg)
77 b = b | "}"
78 return string(b)
79 }
80
81 func unknownMethod() (s string) {
82 return jsonErr(string(push([]byte(nil), 'u', 'n', 'k', 'n', 'o', 'w', 'n', ' ', 'm', 'e', 't', 'h', 'o', 'd')))
83 }
84
85 // signerState is the signer worker's mutable state: the NWC wallet connection
86 // list, the per-host permission table, the unlocked vault and its identity
87 // list, and the HD vault's mnemonic state. Package globals are immutable
88 // outside initialization, so all of it lives in one self-mutating type
89 // reached through a package-level pointer.
90 type signerState struct {
91 walletConns []nwcConn
92 permissions []permission
93
94 // vault.mx
95 vaultKey []byte
96 vaultSalt []byte // 32-byte Argon2id salt (v2+ only)
97 vaultHash string // hex SHA-256 of password, for fast unlock check
98 vaultVersion int32 // 1, 2, or 3
99 vaultArgon2M int32 // argon2 memory param this vault was encrypted with
100 vaultOpen bool
101 vaultExists bool
102 vaultRawCache string // cached JSON from storage, set in loadVault callback
103 // v2 compat: shared IV from stored vault, used only to decrypt v2 fields
104 // during migration.
105 v2IV []byte
106
107 identities []identity
108 activeIdx int32
109
110 // Set by unlockVault on failure for diagnostic surface via mgmtUnlockVault.
111 // Cleared on success. Values: "no-vault", "no-hash", "wrong-password",
112 // "bad-iv", "kdf-failed", "decrypt-failed".
113 lastUnlockErr string
114
115 // hd.mx
116 hdMnemonic string // decrypted BIP-39 phrase, "" when not an HD vault
117 hdNextAccount int32 // next unused account index
118 }
119
120 var signSt *signerState
121
122 func initState() {
123 if signSt != nil {
124 return
125 }
126 // signSt and everything it holds live as long as this worker: build it in
127 // the root arena, not in this call's frame arena which dies on return.
128 runtime.SovereignSetArena(runtime.RootArena())
129 signSt = &signerState{}
130 runtime.SovereignRestoreArena(runtime.RootArena())
131 }
132
133 func initSignerGlobals() {
134 _true = string(push([]byte(nil), 't', 'r', 'u', 'e'))
135 _false = string(push([]byte(nil), 'f', 'a', 'l', 's', 'e'))
136 mGetPublicKey = string(push([]byte(nil), 'g', 'e', 't', 'P', 'u', 'b', 'l', 'i', 'c', 'K', 'e', 'y'))
137 mSignEvent = string(push([]byte(nil), 's', 'i', 'g', 'n', 'E', 'v', 'e', 'n', 't'))
138 mGetRelays = string(push([]byte(nil), 'g', 'e', 't', 'R', 'e', 'l', 'a', 'y', 's'))
139 mNip44Encrypt = string(push([]byte(nil), 'n', 'i', 'p', '4', '4', '.', 'e', 'n', 'c', 'r', 'y', 'p', 't'))
140 mNip44Decrypt = string(push([]byte(nil), 'n', 'i', 'p', '4', '4', '.', 'd', 'e', 'c', 'r', 'y', 'p', 't'))
141 mGetSharedSecret = string(push([]byte(nil), 'g', 'e', 't', 'S', 'h', 'a', 'r', 'e', 'd', 'S', 'e', 'c', 'r', 'e', 't'))
142 mNip04Encrypt = string(push([]byte(nil), 'n', 'i', 'p', '0', '4', '.', 'e', 'n', 'c', 'r', 'y', 'p', 't'))
143 mNip04Decrypt = string(push([]byte(nil), 'n', 'i', 'p', '0', '4', '.', 'd', 'e', 'c', 'r', 'y', 'p', 't'))
144 mGetVaultStatus = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'g', 'e', 't', 'V', 'a', 'u', 'l', 't', 'S', 't', 'a', 't', 'u', 's'))
145 mLockVault = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'l', 'o', 'c', 'k', 'V', 'a', 'u', 'l', 't'))
146 mListIdentities = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'l', 'i', 's', 't', 'I', 'd', 'e', 'n', 't', 'i', 't', 'i', 'e', 's'))
147 mSwitchIdentity = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 's', 'w', 'i', 't', 'c', 'h', 'I', 'd', 'e', 'n', 't', 'i', 't', 'y'))
148 mAddIdentity = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'a', 'd', 'd', 'I', 'd', 'e', 'n', 't', 'i', 't', 'y'))
149 mRemoveIdentity = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'r', 'e', 'm', 'o', 'v', 'e', 'I', 'd', 'e', 'n', 't', 'i', 't', 'y'))
150 mNsecLogin = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'n', 's', 'e', 'c', 'L', 'o', 'g', 'i', 'n'))
151 mGetPermissions = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'g', 'e', 't', 'P', 'e', 'r', 'm', 'i', 's', 's', 'i', 'o', 'n', 's'))
152 mSetPermission = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 's', 'e', 't', 'P', 'e', 'r', 'm', 'i', 's', 's', 'i', 'o', 'n'))
153 mPromptResponse = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'p', 'r', 'o', 'm', 'p', 't', 'R', 'e', 's', 'p', 'o', 'n', 's', 'e'))
154 mGetMnemonic = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'g', 'e', 't', 'M', 'n', 'e', 'm', 'o', 'n', 'i', 'c'))
155 mIsHD = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'i', 's', 'H', 'D'))
156 mResetExtension = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'r', 'e', 's', 'e', 't', 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n'))
157 mValidateMnemonic = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'v', 'a', 'l', 'i', 'd', 'a', 't', 'e', 'M', 'n', 'e', 'm', 'o', 'n', 'i', 'c'))
158 mGenerateMnemonic = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'g', 'e', 'n', 'e', 'r', 'a', 't', 'e', 'M', 'n', 'e', 'm', 'o', 'n', 'i', 'c'))
159 mNwcList = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'n', 'w', 'c', '.', 'l', 'i', 's', 't'))
160 mUnlockVault = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'u', 'n', 'l', 'o', 'c', 'k', 'V', 'a', 'u', 'l', 't'))
161 mCreateVault = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'c', 'r', 'e', 'a', 't', 'e', 'V', 'a', 'u', 'l', 't'))
162 mExportVault = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'e', 'x', 'p', 'o', 'r', 't', 'V', 'a', 'u', 'l', 't'))
163 mImportVault = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'i', 'm', 'p', 'o', 'r', 't', 'V', 'a', 'u', 'l', 't'))
164 mCreateHDVault = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'c', 'r', 'e', 'a', 't', 'e', 'H', 'D', 'V', 'a', 'u', 'l', 't'))
165 mRestoreHDVault = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'r', 'e', 's', 't', 'o', 'r', 'e', 'H', 'D', 'V', 'a', 'u', 'l', 't'))
166 mDeriveIdentity = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'd', 'e', 'r', 'i', 'v', 'e', 'I', 'd', 'e', 'n', 't', 'i', 't', 'y'))
167 mProbeAccount = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'p', 'r', 'o', 'b', 'e', 'A', 'c', 'c', 'o', 'u', 'n', 't'))
168 mNwcAdd = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'n', 'w', 'c', '.', 'a', 'd', 'd'))
169 mNwcRemove = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'n', 'w', 'c', '.', 'r', 'e', 'm', 'o', 'v', 'e'))
170 mNwcBuildRequest = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'n', 'w', 'c', '.', 'b', 'u', 'i', 'l', 'd', 'R', 'e', 'q', 'u', 'e', 's', 't'))
171 mNwcParseResponse = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'n', 'w', 'c', '.', 'p', 'a', 'r', 's', 'e', 'R', 'e', 's', 'p', 'o', 'n', 's', 'e'))
172 mEcdhWithSecret = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'e', 'c', 'd', 'h', 'W', 'i', 't', 'h', 'S', 'e', 'c', 'r', 'e', 't'))
173 mSignWithSecret = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 's', 'i', 'g', 'n', 'W', 'i', 't', 'h', 'S', 'e', 'c', 'r', 'e', 't'))
174 mPubkeyFromSecret = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'p', 'u', 'b', 'k', 'e', 'y', 'F', 'r', 'o', 'm', 'S', 'e', 'c', 'r', 'e', 't'))
175 mLastUnlockError = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'l', 'a', 's', 't', 'U', 'n', 'l', 'o', 'c', 'k', 'E', 'r', 'r', 'o', 'r'))
176 }
177
178 func main() {
179 initSignerGlobals()
180
181 initState()
182 loadVault()
183 loadPermissions()
184 ext.OnMessage(handleMessage)
185 }
186
187 // handleMessage dispatches using string equality against heap-built method names.
188 func handleMessage(method, paramsJSON string, senderTabID int32, respond func(string)) {
189 if method == mLockVault { respond(mgmtLockVault()); return }
190 if method == mValidateMnemonic { respond(mgmtValidateMnemonic(paramsJSON)); return }
191 if method == mSwitchIdentity { respond(mgmtSwitchIdentity(paramsJSON)); return }
192 if method == mRemoveIdentity { respond(mgmtRemoveIdentity(paramsJSON)); return }
193 if method == mResetExtension { respond(mgmtResetExtension()); return }
194 if method == mCreateVault { respond(mgmtCreateVault(paramsJSON)); return }
195 if method == mGetPublicKey { respond(nip07GetPublicKey()); return }
196 if method == mSignEvent { respond(nip07SignEvent(paramsJSON)); return }
197 if method == mGetRelays { respond("{\"result\":{}}"); return }
198 if method == mNip44Encrypt { respond(nip07Nip44Encrypt(paramsJSON)); return }
199 if method == mNip44Decrypt { respond(nip07Nip44Decrypt(paramsJSON)); return }
200 if method == mGetSharedSecret { respond(nip07GetSharedSecret(paramsJSON)); return }
201 if method == mNip04Encrypt { respond(nip07Nip04Encrypt(paramsJSON)); return }
202 if method == mNip04Decrypt { respond(nip07Nip04Decrypt(paramsJSON)); return }
203 if method == mGetVaultStatus { respond(mgmtGetVaultStatus()); return }
204 if method == mListIdentities { respond(mgmtListIdentities()); return }
205 if method == mAddIdentity { respond(mgmtAddIdentity(paramsJSON)); return }
206 if method == mNsecLogin { respond(mgmtNsecLogin(paramsJSON)); return }
207 if method == mGetPermissions { respond(mgmtGetPermissions()); return }
208 if method == mSetPermission { respond(mgmtSetPermission(paramsJSON)); return }
209 if method == mPromptResponse { respond(mgmtPromptResponse(paramsJSON)); return }
210 if method == mGetMnemonic { respond(mgmtGetMnemonic()); return }
211 if method == mIsHD { respond(mgmtIsHD()); return }
212 if method == mGenerateMnemonic { respond(mgmtGenerateMnemonic()); return }
213 if method == mNwcList { respond(nwcList()); return }
214 if method == mUnlockVault { respond(mgmtUnlockVault(paramsJSON)); return }
215 if method == mExportVault { respond(mgmtExportVault(paramsJSON)); return }
216 if method == mImportVault { respond(mgmtImportVault(paramsJSON)); return }
217 if method == mCreateHDVault { respond(mgmtCreateHDVault(paramsJSON)); return }
218 if method == mRestoreHDVault { respond(mgmtRestoreHDVault(paramsJSON)); return }
219 if method == mDeriveIdentity { respond(mgmtDeriveIdentity(paramsJSON)); return }
220 if method == mProbeAccount { respond(mgmtProbeAccount(paramsJSON)); return }
221 if method == mNwcAdd { respond(nwcAdd(paramsJSON)); return }
222 if method == mNwcRemove { respond(nwcRemove(paramsJSON)); return }
223 if method == mNwcBuildRequest { respond(nwcBuildRequest(paramsJSON)); return }
224 if method == mNwcParseResponse { respond(nwcParseResponse(paramsJSON)); return }
225 if method == mEcdhWithSecret { respond(cryptoEcdhWithSecret(paramsJSON)); return }
226 if method == mSignWithSecret { respond(cryptoSignWithSecret(paramsJSON)); return }
227 if method == mPubkeyFromSecret { respond(cryptoPubkeyFromSecret(paramsJSON)); return }
228 if method == mLastUnlockError { respond(jsonResultStr(signSt.lastUnlockErr)); return }
229 respond(unknownMethod())
230 }
231