2
0

Initial Java JSON serialization/deserialization.

This commit is contained in:
Maarten Billemont 2018-05-08 22:40:48 -04:00
parent 1cb720da32
commit f0d523fb35
26 changed files with 330 additions and 287 deletions

View File

@ -38,6 +38,7 @@ char *mpw_get_token(const char **in, const char *eol, char *delim) {
time_t mpw_mktime( time_t mpw_mktime(
const char *time) { const char *time) {
// TODO: Support parsing timezone into tm_gmtoff
struct tm tm = { .tm_isdst = -1 }; struct tm tm = { .tm_isdst = -1 };
if (time && sscanf( time, "%4d-%2d-%2dT%2d:%2d:%2dZ", if (time && sscanf( time, "%4d-%2d-%2dT%2d:%2d:%2dZ",
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_year, &tm.tm_mon, &tm.tm_mday,

View File

@ -59,7 +59,12 @@ public abstract class MPAlgorithm {
/** /**
* mpw: defaults: password result type. * mpw: defaults: password result type.
*/ */
public abstract MPResultType mpw_default_type(); public abstract MPResultType mpw_default_password_type();
/**
* mpw: defaults: login result type.
*/
public abstract MPResultType mpw_default_login_type();
/** /**
* mpw: defaults: initial counter value. * mpw: defaults: initial counter value.

View File

@ -83,8 +83,8 @@ public class MPAlgorithmV0 extends MPAlgorithm {
} }
@Override @Override
public byte[] siteKey(final byte[] masterKey, final String siteName, UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose, public byte[] siteKey(final byte[] masterKey, final String siteName, UnsignedInteger siteCounter,
@Nullable final String keyContext) { final MPKeyPurpose keyPurpose, @Nullable final String keyContext) {
String keyScope = keyPurpose.getScope(); String keyScope = keyPurpose.getScope();
logger.trc( "keyScope: %s", keyScope ); logger.trc( "keyScope: %s", keyScope );
@ -117,8 +117,8 @@ public class MPAlgorithmV0 extends MPAlgorithm {
@Override @Override
public String siteResult(final byte[] masterKey, final byte[] siteKey, final String siteName, final UnsignedInteger siteCounter, public String siteResult(final byte[] masterKey, final byte[] siteKey, final String siteName, final UnsignedInteger siteCounter,
final MPKeyPurpose keyPurpose, final MPKeyPurpose keyPurpose, @Nullable final String keyContext,
@Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam) { final MPResultType resultType, @Nullable final String resultParam) {
switch (resultType.getTypeClass()) { switch (resultType.getTypeClass()) {
case Template: case Template:
@ -133,8 +133,8 @@ public class MPAlgorithmV0 extends MPAlgorithm {
} }
@Override @Override
public String sitePasswordFromTemplate(final byte[] masterKey, final byte[] siteKey, final MPResultType resultType, public String sitePasswordFromTemplate(final byte[] masterKey, final byte[] siteKey,
@Nullable final String resultParam) { final MPResultType resultType, @Nullable final String resultParam) {
int[] _siteKey = new int[siteKey.length]; int[] _siteKey = new int[siteKey.length];
for (int i = 0; i < siteKey.length; ++i) { for (int i = 0; i < siteKey.length; ++i) {
@ -168,8 +168,8 @@ public class MPAlgorithmV0 extends MPAlgorithm {
} }
@Override @Override
public String sitePasswordFromCrypt(final byte[] masterKey, final byte[] siteKey, final MPResultType resultType, public String sitePasswordFromCrypt(final byte[] masterKey, final byte[] siteKey,
@Nullable final String resultParam) { final MPResultType resultType, @Nullable final String resultParam) {
Preconditions.checkNotNull( resultParam ); Preconditions.checkNotNull( resultParam );
Preconditions.checkArgument( !resultParam.isEmpty() ); Preconditions.checkArgument( !resultParam.isEmpty() );
@ -192,8 +192,8 @@ public class MPAlgorithmV0 extends MPAlgorithm {
} }
@Override @Override
public String sitePasswordFromDerive(final byte[] masterKey, final byte[] siteKey, final MPResultType resultType, public String sitePasswordFromDerive(final byte[] masterKey, final byte[] siteKey,
@Nullable final String resultParam) { final MPResultType resultType, @Nullable final String resultParam) {
if (resultType == MPResultType.DeriveKey) { if (resultType == MPResultType.DeriveKey) {
int resultParamInt = ConversionUtils.toIntegerNN( resultParam ); int resultParamInt = ConversionUtils.toIntegerNN( resultParam );
@ -220,8 +220,8 @@ public class MPAlgorithmV0 extends MPAlgorithm {
@Override @Override
public String siteState(final byte[] masterKey, final byte[] siteKey, final String siteName, final UnsignedInteger siteCounter, public String siteState(final byte[] masterKey, final byte[] siteKey, final String siteName, final UnsignedInteger siteCounter,
final MPKeyPurpose keyPurpose, final MPKeyPurpose keyPurpose, @Nullable final String keyContext,
@Nullable final String keyContext, final MPResultType resultType, final String resultParam) { final MPResultType resultType, final String resultParam) {
try { try {
// Encrypt // Encrypt
@ -246,111 +246,77 @@ public class MPAlgorithmV0 extends MPAlgorithm {
return MPMasterKey.Version.V0; return MPMasterKey.Version.V0;
} }
/**
* mpw: defaults: password result type.
*/
@Override @Override
public MPResultType mpw_default_type() { public MPResultType mpw_default_password_type() {
return MPResultType.GeneratedLong; return MPResultType.GeneratedLong;
} }
/** @Override
* mpw: defaults: initial counter value. public MPResultType mpw_default_login_type() {
*/ return MPResultType.GeneratedName;
}
@Override @Override
public UnsignedInteger mpw_default_counter() { public UnsignedInteger mpw_default_counter() {
return UnsignedInteger.ONE; return UnsignedInteger.ONE;
} }
/**
* mpw: validity for the time-based rolling counter.
*/
@Override @Override
@SuppressWarnings("MagicNumber") @SuppressWarnings("MagicNumber")
public long mpw_otp_window() { public long mpw_otp_window() {
return 5 * 60 /* s */; return 5 * 60 /* s */;
} }
/**
* mpw: Key ID hash.
*/
@Override @Override
public MessageDigests mpw_hash() { public MessageDigests mpw_hash() {
return MessageDigests.SHA256; return MessageDigests.SHA256;
} }
/**
* mpw: Site digest.
*/
@Override @Override
public MessageAuthenticationDigests mpw_digest() { public MessageAuthenticationDigests mpw_digest() {
return MessageAuthenticationDigests.HmacSHA256; return MessageAuthenticationDigests.HmacSHA256;
} }
/**
* mpw: Platform-agnostic byte order.
*/
@Override @Override
public ByteOrder mpw_byteOrder() { public ByteOrder mpw_byteOrder() {
return ByteOrder.BIG_ENDIAN; return ByteOrder.BIG_ENDIAN;
} }
/**
* mpw: Input character encoding.
*/
@Override @Override
public Charset mpw_charset() { public Charset mpw_charset() {
return Charsets.UTF_8; return Charsets.UTF_8;
} }
/**
* mpw: Master key size (byte).
*/
@Override @Override
@SuppressWarnings("MagicNumber") @SuppressWarnings("MagicNumber")
public int mpw_dkLen() { public int mpw_dkLen() {
return 64; return 64;
} }
/**
* mpw: Minimum size for derived keys (bit).
*/
@Override @Override
@SuppressWarnings("MagicNumber") @SuppressWarnings("MagicNumber")
public int mpw_keySize_min() { public int mpw_keySize_min() {
return 128; return 128;
} }
/**
* mpw: Maximum size for derived keys (bit).
*/
@Override @Override
@SuppressWarnings("MagicNumber") @SuppressWarnings("MagicNumber")
public int mpw_keySize_max() { public int mpw_keySize_max() {
return 512; return 512;
} }
/**
* scrypt: Parallelization parameter.
*/
@Override @Override
@SuppressWarnings("MagicNumber") @SuppressWarnings("MagicNumber")
public int scrypt_p() { public int scrypt_p() {
return 2; return 2;
} }
/**
* scrypt: Memory cost parameter.
*/
@Override @Override
@SuppressWarnings("MagicNumber") @SuppressWarnings("MagicNumber")
public int scrypt_r() { public int scrypt_r() {
return 8; return 8;
} }
/**
* scrypt: CPU cost parameter.
*/
@Override @Override
@SuppressWarnings("MagicNumber") @SuppressWarnings("MagicNumber")
public int scrypt_N() { public int scrypt_N() {

View File

@ -42,5 +42,5 @@ public final class MPConstant {
public static final int MS_PER_S = 1000; public static final int MS_PER_S = 1000;
public static final DateTimeFormatter dateTimeFormatter = ISODateTimeFormat.dateTimeNoMillis(); public static final DateTimeFormatter dateTimeFormatter = ISODateTimeFormat.dateTimeNoMillis().withZoneUTC();
} }

View File

@ -1,25 +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;
/**
* @author lhunath, 2017-09-21
*/
public class MPInvalidatedException extends Exception {
}

View File

@ -56,14 +56,14 @@ public class MPMasterKey {
/** /**
* Derive the master key for a user based on their name and master password. * Derive the master key for a user based on their name and master password.
* *
* @throws MPInvalidatedException {@link #invalidate()} has been called on this object. * @throws MPKeyUnavailableException {@link #invalidate()} has been called on this object.
*/ */
private byte[] masterKey(final MPAlgorithm algorithm) private byte[] masterKey(final MPAlgorithm algorithm)
throws MPInvalidatedException { throws MPKeyUnavailableException {
Preconditions.checkArgument( masterPassword.length > 0 ); Preconditions.checkArgument( masterPassword.length > 0 );
if (invalidated) if (invalidated)
throw new MPInvalidatedException(); throw new MPKeyUnavailableException();
byte[] key = keyByVersion.get( algorithm.version() ); byte[] key = keyByVersion.get( algorithm.version() );
if (key == null) { if (key == null) {
@ -81,11 +81,11 @@ public class MPMasterKey {
/** /**
* Derive the master key for a user based on their name and master password. * Derive the master key for a user based on their name and master password.
* *
* @throws MPInvalidatedException {@link #invalidate()} has been called on this object. * @throws MPKeyUnavailableException {@link #invalidate()} has been called on this object.
*/ */
private byte[] siteKey(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose, private byte[] siteKey(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
@Nullable final String keyContext, final MPAlgorithm algorithm) @Nullable final String keyContext, final MPAlgorithm algorithm)
throws MPInvalidatedException { throws MPKeyUnavailableException {
Preconditions.checkArgument( !siteName.isEmpty() ); Preconditions.checkArgument( !siteName.isEmpty() );
byte[] masterKey = masterKey( algorithm ); byte[] masterKey = masterKey( algorithm );
@ -110,12 +110,12 @@ public class MPMasterKey {
* @param resultParam A parameter for the resultType. For stateful result types, the output of * @param resultParam A parameter for the resultType. For stateful result types, the output of
* {@link #siteState(String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String, MPAlgorithm)}. * {@link #siteState(String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String, MPAlgorithm)}.
* *
* @throws MPInvalidatedException {@link #invalidate()} has been called on this object. * @throws MPKeyUnavailableException {@link #invalidate()} has been called on this object.
*/ */
public String siteResult(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose, public String siteResult(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
@Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam, @Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam,
final MPAlgorithm algorithm) final MPAlgorithm algorithm)
throws MPInvalidatedException { throws MPKeyUnavailableException {
byte[] masterKey = masterKey( algorithm ); byte[] masterKey = masterKey( algorithm );
byte[] siteKey = siteKey( siteName, siteCounter, keyPurpose, keyContext, algorithm ); byte[] siteKey = siteKey( siteName, siteCounter, keyPurpose, keyContext, algorithm );
@ -139,12 +139,12 @@ public class MPMasterKey {
* @param resultParam The result token desired from * @param resultParam The result token desired from
* {@link #siteResult(String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String, MPAlgorithm)}. * {@link #siteResult(String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String, MPAlgorithm)}.
* *
* @throws MPInvalidatedException {@link #invalidate()} has been called on this object. * @throws MPKeyUnavailableException {@link #invalidate()} has been called on this object.
*/ */
public String siteState(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose, public String siteState(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
@Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam, @Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam,
final MPAlgorithm algorithm) final MPAlgorithm algorithm)
throws MPInvalidatedException { throws MPKeyUnavailableException {
Preconditions.checkNotNull( resultParam ); Preconditions.checkNotNull( resultParam );
Preconditions.checkArgument( !resultParam.isEmpty() ); Preconditions.checkArgument( !resultParam.isEmpty() );
@ -169,10 +169,10 @@ public class MPMasterKey {
/** /**
* Calculate an identifier for the master key. * Calculate an identifier for the master key.
* *
* @throws MPInvalidatedException {@link #invalidate()} has been called on this object. * @throws MPKeyUnavailableException {@link #invalidate()} has been called on this object.
*/ */
public byte[] getKeyID(final MPAlgorithm algorithm) public byte[] getKeyID(final MPAlgorithm algorithm)
throws MPInvalidatedException { throws MPKeyUnavailableException {
return algorithm.toID( masterKey( algorithm ) ); return algorithm.toID( masterKey( algorithm ) );
} }

View File

@ -18,10 +18,14 @@
package com.lyndir.masterpassword.model; package com.lyndir.masterpassword.model;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedInteger;
import com.lyndir.masterpassword.*; import com.lyndir.masterpassword.*;
import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.joda.time.Instant; import org.joda.time.Instant;
import org.joda.time.ReadableInstant;
/** /**
@ -29,22 +33,23 @@ import org.joda.time.Instant;
*/ */
public class MPFileSite extends MPSite { public class MPFileSite extends MPSite {
private final MPFileUser user; private final MPFileUser user;
private String siteName;
private String siteName;
@Nullable @Nullable
private String siteContent; private String siteState;
private UnsignedInteger siteCounter; private UnsignedInteger siteCounter;
private MPResultType resultType; private MPResultType resultType;
private MPAlgorithm algorithm; private MPAlgorithm algorithm;
@Nullable @Nullable
private String loginContent; private String loginState;
private MPResultType loginType; private MPResultType loginType;
@Nullable @Nullable
private String url; private String url;
private int uses; private int uses;
private Instant lastUsed; private ReadableInstant lastUsed;
public MPFileSite(final MPFileUser user, final String siteName) { public MPFileSite(final MPFileUser user, final String siteName) {
this( user, siteName, null, null, user.getAlgorithm() ); this( user, siteName, null, null, user.getAlgorithm() );
@ -56,45 +61,43 @@ public class MPFileSite extends MPSite {
null, null, null, 0, new Instant() ); null, null, null, 0, new Instant() );
} }
protected MPFileSite(final MPFileUser user, final String siteName, @Nullable final String siteContent, protected MPFileSite(final MPFileUser user, final String siteName, @Nullable final String siteState,
@Nullable final UnsignedInteger siteCounter, @Nullable final MPResultType resultType, final MPAlgorithm algorithm, @Nullable final UnsignedInteger siteCounter, @Nullable final MPResultType resultType, final MPAlgorithm algorithm,
@Nullable final String loginContent, @Nullable final MPResultType loginType, @Nullable final String loginState, @Nullable final MPResultType loginType,
@Nullable final String url, final int uses, final Instant lastUsed) { @Nullable final String url, final int uses, final ReadableInstant lastUsed) {
this.user = user; this.user = user;
this.siteName = siteName; this.siteName = siteName;
this.siteContent = siteContent; this.siteState = siteState;
this.siteCounter = (siteCounter == null)? user.getAlgorithm().mpw_default_counter(): siteCounter; this.siteCounter = ifNotNullElse( siteCounter, user.getAlgorithm().mpw_default_counter() );
this.resultType = (resultType == null)? user.getAlgorithm().mpw_default_type(): resultType; this.resultType = ifNotNullElse( resultType, user.getAlgorithm().mpw_default_password_type() );
this.algorithm = algorithm; this.algorithm = algorithm;
this.loginContent = loginContent; this.loginState = loginState;
this.loginType = (loginType == null)? MPResultType.GeneratedName: loginType; this.loginType = ifNotNullElse( loginType, getAlgorithm().mpw_default_login_type() );
this.url = url; this.url = url;
this.uses = uses; this.uses = uses;
this.lastUsed = lastUsed; this.lastUsed = lastUsed;
} }
public String resultFor(final MPMasterKey masterKey) public String getResult()
throws MPInvalidatedException { throws MPKeyUnavailableException {
return resultFor( masterKey, MPKeyPurpose.Authentication, null ); return getResult( MPKeyPurpose.Authentication, null );
} }
public String resultFor(final MPMasterKey masterKey, final MPKeyPurpose keyPurpose, @Nullable final String keyContext) public String getResult(final MPKeyPurpose keyPurpose, @Nullable final String keyContext)
throws MPInvalidatedException { throws MPKeyUnavailableException {
return resultFor( masterKey, keyPurpose, keyContext, getSiteContent() ); return getResult( keyPurpose, keyContext, siteState );
} }
public String loginFor(final MPMasterKey masterKey) public String getLogin()
throws MPInvalidatedException { throws MPKeyUnavailableException {
if (loginType == null) return getLogin( loginState );
loginType = MPResultType.GeneratedName;
return loginFor( masterKey, loginType, loginContent );
} }
public MPFileUser getUser() { @Override
public MPUser<?> getUser() {
return user; return user;
} }
@ -109,18 +112,18 @@ public class MPFileSite extends MPSite {
} }
@Nullable @Nullable
public String getSiteContent() { public String getSiteState() {
return siteContent; return siteState;
} }
public void setSitePassword(final MPMasterKey masterKey, final MPResultType resultType, @Nullable final String result) public void setSitePassword(final MPResultType resultType, @Nullable final String result)
throws MPInvalidatedException { throws MPKeyUnavailableException {
this.resultType = resultType; this.resultType = resultType;
if (result == null) if (result == null)
this.siteContent = null; this.siteState = null;
else else
this.siteContent = masterKey.siteState( this.siteState = user.getMasterKey().siteState(
siteName, siteCounter, MPKeyPurpose.Authentication, null, resultType, result, algorithm ); siteName, siteCounter, MPKeyPurpose.Authentication, null, resultType, result, algorithm );
} }
@ -144,6 +147,17 @@ public class MPFileSite extends MPSite {
this.resultType = resultType; this.resultType = resultType;
} }
@Override
public MPResultType getLoginType() {
return loginType;
}
@Override
public void setLoginType(@Nullable final MPResultType loginType) {
this.loginType = ifNotNullElse( loginType, getAlgorithm().mpw_default_login_type() );
}
@Override @Override
public MPAlgorithm getAlgorithm() { public MPAlgorithm getAlgorithm() {
return algorithm; return algorithm;
@ -154,25 +168,17 @@ public class MPFileSite extends MPSite {
this.algorithm = algorithm; this.algorithm = algorithm;
} }
public MPResultType getLoginType() {
return loginType;
}
@Nullable @Nullable
public String getLoginContent() { public String getLoginState() {
return loginContent; return loginState;
} }
public void setLoginName(final MPMasterKey masterKey, @Nullable final MPResultType loginType, @Nullable final String result) public void setLoginName(@Nonnull final MPResultType loginType, @Nonnull final String loginName)
throws MPInvalidatedException { throws MPKeyUnavailableException {
this.loginType = loginType; this.loginType = loginType;
if (this.loginType != null) this.loginState = user.getMasterKey().siteState(
if (result == null) siteName, algorithm.mpw_default_counter(), MPKeyPurpose.Identification, null,
this.loginContent = null; this.loginType, loginName, algorithm );
else
this.loginContent = masterKey.siteState(
siteName, algorithm.mpw_default_counter(), MPKeyPurpose.Identification, null, this.loginType, result,
algorithm );
} }
@Nullable @Nullable
@ -188,7 +194,7 @@ public class MPFileSite extends MPSite {
return uses; return uses;
} }
public Instant getLastUsed() { public ReadableInstant getLastUsed() {
return lastUsed; return lastUsed;
} }

View File

@ -42,9 +42,10 @@ public class MPFileUser extends MPUser<MPFileSite> implements Comparable<MPFileU
private final Collection<MPFileSite> sites = Sets.newHashSet(); private final Collection<MPFileSite> sites = Sets.newHashSet();
@Nullable @Nullable
private byte[] keyID; private byte[] keyID;
private MPAlgorithm algorithm; private MPAlgorithm algorithm;
private MPMarshalFormat format; private MPMarshalFormat format;
private MPMarshaller.ContentMode contentMode;
private int avatar; private int avatar;
private MPResultType defaultType; private MPResultType defaultType;
@ -55,11 +56,13 @@ public class MPFileUser extends MPUser<MPFileSite> implements Comparable<MPFileU
} }
public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPAlgorithm algorithm) { public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPAlgorithm algorithm) {
this( fullName, keyID, algorithm, 0, algorithm.mpw_default_type(), new Instant(), MPMarshalFormat.DEFAULT ); this( fullName, keyID, algorithm, 0, algorithm.mpw_default_password_type(), new Instant(),
MPMarshalFormat.DEFAULT, MPMarshaller.ContentMode.PROTECTED );
} }
public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPAlgorithm algorithm, final int avatar, public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPAlgorithm algorithm, final int avatar,
final MPResultType defaultType, final ReadableInstant lastUsed, final MPMarshalFormat format) { final MPResultType defaultType, final ReadableInstant lastUsed,
final MPMarshalFormat format, final MPMarshaller.ContentMode contentMode) {
this.fullName = fullName; this.fullName = fullName;
this.keyID = (keyID == null)? null: keyID.clone(); this.keyID = (keyID == null)? null: keyID.clone();
this.algorithm = algorithm; this.algorithm = algorithm;
@ -67,6 +70,7 @@ public class MPFileUser extends MPUser<MPFileSite> implements Comparable<MPFileU
this.defaultType = defaultType; this.defaultType = defaultType;
this.lastUsed = lastUsed; this.lastUsed = lastUsed;
this.format = format; this.format = format;
this.contentMode = contentMode;
} }
@Override @Override
@ -74,6 +78,11 @@ public class MPFileUser extends MPUser<MPFileSite> implements Comparable<MPFileU
return fullName; return fullName;
} }
@Nullable
public byte[] getKeyID() {
return (keyID == null)? null: keyID.clone();
}
@Override @Override
public MPAlgorithm getAlgorithm() { public MPAlgorithm getAlgorithm() {
return algorithm; return algorithm;
@ -91,6 +100,14 @@ public class MPFileUser extends MPUser<MPFileSite> implements Comparable<MPFileU
this.format = format; this.format = format;
} }
public MPMarshaller.ContentMode getContentMode() {
return contentMode;
}
public void setContentMode(final MPMarshaller.ContentMode contentMode) {
this.contentMode = contentMode;
}
@Override @Override
public int getAvatar() { public int getAvatar() {
return avatar; return avatar;
@ -164,13 +181,13 @@ public class MPFileUser extends MPUser<MPFileSite> implements Comparable<MPFileU
return key; return key;
} }
catch (final MPInvalidatedException e) { catch (final MPKeyUnavailableException e) {
throw logger.bug( e ); throw logger.bug( e );
} }
} }
void save() void save()
throws MPInvalidatedException { throws MPKeyUnavailableException {
MPFileUserManager.get().save( this, getMasterKey() ); MPFileUserManager.get().save( this, getMasterKey() );
} }

View File

@ -78,7 +78,7 @@ public class MPFileUserManager extends MPUserManager {
for (final MPMarshalFormat format : MPMarshalFormat.values()) for (final MPMarshalFormat format : MPMarshalFormat.values())
if (userFile.getName().endsWith( format.fileSuffix() )) if (userFile.getName().endsWith( format.fileSuffix() ))
try { try {
MPFileUser user = format.unmarshaller().unmarshall( userFile ); MPFileUser user = format.unmarshaller().unmarshall( userFile, null );
MPFileUser previousUser = users.put( user.getFullName(), user ); MPFileUser previousUser = users.put( user.getFullName(), user );
if ((previousUser != null) && (previousUser.getFormat().ordinal() > user.getFormat().ordinal())) if ((previousUser != null) && (previousUser.getFormat().ordinal() > user.getFormat().ordinal()))
users.put( previousUser.getFullName(), previousUser ); users.put( previousUser.getFullName(), previousUser );
@ -86,6 +86,9 @@ public class MPFileUserManager extends MPUserManager {
catch (final IOException | MPMarshalException e) { catch (final IOException | MPMarshalException e) {
logger.err( e, "Couldn't read user from: %s", userFile ); logger.err( e, "Couldn't read user from: %s", userFile );
} }
catch (final MPKeyUnavailableException | MPIncorrectMasterPasswordException e) {
logger.err( e, "Couldn't authenticate user for: %s", userFile );
}
return users.values(); return users.values();
} }
@ -117,7 +120,7 @@ public class MPFileUserManager extends MPUserManager {
* Write the current user state to disk. * Write the current user state to disk.
*/ */
public void save(final MPFileUser user, final MPMasterKey masterKey) public void save(final MPFileUser user, final MPMasterKey masterKey)
throws MPInvalidatedException { throws MPKeyUnavailableException {
try { try {
final MPMarshalFormat format = user.getFormat(); final MPMarshalFormat format = user.getFormat();
new CharSink() { new CharSink() {
@ -126,7 +129,7 @@ public class MPFileUserManager extends MPUserManager {
throws IOException { throws IOException {
return new OutputStreamWriter( new FileOutputStream( getUserFile( user, format ) ), Charsets.UTF_8 ); return new OutputStreamWriter( new FileOutputStream( getUserFile( user, format ) ), Charsets.UTF_8 );
} }
}.write( format.marshaller().marshall( user, masterKey, MPMarshaller.ContentMode.PROTECTED ) ); }.write( format.marshaller().marshall( user ) );
} }
catch (final MPMarshalException | IOException e) { catch (final MPMarshalException | IOException e) {
logger.err( e, "Unable to save sites for user: %s", user ); logger.err( e, "Unable to save sites for user: %s", user );

View File

@ -36,11 +36,11 @@ public class MPFlatMarshaller implements MPMarshaller {
@Nonnull @Nonnull
@Override @Override
public String marshall(final MPFileUser user, final MPMasterKey masterKey, final ContentMode contentMode) public String marshall(final MPFileUser user)
throws MPInvalidatedException, MPMarshalException { throws MPKeyUnavailableException, MPMarshalException {
StringBuilder content = new StringBuilder(); StringBuilder content = new StringBuilder();
content.append( "# Master Password site export\n" ); content.append( "# Master Password site export\n" );
content.append( "# " ).append( contentMode.description() ).append( '\n' ); content.append( "# " ).append( user.getContentMode().description() ).append( '\n' );
content.append( "# \n" ); content.append( "# \n" );
content.append( "##\n" ); content.append( "##\n" );
content.append( "# Format: " ).append( FORMAT ).append( '\n' ); content.append( "# Format: " ).append( FORMAT ).append( '\n' );
@ -51,18 +51,18 @@ public class MPFlatMarshaller implements MPMarshaller {
content.append( "# Key ID: " ).append( user.exportKeyID() ).append( '\n' ); content.append( "# Key ID: " ).append( user.exportKeyID() ).append( '\n' );
content.append( "# Algorithm: " ).append( user.getAlgorithm().version().toInt() ).append( '\n' ); content.append( "# Algorithm: " ).append( user.getAlgorithm().version().toInt() ).append( '\n' );
content.append( "# Default Type: " ).append( user.getDefaultType().getType() ).append( '\n' ); content.append( "# Default Type: " ).append( user.getDefaultType().getType() ).append( '\n' );
content.append( "# Passwords: " ).append( contentMode.name() ).append( '\n' ); content.append( "# Passwords: " ).append( user.getContentMode().name() ).append( '\n' );
content.append( "##\n" ); content.append( "##\n" );
content.append( "#\n" ); content.append( "#\n" );
content.append( "# Last Times Password Login\t Site\tSite\n" ); content.append( "# Last Times Password Login\t Site\tSite\n" );
content.append( "# used used type name\t name\tpassword\n" ); content.append( "# used used type name\t name\tpassword\n" );
for (final MPFileSite site : user.getSites()) { for (final MPFileSite site : user.getSites()) {
String loginName = site.getLoginContent(); String loginName = site.getLoginState();
String password = site.getSiteContent(); String password = site.getSiteState();
if (!contentMode.isRedacted()) { if (!user.getContentMode().isRedacted()) {
loginName = site.loginFor( masterKey ); loginName = site.getLogin();
password = site.resultFor( masterKey ); password = site.getResult();
} }
content.append( strf( "%s %8d %8s %25s\t%25s\t%s\n", // content.append( strf( "%s %8d %8s %25s\t%25s\t%s\n", //

View File

@ -29,6 +29,7 @@ import java.io.*;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.joda.time.DateTime; import org.joda.time.DateTime;
@ -46,17 +47,17 @@ public class MPFlatUnmarshaller implements MPUnmarshaller {
@Nonnull @Nonnull
@Override @Override
public MPFileUser unmarshall(@Nonnull final File file) public MPFileUser unmarshall(@Nonnull final File file, @Nullable final char[] masterPassword)
throws IOException, MPMarshalException { throws IOException, MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException {
try (Reader reader = new InputStreamReader( new FileInputStream( file ), Charsets.UTF_8 )) { try (Reader reader = new InputStreamReader( new FileInputStream( file ), Charsets.UTF_8 )) {
return unmarshall( CharStreams.toString( reader ) ); return unmarshall( CharStreams.toString( reader ), masterPassword );
} }
} }
@Nonnull @Nonnull
@Override @Override
public MPFileUser unmarshall(@Nonnull final String content) public MPFileUser unmarshall(@Nonnull final String content, @Nullable final char[] masterPassword)
throws MPMarshalException { throws MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException {
MPFileUser user = null; MPFileUser user = null;
byte[] keyID = null; byte[] keyID = null;
String fullName = null; String fullName = null;
@ -74,7 +75,8 @@ public class MPFlatUnmarshaller implements MPUnmarshaller {
else else
// Ends the header. // Ends the header.
user = new MPFileUser( fullName, keyID, MPMasterKey.Version.fromInt( mpVersion ).getAlgorithm(), user = new MPFileUser( fullName, keyID, MPMasterKey.Version.fromInt( mpVersion ).getAlgorithm(),
avatar, defaultType, new DateTime( 0 ), MPMarshalFormat.Flat ); avatar, defaultType, new DateTime( 0 ), MPMarshalFormat.Flat,
clearContent? MPMarshaller.ContentMode.VISIBLE : MPMarshaller.ContentMode.PROTECTED );
// Comment. // Comment.
else if (line.startsWith( "#" )) { else if (line.startsWith( "#" )) {
@ -113,25 +115,31 @@ public class MPFlatUnmarshaller implements MPUnmarshaller {
switch (importFormat) { switch (importFormat) {
case 0: case 0:
site = new MPFileSite( user, // site = new MPFileSite( user, //
siteMatcher.group( 5 ), siteMatcher.group( 6 ), siteMatcher.group( 5 ), clearContent? null: siteMatcher.group( 6 ),
user.getAlgorithm().mpw_default_counter(), user.getAlgorithm().mpw_default_counter(),
MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ), MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
MPMasterKey.Version.fromInt( ConversionUtils.toIntegerNN( MPMasterKey.Version.fromInt( ConversionUtils.toIntegerNN(
colon.matcher( siteMatcher.group( 4 ) ).replaceAll( "" ) ) ).getAlgorithm(), colon.matcher( siteMatcher.group( 4 ) ).replaceAll( "" ) ) ).getAlgorithm(),
null, null, null, ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ), null, null, null, ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ),
MPConstant.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() ); MPConstant.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() );
if (clearContent)
site.setSitePassword( site.getResultType(), siteMatcher.group( 6 ) );
break; break;
case 1: case 1:
site = new MPFileSite( user, // site = new MPFileSite( user, //
siteMatcher.group( 7 ), siteMatcher.group( 8 ), siteMatcher.group( 7 ), clearContent? null: siteMatcher.group( 8 ),
UnsignedInteger.valueOf( colon.matcher( siteMatcher.group( 5 ) ).replaceAll( "" ) ), UnsignedInteger.valueOf( colon.matcher( siteMatcher.group( 5 ) ).replaceAll( "" ) ),
MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ), MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
MPMasterKey.Version.fromInt( ConversionUtils.toIntegerNN( MPMasterKey.Version.fromInt( ConversionUtils.toIntegerNN(
colon.matcher( siteMatcher.group( 4 ) ).replaceAll( "" ) ) ).getAlgorithm(), colon.matcher( siteMatcher.group( 4 ) ).replaceAll( "" ) ) ).getAlgorithm(),
siteMatcher.group( 6 ), MPResultType.GeneratedName, null, clearContent? null: siteMatcher.group( 6 ), MPResultType.GeneratedName, null,
ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ), ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ),
MPConstant.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() ); MPConstant.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() );
if (clearContent) {
site.setSitePassword( site.getResultType(), siteMatcher.group( 8 ) );
site.setLoginName( MPResultType.StoredPersonal, siteMatcher.group( 6 ) );
}
break; break;
default: default:

View File

@ -25,8 +25,6 @@ import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.joda.time.Instant; import org.joda.time.Instant;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
/** /**
@ -34,74 +32,57 @@ import org.joda.time.format.ISODateTimeFormat;
*/ */
public class MPJSONFile { public class MPJSONFile {
private static final DateTimeFormatter dateFormatter = ISODateTimeFormat.dateTimeNoMillis(); public MPJSONFile(final MPFileUser user)
throws MPKeyUnavailableException {
Export export;
User user;
public MPJSONFile(final MPFileUser user, final MPMasterKey masterKey, final MPMarshaller.ContentMode contentMode)
throws MPInvalidatedException {
// if (!user.fullName || !strlen( user.fullName )) {
// *error = (MPMarshalError){ MPMarshalErrorMissing, "Missing full name." };
// return false;
// }
// if (!user.masterPassword || !strlen( user.masterPassword )) {
// *error = (MPMarshalError){ MPMarshalErrorMasterPassword, "Missing master password." };
// return false;
// }
// if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, user.algorithm, user.fullName, user.masterPassword )) {
// *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
// return false;
// }
// Section: "export" // Section: "export"
Export fileExport = this.export = new Export(); Export fileExport = this.export = new Export();
fileExport.format = 1; fileExport.format = 1;
fileExport.redacted = contentMode.isRedacted(); fileExport.redacted = user.getContentMode().isRedacted();
fileExport.date = dateFormatter.print( new Instant() ); fileExport.date = MPConstant.dateTimeFormatter.print( new Instant() );
// Section: "user" // Section: "user"
User fileUser = this.user = new User(); User fileUser = this.user = new User();
fileUser.avatar = user.getAvatar(); fileUser.avatar = user.getAvatar();
fileUser.fullName = user.getFullName(); fileUser.full_name = user.getFullName();
fileUser.lastUsed = dateFormatter.print( user.getLastUsed() ); fileUser.last_used = MPConstant.dateTimeFormatter.print( user.getLastUsed() );
fileUser.keyId = CodeUtils.encodeHex( masterKey.getKeyID( user.getAlgorithm() ) ); fileUser.key_id = CodeUtils.encodeHex( user.getKeyID() );
fileUser.algorithm = user.getAlgorithm().version(); fileUser.algorithm = user.getAlgorithm().version();
fileUser.defaultType = user.getDefaultType(); fileUser.default_type = user.getDefaultType();
// Section "sites" // Section "sites"
fileUser.sites = new LinkedHashMap<>(); sites = new LinkedHashMap<>();
for (final MPFileSite site : user.getSites()) { for (final MPFileSite site : user.getSites()) {
Site fileSite; Site fileSite;
String content = null, loginContent = null; String content = null, loginContent = null;
if (!contentMode.isRedacted()) { if (!fileExport.redacted) {
// Clear Text // Clear Text
content = masterKey.siteResult( site.getSiteName(), site.getSiteCounter(), content = site.getResult();
MPKeyPurpose.Authentication, null, site.getResultType(), site.getSiteContent(), loginContent = user.getMasterKey().siteResult(
site.getAlgorithm() ); site.getSiteName(), site.getAlgorithm().mpw_default_counter(),
loginContent = masterKey.siteResult( site.getSiteName(), site.getAlgorithm().mpw_default_counter(), MPKeyPurpose.Identification, null, site.getLoginType(), site.getLoginState(), site.getAlgorithm() );
MPKeyPurpose.Identification, null, site.getLoginType(), site.getLoginContent(),
site.getAlgorithm() );
} else { } else {
// Redacted // Redacted
if (site.getResultType().supportsTypeFeature( MPSiteFeature.ExportContent )) if (site.getResultType().supportsTypeFeature( MPSiteFeature.ExportContent ))
content = site.getSiteContent(); content = site.getSiteState();
if (site.getLoginType().supportsTypeFeature( MPSiteFeature.ExportContent )) if (site.getLoginType().supportsTypeFeature( MPSiteFeature.ExportContent ))
loginContent = site.getLoginContent(); loginContent = site.getLoginState();
} }
fileUser.sites.put( site.getSiteName(), fileSite = new Site() ); sites.put( site.getSiteName(), fileSite = new Site() );
fileSite.type = site.getResultType(); fileSite.type = site.getResultType();
fileSite.counter = site.getSiteCounter(); fileSite.counter = site.getSiteCounter().longValue();
fileSite.algorithm = site.getAlgorithm().version(); fileSite.algorithm = site.getAlgorithm().version();
fileSite.password = content; fileSite.password = content;
fileSite.login_name = loginContent; fileSite.login_name = loginContent;
fileSite.loginType = site.getLoginType(); fileSite.login_type = site.getLoginType();
fileSite.uses = site.getUses(); fileSite.uses = site.getUses();
fileSite.lastUsed = dateFormatter.print( site.getLastUsed() ); fileSite.last_used = MPConstant.dateTimeFormatter.print( site.getLastUsed() );
fileSite._ext_mpw = new Site.Ext();
fileSite._ext_mpw.url = site.getUrl();
fileSite.questions = new LinkedHashMap<>(); fileSite.questions = new LinkedHashMap<>();
// for (size_t q = 0; q < site.questions_count; ++q) { // for (size_t q = 0; q < site.questions_count; ++q) {
@ -133,10 +114,44 @@ public class MPJSONFile {
} }
} }
public MPFileUser toUser() { public MPFileUser toUser(@Nullable final char[] masterPassword)
return new MPFileUser( user.fullName, CodeUtils.decodeHex( user.keyId ), user.algorithm.getAlgorithm(), user.avatar, user.defaultType, dateFormatter.parseDateTime( user.lastUsed ), MPMarshalFormat.JSON ); throws MPIncorrectMasterPasswordException, MPKeyUnavailableException {
MPFileUser user = new MPFileUser(
this.user.full_name, CodeUtils.decodeHex( this.user.key_id ), this.user.algorithm.getAlgorithm(),
this.user.avatar, this.user.default_type, MPConstant.dateTimeFormatter.parseDateTime( this.user.last_used ),
MPMarshalFormat.JSON, export.redacted? MPMarshaller.ContentMode.PROTECTED: MPMarshaller.ContentMode.VISIBLE );
if (masterPassword != null)
user.authenticate( masterPassword );
for (final Map.Entry<String, Site> siteEntry : sites.entrySet()) {
String siteName = siteEntry.getKey();
Site fileSite = siteEntry.getValue();
MPFileSite site = new MPFileSite(
user, siteName, export.redacted? fileSite.password: null, UnsignedInteger.valueOf( fileSite.counter ),
fileSite.type, fileSite.algorithm.getAlgorithm(),
export.redacted? fileSite.login_name: null, fileSite.login_type,
fileSite._ext_mpw.url, fileSite.uses, MPConstant.dateTimeFormatter.parseDateTime( fileSite.last_used ) );
if (!export.redacted) {
if (fileSite.password != null)
site.setSitePassword( fileSite.type, fileSite.password );
if (fileSite.login_name != null)
site.setLoginName( fileSite.login_type, fileSite.login_name );
}
user.addSite( site );
}
return user;
} }
// -- Data
Export export;
User user;
Map<String, Site> sites;
public static class Export { public static class Export {
int format; int format;
@ -147,47 +162,44 @@ public class MPJSONFile {
public static class User { public static class User {
String fullName; int avatar;
String full_name;
String last_used;
String key_id;
MPMasterKey.Version algorithm; MPMasterKey.Version algorithm;
boolean redacted; MPResultType default_type;
int avatar;
MPResultType defaultType;
String lastUsed;
String keyId;
Map<String, Site> sites;
} }
public static class Site { public static class Site {
MPResultType type;
long counter;
MPMasterKey.Version algorithm;
@Nullable @Nullable
String password; String password;
@Nullable @Nullable
String login_name; String login_name;
String name; MPResultType login_type;
String content; int uses;
MPResultType type; String last_used;
UnsignedInteger counter;
MPMasterKey.Version algorithm;
String loginContent;
MPResultType loginType;
String url;
int uses;
String lastUsed;
Map<String, Question> questions; Map<String, Question> questions;
}
Ext _ext_mpw;
public static class Question { public static class Ext {
String keyword; @Nullable
String content; String url;
MPResultType type; }
public static class Question {
MPResultType type;
String answer;
}
} }
} }

View File

@ -31,14 +31,14 @@ public class MPJSONMarshaller implements MPMarshaller {
private final Gson gson = new GsonBuilder() private final Gson gson = new GsonBuilder()
.registerTypeAdapter( MPMasterKey.Version.class, new EnumOrdinalAdapter() ) .registerTypeAdapter( MPMasterKey.Version.class, new EnumOrdinalAdapter() )
.registerTypeAdapter( MPResultType.class, new MPResultTypeAdapter() ) .registerTypeAdapter( MPResultType.class, new MPResultTypeAdapter() )
.setFieldNamingStrategy( FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES ) .setFieldNamingStrategy( FieldNamingPolicy.IDENTITY )
.setPrettyPrinting().create(); .setPrettyPrinting().create();
@Nonnull @Nonnull
@Override @Override
public String marshall(final MPFileUser user, final MPMasterKey masterKey, final ContentMode contentMode) public String marshall(final MPFileUser user)
throws MPInvalidatedException, MPMarshalException { throws MPKeyUnavailableException, MPMarshalException {
return gson.toJson( new MPJSONFile( user, masterKey, contentMode ) ); return gson.toJson( new MPJSONFile( user ) );
} }
} }

View File

@ -19,11 +19,11 @@
package com.lyndir.masterpassword.model; package com.lyndir.masterpassword.model;
import com.google.gson.*; import com.google.gson.*;
import com.lyndir.masterpassword.MPMasterKey; import com.lyndir.masterpassword.*;
import com.lyndir.masterpassword.MPResultType;
import java.io.*; import java.io.*;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/** /**
@ -39,19 +39,29 @@ public class MPJSONUnmarshaller implements MPUnmarshaller {
@Nonnull @Nonnull
@Override @Override
public MPFileUser unmarshall(@Nonnull final File file) public MPFileUser unmarshall(@Nonnull final File file, @Nullable final char[] masterPassword)
throws IOException, MPMarshalException { throws IOException, MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException {
try (Reader reader = new InputStreamReader( new FileInputStream( file ), StandardCharsets.UTF_8 )) { try (Reader reader = new InputStreamReader( new FileInputStream( file ), StandardCharsets.UTF_8 )) {
return gson.fromJson( reader, MPJSONFile.class ).toUser(); try {
return gson.fromJson( reader, MPJSONFile.class ).toUser( masterPassword );
}
catch (final JsonSyntaxException e) {
throw new MPMarshalException( "Couldn't parse JSON in: " + file, e );
}
} }
} }
@Nonnull @Nonnull
@Override @Override
public MPFileUser unmarshall(@Nonnull final String content) public MPFileUser unmarshall(@Nonnull final String content, @Nullable final char[] masterPassword)
throws MPMarshalException { throws MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException {
return gson.fromJson( content, MPJSONFile.class ).toUser(); try {
return gson.fromJson( content, MPJSONFile.class ).toUser( masterPassword );
}
catch (final JsonSyntaxException e) {
throw new MPMarshalException( "Couldn't parse JSON", e );
}
} }
} }

View File

@ -18,8 +18,7 @@
package com.lyndir.masterpassword.model; package com.lyndir.masterpassword.model;
import com.lyndir.masterpassword.MPInvalidatedException; import com.lyndir.masterpassword.MPKeyUnavailableException;
import com.lyndir.masterpassword.MPMasterKey;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -29,8 +28,8 @@ import javax.annotation.Nonnull;
public interface MPMarshaller { public interface MPMarshaller {
@Nonnull @Nonnull
String marshall(MPFileUser user, MPMasterKey masterKey, ContentMode contentMode) String marshall(MPFileUser user)
throws MPInvalidatedException, MPMarshalException; throws MPKeyUnavailableException, MPMarshalException;
enum ContentMode { enum ContentMode {
PROTECTED( "Export of site names and stored passwords (unless device-private) encrypted with the master key.", true ), PROTECTED( "Export of site names and stored passwords (unless device-private) encrypted with the master key.", true ),

View File

@ -31,6 +31,8 @@ import javax.annotation.Nullable;
*/ */
public abstract class MPSite { public abstract class MPSite {
public abstract MPUser<?> getUser();
public abstract String getSiteName(); public abstract String getSiteName();
public abstract void setSiteName(String siteName); public abstract void setSiteName(String siteName);
@ -43,24 +45,28 @@ public abstract class MPSite {
public abstract void setResultType(MPResultType resultType); public abstract void setResultType(MPResultType resultType);
public abstract MPResultType getLoginType();
public abstract void setLoginType(@Nullable MPResultType loginType);
public abstract MPAlgorithm getAlgorithm(); public abstract MPAlgorithm getAlgorithm();
public abstract void setAlgorithm(MPAlgorithm algorithm); public abstract void setAlgorithm(MPAlgorithm algorithm);
public String resultFor(final MPMasterKey masterKey, final MPKeyPurpose keyPurpose, @Nullable final String keyContext, public String getResult(final MPKeyPurpose keyPurpose, @Nullable final String keyContext,
@Nullable final String siteContent) @Nullable final String siteContent)
throws MPInvalidatedException { throws MPKeyUnavailableException {
return masterKey.siteResult( return getUser().getMasterKey().siteResult(
getSiteName(), getSiteCounter(), keyPurpose, keyContext, getResultType(), siteContent, getAlgorithm() ); getSiteName(), getSiteCounter(), keyPurpose, keyContext, getResultType(), siteContent, getAlgorithm() );
} }
public String loginFor(final MPMasterKey masterKey, final MPResultType loginType, @Nullable final String loginContent) public String getLogin(@Nullable final String loginContent)
throws MPInvalidatedException { throws MPKeyUnavailableException {
return masterKey.siteResult( return getUser().getMasterKey().siteResult(
getSiteName(), getAlgorithm().mpw_default_counter(), MPKeyPurpose.Identification, null, loginType, loginContent, getSiteName(), getAlgorithm().mpw_default_counter(), MPKeyPurpose.Identification, null,
getAlgorithm() ); getLoginType(), loginContent, getAlgorithm() );
} }
@Override @Override

View File

@ -18,9 +18,11 @@
package com.lyndir.masterpassword.model; package com.lyndir.masterpassword.model;
import com.lyndir.masterpassword.MPKeyUnavailableException;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/** /**
@ -29,10 +31,10 @@ import javax.annotation.Nonnull;
public interface MPUnmarshaller { public interface MPUnmarshaller {
@Nonnull @Nonnull
MPFileUser unmarshall(@Nonnull File file) MPFileUser unmarshall(@Nonnull File file, @Nullable char[] masterPassword)
throws IOException, MPMarshalException; throws IOException, MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException;
@Nonnull @Nonnull
MPFileUser unmarshall(@Nonnull String content) MPFileUser unmarshall(@Nonnull String content, @Nullable char[] masterPassword)
throws MPMarshalException; throws MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException;
} }

View File

@ -20,7 +20,6 @@ package com.lyndir.masterpassword.model;
import static com.lyndir.lhunath.opal.system.util.StringUtils.*; import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
import com.google.common.base.Preconditions;
import com.lyndir.lhunath.opal.system.CodeUtils; import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.masterpassword.*; import com.lyndir.masterpassword.*;
import java.util.Collection; import java.util.Collection;
@ -44,12 +43,16 @@ public abstract class MPUser<S extends MPSite> {
} }
@Nonnull @Nonnull
public MPMasterKey getMasterKey() { public MPMasterKey getMasterKey()
return Preconditions.checkNotNull( key, "User is not authenticated: %s", getFullName() ); throws MPKeyUnavailableException {
if (key == null)
throw new MPKeyUnavailableException();
return key;
} }
public String exportKeyID() public String exportKeyID()
throws MPInvalidatedException { throws MPKeyUnavailableException {
return CodeUtils.encodeHex( getMasterKey().getKeyID( getAlgorithm() ) ); return CodeUtils.encodeHex( getMasterKey().getKeyID( getAlgorithm() ) );
} }

View File

@ -64,7 +64,7 @@ public class MPMasterKeyTest {
masterKey.getKeyID( testCase.getAlgorithm() ); masterKey.getKeyID( testCase.getAlgorithm() );
fail( "[testMasterKey] invalidate ineffective: " + testCase ); fail( "[testMasterKey] invalidate ineffective: " + testCase );
} }
catch (final MPInvalidatedException ignored) { catch (final MPKeyUnavailableException ignored) {
} }
assertNotEquals( assertNotEquals(
masterPassword, masterPassword,

View File

@ -18,8 +18,11 @@
package com.lyndir.masterpassword; package com.lyndir.masterpassword;
import com.lyndir.masterpassword.model.MPJSONUnmarshaller; import com.google.common.base.Charsets;
import java.io.File; import com.google.common.io.CharStreams;
import com.lyndir.masterpassword.model.*;
import java.io.*;
import org.testng.Assert;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -31,7 +34,12 @@ public class MPModelTest {
@Test @Test
public void testMasterKey() public void testMasterKey()
throws Exception { throws Exception {
System.err.println( new MPJSONUnmarshaller().unmarshall( File file = new File( "/Users/lhunath/.mpw.d/Maarten Billemont.mpsites.json" );
new File( "/Users/lhunath/.mpw.d/Maarten Billemont.mpsites.json" ) ) ); String orig = CharStreams.toString( new InputStreamReader( new FileInputStream( file ), Charsets.UTF_8 ) );
System.out.println(orig);
MPFileUser user = new MPJSONUnmarshaller().unmarshall( file, null );
String result = new MPJSONMarshaller().marshall( user );
System.out.println(result);
Assert.assertEquals( result, orig, "Marshalled sites do not match original sites." );
} }
} }

View File

@ -316,7 +316,7 @@ public class EmergencyActivity extends Activity {
} }
} ); } );
} }
catch (final MPInvalidatedException ignored) { catch (final MPKeyUnavailableException ignored) {
sitePasswordField.setText( "" ); sitePasswordField.setText( "" );
progressView.setVisibility( View.INVISIBLE ); progressView.setVisibility( View.INVISIBLE );
} }

View File

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

View File

@ -18,10 +18,14 @@
package com.lyndir.masterpassword.gui.model; package com.lyndir.masterpassword.gui.model;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.ifNotNullElse;
import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedInteger;
import com.lyndir.masterpassword.MPAlgorithm; import com.lyndir.masterpassword.MPAlgorithm;
import com.lyndir.masterpassword.MPResultType; import com.lyndir.masterpassword.MPResultType;
import com.lyndir.masterpassword.model.MPSite; import com.lyndir.masterpassword.model.MPSite;
import com.lyndir.masterpassword.model.MPUser;
import javax.annotation.Nullable;
/** /**
@ -29,19 +33,28 @@ import com.lyndir.masterpassword.model.MPSite;
*/ */
public class IncognitoSite extends MPSite { public class IncognitoSite extends MPSite {
private final IncognitoUser user;
private String siteName; private String siteName;
private UnsignedInteger siteCounter; private UnsignedInteger siteCounter;
private MPResultType resultType; private MPResultType resultType;
private MPResultType loginType;
private MPAlgorithm algorithm; private MPAlgorithm algorithm;
public IncognitoSite(final String siteName, final UnsignedInteger siteCounter, final MPResultType resultType, public IncognitoSite(final IncognitoUser user, final String siteName, final UnsignedInteger siteCounter, final MPResultType resultType,
final MPAlgorithm algorithm) { final MPAlgorithm algorithm) {
this.user = user;
this.siteName = siteName; this.siteName = siteName;
this.siteCounter = siteCounter; this.siteCounter = siteCounter;
this.resultType = resultType; this.resultType = resultType;
this.algorithm = algorithm; this.algorithm = algorithm;
} }
@Override
public MPUser<?> getUser() {
return user;
}
@Override @Override
public String getSiteName() { public String getSiteName() {
return siteName; return siteName;
@ -62,6 +75,16 @@ public class IncognitoSite extends MPSite {
this.resultType = resultType; this.resultType = resultType;
} }
@Override
public MPResultType getLoginType() {
return loginType;
}
@Override
public void setLoginType(@Nullable final MPResultType loginType) {
this.loginType = ifNotNullElse( loginType, getAlgorithm().mpw_default_login_type() );
}
@Override @Override
public MPAlgorithm getAlgorithm() { public MPAlgorithm getAlgorithm() {
return algorithm; return algorithm;

View File

@ -86,9 +86,8 @@ public class IncognitoAuthenticationPanel extends AuthenticationPanel<IncognitoU
return new PasswordFrame<IncognitoUser, IncognitoSite>( Preconditions.checkNotNull( getSelectedUser() ) ) { return new PasswordFrame<IncognitoUser, IncognitoSite>( Preconditions.checkNotNull( getSelectedUser() ) ) {
@Override @Override
protected IncognitoSite createSite(final IncognitoUser user, final String siteName, final UnsignedInteger siteCounter, protected IncognitoSite createSite(final IncognitoUser user, final String siteName, final UnsignedInteger siteCounter,
final MPResultType resultType, final MPResultType resultType, final MPAlgorithm algorithm) {
final MPAlgorithm algorithm) { return new IncognitoSite( user, siteName, siteCounter, resultType, algorithm );
return new IncognitoSite( siteName, siteCounter, resultType, algorithm );
} }
}; };
} }

View File

@ -147,7 +147,7 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPSite> exten
siteCounterField = Components.spinner( siteCounterModel ) ); siteCounterField = Components.spinner( siteCounterModel ) );
sitePanel.add( siteSettings ); sitePanel.add( siteSettings );
resultTypeField.setFont( Res.valueFont().deriveFont( resultTypeField.getFont().getSize2D() ) ); resultTypeField.setFont( Res.valueFont().deriveFont( resultTypeField.getFont().getSize2D() ) );
resultTypeField.setSelectedItem( user.getAlgorithm().mpw_default_type() ); resultTypeField.setSelectedItem( user.getAlgorithm().mpw_default_password_type() );
resultTypeField.addItemListener( new ItemListener() { resultTypeField.addItemListener( new ItemListener() {
@Override @Override
public void itemStateChanged(final ItemEvent e) { public void itemStateChanged(final ItemEvent e) {

@ -1 +1 @@
Subproject commit e886da7af01747972cde23ff09827b56dbba973c Subproject commit 54bd876ed6b8cebba3fdfe8b546d1783894d5401