Fix decryption function
This commit is contained in:
@@ -3,8 +3,6 @@ package com.github.mondei1.offpass
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
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.*
|
import kotlinx.android.synthetic.main.activity_create.*
|
||||||
|
|
||||||
class CreateActivity : AppCompatActivity() {
|
class CreateActivity : AppCompatActivity() {
|
||||||
@@ -18,13 +16,16 @@ class CreateActivity : AppCompatActivity() {
|
|||||||
fragment_title = TextInput.newInstance("Title", "ENTER TITLE", "30dp")
|
fragment_title = TextInput.newInstance("Title", "ENTER TITLE", "30dp")
|
||||||
fragment_username = TextInput.newInstance("Username", "ENTER USERNAME", "30dp")
|
fragment_username = TextInput.newInstance("Username", "ENTER USERNAME", "30dp")
|
||||||
supportFragmentManager.beginTransaction()
|
supportFragmentManager.beginTransaction()
|
||||||
.replace(R.id.title, fragment_title!!)
|
.replace(R.id.title_box, fragment_title!!)
|
||||||
.replace(R.id.username, fragment_username!!)
|
.replace(R.id.username, fragment_username!!)
|
||||||
.commit()
|
.commit()
|
||||||
|
|
||||||
this.schema = QRSchema(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!!.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!!.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))
|
setSupportActionBar(findViewById(R.id.toolbar))
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import android.util.Log
|
|||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.security.spec.KeySpec
|
import java.security.spec.KeySpec
|
||||||
import java.util.concurrent.CountDownLatch
|
|
||||||
import javax.crypto.Cipher
|
import javax.crypto.Cipher
|
||||||
import javax.crypto.SecretKey
|
import javax.crypto.SecretKey
|
||||||
import javax.crypto.SecretKeyFactory
|
import javax.crypto.SecretKeyFactory
|
||||||
@@ -57,22 +56,18 @@ class CryptoOperations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val key: SecretKey = SecretKeySpec(aes_key, "AES")
|
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
|
// 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)}")
|
Log.i("Crypto", "Encrypt using ${String(aes_key)} '$plain' ${String(iv)}")
|
||||||
val ciphered = cipher.doFinal(plain.toByteArray(Charsets.UTF_8))
|
val ciphered = cipher.doFinal(plain.toByteArray(Charsets.UTF_8))
|
||||||
|
|
||||||
// Concat everything into one byte array
|
Log.d("Crypto", "Encrypted $plain => ${Base64.encodeToString(ciphered, Base64.NO_WRAP)}")
|
||||||
val byteBuffer: ByteBuffer = ByteBuffer.allocate(ciphered.size)
|
return EncryptionResult(Base64.encodeToString(ciphered, Base64.NO_WRAP), salt, iv)
|
||||||
byteBuffer.put(ciphered)
|
|
||||||
|
|
||||||
Log.d("Crypto", "Encrypted $plain => ${String(byteBuffer.array())}")
|
|
||||||
return EncryptionResult(Base64.encodeToString(byteBuffer.array(), Base64.DEFAULT), 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 key_bytes: ByteArray = hash(key.toCharArray(), salt).toByteArray(Charsets.UTF_8)
|
||||||
val aes_key: ByteArray = ByteArray(32)
|
val aes_key: ByteArray = ByteArray(32)
|
||||||
|
|
||||||
@@ -84,17 +79,16 @@ class CryptoOperations {
|
|||||||
aes_key.set(i, key_bytes[i])
|
aes_key.set(i, key_bytes[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode Base64
|
val encrypted_raw = Base64.decode(encrypted, Base64.NO_WRAP)
|
||||||
val raw_encrypted = Base64.decode(encrypted, Base64.DEFAULT)
|
|
||||||
|
|
||||||
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 keySpec: 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
|
// Performing actual crypto operation
|
||||||
cipher.init(Cipher.DECRYPT_MODE, key, IvParameterSpec(iv))
|
cipher.init(Cipher.DECRYPT_MODE, keySpec, GCMParameterSpec(128, iv))
|
||||||
val ciphered = cipher.doFinal(raw_encrypted)
|
val ciphered = cipher.doFinal(encrypted_raw)
|
||||||
|
|
||||||
// Concat everything into one byte array
|
// Concat everything into one byte array
|
||||||
val byteBuffer: ByteBuffer = ByteBuffer.allocate(ciphered.size)
|
val byteBuffer: ByteBuffer = ByteBuffer.allocate(ciphered.size)
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import com.google.zxing.integration.android.IntentResult
|
|||||||
import kotlinx.android.synthetic.main.activity_main.*
|
import kotlinx.android.synthetic.main.activity_main.*
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
@@ -26,13 +25,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
var dbHandler = CompressionHelper(this, null)
|
var dbHandler = CompressionHelper(this, null)
|
||||||
dbHandler.writableDatabase.execSQL("DELETE FROM `${CompressionHelper.TABLE_NAME}`;")
|
dbHandler.writableDatabase.execSQL("DELETE FROM `${CompressionHelper.TABLE_NAME}`;")
|
||||||
|
|
||||||
var dummy_email = Compression("ul", "https://nicolasklier.de:3000", null)
|
var dummy_email = Compression("ul", "https://nicolasklier.de:3000", null, null)
|
||||||
var dummy_custom_key = Compression("vb", "Note", 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)
|
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 {
|
val encrypted_result = GlobalScope.async {
|
||||||
dbHandler.add("JtuB4O9M42", dummy_email)
|
dbHandler.add("JtuB4O9M42", dummy_email)
|
||||||
@@ -40,7 +36,6 @@ class MainActivity : AppCompatActivity() {
|
|||||||
dbHandler.add("JtuB4O9M42", dummy_custom_value)
|
dbHandler.add("JtuB4O9M42", dummy_custom_value)
|
||||||
|
|
||||||
Log.i("Main", "Encrypted target data to: ${CryptoOperations().encrypt("My cool key", "my secret")}")
|
Log.i("Main", "Encrypted target data to: ${CryptoOperations().encrypt("My cool key", "my secret")}")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dbHandler.writableDatabase.close()
|
dbHandler.writableDatabase.close()
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
package com.github.mondei1.offpass
|
package com.github.mondei1.offpass
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.util.Base64
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.github.mondei1.offpass.entities.Compression
|
import com.github.mondei1.offpass.entities.Compression
|
||||||
import com.github.mondei1.offpass.entities.CompressionHelper
|
import com.github.mondei1.offpass.entities.CompressionHelper
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.channels.broadcast
|
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import java.lang.Error
|
import java.lang.Error
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ class QRSchema {
|
|||||||
fields[0] = fields[0].replace("%", "")
|
fields[0] = fields[0].replace("%", "")
|
||||||
session_key = fields[0].substring(0, 10) // Get first 10 chars, which are the
|
session_key = fields[0].substring(0, 10) // Get first 10 chars, which are the
|
||||||
// session key
|
// session key
|
||||||
this.title = fields[0].substring(11, fields[0].length)
|
this.title = fields[0].substring(10, fields[0].length)
|
||||||
} else {
|
} else {
|
||||||
this.title = fields[0]
|
this.title = fields[0]
|
||||||
}
|
}
|
||||||
@@ -121,18 +121,110 @@ class QRSchema {
|
|||||||
+ fields[i].toString())
|
+ fields[i].toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Final step is to resolve any compressed values
|
|
||||||
}
|
}
|
||||||
Log.i("QR-Code schema", "Found: $session_key, $title, $username, $password, " +
|
Log.i("QR-Code schema", "Found: $session_key, $title, $username, $password, " +
|
||||||
"$email, $website_url. ${custom.toString()} and ${question_awnser.toString()}")
|
"$email, $website_url. ${custom.toString()} and ${question_awnser.toString()}")
|
||||||
|
|
||||||
return true
|
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<String>, 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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,8 +44,6 @@ class TextInput : Fragment() {
|
|||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
head.text = arg_head
|
head.text = arg_head
|
||||||
title.background = null
|
|
||||||
title.hint = arg_title
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ class Compression {
|
|||||||
var key: String = "DEFAULT"
|
var key: String = "DEFAULT"
|
||||||
var value: String? = null
|
var value: String? = null
|
||||||
var iv: ByteArray? = 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.key = key
|
||||||
this.value = value
|
this.value = value
|
||||||
this.iv = iv
|
this.iv = iv
|
||||||
|
this.salt = salt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ import android.content.ContentValues
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.sqlite.SQLiteDatabase
|
import android.database.sqlite.SQLiteDatabase
|
||||||
import android.database.sqlite.SQLiteOpenHelper
|
import android.database.sqlite.SQLiteOpenHelper
|
||||||
|
import android.util.Base64
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.github.mondei1.offpass.CryptoOperations
|
import com.github.mondei1.offpass.CryptoOperations
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
@@ -71,6 +72,7 @@ class CompressionHelper(context: Context, factory: SQLiteDatabase.CursorFactory?
|
|||||||
}
|
}
|
||||||
|
|
||||||
val decrypted_result = GlobalScope.async {
|
val decrypted_result = GlobalScope.async {
|
||||||
|
// TODO: Is a list even required?
|
||||||
CryptoOperations().decrypt(list[0], session_key, iv, salt)
|
CryptoOperations().decrypt(list[0], session_key, iv, salt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@
|
|||||||
</androidx.appcompat.widget.Toolbar>
|
</androidx.appcompat.widget.Toolbar>
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/title"
|
android:id="@+id/title_box"
|
||||||
android:name="com.github.mondei1.offpass.TextInput"
|
android:name="com.github.mondei1.offpass.TextInput"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -92,6 +92,6 @@
|
|||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginTop="10dp"
|
android:layout_marginTop="10dp"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/title" />
|
app:layout_constraintTop_toBottomOf="@+id/title_box" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
android:textSize="20sp" />
|
android:textSize="20sp" />
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/title"
|
android:id="@+id/title_box"
|
||||||
style="@style/Widget.AppCompat.EditText"
|
style="@style/Widget.AppCompat.EditText"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">OffPass</string>
|
<string name="app_name">OffPass</string>
|
||||||
|
<string name="qr_schema_support">1</string>
|
||||||
<string name="title_activity_create">CreateActivity</string>
|
<string name="title_activity_create">CreateActivity</string>
|
||||||
<!--
|
<!--
|
||||||
This string is used for square devices and overridden by hello_world in
|
This string is used for square devices and overridden by hello_world in
|
||||||
|
|||||||
Reference in New Issue
Block a user