add Reinstall command

This commit is contained in:
Michele Balistreri 2020-06-08 12:51:03 +03:00
parent 48c3e70180
commit 714d9be6f7
No known key found for this signature in database
GPG Key ID: E9567DA33A4F791A
11 changed files with 189 additions and 5 deletions

1
README.md Normal file
View File

@ -0,0 +1 @@
## Keycard Connect

View File

@ -29,6 +29,7 @@
<activity android:name=".ui.InitActivity" />
<activity android:name=".ui.PairingActivity" />
<activity android:name=".ui.PINActivity" />
<activity android:name=".ui.ReinstallActivity" />
<activity
android:name=".ui.MainActivity"
android:label="@string/app_name">

View File

@ -8,7 +8,7 @@ import im.status.keycard.io.CardChannel
import im.status.keycard.io.CardListener
class CardScriptExecutor(private val listener: ScriptListener) : CardListener {
data class ScriptContext(val activity: Activity, val cmdSet: KeycardCommandSet)
data class ScriptContext(val activity: Activity, val channel: CardChannel, val cmdSet: KeycardCommandSet)
enum class State {
READY, UX_ONGOING, RUNNING
@ -21,7 +21,7 @@ class CardScriptExecutor(private val listener: ScriptListener) : CardListener {
private var waitingCmd: CardCommand? = null
override fun onConnected(channel: CardChannel) {
val executionContext = ScriptContext(Registry.mainActivity, KeycardCommandSet(channel))
val executionContext = ScriptContext(Registry.mainActivity, channel, KeycardCommandSet(channel))
if (state == State.READY) {
startScript()

View File

@ -0,0 +1,24 @@
package im.status.keycard.connect.card
import android.net.Uri
import im.status.keycard.connect.Registry
import im.status.keycard.globalplatform.GlobalPlatformCommandSet
class ReinstallCommand(private val applet: Uri, private val installWallet: Boolean, private val installCash: Boolean, private val installNDEF: Boolean, private val cashParams: ByteArray = ByteArray(0), private val ndefParams: ByteArray = ByteArray(0)) : CardCommand {
override fun run(context: CardScriptExecutor.ScriptContext): CardCommand.Result {
return runOnCard {
val gpCmd = GlobalPlatformCommandSet(context.channel)
gpCmd.select().checkOK()
gpCmd.openSecureChannel()
gpCmd.deleteKeycardInstancesAndPackage()
Registry.mainActivity.applicationContext.contentResolver.openInputStream(applet)?.use {
gpCmd.loadKeycardPackage(it) {_, _ -> }
}
if (installWallet) { gpCmd.installKeycardApplet().checkOK() }
if (installCash) { gpCmd.installCashApplet(cashParams).checkOK() }
if (installNDEF) { gpCmd.installNDEFApplet(ndefParams).checkOK() }
}
}
}

View File

@ -30,6 +30,7 @@ const val MNEMONIC_PHRASE = "mnemonicPhrase"
const val REQ_INTERACTIVE_SCRIPT = 0x01
const val REQ_WALLETCONNECT = 0x02
const val REQ_LOADKEY = 0x03
const val REQ_APPLET_FILE = 0x04
const val CACHE_VALIDITY = 15 * 60 * 1000

View File

@ -11,7 +11,7 @@ import im.status.keycard.connect.card.scriptWithAuthentication
class ChangePairingPasswordActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
//TODO: puk validation and confirmation
//TODO: pairing password confirmation
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_change_pairing_password)
}

View File

@ -106,6 +106,10 @@ class MainActivity : AppCompatActivity(), ScriptListener {
Registry.scriptExecutor.runScript(scriptWithAuthentication().plus(RemoveKeyCommand()))
}
fun reinstall(@Suppress("UNUSED_PARAMETER") view: View) {
startCommand(ReinstallActivity::class)
}
fun settings(@Suppress("UNUSED_PARAMETER") view: View) {
startCommand(SettingsActivity::class)
}

View File

@ -0,0 +1,47 @@
package im.status.keycard.connect.ui
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.CheckBox
import im.status.keycard.connect.R
import im.status.keycard.connect.Registry
import im.status.keycard.connect.card.ReinstallCommand
import im.status.keycard.connect.data.REQ_APPLET_FILE
class ReinstallActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_reinstall)
}
fun reinstall(@Suppress("UNUSED_PARAMETER") view: View) {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
}
startActivityForResult(intent, REQ_APPLET_FILE)
}
fun cancel(@Suppress("UNUSED_PARAMETER") view: View) {
finish()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQ_APPLET_FILE && resultCode == RESULT_OK) {
val reinstallWallet = findViewById<CheckBox>(R.id.reinstallWalletCheckbox).isChecked
val reinstallCash = findViewById<CheckBox>(R.id.reinstallCashCheckbox).isChecked
val reinstallNDEF = findViewById<CheckBox>(R.id.reinstallNDEFCheckbox).isChecked
data?.data?.also { uri ->
val script = listOf(ReinstallCommand(uri, reinstallWallet, reinstallCash, reinstallNDEF))
Registry.scriptExecutor.runScript(script)
}
}
finish()
}
}

View File

@ -23,7 +23,6 @@
android:id="@+id/walletConnectButton"
android:layout_width="236dp"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:onClick="connectWallet"
android:text="@string/connect_wallet"
app:layout_constraintEnd_toEndOf="parent"
@ -96,6 +95,18 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/changeKeyButton" />
<Button
android:id="@+id/reinstall"
android:layout_width="236dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:onClick="reinstall"
android:text="@string/reinstall"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.497"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/removeKey" />
<Button
android:id="@+id/settingsButton"
android:layout_width="236dp"
@ -105,5 +116,5 @@
android:text="@string/settings"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/removeKey" />
app:layout_constraintTop_toBottomOf="@+id/reinstall" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.ReinstallActivity">
<TextView
android:id="@+id/reinstallPrompt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="52dp"
android:text="@string/reinstall_applet_prompt"
android:textAppearance="@style/TextAppearance.AppCompat.Display1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.497"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/reinstallWarning"
android:layout_width="327dp"
android:layout_height="127dp"
android:layout_marginTop="24dp"
android:text="@string/reinstall_warning"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/reinstallPrompt" />
<CheckBox
android:id="@+id/reinstallWalletCheckbox"
android:layout_width="324dp"
android:layout_height="25dp"
android:layout_marginTop="16dp"
android:checked="true"
android:text="@string/reinstall_wallet_checkbox"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/reinstallWarning" />
<CheckBox
android:id="@+id/reinstallCashCheckbox"
android:layout_width="324dp"
android:layout_height="25dp"
android:layout_marginTop="16dp"
android:checked="true"
android:text="@string/reinstall_cash_checkbox"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.494"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/reinstallWalletCheckbox" />
<CheckBox
android:id="@+id/reinstallNDEFCheckbox"
android:layout_width="324dp"
android:layout_height="25dp"
android:layout_marginTop="16dp"
android:checked="true"
android:text="@string/reinstall_ndef_checkbox"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.494"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/reinstallCashCheckbox" />
<Button
android:id="@+id/cancelButton"
android:layout_width="324dp"
android:layout_height="49dp"
android:layout_marginTop="16dp"
android:onClick="cancel"
android:text="@android:string/cancel"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/reinstallNDEFCheckbox" />
<Button
android:id="@+id/reinstallButton"
android:layout_width="324dp"
android:layout_height="49dp"
android:layout_marginTop="16dp"
android:onClick="reinstall"
android:text="@string/reinstall_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/cancelButton" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -37,4 +37,11 @@
<string name="wallet_path_label">Wallet Path</string>
<string name="wallet_path_hint">m/44\'/...</string>
<string name="settings">Settings</string>
<string name="reinstall">(Re)install applet</string>
<string name="reinstall_applet_prompt">Reinstall applet</string>
<string name="reinstall_warning">WARNING: Reinstalling the applet completely destroys the currently loaded keys and resets all credentials. Make sure you have your seed phrase somewhere to restore from. Additionally, JCOP3 cards can be destroyed if the connection is lost during this process. Hold the card tightly to the phone!</string>
<string name="reinstall_wallet_checkbox">Install Wallet applet</string>
<string name="reinstall_cash_checkbox">Install Cash applet</string>
<string name="reinstall_ndef_checkbox">Install NDEF applet</string>
<string name="reinstall_button">Select cap file and install</string>
</resources>