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

@@ -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() );
// Create a memory-safe NUL-terminated UTF-8 C-string byte array variant of masterPassword.
CharsetEncoder encoder = mpw_charset().newEncoder();
byte[] masterPasswordBytes = new byte[(int) (masterPassword.length * (double) encoder.maxBytesPerChar()) + 1];
try {
Arrays.fill( masterPasswordBytes, (byte) 0 );
ByteBuffer masterPasswordBuffer = ByteBuffer.wrap( masterPasswordBytes );
String keyScope = MPKeyPurpose.Authentication.getScope();
logger.trc( "keyScope: %s", keyScope );
CoderResult result = encoder.encode( CharBuffer.wrap( masterPassword ), masterPasswordBuffer, true );
if (!result.isUnderflow())
result.throwException();
result = encoder.flush( masterPasswordBuffer );
if (!result.isUnderflow())
result.throwException();
// Calculate the master key salt.
logger.trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s",
keyScope, CodeUtils.encodeHex( fullNameLengthBytes ), fullName );
byte[] masterKeySalt = Bytes.concat( keyScope.getBytes( mpw_charset() ), fullNameLengthBytes, fullNameBytes );
logger.trc( " => masterKeySalt.id: %s", CodeUtils.encodeHex( toID( masterKeySalt ) ) );
// Calculate the master key.
logger.trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%d, r=%d, p=%d )",
scrypt_N(), scrypt_r(), scrypt_p() );
byte[] masterPasswordBytes = toBytes( masterPassword );
byte[] masterKey = scrypt( masterPasswordBytes, masterKeySalt, mpw_dkLen() );
Arrays.fill( masterKeySalt, (byte) 0 );
Arrays.fill( masterPasswordBytes, (byte) 0 );
if (masterKey == null)
throw new IllegalStateException( "Could not derive master key." );
logger.trc( " => masterKey.id: %s", CodeUtils.encodeHex( toID( masterKey ) ) );
return masterKey;
return _masterKey( fullName, masterPasswordBytes, version().toInt() );
}
catch (final CharacterCodingException e) {
throw new IllegalStateException( e );
}
finally {
Arrays.fill( masterPasswordBytes, (byte) 0 );
}
}
@Nullable
protected byte[] scrypt(final byte[] secret, final byte[] salt, final int keySize) {
byte[] buffer = new byte[keySize];
if (_scrypt( secret, salt, scrypt_N(), scrypt_r(), scrypt_p(), buffer ) < 0)
return null;
return buffer;
}
protected native int _scrypt(byte[] passwd, byte[] salt, int N, int r, int p, byte[] buf);
protected native byte[] _masterKey(final String fullName, final byte[] masterPassword, final int algorithmVersion);
@Nullable
@Override
public byte[] siteKey(final byte[] masterKey, final String siteName, UnsignedInteger siteCounter,
public byte[] siteKey(final byte[] masterKey, final String siteName, final UnsignedInteger siteCounter,
final MPKeyPurpose keyPurpose, @Nullable final String keyContext) {
String keyScope = keyPurpose.getScope();
logger.trc( "keyScope: %s", keyScope );
// OTP counter value.
if (siteCounter.longValue() == 0)
siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (mpw_otp_window() * 1000)) * mpw_otp_window() );
// Calculate the site seed.
byte[] siteNameBytes = siteName.getBytes( mpw_charset() );
byte[] siteNameLengthBytes = toBytes( siteName.length() );
byte[] siteCounterBytes = toBytes( siteCounter );
byte[] keyContextBytes = ((keyContext == null) || keyContext.isEmpty())? null: keyContext.getBytes( mpw_charset() );
byte[] keyContextLengthBytes = (keyContextBytes == null)? null: toBytes( keyContextBytes.length );
logger.trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s",
keyScope, CodeUtils.encodeHex( siteNameLengthBytes ), siteName, CodeUtils.encodeHex( siteCounterBytes ),
(keyContextLengthBytes == null)? null: CodeUtils.encodeHex( keyContextLengthBytes ), keyContext );
byte[] sitePasswordInfo = Bytes.concat( keyScope.getBytes( mpw_charset() ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
if (keyContextBytes != null)
sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes );
logger.trc( " => siteSalt.id: %s", CodeUtils.encodeHex( toID( sitePasswordInfo ) ) );
logger.trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )", CodeUtils.encodeHex( toID( masterKey ) ) );
byte[] sitePasswordSeedBytes = mpw_digest().of( masterKey, sitePasswordInfo );
logger.trc( " => siteKey.id: %s", CodeUtils.encodeHex( toID( sitePasswordSeedBytes ) ) );
return sitePasswordSeedBytes;
return _siteKey( masterKey, siteName, siteCounter.longValue(), keyPurpose.toInt(), keyContext, version().toInt() );
}
@Nullable
protected native byte[] _siteKey(final byte[] masterKey, final String siteName, final long siteCounter,
final int keyPurpose, @Nullable final String keyContext, final int version);
@Nullable
@Override
public String siteResult(final byte[] masterKey, final byte[] siteKey, final String siteName, final UnsignedInteger siteCounter,
final MPKeyPurpose keyPurpose, @Nullable final String keyContext,
final MPResultType resultType, @Nullable final String resultParam) {
switch (resultType.getTypeClass()) {
case Template:
return siteResultFromTemplate( masterKey, siteKey, resultType, resultParam );
case Stateful:
return siteResultFromState( masterKey, siteKey, resultType, Preconditions.checkNotNull( resultParam ) );
case Derive:
return siteResultFromDerive( masterKey, siteKey, resultType, resultParam );
}
throw logger.bug( "Unsupported result type class: %s", resultType.getTypeClass() );
return _siteResult( masterKey, siteKey, siteName, siteCounter.longValue(),
keyPurpose.toInt(), keyContext, resultType.getType(), resultParam, version().toInt() );
}
@Override
public String siteResultFromTemplate(final byte[] masterKey, final byte[] siteKey,
final MPResultType resultType, @Nullable final String resultParam) {
int[] _siteKey = new int[siteKey.length];
for (int i = 0; i < siteKey.length; ++i) {
ByteBuffer buf = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( mpw_byteOrder() );
Arrays.fill( buf.array(), (byte) ((siteKey[i] > 0)? 0x00: 0xFF) );
buf.position( 2 );
buf.put( siteKey[i] ).rewind();
_siteKey[i] = buf.getInt() & 0xFFFF;
}
// Determine the template.
Preconditions.checkState( _siteKey.length > 0 );
int templateIndex = _siteKey[0];
MPTemplate template = resultType.getTemplateAtRollingIndex( templateIndex );
logger.trc( "template: %d => %s", templateIndex, template.getTemplateString() );
// Encode the password from the seed using the template.
StringBuilder password = new StringBuilder( template.length() );
for (int i = 0; i < template.length(); ++i) {
int characterIndex = _siteKey[i + 1];
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
logger.trc( " - class: %c, index: %5d (0x%2H) => character: %c",
characterClass.getIdentifier(), characterIndex, _siteKey[i + 1], passwordCharacter );
password.append( passwordCharacter );
}
logger.trc( " => password: %s", password );
return password.toString();
}
@Override
public String siteResultFromState(final byte[] masterKey, final byte[] siteKey,
final MPResultType resultType, final String resultParam) {
Preconditions.checkNotNull( resultParam );
Preconditions.checkArgument( !resultParam.isEmpty() );
// Base64-decode
byte[] cipherBuf = BaseEncoding.base64().decode( resultParam );
logger.trc( "b64 decoded: %d bytes = %s", cipherBuf.length, CodeUtils.encodeHex( cipherBuf ) );
// Decrypt
byte[] plainBuf = aes_decrypt( cipherBuf, masterKey );
String plainText = mpw_charset().decode( ByteBuffer.wrap( plainBuf ) ).toString();
logger.trc( "decrypted -> plainText: %d bytes = %s = %s", plainBuf.length, plainText, CodeUtils.encodeHex( plainBuf ) );
return plainText;
}
protected byte[] aes_encrypt(final byte[] buf, final byte[] key) {
return aes( true, buf, key );
}
protected byte[] aes_decrypt(final byte[] buf, final byte[] key) {
return aes( false, buf, key );
}
protected byte[] aes(final boolean encrypt, final byte[] buf, final byte[] key) {
int blockByteSize = AES_BLOCKSIZE / Byte.SIZE;
byte[] blockSizedKey = key;
if (blockSizedKey.length != blockByteSize) {
blockSizedKey = new byte[blockByteSize];
System.arraycopy( key, 0, blockSizedKey, 0, blockByteSize );
}
// Encrypt data with key.
try {
Cipher cipher = Cipher.getInstance( AES_TRANSFORMATION );
AlgorithmParameterSpec parameters = new IvParameterSpec( new byte[blockByteSize] );
cipher.init( encrypt? Cipher.ENCRYPT_MODE: Cipher.DECRYPT_MODE, new SecretKeySpec( blockSizedKey, "AES" ), parameters );
return cipher.doFinal( buf );
}
catch (final NoSuchAlgorithmException e) {
throw new IllegalStateException(
strf( "Cipher transformation: %s, is not valid or not supported by the provider.", AES_TRANSFORMATION ), e );
}
catch (final NoSuchPaddingException e) {
throw new IllegalStateException(
strf( "Cipher transformation: %s, padding scheme is not supported by the provider.", AES_TRANSFORMATION ), e );
}
catch (final BadPaddingException e) {
throw new IllegalArgumentException(
strf( "Message is incorrectly padded for cipher transformation: %s.", AES_TRANSFORMATION ), e );
}
catch (final IllegalBlockSizeException e) {
throw new IllegalArgumentException(
strf( "Message size is invalid for cipher transformation: %s.", AES_TRANSFORMATION ), e );
}
catch (final InvalidKeyException e) {
throw new IllegalArgumentException(
strf( "Key is inappropriate for cipher transformation: %s.", AES_TRANSFORMATION ), e );
}
catch (final InvalidAlgorithmParameterException e) {
throw new IllegalStateException(
strf( "IV is inappropriate for cipher transformation: %s.", AES_TRANSFORMATION ), e );
}
}
@Override
public String siteResultFromDerive(final byte[] masterKey, final byte[] siteKey,
final MPResultType resultType, @Nullable final String resultParam) {
throw new UnsupportedOperationException( "TODO" );
// if (resultType == MPResultType.DeriveKey) {
// int resultParamInt = ConversionUtils.toIntegerNN( resultParam );
// if (resultParamInt == 0)
// resultParamInt = mpw_keySize_max();
// if ((resultParamInt < mpw_keySize_min()) || (resultParamInt > mpw_keySize_max()) || ((resultParamInt % 8) != 0))
// throw logger.bug( "Parameter is not a valid key size (should be 128 - 512): %s", resultParam );
// int keySize = resultParamInt / 8;
// logger.trc( "keySize: %d", keySize );
//
// // Derive key
// byte[] resultKey = null; // TODO: mpw_kdf_blake2b()( keySize, siteKey, MPSiteKeySize, NULL, 0, 0, NULL );
// if (resultKey == null)
// throw logger.bug( "Could not derive result key." );
//
// // Base64-encode
// String b64Key = Preconditions.checkNotNull( BaseEncoding.base64().encode( resultKey ) );
// logger.trc( "b64 encoded -> key: %s", b64Key );
//
// return b64Key;
// } else
// throw logger.bug( "Unsupported derived password type: %s", resultType );
}
@Nullable
protected native String _siteResult(final byte[] masterKey, final byte[] siteKey, final String siteName, final long siteCounter,
final int keyPurpose, @Nullable final String keyContext,
final int resultType, @Nullable final String resultParam, final int algorithmVersion);
@Nullable
@Override
public String siteState(final byte[] masterKey, final byte[] siteKey, final String siteName, final UnsignedInteger siteCounter,
final MPKeyPurpose keyPurpose, @Nullable final String keyContext,
final MPResultType resultType, final String resultParam) {
// Encrypt
byte[] cipherBuf = aes_encrypt( resultParam.getBytes( mpw_charset() ), masterKey );
logger.trc( "cipherBuf: %d bytes = %s", cipherBuf.length, CodeUtils.encodeHex( cipherBuf ) );
// Base64-encode
String cipherText = Preconditions.checkNotNull( BaseEncoding.base64().encode( cipherBuf ) );
logger.trc( "b64 encoded -> cipherText: %s", cipherText );
return cipherText;
return _siteState( masterKey, siteKey, siteName, siteCounter.longValue(),
keyPurpose.toInt(), keyContext, resultType.getType(), resultParam, version().toInt() );
}
@Nullable
protected native String _siteState(final byte[] masterKey, final byte[] siteKey, final String siteName, final long siteCounter,
final int keyPurpose, @Nullable final String keyContext,
final int resultType, final String resultParam, final int algorithmVersion);
// Configuration
@Override

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