Passphrase & hint can now be set.

- Built-in password list
This commit is contained in:
2020-07-12 00:30:57 +02:00
parent 8865e9a394
commit 1bb1b55452
7 changed files with 183220 additions and 40 deletions

View File

@@ -1,10 +1,7 @@
package com.github.mondei1.offpass package com.github.mondei1.offpass
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.ClipData import android.content.*
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.Color import android.graphics.Color
@@ -15,11 +12,14 @@ import android.print.PrintJob
import android.print.PrintManager import android.print.PrintManager
import android.util.Base64 import android.util.Base64
import android.util.Log import android.util.Log
import android.view.ContextThemeWrapper
import android.view.View import android.view.View
import android.view.WindowManager
import android.webkit.WebResourceRequest import android.webkit.WebResourceRequest
import android.webkit.WebView import android.webkit.WebView
import android.webkit.WebViewClient import android.webkit.WebViewClient
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.zxing.BarcodeFormat import com.google.zxing.BarcodeFormat
import com.google.zxing.integration.android.IntentIntegrator import com.google.zxing.integration.android.IntentIntegrator
@@ -27,6 +27,8 @@ import com.google.zxing.integration.android.IntentResult
import com.journeyapps.barcodescanner.BarcodeEncoder import com.journeyapps.barcodescanner.BarcodeEncoder
import dev.turingcomplete.kotlinonetimepassword.GoogleAuthenticator import dev.turingcomplete.kotlinonetimepassword.GoogleAuthenticator
import kotlinx.android.synthetic.main.activity_create.* import kotlinx.android.synthetic.main.activity_create.*
import kotlinx.android.synthetic.main.dialogpassphrase.*
import kotlinx.android.synthetic.main.dialogpassphrase.view.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.util.* import java.util.*
@@ -39,7 +41,31 @@ class CreateActivity : AppCompatActivity() {
private var mWebView: WebView? = null private var mWebView: WebView? = null
private var printJob: PrintJob? = null private var printJob: PrintJob? = null
fun doPrint() { private var fa_uri: Uri? = null
/**
* Gets triggered when the user clicks on printing button (top left) and renders the paper to
* print.
*/
fun doPrint(passphrase: String, hint: String) {
// Set up printing
this.schema = QRSchema(this)
this.schema!!.title = title_input.text.toString()
this.schema!!.username = username_input.text.toString()
this.schema!!.password = password_input.text.toString()
this.schema!!.email = email_input.text.toString()
this.schema!!.website_url = url_input.text.toString()
if (fa_uri != null) {
this.schema!!.custom.put("2fa", fa_uri.toString())
}
this.schema!!.build(arrayOf(), passphrase)
val barcodeEncoder: BarcodeEncoder = BarcodeEncoder()
this.qrCodeBitmap = barcodeEncoder.encodeBitmap(this.schema!!.raw, BarcodeFormat.QR_CODE, 400, 400)
val webView = WebView(this) val webView = WebView(this)
webView.webViewClient = object : WebViewClient() { webView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView, requesst: WebResourceRequest) = false override fun shouldOverrideUrlLoading(view: WebView, requesst: WebResourceRequest) = false
@@ -59,7 +85,7 @@ class CreateActivity : AppCompatActivity() {
var byteArrayOutputStream = ByteArrayOutputStream() var byteArrayOutputStream = ByteArrayOutputStream()
this.qrCodeBitmap?.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream) this.qrCodeBitmap?.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream)
htmlDocument = htmlDocument.replace("\$DATE", Date().time.toString()) htmlDocument = htmlDocument.replace("\$DATE", Date().time.toString())
.replace("\$HINT", "Not configurable yet!") .replace("\$HINT", hint)
.replace("\$QRCODE", Base64.encodeToString(byteArrayOutputStream.toByteArray(), Base64.NO_WRAP)) .replace("\$QRCODE", Base64.encodeToString(byteArrayOutputStream.toByteArray(), Base64.NO_WRAP))
Log.i("Create Activity", htmlDocument) Log.i("Create Activity", htmlDocument)
@@ -68,6 +94,9 @@ class CreateActivity : AppCompatActivity() {
mWebView = webView mWebView = webView
} }
/**
* Invoked after doPrint() and takes the rendered web view and creates a new print job.
*/
fun createWebPrintJob(webView: WebView) { fun createWebPrintJob(webView: WebView) {
(this.getSystemService(Context.PRINT_SERVICE) as? PrintManager)?.let { printManager -> (this.getSystemService(Context.PRINT_SERVICE) as? PrintManager)?.let { printManager ->
val jobName = "Offpass Document" val jobName = "Offpass Document"
@@ -83,15 +112,49 @@ class CreateActivity : AppCompatActivity() {
} }
} }
/**
* Invoked when user gave empty passphrase.
*/
fun showError(title: String, body: String) {
val builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.ErrorDialog))
with(builder) {
setTitle(title)
setMessage(body)
setPositiveButton("Got it", DialogInterface.OnClickListener { dialogInterface, i ->
dialogInterface.dismiss()
})
show()
}
}
fun passwordKnown(password: String): Boolean {
val known_passwords = String(this.resources.openRawResource(
this.resources.getIdentifier("passwordlist", "raw", this.packageName)
).readBytes())
val known_passwords_array = runBlocking {
known_passwords.split("\n")
}
Log.i("Create Activity", "Size of passwords: ${known_passwords_array.size}")
return known_passwords_array.contains(password)
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
// Prevent other apps from making screenshots or to record Offpass.
window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE)
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!!.build(arrayOf("website_url", "2fa", "What's your favorite series"), "123")
this.schema!!.decrypt(this.schema!!.raw, "123") this.schema!!.decrypt(this.schema!!.raw, "123")*/
setSupportActionBar(findViewById(R.id.toolbar)) setSupportActionBar(findViewById(R.id.toolbar))
@@ -101,18 +164,48 @@ class CreateActivity : AppCompatActivity() {
finish() finish()
} }
print_button.setOnClickListener { print_button.setOnClickListener {
// Set up printing // Ask user for passhprase and hint
this.schema = QRSchema(this) val builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.PassphraseDialog))
this.schema!!.title = title_input.text.toString()
this.schema!!.username = username_input.text.toString() with(builder) {
this.schema!!.password = password_input.text.toString() val editTextLayout = layoutInflater.inflate(R.layout.dialogpassphrase, null)
this.schema!!.email = email_input.text.toString() setView(editTextLayout)
this.schema!!.website_url = url_input.text.toString() setTitle("Set Passphrase")
this.schema!!.build(arrayOf(), "123") setMessage("Final step is it to set your passphrase. Minimum are 8 characters.")
setPositiveButton("Print", DialogInterface.OnClickListener { dialogInterface, i ->
val passphrase = editTextLayout.passphrase_input.text.toString();
if (passphrase == "") {
// Make a new alert, telling the user that passphrase must not be null.
dialogInterface.cancel()
showError("Empty passphrase", "Passphrase must not be null.")
return@OnClickListener
}
if (passphrase.length < 8) {
// Make a new alert, telling the user that his passphrase doesn't meet the minimum.
dialogInterface.cancel()
showError("Weak passphrase", "Passphrase has to be at least 8 characters long.")
return@OnClickListener
}
// Check if password is found in our offline password list
if(passwordKnown(passphrase)) {
// Make a new alert, telling the user that his passphrase was found in our local wordlist.
dialogInterface.cancel()
showError("Passphrase is compromised!", "Passphrase got found in a wordlist of bad passwords. " +
"If you already used it somewhere, change it immediately!")
return@OnClickListener
}
doPrint(editTextLayout.passphrase_input.text.toString(),
editTextLayout.hint_input.text.toString())
})
setNegativeButton("Go back", DialogInterface.OnClickListener { dialogInterface, i ->
dialogInterface.dismiss()
})
show()
}
val barcodeEncoder: BarcodeEncoder = BarcodeEncoder()
this.qrCodeBitmap = barcodeEncoder.encodeBitmap(this.schema!!.raw, BarcodeFormat.QR_CODE, 400, 400)
doPrint()
} }
fa_input.isFocusable = false fa_input.isFocusable = false
@@ -148,8 +241,9 @@ class CreateActivity : AppCompatActivity() {
// We scanned a 2FA code // We scanned a 2FA code
if (result.contents.startsWith("otpauth://totp/")) { if (result.contents.startsWith("otpauth://totp/")) {
fa_progress.visibility = View.VISIBLE fa_progress.visibility = View.VISIBLE
val fa_uri = Uri.parse(result.contents) fa_uri = Uri.parse(result.contents)
val fa_generator = GoogleAuthenticator(base32secret = fa_uri.getQueryParameter("secret").toString()) val fa_generator = GoogleAuthenticator(base32secret = fa_uri?.getQueryParameter("secret").toString())
this.schema!!.custom.put("2fa", fa_uri?.getQueryParameter("secret").toString())
/*val objAnim: ObjectAnimator = ObjectAnimator.ofInt(fa_progress, "progress") /*val objAnim: ObjectAnimator = ObjectAnimator.ofInt(fa_progress, "progress")
objAnim.duration = 300 objAnim.duration = 300
@@ -180,7 +274,7 @@ class CreateActivity : AppCompatActivity() {
} }
} }
fa_label.text = "${fa_uri.path?.replace("/", "")} (${fa_uri.getQueryParameter("issuer")})" fa_label.text = "${fa_uri?.path?.replace("/", "")} (${fa_uri?.getQueryParameter("issuer")})"
} }
} }
} }

View File

@@ -24,19 +24,6 @@ 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, 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)
val encrypted_result = GlobalScope.async {
dbHandler.add("JtuB4O9M42", dummy_email)
dbHandler.add("JtuB4O9M42", dummy_custom_key)
dbHandler.add("JtuB4O9M42", dummy_custom_value)
Log.i("Main", "Encrypted target data to: ${CryptoOperations().encrypt("My cool key", "my secret")}")
}
dbHandler.writableDatabase.close() dbHandler.writableDatabase.close()
main_screen.setOnClickListener { main_screen.setOnClickListener {

View File

@@ -2,6 +2,7 @@ package com.github.mondei1.offpass
import android.R.attr.key import android.R.attr.key
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.ActionBar
import android.content.Intent import android.content.Intent
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Color import android.graphics.Color
@@ -9,6 +10,7 @@ import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import android.view.WindowManager
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import dev.turingcomplete.kotlinonetimepassword.GoogleAuthenticator import dev.turingcomplete.kotlinonetimepassword.GoogleAuthenticator
@@ -20,6 +22,18 @@ import kotlinx.coroutines.launch
class ViewActivity : AppCompatActivity() { class ViewActivity : AppCompatActivity() {
override fun onBackPressed() {
val i = Intent(this, MainActivity::class.java)
i.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
startActivity(i)
}
override fun setContentView(view: View?) {
// Prevent other apps from making screenshots or to record Offpass.
window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE)
}
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="@color/colorPrimaryDark"
android:paddingLeft="50dp"
android:paddingRight="50dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/passphrase_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="@color/colorPrimary"
android:textColorHint="@color/colorPrimary"
android:textColor="@color/colorPrimary"
android:hint="Enter passphrase"/>
<EditText
android:id="@+id/hint_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="@color/colorPrimary"
android:textColorHint="@color/colorPrimary"
android:textColor="@color/colorPrimary"
android:layout_marginTop="10dp"
android:layout_marginBottom="30dp"
android:maxLength="32"
android:hint="Enter hint (optional)" />
</LinearLayout>

File diff suppressed because it is too large Load Diff

View File

@@ -49,6 +49,7 @@
margin-top: 10rem; margin-top: 10rem;
font-weight: bold; font-weight: bold;
} }
img { img {
margin-top: 5rem; margin-top: 5rem;
height: 300px; height: 300px;

View File

@@ -3,15 +3,20 @@
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. --> <!-- Customize your theme here. -->
<item name="colorAccent">@color/colorAccent</item> <item name="colorAccent">@color/colorAccent</item>
<item name="android:actionBarStyle">@style/OffPassActionbar</item>
<item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">#fff</item> <item name="colorPrimaryDark">#fff</item>
<item name="android:statusBarColor">@color/colorPrimaryDark</item> <item name="android:statusBarColor">@color/colorPrimaryDark</item>
</style> </style>
<style name="OffPassActionbar" parent="Theme.AppCompat.Light.NoActionBar"> <style name="PassphraseDialog" parent="@android:style/Theme.Material.Dialog">
<item name="background">@color/colorPrimaryDark</item> <item name="android:textColor">@color/colorPrimary</item>
<item name="android:foreground">@color/colorPrimary</item> <item name="android:textStyle">bold</item>
<item name="android:backgroundDimAmount">0.9</item>
<item name="android:background">@color/colorPrimaryDark</item>
</style>
<style name="ErrorDialog" parent="@android:style/Theme.Material.Dialog">
<item name="android:textColor">@color/colorPrimary</item>
<item name="android:textStyle">bold</item>
<item name="android:background">@android:color/holo_red_light</item>
</style> </style>
</resources> </resources>