Deep Java refactoring to match the C API logic and clean up some OO oddities.
This commit is contained in:
		@@ -24,11 +24,11 @@ import javax.annotation.Nullable;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @see MasterKey.Version
 | 
			
		||||
 * @see MPMasterKey.Version
 | 
			
		||||
 */
 | 
			
		||||
public interface MasterKeyAlgorithm extends Serializable {
 | 
			
		||||
public interface MPAlgorithm extends Serializable {
 | 
			
		||||
 | 
			
		||||
    MasterKey.Version getAlgorithmVersion();
 | 
			
		||||
    MPMasterKey.Version getAlgorithmVersion();
 | 
			
		||||
 | 
			
		||||
    byte[] deriveKey(String fullName, char[] masterPassword);
 | 
			
		||||
 | 
			
		||||
@@ -38,11 +38,11 @@ import javax.crypto.IllegalBlockSizeException;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @see MasterKey.Version#V0
 | 
			
		||||
 * @see MPMasterKey.Version#V0
 | 
			
		||||
 *
 | 
			
		||||
 * @author lhunath, 2014-08-30
 | 
			
		||||
 */
 | 
			
		||||
public class MasterKeyV0 implements MasterKeyAlgorithm {
 | 
			
		||||
public class MPAlgorithmV0 implements MPAlgorithm {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * mpw: validity for the time-based rolling counter.
 | 
			
		||||
@@ -84,9 +84,9 @@ public class MasterKeyV0 implements MasterKeyAlgorithm {
 | 
			
		||||
    protected final Logger logger = Logger.get( getClass() );
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public MasterKey.Version getAlgorithmVersion() {
 | 
			
		||||
    public MPMasterKey.Version getAlgorithmVersion() {
 | 
			
		||||
 | 
			
		||||
        return MasterKey.Version.V0;
 | 
			
		||||
        return MPMasterKey.Version.V0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -23,16 +23,16 @@ import javax.annotation.Nullable;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @see MasterKey.Version#V1
 | 
			
		||||
 * @see MPMasterKey.Version#V1
 | 
			
		||||
 *
 | 
			
		||||
 * @author lhunath, 2014-08-30
 | 
			
		||||
 */
 | 
			
		||||
public class MasterKeyV1 extends MasterKeyV0 {
 | 
			
		||||
public class MPAlgorithmV1 extends MPAlgorithmV0 {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public MasterKey.Version getAlgorithmVersion() {
 | 
			
		||||
    public MPMasterKey.Version getAlgorithmVersion() {
 | 
			
		||||
 | 
			
		||||
        return MasterKey.Version.V1;
 | 
			
		||||
        return MPMasterKey.Version.V1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -28,16 +28,16 @@ import javax.annotation.Nullable;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @see MasterKey.Version#V2
 | 
			
		||||
 * @see MPMasterKey.Version#V2
 | 
			
		||||
 * 
 | 
			
		||||
 * @author lhunath, 2014-08-30
 | 
			
		||||
 */
 | 
			
		||||
public class MasterKeyV2 extends MasterKeyV1 {
 | 
			
		||||
public class MPAlgorithmV2 extends MPAlgorithmV1 {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public MasterKey.Version getAlgorithmVersion() {
 | 
			
		||||
    public MPMasterKey.Version getAlgorithmVersion() {
 | 
			
		||||
 | 
			
		||||
        return MasterKey.Version.V2;
 | 
			
		||||
        return MPMasterKey.Version.V2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -56,25 +56,25 @@ public class MasterKeyV2 extends MasterKeyV1 {
 | 
			
		||||
 | 
			
		||||
        // OTP counter value.
 | 
			
		||||
        if (siteCounter.longValue() == 0)
 | 
			
		||||
            siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (MasterKeyV0.mpw_otp_window * 1000)) * MasterKeyV0.mpw_otp_window );
 | 
			
		||||
            siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (MPAlgorithmV0.mpw_otp_window * 1000)) * MPAlgorithmV0.mpw_otp_window );
 | 
			
		||||
 | 
			
		||||
        // Calculate the site seed.
 | 
			
		||||
        byte[] siteNameBytes = siteName.getBytes( MasterKeyV0.mpw_charset );
 | 
			
		||||
        byte[] siteNameBytes = siteName.getBytes( MPAlgorithmV0.mpw_charset );
 | 
			
		||||
        byte[] siteNameLengthBytes = bytesForInt( siteNameBytes.length );
 | 
			
		||||
        byte[] siteCounterBytes = bytesForInt( siteCounter );
 | 
			
		||||
        byte[] keyContextBytes = ((keyContext == null) || keyContext.isEmpty())? null: keyContext.getBytes( MasterKeyV0.mpw_charset );
 | 
			
		||||
        byte[] keyContextBytes = ((keyContext == null) || keyContext.isEmpty())? null: keyContext.getBytes( MPAlgorithmV0.mpw_charset );
 | 
			
		||||
        byte[] keyContextLengthBytes = (keyContextBytes == null)? null: bytesForInt( keyContextBytes.length );
 | 
			
		||||
        logger.trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s",
 | 
			
		||||
                    keyScope, CodeUtils.encodeHex( siteNameLengthBytes ), siteName, CodeUtils.encodeHex( siteCounterBytes ),
 | 
			
		||||
                    (keyContextLengthBytes == null)? null: CodeUtils.encodeHex( keyContextLengthBytes ), keyContext );
 | 
			
		||||
 | 
			
		||||
        byte[] sitePasswordInfo = Bytes.concat( keyScope.getBytes( MasterKeyV0.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
 | 
			
		||||
        byte[] sitePasswordInfo = Bytes.concat( keyScope.getBytes( MPAlgorithmV0.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
 | 
			
		||||
        if (keyContextBytes != null)
 | 
			
		||||
            sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes );
 | 
			
		||||
        logger.trc( "  => siteSalt.id: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
 | 
			
		||||
 | 
			
		||||
        logger.trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )", (Object) idForBytes( masterKey ) );
 | 
			
		||||
        byte[] sitePasswordSeedBytes = MasterKeyV0.mpw_digest.of( masterKey, sitePasswordInfo );
 | 
			
		||||
        byte[] sitePasswordSeedBytes = MPAlgorithmV0.mpw_digest.of( masterKey, sitePasswordInfo );
 | 
			
		||||
        logger.trc( "  => siteKey.id: %s", (Object) idForBytes( sitePasswordSeedBytes ) );
 | 
			
		||||
 | 
			
		||||
        return sitePasswordSeedBytes;
 | 
			
		||||
@@ -29,25 +29,25 @@ import java.util.Arrays;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @see MasterKey.Version#V3
 | 
			
		||||
 * @see MPMasterKey.Version#V3
 | 
			
		||||
 *
 | 
			
		||||
 * @author lhunath, 2014-08-30
 | 
			
		||||
 */
 | 
			
		||||
public class MasterKeyV3 extends MasterKeyV2 {
 | 
			
		||||
public class MPAlgorithmV3 extends MPAlgorithmV2 {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public MasterKey.Version getAlgorithmVersion() {
 | 
			
		||||
    public MPMasterKey.Version getAlgorithmVersion() {
 | 
			
		||||
 | 
			
		||||
        return MasterKey.Version.V3;
 | 
			
		||||
        return MPMasterKey.Version.V3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public byte[] deriveKey(final String fullName, final char[] masterPassword) {
 | 
			
		||||
        Preconditions.checkArgument( masterPassword.length > 0 );
 | 
			
		||||
 | 
			
		||||
        byte[] fullNameBytes = fullName.getBytes( MasterKeyV0.mpw_charset );
 | 
			
		||||
        byte[] fullNameBytes = fullName.getBytes( MPAlgorithmV0.mpw_charset );
 | 
			
		||||
        byte[] fullNameLengthBytes = MPUtils.bytesForInt( fullNameBytes.length );
 | 
			
		||||
        ByteBuffer mpBytesBuf = MasterKeyV0.mpw_charset.encode( CharBuffer.wrap( masterPassword ) );
 | 
			
		||||
        ByteBuffer mpBytesBuf = MPAlgorithmV0.mpw_charset.encode( CharBuffer.wrap( masterPassword ) );
 | 
			
		||||
 | 
			
		||||
        logger.trc( "-- mpw_masterKey (algorithm: %u)", getAlgorithmVersion().toInt() );
 | 
			
		||||
        logger.trc( "fullName: %s", fullName );
 | 
			
		||||
@@ -59,12 +59,12 @@ public class MasterKeyV3 extends MasterKeyV2 {
 | 
			
		||||
        // Calculate the master key salt.
 | 
			
		||||
        logger.trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s",
 | 
			
		||||
                    keyScope, CodeUtils.encodeHex( fullNameLengthBytes ), fullName );
 | 
			
		||||
        byte[] masterKeySalt = Bytes.concat( keyScope.getBytes( MasterKeyV0.mpw_charset ), fullNameLengthBytes, fullNameBytes );
 | 
			
		||||
        byte[] masterKeySalt = Bytes.concat( keyScope.getBytes( MPAlgorithmV0.mpw_charset ), fullNameLengthBytes, fullNameBytes );
 | 
			
		||||
        logger.trc( "  => masterKeySalt.id: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
 | 
			
		||||
 | 
			
		||||
        // Calculate the master key.
 | 
			
		||||
        logger.trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%lu, r=%u, p=%u )",
 | 
			
		||||
                    MasterKeyV0.scrypt_N, MasterKeyV0.scrypt_r, MasterKeyV0.scrypt_p );
 | 
			
		||||
                    MPAlgorithmV0.scrypt_N, MPAlgorithmV0.scrypt_r, MPAlgorithmV0.scrypt_p );
 | 
			
		||||
        byte[] mpBytes = new byte[mpBytesBuf.remaining()];
 | 
			
		||||
        mpBytesBuf.get( mpBytes, 0, mpBytes.length );
 | 
			
		||||
        Arrays.fill( mpBytesBuf.array(), (byte) 0 );
 | 
			
		||||
@@ -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/>.
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
package com.lyndir.masterpassword;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author lhunath, 2017-09-21
 | 
			
		||||
 */
 | 
			
		||||
public class MPInvalidatedException extends Exception {
 | 
			
		||||
}
 | 
			
		||||
@@ -20,9 +20,10 @@ package com.lyndir.masterpassword;
 | 
			
		||||
 | 
			
		||||
import static com.lyndir.masterpassword.MPUtils.idForBytes;
 | 
			
		||||
 | 
			
		||||
import com.google.common.base.Preconditions;
 | 
			
		||||
import com.google.common.primitives.UnsignedInteger;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.EnumMap;
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
 | 
			
		||||
@@ -30,26 +31,28 @@ import javax.annotation.Nullable;
 | 
			
		||||
/**
 | 
			
		||||
 * @author lhunath, 2014-08-30
 | 
			
		||||
 */
 | 
			
		||||
public class MasterKey {
 | 
			
		||||
public class MPMasterKey {
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("UnusedDeclaration")
 | 
			
		||||
    private static final Logger logger = Logger.get( MasterKey.class );
 | 
			
		||||
    private static final Logger logger = Logger.get( MPMasterKey.class );
 | 
			
		||||
 | 
			
		||||
    private final EnumMap<Version, byte[]> keyByVersion = new EnumMap<>( Version.class );
 | 
			
		||||
    private final String fullName;
 | 
			
		||||
    private final char[] masterPassword;
 | 
			
		||||
 | 
			
		||||
    private boolean invalidated;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param masterPassword The characters of the user's master password.  Note: this array is held by reference and its contents
 | 
			
		||||
     *                       invalidated on {@link #invalidate()}.
 | 
			
		||||
     */
 | 
			
		||||
    @SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter")
 | 
			
		||||
    public MasterKey(final String fullName, final char[] masterPassword) {
 | 
			
		||||
    public MPMasterKey(final String fullName, final char[] masterPassword) {
 | 
			
		||||
 | 
			
		||||
        this.fullName = fullName;
 | 
			
		||||
        this.masterPassword = masterPassword;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private byte[] getKey(final Version algorithmVersion) {
 | 
			
		||||
        // TODO: Cache keys.
 | 
			
		||||
        return algorithmVersion.getAlgorithm().deriveKey( fullName, masterPassword );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Generate a site result token.
 | 
			
		||||
     *
 | 
			
		||||
@@ -60,10 +63,13 @@ public class MasterKey {
 | 
			
		||||
     * @param resultType  The type of result to generate.
 | 
			
		||||
     * @param resultParam A parameter for the resultType.  For stateful result types, the output of
 | 
			
		||||
     *                    {@link #siteState(String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String, Version)}.
 | 
			
		||||
     *
 | 
			
		||||
     * @throws MPInvalidatedException {@link #invalidate()} has been called on this object.
 | 
			
		||||
     */
 | 
			
		||||
    public String siteResult(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
 | 
			
		||||
                             @Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam,
 | 
			
		||||
                             final Version algorithmVersion) {
 | 
			
		||||
                             final Version algorithmVersion)
 | 
			
		||||
            throws MPInvalidatedException {
 | 
			
		||||
        return algorithmVersion.getAlgorithm().siteResult(
 | 
			
		||||
                getKey( algorithmVersion ), siteName, siteCounter, keyPurpose, keyContext, resultType, resultParam );
 | 
			
		||||
    }
 | 
			
		||||
@@ -78,10 +84,13 @@ public class MasterKey {
 | 
			
		||||
     * @param resultType  The type of result token to encrypt.
 | 
			
		||||
     * @param resultParam The result token desired from
 | 
			
		||||
     *                    {@link #siteResult(String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String, Version)}.
 | 
			
		||||
     *
 | 
			
		||||
     * @throws MPInvalidatedException {@link #invalidate()} has been called on this object.
 | 
			
		||||
     */
 | 
			
		||||
    public String siteState(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
 | 
			
		||||
                            @Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam,
 | 
			
		||||
                            final Version algorithmVersion) {
 | 
			
		||||
                            final Version algorithmVersion)
 | 
			
		||||
            throws MPInvalidatedException {
 | 
			
		||||
        return algorithmVersion.getAlgorithm().siteState(
 | 
			
		||||
                getKey( algorithmVersion ), siteName, siteCounter, keyPurpose, keyContext, resultType, resultParam );
 | 
			
		||||
    }
 | 
			
		||||
@@ -92,45 +101,81 @@ public class MasterKey {
 | 
			
		||||
        return fullName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public byte[] getKeyID(final Version algorithmVersion) {
 | 
			
		||||
    /**
 | 
			
		||||
     * Calculate an identifier for the master key.
 | 
			
		||||
     *
 | 
			
		||||
     * @throws MPInvalidatedException {@link #invalidate()} has been called on this object.
 | 
			
		||||
     */
 | 
			
		||||
    public byte[] getKeyID(final Version algorithmVersion)
 | 
			
		||||
            throws MPInvalidatedException {
 | 
			
		||||
 | 
			
		||||
        return idForBytes( getKey( algorithmVersion ) );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Wipe this key's secrets from memory, making the object permanently unusable.
 | 
			
		||||
     */
 | 
			
		||||
    public void invalidate() {
 | 
			
		||||
 | 
			
		||||
        invalidated = true;
 | 
			
		||||
        for (final byte[] key : keyByVersion.values())
 | 
			
		||||
            Arrays.fill( key, (byte) 0 );
 | 
			
		||||
        Arrays.fill( masterPassword, (char) 0 );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private byte[] getKey(final Version algorithmVersion)
 | 
			
		||||
            throws MPInvalidatedException {
 | 
			
		||||
        if (invalidated)
 | 
			
		||||
            throw new MPInvalidatedException();
 | 
			
		||||
 | 
			
		||||
        byte[] key = keyByVersion.get( algorithmVersion );
 | 
			
		||||
        if (key == null)
 | 
			
		||||
            keyByVersion.put( algorithmVersion, key = algorithmVersion.getAlgorithm().deriveKey( fullName, masterPassword ) );
 | 
			
		||||
 | 
			
		||||
        return key;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The algorithm iterations.
 | 
			
		||||
     */
 | 
			
		||||
    public enum Version {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * bugs:
 | 
			
		||||
         * - does math with chars whose signedness was platform-dependent.
 | 
			
		||||
         * - miscounted the byte-length for multi-byte site names.
 | 
			
		||||
         * - miscounted the byte-length for multi-byte full names.
 | 
			
		||||
         * - miscounted the byte-length for multi-byte user names.
 | 
			
		||||
         */
 | 
			
		||||
        V0( new MasterKeyV0() ),
 | 
			
		||||
        V0( new MPAlgorithmV0() ),
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * bugs:
 | 
			
		||||
         * - miscounted the byte-length for multi-byte site names.
 | 
			
		||||
         * - miscounted the byte-length for multi-byte full names.
 | 
			
		||||
         * - miscounted the byte-length for multi-byte user names.
 | 
			
		||||
         */
 | 
			
		||||
        V1( new MasterKeyV1() ),
 | 
			
		||||
        V1( new MPAlgorithmV1() ),
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * bugs:
 | 
			
		||||
         * - miscounted the byte-length for multi-byte full names.
 | 
			
		||||
         * - miscounted the byte-length for multi-byte user names.
 | 
			
		||||
         */
 | 
			
		||||
        V2( new MasterKeyV2() ),
 | 
			
		||||
        V2( new MPAlgorithmV2() ),
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * bugs:
 | 
			
		||||
         * - no known issues.
 | 
			
		||||
         */
 | 
			
		||||
        V3( new MasterKeyV3() );
 | 
			
		||||
        V3( new MPAlgorithmV3() );
 | 
			
		||||
 | 
			
		||||
        public static final Version CURRENT = V3;
 | 
			
		||||
 | 
			
		||||
        private final MasterKeyAlgorithm algorithm;
 | 
			
		||||
        private final MPAlgorithm algorithm;
 | 
			
		||||
 | 
			
		||||
        Version(final MasterKeyAlgorithm algorithm) {
 | 
			
		||||
        Version(final MPAlgorithm algorithm) {
 | 
			
		||||
            this.algorithm = algorithm;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public MasterKeyAlgorithm getAlgorithm() {
 | 
			
		||||
        public MPAlgorithm getAlgorithm() {
 | 
			
		||||
            return algorithm;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -28,14 +28,14 @@ import java.nio.ByteBuffer;
 | 
			
		||||
public final class MPUtils {
 | 
			
		||||
 | 
			
		||||
    public static byte[] bytesForInt(final int number) {
 | 
			
		||||
        return ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( MasterKeyV0.mpw_byteOrder ).putInt( number ).array();
 | 
			
		||||
        return ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( MPAlgorithmV0.mpw_byteOrder ).putInt( number ).array();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static byte[] bytesForInt(final UnsignedInteger number) {
 | 
			
		||||
        return ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( MasterKeyV0.mpw_byteOrder ).putInt( number.intValue() ).array();
 | 
			
		||||
        return ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( MPAlgorithmV0.mpw_byteOrder ).putInt( number.intValue() ).array();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static byte[] idForBytes(final byte[] bytes) {
 | 
			
		||||
        return MasterKeyV0.mpw_hash.of( bytes );
 | 
			
		||||
        return MPAlgorithmV0.mpw_hash.of( bytes );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,205 @@
 | 
			
		||||
//==============================================================================
 | 
			
		||||
// 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/>.
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
package com.lyndir.masterpassword.model;
 | 
			
		||||
 | 
			
		||||
import com.google.common.primitives.UnsignedInteger;
 | 
			
		||||
import com.lyndir.masterpassword.*;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import org.joda.time.Instant;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author lhunath, 14-12-05
 | 
			
		||||
 */
 | 
			
		||||
public class MPFileSite extends MPSite {
 | 
			
		||||
 | 
			
		||||
    private final MPFileUser          user;
 | 
			
		||||
    private       String              siteName;
 | 
			
		||||
    @Nullable
 | 
			
		||||
    private       String              siteContent;
 | 
			
		||||
    private       UnsignedInteger     siteCounter;
 | 
			
		||||
    private       MPResultType        resultType;
 | 
			
		||||
    private       MPMasterKey.Version algorithmVersion;
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    private String       loginContent;
 | 
			
		||||
    @Nullable
 | 
			
		||||
    private MPResultType loginType;
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    private String  url;
 | 
			
		||||
    private int     uses;
 | 
			
		||||
    private Instant lastUsed;
 | 
			
		||||
 | 
			
		||||
    public MPFileSite(final MPFileUser user, final String siteName) {
 | 
			
		||||
        this( user, siteName, DEFAULT_COUNTER, MPResultType.DEFAULT, MPMasterKey.Version.CURRENT );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MPFileSite(final MPFileUser user, final String siteName, final UnsignedInteger siteCounter, final MPResultType resultType,
 | 
			
		||||
                      final MPMasterKey.Version algorithmVersion) {
 | 
			
		||||
        this.user = user;
 | 
			
		||||
        this.siteName = siteName;
 | 
			
		||||
        this.siteCounter = siteCounter;
 | 
			
		||||
        this.resultType = resultType;
 | 
			
		||||
        this.algorithmVersion = algorithmVersion;
 | 
			
		||||
        this.lastUsed = new Instant();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected MPFileSite(final MPFileUser user, final String siteName, @Nullable final String siteContent,
 | 
			
		||||
                         final UnsignedInteger siteCounter,
 | 
			
		||||
                         final MPResultType resultType, final MPMasterKey.Version algorithmVersion,
 | 
			
		||||
                         @Nullable final String loginContent, @Nullable final MPResultType loginType,
 | 
			
		||||
                         @Nullable final String url, final int uses, final Instant lastUsed) {
 | 
			
		||||
        this.user = user;
 | 
			
		||||
        this.siteName = siteName;
 | 
			
		||||
        this.siteContent = siteContent;
 | 
			
		||||
        this.siteCounter = siteCounter;
 | 
			
		||||
        this.resultType = resultType;
 | 
			
		||||
        this.algorithmVersion = algorithmVersion;
 | 
			
		||||
        this.loginContent = loginContent;
 | 
			
		||||
        this.loginType = loginType;
 | 
			
		||||
        this.url = url;
 | 
			
		||||
        this.uses = uses;
 | 
			
		||||
        this.lastUsed = lastUsed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String resultFor(final MPMasterKey masterKey)
 | 
			
		||||
            throws MPInvalidatedException {
 | 
			
		||||
 | 
			
		||||
        return resultFor( masterKey, MPKeyPurpose.Authentication, null );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String resultFor(final MPMasterKey masterKey, final MPKeyPurpose keyPurpose, @Nullable final String keyContext)
 | 
			
		||||
            throws MPInvalidatedException {
 | 
			
		||||
 | 
			
		||||
        return resultFor( masterKey, keyPurpose, keyContext, getSiteContent() );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String loginFor(final MPMasterKey masterKey)
 | 
			
		||||
            throws MPInvalidatedException {
 | 
			
		||||
 | 
			
		||||
        if (loginType == null)
 | 
			
		||||
            loginType = MPResultType.GeneratedName;
 | 
			
		||||
 | 
			
		||||
        return loginFor( masterKey, loginType, loginContent );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MPFileUser getUser() {
 | 
			
		||||
        return user;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getSiteName() {
 | 
			
		||||
        return siteName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setSiteName(final String siteName) {
 | 
			
		||||
        this.siteName = siteName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    public String getSiteContent() {
 | 
			
		||||
        return siteContent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setSitePassword(final MPMasterKey masterKey, @Nullable final MPResultType resultType, @Nullable final String result)
 | 
			
		||||
            throws MPInvalidatedException {
 | 
			
		||||
        this.resultType = resultType;
 | 
			
		||||
        if (result == null)
 | 
			
		||||
            this.siteContent = null;
 | 
			
		||||
        else
 | 
			
		||||
            this.siteContent = masterKey.siteState(
 | 
			
		||||
                    getSiteName(), getSiteCounter(), MPKeyPurpose.Authentication, null, getResultType(), result, getAlgorithmVersion() );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public UnsignedInteger getSiteCounter() {
 | 
			
		||||
        return siteCounter;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setSiteCounter(final UnsignedInteger siteCounter) {
 | 
			
		||||
        this.siteCounter = siteCounter;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public MPResultType getResultType() {
 | 
			
		||||
        return resultType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setResultType(final MPResultType resultType) {
 | 
			
		||||
        this.resultType = resultType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public MPMasterKey.Version getAlgorithmVersion() {
 | 
			
		||||
        return algorithmVersion;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setAlgorithmVersion(final MPMasterKey.Version algorithmVersion) {
 | 
			
		||||
        this.algorithmVersion = algorithmVersion;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    public MPResultType getLoginType() {
 | 
			
		||||
        return loginType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    public String getLoginContent() {
 | 
			
		||||
        return loginContent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setLoginName(final MPMasterKey masterKey, @Nullable final MPResultType loginType, @Nullable final String result)
 | 
			
		||||
            throws MPInvalidatedException {
 | 
			
		||||
        this.loginType = loginType;
 | 
			
		||||
        if (this.loginType != null)
 | 
			
		||||
            if (result == null)
 | 
			
		||||
                this.loginContent = null;
 | 
			
		||||
            else
 | 
			
		||||
                this.loginContent = masterKey.siteState(
 | 
			
		||||
                        siteName, DEFAULT_COUNTER, MPKeyPurpose.Identification, null, this.loginType, result, algorithmVersion );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    public String getUrl() {
 | 
			
		||||
        return url;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setUrl(@Nullable final String url) {
 | 
			
		||||
        this.url = url;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getUses() {
 | 
			
		||||
        return uses;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Instant getLastUsed() {
 | 
			
		||||
        return lastUsed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void use() {
 | 
			
		||||
        uses++;
 | 
			
		||||
        lastUsed = new Instant();
 | 
			
		||||
        user.use();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										172
									
								
								core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileUser.java
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										172
									
								
								core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileUser.java
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,172 @@
 | 
			
		||||
//==============================================================================
 | 
			
		||||
// 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/>.
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
package com.lyndir.masterpassword.model;
 | 
			
		||||
 | 
			
		||||
import com.google.common.collect.*;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
			
		||||
import com.lyndir.masterpassword.*;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import org.joda.time.*;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author lhunath, 14-12-07
 | 
			
		||||
 */
 | 
			
		||||
public class MPFileUser extends MPUser<MPFileSite> implements Comparable<MPFileUser> {
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("UnusedDeclaration")
 | 
			
		||||
    private static final Logger logger = Logger.get( MPFileUser.class );
 | 
			
		||||
 | 
			
		||||
    private final String fullName;
 | 
			
		||||
    private final Collection<MPFileSite> sites = Sets.newHashSet();
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    private byte[]              keyID;
 | 
			
		||||
    private MPMasterKey.Version algorithmVersion;
 | 
			
		||||
 | 
			
		||||
    private int             avatar;
 | 
			
		||||
    private MPResultType    defaultType;
 | 
			
		||||
    private ReadableInstant lastUsed;
 | 
			
		||||
 | 
			
		||||
    public MPFileUser(final String fullName) {
 | 
			
		||||
        this( fullName, null, MPMasterKey.Version.CURRENT );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPMasterKey.Version algorithmVersion) {
 | 
			
		||||
        this( fullName, keyID, algorithmVersion, 0, MPResultType.DEFAULT, new Instant() );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPMasterKey.Version algorithmVersion, final int avatar,
 | 
			
		||||
                      final MPResultType defaultType, final ReadableInstant lastUsed) {
 | 
			
		||||
        this.fullName = fullName;
 | 
			
		||||
        this.keyID = (keyID == null)? null: keyID.clone();
 | 
			
		||||
        this.algorithmVersion = algorithmVersion;
 | 
			
		||||
        this.avatar = avatar;
 | 
			
		||||
        this.defaultType = defaultType;
 | 
			
		||||
        this.lastUsed = lastUsed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getFullName() {
 | 
			
		||||
        return fullName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public MPMasterKey.Version getAlgorithmVersion() {
 | 
			
		||||
        return algorithmVersion;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setAlgorithmVersion(final MPMasterKey.Version algorithmVersion) {
 | 
			
		||||
        this.algorithmVersion = algorithmVersion;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getAvatar() {
 | 
			
		||||
        return avatar;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setAvatar(final int avatar) {
 | 
			
		||||
        this.avatar = avatar;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MPResultType getDefaultType() {
 | 
			
		||||
        return defaultType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setDefaultType(final MPResultType defaultType) {
 | 
			
		||||
        this.defaultType = defaultType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ReadableInstant getLastUsed() {
 | 
			
		||||
        return lastUsed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void use() {
 | 
			
		||||
        lastUsed = new Instant();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Iterable<MPFileSite> getSites() {
 | 
			
		||||
        return sites;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void addSite(final MPFileSite site) {
 | 
			
		||||
        sites.add( site );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void deleteSite(final MPFileSite site) {
 | 
			
		||||
        sites.remove( site );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Collection<MPFileSite> findSites(final String query) {
 | 
			
		||||
        ImmutableList.Builder<MPFileSite> results = ImmutableList.builder();
 | 
			
		||||
        for (final MPFileSite site : getSites())
 | 
			
		||||
            if (site.getSiteName().startsWith( query ))
 | 
			
		||||
                results.add( site );
 | 
			
		||||
 | 
			
		||||
        return results.build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Performs an authentication attempt against the keyID for this user.
 | 
			
		||||
     *
 | 
			
		||||
     * Note: If this user doesn't have a keyID set yet, authentication will always succeed and the key ID will be set as a result.
 | 
			
		||||
     *
 | 
			
		||||
     * @param masterPassword The password to authenticate with.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The master key for the user if authentication was successful.
 | 
			
		||||
     *
 | 
			
		||||
     * @throws MPIncorrectMasterPasswordException If authentication fails due to the given master password not matching the user's keyID.
 | 
			
		||||
     */
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public MPMasterKey authenticate(final char[] masterPassword)
 | 
			
		||||
            throws MPIncorrectMasterPasswordException {
 | 
			
		||||
        try {
 | 
			
		||||
            key = new MPMasterKey( getFullName(), masterPassword );
 | 
			
		||||
            if ((keyID == null) || (keyID.length == 0))
 | 
			
		||||
                keyID = key.getKeyID( algorithmVersion );
 | 
			
		||||
            else if (!Arrays.equals( key.getKeyID( algorithmVersion ), keyID ))
 | 
			
		||||
                throw new MPIncorrectMasterPasswordException( this );
 | 
			
		||||
 | 
			
		||||
            return key;
 | 
			
		||||
        }
 | 
			
		||||
        catch (final MPInvalidatedException e) {
 | 
			
		||||
            throw logger.bug( e );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void save()
 | 
			
		||||
            throws MPInvalidatedException {
 | 
			
		||||
        MPFileUserManager.get().save( this, getMasterKey() );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int compareTo(final MPFileUser o) {
 | 
			
		||||
        int comparison = getLastUsed().compareTo( o.getLastUsed() );
 | 
			
		||||
        if (comparison == 0)
 | 
			
		||||
            comparison = getFullName().compareTo( o.getFullName() );
 | 
			
		||||
 | 
			
		||||
        return comparison;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -24,20 +24,22 @@ import com.google.common.base.*;
 | 
			
		||||
import com.google.common.collect.*;
 | 
			
		||||
import com.google.common.io.CharSink;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
			
		||||
import com.lyndir.masterpassword.MPConstant;
 | 
			
		||||
import com.lyndir.masterpassword.*;
 | 
			
		||||
import java.io.*;
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Manages user data stored in user-specific {@code .mpsites} files under {@code .mpw.d}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author lhunath, 14-12-07
 | 
			
		||||
 */
 | 
			
		||||
public class MPUserFileManager extends MPUserManager {
 | 
			
		||||
public class MPFileUserManager extends MPUserManager {
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("UnusedDeclaration")
 | 
			
		||||
    private static final Logger logger = Logger.get( MPUserFileManager.class );
 | 
			
		||||
    private static final MPUserFileManager instance;
 | 
			
		||||
    private static final Logger logger = Logger.get( MPFileUserManager.class );
 | 
			
		||||
    private static final MPFileUserManager instance;
 | 
			
		||||
 | 
			
		||||
    static {
 | 
			
		||||
        String rcDir = System.getenv( MPConstant.env_rcDir );
 | 
			
		||||
@@ -49,31 +51,31 @@ public class MPUserFileManager extends MPUserManager {
 | 
			
		||||
 | 
			
		||||
    private final File userFilesDirectory;
 | 
			
		||||
 | 
			
		||||
    public static MPUserFileManager get() {
 | 
			
		||||
    public static MPFileUserManager get() {
 | 
			
		||||
        MPUserManager.instance = instance;
 | 
			
		||||
        return instance;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static MPUserFileManager create(final File userFilesDirectory) {
 | 
			
		||||
        return new MPUserFileManager( userFilesDirectory );
 | 
			
		||||
    public static MPFileUserManager create(final File userFilesDirectory) {
 | 
			
		||||
        return new MPFileUserManager( userFilesDirectory );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected MPUserFileManager(final File userFilesDirectory) {
 | 
			
		||||
    protected MPFileUserManager(final File userFilesDirectory) {
 | 
			
		||||
 | 
			
		||||
        super( unmarshallUsers( userFilesDirectory ) );
 | 
			
		||||
        this.userFilesDirectory = userFilesDirectory;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static Iterable<MPUser> unmarshallUsers(final File userFilesDirectory) {
 | 
			
		||||
    private static Iterable<MPFileUser> unmarshallUsers(final File userFilesDirectory) {
 | 
			
		||||
        if (!userFilesDirectory.mkdirs() && !userFilesDirectory.isDirectory()) {
 | 
			
		||||
            logger.err( "Couldn't create directory for user files: %s", userFilesDirectory );
 | 
			
		||||
            return ImmutableList.of();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return FluentIterable.from( listUserFiles( userFilesDirectory ) ).transform( new Function<File, MPUser>() {
 | 
			
		||||
        return FluentIterable.from( listUserFiles( userFilesDirectory ) ).transform( new Function<File, MPFileUser>() {
 | 
			
		||||
            @Nullable
 | 
			
		||||
            @Override
 | 
			
		||||
            public MPUser apply(@Nullable final File file) {
 | 
			
		||||
            public MPFileUser apply(@Nullable final File file) {
 | 
			
		||||
                try {
 | 
			
		||||
                    return new MPFlatUnmarshaller().unmarshall( Preconditions.checkNotNull( file ) );
 | 
			
		||||
                }
 | 
			
		||||
@@ -95,42 +97,37 @@ public class MPUserFileManager extends MPUserManager {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void addUser(final MPUser user) {
 | 
			
		||||
        super.addUser( user );
 | 
			
		||||
        save();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void deleteUser(final MPUser user) {
 | 
			
		||||
    public void deleteUser(final MPFileUser user) {
 | 
			
		||||
        super.deleteUser( user );
 | 
			
		||||
        save();
 | 
			
		||||
 | 
			
		||||
        // Remove deleted users.
 | 
			
		||||
        File userFile = getUserFile( user );
 | 
			
		||||
        if (userFile.exists() && !userFile.delete())
 | 
			
		||||
            logger.err( "Couldn't delete file: %s", userFile );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Write the current user state to disk.
 | 
			
		||||
     */
 | 
			
		||||
    public void save() {
 | 
			
		||||
        // Save existing users.
 | 
			
		||||
        for (final MPUser user : getUsers())
 | 
			
		||||
            try {
 | 
			
		||||
                new CharSink() {
 | 
			
		||||
                    @Override
 | 
			
		||||
                    public Writer openStream()
 | 
			
		||||
                            throws IOException {
 | 
			
		||||
                        File mpsitesFile = new File( userFilesDirectory, user.getFullName() + ".mpsites" );
 | 
			
		||||
                        return new OutputStreamWriter( new FileOutputStream( mpsitesFile ), Charsets.UTF_8 );
 | 
			
		||||
                    }
 | 
			
		||||
                }.write( new MPFlatMarshaller().marshall( user, null/*TODO: masterKey*/, MPMarshaller.ContentMode.PROTECTED ) );
 | 
			
		||||
            }
 | 
			
		||||
            catch (final IOException e) {
 | 
			
		||||
                logger.err( e, "Unable to save sites for user: %s", user );
 | 
			
		||||
            }
 | 
			
		||||
    public void save(final MPFileUser user, final MPMasterKey masterKey)
 | 
			
		||||
            throws MPInvalidatedException {
 | 
			
		||||
        try {
 | 
			
		||||
            new CharSink() {
 | 
			
		||||
                @Override
 | 
			
		||||
                public Writer openStream()
 | 
			
		||||
                        throws IOException {
 | 
			
		||||
                    return new OutputStreamWriter( new FileOutputStream( getUserFile( user ) ), Charsets.UTF_8 );
 | 
			
		||||
                }
 | 
			
		||||
            }.write( new MPFlatMarshaller().marshall( user, masterKey, MPMarshaller.ContentMode.PROTECTED ) );
 | 
			
		||||
        }
 | 
			
		||||
        catch (final IOException e) {
 | 
			
		||||
            logger.err( e, "Unable to save sites for user: %s", user );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        // Remove deleted users.
 | 
			
		||||
        for (final File userFile : listUserFiles( userFilesDirectory ))
 | 
			
		||||
            if (getUserNamed( userFile.getName().replaceFirst( "\\.mpsites$", "" ) ) == null)
 | 
			
		||||
                if (!userFile.delete())
 | 
			
		||||
                    logger.err( "Couldn't delete file: %s", userFile );
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    private File getUserFile(final MPFileUser user) {
 | 
			
		||||
        return new File( userFilesDirectory, user.getFullName() + ".mpsites" );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -21,8 +21,7 @@ package com.lyndir.masterpassword.model;
 | 
			
		||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.ifNotNullElse;
 | 
			
		||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
 | 
			
		||||
 | 
			
		||||
import com.lyndir.masterpassword.MPConstant;
 | 
			
		||||
import com.lyndir.masterpassword.MasterKey;
 | 
			
		||||
import com.lyndir.masterpassword.*;
 | 
			
		||||
import org.joda.time.Instant;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -34,7 +33,8 @@ public class MPFlatMarshaller implements MPMarshaller {
 | 
			
		||||
    private static final int FORMAT = 1;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String marshall(final MPUser user, final MasterKey masterKey, final ContentMode contentMode) {
 | 
			
		||||
    public String marshall(final MPFileUser user, final MPMasterKey masterKey, final ContentMode contentMode)
 | 
			
		||||
            throws MPInvalidatedException {
 | 
			
		||||
        StringBuilder content = new StringBuilder();
 | 
			
		||||
        content.append( "# Master Password site export\n" );
 | 
			
		||||
        content.append( "#     " ).append( contentMode.description() ).append( '\n' );
 | 
			
		||||
@@ -46,7 +46,7 @@ public class MPFlatMarshaller implements MPMarshaller {
 | 
			
		||||
        content.append( "# Full Name: " ).append( user.getFullName() ).append( '\n' );
 | 
			
		||||
        content.append( "# Avatar: " ).append( user.getAvatar() ).append( '\n' );
 | 
			
		||||
        content.append( "# Key ID: " ).append( user.exportKeyID() ).append( '\n' );
 | 
			
		||||
        content.append( "# Algorithm: " ).append( MasterKey.Version.CURRENT.toInt() ).append( '\n' );
 | 
			
		||||
        content.append( "# Algorithm: " ).append( MPMasterKey.Version.CURRENT.toInt() ).append( '\n' );
 | 
			
		||||
        content.append( "# Default Type: " ).append( user.getDefaultType().getType() ).append( '\n' );
 | 
			
		||||
        content.append( "# Passwords: " ).append( contentMode.name() ).append( '\n' );
 | 
			
		||||
        content.append( "##\n" );
 | 
			
		||||
@@ -54,7 +54,7 @@ public class MPFlatMarshaller implements MPMarshaller {
 | 
			
		||||
        content.append( "#               Last     Times  Password                      Login\t                     Site\tSite\n" );
 | 
			
		||||
        content.append( "#               used      used      type                       name\t                     name\tpassword\n" );
 | 
			
		||||
 | 
			
		||||
        for (final MPSite site : user.getSites()) {
 | 
			
		||||
        for (final MPFileSite site : user.getSites()) {
 | 
			
		||||
            String loginName = site.getLoginContent();
 | 
			
		||||
            String password = site.getSiteContent();
 | 
			
		||||
            if (!contentMode.isRedacted()) {
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,7 @@ public class MPFlatUnmarshaller implements MPUnmarshaller {
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public MPUser unmarshall(@Nonnull final File file)
 | 
			
		||||
    public MPFileUser unmarshall(@Nonnull final File file)
 | 
			
		||||
            throws IOException {
 | 
			
		||||
        try (Reader reader = new InputStreamReader( new FileInputStream( file ), Charsets.UTF_8 )) {
 | 
			
		||||
            return unmarshall( CharStreams.toString( reader ) );
 | 
			
		||||
@@ -53,8 +53,8 @@ public class MPFlatUnmarshaller implements MPUnmarshaller {
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public MPUser unmarshall(@Nonnull final String content) {
 | 
			
		||||
        MPUser       user         = null;
 | 
			
		||||
    public MPFileUser unmarshall(@Nonnull final String content) {
 | 
			
		||||
        MPFileUser   user         = null;
 | 
			
		||||
        byte[]       keyID        = null;
 | 
			
		||||
        String       fullName     = null;
 | 
			
		||||
        int          mpVersion    = 0, importFormat = 0, avatar = 0;
 | 
			
		||||
@@ -70,7 +70,7 @@ public class MPFlatUnmarshaller implements MPUnmarshaller {
 | 
			
		||||
                    headerStarted = true;
 | 
			
		||||
                else
 | 
			
		||||
                    // Ends the header.
 | 
			
		||||
                    user = new MPUser( fullName, keyID, MasterKey.Version.fromInt( mpVersion ), avatar, defaultType, new DateTime( 0 ) );
 | 
			
		||||
                    user = new MPFileUser( fullName, keyID, MPMasterKey.Version.fromInt( mpVersion ), avatar, defaultType, new DateTime( 0 ) );
 | 
			
		||||
 | 
			
		||||
                // Comment.
 | 
			
		||||
            else if (line.startsWith( "#" )) {
 | 
			
		||||
@@ -103,28 +103,28 @@ public class MPFlatUnmarshaller implements MPUnmarshaller {
 | 
			
		||||
                if (!siteMatcher.matches())
 | 
			
		||||
                    return null;
 | 
			
		||||
 | 
			
		||||
                MPSite site;
 | 
			
		||||
                MPFileSite site;
 | 
			
		||||
                switch (importFormat) {
 | 
			
		||||
                    case 0:
 | 
			
		||||
                        site = new MPSite( user, //
 | 
			
		||||
                                           siteMatcher.group( 5 ), siteMatcher.group( 6 ), MPSite.DEFAULT_COUNTER,
 | 
			
		||||
                                           MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
 | 
			
		||||
                                           MasterKey.Version.fromInt( ConversionUtils.toIntegerNN(
 | 
			
		||||
                        site = new MPFileSite( user, //
 | 
			
		||||
                                               siteMatcher.group( 5 ), siteMatcher.group( 6 ), MPFileSite.DEFAULT_COUNTER,
 | 
			
		||||
                                               MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
 | 
			
		||||
                                               MPMasterKey.Version.fromInt( ConversionUtils.toIntegerNN(
 | 
			
		||||
                                                   colon.matcher( siteMatcher.group( 4 ) ).replaceAll( "" ) ) ),
 | 
			
		||||
                                           null, null, null, ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ),
 | 
			
		||||
                                           MPConstant.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() );
 | 
			
		||||
                                               null, null, null, ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ),
 | 
			
		||||
                                               MPConstant.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() );
 | 
			
		||||
                        break;
 | 
			
		||||
 | 
			
		||||
                    case 1:
 | 
			
		||||
                        site = new MPSite( user, //
 | 
			
		||||
                                           siteMatcher.group( 7 ), siteMatcher.group( 8 ),
 | 
			
		||||
                                           UnsignedInteger.valueOf( colon.matcher( siteMatcher.group( 5 ) ).replaceAll( "" ) ),
 | 
			
		||||
                                           MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
 | 
			
		||||
                                           MasterKey.Version.fromInt( ConversionUtils.toIntegerNN(
 | 
			
		||||
                        site = new MPFileSite( user, //
 | 
			
		||||
                                               siteMatcher.group( 7 ), siteMatcher.group( 8 ),
 | 
			
		||||
                                               UnsignedInteger.valueOf( colon.matcher( siteMatcher.group( 5 ) ).replaceAll( "" ) ),
 | 
			
		||||
                                               MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
 | 
			
		||||
                                               MPMasterKey.Version.fromInt( ConversionUtils.toIntegerNN(
 | 
			
		||||
                                                   colon.matcher( siteMatcher.group( 4 ) ).replaceAll( "" ) ) ),
 | 
			
		||||
                                           siteMatcher.group( 6 ), MPResultType.GeneratedName, null,
 | 
			
		||||
                                           ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ),
 | 
			
		||||
                                           MPConstant.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() );
 | 
			
		||||
                                               siteMatcher.group( 6 ), MPResultType.GeneratedName, null,
 | 
			
		||||
                                               ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ),
 | 
			
		||||
                                               MPConstant.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() );
 | 
			
		||||
                        break;
 | 
			
		||||
 | 
			
		||||
                    default:
 | 
			
		||||
 
 | 
			
		||||
@@ -21,17 +21,17 @@ package com.lyndir.masterpassword.model;
 | 
			
		||||
/**
 | 
			
		||||
 * @author lhunath, 14-12-17
 | 
			
		||||
 */
 | 
			
		||||
public class IncorrectMasterPasswordException extends Exception {
 | 
			
		||||
public class MPIncorrectMasterPasswordException extends Exception {
 | 
			
		||||
 | 
			
		||||
    private final MPUser user;
 | 
			
		||||
    private final MPFileUser user;
 | 
			
		||||
 | 
			
		||||
    public IncorrectMasterPasswordException(final MPUser user) {
 | 
			
		||||
    public MPIncorrectMasterPasswordException(final MPFileUser user) {
 | 
			
		||||
        super( "Incorrect master password for user: " + user.getFullName() );
 | 
			
		||||
 | 
			
		||||
        this.user = user;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MPUser getUser() {
 | 
			
		||||
    public MPFileUser getUser() {
 | 
			
		||||
        return user;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -18,7 +18,7 @@
 | 
			
		||||
 | 
			
		||||
package com.lyndir.masterpassword.model;
 | 
			
		||||
 | 
			
		||||
import com.lyndir.masterpassword.MasterKey;
 | 
			
		||||
import com.lyndir.masterpassword.MPMasterKey;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -27,7 +27,7 @@ import com.lyndir.masterpassword.MasterKey;
 | 
			
		||||
public class MPJSONMarshaller implements MPMarshaller {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String marshall(final MPUser user, final MasterKey masterKey, final ContentMode contentMode) {
 | 
			
		||||
    public String marshall(final MPFileUser user, final MPMasterKey masterKey, final ContentMode contentMode) {
 | 
			
		||||
        // TODO
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ public class MPJSONUnmarshaller implements MPUnmarshaller {
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public MPUser unmarshall(@Nonnull final File file)
 | 
			
		||||
    public MPFileUser unmarshall(@Nonnull final File file)
 | 
			
		||||
            throws IOException {
 | 
			
		||||
        // TODO
 | 
			
		||||
        return null;
 | 
			
		||||
@@ -38,7 +38,7 @@ public class MPJSONUnmarshaller implements MPUnmarshaller {
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public MPUser unmarshall(@Nonnull final String content) {
 | 
			
		||||
    public MPFileUser unmarshall(@Nonnull final String content) {
 | 
			
		||||
        // TODO
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,8 @@
 | 
			
		||||
 | 
			
		||||
package com.lyndir.masterpassword.model;
 | 
			
		||||
 | 
			
		||||
import com.lyndir.masterpassword.MasterKey;
 | 
			
		||||
import com.lyndir.masterpassword.MPInvalidatedException;
 | 
			
		||||
import com.lyndir.masterpassword.MPMasterKey;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -26,7 +27,8 @@ import com.lyndir.masterpassword.MasterKey;
 | 
			
		||||
 */
 | 
			
		||||
public interface MPMarshaller {
 | 
			
		||||
 | 
			
		||||
    String marshall(MPUser user, MasterKey masterKey, ContentMode contentMode);
 | 
			
		||||
    String marshall(MPFileUser user, MPMasterKey masterKey, ContentMode contentMode)
 | 
			
		||||
            throws MPInvalidatedException;
 | 
			
		||||
 | 
			
		||||
    enum ContentMode {
 | 
			
		||||
        PROTECTED( "Export of site names and stored passwords (unless device-private) encrypted with the master key." ),
 | 
			
		||||
 
 | 
			
		||||
@@ -24,170 +24,44 @@ import com.google.common.primitives.UnsignedInteger;
 | 
			
		||||
import com.lyndir.masterpassword.*;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import org.joda.time.Instant;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author lhunath, 14-12-05
 | 
			
		||||
 * @author lhunath, 14-12-16
 | 
			
		||||
 */
 | 
			
		||||
public class MPSite {
 | 
			
		||||
public abstract class MPSite {
 | 
			
		||||
 | 
			
		||||
    public static final UnsignedInteger DEFAULT_COUNTER = UnsignedInteger.ONE;
 | 
			
		||||
 | 
			
		||||
    private final MPUser            user;
 | 
			
		||||
    private       String            siteName;
 | 
			
		||||
    @Nullable
 | 
			
		||||
    private       String            siteContent;
 | 
			
		||||
    private       UnsignedInteger   siteCounter;
 | 
			
		||||
    private       MPResultType      resultType;
 | 
			
		||||
    private       MasterKey.Version algorithmVersion;
 | 
			
		||||
    public abstract String getSiteName();
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    private String       loginContent;
 | 
			
		||||
    @Nullable
 | 
			
		||||
    private MPResultType loginType;
 | 
			
		||||
    public abstract void setSiteName(String siteName);
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    private String  url;
 | 
			
		||||
    private int     uses;
 | 
			
		||||
    private Instant lastUsed;
 | 
			
		||||
    public abstract UnsignedInteger getSiteCounter();
 | 
			
		||||
 | 
			
		||||
    public MPSite(final MPUser user, final String siteName) {
 | 
			
		||||
        this( user, siteName, DEFAULT_COUNTER, MPResultType.DEFAULT );
 | 
			
		||||
    public abstract void setSiteCounter(UnsignedInteger siteCounter);
 | 
			
		||||
 | 
			
		||||
    public abstract MPResultType getResultType();
 | 
			
		||||
 | 
			
		||||
    public abstract void setResultType(MPResultType resultType);
 | 
			
		||||
 | 
			
		||||
    public abstract MPMasterKey.Version getAlgorithmVersion();
 | 
			
		||||
 | 
			
		||||
    public abstract void setAlgorithmVersion(MPMasterKey.Version algorithmVersion);
 | 
			
		||||
 | 
			
		||||
    public String resultFor(final MPMasterKey masterKey, final MPKeyPurpose keyPurpose, @Nullable final String keyContext,
 | 
			
		||||
                            @Nullable final String siteContent)
 | 
			
		||||
            throws MPInvalidatedException {
 | 
			
		||||
 | 
			
		||||
        return masterKey.siteResult(
 | 
			
		||||
                getSiteName(), getSiteCounter(), keyPurpose, keyContext, getResultType(), siteContent, getAlgorithmVersion() );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MPSite(final MPUser user, final String siteName, final UnsignedInteger siteCounter, final MPResultType resultType) {
 | 
			
		||||
        this.user = user;
 | 
			
		||||
        this.siteName = siteName;
 | 
			
		||||
        this.siteCounter = siteCounter;
 | 
			
		||||
        this.resultType = resultType;
 | 
			
		||||
        this.algorithmVersion = MasterKey.Version.CURRENT;
 | 
			
		||||
        this.lastUsed = new Instant();
 | 
			
		||||
    }
 | 
			
		||||
    public String loginFor(final MPMasterKey masterKey, final MPResultType loginType, @Nullable final String loginContent)
 | 
			
		||||
            throws MPInvalidatedException {
 | 
			
		||||
 | 
			
		||||
    protected MPSite(final MPUser user, final String siteName, @Nullable final String siteContent, final UnsignedInteger siteCounter,
 | 
			
		||||
                     final MPResultType resultType, final MasterKey.Version algorithmVersion,
 | 
			
		||||
                     @Nullable final String loginContent, @Nullable final MPResultType loginType,
 | 
			
		||||
                     @Nullable final String url, final int uses, final Instant lastUsed) {
 | 
			
		||||
        this.user = user;
 | 
			
		||||
        this.siteName = siteName;
 | 
			
		||||
        this.siteContent = siteContent;
 | 
			
		||||
        this.siteCounter = siteCounter;
 | 
			
		||||
        this.resultType = resultType;
 | 
			
		||||
        this.algorithmVersion = algorithmVersion;
 | 
			
		||||
        this.loginContent = loginContent;
 | 
			
		||||
        this.loginType = loginType;
 | 
			
		||||
        this.url = url;
 | 
			
		||||
        this.uses = uses;
 | 
			
		||||
        this.lastUsed = lastUsed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String resultFor(final MasterKey masterKey) {
 | 
			
		||||
        return resultFor( masterKey, MPKeyPurpose.Authentication, null );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String resultFor(final MasterKey masterKey, final MPKeyPurpose purpose, @Nullable final String context) {
 | 
			
		||||
        return masterKey.siteResult( siteName, siteCounter, purpose, context, resultType, siteContent, algorithmVersion );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String loginFor(final MasterKey masterKey) {
 | 
			
		||||
        if (loginType == null)
 | 
			
		||||
            loginType = MPResultType.GeneratedName;
 | 
			
		||||
 | 
			
		||||
        return masterKey.siteResult( siteName, DEFAULT_COUNTER, MPKeyPurpose.Identification, null, loginType, loginContent,
 | 
			
		||||
                                     algorithmVersion );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MPUser getUser() {
 | 
			
		||||
        return user;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    protected String exportContent() {
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MasterKey.Version getAlgorithmVersion() {
 | 
			
		||||
        return algorithmVersion;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setAlgorithmVersion(final MasterKey.Version mpVersion) {
 | 
			
		||||
        this.algorithmVersion = mpVersion;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Instant getLastUsed() {
 | 
			
		||||
        return lastUsed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void updateLastUsed() {
 | 
			
		||||
        lastUsed = new Instant();
 | 
			
		||||
        user.updateLastUsed();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getSiteName() {
 | 
			
		||||
        return siteName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setSiteName(final String siteName) {
 | 
			
		||||
        this.siteName = siteName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    public String getSiteContent() {
 | 
			
		||||
        return siteContent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MPResultType getResultType() {
 | 
			
		||||
        return resultType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setResultType(final MPResultType resultType) {
 | 
			
		||||
        this.resultType = resultType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public UnsignedInteger getSiteCounter() {
 | 
			
		||||
        return siteCounter;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setSiteCounter(final UnsignedInteger siteCounter) {
 | 
			
		||||
        this.siteCounter = siteCounter;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getUses() {
 | 
			
		||||
        return uses;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setUses(final int uses) {
 | 
			
		||||
        this.uses = uses;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    public MPResultType getLoginType() {
 | 
			
		||||
        return loginType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    public String getLoginContent() {
 | 
			
		||||
        return loginContent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setLoginName(final MasterKey masterKey, @Nullable final MPResultType loginType, @Nullable final String result) {
 | 
			
		||||
        this.loginType = loginType;
 | 
			
		||||
        if (this.loginType != null)
 | 
			
		||||
            if (result == null)
 | 
			
		||||
                this.loginContent = null;
 | 
			
		||||
            else
 | 
			
		||||
                this.loginContent = masterKey.siteState(
 | 
			
		||||
                        siteName, DEFAULT_COUNTER, MPKeyPurpose.Identification, null, this.loginType, result, algorithmVersion );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    public String getUrl() {
 | 
			
		||||
        return url;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setUrl(@Nullable final String url) {
 | 
			
		||||
        this.url = url;
 | 
			
		||||
        return masterKey.siteResult(
 | 
			
		||||
                getSiteName(), DEFAULT_COUNTER, MPKeyPurpose.Identification, null, loginType, loginContent, getAlgorithmVersion() );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -202,6 +76,6 @@ public class MPSite {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String toString() {
 | 
			
		||||
        return strf( "{MPSite: %s}", getSiteName() );
 | 
			
		||||
        return strf( "{%s: %s}", getClass().getSimpleName(), getSiteName() );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -28,13 +28,13 @@ import java.util.Objects;
 | 
			
		||||
 */
 | 
			
		||||
public class MPSiteResult {
 | 
			
		||||
 | 
			
		||||
    private final MPSite site;
 | 
			
		||||
    private final MPFileSite site;
 | 
			
		||||
 | 
			
		||||
    public MPSiteResult(final MPSite site) {
 | 
			
		||||
    public MPSiteResult(final MPFileSite site) {
 | 
			
		||||
        this.site = site;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MPSite getSite() {
 | 
			
		||||
    public MPFileSite getSite() {
 | 
			
		||||
        return site;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -28,9 +28,9 @@ import javax.annotation.Nonnull;
 | 
			
		||||
public interface MPUnmarshaller {
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    MPUser unmarshall(@Nonnull File file)
 | 
			
		||||
    MPFileUser unmarshall(@Nonnull File file)
 | 
			
		||||
            throws IOException;
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    MPUser unmarshall(@Nonnull String content);
 | 
			
		||||
    MPFileUser unmarshall(@Nonnull String content);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -18,155 +18,69 @@
 | 
			
		||||
 | 
			
		||||
package com.lyndir.masterpassword.model;
 | 
			
		||||
 | 
			
		||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
 | 
			
		||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
 | 
			
		||||
 | 
			
		||||
import com.google.common.collect.ImmutableList;
 | 
			
		||||
import com.google.common.collect.Sets;
 | 
			
		||||
import com.google.common.base.Preconditions;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.CodeUtils;
 | 
			
		||||
import com.lyndir.masterpassword.MPResultType;
 | 
			
		||||
import com.lyndir.masterpassword.MasterKey;
 | 
			
		||||
import com.lyndir.masterpassword.MPInvalidatedException;
 | 
			
		||||
import com.lyndir.masterpassword.MPMasterKey;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import org.joda.time.*;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author lhunath, 14-12-07
 | 
			
		||||
 * @author lhunath, 2014-06-08
 | 
			
		||||
 */
 | 
			
		||||
public class MPUser implements Comparable<MPUser> {
 | 
			
		||||
 | 
			
		||||
    private final String fullName;
 | 
			
		||||
    private final Collection<MPSite> sites = Sets.newHashSet();
 | 
			
		||||
public abstract class MPUser<S extends MPSite> {
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    private byte[]            keyID;
 | 
			
		||||
    private MasterKey.Version algorithmVersion;
 | 
			
		||||
    protected MPMasterKey key;
 | 
			
		||||
 | 
			
		||||
    private int             avatar;
 | 
			
		||||
    private MPResultType    defaultType;
 | 
			
		||||
    private ReadableInstant lastUsed;
 | 
			
		||||
    public abstract String getFullName();
 | 
			
		||||
 | 
			
		||||
    public MPUser(final String fullName) {
 | 
			
		||||
        this( fullName, null, MasterKey.Version.CURRENT );
 | 
			
		||||
    public boolean isMasterKeyAvailable() {
 | 
			
		||||
        return key != null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MPUser(final String fullName, @Nullable final byte[] keyID, final MasterKey.Version algorithmVersion) {
 | 
			
		||||
        this( fullName, keyID, algorithmVersion, 0, MPResultType.DEFAULT, new Instant() );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MPUser(final String fullName, @Nullable final byte[] keyID, final MasterKey.Version algorithmVersion, final int avatar,
 | 
			
		||||
                  final MPResultType defaultType, final ReadableInstant lastUsed) {
 | 
			
		||||
        this.fullName = fullName;
 | 
			
		||||
        this.keyID = (keyID == null)? null: keyID.clone();
 | 
			
		||||
        this.algorithmVersion = algorithmVersion;
 | 
			
		||||
        this.avatar = avatar;
 | 
			
		||||
        this.defaultType = defaultType;
 | 
			
		||||
        this.lastUsed = lastUsed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Collection<MPSiteResult> findSitesByName(final String query) {
 | 
			
		||||
        ImmutableList.Builder<MPSiteResult> results = ImmutableList.builder();
 | 
			
		||||
        for (final MPSite site : getSites())
 | 
			
		||||
            if (site.getSiteName().startsWith( query ))
 | 
			
		||||
                results.add( new MPSiteResult( site ) );
 | 
			
		||||
 | 
			
		||||
        return results.build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void addSite(final MPSite site) {
 | 
			
		||||
        sites.add( site );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void deleteSite(final MPSite site) {
 | 
			
		||||
        sites.remove( site );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getFullName() {
 | 
			
		||||
        return fullName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean hasKeyID() {
 | 
			
		||||
        return keyID != null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String exportKeyID() {
 | 
			
		||||
        return CodeUtils.encodeHex( keyID );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Performs an authentication attempt against the keyID for this user.
 | 
			
		||||
     *
 | 
			
		||||
     * Note: If this user doesn't have a keyID set yet, authentication will always succeed and the key ID will be set as a result.
 | 
			
		||||
     *
 | 
			
		||||
     * @param masterPassword The password to authenticate with.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The master key for the user if authentication was successful.
 | 
			
		||||
     *
 | 
			
		||||
     * @throws IncorrectMasterPasswordException If authentication fails due to the given master password not matching the user's keyID.
 | 
			
		||||
     */
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @SuppressWarnings("MethodCanBeVariableArityMethod")
 | 
			
		||||
    public MasterKey authenticate(final char[] masterPassword)
 | 
			
		||||
            throws IncorrectMasterPasswordException {
 | 
			
		||||
        MasterKey masterKey = new MasterKey( getFullName(), masterPassword );
 | 
			
		||||
        if ((keyID == null) || (keyID.length == 0))
 | 
			
		||||
            keyID = masterKey.getKeyID( algorithmVersion );
 | 
			
		||||
        else if (!Arrays.equals( masterKey.getKeyID( algorithmVersion ), keyID ))
 | 
			
		||||
            throw new IncorrectMasterPasswordException( this );
 | 
			
		||||
 | 
			
		||||
        return masterKey;
 | 
			
		||||
    public MPMasterKey getMasterKey() {
 | 
			
		||||
        return Preconditions.checkNotNull( key, "User is not authenticated: " + getFullName() );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String exportKeyID()
 | 
			
		||||
            throws MPInvalidatedException {
 | 
			
		||||
        return CodeUtils.encodeHex( getMasterKey().getKeyID( getAlgorithmVersion() ) );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public abstract MPMasterKey.Version getAlgorithmVersion();
 | 
			
		||||
 | 
			
		||||
    public int getAvatar() {
 | 
			
		||||
        return avatar;
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setAvatar(final int avatar) {
 | 
			
		||||
        this.avatar = avatar;
 | 
			
		||||
    }
 | 
			
		||||
    public abstract void addSite(S site);
 | 
			
		||||
 | 
			
		||||
    public MPResultType getDefaultType() {
 | 
			
		||||
        return defaultType;
 | 
			
		||||
    }
 | 
			
		||||
    public abstract void deleteSite(S site);
 | 
			
		||||
 | 
			
		||||
    public void setDefaultType(final MPResultType defaultType) {
 | 
			
		||||
        this.defaultType = defaultType;
 | 
			
		||||
    }
 | 
			
		||||
    public abstract Collection<S> findSites(String query);
 | 
			
		||||
 | 
			
		||||
    public ReadableInstant getLastUsed() {
 | 
			
		||||
        return lastUsed;
 | 
			
		||||
    }
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    public abstract MPMasterKey authenticate(char[] masterPassword)
 | 
			
		||||
            throws MPIncorrectMasterPasswordException;
 | 
			
		||||
 | 
			
		||||
    public void updateLastUsed() {
 | 
			
		||||
        lastUsed = new Instant();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Iterable<MPSite> getSites() {
 | 
			
		||||
        return sites;
 | 
			
		||||
    @Override
 | 
			
		||||
    public int hashCode() {
 | 
			
		||||
        return Objects.hashCode( getFullName() );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean equals(final Object obj) {
 | 
			
		||||
        return (this == obj) || ((obj instanceof MPUser) && Objects.equals( fullName, ((MPUser) obj).fullName ));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int hashCode() {
 | 
			
		||||
        return Objects.hashCode( fullName );
 | 
			
		||||
        return (this == obj) || ((obj instanceof MPUser) && Objects.equals( getFullName(), ((MPUser<?>) obj).getFullName() ));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String toString() {
 | 
			
		||||
        return strf( "{MPUser: %s}", fullName );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int compareTo(final MPUser o) {
 | 
			
		||||
        int comparison = lastUsed.compareTo( o.lastUsed );
 | 
			
		||||
        if (comparison == 0)
 | 
			
		||||
            comparison = fullName.compareTo( o.fullName );
 | 
			
		||||
 | 
			
		||||
        return comparison;
 | 
			
		||||
        return strf( "{%s: %s}", getClass().getSimpleName(), getFullName() );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@
 | 
			
		||||
package com.lyndir.masterpassword.model;
 | 
			
		||||
 | 
			
		||||
import com.google.common.collect.*;
 | 
			
		||||
import com.lyndir.masterpassword.MPInvalidatedException;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -27,31 +28,31 @@ import java.util.*;
 | 
			
		||||
 */
 | 
			
		||||
public abstract class MPUserManager {
 | 
			
		||||
 | 
			
		||||
    private final Map<String, MPUser> usersByName = Maps.newHashMap();
 | 
			
		||||
    private final Map<String, MPFileUser> usersByName = Maps.newHashMap();
 | 
			
		||||
    static MPUserManager instance;
 | 
			
		||||
 | 
			
		||||
    public static MPUserManager get() {
 | 
			
		||||
        return instance;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected MPUserManager(final Iterable<MPUser> users) {
 | 
			
		||||
        for (final MPUser user : users)
 | 
			
		||||
    protected MPUserManager(final Iterable<MPFileUser> users) {
 | 
			
		||||
        for (final MPFileUser user : users)
 | 
			
		||||
            usersByName.put( user.getFullName(), user );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public SortedSet<MPUser> getUsers() {
 | 
			
		||||
    public SortedSet<MPFileUser> getUsers() {
 | 
			
		||||
        return FluentIterable.from( usersByName.values() ).toSortedSet( Ordering.natural() );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MPUser getUserNamed(final String fullName) {
 | 
			
		||||
    public MPFileUser getUserNamed(final String fullName) {
 | 
			
		||||
        return usersByName.get( fullName );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void addUser(final MPUser user) {
 | 
			
		||||
    public void addUser(final MPFileUser user) {
 | 
			
		||||
        usersByName.put( user.getFullName(), user );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void deleteUser(final MPUser user) {
 | 
			
		||||
    public void deleteUser(final MPFileUser user) {
 | 
			
		||||
        usersByName.remove( user.getFullName() );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -23,12 +23,10 @@ import com.google.common.collect.Lists;
 | 
			
		||||
import com.google.common.primitives.UnsignedInteger;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.util.NNFunctionNN;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.Deque;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.concurrent.Callable;
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import javax.xml.parsers.*;
 | 
			
		||||
import org.xml.sax.Attributes;
 | 
			
		||||
import org.xml.sax.SAXException;
 | 
			
		||||
@@ -38,7 +36,7 @@ import org.xml.sax.ext.DefaultHandler2;
 | 
			
		||||
/**
 | 
			
		||||
 * @author lhunath, 2015-12-22
 | 
			
		||||
 */
 | 
			
		||||
@SuppressWarnings("HardCodedStringLiteral")
 | 
			
		||||
@SuppressWarnings({ "HardCodedStringLiteral", "ProhibitedExceptionDeclared" })
 | 
			
		||||
public class MPTestSuite implements Callable<Boolean> {
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("UnusedDeclaration")
 | 
			
		||||
@@ -134,7 +132,8 @@ public class MPTestSuite implements Callable<Boolean> {
 | 
			
		||||
        return tests;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean forEach(final String testName, final NNFunctionNN<MPTests.Case, Boolean> testFunction) {
 | 
			
		||||
    public boolean forEach(final String testName, final TestCase testFunction)
 | 
			
		||||
            throws Exception {
 | 
			
		||||
        List<MPTests.Case> cases = tests.getCases();
 | 
			
		||||
        for (int c = 0; c < cases.size(); c++) {
 | 
			
		||||
            MPTests.Case testCase = cases.get( c );
 | 
			
		||||
@@ -144,7 +143,7 @@ public class MPTestSuite implements Callable<Boolean> {
 | 
			
		||||
            progress( Logger.Target.INFO, c, cases.size(), //
 | 
			
		||||
                      "[%s] on %s...", testName, testCase.getIdentifier() );
 | 
			
		||||
 | 
			
		||||
            if (!testFunction.apply( testCase )) {
 | 
			
		||||
            if (!testFunction.run( testCase )) {
 | 
			
		||||
                progress( Logger.Target.ERROR, cases.size(), cases.size(), //
 | 
			
		||||
                          "[%s] on %s: FAILED!", testName, testCase.getIdentifier() );
 | 
			
		||||
 | 
			
		||||
@@ -168,11 +167,11 @@ public class MPTestSuite implements Callable<Boolean> {
 | 
			
		||||
    @Override
 | 
			
		||||
    public Boolean call()
 | 
			
		||||
            throws Exception {
 | 
			
		||||
        return forEach( "mpw", new NNFunctionNN<MPTests.Case, Boolean>() {
 | 
			
		||||
            @Nonnull
 | 
			
		||||
        return forEach( "mpw", new TestCase() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public Boolean apply(@Nonnull final MPTests.Case testCase) {
 | 
			
		||||
                MasterKey masterKey = new MasterKey( testCase.getFullName(), testCase.getMasterPassword() );
 | 
			
		||||
            public boolean run(final MPTests.Case testCase)
 | 
			
		||||
                    throws Exception {
 | 
			
		||||
                MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), testCase.getMasterPassword() );
 | 
			
		||||
                String sitePassword = masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
 | 
			
		||||
                                                            testCase.getKeyContext(), testCase.getResultType(),
 | 
			
		||||
                                                            null, testCase.getAlgorithm() );
 | 
			
		||||
@@ -196,4 +195,11 @@ public class MPTestSuite implements Callable<Boolean> {
 | 
			
		||||
 | 
			
		||||
        void progress(int current, int max, String messageFormat, Object... args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public interface TestCase {
 | 
			
		||||
 | 
			
		||||
        boolean run(MPTests.Case testCase)
 | 
			
		||||
                throws Exception;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -171,8 +171,8 @@ public class MPTests {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Nonnull
 | 
			
		||||
        public MasterKey.Version getAlgorithm() {
 | 
			
		||||
            return MasterKey.Version.fromInt( checkNotNull( algorithm ) );
 | 
			
		||||
        public MPMasterKey.Version getAlgorithm() {
 | 
			
		||||
            return MPMasterKey.Version.fromInt( checkNotNull( algorithm ) );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Nonnull
 | 
			
		||||
 
 | 
			
		||||
@@ -1,279 +0,0 @@
 | 
			
		||||
<tests>
 | 
			
		||||
    <!-- Default values for all parameters. -->
 | 
			
		||||
    <case id="default">
 | 
			
		||||
        <algorithm>-1</algorithm>
 | 
			
		||||
        <fullName>Robert Lee Mitchell</fullName>
 | 
			
		||||
        <masterPassword>banana colored duckling</masterPassword>
 | 
			
		||||
        <keyID>98EEF4D1DF46D849574A82A03C3177056B15DFFCA29BB3899DE4628453675302</keyID>
 | 
			
		||||
        <siteName>masterpasswordapp.com</siteName>
 | 
			
		||||
        <siteCounter>1</siteCounter>
 | 
			
		||||
        <resultType>Long</resultType>
 | 
			
		||||
        <keyPurpose>Authentication</keyPurpose>
 | 
			
		||||
        <result><!-- abstract --></result>
 | 
			
		||||
    </case>
 | 
			
		||||
 | 
			
		||||
    <!-- Algorithm 3 -->
 | 
			
		||||
    <case id="v3" parent="default">
 | 
			
		||||
        <algorithm>3</algorithm>
 | 
			
		||||
        <result>Jejr5[RepuSosp</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v3_mb_fullName" parent="v3">
 | 
			
		||||
        <fullName>⛄</fullName>
 | 
			
		||||
        <keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
 | 
			
		||||
        <result>NopaDajh8=Fene</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v3_mb_masterPassword" parent="v3">
 | 
			
		||||
        <masterPassword>⛄</masterPassword>
 | 
			
		||||
        <keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
 | 
			
		||||
        <result>QesuHirv5-Xepl</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v3_mb_siteName" parent="v3">
 | 
			
		||||
        <siteName>⛄</siteName>
 | 
			
		||||
        <result>LiheCuwhSerz6)</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v3_loginName" parent="v3">
 | 
			
		||||
        <keyPurpose>Identification</keyPurpose>
 | 
			
		||||
        <resultType>Name</resultType>
 | 
			
		||||
        <result>wohzaqage</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v3_securityAnswer" parent="v3">
 | 
			
		||||
        <keyPurpose>Recovery</keyPurpose>
 | 
			
		||||
        <resultType>Phrase</resultType>
 | 
			
		||||
        <result>xin diyjiqoja hubu</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v3_securityAnswer_context" parent="v3_securityAnswer">
 | 
			
		||||
        <keyContext>question</keyContext>
 | 
			
		||||
        <result>xogx tem cegyiva jab</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v3_type_maximum" parent="v3">
 | 
			
		||||
        <resultType>Maximum</resultType>
 | 
			
		||||
        <result>W6@692^B1#&@gVdSdLZ@</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v3_type_medium" parent="v3">
 | 
			
		||||
        <resultType>Medium</resultType>
 | 
			
		||||
        <result>Jej2$Quv</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v3_type_basic" parent="v3">
 | 
			
		||||
        <resultType>Basic</resultType>
 | 
			
		||||
        <result>WAo2xIg6</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v3_type_short" parent="v3">
 | 
			
		||||
        <resultType>Short</resultType>
 | 
			
		||||
        <result>Jej2</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v3_type_pin" parent="v3">
 | 
			
		||||
        <resultType>PIN</resultType>
 | 
			
		||||
        <result>7662</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v3_type_name" parent="v3">
 | 
			
		||||
        <resultType>Name</resultType>
 | 
			
		||||
        <result>jejraquvo</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v3_type_phrase" parent="v3">
 | 
			
		||||
        <resultType>Phrase</resultType>
 | 
			
		||||
        <result>jejr quv cabsibu tam</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v3_counter_ceiling" parent="v3">
 | 
			
		||||
        <siteCounter>4294967295</siteCounter>
 | 
			
		||||
        <result>XambHoqo6[Peni</result>
 | 
			
		||||
    </case>
 | 
			
		||||
 | 
			
		||||
    <!-- Algorithm 2 -->
 | 
			
		||||
    <case id="v2" parent="default">
 | 
			
		||||
        <algorithm>2</algorithm>
 | 
			
		||||
        <result>Jejr5[RepuSosp</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v2_mb_fullName" parent="v2">
 | 
			
		||||
        <fullName>⛄</fullName>
 | 
			
		||||
        <keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
 | 
			
		||||
        <result>WaqoGuho2[Xaxw</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v2_mb_masterPassword" parent="v2">
 | 
			
		||||
        <masterPassword>⛄</masterPassword>
 | 
			
		||||
        <keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
 | 
			
		||||
        <result>QesuHirv5-Xepl</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v2_mb_siteName" parent="v2">
 | 
			
		||||
        <siteName>⛄</siteName>
 | 
			
		||||
        <result>LiheCuwhSerz6)</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v2_loginName" parent="v2">
 | 
			
		||||
        <keyPurpose>Identification</keyPurpose>
 | 
			
		||||
        <resultType>Name</resultType>
 | 
			
		||||
        <result>wohzaqage</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v2_securityAnswer" parent="v2">
 | 
			
		||||
        <keyPurpose>Recovery</keyPurpose>
 | 
			
		||||
        <resultType>Phrase</resultType>
 | 
			
		||||
        <result>xin diyjiqoja hubu</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v2_securityAnswer_context" parent="v2_securityAnswer">
 | 
			
		||||
        <keyContext>question</keyContext>
 | 
			
		||||
        <result>xogx tem cegyiva jab</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v2_type_maximum" parent="v2">
 | 
			
		||||
        <resultType>Maximum</resultType>
 | 
			
		||||
        <result>W6@692^B1#&@gVdSdLZ@</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v2_type_medium" parent="v2">
 | 
			
		||||
        <resultType>Medium</resultType>
 | 
			
		||||
        <result>Jej2$Quv</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v2_type_basic" parent="v2">
 | 
			
		||||
        <resultType>Basic</resultType>
 | 
			
		||||
        <result>WAo2xIg6</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v2_type_short" parent="v2">
 | 
			
		||||
        <resultType>Short</resultType>
 | 
			
		||||
        <result>Jej2</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v2_type_pin" parent="v2">
 | 
			
		||||
        <resultType>PIN</resultType>
 | 
			
		||||
        <result>7662</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v2_type_name" parent="v2">
 | 
			
		||||
        <resultType>Name</resultType>
 | 
			
		||||
        <result>jejraquvo</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v2_type_phrase" parent="v2">
 | 
			
		||||
        <resultType>Phrase</resultType>
 | 
			
		||||
        <result>jejr quv cabsibu tam</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v2_counter_ceiling" parent="v2">
 | 
			
		||||
        <siteCounter>4294967295</siteCounter>
 | 
			
		||||
        <result>XambHoqo6[Peni</result>
 | 
			
		||||
    </case>
 | 
			
		||||
 | 
			
		||||
    <!-- Algorithm 1 -->
 | 
			
		||||
    <case id="v1" parent="default">
 | 
			
		||||
        <algorithm>1</algorithm>
 | 
			
		||||
        <result>Jejr5[RepuSosp</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v1_mb_fullName" parent="v1">
 | 
			
		||||
        <fullName>⛄</fullName>
 | 
			
		||||
        <keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
 | 
			
		||||
        <result>WaqoGuho2[Xaxw</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v1_mb_masterPassword" parent="v1">
 | 
			
		||||
        <masterPassword>⛄</masterPassword>
 | 
			
		||||
        <keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
 | 
			
		||||
        <result>QesuHirv5-Xepl</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v1_mb_siteName" parent="v1">
 | 
			
		||||
        <siteName>⛄</siteName>
 | 
			
		||||
        <result>WawiYarp2@Kodh</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v1_loginName" parent="v1">
 | 
			
		||||
        <keyPurpose>Identification</keyPurpose>
 | 
			
		||||
        <resultType>Name</resultType>
 | 
			
		||||
        <result>wohzaqage</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v1_securityAnswer" parent="v1">
 | 
			
		||||
        <keyPurpose>Recovery</keyPurpose>
 | 
			
		||||
        <resultType>Phrase</resultType>
 | 
			
		||||
        <result>xin diyjiqoja hubu</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v1_securityAnswer_context" parent="v1_securityAnswer">
 | 
			
		||||
        <keyContext>question</keyContext>
 | 
			
		||||
        <result>xogx tem cegyiva jab</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v1_type_maximum" parent="v1">
 | 
			
		||||
        <resultType>Maximum</resultType>
 | 
			
		||||
        <result>W6@692^B1#&@gVdSdLZ@</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v1_type_medium" parent="v1">
 | 
			
		||||
        <resultType>Medium</resultType>
 | 
			
		||||
        <result>Jej2$Quv</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v1_type_basic" parent="v1">
 | 
			
		||||
        <resultType>Basic</resultType>
 | 
			
		||||
        <result>WAo2xIg6</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v1_type_short" parent="v1">
 | 
			
		||||
        <resultType>Short</resultType>
 | 
			
		||||
        <result>Jej2</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v1_type_pin" parent="v1">
 | 
			
		||||
        <resultType>PIN</resultType>
 | 
			
		||||
        <result>7662</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v1_type_name" parent="v1">
 | 
			
		||||
        <resultType>Name</resultType>
 | 
			
		||||
        <result>jejraquvo</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v1_type_phrase" parent="v1">
 | 
			
		||||
        <resultType>Phrase</resultType>
 | 
			
		||||
        <result>jejr quv cabsibu tam</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v1_counter_ceiling" parent="v1">
 | 
			
		||||
        <siteCounter>4294967295</siteCounter>
 | 
			
		||||
        <result>XambHoqo6[Peni</result>
 | 
			
		||||
    </case>
 | 
			
		||||
 | 
			
		||||
    <!-- Algorithm 0 -->
 | 
			
		||||
    <case id="v0" parent="default">
 | 
			
		||||
        <algorithm>0</algorithm>
 | 
			
		||||
        <result>Feji5@ReduWosh</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v0_mb_fullName" parent="v0">
 | 
			
		||||
        <fullName>⛄</fullName>
 | 
			
		||||
        <keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
 | 
			
		||||
        <result>HajrYudo7@Mamh</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v0_mb_masterPassword" parent="v0">
 | 
			
		||||
        <masterPassword>⛄</masterPassword>
 | 
			
		||||
        <keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
 | 
			
		||||
        <result>MewmDini0]Meho</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v0_mb_siteName" parent="v0">
 | 
			
		||||
        <siteName>⛄</siteName>
 | 
			
		||||
        <result>HahiVana2@Nole</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v0_loginName" parent="v0">
 | 
			
		||||
        <keyPurpose>Identification</keyPurpose>
 | 
			
		||||
        <resultType>Name</resultType>
 | 
			
		||||
        <result>lozwajave</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v0_securityAnswer" parent="v0">
 | 
			
		||||
        <keyPurpose>Recovery</keyPurpose>
 | 
			
		||||
        <resultType>Phrase</resultType>
 | 
			
		||||
        <result>miy lirfijoja dubu</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v0_securityAnswer_context" parent="v0_securityAnswer">
 | 
			
		||||
        <keyContext>question</keyContext>
 | 
			
		||||
        <result>movm bex gevrica jaf</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v0_type_maximum" parent="v0">
 | 
			
		||||
        <resultType>Maximum</resultType>
 | 
			
		||||
        <result>w1!3bA3icmRAc)SS@lwl</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v0_type_medium" parent="v0">
 | 
			
		||||
        <resultType>Medium</resultType>
 | 
			
		||||
        <result>Fej7]Jug</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v0_type_basic" parent="v0">
 | 
			
		||||
        <resultType>Basic</resultType>
 | 
			
		||||
        <result>wvH7irC1</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v0_type_short" parent="v0">
 | 
			
		||||
        <resultType>Short</resultType>
 | 
			
		||||
        <result>Fej7</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v0_type_pin" parent="v0">
 | 
			
		||||
        <resultType>PIN</resultType>
 | 
			
		||||
        <result>2117</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v0_type_name" parent="v0">
 | 
			
		||||
        <resultType>Name</resultType>
 | 
			
		||||
        <result>fejrajugo</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v0_type_phrase" parent="v0">
 | 
			
		||||
        <resultType>Phrase</resultType>
 | 
			
		||||
        <result>fejr jug gabsibu bax</result>
 | 
			
		||||
    </case>
 | 
			
		||||
    <case id="v0_counter_ceiling" parent="v0">
 | 
			
		||||
        <siteCounter>4294967295</siteCounter>
 | 
			
		||||
        <result>QateDojh1@Hecn</result>
 | 
			
		||||
    </case>
 | 
			
		||||
</tests>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								core/java/tests/src/main/resources/mpw_tests.xml
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								core/java/tests/src/main/resources/mpw_tests.xml
									
									
									
									
									
										Symbolic link
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
../../../../../mpw_tests.xml
 | 
			
		||||
@@ -22,17 +22,15 @@ import static org.testng.Assert.*;
 | 
			
		||||
 | 
			
		||||
import com.lyndir.lhunath.opal.system.CodeUtils;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.util.NNFunctionNN;
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import org.jetbrains.annotations.NonNls;
 | 
			
		||||
import org.testng.annotations.BeforeMethod;
 | 
			
		||||
import org.testng.annotations.Test;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
public class MasterKeyTest {
 | 
			
		||||
public class MPMasterKeyTest {
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("UnusedDeclaration")
 | 
			
		||||
    private static final Logger logger = Logger.get( MasterKeyTest.class );
 | 
			
		||||
    private static final Logger logger = Logger.get( MPMasterKeyTest.class );
 | 
			
		||||
 | 
			
		||||
    @NonNls
 | 
			
		||||
    private MPTestSuite testSuite;
 | 
			
		||||
@@ -48,11 +46,11 @@ public class MasterKeyTest {
 | 
			
		||||
    public void testEncode()
 | 
			
		||||
            throws Exception {
 | 
			
		||||
 | 
			
		||||
        testSuite.forEach( "testEncode", new NNFunctionNN<MPTests.Case, Boolean>() {
 | 
			
		||||
            @Nonnull
 | 
			
		||||
        testSuite.forEach( "testEncode", new MPTestSuite.TestCase() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public Boolean apply(@Nonnull final MPTests.Case testCase) {
 | 
			
		||||
                MasterKey masterKey = new MasterKey( testCase.getFullName(), testCase.getMasterPassword() );
 | 
			
		||||
            public boolean run(final MPTests.Case testCase)
 | 
			
		||||
                    throws Exception {
 | 
			
		||||
                MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), testCase.getMasterPassword() );
 | 
			
		||||
 | 
			
		||||
                assertEquals(
 | 
			
		||||
                        masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
 | 
			
		||||
@@ -71,7 +69,7 @@ public class MasterKeyTest {
 | 
			
		||||
 | 
			
		||||
        MPTests.Case defaultCase = testSuite.getTests().getDefaultCase();
 | 
			
		||||
 | 
			
		||||
        assertEquals( new MasterKey( defaultCase.getFullName(), defaultCase.getMasterPassword() ).getFullName(),
 | 
			
		||||
        assertEquals( new MPMasterKey( defaultCase.getFullName(), defaultCase.getMasterPassword() ).getFullName(),
 | 
			
		||||
                      defaultCase.getFullName(), "[testGetUserName] Failed test case: " + defaultCase );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -79,11 +77,11 @@ public class MasterKeyTest {
 | 
			
		||||
    public void testGetKeyID()
 | 
			
		||||
            throws Exception {
 | 
			
		||||
 | 
			
		||||
        testSuite.forEach( "testGetKeyID", new NNFunctionNN<MPTests.Case, Boolean>() {
 | 
			
		||||
            @Nonnull
 | 
			
		||||
        testSuite.forEach( "testGetKeyID", new MPTestSuite.TestCase() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public Boolean apply(@Nonnull final MPTests.Case testCase) {
 | 
			
		||||
                MasterKey masterKey = new MasterKey( testCase.getFullName(), testCase.getMasterPassword() );
 | 
			
		||||
            public boolean run(final MPTests.Case testCase)
 | 
			
		||||
                    throws Exception {
 | 
			
		||||
                MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), testCase.getMasterPassword() );
 | 
			
		||||
 | 
			
		||||
                assertEquals( CodeUtils.encodeHex( masterKey.getKeyID( testCase.getAlgorithm() ) ),
 | 
			
		||||
                              testCase.getKeyID(), "[testGetKeyID] Failed test case: " + testCase );
 | 
			
		||||
@@ -51,12 +51,12 @@ public class EmergencyActivity extends Activity {
 | 
			
		||||
    private static final int      PASSWORD_NOTIFICATION = 0;
 | 
			
		||||
    public static final int CLIPBOARD_CLEAR_DELAY = 20 /* s */ * MPConstant.MS_PER_S;
 | 
			
		||||
 | 
			
		||||
    private final Preferences                      preferences    = Preferences.get( this );
 | 
			
		||||
    private final ListeningExecutorService         executor       = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor() );
 | 
			
		||||
    private final ImmutableList<MPResultType>      allResultTypes = ImmutableList.copyOf( MPResultType.forClass( MPResultTypeClass.Template ) );
 | 
			
		||||
    private final ImmutableList<MasterKey.Version> allVersions    = ImmutableList.copyOf( MasterKey.Version.values() );
 | 
			
		||||
    private final Preferences                        preferences    = Preferences.get( this );
 | 
			
		||||
    private final ListeningExecutorService           executor       = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor() );
 | 
			
		||||
    private final ImmutableList<MPResultType>        allResultTypes = ImmutableList.copyOf( MPResultType.forClass( MPResultTypeClass.Template ) );
 | 
			
		||||
    private final ImmutableList<MPMasterKey.Version> allVersions    = ImmutableList.copyOf( MPMasterKey.Version.values() );
 | 
			
		||||
 | 
			
		||||
    private MasterKey masterKey;
 | 
			
		||||
    private MPMasterKey masterKey;
 | 
			
		||||
 | 
			
		||||
    @BindView(R.id.progressView)
 | 
			
		||||
    ProgressBar progressView;
 | 
			
		||||
@@ -154,7 +154,7 @@ public class EmergencyActivity extends Activity {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onClick(final View v) {
 | 
			
		||||
                @SuppressWarnings("SuspiciousMethodCalls")
 | 
			
		||||
                MasterKey.Version siteVersion =
 | 
			
		||||
                MPMasterKey.Version siteVersion =
 | 
			
		||||
                        allVersions.get( (allVersions.indexOf( siteVersionButton.getTag() ) + 1) % allVersions.size() );
 | 
			
		||||
                preferences.setDefaultVersion( siteVersion );
 | 
			
		||||
                siteVersionButton.setTag( siteVersion );
 | 
			
		||||
@@ -221,7 +221,7 @@ public class EmergencyActivity extends Activity {
 | 
			
		||||
        MPResultType defaultResultType = preferences.getDefaultResultType();
 | 
			
		||||
        resultTypeButton.setTag( defaultResultType );
 | 
			
		||||
        resultTypeButton.setText( defaultResultType.getShortName() );
 | 
			
		||||
        MasterKey.Version defaultVersion = preferences.getDefaultVersion();
 | 
			
		||||
        MPMasterKey.Version defaultVersion = preferences.getDefaultVersion();
 | 
			
		||||
        siteVersionButton.setTag( defaultVersion );
 | 
			
		||||
        siteVersionButton.setText( defaultVersion.name() );
 | 
			
		||||
        siteCounterButton.setText( MessageFormat.format( "{0}", 1 ) );
 | 
			
		||||
@@ -275,15 +275,15 @@ public class EmergencyActivity extends Activity {
 | 
			
		||||
 | 
			
		||||
        sitePasswordField.setText( "" );
 | 
			
		||||
        progressView.setVisibility( View.VISIBLE );
 | 
			
		||||
        masterKey = new MasterKey( fullName, masterPassword );
 | 
			
		||||
        masterKey = new MPMasterKey( fullName, masterPassword );
 | 
			
		||||
        updateSitePassword();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void updateSitePassword() {
 | 
			
		||||
        final String          siteName = siteNameField.getText().toString();
 | 
			
		||||
        final MPResultType    type     = (MPResultType) resultTypeButton.getTag();
 | 
			
		||||
        final UnsignedInteger counter  = UnsignedInteger.valueOf( siteCounterButton.getText().toString() );
 | 
			
		||||
        final MasterKey.Version version = (MasterKey.Version) siteVersionButton.getTag();
 | 
			
		||||
        final String              siteName = siteNameField.getText().toString();
 | 
			
		||||
        final MPResultType        type     = (MPResultType) resultTypeButton.getTag();
 | 
			
		||||
        final UnsignedInteger     counter  = UnsignedInteger.valueOf( siteCounterButton.getText().toString() );
 | 
			
		||||
        final MPMasterKey.Version version  = (MPMasterKey.Version) siteVersionButton.getTag();
 | 
			
		||||
 | 
			
		||||
        if ((masterKey == null) || siteName.isEmpty() || (type == null)) {
 | 
			
		||||
            sitePasswordField.setText( "" );
 | 
			
		||||
@@ -310,6 +310,10 @@ public class EmergencyActivity extends Activity {
 | 
			
		||||
                        }
 | 
			
		||||
                    } );
 | 
			
		||||
                }
 | 
			
		||||
                catch (final MPInvalidatedException ignored) {
 | 
			
		||||
                    sitePasswordField.setText( "" );
 | 
			
		||||
                    progressView.setVisibility( View.INVISIBLE );
 | 
			
		||||
                }
 | 
			
		||||
                catch (final RuntimeException e) {
 | 
			
		||||
                    sitePasswordField.setText( "" );
 | 
			
		||||
                    progressView.setVisibility( View.INVISIBLE );
 | 
			
		||||
 
 | 
			
		||||
@@ -151,7 +151,7 @@ public final class Preferences {
 | 
			
		||||
        return MPResultType.values()[prefs().getInt( PREF_RESULT_TYPE, MPResultType.DEFAULT.ordinal() )];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean setDefaultVersion(final MasterKey.Version value) {
 | 
			
		||||
    public boolean setDefaultVersion(final MPMasterKey.Version value) {
 | 
			
		||||
        if (getDefaultVersion() == value)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
@@ -160,7 +160,7 @@ public final class Preferences {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    public MasterKey.Version getDefaultVersion() {
 | 
			
		||||
        return MasterKey.Version.values()[prefs().getInt( PREF_ALGORITHM_VERSION, MasterKey.Version.CURRENT.ordinal() )];
 | 
			
		||||
    public MPMasterKey.Version getDefaultVersion() {
 | 
			
		||||
        return MPMasterKey.Version.values()[prefs().getInt( PREF_ALGORITHM_VERSION, MPMasterKey.Version.CURRENT.ordinal() )];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +1 @@
 | 
			
		||||
../../core/java/tests/src/main/resources/mpw_tests.xml
 | 
			
		||||
../../core/mpw_tests.xml
 | 
			
		||||
@@ -24,7 +24,6 @@ import com.google.common.io.*;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.util.TypeUtils;
 | 
			
		||||
 | 
			
		||||
import com.lyndir.masterpassword.gui.model.User;
 | 
			
		||||
import com.lyndir.masterpassword.gui.view.PasswordFrame;
 | 
			
		||||
import com.lyndir.masterpassword.gui.view.UnlockFrame;
 | 
			
		||||
import java.io.*;
 | 
			
		||||
@@ -46,7 +45,7 @@ public class GUI implements UnlockFrame.SignInCallback {
 | 
			
		||||
    private static final Logger logger = Logger.get( GUI.class );
 | 
			
		||||
 | 
			
		||||
    private final UnlockFrame unlockFrame = new UnlockFrame( this );
 | 
			
		||||
    private PasswordFrame passwordFrame;
 | 
			
		||||
    private PasswordFrame<?, ?> passwordFrame;
 | 
			
		||||
 | 
			
		||||
    public static void main(final String... args) {
 | 
			
		||||
 | 
			
		||||
@@ -104,12 +103,8 @@ public class GUI implements UnlockFrame.SignInCallback {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void signedIn(final User user) {
 | 
			
		||||
        passwordFrame = newPasswordFrame( user );
 | 
			
		||||
    public void signedIn(final PasswordFrame<?, ?> passwordFrame) {
 | 
			
		||||
        this.passwordFrame = passwordFrame;
 | 
			
		||||
        open();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected PasswordFrame newPasswordFrame(final User user) {
 | 
			
		||||
        return new PasswordFrame( user );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,21 +20,22 @@ package com.lyndir.masterpassword.gui.model;
 | 
			
		||||
 | 
			
		||||
import com.google.common.primitives.UnsignedInteger;
 | 
			
		||||
import com.lyndir.masterpassword.MPResultType;
 | 
			
		||||
import com.lyndir.masterpassword.MasterKey;
 | 
			
		||||
import com.lyndir.masterpassword.MPMasterKey;
 | 
			
		||||
import com.lyndir.masterpassword.model.MPSite;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author lhunath, 14-12-16
 | 
			
		||||
 */
 | 
			
		||||
public class IncognitoSite extends Site {
 | 
			
		||||
public class IncognitoSite extends MPSite {
 | 
			
		||||
 | 
			
		||||
    private String            siteName;
 | 
			
		||||
    private UnsignedInteger   siteCounter;
 | 
			
		||||
    private MPResultType      resultType;
 | 
			
		||||
    private MasterKey.Version algorithmVersion;
 | 
			
		||||
    private String              siteName;
 | 
			
		||||
    private UnsignedInteger     siteCounter;
 | 
			
		||||
    private MPResultType        resultType;
 | 
			
		||||
    private MPMasterKey.Version algorithmVersion;
 | 
			
		||||
 | 
			
		||||
    public IncognitoSite(final String siteName, final UnsignedInteger siteCounter, final MPResultType resultType,
 | 
			
		||||
                         final MasterKey.Version algorithmVersion) {
 | 
			
		||||
                         final MPMasterKey.Version algorithmVersion) {
 | 
			
		||||
        this.siteName = siteName;
 | 
			
		||||
        this.siteCounter = siteCounter;
 | 
			
		||||
        this.resultType = resultType;
 | 
			
		||||
@@ -62,12 +63,12 @@ public class IncognitoSite extends Site {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public MasterKey.Version getAlgorithmVersion() {
 | 
			
		||||
    public MPMasterKey.Version getAlgorithmVersion() {
 | 
			
		||||
        return algorithmVersion;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setAlgorithmVersion(final MasterKey.Version algorithmVersion) {
 | 
			
		||||
    public void setAlgorithmVersion(final MPMasterKey.Version algorithmVersion) {
 | 
			
		||||
        this.algorithmVersion = algorithmVersion;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -19,15 +19,17 @@
 | 
			
		||||
package com.lyndir.masterpassword.gui.model;
 | 
			
		||||
 | 
			
		||||
import com.google.common.collect.ImmutableList;
 | 
			
		||||
import com.lyndir.masterpassword.MasterKey;
 | 
			
		||||
import com.lyndir.masterpassword.model.IncorrectMasterPasswordException;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import com.lyndir.masterpassword.MPMasterKey;
 | 
			
		||||
import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException;
 | 
			
		||||
import com.lyndir.masterpassword.model.MPUser;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author lhunath, 2014-06-08
 | 
			
		||||
 */
 | 
			
		||||
public class IncognitoUser extends User {
 | 
			
		||||
public class IncognitoUser extends MPUser<IncognitoSite> {
 | 
			
		||||
 | 
			
		||||
    private final String fullName;
 | 
			
		||||
 | 
			
		||||
@@ -41,21 +43,27 @@ public class IncognitoUser extends User {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void authenticate(final char[] masterPassword)
 | 
			
		||||
            throws IncorrectMasterPasswordException {
 | 
			
		||||
        this.key = new MasterKey( getFullName(), masterPassword );
 | 
			
		||||
    public MPMasterKey.Version getAlgorithmVersion() {
 | 
			
		||||
        return MPMasterKey.Version.CURRENT;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Iterable<Site> findSitesByName(final String siteName) {
 | 
			
		||||
    public void addSite(final IncognitoSite site) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void deleteSite(final IncognitoSite site) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Collection<IncognitoSite> findSites(final String query) {
 | 
			
		||||
        return ImmutableList.of();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public void addSite(final Site site) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void deleteSite(final Site site) {
 | 
			
		||||
    public MPMasterKey authenticate(final char[] masterPassword)
 | 
			
		||||
            throws MPIncorrectMasterPasswordException {
 | 
			
		||||
        return key = new MPMasterKey( getFullName(), masterPassword );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,96 +0,0 @@
 | 
			
		||||
//==============================================================================
 | 
			
		||||
// 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/>.
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
package com.lyndir.masterpassword.gui.model;
 | 
			
		||||
 | 
			
		||||
import com.google.common.primitives.UnsignedInteger;
 | 
			
		||||
import com.lyndir.masterpassword.MPResultType;
 | 
			
		||||
import com.lyndir.masterpassword.MasterKey;
 | 
			
		||||
import com.lyndir.masterpassword.model.*;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author lhunath, 14-12-16
 | 
			
		||||
 */
 | 
			
		||||
public class ModelSite extends Site {
 | 
			
		||||
 | 
			
		||||
    private final MPSite model;
 | 
			
		||||
 | 
			
		||||
    public ModelSite(final MPSiteResult result) {
 | 
			
		||||
        model = result.getSite();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MPSite getModel() {
 | 
			
		||||
        return model;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getSiteName() {
 | 
			
		||||
        return model.getSiteName();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setSiteName(final String siteName) {
 | 
			
		||||
        model.setSiteName( siteName );
 | 
			
		||||
        MPUserFileManager.get().save();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public MPResultType getResultType() {
 | 
			
		||||
        return model.getResultType();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setResultType(final MPResultType resultType) {
 | 
			
		||||
        if (resultType != getResultType()) {
 | 
			
		||||
            model.setResultType( resultType );
 | 
			
		||||
            MPUserFileManager.get().save();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public MasterKey.Version getAlgorithmVersion() {
 | 
			
		||||
        return model.getAlgorithmVersion();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setAlgorithmVersion(final MasterKey.Version algorithmVersion) {
 | 
			
		||||
        if (algorithmVersion != getAlgorithmVersion()) {
 | 
			
		||||
            model.setAlgorithmVersion( algorithmVersion );
 | 
			
		||||
            MPUserFileManager.get().save();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public UnsignedInteger getSiteCounter() {
 | 
			
		||||
        return model.getSiteCounter();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setSiteCounter(final UnsignedInteger siteCounter) {
 | 
			
		||||
        if (siteCounter.equals( getSiteCounter() )) {
 | 
			
		||||
            model.setSiteCounter( siteCounter );
 | 
			
		||||
            MPUserFileManager.get().save();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void use() {
 | 
			
		||||
        model.updateLastUsed();
 | 
			
		||||
        MPUserFileManager.get().save();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,97 +0,0 @@
 | 
			
		||||
//==============================================================================
 | 
			
		||||
// 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/>.
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
package com.lyndir.masterpassword.gui.model;
 | 
			
		||||
 | 
			
		||||
import com.google.common.base.Function;
 | 
			
		||||
import com.google.common.base.Preconditions;
 | 
			
		||||
import com.google.common.collect.FluentIterable;
 | 
			
		||||
import com.lyndir.masterpassword.gui.*;
 | 
			
		||||
import com.lyndir.masterpassword.model.*;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author lhunath, 14-12-08
 | 
			
		||||
 */
 | 
			
		||||
public class ModelUser extends User {
 | 
			
		||||
 | 
			
		||||
    private final MPUser model;
 | 
			
		||||
 | 
			
		||||
    public ModelUser(final MPUser model) {
 | 
			
		||||
        this.model = model;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MPUser getModel() {
 | 
			
		||||
        return model;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getFullName() {
 | 
			
		||||
        return model.getFullName();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getAvatar() {
 | 
			
		||||
        return model.getAvatar();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setAvatar(final int avatar) {
 | 
			
		||||
        model.setAvatar( avatar % Res.avatars());
 | 
			
		||||
        MPUserFileManager.get().save();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void authenticate(final char[] masterPassword)
 | 
			
		||||
            throws IncorrectMasterPasswordException {
 | 
			
		||||
        key = model.authenticate( masterPassword );
 | 
			
		||||
        MPUserFileManager.get().save();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Iterable<Site> findSitesByName(final String siteName) {
 | 
			
		||||
        return FluentIterable.from( model.findSitesByName( siteName ) ).transform( new Function<MPSiteResult, Site>() {
 | 
			
		||||
            @Nullable
 | 
			
		||||
            @Override
 | 
			
		||||
            public Site apply(@Nullable final MPSiteResult site) {
 | 
			
		||||
                return new ModelSite( Preconditions.checkNotNull( site ) );
 | 
			
		||||
            }
 | 
			
		||||
        } );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void addSite(final Site site) {
 | 
			
		||||
        model.addSite( new MPSite( model, site.getSiteName(), site.getSiteCounter(), site.getResultType() ) );
 | 
			
		||||
        model.updateLastUsed();
 | 
			
		||||
        MPUserFileManager.get().save();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void deleteSite(final Site site) {
 | 
			
		||||
        if (site instanceof ModelSite) {
 | 
			
		||||
            model.deleteSite(((ModelSite) site).getModel());
 | 
			
		||||
            MPUserFileManager.get().save();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean keySaved() {
 | 
			
		||||
        // TODO
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,53 +0,0 @@
 | 
			
		||||
//==============================================================================
 | 
			
		||||
// 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/>.
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
package com.lyndir.masterpassword.gui.model;
 | 
			
		||||
 | 
			
		||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
 | 
			
		||||
 | 
			
		||||
import com.google.common.primitives.UnsignedInteger;
 | 
			
		||||
import com.lyndir.masterpassword.MPResultType;
 | 
			
		||||
import com.lyndir.masterpassword.MasterKey;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author lhunath, 14-12-16
 | 
			
		||||
 */
 | 
			
		||||
public abstract class Site {
 | 
			
		||||
 | 
			
		||||
    public abstract String getSiteName();
 | 
			
		||||
 | 
			
		||||
    public abstract void setSiteName(String siteName);
 | 
			
		||||
 | 
			
		||||
    public abstract MPResultType getResultType();
 | 
			
		||||
 | 
			
		||||
    public abstract void setResultType(MPResultType resultType);
 | 
			
		||||
 | 
			
		||||
    public abstract MasterKey.Version getAlgorithmVersion();
 | 
			
		||||
 | 
			
		||||
    public abstract void setAlgorithmVersion(MasterKey.Version algorithmVersion);
 | 
			
		||||
 | 
			
		||||
    public abstract UnsignedInteger getSiteCounter();
 | 
			
		||||
 | 
			
		||||
    public abstract void setSiteCounter(UnsignedInteger siteCounter);
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String toString() {
 | 
			
		||||
        return strf( "{%s: %s}", getClass().getSimpleName(), getSiteName() );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,76 +0,0 @@
 | 
			
		||||
//==============================================================================
 | 
			
		||||
// 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/>.
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
package com.lyndir.masterpassword.gui.model;
 | 
			
		||||
 | 
			
		||||
import com.google.common.base.Preconditions;
 | 
			
		||||
import com.lyndir.masterpassword.MasterKey;
 | 
			
		||||
import com.lyndir.masterpassword.model.IncorrectMasterPasswordException;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author lhunath, 2014-06-08
 | 
			
		||||
 */
 | 
			
		||||
public abstract class User {
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    protected MasterKey key;
 | 
			
		||||
 | 
			
		||||
    public abstract String getFullName();
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("MethodCanBeVariableArityMethod")
 | 
			
		||||
    public abstract void authenticate(char[] masterPassword)
 | 
			
		||||
            throws IncorrectMasterPasswordException;
 | 
			
		||||
 | 
			
		||||
    public int getAvatar() {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean isKeyAvailable() {
 | 
			
		||||
        return key != null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    public MasterKey getKey() {
 | 
			
		||||
        return Preconditions.checkNotNull( key, "User is not authenticated: " + getFullName() );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public abstract Iterable<Site> findSitesByName(String siteName);
 | 
			
		||||
 | 
			
		||||
    public abstract void addSite(Site site);
 | 
			
		||||
 | 
			
		||||
    public abstract void deleteSite(Site site);
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean equals(final Object obj) {
 | 
			
		||||
        return (this == obj) || ((obj instanceof User) && Objects.equals( getFullName(), ((User) obj).getFullName() ));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int hashCode() {
 | 
			
		||||
        return Objects.hashCode( getFullName() );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String toString() {
 | 
			
		||||
        return getFullName();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -21,7 +21,7 @@ package com.lyndir.masterpassword.gui.platform.mac;
 | 
			
		||||
import com.apple.eawt.*;
 | 
			
		||||
import com.lyndir.masterpassword.gui.GUI;
 | 
			
		||||
import com.lyndir.masterpassword.gui.view.PasswordFrame;
 | 
			
		||||
import com.lyndir.masterpassword.gui.model.User;
 | 
			
		||||
import com.lyndir.masterpassword.model.MPUser;
 | 
			
		||||
 | 
			
		||||
import javax.swing.*;
 | 
			
		||||
 | 
			
		||||
@@ -52,12 +52,4 @@ public class AppleGUI extends GUI {
 | 
			
		||||
            }
 | 
			
		||||
        } );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected PasswordFrame newPasswordFrame(final User user) {
 | 
			
		||||
        PasswordFrame frame = super.newPasswordFrame( user );
 | 
			
		||||
        frame.setDefaultCloseOperation( WindowConstants.HIDE_ON_CLOSE );
 | 
			
		||||
 | 
			
		||||
        return frame;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,9 +20,8 @@ package com.lyndir.masterpassword.gui.view;
 | 
			
		||||
 | 
			
		||||
import com.google.common.collect.ImmutableList;
 | 
			
		||||
import com.lyndir.masterpassword.gui.Res;
 | 
			
		||||
import com.lyndir.masterpassword.gui.model.User;
 | 
			
		||||
import com.lyndir.masterpassword.model.MPUser;
 | 
			
		||||
import com.lyndir.masterpassword.gui.util.Components;
 | 
			
		||||
import com.lyndir.masterpassword.gui.view.UnlockFrame;
 | 
			
		||||
import java.awt.*;
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
@@ -32,7 +31,7 @@ import javax.swing.*;
 | 
			
		||||
/**
 | 
			
		||||
 * @author lhunath, 2014-06-11
 | 
			
		||||
 */
 | 
			
		||||
public abstract class AuthenticationPanel extends Components.GradientPanel {
 | 
			
		||||
public abstract class AuthenticationPanel<U extends MPUser<?>> extends Components.GradientPanel {
 | 
			
		||||
 | 
			
		||||
    protected final UnlockFrame unlockFrame;
 | 
			
		||||
    protected final JLabel      avatarLabel;
 | 
			
		||||
@@ -65,11 +64,12 @@ public abstract class AuthenticationPanel extends Components.GradientPanel {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    protected abstract User getSelectedUser();
 | 
			
		||||
    protected abstract U getSelectedUser();
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    public abstract char[] getMasterPassword();
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    public Component getFocusComponent() {
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
@@ -79,4 +79,6 @@ public abstract class AuthenticationPanel extends Components.GradientPanel {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public abstract void reset();
 | 
			
		||||
 | 
			
		||||
    public abstract PasswordFrame<?, ?> newPasswordFrame();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -18,9 +18,12 @@
 | 
			
		||||
 | 
			
		||||
package com.lyndir.masterpassword.gui.view;
 | 
			
		||||
 | 
			
		||||
import com.google.common.primitives.UnsignedInteger;
 | 
			
		||||
import com.lyndir.masterpassword.MPMasterKey;
 | 
			
		||||
import com.lyndir.masterpassword.MPResultType;
 | 
			
		||||
import com.lyndir.masterpassword.gui.Res;
 | 
			
		||||
import com.lyndir.masterpassword.gui.model.IncognitoSite;
 | 
			
		||||
import com.lyndir.masterpassword.gui.model.IncognitoUser;
 | 
			
		||||
import com.lyndir.masterpassword.gui.model.User;
 | 
			
		||||
import com.lyndir.masterpassword.gui.util.Components;
 | 
			
		||||
import java.awt.*;
 | 
			
		||||
import java.awt.event.ActionEvent;
 | 
			
		||||
@@ -34,7 +37,8 @@ import javax.swing.event.DocumentListener;
 | 
			
		||||
/**
 | 
			
		||||
 * @author lhunath, 2014-06-11
 | 
			
		||||
 */
 | 
			
		||||
public class IncognitoAuthenticationPanel extends AuthenticationPanel implements DocumentListener, ActionListener {
 | 
			
		||||
@SuppressWarnings({ "serial", "MagicNumber" })
 | 
			
		||||
public class IncognitoAuthenticationPanel extends AuthenticationPanel<IncognitoUser> implements DocumentListener, ActionListener {
 | 
			
		||||
 | 
			
		||||
    private final JTextField     fullNameField;
 | 
			
		||||
    private final JPasswordField masterPasswordField;
 | 
			
		||||
@@ -76,7 +80,19 @@ public class IncognitoAuthenticationPanel extends AuthenticationPanel implements
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected User getSelectedUser() {
 | 
			
		||||
    public PasswordFrame<IncognitoUser, ?> newPasswordFrame() {
 | 
			
		||||
        return new PasswordFrame<IncognitoUser, IncognitoSite>(getSelectedUser()) {
 | 
			
		||||
            @Override
 | 
			
		||||
            protected IncognitoSite createSite(final IncognitoUser user, final String siteName, final UnsignedInteger siteCounter,
 | 
			
		||||
                                               final MPResultType resultType,
 | 
			
		||||
                                               final MPMasterKey.Version algorithmVersion) {
 | 
			
		||||
                return new IncognitoSite( siteName, siteCounter, resultType, algorithmVersion );
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected IncognitoUser getSelectedUser() {
 | 
			
		||||
        return new IncognitoUser( fullNameField.getText() );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,19 +20,17 @@ package com.lyndir.masterpassword.gui.view;
 | 
			
		||||
 | 
			
		||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
 | 
			
		||||
 | 
			
		||||
import com.google.common.base.Function;
 | 
			
		||||
import com.google.common.base.Preconditions;
 | 
			
		||||
import com.google.common.collect.*;
 | 
			
		||||
import com.google.common.primitives.UnsignedInteger;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
			
		||||
import com.lyndir.masterpassword.MPMasterKey;
 | 
			
		||||
import com.lyndir.masterpassword.MPResultType;
 | 
			
		||||
import com.lyndir.masterpassword.gui.Res;
 | 
			
		||||
import com.lyndir.masterpassword.gui.model.ModelUser;
 | 
			
		||||
import com.lyndir.masterpassword.model.MPUser;
 | 
			
		||||
import com.lyndir.masterpassword.model.MPUserFileManager;
 | 
			
		||||
import com.lyndir.masterpassword.model.*;
 | 
			
		||||
import com.lyndir.masterpassword.gui.util.Components;
 | 
			
		||||
import java.awt.*;
 | 
			
		||||
import java.awt.event.*;
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import javax.swing.*;
 | 
			
		||||
import javax.swing.event.DocumentEvent;
 | 
			
		||||
import javax.swing.event.DocumentListener;
 | 
			
		||||
@@ -47,7 +45,7 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
 | 
			
		||||
    @SuppressWarnings("UnusedDeclaration")
 | 
			
		||||
    private static final Logger logger = Logger.get( ModelAuthenticationPanel.class );
 | 
			
		||||
 | 
			
		||||
    private final JComboBox<ModelUser> userField;
 | 
			
		||||
    private final JComboBox<MPFileUser> userField;
 | 
			
		||||
    private final JLabel               masterPasswordLabel;
 | 
			
		||||
    private final JPasswordField       masterPasswordField;
 | 
			
		||||
 | 
			
		||||
@@ -59,7 +57,7 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
 | 
			
		||||
        avatarLabel.addMouseListener( new MouseAdapter() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void mouseClicked(final MouseEvent e) {
 | 
			
		||||
                ModelUser selectedUser = getSelectedUser();
 | 
			
		||||
                MPFileUser selectedUser = getSelectedUser();
 | 
			
		||||
                if (selectedUser != null) {
 | 
			
		||||
                    selectedUser.setAvatar( selectedUser.getAvatar() + 1 );
 | 
			
		||||
                    updateUser( false );
 | 
			
		||||
@@ -104,10 +102,10 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void updateUser(boolean repack) {
 | 
			
		||||
        ModelUser selectedUser = getSelectedUser();
 | 
			
		||||
        MPFileUser selectedUser = getSelectedUser();
 | 
			
		||||
        if (selectedUser != null) {
 | 
			
		||||
            avatarLabel.setIcon( Res.avatar( selectedUser.getAvatar() ) );
 | 
			
		||||
            boolean showPasswordField = !selectedUser.keySaved();
 | 
			
		||||
            boolean showPasswordField = !selectedUser.isMasterKeyAvailable(); // TODO: is this the same as keySaved()?
 | 
			
		||||
            if (masterPasswordField.isVisible() != showPasswordField) {
 | 
			
		||||
                masterPasswordLabel.setVisible( showPasswordField );
 | 
			
		||||
                masterPasswordField.setVisible( showPasswordField );
 | 
			
		||||
@@ -119,7 +117,7 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected ModelUser getSelectedUser() {
 | 
			
		||||
    protected MPFileUser getSelectedUser() {
 | 
			
		||||
        int selectedIndex = userField.getSelectedIndex();
 | 
			
		||||
        if (selectedIndex < 0)
 | 
			
		||||
            return null;
 | 
			
		||||
@@ -143,7 +141,7 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
 | 
			
		||||
                        String fullName = JOptionPane.showInputDialog( ModelAuthenticationPanel.this, //
 | 
			
		||||
                                                                       "Enter your full name, ensuring it is correctly spelled and capitalized:",
 | 
			
		||||
                                                                       "New User", JOptionPane.QUESTION_MESSAGE );
 | 
			
		||||
                        MPUserFileManager.get().addUser( new MPUser( fullName ) );
 | 
			
		||||
                        MPFileUserManager.get().addUser( new MPFileUser( fullName ) );
 | 
			
		||||
                        userField.setModel( new DefaultComboBoxModel<>( readConfigUsers() ) );
 | 
			
		||||
                        updateUser( true );
 | 
			
		||||
                    }
 | 
			
		||||
@@ -155,7 +153,7 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
 | 
			
		||||
                addActionListener( new ActionListener() {
 | 
			
		||||
                    @Override
 | 
			
		||||
                    public void actionPerformed(final ActionEvent e) {
 | 
			
		||||
                        ModelUser deleteUser = getSelectedUser();
 | 
			
		||||
                        MPFileUser deleteUser = getSelectedUser();
 | 
			
		||||
                        if (deleteUser == null)
 | 
			
		||||
                            return;
 | 
			
		||||
 | 
			
		||||
@@ -165,7 +163,7 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
 | 
			
		||||
                                                       "Delete User", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE ) == JOptionPane.CANCEL_OPTION)
 | 
			
		||||
                            return;
 | 
			
		||||
 | 
			
		||||
                        MPUserFileManager.get().deleteUser( deleteUser.getModel() );
 | 
			
		||||
                        MPFileUserManager.get().deleteUser( deleteUser );
 | 
			
		||||
                        userField.setModel( new DefaultComboBoxModel<>( readConfigUsers() ) );
 | 
			
		||||
                        updateUser( true );
 | 
			
		||||
                    }
 | 
			
		||||
@@ -179,7 +177,7 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
 | 
			
		||||
                    public void actionPerformed(final ActionEvent e) {
 | 
			
		||||
                        JOptionPane.showMessageDialog( ModelAuthenticationPanel.this, //
 | 
			
		||||
                                                       strf( "Reads users and sites from the directory at:\n%s",
 | 
			
		||||
                                                             MPUserFileManager.get().getPath().getAbsolutePath() ), //
 | 
			
		||||
                                                             MPFileUserManager.get().getPath().getAbsolutePath() ), //
 | 
			
		||||
                                                       "Help", JOptionPane.INFORMATION_MESSAGE );
 | 
			
		||||
                    }
 | 
			
		||||
                } );
 | 
			
		||||
@@ -193,14 +191,19 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
 | 
			
		||||
        masterPasswordField.setText( "" );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static ModelUser[] readConfigUsers() {
 | 
			
		||||
        return FluentIterable.from( MPUserFileManager.get().getUsers() ).transform( new Function<MPUser, ModelUser>() {
 | 
			
		||||
            @Nullable
 | 
			
		||||
    @Override
 | 
			
		||||
    public PasswordFrame<MPFileUser, MPFileSite> newPasswordFrame() {
 | 
			
		||||
        return new PasswordFrame<MPFileUser, MPFileSite>(getSelectedUser()) {
 | 
			
		||||
            @Override
 | 
			
		||||
            public ModelUser apply(@Nullable final MPUser model) {
 | 
			
		||||
                return new ModelUser( Preconditions.checkNotNull( model ) );
 | 
			
		||||
            protected MPFileSite createSite(final MPFileUser user, final String siteName, final UnsignedInteger siteCounter, final MPResultType resultType,
 | 
			
		||||
                                            final MPMasterKey.Version algorithmVersion) {
 | 
			
		||||
                return new MPFileSite( user, siteName, siteCounter, resultType, algorithmVersion );
 | 
			
		||||
            }
 | 
			
		||||
        } ).toArray( ModelUser.class );
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static MPFileUser[] readConfigUsers() {
 | 
			
		||||
        return MPFileUserManager.get().getUsers().toArray( new MPFileUser[0] );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 
 | 
			
		||||
@@ -26,11 +26,12 @@ import com.google.common.collect.FluentIterable;
 | 
			
		||||
import com.google.common.collect.Iterables;
 | 
			
		||||
import com.google.common.primitives.UnsignedInteger;
 | 
			
		||||
import com.google.common.util.concurrent.*;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.util.ObjectUtils;
 | 
			
		||||
import com.lyndir.masterpassword.*;
 | 
			
		||||
import com.lyndir.masterpassword.gui.Res;
 | 
			
		||||
import com.lyndir.masterpassword.gui.model.*;
 | 
			
		||||
import com.lyndir.masterpassword.gui.util.Components;
 | 
			
		||||
import com.lyndir.masterpassword.gui.util.UnsignedIntegerModel;
 | 
			
		||||
import com.lyndir.masterpassword.model.*;
 | 
			
		||||
import java.awt.*;
 | 
			
		||||
import java.awt.datatransfer.StringSelection;
 | 
			
		||||
import java.awt.event.*;
 | 
			
		||||
@@ -44,28 +45,28 @@ import javax.swing.event.*;
 | 
			
		||||
/**
 | 
			
		||||
 * @author lhunath, 2014-06-08
 | 
			
		||||
 */
 | 
			
		||||
public class PasswordFrame extends JFrame implements DocumentListener {
 | 
			
		||||
public abstract class PasswordFrame<U extends MPUser<S>, S extends MPSite> extends JFrame implements DocumentListener {
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("FieldCanBeLocal")
 | 
			
		||||
    private final Components.GradientPanel     root;
 | 
			
		||||
    private final JTextField                   siteNameField;
 | 
			
		||||
    private final JButton                      siteActionButton;
 | 
			
		||||
    private final JComboBox<MasterKey.Version> siteVersionField;
 | 
			
		||||
    private final JSpinner                     siteCounterField;
 | 
			
		||||
    private final UnsignedIntegerModel         siteCounterModel;
 | 
			
		||||
    private final JComboBox<MPResultType>      resultTypeField;
 | 
			
		||||
    private final JPasswordField               passwordField;
 | 
			
		||||
    private final JLabel                       tipLabel;
 | 
			
		||||
    private final JCheckBox                    maskPasswordField;
 | 
			
		||||
    private final char                         passwordEchoChar;
 | 
			
		||||
    private final Font                         passwordEchoFont;
 | 
			
		||||
    private final User                         user;
 | 
			
		||||
    private final Components.GradientPanel       root;
 | 
			
		||||
    private final JTextField                     siteNameField;
 | 
			
		||||
    private final JButton                        siteActionButton;
 | 
			
		||||
    private final JComboBox<MPMasterKey.Version> siteVersionField;
 | 
			
		||||
    private final JSpinner                       siteCounterField;
 | 
			
		||||
    private final UnsignedIntegerModel           siteCounterModel;
 | 
			
		||||
    private final JComboBox<MPResultType>        resultTypeField;
 | 
			
		||||
    private final JPasswordField                 passwordField;
 | 
			
		||||
    private final JLabel                         tipLabel;
 | 
			
		||||
    private final JCheckBox                      maskPasswordField;
 | 
			
		||||
    private final char                           passwordEchoChar;
 | 
			
		||||
    private final Font                           passwordEchoFont;
 | 
			
		||||
    private final U                              user;
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    private Site    currentSite;
 | 
			
		||||
    private S       currentSite;
 | 
			
		||||
    private boolean updatingUI;
 | 
			
		||||
 | 
			
		||||
    public PasswordFrame(final User user) {
 | 
			
		||||
    public PasswordFrame(final U user) {
 | 
			
		||||
        super( "Master Password" );
 | 
			
		||||
        this.user = user;
 | 
			
		||||
 | 
			
		||||
@@ -122,7 +123,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
 | 
			
		||||
            public void actionPerformed(final ActionEvent e) {
 | 
			
		||||
                if (currentSite == null)
 | 
			
		||||
                    return;
 | 
			
		||||
                if (currentSite instanceof ModelSite)
 | 
			
		||||
                if (currentSite instanceof MPFileSite)
 | 
			
		||||
                    PasswordFrame.this.user.deleteSite( currentSite );
 | 
			
		||||
                else
 | 
			
		||||
                    PasswordFrame.this.user.addSite( currentSite );
 | 
			
		||||
@@ -140,7 +141,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
 | 
			
		||||
        JComponent siteSettings = Components.boxLayout( BoxLayout.LINE_AXIS,                                                  //
 | 
			
		||||
                                                        resultTypeField = Components.comboBox( types ),                         //
 | 
			
		||||
                                                        Components.stud(),                                                    //
 | 
			
		||||
                                                        siteVersionField = Components.comboBox( MasterKey.Version.values() ), //
 | 
			
		||||
                                                        siteVersionField = Components.comboBox( MPMasterKey.Version.values() ), //
 | 
			
		||||
                                                        Components.stud(),                                                    //
 | 
			
		||||
                                                        siteCounterField = Components.spinner( siteCounterModel ) );
 | 
			
		||||
        sitePanel.add( siteSettings );
 | 
			
		||||
@@ -155,7 +156,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
 | 
			
		||||
 | 
			
		||||
        siteVersionField.setFont( Res.valueFont().deriveFont( 12f ) );
 | 
			
		||||
        siteVersionField.setAlignmentX( RIGHT_ALIGNMENT );
 | 
			
		||||
        siteVersionField.setSelectedItem( MasterKey.Version.CURRENT );
 | 
			
		||||
        siteVersionField.setSelectedItem( MPMasterKey.Version.CURRENT );
 | 
			
		||||
        siteVersionField.addItemListener( new ItemListener() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void itemStateChanged(final ItemEvent e) {
 | 
			
		||||
@@ -226,27 +227,27 @@ public class PasswordFrame extends JFrame implements DocumentListener {
 | 
			
		||||
        final String siteNameQuery = siteNameField.getText();
 | 
			
		||||
        if (updatingUI)
 | 
			
		||||
            return Futures.immediateCancelledFuture();
 | 
			
		||||
        if ((siteNameQuery == null) || siteNameQuery.isEmpty() || !user.isKeyAvailable()) {
 | 
			
		||||
        if ((siteNameQuery == null) || siteNameQuery.isEmpty() || !user.isMasterKeyAvailable()) {
 | 
			
		||||
            siteActionButton.setVisible( false );
 | 
			
		||||
            tipLabel.setText( null );
 | 
			
		||||
            passwordField.setText( null );
 | 
			
		||||
            return Futures.immediateCancelledFuture();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        MPResultType      resultType    = resultTypeField.getModel().getElementAt( resultTypeField.getSelectedIndex() );
 | 
			
		||||
        MasterKey.Version siteVersion = siteVersionField.getItemAt( siteVersionField.getSelectedIndex() );
 | 
			
		||||
        UnsignedInteger   siteCounter = siteCounterModel.getNumber();
 | 
			
		||||
        MPResultType        resultType  = resultTypeField.getModel().getElementAt( resultTypeField.getSelectedIndex() );
 | 
			
		||||
        MPMasterKey.Version siteVersion = siteVersionField.getItemAt( siteVersionField.getSelectedIndex() );
 | 
			
		||||
        UnsignedInteger     siteCounter = siteCounterModel.getNumber();
 | 
			
		||||
 | 
			
		||||
        Iterable<Site> siteResults = user.findSitesByName( siteNameQuery );
 | 
			
		||||
        Iterable<S> siteResults = user.findSites( siteNameQuery );
 | 
			
		||||
        if (!allowNameCompletion)
 | 
			
		||||
            siteResults = FluentIterable.from( siteResults ).filter( new Predicate<Site>() {
 | 
			
		||||
            siteResults = FluentIterable.from( siteResults ).filter( new Predicate<S>() {
 | 
			
		||||
                @Override
 | 
			
		||||
                public boolean apply(@Nullable final Site siteResult) {
 | 
			
		||||
                public boolean apply(@Nullable final S siteResult) {
 | 
			
		||||
                    return (siteResult != null) && siteNameQuery.equals( siteResult.getSiteName() );
 | 
			
		||||
                }
 | 
			
		||||
            } );
 | 
			
		||||
        final Site site = ifNotNullElse( Iterables.getFirst( siteResults, null ),
 | 
			
		||||
                                         new IncognitoSite( siteNameQuery, siteCounter, resultType, siteVersion ) );
 | 
			
		||||
        final S site = ifNotNullElse( Iterables.getFirst( siteResults, null ),
 | 
			
		||||
                                      createSite( user, siteNameQuery, siteCounter, resultType, siteVersion ) );
 | 
			
		||||
        if ((currentSite != null) && currentSite.getSiteName().equals( site.getSiteName() )) {
 | 
			
		||||
            site.setResultType( resultType );
 | 
			
		||||
            site.setAlgorithmVersion( siteVersion );
 | 
			
		||||
@@ -257,8 +258,9 @@ public class PasswordFrame extends JFrame implements DocumentListener {
 | 
			
		||||
            @Override
 | 
			
		||||
            public String call()
 | 
			
		||||
                    throws Exception {
 | 
			
		||||
                return user.getKey()
 | 
			
		||||
                           .siteResult( site.getSiteName(), site.getSiteCounter(), MPKeyPurpose.Authentication, null, site.getResultType(), null, site.getAlgorithmVersion() );
 | 
			
		||||
                return user.getMasterKey()
 | 
			
		||||
                           .siteResult( site.getSiteName(), site.getSiteCounter(), MPKeyPurpose.Authentication, null, site.getResultType(),
 | 
			
		||||
                                        null, site.getAlgorithmVersion() );
 | 
			
		||||
            }
 | 
			
		||||
        } );
 | 
			
		||||
        Futures.addCallback( passwordFuture, new FutureCallback<String>() {
 | 
			
		||||
@@ -269,8 +271,8 @@ public class PasswordFrame extends JFrame implements DocumentListener {
 | 
			
		||||
                    public void run() {
 | 
			
		||||
                        updatingUI = true;
 | 
			
		||||
                        currentSite = site;
 | 
			
		||||
                        siteActionButton.setVisible( user instanceof ModelUser );
 | 
			
		||||
                        if (currentSite instanceof ModelSite)
 | 
			
		||||
                        siteActionButton.setVisible( user instanceof MPFileUser );
 | 
			
		||||
                        if (currentSite instanceof MPFileSite)
 | 
			
		||||
                            siteActionButton.setText( "Delete Site" );
 | 
			
		||||
                        else
 | 
			
		||||
                            siteActionButton.setText( "Add Site" );
 | 
			
		||||
@@ -296,6 +298,9 @@ public class PasswordFrame extends JFrame implements DocumentListener {
 | 
			
		||||
        return passwordFuture;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected abstract S createSite(U user, String siteName, UnsignedInteger siteCounter, MPResultType resultType,
 | 
			
		||||
                                    MPMasterKey.Version algorithmVersion);
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void insertUpdate(final DocumentEvent e) {
 | 
			
		||||
        updatePassword( true );
 | 
			
		||||
 
 | 
			
		||||
@@ -22,9 +22,9 @@ import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
 | 
			
		||||
 | 
			
		||||
import com.lyndir.masterpassword.MPIdenticon;
 | 
			
		||||
import com.lyndir.masterpassword.gui.*;
 | 
			
		||||
import com.lyndir.masterpassword.gui.model.User;
 | 
			
		||||
import com.lyndir.masterpassword.model.MPUser;
 | 
			
		||||
import com.lyndir.masterpassword.gui.util.Components;
 | 
			
		||||
import com.lyndir.masterpassword.model.IncorrectMasterPasswordException;
 | 
			
		||||
import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException;
 | 
			
		||||
import java.awt.*;
 | 
			
		||||
import java.awt.event.*;
 | 
			
		||||
import java.util.concurrent.Future;
 | 
			
		||||
@@ -36,6 +36,7 @@ import javax.swing.*;
 | 
			
		||||
/**
 | 
			
		||||
 * @author lhunath, 2014-06-08
 | 
			
		||||
 */
 | 
			
		||||
@SuppressWarnings({ "MagicNumber", "serial" })
 | 
			
		||||
public class UnlockFrame extends JFrame {
 | 
			
		||||
 | 
			
		||||
    private final SignInCallback           signInCallback;
 | 
			
		||||
@@ -43,10 +44,10 @@ public class UnlockFrame extends JFrame {
 | 
			
		||||
    private final JLabel                   identiconLabel;
 | 
			
		||||
    private final JButton                  signInButton;
 | 
			
		||||
    private final JPanel                   authenticationContainer;
 | 
			
		||||
    private       AuthenticationPanel      authenticationPanel;
 | 
			
		||||
    private       AuthenticationPanel<?>      authenticationPanel;
 | 
			
		||||
    private       Future<?>                identiconFuture;
 | 
			
		||||
    private       boolean                  incognito;
 | 
			
		||||
    private       User                     user;
 | 
			
		||||
    private       MPUser<?>                   user;
 | 
			
		||||
 | 
			
		||||
    public UnlockFrame(final SignInCallback signInCallback) {
 | 
			
		||||
        super( "Unlock Master Password" );
 | 
			
		||||
@@ -155,7 +156,7 @@ public class UnlockFrame extends JFrame {
 | 
			
		||||
        } );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void updateUser(@Nullable final User user) {
 | 
			
		||||
    void updateUser(@Nullable final MPUser<?> user) {
 | 
			
		||||
        this.user = user;
 | 
			
		||||
        checkSignIn();
 | 
			
		||||
    }
 | 
			
		||||
@@ -213,12 +214,12 @@ public class UnlockFrame extends JFrame {
 | 
			
		||||
                    SwingUtilities.invokeLater( new Runnable() {
 | 
			
		||||
                        @Override
 | 
			
		||||
                        public void run() {
 | 
			
		||||
                            signInCallback.signedIn( user );
 | 
			
		||||
                            signInCallback.signedIn( authenticationPanel.newPasswordFrame() );
 | 
			
		||||
                            dispose();
 | 
			
		||||
                        }
 | 
			
		||||
                    } );
 | 
			
		||||
                }
 | 
			
		||||
                catch (final IncorrectMasterPasswordException e) {
 | 
			
		||||
                catch (final MPIncorrectMasterPasswordException e) {
 | 
			
		||||
                    SwingUtilities.invokeLater( new Runnable() {
 | 
			
		||||
                        @Override
 | 
			
		||||
                        public void run() {
 | 
			
		||||
@@ -237,6 +238,6 @@ public class UnlockFrame extends JFrame {
 | 
			
		||||
 | 
			
		||||
    public interface SignInCallback {
 | 
			
		||||
 | 
			
		||||
        void signedIn(User user);
 | 
			
		||||
        void signedIn(PasswordFrame<?, ?> passwordFrame);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user