diff --git a/MasterPassword/Java/masterpassword-algorithm/pom.xml b/MasterPassword/Java/masterpassword-algorithm/pom.xml
index 2384312a..0ea494d3 100644
--- a/MasterPassword/Java/masterpassword-algorithm/pom.xml
+++ b/MasterPassword/Java/masterpassword-algorithm/pom.xml
@@ -24,12 +24,12 @@
com.lyndir.lhunath.opal
opal-system
- 1.6-p7
+ 1.6-p8
com.lyndir.lhunath.opal
opal-crypto
- 1.6-p7
+ 1.6-p8
diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPSiteType.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPSiteType.java
index a2c877cb..f78d2a44 100644
--- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPSiteType.java
+++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPSiteType.java
@@ -143,7 +143,7 @@ public enum MPSiteType {
}
/**
- * @param name The name of the type to look up. It is matched case insensitively.
+ * @param name The name fromInt the type to look up. It is matched case insensitively.
*
* @return The type registered with the given name.
*/
diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPSiteVariant.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPSiteVariant.java
index 68f38d35..f87f32cd 100644
--- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPSiteVariant.java
+++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPSiteVariant.java
@@ -61,7 +61,7 @@ public enum MPSiteVariant {
throw logger.bug( "No variant for option: %s", option );
}
/**
- * @param name The name of the variant to look up. It is matched case insensitively.
+ * @param name The name fromInt the variant to look up. It is matched case insensitively.
*
* @return The variant registered with the given name.
*/
diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java
index 34710ad9..981fc590 100644
--- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java
+++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java
@@ -1,145 +1,155 @@
package com.lyndir.masterpassword;
-import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
-import com.google.common.primitives.Bytes;
-import com.lambdaworks.crypto.SCrypt;
import com.lyndir.lhunath.opal.system.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.charset.Charset;
-import java.security.GeneralSecurityException;
import java.util.Arrays;
+import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import org.jetbrains.annotations.NotNull;
/**
* @author lhunath, 2014-08-30
*/
-public class MasterKey {
-
- public static final int ALGORITHM = 1;
- public static final String VERSION = "2.1";
+public abstract class MasterKey {
@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;
- private static final int MP_dkLen = 64;
- private static final int MP_intLen = 32;
- private static final Charset MP_charset = Charsets.UTF_8;
- private static final ByteOrder MP_byteOrder = ByteOrder.BIG_ENDIAN;
- private static final MessageDigests MP_hash = MessageDigests.SHA256;
- private static final MessageAuthenticationDigests MP_mac = MessageAuthenticationDigests.HmacSHA256;
+ private static final Logger logger = Logger.get( MasterKey.class );
+ @Nonnull
private final String fullName;
- private final byte[] masterKey;
- private boolean valid;
+ @Nullable
+ private byte[] masterKey;
- public MasterKey(final String fullName, final String masterPassword) {
+ public static MasterKey create(final String fullName, final String masterPassword) {
+
+ return create( Version.CURRENT, fullName, masterPassword );
+ }
+
+ public static MasterKey create(Version version, final String fullName, final String masterPassword) {
+
+ switch (version) {
+ case V0:
+ return new MasterKeyV0( fullName ).revalidate( masterPassword );
+ case V1:
+ return new MasterKeyV1( fullName ).revalidate( masterPassword );
+ case V2:
+ return new MasterKeyV2( fullName ).revalidate( masterPassword );
+ case V3:
+ return new MasterKeyV3( fullName ).revalidate( masterPassword );
+ }
+
+ throw new UnsupportedOperationException( "Unsupported version: " + version );
+ }
+
+ protected MasterKey(@NotNull final String fullName) {
this.fullName = fullName;
logger.trc( "fullName: %s", fullName );
- logger.trc( "masterPassword: %s", masterPassword );
-
- long start = System.currentTimeMillis();
- byte[] userNameBytes = fullName.getBytes( MP_charset );
- byte[] userNameLengthBytes = bytesForInt( userNameBytes.length );
-
- String mpKeyScope = MPSiteVariant.Password.getScope();
- byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MP_charset ), userNameLengthBytes, userNameBytes );
- logger.trc( "key scope: %s", mpKeyScope );
- logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
-
- try {
- masterKey = SCrypt.scrypt( masterPassword.getBytes( MP_charset ), masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen );
- valid = true;
-
- logger.trc( "masterKey ID: %s (derived in %.2fs)", CodeUtils.encodeHex( idForBytes( masterKey ) ),
- (System.currentTimeMillis() - start) / 1000D );
- }
- catch (GeneralSecurityException e) {
- throw logger.bug( e );
- }
}
+ @Nullable
+ protected abstract byte[] deriveKey(final String masterPassword);
+
+ protected abstract Version getAlgorithm();
+
+ @NotNull
public String getFullName() {
return fullName;
}
+ @Nonnull
+ protected byte[] getMasterKey() {
+
+ return Preconditions.checkNotNull( masterKey );
+ }
+
public byte[] getKeyID() {
- Preconditions.checkState( valid );
- return idForBytes( masterKey );
+ return idForBytes( getMasterKey() );
}
- public String encode(final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant,
- @Nullable final String siteContext) {
- Preconditions.checkState( valid );
- Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
- Preconditions.checkArgument( !siteName.isEmpty() );
-
- logger.trc( "siteName: %s", siteName );
- logger.trc( "siteCounter: %d", siteCounter );
- logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
- logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
-
- if (siteCounter == 0)
- siteCounter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
-
- String siteScope = siteVariant.getScope();
- byte[] siteNameBytes = siteName.getBytes( MP_charset );
- byte[] siteNameLengthBytes = bytesForInt( siteNameBytes.length );
- byte[] siteCounterBytes = bytesForInt( siteCounter );
- byte[] siteContextBytes = siteContext == null? null: siteContext.getBytes( MP_charset );
- byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
- logger.trc( "site scope: %s, context: %s", siteScope, siteContext == null? "": siteContext );
- logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
- siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
- siteContext == null? "(null)": siteContext );
-
- byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
- if (siteContextBytes != null)
- sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
- logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
-
- byte[] sitePasswordSeed = MP_mac.of( masterKey, sitePasswordInfo );
- logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
-
- Preconditions.checkState( sitePasswordSeed.length > 0 );
- int templateIndex = sitePasswordSeed[0] & 0xFF; // Mask the integer's sign.
- MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
- logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
-
- StringBuilder password = new StringBuilder( template.length() );
- for (int i = 0; i < template.length(); ++i) {
- int characterIndex = sitePasswordSeed[i + 1] & 0xFF; // Mask the integer's sign.
- MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
- char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
- logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
- sitePasswordSeed[i + 1], passwordCharacter );
-
- password.append( passwordCharacter );
- }
-
- return password.toString();
- }
+ public abstract String encode(final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant,
+ @Nullable final String siteContext);
public void invalidate() {
- valid = false;
- Arrays.fill( masterKey, (byte) 0 );
+ if (masterKey != null) {
+ Arrays.fill( masterKey, (byte) 0 );
+ masterKey = null;
+ }
}
- private static byte[] bytesForInt(final int integer) {
- return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MP_byteOrder ).putInt( integer ).array();
+ public MasterKey revalidate(final String masterPassword) {
+ invalidate();
+
+ logger.trc( "masterPassword: %s", masterPassword );
+
+ long start = System.currentTimeMillis();
+ masterKey = deriveKey( masterPassword );
+ logger.trc( "masterKey ID: %s (derived in %.2fs)", CodeUtils.encodeHex( idForBytes( masterKey ) ),
+ (System.currentTimeMillis() - start) / 1000D );
+
+ return this;
}
- private static byte[] idForBytes(final byte[] bytes) {
- return MP_hash.of( bytes );
+ protected abstract byte[] bytesForInt(final int integer);
+
+ protected abstract byte[] idForBytes(final byte[] bytes);
+
+ public enum Version {
+ /**
+ * bugs:
+ * - does math with chars whose signedness was platform-dependent.
+ * - miscounted the byte-length fromInt multi-byte site names.
+ * - miscounted the byte-length fromInt multi-byte full names.
+ */
+ V0,
+ /**
+ * bugs:
+ * - miscounted the byte-length fromInt multi-byte site names.
+ * - miscounted the byte-length fromInt multi-byte full names.
+ */
+ V1,
+ /**
+ * bugs:
+ * - miscounted the byte-length fromInt multi-byte full names.
+ */
+ V2,
+ /**
+ * bugs:
+ * - no known issues.
+ */
+ V3;
+
+ public static final Version CURRENT = V3;
+
+ public static Version fromInt(final int algorithmVersion) {
+
+ return values()[algorithmVersion];
+ }
+
+ public int toInt() {
+
+ return ordinal();
+ }
+
+ public String toBundleVersion() {
+ switch (this) {
+ case V0:
+ return "1.0";
+ case V1:
+ return "2.0";
+ case V2:
+ return "2.1";
+ case V3:
+ return "2.2";
+ }
+
+ throw new UnsupportedOperationException( "Unsupported version: " + this );
+ }
}
}
diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV0.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV0.java
new file mode 100644
index 00000000..a55614df
--- /dev/null
+++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV0.java
@@ -0,0 +1,130 @@
+package com.lyndir.masterpassword;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Preconditions;
+import com.google.common.primitives.Bytes;
+import com.lambdaworks.crypto.SCrypt;
+import com.lyndir.lhunath.opal.system.*;
+import com.lyndir.lhunath.opal.system.logging.Logger;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.Charset;
+import java.security.GeneralSecurityException;
+import javax.annotation.Nullable;
+
+
+/**
+ * bugs:
+ * - does math with chars whose signedness was platform-dependent.
+ * - miscounted the byte-length fromInt multi-byte site names.
+ * - miscounted the byte-length fromInt multi-byte full names.
+ *
+ * @author lhunath, 2014-08-30
+ */
+public class MasterKeyV0 extends MasterKey {
+
+ @SuppressWarnings("UnusedDeclaration")
+ private static final Logger logger = Logger.get( MasterKeyV0.class );
+
+ protected final int MP_N = 32768;
+ protected final int MP_r = 8;
+ protected final int MP_p = 2;
+ protected final int MP_dkLen = 64;
+ protected final int MP_intLen = 32;
+ protected final Charset MP_charset = Charsets.UTF_8;
+ protected final ByteOrder MP_byteOrder = ByteOrder.BIG_ENDIAN;
+ protected final MessageDigests MP_hash = MessageDigests.SHA256;
+ protected final MessageAuthenticationDigests MP_mac = MessageAuthenticationDigests.HmacSHA256;
+
+ public MasterKeyV0(final String fullName) {
+ super( fullName );
+ }
+
+ @Override
+ protected Version getAlgorithm() {
+
+ return Version.V0;
+ }
+
+ @Nullable
+ @Override
+ protected byte[] deriveKey(final String masterPassword) {
+ String fullName = getFullName();
+ byte[] fullNameBytes = fullName.getBytes( MP_charset );
+ byte[] fullNameLengthBytes = bytesForInt( fullName.length() );
+
+ String mpKeyScope = MPSiteVariant.Password.getScope();
+ byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MP_charset ), fullNameLengthBytes, fullNameBytes );
+ logger.trc( "key scope: %s", mpKeyScope );
+ logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
+
+ try {
+ return SCrypt.scrypt( masterPassword.getBytes( MP_charset ), masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen );
+ }
+ catch (GeneralSecurityException e) {
+ logger.bug( e );
+ return null;
+ }
+ }
+
+ public String encode(final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant,
+ @Nullable final String siteContext) {
+ Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
+ Preconditions.checkArgument( !siteName.isEmpty() );
+
+ logger.trc( "siteName: %s", siteName );
+ logger.trc( "siteCounter: %d", siteCounter );
+ logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
+ logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
+
+ if (siteCounter == 0)
+ siteCounter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
+
+ String siteScope = siteVariant.getScope();
+ byte[] siteNameBytes = siteName.getBytes( MP_charset );
+ byte[] siteNameLengthBytes = bytesForInt( siteName.length() );
+ byte[] siteCounterBytes = bytesForInt( siteCounter );
+ byte[] siteContextBytes = siteContext == null? null: siteContext.getBytes( MP_charset );
+ byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
+ logger.trc( "site scope: %s, context: %s", siteScope, siteContext == null? "": siteContext );
+ logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
+ siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
+ siteContext == null? "(null)": siteContext );
+
+ byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
+ if (siteContextBytes != null)
+ sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
+ logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
+
+ byte[] sitePasswordSeed = MP_mac.of( getMasterKey(), sitePasswordInfo );
+ logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
+
+ Preconditions.checkState( sitePasswordSeed.length > 0 );
+ int templateIndex = sitePasswordSeed[0];
+ MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
+ logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
+
+ StringBuilder password = new StringBuilder( template.length() );
+ for (int i = 0; i < template.length(); ++i) {
+ int characterIndex = sitePasswordSeed[i + 1];
+ MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
+ char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
+ logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
+ sitePasswordSeed[i + 1], passwordCharacter );
+
+ password.append( passwordCharacter );
+ }
+
+ return password.toString();
+ }
+
+ @Override
+ protected byte[] bytesForInt(final int integer) {
+ return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MP_byteOrder ).putInt( integer ).array();
+ }
+
+ @Override
+ protected byte[] idForBytes(final byte[] bytes) {
+ return MP_hash.of( bytes );
+ }
+}
diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV1.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV1.java
new file mode 100644
index 00000000..6889863e
--- /dev/null
+++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV1.java
@@ -0,0 +1,88 @@
+package com.lyndir.masterpassword;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Preconditions;
+import com.google.common.primitives.Bytes;
+import com.lambdaworks.crypto.SCrypt;
+import com.lyndir.lhunath.opal.system.*;
+import com.lyndir.lhunath.opal.system.logging.Logger;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.Charset;
+import java.security.GeneralSecurityException;
+import javax.annotation.Nullable;
+
+
+/**
+ * bugs:
+ * - miscounted the byte-length fromInt multi-byte site names.
+ * - miscounted the byte-length fromInt multi-byte full names.
+ *
+ * @author lhunath, 2014-08-30
+ */
+public class MasterKeyV1 extends MasterKeyV0 {
+
+ @SuppressWarnings("UnusedDeclaration")
+ private static final Logger logger = Logger.get( MasterKeyV1.class );
+
+ public MasterKeyV1(final String fullName) {
+ super( fullName );
+ }
+
+ @Override
+ protected Version getAlgorithm() {
+
+ return Version.V1;
+ }
+
+ public String encode(final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant,
+ @Nullable final String siteContext) {
+ Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
+ Preconditions.checkArgument( !siteName.isEmpty() );
+
+ logger.trc( "siteName: %s", siteName );
+ logger.trc( "siteCounter: %d", siteCounter );
+ logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
+ logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
+
+ if (siteCounter == 0)
+ siteCounter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
+
+ String siteScope = siteVariant.getScope();
+ byte[] siteNameBytes = siteName.getBytes( MP_charset );
+ byte[] siteNameLengthBytes = bytesForInt( siteName.length() );
+ byte[] siteCounterBytes = bytesForInt( siteCounter );
+ byte[] siteContextBytes = siteContext == null? null: siteContext.getBytes( MP_charset );
+ byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
+ logger.trc( "site scope: %s, context: %s", siteScope, siteContext == null? "": siteContext );
+ logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
+ siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
+ siteContext == null? "(null)": siteContext );
+
+ byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
+ if (siteContextBytes != null)
+ sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
+ logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
+
+ byte[] sitePasswordSeed = MP_mac.of( getMasterKey(), sitePasswordInfo );
+ logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
+
+ Preconditions.checkState( sitePasswordSeed.length > 0 );
+ int templateIndex = sitePasswordSeed[0] & 0xFF; // Mask the integer's sign.
+ MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
+ logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
+
+ StringBuilder password = new StringBuilder( template.length() );
+ for (int i = 0; i < template.length(); ++i) {
+ int characterIndex = sitePasswordSeed[i + 1] & 0xFF; // Mask the integer's sign.
+ MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
+ char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
+ logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
+ sitePasswordSeed[i + 1], passwordCharacter );
+
+ password.append( passwordCharacter );
+ }
+
+ return password.toString();
+ }
+}
diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV2.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV2.java
new file mode 100644
index 00000000..6d414938
--- /dev/null
+++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV2.java
@@ -0,0 +1,81 @@
+package com.lyndir.masterpassword;
+
+import com.google.common.base.Preconditions;
+import com.google.common.primitives.Bytes;
+import com.lyndir.lhunath.opal.system.CodeUtils;
+import com.lyndir.lhunath.opal.system.logging.Logger;
+import javax.annotation.Nullable;
+
+
+/**
+ * bugs:
+ * - miscounted the byte-length fromInt multi-byte full names.
+ *
+ * @author lhunath, 2014-08-30
+ */
+public class MasterKeyV2 extends MasterKeyV0 {
+
+ @SuppressWarnings("UnusedDeclaration")
+ private static final Logger logger = Logger.get( MasterKeyV2.class );
+
+ public MasterKeyV2(final String fullName) {
+ super( fullName );
+ }
+
+ @Override
+ protected Version getAlgorithm() {
+
+ return Version.V2;
+ }
+
+ public String encode(final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant,
+ @Nullable final String siteContext) {
+ Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
+ Preconditions.checkArgument( !siteName.isEmpty() );
+
+ logger.trc( "siteName: %s", siteName );
+ logger.trc( "siteCounter: %d", siteCounter );
+ logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
+ logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
+
+ if (siteCounter == 0)
+ siteCounter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
+
+ String siteScope = siteVariant.getScope();
+ byte[] siteNameBytes = siteName.getBytes( MP_charset );
+ byte[] siteNameLengthBytes = bytesForInt( siteNameBytes.length );
+ byte[] siteCounterBytes = bytesForInt( siteCounter );
+ byte[] siteContextBytes = siteContext == null? null: siteContext.getBytes( MP_charset );
+ byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
+ logger.trc( "site scope: %s, context: %s", siteScope, siteContext == null? "": siteContext );
+ logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
+ siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
+ siteContext == null? "(null)": siteContext );
+
+ byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
+ if (siteContextBytes != null)
+ sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
+ logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
+
+ byte[] sitePasswordSeed = MP_mac.of( getMasterKey(), sitePasswordInfo );
+ logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
+
+ Preconditions.checkState( sitePasswordSeed.length > 0 );
+ int templateIndex = sitePasswordSeed[0] & 0xFF; // Mask the integer's sign.
+ MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
+ logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
+
+ StringBuilder password = new StringBuilder( template.length() );
+ for (int i = 0; i < template.length(); ++i) {
+ int characterIndex = sitePasswordSeed[i + 1] & 0xFF; // Mask the integer's sign.
+ MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
+ char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
+ logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
+ sitePasswordSeed[i + 1], passwordCharacter );
+
+ password.append( passwordCharacter );
+ }
+
+ return password.toString();
+ }
+}
diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV3.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV3.java
new file mode 100644
index 00000000..a4ec51df
--- /dev/null
+++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV3.java
@@ -0,0 +1,51 @@
+package com.lyndir.masterpassword;
+
+import com.google.common.primitives.Bytes;
+import com.lambdaworks.crypto.SCrypt;
+import com.lyndir.lhunath.opal.system.CodeUtils;
+import com.lyndir.lhunath.opal.system.logging.Logger;
+import java.security.GeneralSecurityException;
+import javax.annotation.Nullable;
+
+
+/**
+ * bugs:
+ * - no known issues.
+ *
+ * @author lhunath, 2014-08-30
+ */
+public class MasterKeyV3 extends MasterKeyV0 {
+
+ @SuppressWarnings("UnusedDeclaration")
+ private static final Logger logger = Logger.get( MasterKeyV3.class );
+
+ public MasterKeyV3(final String fullName) {
+ super( fullName );
+ }
+
+ @Override
+ protected Version getAlgorithm() {
+
+ return Version.V3;
+ }
+
+ @Nullable
+ @Override
+ protected byte[] deriveKey(final String masterPassword) {
+ byte[] fullNameBytes = getFullName().getBytes( MP_charset );
+ byte[] fullNameLengthBytes = bytesForInt( fullNameBytes.length );
+
+ String mpKeyScope = MPSiteVariant.Password.getScope();
+ byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MP_charset ), fullNameLengthBytes, fullNameBytes );
+ logger.trc( "key scope: %s", mpKeyScope );
+ logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
+
+ try {
+ return SCrypt.scrypt( masterPassword.getBytes( MP_charset ), masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen );
+ }
+ catch (GeneralSecurityException e) {
+ logger.bug( e );
+ return null;
+ }
+ }
+}
diff --git a/MasterPassword/Java/masterpassword-algorithm/src/test/java/com/lyndir/masterpassword/MasterKeyTest.java b/MasterPassword/Java/masterpassword-algorithm/src/test/java/com/lyndir/masterpassword/MasterKeyTest.java
index 523dc44b..6b04ac50 100644
--- a/MasterPassword/Java/masterpassword-algorithm/src/test/java/com/lyndir/masterpassword/MasterKeyTest.java
+++ b/MasterPassword/Java/masterpassword-algorithm/src/test/java/com/lyndir/masterpassword/MasterKeyTest.java
@@ -35,7 +35,7 @@ public class MasterKeyTest {
throws Exception {
for (MPWTests.Case testCase : tests.getCases()) {
- MasterKey masterKey = new MasterKey( testCase.getFullName(), testCase.getMasterPassword() );
+ MasterKey masterKey = MasterKey.create( testCase.getFullName(), testCase.getMasterPassword() );
assertEquals(
masterKey.encode( testCase.getSiteName(), testCase.getSiteType(), testCase.getSiteCounter(), testCase.getSiteVariant(),
testCase.getSiteContext() ), testCase.getResult(), "Failed test case: " + testCase );
@@ -46,7 +46,7 @@ public class MasterKeyTest {
public void testGetUserName()
throws Exception {
- assertEquals( new MasterKey( defaultCase.getFullName(), defaultCase.getMasterPassword() ).getFullName(),
+ assertEquals( MasterKey.create( defaultCase.getFullName(), defaultCase.getMasterPassword() ).getFullName(),
defaultCase.getFullName() );
}
@@ -55,7 +55,7 @@ public class MasterKeyTest {
throws Exception {
for (MPWTests.Case testCase : tests.getCases()) {
- MasterKey masterKey = new MasterKey( testCase.getFullName(), testCase.getMasterPassword() );
+ MasterKey masterKey = MasterKey.create( testCase.getFullName(), testCase.getMasterPassword() );
assertEquals( CodeUtils.encodeHex( masterKey.getKeyID() ), testCase.getKeyID(), "Failed test case: " + testCase );
}
}
@@ -65,7 +65,7 @@ public class MasterKeyTest {
throws Exception {
try {
- MasterKey masterKey = new MasterKey( defaultCase.getFullName(), defaultCase.getMasterPassword() );
+ MasterKey masterKey = MasterKey.create( defaultCase.getFullName(), defaultCase.getMasterPassword() );
masterKey.invalidate();
masterKey.encode( defaultCase.getSiteName(), defaultCase.getSiteType(), defaultCase.getSiteCounter(),
defaultCase.getSiteVariant(), defaultCase.getSiteContext() );
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 7da1ace1..02f6749f 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
@@ -159,7 +159,7 @@ public class EmergencyActivity extends Activity {
public MasterKey call()
throws Exception {
try {
- return new MasterKey( userName, masterPassword );
+ return MasterKey.create( userName, masterPassword );
}
catch (RuntimeException e) {
sitePasswordField.setText( "" );
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 5c9b04c0..20ad55d9 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
@@ -183,6 +183,6 @@ public class CLI {
}
// Encode and write out the site password.
- System.out.println( new MasterKey( userName, masterPassword ).encode( siteName, siteType, siteCounter, variant, context ) );
+ System.out.println( MasterKey.create( userName, masterPassword ).encode( siteName, siteType, siteCounter, variant, context ) );
}
}
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/GUI.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/GUI.java
index 258d81da..6be574c4 100644
--- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/GUI.java
+++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/GUI.java
@@ -48,14 +48,7 @@ public class GUI implements UnlockFrame.SignInCallback {
if (Config.get().checkForUpdates())
checkUpdate();
- GUI gui;
- try {
- gui = TypeUtils.newInstance( AppleGUI.class );
- }
- catch (NoClassDefFoundError e) {
- gui = new GUI();
- }
- gui.open();
+ TypeUtils.newInstance( AppleGUI.class ).or( new GUI() ).open();
}
private static void checkUpdate() {
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/IncognitoAuthenticationPanel.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/IncognitoAuthenticationPanel.java
index 27ac0975..bdb4dfa7 100644
--- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/IncognitoAuthenticationPanel.java
+++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/IncognitoAuthenticationPanel.java
@@ -1,5 +1,6 @@
package com.lyndir.masterpassword.gui;
+import com.lyndir.masterpassword.util.Components;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
@@ -20,39 +21,30 @@ public class IncognitoAuthenticationPanel extends AuthenticationPanel implements
// Full Name
super( unlockFrame );
- JLabel fullNameLabel = new JLabel( "Full Name:" );
- fullNameLabel.setFont( Res.exoRegular().deriveFont( 12f ) );
+ add( Components.stud() );
+
+ JLabel fullNameLabel = Components.label( "Full Name:" );
fullNameLabel.setAlignmentX( LEFT_ALIGNMENT );
fullNameLabel.setHorizontalAlignment( SwingConstants.CENTER );
fullNameLabel.setVerticalAlignment( SwingConstants.BOTTOM );
add( fullNameLabel );
- fullNameField = new JTextField() {
- @Override
- public Dimension getMaximumSize() {
- return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
- }
- };
- fullNameField.setFont( Res.sourceCodeProRegular().deriveFont( 12f ) );
+ fullNameField = Components.textField();
+ fullNameField.setFont( Res.valueFont().deriveFont( 12f ) );
fullNameField.setAlignmentX( LEFT_ALIGNMENT );
fullNameField.getDocument().addDocumentListener( this );
fullNameField.addActionListener( this );
add( fullNameField );
+ add( Components.stud() );
// Master Password
- JLabel masterPasswordLabel = new JLabel( "Master Password:" );
- masterPasswordLabel.setFont( Res.exoRegular().deriveFont( 12f ) );
+ JLabel masterPasswordLabel = Components.label( "Master Password:" );
masterPasswordLabel.setAlignmentX( LEFT_ALIGNMENT );
masterPasswordLabel.setHorizontalAlignment( SwingConstants.CENTER );
masterPasswordLabel.setVerticalAlignment( SwingConstants.BOTTOM );
add( masterPasswordLabel );
- masterPasswordField = new JPasswordField() {
- @Override
- public Dimension getMaximumSize() {
- return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
- }
- };
+ masterPasswordField = Components.passwordField();
masterPasswordField.setAlignmentX( LEFT_ALIGNMENT );
masterPasswordField.addActionListener( this );
masterPasswordField.getDocument().addDocumentListener( this );
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/IncognitoUser.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/IncognitoUser.java
index 6fd231d1..bd71ef8d 100644
--- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/IncognitoUser.java
+++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/IncognitoUser.java
@@ -1,6 +1,7 @@
package com.lyndir.masterpassword.gui;
import com.google.common.collect.ImmutableList;
+import com.lyndir.masterpassword.MasterKey;
/**
@@ -25,6 +26,11 @@ public class IncognitoUser extends User {
return masterPassword;
}
+ @Override
+ public MasterKey.Version getAlgorithmVersion() {
+ return MasterKey.Version.CURRENT;
+ }
+
@Override
public Iterable findSitesByName(final String siteName) {
return ImmutableList.of();
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelAuthenticationPanel.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelAuthenticationPanel.java
index 050f923c..4a92a8a1 100644
--- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelAuthenticationPanel.java
+++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelAuthenticationPanel.java
@@ -5,6 +5,7 @@ import com.google.common.collect.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.masterpassword.model.MPUser;
import com.lyndir.masterpassword.model.MPUserFileManager;
+import com.lyndir.masterpassword.util.Components;
import java.awt.*;
import java.awt.event.*;
import javax.annotation.Nullable;
@@ -27,6 +28,7 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
public ModelAuthenticationPanel(final UnlockFrame unlockFrame) {
super( unlockFrame );
+ add( Components.stud() );
// Avatar
avatarLabel.addMouseListener( new MouseAdapter() {
@@ -41,8 +43,7 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
} );
// User
- JLabel userLabel = new JLabel( "User:" );
- userLabel.setFont( Res.exoRegular().deriveFont( 12f ) );
+ JLabel userLabel = Components.label( "User:" );
userLabel.setAlignmentX( LEFT_ALIGNMENT );
userLabel.setHorizontalAlignment( SwingConstants.CENTER );
userLabel.setVerticalAlignment( SwingConstants.BOTTOM );
@@ -54,26 +55,21 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
}
};
- userField.setFont( Res.sourceCodeProRegular().deriveFont( 12f ) );
+ userField.setFont( Res.valueFont().deriveFont( 12f ) );
userField.setAlignmentX( LEFT_ALIGNMENT );
userField.addItemListener( this );
userField.addActionListener( this );
add( userField );
+ add( Components.stud() );
// Master Password
- masterPasswordLabel = new JLabel( "Master Password:" );
- masterPasswordLabel.setFont( Res.exoRegular().deriveFont( 12f ) );
+ masterPasswordLabel = Components.label( "Master Password:" );
masterPasswordLabel.setAlignmentX( LEFT_ALIGNMENT );
masterPasswordLabel.setHorizontalAlignment( SwingConstants.CENTER );
masterPasswordLabel.setVerticalAlignment( SwingConstants.BOTTOM );
add( masterPasswordLabel );
- masterPasswordField = new JPasswordField() {
- @Override
- public Dimension getMaximumSize() {
- return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
- }
- };
+ masterPasswordField = Components.passwordField();
masterPasswordField.setAlignmentX( LEFT_ALIGNMENT );
masterPasswordField.addActionListener( this );
masterPasswordField.getDocument().addDocumentListener( this );
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelUser.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelUser.java
index ce253eba..a2915b53 100644
--- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelUser.java
+++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelUser.java
@@ -37,6 +37,11 @@ public class ModelUser extends User {
return masterPassword;
}
+ @Override
+ public MasterKey.Version getAlgorithmVersion() {
+ return model.getAlgorithmVersion();
+ }
+
@Override
public int getAvatar() {
return model.getAvatar();
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/PasswordFrame.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/PasswordFrame.java
index 3123ebb0..667ab7f3 100644
--- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/PasswordFrame.java
+++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/PasswordFrame.java
@@ -12,7 +12,6 @@ import java.awt.event.*;
import java.util.concurrent.Callable;
import javax.annotation.Nonnull;
import javax.swing.*;
-import javax.swing.border.*;
import javax.swing.event.*;
@@ -44,52 +43,28 @@ public class PasswordFrame extends JFrame implements DocumentListener {
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
setContentPane( new JPanel( new BorderLayout( 20, 20 ) ) {
{
- setBorder( new EmptyBorder( 20, 20, 20, 20 ) );
+ setBackground( Res.colors().frameBg() );
+ setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
}
} );
// User
- add( label = new JLabel( strf( "Generating passwords for: %s", user.getFullName() ) ), BorderLayout.NORTH );
- label.setFont( Res.exoRegular().deriveFont( 12f ) );
+ add( label = Components.label( strf( "Generating passwords for: %s", user.getFullName() ) ), BorderLayout.NORTH );
label.setAlignmentX( LEFT_ALIGNMENT );
// Site
- JPanel sitePanel = new JPanel();
- sitePanel.setLayout( new BoxLayout( sitePanel, BoxLayout.PAGE_AXIS ) );
- sitePanel.setBorder( new CompoundBorder( new EtchedBorder( EtchedBorder.RAISED ), new EmptyBorder( 8, 8, 8, 8 ) ) );
- add( sitePanel, BorderLayout.CENTER );
+ JPanel sitePanel = Components.boxLayout( BoxLayout.PAGE_AXIS );
+ sitePanel.setBackground( Res.colors().controlBg() );
+ sitePanel.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
+ add( Components.bordered( sitePanel, BorderFactory.createRaisedBevelBorder(), Res.colors().frameBg() ), BorderLayout.CENTER );
// Site Name
- sitePanel.add( label = new JLabel( "Site Name:", JLabel.LEADING ) );
- label.setFont( Res.exoRegular().deriveFont( 12f ) );
+ sitePanel.add( label = Components.label( "Site Name:" ) );
label.setAlignmentX( LEFT_ALIGNMENT );
JComponent siteControls = Components.boxLayout( BoxLayout.LINE_AXIS, //
- siteNameField = new JTextField() {
- @Override
- public Dimension getMaximumSize() {
- return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
- }
- }, siteAddButton = new JButton( "Add Site" ) {
- @Override
- public Dimension getMaximumSize() {
- return new Dimension( 20, getPreferredSize().height );
- }
- } );
- siteAddButton.setVisible( false );
- siteAddButton.setFont( Res.exoRegular().deriveFont( 12f ) );
- siteAddButton.setAlignmentX( RIGHT_ALIGNMENT );
- siteAddButton.setAlignmentY( CENTER_ALIGNMENT );
- siteAddButton.addActionListener( new ActionListener() {
- @Override
- public void actionPerformed(final ActionEvent e) {
- PasswordFrame.this.user.addSite( currentSite );
- siteAddButton.setVisible( false );
- }
- } );
- siteControls.setAlignmentX( LEFT_ALIGNMENT );
- sitePanel.add( siteControls );
- siteNameField.setFont( Res.sourceCodeProRegular().deriveFont( 12f ) );
+ siteNameField = Components.textField(), Components.stud(),
+ siteAddButton = Components.button( "Add Site" ) );
siteNameField.setAlignmentX( LEFT_ALIGNMENT );
siteNameField.getDocument().addDocumentListener( this );
siteNameField.addActionListener( new ActionListener() {
@@ -118,21 +93,31 @@ public class PasswordFrame extends JFrame implements DocumentListener {
} );
}
} );
+ siteAddButton.setVisible( false );
+ siteAddButton.setAlignmentX( RIGHT_ALIGNMENT );
+ siteAddButton.setAlignmentY( CENTER_ALIGNMENT );
+ siteAddButton.addActionListener( new ActionListener() {
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ PasswordFrame.this.user.addSite( currentSite );
+ siteAddButton.setVisible( false );
+ }
+ } );
+ siteControls.setBackground( null );
+ siteControls.setAlignmentX( LEFT_ALIGNMENT );
+ sitePanel.add( siteControls );
// Site Type & Counter
MPSiteType[] types = Iterables.toArray( MPSiteType.forClass( MPSiteTypeClass.Generated ), MPSiteType.class );
JComponent siteSettings = Components.boxLayout( BoxLayout.LINE_AXIS, //
siteTypeField = new JComboBox<>( types ), //
- siteCounterField = new JSpinner(
- new SpinnerNumberModel( 1, 1, Integer.MAX_VALUE, 1 ) ) {
- @Override
- public Dimension getMaximumSize() {
- return new Dimension( 20, getPreferredSize().height );
- }
- } );
+ Components.stud(), //
+ siteCounterField = Components.spinner(
+ new SpinnerNumberModel( 1, 1, Integer.MAX_VALUE, 1 ) ) );
+ siteSettings.setBackground( null );
siteSettings.setAlignmentX( LEFT_ALIGNMENT );
sitePanel.add( siteSettings );
- siteTypeField.setFont( Res.sourceCodeProRegular().deriveFont( 12f ) );
+ siteTypeField.setFont( Res.valueFont().deriveFont( 12f ) );
siteTypeField.setAlignmentX( LEFT_ALIGNMENT );
siteTypeField.setAlignmentY( CENTER_ALIGNMENT );
siteTypeField.setSelectedItem( MPSiteType.GeneratedLong );
@@ -143,7 +128,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
}
} );
- siteCounterField.setFont( Res.sourceCodeProRegular().deriveFont( 12f ) );
+ siteCounterField.setFont( Res.valueFont().deriveFont( 12f ) );
siteCounterField.setAlignmentX( RIGHT_ALIGNMENT );
siteCounterField.setAlignmentY( CENTER_ALIGNMENT );
siteCounterField.addChangeListener( new ChangeListener() {
@@ -154,10 +139,8 @@ public class PasswordFrame extends JFrame implements DocumentListener {
} );
// Mask
- maskPasswordField = new JCheckBox();
- maskPasswordField.setFont( Res.exoRegular().deriveFont( 12f ) );
+ maskPasswordField = Components.checkBox( "Hide Password" );
maskPasswordField.setAlignmentX( Component.CENTER_ALIGNMENT );
- maskPasswordField.setText( "Hide Password" );
maskPasswordField.setSelected( true );
maskPasswordField.addItemListener( new ItemListener() {
@Override
@@ -168,20 +151,22 @@ public class PasswordFrame extends JFrame implements DocumentListener {
// Password
passwordField = new JPasswordField();
- passwordField.setHorizontalAlignment( JTextField.CENTER );
- passwordField.setAlignmentX( Component.CENTER_ALIGNMENT );
passwordField.setEditable( false );
+ passwordField.setHorizontalAlignment( JTextField.CENTER );
passwordField.putClientProperty( "JPasswordField.cutCopyAllowed", true );
passwordEchoChar = passwordField.getEchoChar();
passwordEchoFont = passwordField.getFont().deriveFont( 40f );
updateMask();
// Tip
- tipLabel = new JLabel( " ", JLabel.CENTER );
- tipLabel.setFont( Res.exoRegular().deriveFont( 9f ) );
+ tipLabel = Components.label( " ", JLabel.CENTER );
tipLabel.setAlignmentX( Component.CENTER_ALIGNMENT );
- add( Components.boxLayout( BoxLayout.PAGE_AXIS, maskPasswordField, passwordField, tipLabel ), BorderLayout.SOUTH );
+ JPanel passwordContainer = Components.boxLayout( BoxLayout.PAGE_AXIS, maskPasswordField,
+ Components.bordered( passwordField, BorderFactory.createLoweredSoftBevelBorder(),
+ Res.colors().frameBg() ), tipLabel );
+ passwordContainer.setBackground( null );
+ add( passwordContainer, BorderLayout.SOUTH );
pack();
setMinimumSize( getSize() );
@@ -194,7 +179,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
private void updateMask() {
passwordField.setEchoChar( maskPasswordField.isSelected()? passwordEchoChar: (char) 0 );
- passwordField.setFont( maskPasswordField.isSelected()? passwordEchoFont: Res.sourceCodeProBlack().deriveFont( 40f ) );
+ passwordField.setFont( maskPasswordField.isSelected()? passwordEchoFont: Res.bigValueFont().deriveFont( 40f ) );
}
@Nonnull
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/Res.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/Res.java
index 154834b3..74fb5c84 100644
--- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/Res.java
+++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/Res.java
@@ -4,6 +4,7 @@ import static com.lyndir.lhunath.opal.system.util.ObjectUtils.ifNotNullElse;
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
import com.google.common.base.Throwables;
+import com.google.common.collect.Maps;
import com.google.common.io.Resources;
import com.google.common.util.concurrent.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
@@ -11,7 +12,9 @@ import java.awt.*;
import java.awt.event.*;
import java.awt.image.ImageObserver;
import java.io.IOException;
+import java.lang.ref.SoftReference;
import java.net.URL;
+import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.*;
import java.util.regex.Matcher;
@@ -26,13 +29,7 @@ public abstract class Res {
private static final WeakHashMap executorByWindow = new WeakHashMap<>();
private static final Logger logger = Logger.get( Res.class );
-
- private static Font sourceCodeProRegular;
- private static Font sourceCodeProBlack;
- private static Font exoBold;
- private static Font exoExtraBold;
- private static Font exoRegular;
- private static Font exoThin;
+ private static final Colors colors = new Colors();
public static Future> execute(final Window host, final Runnable job) {
return getExecutor( host ).submit( new Runnable() {
@@ -100,64 +97,84 @@ public abstract class Res {
return 19;
}
+ public static Font controlFont() {
+ return arimoRegular();
+ }
+
+ public static Font valueFont() {
+ return sourceSansProRegular();
+ }
+
+ public static Font bigValueFont() {
+ return sourceSansProBlack();
+ }
+
public static Font sourceCodeProRegular() {
- try {
- return sourceCodeProRegular != null? sourceCodeProRegular: (sourceCodeProRegular =
- Font.createFont( Font.TRUETYPE_FONT, Resources.getResource( "fonts/SourceCodePro-Regular.otf" ).openStream() ));
- }
- catch (FontFormatException | IOException e) {
- throw Throwables.propagate( e );
- }
+ return font( "fonts/SourceCodePro-Regular.otf" );
}
public static Font sourceCodeProBlack() {
- try {
- return sourceCodeProBlack != null? sourceCodeProBlack: (sourceCodeProBlack =
- Font.createFont( Font.TRUETYPE_FONT, Resources.getResource( "fonts/SourceCodePro-Bold.otf" ).openStream() ));
- }
- catch (FontFormatException | IOException e) {
- throw Throwables.propagate( e );
- }
+ return font( "fonts/SourceCodePro-Bold.otf" );
+ }
+
+ public static Font sourceSansProRegular() {
+ return font( "fonts/SourceSansPro-Regular.otf" );
+ }
+
+ public static Font sourceSansProBlack() {
+ return font( "fonts/SourceSansPro-Bold.otf" );
}
public static Font exoBold() {
- try {
- return exoBold != null? exoBold: (exoBold =
- Font.createFont( Font.TRUETYPE_FONT, Resources.getResource( "fonts/Exo2.0-Bold.otf" ).openStream() ));
- }
- catch (FontFormatException | IOException e) {
- throw Throwables.propagate( e );
- }
+ return font( "fonts/Exo2.0-Bold.otf" );
}
public static Font exoExtraBold() {
- try {
- return exoExtraBold != null? exoExtraBold: (exoExtraBold
- = Font.createFont( Font.TRUETYPE_FONT, Resources.getResource( "fonts/Exo2.0-ExtraBold.otf" ).openStream() ));
- }
- catch (FontFormatException | IOException e) {
- throw Throwables.propagate( e );
- }
+ return font( "fonts/Exo2.0-ExtraBold.otf" );
}
public static Font exoRegular() {
- try {
- return exoRegular != null? exoRegular: (exoRegular =
- Font.createFont( Font.TRUETYPE_FONT, Resources.getResource( "fonts/Exo2.0-Regular.otf" ).openStream() ));
- }
- catch (FontFormatException | IOException e) {
- throw Throwables.propagate( e );
- }
+ return font( "fonts/Exo2.0-Regular.otf" );
}
public static Font exoThin() {
- try {
- return exoThin != null? exoThin: (exoThin =
- Font.createFont( Font.TRUETYPE_FONT, Resources.getResource( "fonts/Exo2.0-Thin.otf" ).openStream() ));
- }
- catch (FontFormatException | IOException e) {
- throw Throwables.propagate( e );
- }
+ return font( "fonts/Exo2.0-Thin.otf" );
+ }
+
+ public static Font arimoBold() {
+ return font( "fonts/Arimo-Bold.ttf" );
+ }
+
+ public static Font arimoBoldItalic() {
+ return font( "fonts/Arimo-BoldItalic.ttf" );
+ }
+
+ public static Font arimoItalic() {
+ return font( "fonts/Arimo-Italic.ttf" );
+ }
+
+ public static Font arimoRegular() {
+ return font( "fonts/Arimo-Regular.ttf" );
+ }
+
+ private static Font font(String fontResourceName) {
+ Map> fontsByResourceName = Maps.newHashMap();
+ SoftReference fontRef = fontsByResourceName.get( fontResourceName );
+ Font font = fontRef == null? null: fontRef.get();
+ if (font == null)
+ try {
+ fontsByResourceName.put( fontResourceName, new SoftReference<>(
+ font = Font.createFont( Font.TRUETYPE_FONT, Resources.getResource( fontResourceName ).openStream() ) ) );
+ }
+ catch (FontFormatException | IOException e) {
+ throw Throwables.propagate( e );
+ }
+
+ return font;
+ }
+
+ public static Colors colors() {
+ return colors;
}
private static final class RetinaIcon extends ImageIcon {
@@ -216,4 +233,24 @@ public abstract class Res {
g2d.dispose();
}
}
+
+
+ public static class Colors {
+
+ private final Color frameBg = Color.decode( "#5A5D6B" );
+ private final Color controlBg = Color.decode( "#ECECEC" );
+ private final Color controlBorder = Color.decode( "#BFBFBF" );
+
+ public Color frameBg() {
+ return frameBg;
+ }
+
+ public Color controlBg() {
+ return controlBg;
+ }
+
+ public Color controlBorder() {
+ return controlBorder;
+ }
+ }
}
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/UnlockFrame.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/UnlockFrame.java
index 0ea5acf0..7d74eb37 100644
--- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/UnlockFrame.java
+++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/UnlockFrame.java
@@ -14,13 +14,13 @@ import javax.swing.border.*;
*/
public class UnlockFrame extends JFrame {
- private final SignInCallback signInCallback;
- private final JPanel root;
- private final JButton signInButton;
- private final JPanel authenticationContainer;
+ private final SignInCallback signInCallback;
+ private final JPanel root;
+ private final JButton signInButton;
+ private final JPanel authenticationContainer;
private AuthenticationPanel authenticationPanel;
- private boolean incognito;
- public User user;
+ private boolean incognito;
+ public User user;
public UnlockFrame(final SignInCallback signInCallback)
throws HeadlessException {
@@ -29,17 +29,19 @@ public class UnlockFrame extends JFrame {
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
setContentPane( root = new JPanel( new BorderLayout( 20, 20 ) ) );
+ root.setBackground( Res.colors().frameBg() );
root.setBorder( new EmptyBorder( 20, 20, 20, 20 ) );
- authenticationContainer = new JPanel();
- authenticationContainer.setLayout( new BoxLayout( authenticationContainer, BoxLayout.PAGE_AXIS ) );
- authenticationContainer.setBorder( new CompoundBorder( new EtchedBorder( EtchedBorder.RAISED ), new EmptyBorder( 8, 8, 8, 8 ) ) );
- add( authenticationContainer );
+ authenticationContainer = Components.boxLayout( BoxLayout.PAGE_AXIS );
+ authenticationContainer.setBackground( Res.colors().controlBg() );
+ authenticationContainer.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
+ add( Components.bordered( authenticationContainer, BorderFactory.createRaisedBevelBorder(), Res.colors().frameBg() ) );
// Sign In
- root.add( Components.boxLayout( BoxLayout.LINE_AXIS, Box.createGlue(), signInButton = new JButton( "Sign In" ), Box.createGlue() ),
- BorderLayout.SOUTH );
- signInButton.setFont( Res.exoRegular().deriveFont( 12f ) );
+ JPanel signInBox = Components.boxLayout( BoxLayout.LINE_AXIS, Box.createGlue(), signInButton = Components.button( "Sign In" ),
+ Box.createGlue() );
+ signInBox.setBackground( null );
+ root.add( signInBox, BorderLayout.SOUTH );
signInButton.setAlignmentX( LEFT_ALIGNMENT );
signInButton.addActionListener( new AbstractAction() {
@Override
@@ -55,10 +57,8 @@ public class UnlockFrame extends JFrame {
}
protected void repack() {
- setPreferredSize( null );
pack();
- setMinimumSize( getSize() );
- setPreferredSize( new Dimension( 300, 300 ) );
+ setMinimumSize( new Dimension( Math.max( 300, getPreferredSize().width ), Math.max( 300, getPreferredSize().height ) ) );
pack();
}
@@ -71,10 +71,10 @@ public class UnlockFrame extends JFrame {
authenticationPanel = new ModelAuthenticationPanel( this );
}
authenticationPanel.updateUser( false );
- authenticationContainer.add( authenticationPanel, BorderLayout.CENTER );
+ authenticationContainer.add( authenticationPanel );
+ authenticationContainer.add( Components.stud() );
- final JCheckBox incognitoCheckBox = new JCheckBox( "Incognito" );
- incognitoCheckBox.setFont( Res.exoRegular().deriveFont( 12f ) );
+ final JCheckBox incognitoCheckBox = Components.checkBox( "Incognito" );
incognitoCheckBox.setAlignmentX( LEFT_ALIGNMENT );
incognitoCheckBox.setSelected( incognito );
incognitoCheckBox.addItemListener( new ItemListener() {
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/User.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/User.java
index 4720ae1d..eff50565 100644
--- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/User.java
+++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/User.java
@@ -20,6 +20,8 @@ public abstract class User {
protected abstract String getMasterPassword();
+ public abstract MasterKey.Version getAlgorithmVersion();
+
public int getAvatar() {
return 0;
}
@@ -38,7 +40,7 @@ public abstract class User {
throw new MasterKeyException( strf( "Master password unknown for user: %s", getFullName() ) );
}
- key = new MasterKey( getFullName(), masterPassword );
+ key = MasterKey.create( getAlgorithmVersion(), getFullName(), masterPassword );
}
return key;
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/util/Components.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/util/Components.java
index 70d5a75a..522db2a8 100644
--- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/util/Components.java
+++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/util/Components.java
@@ -1,7 +1,10 @@
package com.lyndir.masterpassword.util;
+import com.lyndir.masterpassword.gui.Res;
import java.awt.*;
import javax.swing.*;
+import javax.swing.border.Border;
+import javax.swing.border.CompoundBorder;
/**
@@ -17,4 +20,102 @@ public abstract class Components {
return container;
}
+
+ public static JPanel bordered(final JComponent component, final Border border) {
+ return bordered( component, border, null );
+ }
+
+ public static JPanel bordered(final JComponent component, final Border border, Color background) {
+ JPanel box = boxLayout( BoxLayout.LINE_AXIS, component );
+
+ if (border != null)
+ box.setBorder( border );
+
+ if (background != null)
+ box.setBackground( background );
+
+ return box;
+ }
+
+ public static JTextField textField() {
+ return new JTextField() {
+ {
+ setBorder( BorderFactory.createCompoundBorder( BorderFactory.createLineBorder( Res.colors().controlBorder(), 1, true ),
+ BorderFactory.createEmptyBorder( 4, 4, 4, 4 ) ) );
+ setFont( Res.valueFont().deriveFont( 12f ) );
+ }
+
+ @Override
+ public Dimension getMaximumSize() {
+ return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
+ }
+ };
+ }
+
+ public static JPasswordField passwordField() {
+ return new JPasswordField() {
+ {
+ setBorder( BorderFactory.createCompoundBorder( BorderFactory.createLineBorder( Res.colors().controlBorder(), 1, true ),
+ BorderFactory.createEmptyBorder( 4, 4, 4, 4 ) ) );
+ }
+
+ @Override
+ public Dimension getMaximumSize() {
+ return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
+ }
+ };
+ }
+
+ public static JButton button(String label) {
+ return new JButton( label ) {
+ {
+ setFont( Res.controlFont().deriveFont( 12f ) );
+ }
+
+ @Override
+ public Dimension getMaximumSize() {
+ return new Dimension( 20, getPreferredSize().height );
+ }
+ };
+ }
+
+ public static Component stud() {
+ return Box.createRigidArea( new Dimension( 8, 8 ) );
+ }
+
+ public static JSpinner spinner(final SpinnerModel model) {
+ return new JSpinner( model ) {
+ {
+ CompoundBorder editorBorder = BorderFactory.createCompoundBorder(
+ BorderFactory.createLineBorder( Res.colors().controlBorder(), 1, true ),
+ BorderFactory.createEmptyBorder( 4, 4, 4, 4 ) );
+ ((DefaultEditor) getEditor()).getTextField().setBorder( editorBorder );
+ }
+
+ @Override
+ public Dimension getMaximumSize() {
+ return new Dimension( 20, getPreferredSize().height );
+ }
+ };
+ }
+
+ public static JLabel label(final String label) {
+ return label( label, JLabel.LEADING );
+ }
+
+ public static JLabel label(final String label, final int alignment) {
+ return new JLabel( label, alignment ) {
+ {
+ setFont( Res.controlFont().deriveFont( 12f ) );
+ }
+ };
+ }
+
+ public static JCheckBox checkBox(final String label) {
+ return new JCheckBox( label ) {
+ {
+ setFont( Res.controlFont().deriveFont( 12f ) );
+ }
+ };
+ }
}
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/Arimo-Bold.ttf b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/Arimo-Bold.ttf
new file mode 100755
index 00000000..da89a631
Binary files /dev/null and b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/Arimo-Bold.ttf differ
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/Arimo-BoldItalic.ttf b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/Arimo-BoldItalic.ttf
new file mode 100755
index 00000000..6dc45e79
Binary files /dev/null and b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/Arimo-BoldItalic.ttf differ
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/Arimo-Italic.ttf b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/Arimo-Italic.ttf
new file mode 100755
index 00000000..6a8837cb
Binary files /dev/null and b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/Arimo-Italic.ttf differ
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/Arimo-Regular.ttf b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/Arimo-Regular.ttf
new file mode 100755
index 00000000..9be443c7
Binary files /dev/null and b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/Arimo-Regular.ttf differ
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-Black.otf b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-Black.otf
new file mode 100755
index 00000000..492661cb
Binary files /dev/null and b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-Black.otf differ
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-BlackIt.otf b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-BlackIt.otf
new file mode 100755
index 00000000..2fbb1d1a
Binary files /dev/null and b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-BlackIt.otf differ
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-Bold.otf b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-Bold.otf
new file mode 100755
index 00000000..597072f5
Binary files /dev/null and b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-Bold.otf differ
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-BoldIt.otf b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-BoldIt.otf
new file mode 100755
index 00000000..56bdfacc
Binary files /dev/null and b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-BoldIt.otf differ
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-ExtraLight.otf b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-ExtraLight.otf
new file mode 100755
index 00000000..20a21c63
Binary files /dev/null and b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-ExtraLight.otf differ
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-ExtraLightIt.otf b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-ExtraLightIt.otf
new file mode 100755
index 00000000..787bfcfa
Binary files /dev/null and b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-ExtraLightIt.otf differ
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-It.otf b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-It.otf
new file mode 100755
index 00000000..7ab613d4
Binary files /dev/null and b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-It.otf differ
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-Light.otf b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-Light.otf
new file mode 100755
index 00000000..4a8eafd7
Binary files /dev/null and b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-Light.otf differ
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-LightIt.otf b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-LightIt.otf
new file mode 100755
index 00000000..c5b8ca86
Binary files /dev/null and b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-LightIt.otf differ
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-Regular.otf b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-Regular.otf
new file mode 100755
index 00000000..38941ae7
Binary files /dev/null and b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-Regular.otf differ
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-Semibold.otf b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-Semibold.otf
new file mode 100755
index 00000000..fd41bcf1
Binary files /dev/null and b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-Semibold.otf differ
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-SemiboldIt.otf b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-SemiboldIt.otf
new file mode 100755
index 00000000..447ff80b
Binary files /dev/null and b/MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/SourceSansPro-SemiboldIt.otf differ
diff --git a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSite.java b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSite.java
index 87b36584..6c578341 100644
--- a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSite.java
+++ b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSite.java
@@ -18,14 +18,14 @@ public class MPSite {
public static final MPSiteType DEFAULT_TYPE = MPSiteType.GeneratedLong;
public static final int DEFAULT_COUNTER = 1;
- private final MPUser user;
- private int mpVersion;
- private Instant lastUsed;
- private String siteName;
- private MPSiteType siteType;
- private int siteCounter;
- private int uses;
- private String loginName;
+ private final MPUser user;
+ private MasterKey.Version mpVersion;
+ private Instant lastUsed;
+ private String siteName;
+ private MPSiteType siteType;
+ private int siteCounter;
+ private int uses;
+ private String loginName;
public MPSite(final MPUser user, final String siteName) {
this( user, siteName, DEFAULT_TYPE, DEFAULT_COUNTER );
@@ -33,14 +33,14 @@ public class MPSite {
public MPSite(final MPUser user, final String siteName, final MPSiteType siteType, final int siteCounter) {
this.user = user;
- this.mpVersion = MasterKey.ALGORITHM;
+ this.mpVersion = MasterKey.Version.CURRENT;
this.lastUsed = new Instant();
this.siteName = siteName;
this.siteType = siteType;
this.siteCounter = siteCounter;
}
- protected MPSite(final MPUser user, final int mpVersion, final Instant lastUsed, final String siteName, final MPSiteType siteType, final int siteCounter,
+ protected MPSite(final MPUser user, final MasterKey.Version mpVersion, final Instant lastUsed, final String siteName, final MPSiteType siteType, final int siteCounter,
final int uses, final String loginName, final String importContent) {
this.user = user;
this.mpVersion = mpVersion;
@@ -69,11 +69,11 @@ public class MPSite {
return null;
}
- public int getMPVersion() {
+ public MasterKey.Version getMPVersion() {
return mpVersion;
}
- public void setMPVersion(final int mpVersion) {
+ public void setMPVersion(final MasterKey.Version mpVersion) {
this.mpVersion = mpVersion;
}
diff --git a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSiteMarshaller.java b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSiteMarshaller.java
index 27ddbf19..6a20757c 100644
--- a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSiteMarshaller.java
+++ b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSiteMarshaller.java
@@ -63,8 +63,8 @@ public class MPSiteMarshaller {
header.append( "# Full Name: " ).append( user.getFullName() ).append( '\n' );
header.append( "# Avatar: " ).append( user.getAvatar() ).append( '\n' );
header.append( "# Key ID: " ).append( user.exportKeyID() ).append( '\n' );
- header.append( "# Version: " ).append( MasterKey.VERSION ).append( '\n' );
- header.append( "# Algorithm: " ).append( MasterKey.ALGORITHM ).append( '\n' );
+ header.append( "# Version: " ).append( user.getAlgorithmVersion().toBundleVersion() ).append( '\n' );
+ header.append( "# Algorithm: " ).append( user.getAlgorithmVersion().toInt() ).append( '\n' );
header.append( "# Default Type: " ).append( user.getDefaultType().getType() ).append( '\n' );
header.append( "# Passwords: " ).append( contentMode.name() ).append( '\n' );
header.append( "##\n" );
diff --git a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSiteUnmarshaller.java b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSiteUnmarshaller.java
index 1e104964..8652e7ca 100644
--- a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSiteUnmarshaller.java
+++ b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSiteUnmarshaller.java
@@ -10,6 +10,7 @@ import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
import com.lyndir.lhunath.opal.system.util.NNOperation;
import com.lyndir.masterpassword.MPSiteType;
+import com.lyndir.masterpassword.MasterKey;
import java.io.*;
import java.util.List;
import java.util.regex.Matcher;
@@ -110,7 +111,7 @@ public class MPSiteUnmarshaller {
this.mpVersion = mpVersion;
this.clearContent = clearContent;
- user = new MPUser( fullName, keyID, avatar, defaultType, new DateTime( 0 ) );
+ user = new MPUser( fullName, keyID, MasterKey.Version.fromInt( mpVersion ), avatar, defaultType, new DateTime( 0 ) );
}
@Nullable
@@ -123,11 +124,10 @@ public class MPSiteUnmarshaller {
switch (importFormat) {
case 0:
site = new MPSite( user, //
- ConversionUtils.toIntegerNN( siteMatcher.group( 4 ).replace( ":", "" ) ), //
+ MasterKey.Version.fromInt( ConversionUtils.toIntegerNN( siteMatcher.group( 4 ).replace( ":", "" ) ) ), //
rfc3339.parseDateTime( siteMatcher.group( 1 ) ).toInstant(), //
siteMatcher.group( 5 ), //
- MPSiteType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
- MPSite.DEFAULT_COUNTER, //
+ MPSiteType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ), MPSite.DEFAULT_COUNTER, //
ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ), //
null, //
siteMatcher.group( 6 ) );
@@ -135,7 +135,7 @@ public class MPSiteUnmarshaller {
case 1:
site = new MPSite( user, //
- ConversionUtils.toIntegerNN( siteMatcher.group( 4 ).replace( ":", "" ) ), //
+ MasterKey.Version.fromInt( ConversionUtils.toIntegerNN( siteMatcher.group( 4 ).replace( ":", "" ) ) ), //
rfc3339.parseDateTime( siteMatcher.group( 1 ) ).toInstant(), //
siteMatcher.group( 7 ), //
MPSiteType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
diff --git a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUser.java b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUser.java
index ce6f7d14..ec0dedbf 100644
--- a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUser.java
+++ b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUser.java
@@ -6,6 +6,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.masterpassword.MPSiteType;
+import com.lyndir.masterpassword.MasterKey;
import java.util.*;
import org.joda.time.*;
@@ -18,23 +19,25 @@ public class MPUser implements Comparable {
private final String fullName;
private final Collection sites = Sets.newHashSet();
- private byte[] keyID;
- private int avatar;
- private MPSiteType defaultType;
- private ReadableInstant lastUsed;
+ private byte[] keyID;
+ private MasterKey.Version algorithmVersion;
+ private int avatar;
+ private MPSiteType defaultType;
+ private ReadableInstant lastUsed;
public MPUser(final String fullName) {
this( fullName, null );
}
public MPUser(final String fullName, final byte[] keyID) {
- this( fullName, keyID, 0, MPSiteType.GeneratedLong, new DateTime() );
+ this( fullName, keyID, MasterKey.Version.CURRENT, 0, MPSiteType.GeneratedLong, new DateTime() );
}
- public MPUser(final String fullName, final byte[] keyID, final int avatar, final MPSiteType defaultType,
+ public MPUser(final String fullName, final byte[] keyID, final MasterKey.Version algorithmVersion, final int avatar, final MPSiteType defaultType,
final ReadableInstant lastUsed) {
this.fullName = fullName;
this.keyID = keyID;
+ this.algorithmVersion = algorithmVersion;
this.avatar = avatar;
this.defaultType = defaultType;
this.lastUsed = lastUsed;
@@ -73,6 +76,14 @@ public class MPUser implements Comparable {
this.keyID = keyID;
}
+ public MasterKey.Version getAlgorithmVersion() {
+ return algorithmVersion;
+ }
+
+ public void setAlgorithmVersion(final MasterKey.Version algorithmVersion) {
+ this.algorithmVersion = algorithmVersion;
+ }
+
public int getAvatar() {
return avatar;
}