2
0

Replace Version API with MPAlgorithm, make configuration instance-specific.

This commit is contained in:
Maarten Billemont 2018-04-26 15:56:12 -04:00
parent 33f2e0edda
commit 82e2d0b5ac
29 changed files with 397 additions and 226 deletions

View File

@ -30,89 +30,102 @@ import javax.annotation.Nullable;
/** /**
* @see MPMasterKey.Version * @see MPMasterKey.Version
*/ */
public interface MPAlgorithm { @SuppressWarnings({ "FieldMayBeStatic", "NewMethodNamingConvention" })
public abstract class MPAlgorithm {
public abstract byte[] masterKey(String fullName, char[] masterPassword);
public abstract byte[] siteKey(byte[] masterKey, String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
@Nullable String keyContext);
public abstract String siteResult(byte[] masterKey, byte[] siteKey, String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
@Nullable String keyContext, MPResultType resultType, @Nullable String resultParam);
public abstract String sitePasswordFromTemplate(byte[] masterKey, byte[] siteKey, MPResultType resultType, @Nullable String resultParam);
public abstract String sitePasswordFromCrypt(byte[] masterKey, byte[] siteKey, MPResultType resultType, @Nullable String resultParam);
public abstract String sitePasswordFromDerive(byte[] masterKey, byte[] siteKey, MPResultType resultType, @Nullable String resultParam);
public abstract String siteState(byte[] masterKey, byte[] siteKey, String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
@Nullable String keyContext, MPResultType resultType, String resultParam);
// Configuration
public abstract MPMasterKey.Version version();
/** /**
* mpw: defaults: password result type. * mpw: defaults: password result type.
*/ */
MPResultType mpw_default_type = MPResultType.GeneratedLong; public abstract MPResultType mpw_default_type();
/** /**
* mpw: defaults: initial counter value. * mpw: defaults: initial counter value.
*/ */
UnsignedInteger mpw_default_counter = UnsignedInteger.ONE; public abstract UnsignedInteger mpw_default_counter();
/** /**
* mpw: validity for the time-based rolling counter. * mpw: validity for the time-based rolling counter (s).
*/ */
int mpw_otp_window = 5 * 60 /* s */; public abstract long mpw_otp_window();
/** /**
* mpw: Key ID hash. * mpw: Key ID hash.
*/ */
MessageDigests mpw_hash = MessageDigests.SHA256; public abstract MessageDigests mpw_hash();
/** /**
* mpw: Site digest. * mpw: Site digest.
*/ */
MessageAuthenticationDigests mpw_digest = MessageAuthenticationDigests.HmacSHA256; public abstract MessageAuthenticationDigests mpw_digest();
/** /**
* mpw: Platform-agnostic byte order. * mpw: Platform-agnostic byte order.
*/ */
ByteOrder mpw_byteOrder = ByteOrder.BIG_ENDIAN; public abstract ByteOrder mpw_byteOrder();
/** /**
* mpw: Input character encoding. * mpw: Input character encoding.
*/ */
Charset mpw_charset = Charsets.UTF_8; public abstract Charset mpw_charset();
/** /**
* mpw: Master key size (byte). * mpw: Master key size (byte).
*/ */
int mpw_dkLen = 64; public abstract int mpw_dkLen();
/**
* mpw: Minimum size for derived keys (bit).
*/
public abstract int mpw_keySize_min();
/**
* mpw: Maximum size for derived keys (bit).
*/
public abstract int mpw_keySize_max();
/** /**
* scrypt: Parallelization parameter. * scrypt: Parallelization parameter.
*/ */
int scrypt_p = 2; public abstract int scrypt_p();
/** /**
* scrypt: Memory cost parameter. * scrypt: Memory cost parameter.
*/ */
int scrypt_r = 8; public abstract int scrypt_r();
/** /**
* scrypt: CPU cost parameter. * scrypt: CPU cost parameter.
*/ */
int scrypt_N = 32768; public abstract int scrypt_N();
MPMasterKey.Version getAlgorithmVersion();
byte[] masterKey(String fullName, char[] masterPassword);
byte[] siteKey(byte[] masterKey, String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
@Nullable String keyContext);
String siteResult(byte[] masterKey, byte[] siteKey, String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
@Nullable String keyContext, MPResultType resultType, @Nullable String resultParam);
String sitePasswordFromTemplate(byte[] masterKey, byte[] siteKey, MPResultType resultType, @Nullable String resultParam);
String sitePasswordFromCrypt(byte[] masterKey, byte[] siteKey, MPResultType resultType, @Nullable String resultParam);
String sitePasswordFromDerive(byte[] masterKey, byte[] siteKey, MPResultType resultType, @Nullable String resultParam);
String siteState(byte[] masterKey, byte[] siteKey, String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
@Nullable String keyContext, MPResultType resultType, String resultParam);
// Utilities // Utilities
byte[] toBytes(int number); abstract byte[] toBytes(int number);
byte[] toBytes(UnsignedInteger number); abstract byte[] toBytes(UnsignedInteger number);
byte[] toBytes(char[] characters); abstract byte[] toBytes(char[] characters);
byte[] toID(byte[] bytes); abstract byte[] toID(byte[] bytes);
} }

View File

@ -18,17 +18,16 @@
package com.lyndir.masterpassword; package com.lyndir.masterpassword;
import com.google.common.base.Preconditions; import com.google.common.base.*;
import com.google.common.base.Throwables;
import com.google.common.primitives.Bytes; import com.google.common.primitives.Bytes;
import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedInteger;
import com.lambdaworks.crypto.SCrypt; import com.lambdaworks.crypto.SCrypt;
import com.lyndir.lhunath.opal.crypto.CryptUtils; import com.lyndir.lhunath.opal.crypto.CryptUtils;
import com.lyndir.lhunath.opal.system.CodeUtils; import com.lyndir.lhunath.opal.system.*;
import com.lyndir.lhunath.opal.system.logging.Logger; 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 java.nio.ByteBuffer; import java.nio.*;
import java.nio.CharBuffer; import java.nio.charset.Charset;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.util.Arrays; import java.util.Arrays;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -40,20 +39,17 @@ import javax.crypto.IllegalBlockSizeException;
* @author lhunath, 2014-08-30 * @author lhunath, 2014-08-30
* @see MPMasterKey.Version#V0 * @see MPMasterKey.Version#V0
*/ */
public class MPAlgorithmV0 implements MPAlgorithm { @SuppressWarnings("NewMethodNamingConvention")
public class MPAlgorithmV0 extends MPAlgorithm {
public final MPMasterKey.Version version = MPMasterKey.Version.V0;
protected final Logger logger = Logger.get( getClass() ); protected final Logger logger = Logger.get( getClass() );
@Override
public MPMasterKey.Version getAlgorithmVersion() {
return MPMasterKey.Version.V0;
}
@Override @Override
public byte[] masterKey(final String fullName, final char[] masterPassword) { public byte[] masterKey(final String fullName, final char[] masterPassword) {
byte[] fullNameBytes = fullName.getBytes( mpw_charset ); byte[] fullNameBytes = fullName.getBytes( mpw_charset() );
byte[] fullNameLengthBytes = toBytes( fullName.length() ); byte[] fullNameLengthBytes = toBytes( fullName.length() );
String keyScope = MPKeyPurpose.Authentication.getScope(); String keyScope = MPKeyPurpose.Authentication.getScope();
@ -62,12 +58,12 @@ public class MPAlgorithmV0 implements MPAlgorithm {
// Calculate the master key salt. // Calculate the master key salt.
logger.trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s", logger.trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s",
keyScope, CodeUtils.encodeHex( fullNameLengthBytes ), fullName ); keyScope, CodeUtils.encodeHex( fullNameLengthBytes ), fullName );
byte[] masterKeySalt = Bytes.concat( keyScope.getBytes( mpw_charset ), fullNameLengthBytes, fullNameBytes ); byte[] masterKeySalt = Bytes.concat( keyScope.getBytes( mpw_charset() ), fullNameLengthBytes, fullNameBytes );
logger.trc( " => masterKeySalt.id: %s", CodeUtils.encodeHex( toID( masterKeySalt ) ) ); logger.trc( " => masterKeySalt.id: %s", CodeUtils.encodeHex( toID( masterKeySalt ) ) );
// Calculate the master key. // Calculate the master key.
logger.trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%d, r=%d, p=%d )", logger.trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%d, r=%d, p=%d )",
scrypt_N, scrypt_r, scrypt_p ); scrypt_N(), scrypt_r(), scrypt_p() );
byte[] masterPasswordBytes = toBytes( masterPassword ); byte[] masterPasswordBytes = toBytes( masterPassword );
byte[] masterKey = scrypt( masterKeySalt, masterPasswordBytes ); byte[] masterKey = scrypt( masterKeySalt, masterPasswordBytes );
Arrays.fill( masterKeySalt, (byte) 0 ); Arrays.fill( masterKeySalt, (byte) 0 );
@ -79,7 +75,7 @@ public class MPAlgorithmV0 implements MPAlgorithm {
protected byte[] scrypt(final byte[] masterKeySalt, final byte[] mpBytes) { protected byte[] scrypt(final byte[] masterKeySalt, final byte[] mpBytes) {
try { try {
return SCrypt.scrypt( mpBytes, masterKeySalt, scrypt_N, scrypt_r, scrypt_p, mpw_dkLen ); return SCrypt.scrypt( mpBytes, masterKeySalt, scrypt_N(), scrypt_r(), scrypt_p(), mpw_dkLen() );
} }
catch (final GeneralSecurityException e) { catch (final GeneralSecurityException e) {
throw logger.bug( e ); throw logger.bug( e );
@ -95,25 +91,25 @@ public class MPAlgorithmV0 implements MPAlgorithm {
// OTP counter value. // OTP counter value.
if (siteCounter.longValue() == 0) if (siteCounter.longValue() == 0)
siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (mpw_otp_window * 1000)) * mpw_otp_window ); siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (mpw_otp_window() * 1000)) * mpw_otp_window() );
// Calculate the site seed. // Calculate the site seed.
byte[] siteNameBytes = siteName.getBytes( mpw_charset ); byte[] siteNameBytes = siteName.getBytes( mpw_charset() );
byte[] siteNameLengthBytes = toBytes( siteName.length() ); byte[] siteNameLengthBytes = toBytes( siteName.length() );
byte[] siteCounterBytes = toBytes( siteCounter ); byte[] siteCounterBytes = toBytes( siteCounter );
byte[] keyContextBytes = ((keyContext == null) || keyContext.isEmpty())? null: keyContext.getBytes( mpw_charset ); byte[] keyContextBytes = ((keyContext == null) || keyContext.isEmpty())? null: keyContext.getBytes( mpw_charset() );
byte[] keyContextLengthBytes = (keyContextBytes == null)? null: toBytes( keyContextBytes.length ); byte[] keyContextLengthBytes = (keyContextBytes == null)? null: toBytes( keyContextBytes.length );
logger.trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s", logger.trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s",
keyScope, CodeUtils.encodeHex( siteNameLengthBytes ), siteName, CodeUtils.encodeHex( siteCounterBytes ), keyScope, CodeUtils.encodeHex( siteNameLengthBytes ), siteName, CodeUtils.encodeHex( siteCounterBytes ),
(keyContextLengthBytes == null)? null: CodeUtils.encodeHex( keyContextLengthBytes ), keyContext ); (keyContextLengthBytes == null)? null: CodeUtils.encodeHex( keyContextLengthBytes ), keyContext );
byte[] sitePasswordInfo = Bytes.concat( keyScope.getBytes( mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes ); byte[] sitePasswordInfo = Bytes.concat( keyScope.getBytes( mpw_charset() ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
if (keyContextBytes != null) if (keyContextBytes != null)
sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes ); sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes );
logger.trc( " => siteSalt.id: %s", CodeUtils.encodeHex( toID( sitePasswordInfo ) ) ); logger.trc( " => siteSalt.id: %s", CodeUtils.encodeHex( toID( sitePasswordInfo ) ) );
logger.trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )", CodeUtils.encodeHex( toID( masterKey ) ) ); logger.trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )", CodeUtils.encodeHex( toID( masterKey ) ) );
byte[] sitePasswordSeedBytes = mpw_digest.of( masterKey, sitePasswordInfo ); byte[] sitePasswordSeedBytes = mpw_digest().of( masterKey, sitePasswordInfo );
logger.trc( " => siteKey.id: %s", CodeUtils.encodeHex( toID( sitePasswordSeedBytes ) ) ); logger.trc( " => siteKey.id: %s", CodeUtils.encodeHex( toID( sitePasswordSeedBytes ) ) );
return sitePasswordSeedBytes; return sitePasswordSeedBytes;
@ -142,7 +138,7 @@ public class MPAlgorithmV0 implements MPAlgorithm {
int[] _siteKey = new int[siteKey.length]; int[] _siteKey = new int[siteKey.length];
for (int i = 0; i < siteKey.length; ++i) { for (int i = 0; i < siteKey.length; ++i) {
ByteBuffer buf = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( mpw_byteOrder ); ByteBuffer buf = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( mpw_byteOrder() );
Arrays.fill( buf.array(), (byte) ((siteKey[i] > 0)? 0x00: 0xFF) ); Arrays.fill( buf.array(), (byte) ((siteKey[i] > 0)? 0x00: 0xFF) );
buf.position( 2 ); buf.position( 2 );
buf.put( siteKey[i] ).rewind(); buf.put( siteKey[i] ).rewind();
@ -185,7 +181,7 @@ public class MPAlgorithmV0 implements MPAlgorithm {
// Decrypt // Decrypt
byte[] plainBuf = CryptUtils.decrypt( cipherBuf, masterKey, true ); byte[] plainBuf = CryptUtils.decrypt( cipherBuf, masterKey, true );
String plainText = mpw_charset.decode( ByteBuffer.wrap( plainBuf ) ).toString(); String plainText = mpw_charset().decode( ByteBuffer.wrap( plainBuf ) ).toString();
logger.trc( "decrypted -> plainText: %d bytes = %s = %s", plainBuf.length, plainText, CodeUtils.encodeHex( plainBuf ) ); logger.trc( "decrypted -> plainText: %d bytes = %s = %s", plainBuf.length, plainText, CodeUtils.encodeHex( plainBuf ) );
return plainText; return plainText;
@ -202,14 +198,14 @@ public class MPAlgorithmV0 implements MPAlgorithm {
if (resultType == MPResultType.DeriveKey) { if (resultType == MPResultType.DeriveKey) {
int resultParamInt = ConversionUtils.toIntegerNN( resultParam ); int resultParamInt = ConversionUtils.toIntegerNN( resultParam );
if (resultParamInt == 0) if (resultParamInt == 0)
resultParamInt = 512; resultParamInt = mpw_keySize_max();
if ((resultParamInt < 128) || (resultParamInt > 512) || ((resultParamInt % 8) != 0)) if ((resultParamInt < mpw_keySize_min()) || (resultParamInt > mpw_keySize_max()) || ((resultParamInt % 8) != 0))
throw logger.bug( "Parameter is not a valid key size (should be 128 - 512): %s", resultParam ); throw logger.bug( "Parameter is not a valid key size (should be 128 - 512): %s", resultParam );
int keySize = resultParamInt / 8; int keySize = resultParamInt / 8;
logger.trc( "keySize: %d", keySize ); logger.trc( "keySize: %d", keySize );
// Derive key // Derive key
byte[] resultKey = null; // TODO: mpw_kdf_blake2b( keySize, siteKey, MPSiteKeySize, NULL, 0, 0, NULL ); byte[] resultKey = null; // TODO: mpw_kdf_blake2b()( keySize, siteKey, MPSiteKeySize, NULL, 0, 0, NULL );
if (resultKey == null) if (resultKey == null)
throw logger.bug( "Could not derive result key." ); throw logger.bug( "Could not derive result key." );
@ -229,7 +225,7 @@ public class MPAlgorithmV0 implements MPAlgorithm {
try { try {
// Encrypt // Encrypt
byte[] cipherBuf = CryptUtils.encrypt( resultParam.getBytes( mpw_charset ), masterKey, true ); byte[] cipherBuf = CryptUtils.encrypt( resultParam.getBytes( mpw_charset() ), masterKey, true );
logger.trc( "cipherBuf: %d bytes = %s", cipherBuf.length, CodeUtils.encodeHex( cipherBuf ) ); logger.trc( "cipherBuf: %d bytes = %s", cipherBuf.length, CodeUtils.encodeHex( cipherBuf ) );
// Base64-encode // Base64-encode
@ -243,21 +239,139 @@ public class MPAlgorithmV0 implements MPAlgorithm {
} }
} }
// Configuration
@Override
public MPMasterKey.Version version() {
return MPMasterKey.Version.V0;
}
/**
* mpw: defaults: password result type.
*/
@Override
public MPResultType mpw_default_type() {
return MPResultType.GeneratedLong;
}
/**
* mpw: defaults: initial counter value.
*/
@Override
public UnsignedInteger mpw_default_counter() {
return UnsignedInteger.ONE;
}
/**
* mpw: validity for the time-based rolling counter.
*/
@Override
@SuppressWarnings("MagicNumber")
public long mpw_otp_window() {
return 5 * 60 /* s */;
}
/**
* mpw: Key ID hash.
*/
@Override
public MessageDigests mpw_hash() {
return MessageDigests.SHA256;
}
/**
* mpw: Site digest.
*/
@Override
public MessageAuthenticationDigests mpw_digest() {
return MessageAuthenticationDigests.HmacSHA256;
}
/**
* mpw: Platform-agnostic byte order.
*/
@Override
public ByteOrder mpw_byteOrder() {
return ByteOrder.BIG_ENDIAN;
}
/**
* mpw: Input character encoding.
*/
@Override
public Charset mpw_charset() {
return Charsets.UTF_8;
}
/**
* mpw: Master key size (byte).
*/
@Override
@SuppressWarnings("MagicNumber")
public int mpw_dkLen() {
return 64;
}
/**
* mpw: Minimum size for derived keys (bit).
*/
@Override
@SuppressWarnings("MagicNumber")
public int mpw_keySize_min() {
return 128;
}
/**
* mpw: Maximum size for derived keys (bit).
*/
@Override
@SuppressWarnings("MagicNumber")
public int mpw_keySize_max() {
return 512;
}
/**
* scrypt: Parallelization parameter.
*/
@Override
@SuppressWarnings("MagicNumber")
public int scrypt_p() {
return 2;
}
/**
* scrypt: Memory cost parameter.
*/
@Override
@SuppressWarnings("MagicNumber")
public int scrypt_r() {
return 8;
}
/**
* scrypt: CPU cost parameter.
*/
@Override
@SuppressWarnings("MagicNumber")
public int scrypt_N() {
return 32768;
}
// Utilities // Utilities
@Override @Override
public byte[] toBytes(final int number) { public byte[] toBytes(final int number) {
return ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( mpw_byteOrder ).putInt( number ).array(); return ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( mpw_byteOrder() ).putInt( number ).array();
} }
@Override @Override
public byte[] toBytes(final UnsignedInteger number) { public byte[] toBytes(final UnsignedInteger number) {
return ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( mpw_byteOrder ).putInt( number.intValue() ).array(); return ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( mpw_byteOrder() ).putInt( number.intValue() ).array();
} }
@Override @Override
public byte[] toBytes(final char[] characters) { public byte[] toBytes(final char[] characters) {
ByteBuffer byteBuffer = mpw_charset.encode( CharBuffer.wrap( characters ) ); ByteBuffer byteBuffer = mpw_charset().encode( CharBuffer.wrap( characters ) );
byte[] bytes = new byte[byteBuffer.remaining()]; byte[] bytes = new byte[byteBuffer.remaining()];
byteBuffer.get( bytes ); byteBuffer.get( bytes );
@ -268,6 +382,6 @@ public class MPAlgorithmV0 implements MPAlgorithm {
@Override @Override
public byte[] toID(final byte[] bytes) { public byte[] toID(final byte[] bytes) {
return mpw_hash.of( bytes ); return mpw_hash().of( bytes );
} }
} }

View File

@ -29,12 +29,6 @@ import javax.annotation.Nullable;
*/ */
public class MPAlgorithmV1 extends MPAlgorithmV0 { public class MPAlgorithmV1 extends MPAlgorithmV0 {
@Override
public MPMasterKey.Version getAlgorithmVersion() {
return MPMasterKey.Version.V1;
}
@Override @Override
public String sitePasswordFromTemplate(final byte[] masterKey, final byte[] siteKey, final MPResultType resultType, public String sitePasswordFromTemplate(final byte[] masterKey, final byte[] siteKey, final MPResultType resultType,
@Nullable final String resultParam) { @Nullable final String resultParam) {
@ -60,4 +54,11 @@ public class MPAlgorithmV1 extends MPAlgorithmV0 {
return password.toString(); return password.toString();
} }
// Configuration
@Override
public MPMasterKey.Version version() {
return MPMasterKey.Version.V1;
}
} }

View File

@ -30,12 +30,6 @@ import javax.annotation.Nullable;
*/ */
public class MPAlgorithmV2 extends MPAlgorithmV1 { public class MPAlgorithmV2 extends MPAlgorithmV1 {
@Override
public MPMasterKey.Version getAlgorithmVersion() {
return MPMasterKey.Version.V2;
}
@Override @Override
public byte[] siteKey(final byte[] masterKey, final String siteName, UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose, public byte[] siteKey(final byte[] masterKey, final String siteName, UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
@Nullable final String keyContext) { @Nullable final String keyContext) {
@ -45,27 +39,34 @@ public class MPAlgorithmV2 extends MPAlgorithmV1 {
// OTP counter value. // OTP counter value.
if (siteCounter.longValue() == 0) if (siteCounter.longValue() == 0)
siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (mpw_otp_window * 1000)) * mpw_otp_window ); siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (mpw_otp_window() * 1000)) * mpw_otp_window() );
// Calculate the site seed. // Calculate the site seed.
byte[] siteNameBytes = siteName.getBytes( mpw_charset ); byte[] siteNameBytes = siteName.getBytes( mpw_charset() );
byte[] siteNameLengthBytes = toBytes( siteNameBytes.length ); byte[] siteNameLengthBytes = toBytes( siteNameBytes.length );
byte[] siteCounterBytes = toBytes( siteCounter ); byte[] siteCounterBytes = toBytes( siteCounter );
byte[] keyContextBytes = ((keyContext == null) || keyContext.isEmpty())? null: keyContext.getBytes( mpw_charset ); byte[] keyContextBytes = ((keyContext == null) || keyContext.isEmpty())? null: keyContext.getBytes( mpw_charset() );
byte[] keyContextLengthBytes = (keyContextBytes == null)? null: toBytes( keyContextBytes.length ); byte[] keyContextLengthBytes = (keyContextBytes == null)? null: toBytes( keyContextBytes.length );
logger.trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s", logger.trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s",
keyScope, CodeUtils.encodeHex( siteNameLengthBytes ), siteName, CodeUtils.encodeHex( siteCounterBytes ), keyScope, CodeUtils.encodeHex( siteNameLengthBytes ), siteName, CodeUtils.encodeHex( siteCounterBytes ),
(keyContextLengthBytes == null)? null: CodeUtils.encodeHex( keyContextLengthBytes ), keyContext ); (keyContextLengthBytes == null)? null: CodeUtils.encodeHex( keyContextLengthBytes ), keyContext );
byte[] sitePasswordInfo = Bytes.concat( keyScope.getBytes( mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes ); byte[] sitePasswordInfo = Bytes.concat( keyScope.getBytes( mpw_charset() ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
if (keyContextBytes != null) if (keyContextBytes != null)
sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes ); sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes );
logger.trc( " => siteSalt.id: %s", CodeUtils.encodeHex( toID( sitePasswordInfo ) ) ); logger.trc( " => siteSalt.id: %s", CodeUtils.encodeHex( toID( sitePasswordInfo ) ) );
logger.trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )", CodeUtils.encodeHex( toID( masterKey ) ) ); logger.trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )", CodeUtils.encodeHex( toID( masterKey ) ) );
byte[] sitePasswordSeedBytes = mpw_digest.of( masterKey, sitePasswordInfo ); byte[] sitePasswordSeedBytes = mpw_digest().of( masterKey, sitePasswordInfo );
logger.trc( " => siteKey.id: %s", CodeUtils.encodeHex( toID( sitePasswordSeedBytes ) ) ); logger.trc( " => siteKey.id: %s", CodeUtils.encodeHex( toID( sitePasswordSeedBytes ) ) );
return sitePasswordSeedBytes; return sitePasswordSeedBytes;
} }
// Configuration
@Override
public MPMasterKey.Version version() {
return MPMasterKey.Version.V2;
}
} }

View File

@ -29,16 +29,10 @@ import java.util.Arrays;
*/ */
public class MPAlgorithmV3 extends MPAlgorithmV2 { public class MPAlgorithmV3 extends MPAlgorithmV2 {
@Override
public MPMasterKey.Version getAlgorithmVersion() {
return MPMasterKey.Version.V3;
}
@Override @Override
public byte[] masterKey(final String fullName, final char[] masterPassword) { public byte[] masterKey(final String fullName, final char[] masterPassword) {
byte[] fullNameBytes = fullName.getBytes( mpw_charset ); byte[] fullNameBytes = fullName.getBytes( mpw_charset() );
byte[] fullNameLengthBytes = toBytes( fullNameBytes.length ); byte[] fullNameLengthBytes = toBytes( fullNameBytes.length );
String keyScope = MPKeyPurpose.Authentication.getScope(); String keyScope = MPKeyPurpose.Authentication.getScope();
@ -47,12 +41,12 @@ public class MPAlgorithmV3 extends MPAlgorithmV2 {
// Calculate the master key salt. // Calculate the master key salt.
logger.trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s", logger.trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s",
keyScope, CodeUtils.encodeHex( fullNameLengthBytes ), fullName ); keyScope, CodeUtils.encodeHex( fullNameLengthBytes ), fullName );
byte[] masterKeySalt = Bytes.concat( keyScope.getBytes( mpw_charset ), fullNameLengthBytes, fullNameBytes ); byte[] masterKeySalt = Bytes.concat( keyScope.getBytes( mpw_charset() ), fullNameLengthBytes, fullNameBytes );
logger.trc( " => masterKeySalt.id: %s", CodeUtils.encodeHex( toID( masterKeySalt ) ) ); logger.trc( " => masterKeySalt.id: %s", CodeUtils.encodeHex( toID( masterKeySalt ) ) );
// Calculate the master key. // Calculate the master key.
logger.trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%d, r=%d, p=%d )", logger.trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%d, r=%d, p=%d )",
scrypt_N, scrypt_r, scrypt_p ); scrypt_N(), scrypt_r(), scrypt_p() );
byte[] mpBytes = toBytes( masterPassword ); byte[] mpBytes = toBytes( masterPassword );
byte[] masterKey = scrypt( masterKeySalt, mpBytes ); byte[] masterKey = scrypt( masterKeySalt, mpBytes );
Arrays.fill( masterKeySalt, (byte) 0 ); Arrays.fill( masterKeySalt, (byte) 0 );
@ -61,4 +55,11 @@ public class MPAlgorithmV3 extends MPAlgorithmV2 {
return masterKey; return masterKey;
} }
// Configuration
@Override
public MPMasterKey.Version version() {
return MPMasterKey.Version.V3;
}
} }

View File

@ -21,6 +21,8 @@ package com.lyndir.masterpassword;
import static com.lyndir.lhunath.opal.system.util.StringUtils.*; import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
import com.google.common.primitives.UnsignedBytes;
import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.MessageAuthenticationDigests; import com.lyndir.lhunath.opal.system.MessageAuthenticationDigests;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import java.nio.*; import java.nio.*;
@ -66,7 +68,7 @@ public class MPIdenticon {
IntBuffer identiconSeedBuffer = IntBuffer.allocate( identiconSeedBytes.capacity() ); IntBuffer identiconSeedBuffer = IntBuffer.allocate( identiconSeedBytes.capacity() );
while (identiconSeedBytes.hasRemaining()) while (identiconSeedBytes.hasRemaining())
identiconSeedBuffer.put( identiconSeedBytes.get() & 0xFF ); identiconSeedBuffer.put( UnsignedBytes.toInt( identiconSeedBytes.get() ) );
int[] identiconSeed = identiconSeedBuffer.array(); int[] identiconSeed = identiconSeedBuffer.array();
color = colors[identiconSeed[4] % colors.length]; color = colors[identiconSeed[4] % colors.length];

View File

@ -58,21 +58,21 @@ public class MPMasterKey {
* *
* @throws MPInvalidatedException {@link #invalidate()} has been called on this object. * @throws MPInvalidatedException {@link #invalidate()} has been called on this object.
*/ */
private byte[] masterKey(final Version algorithmVersion) private byte[] masterKey(final MPAlgorithm algorithm)
throws MPInvalidatedException { throws MPInvalidatedException {
Preconditions.checkArgument( masterPassword.length > 0 ); Preconditions.checkArgument( masterPassword.length > 0 );
if (invalidated) if (invalidated)
throw new MPInvalidatedException(); throw new MPInvalidatedException();
byte[] key = keyByVersion.get( algorithmVersion ); byte[] key = keyByVersion.get( algorithm.version() );
if (key == null) { if (key == null) {
logger.trc( "-- mpw_masterKey (algorithm: %d)", algorithmVersion.toInt() ); logger.trc( "-- mpw_masterKey (algorithm: %s)", algorithm );
logger.trc( "fullName: %s", fullName ); logger.trc( "fullName: %s", fullName );
logger.trc( "masterPassword.id: %s", CodeUtils.encodeHex( logger.trc( "masterPassword.id: %s", CodeUtils.encodeHex(
algorithmVersion.getAlgorithm().toID( algorithmVersion.getAlgorithm().toBytes( masterPassword ) ) ) ); algorithm.toID( algorithm.toBytes( masterPassword ) ) ) );
keyByVersion.put( algorithmVersion, key = algorithmVersion.getAlgorithm().masterKey( fullName, masterPassword ) ); keyByVersion.put( algorithm.version(), key = algorithm.masterKey( fullName, masterPassword ) );
} }
return key; return key;
@ -84,19 +84,19 @@ public class MPMasterKey {
* @throws MPInvalidatedException {@link #invalidate()} has been called on this object. * @throws MPInvalidatedException {@link #invalidate()} has been called on this object.
*/ */
private byte[] siteKey(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose, private byte[] siteKey(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
@Nullable final String keyContext, final Version algorithmVersion) @Nullable final String keyContext, final MPAlgorithm algorithm)
throws MPInvalidatedException { throws MPInvalidatedException {
Preconditions.checkArgument( !siteName.isEmpty() ); Preconditions.checkArgument( !siteName.isEmpty() );
byte[] masterKey = masterKey( algorithmVersion ); byte[] masterKey = masterKey( algorithm );
logger.trc( "-- mpw_siteKey (algorithm: %d)", algorithmVersion.toInt() ); logger.trc( "-- mpw_siteKey (algorithm: %s)", algorithm );
logger.trc( "siteName: %s", siteName ); logger.trc( "siteName: %s", siteName );
logger.trc( "siteCounter: %s", siteCounter ); logger.trc( "siteCounter: %s", siteCounter );
logger.trc( "keyPurpose: %d (%s)", keyPurpose.toInt(), keyPurpose.getShortName() ); logger.trc( "keyPurpose: %d (%s)", keyPurpose.toInt(), keyPurpose.getShortName() );
logger.trc( "keyContext: %s", keyContext ); logger.trc( "keyContext: %s", keyContext );
return algorithmVersion.getAlgorithm().siteKey( masterKey, siteName, siteCounter, keyPurpose, keyContext ); return algorithm.siteKey( masterKey, siteName, siteCounter, keyPurpose, keyContext );
} }
/** /**
@ -108,23 +108,23 @@ public class MPMasterKey {
* @param keyContext A site-scoped result modifier. * @param keyContext A site-scoped result modifier.
* @param resultType The type of result to generate. * @param resultType The type of result to generate.
* @param resultParam A parameter for the resultType. For stateful result types, the output of * @param resultParam A parameter for the resultType. For stateful result types, the output of
* {@link #siteState(String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String, Version)}. * {@link #siteState(String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String, MPAlgorithm)}.
* *
* @throws MPInvalidatedException {@link #invalidate()} has been called on this object. * @throws MPInvalidatedException {@link #invalidate()} has been called on this object.
*/ */
public String siteResult(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose, public String siteResult(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
@Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam, @Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam,
final Version algorithmVersion) final MPAlgorithm algorithm)
throws MPInvalidatedException { throws MPInvalidatedException {
byte[] masterKey = masterKey( algorithmVersion ); byte[] masterKey = masterKey( algorithm );
byte[] siteKey = siteKey( siteName, siteCounter, keyPurpose, keyContext, algorithmVersion ); byte[] siteKey = siteKey( siteName, siteCounter, keyPurpose, keyContext, algorithm );
logger.trc( "-- mpw_siteResult (algorithm: %d)", algorithmVersion.toInt() ); logger.trc( "-- mpw_siteResult (algorithm: %s)", algorithm );
logger.trc( "resultType: %d (%s)", resultType.getType(), resultType.getShortName() ); logger.trc( "resultType: %d (%s)", resultType.getType(), resultType.getShortName() );
logger.trc( "resultParam: %s", resultParam ); logger.trc( "resultParam: %s", resultParam );
return algorithmVersion.getAlgorithm().siteResult( return algorithm.siteResult(
masterKey, siteKey, siteName, siteCounter, keyPurpose, keyContext, resultType, resultParam ); masterKey, siteKey, siteName, siteCounter, keyPurpose, keyContext, resultType, resultParam );
} }
@ -137,26 +137,26 @@ public class MPMasterKey {
* @param keyContext A site-scoped key modifier. * @param keyContext A site-scoped key modifier.
* @param resultType The type of result token to encrypt. * @param resultType The type of result token to encrypt.
* @param resultParam The result token desired from * @param resultParam The result token desired from
* {@link #siteResult(String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String, Version)}. * {@link #siteResult(String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String, MPAlgorithm)}.
* *
* @throws MPInvalidatedException {@link #invalidate()} has been called on this object. * @throws MPInvalidatedException {@link #invalidate()} has been called on this object.
*/ */
public String siteState(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose, public String siteState(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
@Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam, @Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam,
final Version algorithmVersion) final MPAlgorithm algorithm)
throws MPInvalidatedException { throws MPInvalidatedException {
Preconditions.checkNotNull( resultParam ); Preconditions.checkNotNull( resultParam );
Preconditions.checkArgument( !resultParam.isEmpty() ); Preconditions.checkArgument( !resultParam.isEmpty() );
byte[] masterKey = masterKey( algorithmVersion ); byte[] masterKey = masterKey( algorithm );
byte[] siteKey = siteKey( siteName, siteCounter, keyPurpose, keyContext, algorithmVersion ); byte[] siteKey = siteKey( siteName, siteCounter, keyPurpose, keyContext, algorithm );
logger.trc( "-- mpw_siteState (algorithm: %d)", algorithmVersion.toInt() ); logger.trc( "-- mpw_siteState (algorithm: %s)", algorithm );
logger.trc( "resultType: %d (%s)", resultType.getType(), resultType.getShortName() ); logger.trc( "resultType: %d (%s)", resultType.getType(), resultType.getShortName() );
logger.trc( "resultParam: %d bytes = %s", resultParam.getBytes( MPAlgorithm.mpw_charset ).length, resultParam ); logger.trc( "resultParam: %d bytes = %s", resultParam.getBytes( algorithm.mpw_charset() ).length, resultParam );
return algorithmVersion.getAlgorithm().siteState( return algorithm.siteState(
masterKey, siteKey, siteName, siteCounter, keyPurpose, keyContext, resultType, resultParam ); masterKey, siteKey, siteName, siteCounter, keyPurpose, keyContext, resultType, resultParam );
} }
@ -171,10 +171,10 @@ public class MPMasterKey {
* *
* @throws MPInvalidatedException {@link #invalidate()} has been called on this object. * @throws MPInvalidatedException {@link #invalidate()} has been called on this object.
*/ */
public byte[] getKeyID(final Version algorithmVersion) public byte[] getKeyID(final MPAlgorithm algorithm)
throws MPInvalidatedException { throws MPInvalidatedException {
return algorithmVersion.getAlgorithm().toID( masterKey( algorithmVersion ) ); return algorithm.toID( masterKey( algorithm ) );
} }
/** /**

View File

@ -31,6 +31,7 @@ import org.jetbrains.annotations.Contract;
* *
* @author lhunath * @author lhunath
*/ */
@SuppressWarnings("RedundantTypeArguments" /* IDEA-191043 */)
public enum MPResultType { public enum MPResultType {
// bit 0-3 | MPResultTypeClass | MPSiteFeature // bit 0-3 | MPResultTypeClass | MPSiteFeature
@ -134,7 +135,7 @@ public enum MPResultType {
private final List<MPTemplate> templates; private final List<MPTemplate> templates;
private final MPResultTypeClass typeClass; private final MPResultTypeClass typeClass;
private final int typeIndex; private final int typeIndex;
private final Set<MPSiteFeature> typeFeatures; private final ImmutableSet<MPSiteFeature> typeFeatures;
MPResultType(final String shortName, final String description, final List<MPTemplate> templates, MPResultType(final String shortName, final String description, final List<MPTemplate> templates,
final MPResultTypeClass typeClass, final int typeIndex, final MPSiteFeature... typeFeatures) { final MPResultTypeClass typeClass, final int typeIndex, final MPSiteFeature... typeFeatures) {
@ -166,7 +167,8 @@ public enum MPResultType {
return typeClass; return typeClass;
} }
public Set<MPSiteFeature> getTypeFeatures() { @SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType" /* IDEA-191042 */ )
public ImmutableSet<MPSiteFeature> getTypeFeatures() {
return typeFeatures; return typeFeatures;
} }
@ -232,6 +234,7 @@ public enum MPResultType {
* *
* @return All types that support the given mask's class & features. * @return All types that support the given mask's class & features.
*/ */
@SuppressWarnings({ "MagicNumber", "UnnecessaryParentheses" /* IDEA-191040 */ })
public static ImmutableList<MPResultType> forMask(final int mask) { public static ImmutableList<MPResultType> forMask(final int mask) {
int typeMask = mask & ~0xF; // Ignore resultType bit 0-3 int typeMask = mask & ~0xF; // Ignore resultType bit 0-3

View File

@ -35,7 +35,7 @@ public class MPFileSite extends MPSite {
private String siteContent; private String siteContent;
private UnsignedInteger siteCounter; private UnsignedInteger siteCounter;
private MPResultType resultType; private MPResultType resultType;
private MPMasterKey.Version algorithmVersion; private MPAlgorithm algorithm;
@Nullable @Nullable
private String loginContent; private String loginContent;
@ -49,24 +49,24 @@ public class MPFileSite extends MPSite {
public MPFileSite(final MPFileUser user, final String siteName) { public MPFileSite(final MPFileUser user, final String siteName) {
this( user, siteName, this( user, siteName,
user.getAlgorithmVersion().getAlgorithm().mpw_default_counter, user.getAlgorithm().mpw_default_counter(),
user.getAlgorithmVersion().getAlgorithm().mpw_default_type, user.getAlgorithm().mpw_default_type(),
user.getAlgorithmVersion() ); user.getAlgorithm() );
} }
public MPFileSite(final MPFileUser user, final String siteName, final UnsignedInteger siteCounter, final MPResultType resultType, public MPFileSite(final MPFileUser user, final String siteName, final UnsignedInteger siteCounter, final MPResultType resultType,
final MPMasterKey.Version algorithmVersion) { final MPAlgorithm algorithm) {
this.user = user; this.user = user;
this.siteName = siteName; this.siteName = siteName;
this.siteCounter = siteCounter; this.siteCounter = siteCounter;
this.resultType = resultType; this.resultType = resultType;
this.algorithmVersion = algorithmVersion; this.algorithm = algorithm;
this.lastUsed = new Instant(); this.lastUsed = new Instant();
} }
protected MPFileSite(final MPFileUser user, final String siteName, @Nullable final String siteContent, protected MPFileSite(final MPFileUser user, final String siteName, @Nullable final String siteContent,
final UnsignedInteger siteCounter, final UnsignedInteger siteCounter,
final MPResultType resultType, final MPMasterKey.Version algorithmVersion, final MPResultType resultType, final MPAlgorithm algorithm,
@Nullable final String loginContent, @Nullable final MPResultType loginType, @Nullable final String loginContent, @Nullable final MPResultType loginType,
@Nullable final String url, final int uses, final Instant lastUsed) { @Nullable final String url, final int uses, final Instant lastUsed) {
this.user = user; this.user = user;
@ -74,7 +74,7 @@ public class MPFileSite extends MPSite {
this.siteContent = siteContent; this.siteContent = siteContent;
this.siteCounter = siteCounter; this.siteCounter = siteCounter;
this.resultType = resultType; this.resultType = resultType;
this.algorithmVersion = algorithmVersion; this.algorithm = algorithm;
this.loginContent = loginContent; this.loginContent = loginContent;
this.loginType = loginType; this.loginType = loginType;
this.url = url; this.url = url;
@ -129,7 +129,7 @@ public class MPFileSite extends MPSite {
this.siteContent = null; this.siteContent = null;
else else
this.siteContent = masterKey.siteState( this.siteContent = masterKey.siteState(
getSiteName(), getSiteCounter(), MPKeyPurpose.Authentication, null, getResultType(), result, getAlgorithmVersion() ); getSiteName(), getSiteCounter(), MPKeyPurpose.Authentication, null, getResultType(), result, algorithm );
} }
@Override @Override
@ -153,13 +153,13 @@ public class MPFileSite extends MPSite {
} }
@Override @Override
public MPMasterKey.Version getAlgorithmVersion() { public MPAlgorithm getAlgorithm() {
return algorithmVersion; return algorithm;
} }
@Override @Override
public void setAlgorithmVersion(final MPMasterKey.Version algorithmVersion) { public void setAlgorithm(final MPAlgorithm algorithm) {
this.algorithmVersion = algorithmVersion; this.algorithm = algorithm;
} }
@Nullable @Nullable
@ -180,8 +180,8 @@ public class MPFileSite extends MPSite {
this.loginContent = null; this.loginContent = null;
else else
this.loginContent = masterKey.siteState( this.loginContent = masterKey.siteState(
siteName, MPAlgorithm.mpw_default_counter, MPKeyPurpose.Identification, null, this.loginType, result, siteName, algorithm.mpw_default_counter(), MPKeyPurpose.Identification, null, this.loginType, result,
algorithmVersion ); algorithm );
} }
@Nullable @Nullable

View File

@ -22,8 +22,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.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.masterpassword.*; import com.lyndir.masterpassword.*;
import java.util.Arrays; import java.util.*;
import java.util.Collection;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.joda.time.Instant; import org.joda.time.Instant;
@ -43,7 +42,7 @@ public class MPFileUser extends MPUser<MPFileSite> implements Comparable<MPFileU
@Nullable @Nullable
private byte[] keyID; private byte[] keyID;
private MPMasterKey.Version algorithmVersion; private MPAlgorithm algorithm;
private MPMarshalFormat format; private MPMarshalFormat format;
private int avatar; private int avatar;
@ -51,18 +50,18 @@ public class MPFileUser extends MPUser<MPFileSite> implements Comparable<MPFileU
private ReadableInstant lastUsed; private ReadableInstant lastUsed;
public MPFileUser(final String fullName) { public MPFileUser(final String fullName) {
this( fullName, null, MPMasterKey.Version.CURRENT ); this( fullName, null, MPMasterKey.Version.CURRENT.getAlgorithm() );
} }
public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPMasterKey.Version algorithmVersion) { public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPAlgorithm algorithm) {
this( fullName, keyID, algorithmVersion, 0, MPAlgorithm.mpw_default_type, new Instant(), MPMarshalFormat.DEFAULT ); this( fullName, keyID, algorithm, 0, algorithm.mpw_default_type(), new Instant(), MPMarshalFormat.DEFAULT );
} }
public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPMasterKey.Version algorithmVersion, final int avatar, public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPAlgorithm algorithm, final int avatar,
final MPResultType defaultType, final ReadableInstant lastUsed, final MPMarshalFormat format) { final MPResultType defaultType, final ReadableInstant lastUsed, final MPMarshalFormat format) {
this.fullName = fullName; this.fullName = fullName;
this.keyID = (keyID == null)? null: keyID.clone(); this.keyID = (keyID == null)? null: keyID.clone();
this.algorithmVersion = algorithmVersion; this.algorithm = algorithm;
this.avatar = avatar; this.avatar = avatar;
this.defaultType = defaultType; this.defaultType = defaultType;
this.lastUsed = lastUsed; this.lastUsed = lastUsed;
@ -75,12 +74,12 @@ public class MPFileUser extends MPUser<MPFileSite> implements Comparable<MPFileU
} }
@Override @Override
public MPMasterKey.Version getAlgorithmVersion() { public MPAlgorithm getAlgorithm() {
return algorithmVersion; return algorithm;
} }
public void setAlgorithmVersion(final MPMasterKey.Version algorithmVersion) { public void setAlgorithm(final MPAlgorithm algorithm) {
this.algorithmVersion = algorithmVersion; this.algorithm = algorithm;
} }
public MPMarshalFormat getFormat() { public MPMarshalFormat getFormat() {
@ -117,7 +116,7 @@ public class MPFileUser extends MPUser<MPFileSite> implements Comparable<MPFileU
} }
public Iterable<MPFileSite> getSites() { public Iterable<MPFileSite> getSites() {
return sites; return Collections.unmodifiableCollection( sites );
} }
@Override @Override
@ -158,8 +157,8 @@ public class MPFileUser extends MPUser<MPFileSite> implements Comparable<MPFileU
try { try {
key = new MPMasterKey( getFullName(), masterPassword ); key = new MPMasterKey( getFullName(), masterPassword );
if ((keyID == null) || (keyID.length == 0)) if ((keyID == null) || (keyID.length == 0))
keyID = key.getKeyID( algorithmVersion ); keyID = key.getKeyID( algorithm );
else if (!Arrays.equals( key.getKeyID( algorithmVersion ), keyID )) else if (!Arrays.equals( key.getKeyID( algorithm ), keyID ))
throw new MPIncorrectMasterPasswordException( this ); throw new MPIncorrectMasterPasswordException( this );
return key; return key;

View File

@ -127,7 +127,7 @@ public class MPFileUserManager extends MPUserManager {
} }
}.write( user.getFormat().marshaller().marshall( user, masterKey, MPMarshaller.ContentMode.PROTECTED ) ); }.write( user.getFormat().marshaller().marshall( user, masterKey, MPMarshaller.ContentMode.PROTECTED ) );
} }
catch (final IOException e) { catch (final MPMarshalException | IOException e) {
logger.err( e, "Unable to save sites for user: %s", user ); logger.err( e, "Unable to save sites for user: %s", user );
} }
} }

View File

@ -48,7 +48,7 @@ public class MPFlatMarshaller implements MPMarshaller {
content.append( "# Full Name: " ).append( user.getFullName() ).append( '\n' ); content.append( "# Full Name: " ).append( user.getFullName() ).append( '\n' );
content.append( "# Avatar: " ).append( user.getAvatar() ).append( '\n' ); content.append( "# Avatar: " ).append( user.getAvatar() ).append( '\n' );
content.append( "# Key ID: " ).append( user.exportKeyID() ).append( '\n' ); content.append( "# Key ID: " ).append( user.exportKeyID() ).append( '\n' );
content.append( "# Algorithm: " ).append( user.getAlgorithmVersion().toInt() ).append( '\n' ); content.append( "# Algorithm: " ).append( user.getAlgorithm().version().toInt() ).append( '\n' );
content.append( "# Default Type: " ).append( user.getDefaultType().getType() ).append( '\n' ); content.append( "# Default Type: " ).append( user.getDefaultType().getType() ).append( '\n' );
content.append( "# Passwords: " ).append( contentMode.name() ).append( '\n' ); content.append( "# Passwords: " ).append( contentMode.name() ).append( '\n' );
content.append( "##\n" ); content.append( "##\n" );
@ -69,7 +69,7 @@ public class MPFlatMarshaller implements MPMarshaller {
site.getUses(), // uses site.getUses(), // uses
strf( "%d:%d:%d", // strf( "%d:%d:%d", //
site.getResultType().getType(), // type site.getResultType().getType(), // type
site.getAlgorithmVersion().toInt(), // algorithm site.getAlgorithm().version().toInt(), // algorithm
site.getSiteCounter().intValue() ), // counter site.getSiteCounter().intValue() ), // counter
ifNotNullElse( loginName, "" ), // loginName ifNotNullElse( loginName, "" ), // loginName
site.getSiteName(), // siteName site.getSiteName(), // siteName

View File

@ -62,7 +62,7 @@ public class MPFlatUnmarshaller implements MPUnmarshaller {
String fullName = null; String fullName = null;
int mpVersion = 0, importFormat = 0, avatar = 0; int mpVersion = 0, importFormat = 0, avatar = 0;
boolean clearContent = false, headerStarted = false; boolean clearContent = false, headerStarted = false;
MPResultType defaultType = MPAlgorithm.mpw_default_type; MPResultType defaultType = null;
//noinspection HardcodedLineSeparator //noinspection HardcodedLineSeparator
for (final String line : Splitter.on( CharMatcher.anyOf( "\r\n" ) ).omitEmptyStrings().split( content )) for (final String line : Splitter.on( CharMatcher.anyOf( "\r\n" ) ).omitEmptyStrings().split( content ))
@ -73,8 +73,8 @@ public class MPFlatUnmarshaller implements MPUnmarshaller {
headerStarted = true; headerStarted = true;
else else
// Ends the header. // Ends the header.
user = new MPFileUser( fullName, keyID, MPMasterKey.Version.fromInt( mpVersion ), avatar, defaultType, user = new MPFileUser( fullName, keyID, MPMasterKey.Version.fromInt( mpVersion ).getAlgorithm(),
new DateTime( 0 ), MPMarshalFormat.Flat ); avatar, defaultType, new DateTime( 0 ), MPMarshalFormat.Flat );
// Comment. // Comment.
else if (line.startsWith( "#" )) { else if (line.startsWith( "#" )) {
@ -114,10 +114,10 @@ public class MPFlatUnmarshaller implements MPUnmarshaller {
case 0: case 0:
site = new MPFileSite( user, // site = new MPFileSite( user, //
siteMatcher.group( 5 ), siteMatcher.group( 6 ), siteMatcher.group( 5 ), siteMatcher.group( 6 ),
user.getAlgorithmVersion().getAlgorithm().mpw_default_counter, user.getAlgorithm().mpw_default_counter(),
MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ), MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
MPMasterKey.Version.fromInt( ConversionUtils.toIntegerNN( MPMasterKey.Version.fromInt( ConversionUtils.toIntegerNN(
colon.matcher( siteMatcher.group( 4 ) ).replaceAll( "" ) ) ), colon.matcher( siteMatcher.group( 4 ) ).replaceAll( "" ) ) ).getAlgorithm(),
null, null, null, ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ), null, null, null, ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ),
MPConstant.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() ); MPConstant.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() );
break; break;
@ -128,7 +128,7 @@ public class MPFlatUnmarshaller implements MPUnmarshaller {
UnsignedInteger.valueOf( colon.matcher( siteMatcher.group( 5 ) ).replaceAll( "" ) ), UnsignedInteger.valueOf( colon.matcher( siteMatcher.group( 5 ) ).replaceAll( "" ) ),
MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ), MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
MPMasterKey.Version.fromInt( ConversionUtils.toIntegerNN( MPMasterKey.Version.fromInt( ConversionUtils.toIntegerNN(
colon.matcher( siteMatcher.group( 4 ) ).replaceAll( "" ) ) ), colon.matcher( siteMatcher.group( 4 ) ).replaceAll( "" ) ) ).getAlgorithm(),
siteMatcher.group( 6 ), MPResultType.GeneratedName, null, siteMatcher.group( 6 ), MPResultType.GeneratedName, null,
ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ), ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ),
MPConstant.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() ); MPConstant.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() );

View File

@ -43,24 +43,24 @@ public abstract class MPSite {
public abstract void setResultType(MPResultType resultType); public abstract void setResultType(MPResultType resultType);
public abstract MPMasterKey.Version getAlgorithmVersion(); public abstract MPAlgorithm getAlgorithm();
public abstract void setAlgorithmVersion(MPMasterKey.Version algorithmVersion); public abstract void setAlgorithm(MPAlgorithm algorithm);
public String resultFor(final MPMasterKey masterKey, final MPKeyPurpose keyPurpose, @Nullable final String keyContext, public String resultFor(final MPMasterKey masterKey, final MPKeyPurpose keyPurpose, @Nullable final String keyContext,
@Nullable final String siteContent) @Nullable final String siteContent)
throws MPInvalidatedException { throws MPInvalidatedException {
return masterKey.siteResult( return masterKey.siteResult(
getSiteName(), getSiteCounter(), keyPurpose, keyContext, getResultType(), siteContent, getAlgorithmVersion() ); getSiteName(), getSiteCounter(), keyPurpose, keyContext, getResultType(), siteContent, getAlgorithm() );
} }
public String loginFor(final MPMasterKey masterKey, final MPResultType loginType, @Nullable final String loginContent) public String loginFor(final MPMasterKey masterKey, final MPResultType loginType, @Nullable final String loginContent)
throws MPInvalidatedException { throws MPInvalidatedException {
return masterKey.siteResult( return masterKey.siteResult(
getSiteName(), MPAlgorithm.mpw_default_counter, MPKeyPurpose.Identification, null, loginType, loginContent, getSiteName(), getAlgorithm().mpw_default_counter(), MPKeyPurpose.Identification, null, loginType, loginContent,
getAlgorithmVersion() ); getAlgorithm() );
} }
@Override @Override

View File

@ -22,8 +22,7 @@ import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.lyndir.lhunath.opal.system.CodeUtils; import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.masterpassword.MPInvalidatedException; import com.lyndir.masterpassword.*;
import com.lyndir.masterpassword.MPMasterKey;
import java.util.Collection; import java.util.Collection;
import java.util.Objects; import java.util.Objects;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -51,10 +50,10 @@ public abstract class MPUser<S extends MPSite> {
public String exportKeyID() public String exportKeyID()
throws MPInvalidatedException { throws MPInvalidatedException {
return CodeUtils.encodeHex( getMasterKey().getKeyID( getAlgorithmVersion() ) ); return CodeUtils.encodeHex( getMasterKey().getKeyID( getAlgorithm() ) );
} }
public abstract MPMasterKey.Version getAlgorithmVersion(); public abstract MPAlgorithm getAlgorithm();
public int getAvatar() { public int getAvatar() {
return 0; return 0;

View File

@ -171,8 +171,8 @@ public class MPTests {
} }
@Nonnull @Nonnull
public MPMasterKey.Version getAlgorithm() { public MPAlgorithm getAlgorithm() {
return MPMasterKey.Version.fromInt( checkNotNull( algorithm ) ); return MPMasterKey.Version.fromInt( checkNotNull( algorithm ) ).getAlgorithm();
} }
@Nonnull @Nonnull

View File

@ -109,14 +109,15 @@ public class MPMasterKeyTest {
MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), masterPassword ); MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), masterPassword );
String password = randomString( 8 ); String password = randomString( 8 );
MPResultType resultType = MPResultType.StoredPersonal;
for (final MPMasterKey.Version version : MPMasterKey.Version.values()) { for (final MPMasterKey.Version version : MPMasterKey.Version.values()) {
MPResultType resultType = MPResultType.StoredPersonal; MPAlgorithm algorithm = version.getAlgorithm();
// Test site state // Test site state
String state = masterKey.siteState( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(), String state = masterKey.siteState( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
testCase.getKeyContext(), resultType, password, version ); testCase.getKeyContext(), resultType, password, algorithm );
String result = masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(), String result = masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
testCase.getKeyContext(), resultType, state, version ); testCase.getKeyContext(), resultType, state, algorithm );
assertEquals( assertEquals(
result, result,

View File

@ -303,7 +303,7 @@ public class EmergencyActivity extends Activity {
@Override @Override
public void run() { public void run() {
try { try {
sitePassword = masterKey.siteResult( siteName, counter, MPKeyPurpose.Authentication, null, type, null, version ); sitePassword = masterKey.siteResult( siteName, counter, MPKeyPurpose.Authentication, null, type, null, version.getAlgorithm() );
runOnUiThread( new Runnable() { runOnUiThread( new Runnable() {
@Override @Override

View File

@ -148,7 +148,7 @@ public final class Preferences {
@Nonnull @Nonnull
public MPResultType getDefaultResultType() { public MPResultType getDefaultResultType() {
return MPResultType.values()[prefs().getInt( PREF_RESULT_TYPE, MPAlgorithm.mpw_default_type.ordinal() )]; return MPResultType.values()[prefs().getInt( PREF_RESULT_TYPE, getDefaultVersion().getAlgorithm().mpw_default_type().ordinal() )];
} }
public boolean setDefaultVersion(final MPMasterKey.Version value) { public boolean setDefaultVersion(final MPMasterKey.Version value) {

View File

@ -38,6 +38,7 @@ import java.util.concurrent.Executors;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@SuppressWarnings("PublicMethodNotExposedInInterface" /* IDEA-191044 */)
public class TestActivity extends Activity implements MPTestSuite.Listener { public class TestActivity extends Activity implements MPTestSuite.Listener {
@SuppressWarnings("UnusedDeclaration") @SuppressWarnings("UnusedDeclaration")
@ -64,6 +65,7 @@ public class TestActivity extends Activity implements MPTestSuite.Listener {
private MPTestSuite testSuite; private MPTestSuite testSuite;
private ListenableFuture<Boolean> testFuture; private ListenableFuture<Boolean> testFuture;
@Nullable
private Runnable action; private Runnable action;
private ImmutableSet<String> testNames; private ImmutableSet<String> testNames;

View File

@ -19,8 +19,7 @@
package com.lyndir.masterpassword.gui.model; package com.lyndir.masterpassword.gui.model;
import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedInteger;
import com.lyndir.masterpassword.MPMasterKey; import com.lyndir.masterpassword.*;
import com.lyndir.masterpassword.MPResultType;
import com.lyndir.masterpassword.model.MPSite; import com.lyndir.masterpassword.model.MPSite;
@ -29,17 +28,17 @@ import com.lyndir.masterpassword.model.MPSite;
*/ */
public class IncognitoSite extends MPSite { public class IncognitoSite extends MPSite {
private String siteName; private String siteName;
private UnsignedInteger siteCounter; private UnsignedInteger siteCounter;
private MPResultType resultType; private MPResultType resultType;
private MPMasterKey.Version algorithmVersion; private MPAlgorithm algorithm;
public IncognitoSite(final String siteName, final UnsignedInteger siteCounter, final MPResultType resultType, public IncognitoSite(final String siteName, final UnsignedInteger siteCounter, final MPResultType resultType,
final MPMasterKey.Version algorithmVersion) { final MPAlgorithm algorithm) {
this.siteName = siteName; this.siteName = siteName;
this.siteCounter = siteCounter; this.siteCounter = siteCounter;
this.resultType = resultType; this.resultType = resultType;
this.algorithmVersion = algorithmVersion; this.algorithm = algorithm;
} }
@Override @Override
@ -63,13 +62,13 @@ public class IncognitoSite extends MPSite {
} }
@Override @Override
public MPMasterKey.Version getAlgorithmVersion() { public MPAlgorithm getAlgorithm() {
return algorithmVersion; return algorithm;
} }
@Override @Override
public void setAlgorithmVersion(final MPMasterKey.Version algorithmVersion) { public void setAlgorithm(final MPAlgorithm algorithm) {
this.algorithmVersion = algorithmVersion; this.algorithm = algorithm;
} }
@Override @Override

View File

@ -19,6 +19,7 @@
package com.lyndir.masterpassword.gui.model; package com.lyndir.masterpassword.gui.model;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.lyndir.masterpassword.MPAlgorithm;
import com.lyndir.masterpassword.MPMasterKey; import com.lyndir.masterpassword.MPMasterKey;
import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException; import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException;
import com.lyndir.masterpassword.model.MPUser; import com.lyndir.masterpassword.model.MPUser;
@ -43,8 +44,8 @@ public class IncognitoUser extends MPUser<IncognitoSite> {
} }
@Override @Override
public MPMasterKey.Version getAlgorithmVersion() { public MPAlgorithm getAlgorithm() {
return MPMasterKey.Version.CURRENT; return MPMasterKey.Version.CURRENT.getAlgorithm();
} }
@Override @Override

View File

@ -31,6 +31,8 @@ import javax.swing.border.CompoundBorder;
*/ */
public abstract class Components { public abstract class Components {
public static final float CONTROL_TEXT_SIZE = 12f;
public static GradientPanel boxLayout(final int axis, final Component... components) { public static GradientPanel boxLayout(final int axis, final Component... components) {
GradientPanel container = gradientPanel( null, null ); GradientPanel container = gradientPanel( null, null );
// container.setBackground( Color.red ); // container.setBackground( Color.red );
@ -73,7 +75,7 @@ public abstract class Components {
{ {
setBorder( BorderFactory.createCompoundBorder( BorderFactory.createLineBorder( Res.colors().controlBorder(), 1, true ), setBorder( BorderFactory.createCompoundBorder( BorderFactory.createLineBorder( Res.colors().controlBorder(), 1, true ),
BorderFactory.createEmptyBorder( 4, 4, 4, 4 ) ) ); BorderFactory.createEmptyBorder( 4, 4, 4, 4 ) ) );
setFont( Res.valueFont().deriveFont( 12f ) ); setFont( Res.valueFont().deriveFont( CONTROL_TEXT_SIZE ) );
setAlignmentX( LEFT_ALIGNMENT ); setAlignmentX( LEFT_ALIGNMENT );
setAlignmentY( BOTTOM_ALIGNMENT ); setAlignmentY( BOTTOM_ALIGNMENT );
} }
@ -104,7 +106,7 @@ public abstract class Components {
public static JButton button(final String label) { public static JButton button(final String label) {
return new JButton( label ) { return new JButton( label ) {
{ {
setFont( Res.controlFont().deriveFont( 12f ) ); setFont( Res.controlFont().deriveFont( CONTROL_TEXT_SIZE ) );
setAlignmentX( LEFT_ALIGNMENT ); setAlignmentX( LEFT_ALIGNMENT );
setAlignmentY( BOTTOM_ALIGNMENT ); setAlignmentY( BOTTOM_ALIGNMENT );
} }
@ -160,7 +162,7 @@ public abstract class Components {
public static JLabel label(@Nullable final String label, final int horizontalAlignment) { public static JLabel label(@Nullable final String label, final int horizontalAlignment) {
return new JLabel( label, horizontalAlignment ) { return new JLabel( label, horizontalAlignment ) {
{ {
setFont( Res.controlFont().deriveFont( 12f ) ); setFont( Res.controlFont().deriveFont( CONTROL_TEXT_SIZE ) );
setAlignmentX( LEFT_ALIGNMENT ); setAlignmentX( LEFT_ALIGNMENT );
setAlignmentY( BOTTOM_ALIGNMENT ); setAlignmentY( BOTTOM_ALIGNMENT );
} }
@ -175,7 +177,7 @@ public abstract class Components {
public static JCheckBox checkBox(final String label) { public static JCheckBox checkBox(final String label) {
return new JCheckBox( label ) { return new JCheckBox( label ) {
{ {
setFont( Res.controlFont().deriveFont( 12f ) ); setFont( Res.controlFont().deriveFont( CONTROL_TEXT_SIZE ) );
setBackground( null ); setBackground( null );
setAlignmentX( LEFT_ALIGNMENT ); setAlignmentX( LEFT_ALIGNMENT );
setAlignmentY( BOTTOM_ALIGNMENT ); setAlignmentY( BOTTOM_ALIGNMENT );
@ -195,7 +197,7 @@ public abstract class Components {
// BorderFactory.createLineBorder( Res.colors().controlBorder(), 1, true ), // BorderFactory.createLineBorder( Res.colors().controlBorder(), 1, true ),
// BorderFactory.createEmptyBorder( 4, 4, 4, 4 ) ); // BorderFactory.createEmptyBorder( 4, 4, 4, 4 ) );
// ((JComponent) ((BasicComboBoxEditor) getEditor()).getEditorComponent()).setBorder(editorBorder); // ((JComponent) ((BasicComboBoxEditor) getEditor()).getEditorComponent()).setBorder(editorBorder);
setFont( Res.controlFont().deriveFont( 12f ) ); setFont( Res.controlFont().deriveFont( CONTROL_TEXT_SIZE ) );
setAlignmentX( LEFT_ALIGNMENT ); setAlignmentX( LEFT_ALIGNMENT );
setAlignmentY( BOTTOM_ALIGNMENT ); setAlignmentY( BOTTOM_ALIGNMENT );
// setBorder(null); // setBorder(null);

View File

@ -27,6 +27,8 @@ import javax.swing.*;
*/ */
public class UnsignedIntegerModel extends SpinnerNumberModel { public class UnsignedIntegerModel extends SpinnerNumberModel {
private static final long serialVersionUID = 1L;
public UnsignedIntegerModel() { public UnsignedIntegerModel() {
this( UnsignedInteger.ZERO, UnsignedInteger.ZERO, UnsignedInteger.MAX_VALUE, UnsignedInteger.ONE ); this( UnsignedInteger.ZERO, UnsignedInteger.ZERO, UnsignedInteger.MAX_VALUE, UnsignedInteger.ONE );
} }
@ -43,6 +45,7 @@ public class UnsignedIntegerModel extends SpinnerNumberModel {
this( value, minimum, maximum, UnsignedInteger.ONE ); this( value, minimum, maximum, UnsignedInteger.ONE );
} }
@SuppressWarnings("TypeMayBeWeakened")
public UnsignedIntegerModel(final UnsignedInteger value, final UnsignedInteger minimum, final UnsignedInteger maximum, public UnsignedIntegerModel(final UnsignedInteger value, final UnsignedInteger minimum, final UnsignedInteger maximum,
final UnsignedInteger stepSize) { final UnsignedInteger stepSize) {
super( value, minimum, maximum, stepSize ); super( value, minimum, maximum, stepSize );

View File

@ -18,9 +18,9 @@
package com.lyndir.masterpassword.gui.view; package com.lyndir.masterpassword.gui.view;
import com.google.common.base.Preconditions;
import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedInteger;
import com.lyndir.masterpassword.MPMasterKey; import com.lyndir.masterpassword.*;
import com.lyndir.masterpassword.MPResultType;
import com.lyndir.masterpassword.gui.Res; import com.lyndir.masterpassword.gui.Res;
import com.lyndir.masterpassword.gui.model.IncognitoSite; import com.lyndir.masterpassword.gui.model.IncognitoSite;
import com.lyndir.masterpassword.gui.model.IncognitoUser; import com.lyndir.masterpassword.gui.model.IncognitoUser;
@ -29,6 +29,7 @@ import java.awt.*;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.swing.*; import javax.swing.*;
import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener; import javax.swing.event.DocumentListener;
@ -81,16 +82,17 @@ public class IncognitoAuthenticationPanel extends AuthenticationPanel<IncognitoU
@Override @Override
public PasswordFrame<IncognitoUser, ?> newPasswordFrame() { public PasswordFrame<IncognitoUser, ?> newPasswordFrame() {
return new PasswordFrame<IncognitoUser, IncognitoSite>( getSelectedUser() ) { return new PasswordFrame<IncognitoUser, IncognitoSite>( Preconditions.checkNotNull( getSelectedUser() ) ) {
@Override @Override
protected IncognitoSite createSite(final IncognitoUser user, final String siteName, final UnsignedInteger siteCounter, protected IncognitoSite createSite(final IncognitoUser user, final String siteName, final UnsignedInteger siteCounter,
final MPResultType resultType, final MPResultType resultType,
final MPMasterKey.Version algorithmVersion) { final MPAlgorithm algorithm) {
return new IncognitoSite( siteName, siteCounter, resultType, algorithmVersion ); return new IncognitoSite( siteName, siteCounter, resultType, algorithm );
} }
}; };
} }
@Nullable
@Override @Override
protected IncognitoUser getSelectedUser() { protected IncognitoUser getSelectedUser() {
return new IncognitoUser( fullNameField.getText() ); return new IncognitoUser( fullNameField.getText() );

View File

@ -23,14 +23,14 @@ import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.masterpassword.MPMasterKey; import com.lyndir.masterpassword.*;
import com.lyndir.masterpassword.MPResultType;
import com.lyndir.masterpassword.gui.Res; import com.lyndir.masterpassword.gui.Res;
import com.lyndir.masterpassword.gui.util.Components; import com.lyndir.masterpassword.gui.util.Components;
import com.lyndir.masterpassword.model.*; import com.lyndir.masterpassword.model.*;
import java.awt.*; import java.awt.*;
import java.awt.event.*; import java.awt.event.*;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.swing.*; import javax.swing.*;
import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener; import javax.swing.event.DocumentListener;
@ -71,7 +71,7 @@ public class ModelAuthenticationPanel extends AuthenticationPanel<MPFileUser> im
add( userLabel ); add( userLabel );
userField = Components.comboBox( readConfigUsers() ); userField = Components.comboBox( readConfigUsers() );
userField.setFont( Res.valueFont().deriveFont( 12f ) ); userField.setFont( Res.valueFont().deriveFont( userField.getFont().getSize2D() ) );
userField.addItemListener( this ); userField.addItemListener( this );
userField.addActionListener( this ); userField.addActionListener( this );
userField.setRenderer( new DefaultListCellRenderer() { userField.setRenderer( new DefaultListCellRenderer() {
@ -128,6 +128,7 @@ public class ModelAuthenticationPanel extends AuthenticationPanel<MPFileUser> im
super.updateUser( repack ); super.updateUser( repack );
} }
@Nullable
@Override @Override
protected MPFileUser getSelectedUser() { protected MPFileUser getSelectedUser() {
int selectedIndex = userField.getSelectedIndex(); int selectedIndex = userField.getSelectedIndex();
@ -210,8 +211,8 @@ public class ModelAuthenticationPanel extends AuthenticationPanel<MPFileUser> im
@Override @Override
protected MPFileSite createSite(final MPFileUser user, final String siteName, final UnsignedInteger siteCounter, protected MPFileSite createSite(final MPFileUser user, final String siteName, final UnsignedInteger siteCounter,
final MPResultType resultType, final MPResultType resultType,
final MPMasterKey.Version algorithmVersion) { final MPAlgorithm algorithm) {
return new MPFileSite( user, siteName, siteCounter, resultType, algorithmVersion ); return new MPFileSite( user, siteName, siteCounter, resultType, algorithm );
} }
}; };
} }

View File

@ -33,6 +33,7 @@ import com.lyndir.masterpassword.gui.util.UnsignedIntegerModel;
import com.lyndir.masterpassword.model.*; import com.lyndir.masterpassword.model.*;
import java.awt.*; import java.awt.*;
import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.event.*; import java.awt.event.*;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -65,7 +66,8 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPSite> exten
private S currentSite; private S currentSite;
private boolean updatingUI; private boolean updatingUI;
public PasswordFrame(final U user) { @SuppressWarnings({ "MagicNumber", "OverridableMethodCallDuringObjectConstruction" })
protected PasswordFrame(final U user) {
super( "Master Password" ); super( "Master Password" );
this.user = user; this.user = user;
@ -97,7 +99,7 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPSite> exten
Futures.addCallback( updatePassword( true ), new FutureCallback<String>() { Futures.addCallback( updatePassword( true ), new FutureCallback<String>() {
@Override @Override
public void onSuccess(@Nullable final String sitePassword) { public void onSuccess(@Nullable final String sitePassword) {
StringSelection clipboardContents = new StringSelection( sitePassword ); Transferable clipboardContents = new StringSelection( sitePassword );
Toolkit.getDefaultToolkit().getSystemClipboard().setContents( clipboardContents, null ); Toolkit.getDefaultToolkit().getSystemClipboard().setContents( clipboardContents, null );
SwingUtilities.invokeLater( new Runnable() { SwingUtilities.invokeLater( new Runnable() {
@ -112,7 +114,7 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPSite> exten
} }
@Override @Override
public void onFailure(final Throwable t) { public void onFailure(@Nonnull final Throwable t) {
} }
} ); } );
} }
@ -144,8 +146,8 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPSite> exten
Components.stud(), // Components.stud(), //
siteCounterField = Components.spinner( siteCounterModel ) ); siteCounterField = Components.spinner( siteCounterModel ) );
sitePanel.add( siteSettings ); sitePanel.add( siteSettings );
resultTypeField.setFont( Res.valueFont().deriveFont( 12f ) ); resultTypeField.setFont( Res.valueFont().deriveFont( resultTypeField.getFont().getSize2D() ) );
resultTypeField.setSelectedItem( MPAlgorithm.mpw_default_type ); resultTypeField.setSelectedItem( user.getAlgorithm().mpw_default_type() );
resultTypeField.addItemListener( new ItemListener() { resultTypeField.addItemListener( new ItemListener() {
@Override @Override
public void itemStateChanged(final ItemEvent e) { public void itemStateChanged(final ItemEvent e) {
@ -153,9 +155,9 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPSite> exten
} }
} ); } );
siteVersionField.setFont( Res.valueFont().deriveFont( 12f ) ); siteVersionField.setFont( Res.valueFont().deriveFont( siteVersionField.getFont().getSize2D() ) );
siteVersionField.setAlignmentX( RIGHT_ALIGNMENT ); siteVersionField.setAlignmentX( RIGHT_ALIGNMENT );
siteVersionField.setSelectedItem( user.getAlgorithmVersion() ); siteVersionField.setSelectedItem( user.getAlgorithm() );
siteVersionField.addItemListener( new ItemListener() { siteVersionField.addItemListener( new ItemListener() {
@Override @Override
public void itemStateChanged(final ItemEvent e) { public void itemStateChanged(final ItemEvent e) {
@ -163,7 +165,7 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPSite> exten
} }
} ); } );
siteCounterField.setFont( Res.valueFont().deriveFont( 12f ) ); siteCounterField.setFont( Res.valueFont().deriveFont( siteCounterField.getFont().getSize2D() ) );
siteCounterField.setAlignmentX( RIGHT_ALIGNMENT ); siteCounterField.setAlignmentX( RIGHT_ALIGNMENT );
siteCounterField.addChangeListener( new ChangeListener() { siteCounterField.addChangeListener( new ChangeListener() {
@Override @Override
@ -186,7 +188,7 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPSite> exten
// Password // Password
passwordField = Components.passwordField(); passwordField = Components.passwordField();
passwordField.setAlignmentX( Component.CENTER_ALIGNMENT ); passwordField.setAlignmentX( Component.CENTER_ALIGNMENT );
passwordField.setHorizontalAlignment( JTextField.CENTER ); passwordField.setHorizontalAlignment( SwingConstants.CENTER );
passwordField.putClientProperty( "JPasswordField.cutCopyAllowed", true ); passwordField.putClientProperty( "JPasswordField.cutCopyAllowed", true );
passwordField.setEditable( false ); passwordField.setEditable( false );
passwordField.setBackground( null ); passwordField.setBackground( null );
@ -215,6 +217,7 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPSite> exten
setLocationRelativeTo( null ); setLocationRelativeTo( null );
} }
@SuppressWarnings("MagicNumber")
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.bigValueFont().deriveFont( 40f ) ); passwordField.setFont( maskPasswordField.isSelected()? passwordEchoFont: Res.bigValueFont().deriveFont( 40f ) );
@ -233,9 +236,9 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPSite> exten
return Futures.immediateCancelledFuture(); return Futures.immediateCancelledFuture();
} }
MPResultType resultType = resultTypeField.getModel().getElementAt( resultTypeField.getSelectedIndex() ); MPResultType resultType = resultTypeField.getModel().getElementAt( resultTypeField.getSelectedIndex() );
MPMasterKey.Version siteVersion = siteVersionField.getItemAt( siteVersionField.getSelectedIndex() ); MPAlgorithm siteAlgorithm = siteVersionField.getItemAt( siteVersionField.getSelectedIndex() ).getAlgorithm();
UnsignedInteger siteCounter = siteCounterModel.getNumber(); UnsignedInteger siteCounter = siteCounterModel.getNumber();
Iterable<S> siteResults = user.findSites( siteNameQuery ); Iterable<S> siteResults = user.findSites( siteNameQuery );
if (!allowNameCompletion) if (!allowNameCompletion)
@ -246,10 +249,10 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPSite> exten
} }
} ); } );
final S site = ifNotNullElse( Iterables.getFirst( siteResults, null ), final S site = ifNotNullElse( Iterables.getFirst( siteResults, null ),
createSite( user, siteNameQuery, siteCounter, resultType, siteVersion ) ); createSite( user, siteNameQuery, siteCounter, resultType, siteAlgorithm ) );
if ((currentSite != null) && currentSite.getSiteName().equals( site.getSiteName() )) { if ((currentSite != null) && currentSite.getSiteName().equals( site.getSiteName() )) {
site.setResultType( resultType ); site.setResultType( resultType );
site.setAlgorithmVersion( siteVersion ); site.setAlgorithm( siteAlgorithm );
site.setSiteCounter( siteCounter ); site.setSiteCounter( siteCounter );
} }
@ -259,7 +262,7 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPSite> exten
throws Exception { throws Exception {
return user.getMasterKey() return user.getMasterKey()
.siteResult( site.getSiteName(), site.getSiteCounter(), MPKeyPurpose.Authentication, null, site.getResultType(), .siteResult( site.getSiteName(), site.getSiteCounter(), MPKeyPurpose.Authentication, null, site.getResultType(),
null, site.getAlgorithmVersion() ); null, site.getAlgorithm() );
} }
} ); } );
Futures.addCallback( passwordFuture, new FutureCallback<String>() { Futures.addCallback( passwordFuture, new FutureCallback<String>() {
@ -276,7 +279,7 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPSite> exten
else else
siteActionButton.setText( "Add Site" ); siteActionButton.setText( "Add Site" );
resultTypeField.setSelectedItem( currentSite.getResultType() ); resultTypeField.setSelectedItem( currentSite.getResultType() );
siteVersionField.setSelectedItem( currentSite.getAlgorithmVersion() ); siteVersionField.setSelectedItem( currentSite.getAlgorithm() );
siteCounterField.setValue( currentSite.getSiteCounter() ); siteCounterField.setValue( currentSite.getSiteCounter() );
siteNameField.setText( currentSite.getSiteName() ); siteNameField.setText( currentSite.getSiteName() );
if (siteNameField.getText().startsWith( siteNameQuery )) if (siteNameField.getText().startsWith( siteNameQuery ))
@ -290,15 +293,14 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPSite> exten
} }
@Override @Override
public void onFailure(final Throwable t) { public void onFailure(@Nonnull final Throwable t) {
} }
} ); } );
return passwordFuture; return passwordFuture;
} }
protected abstract S createSite(U user, String siteName, UnsignedInteger siteCounter, MPResultType resultType, protected abstract S createSite(U user, String siteName, UnsignedInteger siteCounter, MPResultType resultType, MPAlgorithm algorithm);
MPMasterKey.Version algorithmVersion);
@Override @Override
public void insertUpdate(final DocumentEvent e) { public void insertUpdate(final DocumentEvent e) {

View File

@ -21,10 +21,10 @@ package com.lyndir.masterpassword.gui.view;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*; import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
import com.lyndir.masterpassword.MPIdenticon; import com.lyndir.masterpassword.MPIdenticon;
import com.lyndir.masterpassword.gui.Res; import com.lyndir.masterpassword.gui.*;
import com.lyndir.masterpassword.model.MPUser;
import com.lyndir.masterpassword.gui.util.Components; import com.lyndir.masterpassword.gui.util.Components;
import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException; import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException;
import com.lyndir.masterpassword.model.MPUser;
import java.awt.*; import java.awt.*;
import java.awt.event.*; import java.awt.event.*;
import java.util.concurrent.Future; import java.util.concurrent.Future;
@ -196,7 +196,7 @@ public class UnlockFrame extends JFrame {
} }
void trySignIn(final JComponent... signInComponents) { void trySignIn(final JComponent... signInComponents) {
if (!checkSignIn()) if ((user == null) || !checkSignIn())
return; return;
for (final JComponent signInComponent : signInComponents) for (final JComponent signInComponent : signInComponents)

View File

@ -0,0 +1,25 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
/**
* @author lhunath, 2018-04-26
*/
@ParametersAreNonnullByDefault
package com.lyndir.masterpassword.gui.view;
import javax.annotation.ParametersAreNonnullByDefault;