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