2
0

Many UI improvements to the Java GUI.

This commit is contained in:
Maarten Billemont 2015-02-04 11:25:18 -05:00
parent 5b08149ca6
commit 78c593fc08
41 changed files with 785 additions and 297 deletions

View File

@ -24,12 +24,12 @@
<dependency>
<groupId>com.lyndir.lhunath.opal</groupId>
<artifactId>opal-system</artifactId>
<version>1.6-p7</version>
<version>1.6-p8</version>
</dependency>
<dependency>
<groupId>com.lyndir.lhunath.opal</groupId>
<artifactId>opal-crypto</artifactId>
<version>1.6-p7</version>
<version>1.6-p8</version>
</dependency>
<!-- EXTERNAL DEPENDENCIES -->

View File

@ -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.
*/

View File

@ -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.
*/

View File

@ -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? "<empty>": 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 );
}
}
}

View File

@ -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? "<empty>": 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 );
}
}

View File

@ -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? "<empty>": 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();
}
}

View File

@ -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? "<empty>": 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();
}
}

View File

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

View File

@ -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() );

View File

@ -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( "" );

View File

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

View File

@ -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.<GUI>newInstance( AppleGUI.class ).or( new GUI() ).open();
}
private static void checkUpdate() {

View File

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

View File

@ -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<Site> findSitesByName(final String siteName) {
return ImmutableList.of();

View File

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

View File

@ -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();

View File

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

View File

@ -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<Window, ExecutorService> 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<String, SoftReference<Font>> fontsByResourceName = Maps.newHashMap();
SoftReference<Font> 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;
}
}
}

View File

@ -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() {

View File

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

View File

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

View File

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

View File

@ -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" );

View File

@ -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 ) ) ),

View File

@ -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<MPUser> {
private final String fullName;
private final Collection<MPSite> 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<MPUser> {
this.keyID = keyID;
}
public MasterKey.Version getAlgorithmVersion() {
return algorithmVersion;
}
public void setAlgorithmVersion(final MasterKey.Version algorithmVersion) {
this.algorithmVersion = algorithmVersion;
}
public int getAvatar() {
return avatar;
}