/*
* Password Management Servlets (PWM)
* http://www.pwm-project.org
*
* Copyright (c) 2006-2009 Novell, Inc.
* Copyright (c) 2009-2017 The PWM Project
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package password.pwm.util;
import password.pwm.PwmConstants;
import password.pwm.error.ErrorInformation;
import password.pwm.error.PwmError;
import password.pwm.error.PwmException;
import password.pwm.error.PwmUnrecoverableException;
import password.pwm.util.logging.PwmLogger;
import password.pwm.util.secure.PwmHashAlgorithm;
import password.pwm.util.secure.PwmRandom;
import password.pwm.util.secure.PwmSecurityKey;
import password.pwm.util.secure.SecureEngine;
import java.io.Serializable;
import java.util.Arrays;
/*
* A in-memory password value wrapper. Instances of this class cannot be serialized. The actual password value is encrypted using a
* a per-jvm instance key.
*
*/
public class PasswordData implements Serializable {
private static final PwmLogger LOGGER = PwmLogger.forClass(PasswordData.class);
private final byte[] passwordData;
private final String keyHash; // not a secure value, used to detect if key is same over time.
private static final transient PwmSecurityKey STATIC_KEY;
private static final transient String STATIC_KEY_HASH;
private static final transient ErrorInformation INITIALIZATION_ERROR;
private String passwordHashCache;
static {
PwmSecurityKey newKey = null;
String newKeyHash = null;
ErrorInformation newInitializationError = null;
try {
final byte[] randomBytes = new byte[1024 * 10];
PwmRandom.getInstance().nextBytes(randomBytes);
newKey = new PwmSecurityKey(randomBytes);
newKeyHash = SecureEngine.hash(randomBytes, PwmHashAlgorithm.SHA512);
} catch (Exception e) {
LOGGER.fatal("can't initialize PasswordData handler: " + e.getMessage(),e);
e.printStackTrace();
if (e instanceof PwmException) {
newInitializationError = ((PwmException) e).getErrorInformation();
} else {
newInitializationError = new ErrorInformation(PwmError.ERROR_UNKNOWN,"error initializing password data class: " + e.getMessage());
}
}
STATIC_KEY = newKey;
STATIC_KEY_HASH = newKeyHash;
INITIALIZATION_ERROR = newInitializationError;
}
public PasswordData(final String passwordData)
throws PwmUnrecoverableException
{
checkInitStatus();
if (passwordData == null) {
throw new NullPointerException("password data can not be null");
}
if (passwordData.isEmpty()) {
throw new NullPointerException("password data can not be empty");
}
this.passwordData = SecureEngine.encryptToBytes(passwordData, STATIC_KEY, PwmConstants.IN_MEMORY_PASSWORD_ENCRYPT_METHOD);
this.keyHash = STATIC_KEY_HASH;
}
private void checkInitStatus()
throws PwmUnrecoverableException
{
if (STATIC_KEY == null || STATIC_KEY_HASH == null || INITIALIZATION_ERROR != null) {
throw new PwmUnrecoverableException(INITIALIZATION_ERROR);
}
}
private void checkCurrentStatus()
throws PwmUnrecoverableException
{
if (!keyHash.equals(STATIC_KEY_HASH)) {
throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_CRYPT_ERROR,"in-memory password is no longer valid"));
}
}
public String getStringValue()
throws PwmUnrecoverableException
{
checkCurrentStatus();
return SecureEngine.decryptBytes(passwordData, STATIC_KEY, PwmConstants.IN_MEMORY_PASSWORD_ENCRYPT_METHOD);
}
@Override
public String toString()
{
return PwmConstants.LOG_REMOVED_VALUE_REPLACEMENT;
}
@Override
public boolean equals(final Object obj)
{
return equals(obj, false);
}
@Override
public int hashCode() {
int result = Arrays.hashCode(passwordData);
result = 31 * result + keyHash.hashCode();
return result;
}
public boolean equalsIgnoreCase(final PasswordData obj) {
return equals(obj, true);
}
private boolean equals(final Object obj, final boolean ignoreCase)
{
if (obj == null) {
return false;
}
if (!(obj instanceof PasswordData)) {
return false;
}
try {
final String strValue = this.getStringValue();
final String objValue = ((PasswordData)obj).getStringValue();
return ignoreCase ? strValue.equalsIgnoreCase(objValue) : strValue.equals(objValue);
} catch (PwmUnrecoverableException e) {
e.printStackTrace();
}
return super.equals(obj);
}
public static PasswordData forStringValue(final String input)
throws PwmUnrecoverableException
{
return input == null || input.isEmpty()
? null
: new PasswordData(input);
}
public String hash() throws PwmUnrecoverableException {
if (passwordHashCache == null) {
passwordHashCache = SecureEngine.hash(this.getStringValue(), PwmHashAlgorithm.SHA1);
}
return passwordHashCache;
}
}