Harmonize C/Java code more, WIP crypt/derive in Java.
This commit is contained in:
parent
473e3ca11f
commit
ad00ceb4ce
@ -180,7 +180,7 @@ static const char *mpw_sitePasswordFromCrypt_v0(
|
|||||||
mpw_free( &plainBytes, bufSize );
|
mpw_free( &plainBytes, bufSize );
|
||||||
if (!plainText)
|
if (!plainText)
|
||||||
err( "AES decryption error: %s\n", strerror( errno ) );
|
err( "AES decryption error: %s\n", strerror( errno ) );
|
||||||
trc( "decrypted -> plainText: %s = %s\n", plainText, mpw_hex( plainText, sizeof( plainText ) ) );
|
trc( "decrypted -> plainText: %s\n", plainText );
|
||||||
|
|
||||||
return plainText;
|
return plainText;
|
||||||
}
|
}
|
||||||
@ -217,7 +217,7 @@ static const char *mpw_sitePasswordFromDerive_v0(
|
|||||||
mpw_free_string( &b64Key );
|
mpw_free_string( &b64Key );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
trc( "b64 encoded -> key.id: %s\n", mpw_id_buf( b64Key, strlen( b64Key ) ) );
|
trc( "b64 encoded -> key: %s\n", b64Key );
|
||||||
mpw_free( &resultKey, keySize );
|
mpw_free( &resultKey, keySize );
|
||||||
|
|
||||||
return b64Key;
|
return b64Key;
|
||||||
@ -248,7 +248,7 @@ static const char *mpw_siteState_v0(
|
|||||||
mpw_free_string( &cipherText );
|
mpw_free_string( &cipherText );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
trc( "b64 encoded -> cipherText: %s = %s\n", cipherText, mpw_hex( cipherText, sizeof( cipherText ) ) );
|
trc( "b64 encoded -> cipherText: %s\n", cipherText );
|
||||||
mpw_free( &cipherBuf, bufSize );
|
mpw_free( &cipherBuf, bufSize );
|
||||||
|
|
||||||
return cipherText;
|
return cipherText;
|
||||||
|
@ -18,12 +18,6 @@
|
|||||||
|
|
||||||
package com.lyndir.masterpassword;
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
import com.google.common.base.Charsets;
|
|
||||||
import com.lyndir.lhunath.opal.system.MessageAuthenticationDigests;
|
|
||||||
import com.lyndir.lhunath.opal.system.MessageDigests;
|
|
||||||
import java.nio.ByteOrder;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author lhunath, 2016-10-29
|
* @author lhunath, 2016-10-29
|
||||||
|
@ -18,9 +18,7 @@
|
|||||||
|
|
||||||
package com.lyndir.masterpassword;
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
@ -31,9 +29,18 @@ import org.jetbrains.annotations.NonNls;
|
|||||||
* @author lhunath, 14-12-02
|
* @author lhunath, 14-12-02
|
||||||
*/
|
*/
|
||||||
public enum MPKeyPurpose {
|
public enum MPKeyPurpose {
|
||||||
Password( "authentication", "Generate a key for authentication.", "com.lyndir.masterpassword" ),
|
/**
|
||||||
Login( "identification", "Generate a name for identification.", "com.lyndir.masterpassword.login" ),
|
* Generate a key for authentication.
|
||||||
Answer( "recovery", "Generate an account recovery token.", "com.lyndir.masterpassword.answer" );
|
*/
|
||||||
|
Authentication( "authentication", "Generate a key for authentication.", "com.lyndir.masterpassword" ),
|
||||||
|
/**
|
||||||
|
* Generate a name for identification.
|
||||||
|
*/
|
||||||
|
Identification( "identification", "Generate a name for identification.", "com.lyndir.masterpassword.login" ),
|
||||||
|
/**
|
||||||
|
* Generate a recovery token.
|
||||||
|
*/
|
||||||
|
Recovery( "recovery", "Generate a recovery token.", "com.lyndir.masterpassword.answer" );
|
||||||
|
|
||||||
static final Logger logger = Logger.get( MPResultType.class );
|
static final Logger logger = Logger.get( MPResultType.class );
|
||||||
|
|
||||||
|
@ -32,11 +32,19 @@ import org.jetbrains.annotations.Contract;
|
|||||||
* @author lhunath
|
* @author lhunath
|
||||||
*/
|
*/
|
||||||
public enum MPResultType {
|
public enum MPResultType {
|
||||||
|
// bit 0-3 | MPResultTypeClass | MPSiteFeature
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pg^VMAUBk5x3p%HP%i4=
|
||||||
|
*/
|
||||||
GeneratedMaximum( "Maximum", "20 characters, contains symbols.", //
|
GeneratedMaximum( "Maximum", "20 characters, contains symbols.", //
|
||||||
ImmutableList.of( new MPTemplate( "anoxxxxxxxxxxxxxxxxx" ), new MPTemplate( "axxxxxxxxxxxxxxxxxno" ) ), //
|
ImmutableList.of( new MPTemplate( "anoxxxxxxxxxxxxxxxxx" ),
|
||||||
MPResultTypeClass.Generated, 0x0 ),
|
new MPTemplate( "axxxxxxxxxxxxxxxxxno" ) ), //
|
||||||
|
MPResultTypeClass.Template, 0x0 ),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BiroYena8:Kixa
|
||||||
|
*/
|
||||||
GeneratedLong( "Long", "Copy-friendly, 14 characters, contains symbols.", //
|
GeneratedLong( "Long", "Copy-friendly, 14 characters, contains symbols.", //
|
||||||
ImmutableList.of( new MPTemplate( "CvcvnoCvcvCvcv" ), new MPTemplate( "CvcvCvcvnoCvcv" ),
|
ImmutableList.of( new MPTemplate( "CvcvnoCvcvCvcv" ), new MPTemplate( "CvcvCvcvnoCvcv" ),
|
||||||
new MPTemplate( "CvcvCvcvCvcvno" ), new MPTemplate( "CvccnoCvcvCvcv" ),
|
new MPTemplate( "CvcvCvcvCvcvno" ), new MPTemplate( "CvccnoCvcvCvcv" ),
|
||||||
@ -49,40 +57,77 @@ public enum MPResultType {
|
|||||||
new MPTemplate( "CvcvCvccnoCvcc" ), new MPTemplate( "CvcvCvccCvccno" ),
|
new MPTemplate( "CvcvCvccnoCvcc" ), new MPTemplate( "CvcvCvccCvccno" ),
|
||||||
new MPTemplate( "CvccnoCvcvCvcc" ), new MPTemplate( "CvccCvcvnoCvcc" ),
|
new MPTemplate( "CvccnoCvcvCvcc" ), new MPTemplate( "CvccCvcvnoCvcc" ),
|
||||||
new MPTemplate( "CvccCvcvCvccno" ) ), //
|
new MPTemplate( "CvccCvcvCvccno" ) ), //
|
||||||
MPResultTypeClass.Generated, 0x1 ),
|
MPResultTypeClass.Template, 0x1 ),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BirSuj0-
|
||||||
|
*/
|
||||||
GeneratedMedium( "Medium", "Copy-friendly, 8 characters, contains symbols.", //
|
GeneratedMedium( "Medium", "Copy-friendly, 8 characters, contains symbols.", //
|
||||||
ImmutableList.of( new MPTemplate( "CvcnoCvc" ), new MPTemplate( "CvcCvcno" ) ), //
|
ImmutableList.of( new MPTemplate( "CvcnoCvc" ),
|
||||||
MPResultTypeClass.Generated, 0x2 ),
|
new MPTemplate( "CvcCvcno" ) ), //
|
||||||
|
MPResultTypeClass.Template, 0x2 ),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pO98MoD0
|
||||||
|
*/
|
||||||
GeneratedBasic( "Basic", "8 characters, no symbols.", //
|
GeneratedBasic( "Basic", "8 characters, no symbols.", //
|
||||||
ImmutableList.of( new MPTemplate( "aaanaaan" ), new MPTemplate( "aannaaan" ), new MPTemplate( "aaannaaa" ) ), //
|
ImmutableList.of( new MPTemplate( "aaanaaan" ),
|
||||||
MPResultTypeClass.Generated, 0x3 ),
|
new MPTemplate( "aannaaan" ),
|
||||||
|
new MPTemplate( "aaannaaa" ) ), //
|
||||||
|
MPResultTypeClass.Template, 0x3 ),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bir8
|
||||||
|
*/
|
||||||
GeneratedShort( "Short", "Copy-friendly, 4 characters, no symbols.", //
|
GeneratedShort( "Short", "Copy-friendly, 4 characters, no symbols.", //
|
||||||
ImmutableList.of( new MPTemplate( "Cvcn" ) ), //
|
ImmutableList.of( new MPTemplate( "Cvcn" ) ), //
|
||||||
MPResultTypeClass.Generated, 0x4 ),
|
MPResultTypeClass.Template, 0x4 ),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 2798
|
||||||
|
*/
|
||||||
GeneratedPIN( "PIN", "4 numbers.", //
|
GeneratedPIN( "PIN", "4 numbers.", //
|
||||||
ImmutableList.of( new MPTemplate( "nnnn" ) ), //
|
ImmutableList.of( new MPTemplate( "nnnn" ) ), //
|
||||||
MPResultTypeClass.Generated, 0x5 ),
|
MPResultTypeClass.Template, 0x5 ),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* birsujano
|
||||||
|
*/
|
||||||
GeneratedName( "Name", "9 letter name.", //
|
GeneratedName( "Name", "9 letter name.", //
|
||||||
ImmutableList.of( new MPTemplate( "cvccvcvcv" ) ), //
|
ImmutableList.of( new MPTemplate( "cvccvcvcv" ) ), //
|
||||||
MPResultTypeClass.Generated, 0xE ),
|
MPResultTypeClass.Template, 0xE ),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bir yennoquce fefi
|
||||||
|
*/
|
||||||
GeneratedPhrase( "Phrase", "20 character sentence.", //
|
GeneratedPhrase( "Phrase", "20 character sentence.", //
|
||||||
ImmutableList.of( new MPTemplate( "cvcc cvc cvccvcv cvc" ), new MPTemplate( "cvc cvccvcvcv cvcv" ),
|
ImmutableList.of( new MPTemplate( "cvcc cvc cvccvcv cvc" ),
|
||||||
|
new MPTemplate( "cvc cvccvcvcv cvcv" ),
|
||||||
new MPTemplate( "cv cvccv cvc cvcvccv" ) ), //
|
new MPTemplate( "cv cvccv cvc cvcvccv" ) ), //
|
||||||
MPResultTypeClass.Generated, 0xF ),
|
MPResultTypeClass.Template, 0xF ),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom saved password.
|
||||||
|
*/
|
||||||
StoredPersonal( "Personal", "AES-encrypted, exportable.", //
|
StoredPersonal( "Personal", "AES-encrypted, exportable.", //
|
||||||
ImmutableList.<MPTemplate>of(), //
|
ImmutableList.<MPTemplate>of(), //
|
||||||
MPResultTypeClass.Stored, 0x0, MPSiteFeature.ExportContent ),
|
MPResultTypeClass.Stateful, 0x0, MPSiteFeature.ExportContent ),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom saved password that should not be exported from the device.
|
||||||
|
*/
|
||||||
StoredDevicePrivate( "Device", "AES-encrypted, not exported.", //
|
StoredDevicePrivate( "Device", "AES-encrypted, not exported.", //
|
||||||
ImmutableList.<MPTemplate>of(), //
|
ImmutableList.<MPTemplate>of(), //
|
||||||
MPResultTypeClass.Stored, 0x1, MPSiteFeature.DevicePrivate );
|
MPResultTypeClass.Stateful, 0x1, MPSiteFeature.DevicePrivate ),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derive a unique binary key.
|
||||||
|
*/
|
||||||
|
DeriveKey( "Key", "Encryption key.", //
|
||||||
|
ImmutableList.<MPTemplate>of(), //
|
||||||
|
MPResultTypeClass.Derive, 0x0, MPSiteFeature.Alternative );
|
||||||
|
|
||||||
|
public static MPResultType DEFAULT = GeneratedLong;
|
||||||
|
|
||||||
static final Logger logger = Logger.get( MPResultType.class );
|
static final Logger logger = Logger.get( MPResultType.class );
|
||||||
|
|
||||||
@ -185,13 +230,14 @@ public enum MPResultType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param mask The mask for which we look up types.
|
* @param mask The type mask for which we look up types.
|
||||||
*
|
*
|
||||||
* @return All types that support the given mask.
|
* @return All types that support the given mask's class & features.
|
||||||
*/
|
*/
|
||||||
public static ImmutableList<MPResultType> forMask(final int mask) {
|
public static ImmutableList<MPResultType> forMask(final int mask) {
|
||||||
|
|
||||||
int typeMask = mask & ~0xF;
|
int typeMask = mask & ~0xF; // Ignore resultType bit 0-3
|
||||||
|
|
||||||
ImmutableList.Builder<MPResultType> types = ImmutableList.builder();
|
ImmutableList.Builder<MPResultType> types = ImmutableList.builder();
|
||||||
for (final MPResultType resultType : values())
|
for (final MPResultType resultType : values())
|
||||||
if (((resultType.getType() & ~0xF) & typeMask) != 0)
|
if (((resultType.getType() & ~0xF) & typeMask) != 0)
|
||||||
|
@ -24,8 +24,20 @@ package com.lyndir.masterpassword;
|
|||||||
* @author lhunath
|
* @author lhunath
|
||||||
*/
|
*/
|
||||||
public enum MPResultTypeClass {
|
public enum MPResultTypeClass {
|
||||||
Generated( 1 << 4 ),
|
// bit 4 - 9
|
||||||
Stored( 1 << 5 );
|
|
||||||
|
/**
|
||||||
|
* Use the site key to generate a password from a template.
|
||||||
|
*/
|
||||||
|
Template( 1 << 4 ),
|
||||||
|
/**
|
||||||
|
* Use the site key to encrypt and decrypt a stateful entity.
|
||||||
|
*/
|
||||||
|
Stateful( 1 << 5 ),
|
||||||
|
/**
|
||||||
|
* Use the site key to derive a site-specific object.
|
||||||
|
*/
|
||||||
|
Derive( 1 << 6 );
|
||||||
|
|
||||||
private final int mask;
|
private final int mask;
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ package com.lyndir.masterpassword;
|
|||||||
* @author lhunath
|
* @author lhunath
|
||||||
*/
|
*/
|
||||||
public enum MPSiteFeature {
|
public enum MPSiteFeature {
|
||||||
|
// bit 10 - 15
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export the key-protected content data.
|
* Export the key-protected content data.
|
||||||
@ -33,7 +34,12 @@ public enum MPSiteFeature {
|
|||||||
/**
|
/**
|
||||||
* Never export content.
|
* Never export content.
|
||||||
*/
|
*/
|
||||||
DevicePrivate( 1 << 11 );
|
DevicePrivate( 1 << 11 ),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Don't use this as the primary authentication result type.
|
||||||
|
*/
|
||||||
|
Alternative( 1 << 12 );
|
||||||
|
|
||||||
MPSiteFeature(final int mask) {
|
MPSiteFeature(final int mask) {
|
||||||
this.mask = mask;
|
this.mask = mask;
|
||||||
|
@ -127,6 +127,12 @@ public abstract class MasterKey {
|
|||||||
public abstract String siteResult(String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
|
public abstract String siteResult(String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
|
||||||
@Nullable String keyContext, MPResultType resultType, @Nullable String resultParam);
|
@Nullable String keyContext, MPResultType resultType, @Nullable String resultParam);
|
||||||
|
|
||||||
|
protected abstract String sitePasswordFromTemplate(byte[] siteKey, MPResultType resultType, @Nullable String resultParam);
|
||||||
|
|
||||||
|
protected abstract String sitePasswordFromCrypt(byte[] siteKey, MPResultType resultType, @Nullable String resultParam);
|
||||||
|
|
||||||
|
protected abstract String sitePasswordFromDerive(byte[] siteKey, MPResultType resultType, @Nullable String resultParam);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encrypt a stateful site token for persistence.
|
* Encrypt a stateful site token for persistence.
|
||||||
*
|
*
|
||||||
|
@ -25,11 +25,13 @@ import com.lambdaworks.crypto.SCrypt;
|
|||||||
import com.lyndir.lhunath.opal.crypto.CryptUtils;
|
import com.lyndir.lhunath.opal.crypto.CryptUtils;
|
||||||
import com.lyndir.lhunath.opal.system.*;
|
import com.lyndir.lhunath.opal.system.*;
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
|
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
|
||||||
import java.nio.*;
|
import java.nio.*;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import javax.crypto.BadPaddingException;
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
import javax.crypto.IllegalBlockSizeException;
|
||||||
|
|
||||||
|
|
||||||
@ -79,7 +81,6 @@ public class MasterKeyV0 extends MasterKey {
|
|||||||
* scrypt: CPU cost parameter.
|
* scrypt: CPU cost parameter.
|
||||||
*/
|
*/
|
||||||
protected static final int scrypt_N = 32768;
|
protected static final int scrypt_N = 32768;
|
||||||
private static final int MP_intLen = 32;
|
|
||||||
|
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
private static final Logger logger = Logger.get( MasterKeyV0.class );
|
private static final Logger logger = Logger.get( MasterKeyV0.class );
|
||||||
@ -108,7 +109,7 @@ public class MasterKeyV0 extends MasterKey {
|
|||||||
logger.trc( "fullName: %s", fullName );
|
logger.trc( "fullName: %s", fullName );
|
||||||
logger.trc( "masterPassword.id: %s", (Object) idForBytes( mpBytesBuf.array() ) );
|
logger.trc( "masterPassword.id: %s", (Object) idForBytes( mpBytesBuf.array() ) );
|
||||||
|
|
||||||
String keyScope = MPKeyPurpose.Password.getScope();
|
String keyScope = MPKeyPurpose.Authentication.getScope();
|
||||||
logger.trc( "keyScope: %s", keyScope );
|
logger.trc( "keyScope: %s", keyScope );
|
||||||
|
|
||||||
// Calculate the master key salt.
|
// Calculate the master key salt.
|
||||||
@ -191,33 +192,49 @@ public class MasterKeyV0 extends MasterKey {
|
|||||||
@Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam) {
|
@Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam) {
|
||||||
|
|
||||||
byte[] siteKey = siteKey( siteName, siteCounter, keyPurpose, keyContext );
|
byte[] siteKey = siteKey( siteName, siteCounter, keyPurpose, keyContext );
|
||||||
int[] sitePasswordSeed = new int[siteKey.length];
|
|
||||||
for (int i = 0; i < siteKey.length; ++i) {
|
|
||||||
ByteBuffer buf = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( ByteOrder.BIG_ENDIAN );
|
|
||||||
Arrays.fill( buf.array(), (byte) ((siteKey[i] > 0)? 0x00: 0xFF) );
|
|
||||||
buf.position( 2 );
|
|
||||||
buf.put( siteKey[i] ).rewind();
|
|
||||||
sitePasswordSeed[i] = buf.getInt() & 0xFFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.trc( "-- mpw_siteResult (algorithm: %u)", getAlgorithmVersion().toInt() );
|
logger.trc( "-- mpw_siteResult (algorithm: %u)", getAlgorithmVersion().toInt() );
|
||||||
logger.trc( "resultType: %d (%s)", resultType.toInt(), resultType.getShortName() );
|
logger.trc( "resultType: %d (%s)", resultType.toInt(), resultType.getShortName() );
|
||||||
logger.trc( "resultParam: %s", resultParam );
|
logger.trc( "resultParam: %s", resultParam );
|
||||||
|
|
||||||
|
switch (resultType.getTypeClass()) {
|
||||||
|
case Template:
|
||||||
|
return sitePasswordFromTemplate( siteKey, resultType, resultParam );
|
||||||
|
case Stateful:
|
||||||
|
return sitePasswordFromCrypt( siteKey, resultType, resultParam );
|
||||||
|
case Derive:
|
||||||
|
return sitePasswordFromDerive( siteKey, resultType, resultParam );
|
||||||
|
}
|
||||||
|
|
||||||
|
throw logger.bug( "Unsupported result type class: %s", resultType.getTypeClass() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String sitePasswordFromTemplate(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.
|
// Determine the template.
|
||||||
Preconditions.checkState( sitePasswordSeed.length > 0 );
|
Preconditions.checkState( _siteKey.length > 0 );
|
||||||
int templateIndex = sitePasswordSeed[0];
|
int templateIndex = _siteKey[0];
|
||||||
MPTemplate template = resultType.getTemplateAtRollingIndex( templateIndex );
|
MPTemplate template = resultType.getTemplateAtRollingIndex( templateIndex );
|
||||||
logger.trc( "template: %u => %s", templateIndex, template.getTemplateString() );
|
logger.trc( "template: %u => %s", templateIndex, template.getTemplateString() );
|
||||||
|
|
||||||
// Encode the password from the seed using the template.
|
// Encode the password from the seed using the template.
|
||||||
StringBuilder password = new StringBuilder( template.length() );
|
StringBuilder password = new StringBuilder( template.length() );
|
||||||
for (int i = 0; i < template.length(); ++i) {
|
for (int i = 0; i < template.length(); ++i) {
|
||||||
int characterIndex = sitePasswordSeed[i + 1];
|
int characterIndex = _siteKey[i + 1];
|
||||||
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
|
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
|
||||||
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
|
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
|
||||||
logger.trc( " - class: %c, index: %5u (0x%02hX) => character: %c",
|
logger.trc( " - class: %c, index: %5u (0x%02hX) => character: %c",
|
||||||
characterClass.getIdentifier(), characterIndex, sitePasswordSeed[i + 1], passwordCharacter );
|
characterClass.getIdentifier(), characterIndex, _siteKey[i + 1], passwordCharacter );
|
||||||
|
|
||||||
password.append( passwordCharacter );
|
password.append( passwordCharacter );
|
||||||
}
|
}
|
||||||
@ -226,6 +243,58 @@ public class MasterKeyV0 extends MasterKey {
|
|||||||
return password.toString();
|
return password.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String sitePasswordFromCrypt(final byte[] siteKey, final MPResultType resultType, @Nullable final String resultParam) {
|
||||||
|
|
||||||
|
Preconditions.checkNotNull( resultParam );
|
||||||
|
Preconditions.checkArgument( !resultParam.isEmpty() );
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Base64-decode
|
||||||
|
byte[] cipherBuf = CryptUtils.decodeBase64( resultParam );
|
||||||
|
logger.trc( "b64 decoded: %zu bytes = %s", cipherBuf.length, CodeUtils.encodeHex( cipherBuf ) );
|
||||||
|
|
||||||
|
// Decrypt
|
||||||
|
byte[] plainBuf = CryptUtils.decrypt( cipherBuf, getKey(), true );
|
||||||
|
String plainText = mpw_charset.decode( ByteBuffer.wrap( plainBuf ) ).toString();
|
||||||
|
logger.trc( "decrypted -> plainText: %s", plainText );
|
||||||
|
|
||||||
|
return plainText;
|
||||||
|
}
|
||||||
|
catch (final BadPaddingException e) {
|
||||||
|
throw Throwables.propagate( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String sitePasswordFromDerive(final byte[] siteKey, final MPResultType resultType, @Nullable final String resultParam) {
|
||||||
|
|
||||||
|
if (resultType == MPResultType.DeriveKey) {
|
||||||
|
Preconditions.checkNotNull( resultParam );
|
||||||
|
Preconditions.checkArgument( !resultParam.isEmpty() );
|
||||||
|
|
||||||
|
int resultParamInt = ConversionUtils.toIntegerNN( resultParam );
|
||||||
|
if ((resultParamInt < 128) || (resultParamInt > 512) || ((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: %u", 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 = Verify.verifyNotNull( CryptUtils.encodeBase64( resultKey ) );
|
||||||
|
logger.trc( "b64 encoded -> key: %s", b64Key );
|
||||||
|
|
||||||
|
return b64Key;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
throw logger.bug( "Unsupported derived password type: %s", resultType );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String siteState(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
|
public String siteState(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
|
||||||
@Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam) {
|
@Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam) {
|
||||||
@ -252,12 +321,12 @@ public class MasterKeyV0 extends MasterKey {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected byte[] bytesForInt(final int number) {
|
protected byte[] bytesForInt(final int number) {
|
||||||
return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( mpw_byteOrder ).putInt( number ).array();
|
return ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( mpw_byteOrder ).putInt( number ).array();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected byte[] bytesForInt(final UnsignedInteger number) {
|
protected byte[] bytesForInt(final UnsignedInteger number) {
|
||||||
return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( mpw_byteOrder ).putInt( number.intValue() ).array();
|
return ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( mpw_byteOrder ).putInt( number.intValue() ).array();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -19,9 +19,7 @@
|
|||||||
package com.lyndir.masterpassword;
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.primitives.Bytes;
|
|
||||||
import com.google.common.primitives.UnsignedInteger;
|
import com.google.common.primitives.UnsignedInteger;
|
||||||
import com.lyndir.lhunath.opal.system.*;
|
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
@ -49,29 +47,26 @@ public class MasterKeyV1 extends MasterKeyV0 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String siteResult(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
|
protected String sitePasswordFromTemplate(final byte[] siteKey, final MPResultType resultType, @Nullable final String resultParam) {
|
||||||
@Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam) {
|
|
||||||
|
|
||||||
byte[] sitePasswordSeed = siteKey( siteName, siteCounter, keyPurpose, keyContext );
|
|
||||||
|
|
||||||
logger.trc( "-- mpw_siteResult (algorithm: %u)", getAlgorithmVersion().toInt() );
|
logger.trc( "-- mpw_siteResult (algorithm: %u)", getAlgorithmVersion().toInt() );
|
||||||
logger.trc( "resultType: %d (%s)", resultType.toInt(), resultType.getShortName() );
|
logger.trc( "resultType: %d (%s)", resultType.toInt(), resultType.getShortName() );
|
||||||
logger.trc( "resultParam: %s", resultParam );
|
logger.trc( "resultParam: %s", resultParam );
|
||||||
|
|
||||||
// Determine the template.
|
// Determine the template.
|
||||||
Preconditions.checkState( sitePasswordSeed.length > 0 );
|
Preconditions.checkState( siteKey.length > 0 );
|
||||||
int templateIndex = sitePasswordSeed[0] & 0xFF; // Mask the integer's sign.
|
int templateIndex = siteKey[0] & 0xFF; // Convert to unsigned int.
|
||||||
MPTemplate template = resultType.getTemplateAtRollingIndex( templateIndex );
|
MPTemplate template = resultType.getTemplateAtRollingIndex( templateIndex );
|
||||||
logger.trc( "template: %u => %s", templateIndex, template.getTemplateString() );
|
logger.trc( "template: %u => %s", templateIndex, template.getTemplateString() );
|
||||||
|
|
||||||
// Encode the password from the seed using the template.
|
// Encode the password from the seed using the template.
|
||||||
StringBuilder password = new StringBuilder( template.length() );
|
StringBuilder password = new StringBuilder( template.length() );
|
||||||
for (int i = 0; i < template.length(); ++i) {
|
for (int i = 0; i < template.length(); ++i) {
|
||||||
int characterIndex = sitePasswordSeed[i + 1] & 0xFF; // Mask the integer's sign.
|
int characterIndex = siteKey[i + 1] & 0xFF; // Convert to unsigned int.
|
||||||
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
|
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
|
||||||
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
|
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
|
||||||
logger.trc( " - class: %c, index: %3u (0x%02hhX) => character: %c",
|
logger.trc( " - class: %c, index: %3u (0x%02hhX) => character: %c",
|
||||||
characterClass.getIdentifier(), characterIndex, sitePasswordSeed[i + 1], passwordCharacter );
|
characterClass.getIdentifier(), characterIndex, siteKey[i + 1], passwordCharacter );
|
||||||
|
|
||||||
password.append( passwordCharacter );
|
password.append( passwordCharacter );
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ public class MasterKeyV3 extends MasterKeyV2 {
|
|||||||
logger.trc( "fullName: %s", fullName );
|
logger.trc( "fullName: %s", fullName );
|
||||||
logger.trc( "masterPassword.id: %s", (Object) idForBytes( mpBytesBuf.array() ) );
|
logger.trc( "masterPassword.id: %s", (Object) idForBytes( mpBytesBuf.array() ) );
|
||||||
|
|
||||||
String keyScope = MPKeyPurpose.Password.getScope();
|
String keyScope = MPKeyPurpose.Authentication.getScope();
|
||||||
logger.trc( "keyScope: %s", keyScope );
|
logger.trc( "keyScope: %s", keyScope );
|
||||||
|
|
||||||
// Calculate the master key salt.
|
// Calculate the master key salt.
|
||||||
|
@ -14,7 +14,6 @@ import org.joda.time.Instant;
|
|||||||
*/
|
*/
|
||||||
public class MPSite {
|
public class MPSite {
|
||||||
|
|
||||||
public static final MPResultType DEFAULT_TYPE = MPResultType.GeneratedLong;
|
|
||||||
public static final UnsignedInteger DEFAULT_COUNTER = UnsignedInteger.valueOf( 1 );
|
public static final UnsignedInteger DEFAULT_COUNTER = UnsignedInteger.valueOf( 1 );
|
||||||
|
|
||||||
private final MPUser user;
|
private final MPUser user;
|
||||||
@ -27,7 +26,7 @@ public class MPSite {
|
|||||||
private String loginName;
|
private String loginName;
|
||||||
|
|
||||||
public MPSite(final MPUser user, final String siteName) {
|
public MPSite(final MPUser user, final String siteName) {
|
||||||
this( user, siteName, DEFAULT_COUNTER, DEFAULT_TYPE );
|
this( user, siteName, DEFAULT_COUNTER, MPResultType.DEFAULT );
|
||||||
}
|
}
|
||||||
|
|
||||||
public MPSite(final MPUser user, final String siteName, final UnsignedInteger siteCounter, final MPResultType resultType) {
|
public MPSite(final MPUser user, final String siteName, final UnsignedInteger siteCounter, final MPResultType resultType) {
|
||||||
@ -53,7 +52,7 @@ public class MPSite {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String resultFor(final MasterKey masterKey) {
|
public String resultFor(final MasterKey masterKey) {
|
||||||
return resultFor( masterKey, MPKeyPurpose.Password, null );
|
return resultFor( masterKey, MPKeyPurpose.Authentication, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
public String resultFor(final MasterKey masterKey, final MPKeyPurpose purpose, @Nullable final String context) {
|
public String resultFor(final MasterKey masterKey, final MPKeyPurpose purpose, @Nullable final String context) {
|
||||||
|
@ -58,7 +58,7 @@ public class MPSiteUnmarshaller {
|
|||||||
String fullName = null;
|
String fullName = null;
|
||||||
int mpVersion = 0, importFormat = 0, avatar = 0;
|
int mpVersion = 0, importFormat = 0, avatar = 0;
|
||||||
boolean clearContent = false, headerStarted = false;
|
boolean clearContent = false, headerStarted = false;
|
||||||
MPResultType defaultType = MPResultType.GeneratedLong;
|
MPResultType defaultType = MPResultType.DEFAULT;
|
||||||
MPSiteUnmarshaller marshaller = null;
|
MPSiteUnmarshaller marshaller = null;
|
||||||
final ImmutableList.Builder<MPSite> sites = ImmutableList.builder();
|
final ImmutableList.Builder<MPSite> sites = ImmutableList.builder();
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ public class MPUser implements Comparable<MPUser> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public MPUser(final String fullName, @Nullable final byte[] keyID) {
|
public MPUser(final String fullName, @Nullable final byte[] keyID) {
|
||||||
this( fullName, keyID, MasterKey.Version.CURRENT, 0, MPResultType.GeneratedLong, new DateTime() );
|
this( fullName, keyID, MasterKey.Version.CURRENT, 0, MPResultType.DEFAULT, new DateTime() );
|
||||||
}
|
}
|
||||||
|
|
||||||
public MPUser(final String fullName, @Nullable final byte[] keyID, final MasterKey.Version algorithmVersion, final int avatar,
|
public MPUser(final String fullName, @Nullable final byte[] keyID, final MasterKey.Version algorithmVersion, final int avatar,
|
||||||
|
@ -54,7 +54,7 @@ public class EmergencyActivity extends Activity {
|
|||||||
|
|
||||||
private final Preferences preferences = Preferences.get( this );
|
private final Preferences preferences = Preferences.get( this );
|
||||||
private final ListeningExecutorService executor = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor() );
|
private final ListeningExecutorService executor = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor() );
|
||||||
private final ImmutableList<MPResultType> allResultTypes = ImmutableList.copyOf( MPResultType.forClass( MPResultTypeClass.Generated ) );
|
private final ImmutableList<MPResultType> allResultTypes = ImmutableList.copyOf( MPResultType.forClass( MPResultTypeClass.Template ) );
|
||||||
private final ImmutableList<MasterKey.Version> allVersions = ImmutableList.copyOf( MasterKey.Version.values() );
|
private final ImmutableList<MasterKey.Version> allVersions = ImmutableList.copyOf( MasterKey.Version.values() );
|
||||||
|
|
||||||
private ListenableFuture<MasterKey> masterKeyFuture;
|
private ListenableFuture<MasterKey> masterKeyFuture;
|
||||||
@ -332,7 +332,7 @@ public class EmergencyActivity extends Activity {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
sitePassword = masterKeyFuture.get().siteResult( siteName, counter, MPKeyPurpose.Password, null, type, null );
|
sitePassword = masterKeyFuture.get().siteResult( siteName, counter, MPKeyPurpose.Authentication, null, type, null );
|
||||||
|
|
||||||
runOnUiThread( new Runnable() {
|
runOnUiThread( new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -148,7 +148,7 @@ public final class Preferences {
|
|||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public MPResultType getDefaultResultType() {
|
public MPResultType getDefaultResultType() {
|
||||||
return MPResultType.values()[prefs().getInt( PREF_RESULT_TYPE, MPResultType.GeneratedLong.ordinal() )];
|
return MPResultType.values()[prefs().getInt( PREF_RESULT_TYPE, MPResultType.DEFAULT.ordinal() )];
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean setDefaultVersion(final MasterKey.Version value) {
|
public boolean setDefaultVersion(final MasterKey.Version value) {
|
||||||
|
@ -118,7 +118,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
|||||||
|
|
||||||
// Site Type & Counter
|
// Site Type & Counter
|
||||||
siteCounterModel = new UnsignedIntegerModel( UnsignedInteger.ONE, UnsignedInteger.ONE );
|
siteCounterModel = new UnsignedIntegerModel( UnsignedInteger.ONE, UnsignedInteger.ONE );
|
||||||
MPResultType[] types = Iterables.toArray( MPResultType.forClass( MPResultTypeClass.Generated ), MPResultType.class );
|
MPResultType[] types = Iterables.toArray( MPResultType.forClass( MPResultTypeClass.Template ), MPResultType.class );
|
||||||
JComponent siteSettings = Components.boxLayout( BoxLayout.LINE_AXIS, //
|
JComponent siteSettings = Components.boxLayout( BoxLayout.LINE_AXIS, //
|
||||||
resultTypeField = Components.comboBox( types ), //
|
resultTypeField = Components.comboBox( types ), //
|
||||||
Components.stud(), //
|
Components.stud(), //
|
||||||
@ -127,7 +127,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
|||||||
siteCounterField = Components.spinner( siteCounterModel ) );
|
siteCounterField = Components.spinner( siteCounterModel ) );
|
||||||
sitePanel.add( siteSettings );
|
sitePanel.add( siteSettings );
|
||||||
resultTypeField.setFont( Res.valueFont().deriveFont( 12f ) );
|
resultTypeField.setFont( Res.valueFont().deriveFont( 12f ) );
|
||||||
resultTypeField.setSelectedItem( MPResultType.GeneratedLong );
|
resultTypeField.setSelectedItem( MPResultType.DEFAULT );
|
||||||
resultTypeField.addItemListener( new ItemListener() {
|
resultTypeField.addItemListener( new ItemListener() {
|
||||||
@Override
|
@Override
|
||||||
public void itemStateChanged(final ItemEvent e) {
|
public void itemStateChanged(final ItemEvent e) {
|
||||||
@ -240,7 +240,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
|||||||
public String call()
|
public String call()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
return user.getKey( site.getAlgorithmVersion() )
|
return user.getKey( site.getAlgorithmVersion() )
|
||||||
.siteResult( site.getSiteName(), site.getSiteCounter(), MPKeyPurpose.Password, null, site.getResultType(), null );
|
.siteResult( site.getSiteName(), site.getSiteCounter(), MPKeyPurpose.Authentication, null, site.getResultType(), null );
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
Futures.addCallback( passwordFuture, new FutureCallback<String>() {
|
Futures.addCallback( passwordFuture, new FutureCallback<String>() {
|
||||||
|
Loading…
Reference in New Issue
Block a user