From 882de547d0bf752a2c7a8cc9e1fad5b833634804 Mon Sep 17 00:00:00 2001 From: Maarten Billemont Date: Mon, 4 Jun 2018 01:43:46 -0400 Subject: [PATCH] Fully replace Java mpw algorithm implementation with proxy to standard C implementation. --- core/c/src/mpw-jni.c | 122 ++++++-- core/c/src/mpw-jni.h | 32 ++- core/java/algorithm/build.gradle | 3 +- .../lyndir/masterpassword/MPAlgorithm.java | 25 +- .../masterpassword/MPAlgorithmException.java | 33 +++ .../lyndir/masterpassword/MPException.java | 33 +++ .../MPKeyUnavailableException.java | 10 +- .../lyndir/masterpassword/MPMasterKey.java | 40 ++- .../masterpassword/impl/MPAlgorithmV0.java | 269 +++--------------- .../masterpassword/impl/MPAlgorithmV1.java | 29 -- .../masterpassword/impl/MPAlgorithmV2.java | 38 --- .../masterpassword/impl/MPAlgorithmV3.java | 33 --- core/java/model/build.gradle | 15 +- .../MPIncorrectMasterPasswordException.java | 5 +- .../masterpassword/model/MPQuestion.java | 5 +- .../lyndir/masterpassword/model/MPSite.java | 4 +- .../lyndir/masterpassword/model/MPUser.java | 4 +- .../model/impl/MPBasicQuestion.java | 2 +- .../model/impl/MPBasicSite.java | 8 +- .../model/impl/MPBasicUser.java | 12 +- .../model/impl/MPFileQuestion.java | 4 +- .../masterpassword/model/impl/MPFileSite.java | 10 +- .../masterpassword/model/impl/MPFileUser.java | 7 +- .../model/impl/MPFileUserManager.java | 7 +- .../model/impl/MPFlatMarshaller.java | 3 +- .../model/impl/MPFlatUnmarshaller.java | 4 +- .../masterpassword/model/impl/MPJSONFile.java | 6 +- .../model/impl/MPJSONMarshaller.java | 3 +- .../model/impl/MPJSONUnmarshaller.java | 5 +- .../model/impl/MPMarshalException.java | 5 +- .../model/impl/MPMarshaller.java | 3 +- .../model/impl/MPUnmarshaller.java | 5 +- .../masterpassword/gui/view/UnlockFrame.java | 3 +- 33 files changed, 356 insertions(+), 431 deletions(-) create mode 100644 core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmException.java create mode 100644 core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPException.java diff --git a/core/c/src/mpw-jni.c b/core/c/src/mpw-jni.c index efcfc3d7..5c936e16 100644 --- a/core/c/src/mpw-jni.c +++ b/core/c/src/mpw-jni.c @@ -1,29 +1,113 @@ #include #include "mpw-jni.h" +#include "mpw-algorithm.h" #include "mpw-util.h" -/** native int _scrypt(byte[] passwd, byte[] salt, int N, int r, int p, byte[] buf); */ -JNIEXPORT jint JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1scrypt(JNIEnv *env, jobject obj, - jbyteArray passwd, jbyteArray salt, jint N, jint r, jint p, jbyteArray buf) { +// TODO: We may need to zero the jbytes safely. - jbyte *passwdBytes = (*env)->GetByteArrayElements( env, passwd, NULL ); - jbyte *saltBytes = (*env)->GetByteArrayElements( env, salt, NULL ); - const size_t keyLength = (*env)->GetArrayLength( env, buf ); - const uint8_t *key = mpw_kdf_scrypt( keyLength, - (uint8_t *)passwdBytes, (size_t)(*env)->GetArrayLength( env, passwd ), - (uint8_t *)saltBytes, (size_t)(*env)->GetArrayLength( env, salt ), - (uint64_t)N, (uint32_t)r, (uint32_t)p ); - (*env)->ReleaseByteArrayElements( env, passwd, passwdBytes, JNI_ABORT ); - (*env)->ReleaseByteArrayElements( env, salt, saltBytes, JNI_ABORT ); +/* native int _masterKey(final String fullName, final byte[] masterPassword, final Version version) */ +JNIEXPORT jbyteArray JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1masterKey(JNIEnv *env, jobject obj, + jstring fullName, jbyteArray masterPassword, jint algorithmVersion) { - if (!key) - return ERR; + const char *fullNameString = (*env)->GetStringUTFChars( env, fullName, NULL ); + jbyte *masterPasswordString = (*env)->GetByteArrayElements( env, masterPassword, NULL ); - jbyte *bufBytes = (*env)->GetByteArrayElements( env, buf, NULL ); - memcpy( bufBytes, key, keyLength ); - (*env)->ReleaseByteArrayElements( env, buf, bufBytes, JNI_OK ); - mpw_free( &key, keyLength ); + MPMasterKey masterKeyBytes = mpw_masterKey( fullNameString, (char *)masterPasswordString, (MPAlgorithmVersion)algorithmVersion ); + (*env)->ReleaseStringUTFChars( env, fullName, fullNameString ); + (*env)->ReleaseByteArrayElements( env, masterPassword, masterPasswordString, JNI_ABORT ); - return OK; + if (!masterKeyBytes) + return NULL; + + jbyteArray masterKey = (*env)->NewByteArray( env, (jsize)MPMasterKeySize ); + (*env)->SetByteArrayRegion( env, masterKey, 0, (jsize)MPMasterKeySize, (jbyte *)masterKeyBytes ); + mpw_free( &masterKeyBytes, MPMasterKeySize ); + + return masterKey; +} + +/* native int _siteKey(final byte[] masterKey, final String siteName, final long siteCounter, + final MPKeyPurpose keyPurpose, @Nullable final String keyContext, final Version version) */ +JNIEXPORT jbyteArray JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1siteKey(JNIEnv *env, jobject obj, + jbyteArray masterKey, jstring siteName, jlong siteCounter, jint keyPurpose, jstring keyContext, jint algorithmVersion) { + + jbyte *masterKeyBytes = (*env)->GetByteArrayElements( env, masterKey, NULL ); + const char *siteNameString = (*env)->GetStringUTFChars( env, siteName, NULL ); + const char *keyContextString = keyContext? (*env)->GetStringUTFChars( env, keyContext, NULL ): NULL; + MPMasterKey siteKeyBytes = mpw_siteKey( + (MPMasterKey)masterKeyBytes, siteNameString, (MPCounterValue)siteCounter, + (MPKeyPurpose)keyPurpose, keyContextString, (MPAlgorithmVersion)algorithmVersion ); + (*env)->ReleaseByteArrayElements( env, masterKey, masterKeyBytes, JNI_ABORT ); + (*env)->ReleaseStringUTFChars( env, siteName, siteNameString ); + (*env)->ReleaseStringUTFChars( env, keyContext, keyContextString ); + + if (!siteKeyBytes) + return NULL; + + jbyteArray siteKey = (*env)->NewByteArray( env, (jsize)MPMasterKeySize ); + (*env)->SetByteArrayRegion( env, siteKey, 0, (jsize)MPMasterKeySize, (jbyte *)siteKeyBytes ); + mpw_free( &siteKeyBytes, MPSiteKeySize ); + + return siteKey; +} + +/* native String _siteResult(final byte[] masterKey, final byte[] siteKey, final String siteName, final long siteCounter, + final MPKeyPurpose keyPurpose, @Nullable final String keyContext, + final MPResultType resultType, @Nullable final String resultParam, final Version version) */ +JNIEXPORT jstring JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1siteResult(JNIEnv *env, jobject obj, + jbyteArray masterKey, jbyteArray siteKey, jstring siteName, jlong siteCounter, jint keyPurpose, jstring keyContext, + jint resultType, jstring resultParam, jint algorithmVersion) { + + jbyte *masterKeyBytes = (*env)->GetByteArrayElements( env, masterKey, NULL ); + jbyte *siteKeyBytes = (*env)->GetByteArrayElements( env, siteKey, NULL ); + const char *siteNameString = (*env)->GetStringUTFChars( env, siteName, NULL ); + const char *keyContextString = keyContext? (*env)->GetStringUTFChars( env, keyContext, NULL ): NULL; + const char *resultParamString = resultParam? (*env)->GetStringUTFChars( env, resultParam, NULL ): NULL; + const char *siteResultString = mpw_siteResult( + (MPMasterKey)masterKeyBytes, siteNameString, (MPCounterValue)siteCounter, + (MPKeyPurpose)keyPurpose, keyContextString, (MPResultType)resultType, resultParamString, (MPAlgorithmVersion)algorithmVersion ); + (*env)->ReleaseByteArrayElements( env, masterKey, masterKeyBytes, JNI_ABORT ); + (*env)->ReleaseByteArrayElements( env, siteKey, siteKeyBytes, JNI_ABORT ); + (*env)->ReleaseStringUTFChars( env, siteName, siteNameString ); + (*env)->ReleaseStringUTFChars( env, keyContext, keyContextString ); + (*env)->ReleaseStringUTFChars( env, resultParam, resultParamString ); + + if (!siteResultString) + return NULL; + + jstring siteResult = (*env)->NewStringUTF( env, siteResultString ); + mpw_free_string( &siteResultString ); + + return siteResult; +} + +/* native String _siteState(final byte[] masterKey, final byte[] siteKey, final String siteName, final long siteCounter, + final MPKeyPurpose keyPurpose, @Nullable final String keyContext, + final MPResultType resultType, final String resultParam, final Version version) */ +JNIEXPORT jstring JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1siteState(JNIEnv *env, jobject obj, + jbyteArray masterKey, jbyteArray siteKey, jstring siteName, jlong siteCounter, jint keyPurpose, jstring keyContext, + jint resultType, jstring resultParam, jint algorithmVersion) { + + jbyte *masterKeyBytes = (*env)->GetByteArrayElements( env, masterKey, NULL ); + jbyte *siteKeyBytes = (*env)->GetByteArrayElements( env, siteKey, NULL ); + const char *siteNameString = (*env)->GetStringUTFChars( env, siteName, NULL ); + const char *keyContextString = keyContext? (*env)->GetStringUTFChars( env, keyContext, NULL ): NULL; + const char *resultParamString = (*env)->GetStringUTFChars( env, resultParam, NULL ); + const char *siteStateString = mpw_siteState( + (MPMasterKey)masterKeyBytes, siteNameString, (MPCounterValue)siteCounter, + (MPKeyPurpose)keyPurpose, keyContextString, (MPResultType)resultType, resultParamString, (MPAlgorithmVersion)algorithmVersion ); + (*env)->ReleaseByteArrayElements( env, masterKey, masterKeyBytes, JNI_ABORT ); + (*env)->ReleaseByteArrayElements( env, siteKey, siteKeyBytes, JNI_ABORT ); + (*env)->ReleaseStringUTFChars( env, siteName, siteNameString ); + (*env)->ReleaseStringUTFChars( env, keyContext, keyContextString ); + (*env)->ReleaseStringUTFChars( env, resultParam, resultParamString ); + + if (!siteStateString) + return NULL; + + jstring siteState = (*env)->NewStringUTF( env, siteStateString ); + mpw_free_string( &siteStateString ); + + return siteState; } diff --git a/core/c/src/mpw-jni.h b/core/c/src/mpw-jni.h index e9116a74..a2a958a7 100644 --- a/core/c/src/mpw-jni.h +++ b/core/c/src/mpw-jni.h @@ -11,11 +11,35 @@ extern "C" { #define com_lyndir_masterpassword_impl_MPAlgorithmV0_AES_BLOCKSIZE 128L /* * Class: com_lyndir_masterpassword_impl_MPAlgorithmV0 - * Method: _scrypt - * Signature: ([B[BIII[B)I + * Method: _masterKey + * Signature: (Ljava/lang/String;[BI)[B */ -JNIEXPORT jint JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1scrypt - (JNIEnv *, jobject, jbyteArray, jbyteArray, jint, jint, jint, jbyteArray); +JNIEXPORT jbyteArray JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1masterKey + (JNIEnv *, jobject, jstring, jbyteArray, jint); + +/* + * Class: com_lyndir_masterpassword_impl_MPAlgorithmV0 + * Method: _siteKey + * Signature: ([BLjava/lang/String;JILjava/lang/String;I)[B + */ +JNIEXPORT jbyteArray JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1siteKey + (JNIEnv *, jobject, jbyteArray, jstring, jlong, jint, jstring, jint); + +/* + * Class: com_lyndir_masterpassword_impl_MPAlgorithmV0 + * Method: _siteResult + * Signature: ([B[BLjava/lang/String;JILjava/lang/String;ILjava/lang/String;I)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1siteResult + (JNIEnv *, jobject, jbyteArray, jbyteArray, jstring, jlong, jint, jstring, jint, jstring, jint); + +/* + * Class: com_lyndir_masterpassword_impl_MPAlgorithmV0 + * Method: _siteState + * Signature: ([B[BLjava/lang/String;JILjava/lang/String;ILjava/lang/String;I)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1siteState + (JNIEnv *, jobject, jbyteArray, jbyteArray, jstring, jlong, jint, jstring, jint, jstring, jint); #ifdef __cplusplus } diff --git a/core/java/algorithm/build.gradle b/core/java/algorithm/build.gradle index b50b2833..f8c57f06 100644 --- a/core/java/algorithm/build.gradle +++ b/core/java/algorithm/build.gradle @@ -13,7 +13,8 @@ dependencies { api group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.9.5' api group: 'org.jetbrains', name: 'annotations', version: '13.0' - api group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.1' + api group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.2' + api group: 'com.google.code.findbugs', name: 'findbugs-annotations', version: '3.0.1' lib project( path: ':masterpassword-core', configuration: 'default' ) } 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 4c48ccbd..0e87ee8e 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 @@ -43,6 +43,7 @@ public abstract class MPAlgorithm { * @param fullName The name of the user whose identity is described by the key. * @param masterPassword The user's secret that authenticates his access to the identity. */ + @Nullable public abstract byte[] masterKey(String fullName, char[] masterPassword); /** @@ -54,6 +55,7 @@ public abstract class MPAlgorithm { * @param keyPurpose The action that the user aims to undertake with this key. * @param keyContext An action-specific context within which to scope the key. */ + @Nullable public abstract byte[] siteKey(byte[] masterKey, String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose, @Nullable String keyContext); @@ -63,31 +65,11 @@ public abstract class MPAlgorithm { * @param resultType The template to base the site key's encoding on. * @param resultParam A parameter that provides contextual data specific to the type template. */ + @Nullable public abstract String siteResult(byte[] masterKey, byte[] siteKey, String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose, @Nullable String keyContext, MPResultType resultType, @Nullable String resultParam); - /** - * The result for {@link #siteResult(byte[], byte[], String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String)} - * for the case where {@code resultType} is a {@link MPResultTypeClass#Template}. - */ - public abstract String siteResultFromTemplate(byte[] masterKey, byte[] siteKey, - MPResultType resultType, @Nullable String resultParam); - - /** - * The result for {@link #siteResult(byte[], byte[], String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String)} - * for the case where {@code resultType} is a {@link MPResultTypeClass#Stateful}. - */ - public abstract String siteResultFromState(byte[] masterKey, byte[] siteKey, - MPResultType resultType, String resultParam); - - /** - * The result for {@link #siteResult(byte[], byte[], String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String)} - * for the case where {@code resultType} is a {@link MPResultTypeClass#Derive}. - */ - public abstract String siteResultFromDerive(byte[] masterKey, byte[] siteKey, - MPResultType resultType, @Nullable String resultParam); - /** * For {@link MPResultTypeClass#Stateful} {@code resultType}s, generate the {@code resultParam} to use with the * {@link #siteResult(byte[], byte[], String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String)} call @@ -96,6 +78,7 @@ public abstract class MPAlgorithm { * @param resultType The template to base the site key's encoding on. * @param resultParam A parameter that provides contextual data specific to the type template. */ + @Nullable public abstract String siteState(byte[] masterKey, byte[] siteKey, String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose, @Nullable String keyContext, MPResultType resultType, String resultParam); diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmException.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmException.java new file mode 100644 index 00000000..069e1068 --- /dev/null +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmException.java @@ -0,0 +1,33 @@ +//============================================================================== +// 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 MPAlgorithmException extends MPException { + + public MPAlgorithmException(final String message) { + super( message ); + } + + public MPAlgorithmException(final String message, final Throwable cause) { + super( message, cause ); + } +} diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPException.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPException.java new file mode 100644 index 00000000..b9242cec --- /dev/null +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPException.java @@ -0,0 +1,33 @@ +//============================================================================== +// 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, 2018-06-03 + */ +public class MPException extends Exception { + + public MPException(final String message) { + super( message ); + } + + public MPException(final String message, final Throwable cause) { + super( message, cause ); + } +} diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPKeyUnavailableException.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPKeyUnavailableException.java index 2eac4233..2f41b1db 100644 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPKeyUnavailableException.java +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPKeyUnavailableException.java @@ -21,5 +21,13 @@ package com.lyndir.masterpassword; /** * @author lhunath, 2017-09-21 */ -public class MPKeyUnavailableException extends Exception { +public class MPKeyUnavailableException extends MPException { + + public MPKeyUnavailableException(final String message) { + super( message ); + } + + public MPKeyUnavailableException(final String message, final Throwable cause) { + super( message, cause ); + } } 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 030b8cd4..0ef0e20b 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 @@ -66,7 +66,7 @@ public class MPMasterKey { * @throws MPKeyUnavailableException {@link #invalidate()} has been called on this object. */ public byte[] getKeyID(final MPAlgorithm algorithm) - throws MPKeyUnavailableException { + throws MPKeyUnavailableException, MPAlgorithmException { return algorithm.toID( masterKey( algorithm ) ); } @@ -83,28 +83,30 @@ public class MPMasterKey { } private byte[] masterKey(final MPAlgorithm algorithm) - throws MPKeyUnavailableException { + throws MPKeyUnavailableException, MPAlgorithmException { Preconditions.checkArgument( masterPassword.length > 0 ); if (invalidated) - throw new MPKeyUnavailableException(); + throw new MPKeyUnavailableException( "Master key was invalidated." ); - byte[] key = keyByVersion.get( algorithm.version() ); - if (key == null) { + byte[] masterKey = keyByVersion.get( algorithm.version() ); + if (masterKey == null) { logger.trc( "-- mpw_masterKey (algorithm: %s)", algorithm ); logger.trc( "fullName: %s", fullName ); logger.trc( "masterPassword.id: %s", CodeUtils.encodeHex( algorithm.toID( algorithm.toBytes( masterPassword ) ) ) ); - keyByVersion.put( algorithm.version(), key = algorithm.masterKey( fullName, masterPassword ) ); + keyByVersion.put( algorithm.version(), masterKey = algorithm.masterKey( fullName, masterPassword ) ); } + if (masterKey == null) + throw new MPAlgorithmException( "Could not derive master key." ); - return key; + return masterKey; } private byte[] siteKey(final String siteName, final MPAlgorithm algorithm, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose, @Nullable final String keyContext) - throws MPKeyUnavailableException { + throws MPKeyUnavailableException, MPAlgorithmException { Preconditions.checkArgument( !siteName.isEmpty() ); byte[] masterKey = masterKey( algorithm ); @@ -115,7 +117,11 @@ public class MPMasterKey { logger.trc( "keyPurpose: %d (%s)", keyPurpose.toInt(), keyPurpose.getShortName() ); logger.trc( "keyContext: %s", keyContext ); - return algorithm.siteKey( masterKey, siteName, siteCounter, keyPurpose, keyContext ); + byte[] siteKey = algorithm.siteKey( masterKey, siteName, siteCounter, keyPurpose, keyContext ); + if (siteKey == null) + throw new MPAlgorithmException( "Could not derive site key." ); + + return siteKey; } /** @@ -135,7 +141,7 @@ public class MPMasterKey { public String siteResult(final String siteName, final MPAlgorithm algorithm, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose, @Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam) - throws MPKeyUnavailableException { + throws MPKeyUnavailableException, MPAlgorithmException { byte[] masterKey = masterKey( algorithm ); byte[] siteKey = siteKey( siteName, algorithm, siteCounter, keyPurpose, keyContext ); @@ -144,8 +150,12 @@ public class MPMasterKey { logger.trc( "resultType: %d (%s)", resultType.getType(), resultType.getShortName() ); logger.trc( "resultParam: %s", resultParam ); - return algorithm.siteResult( + String siteResult = algorithm.siteResult( masterKey, siteKey, siteName, siteCounter, keyPurpose, keyContext, resultType, resultParam ); + if (siteResult == null) + throw new MPAlgorithmException( "Could not derive site result." ); + + return siteResult; } /** @@ -164,7 +174,7 @@ public class MPMasterKey { public String siteState(final String siteName, final MPAlgorithm algorithm, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose, @Nullable final String keyContext, final MPResultType resultType, final String resultParam) - throws MPKeyUnavailableException { + throws MPKeyUnavailableException, MPAlgorithmException { Preconditions.checkNotNull( resultParam ); Preconditions.checkArgument( !resultParam.isEmpty() ); @@ -176,7 +186,11 @@ public class MPMasterKey { logger.trc( "resultType: %d (%s)", resultType.getType(), resultType.getShortName() ); logger.trc( "resultParam: %d bytes = %s", resultParam.getBytes( algorithm.mpw_charset() ).length, resultParam ); - return algorithm.siteState( + String siteState = algorithm.siteState( masterKey, siteKey, siteName, siteCounter, keyPurpose, keyContext, resultType, resultParam ); + if (siteState == null) + throw new MPAlgorithmException( "Could not derive site state." ); + + return siteState; } } diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV0.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV0.java index cef8080a..eed0cdb9 100644 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV0.java +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV0.java @@ -18,25 +18,16 @@ package com.lyndir.masterpassword.impl; -import static com.lyndir.lhunath.opal.system.util.StringUtils.*; - import com.google.common.base.Charsets; -import com.google.common.base.Preconditions; -import com.google.common.io.BaseEncoding; -import com.google.common.primitives.Bytes; import com.google.common.primitives.UnsignedInteger; -import com.lyndir.lhunath.opal.system.*; +import com.lyndir.lhunath.opal.system.MessageAuthenticationDigests; +import com.lyndir.lhunath.opal.system.MessageDigests; import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.masterpassword.*; import java.nio.*; -import java.nio.charset.Charset; -import java.security.*; -import java.security.spec.AlgorithmParameterSpec; +import java.nio.charset.*; import java.util.Arrays; import javax.annotation.Nullable; -import javax.crypto.*; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; /** @@ -58,245 +49,79 @@ public class MPAlgorithmV0 extends MPAlgorithm { protected final Logger logger = Logger.get( getClass() ); + @Nullable @Override public byte[] masterKey(final String fullName, final char[] masterPassword) { - byte[] fullNameBytes = fullName.getBytes( mpw_charset() ); - byte[] fullNameLengthBytes = toBytes( fullName.length() ); + // Create a memory-safe NUL-terminated UTF-8 C-string byte array variant of masterPassword. + CharsetEncoder encoder = mpw_charset().newEncoder(); + byte[] masterPasswordBytes = new byte[(int) (masterPassword.length * (double) encoder.maxBytesPerChar()) + 1]; + try { + Arrays.fill( masterPasswordBytes, (byte) 0 ); + ByteBuffer masterPasswordBuffer = ByteBuffer.wrap( masterPasswordBytes ); - String keyScope = MPKeyPurpose.Authentication.getScope(); - logger.trc( "keyScope: %s", keyScope ); + CoderResult result = encoder.encode( CharBuffer.wrap( masterPassword ), masterPasswordBuffer, true ); + if (!result.isUnderflow()) + result.throwException(); + result = encoder.flush( masterPasswordBuffer ); + if (!result.isUnderflow()) + result.throwException(); - // Calculate the master key salt. - logger.trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s", - keyScope, CodeUtils.encodeHex( fullNameLengthBytes ), fullName ); - byte[] masterKeySalt = Bytes.concat( keyScope.getBytes( mpw_charset() ), fullNameLengthBytes, fullNameBytes ); - logger.trc( " => masterKeySalt.id: %s", CodeUtils.encodeHex( toID( masterKeySalt ) ) ); - - // Calculate the master key. - logger.trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%d, r=%d, p=%d )", - scrypt_N(), scrypt_r(), scrypt_p() ); - byte[] masterPasswordBytes = toBytes( masterPassword ); - byte[] masterKey = scrypt( masterPasswordBytes, masterKeySalt, mpw_dkLen() ); - Arrays.fill( masterKeySalt, (byte) 0 ); - Arrays.fill( masterPasswordBytes, (byte) 0 ); - if (masterKey == null) - throw new IllegalStateException( "Could not derive master key." ); - logger.trc( " => masterKey.id: %s", CodeUtils.encodeHex( toID( masterKey ) ) ); - - return masterKey; + return _masterKey( fullName, masterPasswordBytes, version().toInt() ); + } + catch (final CharacterCodingException e) { + throw new IllegalStateException( e ); + } + finally { + Arrays.fill( masterPasswordBytes, (byte) 0 ); + } } @Nullable - protected byte[] scrypt(final byte[] secret, final byte[] salt, final int keySize) { - byte[] buffer = new byte[keySize]; - if (_scrypt( secret, salt, scrypt_N(), scrypt_r(), scrypt_p(), buffer ) < 0) - return null; - - return buffer; - } - - protected native int _scrypt(byte[] passwd, byte[] salt, int N, int r, int p, byte[] buf); + protected native byte[] _masterKey(final String fullName, final byte[] masterPassword, final int algorithmVersion); + @Nullable @Override - public byte[] siteKey(final byte[] masterKey, final String siteName, UnsignedInteger siteCounter, + public byte[] siteKey(final byte[] masterKey, final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose, @Nullable final String keyContext) { - String keyScope = keyPurpose.getScope(); - logger.trc( "keyScope: %s", keyScope ); - - // OTP counter value. - if (siteCounter.longValue() == 0) - siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (mpw_otp_window() * 1000)) * mpw_otp_window() ); - - // Calculate the site seed. - byte[] siteNameBytes = siteName.getBytes( mpw_charset() ); - byte[] siteNameLengthBytes = toBytes( siteName.length() ); - byte[] siteCounterBytes = toBytes( siteCounter ); - byte[] keyContextBytes = ((keyContext == null) || keyContext.isEmpty())? null: keyContext.getBytes( mpw_charset() ); - byte[] keyContextLengthBytes = (keyContextBytes == null)? null: toBytes( keyContextBytes.length ); - logger.trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s", - keyScope, CodeUtils.encodeHex( siteNameLengthBytes ), siteName, CodeUtils.encodeHex( siteCounterBytes ), - (keyContextLengthBytes == null)? null: CodeUtils.encodeHex( keyContextLengthBytes ), keyContext ); - - byte[] sitePasswordInfo = Bytes.concat( keyScope.getBytes( mpw_charset() ), siteNameLengthBytes, siteNameBytes, siteCounterBytes ); - if (keyContextBytes != null) - sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes ); - logger.trc( " => siteSalt.id: %s", CodeUtils.encodeHex( toID( sitePasswordInfo ) ) ); - - logger.trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )", CodeUtils.encodeHex( toID( masterKey ) ) ); - byte[] sitePasswordSeedBytes = mpw_digest().of( masterKey, sitePasswordInfo ); - logger.trc( " => siteKey.id: %s", CodeUtils.encodeHex( toID( sitePasswordSeedBytes ) ) ); - - return sitePasswordSeedBytes; + return _siteKey( masterKey, siteName, siteCounter.longValue(), keyPurpose.toInt(), keyContext, version().toInt() ); } + @Nullable + protected native byte[] _siteKey(final byte[] masterKey, final String siteName, final long siteCounter, + final int keyPurpose, @Nullable final String keyContext, final int version); + + @Nullable @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) { - switch (resultType.getTypeClass()) { - case Template: - return siteResultFromTemplate( masterKey, siteKey, resultType, resultParam ); - case Stateful: - return siteResultFromState( masterKey, siteKey, resultType, Preconditions.checkNotNull( resultParam ) ); - case Derive: - return siteResultFromDerive( masterKey, siteKey, resultType, resultParam ); - } - - throw logger.bug( "Unsupported result type class: %s", resultType.getTypeClass() ); + return _siteResult( masterKey, siteKey, siteName, siteCounter.longValue(), + keyPurpose.toInt(), keyContext, resultType.getType(), resultParam, version().toInt() ); } - @Override - public String siteResultFromTemplate(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) { - ByteBuffer buf = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( mpw_byteOrder() ); - Arrays.fill( buf.array(), (byte) ((siteKey[i] > 0)? 0x00: 0xFF) ); - buf.position( 2 ); - buf.put( siteKey[i] ).rewind(); - _siteKey[i] = buf.getInt() & 0xFFFF; - } - - // Determine the template. - Preconditions.checkState( _siteKey.length > 0 ); - int templateIndex = _siteKey[0]; - MPTemplate template = resultType.getTemplateAtRollingIndex( templateIndex ); - logger.trc( "template: %d => %s", templateIndex, template.getTemplateString() ); - - // Encode the password from the seed using the template. - StringBuilder password = new StringBuilder( template.length() ); - for (int i = 0; i < template.length(); ++i) { - int characterIndex = _siteKey[i + 1]; - MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i ); - char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex ); - logger.trc( " - class: %c, index: %5d (0x%2H) => character: %c", - characterClass.getIdentifier(), characterIndex, _siteKey[i + 1], passwordCharacter ); - - password.append( passwordCharacter ); - } - logger.trc( " => password: %s", password ); - - return password.toString(); - } - - @Override - public String siteResultFromState(final byte[] masterKey, final byte[] siteKey, - final MPResultType resultType, final String resultParam) { - - Preconditions.checkNotNull( resultParam ); - Preconditions.checkArgument( !resultParam.isEmpty() ); - - // Base64-decode - byte[] cipherBuf = BaseEncoding.base64().decode( resultParam ); - logger.trc( "b64 decoded: %d bytes = %s", cipherBuf.length, CodeUtils.encodeHex( cipherBuf ) ); - - // Decrypt - byte[] plainBuf = aes_decrypt( cipherBuf, masterKey ); - String plainText = mpw_charset().decode( ByteBuffer.wrap( plainBuf ) ).toString(); - logger.trc( "decrypted -> plainText: %d bytes = %s = %s", plainBuf.length, plainText, CodeUtils.encodeHex( plainBuf ) ); - - return plainText; - } - - protected byte[] aes_encrypt(final byte[] buf, final byte[] key) { - return aes( true, buf, key ); - } - - protected byte[] aes_decrypt(final byte[] buf, final byte[] key) { - return aes( false, buf, key ); - } - - protected byte[] aes(final boolean encrypt, final byte[] buf, final byte[] key) { - int blockByteSize = AES_BLOCKSIZE / Byte.SIZE; - byte[] blockSizedKey = key; - if (blockSizedKey.length != blockByteSize) { - blockSizedKey = new byte[blockByteSize]; - System.arraycopy( key, 0, blockSizedKey, 0, blockByteSize ); - } - - // Encrypt data with key. - try { - Cipher cipher = Cipher.getInstance( AES_TRANSFORMATION ); - AlgorithmParameterSpec parameters = new IvParameterSpec( new byte[blockByteSize] ); - cipher.init( encrypt? Cipher.ENCRYPT_MODE: Cipher.DECRYPT_MODE, new SecretKeySpec( blockSizedKey, "AES" ), parameters ); - - return cipher.doFinal( buf ); - } - catch (final NoSuchAlgorithmException e) { - throw new IllegalStateException( - strf( "Cipher transformation: %s, is not valid or not supported by the provider.", AES_TRANSFORMATION ), e ); - } - catch (final NoSuchPaddingException e) { - throw new IllegalStateException( - strf( "Cipher transformation: %s, padding scheme is not supported by the provider.", AES_TRANSFORMATION ), e ); - } - catch (final BadPaddingException e) { - throw new IllegalArgumentException( - strf( "Message is incorrectly padded for cipher transformation: %s.", AES_TRANSFORMATION ), e ); - } - catch (final IllegalBlockSizeException e) { - throw new IllegalArgumentException( - strf( "Message size is invalid for cipher transformation: %s.", AES_TRANSFORMATION ), e ); - } - catch (final InvalidKeyException e) { - throw new IllegalArgumentException( - strf( "Key is inappropriate for cipher transformation: %s.", AES_TRANSFORMATION ), e ); - } - catch (final InvalidAlgorithmParameterException e) { - throw new IllegalStateException( - strf( "IV is inappropriate for cipher transformation: %s.", AES_TRANSFORMATION ), e ); - } - } - - @Override - public String siteResultFromDerive(final byte[] masterKey, final byte[] siteKey, - final MPResultType resultType, @Nullable final String resultParam) { - - throw new UnsupportedOperationException( "TODO" ); - - // if (resultType == MPResultType.DeriveKey) { - // int resultParamInt = ConversionUtils.toIntegerNN( resultParam ); - // if (resultParamInt == 0) - // resultParamInt = mpw_keySize_max(); - // if ((resultParamInt < mpw_keySize_min()) || (resultParamInt > mpw_keySize_max()) || ((resultParamInt % 8) != 0)) - // throw logger.bug( "Parameter is not a valid key size (should be 128 - 512): %s", resultParam ); - // int keySize = resultParamInt / 8; - // logger.trc( "keySize: %d", keySize ); - // - // // Derive key - // byte[] resultKey = null; // TODO: mpw_kdf_blake2b()( keySize, siteKey, MPSiteKeySize, NULL, 0, 0, NULL ); - // if (resultKey == null) - // throw logger.bug( "Could not derive result key." ); - // - // // Base64-encode - // String b64Key = Preconditions.checkNotNull( BaseEncoding.base64().encode( resultKey ) ); - // logger.trc( "b64 encoded -> key: %s", b64Key ); - // - // return b64Key; - // } else - // throw logger.bug( "Unsupported derived password type: %s", resultType ); - } + @Nullable + protected native String _siteResult(final byte[] masterKey, final byte[] siteKey, final String siteName, final long siteCounter, + final int keyPurpose, @Nullable final String keyContext, + final int resultType, @Nullable final String resultParam, final int algorithmVersion); + @Nullable @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) { - // Encrypt - byte[] cipherBuf = aes_encrypt( resultParam.getBytes( mpw_charset() ), masterKey ); - logger.trc( "cipherBuf: %d bytes = %s", cipherBuf.length, CodeUtils.encodeHex( cipherBuf ) ); - - // Base64-encode - String cipherText = Preconditions.checkNotNull( BaseEncoding.base64().encode( cipherBuf ) ); - logger.trc( "b64 encoded -> cipherText: %s", cipherText ); - - return cipherText; + return _siteState( masterKey, siteKey, siteName, siteCounter.longValue(), + keyPurpose.toInt(), keyContext, resultType.getType(), resultParam, version().toInt() ); } + @Nullable + protected native String _siteState(final byte[] masterKey, final byte[] siteKey, final String siteName, final long siteCounter, + final int keyPurpose, @Nullable final String keyContext, + final int resultType, final String resultParam, final int algorithmVersion); + // Configuration @Override diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV1.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV1.java index dc5a5f9c..f5064eb4 100644 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV1.java +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV1.java @@ -18,10 +18,7 @@ package com.lyndir.masterpassword.impl; -import com.google.common.base.Preconditions; -import com.google.common.primitives.UnsignedBytes; import com.lyndir.masterpassword.*; -import javax.annotation.Nullable; /** @@ -30,32 +27,6 @@ import javax.annotation.Nullable; */ public class MPAlgorithmV1 extends MPAlgorithmV0 { - @Override - public String siteResultFromTemplate(final byte[] masterKey, final byte[] siteKey, - final MPResultType resultType, @Nullable final String resultParam) { - - // Determine the template. - Preconditions.checkState( siteKey.length > 0 ); - int templateIndex = UnsignedBytes.toInt( siteKey[0] ); - MPTemplate template = resultType.getTemplateAtRollingIndex( templateIndex ); - logger.trc( "template: %d => %s", templateIndex, template.getTemplateString() ); - - // Encode the password from the seed using the template. - StringBuilder password = new StringBuilder( template.length() ); - for (int i = 0; i < template.length(); ++i) { - int characterIndex = UnsignedBytes.toInt( siteKey[i + 1] ); - MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i ); - char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex ); - logger.trc( " - class: %c, index: %3d (0x%2H) => character: %c", - characterClass.getIdentifier(), characterIndex, siteKey[i + 1], passwordCharacter ); - - password.append( passwordCharacter ); - } - logger.trc( " => password: %s", password ); - - return password.toString(); - } - // Configuration @Override diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV2.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV2.java index a71cc9ce..35a70333 100644 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV2.java +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV2.java @@ -18,12 +18,7 @@ package com.lyndir.masterpassword.impl; -import com.google.common.primitives.Bytes; -import com.google.common.primitives.UnsignedInteger; -import com.lyndir.lhunath.opal.system.CodeUtils; import com.lyndir.masterpassword.MPAlgorithm; -import com.lyndir.masterpassword.MPKeyPurpose; -import javax.annotation.Nullable; /** @@ -32,39 +27,6 @@ import javax.annotation.Nullable; */ public class MPAlgorithmV2 extends MPAlgorithmV1 { - @Override - 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 ); - - // OTP counter value. - if (siteCounter.longValue() == 0) - siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (mpw_otp_window() * 1000)) * mpw_otp_window() ); - - // Calculate the site seed. - byte[] siteNameBytes = siteName.getBytes( mpw_charset() ); - byte[] siteNameLengthBytes = toBytes( siteNameBytes.length ); - byte[] siteCounterBytes = toBytes( siteCounter ); - byte[] keyContextBytes = ((keyContext == null) || keyContext.isEmpty())? null: keyContext.getBytes( mpw_charset() ); - byte[] keyContextLengthBytes = (keyContextBytes == null)? null: toBytes( keyContextBytes.length ); - logger.trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s", - keyScope, CodeUtils.encodeHex( siteNameLengthBytes ), siteName, CodeUtils.encodeHex( siteCounterBytes ), - (keyContextLengthBytes == null)? null: CodeUtils.encodeHex( keyContextLengthBytes ), keyContext ); - - byte[] sitePasswordInfo = Bytes.concat( keyScope.getBytes( mpw_charset() ), siteNameLengthBytes, siteNameBytes, siteCounterBytes ); - if (keyContextBytes != null) - sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes ); - logger.trc( " => siteSalt.id: %s", CodeUtils.encodeHex( toID( sitePasswordInfo ) ) ); - - logger.trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )", CodeUtils.encodeHex( toID( masterKey ) ) ); - byte[] sitePasswordSeedBytes = mpw_digest().of( masterKey, sitePasswordInfo ); - logger.trc( " => siteKey.id: %s", CodeUtils.encodeHex( toID( sitePasswordSeedBytes ) ) ); - - return sitePasswordSeedBytes; - } - // Configuration @Override diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV3.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV3.java index e6013216..a5e9dafa 100644 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV3.java +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV3.java @@ -18,11 +18,7 @@ package com.lyndir.masterpassword.impl; -import com.google.common.primitives.Bytes; -import com.lyndir.lhunath.opal.system.CodeUtils; import com.lyndir.masterpassword.MPAlgorithm; -import com.lyndir.masterpassword.MPKeyPurpose; -import java.util.Arrays; /** @@ -31,35 +27,6 @@ import java.util.Arrays; */ public class MPAlgorithmV3 extends MPAlgorithmV2 { - @Override - public byte[] masterKey(final String fullName, final char[] masterPassword) { - - byte[] fullNameBytes = fullName.getBytes( mpw_charset() ); - byte[] fullNameLengthBytes = toBytes( fullNameBytes.length ); - - String keyScope = MPKeyPurpose.Authentication.getScope(); - logger.trc( "keyScope: %s", keyScope ); - - // Calculate the master key salt. - logger.trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s", - keyScope, CodeUtils.encodeHex( fullNameLengthBytes ), fullName ); - byte[] masterKeySalt = Bytes.concat( keyScope.getBytes( mpw_charset() ), fullNameLengthBytes, fullNameBytes ); - logger.trc( " => masterKeySalt.id: %s", CodeUtils.encodeHex( toID( masterKeySalt ) ) ); - - // Calculate the master key. - logger.trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%d, r=%d, p=%d )", - scrypt_N(), scrypt_r(), scrypt_p() ); - byte[] masterPasswordBytes = toBytes( masterPassword ); - byte[] masterKey = scrypt( masterPasswordBytes, masterKeySalt, mpw_dkLen() ); - Arrays.fill( masterKeySalt, (byte) 0 ); - Arrays.fill( masterPasswordBytes, (byte) 0 ); - if (masterKey == null) - throw new IllegalStateException( "Could not derive master key." ); - logger.trc( " => masterKey.id: %s", CodeUtils.encodeHex( toID( masterKey ) ) ); - - return masterKey; - } - // Configuration @Override diff --git a/core/java/model/build.gradle b/core/java/model/build.gradle index ca26b226..fe9ccc7f 100644 --- a/core/java/model/build.gradle +++ b/core/java/model/build.gradle @@ -7,17 +7,18 @@ description = 'Master Password Site Model' dependencies { implementation group: 'com.lyndir.lhunath.opal', name: 'opal-system', version: '1.7-p1' - implementation 'com.fasterxml.jackson.core:jackson-core:2.9.5' + implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.9.5' api project( ':masterpassword-algorithm' ) - api 'joda-time:joda-time:2.4' - api 'com.fasterxml.jackson.core:jackson-annotations:2.9.5' - api 'com.fasterxml.jackson.core:jackson-databind:2.9.5' + api group: 'joda-time', name: 'joda-time', version: '2.4' + api group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.9.5' + api group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.5' + api group: 'com.google.code.findbugs', name: 'findbugs-annotations', version: '3.0.1' - compileOnly 'com.google.auto.value:auto-value:1.2' + compileOnly group: 'com.google.auto.value', name: 'auto-value', version: '1.2' apt group: 'com.google.auto.value', name: 'auto-value', version: '1.2' - testCompile 'org.testng:testng:6.8.5' - testCompile 'ch.qos.logback:logback-classic:1.1.2' + testCompile group: 'org.testng', name: 'testng', version: '6.8.5' + testCompile group: 'ch.qos.logback', name: 'logback-classic', version: '1.1.2' } test.useTestNG() diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPIncorrectMasterPasswordException.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPIncorrectMasterPasswordException.java index dd5a52c6..e2dedf92 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPIncorrectMasterPasswordException.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPIncorrectMasterPasswordException.java @@ -18,10 +18,13 @@ package com.lyndir.masterpassword.model; +import com.lyndir.masterpassword.MPException; + + /** * @author lhunath, 14-12-17 */ -public class MPIncorrectMasterPasswordException extends Exception { +public class MPIncorrectMasterPasswordException extends MPException { private final MPUser user; diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPQuestion.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPQuestion.java index e0e6ef40..478a8f7d 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPQuestion.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPQuestion.java @@ -18,8 +18,7 @@ package com.lyndir.masterpassword.model; -import com.lyndir.masterpassword.MPKeyUnavailableException; -import com.lyndir.masterpassword.MPResultType; +import com.lyndir.masterpassword.*; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -43,7 +42,7 @@ public interface MPQuestion extends Comparable { @Nonnull String getAnswer(@Nullable String state) - throws MPKeyUnavailableException; + throws MPKeyUnavailableException, MPAlgorithmException; // -- Relationship 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 77498ad9..feb602ac 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 @@ -54,10 +54,10 @@ public interface MPSite extends Comparable> { void setLoginType(@Nullable MPResultType loginType); String getResult(MPKeyPurpose keyPurpose, @Nullable String keyContext, @Nullable String state) - throws MPKeyUnavailableException; + throws MPKeyUnavailableException, MPAlgorithmException; String getLogin(@Nullable String state) - throws MPKeyUnavailableException; + throws MPKeyUnavailableException, MPAlgorithmException; // - Relations 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 ce0cc73a..5c9f2f31 100644 --- 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 @@ -62,7 +62,7 @@ public interface MPUser> extends Comparable> { * @throws MPIncorrectMasterPasswordException If authentication fails due to the given master password not matching the user's keyID. */ void authenticate(char[] masterPassword) - throws MPIncorrectMasterPasswordException; + throws MPIncorrectMasterPasswordException, MPAlgorithmException; /** * Performs an authentication attempt against the keyID for this user. @@ -74,7 +74,7 @@ public interface MPUser> extends Comparable> { * @throws MPIncorrectMasterPasswordException If authentication fails due to the given master password not matching the user's keyID. */ void authenticate(MPMasterKey masterKey) - throws MPIncorrectMasterPasswordException, MPKeyUnavailableException; + throws MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException; boolean isMasterKeyAvailable(); diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicQuestion.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicQuestion.java index 4d91cb45..9cd90b21 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicQuestion.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicQuestion.java @@ -61,7 +61,7 @@ public abstract class MPBasicQuestion implements MPQuestion { @Nonnull @Override public String getAnswer(@Nullable final String state) - throws MPKeyUnavailableException { + throws MPKeyUnavailableException, MPAlgorithmException { return getSite().getResult( MPKeyPurpose.Recovery, getKeyword(), null, getType(), state ); } diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicSite.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicSite.java index eae0fb34..b979bde6 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicSite.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicSite.java @@ -108,14 +108,14 @@ public abstract class MPBasicSite implements MPSite { @Override public String getResult(final MPKeyPurpose keyPurpose, @Nullable final String keyContext, @Nullable final String state) - throws MPKeyUnavailableException { + throws MPKeyUnavailableException, MPAlgorithmException { return getResult( keyPurpose, keyContext, getCounter(), getResultType(), state ); } protected String getResult(final MPKeyPurpose keyPurpose, @Nullable final String keyContext, @Nullable final UnsignedInteger counter, final MPResultType type, @Nullable final String state) - throws MPKeyUnavailableException { + throws MPKeyUnavailableException, MPAlgorithmException { return getUser().getMasterKey().siteResult( getName(), getAlgorithm(), ifNotNullElse( counter, getAlgorithm().mpw_default_counter() ), @@ -124,7 +124,7 @@ public abstract class MPBasicSite implements MPSite { protected String getState(final MPKeyPurpose keyPurpose, @Nullable final String keyContext, @Nullable final UnsignedInteger counter, final MPResultType type, final String state) - throws MPKeyUnavailableException { + throws MPKeyUnavailableException, MPAlgorithmException { return getUser().getMasterKey().siteState( getName(), getAlgorithm(), ifNotNullElse( counter, getAlgorithm().mpw_default_counter() ), @@ -133,7 +133,7 @@ public abstract class MPBasicSite implements MPSite { @Override public String getLogin(@Nullable final String state) - throws MPKeyUnavailableException { + throws MPKeyUnavailableException, MPAlgorithmException { return getResult( MPKeyPurpose.Identification, null, null, getLoginType(), state ); } diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicUser.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicUser.java index 66949190..d0f65cac 100755 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicUser.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicUser.java @@ -22,6 +22,7 @@ import static com.lyndir.lhunath.opal.system.util.StringUtils.*; import com.google.common.collect.ImmutableList; import com.lyndir.lhunath.opal.system.CodeUtils; +import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.masterpassword.*; import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException; import com.lyndir.masterpassword.model.MPUser; @@ -35,6 +36,8 @@ import javax.annotation.Nullable; */ public abstract class MPBasicUser> implements MPUser { + protected final Logger logger = Logger.get( getClass() ); + private int avatar; private final String fullName; private MPAlgorithm algorithm; @@ -86,7 +89,8 @@ public abstract class MPBasicUser> implements MPUser try { return getMasterKey().getKeyID( getAlgorithm() ); } - catch (final MPKeyUnavailableException ignore) { + catch (final MPException e) { + logger.wrn( e, "While deriving key ID for user: %s", this ); return null; } } @@ -99,7 +103,7 @@ public abstract class MPBasicUser> implements MPUser @Override public void authenticate(final char[] masterPassword) - throws MPIncorrectMasterPasswordException { + throws MPIncorrectMasterPasswordException, MPAlgorithmException { try { authenticate( new MPMasterKey( getFullName(), masterPassword ) ); } @@ -110,7 +114,7 @@ public abstract class MPBasicUser> implements MPUser @Override public void authenticate(final MPMasterKey masterKey) - throws MPIncorrectMasterPasswordException, MPKeyUnavailableException { + throws MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException { if (!masterKey.getFullName().equals( getFullName() )) throw new IllegalArgumentException( "Master key (for " + masterKey.getFullName() + ") is not for this user (" + getFullName() + ")." ); @@ -132,7 +136,7 @@ public abstract class MPBasicUser> implements MPUser public MPMasterKey getMasterKey() throws MPKeyUnavailableException { if (masterKey == null) - throw new MPKeyUnavailableException(); + throw new MPKeyUnavailableException( "Master key was not yet set." ); return masterKey; } diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileQuestion.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileQuestion.java index 8a874eff..2a656253 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileQuestion.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileQuestion.java @@ -44,12 +44,12 @@ public class MPFileQuestion extends MPBasicQuestion { } public String getAnswer() - throws MPKeyUnavailableException { + throws MPKeyUnavailableException, MPAlgorithmException { return getAnswer( state ); } public void setAnswer(final MPResultType type, @Nullable final String answer) - throws MPKeyUnavailableException { + throws MPKeyUnavailableException, MPAlgorithmException { setType( type ); if (answer == null) diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileSite.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileSite.java index b105f6a2..d258d26d 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileSite.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileSite.java @@ -94,19 +94,19 @@ public class MPFileSite extends MPBasicSite { } public String getResult() - throws MPKeyUnavailableException { + throws MPKeyUnavailableException, MPAlgorithmException { return getResult( MPKeyPurpose.Authentication, null ); } public String getResult(final MPKeyPurpose keyPurpose, @Nullable final String keyContext) - throws MPKeyUnavailableException { + throws MPKeyUnavailableException, MPAlgorithmException { return getResult( keyPurpose, keyContext, getResultState() ); } public String getLogin() - throws MPKeyUnavailableException { + throws MPKeyUnavailableException, MPAlgorithmException { return getLogin( getLoginState() ); } @@ -117,7 +117,7 @@ public class MPFileSite extends MPBasicSite { } public void setSitePassword(final MPResultType resultType, @Nullable final String password) - throws MPKeyUnavailableException { + throws MPKeyUnavailableException, MPAlgorithmException { setResultType( resultType ); if (password == null) @@ -133,7 +133,7 @@ public class MPFileSite extends MPBasicSite { } public void setLoginName(@Nonnull final MPResultType loginType, @Nullable final String loginName) - throws MPKeyUnavailableException { + throws MPKeyUnavailableException, MPAlgorithmException { setLoginType( loginType ); if (loginName == null) diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUser.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUser.java index 29751c35..558fa798 100755 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUser.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUser.java @@ -87,6 +87,9 @@ public class MPFileUser extends MPBasicUser { catch (final MPKeyUnavailableException e) { throw new IllegalStateException( "Cannot update algorithm when keyID is set but masterKey is unavailable.", e ); } + catch (final MPAlgorithmException e) { + throw new IllegalStateException( e ); + } } super.setAlgorithm( algorithm ); @@ -135,7 +138,7 @@ public class MPFileUser extends MPBasicUser { @Override public void authenticate(final MPMasterKey masterKey) - throws MPIncorrectMasterPasswordException, MPKeyUnavailableException { + throws MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException { super.authenticate( masterKey ); if (keyID == null) @@ -143,7 +146,7 @@ public class MPFileUser extends MPBasicUser { } void save() - throws MPKeyUnavailableException { + throws MPKeyUnavailableException, MPAlgorithmException { MPFileUserManager.get().save( this, getMasterKey() ); } diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUserManager.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUserManager.java index bcb06431..eb854f8f 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUserManager.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUserManager.java @@ -24,8 +24,7 @@ import com.google.common.base.Charsets; import com.google.common.collect.ImmutableList; import com.google.common.io.CharSink; import com.lyndir.lhunath.opal.system.logging.Logger; -import com.lyndir.masterpassword.MPKeyUnavailableException; -import com.lyndir.masterpassword.MPMasterKey; +import com.lyndir.masterpassword.*; import com.lyndir.masterpassword.model.*; import java.io.*; import java.util.HashMap; @@ -88,7 +87,7 @@ 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) { + catch (final MPKeyUnavailableException | MPIncorrectMasterPasswordException | MPAlgorithmException e) { logger.err( e, "Couldn't authenticate user for: %s", userFile ); } @@ -119,7 +118,7 @@ public class MPFileUserManager extends MPUserManager { * Write the current user state to disk. */ public void save(final MPFileUser user, final MPMasterKey masterKey) - throws MPKeyUnavailableException { + throws MPKeyUnavailableException, MPAlgorithmException { try { MPMarshalFormat format = user.getFormat(); new CharSink() { diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFlatMarshaller.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFlatMarshaller.java index 171bed0f..6ba0263d 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFlatMarshaller.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFlatMarshaller.java @@ -21,6 +21,7 @@ package com.lyndir.masterpassword.model.impl; import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*; import static com.lyndir.lhunath.opal.system.util.StringUtils.*; +import com.lyndir.masterpassword.MPAlgorithmException; import com.lyndir.masterpassword.MPKeyUnavailableException; import com.lyndir.masterpassword.model.MPConstant; import javax.annotation.Nonnull; @@ -38,7 +39,7 @@ public class MPFlatMarshaller implements MPMarshaller { @Nonnull @Override public String marshall(final MPFileUser user) - throws MPKeyUnavailableException, MPMarshalException { + throws MPKeyUnavailableException, MPMarshalException, MPAlgorithmException { StringBuilder content = new StringBuilder(); content.append( "# Master Password site export\n" ); content.append( "# " ).append( user.getContentMode().description() ).append( '\n' ); diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFlatUnmarshaller.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFlatUnmarshaller.java index d1f0f381..34abfad1 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFlatUnmarshaller.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFlatUnmarshaller.java @@ -50,7 +50,7 @@ public class MPFlatUnmarshaller implements MPUnmarshaller { @Nonnull @Override public MPFileUser unmarshall(@Nonnull final File file, @Nullable final char[] masterPassword) - throws IOException, MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException { + throws IOException, MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException { try (Reader reader = new InputStreamReader( new FileInputStream( file ), Charsets.UTF_8 )) { return unmarshall( CharStreams.toString( reader ), masterPassword ); } @@ -59,7 +59,7 @@ public class MPFlatUnmarshaller implements MPUnmarshaller { @Nonnull @Override public MPFileUser unmarshall(@Nonnull final String content, @Nullable final char[] masterPassword) - throws MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException { + throws MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException { MPFileUser user = null; byte[] keyID = null; String fullName = null; diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONFile.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONFile.java index b24fe955..9554dec8 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONFile.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONFile.java @@ -27,6 +27,7 @@ import com.lyndir.lhunath.opal.system.CodeUtils; import com.lyndir.masterpassword.*; import com.lyndir.masterpassword.model.MPConstant; import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.LinkedHashMap; import java.util.Map; import javax.annotation.Nullable; @@ -36,6 +37,7 @@ import org.joda.time.Instant; /** * @author lhunath, 2018-04-27 */ +@SuppressFBWarnings( "URF_UNREAD_FIELD" ) public class MPJSONFile extends MPJSONAnyObject { protected static final ObjectMapper objectMapper = new ObjectMapper(); @@ -46,7 +48,7 @@ public class MPJSONFile extends MPJSONAnyObject { } public MPJSONFile write(final MPFileUser modelUser) - throws MPKeyUnavailableException { + throws MPKeyUnavailableException, MPAlgorithmException { // Section: "export" if (export == null) export = new Export(); @@ -134,7 +136,7 @@ public class MPJSONFile extends MPJSONAnyObject { } public MPFileUser read(@Nullable final char[] masterPassword) - throws MPIncorrectMasterPasswordException, MPKeyUnavailableException { + throws MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException { MPAlgorithm algorithm = ifNotNullElse( user.algorithm, MPAlgorithm.Version.CURRENT ).getAlgorithm(); MPFileUser model = new MPFileUser( user.full_name, CodeUtils.decodeHex( user.key_id ), algorithm, user.avatar, diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONMarshaller.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONMarshaller.java index 35d1af93..e5ce1889 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONMarshaller.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONMarshaller.java @@ -21,6 +21,7 @@ package com.lyndir.masterpassword.model.impl; import static com.lyndir.masterpassword.model.impl.MPJSONFile.*; import com.fasterxml.jackson.core.JsonProcessingException; +import com.lyndir.masterpassword.MPAlgorithmException; import com.lyndir.masterpassword.MPKeyUnavailableException; import javax.annotation.Nonnull; @@ -33,7 +34,7 @@ public class MPJSONMarshaller implements MPMarshaller { @Nonnull @Override public String marshall(final MPFileUser user) - throws MPKeyUnavailableException, MPMarshalException { + throws MPKeyUnavailableException, MPMarshalException, MPAlgorithmException { try { return objectMapper.writeValueAsString( user.getJSON().write( user ) ); diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONUnmarshaller.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONUnmarshaller.java index d38c7e75..3aac982c 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONUnmarshaller.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONUnmarshaller.java @@ -22,6 +22,7 @@ import static com.lyndir.masterpassword.model.impl.MPJSONFile.*; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; +import com.lyndir.masterpassword.MPAlgorithmException; import com.lyndir.masterpassword.MPKeyUnavailableException; import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException; import java.io.File; @@ -38,7 +39,7 @@ public class MPJSONUnmarshaller implements MPUnmarshaller { @Nonnull @Override public MPFileUser unmarshall(@Nonnull final File file, @Nullable final char[] masterPassword) - throws IOException, MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException { + throws IOException, MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException { try { return objectMapper.readValue( file, MPJSONFile.class ).read( masterPassword ); @@ -54,7 +55,7 @@ public class MPJSONUnmarshaller implements MPUnmarshaller { @Nonnull @Override public MPFileUser unmarshall(@Nonnull final String content, @Nullable final char[] masterPassword) - throws MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException { + throws MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException { try { return objectMapper.readValue( content, MPJSONFile.class ).read( masterPassword ); diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPMarshalException.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPMarshalException.java index bf16f60a..4057f1bf 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPMarshalException.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPMarshalException.java @@ -18,10 +18,13 @@ package com.lyndir.masterpassword.model.impl; +import com.lyndir.masterpassword.MPException; + + /** * @author lhunath, 2018-04-26 */ -public class MPMarshalException extends Exception { +public class MPMarshalException extends MPException { public MPMarshalException(final String message) { super( message ); diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPMarshaller.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPMarshaller.java index 8f881d67..5dc03b24 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPMarshaller.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPMarshaller.java @@ -18,6 +18,7 @@ package com.lyndir.masterpassword.model.impl; +import com.lyndir.masterpassword.MPAlgorithmException; import com.lyndir.masterpassword.MPKeyUnavailableException; import javax.annotation.Nonnull; @@ -30,7 +31,7 @@ public interface MPMarshaller { @Nonnull String marshall(MPFileUser user) - throws MPKeyUnavailableException, MPMarshalException; + throws MPKeyUnavailableException, MPMarshalException, MPAlgorithmException; 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/impl/MPUnmarshaller.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPUnmarshaller.java index 033e0b23..6fbfe593 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPUnmarshaller.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPUnmarshaller.java @@ -18,6 +18,7 @@ package com.lyndir.masterpassword.model.impl; +import com.lyndir.masterpassword.MPAlgorithmException; import com.lyndir.masterpassword.MPKeyUnavailableException; import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException; import java.io.File; @@ -33,9 +34,9 @@ public interface MPUnmarshaller { @Nonnull MPFileUser unmarshall(@Nonnull File file, @Nullable char[] masterPassword) - throws IOException, MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException; + throws IOException, MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException; @Nonnull MPFileUser unmarshall(@Nonnull String content, @Nullable char[] masterPassword) - throws MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException; + throws MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException; } diff --git a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/UnlockFrame.java b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/UnlockFrame.java index 670ef3cc..3354f74c 100644 --- a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/UnlockFrame.java +++ b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/UnlockFrame.java @@ -21,6 +21,7 @@ package com.lyndir.masterpassword.gui.view; import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*; import static com.lyndir.lhunath.opal.system.util.StringUtils.*; +import com.lyndir.masterpassword.MPAlgorithmException; import com.lyndir.masterpassword.MPIdenticon; import com.lyndir.masterpassword.gui.Res; import com.lyndir.masterpassword.gui.util.Components; @@ -194,7 +195,7 @@ public class UnlockFrame extends JFrame { dispose(); } ); } - catch (final MPIncorrectMasterPasswordException e) { + catch (final MPIncorrectMasterPasswordException | MPAlgorithmException e) { SwingUtilities.invokeLater( () -> { JOptionPane.showMessageDialog( null, e.getLocalizedMessage(), "Sign In Failed", JOptionPane.ERROR_MESSAGE ); authenticationPanel.reset();