package main import ( "runtime" "git.smesh.lol/musiquay/web/common/helpers" "git.smesh.lol/musiquay/web/common/jsbridge/ext" ) // Method name strings built from byte constants. Package var initializers run // before main (moxie has no init()); push keeps them heap-allocated instead of // placing them in the data section, which wasm32 relocations have corrupted. var ( _true string _false string mGetPublicKey string mSignEvent string mGetRelays string mNip44Encrypt string mNip44Decrypt string mGetSharedSecret string mNip04Encrypt string mNip04Decrypt string mGetVaultStatus string mLockVault string mListIdentities string mSwitchIdentity string mAddIdentity string mRemoveIdentity string mNsecLogin string mGetPermissions string mSetPermission string mPromptResponse string mGetMnemonic string mIsHD string mResetExtension string mValidateMnemonic string mGenerateMnemonic string mNwcList string mUnlockVault string mCreateVault string mExportVault string mImportVault string mCreateHDVault string mRestoreHDVault string mDeriveIdentity string mProbeAccount string mNwcAdd string mNwcRemove string mNwcBuildRequest string mNwcParseResponse string mEcdhWithSecret string mSignWithSecret string mPubkeyFromSecret string mLastUnlockError string ) // --- Response builders: byte constants only --- func jsonTrue() (s string) { return jsonResult(_true) } func jsonFalse() (s string) { return jsonResult(_false) } func jsonResult(val string) (s string) { b := push([]byte(nil), '{', '"', 'r', 'e', 's', 'u', 'l', 't', '"', ':') b = b | val b = b | "}" return string(b) } func jsonResultStr(s string) (sv string) { return jsonResult(helpers.JsonString(s)) } func jsonErr(msg string) (s string) { b := push([]byte(nil), '{', '"', 'e', 'r', 'r', 'o', 'r', '"', ':') b = b | helpers.JsonString(msg) b = b | "}" return string(b) } func unknownMethod() (s string) { return jsonErr(string(push([]byte(nil), 'u', 'n', 'k', 'n', 'o', 'w', 'n', ' ', 'm', 'e', 't', 'h', 'o', 'd'))) } // signerState is the signer worker's mutable state: the NWC wallet connection // list, the per-host permission table, the unlocked vault and its identity // list, and the HD vault's mnemonic state. Package globals are immutable // outside initialization, so all of it lives in one self-mutating type // reached through a package-level pointer. type signerState struct { walletConns []nwcConn permissions []permission // vault.mx vaultKey []byte vaultSalt []byte // 32-byte Argon2id salt (v2+ only) vaultHash string // hex SHA-256 of password, for fast unlock check vaultVersion int32 // 1, 2, or 3 vaultArgon2M int32 // argon2 memory param this vault was encrypted with vaultOpen bool vaultExists bool vaultRawCache string // cached JSON from storage, set in loadVault callback // v2 compat: shared IV from stored vault, used only to decrypt v2 fields // during migration. v2IV []byte identities []identity activeIdx int32 // Set by unlockVault on failure for diagnostic surface via mgmtUnlockVault. // Cleared on success. Values: "no-vault", "no-hash", "wrong-password", // "bad-iv", "kdf-failed", "decrypt-failed". lastUnlockErr string // hd.mx hdMnemonic string // decrypted BIP-39 phrase, "" when not an HD vault hdNextAccount int32 // next unused account index } var signSt *signerState func initState() { if signSt != nil { return } // signSt and everything it holds live as long as this worker: build it in // the root arena, not in this call's frame arena which dies on return. runtime.SovereignSetArena(runtime.RootArena()) signSt = &signerState{} runtime.SovereignRestoreArena(runtime.RootArena()) } func initSignerGlobals() { _true = string(push([]byte(nil), 't', 'r', 'u', 'e')) _false = string(push([]byte(nil), 'f', 'a', 'l', 's', 'e')) mGetPublicKey = string(push([]byte(nil), 'g', 'e', 't', 'P', 'u', 'b', 'l', 'i', 'c', 'K', 'e', 'y')) mSignEvent = string(push([]byte(nil), 's', 'i', 'g', 'n', 'E', 'v', 'e', 'n', 't')) mGetRelays = string(push([]byte(nil), 'g', 'e', 't', 'R', 'e', 'l', 'a', 'y', 's')) mNip44Encrypt = string(push([]byte(nil), 'n', 'i', 'p', '4', '4', '.', 'e', 'n', 'c', 'r', 'y', 'p', 't')) mNip44Decrypt = string(push([]byte(nil), 'n', 'i', 'p', '4', '4', '.', 'd', 'e', 'c', 'r', 'y', 'p', 't')) mGetSharedSecret = string(push([]byte(nil), 'g', 'e', 't', 'S', 'h', 'a', 'r', 'e', 'd', 'S', 'e', 'c', 'r', 'e', 't')) mNip04Encrypt = string(push([]byte(nil), 'n', 'i', 'p', '0', '4', '.', 'e', 'n', 'c', 'r', 'y', 'p', 't')) mNip04Decrypt = string(push([]byte(nil), 'n', 'i', 'p', '0', '4', '.', 'd', 'e', 'c', 'r', 'y', 'p', 't')) 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')) mLockVault = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'l', 'o', 'c', 'k', 'V', 'a', 'u', 'l', 't')) 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')) 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')) mAddIdentity = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'a', 'd', 'd', 'I', 'd', 'e', 'n', 't', 'i', 't', 'y')) 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')) mNsecLogin = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'n', 's', 'e', 'c', 'L', 'o', 'g', 'i', 'n')) 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')) 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')) 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')) mGetMnemonic = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'g', 'e', 't', 'M', 'n', 'e', 'm', 'o', 'n', 'i', 'c')) mIsHD = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'i', 's', 'H', 'D')) 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')) 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')) 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')) mNwcList = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'n', 'w', 'c', '.', 'l', 'i', 's', 't')) mUnlockVault = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'u', 'n', 'l', 'o', 'c', 'k', 'V', 'a', 'u', 'l', 't')) mCreateVault = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'c', 'r', 'e', 'a', 't', 'e', 'V', 'a', 'u', 'l', 't')) mExportVault = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'e', 'x', 'p', 'o', 'r', 't', 'V', 'a', 'u', 'l', 't')) mImportVault = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'i', 'm', 'p', 'o', 'r', 't', 'V', 'a', 'u', 'l', 't')) 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')) 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')) 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')) 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')) mNwcAdd = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'n', 'w', 'c', '.', 'a', 'd', 'd')) mNwcRemove = string(push([]byte(nil), 'm', 'u', 's', 'i', 'q', 'u', 'a', 'y', '.', 'n', 'w', 'c', '.', 'r', 'e', 'm', 'o', 'v', 'e')) 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')) 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')) 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')) 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')) 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')) 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')) } func main() { initSignerGlobals() initState() loadVault() loadPermissions() ext.OnMessage(handleMessage) } // handleMessage dispatches using string equality against heap-built method names. func handleMessage(method, paramsJSON string, senderTabID int32, respond func(string)) { if method == mLockVault { respond(mgmtLockVault()); return } if method == mValidateMnemonic { respond(mgmtValidateMnemonic(paramsJSON)); return } if method == mSwitchIdentity { respond(mgmtSwitchIdentity(paramsJSON)); return } if method == mRemoveIdentity { respond(mgmtRemoveIdentity(paramsJSON)); return } if method == mResetExtension { respond(mgmtResetExtension()); return } if method == mCreateVault { respond(mgmtCreateVault(paramsJSON)); return } if method == mGetPublicKey { respond(nip07GetPublicKey()); return } if method == mSignEvent { respond(nip07SignEvent(paramsJSON)); return } if method == mGetRelays { respond("{\"result\":{}}"); return } if method == mNip44Encrypt { respond(nip07Nip44Encrypt(paramsJSON)); return } if method == mNip44Decrypt { respond(nip07Nip44Decrypt(paramsJSON)); return } if method == mGetSharedSecret { respond(nip07GetSharedSecret(paramsJSON)); return } if method == mNip04Encrypt { respond(nip07Nip04Encrypt(paramsJSON)); return } if method == mNip04Decrypt { respond(nip07Nip04Decrypt(paramsJSON)); return } if method == mGetVaultStatus { respond(mgmtGetVaultStatus()); return } if method == mListIdentities { respond(mgmtListIdentities()); return } if method == mAddIdentity { respond(mgmtAddIdentity(paramsJSON)); return } if method == mNsecLogin { respond(mgmtNsecLogin(paramsJSON)); return } if method == mGetPermissions { respond(mgmtGetPermissions()); return } if method == mSetPermission { respond(mgmtSetPermission(paramsJSON)); return } if method == mPromptResponse { respond(mgmtPromptResponse(paramsJSON)); return } if method == mGetMnemonic { respond(mgmtGetMnemonic()); return } if method == mIsHD { respond(mgmtIsHD()); return } if method == mGenerateMnemonic { respond(mgmtGenerateMnemonic()); return } if method == mNwcList { respond(nwcList()); return } if method == mUnlockVault { respond(mgmtUnlockVault(paramsJSON)); return } if method == mExportVault { respond(mgmtExportVault(paramsJSON)); return } if method == mImportVault { respond(mgmtImportVault(paramsJSON)); return } if method == mCreateHDVault { respond(mgmtCreateHDVault(paramsJSON)); return } if method == mRestoreHDVault { respond(mgmtRestoreHDVault(paramsJSON)); return } if method == mDeriveIdentity { respond(mgmtDeriveIdentity(paramsJSON)); return } if method == mProbeAccount { respond(mgmtProbeAccount(paramsJSON)); return } if method == mNwcAdd { respond(nwcAdd(paramsJSON)); return } if method == mNwcRemove { respond(nwcRemove(paramsJSON)); return } if method == mNwcBuildRequest { respond(nwcBuildRequest(paramsJSON)); return } if method == mNwcParseResponse { respond(nwcParseResponse(paramsJSON)); return } if method == mEcdhWithSecret { respond(cryptoEcdhWithSecret(paramsJSON)); return } if method == mSignWithSecret { respond(cryptoSignWithSecret(paramsJSON)); return } if method == mPubkeyFromSecret { respond(cryptoPubkeyFromSecret(paramsJSON)); return } if method == mLastUnlockError { respond(jsonResultStr(signSt.lastUnlockErr)); return } respond(unknownMethod()) }