wip: external signer
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
package su.reya.coop
|
||||
|
||||
import rust.nostr.sdk.PublicKey
|
||||
import rust.nostr.sdk.UnsignedEvent
|
||||
|
||||
/**
|
||||
* Platform interface for NIP-55 external signer communication.
|
||||
* Implemented on Android; no-op/null on other platforms.
|
||||
*/
|
||||
interface ExternalSignerHandler {
|
||||
fun isAvailable(): Boolean
|
||||
|
||||
suspend fun getPublicKey(permissions: String? = null): ExternalSignerResult?
|
||||
suspend fun signEvent(event: UnsignedEvent, currentUser: PublicKey): String?
|
||||
suspend fun nip04Encrypt(plaintext: String, pubkey: PublicKey): String?
|
||||
suspend fun nip04Decrypt(ciphertext: String, pubkey: PublicKey): String?
|
||||
suspend fun nip44Encrypt(plaintext: String, pubkey: PublicKey, currentUser: PublicKey): String?
|
||||
suspend fun nip44Decrypt(ciphertext: String, pubkey: PublicKey, currentUser: PublicKey): String?
|
||||
}
|
||||
|
||||
data class ExternalSignerResult(
|
||||
val pubkey: String,
|
||||
val packageName: String,
|
||||
)
|
||||
@@ -0,0 +1,41 @@
|
||||
package su.reya.coop
|
||||
|
||||
import rust.nostr.sdk.AsyncNostrSigner
|
||||
import rust.nostr.sdk.Event
|
||||
import rust.nostr.sdk.PublicKey
|
||||
import rust.nostr.sdk.UnsignedEvent
|
||||
|
||||
class ExternalSignerProxy(
|
||||
private val handler: ExternalSignerHandler,
|
||||
private val packageName: String,
|
||||
private val currentUser: PublicKey,
|
||||
) : AsyncNostrSigner {
|
||||
override suspend fun getPublicKeyAsync(): PublicKey {
|
||||
return currentUser
|
||||
}
|
||||
|
||||
override suspend fun signEventAsync(unsignedEvent: UnsignedEvent): Event? {
|
||||
val signedJson = handler.signEvent(unsignedEvent, currentUser) ?: return null
|
||||
return Event.fromJson(signedJson)
|
||||
}
|
||||
|
||||
override suspend fun nip04EncryptAsync(publicKey: PublicKey, content: String): String {
|
||||
return handler.nip04Encrypt(content, publicKey)
|
||||
?: throw Exception("NIP-04 encrypt rejected")
|
||||
}
|
||||
|
||||
override suspend fun nip04DecryptAsync(publicKey: PublicKey, encryptedContent: String): String {
|
||||
return handler.nip04Decrypt(encryptedContent, publicKey)
|
||||
?: throw Exception("NIP-04 decrypt rejected")
|
||||
}
|
||||
|
||||
override suspend fun nip44EncryptAsync(publicKey: PublicKey, content: String): String {
|
||||
return handler.nip44Encrypt(content, publicKey, currentUser)
|
||||
?: throw Exception("NIP-44 encrypt rejected")
|
||||
}
|
||||
|
||||
override suspend fun nip44DecryptAsync(publicKey: PublicKey, payload: String): String {
|
||||
return handler.nip44Decrypt(payload, publicKey, currentUser)
|
||||
?: throw Exception("NIP-44 decrypt rejected")
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,8 @@ import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
class NostrViewModel(
|
||||
private val nostr: Nostr,
|
||||
private val secretStore: SecretStorage
|
||||
private val secretStore: SecretStorage,
|
||||
private val externalSignerHandler: ExternalSignerHandler? = null,
|
||||
) : ViewModel() {
|
||||
private val _isNotificationBannerDismissed = MutableStateFlow(false)
|
||||
val isNotificationBannerDismissed = _isNotificationBannerDismissed.asStateFlow()
|
||||
@@ -374,6 +375,7 @@ class NostrViewModel(
|
||||
private suspend fun createSigner(secret: String): AsyncNostrSigner {
|
||||
return when {
|
||||
secret.startsWith("nsec1") -> Keys.parse(secret)
|
||||
|
||||
secret.startsWith("bunker://") -> {
|
||||
val appKeys = getOrInitAppKeys()
|
||||
val bunker = NostrConnectUri.parse(secret)
|
||||
@@ -381,6 +383,18 @@ class NostrViewModel(
|
||||
NostrConnect(uri = bunker, appKeys, timeout, null)
|
||||
}
|
||||
|
||||
secret.startsWith("nip55://") -> {
|
||||
val handler = externalSignerHandler
|
||||
?: throw IllegalStateException("External signer not available on this platform")
|
||||
|
||||
// Format: nip55://packageName/hexPubkey
|
||||
val parts = secret.removePrefix("nip55://").split("/", limit = 2)
|
||||
val packageName = parts[0]
|
||||
val pubkey = PublicKey.parse(parts[1])
|
||||
|
||||
ExternalSignerProxy(handler, packageName, pubkey)
|
||||
}
|
||||
|
||||
else -> throw IllegalArgumentException("Invalid secret format")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user