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