View Javadoc
1   /*
2    * Prometheus: Application Framework
3    * Copyright 2012-2026. Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6    * use this file except in compliance with the License.  You may obtain a copy
7    * of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
14   * License for the specific language governing permissions and limitations under
15   * the License.
16   */
17  package io.github.tonywasher.joceanus.prometheus.data;
18  
19  import io.github.tonywasher.joceanus.gordianknot.api.base.GordianException;
20  import io.github.tonywasher.joceanus.gordianknot.api.factory.GordianFactory;
21  import io.github.tonywasher.joceanus.gordianknot.api.keyset.GordianKeySet;
22  import io.github.tonywasher.joceanus.gordianknot.api.keyset.GordianKeySetFactory;
23  import io.github.tonywasher.joceanus.gordianknot.util.GordianUtilities;
24  import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataList;
25  import io.github.tonywasher.joceanus.metis.data.MetisDataResource;
26  import io.github.tonywasher.joceanus.metis.field.MetisFieldItem;
27  import io.github.tonywasher.joceanus.metis.field.MetisFieldSet;
28  import io.github.tonywasher.joceanus.metis.field.MetisFieldVersionedSet;
29  import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
30  import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
31  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataKeySet.PrometheusDataKeySetList;
32  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataList.PrometheusListStyle;
33  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataSet.PrometheusCryptographyDataType;
34  import io.github.tonywasher.joceanus.prometheus.exc.PrometheusDataException;
35  import io.github.tonywasher.joceanus.prometheus.exc.PrometheusSecurityException;
36  
37  import java.util.ArrayList;
38  import java.util.Iterator;
39  import java.util.List;
40  import java.util.Objects;
41  
42  /**
43   * ControlKeySet definition and list. The controlKeySet secures a set of dataKeySets.
44   *
45   * @author Tony Washer
46   */
47  public class PrometheusControlKeySet
48          extends PrometheusDataItem {
49      /**
50       * Object name.
51       */
52      public static final String OBJECT_NAME = PrometheusCryptographyDataType.CONTROLKEYSET.getItemName();
53  
54      /**
55       * List name.
56       */
57      public static final String LIST_NAME = PrometheusCryptographyDataType.CONTROLKEYSET.getListName();
58  
59      /**
60       * KeySetWrapLength.
61       */
62      public static final int WRAPLEN = GordianUtilities.getMaximumKeySetWrapLength();
63  
64      /**
65       * Report fields.
66       */
67      private static final MetisFieldVersionedSet<PrometheusControlKeySet> FIELD_DEFS = MetisFieldVersionedSet.newVersionedFieldSet(PrometheusControlKeySet.class);
68  
69      /*
70       * FieldIds.
71       */
72      static {
73          FIELD_DEFS.declareLinkField(PrometheusCryptographyDataType.CONTROLKEY);
74          FIELD_DEFS.declareByteArrayField(PrometheusDataResource.KEYSET_KEYSETDEF, WRAPLEN);
75          FIELD_DEFS.declareDerivedVersionedField(PrometheusDataResource.KEYSET_KEYSET);
76          FIELD_DEFS.declareLocalField(PrometheusDataResource.DATAKEYSET_LIST, PrometheusControlKeySet::getDataKeySets);
77      }
78  
79      /**
80       * The DataKeySetCache.
81       */
82      private DataKeySetCache theKeySetCache = new DataKeySetCache();
83  
84      /**
85       * Copy Constructor.
86       *
87       * @param pList   the list the copy belongs to
88       * @param pSource The Key to copy
89       */
90      protected PrometheusControlKeySet(final PrometheusControlKeySetList pList,
91                                        final PrometheusControlKeySet pSource) {
92          /* Set standard values */
93          super(pList, pSource);
94  
95          /* Switch on the LinkStyle */
96          if (Objects.requireNonNull(getStyle()) == PrometheusListStyle.CLONE) {
97              final GordianKeySet myKeySet = pSource.getKeySet();
98              setValueKeySet(myKeySet);
99          }
100     }
101 
102     /**
103      * Values constructor.
104      *
105      * @param pList   the List to add to
106      * @param pValues the values constructor
107      * @throws OceanusException on error
108      */
109     private PrometheusControlKeySet(final PrometheusControlKeySetList pList,
110                                     final PrometheusDataValues pValues) throws OceanusException {
111         /* Initialise the item */
112         super(pList, pValues);
113 
114         /* Access the DataSet */
115         final PrometheusDataSet myData = getDataSet();
116 
117         /* Store the ControlKey */
118         Object myValue = pValues.getValue(PrometheusCryptographyDataType.CONTROLKEY);
119         if (myValue instanceof Integer i) {
120             /* Store the integer */
121             setValueControlKey(i);
122 
123             /* Resolve the ControlKey */
124             resolveDataLink(PrometheusCryptographyDataType.CONTROLKEY, myData.getControlKeys());
125         } else if (myValue instanceof PrometheusControlKey k) {
126             /* Store the controlKey */
127             setValueControlKey(k);
128         }
129 
130         /* Access the controlKey */
131         final PrometheusControlKey myControl = getControlKey();
132 
133         /* Store the WrappedKeySetDef */
134         myValue = pValues.getValue(PrometheusDataResource.KEYSET_KEYSETDEF);
135         if (myValue instanceof byte[] ba) {
136             setValueSecuredKeySetDef(ba);
137         }
138 
139         /* Store/Resolve the keySet */
140         myValue = pValues.getValue(PrometheusDataResource.KEYSET_KEYSET);
141         if (myValue instanceof GordianKeySet ks) {
142             setValueKeySet(ks);
143         } else if (getSecuredKeySetDef() != null) {
144             /* Protect against exceptions */
145             try {
146                 final GordianKeySet myKeySet = getSecurityFactory().getEmbeddedKeySet().deriveKeySet(getSecuredKeySetDef());
147                 setValueKeySet(myKeySet);
148             } catch (GordianException e) {
149                 throw new PrometheusSecurityException(e);
150             }
151         }
152 
153         /* Register the DataKeySet */
154         myControl.registerControlKeySet(this);
155     }
156 
157     /**
158      * Constructor for a new DataKeySet.
159      *
160      * @param pList       the list to which to add the keySet to
161      * @param pControlKey the control key
162      * @throws OceanusException on error
163      */
164     protected PrometheusControlKeySet(final PrometheusControlKeySetList pList,
165                                       final PrometheusControlKey pControlKey) throws OceanusException {
166         /* Initialise the item */
167         super(pList, 0);
168 
169         /* Protect against exceptions */
170         try {
171             /* Store the Details */
172             setValueControlKey(pControlKey);
173 
174             /* Access the Security manager */
175             final PrometheusDataSet myData = getDataSet();
176 
177             /* Create the KeySet */
178             final GordianFactory myFactory = getSecurityFactory();
179             final GordianKeySetFactory myKeySets = myFactory.getKeySetFactory();
180             final GordianKeySet myKeySet = myKeySets.generateKeySet(getDataSet().getKeySetSpec());
181             setValueKeySet(myKeySet);
182 
183             /* Set the wrappedKeySetDef */
184             setValueSecuredKeySetDef(myFactory.getEmbeddedKeySet().secureKeySet(myKeySet));
185 
186             /* Allocate the DataKeySets */
187             allocateDataKeySets(myData);
188 
189             /* Catch Exceptions */
190         } catch (GordianException
191                  | OceanusException e) {
192             /* Pass on exception */
193             throw new PrometheusDataException(this, ERROR_CREATEITEM, e);
194         }
195     }
196 
197     @Override
198     public MetisFieldSetDef getDataFieldSet() {
199         return FIELD_DEFS;
200     }
201 
202     /**
203      * Obtain the security factory.
204      *
205      * @return the security factory
206      */
207     GordianFactory getSecurityFactory() {
208         final PrometheusControlKey myControl = getControlKey();
209         return myControl == null ? null : myControl.getSecurityFactory();
210     }
211 
212     /**
213      * Obtain the dataKeySetCache.
214      *
215      * @return the dataKeySets
216      */
217     private DataKeySetCache getDataKeySets() {
218         return theKeySetCache;
219     }
220 
221     /**
222      * Obtain the next DataKeySet.
223      *
224      * @return the next dataKeySet
225      */
226     PrometheusDataKeySet getNextDataKeySet() {
227         return theKeySetCache.getNextDataKeySet();
228     }
229 
230     /**
231      * Get the ControlKey.
232      *
233      * @return the controlKey
234      */
235     public final PrometheusControlKey getControlKey() {
236         return getValues().getValue(PrometheusCryptographyDataType.CONTROLKEY, PrometheusControlKey.class);
237     }
238 
239     /**
240      * Get the ControlKeyId for this item.
241      *
242      * @return the ControlKeyId
243      */
244     public Integer getControlKeyId() {
245         final PrometheusControlKey myKey = getControlKey();
246         return myKey == null
247                 ? null
248                 : myKey.getIndexedId();
249     }
250 
251     /**
252      * Get the securedKeySetDef.
253      *
254      * @return the securedKeySetDef
255      */
256     public final byte[] getSecuredKeySetDef() {
257         return getValues().getValue(PrometheusDataResource.KEYSET_KEYSETDEF, byte[].class);
258     }
259 
260     /**
261      * Get the KeySet.
262      *
263      * @return the keySet
264      */
265     public GordianKeySet getKeySet() {
266         return getValues().getValue(PrometheusDataResource.KEYSET_KEYSET, GordianKeySet.class);
267     }
268 
269     /**
270      * Set the ControlKey Id.
271      *
272      * @param pId the controlKey id
273      * @throws OceanusException on error
274      */
275     private void setValueControlKey(final Integer pId) throws OceanusException {
276         getValues().setValue(PrometheusCryptographyDataType.CONTROLKEY, pId);
277     }
278 
279     /**
280      * Set the ControlKey.
281      *
282      * @param pKey the controlKey
283      * @throws OceanusException on error
284      */
285     private void setValueControlKey(final PrometheusControlKey pKey) throws OceanusException {
286         getValues().setValue(PrometheusCryptographyDataType.CONTROLKEY, pKey);
287     }
288 
289     /**
290      * Set the securedKeySetDef.
291      *
292      * @param pValue the securedKeySetDef
293      */
294     private void setValueSecuredKeySetDef(final byte[] pValue) {
295         getValues().setUncheckedValue(PrometheusDataResource.KEYSET_KEYSETDEF, pValue);
296     }
297 
298     /**
299      * Set the keySet.
300      *
301      * @param pValue the keySet
302      */
303     private void setValueKeySet(final GordianKeySet pValue) {
304         getValues().setUncheckedValue(PrometheusDataResource.KEYSET_KEYSET, pValue);
305     }
306 
307     @Override
308     public PrometheusControlKeySet getBase() {
309         return (PrometheusControlKeySet) super.getBase();
310     }
311 
312     @Override
313     public PrometheusControlKeySetList getList() {
314         return (PrometheusControlKeySetList) super.getList();
315     }
316 
317     @Override
318     public int compareValues(final PrometheusDataItem pThat) {
319         /* Only sort on id */
320         return 0;
321     }
322 
323     @Override
324     public void resolveDataSetLinks() throws OceanusException {
325         /* Resolve the ControlKey */
326         final PrometheusDataSet myData = getDataSet();
327         resolveDataLink(PrometheusCryptographyDataType.CONTROLKEY, myData.getControlKeys());
328         final PrometheusControlKey myControlKey = getControlKey();
329 
330         /* Register the KeySet */
331         myControlKey.registerControlKeySet(this);
332     }
333 
334     /**
335      * Allocate a new DataKeySet.
336      *
337      * @param pData the DataSet
338      * @throws OceanusException on error
339      */
340     private void allocateDataKeySets(final PrometheusDataSet pData) throws OceanusException {
341         /* Access the DataKeySet List */
342         final PrometheusDataKeySetList mySets = pData.getDataKeySets();
343         setNewVersion();
344 
345         /* Loop to create sufficient DataKeySets */
346         final int myNumKeySets = pData.getNumActiveKeySets();
347         for (int i = 0; i < myNumKeySets; i++) {
348             /* Allocate the DataKeySet */
349             final PrometheusDataKeySet mySet = new PrometheusDataKeySet(mySets, this);
350             mySet.setNewVersion();
351             mySets.add(mySet);
352 
353             /* Register the DataKeySet */
354             theKeySetCache.registerDataKeySet(mySet);
355         }
356     }
357 
358     /**
359      * Delete the old ControlKeySet and DataKeySets.
360      */
361     protected void deleteControlKeySet() {
362         /* Mark this dataKeySet as deleted */
363         setDeleted(true);
364 
365         /* Delete the dataKeySets */
366         theKeySetCache.deleteDataKeySets();
367     }
368 
369     /**
370      * Register DataKeySet.
371      *
372      * @param pKeySet the DataKeySet to register
373      */
374     void registerDataKeySet(final PrometheusDataKeySet pKeySet) {
375         /* Store the DataKey into the map */
376         theKeySetCache.registerDataKeySet(pKeySet);
377     }
378 
379     /**
380      * DataKeySet List.
381      */
382     public static class PrometheusControlKeySetList
383             extends PrometheusDataList<PrometheusControlKeySet> {
384         /**
385          * Report fields.
386          */
387         private static final MetisFieldSet<PrometheusControlKeySetList> FIELD_DEFS = MetisFieldSet.newFieldSet(PrometheusControlKeySetList.class);
388 
389         /**
390          * Construct an empty CORE list.
391          *
392          * @param pData the DataSet for the list
393          */
394         protected PrometheusControlKeySetList(final PrometheusDataSet pData) {
395             this(pData, PrometheusListStyle.CORE);
396         }
397 
398         /**
399          * Construct an empty generic ControlKey list.
400          *
401          * @param pData  the DataSet for the list
402          * @param pStyle the style of the list
403          */
404         protected PrometheusControlKeySetList(final PrometheusDataSet pData,
405                                               final PrometheusListStyle pStyle) {
406             super(PrometheusControlKeySet.class, pData, PrometheusCryptographyDataType.CONTROLKEYSET, pStyle);
407         }
408 
409         /**
410          * Constructor for a cloned List.
411          *
412          * @param pSource the source List
413          */
414         private PrometheusControlKeySetList(final PrometheusControlKeySetList pSource) {
415             super(pSource);
416         }
417 
418         @Override
419         public MetisFieldSet<PrometheusControlKeySetList> getDataFieldSet() {
420             return FIELD_DEFS;
421         }
422 
423         @Override
424         public String listName() {
425             return LIST_NAME;
426         }
427 
428         @Override
429         public MetisFieldSet<PrometheusControlKeySet> getItemFields() {
430             return PrometheusControlKeySet.FIELD_DEFS;
431         }
432 
433         @Override
434         public boolean includeDataXML() {
435             return false;
436         }
437 
438         @Override
439         protected PrometheusControlKeySetList getEmptyList(final PrometheusListStyle pStyle) {
440             final PrometheusControlKeySetList myList = new PrometheusControlKeySetList(this);
441             myList.setStyle(pStyle);
442             return myList;
443         }
444 
445         @Override
446         public PrometheusControlKeySetList deriveList(final PrometheusListStyle pStyle) throws OceanusException {
447             return (PrometheusControlKeySetList) super.deriveList(pStyle);
448         }
449 
450         @Override
451         public PrometheusControlKeySetList deriveDifferences(final PrometheusDataSet pDataSet,
452                                                              final PrometheusDataList<?> pOld) {
453             return (PrometheusControlKeySetList) super.deriveDifferences(pDataSet, pOld);
454         }
455 
456         @Override
457         public PrometheusControlKeySet addCopyItem(final PrometheusDataItem pItem) {
458             /* Can only clone a DataKeySet */
459             if (!(pItem instanceof PrometheusControlKeySet)) {
460                 return null;
461             }
462 
463             /* Clone the control key set */
464             final PrometheusControlKeySet mySet = new PrometheusControlKeySet(this, (PrometheusControlKeySet) pItem);
465             add(mySet);
466             return mySet;
467         }
468 
469         @Override
470         public PrometheusControlKeySet addNewItem() {
471             throw new UnsupportedOperationException();
472         }
473 
474         @Override
475         public PrometheusControlKeySet addValuesItem(final PrometheusDataValues pValues) throws OceanusException {
476             /* Create the dataKeySet */
477             final PrometheusControlKeySet mySet = new PrometheusControlKeySet(this, pValues);
478 
479             /* Check that this keyId has not been previously added */
480             if (!isIdUnique(mySet.getIndexedId())) {
481                 mySet.addError(ERROR_DUPLICATE, MetisDataResource.DATA_ID);
482                 throw new PrometheusDataException(mySet, ERROR_VALIDATION);
483             }
484 
485             /* Add to the list */
486             add(mySet);
487 
488             /* Return it */
489             return mySet;
490         }
491 
492         /**
493          * Clone KeySet from a DataBase.
494          *
495          * @param pControlKey the ControlKey to clone
496          * @param pKeySet     the DataKeySet to clone
497          * @return the new DataKeySet
498          * @throws OceanusException on error
499          */
500         protected PrometheusControlKeySet cloneControlKeySet(final PrometheusControlKey pControlKey,
501                                                              final PrometheusControlKeySet pKeySet) throws OceanusException {
502             /* Build data values */
503             final PrometheusDataValues myValues = new PrometheusDataValues(OBJECT_NAME);
504             myValues.addValue(MetisDataResource.DATA_ID, pKeySet.getIndexedId());
505             myValues.addValue(PrometheusCryptographyDataType.CONTROLKEY, pControlKey);
506             myValues.addValue(PrometheusDataResource.KEYSET_KEYSETDEF, pKeySet.getSecuredKeySetDef());
507             myValues.addValue(PrometheusDataResource.KEYSET_KEYSET, pKeySet.getKeySet());
508 
509             /* Clone the controlKeySet */
510             final PrometheusControlKeySet mySet = addValuesItem(myValues);
511 
512             /* Access the ControlKeySet List */
513             final PrometheusDataSet myData = getDataSet();
514             final PrometheusDataKeySetList myKeySets = myData.getDataKeySets();
515 
516             /* Create a new DataKeySetCache for this ControlKeySet */
517             final DataKeySetCache mySource = pKeySet.getDataKeySets();
518             mySet.theKeySetCache = mySource.cloneDataKeySetCache(mySet, myKeySets);
519 
520             return mySet;
521         }
522 
523         @Override
524         public void postProcessOnLoad() throws OceanusException {
525             /* Just sort the list */
526             reSort();
527         }
528 
529         @Override
530         protected PrometheusDataMapItem allocateDataMap() {
531             /* Unused */
532             throw new UnsupportedOperationException();
533         }
534     }
535 
536     /**
537      * DataKeySetCache.
538      */
539     private static final class DataKeySetCache
540             implements MetisFieldItem, MetisDataList<PrometheusDataKeySet> {
541         /**
542          * Report fields.
543          */
544         private static final MetisFieldSet<DataKeySetCache> FIELD_DEFS = MetisFieldSet.newFieldSet(DataKeySetCache.class);
545 
546         /*
547          * Size Field Id.
548          */
549         static {
550             FIELD_DEFS.declareLocalField(MetisDataResource.LIST_SIZE, DataKeySetCache::size);
551         }
552 
553         /**
554          * The list.
555          */
556         private final List<PrometheusDataKeySet> theList;
557 
558         /**
559          * Iterator.
560          */
561         private Iterator<PrometheusDataKeySet> theIterator;
562 
563         /**
564          * Constructor.
565          */
566         DataKeySetCache() {
567             theList = new ArrayList<>();
568         }
569 
570         @Override
571         public MetisFieldSet<DataKeySetCache> getDataFieldSet() {
572             return FIELD_DEFS;
573         }
574 
575         @Override
576         public List<PrometheusDataKeySet> getUnderlyingList() {
577             return theList;
578         }
579 
580         @Override
581         public String formatObject(final OceanusDataFormatter pFormatter) {
582             return getDataFieldSet().getName();
583         }
584 
585         /**
586          * Register the KeySet.
587          *
588          * @param pKeySet the KeySet to register
589          */
590         private void registerDataKeySet(final PrometheusDataKeySet pKeySet) {
591             /* If this is first registration */
592             if (!theList.contains(pKeySet)) {
593                 /* Add the KeySet */
594                 theList.add(pKeySet);
595 
596                 /* Reset any iterator */
597                 if (theIterator != null) {
598                     theIterator = iterator();
599                 }
600             }
601         }
602 
603         /**
604          * Get next DataKeySet.
605          *
606          * @return the next KeySet
607          */
608         private PrometheusDataKeySet getNextDataKeySet() {
609             /* Handle empty list */
610             if (isEmpty()) {
611                 return null;
612             }
613 
614             /* Handle initialisation and wrapping */
615             if (theIterator == null
616                     || !theIterator.hasNext()) {
617                 theIterator = iterator();
618             }
619 
620             /* Return the next KeySet */
621             return theIterator.next();
622         }
623 
624         /**
625          * Delete the KeySets.
626          */
627         private void deleteDataKeySets() {
628             /* Loop through the KeySets */
629             final Iterator<PrometheusDataKeySet> myIterator = iterator();
630             while (myIterator.hasNext()) {
631                 final PrometheusDataKeySet mySet = myIterator.next();
632 
633                 /* Delete the KeySet */
634                 mySet.deleteDataKeySet();
635             }
636         }
637 
638         /**
639          * Clone dataKeySet Cache from a DataBase.
640          *
641          * @param pControlKeySet the ControlKeySet to clone
642          * @param pKeySets       the DataKeySetList
643          * @return the new DataKeySetCache
644          * @throws OceanusException on error
645          */
646         private DataKeySetCache cloneDataKeySetCache(final PrometheusControlKeySet pControlKeySet,
647                                                      final PrometheusDataKeySetList pKeySets) throws OceanusException {
648             /* Create a new cache */
649             final DataKeySetCache myCache = new DataKeySetCache();
650 
651             /* Loop through the KeySets */
652             final Iterator<PrometheusDataKeySet> myIterator = iterator();
653             while (myIterator.hasNext()) {
654                 final PrometheusDataKeySet mySet = myIterator.next();
655 
656                 /* Create a new DataKeySet for this ControlKeySet */
657                 final PrometheusDataKeySet myNewSet = pKeySets.cloneDataKeySet(pControlKeySet, mySet);
658                 myCache.registerDataKeySet(myNewSet);
659             }
660 
661             /* Return the cache */
662             return myCache;
663         }
664     }
665 }