2
0

Standardize mpw-wide constants and explain their purpose, also allow non-home .mpw.d.

This commit is contained in:
Maarten Billemont 2016-10-29 09:28:41 -04:00
parent 0498a245c6
commit 564e5d9084
8 changed files with 128 additions and 69 deletions

View File

@ -0,0 +1,74 @@
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
*/
public class MPConstant {
/* Environment */
/**
* mpw: default user name if one is not provided.
*/
public static final String env_userName = "MP_USERNAME";
/**
* mpw: default site type if one is not provided.
*
* @see MPSiteType#forOption(String)
*/
public static final String env_siteType = "MP_SITETYPE";
/**
* mpw: default site counter value if one is not provided.
*/
public static final String env_siteCounter = "MP_SITECOUNTER";
/**
* mpw: default path to look for run configuration files if the platform default is not desired.
*/
public static final String env_rcDir = "MP_RCDIR";
/**
* mpw: permit automatic update checks.
*/
public static final String env_checkUpdates = "MP_CHECKUPDATES";
/* Algorithm */
/**
* scrypt: CPU cost parameter.
*/
public static final int scrypt_N = 32768;
/**
* scrypt: Memory cost parameter.
*/
public static final int scrypt_r = 8;
/**
* scrypt: Parallelization parameter.
*/
public static final int scrypt_p = 2;
/**
* mpw: Master key size (byte).
*/
public static final int mpw_dkLen = 64;
/**
* mpw: Input character encoding.
*/
public static final Charset mpw_charset = Charsets.UTF_8;
/**
* mpw: Platform-agnostic byte order.
*/
public static final ByteOrder mpw_byteOrder = ByteOrder.BIG_ENDIAN;
/**
* mpw: Site digest.
*/
public static final MessageAuthenticationDigests mpw_digest = MessageAuthenticationDigests.HmacSHA256;
/**
* mpw: Key ID hash.
*/
public static final MessageDigests mpw_hash = MessageDigests.SHA256;
}

View File

@ -1,6 +1,5 @@
package com.lyndir.masterpassword;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Bytes;
import com.google.common.primitives.UnsignedInteger;
@ -8,7 +7,6 @@ import com.lambdaworks.crypto.SCrypt;
import com.lyndir.lhunath.opal.system.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.nio.*;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import javax.annotation.Nonnull;
@ -25,19 +23,11 @@ import javax.annotation.Nullable;
*/
public class MasterKeyV0 extends MasterKey {
private static final int MP_intLen = 32;
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MasterKeyV0.class );
protected final int MP_N = 32768;
protected final int MP_r = 8;
protected final int MP_p = 2;
protected final int MP_dkLen = 64;
protected final int MP_intLen = 32;
protected final Charset MP_charset = Charsets.UTF_8;
protected final ByteOrder MP_byteOrder = ByteOrder.BIG_ENDIAN;
protected final MessageDigests MP_hash = MessageDigests.SHA256;
protected final MessageAuthenticationDigests MP_mac = MessageAuthenticationDigests.HmacSHA256;
public MasterKeyV0(final String fullName) {
super( fullName );
}
@ -52,15 +42,15 @@ public class MasterKeyV0 extends MasterKey {
@Override
protected byte[] deriveKey(final char[] masterPassword) {
String fullName = getFullName();
byte[] fullNameBytes = fullName.getBytes( MP_charset );
byte[] fullNameBytes = fullName.getBytes( MPConstant.mpw_charset );
byte[] fullNameLengthBytes = bytesForInt( fullName.length() );
String mpKeyScope = MPSiteVariant.Password.getScope();
byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MP_charset ), fullNameLengthBytes, fullNameBytes );
byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MPConstant.mpw_charset ), fullNameLengthBytes, fullNameBytes );
logger.trc( "key scope: %s", mpKeyScope );
logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
ByteBuffer mpBytesBuf = MP_charset.encode( CharBuffer.wrap( masterPassword ) );
ByteBuffer mpBytesBuf = MPConstant.mpw_charset.encode( CharBuffer.wrap( masterPassword ) );
byte[] mpBytes = new byte[mpBytesBuf.remaining()];
mpBytesBuf.get( mpBytes, 0, mpBytes.length );
Arrays.fill( mpBytesBuf.array(), (byte) 0 );
@ -71,9 +61,9 @@ public class MasterKeyV0 extends MasterKey {
protected byte[] scrypt(final byte[] masterKeySalt, final byte[] mpBytes) {
try {
if (isAllowNative())
return SCrypt.scrypt( mpBytes, masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen );
return SCrypt.scrypt( mpBytes, masterKeySalt, MPConstant.scrypt_N, MPConstant.scrypt_r, MPConstant.scrypt_p, MPConstant.mpw_dkLen );
else
return SCrypt.scryptJ( mpBytes, masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen );
return SCrypt.scryptJ( mpBytes, masterKeySalt, MPConstant.scrypt_N, MPConstant.scrypt_r, MPConstant.scrypt_p, MPConstant.mpw_dkLen );
}
catch (GeneralSecurityException e) {
logger.bug( e );
@ -99,22 +89,22 @@ public class MasterKeyV0 extends MasterKey {
siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (300 * 1000)) * 300 );
String siteScope = siteVariant.getScope();
byte[] siteNameBytes = siteName.getBytes( MP_charset );
byte[] siteNameBytes = siteName.getBytes( MPConstant.mpw_charset );
byte[] siteNameLengthBytes = bytesForInt( siteName.length() );
byte[] siteCounterBytes = bytesForInt( siteCounter );
byte[] siteContextBytes = siteContext == null || siteContext.isEmpty()? null: siteContext.getBytes( MP_charset );
byte[] siteContextBytes = siteContext == null || siteContext.isEmpty()? null: siteContext.getBytes( MPConstant.mpw_charset );
byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
logger.trc( "site scope: %s, context: %s", siteScope, siteContextBytes == null? "<empty>": siteContext );
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
siteContextBytes == null? "(null)": siteContext );
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MPConstant.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
if (siteContextBytes != null)
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
byte[] sitePasswordSeedBytes = MP_mac.of( getKey(), sitePasswordInfo );
byte[] sitePasswordSeedBytes = MPConstant.mpw_digest.of( getKey(), sitePasswordInfo );
int[] sitePasswordSeed = new int[sitePasswordSeedBytes.length];
for (int i = 0; i < sitePasswordSeedBytes.length; ++i) {
ByteBuffer buf = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( ByteOrder.BIG_ENDIAN );
@ -146,16 +136,16 @@ public class MasterKeyV0 extends MasterKey {
@Override
protected byte[] bytesForInt(final int number) {
return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MP_byteOrder ).putInt( number ).array();
return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MPConstant.mpw_byteOrder ).putInt( number ).array();
}
@Override
protected byte[] bytesForInt(@Nonnull final UnsignedInteger number) {
return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MP_byteOrder ).putInt( number.intValue() ).array();
return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MPConstant.mpw_byteOrder ).putInt( number.intValue() ).array();
}
@Override
protected byte[] idForBytes(final byte[] bytes) {
return MP_hash.of( bytes );
return MPConstant.mpw_hash.of( bytes );
}
}

View File

@ -46,22 +46,22 @@ public class MasterKeyV1 extends MasterKeyV0 {
siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (300 * 1000)) * 300 );
String siteScope = siteVariant.getScope();
byte[] siteNameBytes = siteName.getBytes( MP_charset );
byte[] siteNameBytes = siteName.getBytes( MPConstant.mpw_charset );
byte[] siteNameLengthBytes = bytesForInt( siteName.length() );
byte[] siteCounterBytes = bytesForInt( siteCounter );
byte[] siteContextBytes = siteContext == null || siteContext.isEmpty()? null: siteContext.getBytes( MP_charset );
byte[] siteContextBytes = siteContext == null || siteContext.isEmpty()? null: siteContext.getBytes( MPConstant.mpw_charset );
byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
logger.trc( "site scope: %s, context: %s", siteScope, siteContextBytes == null? "<empty>": siteContext );
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
siteContextBytes == null? "(null)": siteContext );
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MPConstant.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
if (siteContextBytes != null)
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
byte[] sitePasswordSeed = MP_mac.of( getKey(), sitePasswordInfo );
byte[] sitePasswordSeed = MPConstant.mpw_digest.of( getKey(), sitePasswordInfo );
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
Preconditions.checkState( sitePasswordSeed.length > 0 );

View File

@ -45,22 +45,22 @@ public class MasterKeyV2 extends MasterKeyV1 {
siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (300 * 1000)) * 300 );
String siteScope = siteVariant.getScope();
byte[] siteNameBytes = siteName.getBytes( MP_charset );
byte[] siteNameBytes = siteName.getBytes( MPConstant.mpw_charset );
byte[] siteNameLengthBytes = bytesForInt( siteNameBytes.length );
byte[] siteCounterBytes = bytesForInt( siteCounter );
byte[] siteContextBytes = siteContext == null || siteContext.isEmpty()? null: siteContext.getBytes( MP_charset );
byte[] siteContextBytes = siteContext == null || siteContext.isEmpty()? null: siteContext.getBytes( MPConstant.mpw_charset );
byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
logger.trc( "site scope: %s, context: %s", siteScope, siteContextBytes == null? "<empty>": siteContext );
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
siteContextBytes == null? "(null)": siteContext );
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MPConstant.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
if (siteContextBytes != null)
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
byte[] sitePasswordSeed = MP_mac.of( getKey(), sitePasswordInfo );
byte[] sitePasswordSeed = MPConstant.mpw_digest.of( getKey(), sitePasswordInfo );
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
Preconditions.checkState( sitePasswordSeed.length > 0 );

View File

@ -1,12 +1,10 @@
package com.lyndir.masterpassword;
import com.google.common.primitives.Bytes;
import com.lambdaworks.crypto.SCrypt;
import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import javax.annotation.Nullable;
@ -35,15 +33,15 @@ public class MasterKeyV3 extends MasterKeyV2 {
@Nullable
@Override
protected byte[] deriveKey(final char[] masterPassword) {
byte[] fullNameBytes = getFullName().getBytes( MP_charset );
byte[] fullNameBytes = getFullName().getBytes( MPConstant.mpw_charset );
byte[] fullNameLengthBytes = bytesForInt( fullNameBytes.length );
String mpKeyScope = MPSiteVariant.Password.getScope();
byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MP_charset ), fullNameLengthBytes, fullNameBytes );
byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MPConstant.mpw_charset ), fullNameLengthBytes, fullNameBytes );
logger.trc( "key scope: %s", mpKeyScope );
logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
ByteBuffer mpBytesBuf = MP_charset.encode( CharBuffer.wrap( masterPassword ) );
ByteBuffer mpBytesBuf = MPConstant.mpw_charset.encode( CharBuffer.wrap( masterPassword ) );
byte[] mpBytes = new byte[mpBytesBuf.remaining()];
mpBytesBuf.get( mpBytes, 0, mpBytes.length );
Arrays.fill( mpBytesBuf.array(), (byte) 0 );

View File

@ -37,21 +37,17 @@ import java.util.Map;
*/
public class CLI {
private static final String ENV_USERNAME = "MP_USERNAME";
private static final String ENV_SITETYPE = "MP_SITETYPE";
private static final String ENV_SITECOUNTER = "MP_SITECOUNTER";
public static void main(final String[] args)
throws IOException {
// Read information from the environment.
char[] masterPassword;
String siteName = null, context = null;
String userName = System.getenv( ENV_USERNAME );
String siteTypeName = ifNotNullElse( System.getenv( ENV_SITETYPE ), "" );
String userName = System.getenv( MPConstant.env_userName );
String siteTypeName = ifNotNullElse( System.getenv( MPConstant.env_siteType ), "" );
MPSiteType siteType = siteTypeName.isEmpty()? MPSiteType.GeneratedLong: MPSiteType.forOption( siteTypeName );
MPSiteVariant variant = MPSiteVariant.Password;
String siteCounterName = ifNotNullElse( System.getenv( ENV_SITECOUNTER ), "" );
String siteCounterName = ifNotNullElse( System.getenv( MPConstant.env_siteCounter ), "" );
UnsignedInteger siteCounter = siteCounterName.isEmpty()? UnsignedInteger.valueOf( 1 ): UnsignedInteger.valueOf( siteCounterName );
// Parse information from option arguments.
@ -102,9 +98,9 @@ public class CLI {
System.out.println();
System.out.format( "Usage: mpw [-u name] [-t type] [-c counter] site\n\n" );
System.out.format( " -u name Specify the full name of the user.\n" );
System.out.format( " Defaults to %s in env.\n\n", ENV_USERNAME );
System.out.format( " Defaults to %s in env.\n\n", MPConstant.env_userName );
System.out.format( " -t type Specify the password's template.\n" );
System.out.format( " Defaults to %s in env or 'long' for password, 'name' for login.\n", ENV_SITETYPE );
System.out.format( " Defaults to %s in env or 'long' for password, 'name' for login.\n", MPConstant.env_siteType );
int optionsLength = 0;
Map<String, MPSiteType> typeMap = Maps.newLinkedHashMap();
@ -122,7 +118,7 @@ public class CLI {
System.out.println();
System.out.format( " -c counter The value of the counter.\n" );
System.out.format( " Defaults to %s in env or '1'.\n\n", ENV_SITECOUNTER );
System.out.format( " Defaults to %s in env or '1'.\n\n", MPConstant.env_siteCounter );
System.out.format( " -v variant The kind of content to generate.\n" );
System.out.format( " Defaults to 'password'.\n" );

View File

@ -1,6 +1,7 @@
package com.lyndir.masterpassword.gui;
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
import com.lyndir.masterpassword.MPConstant;
/**
@ -15,6 +16,6 @@ public class Config {
}
public boolean checkForUpdates() {
return ConversionUtils.toBoolean( System.getProperty( "mp.update.check" ) ).or( true );
return ConversionUtils.toBoolean( System.getenv( MPConstant.env_checkUpdates ) ).or( true );
}
}

View File

@ -1,31 +1,32 @@
package com.lyndir.masterpassword.model;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
import com.google.common.base.*;
import com.google.common.collect.*;
import com.google.common.io.CharSink;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.masterpassword.MPConstant;
import java.io.*;
import java.util.SortedSet;
import javax.annotation.Nullable;
/**
* Manages user data stored in user-specific {@code .mpsites} files under {@code .mpw.d}.
* @author lhunath, 14-12-07
*/
public class MPUserFileManager extends MPUserManager {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MPUserFileManager.class );
private static final File mpwd = new File( System.getProperty( "user.home" ), ".mpw.d" );
private static final MPUserFileManager instance;
static {
File mpwrc = new File( System.getProperty( "user.home" ), ".mpwrc" );
if (mpwrc.exists() && !mpwd.exists())
if (!mpwrc.renameTo( mpwd ))
logger.err( "Couldn't migrate: %s -> %s", mpwrc, mpwd );
instance = create( mpwd );
String rcDir = System.getenv( MPConstant.env_rcDir );
if (rcDir != null)
instance = create( new File( rcDir ) );
else
instance = create( new File( ifNotNullElseNullable( System.getProperty( "user.home" ), System.getenv( "HOME" ) ), ".mpw.d" ) );
}
private final File userFilesDirectory;
@ -51,12 +52,7 @@ public class MPUserFileManager extends MPUserManager {
return ImmutableList.of();
}
return FluentIterable.from( ImmutableList.copyOf( userFilesDirectory.listFiles( new FilenameFilter() {
@Override
public boolean accept(final File dir, final String name) {
return name.endsWith( ".mpsites" );
}
} ) ) ).transform( new Function<File, MPUser>() {
return FluentIterable.from( listUserFiles( userFilesDirectory ) ).transform( new Function<File, MPUser>() {
@Nullable
@Override
public MPUser apply(@Nullable final File file) {
@ -71,6 +67,15 @@ public class MPUserFileManager extends MPUserManager {
} ).filter( Predicates.notNull() );
}
private static ImmutableList<File> listUserFiles(final File userFilesDirectory) {
return ImmutableList.copyOf( ifNotNullElse( userFilesDirectory.listFiles( new FilenameFilter() {
@Override
public boolean accept(final File dir, final String name) {
return name.endsWith( ".mpsites" );
}
} ), new File[0] ) );
}
@Override
public void addUser(final MPUser user) {
super.addUser( user );
@ -103,12 +108,7 @@ public class MPUserFileManager extends MPUserManager {
}
// Remove deleted users.
for (File userFile : userFilesDirectory.listFiles( new FilenameFilter() {
@Override
public boolean accept(final File dir, final String name) {
return name.endsWith( ".mpsites" );
}
} ))
for (File userFile : listUserFiles( userFilesDirectory ))
if (getUserNamed( userFile.getName().replaceFirst( "\\.mpsites$", "" ) ) == null)
if (!userFile.delete())
logger.err( "Couldn't delete file: %s", userFile );
@ -118,6 +118,6 @@ public class MPUserFileManager extends MPUserManager {
* @return The location on the file system where the user models are stored.
*/
public File getPath() {
return mpwd;
return userFilesDirectory;
}
}