From 92c400fd03ca8b372ba4e7274fc564a83a0f1aa7 Mon Sep 17 00:00:00 2001 From: greenart7c3 Date: Mon, 27 May 2024 13:28:05 -0300 Subject: [PATCH] add support for sign messages --- .../nostrsigner/models/SignerType.kt | 1 + .../nostrsigner/service/IntentUtils.kt | 48 +++++++ .../nostrsigner/ui/components/SignMessage.kt | 131 +++++++++++++++++- .../ui/components/SingleEventHomeScreen.kt | 114 +++++++++++++++ app/src/main/res/values/strings.xml | 3 + gradle/libs.versions.toml | 2 +- 6 files changed, 297 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/models/SignerType.kt b/app/src/main/java/com/greenart7c3/nostrsigner/models/SignerType.kt index 195a097d..6bc7e6d5 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/models/SignerType.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/models/SignerType.kt @@ -2,6 +2,7 @@ package com.greenart7c3.nostrsigner.models enum class SignerType { CONNECT, + SIGN, SIGN_EVENT, NIP04_ENCRYPT, NIP04_DECRYPT, diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/service/IntentUtils.kt b/app/src/main/java/com/greenart7c3/nostrsigner/service/IntentUtils.kt index 7b2e1bd2..8a0c1cf5 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/service/IntentUtils.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/service/IntentUtils.kt @@ -77,6 +77,7 @@ object IntentUtils { if (parameter == "type") { type = when (parameterData) { + "sign" -> SignerType.SIGN "sign_event" -> SignerType.SIGN_EVENT "get_public_key" -> SignerType.GET_PUBLIC_KEY "nip04_encrypt" -> SignerType.NIP04_ENCRYPT @@ -197,6 +198,28 @@ object IntentUtils { ), ) } + SignerType.SIGN -> { + onReady( + IntentData( + localData, + "", + type, + pubKey, + "", + callbackUrl, + compressionType, + returnType, + listOf(), + "", + mutableStateOf(true), + mutableStateOf(false), + null, + route, + null, + null, + ), + ) + } else -> onReady(null) } } @@ -204,6 +227,7 @@ object IntentUtils { fun getTypeFromBunker(bunkerRequest: BunkerRequest): SignerType { return when (bunkerRequest.method) { + "sign" -> SignerType.SIGN "connect" -> SignerType.CONNECT "sign_event" -> SignerType.SIGN_EVENT "get_public_key" -> SignerType.GET_PUBLIC_KEY @@ -217,6 +241,7 @@ object IntentUtils { fun getDataFromBunker(bunkerRequest: BunkerRequest): String { return when (bunkerRequest.method) { + "sign" -> bunkerRequest.params.first() "connect" -> "ack" "sign_event" -> { val amberEvent = AmberEvent.fromJson(bunkerRequest.params.first()) @@ -425,6 +450,28 @@ object IntentUtils { ), ) } + SignerType.SIGN -> { + onReady( + IntentData( + data, + "", + type, + pubKey, + id, + intent.extras?.getString("callbackUrl"), + CompressionType.NONE, + ReturnType.EVENT, + permissions, + bunkerRequest.currentAccount, + mutableStateOf(true), + mutableStateOf(false), + bunkerRequest, + route, + null, + null, + ), + ) + } } } @@ -437,6 +484,7 @@ object IntentUtils { ) { val type = when (intent.extras?.getString("type")) { + "sign" -> SignerType.SIGN "sign_event" -> SignerType.SIGN_EVENT "nip04_encrypt" -> SignerType.NIP04_ENCRYPT "nip04_decrypt" -> SignerType.NIP04_DECRYPT diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/SignMessage.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/SignMessage.kt index b97a3259..b3139131 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/SignMessage.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/SignMessage.kt @@ -1,4 +1,133 @@ package com.greenart7c3.nostrsigner.ui.components -class SignMessage { +import android.widget.Toast +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Card +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalClipboardManager +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.text.withStyle +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.greenart7c3.nostrsigner.R +import com.greenart7c3.nostrsigner.models.SignerType +import kotlinx.coroutines.launch + +@Composable +fun SignMessage( + content: String, + shouldRunOnAccept: Boolean, + remember: MutableState, + packageName: String?, + applicationName: String?, + appName: String, + type: SignerType, + onAccept: () -> Unit, + onReject: () -> Unit, +) { + var showMore by androidx.compose.runtime.remember { + mutableStateOf(false) + } + val clipboardManager = LocalClipboardManager.current + val coroutineScope = rememberCoroutineScope() + val context = LocalContext.current + + Column( + Modifier + .fillMaxSize() + .padding(8.dp), + ) { + val message = stringResource(R.string.sign_message) + Text( + buildAnnotatedString { + withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) { + append(applicationName ?: appName) + } + append(" requests $message") + }, + fontSize = 18.sp, + ) + Spacer(Modifier.size(4.dp)) + + Card( + modifier = Modifier + .fillMaxWidth(), + ) { + Column(Modifier.padding(6.dp)) { + Text( + "Event content", + fontWeight = FontWeight.Bold, + ) + + Text( + content, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp), + ) + } + } + + RawJsonButton( + onCLick = { + showMore = !showMore + }, + if (!showMore) stringResource(R.string.show_details) else stringResource(R.string.hide_details), + ) + if (showMore) { + RawJson( + content, + "", + Modifier.weight(1f), + stringResource(R.string.content), + type, + ) { + clipboardManager.setText(AnnotatedString(content)) + + coroutineScope.launch { + Toast.makeText( + context, + context.getString(R.string.data_copied_to_the_clipboard), + Toast.LENGTH_SHORT, + ).show() + } + } + } else { + Spacer(modifier = Modifier.weight(1f)) + } + + RememberMyChoice( + shouldRunOnAccept, + remember.value, + packageName, + onAccept, + ) { + remember.value = !remember.value + } + + AcceptRejectButtons( + onAccept, + onReject, + ) + } } diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/SingleEventHomeScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/SingleEventHomeScreen.kt index 4172d10a..00cd16e6 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/SingleEventHomeScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/SingleEventHomeScreen.kt @@ -36,6 +36,7 @@ import com.greenart7c3.nostrsigner.service.AmberUtils import com.greenart7c3.nostrsigner.service.getAppCompatActivity import com.greenart7c3.nostrsigner.service.toShortenHex import com.greenart7c3.nostrsigner.ui.sendResult +import com.vitorpamplona.quartz.crypto.CryptoUtils import com.vitorpamplona.quartz.encoders.bechToBytes import com.vitorpamplona.quartz.encoders.toHexKey import com.vitorpamplona.quartz.encoders.toNpub @@ -190,6 +191,119 @@ fun SingleEventHomeScreen( ) } + SignerType.SIGN -> { + val permission = + applicationEntity?.permissions?.firstOrNull { + it.pkKey == key && it.type == intentData.type.toString() + } + val remember = + remember { + mutableStateOf(permission?.acceptable == true) + } + + val shouldRunOnAccept = permission?.acceptable == true + val localPackageName = + if (intentData.bunkerRequest != null) { + intentData.bunkerRequest.localKey + } else { + packageName + } + SignMessage( + intentData.data, + shouldRunOnAccept, + remember, + localPackageName, + applicationName, + appName, + intentData.type, + { + coroutineScope.launch(Dispatchers.IO) { + val result = CryptoUtils.signString(intentData.data, account.keyPair.privKey!!).toHexKey() + + sendResult( + context, + packageName, + account, + key, + remember.value, + clipboardManager, + result, + result, + intentData, + null, + database, + onLoading, + ) + } + }, + { + coroutineScope.launch(Dispatchers.IO) { + if (key == "null") { + return@launch + } + + if (remember.value) { + AmberUtils.acceptOrRejectPermission( + key, + intentData, + null, + false, + applicationName ?: appName, + account, + database, + ) + } + + val savedApplication = database.applicationDao().getByKey(key) + val relays = intentData.bunkerRequest?.relays ?: listOf() + val application = + savedApplication ?: ApplicationWithPermissions( + application = ApplicationEntity( + key, + appName, + relays, + "", + "", + "", + account.keyPair.pubKey.toHexKey(), + true, + intentData.bunkerRequest?.secret ?: "", + intentData.bunkerRequest?.secret != null, + ), + permissions = mutableListOf(), + ) + + database.applicationDao().insertApplicationWithPermissions(application) + + database.applicationDao().addHistory( + HistoryEntity( + 0, + key, + intentData.type.toString(), + null, + TimeUtils.now(), + false, + ), + ) + } + + if (intentData.bunkerRequest != null) { + AmberUtils.sendBunkerError( + account, + intentData.bunkerRequest, + applicationEntity?.application?.relays?.map { url -> Relay(url) } + ?: emptyList(), + context, + onLoading, + ) + } else { + context.getAppCompatActivity()?.intent = null + context.getAppCompatActivity()?.finish() + } + }, + ) + } + SignerType.NIP04_DECRYPT, SignerType.NIP04_ENCRYPT, SignerType.NIP44_ENCRYPT, SignerType.NIP44_DECRYPT, SignerType.DECRYPT_ZAP_EVENT -> { val permission = applicationEntity?.permissions?.firstOrNull { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 09ff614e..6a0b0dda 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -224,4 +224,7 @@ wants you to sign a %1$s wants you to %1$s Sign %1$s + sign message + content + signature diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 723b658a..1e4254e0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,7 +17,7 @@ lifecycle_version = "2.7.0" material3 = "1.2.1" mockk = "1.13.10" nav_version = "2.7.7" -quartz = "0.86.5" +quartz = "main-SNAPSHOT" compose_ui = "1.6.7" richtextUi = "077a2cde64" roomKtx = "2.6.1"