Passphrase & hint can now be set.
- Built-in password list
This commit is contained in:
@@ -1,10 +1,7 @@
|
||||
package com.github.mondei1.offpass
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.*
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
@@ -15,11 +12,14 @@ import android.print.PrintJob
|
||||
import android.print.PrintManager
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import android.view.ContextThemeWrapper
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.webkit.WebResourceRequest
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.zxing.BarcodeFormat
|
||||
import com.google.zxing.integration.android.IntentIntegrator
|
||||
@@ -27,6 +27,8 @@ import com.google.zxing.integration.android.IntentResult
|
||||
import com.journeyapps.barcodescanner.BarcodeEncoder
|
||||
import dev.turingcomplete.kotlinonetimepassword.GoogleAuthenticator
|
||||
import kotlinx.android.synthetic.main.activity_create.*
|
||||
import kotlinx.android.synthetic.main.dialogpassphrase.*
|
||||
import kotlinx.android.synthetic.main.dialogpassphrase.view.*
|
||||
import kotlinx.coroutines.*
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.util.*
|
||||
@@ -39,7 +41,31 @@ class CreateActivity : AppCompatActivity() {
|
||||
private var mWebView: WebView? = 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)
|
||||
webView.webViewClient = object : WebViewClient() {
|
||||
override fun shouldOverrideUrlLoading(view: WebView, requesst: WebResourceRequest) = false
|
||||
@@ -59,7 +85,7 @@ class CreateActivity : AppCompatActivity() {
|
||||
var byteArrayOutputStream = ByteArrayOutputStream()
|
||||
this.qrCodeBitmap?.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream)
|
||||
htmlDocument = htmlDocument.replace("\$DATE", Date().time.toString())
|
||||
.replace("\$HINT", "Not configurable yet!")
|
||||
.replace("\$HINT", hint)
|
||||
.replace("\$QRCODE", Base64.encodeToString(byteArrayOutputStream.toByteArray(), Base64.NO_WRAP))
|
||||
|
||||
Log.i("Create Activity", htmlDocument)
|
||||
@@ -68,6 +94,9 @@ class CreateActivity : AppCompatActivity() {
|
||||
mWebView = webView
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked after doPrint() and takes the rendered web view and creates a new print job.
|
||||
*/
|
||||
fun createWebPrintJob(webView: WebView) {
|
||||
(this.getSystemService(Context.PRINT_SERVICE) as? PrintManager)?.let { printManager ->
|
||||
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?) {
|
||||
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!!.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!!.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))
|
||||
|
||||
@@ -101,18 +164,48 @@ class CreateActivity : AppCompatActivity() {
|
||||
finish()
|
||||
}
|
||||
print_button.setOnClickListener {
|
||||
// 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()
|
||||
this.schema!!.build(arrayOf(), "123")
|
||||
// Ask user for passhprase and hint
|
||||
val builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.PassphraseDialog))
|
||||
|
||||
with(builder) {
|
||||
val editTextLayout = layoutInflater.inflate(R.layout.dialogpassphrase, null)
|
||||
setView(editTextLayout)
|
||||
setTitle("Set Passphrase")
|
||||
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
|
||||
@@ -148,8 +241,9 @@ class CreateActivity : AppCompatActivity() {
|
||||
// We scanned a 2FA code
|
||||
if (result.contents.startsWith("otpauth://totp/")) {
|
||||
fa_progress.visibility = View.VISIBLE
|
||||
val fa_uri = Uri.parse(result.contents)
|
||||
val fa_generator = GoogleAuthenticator(base32secret = fa_uri.getQueryParameter("secret").toString())
|
||||
fa_uri = Uri.parse(result.contents)
|
||||
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")
|
||||
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")})"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,19 +24,6 @@ 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, 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()
|
||||
|
||||
main_screen.setOnClickListener {
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.github.mondei1.offpass
|
||||
|
||||
import android.R.attr.key
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.ActionBar
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
@@ -9,6 +10,7 @@ import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import dev.turingcomplete.kotlinonetimepassword.GoogleAuthenticator
|
||||
@@ -20,6 +22,18 @@ import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
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")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
31
app/src/main/res/layout/dialogpassphrase.xml
Normal file
31
app/src/main/res/layout/dialogpassphrase.xml
Normal 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>
|
||||
183048
app/src/main/res/raw/passwordlist.txt
Normal file
183048
app/src/main/res/raw/passwordlist.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -49,6 +49,7 @@
|
||||
margin-top: 10rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
img {
|
||||
margin-top: 5rem;
|
||||
height: 300px;
|
||||
|
||||
@@ -3,15 +3,20 @@
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
<item name="android:actionBarStyle">@style/OffPassActionbar</item>
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">#fff</item>
|
||||
<item name="android:statusBarColor">@color/colorPrimaryDark</item>
|
||||
</style>
|
||||
|
||||
<style name="OffPassActionbar" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<item name="background">@color/colorPrimaryDark</item>
|
||||
<item name="android:foreground">@color/colorPrimary</item>
|
||||
<style name="PassphraseDialog" parent="@android:style/Theme.Material.Dialog">
|
||||
<item name="android:textColor">@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>
|
||||
|
||||
</resources>
|
||||
Reference in New Issue
Block a user