Merge pull request #101 from wongma7/master

Adding support for password generation
This commit is contained in:
Mohamed Zenadi 2015-07-11 19:41:33 +02:00
commit 62c20d323c
11 changed files with 1031 additions and 1 deletions

View file

@ -20,6 +20,7 @@ import android.view.View;
import com.zeapo.pwdstore.crypto.PgpHandler; import com.zeapo.pwdstore.crypto.PgpHandler;
import com.zeapo.pwdstore.git.GitActivity; import com.zeapo.pwdstore.git.GitActivity;
import com.zeapo.pwdstore.git.GitAsyncTask; import com.zeapo.pwdstore.git.GitAsyncTask;
import com.zeapo.pwdstore.pwgen.PRNGFixes;
import com.zeapo.pwdstore.utils.PasswordItem; import com.zeapo.pwdstore.utils.PasswordItem;
import com.zeapo.pwdstore.utils.PasswordRecyclerAdapter; import com.zeapo.pwdstore.utils.PasswordRecyclerAdapter;
import com.zeapo.pwdstore.utils.PasswordRepository; import com.zeapo.pwdstore.utils.PasswordRepository;
@ -49,6 +50,7 @@ public class PasswordStore extends AppCompatActivity {
setContentView(R.layout.activity_pwdstore); setContentView(R.layout.activity_pwdstore);
settings = PreferenceManager.getDefaultSharedPreferences(this.getApplicationContext()); settings = PreferenceManager.getDefaultSharedPreferences(this.getApplicationContext());
activity = this; activity = this;
PRNGFixes.apply();
} }
@Override @Override

View file

@ -1,6 +1,7 @@
package com.zeapo.pwdstore.crypto; package com.zeapo.pwdstore.crypto;
import android.app.Activity; import android.app.Activity;
import android.app.DialogFragment;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.ClipData; import android.content.ClipData;
import android.content.ClipboardManager; import android.content.ClipboardManager;
@ -28,6 +29,7 @@ import android.widget.Toast;
import com.google.common.primitives.Longs; import com.google.common.primitives.Longs;
import com.zeapo.pwdstore.R; import com.zeapo.pwdstore.R;
import com.zeapo.pwdstore.UserPreference; import com.zeapo.pwdstore.UserPreference;
import com.zeapo.pwdstore.pwgenDialogFragment;
import com.zeapo.pwdstore.utils.PasswordRepository; import com.zeapo.pwdstore.utils.PasswordRepository;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
@ -174,6 +176,9 @@ public class PgpHandler extends AppCompatActivity implements OpenPgpServiceConne
case R.id.crypto_get_key_ids: case R.id.crypto_get_key_ids:
getKeyIds(new Intent()); getKeyIds(new Intent());
break; break;
case R.id.generate_password:
DialogFragment df = new pwgenDialogFragment();
df.show(getFragmentManager(), "generator");
default: default:
// should not happen // should not happen

View file

@ -0,0 +1,336 @@
package com.zeapo.pwdstore.pwgen;
/*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will Google be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, as long as the origin is not misrepresented.
*/
import android.os.Build;
import android.os.Process;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.SecureRandomSpi;
import java.security.Security;
/**
* Fixes for the output of the default PRNG having low entropy.
*
* The fixes need to be applied via {@link #apply()} before any use of Java
* Cryptography Architecture primitives. A good place to invoke them is in the
* application's {@code onCreate}.
*/
public final class PRNGFixes {
private static final int VERSION_CODE_JELLY_BEAN = 16;
private static final int VERSION_CODE_JELLY_BEAN_MR2 = 18;
private static final byte[] BUILD_FINGERPRINT_AND_DEVICE_SERIAL =
getBuildFingerprintAndDeviceSerial();
/** Hidden constructor to prevent instantiation. */
private PRNGFixes() {}
/**
* Applies all fixes.
*
* @throws SecurityException if a fix is needed but could not be applied.
*/
public static void apply() {
applyOpenSSLFix();
installLinuxPRNGSecureRandom();
}
/**
* Applies the fix for OpenSSL PRNG having low entropy. Does nothing if the
* fix is not needed.
*
* @throws SecurityException if the fix is needed but could not be applied.
*/
private static void applyOpenSSLFix() throws SecurityException {
if ((Build.VERSION.SDK_INT < VERSION_CODE_JELLY_BEAN)
|| (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2)) {
// No need to apply the fix
return;
}
try {
// Mix in the device- and invocation-specific seed.
Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
.getMethod("RAND_seed", byte[].class)
.invoke(null, generateSeed());
// Mix output of Linux PRNG into OpenSSL's PRNG
int bytesRead = (Integer) Class.forName(
"org.apache.harmony.xnet.provider.jsse.NativeCrypto")
.getMethod("RAND_load_file", String.class, long.class)
.invoke(null, "/dev/urandom", 1024);
if (bytesRead != 1024) {
throw new IOException(
"Unexpected number of bytes read from Linux PRNG: "
+ bytesRead);
}
} catch (Exception e) {
throw new SecurityException("Failed to seed OpenSSL PRNG", e);
}
}
/**
* Installs a Linux PRNG-backed {@code SecureRandom} implementation as the
* default. Does nothing if the implementation is already the default or if
* there is not need to install the implementation.
*
* @throws SecurityException if the fix is needed but could not be applied.
*/
private static void installLinuxPRNGSecureRandom()
throws SecurityException {
if (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2) {
// No need to apply the fix
return;
}
// Install a Linux PRNG-based SecureRandom implementation as the
// default, if not yet installed.
Provider[] secureRandomProviders =
Security.getProviders("SecureRandom.SHA1PRNG");
if ((secureRandomProviders == null)
|| (secureRandomProviders.length < 1)
|| (!LinuxPRNGSecureRandomProvider.class.equals(
secureRandomProviders[0].getClass()))) {
Security.insertProviderAt(new LinuxPRNGSecureRandomProvider(), 1);
}
// Assert that new SecureRandom() and
// SecureRandom.getInstance("SHA1PRNG") return a SecureRandom backed
// by the Linux PRNG-based SecureRandom implementation.
SecureRandom rng1 = new SecureRandom();
if (!LinuxPRNGSecureRandomProvider.class.equals(
rng1.getProvider().getClass())) {
throw new SecurityException(
"new SecureRandom() backed by wrong Provider: "
+ rng1.getProvider().getClass());
}
SecureRandom rng2;
try {
rng2 = SecureRandom.getInstance("SHA1PRNG");
} catch (NoSuchAlgorithmException e) {
throw new SecurityException("SHA1PRNG not available", e);
}
if (!LinuxPRNGSecureRandomProvider.class.equals(
rng2.getProvider().getClass())) {
throw new SecurityException(
"SecureRandom.getInstance(\"SHA1PRNG\") backed by wrong"
+ " Provider: " + rng2.getProvider().getClass());
}
}
/**
* {@code Provider} of {@code SecureRandom} engines which pass through
* all requests to the Linux PRNG.
*/
private static class LinuxPRNGSecureRandomProvider extends Provider {
public LinuxPRNGSecureRandomProvider() {
super("LinuxPRNG",
1.0,
"A Linux-specific random number provider that uses"
+ " /dev/urandom");
// Although /dev/urandom is not a SHA-1 PRNG, some apps
// explicitly request a SHA1PRNG SecureRandom and we thus need to
// prevent them from getting the default implementation whose output
// may have low entropy.
put("SecureRandom.SHA1PRNG", LinuxPRNGSecureRandom.class.getName());
put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
}
}
/**
* {@link SecureRandomSpi} which passes all requests to the Linux PRNG
* ({@code /dev/urandom}).
*/
public static class LinuxPRNGSecureRandom extends SecureRandomSpi {
/*
* IMPLEMENTATION NOTE: Requests to generate bytes and to mix in a seed
* are passed through to the Linux PRNG (/dev/urandom). Instances of
* this class seed themselves by mixing in the current time, PID, UID,
* build fingerprint, and hardware serial number (where available) into
* Linux PRNG.
*
* Concurrency: Read requests to the underlying Linux PRNG are
* serialized (on sLock) to ensure that multiple threads do not get
* duplicated PRNG output.
*/
private static final File URANDOM_FILE = new File("/dev/urandom");
private static final Object sLock = new Object();
/**
* Input stream for reading from Linux PRNG or {@code null} if not yet
* opened.
*
* @GuardedBy("sLock")
*/
private static DataInputStream sUrandomIn;
/**
* Output stream for writing to Linux PRNG or {@code null} if not yet
* opened.
*
* @GuardedBy("sLock")
*/
private static OutputStream sUrandomOut;
/**
* Whether this engine instance has been seeded. This is needed because
* each instance needs to seed itself if the client does not explicitly
* seed it.
*/
private boolean mSeeded;
@Override
protected void engineSetSeed(byte[] bytes) {
try {
OutputStream out;
synchronized (sLock) {
out = getUrandomOutputStream();
}
out.write(bytes);
out.flush();
} catch (IOException e) {
// On a small fraction of devices /dev/urandom is not writable.
// Log and ignore.
Log.w(PRNGFixes.class.getSimpleName(),
"Failed to mix seed into " + URANDOM_FILE);
} finally {
mSeeded = true;
}
}
@Override
protected void engineNextBytes(byte[] bytes) {
if (!mSeeded) {
// Mix in the device- and invocation-specific seed.
engineSetSeed(generateSeed());
}
try {
DataInputStream in;
synchronized (sLock) {
in = getUrandomInputStream();
}
synchronized (in) {
in.readFully(bytes);
}
} catch (IOException e) {
throw new SecurityException(
"Failed to read from " + URANDOM_FILE, e);
}
}
@Override
protected byte[] engineGenerateSeed(int size) {
byte[] seed = new byte[size];
engineNextBytes(seed);
return seed;
}
private DataInputStream getUrandomInputStream() {
synchronized (sLock) {
if (sUrandomIn == null) {
// NOTE: Consider inserting a BufferedInputStream between
// DataInputStream and FileInputStream if you need higher
// PRNG output performance and can live with future PRNG
// output being pulled into this process prematurely.
try {
sUrandomIn = new DataInputStream(
new FileInputStream(URANDOM_FILE));
} catch (IOException e) {
throw new SecurityException("Failed to open "
+ URANDOM_FILE + " for reading", e);
}
}
return sUrandomIn;
}
}
private OutputStream getUrandomOutputStream() throws IOException {
synchronized (sLock) {
if (sUrandomOut == null) {
sUrandomOut = new FileOutputStream(URANDOM_FILE);
}
return sUrandomOut;
}
}
}
/**
* Generates a device- and invocation-specific seed to be mixed into the
* Linux PRNG.
*/
private static byte[] generateSeed() {
try {
ByteArrayOutputStream seedBuffer = new ByteArrayOutputStream();
DataOutputStream seedBufferOut =
new DataOutputStream(seedBuffer);
seedBufferOut.writeLong(System.currentTimeMillis());
seedBufferOut.writeLong(System.nanoTime());
seedBufferOut.writeInt(Process.myPid());
seedBufferOut.writeInt(Process.myUid());
seedBufferOut.write(BUILD_FINGERPRINT_AND_DEVICE_SERIAL);
seedBufferOut.close();
return seedBuffer.toByteArray();
} catch (IOException e) {
throw new SecurityException("Failed to generate seed", e);
}
}
/**
* Gets the hardware serial number of this device.
*
* @return serial number or {@code null} if not available.
*/
private static String getDeviceSerialNumber() {
// We're using the Reflection API because Build.SERIAL is only available
// since API Level 9 (Gingerbread, Android 2.3).
try {
return (String) Build.class.getField("SERIAL").get(null);
} catch (Exception ignored) {
return null;
}
}
private static byte[] getBuildFingerprintAndDeviceSerial() {
StringBuilder result = new StringBuilder();
String fingerprint = Build.FINGERPRINT;
if (fingerprint != null) {
result.append(fingerprint);
}
String serial = getDeviceSerialNumber();
if (serial != null) {
result.append(serial);
}
try {
return result.toString().getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("UTF-8 encoding not supported");
}
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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 &lt;= n &lt; 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);
}
}

View file

@ -0,0 +1,124 @@
package com.zeapo.pwdstore;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.TextView;
import com.zeapo.pwdstore.pwgen.pwgen;
import java.util.ArrayList;
/**
* A placeholder fragment containing a simple view.
*/
public class pwgenDialogFragment extends DialogFragment {
public pwgenDialogFragment() {
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
final View view = inflater.inflate(R.layout.fragment_pwgen, null);
builder.setView(view);
SharedPreferences prefs
= getActivity().getApplicationContext().getSharedPreferences("pwgen", Context.MODE_PRIVATE);
CheckBox checkBox = (CheckBox) view.findViewById(R.id.numerals);
checkBox.setChecked(!prefs.getBoolean("0", false));
checkBox = (CheckBox) view.findViewById(R.id.symbols);
checkBox.setChecked(prefs.getBoolean("y", false));
checkBox = (CheckBox) view.findViewById(R.id.uppercase);
checkBox.setChecked(!prefs.getBoolean("A", false));
checkBox = (CheckBox) view.findViewById(R.id.ambiguous);
checkBox.setChecked(!prefs.getBoolean("B", false));
checkBox = (CheckBox) view.findViewById(R.id.pronounceable);
checkBox.setChecked(!prefs.getBoolean("s", true));
TextView textView = (TextView) view.findViewById(R.id.lengthNumber);
textView.setText(Integer.toString(prefs.getInt("length", 20)));
builder.setPositiveButton(getResources().getString(R.string.dialog_ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
TextView edit = (TextView) pwgenDialogFragment.this.getActivity().findViewById(R.id.crypto_password_edit);
TextView generate = (TextView) pwgenDialogFragment.this.getDialog().findViewById(R.id.passwordText);
edit.append(generate.getText());
}
});
builder.setNegativeButton(getResources().getString(R.string.dialog_cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.setNeutralButton(getResources().getString(R.string.pwgen_generate), null);
final AlertDialog ad = builder.setTitle("Generate Password").create();
ad.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
setPreferences();
TextView textView = (TextView) view.findViewById(R.id.passwordText);
textView.setText(pwgen.generate(getActivity().getApplicationContext()).get(0));
Button b = ad.getButton(AlertDialog.BUTTON_NEUTRAL);
b.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setPreferences();
TextView textView = (TextView) getDialog().findViewById(R.id.passwordText);
textView.setText(pwgen.generate(getActivity().getApplicationContext()).get(0));
}
});
}
});
return ad;
}
private boolean setPreferences () {
ArrayList<String> preferences = new ArrayList<>();
if (!((CheckBox) getDialog().findViewById(R.id.numerals)).isChecked()) {
preferences.add("0");
}
if (((CheckBox) getDialog().findViewById(R.id.symbols)).isChecked()) {
preferences.add("y");
}
if (!((CheckBox) getDialog().findViewById(R.id.uppercase)).isChecked()) {
preferences.add("A");
}
if (!((CheckBox) getDialog().findViewById(R.id.ambiguous)).isChecked()) {
preferences.add("B");
}
if (!((CheckBox) getDialog().findViewById(R.id.pronounceable)).isChecked()) {
preferences.add("s");
}
TextView textView = (TextView) getDialog().findViewById(R.id.lengthNumber);
try {
int length = Integer.valueOf(textView.getText().toString());
return pwgen.setPrefs(getActivity().getApplicationContext(), preferences, length);
} catch(NumberFormatException e) {
return pwgen.setPrefs(getActivity().getApplicationContext(), preferences);
}
}
}

View file

@ -64,7 +64,15 @@
android:id="@+id/crypto_password_edit" android:id="@+id/crypto_password_edit"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:typeface="monospace"/> android:typeface="monospace"
android:layout_weight="1"/>
<ImageButton
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="@drawable/ic_action_new"
android:background="@drawable/blue_rectangle"
android:id="@+id/generate_password"
android:onClick="handleClick"/>
</LinearLayout> </LinearLayout>
<TextView <TextView

View file

@ -0,0 +1,108 @@
<ScrollView
android:layout_height="match_parent"
android:layout_width="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivityFragment">
<EditText
android:id="@+id/passwordText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:editable="false"
android:textIsSelectable="true"
android:inputType="textVisiblePassword"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:weightSum="2">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/include"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/pwgen_include"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_marginBottom="8dp"/>
<CheckBox
android:id="@+id/numerals"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/pwgen_numerals"/>
<CheckBox
android:id="@+id/symbols"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/pwgen_symbols"/>
<CheckBox
android:id="@+id/uppercase"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/pwgen_uppercase"/>
<CheckBox
android:id="@+id/ambiguous"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/pwgen_ambiguous"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/length"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Length"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_marginBottom="8dp"
/>
<EditText
android:id="@+id/lengthNumber"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:ems="10"
android:inputType="number"
/>
<CheckBox
android:id="@+id/pronounceable"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Pronounceable"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>

View file

@ -114,6 +114,14 @@
<string name="pref_recursive_filter">Recursive filtering</string> <string name="pref_recursive_filter">Recursive filtering</string>
<string name="pref_recursive_filter_hint">Recursively find passwords of the current directory.</string> <string name="pref_recursive_filter_hint">Recursively find passwords of the current directory.</string>
<!-- pwgen fragment -->
<string name="pwgen_generate">Generate</string>
<string name="pwgen_include">Include</string>
<string name="pwgen_numerals">Numerals</string>
<string name="pwgen_symbols">Symbols</string>
<string name="pwgen_uppercase">Uppercase</string>
<string name="pwgen_ambiguous">Ambiguous</string>
<!-- Misc --> <!-- Misc -->
<string name="dialog_ok">OK</string> <string name="dialog_ok">OK</string>
<string name="dialog_yes">Yes</string> <string name="dialog_yes">Yes</string>