Log fixes, test improvements and some refactoring.
This commit is contained in:
parent
888338e107
commit
cecaf1b5cc
@ -5,10 +5,10 @@ plugins {
|
|||||||
description = 'Master Password Algorithm Implementation'
|
description = 'Master Password Algorithm Implementation'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile (group: 'com.lyndir.lhunath.opal', name: 'opal-system', version: '1.6-p10') {
|
compile (group: 'com.lyndir.lhunath.opal', name: 'opal-system', version: '1.6-p11') {
|
||||||
exclude( module: 'joda-time' )
|
exclude( module: 'joda-time' )
|
||||||
}
|
}
|
||||||
compile group: 'com.lyndir.lhunath.opal', name: 'opal-crypto', version: '1.6-p10'
|
compile group: 'com.lyndir.lhunath.opal', name: 'opal-crypto', version: '1.6-p11'
|
||||||
|
|
||||||
compile group: 'com.lambdaworks', name: 'scrypt', version: '1.4.0'
|
compile group: 'com.lambdaworks', name: 'scrypt', version: '1.4.0'
|
||||||
compile group: 'org.jetbrains', name: 'annotations', version: '13.0'
|
compile group: 'org.jetbrains', name: 'annotations', version: '13.0'
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.lyndir.lhunath.opal</groupId>
|
<groupId>com.lyndir.lhunath.opal</groupId>
|
||||||
<artifactId>opal-system</artifactId>
|
<artifactId>opal-system</artifactId>
|
||||||
<version>1.6-p9</version>
|
<version>1.6-p11</version>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
<groupId>joda-time</groupId>
|
<groupId>joda-time</groupId>
|
||||||
@ -34,7 +34,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.lyndir.lhunath.opal</groupId>
|
<groupId>com.lyndir.lhunath.opal</groupId>
|
||||||
<artifactId>opal-crypto</artifactId>
|
<artifactId>opal-crypto</artifactId>
|
||||||
<version>1.6-p9</version>
|
<version>1.6-p11</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- EXTERNAL DEPENDENCIES -->
|
<!-- EXTERNAL DEPENDENCIES -->
|
||||||
|
@ -30,12 +30,12 @@ public interface MPAlgorithm extends Serializable {
|
|||||||
|
|
||||||
MPMasterKey.Version getAlgorithmVersion();
|
MPMasterKey.Version getAlgorithmVersion();
|
||||||
|
|
||||||
byte[] deriveKey(String fullName, char[] masterPassword);
|
byte[] masterKey(String fullName, char[] masterPassword);
|
||||||
|
|
||||||
byte[] siteKey(byte[] masterKey, String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
|
byte[] siteKey(byte[] masterKey, String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
|
||||||
@Nullable String keyContext);
|
@Nullable String keyContext);
|
||||||
|
|
||||||
String siteResult(byte[] masterKey, String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
|
String siteResult(byte[] masterKey, final byte[] siteKey, String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
|
||||||
@Nullable String keyContext, MPResultType resultType, @Nullable String resultParam);
|
@Nullable String keyContext, MPResultType resultType, @Nullable String resultParam);
|
||||||
|
|
||||||
String sitePasswordFromTemplate(byte[] masterKey, byte[] siteKey, MPResultType resultType, @Nullable String resultParam);
|
String sitePasswordFromTemplate(byte[] masterKey, byte[] siteKey, MPResultType resultType, @Nullable String resultParam);
|
||||||
@ -44,6 +44,6 @@ public interface MPAlgorithm extends Serializable {
|
|||||||
|
|
||||||
String sitePasswordFromDerive(byte[] masterKey, byte[] siteKey, MPResultType resultType, @Nullable String resultParam);
|
String sitePasswordFromDerive(byte[] masterKey, byte[] siteKey, MPResultType resultType, @Nullable String resultParam);
|
||||||
|
|
||||||
String siteState(byte[] masterKey, String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
|
String siteState(byte[] masterKey, final byte[] siteKey, String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
|
||||||
@Nullable String keyContext, MPResultType resultType, @Nullable String resultParam);
|
@Nullable String keyContext, MPResultType resultType, @Nullable String resultParam);
|
||||||
}
|
}
|
||||||
|
@ -90,16 +90,10 @@ public class MPAlgorithmV0 implements MPAlgorithm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] deriveKey(final String fullName, final char[] masterPassword) {
|
public byte[] masterKey(final String fullName, final char[] masterPassword) {
|
||||||
Preconditions.checkArgument( masterPassword.length > 0 );
|
|
||||||
|
|
||||||
byte[] fullNameBytes = fullName.getBytes( mpw_charset );
|
byte[] fullNameBytes = fullName.getBytes( mpw_charset );
|
||||||
byte[] fullNameLengthBytes = bytesForInt( fullName.length() );
|
byte[] fullNameLengthBytes = bytesForInt( fullName.length() );
|
||||||
ByteBuffer mpBytesBuf = mpw_charset.encode( CharBuffer.wrap( masterPassword ) );
|
|
||||||
|
|
||||||
logger.trc( "-- mpw_masterKey (algorithm: %u)", getAlgorithmVersion().toInt() );
|
|
||||||
logger.trc( "fullName: %s", fullName );
|
|
||||||
logger.trc( "masterPassword.id: %s", (Object) idForBytes( mpBytesBuf.array() ) );
|
|
||||||
|
|
||||||
String keyScope = MPKeyPurpose.Authentication.getScope();
|
String keyScope = MPKeyPurpose.Authentication.getScope();
|
||||||
logger.trc( "keyScope: %s", keyScope );
|
logger.trc( "keyScope: %s", keyScope );
|
||||||
@ -111,15 +105,13 @@ public class MPAlgorithmV0 implements MPAlgorithm {
|
|||||||
logger.trc( " => masterKeySalt.id: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
|
logger.trc( " => masterKeySalt.id: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
|
||||||
|
|
||||||
// Calculate the master key.
|
// Calculate the master key.
|
||||||
logger.trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%lu, r=%u, p=%u )",
|
logger.trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%d, r=%d, p=%d )",
|
||||||
scrypt_N, scrypt_r, scrypt_p );
|
scrypt_N, scrypt_r, scrypt_p );
|
||||||
byte[] mpBytes = new byte[mpBytesBuf.remaining()];
|
byte[] masterPasswordBytes = bytesForChars( masterPassword );
|
||||||
mpBytesBuf.get( mpBytes, 0, mpBytes.length );
|
byte[] masterKey = scrypt( masterKeySalt, masterPasswordBytes );
|
||||||
Arrays.fill( mpBytesBuf.array(), (byte) 0 );
|
|
||||||
byte[] masterKey = scrypt( masterKeySalt, mpBytes ); // TODO: Why not mpBytesBuf.array()?
|
|
||||||
Arrays.fill( masterKeySalt, (byte) 0 );
|
Arrays.fill( masterKeySalt, (byte) 0 );
|
||||||
Arrays.fill( mpBytes, (byte) 0 );
|
Arrays.fill( masterPasswordBytes, (byte) 0 );
|
||||||
logger.trc( " => masterKey.id: %s", (Object) idForBytes( masterKey ) );
|
logger.trc( " => masterKey.id: %s", CodeUtils.encodeHex( idForBytes( masterKey ) ) );
|
||||||
|
|
||||||
return masterKey;
|
return masterKey;
|
||||||
}
|
}
|
||||||
@ -139,13 +131,6 @@ public class MPAlgorithmV0 implements MPAlgorithm {
|
|||||||
@Override
|
@Override
|
||||||
public byte[] siteKey(final byte[] masterKey, final String siteName, UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
|
public byte[] siteKey(final byte[] masterKey, final String siteName, UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
|
||||||
@Nullable final String keyContext) {
|
@Nullable final String keyContext) {
|
||||||
Preconditions.checkArgument( !siteName.isEmpty() );
|
|
||||||
|
|
||||||
logger.trc( "-- mpw_siteKey (algorithm: %u)", getAlgorithmVersion().toInt() );
|
|
||||||
logger.trc( "siteName: %s", siteName );
|
|
||||||
logger.trc( "siteCounter: %d", siteCounter );
|
|
||||||
logger.trc( "keyPurpose: %d (%s)", keyPurpose.toInt(), keyPurpose.getShortName() );
|
|
||||||
logger.trc( "keyContext: %s", keyContext );
|
|
||||||
|
|
||||||
String keyScope = keyPurpose.getScope();
|
String keyScope = keyPurpose.getScope();
|
||||||
logger.trc( "keyScope: %s", keyScope );
|
logger.trc( "keyScope: %s", keyScope );
|
||||||
@ -169,23 +154,18 @@ public class MPAlgorithmV0 implements MPAlgorithm {
|
|||||||
sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes );
|
sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes );
|
||||||
logger.trc( " => siteSalt.id: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
|
logger.trc( " => siteSalt.id: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
|
||||||
|
|
||||||
logger.trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )", (Object) idForBytes( masterKey ) );
|
logger.trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )", CodeUtils.encodeHex( idForBytes( masterKey ) ) );
|
||||||
byte[] sitePasswordSeedBytes = mpw_digest.of( masterKey, sitePasswordInfo );
|
byte[] sitePasswordSeedBytes = mpw_digest.of( masterKey, sitePasswordInfo );
|
||||||
logger.trc( " => siteKey.id: %s", (Object) idForBytes( sitePasswordSeedBytes ) );
|
logger.trc( " => siteKey.id: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeedBytes ) ) );
|
||||||
|
|
||||||
return sitePasswordSeedBytes;
|
return sitePasswordSeedBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String siteResult(final byte[] masterKey, final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
|
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) {
|
@Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam) {
|
||||||
|
|
||||||
byte[] siteKey = siteKey( masterKey, siteName, siteCounter, keyPurpose, keyContext );
|
|
||||||
|
|
||||||
logger.trc( "-- mpw_siteResult (algorithm: %u)", getAlgorithmVersion().toInt() );
|
|
||||||
logger.trc( "resultType: %d (%s)", resultType.toInt(), resultType.getShortName() );
|
|
||||||
logger.trc( "resultParam: %s", resultParam );
|
|
||||||
|
|
||||||
switch (resultType.getTypeClass()) {
|
switch (resultType.getTypeClass()) {
|
||||||
case Template:
|
case Template:
|
||||||
return sitePasswordFromTemplate( masterKey, siteKey, resultType, resultParam );
|
return sitePasswordFromTemplate( masterKey, siteKey, resultType, resultParam );
|
||||||
@ -214,7 +194,7 @@ public class MPAlgorithmV0 implements MPAlgorithm {
|
|||||||
Preconditions.checkState( _siteKey.length > 0 );
|
Preconditions.checkState( _siteKey.length > 0 );
|
||||||
int templateIndex = _siteKey[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: %d => %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() );
|
||||||
@ -222,7 +202,7 @@ public class MPAlgorithmV0 implements MPAlgorithm {
|
|||||||
int characterIndex = _siteKey[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: %5d (0x%2H) => character: %c",
|
||||||
characterClass.getIdentifier(), characterIndex, _siteKey[i + 1], passwordCharacter );
|
characterClass.getIdentifier(), characterIndex, _siteKey[i + 1], passwordCharacter );
|
||||||
|
|
||||||
password.append( passwordCharacter );
|
password.append( passwordCharacter );
|
||||||
@ -241,7 +221,7 @@ public class MPAlgorithmV0 implements MPAlgorithm {
|
|||||||
try {
|
try {
|
||||||
// Base64-decode
|
// Base64-decode
|
||||||
byte[] cipherBuf = CryptUtils.decodeBase64( resultParam );
|
byte[] cipherBuf = CryptUtils.decodeBase64( resultParam );
|
||||||
logger.trc( "b64 decoded: %zu bytes = %s", cipherBuf.length, CodeUtils.encodeHex( cipherBuf ) );
|
logger.trc( "b64 decoded: %d bytes = %s", cipherBuf.length, CodeUtils.encodeHex( cipherBuf ) );
|
||||||
|
|
||||||
// Decrypt
|
// Decrypt
|
||||||
byte[] plainBuf = CryptUtils.decrypt( cipherBuf, masterKey, true );
|
byte[] plainBuf = CryptUtils.decrypt( cipherBuf, masterKey, true );
|
||||||
@ -266,7 +246,7 @@ public class MPAlgorithmV0 implements MPAlgorithm {
|
|||||||
if ((resultParamInt < 128) || (resultParamInt > 512) || ((resultParamInt % 8) != 0))
|
if ((resultParamInt < 128) || (resultParamInt > 512) || ((resultParamInt % 8) != 0))
|
||||||
throw logger.bug( "Parameter is not a valid key size (should be 128 - 512): %s", resultParam );
|
throw logger.bug( "Parameter is not a valid key size (should be 128 - 512): %s", resultParam );
|
||||||
int keySize = resultParamInt / 8;
|
int keySize = resultParamInt / 8;
|
||||||
logger.trc( "keySize: %u", keySize );
|
logger.trc( "keySize: %d", keySize );
|
||||||
|
|
||||||
// Derive key
|
// Derive key
|
||||||
byte[] resultKey = null; // TODO: mpw_kdf_blake2b( keySize, siteKey, MPSiteKeySize, NULL, 0, 0, NULL );
|
byte[] resultKey = null; // TODO: mpw_kdf_blake2b( keySize, siteKey, MPSiteKeySize, NULL, 0, 0, NULL );
|
||||||
@ -285,7 +265,8 @@ public class MPAlgorithmV0 implements MPAlgorithm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String siteState(final byte[] masterKey, final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
|
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, @Nullable final String resultParam) {
|
@Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam) {
|
||||||
|
|
||||||
Preconditions.checkNotNull( resultParam );
|
Preconditions.checkNotNull( resultParam );
|
||||||
@ -293,9 +274,8 @@ public class MPAlgorithmV0 implements MPAlgorithm {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Encrypt
|
// Encrypt
|
||||||
ByteBuffer plainText = mpw_charset.encode( CharBuffer.wrap( resultParam ) );
|
byte[] cipherBuf = CryptUtils.encrypt( resultParam.getBytes( mpw_charset ), masterKey, true );
|
||||||
byte[] cipherBuf = CryptUtils.encrypt( plainText.array(), masterKey, true );
|
logger.trc( "cipherBuf: %d bytes = %s", cipherBuf.length, CodeUtils.encodeHex( cipherBuf ) );
|
||||||
logger.trc( "cipherBuf: %zu bytes = %s", cipherBuf.length, CodeUtils.encodeHex( cipherBuf ) );
|
|
||||||
|
|
||||||
// Base64-encode
|
// Base64-encode
|
||||||
String cipherText = Verify.verifyNotNull( CryptUtils.encodeBase64( cipherBuf ) );
|
String cipherText = Verify.verifyNotNull( CryptUtils.encodeBase64( cipherBuf ) );
|
||||||
|
@ -38,15 +38,11 @@ public class MPAlgorithmV1 extends MPAlgorithmV0 {
|
|||||||
@Override
|
@Override
|
||||||
public String sitePasswordFromTemplate(final byte[] masterKey, final byte[] siteKey, final MPResultType resultType, @Nullable final String resultParam) {
|
public String sitePasswordFromTemplate(final byte[] masterKey, final byte[] siteKey, final MPResultType resultType, @Nullable final String resultParam) {
|
||||||
|
|
||||||
logger.trc( "-- mpw_siteResult (algorithm: %u)", getAlgorithmVersion().toInt() );
|
|
||||||
logger.trc( "resultType: %d (%s)", resultType.toInt(), resultType.getShortName() );
|
|
||||||
logger.trc( "resultParam: %s", resultParam );
|
|
||||||
|
|
||||||
// Determine the template.
|
// Determine the template.
|
||||||
Preconditions.checkState( siteKey.length > 0 );
|
Preconditions.checkState( siteKey.length > 0 );
|
||||||
int templateIndex = siteKey[0] & 0xFF; // Convert to unsigned int.
|
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: %d => %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() );
|
||||||
@ -54,7 +50,7 @@ public class MPAlgorithmV1 extends MPAlgorithmV0 {
|
|||||||
int characterIndex = siteKey[i + 1] & 0xFF; // Convert to unsigned int.
|
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: %3d (0x%2H) => character: %c",
|
||||||
characterClass.getIdentifier(), characterIndex, siteKey[i + 1], passwordCharacter );
|
characterClass.getIdentifier(), characterIndex, siteKey[i + 1], passwordCharacter );
|
||||||
|
|
||||||
password.append( passwordCharacter );
|
password.append( passwordCharacter );
|
||||||
|
@ -43,13 +43,6 @@ public class MPAlgorithmV2 extends MPAlgorithmV1 {
|
|||||||
@Override
|
@Override
|
||||||
public byte[] siteKey(final byte[] masterKey, final String siteName, UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
|
public byte[] siteKey(final byte[] masterKey, final String siteName, UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
|
||||||
@Nullable final String keyContext) {
|
@Nullable final String keyContext) {
|
||||||
Preconditions.checkArgument( !siteName.isEmpty() );
|
|
||||||
|
|
||||||
logger.trc( "-- mpw_siteKey (algorithm: %u)", getAlgorithmVersion().toInt() );
|
|
||||||
logger.trc( "siteName: %s", siteName );
|
|
||||||
logger.trc( "siteCounter: %d", siteCounter );
|
|
||||||
logger.trc( "keyPurpose: %d (%s)", keyPurpose.toInt(), keyPurpose.getShortName() );
|
|
||||||
logger.trc( "keyContext: %s", keyContext );
|
|
||||||
|
|
||||||
String keyScope = keyPurpose.getScope();
|
String keyScope = keyPurpose.getScope();
|
||||||
logger.trc( "keyScope: %s", keyScope );
|
logger.trc( "keyScope: %s", keyScope );
|
||||||
@ -73,9 +66,9 @@ public class MPAlgorithmV2 extends MPAlgorithmV1 {
|
|||||||
sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes );
|
sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes );
|
||||||
logger.trc( " => siteSalt.id: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
|
logger.trc( " => siteSalt.id: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
|
||||||
|
|
||||||
logger.trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )", (Object) idForBytes( masterKey ) );
|
logger.trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )", CodeUtils.encodeHex( idForBytes( masterKey ) ) );
|
||||||
byte[] sitePasswordSeedBytes = MPAlgorithmV0.mpw_digest.of( masterKey, sitePasswordInfo );
|
byte[] sitePasswordSeedBytes = MPAlgorithmV0.mpw_digest.of( masterKey, sitePasswordInfo );
|
||||||
logger.trc( " => siteKey.id: %s", (Object) idForBytes( sitePasswordSeedBytes ) );
|
logger.trc( " => siteKey.id: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeedBytes ) ) );
|
||||||
|
|
||||||
return sitePasswordSeedBytes;
|
return sitePasswordSeedBytes;
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
package com.lyndir.masterpassword;
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
import static com.lyndir.masterpassword.MPUtils.idForBytes;
|
import static com.lyndir.masterpassword.MPUtils.*;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.primitives.Bytes;
|
import com.google.common.primitives.Bytes;
|
||||||
@ -42,16 +42,10 @@ public class MPAlgorithmV3 extends MPAlgorithmV2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] deriveKey(final String fullName, final char[] masterPassword) {
|
public byte[] masterKey(final String fullName, final char[] masterPassword) {
|
||||||
Preconditions.checkArgument( masterPassword.length > 0 );
|
|
||||||
|
|
||||||
byte[] fullNameBytes = fullName.getBytes( MPAlgorithmV0.mpw_charset );
|
byte[] fullNameBytes = fullName.getBytes( MPAlgorithmV0.mpw_charset );
|
||||||
byte[] fullNameLengthBytes = MPUtils.bytesForInt( fullNameBytes.length );
|
byte[] fullNameLengthBytes = MPUtils.bytesForInt( fullNameBytes.length );
|
||||||
ByteBuffer mpBytesBuf = MPAlgorithmV0.mpw_charset.encode( CharBuffer.wrap( masterPassword ) );
|
|
||||||
|
|
||||||
logger.trc( "-- mpw_masterKey (algorithm: %u)", getAlgorithmVersion().toInt() );
|
|
||||||
logger.trc( "fullName: %s", fullName );
|
|
||||||
logger.trc( "masterPassword.id: %s", (Object) idForBytes( mpBytesBuf.array() ) );
|
|
||||||
|
|
||||||
String keyScope = MPKeyPurpose.Authentication.getScope();
|
String keyScope = MPKeyPurpose.Authentication.getScope();
|
||||||
logger.trc( "keyScope: %s", keyScope );
|
logger.trc( "keyScope: %s", keyScope );
|
||||||
@ -63,15 +57,13 @@ public class MPAlgorithmV3 extends MPAlgorithmV2 {
|
|||||||
logger.trc( " => masterKeySalt.id: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
|
logger.trc( " => masterKeySalt.id: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
|
||||||
|
|
||||||
// Calculate the master key.
|
// Calculate the master key.
|
||||||
logger.trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%lu, r=%u, p=%u )",
|
logger.trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%d, r=%d, p=%d )",
|
||||||
MPAlgorithmV0.scrypt_N, MPAlgorithmV0.scrypt_r, MPAlgorithmV0.scrypt_p );
|
MPAlgorithmV0.scrypt_N, MPAlgorithmV0.scrypt_r, MPAlgorithmV0.scrypt_p );
|
||||||
byte[] mpBytes = new byte[mpBytesBuf.remaining()];
|
byte[] mpBytes = bytesForChars( masterPassword );
|
||||||
mpBytesBuf.get( mpBytes, 0, mpBytes.length );
|
byte[] masterKey = scrypt( masterKeySalt, mpBytes );
|
||||||
Arrays.fill( mpBytesBuf.array(), (byte) 0 );
|
|
||||||
byte[] masterKey = scrypt( masterKeySalt, mpBytes ); // TODO: Why not mpBytesBuf.array()?
|
|
||||||
Arrays.fill( masterKeySalt, (byte) 0 );
|
Arrays.fill( masterKeySalt, (byte) 0 );
|
||||||
Arrays.fill( mpBytes, (byte) 0 );
|
Arrays.fill( mpBytes, (byte) 0 );
|
||||||
logger.trc( " => masterKey.id: %s", (Object) idForBytes( masterKey ) );
|
logger.trc( " => masterKey.id: %s", CodeUtils.encodeHex( idForBytes( masterKey ) ) );
|
||||||
|
|
||||||
return masterKey;
|
return masterKey;
|
||||||
}
|
}
|
||||||
|
@ -18,9 +18,11 @@
|
|||||||
|
|
||||||
package com.lyndir.masterpassword;
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
import static com.lyndir.masterpassword.MPUtils.idForBytes;
|
import static com.lyndir.masterpassword.MPUtils.*;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.primitives.UnsignedInteger;
|
import com.google.common.primitives.UnsignedInteger;
|
||||||
|
import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
@ -53,6 +55,51 @@ public class MPMasterKey {
|
|||||||
this.masterPassword = masterPassword;
|
this.masterPassword = masterPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derive the master key for a user based on their name and master password.
|
||||||
|
*
|
||||||
|
* @throws MPInvalidatedException {@link #invalidate()} has been called on this object.
|
||||||
|
*/
|
||||||
|
private byte[] masterKey(final Version algorithmVersion)
|
||||||
|
throws MPInvalidatedException {
|
||||||
|
Preconditions.checkArgument( masterPassword.length > 0 );
|
||||||
|
|
||||||
|
if (invalidated)
|
||||||
|
throw new MPInvalidatedException();
|
||||||
|
|
||||||
|
byte[] key = keyByVersion.get( algorithmVersion );
|
||||||
|
if (key == null) {
|
||||||
|
logger.trc( "-- mpw_masterKey (algorithm: %d)", algorithmVersion.toInt() );
|
||||||
|
logger.trc( "fullName: %s", fullName );
|
||||||
|
logger.trc( "masterPassword.id: %s", CodeUtils.encodeHex( idForBytes( bytesForChars( masterPassword ) ) ) );
|
||||||
|
|
||||||
|
keyByVersion.put( algorithmVersion, key = algorithmVersion.getAlgorithm().masterKey( fullName, masterPassword ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derive the master key for a user based on their name and master password.
|
||||||
|
*
|
||||||
|
* @throws MPInvalidatedException {@link #invalidate()} has been called on this object.
|
||||||
|
*/
|
||||||
|
private byte[] siteKey(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
|
||||||
|
@Nullable final String keyContext, final Version algorithmVersion)
|
||||||
|
throws MPInvalidatedException {
|
||||||
|
Preconditions.checkArgument( !siteName.isEmpty() );
|
||||||
|
|
||||||
|
byte[] masterKey = masterKey( algorithmVersion );
|
||||||
|
|
||||||
|
logger.trc( "-- mpw_siteKey (algorithm: %d)", algorithmVersion.toInt() );
|
||||||
|
logger.trc( "siteName: %s", siteName );
|
||||||
|
logger.trc( "siteCounter: %s", siteCounter );
|
||||||
|
logger.trc( "keyPurpose: %d (%s)", keyPurpose.toInt(), keyPurpose.getShortName() );
|
||||||
|
logger.trc( "keyContext: %s", keyContext );
|
||||||
|
|
||||||
|
return algorithmVersion.getAlgorithm().siteKey( masterKey, siteName, siteCounter, keyPurpose, keyContext );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a site result token.
|
* Generate a site result token.
|
||||||
*
|
*
|
||||||
@ -70,8 +117,16 @@ public class MPMasterKey {
|
|||||||
@Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam,
|
@Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam,
|
||||||
final Version algorithmVersion)
|
final Version algorithmVersion)
|
||||||
throws MPInvalidatedException {
|
throws MPInvalidatedException {
|
||||||
|
|
||||||
|
byte[] masterKey = masterKey( algorithmVersion );
|
||||||
|
byte[] siteKey = siteKey( siteName, siteCounter, keyPurpose, keyContext, algorithmVersion );
|
||||||
|
|
||||||
|
logger.trc( "-- mpw_siteResult (algorithm: %d)", algorithmVersion.toInt() );
|
||||||
|
logger.trc( "resultType: %d (%s)", resultType.getType(), resultType.getShortName() );
|
||||||
|
logger.trc( "resultParam: %s", resultParam );
|
||||||
|
|
||||||
return algorithmVersion.getAlgorithm().siteResult(
|
return algorithmVersion.getAlgorithm().siteResult(
|
||||||
getKey( algorithmVersion ), siteName, siteCounter, keyPurpose, keyContext, resultType, resultParam );
|
masterKey, siteKey, siteName, siteCounter, keyPurpose, keyContext, resultType, resultParam );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,8 +146,19 @@ public class MPMasterKey {
|
|||||||
@Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam,
|
@Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam,
|
||||||
final Version algorithmVersion)
|
final Version algorithmVersion)
|
||||||
throws MPInvalidatedException {
|
throws MPInvalidatedException {
|
||||||
|
|
||||||
|
Preconditions.checkNotNull( resultParam );
|
||||||
|
Preconditions.checkArgument( !resultParam.isEmpty() );
|
||||||
|
|
||||||
|
byte[] masterKey = masterKey( algorithmVersion );
|
||||||
|
byte[] siteKey = siteKey( siteName, siteCounter, keyPurpose, keyContext, algorithmVersion );
|
||||||
|
|
||||||
|
logger.trc( "-- mpw_siteState (algorithm: %d)", algorithmVersion.toInt() );
|
||||||
|
logger.trc( "resultType: %d (%s)", resultType.getType(), resultType.getShortName() );
|
||||||
|
logger.trc( "resultParam: %s", resultParam );
|
||||||
|
|
||||||
return algorithmVersion.getAlgorithm().siteState(
|
return algorithmVersion.getAlgorithm().siteState(
|
||||||
getKey( algorithmVersion ), siteName, siteCounter, keyPurpose, keyContext, resultType, resultParam );
|
masterKey, siteKey, siteName, siteCounter, keyPurpose, keyContext, resultType, resultParam );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@ -109,7 +175,7 @@ public class MPMasterKey {
|
|||||||
public byte[] getKeyID(final Version algorithmVersion)
|
public byte[] getKeyID(final Version algorithmVersion)
|
||||||
throws MPInvalidatedException {
|
throws MPInvalidatedException {
|
||||||
|
|
||||||
return idForBytes( getKey( algorithmVersion ) );
|
return idForBytes( masterKey( algorithmVersion ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -123,18 +189,6 @@ public class MPMasterKey {
|
|||||||
Arrays.fill( masterPassword, (char) 0 );
|
Arrays.fill( masterPassword, (char) 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] getKey(final Version algorithmVersion)
|
|
||||||
throws MPInvalidatedException {
|
|
||||||
if (invalidated)
|
|
||||||
throw new MPInvalidatedException();
|
|
||||||
|
|
||||||
byte[] key = keyByVersion.get( algorithmVersion );
|
|
||||||
if (key == null)
|
|
||||||
keyByVersion.put( algorithmVersion, key = algorithmVersion.getAlgorithm().deriveKey( fullName, masterPassword ) );
|
|
||||||
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The algorithm iterations.
|
* The algorithm iterations.
|
||||||
*/
|
*/
|
||||||
|
@ -37,7 +37,7 @@ public enum MPResultType {
|
|||||||
/**
|
/**
|
||||||
* pg^VMAUBk5x3p%HP%i4=
|
* pg^VMAUBk5x3p%HP%i4=
|
||||||
*/
|
*/
|
||||||
GeneratedMaximum( "Maximum", "20 characters, contains symbols.", //
|
GeneratedMaximum( "maximum", "20 characters, contains symbols.", //
|
||||||
ImmutableList.of( new MPTemplate( "anoxxxxxxxxxxxxxxxxx" ),
|
ImmutableList.of( new MPTemplate( "anoxxxxxxxxxxxxxxxxx" ),
|
||||||
new MPTemplate( "axxxxxxxxxxxxxxxxxno" ) ), //
|
new MPTemplate( "axxxxxxxxxxxxxxxxxno" ) ), //
|
||||||
MPResultTypeClass.Template, 0x0 ),
|
MPResultTypeClass.Template, 0x0 ),
|
||||||
@ -45,7 +45,7 @@ public enum MPResultType {
|
|||||||
/**
|
/**
|
||||||
* BiroYena8:Kixa
|
* 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" ),
|
||||||
new MPTemplate( "CvccCvcvnoCvcv" ), new MPTemplate( "CvccCvcvCvcvno" ),
|
new MPTemplate( "CvccCvcvnoCvcv" ), new MPTemplate( "CvccCvcvCvcvno" ),
|
||||||
@ -62,7 +62,7 @@ public enum MPResultType {
|
|||||||
/**
|
/**
|
||||||
* BirSuj0-
|
* BirSuj0-
|
||||||
*/
|
*/
|
||||||
GeneratedMedium( "Medium", "Copy-friendly, 8 characters, contains symbols.", //
|
GeneratedMedium( "medium", "Copy-friendly, 8 characters, contains symbols.", //
|
||||||
ImmutableList.of( new MPTemplate( "CvcnoCvc" ),
|
ImmutableList.of( new MPTemplate( "CvcnoCvc" ),
|
||||||
new MPTemplate( "CvcCvcno" ) ), //
|
new MPTemplate( "CvcCvcno" ) ), //
|
||||||
MPResultTypeClass.Template, 0x2 ),
|
MPResultTypeClass.Template, 0x2 ),
|
||||||
@ -70,7 +70,7 @@ public enum MPResultType {
|
|||||||
/**
|
/**
|
||||||
* pO98MoD0
|
* pO98MoD0
|
||||||
*/
|
*/
|
||||||
GeneratedBasic( "Basic", "8 characters, no symbols.", //
|
GeneratedBasic( "basic", "8 characters, no symbols.", //
|
||||||
ImmutableList.of( new MPTemplate( "aaanaaan" ),
|
ImmutableList.of( new MPTemplate( "aaanaaan" ),
|
||||||
new MPTemplate( "aannaaan" ),
|
new MPTemplate( "aannaaan" ),
|
||||||
new MPTemplate( "aaannaaa" ) ), //
|
new MPTemplate( "aaannaaa" ) ), //
|
||||||
@ -79,28 +79,28 @@ public enum MPResultType {
|
|||||||
/**
|
/**
|
||||||
* Bir8
|
* 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.Template, 0x4 ),
|
MPResultTypeClass.Template, 0x4 ),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 2798
|
* 2798
|
||||||
*/
|
*/
|
||||||
GeneratedPIN( "PIN", "4 numbers.", //
|
GeneratedPIN( "pin", "4 numbers.", //
|
||||||
ImmutableList.of( new MPTemplate( "nnnn" ) ), //
|
ImmutableList.of( new MPTemplate( "nnnn" ) ), //
|
||||||
MPResultTypeClass.Template, 0x5 ),
|
MPResultTypeClass.Template, 0x5 ),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* birsujano
|
* birsujano
|
||||||
*/
|
*/
|
||||||
GeneratedName( "Name", "9 letter name.", //
|
GeneratedName( "name", "9 letter name.", //
|
||||||
ImmutableList.of( new MPTemplate( "cvccvcvcv" ) ), //
|
ImmutableList.of( new MPTemplate( "cvccvcvcv" ) ), //
|
||||||
MPResultTypeClass.Template, 0xE ),
|
MPResultTypeClass.Template, 0xE ),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bir yennoquce fefi
|
* bir yennoquce fefi
|
||||||
*/
|
*/
|
||||||
GeneratedPhrase( "Phrase", "20 character sentence.", //
|
GeneratedPhrase( "phrase", "20 character sentence.", //
|
||||||
ImmutableList.of( new MPTemplate( "cvcc cvc cvccvcv cvc" ),
|
ImmutableList.of( new MPTemplate( "cvcc cvc cvccvcv cvc" ),
|
||||||
new MPTemplate( "cvc cvccvcvcv cvcv" ),
|
new MPTemplate( "cvc cvccvcvcv cvcv" ),
|
||||||
new MPTemplate( "cv cvccv cvc cvcvccv" ) ), //
|
new MPTemplate( "cv cvccv cvc cvcvccv" ) ), //
|
||||||
@ -109,25 +109,25 @@ public enum MPResultType {
|
|||||||
/**
|
/**
|
||||||
* Custom saved password.
|
* Custom saved password.
|
||||||
*/
|
*/
|
||||||
StoredPersonal( "Personal", "AES-encrypted, exportable.", //
|
StoredPersonal( "personal", "AES-encrypted, exportable.", //
|
||||||
ImmutableList.<MPTemplate>of(), //
|
ImmutableList.<MPTemplate>of(), //
|
||||||
MPResultTypeClass.Stateful, 0x0, MPSiteFeature.ExportContent ),
|
MPResultTypeClass.Stateful, 0x0, MPSiteFeature.ExportContent ),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom saved password that should not be exported from the device.
|
* 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.Stateful, 0x1, MPSiteFeature.DevicePrivate ),
|
MPResultTypeClass.Stateful, 0x1, MPSiteFeature.DevicePrivate ),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derive a unique binary key.
|
* Derive a unique binary key.
|
||||||
*/
|
*/
|
||||||
DeriveKey( "Key", "Encryption key.", //
|
DeriveKey( "key", "Encryption key.", //
|
||||||
ImmutableList.<MPTemplate>of(), //
|
ImmutableList.<MPTemplate>of(), //
|
||||||
MPResultTypeClass.Derive, 0x0, MPSiteFeature.Alternative );
|
MPResultTypeClass.Derive, 0x0, MPSiteFeature.Alternative );
|
||||||
|
|
||||||
public static MPResultType DEFAULT = GeneratedLong;
|
public static final MPResultType DEFAULT = GeneratedLong;
|
||||||
|
|
||||||
static final Logger logger = Logger.get( MPResultType.class );
|
static final Logger logger = Logger.get( MPResultType.class );
|
||||||
|
|
||||||
@ -246,16 +246,6 @@ public enum MPResultType {
|
|||||||
return types.build();
|
return types.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MPResultType forInt(final int resultType) {
|
|
||||||
|
|
||||||
return values()[resultType];
|
|
||||||
}
|
|
||||||
|
|
||||||
public int toInt() {
|
|
||||||
|
|
||||||
return ordinal();
|
|
||||||
}
|
|
||||||
|
|
||||||
public MPTemplate getTemplateAtRollingIndex(final int templateIndex) {
|
public MPTemplate getTemplateAtRollingIndex(final int templateIndex) {
|
||||||
return templates.get( templateIndex % templates.size() );
|
return templates.get( templateIndex % templates.size() );
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,8 @@ package com.lyndir.masterpassword;
|
|||||||
|
|
||||||
import com.google.common.primitives.UnsignedInteger;
|
import com.google.common.primitives.UnsignedInteger;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.CharBuffer;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -35,6 +37,16 @@ public final class MPUtils {
|
|||||||
return ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( MPAlgorithmV0.mpw_byteOrder ).putInt( number.intValue() ).array();
|
return ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( MPAlgorithmV0.mpw_byteOrder ).putInt( number.intValue() ).array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static byte[] bytesForChars(final char[] characters) {
|
||||||
|
ByteBuffer byteBuffer = MPAlgorithmV0.mpw_charset.encode( CharBuffer.wrap( characters ) );
|
||||||
|
|
||||||
|
byte[] bytes = new byte[byteBuffer.remaining()];
|
||||||
|
byteBuffer.get( bytes );
|
||||||
|
|
||||||
|
Arrays.fill( byteBuffer.array(), (byte) 0 );
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
public static byte[] idForBytes(final byte[] bytes) {
|
public static byte[] idForBytes(final byte[] bytes) {
|
||||||
return MPAlgorithmV0.mpw_hash.of( bytes );
|
return MPAlgorithmV0.mpw_hash.of( bytes );
|
||||||
}
|
}
|
||||||
|
@ -171,7 +171,7 @@ public class MPTestSuite implements Callable<Boolean> {
|
|||||||
@Override
|
@Override
|
||||||
public boolean run(final MPTests.Case testCase)
|
public boolean run(final MPTests.Case testCase)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), testCase.getMasterPassword() );
|
MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), testCase.getMasterPassword().toCharArray() );
|
||||||
String sitePassword = masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
|
String sitePassword = masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
|
||||||
testCase.getKeyContext(), testCase.getResultType(),
|
testCase.getKeyContext(), testCase.getResultType(),
|
||||||
null, testCase.getAlgorithm() );
|
null, testCase.getAlgorithm() );
|
||||||
|
@ -181,8 +181,8 @@ public class MPTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public char[] getMasterPassword() {
|
public String getMasterPassword() {
|
||||||
return checkNotNull( masterPassword ).toCharArray();
|
return checkNotNull( masterPassword );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
|
@ -22,7 +22,7 @@ import static org.testng.Assert.*;
|
|||||||
|
|
||||||
import com.lyndir.lhunath.opal.system.CodeUtils;
|
import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
import org.jetbrains.annotations.NonNls;
|
import java.util.Random;
|
||||||
import org.testng.annotations.BeforeMethod;
|
import org.testng.annotations.BeforeMethod;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
@ -32,7 +32,6 @@ public class MPMasterKeyTest {
|
|||||||
@SuppressWarnings("UnusedDeclaration")
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
private static final Logger logger = Logger.get( MPMasterKeyTest.class );
|
private static final Logger logger = Logger.get( MPMasterKeyTest.class );
|
||||||
|
|
||||||
@NonNls
|
|
||||||
private MPTestSuite testSuite;
|
private MPTestSuite testSuite;
|
||||||
|
|
||||||
@BeforeMethod
|
@BeforeMethod
|
||||||
@ -43,20 +42,58 @@ public class MPMasterKeyTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncode()
|
public void testMasterKey()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
testSuite.forEach( "testEncode", new MPTestSuite.TestCase() {
|
testSuite.forEach( "testMasterKey", new MPTestSuite.TestCase() {
|
||||||
@Override
|
@Override
|
||||||
public boolean run(final MPTests.Case testCase)
|
public boolean run(final MPTests.Case testCase)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), testCase.getMasterPassword() );
|
char[] masterPassword = testCase.getMasterPassword().toCharArray();
|
||||||
|
MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), masterPassword );
|
||||||
|
|
||||||
|
// Test key
|
||||||
|
assertEquals(
|
||||||
|
CodeUtils.encodeHex( masterKey.getKeyID( testCase.getAlgorithm() ) ),
|
||||||
|
testCase.getKeyID(),
|
||||||
|
"[testMasterKey] keyID mismatch: " + testCase );
|
||||||
|
|
||||||
|
// Test invalidation
|
||||||
|
masterKey.invalidate();
|
||||||
|
try {
|
||||||
|
masterKey.getKeyID( testCase.getAlgorithm() );
|
||||||
|
fail( "[testMasterKey] invalidate ineffective: " + testCase );
|
||||||
|
}
|
||||||
|
catch (final MPInvalidatedException ignored) {
|
||||||
|
}
|
||||||
|
assertNotEquals(
|
||||||
|
masterPassword,
|
||||||
|
testCase.getMasterPassword().toCharArray(),
|
||||||
|
"[testMasterKey] masterPassword not wiped: " + testCase );
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSiteResult()
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
testSuite.forEach( "testSiteResult", new MPTestSuite.TestCase() {
|
||||||
|
@Override
|
||||||
|
public boolean run(final MPTests.Case testCase)
|
||||||
|
throws Exception {
|
||||||
|
char[] masterPassword = testCase.getMasterPassword().toCharArray();
|
||||||
|
MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), masterPassword );
|
||||||
|
|
||||||
|
// Test site result
|
||||||
assertEquals(
|
assertEquals(
|
||||||
masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
|
masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
|
||||||
testCase.getKeyContext(), testCase.getResultType(),
|
testCase.getKeyContext(), testCase.getResultType(),
|
||||||
null, testCase.getAlgorithm() ),
|
null, testCase.getAlgorithm() ),
|
||||||
testCase.getResult(), "[testEncode] Failed test case: " + testCase );
|
testCase.getResult(),
|
||||||
|
"[testSiteResult] result mismatch: " + testCase );
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -64,30 +101,30 @@ public class MPMasterKeyTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetUserName()
|
public void testSiteState()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
MPTests.Case defaultCase = testSuite.getTests().getDefaultCase();
|
MPTests.Case testCase = testSuite.getTests().getDefaultCase();
|
||||||
|
char[] masterPassword = testCase.getMasterPassword().toCharArray();
|
||||||
|
MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), masterPassword );
|
||||||
|
|
||||||
assertEquals( new MPMasterKey( defaultCase.getFullName(), defaultCase.getMasterPassword() ).getFullName(),
|
Random random = new Random();
|
||||||
defaultCase.getFullName(), "[testGetUserName] Failed test case: " + defaultCase );
|
StringBuilder password = new StringBuilder();
|
||||||
|
for (int p = 0; p < 8; ++p)
|
||||||
|
password.append( (char) (random.nextInt( Character.MAX_CODE_POINT - Character.MIN_CODE_POINT ) + Character.MIN_CODE_POINT) );
|
||||||
|
|
||||||
|
for (final MPMasterKey.Version version : MPMasterKey.Version.values()) {
|
||||||
|
MPResultType resultType = MPResultType.StoredPersonal;
|
||||||
|
|
||||||
|
// Test site state
|
||||||
|
String state = masterKey.siteState( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
|
||||||
|
testCase.getKeyContext(), resultType, password.toString(), version );
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
|
||||||
|
testCase.getKeyContext(), resultType, state, version ),
|
||||||
|
password.toString(),
|
||||||
|
"[testSiteState] state mismatch: " + testCase );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetKeyID()
|
|
||||||
throws Exception {
|
|
||||||
|
|
||||||
testSuite.forEach( "testGetKeyID", new MPTestSuite.TestCase() {
|
|
||||||
@Override
|
|
||||||
public boolean run(final MPTests.Case testCase)
|
|
||||||
throws Exception {
|
|
||||||
MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), testCase.getMasterPassword() );
|
|
||||||
|
|
||||||
assertEquals( CodeUtils.encodeHex( masterKey.getKeyID( testCase.getAlgorithm() ) ),
|
|
||||||
testCase.getKeyID(), "[testGetKeyID] Failed test case: " + testCase );
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,9 @@
|
|||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
<logger name="com.lyndir" level="${mp.log.level:-INFO}" />
|
<logger name="com.lyndir" level="${mp.log.level:-TRACE}" />
|
||||||
|
|
||||||
<root level="INFO">
|
<root level="TRACE">
|
||||||
<appender-ref ref="stdout" />
|
<appender-ref ref="stdout" />
|
||||||
</root>
|
</root>
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="GUI" type="Application" factoryName="Application">
|
<configuration default="false" name="GUI" type="Application" factoryName="Application" show_console_on_std_err="true">
|
||||||
<option name="MAIN_CLASS_NAME" value="com.lyndir.masterpassword.gui.GUI" />
|
<option name="MAIN_CLASS_NAME" value="com.lyndir.masterpassword.gui.GUI" />
|
||||||
<option name="VM_PARAMETERS" value="" />
|
<option name="VM_PARAMETERS" value="" />
|
||||||
<option name="PROGRAM_PARAMETERS" value="" />
|
<option name="PROGRAM_PARAMETERS" value="" />
|
||||||
@ -9,7 +9,7 @@
|
|||||||
<option name="ENABLE_SWING_INSPECTOR" value="false" />
|
<option name="ENABLE_SWING_INSPECTOR" value="false" />
|
||||||
<option name="ENV_VARIABLES" />
|
<option name="ENV_VARIABLES" />
|
||||||
<option name="PASS_PARENT_ENVS" value="true" />
|
<option name="PASS_PARENT_ENVS" value="true" />
|
||||||
<module name="gui" />
|
<module name="masterpassword-gui" />
|
||||||
<envs />
|
<envs />
|
||||||
<method />
|
<method />
|
||||||
</configuration>
|
</configuration>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="Tests" type="TestNG" factoryName="TestNG">
|
<configuration default="false" name="Tests" type="TestNG" factoryName="TestNG" show_console_on_std_err="true">
|
||||||
<module name="tests" />
|
<module name="" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH" />
|
<option name="ALTERNATIVE_JRE_PATH" />
|
||||||
<option name="SUITE_NAME" value="" />
|
<option name="SUITE_NAME" value="" />
|
||||||
@ -17,7 +17,7 @@
|
|||||||
<option name="ENV_VARIABLES" />
|
<option name="ENV_VARIABLES" />
|
||||||
<option name="PASS_PARENT_ENVS" value="true" />
|
<option name="PASS_PARENT_ENVS" value="true" />
|
||||||
<option name="TEST_SEARCH_SCOPE">
|
<option name="TEST_SEARCH_SCOPE">
|
||||||
<value defaultName="singleModule" />
|
<value defaultName="wholeProject" />
|
||||||
</option>
|
</option>
|
||||||
<option name="USE_DEFAULT_REPORTERS" value="false" />
|
<option name="USE_DEFAULT_REPORTERS" value="false" />
|
||||||
<option name="PROPERTIES_FILE" value="" />
|
<option name="PROPERTIES_FILE" value="" />
|
||||||
|
Loading…
Reference in New Issue
Block a user