Password generator implemented

- Passwords also get's checked if it's compromised
This commit is contained in:
2020-07-13 19:17:43 +02:00
parent 2efdce87a8
commit 3ffd0e063f
26 changed files with 372 additions and 46 deletions

View File

@@ -41,6 +41,9 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.0' implementation 'androidx.core:core-ktx:1.3.0'
/* Password strengh library */
compile 'com.nulab-inc:zxcvbn:1.3.0'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.7" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.7"

View File

@@ -27,12 +27,16 @@ import com.google.zxing.BarcodeFormat
import com.google.zxing.integration.android.IntentIntegrator import com.google.zxing.integration.android.IntentIntegrator
import com.google.zxing.integration.android.IntentResult import com.google.zxing.integration.android.IntentResult
import com.journeyapps.barcodescanner.BarcodeEncoder import com.journeyapps.barcodescanner.BarcodeEncoder
import com.nulabinc.zxcvbn.Zxcvbn
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.activity_generator.*
import kotlinx.android.synthetic.main.activity_generator.view.*
import kotlinx.android.synthetic.main.dialogpassphrase.view.* import kotlinx.android.synthetic.main.dialogpassphrase.view.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.lang.IllegalArgumentException import java.lang.IllegalArgumentException
import java.lang.StringBuilder
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@@ -168,54 +172,70 @@ class CreateActivity : AppCompatActivity() {
finish() finish()
} }
print_button.setOnClickListener { print_button.setOnClickListener {
// Ask user for passhprase and hint fun continueAfterCheck() {
val builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.PassphraseDialog)) // Ask user for passhprase and hint
val builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.PassphraseDialog))
with(builder) { with(builder) {
val editTextLayout = layoutInflater.inflate(R.layout.dialogpassphrase, null) val editTextLayout = layoutInflater.inflate(R.layout.dialogpassphrase, null)
setView(editTextLayout) setView(editTextLayout)
setTitle("Set Passphrase") setTitle("Set Passphrase")
setMessage("Final step is it to set your passphrase. Minimum are 8 characters.") setMessage("Final step is it to set your passphrase. Minimum are 8 characters.")
setPositiveButton("Print", DialogInterface.OnClickListener { dialogInterface, i -> setPositiveButton("Print", DialogInterface.OnClickListener { dialogInterface, i ->
val passphrase = editTextLayout.passphrase_input.text.toString(); val passphrase = editTextLayout.passphrase_input.text.toString();
if (passphrase == "") { if (passphrase == "") {
// Make a new alert, telling the user that passphrase must not be null. // Make a new alert, telling the user that passphrase must not be null.
dialogInterface.cancel() dialogInterface.cancel()
showError("Empty passphrase", "Passphrase must not be null.") showError("Empty passphrase", "Passphrase must not be null.")
return@OnClickListener return@OnClickListener
} }
if (passphrase.length < 8) { if (passphrase.length < 8) {
// Make a new alert, telling the user that his passphrase doesn't meet the minimum. // Make a new alert, telling the user that his passphrase doesn't meet the minimum.
dialogInterface.cancel() dialogInterface.cancel()
showError("Weak passphrase", "Passphrase has to be at least 8 characters long.") showError("Weak passphrase", "Passphrase has to be at least 8 characters long.")
return@OnClickListener return@OnClickListener
} }
if (passphrase != editTextLayout.passphrase2_input.text.toString()) { if (passphrase != editTextLayout.passphrase2_input.text.toString()) {
// Make a new alert, telling the user that his passphrase doesn't match the repeat field. // Make a new alert, telling the user that his passphrase doesn't match the repeat field.
dialogInterface.cancel() dialogInterface.cancel()
showError("Passphrase mismatch", "Both passphrases do not match.") showError("Passphrase mismatch", "Both passphrases do not match.")
return@OnClickListener return@OnClickListener
} }
// Check if password is found in our offline password list // Check if password is found in our offline password list
if(passwordKnown(passphrase)) { if(passwordKnown(passphrase)) {
// Make a new alert, telling the user that his passphrase was found in our local wordlist. // Make a new alert, telling the user that his passphrase was found in our local wordlist.
dialogInterface.cancel() dialogInterface.cancel()
showError("Passphrase is compromised!", "Passphrase got found in a wordlist of bad passwords. " + showError("Passphrase is compromised!", "Passphrase got found in a wordlist of bad passwords. " +
"If you already used it somewhere, change it immediately!") "If you already used it somewhere, change it immediately!")
return@OnClickListener return@OnClickListener
} }
doPrint(editTextLayout.passphrase_input.text.toString(), doPrint(editTextLayout.passphrase_input.text.toString(),
editTextLayout.hint_input.text.toString()) editTextLayout.hint_input.text.toString())
}) })
setNegativeButton("Go back", DialogInterface.OnClickListener { dialogInterface, i -> setNegativeButton("Go back", DialogInterface.OnClickListener { dialogInterface, i ->
dialogInterface.dismiss() dialogInterface.dismiss()
}) })
show() show()
}
} }
// First check if entered password is compromised
if (passwordKnown(password_input.text.toString())) {
val builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.ErrorDialog))
with(builder) {
setTitle("Password compromised!")
setMessage("Your password is weak and should be changed as it got found in a list of weak passwords.")
setPositiveButton("I'll change it", DialogInterface.OnClickListener { dialogInterface, i ->
})
setNegativeButton("I don't care", DialogInterface.OnClickListener { dialogInterface, i ->
continueAfterCheck()
})
show()
}
}
} }
password_hide.setOnClickListener { password_hide.setOnClickListener {
if (password_input.transformationMethod == HideReturnsTransformationMethod.getInstance()) { if (password_input.transformationMethod == HideReturnsTransformationMethod.getInstance()) {
@@ -227,6 +247,121 @@ class CreateActivity : AppCompatActivity() {
} }
} }
password_random.setOnClickListener {
val builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.PassphraseGenerator))
with (builder) {
val layout = layoutInflater.inflate(R.layout.activity_generator, null)
val zxcvbn = Zxcvbn()
var length: Int = 8
setTitle("Generate password")
setView(layout)
fun newPassword() {
var combined = ""
val upper_case = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
val lower_case = "abcdefghijklmnopqrstuvwxyz"
val speicals = "!\";#$%&'()*+,-./:;<=>?@[/]^_`"
val digits = "1234567890"
val emojis = arrayOf(0x1F600, 0x1F603, 0x1F601, 0x1F911, 0x1F910, 0x1F637, 0x1F47D, 0x1F480, 0x1F916)
val rand: Random = Random()
if (layout.AZ.isChecked) combined += upper_case
if (layout.az.isChecked) combined += lower_case
if (layout.zero_nine.isChecked) combined += digits
if (layout.emojis.isChecked) {
emojis.forEach {
combined += String(Character.toChars(it))
}
}
if (layout.special.isChecked) combined += speicals
val sb: StringBuilder = StringBuilder(length)
for (x in 1..length) {
sb.append(combined[rand.nextInt(combined.length)])
}
// Adapt text size
if (sb.length > 10) {
layout.random_string.textSize = 24.0f
} else if (sb.length > 16) {
layout.random_string.textSize = 18.0f
}
// Measure strengh
layout.progressBar.progress = zxcvbn.measure(sb.toString())
.score
if (layout.progressBar.progress == 1) {
layout.progressBar.progressTintList = ColorStateList.valueOf(Color.RED)
} else if (layout.progressBar.progress == 2) {
layout.progressBar.progressTintList = ColorStateList.valueOf(Color.YELLOW)
} else if (layout.progressBar.progress == 3) {
layout.progressBar.progressTintList = ColorStateList.valueOf(Color.GREEN)
} else {
layout.progressBar.progressTintList = ColorStateList.valueOf(Color.GREEN)
}
layout.random_string.setText(sb.toString())
}
// Initial set
layout.length_label.setText(length.toString())
newPassword()
// Add length
layout.add_button.setOnClickListener {
length++
layout.length_label.setText(length.toString())
newPassword()
}
layout.add_button.setOnLongClickListener {
length += 10
layout.length_label.setText(length.toString())
newPassword()
true
}
// Subtract length
layout.subtract_button.setOnClickListener {
length--
layout.length_label.setText(length.toString())
newPassword()
}
layout.subtract_button.setOnLongClickListener {
length -= 10
layout.length_label.setText(length.toString())
newPassword()
true
}
// Refresh
layout.refresh_button.setOnClickListener {
newPassword()
}
// Copy password
layout.copy_button.setOnClickListener {
// Copy code if already scanend
val clip: ClipboardManager = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
clip.setPrimaryClip(ClipData.newPlainText("Random password", layout.random_string.text.toString()))
Toast.makeText(applicationContext, "Copied!", Toast.LENGTH_SHORT).show()
}
setPositiveButton("Use this",
DialogInterface.OnClickListener { dialog, id ->
password_input.setText(layout.random_string.text)
})
setNegativeButton("Go back",
DialogInterface.OnClickListener { dialog, id ->
dialog.dismiss()
})
show()
}
}
fa_input.isFocusable = false fa_input.isFocusable = false

View File

@@ -2,10 +2,27 @@ package com.github.mondei1.offpass
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle import android.os.Bundle
import android.util.Log
import kotlinx.android.synthetic.main.activity_generator.*
class GeneratorActivity : AppCompatActivity() { class GeneratorActivity : AppCompatActivity() {
var length: Int = 12
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_generator) setContentView(R.layout.activity_generator)
length_label.setText(length.toString())
Log.i("Generator", "Length: $length")
add_button.setOnClickListener {
Log.i("Generator", "Length: $length")
length++
length_label.setText(length.toString())
}
subtract_button.setOnClickListener {
Log.i("Generator", "Length: $length")
length--
length_label.setText(length.toString())
}
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 1011 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 914 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 664 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -1,9 +1,171 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="wrap_content"
tools:context=".GeneratorActivity"> tools:context=".GeneratorActivity">
</androidx.constraintlayout.widget.ConstraintLayout> <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="350dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="256dp"
android:layout_height="10dp"
android:layout_marginStart="80dp"
android:layout_marginTop="100dp"
android:layout_marginEnd="80dp"
android:progress="2"
android:min="0"
android:max="4"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/random_string"
android:layout_width="201dp"
android:layout_height="wrap_content"
android:layout_marginStart="80dp"
android:layout_marginEnd="80dp"
android:layout_marginBottom="10dp"
android:gravity="center_vertical"
android:text="D%g$MA0H8d"
android:textAlignment="center"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:textSize="30sp"
app:layout_constraintBottom_toTopOf="@+id/progressBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ImageButton
android:id="@+id/refresh_button"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:background="#00FFFFFF"
app:layout_constraintBottom_toTopOf="@+id/progressBar"
app:layout_constraintEnd_toStartOf="@+id/random_string"
app:srcCompat="@drawable/ic_refresh" />
<ImageButton
android:id="@+id/copy_button"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginStart="16dp"
android:layout_marginBottom="16dp"
android:background="#00FFFFFF"
app:layout_constraintBottom_toTopOf="@+id/progressBar"
app:layout_constraintStart_toEndOf="@+id/random_string"
app:srcCompat="@drawable/ic_content_copy" />
<LinearLayout
android:id="@+id/password_length_container"
android:layout_width="142dp"
android:layout_height="45dp"
android:layout_marginStart="1dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="1dp"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/progressBar">
<ImageButton
android:id="@+id/subtract_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="7dp"
android:layout_weight="1"
android:background="#00FFFFFF"
app:srcCompat="@drawable/ic_remove" />
<TextView
android:id="@+id/length_label"
android:layout_width="45dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:fontFamily="sans-serif-light"
android:gravity="center_horizontal|center_vertical"
android:text="0"
android:textColor="@color/colorPrimaryDark"
android:textSize="24sp" />
<ImageButton
android:id="@+id/add_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="7dp"
android:layout_weight="1"
android:background="#00FFFFFF"
app:srcCompat="@drawable/ic_add" />
</LinearLayout>
<CheckBox
android:id="@+id/AZ"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="32dp"
android:checked="true"
android:text="A-Z"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/password_length_container" />
<CheckBox
android:id="@+id/az"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="8dp"
android:checked="true"
android:text="a-z"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/AZ" />
<CheckBox
android:id="@+id/zero_nine"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="8dp"
android:checked="true"
android:text="0-9"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/az" />
<CheckBox
android:id="@+id/special"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="8dp"
android:checked="true"
android:text="!&quot;#$%'()*+"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/zero_nine" />
<CheckBox
android:id="@+id/emojis"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="8dp"
android:text="😃👽😺👋"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/special" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>

View File

@@ -22,7 +22,9 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:orientation="horizontal"> android:orientation="horizontal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageButton <ImageButton
android:id="@+id/back" android:id="@+id/back"

View File

@@ -19,4 +19,11 @@
<item name="android:textStyle">bold</item> <item name="android:textStyle">bold</item>
<item name="android:background">@android:color/holo_red_light</item> <item name="android:background">@android:color/holo_red_light</item>
</style> </style>
<style name="PassphraseGenerator" parent="@android:style/Theme.Material.Dialog">
<item name="android:paddingTop">0dp</item>
<item name="android:layout_marginTop">0dp</item>
<item name="android:textColor">@color/colorPrimaryDark</item>
<item name="background">@color/colorPrimary</item>
<item name="android:textStyle">bold</item>
</style>
</resources> </resources>