Merge pull request #101 from wongma7/master
Adding support for password generation
This commit is contained in:
commit
62c20d323c
11 changed files with 1031 additions and 1 deletions
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
336
app/src/main/java/com/zeapo/pwdstore/pwgen/PRNGFixes.java
Normal file
336
app/src/main/java/com/zeapo/pwdstore/pwgen/PRNGFixes.java
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
206
app/src/main/java/com/zeapo/pwdstore/pwgen/pw_phonemes.java
Normal file
206
app/src/main/java/com/zeapo/pwdstore/pwgen/pw_phonemes.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
69
app/src/main/java/com/zeapo/pwdstore/pwgen/pw_rand.java
Normal file
69
app/src/main/java/com/zeapo/pwdstore/pwgen/pw_rand.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
138
app/src/main/java/com/zeapo/pwdstore/pwgen/pwgen.java
Normal file
138
app/src/main/java/com/zeapo/pwdstore/pwgen/pwgen.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
26
app/src/main/java/com/zeapo/pwdstore/pwgen/randnum.java
Normal file
26
app/src/main/java/com/zeapo/pwdstore/pwgen/randnum.java
Normal 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 <= 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);
|
||||||
|
}
|
||||||
|
}
|
124
app/src/main/java/com/zeapo/pwdstore/pwgenDialogFragment.java
Normal file
124
app/src/main/java/com/zeapo/pwdstore/pwgenDialogFragment.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
108
app/src/main/res/layout/fragment_pwgen.xml
Normal file
108
app/src/main/res/layout/fragment_pwgen.xml
Normal 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>
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue