From f0d523fb358cb2917dec81db605d4fe6f61eb790 Mon Sep 17 00:00:00 2001 From: Maarten Billemont Date: Tue, 8 May 2018 22:40:48 -0400 Subject: [PATCH] Initial Java JSON serialization/deserialization. --- core/c/mpw-marshal-util.c | 1 + .../lyndir/masterpassword/MPAlgorithm.java | 7 +- .../lyndir/masterpassword/MPAlgorithmV0.java | 70 ++------ .../com/lyndir/masterpassword/MPConstant.java | 2 +- .../MPInvalidatedException.java | 25 --- .../lyndir/masterpassword/MPMasterKey.java | 22 +-- .../masterpassword/model/MPFileSite.java | 112 +++++++------ .../masterpassword/model/MPFileUser.java | 31 +++- .../model/MPFileUserManager.java | 9 +- .../model/MPFlatMarshaller.java | 18 +- .../model/MPFlatUnmarshaller.java | 26 ++- .../masterpassword/model/MPJSONFile.java | 154 ++++++++++-------- .../model/MPJSONMarshaller.java | 8 +- .../model/MPJSONUnmarshaller.java | 26 ++- .../masterpassword/model/MPMarshaller.java | 7 +- .../lyndir/masterpassword/model/MPSite.java | 22 ++- .../masterpassword/model/MPUnmarshaller.java | 10 +- .../lyndir/masterpassword/model/MPUser.java | 11 +- .../masterpassword/MPMasterKeyTest.java | 2 +- .../lyndir/masterpassword/MPModelTest.java | 16 +- .../masterpassword/EmergencyActivity.java | 2 +- .../lyndir/masterpassword/Preferences.java | 2 +- .../gui/model/IncognitoSite.java | 25 ++- .../view/IncognitoAuthenticationPanel.java | 5 +- .../gui/view/PasswordFrame.java | 2 +- public/site | 2 +- 26 files changed, 330 insertions(+), 287 deletions(-) delete mode 100644 core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPInvalidatedException.java diff --git a/core/c/mpw-marshal-util.c b/core/c/mpw-marshal-util.c index 9bd0e66b..17cba9f1 100644 --- a/core/c/mpw-marshal-util.c +++ b/core/c/mpw-marshal-util.c @@ -38,6 +38,7 @@ char *mpw_get_token(const char **in, const char *eol, char *delim) { time_t mpw_mktime( const char *time) { + // TODO: Support parsing timezone into tm_gmtoff struct tm tm = { .tm_isdst = -1 }; if (time && sscanf( time, "%4d-%2d-%2dT%2d:%2d:%2dZ", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithm.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithm.java index bf43c6e5..add7732a 100644 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithm.java +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithm.java @@ -59,7 +59,12 @@ public abstract class MPAlgorithm { /** * 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. diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV0.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV0.java index c0cf5106..1ae201d7 100644 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV0.java +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV0.java @@ -83,8 +83,8 @@ public class MPAlgorithmV0 extends MPAlgorithm { } @Override - public byte[] siteKey(final byte[] masterKey, final String siteName, UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose, - @Nullable final String keyContext) { + public byte[] siteKey(final byte[] masterKey, final String siteName, UnsignedInteger siteCounter, + final MPKeyPurpose keyPurpose, @Nullable final String keyContext) { String keyScope = keyPurpose.getScope(); logger.trc( "keyScope: %s", keyScope ); @@ -117,8 +117,8 @@ public class MPAlgorithmV0 extends MPAlgorithm { @Override public String siteResult(final byte[] masterKey, final byte[] siteKey, final String siteName, final UnsignedInteger siteCounter, - final MPKeyPurpose keyPurpose, - @Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam) { + final MPKeyPurpose keyPurpose, @Nullable final String keyContext, + final MPResultType resultType, @Nullable final String resultParam) { switch (resultType.getTypeClass()) { case Template: @@ -133,8 +133,8 @@ public class MPAlgorithmV0 extends MPAlgorithm { } @Override - public String sitePasswordFromTemplate(final byte[] masterKey, final byte[] siteKey, final MPResultType resultType, - @Nullable final String resultParam) { + public String sitePasswordFromTemplate(final byte[] masterKey, final byte[] siteKey, + final MPResultType resultType, @Nullable final String resultParam) { int[] _siteKey = new int[siteKey.length]; for (int i = 0; i < siteKey.length; ++i) { @@ -168,8 +168,8 @@ public class MPAlgorithmV0 extends MPAlgorithm { } @Override - public String sitePasswordFromCrypt(final byte[] masterKey, final byte[] siteKey, final MPResultType resultType, - @Nullable final String resultParam) { + public String sitePasswordFromCrypt(final byte[] masterKey, final byte[] siteKey, + final MPResultType resultType, @Nullable final String resultParam) { Preconditions.checkNotNull( resultParam ); Preconditions.checkArgument( !resultParam.isEmpty() ); @@ -192,8 +192,8 @@ public class MPAlgorithmV0 extends MPAlgorithm { } @Override - public String sitePasswordFromDerive(final byte[] masterKey, final byte[] siteKey, final MPResultType resultType, - @Nullable final String resultParam) { + public String sitePasswordFromDerive(final byte[] masterKey, final byte[] siteKey, + final MPResultType resultType, @Nullable final String resultParam) { if (resultType == MPResultType.DeriveKey) { int resultParamInt = ConversionUtils.toIntegerNN( resultParam ); @@ -220,8 +220,8 @@ public class MPAlgorithmV0 extends MPAlgorithm { @Override public String siteState(final byte[] masterKey, final byte[] siteKey, final String siteName, final UnsignedInteger siteCounter, - final MPKeyPurpose keyPurpose, - @Nullable final String keyContext, final MPResultType resultType, final String resultParam) { + final MPKeyPurpose keyPurpose, @Nullable final String keyContext, + final MPResultType resultType, final String resultParam) { try { // Encrypt @@ -246,111 +246,77 @@ public class MPAlgorithmV0 extends MPAlgorithm { return MPMasterKey.Version.V0; } - /** - * mpw: defaults: password result type. - */ @Override - public MPResultType mpw_default_type() { + public MPResultType mpw_default_password_type() { return MPResultType.GeneratedLong; } - /** - * mpw: defaults: initial counter value. - */ + @Override + public MPResultType mpw_default_login_type() { + return MPResultType.GeneratedName; + } + @Override public UnsignedInteger mpw_default_counter() { return UnsignedInteger.ONE; } - /** - * mpw: validity for the time-based rolling counter. - */ @Override @SuppressWarnings("MagicNumber") public long mpw_otp_window() { return 5 * 60 /* s */; } - /** - * mpw: Key ID hash. - */ @Override public MessageDigests mpw_hash() { return MessageDigests.SHA256; } - /** - * mpw: Site digest. - */ @Override public MessageAuthenticationDigests mpw_digest() { return MessageAuthenticationDigests.HmacSHA256; } - /** - * mpw: Platform-agnostic byte order. - */ @Override public ByteOrder mpw_byteOrder() { return ByteOrder.BIG_ENDIAN; } - /** - * mpw: Input character encoding. - */ @Override public Charset mpw_charset() { return Charsets.UTF_8; } - /** - * mpw: Master key size (byte). - */ @Override @SuppressWarnings("MagicNumber") public int mpw_dkLen() { return 64; } - /** - * mpw: Minimum size for derived keys (bit). - */ @Override @SuppressWarnings("MagicNumber") public int mpw_keySize_min() { return 128; } - /** - * mpw: Maximum size for derived keys (bit). - */ @Override @SuppressWarnings("MagicNumber") public int mpw_keySize_max() { return 512; } - /** - * scrypt: Parallelization parameter. - */ @Override @SuppressWarnings("MagicNumber") public int scrypt_p() { return 2; } - /** - * scrypt: Memory cost parameter. - */ @Override @SuppressWarnings("MagicNumber") public int scrypt_r() { return 8; } - /** - * scrypt: CPU cost parameter. - */ @Override @SuppressWarnings("MagicNumber") public int scrypt_N() { diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPConstant.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPConstant.java index b4d3cc9b..e0a15cec 100644 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPConstant.java +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPConstant.java @@ -42,5 +42,5 @@ public final class MPConstant { public static final int MS_PER_S = 1000; - public static final DateTimeFormatter dateTimeFormatter = ISODateTimeFormat.dateTimeNoMillis(); + public static final DateTimeFormatter dateTimeFormatter = ISODateTimeFormat.dateTimeNoMillis().withZoneUTC(); } diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPInvalidatedException.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPInvalidatedException.java deleted file mode 100644 index 53802f37..00000000 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPInvalidatedException.java +++ /dev/null @@ -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 . -//============================================================================== - -package com.lyndir.masterpassword; - -/** - * @author lhunath, 2017-09-21 - */ -public class MPInvalidatedException extends Exception { -} diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPMasterKey.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPMasterKey.java index 4a8fd464..f9883326 100644 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPMasterKey.java +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPMasterKey.java @@ -56,14 +56,14 @@ public class MPMasterKey { /** * 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) - throws MPInvalidatedException { + throws MPKeyUnavailableException { Preconditions.checkArgument( masterPassword.length > 0 ); if (invalidated) - throw new MPInvalidatedException(); + throw new MPKeyUnavailableException(); byte[] key = keyByVersion.get( algorithm.version() ); if (key == null) { @@ -81,11 +81,11 @@ public class MPMasterKey { /** * 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, @Nullable final String keyContext, final MPAlgorithm algorithm) - throws MPInvalidatedException { + throws MPKeyUnavailableException { Preconditions.checkArgument( !siteName.isEmpty() ); 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 * {@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, @Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam, final MPAlgorithm algorithm) - throws MPInvalidatedException { + throws MPKeyUnavailableException { byte[] masterKey = masterKey( algorithm ); byte[] siteKey = siteKey( siteName, siteCounter, keyPurpose, keyContext, algorithm ); @@ -139,12 +139,12 @@ public class MPMasterKey { * @param resultParam The result token desired from * {@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, @Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam, final MPAlgorithm algorithm) - throws MPInvalidatedException { + throws MPKeyUnavailableException { Preconditions.checkNotNull( resultParam ); Preconditions.checkArgument( !resultParam.isEmpty() ); @@ -169,10 +169,10 @@ public class MPMasterKey { /** * 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) - throws MPInvalidatedException { + throws MPKeyUnavailableException { return algorithm.toID( masterKey( algorithm ) ); } diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileSite.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileSite.java index 6e8acab0..7b87881a 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileSite.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileSite.java @@ -18,10 +18,14 @@ package com.lyndir.masterpassword.model; +import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*; + import com.google.common.primitives.UnsignedInteger; import com.lyndir.masterpassword.*; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.joda.time.Instant; +import org.joda.time.ReadableInstant; /** @@ -29,22 +33,23 @@ import org.joda.time.Instant; */ public class MPFileSite extends MPSite { - private final MPFileUser user; - private String siteName; + private final MPFileUser user; + + private String siteName; @Nullable - private String siteContent; - private UnsignedInteger siteCounter; - private MPResultType resultType; - private MPAlgorithm algorithm; + private String siteState; + private UnsignedInteger siteCounter; + private MPResultType resultType; + private MPAlgorithm algorithm; @Nullable - private String loginContent; + private String loginState; private MPResultType loginType; @Nullable - private String url; - private int uses; - private Instant lastUsed; + private String url; + private int uses; + private ReadableInstant lastUsed; public MPFileSite(final MPFileUser user, final String siteName) { this( user, siteName, null, null, user.getAlgorithm() ); @@ -56,45 +61,43 @@ public class MPFileSite extends MPSite { 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 String loginContent, @Nullable final MPResultType loginType, - @Nullable final String url, final int uses, final Instant lastUsed) { + @Nullable final String loginState, @Nullable final MPResultType loginType, + @Nullable final String url, final int uses, final ReadableInstant lastUsed) { this.user = user; this.siteName = siteName; - this.siteContent = siteContent; - this.siteCounter = (siteCounter == null)? user.getAlgorithm().mpw_default_counter(): siteCounter; - this.resultType = (resultType == null)? user.getAlgorithm().mpw_default_type(): resultType; + this.siteState = siteState; + this.siteCounter = ifNotNullElse( siteCounter, user.getAlgorithm().mpw_default_counter() ); + this.resultType = ifNotNullElse( resultType, user.getAlgorithm().mpw_default_password_type() ); this.algorithm = algorithm; - this.loginContent = loginContent; - this.loginType = (loginType == null)? MPResultType.GeneratedName: loginType; + this.loginState = loginState; + this.loginType = ifNotNullElse( loginType, getAlgorithm().mpw_default_login_type() ); this.url = url; this.uses = uses; this.lastUsed = lastUsed; } - public String resultFor(final MPMasterKey masterKey) - throws MPInvalidatedException { + public String getResult() + 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) - throws MPInvalidatedException { + public String getResult(final MPKeyPurpose keyPurpose, @Nullable final String keyContext) + throws MPKeyUnavailableException { - return resultFor( masterKey, keyPurpose, keyContext, getSiteContent() ); + return getResult( keyPurpose, keyContext, siteState ); } - public String loginFor(final MPMasterKey masterKey) - throws MPInvalidatedException { + public String getLogin() + throws MPKeyUnavailableException { - if (loginType == null) - loginType = MPResultType.GeneratedName; - - return loginFor( masterKey, loginType, loginContent ); + return getLogin( loginState ); } - public MPFileUser getUser() { + @Override + public MPUser getUser() { return user; } @@ -109,18 +112,18 @@ public class MPFileSite extends MPSite { } @Nullable - public String getSiteContent() { - return siteContent; + public String getSiteState() { + return siteState; } - public void setSitePassword(final MPMasterKey masterKey, final MPResultType resultType, @Nullable final String result) - throws MPInvalidatedException { + public void setSitePassword(final MPResultType resultType, @Nullable final String result) + throws MPKeyUnavailableException { this.resultType = resultType; if (result == null) - this.siteContent = null; + this.siteState = null; else - this.siteContent = masterKey.siteState( + this.siteState = user.getMasterKey().siteState( siteName, siteCounter, MPKeyPurpose.Authentication, null, resultType, result, algorithm ); } @@ -144,6 +147,17 @@ public class MPFileSite extends MPSite { 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 public MPAlgorithm getAlgorithm() { return algorithm; @@ -154,25 +168,17 @@ public class MPFileSite extends MPSite { this.algorithm = algorithm; } - public MPResultType getLoginType() { - return loginType; - } - @Nullable - public String getLoginContent() { - return loginContent; + public String getLoginState() { + return loginState; } - public void setLoginName(final MPMasterKey masterKey, @Nullable final MPResultType loginType, @Nullable final String result) - throws MPInvalidatedException { + public void setLoginName(@Nonnull final MPResultType loginType, @Nonnull final String loginName) + throws MPKeyUnavailableException { this.loginType = loginType; - if (this.loginType != null) - if (result == null) - this.loginContent = null; - else - this.loginContent = masterKey.siteState( - siteName, algorithm.mpw_default_counter(), MPKeyPurpose.Identification, null, this.loginType, result, - algorithm ); + this.loginState = user.getMasterKey().siteState( + siteName, algorithm.mpw_default_counter(), MPKeyPurpose.Identification, null, + this.loginType, loginName, algorithm ); } @Nullable @@ -188,7 +194,7 @@ public class MPFileSite extends MPSite { return uses; } - public Instant getLastUsed() { + public ReadableInstant getLastUsed() { return lastUsed; } diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileUser.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileUser.java index 7f53f98e..099ddf83 100755 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileUser.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileUser.java @@ -42,9 +42,10 @@ public class MPFileUser extends MPUser implements Comparable sites = Sets.newHashSet(); @Nullable - private byte[] keyID; - private MPAlgorithm algorithm; - private MPMarshalFormat format; + private byte[] keyID; + private MPAlgorithm algorithm; + private MPMarshalFormat format; + private MPMarshaller.ContentMode contentMode; private int avatar; private MPResultType defaultType; @@ -55,11 +56,13 @@ public class MPFileUser extends MPUser implements Comparable implements Comparable implements Comparable implements Comparable implements Comparable user.getFormat().ordinal())) users.put( previousUser.getFullName(), previousUser ); @@ -86,6 +86,9 @@ public class MPFileUserManager extends MPUserManager { catch (final IOException | MPMarshalException e) { 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(); } @@ -117,7 +120,7 @@ public class MPFileUserManager extends MPUserManager { * Write the current user state to disk. */ public void save(final MPFileUser user, final MPMasterKey masterKey) - throws MPInvalidatedException { + throws MPKeyUnavailableException { try { final MPMarshalFormat format = user.getFormat(); new CharSink() { @@ -126,7 +129,7 @@ public class MPFileUserManager extends MPUserManager { throws IOException { 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) { logger.err( e, "Unable to save sites for user: %s", user ); diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFlatMarshaller.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFlatMarshaller.java index 4381ace9..794f6dd3 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFlatMarshaller.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFlatMarshaller.java @@ -36,11 +36,11 @@ public class MPFlatMarshaller implements MPMarshaller { @Nonnull @Override - public String marshall(final MPFileUser user, final MPMasterKey masterKey, final ContentMode contentMode) - throws MPInvalidatedException, MPMarshalException { + public String marshall(final MPFileUser user) + throws MPKeyUnavailableException, MPMarshalException { StringBuilder content = new StringBuilder(); 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( "# 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( "# Algorithm: " ).append( user.getAlgorithm().version().toInt() ).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( "# Last Times Password Login\t Site\tSite\n" ); content.append( "# used used type name\t name\tpassword\n" ); for (final MPFileSite site : user.getSites()) { - String loginName = site.getLoginContent(); - String password = site.getSiteContent(); - if (!contentMode.isRedacted()) { - loginName = site.loginFor( masterKey ); - password = site.resultFor( masterKey ); + String loginName = site.getLoginState(); + String password = site.getSiteState(); + if (!user.getContentMode().isRedacted()) { + loginName = site.getLogin(); + password = site.getResult(); } content.append( strf( "%s %8d %8s %25s\t%25s\t%s\n", // diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFlatUnmarshaller.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFlatUnmarshaller.java index cfc1f599..d0760467 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFlatUnmarshaller.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFlatUnmarshaller.java @@ -29,6 +29,7 @@ import java.io.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import org.joda.time.DateTime; @@ -46,17 +47,17 @@ public class MPFlatUnmarshaller implements MPUnmarshaller { @Nonnull @Override - public MPFileUser unmarshall(@Nonnull final File file) - throws IOException, MPMarshalException { + public MPFileUser unmarshall(@Nonnull final File file, @Nullable final char[] masterPassword) + throws IOException, MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException { try (Reader reader = new InputStreamReader( new FileInputStream( file ), Charsets.UTF_8 )) { - return unmarshall( CharStreams.toString( reader ) ); + return unmarshall( CharStreams.toString( reader ), masterPassword ); } } @Nonnull @Override - public MPFileUser unmarshall(@Nonnull final String content) - throws MPMarshalException { + public MPFileUser unmarshall(@Nonnull final String content, @Nullable final char[] masterPassword) + throws MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException { MPFileUser user = null; byte[] keyID = null; String fullName = null; @@ -74,7 +75,8 @@ public class MPFlatUnmarshaller implements MPUnmarshaller { else // Ends the header. 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. else if (line.startsWith( "#" )) { @@ -113,25 +115,31 @@ public class MPFlatUnmarshaller implements MPUnmarshaller { switch (importFormat) { case 0: site = new MPFileSite( user, // - siteMatcher.group( 5 ), siteMatcher.group( 6 ), + siteMatcher.group( 5 ), clearContent? null: siteMatcher.group( 6 ), user.getAlgorithm().mpw_default_counter(), MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ), MPMasterKey.Version.fromInt( ConversionUtils.toIntegerNN( colon.matcher( siteMatcher.group( 4 ) ).replaceAll( "" ) ) ).getAlgorithm(), null, null, null, ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ), MPConstant.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() ); + if (clearContent) + site.setSitePassword( site.getResultType(), siteMatcher.group( 6 ) ); break; case 1: 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( "" ) ), MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ), MPMasterKey.Version.fromInt( ConversionUtils.toIntegerNN( 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 ) ), MPConstant.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() ); + if (clearContent) { + site.setSitePassword( site.getResultType(), siteMatcher.group( 8 ) ); + site.setLoginName( MPResultType.StoredPersonal, siteMatcher.group( 6 ) ); + } break; default: diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONFile.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONFile.java index aac71e0b..d52ba3f0 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONFile.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONFile.java @@ -25,8 +25,6 @@ import java.util.LinkedHashMap; import java.util.Map; import javax.annotation.Nullable; 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 { - private static final DateTimeFormatter dateFormatter = ISODateTimeFormat.dateTimeNoMillis(); - - 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; - // } - + public MPJSONFile(final MPFileUser user) + throws MPKeyUnavailableException { // Section: "export" Export fileExport = this.export = new Export(); fileExport.format = 1; - fileExport.redacted = contentMode.isRedacted(); - fileExport.date = dateFormatter.print( new Instant() ); + fileExport.redacted = user.getContentMode().isRedacted(); + fileExport.date = MPConstant.dateTimeFormatter.print( new Instant() ); // Section: "user" User fileUser = this.user = new User(); fileUser.avatar = user.getAvatar(); - fileUser.fullName = user.getFullName(); + fileUser.full_name = user.getFullName(); - fileUser.lastUsed = dateFormatter.print( user.getLastUsed() ); - fileUser.keyId = CodeUtils.encodeHex( masterKey.getKeyID( user.getAlgorithm() ) ); + fileUser.last_used = MPConstant.dateTimeFormatter.print( user.getLastUsed() ); + fileUser.key_id = CodeUtils.encodeHex( user.getKeyID() ); fileUser.algorithm = user.getAlgorithm().version(); - fileUser.defaultType = user.getDefaultType(); + fileUser.default_type = user.getDefaultType(); // Section "sites" - fileUser.sites = new LinkedHashMap<>(); + sites = new LinkedHashMap<>(); for (final MPFileSite site : user.getSites()) { Site fileSite; String content = null, loginContent = null; - if (!contentMode.isRedacted()) { + if (!fileExport.redacted) { // Clear Text - content = masterKey.siteResult( site.getSiteName(), site.getSiteCounter(), - MPKeyPurpose.Authentication, null, site.getResultType(), site.getSiteContent(), - site.getAlgorithm() ); - loginContent = masterKey.siteResult( site.getSiteName(), site.getAlgorithm().mpw_default_counter(), - MPKeyPurpose.Identification, null, site.getLoginType(), site.getLoginContent(), - site.getAlgorithm() ); + content = site.getResult(); + loginContent = user.getMasterKey().siteResult( + site.getSiteName(), site.getAlgorithm().mpw_default_counter(), + MPKeyPurpose.Identification, null, site.getLoginType(), site.getLoginState(), site.getAlgorithm() ); } else { // Redacted if (site.getResultType().supportsTypeFeature( MPSiteFeature.ExportContent )) - content = site.getSiteContent(); + content = site.getSiteState(); 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.counter = site.getSiteCounter(); + fileSite.counter = site.getSiteCounter().longValue(); fileSite.algorithm = site.getAlgorithm().version(); fileSite.password = content; fileSite.login_name = loginContent; - fileSite.loginType = site.getLoginType(); + fileSite.login_type = site.getLoginType(); 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<>(); // for (size_t q = 0; q < site.questions_count; ++q) { @@ -133,10 +114,44 @@ public class MPJSONFile { } } - public MPFileUser toUser() { - return new MPFileUser( user.fullName, CodeUtils.decodeHex( user.keyId ), user.algorithm.getAlgorithm(), user.avatar, user.defaultType, dateFormatter.parseDateTime( user.lastUsed ), MPMarshalFormat.JSON ); + public MPFileUser toUser(@Nullable final char[] masterPassword) + 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 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 sites; + + public static class Export { int format; @@ -147,47 +162,44 @@ public class MPJSONFile { public static class User { - String fullName; - + int avatar; + String full_name; + String last_used; + String key_id; MPMasterKey.Version algorithm; - boolean redacted; - - int avatar; - MPResultType defaultType; - String lastUsed; - String keyId; - - Map sites; + MPResultType default_type; } public static class Site { + MPResultType type; + long counter; + MPMasterKey.Version algorithm; @Nullable String password; @Nullable String login_name; - String name; - String content; - MPResultType type; - UnsignedInteger counter; - MPMasterKey.Version algorithm; - - String loginContent; - MPResultType loginType; - - String url; - int uses; - String lastUsed; + MPResultType login_type; + int uses; + String last_used; Map questions; - } + + Ext _ext_mpw; - public static class Question { + public static class Ext { - String keyword; - String content; - MPResultType type; + @Nullable + String url; + } + + + public static class Question { + + MPResultType type; + String answer; + } } } diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONMarshaller.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONMarshaller.java index c1a55f98..f6833e89 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONMarshaller.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONMarshaller.java @@ -31,14 +31,14 @@ public class MPJSONMarshaller implements MPMarshaller { private final Gson gson = new GsonBuilder() .registerTypeAdapter( MPMasterKey.Version.class, new EnumOrdinalAdapter() ) .registerTypeAdapter( MPResultType.class, new MPResultTypeAdapter() ) - .setFieldNamingStrategy( FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES ) + .setFieldNamingStrategy( FieldNamingPolicy.IDENTITY ) .setPrettyPrinting().create(); @Nonnull @Override - public String marshall(final MPFileUser user, final MPMasterKey masterKey, final ContentMode contentMode) - throws MPInvalidatedException, MPMarshalException { + public String marshall(final MPFileUser user) + throws MPKeyUnavailableException, MPMarshalException { - return gson.toJson( new MPJSONFile( user, masterKey, contentMode ) ); + return gson.toJson( new MPJSONFile( user ) ); } } diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONUnmarshaller.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONUnmarshaller.java index 511774b2..67ed3a0e 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONUnmarshaller.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONUnmarshaller.java @@ -19,11 +19,11 @@ package com.lyndir.masterpassword.model; import com.google.gson.*; -import com.lyndir.masterpassword.MPMasterKey; -import com.lyndir.masterpassword.MPResultType; +import com.lyndir.masterpassword.*; import java.io.*; import java.nio.charset.StandardCharsets; import javax.annotation.Nonnull; +import javax.annotation.Nullable; /** @@ -39,19 +39,29 @@ public class MPJSONUnmarshaller implements MPUnmarshaller { @Nonnull @Override - public MPFileUser unmarshall(@Nonnull final File file) - throws IOException, MPMarshalException { + public MPFileUser unmarshall(@Nonnull final File file, @Nullable final char[] masterPassword) + throws IOException, MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException { 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 @Override - public MPFileUser unmarshall(@Nonnull final String content) - throws MPMarshalException { + public MPFileUser unmarshall(@Nonnull final String content, @Nullable final char[] masterPassword) + 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 ); + } } } diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPMarshaller.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPMarshaller.java index f874e7e3..4b954bf8 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPMarshaller.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPMarshaller.java @@ -18,8 +18,7 @@ package com.lyndir.masterpassword.model; -import com.lyndir.masterpassword.MPInvalidatedException; -import com.lyndir.masterpassword.MPMasterKey; +import com.lyndir.masterpassword.MPKeyUnavailableException; import javax.annotation.Nonnull; @@ -29,8 +28,8 @@ import javax.annotation.Nonnull; public interface MPMarshaller { @Nonnull - String marshall(MPFileUser user, MPMasterKey masterKey, ContentMode contentMode) - throws MPInvalidatedException, MPMarshalException; + String marshall(MPFileUser user) + throws MPKeyUnavailableException, MPMarshalException; enum ContentMode { PROTECTED( "Export of site names and stored passwords (unless device-private) encrypted with the master key.", true ), diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPSite.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPSite.java index dfb8f909..0d560d76 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPSite.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPSite.java @@ -31,6 +31,8 @@ import javax.annotation.Nullable; */ public abstract class MPSite { + public abstract MPUser getUser(); + public abstract String getSiteName(); public abstract void setSiteName(String siteName); @@ -43,24 +45,28 @@ public abstract class MPSite { public abstract void setResultType(MPResultType resultType); + public abstract MPResultType getLoginType(); + + public abstract void setLoginType(@Nullable MPResultType loginType); + public abstract MPAlgorithm getAlgorithm(); 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) - throws MPInvalidatedException { + throws MPKeyUnavailableException { - return masterKey.siteResult( + return getUser().getMasterKey().siteResult( getSiteName(), getSiteCounter(), keyPurpose, keyContext, getResultType(), siteContent, getAlgorithm() ); } - public String loginFor(final MPMasterKey masterKey, final MPResultType loginType, @Nullable final String loginContent) - throws MPInvalidatedException { + public String getLogin(@Nullable final String loginContent) + throws MPKeyUnavailableException { - return masterKey.siteResult( - getSiteName(), getAlgorithm().mpw_default_counter(), MPKeyPurpose.Identification, null, loginType, loginContent, - getAlgorithm() ); + return getUser().getMasterKey().siteResult( + getSiteName(), getAlgorithm().mpw_default_counter(), MPKeyPurpose.Identification, null, + getLoginType(), loginContent, getAlgorithm() ); } @Override diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUnmarshaller.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUnmarshaller.java index 0be1bbc9..d07555eb 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUnmarshaller.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUnmarshaller.java @@ -18,9 +18,11 @@ package com.lyndir.masterpassword.model; +import com.lyndir.masterpassword.MPKeyUnavailableException; import java.io.File; import java.io.IOException; import javax.annotation.Nonnull; +import javax.annotation.Nullable; /** @@ -29,10 +31,10 @@ import javax.annotation.Nonnull; public interface MPUnmarshaller { @Nonnull - MPFileUser unmarshall(@Nonnull File file) - throws IOException, MPMarshalException; + MPFileUser unmarshall(@Nonnull File file, @Nullable char[] masterPassword) + throws IOException, MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException; @Nonnull - MPFileUser unmarshall(@Nonnull String content) - throws MPMarshalException; + MPFileUser unmarshall(@Nonnull String content, @Nullable char[] masterPassword) + throws MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException; } diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUser.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUser.java index 8f79fb21..e5ae697b 100755 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUser.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUser.java @@ -20,7 +20,6 @@ package com.lyndir.masterpassword.model; 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.masterpassword.*; import java.util.Collection; @@ -44,12 +43,16 @@ public abstract class MPUser { } @Nonnull - public MPMasterKey getMasterKey() { - return Preconditions.checkNotNull( key, "User is not authenticated: %s", getFullName() ); + public MPMasterKey getMasterKey() + throws MPKeyUnavailableException { + if (key == null) + throw new MPKeyUnavailableException(); + + return key; } public String exportKeyID() - throws MPInvalidatedException { + throws MPKeyUnavailableException { return CodeUtils.encodeHex( getMasterKey().getKeyID( getAlgorithm() ) ); } diff --git a/core/java/tests/src/test/java/com/lyndir/masterpassword/MPMasterKeyTest.java b/core/java/tests/src/test/java/com/lyndir/masterpassword/MPMasterKeyTest.java index 6995974e..bfbc6840 100644 --- a/core/java/tests/src/test/java/com/lyndir/masterpassword/MPMasterKeyTest.java +++ b/core/java/tests/src/test/java/com/lyndir/masterpassword/MPMasterKeyTest.java @@ -64,7 +64,7 @@ public class MPMasterKeyTest { masterKey.getKeyID( testCase.getAlgorithm() ); fail( "[testMasterKey] invalidate ineffective: " + testCase ); } - catch (final MPInvalidatedException ignored) { + catch (final MPKeyUnavailableException ignored) { } assertNotEquals( masterPassword, diff --git a/core/java/tests/src/test/java/com/lyndir/masterpassword/MPModelTest.java b/core/java/tests/src/test/java/com/lyndir/masterpassword/MPModelTest.java index 75e67807..e0af955a 100644 --- a/core/java/tests/src/test/java/com/lyndir/masterpassword/MPModelTest.java +++ b/core/java/tests/src/test/java/com/lyndir/masterpassword/MPModelTest.java @@ -18,8 +18,11 @@ package com.lyndir.masterpassword; -import com.lyndir.masterpassword.model.MPJSONUnmarshaller; -import java.io.File; +import com.google.common.base.Charsets; +import com.google.common.io.CharStreams; +import com.lyndir.masterpassword.model.*; +import java.io.*; +import org.testng.Assert; import org.testng.annotations.Test; @@ -31,7 +34,12 @@ public class MPModelTest { @Test public void testMasterKey() throws Exception { - System.err.println( new MPJSONUnmarshaller().unmarshall( - new File( "/Users/lhunath/.mpw.d/Maarten Billemont.mpsites.json" ) ) ); + File file = 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." ); } } diff --git a/platform-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java b/platform-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java index 10323ac5..a77ff351 100644 --- a/platform-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java +++ b/platform-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java @@ -316,7 +316,7 @@ public class EmergencyActivity extends Activity { } } ); } - catch (final MPInvalidatedException ignored) { + catch (final MPKeyUnavailableException ignored) { sitePasswordField.setText( "" ); progressView.setVisibility( View.INVISIBLE ); } diff --git a/platform-android/src/main/java/com/lyndir/masterpassword/Preferences.java b/platform-android/src/main/java/com/lyndir/masterpassword/Preferences.java index 29e103c6..5cbc7eb2 100644 --- a/platform-android/src/main/java/com/lyndir/masterpassword/Preferences.java +++ b/platform-android/src/main/java/com/lyndir/masterpassword/Preferences.java @@ -148,7 +148,7 @@ public final class Preferences { @Nonnull 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) { diff --git a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/IncognitoSite.java b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/IncognitoSite.java index 5151e19c..98683a8b 100644 --- a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/IncognitoSite.java +++ b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/IncognitoSite.java @@ -18,10 +18,14 @@ package com.lyndir.masterpassword.gui.model; +import static com.lyndir.lhunath.opal.system.util.ObjectUtils.ifNotNullElse; + import com.google.common.primitives.UnsignedInteger; import com.lyndir.masterpassword.MPAlgorithm; import com.lyndir.masterpassword.MPResultType; 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 { + private final IncognitoUser user; + private String siteName; private UnsignedInteger siteCounter; private MPResultType resultType; + private MPResultType loginType; 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) { + this.user = user; this.siteName = siteName; this.siteCounter = siteCounter; this.resultType = resultType; this.algorithm = algorithm; } + @Override + public MPUser getUser() { + return user; + } + @Override public String getSiteName() { return siteName; @@ -62,6 +75,16 @@ public class IncognitoSite extends MPSite { 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 public MPAlgorithm getAlgorithm() { return algorithm; diff --git a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/IncognitoAuthenticationPanel.java b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/IncognitoAuthenticationPanel.java index ca3cacdc..bf34b7e0 100644 --- a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/IncognitoAuthenticationPanel.java +++ b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/IncognitoAuthenticationPanel.java @@ -86,9 +86,8 @@ public class IncognitoAuthenticationPanel extends AuthenticationPanel( Preconditions.checkNotNull( getSelectedUser() ) ) { @Override protected IncognitoSite createSite(final IncognitoUser user, final String siteName, final UnsignedInteger siteCounter, - final MPResultType resultType, - final MPAlgorithm algorithm) { - return new IncognitoSite( siteName, siteCounter, resultType, algorithm ); + final MPResultType resultType, final MPAlgorithm algorithm) { + return new IncognitoSite( user, siteName, siteCounter, resultType, algorithm ); } }; } diff --git a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/PasswordFrame.java b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/PasswordFrame.java index a7dd133f..bd6c39dd 100755 --- a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/PasswordFrame.java +++ b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/PasswordFrame.java @@ -147,7 +147,7 @@ public abstract class PasswordFrame, S extends MPSite> exten siteCounterField = Components.spinner( siteCounterModel ) ); sitePanel.add( siteSettings ); 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() { @Override public void itemStateChanged(final ItemEvent e) { diff --git a/public/site b/public/site index e886da7a..54bd876e 160000 --- a/public/site +++ b/public/site @@ -1 +1 @@ -Subproject commit e886da7af01747972cde23ff09827b56dbba973c +Subproject commit 54bd876ed6b8cebba3fdfe8b546d1783894d5401