From 576e16a2fce37af7d3e66359f41161db34dba885 Mon Sep 17 00:00:00 2001 From: Mondei1 Date: Sun, 5 Jul 2020 18:16:37 +0200 Subject: [PATCH] Fix decryption function --- .../github/mondei1/offpass/CreateActivity.kt | 13 ++- .../mondei1/offpass/CryptoOperations.kt | 28 ++--- .../github/mondei1/offpass/MainActivity.kt | 11 +- .../com/github/mondei1/offpass/QRSchema.kt | 104 +++++++++++++++++- .../com/github/mondei1/offpass/TextInput.kt | 2 - .../mondei1/offpass/entities/Compression.kt | 4 +- .../offpass/entities/CompressionHelper.kt | 2 + app/src/main/res/layout/activity_create.xml | 4 +- .../main/res/layout/fragment_text_input.xml | 2 +- app/src/main/res/values/strings.xml | 1 + 10 files changed, 128 insertions(+), 43 deletions(-) diff --git a/app/src/main/java/com/github/mondei1/offpass/CreateActivity.kt b/app/src/main/java/com/github/mondei1/offpass/CreateActivity.kt index 171be4f..40de0bc 100644 --- a/app/src/main/java/com/github/mondei1/offpass/CreateActivity.kt +++ b/app/src/main/java/com/github/mondei1/offpass/CreateActivity.kt @@ -3,8 +3,6 @@ package com.github.mondei1.offpass import android.os.Bundle import android.util.Log import androidx.appcompat.app.AppCompatActivity -import com.github.mondei1.offpass.entities.Compression -import com.github.mondei1.offpass.entities.CompressionHelper import kotlinx.android.synthetic.main.activity_create.* class CreateActivity : AppCompatActivity() { @@ -18,13 +16,16 @@ class CreateActivity : AppCompatActivity() { fragment_title = TextInput.newInstance("Title", "ENTER TITLE", "30dp") fragment_username = TextInput.newInstance("Username", "ENTER USERNAME", "30dp") supportFragmentManager.beginTransaction() - .replace(R.id.title, fragment_title!!) + .replace(R.id.title_box, fragment_title!!) .replace(R.id.username, fragment_username!!) .commit() - this.schema = QRSchema(this) - this.schema!!.decrypted_raw = "%JtuB4O9M42%Gitea|Nicolas|542superGoOD_pW&|klier.nicolas@protonmail.com|\$ul|(\$vb)\$O4|()What's your favorite series%Rick and morty|(2fa)otpauth://totp/OffPass%20Test?secret=d34gfkki5dkd5knifysrpgndd5xb2c7eddwki7ya4pvoisfa5c3ko5pv&issuer=Nicolas%20Klier" - this.schema!!.parse(this) + //this.schema = QRSchema(this) + //this.schema!!.decrypted_raw = "%JtuB4O9M42%Gitea|Nicolas|542superGoOD_pW&|klier.nicolas@protonmail.com|\$ul|(\$vb)\$O4|()What's your favorite series%Rick and morty|(2fa)otpauth://totp/OffPass%20Test?secret=d34gfkki5dkd5knifysrpgndd5xb2c7eddwki7ya4pvoisfa5c3ko5pv&issuer=Nicolas%20Klier" + //this.schema!!.parse(this) + + //this.schema!!.build(arrayOf("website_url", "2fa", "What's your favorite series"), "123") + //this.schema!!.decrypt(this.schema!!.raw, "123") setSupportActionBar(findViewById(R.id.toolbar)) diff --git a/app/src/main/java/com/github/mondei1/offpass/CryptoOperations.kt b/app/src/main/java/com/github/mondei1/offpass/CryptoOperations.kt index 5db7563..aa682b1 100644 --- a/app/src/main/java/com/github/mondei1/offpass/CryptoOperations.kt +++ b/app/src/main/java/com/github/mondei1/offpass/CryptoOperations.kt @@ -7,7 +7,6 @@ import android.util.Log import java.nio.ByteBuffer import java.security.SecureRandom import java.security.spec.KeySpec -import java.util.concurrent.CountDownLatch import javax.crypto.Cipher import javax.crypto.SecretKey import javax.crypto.SecretKeyFactory @@ -57,22 +56,18 @@ class CryptoOperations { } val key: SecretKey = SecretKeySpec(aes_key, "AES") - val cipher: Cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING") + val cipher: Cipher = Cipher.getInstance("AES/GCM/NoPadding") // Performing actual crypto operation - cipher.init(Cipher.ENCRYPT_MODE, key, IvParameterSpec(iv)) + cipher.init(Cipher.ENCRYPT_MODE, key, GCMParameterSpec(128, iv)) Log.i("Crypto", "Encrypt using ${String(aes_key)} '$plain' ${String(iv)}") val ciphered = cipher.doFinal(plain.toByteArray(Charsets.UTF_8)) - // Concat everything into one byte array - val byteBuffer: ByteBuffer = ByteBuffer.allocate(ciphered.size) - byteBuffer.put(ciphered) - - Log.d("Crypto", "Encrypted $plain => ${String(byteBuffer.array())}") - return EncryptionResult(Base64.encodeToString(byteBuffer.array(), Base64.DEFAULT), salt, iv) + Log.d("Crypto", "Encrypted $plain => ${Base64.encodeToString(ciphered, Base64.NO_WRAP)}") + return EncryptionResult(Base64.encodeToString(ciphered, Base64.NO_WRAP), salt, iv) } - fun decrypt(encrypted: String, key: String, iv: ByteArray, salt: ByteArray): String? { + fun decrypt(encrypted: String, key: String, iv: ByteArray, salt: ByteArray): String { val key_bytes: ByteArray = hash(key.toCharArray(), salt).toByteArray(Charsets.UTF_8) val aes_key: ByteArray = ByteArray(32) @@ -84,17 +79,16 @@ class CryptoOperations { aes_key.set(i, key_bytes[i]) } - // Decode Base64 - val raw_encrypted = Base64.decode(encrypted, Base64.DEFAULT) + val encrypted_raw = Base64.decode(encrypted, Base64.NO_WRAP) - Log.i("Crypto", "Decrypt using ${String(aes_key)} '${String(raw_encrypted)}' ${String(iv)}") + Log.i("Crypto", "Decrypt using ${String(aes_key)} '${Base64.encodeToString(encrypted_raw, Base64.NO_WRAP)} (${encrypted_raw.size})' ${String(iv)}") - val key: SecretKey = SecretKeySpec(aes_key, "AES") - val cipher: Cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING") + val keySpec: SecretKey = SecretKeySpec(aes_key, "AES") + val cipher: Cipher = Cipher.getInstance("AES/GCM/NoPadding") // Performing actual crypto operation - cipher.init(Cipher.DECRYPT_MODE, key, IvParameterSpec(iv)) - val ciphered = cipher.doFinal(raw_encrypted) + cipher.init(Cipher.DECRYPT_MODE, keySpec, GCMParameterSpec(128, iv)) + val ciphered = cipher.doFinal(encrypted_raw) // Concat everything into one byte array val byteBuffer: ByteBuffer = ByteBuffer.allocate(ciphered.size) diff --git a/app/src/main/java/com/github/mondei1/offpass/MainActivity.kt b/app/src/main/java/com/github/mondei1/offpass/MainActivity.kt index 21ac6cb..727fc98 100644 --- a/app/src/main/java/com/github/mondei1/offpass/MainActivity.kt +++ b/app/src/main/java/com/github/mondei1/offpass/MainActivity.kt @@ -13,7 +13,6 @@ import com.google.zxing.integration.android.IntentResult import kotlinx.android.synthetic.main.activity_main.* import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.async -import kotlinx.coroutines.runBlocking class MainActivity : AppCompatActivity() { @@ -26,13 +25,10 @@ class MainActivity : AppCompatActivity() { var dbHandler = CompressionHelper(this, null) dbHandler.writableDatabase.execSQL("DELETE FROM `${CompressionHelper.TABLE_NAME}`;") - var dummy_email = Compression("ul", "https://nicolasklier.de:3000", null) - var dummy_custom_key = Compression("vb", "Note", null) - var dummy_custom_value = Compression("O4", "This is a small note I have to keep.", null) + var dummy_email = Compression("ul", "https://nicolasklier.de:3000", null, null) + var dummy_custom_key = Compression("vb", "Note", null, null) + var dummy_custom_value = Compression("O4", "This is a small note I have to keep.", null, null) - //CryptoOperations().hash("secretpassword".toCharArray(), CryptoOperations().nextIV()) - val cryptoThread = HandlerThread("Encryption thread") - cryptoThread.start() val encrypted_result = GlobalScope.async { dbHandler.add("JtuB4O9M42", dummy_email) @@ -40,7 +36,6 @@ class MainActivity : AppCompatActivity() { dbHandler.add("JtuB4O9M42", dummy_custom_value) Log.i("Main", "Encrypted target data to: ${CryptoOperations().encrypt("My cool key", "my secret")}") - } dbHandler.writableDatabase.close() diff --git a/app/src/main/java/com/github/mondei1/offpass/QRSchema.kt b/app/src/main/java/com/github/mondei1/offpass/QRSchema.kt index 19a7c09..7929c21 100644 --- a/app/src/main/java/com/github/mondei1/offpass/QRSchema.kt +++ b/app/src/main/java/com/github/mondei1/offpass/QRSchema.kt @@ -1,12 +1,12 @@ package com.github.mondei1.offpass import android.content.Context +import android.util.Base64 import android.util.Log import com.github.mondei1.offpass.entities.Compression import com.github.mondei1.offpass.entities.CompressionHelper import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.async -import kotlinx.coroutines.channels.broadcast import kotlinx.coroutines.runBlocking import java.lang.Error @@ -71,7 +71,7 @@ class QRSchema { fields[0] = fields[0].replace("%", "") session_key = fields[0].substring(0, 10) // Get first 10 chars, which are the // session key - this.title = fields[0].substring(11, fields[0].length) + this.title = fields[0].substring(10, fields[0].length) } else { this.title = fields[0] } @@ -121,18 +121,110 @@ class QRSchema { + fields[i].toString()) } } - - // Final step is to resolve any compressed values } Log.i("QR-Code schema", "Found: $session_key, $title, $username, $password, " + "$email, $website_url. ${custom.toString()} and ${question_awnser.toString()}") + return true } /** - * This function will take all defined variables and converts them into the QR-Code schema + * This function will take an raw string input and will decrypt it. + * + * @return It returns the raw decrypted raw value. */ - fun build() { + fun decrypt(raw: String, passphrase: String): String { + val parts = raw.split(":") + val iv = parts[1] + val salt = parts[2] + val encrypted_content = parts[3] + Log.i("Decrypt schema", "Give data: $encrypted_content") + + this.decrypted_raw = CryptoOperations().decrypt( + encrypted_content, passphrase, + iv.toByteArray(), salt.toByteArray()) + + return this.decrypted_raw + } + + /** + * This function will take all defined variables and converts them into the QR-Code schema. + * + * @param compress_values Takes an string array where a entry can be `website_url` in case of an URL or + * the key of an optional field. For a security question take the question as key. + */ + fun build(compress_values: Array, passphrase: String): String { + /* First phase is to construct the encrypted data. */ + val session_key = CryptoOperations().nextString(10) // used for + var used_compression: Boolean = false + + var url_copy = website_url + if (compress_values.contains("website_url")) { + used_compression = true + val email_key = CryptoOperations().nextString(4) + this.db?.add(session_key, Compression(email_key, website_url, null, null)) + url_copy = "\$$email_key" + } + var encrypted_content: String = + "${this.title}|${this.username}|${this.password}|${this.email}|${url_copy}|" + + // Loop thought optional/custom fields + for (i in this.custom) { + if (compress_values.contains(i.key)) { + used_compression = true + var key_key = CryptoOperations().nextString(4) + var key_value = CryptoOperations().nextString(4) + + this.db?.add(session_key, + Compression(key_key, i.key, null, null)) + this.db?.add(session_key, + Compression(key_value, i.value, null, null)) + + encrypted_content += "(\$$key_key)\$$key_value|" + } else { + encrypted_content += "(${i.key})${i.value}|" + } + } + + // Loop thought security questions + for (i in this.question_awnser) { + used_compression = true + if (compress_values.contains(i.key)) { + var key_key = CryptoOperations().nextString(4) + var key_value = CryptoOperations().nextString(4) + + this.db?.add(session_key, + Compression(key_key, i.key, null, null)) + this.db?.add(session_key, + Compression(key_value, i.value, null, null)) + + encrypted_content += "()\$$key_key%\$$key_value|" + } else { + encrypted_content += "()${i.key}%${i.value}|" + } + } + encrypted_content = encrypted_content.dropLast(1) + if (used_compression) encrypted_content = "%$session_key%$encrypted_content" + + Log.i("QR-Code Builder", "Constructed encrypted content: $encrypted_content") + + // Now, let's encrypt that + val enc = runBlocking { + CryptoOperations().encrypt(passphrase, encrypted_content) + } + + // TODO: Make schema version dynamic (currently hardcoded) + var final = "op1:" + + "${String(enc.iv)}:" + + "${String(enc.salt)}:" + + enc.result + .replace("\n", "") + + Log.i("QR-Code Builder", "Returning final result: $final") + + this.raw = final + + return final } diff --git a/app/src/main/java/com/github/mondei1/offpass/TextInput.kt b/app/src/main/java/com/github/mondei1/offpass/TextInput.kt index 68863f6..dff8d15 100644 --- a/app/src/main/java/com/github/mondei1/offpass/TextInput.kt +++ b/app/src/main/java/com/github/mondei1/offpass/TextInput.kt @@ -44,8 +44,6 @@ class TextInput : Fragment() { override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) head.text = arg_head - title.background = null - title.hint = arg_title } companion object { diff --git a/app/src/main/java/com/github/mondei1/offpass/entities/Compression.kt b/app/src/main/java/com/github/mondei1/offpass/entities/Compression.kt index ea76b63..0c5bf34 100644 --- a/app/src/main/java/com/github/mondei1/offpass/entities/Compression.kt +++ b/app/src/main/java/com/github/mondei1/offpass/entities/Compression.kt @@ -4,10 +4,12 @@ class Compression { var key: String = "DEFAULT" var value: String? = null var iv: ByteArray? = null + var salt: ByteArray? = null - constructor(key: String, value: String, iv: ByteArray?) { + constructor(key: String, value: String, iv: ByteArray?, salt: ByteArray?) { this.key = key this.value = value this.iv = iv + this.salt = salt } } \ No newline at end of file diff --git a/app/src/main/java/com/github/mondei1/offpass/entities/CompressionHelper.kt b/app/src/main/java/com/github/mondei1/offpass/entities/CompressionHelper.kt index 0e3577b..8820406 100644 --- a/app/src/main/java/com/github/mondei1/offpass/entities/CompressionHelper.kt +++ b/app/src/main/java/com/github/mondei1/offpass/entities/CompressionHelper.kt @@ -4,6 +4,7 @@ import android.content.ContentValues import android.content.Context import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteOpenHelper +import android.util.Base64 import android.util.Log import com.github.mondei1.offpass.CryptoOperations import kotlinx.coroutines.GlobalScope @@ -71,6 +72,7 @@ class CompressionHelper(context: Context, factory: SQLiteDatabase.CursorFactory? } val decrypted_result = GlobalScope.async { + // TODO: Is a list even required? CryptoOperations().decrypt(list[0], session_key, iv, salt) } diff --git a/app/src/main/res/layout/activity_create.xml b/app/src/main/res/layout/activity_create.xml index e15bcdc..4929739 100644 --- a/app/src/main/res/layout/activity_create.xml +++ b/app/src/main/res/layout/activity_create.xml @@ -75,7 +75,7 @@ + app:layout_constraintTop_toBottomOf="@+id/title_box" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_text_input.xml b/app/src/main/res/layout/fragment_text_input.xml index 3541b0e..af3e08d 100644 --- a/app/src/main/res/layout/fragment_text_input.xml +++ b/app/src/main/res/layout/fragment_text_input.xml @@ -17,7 +17,7 @@ android:textSize="20sp" /> OffPass + 1 CreateActivity