Many UI improvements to the Java GUI.
This commit is contained in:
parent
5b08149ca6
commit
78c593fc08
@ -24,12 +24,12 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.lyndir.lhunath.opal</groupId>
|
<groupId>com.lyndir.lhunath.opal</groupId>
|
||||||
<artifactId>opal-system</artifactId>
|
<artifactId>opal-system</artifactId>
|
||||||
<version>1.6-p7</version>
|
<version>1.6-p8</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.lyndir.lhunath.opal</groupId>
|
<groupId>com.lyndir.lhunath.opal</groupId>
|
||||||
<artifactId>opal-crypto</artifactId>
|
<artifactId>opal-crypto</artifactId>
|
||||||
<version>1.6-p7</version>
|
<version>1.6-p8</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- EXTERNAL DEPENDENCIES -->
|
<!-- EXTERNAL DEPENDENCIES -->
|
||||||
|
@ -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.
|
* @return The type registered with the given name.
|
||||||
*/
|
*/
|
||||||
|
@ -61,7 +61,7 @@ public enum MPSiteVariant {
|
|||||||
throw logger.bug( "No variant for option: %s", option );
|
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.
|
* @return The variant registered with the given name.
|
||||||
*/
|
*/
|
||||||
|
@ -1,145 +1,155 @@
|
|||||||
package com.lyndir.masterpassword;
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
import com.google.common.base.Charsets;
|
|
||||||
import com.google.common.base.Preconditions;
|
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.*;
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
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 java.util.Arrays;
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author lhunath, 2014-08-30
|
* @author lhunath, 2014-08-30
|
||||||
*/
|
*/
|
||||||
public class MasterKey {
|
public abstract class MasterKey {
|
||||||
|
|
||||||
public static final int ALGORITHM = 1;
|
|
||||||
public static final String VERSION = "2.1";
|
|
||||||
|
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
private static final Logger logger = Logger.get( MasterKey.class );
|
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;
|
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
private final String fullName;
|
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;
|
this.fullName = fullName;
|
||||||
logger.trc( "fullName: %s", 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() {
|
public String getFullName() {
|
||||||
|
|
||||||
return fullName;
|
return fullName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
protected byte[] getMasterKey() {
|
||||||
|
|
||||||
|
return Preconditions.checkNotNull( masterKey );
|
||||||
|
}
|
||||||
|
|
||||||
public byte[] getKeyID() {
|
public byte[] getKeyID() {
|
||||||
|
|
||||||
Preconditions.checkState( valid );
|
return idForBytes( getMasterKey() );
|
||||||
return idForBytes( masterKey );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String encode(final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant,
|
public abstract String encode(final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant,
|
||||||
@Nullable final String siteContext) {
|
@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 void invalidate() {
|
public void invalidate() {
|
||||||
|
|
||||||
valid = false;
|
if (masterKey != null) {
|
||||||
Arrays.fill( masterKey, (byte) 0 );
|
Arrays.fill( masterKey, (byte) 0 );
|
||||||
|
masterKey = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] bytesForInt(final int integer) {
|
public MasterKey revalidate(final String masterPassword) {
|
||||||
return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MP_byteOrder ).putInt( integer ).array();
|
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) {
|
protected abstract byte[] bytesForInt(final int integer);
|
||||||
return MP_hash.of( bytes );
|
|
||||||
|
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 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -35,7 +35,7 @@ public class MasterKeyTest {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
for (MPWTests.Case testCase : tests.getCases()) {
|
for (MPWTests.Case testCase : tests.getCases()) {
|
||||||
MasterKey masterKey = new MasterKey( testCase.getFullName(), testCase.getMasterPassword() );
|
MasterKey masterKey = MasterKey.create( testCase.getFullName(), testCase.getMasterPassword() );
|
||||||
assertEquals(
|
assertEquals(
|
||||||
masterKey.encode( testCase.getSiteName(), testCase.getSiteType(), testCase.getSiteCounter(), testCase.getSiteVariant(),
|
masterKey.encode( testCase.getSiteName(), testCase.getSiteType(), testCase.getSiteCounter(), testCase.getSiteVariant(),
|
||||||
testCase.getSiteContext() ), testCase.getResult(), "Failed test case: " + testCase );
|
testCase.getSiteContext() ), testCase.getResult(), "Failed test case: " + testCase );
|
||||||
@ -46,7 +46,7 @@ public class MasterKeyTest {
|
|||||||
public void testGetUserName()
|
public void testGetUserName()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
assertEquals( new MasterKey( defaultCase.getFullName(), defaultCase.getMasterPassword() ).getFullName(),
|
assertEquals( MasterKey.create( defaultCase.getFullName(), defaultCase.getMasterPassword() ).getFullName(),
|
||||||
defaultCase.getFullName() );
|
defaultCase.getFullName() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ public class MasterKeyTest {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
for (MPWTests.Case testCase : tests.getCases()) {
|
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 );
|
assertEquals( CodeUtils.encodeHex( masterKey.getKeyID() ), testCase.getKeyID(), "Failed test case: " + testCase );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -65,7 +65,7 @@ public class MasterKeyTest {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
MasterKey masterKey = new MasterKey( defaultCase.getFullName(), defaultCase.getMasterPassword() );
|
MasterKey masterKey = MasterKey.create( defaultCase.getFullName(), defaultCase.getMasterPassword() );
|
||||||
masterKey.invalidate();
|
masterKey.invalidate();
|
||||||
masterKey.encode( defaultCase.getSiteName(), defaultCase.getSiteType(), defaultCase.getSiteCounter(),
|
masterKey.encode( defaultCase.getSiteName(), defaultCase.getSiteType(), defaultCase.getSiteCounter(),
|
||||||
defaultCase.getSiteVariant(), defaultCase.getSiteContext() );
|
defaultCase.getSiteVariant(), defaultCase.getSiteContext() );
|
||||||
|
@ -159,7 +159,7 @@ public class EmergencyActivity extends Activity {
|
|||||||
public MasterKey call()
|
public MasterKey call()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
try {
|
try {
|
||||||
return new MasterKey( userName, masterPassword );
|
return MasterKey.create( userName, masterPassword );
|
||||||
}
|
}
|
||||||
catch (RuntimeException e) {
|
catch (RuntimeException e) {
|
||||||
sitePasswordField.setText( "" );
|
sitePasswordField.setText( "" );
|
||||||
|
@ -183,6 +183,6 @@ public class CLI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Encode and write out the site password.
|
// 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 ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,14 +48,7 @@ public class GUI implements UnlockFrame.SignInCallback {
|
|||||||
if (Config.get().checkForUpdates())
|
if (Config.get().checkForUpdates())
|
||||||
checkUpdate();
|
checkUpdate();
|
||||||
|
|
||||||
GUI gui;
|
TypeUtils.<GUI>newInstance( AppleGUI.class ).or( new GUI() ).open();
|
||||||
try {
|
|
||||||
gui = TypeUtils.newInstance( AppleGUI.class );
|
|
||||||
}
|
|
||||||
catch (NoClassDefFoundError e) {
|
|
||||||
gui = new GUI();
|
|
||||||
}
|
|
||||||
gui.open();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkUpdate() {
|
private static void checkUpdate() {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.lyndir.masterpassword.gui;
|
package com.lyndir.masterpassword.gui;
|
||||||
|
|
||||||
|
import com.lyndir.masterpassword.util.Components;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
@ -20,39 +21,30 @@ public class IncognitoAuthenticationPanel extends AuthenticationPanel implements
|
|||||||
|
|
||||||
// Full Name
|
// Full Name
|
||||||
super( unlockFrame );
|
super( unlockFrame );
|
||||||
JLabel fullNameLabel = new JLabel( "Full Name:" );
|
add( Components.stud() );
|
||||||
fullNameLabel.setFont( Res.exoRegular().deriveFont( 12f ) );
|
|
||||||
|
JLabel fullNameLabel = Components.label( "Full Name:" );
|
||||||
fullNameLabel.setAlignmentX( LEFT_ALIGNMENT );
|
fullNameLabel.setAlignmentX( LEFT_ALIGNMENT );
|
||||||
fullNameLabel.setHorizontalAlignment( SwingConstants.CENTER );
|
fullNameLabel.setHorizontalAlignment( SwingConstants.CENTER );
|
||||||
fullNameLabel.setVerticalAlignment( SwingConstants.BOTTOM );
|
fullNameLabel.setVerticalAlignment( SwingConstants.BOTTOM );
|
||||||
add( fullNameLabel );
|
add( fullNameLabel );
|
||||||
|
|
||||||
fullNameField = new JTextField() {
|
fullNameField = Components.textField();
|
||||||
@Override
|
fullNameField.setFont( Res.valueFont().deriveFont( 12f ) );
|
||||||
public Dimension getMaximumSize() {
|
|
||||||
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
fullNameField.setFont( Res.sourceCodeProRegular().deriveFont( 12f ) );
|
|
||||||
fullNameField.setAlignmentX( LEFT_ALIGNMENT );
|
fullNameField.setAlignmentX( LEFT_ALIGNMENT );
|
||||||
fullNameField.getDocument().addDocumentListener( this );
|
fullNameField.getDocument().addDocumentListener( this );
|
||||||
fullNameField.addActionListener( this );
|
fullNameField.addActionListener( this );
|
||||||
add( fullNameField );
|
add( fullNameField );
|
||||||
|
add( Components.stud() );
|
||||||
|
|
||||||
// Master Password
|
// Master Password
|
||||||
JLabel masterPasswordLabel = new JLabel( "Master Password:" );
|
JLabel masterPasswordLabel = Components.label( "Master Password:" );
|
||||||
masterPasswordLabel.setFont( Res.exoRegular().deriveFont( 12f ) );
|
|
||||||
masterPasswordLabel.setAlignmentX( LEFT_ALIGNMENT );
|
masterPasswordLabel.setAlignmentX( LEFT_ALIGNMENT );
|
||||||
masterPasswordLabel.setHorizontalAlignment( SwingConstants.CENTER );
|
masterPasswordLabel.setHorizontalAlignment( SwingConstants.CENTER );
|
||||||
masterPasswordLabel.setVerticalAlignment( SwingConstants.BOTTOM );
|
masterPasswordLabel.setVerticalAlignment( SwingConstants.BOTTOM );
|
||||||
add( masterPasswordLabel );
|
add( masterPasswordLabel );
|
||||||
|
|
||||||
masterPasswordField = new JPasswordField() {
|
masterPasswordField = Components.passwordField();
|
||||||
@Override
|
|
||||||
public Dimension getMaximumSize() {
|
|
||||||
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
masterPasswordField.setAlignmentX( LEFT_ALIGNMENT );
|
masterPasswordField.setAlignmentX( LEFT_ALIGNMENT );
|
||||||
masterPasswordField.addActionListener( this );
|
masterPasswordField.addActionListener( this );
|
||||||
masterPasswordField.getDocument().addDocumentListener( this );
|
masterPasswordField.getDocument().addDocumentListener( this );
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.lyndir.masterpassword.gui;
|
package com.lyndir.masterpassword.gui;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.lyndir.masterpassword.MasterKey;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,6 +26,11 @@ public class IncognitoUser extends User {
|
|||||||
return masterPassword;
|
return masterPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MasterKey.Version getAlgorithmVersion() {
|
||||||
|
return MasterKey.Version.CURRENT;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterable<Site> findSitesByName(final String siteName) {
|
public Iterable<Site> findSitesByName(final String siteName) {
|
||||||
return ImmutableList.of();
|
return ImmutableList.of();
|
||||||
|
@ -5,6 +5,7 @@ import com.google.common.collect.*;
|
|||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
import com.lyndir.masterpassword.model.MPUser;
|
import com.lyndir.masterpassword.model.MPUser;
|
||||||
import com.lyndir.masterpassword.model.MPUserFileManager;
|
import com.lyndir.masterpassword.model.MPUserFileManager;
|
||||||
|
import com.lyndir.masterpassword.util.Components;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.*;
|
import java.awt.event.*;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -27,6 +28,7 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
|
|||||||
|
|
||||||
public ModelAuthenticationPanel(final UnlockFrame unlockFrame) {
|
public ModelAuthenticationPanel(final UnlockFrame unlockFrame) {
|
||||||
super( unlockFrame );
|
super( unlockFrame );
|
||||||
|
add( Components.stud() );
|
||||||
|
|
||||||
// Avatar
|
// Avatar
|
||||||
avatarLabel.addMouseListener( new MouseAdapter() {
|
avatarLabel.addMouseListener( new MouseAdapter() {
|
||||||
@ -41,8 +43,7 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
|
|||||||
} );
|
} );
|
||||||
|
|
||||||
// User
|
// User
|
||||||
JLabel userLabel = new JLabel( "User:" );
|
JLabel userLabel = Components.label( "User:" );
|
||||||
userLabel.setFont( Res.exoRegular().deriveFont( 12f ) );
|
|
||||||
userLabel.setAlignmentX( LEFT_ALIGNMENT );
|
userLabel.setAlignmentX( LEFT_ALIGNMENT );
|
||||||
userLabel.setHorizontalAlignment( SwingConstants.CENTER );
|
userLabel.setHorizontalAlignment( SwingConstants.CENTER );
|
||||||
userLabel.setVerticalAlignment( SwingConstants.BOTTOM );
|
userLabel.setVerticalAlignment( SwingConstants.BOTTOM );
|
||||||
@ -54,26 +55,21 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
|
|||||||
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
|
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.setAlignmentX( LEFT_ALIGNMENT );
|
||||||
userField.addItemListener( this );
|
userField.addItemListener( this );
|
||||||
userField.addActionListener( this );
|
userField.addActionListener( this );
|
||||||
add( userField );
|
add( userField );
|
||||||
|
add( Components.stud() );
|
||||||
|
|
||||||
// Master Password
|
// Master Password
|
||||||
masterPasswordLabel = new JLabel( "Master Password:" );
|
masterPasswordLabel = Components.label( "Master Password:" );
|
||||||
masterPasswordLabel.setFont( Res.exoRegular().deriveFont( 12f ) );
|
|
||||||
masterPasswordLabel.setAlignmentX( LEFT_ALIGNMENT );
|
masterPasswordLabel.setAlignmentX( LEFT_ALIGNMENT );
|
||||||
masterPasswordLabel.setHorizontalAlignment( SwingConstants.CENTER );
|
masterPasswordLabel.setHorizontalAlignment( SwingConstants.CENTER );
|
||||||
masterPasswordLabel.setVerticalAlignment( SwingConstants.BOTTOM );
|
masterPasswordLabel.setVerticalAlignment( SwingConstants.BOTTOM );
|
||||||
add( masterPasswordLabel );
|
add( masterPasswordLabel );
|
||||||
|
|
||||||
masterPasswordField = new JPasswordField() {
|
masterPasswordField = Components.passwordField();
|
||||||
@Override
|
|
||||||
public Dimension getMaximumSize() {
|
|
||||||
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
masterPasswordField.setAlignmentX( LEFT_ALIGNMENT );
|
masterPasswordField.setAlignmentX( LEFT_ALIGNMENT );
|
||||||
masterPasswordField.addActionListener( this );
|
masterPasswordField.addActionListener( this );
|
||||||
masterPasswordField.getDocument().addDocumentListener( this );
|
masterPasswordField.getDocument().addDocumentListener( this );
|
||||||
|
@ -37,6 +37,11 @@ public class ModelUser extends User {
|
|||||||
return masterPassword;
|
return masterPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MasterKey.Version getAlgorithmVersion() {
|
||||||
|
return model.getAlgorithmVersion();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getAvatar() {
|
public int getAvatar() {
|
||||||
return model.getAvatar();
|
return model.getAvatar();
|
||||||
|
@ -12,7 +12,6 @@ import java.awt.event.*;
|
|||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.*;
|
|
||||||
import javax.swing.event.*;
|
import javax.swing.event.*;
|
||||||
|
|
||||||
|
|
||||||
@ -44,52 +43,28 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
|||||||
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
|
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
|
||||||
setContentPane( new JPanel( new BorderLayout( 20, 20 ) ) {
|
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
|
// User
|
||||||
add( label = new JLabel( strf( "Generating passwords for: %s", user.getFullName() ) ), BorderLayout.NORTH );
|
add( label = Components.label( strf( "Generating passwords for: %s", user.getFullName() ) ), BorderLayout.NORTH );
|
||||||
label.setFont( Res.exoRegular().deriveFont( 12f ) );
|
|
||||||
label.setAlignmentX( LEFT_ALIGNMENT );
|
label.setAlignmentX( LEFT_ALIGNMENT );
|
||||||
|
|
||||||
// Site
|
// Site
|
||||||
JPanel sitePanel = new JPanel();
|
JPanel sitePanel = Components.boxLayout( BoxLayout.PAGE_AXIS );
|
||||||
sitePanel.setLayout( new BoxLayout( sitePanel, BoxLayout.PAGE_AXIS ) );
|
sitePanel.setBackground( Res.colors().controlBg() );
|
||||||
sitePanel.setBorder( new CompoundBorder( new EtchedBorder( EtchedBorder.RAISED ), new EmptyBorder( 8, 8, 8, 8 ) ) );
|
sitePanel.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
|
||||||
add( sitePanel, BorderLayout.CENTER );
|
add( Components.bordered( sitePanel, BorderFactory.createRaisedBevelBorder(), Res.colors().frameBg() ), BorderLayout.CENTER );
|
||||||
|
|
||||||
// Site Name
|
// Site Name
|
||||||
sitePanel.add( label = new JLabel( "Site Name:", JLabel.LEADING ) );
|
sitePanel.add( label = Components.label( "Site Name:" ) );
|
||||||
label.setFont( Res.exoRegular().deriveFont( 12f ) );
|
|
||||||
label.setAlignmentX( LEFT_ALIGNMENT );
|
label.setAlignmentX( LEFT_ALIGNMENT );
|
||||||
|
|
||||||
JComponent siteControls = Components.boxLayout( BoxLayout.LINE_AXIS, //
|
JComponent siteControls = Components.boxLayout( BoxLayout.LINE_AXIS, //
|
||||||
siteNameField = new JTextField() {
|
siteNameField = Components.textField(), Components.stud(),
|
||||||
@Override
|
siteAddButton = Components.button( "Add Site" ) );
|
||||||
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.setAlignmentX( LEFT_ALIGNMENT );
|
siteNameField.setAlignmentX( LEFT_ALIGNMENT );
|
||||||
siteNameField.getDocument().addDocumentListener( this );
|
siteNameField.getDocument().addDocumentListener( this );
|
||||||
siteNameField.addActionListener( new ActionListener() {
|
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
|
// Site Type & Counter
|
||||||
MPSiteType[] types = Iterables.toArray( MPSiteType.forClass( MPSiteTypeClass.Generated ), MPSiteType.class );
|
MPSiteType[] types = Iterables.toArray( MPSiteType.forClass( MPSiteTypeClass.Generated ), MPSiteType.class );
|
||||||
JComponent siteSettings = Components.boxLayout( BoxLayout.LINE_AXIS, //
|
JComponent siteSettings = Components.boxLayout( BoxLayout.LINE_AXIS, //
|
||||||
siteTypeField = new JComboBox<>( types ), //
|
siteTypeField = new JComboBox<>( types ), //
|
||||||
siteCounterField = new JSpinner(
|
Components.stud(), //
|
||||||
new SpinnerNumberModel( 1, 1, Integer.MAX_VALUE, 1 ) ) {
|
siteCounterField = Components.spinner(
|
||||||
@Override
|
new SpinnerNumberModel( 1, 1, Integer.MAX_VALUE, 1 ) ) );
|
||||||
public Dimension getMaximumSize() {
|
siteSettings.setBackground( null );
|
||||||
return new Dimension( 20, getPreferredSize().height );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
siteSettings.setAlignmentX( LEFT_ALIGNMENT );
|
siteSettings.setAlignmentX( LEFT_ALIGNMENT );
|
||||||
sitePanel.add( siteSettings );
|
sitePanel.add( siteSettings );
|
||||||
siteTypeField.setFont( Res.sourceCodeProRegular().deriveFont( 12f ) );
|
siteTypeField.setFont( Res.valueFont().deriveFont( 12f ) );
|
||||||
siteTypeField.setAlignmentX( LEFT_ALIGNMENT );
|
siteTypeField.setAlignmentX( LEFT_ALIGNMENT );
|
||||||
siteTypeField.setAlignmentY( CENTER_ALIGNMENT );
|
siteTypeField.setAlignmentY( CENTER_ALIGNMENT );
|
||||||
siteTypeField.setSelectedItem( MPSiteType.GeneratedLong );
|
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.setAlignmentX( RIGHT_ALIGNMENT );
|
||||||
siteCounterField.setAlignmentY( CENTER_ALIGNMENT );
|
siteCounterField.setAlignmentY( CENTER_ALIGNMENT );
|
||||||
siteCounterField.addChangeListener( new ChangeListener() {
|
siteCounterField.addChangeListener( new ChangeListener() {
|
||||||
@ -154,10 +139,8 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
|||||||
} );
|
} );
|
||||||
|
|
||||||
// Mask
|
// Mask
|
||||||
maskPasswordField = new JCheckBox();
|
maskPasswordField = Components.checkBox( "Hide Password" );
|
||||||
maskPasswordField.setFont( Res.exoRegular().deriveFont( 12f ) );
|
|
||||||
maskPasswordField.setAlignmentX( Component.CENTER_ALIGNMENT );
|
maskPasswordField.setAlignmentX( Component.CENTER_ALIGNMENT );
|
||||||
maskPasswordField.setText( "Hide Password" );
|
|
||||||
maskPasswordField.setSelected( true );
|
maskPasswordField.setSelected( true );
|
||||||
maskPasswordField.addItemListener( new ItemListener() {
|
maskPasswordField.addItemListener( new ItemListener() {
|
||||||
@Override
|
@Override
|
||||||
@ -168,20 +151,22 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
|||||||
|
|
||||||
// Password
|
// Password
|
||||||
passwordField = new JPasswordField();
|
passwordField = new JPasswordField();
|
||||||
passwordField.setHorizontalAlignment( JTextField.CENTER );
|
|
||||||
passwordField.setAlignmentX( Component.CENTER_ALIGNMENT );
|
|
||||||
passwordField.setEditable( false );
|
passwordField.setEditable( false );
|
||||||
|
passwordField.setHorizontalAlignment( JTextField.CENTER );
|
||||||
passwordField.putClientProperty( "JPasswordField.cutCopyAllowed", true );
|
passwordField.putClientProperty( "JPasswordField.cutCopyAllowed", true );
|
||||||
passwordEchoChar = passwordField.getEchoChar();
|
passwordEchoChar = passwordField.getEchoChar();
|
||||||
passwordEchoFont = passwordField.getFont().deriveFont( 40f );
|
passwordEchoFont = passwordField.getFont().deriveFont( 40f );
|
||||||
updateMask();
|
updateMask();
|
||||||
|
|
||||||
// Tip
|
// Tip
|
||||||
tipLabel = new JLabel( " ", JLabel.CENTER );
|
tipLabel = Components.label( " ", JLabel.CENTER );
|
||||||
tipLabel.setFont( Res.exoRegular().deriveFont( 9f ) );
|
|
||||||
tipLabel.setAlignmentX( Component.CENTER_ALIGNMENT );
|
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();
|
pack();
|
||||||
setMinimumSize( getSize() );
|
setMinimumSize( getSize() );
|
||||||
@ -194,7 +179,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
|||||||
|
|
||||||
private void updateMask() {
|
private void updateMask() {
|
||||||
passwordField.setEchoChar( maskPasswordField.isSelected()? passwordEchoChar: (char) 0 );
|
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
|
@Nonnull
|
||||||
|
@ -4,6 +4,7 @@ import static com.lyndir.lhunath.opal.system.util.ObjectUtils.ifNotNullElse;
|
|||||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
||||||
|
|
||||||
import com.google.common.base.Throwables;
|
import com.google.common.base.Throwables;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.io.Resources;
|
import com.google.common.io.Resources;
|
||||||
import com.google.common.util.concurrent.*;
|
import com.google.common.util.concurrent.*;
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
@ -11,7 +12,9 @@ import java.awt.*;
|
|||||||
import java.awt.event.*;
|
import java.awt.event.*;
|
||||||
import java.awt.image.ImageObserver;
|
import java.awt.image.ImageObserver;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.ref.SoftReference;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.regex.Matcher;
|
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 WeakHashMap<Window, ExecutorService> executorByWindow = new WeakHashMap<>();
|
||||||
private static final Logger logger = Logger.get( Res.class );
|
private static final Logger logger = Logger.get( Res.class );
|
||||||
|
private static final Colors colors = new Colors();
|
||||||
private static Font sourceCodeProRegular;
|
|
||||||
private static Font sourceCodeProBlack;
|
|
||||||
private static Font exoBold;
|
|
||||||
private static Font exoExtraBold;
|
|
||||||
private static Font exoRegular;
|
|
||||||
private static Font exoThin;
|
|
||||||
|
|
||||||
public static Future<?> execute(final Window host, final Runnable job) {
|
public static Future<?> execute(final Window host, final Runnable job) {
|
||||||
return getExecutor( host ).submit( new Runnable() {
|
return getExecutor( host ).submit( new Runnable() {
|
||||||
@ -100,64 +97,84 @@ public abstract class Res {
|
|||||||
return 19;
|
return 19;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Font controlFont() {
|
||||||
|
return arimoRegular();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Font valueFont() {
|
||||||
|
return sourceSansProRegular();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Font bigValueFont() {
|
||||||
|
return sourceSansProBlack();
|
||||||
|
}
|
||||||
|
|
||||||
public static Font sourceCodeProRegular() {
|
public static Font sourceCodeProRegular() {
|
||||||
try {
|
return font( "fonts/SourceCodePro-Regular.otf" );
|
||||||
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 );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Font sourceCodeProBlack() {
|
public static Font sourceCodeProBlack() {
|
||||||
try {
|
return font( "fonts/SourceCodePro-Bold.otf" );
|
||||||
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 );
|
public static Font sourceSansProRegular() {
|
||||||
|
return font( "fonts/SourceSansPro-Regular.otf" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Font sourceSansProBlack() {
|
||||||
|
return font( "fonts/SourceSansPro-Bold.otf" );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Font exoBold() {
|
public static Font exoBold() {
|
||||||
try {
|
return font( "fonts/Exo2.0-Bold.otf" );
|
||||||
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 );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Font exoExtraBold() {
|
public static Font exoExtraBold() {
|
||||||
try {
|
return font( "fonts/Exo2.0-ExtraBold.otf" );
|
||||||
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 );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Font exoRegular() {
|
public static Font exoRegular() {
|
||||||
try {
|
return font( "fonts/Exo2.0-Regular.otf" );
|
||||||
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 );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Font exoThin() {
|
public static Font exoThin() {
|
||||||
|
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 {
|
try {
|
||||||
return exoThin != null? exoThin: (exoThin =
|
fontsByResourceName.put( fontResourceName, new SoftReference<>(
|
||||||
Font.createFont( Font.TRUETYPE_FONT, Resources.getResource( "fonts/Exo2.0-Thin.otf" ).openStream() ));
|
font = Font.createFont( Font.TRUETYPE_FONT, Resources.getResource( fontResourceName ).openStream() ) ) );
|
||||||
}
|
}
|
||||||
catch (FontFormatException | IOException e) {
|
catch (FontFormatException | IOException e) {
|
||||||
throw Throwables.propagate( e );
|
throw Throwables.propagate( e );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return font;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Colors colors() {
|
||||||
|
return colors;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class RetinaIcon extends ImageIcon {
|
private static final class RetinaIcon extends ImageIcon {
|
||||||
@ -216,4 +233,24 @@ public abstract class Res {
|
|||||||
g2d.dispose();
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,17 +29,19 @@ public class UnlockFrame extends JFrame {
|
|||||||
|
|
||||||
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
|
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
|
||||||
setContentPane( root = new JPanel( new BorderLayout( 20, 20 ) ) );
|
setContentPane( root = new JPanel( new BorderLayout( 20, 20 ) ) );
|
||||||
|
root.setBackground( Res.colors().frameBg() );
|
||||||
root.setBorder( new EmptyBorder( 20, 20, 20, 20 ) );
|
root.setBorder( new EmptyBorder( 20, 20, 20, 20 ) );
|
||||||
|
|
||||||
authenticationContainer = new JPanel();
|
authenticationContainer = Components.boxLayout( BoxLayout.PAGE_AXIS );
|
||||||
authenticationContainer.setLayout( new BoxLayout( authenticationContainer, BoxLayout.PAGE_AXIS ) );
|
authenticationContainer.setBackground( Res.colors().controlBg() );
|
||||||
authenticationContainer.setBorder( new CompoundBorder( new EtchedBorder( EtchedBorder.RAISED ), new EmptyBorder( 8, 8, 8, 8 ) ) );
|
authenticationContainer.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
|
||||||
add( authenticationContainer );
|
add( Components.bordered( authenticationContainer, BorderFactory.createRaisedBevelBorder(), Res.colors().frameBg() ) );
|
||||||
|
|
||||||
// Sign In
|
// Sign In
|
||||||
root.add( Components.boxLayout( BoxLayout.LINE_AXIS, Box.createGlue(), signInButton = new JButton( "Sign In" ), Box.createGlue() ),
|
JPanel signInBox = Components.boxLayout( BoxLayout.LINE_AXIS, Box.createGlue(), signInButton = Components.button( "Sign In" ),
|
||||||
BorderLayout.SOUTH );
|
Box.createGlue() );
|
||||||
signInButton.setFont( Res.exoRegular().deriveFont( 12f ) );
|
signInBox.setBackground( null );
|
||||||
|
root.add( signInBox, BorderLayout.SOUTH );
|
||||||
signInButton.setAlignmentX( LEFT_ALIGNMENT );
|
signInButton.setAlignmentX( LEFT_ALIGNMENT );
|
||||||
signInButton.addActionListener( new AbstractAction() {
|
signInButton.addActionListener( new AbstractAction() {
|
||||||
@Override
|
@Override
|
||||||
@ -55,10 +57,8 @@ public class UnlockFrame extends JFrame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void repack() {
|
protected void repack() {
|
||||||
setPreferredSize( null );
|
|
||||||
pack();
|
pack();
|
||||||
setMinimumSize( getSize() );
|
setMinimumSize( new Dimension( Math.max( 300, getPreferredSize().width ), Math.max( 300, getPreferredSize().height ) ) );
|
||||||
setPreferredSize( new Dimension( 300, 300 ) );
|
|
||||||
pack();
|
pack();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,10 +71,10 @@ public class UnlockFrame extends JFrame {
|
|||||||
authenticationPanel = new ModelAuthenticationPanel( this );
|
authenticationPanel = new ModelAuthenticationPanel( this );
|
||||||
}
|
}
|
||||||
authenticationPanel.updateUser( false );
|
authenticationPanel.updateUser( false );
|
||||||
authenticationContainer.add( authenticationPanel, BorderLayout.CENTER );
|
authenticationContainer.add( authenticationPanel );
|
||||||
|
authenticationContainer.add( Components.stud() );
|
||||||
|
|
||||||
final JCheckBox incognitoCheckBox = new JCheckBox( "Incognito" );
|
final JCheckBox incognitoCheckBox = Components.checkBox( "Incognito" );
|
||||||
incognitoCheckBox.setFont( Res.exoRegular().deriveFont( 12f ) );
|
|
||||||
incognitoCheckBox.setAlignmentX( LEFT_ALIGNMENT );
|
incognitoCheckBox.setAlignmentX( LEFT_ALIGNMENT );
|
||||||
incognitoCheckBox.setSelected( incognito );
|
incognitoCheckBox.setSelected( incognito );
|
||||||
incognitoCheckBox.addItemListener( new ItemListener() {
|
incognitoCheckBox.addItemListener( new ItemListener() {
|
||||||
|
@ -20,6 +20,8 @@ public abstract class User {
|
|||||||
|
|
||||||
protected abstract String getMasterPassword();
|
protected abstract String getMasterPassword();
|
||||||
|
|
||||||
|
public abstract MasterKey.Version getAlgorithmVersion();
|
||||||
|
|
||||||
public int getAvatar() {
|
public int getAvatar() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -38,7 +40,7 @@ public abstract class User {
|
|||||||
throw new MasterKeyException( strf( "Master password unknown for user: %s", getFullName() ) );
|
throw new MasterKeyException( strf( "Master password unknown for user: %s", getFullName() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
key = new MasterKey( getFullName(), masterPassword );
|
key = MasterKey.create( getAlgorithmVersion(), getFullName(), masterPassword );
|
||||||
}
|
}
|
||||||
|
|
||||||
return key;
|
return key;
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package com.lyndir.masterpassword.util;
|
package com.lyndir.masterpassword.util;
|
||||||
|
|
||||||
|
import com.lyndir.masterpassword.gui.Res;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
import javax.swing.border.Border;
|
||||||
|
import javax.swing.border.CompoundBorder;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -17,4 +20,102 @@ public abstract class Components {
|
|||||||
|
|
||||||
return container;
|
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 ) );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
BIN
MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/Arimo-Bold.ttf
Executable file
BIN
MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/Arimo-Bold.ttf
Executable file
Binary file not shown.
Binary file not shown.
BIN
MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/Arimo-Italic.ttf
Executable file
BIN
MasterPassword/Java/masterpassword-gui/src/main/resources/fonts/Arimo-Italic.ttf
Executable file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -19,7 +19,7 @@ public class MPSite {
|
|||||||
public static final int DEFAULT_COUNTER = 1;
|
public static final int DEFAULT_COUNTER = 1;
|
||||||
|
|
||||||
private final MPUser user;
|
private final MPUser user;
|
||||||
private int mpVersion;
|
private MasterKey.Version mpVersion;
|
||||||
private Instant lastUsed;
|
private Instant lastUsed;
|
||||||
private String siteName;
|
private String siteName;
|
||||||
private MPSiteType siteType;
|
private MPSiteType siteType;
|
||||||
@ -33,14 +33,14 @@ public class MPSite {
|
|||||||
|
|
||||||
public MPSite(final MPUser user, final String siteName, final MPSiteType siteType, final int siteCounter) {
|
public MPSite(final MPUser user, final String siteName, final MPSiteType siteType, final int siteCounter) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.mpVersion = MasterKey.ALGORITHM;
|
this.mpVersion = MasterKey.Version.CURRENT;
|
||||||
this.lastUsed = new Instant();
|
this.lastUsed = new Instant();
|
||||||
this.siteName = siteName;
|
this.siteName = siteName;
|
||||||
this.siteType = siteType;
|
this.siteType = siteType;
|
||||||
this.siteCounter = siteCounter;
|
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) {
|
final int uses, final String loginName, final String importContent) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.mpVersion = mpVersion;
|
this.mpVersion = mpVersion;
|
||||||
@ -69,11 +69,11 @@ public class MPSite {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMPVersion() {
|
public MasterKey.Version getMPVersion() {
|
||||||
return mpVersion;
|
return mpVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMPVersion(final int mpVersion) {
|
public void setMPVersion(final MasterKey.Version mpVersion) {
|
||||||
this.mpVersion = mpVersion;
|
this.mpVersion = mpVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,8 +63,8 @@ public class MPSiteMarshaller {
|
|||||||
header.append( "# Full Name: " ).append( user.getFullName() ).append( '\n' );
|
header.append( "# Full Name: " ).append( user.getFullName() ).append( '\n' );
|
||||||
header.append( "# Avatar: " ).append( user.getAvatar() ).append( '\n' );
|
header.append( "# Avatar: " ).append( user.getAvatar() ).append( '\n' );
|
||||||
header.append( "# Key ID: " ).append( user.exportKeyID() ).append( '\n' );
|
header.append( "# Key ID: " ).append( user.exportKeyID() ).append( '\n' );
|
||||||
header.append( "# Version: " ).append( MasterKey.VERSION ).append( '\n' );
|
header.append( "# Version: " ).append( user.getAlgorithmVersion().toBundleVersion() ).append( '\n' );
|
||||||
header.append( "# Algorithm: " ).append( MasterKey.ALGORITHM ).append( '\n' );
|
header.append( "# Algorithm: " ).append( user.getAlgorithmVersion().toInt() ).append( '\n' );
|
||||||
header.append( "# Default Type: " ).append( user.getDefaultType().getType() ).append( '\n' );
|
header.append( "# Default Type: " ).append( user.getDefaultType().getType() ).append( '\n' );
|
||||||
header.append( "# Passwords: " ).append( contentMode.name() ).append( '\n' );
|
header.append( "# Passwords: " ).append( contentMode.name() ).append( '\n' );
|
||||||
header.append( "##\n" );
|
header.append( "##\n" );
|
||||||
|
@ -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.ConversionUtils;
|
||||||
import com.lyndir.lhunath.opal.system.util.NNOperation;
|
import com.lyndir.lhunath.opal.system.util.NNOperation;
|
||||||
import com.lyndir.masterpassword.MPSiteType;
|
import com.lyndir.masterpassword.MPSiteType;
|
||||||
|
import com.lyndir.masterpassword.MasterKey;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
@ -110,7 +111,7 @@ public class MPSiteUnmarshaller {
|
|||||||
this.mpVersion = mpVersion;
|
this.mpVersion = mpVersion;
|
||||||
this.clearContent = clearContent;
|
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
|
@Nullable
|
||||||
@ -123,11 +124,10 @@ public class MPSiteUnmarshaller {
|
|||||||
switch (importFormat) {
|
switch (importFormat) {
|
||||||
case 0:
|
case 0:
|
||||||
site = new MPSite( user, //
|
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(), //
|
rfc3339.parseDateTime( siteMatcher.group( 1 ) ).toInstant(), //
|
||||||
siteMatcher.group( 5 ), //
|
siteMatcher.group( 5 ), //
|
||||||
MPSiteType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
|
MPSiteType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ), MPSite.DEFAULT_COUNTER, //
|
||||||
MPSite.DEFAULT_COUNTER, //
|
|
||||||
ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ), //
|
ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ), //
|
||||||
null, //
|
null, //
|
||||||
siteMatcher.group( 6 ) );
|
siteMatcher.group( 6 ) );
|
||||||
@ -135,7 +135,7 @@ public class MPSiteUnmarshaller {
|
|||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
site = new MPSite( user, //
|
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(), //
|
rfc3339.parseDateTime( siteMatcher.group( 1 ) ).toInstant(), //
|
||||||
siteMatcher.group( 7 ), //
|
siteMatcher.group( 7 ), //
|
||||||
MPSiteType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
|
MPSiteType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
|
||||||
|
@ -6,6 +6,7 @@ import com.google.common.collect.ImmutableList;
|
|||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import com.lyndir.lhunath.opal.system.CodeUtils;
|
import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||||
import com.lyndir.masterpassword.MPSiteType;
|
import com.lyndir.masterpassword.MPSiteType;
|
||||||
|
import com.lyndir.masterpassword.MasterKey;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import org.joda.time.*;
|
import org.joda.time.*;
|
||||||
|
|
||||||
@ -19,6 +20,7 @@ public class MPUser implements Comparable<MPUser> {
|
|||||||
private final Collection<MPSite> sites = Sets.newHashSet();
|
private final Collection<MPSite> sites = Sets.newHashSet();
|
||||||
|
|
||||||
private byte[] keyID;
|
private byte[] keyID;
|
||||||
|
private MasterKey.Version algorithmVersion;
|
||||||
private int avatar;
|
private int avatar;
|
||||||
private MPSiteType defaultType;
|
private MPSiteType defaultType;
|
||||||
private ReadableInstant lastUsed;
|
private ReadableInstant lastUsed;
|
||||||
@ -28,13 +30,14 @@ public class MPUser implements Comparable<MPUser> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public MPUser(final String fullName, final byte[] keyID) {
|
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) {
|
final ReadableInstant lastUsed) {
|
||||||
this.fullName = fullName;
|
this.fullName = fullName;
|
||||||
this.keyID = keyID;
|
this.keyID = keyID;
|
||||||
|
this.algorithmVersion = algorithmVersion;
|
||||||
this.avatar = avatar;
|
this.avatar = avatar;
|
||||||
this.defaultType = defaultType;
|
this.defaultType = defaultType;
|
||||||
this.lastUsed = lastUsed;
|
this.lastUsed = lastUsed;
|
||||||
@ -73,6 +76,14 @@ public class MPUser implements Comparable<MPUser> {
|
|||||||
this.keyID = keyID;
|
this.keyID = keyID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MasterKey.Version getAlgorithmVersion() {
|
||||||
|
return algorithmVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAlgorithmVersion(final MasterKey.Version algorithmVersion) {
|
||||||
|
this.algorithmVersion = algorithmVersion;
|
||||||
|
}
|
||||||
|
|
||||||
public int getAvatar() {
|
public int getAvatar() {
|
||||||
return avatar;
|
return avatar;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user