add eth_sign/personal_data (UI missing)

This commit is contained in:
Michele Balistreri 2019-11-06 14:01:29 +03:00
parent 24abd90bc4
commit 5d97118ae4
No known key found for this signature in database
GPG Key ID: E9567DA33A4F791A
5 changed files with 85 additions and 10 deletions

View File

@ -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()
}
}

View File

@ -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') }

View File

@ -0,0 +1,7 @@
package im.status.keycard.connect.card
import im.status.keycard.applet.RecoverableSignature
interface SignListener {
fun onResponse(signature: RecoverableSignature?)
}

View File

@ -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
}
}

View File

@ -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")
}
}
}
}