Fully replace Java mpw algorithm implementation with proxy to standard C implementation.
This commit is contained in:
parent
6957d46ef9
commit
882de547d0
@ -1,29 +1,113 @@
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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' )
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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() );
|
||||
|
||||
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 );
|
||||
// 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 );
|
||||
if (masterKey == null)
|
||||
throw new IllegalStateException( "Could not derive master key." );
|
||||
logger.trc( " => masterKey.id: %s", CodeUtils.encodeHex( toID( masterKey ) ) );
|
||||
ByteBuffer masterPasswordBuffer = ByteBuffer.wrap( masterPasswordBytes );
|
||||
|
||||
return masterKey;
|
||||
CoderResult result = encoder.encode( CharBuffer.wrap( masterPassword ), masterPasswordBuffer, true );
|
||||
if (!result.isUnderflow())
|
||||
result.throwException();
|
||||
result = encoder.flush( masterPasswordBuffer );
|
||||
if (!result.isUnderflow())
|
||||
result.throwException();
|
||||
|
||||
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 );
|
||||
return _siteResult( masterKey, siteKey, siteName, siteCounter.longValue(),
|
||||
keyPurpose.toInt(), keyContext, resultType.getType(), resultParam, version().toInt() );
|
||||
}
|
||||
|
||||
throw logger.bug( "Unsupported result type class: %s", resultType.getTypeClass() );
|
||||
}
|
||||
|
||||
@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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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<MPQuestion> {
|
||||
|
||||
@Nonnull
|
||||
String getAnswer(@Nullable String state)
|
||||
throws MPKeyUnavailableException;
|
||||
throws MPKeyUnavailableException, MPAlgorithmException;
|
||||
|
||||
// -- Relationship
|
||||
|
||||
|
@ -54,10 +54,10 @@ public interface MPSite<Q extends MPQuestion> extends Comparable<MPSite<?>> {
|
||||
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
|
||||
|
||||
|
@ -62,7 +62,7 @@ public interface MPUser<S extends MPSite<?>> extends Comparable<MPUser<?>> {
|
||||
* @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<S extends MPSite<?>> extends Comparable<MPUser<?>> {
|
||||
* @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();
|
||||
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -108,14 +108,14 @@ public abstract class MPBasicSite<Q extends MPQuestion> implements MPSite<Q> {
|
||||
|
||||
@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<Q extends MPQuestion> implements MPSite<Q> {
|
||||
|
||||
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<Q extends MPQuestion> implements MPSite<Q> {
|
||||
|
||||
@Override
|
||||
public String getLogin(@Nullable final String state)
|
||||
throws MPKeyUnavailableException {
|
||||
throws MPKeyUnavailableException, MPAlgorithmException {
|
||||
|
||||
return getResult( MPKeyPurpose.Identification, null, null, getLoginType(), state );
|
||||
}
|
||||
|
@ -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<S extends MPBasicSite<?>> implements MPUser<S> {
|
||||
|
||||
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<S extends MPBasicSite<?>> implements MPUser<S>
|
||||
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<S extends MPBasicSite<?>> implements MPUser<S>
|
||||
|
||||
@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<S extends MPBasicSite<?>> implements MPUser<S>
|
||||
|
||||
@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<S extends MPBasicSite<?>> implements MPUser<S>
|
||||
public MPMasterKey getMasterKey()
|
||||
throws MPKeyUnavailableException {
|
||||
if (masterKey == null)
|
||||
throw new MPKeyUnavailableException();
|
||||
throw new MPKeyUnavailableException( "Master key was not yet set." );
|
||||
|
||||
return masterKey;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -94,19 +94,19 @@ public class MPFileSite extends MPBasicSite<MPFileQuestion> {
|
||||
}
|
||||
|
||||
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<MPFileQuestion> {
|
||||
}
|
||||
|
||||
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<MPFileQuestion> {
|
||||
}
|
||||
|
||||
public void setLoginName(@Nonnull final MPResultType loginType, @Nullable final String loginName)
|
||||
throws MPKeyUnavailableException {
|
||||
throws MPKeyUnavailableException, MPAlgorithmException {
|
||||
setLoginType( loginType );
|
||||
|
||||
if (loginName == null)
|
||||
|
@ -87,6 +87,9 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
|
||||
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<MPFileSite> {
|
||||
|
||||
@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<MPFileSite> {
|
||||
}
|
||||
|
||||
void save()
|
||||
throws MPKeyUnavailableException {
|
||||
throws MPKeyUnavailableException, MPAlgorithmException {
|
||||
MPFileUserManager.get().save( this, getMasterKey() );
|
||||
}
|
||||
|
||||
|
@ -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<MPFileUser> {
|
||||
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<MPFileUser> {
|
||||
* 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() {
|
||||
|
@ -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' );
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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 ) );
|
||||
|
@ -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 );
|
||||
|
@ -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 );
|
||||
|
@ -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 ),
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user