diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPElementType.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPElementType.java
index 213cf704..aa0cf4ac 100644
--- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPElementType.java
+++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPElementType.java
@@ -80,11 +80,9 @@ public enum MPElementType {
*/
public static MPElementType forName(final String name) {
- for (final MPElementType type : values()) {
- if (type.getName().equalsIgnoreCase( name ) || type.getShortName().equalsIgnoreCase( name )) {
+ for (final MPElementType type : values())
+ if (type.getName().equalsIgnoreCase( name ) || type.getShortName().equalsIgnoreCase( name ))
return type;
- }
- }
throw logger.bug( "Element type not known: %s", name );
}
diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterPassword.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java
similarity index 63%
rename from MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterPassword.java
rename to MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java
index b1df9ce3..500776ef 100644
--- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterPassword.java
+++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java
@@ -2,6 +2,8 @@ package com.lyndir.masterpassword;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
+import com.google.common.io.CharSource;
+import com.google.common.io.CharStreams;
import com.google.common.primitives.Bytes;
import com.lambdaworks.crypto.SCrypt;
import com.lyndir.lhunath.opal.crypto.CryptUtils;
@@ -11,18 +13,17 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
+import java.util.Arrays;
+import javax.xml.stream.events.Characters;
/**
- * Implementation of the Master Password algorithm.
- *
- * 07 04, 2012
- *
- * @author lhunath
+ * @author lhunath, 2014-08-30
*/
-public abstract class MasterPassword {
+public class MasterKey {
- static final Logger logger = Logger.get( MasterPassword.class );
+ @SuppressWarnings("UnusedDeclaration")
+ private static final Logger logger = Logger.get( MasterKey.class );
private static final int MP_N = 32768;
private static final int MP_r = 8;
private static final int MP_p = 2;
@@ -33,52 +34,60 @@ public abstract class MasterPassword {
private static final MessageAuthenticationDigests MP_mac = MessageAuthenticationDigests.HmacSHA256;
private static final MPTemplates templates = MPTemplates.load();
- public static byte[] keyForPassword(final String password, final String username) {
+ private final String userName;
+ private final byte[] key;
+
+ private boolean valid;
+
+ public MasterKey(final String userName, final String masterPassword) {
+
+ this.userName = userName;
long start = System.currentTimeMillis();
- byte[] nusernameLengthBytes = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE )
- .order( MP_byteOrder )
- .putInt( username.length() )
- .array();
+ byte[] userNameLengthBytes = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE )
+ .order( MP_byteOrder )
+ .putInt( userName.length() )
+ .array();
byte[] salt = Bytes.concat( "com.lyndir.masterpassword".getBytes( MP_charset ), //
- nusernameLengthBytes, //
- username.getBytes( MP_charset ) );
+ userNameLengthBytes, userName.getBytes( MP_charset ) );
try {
- byte[] key = SCrypt.scrypt( password.getBytes( MP_charset ), salt, MP_N, MP_r, MP_p, MP_dkLen );
- logger.trc( "User: %s, password: %s derives to key ID: %s (took %.2fs)", username, password,
- CodeUtils.encodeHex( keyIDForKey( key ) ), (double) (System.currentTimeMillis() - start) / 1000 );
+ key = SCrypt.scrypt( masterPassword.getBytes( MP_charset ), salt, MP_N, MP_r, MP_p, MP_dkLen );
+ valid = true;
- return key;
+ logger.trc( "User: %s, master password derives to key ID: %s (took %.2fs)", //
+ userName, getKeyID(), (double) (System.currentTimeMillis() - start) / 1000 );
}
catch (GeneralSecurityException e) {
throw logger.bug( e );
}
}
- public static byte[] subkeyForKey(final byte[] key, final int subkeyLength) {
+ public String getUserName() {
+ return userName;
+ }
+
+ public String getKeyID() {
+
+ Preconditions.checkState( valid );
+ return CodeUtils.encodeHex( MP_hash.of( key ) );
+ }
+
+ private byte[] getSubkey(final int subkeyLength) {
+
+ Preconditions.checkState( valid );
byte[] subkey = new byte[Math.min( subkeyLength, key.length )];
System.arraycopy( key, 0, subkey, 0, subkey.length );
return subkey;
}
- public static byte[] keyIDForPassword(final String password, final String username) {
-
- return keyIDForKey( keyForPassword( password, username ) );
- }
-
- public static byte[] keyIDForKey(final byte[] key) {
-
- return MP_hash.of( key );
- }
-
- public static String generateContent(final MPElementType type, final String name, final byte[] key, int counter) {
+ public String encode(final String name, final MPElementType type, int counter) {
+ Preconditions.checkState( valid );
Preconditions.checkArgument( type.getTypeClass() == MPElementTypeClass.Generated );
Preconditions.checkArgument( !name.isEmpty() );
- Preconditions.checkArgument( key.length > 0 );
if (counter == 0)
counter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
@@ -112,17 +121,9 @@ public abstract class MasterPassword {
return password.toString();
}
- public static void main(final String... arguments) {
+ public void invalidate() {
- String masterPassword = "test-mp";
- String username = "test-user";
- String siteName = "test-site";
- MPElementType siteType = MPElementType.GeneratedLong;
- int siteCounter = 42;
-
- String sitePassword = generateContent( siteType, siteName, keyForPassword( masterPassword, username ), siteCounter );
-
- logger.inf( "master password: %s, username: %s\nsite name: %s, site type: %s, site counter: %d\n => site password: %s",
- masterPassword, username, siteName, siteType, siteCounter, sitePassword );
+ valid = false;
+ Arrays.fill( key, (byte) 0 );
}
}
diff --git a/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java b/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java
index 571ed493..8376489f 100644
--- a/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java
+++ b/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java
@@ -16,7 +16,6 @@ import com.google.common.base.Throwables;
import com.google.common.util.concurrent.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.concurrent.*;
-import java.util.prefs.Preferences;
public class EmergencyActivity extends Activity {
@@ -38,7 +37,7 @@ public class EmergencyActivity extends Activity {
}
};
- private ListenableFuture masterKeyFuture;
+ private ListenableFuture masterKeyFuture;
@InjectView(R.id.progressView)
ProgressBar progressView;
@@ -142,16 +141,12 @@ public class EmergencyActivity extends Activity {
}
progressView.setVisibility( View.VISIBLE );
- (masterKeyFuture = executor.submit( new Callable() {
+ (masterKeyFuture = executor.submit( new Callable() {
@Override
- public byte[] call()
+ public MasterKey call()
throws Exception {
try {
- long start = System.currentTimeMillis();
- byte[] masterKey = MasterPassword.keyForPassword( masterPassword, userName );
- logger.inf( "masterKey time: %d", System.currentTimeMillis() - start );
-
- return masterKey;
+ return new MasterKey( userName, masterPassword );
}
catch (RuntimeException e) {
sitePasswordField.setText( "" );
@@ -189,9 +184,7 @@ public class EmergencyActivity extends Activity {
@Override
public void run() {
try {
- long start = System.currentTimeMillis();
- final String sitePassword = MasterPassword.generateContent( type, siteName, masterKeyFuture.get(), counter );
- logger.inf( "sitePassword time: %d", System.currentTimeMillis() - start );
+ final String sitePassword = masterKeyFuture.get().encode( siteName, type, counter );
runOnUiThread( new Runnable() {
@Override
diff --git a/MasterPassword/Java/masterpassword-cli/src/main/java/com/lyndir/masterpassword/CLI.java b/MasterPassword/Java/masterpassword-cli/src/main/java/com/lyndir/masterpassword/CLI.java
index 31042c62..351a5112 100644
--- a/MasterPassword/Java/masterpassword-cli/src/main/java/com/lyndir/masterpassword/CLI.java
+++ b/MasterPassword/Java/masterpassword-cli/src/main/java/com/lyndir/masterpassword/CLI.java
@@ -13,13 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+
package com.lyndir.masterpassword;
+import static com.lyndir.lhunath.opal.system.util.ObjectUtils.ifNotNullElse;
+
import com.google.common.io.LineReader;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
-import java.io.IOException;
-import java.io.InputStreamReader;
+import java.io.*;
import java.util.Arrays;
@@ -30,22 +33,24 @@ import java.util.Arrays;
*/
public class CLI {
- static final Logger logger = Logger.get( CLI.class );
- private static final String ENV_USERNAME = "MP_USERNAME";
- private static final String ENV_PASSWORD = "MP_PASSWORD";
+ private static final String ENV_USERNAME = "MP_USERNAME";
+ private static final String ENV_PASSWORD = "MP_PASSWORD";
+ private static final String ENV_SITETYPE = "MP_SITETYPE";
+ private static final String ENV_SITECOUNTER = "MP_SITECOUNTER";
public static void main(final String[] args)
throws IOException {
- String userName, masterPassword, siteName = null;
+ // Read information from the environment.
+ String siteName = null;
+ String userName = System.getenv().get( ENV_USERNAME );
+ String masterPassword = System.getenv().get( ENV_PASSWORD );
+ String siteTypeName = ifNotNullElse( System.getenv().get( ENV_SITETYPE ), "" );
+ MPElementType siteType = siteTypeName.isEmpty()? MPElementType.GeneratedLong: MPElementType.forName( siteTypeName );
+ String siteCounterName = ifNotNullElse( System.getenv().get( ENV_SITECOUNTER ), "" );
+ int siteCounter = siteCounterName.isEmpty()? 1: Integer.parseInt( siteCounterName );
- /* Environment. */
- userName = System.getenv().get( ENV_USERNAME );
- masterPassword = System.getenv().get( ENV_PASSWORD );
-
- /* Arguments. */
- int counter = 1;
- MPElementType type = MPElementType.GeneratedLong;
+ // Parse information from option arguments.
boolean typeArg = false, counterArg = false, userNameArg = false;
for (final String arg : Arrays.asList( args ))
if ("-t".equals( arg ) || "--type".equals( arg ))
@@ -58,12 +63,12 @@ public class CLI {
System.exit( 0 );
}
- type = MPElementType.forName( arg );
+ siteType = MPElementType.forName( arg );
typeArg = false;
} else if ("-c".equals( arg ) || "--counter".equals( arg ))
counterArg = true;
else if (counterArg) {
- counter = ConversionUtils.toIntegerNN( arg );
+ siteCounter = ConversionUtils.toIntegerNN( arg );
counterArg = false;
} else if ("-u".equals( arg ) || "--username".equals( arg ))
userNameArg = true;
@@ -80,12 +85,12 @@ public class CLI {
System.out.println( "Available options:" );
System.out.println( "\t-t | --type [site password type]" );
- System.out.format( "\t\tDefault: %s. The password type to use for this site.\n", type.getName() );
+ System.out.format( "\t\tDefault: %s. The password type to use for this site.\n", siteType.getName() );
System.out.println( "\t\tUse 'list' to see the available types." );
System.out.println();
System.out.println( "\t-c | --counter [site counter]" );
- System.out.format( "\t\tDefault: %d. The counter to use for this site.\n", counter );
+ System.out.format( "\t\tDefault: %d. The counter to use for this site.\n", siteCounter );
System.out.println( "\t\tIncrement the counter if you need a new password." );
System.out.println();
@@ -106,28 +111,33 @@ public class CLI {
} else
siteName = arg;
- InputStreamReader inReader = new InputStreamReader( System.in );
- try {
+ // Read missing information from the console.
+ Console console = System.console();
+ try (InputStreamReader inReader = new InputStreamReader( System.in )) {
LineReader lineReader = new LineReader( inReader );
+
if (siteName == null) {
System.err.format( "Site name: " );
siteName = lineReader.readLine();
}
+
if (userName == null) {
System.err.format( "User's name: " );
userName = lineReader.readLine();
}
- if (masterPassword == null) {
- System.err.format( "%s's master password: ", userName );
- masterPassword = lineReader.readLine();
- }
- byte[] masterKey = MasterPassword.keyForPassword( masterPassword, userName );
- String sitePassword = MasterPassword.generateContent( type, siteName, masterKey, counter );
- System.out.println( sitePassword );
- }
- finally {
- inReader.close();
+ if (masterPassword == null) {
+ if (console != null)
+ masterPassword = new String( console.readPassword( "%s's master password: ", userName ) );
+
+ else {
+ System.err.format( "%s's master password: ", userName );
+ masterPassword = lineReader.readLine();
+ }
+ }
}
+
+ // Encode and write out the site password.
+ System.out.println( new MasterKey( userName, masterPassword ).encode( siteName, siteType, siteCounter ) );
}
}
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/ConfigAuthenticationPanel.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/ConfigAuthenticationPanel.java
index a5f3cf71..688a6b52 100644
--- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/ConfigAuthenticationPanel.java
+++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/ConfigAuthenticationPanel.java
@@ -89,7 +89,7 @@ public class ConfigAuthenticationPanel extends AuthenticationPanel implements It
return selectedUser;
}
- return new User( selectedUser.getName(), new String( masterPasswordField.getPassword() ) );
+ return new User( selectedUser.getUserName(), new String( masterPasswordField.getPassword() ) );
}
public String getHelpText() {
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/PasswordFrame.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/PasswordFrame.java
index a1554ef9..65f6b85d 100644
--- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/PasswordFrame.java
+++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/PasswordFrame.java
@@ -21,7 +21,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
private final JTextField siteNameField;
private final JComboBox siteTypeField;
private final JSpinner siteCounterField;
- private final JLabel passwordLabel;
+ private final JTextField passwordField;
private final JLabel tipLabel;
public PasswordFrame(User user)
@@ -38,7 +38,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
} );
// User
- add( label = new JLabel( strf( "Generating passwords for: %s", user.getName() ) ), BorderLayout.NORTH );
+ add( label = new JLabel( strf( "Generating passwords for: %s", user.getUserName() ) ), BorderLayout.NORTH );
label.setFont( Res.exoRegular().deriveFont( 12f ) );
label.setAlignmentX( LEFT_ALIGNMENT );
@@ -74,6 +74,9 @@ public class PasswordFrame extends JFrame implements DocumentListener {
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
+ passwordField.setText( null );
+ siteNameField.setText( null );
+
if (getDefaultCloseOperation() == WindowConstants.EXIT_ON_CLOSE)
System.exit( 0 );
else
@@ -120,16 +123,18 @@ public class PasswordFrame extends JFrame implements DocumentListener {
} );
// Password
- passwordLabel = new JLabel( " ", JLabel.CENTER );
- passwordLabel.setFont( Res.sourceCodeProBlack().deriveFont( 40f ) );
- passwordLabel.setAlignmentX( Component.CENTER_ALIGNMENT );
+ passwordField = new JTextField( " " );
+ passwordField.setFont( Res.sourceCodeProBlack().deriveFont( 40f ) );
+ passwordField.setHorizontalAlignment( JTextField.CENTER );
+ passwordField.setAlignmentX( Component.CENTER_ALIGNMENT );
+ passwordField.setEditable( false );
// Tip
tipLabel = new JLabel( " ", JLabel.CENTER );
tipLabel.setFont( Res.exoThin().deriveFont( 9f ) );
tipLabel.setAlignmentX( Component.CENTER_ALIGNMENT );
- add( Components.boxLayout( BoxLayout.PAGE_AXIS, passwordLabel, tipLabel ), BorderLayout.SOUTH );
+ add( Components.boxLayout( BoxLayout.PAGE_AXIS, passwordField, tipLabel ), BorderLayout.SOUTH );
pack();
setMinimumSize( getSize() );
@@ -146,7 +151,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
final int siteCounter = (Integer) siteCounterField.getValue();
if (siteType.getTypeClass() != MPElementTypeClass.Generated || siteName == null || siteName.isEmpty() || !user.hasKey()) {
- passwordLabel.setText( null );
+ passwordField.setText( null );
tipLabel.setText( null );
return;
}
@@ -154,14 +159,17 @@ public class PasswordFrame extends JFrame implements DocumentListener {
Res.execute( new Runnable() {
@Override
public void run() {
- final String sitePassword = MasterPassword.generateContent( siteType, siteName, user.getKey(), siteCounter );
+ final String sitePassword = user.getKey().encode( siteName, siteType, siteCounter );
if (callback != null)
callback.passwordGenerated( siteName, sitePassword );
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
- passwordLabel.setText( sitePassword );
+ if (!siteName.equals( siteNameField.getText() ))
+ return;
+
+ passwordField.setText( sitePassword );
tipLabel.setText( "Press [Enter] to copy the password." );
}
} );
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/UnlockFrame.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/UnlockFrame.java
index 068b262c..4c3d796b 100644
--- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/UnlockFrame.java
+++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/UnlockFrame.java
@@ -126,7 +126,7 @@ public class UnlockFrame extends JFrame {
}
boolean checkSignIn() {
- boolean enabled = user != null && !user.getName().isEmpty() && user.hasKey();
+ boolean enabled = user != null && !user.getUserName().isEmpty() && user.hasKey();
signInButton.setEnabled( enabled );
return enabled;
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/User.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/User.java
index 3f6298a2..52a7e1ec 100644
--- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/User.java
+++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/User.java
@@ -8,29 +8,29 @@ import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
*/
public class User {
- private final String name;
- private final String masterPassword;
- private byte[] key;
+ private final String userName;
+ private final String masterPassword;
+ private MasterKey key;
- public User(final String name, final String masterPassword) {
- this.name = name;
+ public User(final String userName, final String masterPassword) {
+ this.userName = userName;
this.masterPassword = masterPassword;
}
- public String getName() {
- return name;
+ public String getUserName() {
+ return userName;
}
public boolean hasKey() {
return key != null || (masterPassword != null && !masterPassword.isEmpty());
}
- public byte[] getKey() {
+ public MasterKey getKey() {
if (key == null) {
if (!hasKey()) {
- throw new IllegalStateException( strf( "Master password unknown for user: %s", name ) );
+ throw new IllegalStateException( strf( "Master password unknown for user: %s", userName ) );
} else {
- key = MasterPassword.keyForPassword( masterPassword, name );
+ key = new MasterKey( userName, masterPassword );
}
}
@@ -39,11 +39,11 @@ public class User {
@Override
public int hashCode() {
- return name.hashCode();
+ return userName.hashCode();
}
@Override
public String toString() {
- return name;
+ return userName;
}
}