Skip to content

Commit

Permalink
Encrypt SSH password & key
Browse files Browse the repository at this point in the history
  • Loading branch information
oakkitten committed Feb 18, 2024
1 parent b8dfb5e commit ba5f9c1
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 12 deletions.
Expand Up @@ -81,7 +81,7 @@ class PrivateKeyPickerPreference(context: Context?, attrs: AttributeSet?) :
successMessage = context.getString(R.string.pref__PrivateKeyPickerPreference__success_key_forgotten)
}

sharedPreferences!!.edit { putString(Constants.PREF_SSH_KEY_FILE, valueToStore) }
sharedPreferences!!.edit { putString(key, valueToStore) }
notifyChanged()

return successMessage
Expand Down
Expand Up @@ -37,9 +37,9 @@ public class Constants {
final static public String PREF_SSH_AUTHENTICATION_METHOD_PASSWORD = "password";
final static public String PREF_SSH_AUTHENTICATION_METHOD_KEY = "key";
final public static String PREF_SSH_AUTHENTICATION_METHOD_D = PREF_SSH_AUTHENTICATION_METHOD_PASSWORD;
final static public String PREF_SSH_PASSWORD = "ssh_password";
final static public String PREF_SSH_PASSWORD = "encrypted_ssh_password";
final public static String PREF_SSH_PASSWORD_D = "";
final static public String PREF_SSH_KEY_FILE = "ssh_key_file";
final static public String PREF_SSH_KEY_FILE = "encrypted_ssh_key";
final public static String PREF_SSH_KEY_FILE_D = null;
final static public String PREF_SSH_SERVER_KEY_VERIFIER = "ssh_server_key_verifier";
final public static String PREF_SSH_SERVER_KEY_VERIFIER_D = "";
Expand Down Expand Up @@ -261,5 +261,8 @@ static public class Deprecated {

final static public String PREF_PASSWORD = "password";
final public static String PREF_UPLOAD_AUTHENTICATION_BASIC_PASSWORD = "upload_authentication_basic_password";
final static public String PREF_SSH_PASSWORD = "ssh_password";
final static public String PREF_SSH_KEY_FILE = "ssh_key_file";
final public static String PREF_SSH_KEY_FILE_D = null;
}
}
Expand Up @@ -76,17 +76,17 @@ class MigratePreferences(private val preferences: SharedPreferences) {

preferences.edit()
.putString(Constants.PREF_SSH_AUTHENTICATION_METHOD, authenticationMethod)
.putString(Constants.PREF_SSH_PASSWORD, sshPassword)
.putString(Constants.PREF_SSH_KEY_FILE, sshKeyFile)
.putString(Constants.Deprecated.PREF_SSH_PASSWORD, sshPassword)
.putString(Constants.Deprecated.PREF_SSH_KEY_FILE, sshKeyFile)
.putString(Constants.Deprecated.PREF_SSH_KEY_PASSPHRASE, sshPassphrase)
.remove(Constants.Deprecated.PREF_SSH_PASS)
.remove(Constants.Deprecated.PREF_SSH_KEY)
.apply()
}

add(1, 2) {
val sshKeyFile = preferences.getString(Constants.PREF_SSH_KEY_FILE,
Constants.PREF_SSH_KEY_FILE_D) ?: return@add
val sshKeyFile = preferences.getString(Constants.Deprecated.PREF_SSH_KEY_FILE,
Constants.Deprecated.PREF_SSH_KEY_FILE_D) ?: return@add

val sshPassphrase = preferences.getString(Constants.Deprecated.PREF_SSH_KEY_PASSPHRASE,
Constants.Deprecated.PREF_SSH_KEY_PASSPHRASE_D)
Expand All @@ -97,7 +97,7 @@ class MigratePreferences(private val preferences: SharedPreferences) {
val keyPair = SSHConnection.makeKeyPair(sshKeyFileBytes, sshPassphrase)
AndroidKeyStoreUtils.putKeyPairIntoAndroidKeyStore(keyPair, SSHConnection.KEYSTORE_ALIAS)
preferences.edit()
.putString(Constants.PREF_SSH_KEY_FILE, PrivateKeyPickerPreference.STORED_IN_KEYSTORE)
.putString(Constants.Deprecated.PREF_SSH_KEY_FILE, PrivateKeyPickerPreference.STORED_IN_KEYSTORE)
.putString(Constants.Deprecated.PREF_SSH_KEY_PASSPHRASE, null)
.apply()
val message = mapOf(
Expand All @@ -117,8 +117,8 @@ class MigratePreferences(private val preferences: SharedPreferences) {
}

add(2, 3) {
val sshKeyFile = preferences.getString(Constants.PREF_SSH_KEY_FILE,
Constants.PREF_SSH_KEY_FILE_D) ?: return@add
val sshKeyFile = preferences.getString(Constants.Deprecated.PREF_SSH_KEY_FILE,
Constants.Deprecated.PREF_SSH_KEY_FILE_D) ?: return@add

val sshPassphrase = preferences.getString(Constants.Deprecated.PREF_SSH_KEY_PASSPHRASE,
Constants.Deprecated.PREF_SSH_KEY_PASSPHRASE_D)
Expand All @@ -128,12 +128,12 @@ class MigratePreferences(private val preferences: SharedPreferences) {
val sshKeyFileBytes = PrivateKeyPickerPreference.getData(sshKeyFile)
val keyPair = SSHConnection.makeKeyPair(sshKeyFileBytes, sshPassphrase)
preferences.edit()
.putString(Constants.PREF_SSH_KEY_FILE, Utils.serialize(keyPair))
.putString(Constants.Deprecated.PREF_SSH_KEY_FILE, Utils.serialize(keyPair))
.apply()
} catch (e: Exception) {
showError("Failed to migrate SSH key: ", e)
preferences.edit()
.putString(Constants.PREF_SSH_KEY_FILE, Constants.PREF_SSH_KEY_FILE_D)
.putString(Constants.Deprecated.PREF_SSH_KEY_FILE, Constants.Deprecated.PREF_SSH_KEY_FILE_D)
.apply()
}
}
Expand Down Expand Up @@ -196,6 +196,8 @@ class MigratePreferences(private val preferences: SharedPreferences) {

move(Constants.Deprecated.PREF_PASSWORD, Constants.PREF_PASSWORD)
move(Constants.Deprecated.PREF_UPLOAD_AUTHENTICATION_BASIC_PASSWORD, Constants.PREF_UPLOAD_AUTHENTICATION_BASIC_PASSWORD)
move(Constants.Deprecated.PREF_SSH_PASSWORD, Constants.PREF_SSH_PASSWORD)
move(Constants.Deprecated.PREF_SSH_KEY_FILE, Constants.PREF_SSH_KEY_FILE)
}
}
}
Expand Down
@@ -1,8 +1,12 @@
package com.ubergeek42.WeechatAndroid.utils

import android.content.SharedPreferences
import android.util.Base64
import androidx.core.content.edit
import androidx.preference.PrivateKeyPickerPreference
import androidx.test.core.app.ApplicationProvider
import com.github.ivanshafran.sharedpreferencesmock.SPMockBuilder
import com.ubergeek42.weechat.relay.connection.SSHConnection
import org.junit.Assert
import org.junit.Before
import org.junit.Test
Expand Down Expand Up @@ -49,4 +53,36 @@ internal class MigratePreferencesTest {
val value = encryptedSharedPreferences.getString(Constants.PREF_UPLOAD_AUTHENTICATION_BASIC_PASSWORD, "missing")
Assert.assertEquals("foo", value)
}

@Test
fun `Test SSH key migration`() {
applicationContext = ApplicationProvider.getApplicationContext()

@Suppress("SpellCheckingInspection")
val base64EncodedPemWithOpenSsh25519Key = """
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACBlA9NtYa1wnZT0e3rPepp3Ea8GXbqDy3dJPHfByfBwygAAAJhT15i+U9eY
vgAAAAtzc2gtZWQyNTUxOQAAACBlA9NtYa1wnZT0e3rPepp3Ea8GXbqDy3dJPHfByfBwyg
AAAEAl/41KTVxCz7P25x0RcXqmwAWiNHQNJiMEpUMaYzdz2WUD021hrXCdlPR7es96mncR
rwZduoPLd0k8d8HJ8HDKAAAAEXNAREVTS1RPUC01TUlIVlFJAQIDBA==
-----END OPENSSH PRIVATE KEY-----
"""
.trimIndent()
.toByteArray()
.let { Base64.encodeToString(it, Base64.NO_WRAP) }

defaultSharedPreferences.edit {
putString(Constants.Deprecated.PREF_SSH_PASS, "needs to be present for migration")
putString(Constants.Deprecated.PREF_SSH_KEY, base64EncodedPemWithOpenSsh25519Key)
}

MigratePreferences(multiSharedPreferences).migrate()

val prefSshKey = encryptedSharedPreferences.getString(Constants.PREF_SSH_KEY_FILE, "missing")
val keyBytes = PrivateKeyPickerPreference.getData(prefSshKey)
val keyPair = SSHConnection.deserializeKeyPair(keyBytes)

Assert.assertEquals("EdDSA", keyPair.public.algorithm)
}
}

0 comments on commit ba5f9c1

Please sign in to comment.