diff options
author | Matthew Wong <wongma@protonmail.ch> | 2015-07-10 19:44:22 -0400 |
---|---|---|
committer | Matthew Wong <wongma@protonmail.ch> | 2015-07-10 19:44:22 -0400 |
commit | d2a252a06b589edee912e1193ab2804ff9238dd3 (patch) | |
tree | 640f9dd6afc0ea0bab55e870e7b49243ad31ded4 | |
parent | 8c885882dc0642f2dbf313980441911edd9f9cc7 (diff) |
Import pwgen classes
4 files changed, 439 insertions, 0 deletions
diff --git a/app/src/main/java/com/zeapo/pwdstore/pwgen/pw_phonemes.java b/app/src/main/java/com/zeapo/pwdstore/pwgen/pw_phonemes.java new file mode 100644 index 00000000..1b312232 --- /dev/null +++ b/app/src/main/java/com/zeapo/pwdstore/pwgen/pw_phonemes.java @@ -0,0 +1,206 @@ +package com.zeapo.pwdstore.pwgen; + +public class pw_phonemes { + private static final int CONSONANT = 0x0001; + private static final int VOWEL = 0x0002; + private static final int DIPTHONG = 0x0004; + private static final int NOT_FIRST = 0x0008; + + private static final element elements[] = { + new element("a", VOWEL), + new element("ae", VOWEL | DIPTHONG), + new element("ah", VOWEL | DIPTHONG), + new element("ai", VOWEL | DIPTHONG), + new element("b", CONSONANT), + new element("c", CONSONANT), + new element("ch", CONSONANT | DIPTHONG), + new element("d", CONSONANT), + new element("e", VOWEL), + new element("ee", VOWEL | DIPTHONG), + new element("ei", VOWEL | DIPTHONG), + new element("f", CONSONANT), + new element("g", CONSONANT), + new element("gh", CONSONANT | DIPTHONG | NOT_FIRST), + new element("h", CONSONANT), + new element("i", VOWEL), + new element("ie", VOWEL | DIPTHONG), + new element("j", CONSONANT), + new element("k", CONSONANT), + new element("l", CONSONANT), + new element("m", CONSONANT), + new element("n", CONSONANT), + new element("ng", CONSONANT | DIPTHONG | NOT_FIRST), + new element("o", VOWEL), + new element("oh", VOWEL | DIPTHONG), + new element("oo", VOWEL | DIPTHONG), + new element("p", CONSONANT), + new element("ph", CONSONANT | DIPTHONG), + new element("qu", CONSONANT | DIPTHONG), + new element("r", CONSONANT), + new element("s", CONSONANT), + new element("sh", CONSONANT | DIPTHONG), + new element("t", CONSONANT), + new element("th", CONSONANT | DIPTHONG), + new element("u", VOWEL), + new element("v", CONSONANT), + new element("w", CONSONANT), + new element("x", CONSONANT), + new element("y", CONSONANT), + new element("z", CONSONANT) + }; + + private static class element { + String str; + int flags; + element(String str, int flags) { + this.str = str; + this.flags = flags; + } + } + + private static final int NUM_ELEMENTS = elements.length; + + /** + * Generates a human-readable password. + * + * @param size length of password to generate + * @param pwFlags flag field where set bits indicate conditions the + * generated password must meet + * <table summary="bits of flag field"> + * <tr><td>Bit</td><td>Condition</td></tr> + * <tr><td>0</td><td>include at least one number</td></tr> + * <tr><td>1</td><td>include at least one uppercase letter</td></tr> + * <tr><td>2</td><td>include at least one symbol</td></tr> + * <tr><td>3</td><td>don't include ambiguous characters</td></tr> + * </table> + * @return the generated password + */ + public static String phonemes(int size, int pwFlags) { + String password; + int curSize, i, length, flags, featureFlags, prev, shouldBe; + boolean first; + String str; + char cha; + + do { + password = ""; + featureFlags = pwFlags; + curSize = 0; + prev = 0; + first = true; + + shouldBe = randnum.number(2) == 1 ? VOWEL : CONSONANT; + + while (curSize < size) { + i = randnum.number(NUM_ELEMENTS); + str = elements[i].str; + length = str.length(); + flags = elements[i].flags; + // Filter on the basic type of the next element + if ((flags & shouldBe) == 0) { + continue; + } + // Handle the NOT_FIRST flag + if (first && (flags & NOT_FIRST) > 0) { + continue; + } + // Don't allow VOWEL followed a Vowel/Dipthong pair + if ((prev & VOWEL) > 0 && (flags & VOWEL) > 0 + && (flags & DIPTHONG) > 0) { + continue; + } + // Don't allow us to overflow the buffer + if (length > size - curSize) { + continue; + } + // OK, we found an element which matches our criteria, let's do + // it + password += str; + + // Handle UPPERS + if ((pwFlags & pwgen.UPPERS) > 0) { + if ((first || (flags & CONSONANT) > 0) + && (randnum.number(10) < 2)) { + int index = password.length() - length; + password = password.substring(0, index) + + str.toUpperCase(); + featureFlags &= ~pwgen.UPPERS; + } + } + + // Handle the AMBIGUOUS flag + if ((pwFlags & pwgen.AMBIGUOUS) > 0) { + for (char ambiguous : pwgen.AMBIGUOUS_STR.toCharArray()) { + if (password.contains(String.valueOf(ambiguous))) { + password = password.substring(0, curSize); + break; + } + } + if (password.length() == curSize) + continue; + } + + curSize += length; + + // Time to stop? + if (curSize >= size) + break; + + // Handle DIGITS + if ((pwFlags & pwgen.DIGITS) > 0) { + if (!first && (randnum.number(10) < 3)) { + String val; + do { + cha = Character.forDigit(randnum.number(10), 10); + val = String.valueOf(cha); + } while ((pwFlags & pwgen.AMBIGUOUS) > 0 + && pwgen.AMBIGUOUS_STR.contains(val)); + password += val; + curSize++; + + featureFlags &= ~pwgen.DIGITS; + + first = true; + prev = 0; + shouldBe = randnum.number(2) == 1 ? VOWEL : CONSONANT; + continue; + } + } + + // Handle SYMBOLS + if ((pwFlags & pwgen.SYMBOLS) > 0) { + if (!first && (randnum.number(10) < 2)) { + String val; + int num; + do { + num = randnum.number(pwgen.SYMBOLS_STR.length()); + cha = pwgen.SYMBOLS_STR.toCharArray()[num]; + val = String.valueOf(cha); + } while ((pwFlags & pwgen.AMBIGUOUS) > 0 + && pwgen.AMBIGUOUS_STR.contains(val)); + password += val; + curSize++; + + featureFlags &= ~pwgen.SYMBOLS; + } + } + + // OK, figure out what the next element should be + if (shouldBe == CONSONANT) { + shouldBe = VOWEL; + } else { + if ((prev & VOWEL) > 0 || (flags & DIPTHONG) > 0 + || (randnum.number(10) > 3)) { + shouldBe = CONSONANT; + } else { + shouldBe = VOWEL; + } + } + prev = flags; + first = false; + } + } while ((featureFlags & (pwgen.UPPERS | pwgen.DIGITS | pwgen.SYMBOLS)) + > 0); + return password; + } +} diff --git a/app/src/main/java/com/zeapo/pwdstore/pwgen/pw_rand.java b/app/src/main/java/com/zeapo/pwdstore/pwgen/pw_rand.java new file mode 100644 index 00000000..1f4acdec --- /dev/null +++ b/app/src/main/java/com/zeapo/pwdstore/pwgen/pw_rand.java @@ -0,0 +1,69 @@ +package com.zeapo.pwdstore.pwgen; + +public class pw_rand { + + /** + * Generates a completely random password. + * + * @param size length of password to generate + * @param pwFlags flag field where set bits indicate conditions the + * generated password must meet + * <table summary ="bits of flag field"> + * <tr><td>Bit</td><td>Condition</td></tr> + * <tr><td>0</td><td>include at least one number</td></tr> + * <tr><td>1</td><td>include at least one uppercase letter</td></tr> + * <tr><td>2</td><td>include at least one symbol</td></tr> + * <tr><td>3</td><td>don't include ambiguous characters</td></tr> + * <tr><td>4</td><td>don't include vowels</td></tr> + * </table> + * @return the generated password + */ + public static String rand(int size, int pwFlags) { + String password = ""; + char cha; + int i, featureFlags, num; + String val; + + String bank = ""; + if ((pwFlags & pwgen.DIGITS) > 0) { + bank += pwgen.DIGITS_STR; + } + if ((pwFlags & pwgen.UPPERS) > 0) { + bank += pwgen.UPPERS_STR; + } + bank += pwgen.LOWERS_STR; + if ((pwFlags & pwgen.SYMBOLS) > 0) { + bank += pwgen.SYMBOLS_STR; + } + do { + featureFlags = pwFlags; + i = 0; + while (i < size) { + num = randnum.number(bank.length()); + cha = bank.toCharArray()[num]; + val = String.valueOf(cha); + if ((pwFlags & pwgen.AMBIGUOUS) > 0 + && pwgen.AMBIGUOUS_STR.contains(val)) { + continue; + } + if ((pwFlags & pwgen.NO_VOWELS) > 0 + && pwgen.VOWELS_STR.contains(val)) { + continue; + } + password += val; + i++; + if (pwgen.DIGITS_STR.contains(val)) { + featureFlags &= ~pwgen.DIGITS; + } + if (pwgen.UPPERS_STR.contains(val)) { + featureFlags &= ~pwgen.UPPERS; + } + if (pwgen.SYMBOLS_STR.contains(val)) { + featureFlags &= ~pwgen.SYMBOLS; + } + } + } while ((featureFlags & (pwgen.UPPERS | pwgen.DIGITS | pwgen.SYMBOLS)) + > 0); + return password; + } +} diff --git a/app/src/main/java/com/zeapo/pwdstore/pwgen/pwgen.java b/app/src/main/java/com/zeapo/pwdstore/pwgen/pwgen.java new file mode 100644 index 00000000..e08573f0 --- /dev/null +++ b/app/src/main/java/com/zeapo/pwdstore/pwgen/pwgen.java @@ -0,0 +1,138 @@ +package com.zeapo.pwdstore.pwgen; + +import android.content.Context; +import android.content.SharedPreferences; + +import java.util.ArrayList; + +public class pwgen { + static final int DIGITS = 0x0001; + static final int UPPERS = 0x0002; + static final int SYMBOLS = 0x0004; + static final int AMBIGUOUS = 0x0008; + static final int NO_VOWELS = 0x0010; + + static final String DIGITS_STR = "0123456789"; + static final String UPPERS_STR = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + static final String LOWERS_STR = "abcdefghijklmnopqrstuvwxyz"; + static final String SYMBOLS_STR = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; + static final String AMBIGUOUS_STR = "B8G6I1l0OQDS5Z2"; + static final String VOWELS_STR = "01aeiouyAEIOUY"; + + // No a, c, n, h, H, C, 1, N + private static final String pwOptions = "0ABsvy"; + + /** + * Sets password generation preferences. + * + * @param ctx context from which to retrieve SharedPreferences from + * preferences file 'pwgen' + * @param argv options for password generation + * <table summary="options for password generation"> + * <tr><td>Option</td><td>Description</td></tr> + * <tr><td>0</td><td>don't include numbers</td></tr> + * <tr><td>A</td><td>don't include uppercase letters</td></tr> + * <tr><td>B</td><td>don't include ambiguous charactersl</td></tr> + * <tr><td>s</td><td>generate completely random passwords</td></tr> + * <tr><td>v</td><td>don't include vowels</td></tr> + * <tr><td>y</td><td>include at least one symbol</td></tr> + * </table> + * @param numArgv numerical options for password generation: length of + * generated passwords followed by number of passwords to + * generate + * @return <code>false</code> if a numerical options is invalid, + * <code>true</code> otherwise + */ + public static boolean setPrefs(Context ctx, ArrayList<String> argv + , int... numArgv) { + SharedPreferences prefs + = ctx.getSharedPreferences("pwgen", Context.MODE_PRIVATE); + SharedPreferences.Editor editor = prefs.edit(); + + for (char option : pwOptions.toCharArray()) { + if (argv.contains(String.valueOf(option))) { + editor.putBoolean(String.valueOf(option), true); + argv.remove(String.valueOf(option)); + } else { + editor.putBoolean(String.valueOf(option), false); + } + } + for (int i = 0; i < numArgv.length && i < 2; i++) { + if (numArgv[i] <= 0) { + // Invalid password length or number of passwords + return false; + } + String name = i == 0 ? "length" : "num"; + editor.putInt(name, numArgv[i]); + } + editor.apply(); + return true; + } + + /** + * Generates passwords using the preferences set by + * {@link #setPrefs(Context, ArrayList, int...)}. + * + * @param ctx context from which to retrieve SharedPreferences from + * preferences file 'pwgen' + * @return list of generated passwords + */ + public static ArrayList<String> generate(Context ctx) { + SharedPreferences prefs + = ctx.getSharedPreferences("pwgen", Context.MODE_PRIVATE); + + boolean phonemes = true; + int pwgenFlags = DIGITS | UPPERS; + + for (char option : pwOptions.toCharArray()) { + if (prefs.getBoolean(String.valueOf(option), false)) { + switch(option) { + case '0': + pwgenFlags &= ~DIGITS; + break; + case 'A': + pwgenFlags &= ~UPPERS; + break; + case 'B': + pwgenFlags |= AMBIGUOUS; + break; + case 's': + phonemes = false; + // pwgenFlags = DIGITS | UPPERS; + break; + case 'y': + pwgenFlags |= SYMBOLS; + break; + case 'v': + phonemes = false; + pwgenFlags |= NO_VOWELS; // | DIGITS | UPPERS; + break; + } + } + } + + int length = prefs.getInt("length", 8); + if (length < 5) { + phonemes = false; + } + if (length <= 2) { + pwgenFlags &= ~UPPERS; + } + if (length <= 1) { + pwgenFlags &= ~DIGITS; + } + + ArrayList<String> passwords = new ArrayList<>(); + int num = prefs.getInt("num", 1); + for (int i = 0; i < num; i++) { + if (phonemes) { + passwords.add(pw_phonemes.phonemes(length, pwgenFlags)); + } else { + passwords.add(pw_rand.rand(length, pwgenFlags)); + } + } + return passwords; + } + +} + diff --git a/app/src/main/java/com/zeapo/pwdstore/pwgen/randnum.java b/app/src/main/java/com/zeapo/pwdstore/pwgen/randnum.java new file mode 100644 index 00000000..83cf4b03 --- /dev/null +++ b/app/src/main/java/com/zeapo/pwdstore/pwgen/randnum.java @@ -0,0 +1,26 @@ +package com.zeapo.pwdstore.pwgen; + +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +public class randnum { + private static SecureRandom random; + + static { + try { + random = SecureRandom.getInstance("SHA1PRNG"); + } catch (NoSuchAlgorithmException e) { + throw new SecurityException("SHA1PRNG not available", e); + } + } + + /** + * Generate a random number n, where 0 <= n < maxNum. + * + * @param maxNum the bound on the random number to be returned + * @return the generated random number + */ + public static int number(int maxNum) { + return random.nextInt(maxNum); + } +} |