PrometheusSecurityPasswordCache.java
/*
* Prometheus: Application Framework
* Copyright 2012-2026. Tony Washer
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package io.github.tonywasher.joceanus.prometheus.security;
import io.github.tonywasher.joceanus.gordianknot.api.base.GordianException;
import io.github.tonywasher.joceanus.gordianknot.api.factory.GordianFactory;
import io.github.tonywasher.joceanus.gordianknot.api.factory.GordianFactory.GordianFactoryLock;
import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianKeyPair;
import io.github.tonywasher.joceanus.gordianknot.api.keyset.GordianBadCredentialsException;
import io.github.tonywasher.joceanus.gordianknot.api.keyset.GordianKeySet;
import io.github.tonywasher.joceanus.gordianknot.api.keyset.GordianKeySetFactory;
import io.github.tonywasher.joceanus.gordianknot.api.lock.GordianKeyPairLock;
import io.github.tonywasher.joceanus.gordianknot.api.lock.GordianKeySetLock;
import io.github.tonywasher.joceanus.gordianknot.api.lock.GordianLock;
import io.github.tonywasher.joceanus.gordianknot.api.lock.GordianLockFactory;
import io.github.tonywasher.joceanus.gordianknot.api.lock.GordianPasswordLockSpec;
import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
import io.github.tonywasher.joceanus.oceanus.convert.OceanusDataConverter;
import io.github.tonywasher.joceanus.oceanus.logger.OceanusLogManager;
import io.github.tonywasher.joceanus.oceanus.logger.OceanusLogger;
import io.github.tonywasher.joceanus.prometheus.exc.PrometheusDataException;
import io.github.tonywasher.joceanus.prometheus.exc.PrometheusSecurityException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* Password Cache.
*/
public class PrometheusSecurityPasswordCache {
/**
* Logger.
*/
private static final OceanusLogger LOGGER = OceanusLogManager.getLogger(PrometheusSecurityPasswordCache.class);
/**
* Password failed message.
*/
private static final String PASSWORD_FAIL = "Password attempt failed";
/**
* List of resolved Locks.
*/
private final List<PrometheusLockCache<?>> theLocks;
/**
* List of successful passwords.
*/
private final List<ByteBuffer> thePasswords;
/**
* The Factory.
*/
private final GordianFactory theFactory;
/**
* The KeySet Factory.
*/
private final GordianKeySetFactory theKeySetFactory;
/**
* The lockFactory.
*/
private final GordianLockFactory theLockFactory;
/**
* PasswordLockSpec.
*/
private final GordianPasswordLockSpec theLockSpec;
/**
* Local keySet.
*/
private final GordianKeySet theKeySet;
/**
* Constructor.
*
* @param pManager the password manager
* @param pLockSpec the passwordLockSpec
* @throws OceanusException on error
*/
PrometheusSecurityPasswordCache(final PrometheusSecurityPasswordManager pManager,
final GordianPasswordLockSpec pLockSpec) throws OceanusException {
/* Protect against exceptions */
try {
/* Store factory and lockSpec*/
theFactory = pManager.getSecurityFactory();
theKeySetFactory = theFactory.getKeySetFactory();
theLockFactory = theFactory.getLockFactory();
theLockSpec = pLockSpec;
/* Create a keySet */
theKeySet = theKeySetFactory.generateKeySet(pLockSpec.getKeySetSpec());
/* Create the lists */
theLocks = new ArrayList<>();
thePasswords = new ArrayList<>();
} catch (GordianException e) {
throw new PrometheusSecurityException(e);
}
}
/**
* Add resolved factoryLock to cache.
*
* @param pFactory the resolved FactoryLock
* @param pPassword the password
* @throws OceanusException on error
*/
void addResolvedFactory(final GordianFactoryLock pFactory,
final char[] pPassword) throws OceanusException {
byte[] myPasswordBytes = null;
try {
/* Encrypt the password */
myPasswordBytes = OceanusDataConverter.charsToByteArray(pPassword);
final byte[] myEncrypted = theKeySet.encryptBytes(myPasswordBytes);
/* Add the entry to the lists */
final ByteBuffer myBuffer = ByteBuffer.wrap(myEncrypted);
theLocks.add(new PrometheusLockCache<>(pFactory, myBuffer));
thePasswords.add(myBuffer);
} catch (GordianException e) {
throw new PrometheusSecurityException(e);
} finally {
/* Clear out password */
if (myPasswordBytes != null) {
Arrays.fill(myPasswordBytes, (byte) 0);
}
}
}
/**
* Add resolved keySetLock to cache.
*
* @param pKeySet the resolved keySetLock
* @param pPassword the password
* @throws OceanusException on error
*/
void addResolvedKeySet(final GordianKeySetLock pKeySet,
final char[] pPassword) throws OceanusException {
byte[] myPasswordBytes = null;
try {
/* Encrypt the password */
myPasswordBytes = OceanusDataConverter.charsToByteArray(pPassword);
final byte[] myEncrypted = theKeySet.encryptBytes(myPasswordBytes);
/* Add the entry to the lists */
final ByteBuffer myBuffer = ByteBuffer.wrap(myEncrypted);
theLocks.add(new PrometheusLockCache<>(pKeySet, myBuffer));
thePasswords.add(myBuffer);
} catch (GordianException e) {
throw new PrometheusSecurityException(e);
} finally {
/* Clear out password */
if (myPasswordBytes != null) {
Arrays.fill(myPasswordBytes, (byte) 0);
}
}
}
/**
* Add resolved keyPairLock to cache.
*
* @param pKeyPair the resolved keyPairLock
* @param pPassword the password
* @throws OceanusException on error
*/
void addResolvedKeyPair(final GordianKeyPairLock pKeyPair,
final char[] pPassword) throws OceanusException {
byte[] myPasswordBytes = null;
try {
/* Encrypt the password */
myPasswordBytes = OceanusDataConverter.charsToByteArray(pPassword);
final byte[] myEncrypted = theKeySet.encryptBytes(myPasswordBytes);
/* Add the entry to the lists */
final ByteBuffer myBuffer = ByteBuffer.wrap(myEncrypted);
theLocks.add(new PrometheusLockCache<>(pKeyPair, myBuffer));
thePasswords.add(myBuffer);
} catch (GordianException e) {
throw new PrometheusSecurityException(e);
} finally {
/* Clear out password */
if (myPasswordBytes != null) {
Arrays.fill(myPasswordBytes, (byte) 0);
}
}
}
/**
* LookUp previously resolved Factory.
*
* @param pLockBytes the LockBytes to search for
* @return the previous factoryLock if found, otherwise null
*/
GordianFactoryLock lookUpResolvedFactoryLock(final byte[] pLockBytes) {
/* Look for the factory in the list */
for (PrometheusLockCache<?> myCurr : theLocks) {
/* If this is the factoryLock we are looking for, return it */
if (myCurr.getLock() instanceof GordianFactoryLock
&& Arrays.equals(pLockBytes, myCurr.getLock().getLockBytes())) {
return (GordianFactoryLock) myCurr.getLock();
}
}
/* Return not found */
return null;
}
/**
* LookUp previously resolved keySet.
*
* @param pLockBytes the LockBytes to search for
* @return the previous keySetLock if found, otherwise null
*/
GordianKeySetLock lookUpResolvedKeySetLock(final byte[] pLockBytes) {
/* Look for the keySet in the list */
for (PrometheusLockCache<?> myCurr : theLocks) {
/* If this is the keySetLock we are looking for, return it */
if (myCurr.getLock() instanceof GordianKeySetLock
&& Arrays.equals(pLockBytes, myCurr.getLock().getLockBytes())) {
return (GordianKeySetLock) myCurr.getLock();
}
}
/* Return not found */
return null;
}
/**
* LookUp previously resolved keyPair.
*
* @param pLockBytes the LockBytes to search for
* @param pKeyPair the keyPair
* @return the previous keySetLock if found, otherwise null
*/
GordianKeyPairLock lookUpResolvedKeyPairLock(final byte[] pLockBytes,
final GordianKeyPair pKeyPair) {
/* Look for the keyPair in the list */
for (PrometheusLockCache<?> myCurr : theLocks) {
/* If this is the keyPairLock we are looking for, return it */
if (myCurr.getLock() instanceof GordianKeyPairLock
&& Arrays.equals(pLockBytes, myCurr.getLock().getLockBytes())
&& pKeyPair.equals(((GordianKeyPairLock) myCurr.getLock()).getKeyPair())) {
return (GordianKeyPairLock) myCurr.getLock();
}
}
/* Return not found */
return null;
}
/**
* LookUp previously resolved Password.
*
* @param pReference the Reference to search for
* @return the encrypted password
* @throws OceanusException on error
*/
ByteBuffer lookUpResolvedPassword(final Object pReference) throws OceanusException {
/* If the reference is a lock */
if (pReference instanceof GordianLock) {
/* Look for the lock in the list */
final GordianLock<?> myReference = (GordianLock<?>) pReference;
for (PrometheusLockCache<?> myCurr : theLocks) {
/* If this is the lock are looking for, return it */
if (Objects.equals(myReference, myCurr.getLock())) {
return myCurr.getPassword();
}
}
}
/* Throw error */
throw new PrometheusDataException("Referenced Object not known");
}
/**
* Attempt known passwords for factory lock.
*
* @param pLockBytes the lockBytes to attempt passwords for
* @return the new FactoryLock if successful, otherwise null
*/
GordianFactoryLock attemptKnownPasswordsForFactoryLock(final byte[] pLockBytes) {
/* Loop through the passwords */
for (ByteBuffer myCurr : thePasswords) {
/* Attempt the password */
final GordianFactoryLock myFactory = attemptPasswordForFactoryLock(pLockBytes, myCurr.array());
/* If we succeeded */
if (myFactory != null) {
/* Add the factory to the list and return it */
theLocks.add(new PrometheusLockCache<>(myFactory, myCurr));
return myFactory;
}
}
/* Return null */
return null;
}
/**
* Attempt the cached password against the passed lock.
*
* @param pLockBytes the Lock to test against
* @param pPassword the encrypted password
* @return the new FactoryLock if successful, otherwise null
*/
private GordianFactoryLock attemptPasswordForFactoryLock(final byte[] pLockBytes,
final byte[] pPassword) {
/* Protect against exceptions */
byte[] myPasswordBytes = null;
char[] myPasswordChars = null;
try {
/* Access the original password */
myPasswordBytes = theKeySet.decryptBytes(pPassword);
myPasswordChars = OceanusDataConverter.bytesToCharArray(myPasswordBytes);
/* Try to resolve the lock and return it */
return theFactory.resolveFactoryLock(pLockBytes, myPasswordChars);
/* Catch Exceptions */
} catch (GordianException
| OceanusException e) {
LOGGER.error(PASSWORD_FAIL, e);
return null;
} catch (GordianBadCredentialsException e) {
return null;
} finally {
/* Clear out password */
if (myPasswordBytes != null) {
Arrays.fill(myPasswordBytes, (byte) 0);
}
if (myPasswordChars != null) {
Arrays.fill(myPasswordChars, (char) 0);
}
}
}
/**
* Attempt known passwords for keySet lock.
*
* @param pLockBytes the lockBytes to attempt passwords for
* @return the new keySetLock if successful, otherwise null
*/
GordianKeySetLock attemptKnownPasswordsForKeySetLock(final byte[] pLockBytes) {
/* Loop through the passwords */
for (ByteBuffer myCurr : thePasswords) {
/* Attempt the password */
final GordianKeySetLock myKeySet = attemptPasswordForKeySetLock(pLockBytes, myCurr.array());
/* If we succeeded */
if (myKeySet != null) {
/* Add the factory to the list and return it */
theLocks.add(new PrometheusLockCache<>(myKeySet, myCurr));
return myKeySet;
}
}
/* Return null */
return null;
}
/**
* Attempt the cached password against the passed lock.
*
* @param pLockBytes the Lock to test against
* @param pPassword the encrypted password
* @return the new keySetLock if successful, otherwise null
*/
private GordianKeySetLock attemptPasswordForKeySetLock(final byte[] pLockBytes,
final byte[] pPassword) {
/* Protect against exceptions */
byte[] myPasswordBytes = null;
char[] myPasswordChars = null;
try {
/* Access the original password */
myPasswordBytes = theKeySet.decryptBytes(pPassword);
myPasswordChars = OceanusDataConverter.bytesToCharArray(myPasswordBytes);
/* Try to resolve the lock and return it */
return theLockFactory.resolveKeySetLock(pLockBytes, myPasswordChars);
/* Catch Exceptions */
} catch (GordianException
| OceanusException e) {
LOGGER.error(PASSWORD_FAIL, e);
return null;
} catch (GordianBadCredentialsException e) {
return null;
} finally {
/* Clear out password */
if (myPasswordBytes != null) {
Arrays.fill(myPasswordBytes, (byte) 0);
}
if (myPasswordChars != null) {
Arrays.fill(myPasswordChars, (char) 0);
}
}
}
/**
* Attempt known passwords for keyPair lock.
*
* @param pLockBytes the lockBytes to attempt passwords for
* @param pKeyPair the keyPair
* @return the new keyPairLock if successful, otherwise null
*/
GordianKeyPairLock attemptKnownPasswordsForKeyPairLock(final byte[] pLockBytes,
final GordianKeyPair pKeyPair) {
/* Loop through the passwords */
for (ByteBuffer myCurr : thePasswords) {
/* Attempt the password */
final GordianKeyPairLock myKeyPair = attemptPasswordForKeyPairLock(pLockBytes, pKeyPair, myCurr.array());
/* If we succeeded */
if (myKeyPair != null) {
/* Add the factory to the list and return it */
theLocks.add(new PrometheusLockCache<>(myKeyPair, myCurr));
return myKeyPair;
}
}
/* Return null */
return null;
}
/**
* Attempt the cached password against the passed lock.
*
* @param pLockBytes the Lock to test against
* @param pKeyPair the keyPair
* @param pPassword the encrypted password
* @return the new keyPairLock if successful, otherwise null
*/
private GordianKeyPairLock attemptPasswordForKeyPairLock(final byte[] pLockBytes,
final GordianKeyPair pKeyPair,
final byte[] pPassword) {
/* Protect against exceptions */
byte[] myPasswordBytes = null;
char[] myPasswordChars = null;
try {
/* Access the original password */
myPasswordBytes = theKeySet.decryptBytes(pPassword);
myPasswordChars = OceanusDataConverter.bytesToCharArray(myPasswordBytes);
/* Try to resolve the lock and return it */
return theLockFactory.resolveKeyPairLock(pLockBytes, pKeyPair, myPasswordChars);
/* Catch Exceptions */
} catch (GordianException
| OceanusException e) {
LOGGER.error(PASSWORD_FAIL, e);
return null;
} catch (GordianBadCredentialsException e) {
return null;
} finally {
/* Clear out password */
if (myPasswordBytes != null) {
Arrays.fill(myPasswordBytes, (byte) 0);
}
if (myPasswordChars != null) {
Arrays.fill(myPasswordChars, (char) 0);
}
}
}
/**
* Create a factoryLock with a previously used password.
*
* @param pFactory the new factory
* @param pPassword the encrypted password
* @return the new factoryLock
* @throws OceanusException on error
*/
GordianFactoryLock createSimilarFactoryLock(final GordianFactory pFactory,
final ByteBuffer pPassword) throws OceanusException {
/* Protect against exceptions */
byte[] myPasswordBytes = null;
char[] myPasswordChars = null;
try {
/* Access the original password */
myPasswordBytes = theKeySet.decryptBytes(pPassword.array());
myPasswordChars = OceanusDataConverter.bytesToCharArray(myPasswordBytes);
/* Create the new lock */
final GordianFactoryLock myLock = theFactory.newFactoryLock(pFactory, theLockSpec, myPasswordChars);
/* Add the entry to the list and return the hash */
theLocks.add(new PrometheusLockCache<>(myLock, pPassword));
return myLock;
} catch (GordianException e) {
throw new PrometheusSecurityException(e);
} finally {
/* Clear out password */
if (myPasswordBytes != null) {
Arrays.fill(myPasswordBytes, (byte) 0);
}
if (myPasswordChars != null) {
Arrays.fill(myPasswordChars, (char) 0);
}
}
}
/**
* Create a keySetLock with a previously used password.
*
* @param pKeySet the new keySet
* @param pPassword the encrypted password
* @return the new factoryLock
* @throws OceanusException on error
*/
GordianKeySetLock createSimilarKeySetLock(final GordianKeySet pKeySet,
final ByteBuffer pPassword) throws OceanusException {
/* Protect against exceptions */
byte[] myPasswordBytes = null;
char[] myPasswordChars = null;
try {
/* Access the original password */
myPasswordBytes = theKeySet.decryptBytes(pPassword.array());
myPasswordChars = OceanusDataConverter.bytesToCharArray(myPasswordBytes);
/* Create the new lock */
final GordianKeySetLock myLock = theLockFactory.newKeySetLock(pKeySet, theLockSpec, myPasswordChars);
/* Add the entry to the list and return the hash */
theLocks.add(new PrometheusLockCache<>(myLock, pPassword));
return myLock;
} catch (GordianException e) {
throw new PrometheusSecurityException(e);
} finally {
/* Clear out password */
if (myPasswordBytes != null) {
Arrays.fill(myPasswordBytes, (byte) 0);
}
if (myPasswordChars != null) {
Arrays.fill(myPasswordChars, (char) 0);
}
}
}
/**
* Create a zipLock with a previously used password.
*
* @param pKeyPair the keyPair
* @param pPassword the encrypted password
* @return the new PasswordHash
* @throws OceanusException on error
*/
GordianKeyPairLock createSimilarKeyPairLock(final GordianKeyPair pKeyPair,
final ByteBuffer pPassword) throws OceanusException {
/* Protect against exceptions */
byte[] myPasswordBytes = null;
char[] myPasswordChars = null;
try {
/* Access the original password */
myPasswordBytes = theKeySet.decryptBytes(pPassword.array());
myPasswordChars = OceanusDataConverter.bytesToCharArray(myPasswordBytes);
/* Create the similar passwordLock and return it */
return theLockFactory.newKeyPairLock(theLockSpec, pKeyPair, myPasswordChars);
} catch (GordianException e) {
throw new PrometheusSecurityException(e);
} finally {
/* Clear out password */
if (myPasswordBytes != null) {
Arrays.fill(myPasswordBytes, (byte) 0);
}
if (myPasswordChars != null) {
Arrays.fill(myPasswordChars, (char) 0);
}
}
}
/**
* The lockCache.
*
* @param <T> the locked object
*
*/
static class PrometheusLockCache<T> {
/**
* The FactoryLock.
*/
private final GordianLock<T> theLock;
/**
* The Encrypted password.
*/
private final ByteBuffer thePassword;
/**
* Constructor.
*
* @param pLock the Lock
* @param pPassword the encrypted password
*/
PrometheusLockCache(final GordianLock<T> pLock,
final ByteBuffer pPassword) {
theLock = pLock;
thePassword = pPassword;
}
/**
* Obtain the lock.
*
* @return the Lock
*/
GordianLock<T> getLock() {
return theLock;
}
/**
* Obtain the encrypted password.
*
* @return the password
*/
ByteBuffer getPassword() {
return thePassword;
}
}
}