2
0

Fully replace Java mpw algorithm implementation with proxy to standard C implementation.

This commit is contained in:
Maarten Billemont 2018-06-04 01:43:46 -04:00
parent 6957d46ef9
commit 882de547d0
33 changed files with 356 additions and 431 deletions

View File

@ -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;
}

View File

@ -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
}

View File

@ -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' )
}

View File

@ -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);

View File

@ -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 );
}
}

View File

@ -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 );
}
}

View File

@ -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 );
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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 );
}

View File

@ -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 );
}

View File

@ -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;
}

View File

@ -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)

View File

@ -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)

View File

@ -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() );
}

View File

@ -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() {

View File

@ -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' );

View File

@ -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;

View File

@ -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,

View File

@ -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 ) );

View File

@ -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 );

View File

@ -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 );

View File

@ -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 ),

View File

@ -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;
}

View File

@ -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();