PrometheusEncryptor.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.data;
import io.github.tonywasher.joceanus.gordianknot.api.base.GordianException;
import io.github.tonywasher.joceanus.gordianknot.api.keyset.GordianKeySet;
import io.github.tonywasher.joceanus.metis.data.MetisDataDifference;
import io.github.tonywasher.joceanus.metis.data.MetisDataType;
import io.github.tonywasher.joceanus.metis.field.MetisFieldItem.MetisFieldDef;
import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
import io.github.tonywasher.joceanus.oceanus.convert.OceanusDataConverter;
import io.github.tonywasher.joceanus.oceanus.date.OceanusDate;
import io.github.tonywasher.joceanus.oceanus.decimal.OceanusDecimal;
import io.github.tonywasher.joceanus.oceanus.decimal.OceanusMoney;
import io.github.tonywasher.joceanus.oceanus.decimal.OceanusPrice;
import io.github.tonywasher.joceanus.oceanus.decimal.OceanusRate;
import io.github.tonywasher.joceanus.oceanus.decimal.OceanusRatio;
import io.github.tonywasher.joceanus.oceanus.decimal.OceanusUnits;
import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
import io.github.tonywasher.joceanus.prometheus.exc.PrometheusDataException;
import io.github.tonywasher.joceanus.prometheus.exc.PrometheusLogicException;
import io.github.tonywasher.joceanus.prometheus.exc.PrometheusSecurityException;
import java.util.EnumMap;
import java.util.Map;
/**
* Encryptor/Decryptor.
*/
public class PrometheusEncryptor {
/**
* Encrypted data conversion failure message.
*/
private static final String ERROR_BYTES_CONVERT = "Failed to convert value from bytes";
/**
* Invalid class error text.
*/
private static final String ERROR_CLASS = "Invalid Object Class for Encryption ";
/**
* Unsupported dataType error text.
*/
private static final String ERROR_DATATYPE = "Unsupported Data Type";
/**
* The Encryptor map.
*/
private static final Map<MetisDataType, PrometheusDataEncryptor> ENCRYPTORS = buildEncryptorMap();
/**
* The KeySet.
*/
private final GordianKeySet theKeySet;
/**
* The Data formatter.
*/
private final OceanusDataFormatter theFormatter;
/**
* Constructor.
*
* @param pFormatter the formatter
* @param pKeySet the keySet
*/
public PrometheusEncryptor(final OceanusDataFormatter pFormatter,
final GordianKeySet pKeySet) {
theFormatter = pFormatter;
theKeySet = pKeySet;
}
/**
* Obtain the keySet.
*
* @return the keySet
*/
public GordianKeySet getKeySet() {
return theKeySet;
}
/**
* Encrypt a value.
*
* @param pValue the value to encrypt.
* @return the encryptedBytes
* @throws OceanusException on error
*/
public byte[] encryptValue(final Object pValue) throws OceanusException {
/* Protect against exceptions */
try {
/* Access the encryptor */
final MetisDataType myDataType = getDataTypeForValue(pValue);
final PrometheusDataEncryptor myEncryptor = ENCRYPTORS.get(myDataType);
final byte[] myBytes = myEncryptor.convertValue(theFormatter, pValue);
return theKeySet == null ? null : theKeySet.encryptBytes(myBytes);
} catch (GordianException e) {
throw new PrometheusSecurityException(e);
}
}
/**
* Encrypt a value.
*
* @param pCurrent the current value
* @param pValue the value to encrypt.
* @return the encryptedPair.
* @throws OceanusException on error
*/
public PrometheusEncryptedPair encryptValue(final PrometheusEncryptedPair pCurrent,
final Object pValue) throws OceanusException {
/* If we are passed a null value just return null */
if (pValue == null) {
return null;
}
/* If we have no keySet or else a different keySet, ignore the current value */
PrometheusEncryptedPair myCurrent = pCurrent;
if (myCurrent != null
&& (theKeySet == null || !theKeySet.equals(myCurrent.getKeySet()))) {
myCurrent = null;
}
/* If the value is not changed return the current value */
if (myCurrent != null
&& MetisDataDifference.isEqual(myCurrent.getValue(), pValue)) {
return pCurrent;
}
/* Encrypt the data */
final byte[] myEncrypted = encryptValue(pValue);
return new PrometheusEncryptedPair(theKeySet, pValue, myEncrypted);
}
/**
* Encrypt a value.
*
* @param pValue the value to encrypt.
* @param pField the field definition
* @return the encryptedPair.
* @throws OceanusException on error
*/
public PrometheusEncryptedPair encryptValue(final Object pValue,
final MetisFieldDef pField) throws OceanusException {
/* Protect against exceptions */
try {
/* If we are passed a null value just return null */
if (pValue == null) {
return null;
}
/* Handle Context dataType */
MetisDataType myDataType = pField.getDataType();
if (myDataType == MetisDataType.CONTEXT) {
myDataType = getDataTypeForValue(pValue);
}
/* Access the encryptor */
final PrometheusDataEncryptor myEncryptor = ENCRYPTORS.get(myDataType);
if (myEncryptor == null) {
throw new PrometheusLogicException(ERROR_DATATYPE);
}
/* Encrypt the data */
final byte[] myBytes = myEncryptor.convertValue(theFormatter, pValue);
final byte[] myEncrypted = theKeySet.encryptBytes(myBytes);
return new PrometheusEncryptedPair(theKeySet, pValue, myEncrypted);
} catch (GordianException e) {
throw new PrometheusSecurityException(e);
}
}
/**
* Decrypt bytes.
*
* @param pBytes the bytes to decrypt.
* @param pField the field definition
* @return the encryptedPair.
* @throws OceanusException on error
*/
PrometheusEncryptedPair decryptValue(final byte[] pBytes,
final MetisFieldDef pField) throws OceanusException {
/* Protect agains exceptions */
try {
/* Access the encryptor */
final PrometheusDataEncryptor myEncryptor = ENCRYPTORS.get(pField.getDataType());
if (myEncryptor == null) {
throw new PrometheusLogicException(ERROR_DATATYPE);
}
/* Decrypt the data */
final byte[] myDecrypted = theKeySet.decryptBytes(pBytes);
final Object myValue = myEncryptor.parseValue(theFormatter, myDecrypted);
return new PrometheusEncryptedPair(theKeySet, myValue, pBytes);
} catch (GordianException e) {
throw new PrometheusSecurityException(e);
}
}
/**
* Decrypt bytes.
*
* @param pBytes the bytes to decrypt.
* @param pClazz the class to decrypt to
* @return the encryptedPair.
* @throws OceanusException on error
*/
PrometheusEncryptedPair decryptValue(final byte[] pBytes,
final Class<?> pClazz) throws OceanusException {
/* Protect agains exceptions */
try {
/* Access the encryptor */
final MetisDataType myDataType = getDataTypeForClass(pClazz);
final PrometheusDataEncryptor myEncryptor = ENCRYPTORS.get(myDataType);
if (myEncryptor == null) {
throw new PrometheusLogicException(ERROR_DATATYPE);
}
/* Decrypt the data */
final byte[] myDecrypted = theKeySet.decryptBytes(pBytes);
final Object myValue = myEncryptor.parseValue(theFormatter, myDecrypted);
return new PrometheusEncryptedPair(theKeySet, myValue, pBytes);
} catch (GordianException e) {
throw new PrometheusSecurityException(e);
}
}
/**
* Determine dataType.
*
* @param pValue the value
* @return the dataType
* @throws OceanusException on error
*/
public static MetisDataType getDataTypeForValue(final Object pValue) throws OceanusException {
if (pValue instanceof String) {
return MetisDataType.STRING;
}
if (pValue instanceof Short) {
return MetisDataType.SHORT;
}
if (pValue instanceof Integer) {
return MetisDataType.INTEGER;
}
if (pValue instanceof Long) {
return MetisDataType.LONG;
}
if (pValue instanceof Boolean) {
return MetisDataType.BOOLEAN;
}
if (pValue instanceof char[]) {
return MetisDataType.CHARARRAY;
}
/* Handle decimal instances */
if (pValue instanceof OceanusDate) {
return MetisDataType.DATE;
}
if (pValue instanceof OceanusUnits) {
return MetisDataType.UNITS;
}
if (pValue instanceof OceanusRate) {
return MetisDataType.RATE;
}
if (pValue instanceof OceanusPrice) {
return MetisDataType.PRICE;
}
if (pValue instanceof OceanusMoney) {
return MetisDataType.MONEY;
}
if (pValue instanceof OceanusRatio) {
return MetisDataType.RATIO;
}
/* Unsupported so reject */
throw new PrometheusLogicException(ERROR_CLASS
+ pValue.getClass().getCanonicalName());
}
/**
* Determine dataType.
*
* @param pClazz the class
* @return the dataType
* @throws OceanusException on error
*/
static MetisDataType getDataTypeForClass(final Class<?> pClazz) throws OceanusException {
if (String.class.equals(pClazz)) {
return MetisDataType.STRING;
}
if (Short.class.equals(pClazz)) {
return MetisDataType.SHORT;
}
if (Integer.class.equals(pClazz)) {
return MetisDataType.INTEGER;
}
if (Long.class.equals(pClazz)) {
return MetisDataType.LONG;
}
if (Boolean.class.equals(pClazz)) {
return MetisDataType.BOOLEAN;
}
if (char[].class.equals(pClazz)) {
return MetisDataType.CHARARRAY;
}
/* Handle decimal instances */
if (OceanusDate.class.equals(pClazz)) {
return MetisDataType.DATE;
}
if (OceanusUnits.class.equals(pClazz)) {
return MetisDataType.UNITS;
}
if (OceanusRate.class.equals(pClazz)) {
return MetisDataType.RATE;
}
if (OceanusPrice.class.equals(pClazz)) {
return MetisDataType.PRICE;
}
if (OceanusMoney.class.equals(pClazz)) {
return MetisDataType.MONEY;
}
if (OceanusRatio.class.equals(pClazz)) {
return MetisDataType.RATIO;
}
/* Unsupported so reject */
throw new PrometheusLogicException(ERROR_CLASS
+ pClazz.getCanonicalName());
}
/**
* Build the encryptor map.
*
* @return the map
*/
private static Map<MetisDataType, PrometheusDataEncryptor> buildEncryptorMap() {
final Map<MetisDataType, PrometheusDataEncryptor> myMap = new EnumMap<>(MetisDataType.class);
myMap.put(MetisDataType.DATE, new PrometheusDateEncryptor());
myMap.put(MetisDataType.SHORT, new PrometheusShortEncryptor());
myMap.put(MetisDataType.INTEGER, new PrometheusIntegerEncryptor());
myMap.put(MetisDataType.LONG, new PrometheusLongEncryptor());
myMap.put(MetisDataType.STRING, new PrometheusStringEncryptor());
myMap.put(MetisDataType.CHARARRAY, new PrometheusCharArrayEncryptor());
myMap.put(MetisDataType.BOOLEAN, new PrometheusBooleanEncryptor());
myMap.put(MetisDataType.MONEY, new PrometheusMoneyEncryptor());
myMap.put(MetisDataType.PRICE, new PrometheusPriceEncryptor());
myMap.put(MetisDataType.RATE, new PrometheusRateEncryptor());
myMap.put(MetisDataType.UNITS, new PrometheusUnitsEncryptor());
myMap.put(MetisDataType.RATIO, new PrometheusRatioEncryptor());
return myMap;
}
/**
* Adopt Encryption.
*
* @param pTarget the target field
* @param pSource the source field
* @throws OceanusException on error
*/
public void adoptEncryption(final PrometheusEncryptedPair pTarget,
final PrometheusEncryptedPair pSource) throws OceanusException {
/* Adopt the encryption */
pTarget.adoptEncryption(this, pSource);
}
/**
* Encryptor Base.
*/
private interface PrometheusDataEncryptor {
/**
* Convert a value to bytes.
*
* @param pFormatter the data formatter
* @param pValue the value to convert.
* @return the converted bytes.
* @throws OceanusException on error
*/
byte[] convertValue(OceanusDataFormatter pFormatter,
Object pValue) throws OceanusException;
/**
* Parse a value from bytes.
*
* @param pFormatter the data formatter
* @param pBytes the bytes to parse.
* @return the parsed value.
* @throws OceanusException on error
*/
Object parseValue(OceanusDataFormatter pFormatter,
byte[] pBytes) throws OceanusException;
}
/**
* DateEncryptor.
*/
private static final class PrometheusDateEncryptor
implements PrometheusDataEncryptor {
@Override
public byte[] convertValue(final OceanusDataFormatter pFormatter,
final Object pValue) {
return pFormatter.getDateFormatter().toBytes((OceanusDate) pValue);
}
@Override
public Object parseValue(final OceanusDataFormatter pFormatter,
final byte[] pBytes) throws OceanusException {
try {
return pFormatter.getDateFormatter().fromBytes(pBytes);
} catch (IllegalArgumentException e) {
throw new PrometheusDataException(ERROR_BYTES_CONVERT, e);
}
}
}
/**
* IntegerEncryptor.
*/
private static final class PrometheusShortEncryptor
implements PrometheusDataEncryptor {
@Override
public byte[] convertValue(final OceanusDataFormatter pFormatter,
final Object pValue) {
return OceanusDataConverter.shortToByteArray((short) pValue);
}
@Override
public Object parseValue(final OceanusDataFormatter pFormatter,
final byte[] pBytes) throws OceanusException {
return OceanusDataConverter.byteArrayToShort(pBytes);
}
}
/**
* IntegerEncryptor.
*/
private static final class PrometheusIntegerEncryptor
implements PrometheusDataEncryptor {
@Override
public byte[] convertValue(final OceanusDataFormatter pFormatter,
final Object pValue) {
return OceanusDataConverter.integerToByteArray((int) pValue);
}
@Override
public Object parseValue(final OceanusDataFormatter pFormatter,
final byte[] pBytes) throws OceanusException {
return OceanusDataConverter.byteArrayToInteger(pBytes);
}
}
/**
* LongEncryptor.
*/
private static final class PrometheusLongEncryptor
implements PrometheusDataEncryptor {
@Override
public byte[] convertValue(final OceanusDataFormatter pFormatter,
final Object pValue) {
return OceanusDataConverter.longToByteArray((long) pValue);
}
@Override
public Object parseValue(final OceanusDataFormatter pFormatter,
final byte[] pBytes) throws OceanusException {
return OceanusDataConverter.byteArrayToLong(pBytes);
}
}
/**
* BooleanEncryptor.
*/
private static final class PrometheusBooleanEncryptor
implements PrometheusDataEncryptor {
@Override
public byte[] convertValue(final OceanusDataFormatter pFormatter,
final Object pValue) {
return OceanusDataConverter.stringToByteArray(pValue.toString());
}
@Override
public Object parseValue(final OceanusDataFormatter pFormatter,
final byte[] pBytes) {
final String myBoolString = OceanusDataConverter.byteArrayToString(pBytes);
return Boolean.parseBoolean(myBoolString);
}
}
/**
* StringEncryptor.
*/
private static final class PrometheusStringEncryptor
implements PrometheusDataEncryptor {
@Override
public byte[] convertValue(final OceanusDataFormatter pFormatter,
final Object pValue) {
return OceanusDataConverter.stringToByteArray((String) pValue);
}
@Override
public Object parseValue(final OceanusDataFormatter pFormatter,
final byte[] pBytes) {
return OceanusDataConverter.byteArrayToString(pBytes);
}
}
/**
* CharArrayEncryptor.
*/
private static final class PrometheusCharArrayEncryptor
implements PrometheusDataEncryptor {
@Override
public byte[] convertValue(final OceanusDataFormatter pFormatter,
final Object pValue) throws OceanusException {
return OceanusDataConverter.charsToByteArray((char[]) pValue);
}
@Override
public Object parseValue(final OceanusDataFormatter pFormatter,
final byte[] pBytes) throws OceanusException {
return OceanusDataConverter.bytesToCharArray(pBytes);
}
}
/**
* DecimalEncryptor.
*/
private abstract static class PrometheusDecimalEncryptor
implements PrometheusDataEncryptor {
@Override
public byte[] convertValue(final OceanusDataFormatter pFormatter,
final Object pValue) {
return ((OceanusDecimal) pValue).toBytes();
}
}
/**
* MoneyEncryptor.
*/
private static final class PrometheusMoneyEncryptor
extends PrometheusDecimalEncryptor {
@Override
public Object parseValue(final OceanusDataFormatter pFormatter,
final byte[] pBytes) throws OceanusException {
return new OceanusMoney(pBytes);
}
}
/**
* PriceEncryptor.
*/
private static final class PrometheusPriceEncryptor
extends PrometheusDecimalEncryptor {
@Override
public Object parseValue(final OceanusDataFormatter pFormatter,
final byte[] pBytes) throws OceanusException {
return new OceanusPrice(pBytes);
}
}
/**
* RatioEncryptor.
*/
private static final class PrometheusRatioEncryptor
extends PrometheusDecimalEncryptor {
@Override
public Object parseValue(final OceanusDataFormatter pFormatter,
final byte[] pBytes) throws OceanusException {
return new OceanusRatio(pBytes);
}
}
/**
* UnitsEncryptor.
*/
private static final class PrometheusUnitsEncryptor
extends PrometheusDecimalEncryptor {
@Override
public Object parseValue(final OceanusDataFormatter pFormatter,
final byte[] pBytes) throws OceanusException {
return new OceanusUnits(pBytes);
}
}
/**
* RateEncryptor.
*/
private static final class PrometheusRateEncryptor
extends PrometheusDecimalEncryptor {
@Override
public Object parseValue(final OceanusDataFormatter pFormatter,
final byte[] pBytes) throws OceanusException {
return new OceanusRate(pBytes);
}
}
}