PrometheusControlKeySet.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.factory.GordianFactory;
import io.github.tonywasher.joceanus.gordianknot.api.keyset.GordianKeySet;
import io.github.tonywasher.joceanus.gordianknot.api.keyset.GordianKeySetFactory;
import io.github.tonywasher.joceanus.gordianknot.util.GordianUtilities;
import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataList;
import io.github.tonywasher.joceanus.metis.data.MetisDataResource;
import io.github.tonywasher.joceanus.metis.field.MetisFieldItem;
import io.github.tonywasher.joceanus.metis.field.MetisFieldSet;
import io.github.tonywasher.joceanus.metis.field.MetisFieldVersionedSet;
import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataKeySet.PrometheusDataKeySetList;
import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataList.PrometheusListStyle;
import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataSet.PrometheusCryptographyDataType;
import io.github.tonywasher.joceanus.prometheus.exc.PrometheusDataException;
import io.github.tonywasher.joceanus.prometheus.exc.PrometheusSecurityException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
/**
* ControlKeySet definition and list. The controlKeySet secures a set of dataKeySets.
*
* @author Tony Washer
*/
public class PrometheusControlKeySet
extends PrometheusDataItem {
/**
* Object name.
*/
public static final String OBJECT_NAME = PrometheusCryptographyDataType.CONTROLKEYSET.getItemName();
/**
* List name.
*/
public static final String LIST_NAME = PrometheusCryptographyDataType.CONTROLKEYSET.getListName();
/**
* KeySetWrapLength.
*/
public static final int WRAPLEN = GordianUtilities.getMaximumKeySetWrapLength();
/**
* Report fields.
*/
private static final MetisFieldVersionedSet<PrometheusControlKeySet> FIELD_DEFS = MetisFieldVersionedSet.newVersionedFieldSet(PrometheusControlKeySet.class);
/*
* FieldIds.
*/
static {
FIELD_DEFS.declareLinkField(PrometheusCryptographyDataType.CONTROLKEY);
FIELD_DEFS.declareByteArrayField(PrometheusDataResource.KEYSET_KEYSETDEF, WRAPLEN);
FIELD_DEFS.declareDerivedVersionedField(PrometheusDataResource.KEYSET_KEYSET);
FIELD_DEFS.declareLocalField(PrometheusDataResource.DATAKEYSET_LIST, PrometheusControlKeySet::getDataKeySets);
}
/**
* The DataKeySetCache.
*/
private DataKeySetCache theKeySetCache = new DataKeySetCache();
/**
* Copy Constructor.
*
* @param pList the list the copy belongs to
* @param pSource The Key to copy
*/
protected PrometheusControlKeySet(final PrometheusControlKeySetList pList,
final PrometheusControlKeySet pSource) {
/* Set standard values */
super(pList, pSource);
/* Switch on the LinkStyle */
if (Objects.requireNonNull(getStyle()) == PrometheusListStyle.CLONE) {
final GordianKeySet myKeySet = pSource.getKeySet();
setValueKeySet(myKeySet);
}
}
/**
* Values constructor.
*
* @param pList the List to add to
* @param pValues the values constructor
* @throws OceanusException on error
*/
private PrometheusControlKeySet(final PrometheusControlKeySetList pList,
final PrometheusDataValues pValues) throws OceanusException {
/* Initialise the item */
super(pList, pValues);
/* Access the DataSet */
final PrometheusDataSet myData = getDataSet();
/* Store the ControlKey */
Object myValue = pValues.getValue(PrometheusCryptographyDataType.CONTROLKEY);
if (myValue instanceof Integer i) {
/* Store the integer */
setValueControlKey(i);
/* Resolve the ControlKey */
resolveDataLink(PrometheusCryptographyDataType.CONTROLKEY, myData.getControlKeys());
} else if (myValue instanceof PrometheusControlKey k) {
/* Store the controlKey */
setValueControlKey(k);
}
/* Access the controlKey */
final PrometheusControlKey myControl = getControlKey();
/* Store the WrappedKeySetDef */
myValue = pValues.getValue(PrometheusDataResource.KEYSET_KEYSETDEF);
if (myValue instanceof byte[] ba) {
setValueSecuredKeySetDef(ba);
}
/* Store/Resolve the keySet */
myValue = pValues.getValue(PrometheusDataResource.KEYSET_KEYSET);
if (myValue instanceof GordianKeySet ks) {
setValueKeySet(ks);
} else if (getSecuredKeySetDef() != null) {
/* Protect against exceptions */
try {
final GordianKeySet myKeySet = getSecurityFactory().getEmbeddedKeySet().deriveKeySet(getSecuredKeySetDef());
setValueKeySet(myKeySet);
} catch (GordianException e) {
throw new PrometheusSecurityException(e);
}
}
/* Register the DataKeySet */
myControl.registerControlKeySet(this);
}
/**
* Constructor for a new DataKeySet.
*
* @param pList the list to which to add the keySet to
* @param pControlKey the control key
* @throws OceanusException on error
*/
protected PrometheusControlKeySet(final PrometheusControlKeySetList pList,
final PrometheusControlKey pControlKey) throws OceanusException {
/* Initialise the item */
super(pList, 0);
/* Protect against exceptions */
try {
/* Store the Details */
setValueControlKey(pControlKey);
/* Access the Security manager */
final PrometheusDataSet myData = getDataSet();
/* Create the KeySet */
final GordianFactory myFactory = getSecurityFactory();
final GordianKeySetFactory myKeySets = myFactory.getKeySetFactory();
final GordianKeySet myKeySet = myKeySets.generateKeySet(getDataSet().getKeySetSpec());
setValueKeySet(myKeySet);
/* Set the wrappedKeySetDef */
setValueSecuredKeySetDef(myFactory.getEmbeddedKeySet().secureKeySet(myKeySet));
/* Allocate the DataKeySets */
allocateDataKeySets(myData);
/* Catch Exceptions */
} catch (GordianException
| OceanusException e) {
/* Pass on exception */
throw new PrometheusDataException(this, ERROR_CREATEITEM, e);
}
}
@Override
public MetisFieldSetDef getDataFieldSet() {
return FIELD_DEFS;
}
/**
* Obtain the security factory.
*
* @return the security factory
*/
GordianFactory getSecurityFactory() {
final PrometheusControlKey myControl = getControlKey();
return myControl == null ? null : myControl.getSecurityFactory();
}
/**
* Obtain the dataKeySetCache.
*
* @return the dataKeySets
*/
private DataKeySetCache getDataKeySets() {
return theKeySetCache;
}
/**
* Obtain the next DataKeySet.
*
* @return the next dataKeySet
*/
PrometheusDataKeySet getNextDataKeySet() {
return theKeySetCache.getNextDataKeySet();
}
/**
* Get the ControlKey.
*
* @return the controlKey
*/
public final PrometheusControlKey getControlKey() {
return getValues().getValue(PrometheusCryptographyDataType.CONTROLKEY, PrometheusControlKey.class);
}
/**
* Get the ControlKeyId for this item.
*
* @return the ControlKeyId
*/
public Integer getControlKeyId() {
final PrometheusControlKey myKey = getControlKey();
return myKey == null
? null
: myKey.getIndexedId();
}
/**
* Get the securedKeySetDef.
*
* @return the securedKeySetDef
*/
public final byte[] getSecuredKeySetDef() {
return getValues().getValue(PrometheusDataResource.KEYSET_KEYSETDEF, byte[].class);
}
/**
* Get the KeySet.
*
* @return the keySet
*/
public GordianKeySet getKeySet() {
return getValues().getValue(PrometheusDataResource.KEYSET_KEYSET, GordianKeySet.class);
}
/**
* Set the ControlKey Id.
*
* @param pId the controlKey id
* @throws OceanusException on error
*/
private void setValueControlKey(final Integer pId) throws OceanusException {
getValues().setValue(PrometheusCryptographyDataType.CONTROLKEY, pId);
}
/**
* Set the ControlKey.
*
* @param pKey the controlKey
* @throws OceanusException on error
*/
private void setValueControlKey(final PrometheusControlKey pKey) throws OceanusException {
getValues().setValue(PrometheusCryptographyDataType.CONTROLKEY, pKey);
}
/**
* Set the securedKeySetDef.
*
* @param pValue the securedKeySetDef
*/
private void setValueSecuredKeySetDef(final byte[] pValue) {
getValues().setUncheckedValue(PrometheusDataResource.KEYSET_KEYSETDEF, pValue);
}
/**
* Set the keySet.
*
* @param pValue the keySet
*/
private void setValueKeySet(final GordianKeySet pValue) {
getValues().setUncheckedValue(PrometheusDataResource.KEYSET_KEYSET, pValue);
}
@Override
public PrometheusControlKeySet getBase() {
return (PrometheusControlKeySet) super.getBase();
}
@Override
public PrometheusControlKeySetList getList() {
return (PrometheusControlKeySetList) super.getList();
}
@Override
public int compareValues(final PrometheusDataItem pThat) {
/* Only sort on id */
return 0;
}
@Override
public void resolveDataSetLinks() throws OceanusException {
/* Resolve the ControlKey */
final PrometheusDataSet myData = getDataSet();
resolveDataLink(PrometheusCryptographyDataType.CONTROLKEY, myData.getControlKeys());
final PrometheusControlKey myControlKey = getControlKey();
/* Register the KeySet */
myControlKey.registerControlKeySet(this);
}
/**
* Allocate a new DataKeySet.
*
* @param pData the DataSet
* @throws OceanusException on error
*/
private void allocateDataKeySets(final PrometheusDataSet pData) throws OceanusException {
/* Access the DataKeySet List */
final PrometheusDataKeySetList mySets = pData.getDataKeySets();
setNewVersion();
/* Loop to create sufficient DataKeySets */
final int myNumKeySets = pData.getNumActiveKeySets();
for (int i = 0; i < myNumKeySets; i++) {
/* Allocate the DataKeySet */
final PrometheusDataKeySet mySet = new PrometheusDataKeySet(mySets, this);
mySet.setNewVersion();
mySets.add(mySet);
/* Register the DataKeySet */
theKeySetCache.registerDataKeySet(mySet);
}
}
/**
* Delete the old ControlKeySet and DataKeySets.
*/
protected void deleteControlKeySet() {
/* Mark this dataKeySet as deleted */
setDeleted(true);
/* Delete the dataKeySets */
theKeySetCache.deleteDataKeySets();
}
/**
* Register DataKeySet.
*
* @param pKeySet the DataKeySet to register
*/
void registerDataKeySet(final PrometheusDataKeySet pKeySet) {
/* Store the DataKey into the map */
theKeySetCache.registerDataKeySet(pKeySet);
}
/**
* DataKeySet List.
*/
public static class PrometheusControlKeySetList
extends PrometheusDataList<PrometheusControlKeySet> {
/**
* Report fields.
*/
private static final MetisFieldSet<PrometheusControlKeySetList> FIELD_DEFS = MetisFieldSet.newFieldSet(PrometheusControlKeySetList.class);
/**
* Construct an empty CORE list.
*
* @param pData the DataSet for the list
*/
protected PrometheusControlKeySetList(final PrometheusDataSet pData) {
this(pData, PrometheusListStyle.CORE);
}
/**
* Construct an empty generic ControlKey list.
*
* @param pData the DataSet for the list
* @param pStyle the style of the list
*/
protected PrometheusControlKeySetList(final PrometheusDataSet pData,
final PrometheusListStyle pStyle) {
super(PrometheusControlKeySet.class, pData, PrometheusCryptographyDataType.CONTROLKEYSET, pStyle);
}
/**
* Constructor for a cloned List.
*
* @param pSource the source List
*/
private PrometheusControlKeySetList(final PrometheusControlKeySetList pSource) {
super(pSource);
}
@Override
public MetisFieldSet<PrometheusControlKeySetList> getDataFieldSet() {
return FIELD_DEFS;
}
@Override
public String listName() {
return LIST_NAME;
}
@Override
public MetisFieldSet<PrometheusControlKeySet> getItemFields() {
return PrometheusControlKeySet.FIELD_DEFS;
}
@Override
public boolean includeDataXML() {
return false;
}
@Override
protected PrometheusControlKeySetList getEmptyList(final PrometheusListStyle pStyle) {
final PrometheusControlKeySetList myList = new PrometheusControlKeySetList(this);
myList.setStyle(pStyle);
return myList;
}
@Override
public PrometheusControlKeySetList deriveList(final PrometheusListStyle pStyle) throws OceanusException {
return (PrometheusControlKeySetList) super.deriveList(pStyle);
}
@Override
public PrometheusControlKeySetList deriveDifferences(final PrometheusDataSet pDataSet,
final PrometheusDataList<?> pOld) {
return (PrometheusControlKeySetList) super.deriveDifferences(pDataSet, pOld);
}
@Override
public PrometheusControlKeySet addCopyItem(final PrometheusDataItem pItem) {
/* Can only clone a DataKeySet */
if (!(pItem instanceof PrometheusControlKeySet)) {
return null;
}
/* Clone the control key set */
final PrometheusControlKeySet mySet = new PrometheusControlKeySet(this, (PrometheusControlKeySet) pItem);
add(mySet);
return mySet;
}
@Override
public PrometheusControlKeySet addNewItem() {
throw new UnsupportedOperationException();
}
@Override
public PrometheusControlKeySet addValuesItem(final PrometheusDataValues pValues) throws OceanusException {
/* Create the dataKeySet */
final PrometheusControlKeySet mySet = new PrometheusControlKeySet(this, pValues);
/* Check that this keyId has not been previously added */
if (!isIdUnique(mySet.getIndexedId())) {
mySet.addError(ERROR_DUPLICATE, MetisDataResource.DATA_ID);
throw new PrometheusDataException(mySet, ERROR_VALIDATION);
}
/* Add to the list */
add(mySet);
/* Return it */
return mySet;
}
/**
* Clone KeySet from a DataBase.
*
* @param pControlKey the ControlKey to clone
* @param pKeySet the DataKeySet to clone
* @return the new DataKeySet
* @throws OceanusException on error
*/
protected PrometheusControlKeySet cloneControlKeySet(final PrometheusControlKey pControlKey,
final PrometheusControlKeySet pKeySet) throws OceanusException {
/* Build data values */
final PrometheusDataValues myValues = new PrometheusDataValues(OBJECT_NAME);
myValues.addValue(MetisDataResource.DATA_ID, pKeySet.getIndexedId());
myValues.addValue(PrometheusCryptographyDataType.CONTROLKEY, pControlKey);
myValues.addValue(PrometheusDataResource.KEYSET_KEYSETDEF, pKeySet.getSecuredKeySetDef());
myValues.addValue(PrometheusDataResource.KEYSET_KEYSET, pKeySet.getKeySet());
/* Clone the controlKeySet */
final PrometheusControlKeySet mySet = addValuesItem(myValues);
/* Access the ControlKeySet List */
final PrometheusDataSet myData = getDataSet();
final PrometheusDataKeySetList myKeySets = myData.getDataKeySets();
/* Create a new DataKeySetCache for this ControlKeySet */
final DataKeySetCache mySource = pKeySet.getDataKeySets();
mySet.theKeySetCache = mySource.cloneDataKeySetCache(mySet, myKeySets);
return mySet;
}
@Override
public void postProcessOnLoad() throws OceanusException {
/* Just sort the list */
reSort();
}
@Override
protected PrometheusDataMapItem allocateDataMap() {
/* Unused */
throw new UnsupportedOperationException();
}
}
/**
* DataKeySetCache.
*/
private static final class DataKeySetCache
implements MetisFieldItem, MetisDataList<PrometheusDataKeySet> {
/**
* Report fields.
*/
private static final MetisFieldSet<DataKeySetCache> FIELD_DEFS = MetisFieldSet.newFieldSet(DataKeySetCache.class);
/*
* Size Field Id.
*/
static {
FIELD_DEFS.declareLocalField(MetisDataResource.LIST_SIZE, DataKeySetCache::size);
}
/**
* The list.
*/
private final List<PrometheusDataKeySet> theList;
/**
* Iterator.
*/
private Iterator<PrometheusDataKeySet> theIterator;
/**
* Constructor.
*/
DataKeySetCache() {
theList = new ArrayList<>();
}
@Override
public MetisFieldSet<DataKeySetCache> getDataFieldSet() {
return FIELD_DEFS;
}
@Override
public List<PrometheusDataKeySet> getUnderlyingList() {
return theList;
}
@Override
public String formatObject(final OceanusDataFormatter pFormatter) {
return getDataFieldSet().getName();
}
/**
* Register the KeySet.
*
* @param pKeySet the KeySet to register
*/
private void registerDataKeySet(final PrometheusDataKeySet pKeySet) {
/* If this is first registration */
if (!theList.contains(pKeySet)) {
/* Add the KeySet */
theList.add(pKeySet);
/* Reset any iterator */
if (theIterator != null) {
theIterator = iterator();
}
}
}
/**
* Get next DataKeySet.
*
* @return the next KeySet
*/
private PrometheusDataKeySet getNextDataKeySet() {
/* Handle empty list */
if (isEmpty()) {
return null;
}
/* Handle initialisation and wrapping */
if (theIterator == null
|| !theIterator.hasNext()) {
theIterator = iterator();
}
/* Return the next KeySet */
return theIterator.next();
}
/**
* Delete the KeySets.
*/
private void deleteDataKeySets() {
/* Loop through the KeySets */
final Iterator<PrometheusDataKeySet> myIterator = iterator();
while (myIterator.hasNext()) {
final PrometheusDataKeySet mySet = myIterator.next();
/* Delete the KeySet */
mySet.deleteDataKeySet();
}
}
/**
* Clone dataKeySet Cache from a DataBase.
*
* @param pControlKeySet the ControlKeySet to clone
* @param pKeySets the DataKeySetList
* @return the new DataKeySetCache
* @throws OceanusException on error
*/
private DataKeySetCache cloneDataKeySetCache(final PrometheusControlKeySet pControlKeySet,
final PrometheusDataKeySetList pKeySets) throws OceanusException {
/* Create a new cache */
final DataKeySetCache myCache = new DataKeySetCache();
/* Loop through the KeySets */
final Iterator<PrometheusDataKeySet> myIterator = iterator();
while (myIterator.hasNext()) {
final PrometheusDataKeySet mySet = myIterator.next();
/* Create a new DataKeySet for this ControlKeySet */
final PrometheusDataKeySet myNewSet = pKeySets.cloneDataKeySet(pControlKeySet, mySet);
myCache.registerDataKeySet(myNewSet);
}
/* Return the cache */
return myCache;
}
}
}