mirror of
https://github.com/status-im/keycard-connect.git
synced 2025-02-27 10:00:38 +00:00
add eth_sign/personal_data (UI missing)
This commit is contained in:
parent
24abd90bc4
commit
5d97118ae4
@ -7,7 +7,7 @@ import im.status.keycard.android.NFCCardManager
|
||||
import im.status.keycard.connect.card.*
|
||||
import im.status.keycard.connect.data.PINCache
|
||||
import im.status.keycard.connect.data.PairingManager
|
||||
import im.status.keycard.connect.walletconnect.SessionManager
|
||||
import im.status.keycard.connect.walletconnect.WalletConnect
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
object Registry {
|
||||
@ -29,7 +29,7 @@ object Registry {
|
||||
lateinit var nfcAdapter: NfcAdapter
|
||||
private set
|
||||
|
||||
lateinit var walletConnect: SessionManager
|
||||
lateinit var walletConnect: WalletConnect
|
||||
|
||||
fun init(activity: Activity, listener: ScriptListener) {
|
||||
this.mainActivity = activity
|
||||
@ -44,6 +44,6 @@ object Registry {
|
||||
cardManager.setCardListener(scriptExecutor)
|
||||
cardManager.start()
|
||||
|
||||
walletConnect = SessionManager()
|
||||
walletConnect = WalletConnect()
|
||||
}
|
||||
}
|
@ -3,5 +3,3 @@ package im.status.keycard.connect.card
|
||||
fun scriptWithSecureChannel(): List<CardCommand> = listOf(SelectCommand(), InitCommand(), OpenSecureChannelCommand())
|
||||
fun scriptWithAuthentication(): List<CardCommand> = scriptWithSecureChannel().plus(VerifyPINCommand())
|
||||
fun cardCheckupScript(): List<CardCommand> = scriptWithSecureChannel().plus(CheckMasterKeyCommand()).plus(VerifyPINCommand()).plus(LoadKeyCommand())
|
||||
|
||||
fun ByteArray.toHexString() = asUByteArray().joinToString("") { it.toString(16).padStart(2, '0') }
|
||||
|
@ -0,0 +1,7 @@
|
||||
package im.status.keycard.connect.card
|
||||
|
||||
import im.status.keycard.applet.RecoverableSignature
|
||||
|
||||
interface SignListener {
|
||||
fun onResponse(signature: RecoverableSignature?)
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package im.status.keycard.connect.card
|
||||
|
||||
import im.status.keycard.applet.RecoverableSignature
|
||||
import org.bouncycastle.jcajce.provider.digest.Keccak
|
||||
import java.io.IOException
|
||||
import java.lang.Exception
|
||||
|
||||
class SignMessageCommand(private val listener: SignListener, private val message: ByteArray) : CardCommand {
|
||||
override fun run(context: CardScriptExecutor.ScriptContext): CardCommand.Result {
|
||||
try {
|
||||
val keccak256 = Keccak.Digest256()
|
||||
val hash = keccak256.digest(byteArrayOf(0x19) + "Ethereum Signed Message:\n${message.size}".toByteArray() + message)
|
||||
val signature = RecoverableSignature(hash, context.cmdSet.sign(hash).checkOK().data)
|
||||
listener.onResponse(signature)
|
||||
} catch(e: IOException) {
|
||||
return CardCommand.Result.RETRY
|
||||
} catch (e: Exception) {
|
||||
return CardCommand.Result.CANCEL
|
||||
}
|
||||
|
||||
return CardCommand.Result.OK
|
||||
}
|
||||
}
|
@ -3,19 +3,23 @@ package im.status.keycard.connect.walletconnect
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import im.status.keycard.applet.BIP32KeyPair
|
||||
import im.status.keycard.applet.RecoverableSignature
|
||||
import im.status.keycard.connect.Registry
|
||||
import im.status.keycard.connect.card.ExportKeyCommand
|
||||
import im.status.keycard.connect.card.SignListener
|
||||
import im.status.keycard.connect.card.SignMessageCommand
|
||||
import im.status.keycard.connect.card.scriptWithAuthentication
|
||||
import im.status.keycard.connect.card.toHexString
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.OkHttpClient
|
||||
import org.bouncycastle.util.encoders.Hex
|
||||
import org.bouncycastle.util.encoders.Hex.toHexString
|
||||
import org.walletconnect.Session
|
||||
import org.walletconnect.Session.Config.Companion.fromWCUri
|
||||
import org.walletconnect.impls.*
|
||||
import java.io.File
|
||||
|
||||
class SessionManager : ExportKeyCommand.Listener {
|
||||
class WalletConnect : ExportKeyCommand.Listener, SignListener {
|
||||
//TODO: Provide settings for these two
|
||||
private val bip39Path = "m/44'/60'/0'/0"
|
||||
private val chainID: Long = 1
|
||||
@ -25,6 +29,7 @@ class SessionManager : ExportKeyCommand.Listener {
|
||||
private val okHttpClient = OkHttpClient()
|
||||
private val sessionStore = FileWCSessionStore(File(Registry.mainActivity.filesDir, "wcSessions.json").apply { createNewFile() }, moshi)
|
||||
private var session: WCSession? = null
|
||||
private var requestId: Long = 0
|
||||
|
||||
private val sessionCB = object : Session.Callback {
|
||||
override fun onStatus(status: Session.Status) {
|
||||
@ -41,12 +46,43 @@ class SessionManager : ExportKeyCommand.Listener {
|
||||
scope.launch {
|
||||
when (call) {
|
||||
is Session.MethodCall.SessionRequest -> Registry.scriptExecutor.runScript(scriptWithAuthentication().plus(ExportKeyCommand(Registry.walletConnect, bip39Path)))
|
||||
is Session.MethodCall.SignMessage -> session?.rejectRequest(call.id, 1L, "Not implemented yet")
|
||||
is Session.MethodCall.SignMessage -> signText(call.id, call.message)
|
||||
is Session.MethodCall.SendTransaction -> session?.rejectRequest(call.id, 1L, "Not implemented yet")
|
||||
is Session.MethodCall.Custom -> session?.rejectRequest(call.id, 1L, "Not implemented yet")
|
||||
is Session.MethodCall.Custom -> onCustomCall(call)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onCustomCall(call: Session.MethodCall.Custom) {
|
||||
when(call.method) {
|
||||
"personal_sign" -> {
|
||||
val message = call.params?.first()
|
||||
|
||||
if (message is String) {
|
||||
signText(call.id, message)
|
||||
} else {
|
||||
session?.rejectRequest(call.id, 1L, "Invalid params")
|
||||
}
|
||||
}
|
||||
|
||||
"eth_signTypedData" -> {
|
||||
session?.rejectRequest(call.id, 1L, "Not implemented yet")
|
||||
}
|
||||
|
||||
"eth_sendRawTransaction" -> {
|
||||
session?.rejectRequest(call.id, 1L, "Not implemented yet")
|
||||
}
|
||||
|
||||
else -> session?.rejectRequest(call.id, 1L, "Not implemented")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun signText(id: Long, message: String) {
|
||||
requestId = id
|
||||
val msg = Hex.decode(if (message.startsWith("0x", true)) message.drop(2) else message)
|
||||
Registry.scriptExecutor.runScript(scriptWithAuthentication().plus(SignMessageCommand(Registry.walletConnect, msg)))
|
||||
}
|
||||
}
|
||||
|
||||
fun connect(uri: String) {
|
||||
@ -68,8 +104,19 @@ class SessionManager : ExportKeyCommand.Listener {
|
||||
|
||||
override fun onResponse(keyPair: BIP32KeyPair) {
|
||||
scope.launch {
|
||||
val addr = "0x${keyPair.toEthereumAddress().toHexString()}"
|
||||
val addr = "0x${toHexString(keyPair.toEthereumAddress())}"
|
||||
session?.approve(listOf(addr), chainID)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResponse(signature: RecoverableSignature?) {
|
||||
scope.launch {
|
||||
if (signature != null) {
|
||||
session?.approveRequest(requestId, "0x${toHexString(signature.r)}${toHexString(signature.s)}${toHexString(byteArrayOf(signature.recId.toByte()))}")
|
||||
} else {
|
||||
session?.rejectRequest(requestId, -1, "Rejected by user")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user