aboutsummaryrefslogtreecommitdiffstats
path: root/app/src/main/java/dev/msfjarvis/aps/util/pwgenxkpwd
diff options
context:
space:
mode:
authorHarsh Shandilya <me@msfjarvis.dev>2020-12-05 06:07:18 +0530
committerGitHub <noreply@github.com>2020-12-05 06:07:18 +0530
commit5e66d99c852ea67a88b650c03b0e8d55e83eccde (patch)
treeaee323a08253fe7b863976eff92a1bd412bd1430 /app/src/main/java/dev/msfjarvis/aps/util/pwgenxkpwd
parent8eb55f18a11d6b2f155c7c9d48ca833313ee13db (diff)
downloadHardenedPass-5e66d99c852ea67a88b650c03b0e8d55e83eccde.tar.gz
HardenedPass-5e66d99c852ea67a88b650c03b0e8d55e83eccde.zip
Refactor package structure (#1233)
* idea: default test runner to Gradle * Kick off package structure revamp * Reparent all classes under dev.msfjarvis.aps Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
Diffstat (limited to 'app/src/main/java/dev/msfjarvis/aps/util/pwgenxkpwd')
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/util/pwgenxkpwd/CapsType.kt9
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/util/pwgenxkpwd/PasswordBuilder.kt142
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/util/pwgenxkpwd/XkpwdDictionary.kt40
3 files changed, 191 insertions, 0 deletions
diff --git a/app/src/main/java/dev/msfjarvis/aps/util/pwgenxkpwd/CapsType.kt b/app/src/main/java/dev/msfjarvis/aps/util/pwgenxkpwd/CapsType.kt
new file mode 100644
index 00000000..83274171
--- /dev/null
+++ b/app/src/main/java/dev/msfjarvis/aps/util/pwgenxkpwd/CapsType.kt
@@ -0,0 +1,9 @@
+/*
+ * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+package dev.msfjarvis.aps.util.pwgenxkpwd
+
+enum class CapsType {
+ lowercase, UPPERCASE, TitleCase, Sentence, As_iS
+}
diff --git a/app/src/main/java/dev/msfjarvis/aps/util/pwgenxkpwd/PasswordBuilder.kt b/app/src/main/java/dev/msfjarvis/aps/util/pwgenxkpwd/PasswordBuilder.kt
new file mode 100644
index 00000000..4bd1a6e6
--- /dev/null
+++ b/app/src/main/java/dev/msfjarvis/aps/util/pwgenxkpwd/PasswordBuilder.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+package dev.msfjarvis.aps.util.pwgenxkpwd
+
+import android.content.Context
+import com.github.michaelbull.result.Result
+import com.github.michaelbull.result.runCatching
+import dev.msfjarvis.aps.R
+import dev.msfjarvis.aps.util.pwgen.PasswordGenerator.PasswordGeneratorException
+import dev.msfjarvis.aps.util.pwgen.secureRandomCharacter
+import dev.msfjarvis.aps.util.pwgen.secureRandomElement
+import dev.msfjarvis.aps.util.pwgen.secureRandomNumber
+import java.util.Locale
+
+class PasswordBuilder(ctx: Context) {
+
+ private var numSymbols = 0
+ private var isAppendSymbolsSeparator = false
+ private var context = ctx
+ private var numWords = 3
+ private var maxWordLength = 9
+ private var minWordLength = 5
+ private var separator = "."
+ private var capsType = CapsType.Sentence
+ private var prependDigits = 0
+ private var numDigits = 0
+ private var isPrependWithSeparator = false
+ private var isAppendNumberSeparator = false
+
+ fun setNumberOfWords(amount: Int) = apply {
+ numWords = amount
+ }
+
+ fun setMinimumWordLength(min: Int) = apply {
+ minWordLength = min
+ }
+
+ fun setMaximumWordLength(max: Int) = apply {
+ maxWordLength = max
+ }
+
+ fun setSeparator(separator: String) = apply {
+ this.separator = separator
+ }
+
+ fun setCapitalization(capitalizationScheme: CapsType) = apply {
+ capsType = capitalizationScheme
+ }
+
+ @JvmOverloads
+ fun prependNumbers(numDigits: Int, addSeparator: Boolean = true) = apply {
+ prependDigits = numDigits
+ isPrependWithSeparator = addSeparator
+ }
+
+ @JvmOverloads
+ fun appendNumbers(numDigits: Int, addSeparator: Boolean = false) = apply {
+ this.numDigits = numDigits
+ isAppendNumberSeparator = addSeparator
+ }
+
+ @JvmOverloads
+ fun appendSymbols(numSymbols: Int, addSeparator: Boolean = false) = apply {
+ this.numSymbols = numSymbols
+ isAppendSymbolsSeparator = addSeparator
+ }
+
+ private fun generateRandomNumberSequence(totalNumbers: Int): String {
+ val numbers = StringBuilder(totalNumbers)
+ for (i in 0 until totalNumbers) {
+ numbers.append(secureRandomNumber(10))
+ }
+ return numbers.toString()
+ }
+
+ private fun generateRandomSymbolSequence(numSymbols: Int): String {
+ val numbers = StringBuilder(numSymbols)
+ for (i in 0 until numSymbols) {
+ numbers.append(SYMBOLS.secureRandomCharacter())
+ }
+ return numbers.toString()
+ }
+
+ @OptIn(ExperimentalStdlibApi::class)
+ fun create(): Result<String, Throwable> {
+ val wordBank = mutableListOf<String>()
+ val password = StringBuilder()
+
+ if (prependDigits != 0) {
+ password.append(generateRandomNumberSequence(prependDigits))
+ if (isPrependWithSeparator) {
+ password.append(separator)
+ }
+ }
+ return runCatching {
+ val dictionary = XkpwdDictionary(context)
+ val words = dictionary.words
+ for (wordLength in minWordLength..maxWordLength) {
+ wordBank.addAll(words[wordLength] ?: emptyList())
+ }
+
+ if (wordBank.size == 0) {
+ throw PasswordGeneratorException(context.getString(R.string.xkpwgen_builder_error, minWordLength, maxWordLength))
+ }
+
+ for (i in 0 until numWords) {
+ val candidate = wordBank.secureRandomElement()
+ val s = when (capsType) {
+ CapsType.UPPERCASE -> candidate.toUpperCase(Locale.getDefault())
+ CapsType.Sentence -> if (i == 0) candidate.capitalize(Locale.getDefault()) else candidate
+ CapsType.TitleCase -> candidate.capitalize(Locale.getDefault())
+ CapsType.lowercase -> candidate.toLowerCase(Locale.getDefault())
+ CapsType.As_iS -> candidate
+ }
+ password.append(s)
+ if (i + 1 < numWords) {
+ password.append(separator)
+ }
+ }
+ if (numDigits != 0) {
+ if (isAppendNumberSeparator) {
+ password.append(separator)
+ }
+ password.append(generateRandomNumberSequence(numDigits))
+ }
+ if (numSymbols != 0) {
+ if (isAppendSymbolsSeparator) {
+ password.append(separator)
+ }
+ password.append(generateRandomSymbolSequence(numSymbols))
+ }
+ password.toString()
+ }
+ }
+
+ companion object {
+
+ private const val SYMBOLS = "!@\$%^&*-_+=:|~?/.;#"
+ }
+}
diff --git a/app/src/main/java/dev/msfjarvis/aps/util/pwgenxkpwd/XkpwdDictionary.kt b/app/src/main/java/dev/msfjarvis/aps/util/pwgenxkpwd/XkpwdDictionary.kt
new file mode 100644
index 00000000..95e65bcb
--- /dev/null
+++ b/app/src/main/java/dev/msfjarvis/aps/util/pwgenxkpwd/XkpwdDictionary.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+package dev.msfjarvis.aps.util.pwgenxkpwd
+
+import android.content.Context
+import dev.msfjarvis.aps.R
+import dev.msfjarvis.aps.util.settings.PreferenceKeys
+import dev.msfjarvis.aps.util.extensions.getString
+import dev.msfjarvis.aps.util.extensions.sharedPrefs
+import java.io.File
+
+class XkpwdDictionary(context: Context) {
+
+ val words: Map<Int, List<String>>
+
+ init {
+ val prefs = context.sharedPrefs
+ val uri = prefs.getString(PreferenceKeys.PREF_KEY_CUSTOM_DICT) ?: ""
+ val customDictFile = File(context.filesDir, XKPWD_CUSTOM_DICT_FILE)
+
+ val lines = if (prefs.getBoolean(PreferenceKeys.PREF_KEY_IS_CUSTOM_DICT, false) &&
+ uri.isNotEmpty() && customDictFile.canRead()) {
+ customDictFile.readLines()
+ } else {
+ context.resources.openRawResource(R.raw.xkpwdict).bufferedReader().readLines()
+ }
+
+ words = lines.asSequence()
+ .map { it.trim() }
+ .filter { it.isNotEmpty() && !it.contains(' ') }
+ .groupBy { it.length }
+ }
+
+ companion object {
+
+ const val XKPWD_CUSTOM_DICT_FILE = "custom_dict.txt"
+ }
+}