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.keyset.GordianKeySet;
21  import io.github.tonywasher.joceanus.gordianknot.api.keyset.GordianKeySetFactory;
22  import io.github.tonywasher.joceanus.gordianknot.util.GordianUtilities;
23  import io.github.tonywasher.joceanus.metis.data.MetisDataResource;
24  import io.github.tonywasher.joceanus.metis.field.MetisFieldSet;
25  import io.github.tonywasher.joceanus.metis.field.MetisFieldVersionedSet;
26  import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
27  import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
28  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataList.PrometheusListStyle;
29  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataSet.PrometheusCryptographyDataType;
30  import io.github.tonywasher.joceanus.prometheus.exc.PrometheusDataException;
31  import io.github.tonywasher.joceanus.prometheus.exc.PrometheusSecurityException;
32  
33  import java.util.Objects;
34  
35  /**
36   * ControlKey definition and list. The Control Key represents the passwordHash that controls
37   * securing of the dataKeys. It maintains a map of the associated DataKeys.
38   *
39   * @author Tony Washer
40   */
41  public class PrometheusDataKeySet
42          extends PrometheusDataItem {
43      /**
44       * Object name.
45       */
46      public static final String OBJECT_NAME = PrometheusCryptographyDataType.DATAKEYSET.getItemName();
47  
48      /**
49       * List name.
50       */
51      public static final String LIST_NAME = PrometheusCryptographyDataType.DATAKEYSET.getListName();
52  
53      /**
54       * KeySetWrapLength.
55       */
56      public static final int WRAPLEN = GordianUtilities.getMaximumKeySetWrapLength();
57  
58      /**
59       * Report fields.
60       */
61      private static final MetisFieldVersionedSet<PrometheusDataKeySet> FIELD_DEFS = MetisFieldVersionedSet.newVersionedFieldSet(PrometheusDataKeySet.class);
62  
63      /*
64       * FieldIds.
65       */
66      static {
67          FIELD_DEFS.declareLinkField(PrometheusCryptographyDataType.CONTROLKEYSET);
68          FIELD_DEFS.declareByteArrayField(PrometheusDataResource.KEYSET_KEYSETDEF, WRAPLEN);
69          FIELD_DEFS.declareDerivedVersionedField(PrometheusDataResource.KEYSET_KEYSET);
70          FIELD_DEFS.declareDerivedVersionedField(PrometheusDataResource.KEYSET_ENCRYPTOR);
71      }
72  
73      /**
74       * Copy Constructor.
75       *
76       * @param pList   the list the copy belongs to
77       * @param pSource The Key to copy
78       */
79      protected PrometheusDataKeySet(final PrometheusDataKeySetList pList,
80                                     final PrometheusDataKeySet pSource) {
81          /* Set standard values */
82          super(pList, pSource);
83  
84          /* Switch on the LinkStyle */
85          if (Objects.requireNonNull(getStyle()) == PrometheusListStyle.CLONE) {
86              final GordianKeySet myKeySet = pSource.getKeySet();
87              setValueKeySet(myKeySet);
88              setValueEncryptor(pSource.getEncryptor());
89          }
90      }
91  
92      /**
93       * Values constructor.
94       *
95       * @param pList   the List to add to
96       * @param pValues the values constructor
97       * @throws OceanusException on error
98       */
99      private PrometheusDataKeySet(final PrometheusDataKeySetList pList,
100                                  final PrometheusDataValues pValues) throws OceanusException {
101         /* Initialise the item */
102         super(pList, pValues);
103 
104         /* Access the Password manager */
105         final PrometheusDataSet myData = getDataSet();
106         final OceanusDataFormatter myFormatter = myData.getDataFormatter();
107 
108         /* Store the ControlKey */
109         Object myValue = pValues.getValue(PrometheusCryptographyDataType.CONTROLKEYSET);
110         if (myValue instanceof Integer i) {
111             /* Store the integer */
112             setValueControlKeySet(i);
113 
114             /* Resolve the ControlKey */
115             resolveDataLink(PrometheusCryptographyDataType.CONTROLKEYSET, myData.getControlKeySets());
116         } else if (myValue instanceof PrometheusControlKeySet ks) {
117             /* Store the controlKey */
118             setValueControlKeySet(ks);
119         }
120 
121         /* Access the controlKey */
122         final PrometheusControlKeySet myControlKeySet = getControlKeySet();
123 
124         /* Store the WrappedKeySetDef */
125         myValue = pValues.getValue(PrometheusDataResource.KEYSET_KEYSETDEF);
126         if (myValue instanceof byte[] ba) {
127             setValueSecuredKeySetDef(ba);
128         }
129 
130         /* Store/Resolve the keySet */
131         myValue = pValues.getValue(PrometheusDataResource.KEYSET_KEYSET);
132         if (myValue instanceof GordianKeySet ks) {
133             setValueKeySet(ks);
134             setValueEncryptor(new PrometheusEncryptor(myFormatter, ks));
135         } else if (getSecuredKeySetDef() != null) {
136             /* Protect against exceptions */
137             try {
138                 final GordianKeySet myKeySet = myControlKeySet.getKeySet().deriveKeySet(getSecuredKeySetDef());
139                 setValueKeySet(myKeySet);
140                 setValueEncryptor(new PrometheusEncryptor(myFormatter, myKeySet));
141             } catch (GordianException e) {
142                 throw new PrometheusSecurityException(e);
143             }
144         }
145 
146         /* Register the DataKeySet */
147         myControlKeySet.registerDataKeySet(this);
148     }
149 
150     /**
151      * Constructor for a new DataKeySet.
152      *
153      * @param pList          the list to which to add the keySet to
154      * @param pControlKeySet the control keySet
155      * @throws OceanusException on error
156      */
157     protected PrometheusDataKeySet(final PrometheusDataKeySetList pList,
158                                    final PrometheusControlKeySet pControlKeySet) throws OceanusException {
159         /* Initialise the item */
160         super(pList, 0);
161 
162         /* Protect against exceptions */
163         try {
164             /* Store the Details */
165             setValueControlKeySet(pControlKeySet);
166 
167             /* Access the Formatter */
168             final PrometheusDataSet myData = getDataSet();
169             final OceanusDataFormatter myFormatter = myData.getDataFormatter();
170 
171             /* Create the KeySet */
172             final GordianKeySetFactory myKeySets = pControlKeySet.getSecurityFactory().getKeySetFactory();
173             final GordianKeySet myKeySet = myKeySets.generateKeySet(getDataSet().getKeySetSpec());
174             setValueKeySet(myKeySet);
175             setValueEncryptor(new PrometheusEncryptor(myFormatter, myKeySet));
176 
177             /* Set the wrappedKeySetDef */
178             setValueSecuredKeySetDef(pControlKeySet.getKeySet().secureKeySet(myKeySet));
179 
180             /* Catch Exceptions */
181         } catch (GordianException
182                  | OceanusException e) {
183             /* Pass on exception */
184             throw new PrometheusDataException(this, ERROR_CREATEITEM, e);
185         }
186     }
187 
188     @Override
189     public MetisFieldSetDef getDataFieldSet() {
190         return FIELD_DEFS;
191     }
192 
193     /**
194      * Get the ControlKey.
195      *
196      * @return the controlKey
197      */
198     public final PrometheusControlKey getControlKey() {
199         return getControlKeySet().getControlKey();
200     }
201 
202     /**
203      * Get the ControlKeySet.
204      *
205      * @return the controlKeySet
206      */
207     public final PrometheusControlKeySet getControlKeySet() {
208         return getValues().getValue(PrometheusCryptographyDataType.CONTROLKEYSET, PrometheusControlKeySet.class);
209     }
210 
211     /**
212      * Get the ControlKeyId for this item.
213      *
214      * @return the ControlKeyId
215      */
216     public Integer getControlKeySetId() {
217         final PrometheusControlKeySet myKeySet = getControlKeySet();
218         return myKeySet == null
219                 ? null
220                 : myKeySet.getIndexedId();
221     }
222 
223     /**
224      * Get the securedKeySetDef.
225      *
226      * @return the securedKeySetDef
227      */
228     public final byte[] getSecuredKeySetDef() {
229         return getValues().getValue(PrometheusDataResource.KEYSET_KEYSETDEF, byte[].class);
230     }
231 
232     /**
233      * Get the KeySet.
234      *
235      * @return the keySet
236      */
237     public GordianKeySet getKeySet() {
238         return getValues().getValue(PrometheusDataResource.KEYSET_KEYSET, GordianKeySet.class);
239     }
240 
241     /**
242      * Get the Encryptor.
243      *
244      * @return the encryptor
245      */
246     public PrometheusEncryptor getEncryptor() {
247         return getValues().getValue(PrometheusDataResource.KEYSET_ENCRYPTOR, PrometheusEncryptor.class);
248     }
249 
250     /**
251      * Set the ControlKeySet Id.
252      *
253      * @param pId the controlKeySet id
254      * @throws OceanusException on error
255      */
256     private void setValueControlKeySet(final Integer pId) throws OceanusException {
257         getValues().setValue(PrometheusCryptographyDataType.CONTROLKEYSET, pId);
258     }
259 
260     /**
261      * Set the ControlKeySet.
262      *
263      * @param pKeySet the controlKeySet
264      * @throws OceanusException on error
265      */
266     private void setValueControlKeySet(final PrometheusControlKeySet pKeySet) throws OceanusException {
267         getValues().setValue(PrometheusCryptographyDataType.CONTROLKEYSET, pKeySet);
268     }
269 
270     /**
271      * Set the securedKeySetDef.
272      *
273      * @param pValue the securedKeySetDef
274      */
275     private void setValueSecuredKeySetDef(final byte[] pValue) {
276         getValues().setUncheckedValue(PrometheusDataResource.KEYSET_KEYSETDEF, pValue);
277     }
278 
279     /**
280      * Set the keySet.
281      *
282      * @param pValue the keySet
283      */
284     private void setValueKeySet(final GordianKeySet pValue) {
285         getValues().setUncheckedValue(PrometheusDataResource.KEYSET_KEYSET, pValue);
286     }
287 
288     /**
289      * Set the encryptor.
290      *
291      * @param pValue the encryptor
292      */
293     private void setValueEncryptor(final PrometheusEncryptor pValue) {
294         getValues().setUncheckedValue(PrometheusDataResource.KEYSET_ENCRYPTOR, pValue);
295     }
296 
297     @Override
298     public PrometheusDataKeySet getBase() {
299         return (PrometheusDataKeySet) super.getBase();
300     }
301 
302     @Override
303     public PrometheusDataKeySetList getList() {
304         return (PrometheusDataKeySetList) super.getList();
305     }
306 
307     @Override
308     public int compareValues(final PrometheusDataItem pThat) {
309         /* Only sort on id */
310         return 0;
311     }
312 
313     @Override
314     public void resolveDataSetLinks() throws OceanusException {
315         /* Resolve the ControlKey */
316         final PrometheusDataSet myData = getDataSet();
317         resolveDataLink(PrometheusCryptographyDataType.CONTROLKEYSET, myData.getControlKeySets());
318         final PrometheusControlKeySet myControlKeySet = getControlKeySet();
319 
320         /* Register the KeySet */
321         myControlKeySet.registerDataKeySet(this);
322     }
323 
324     /**
325      * Delete the old set of DataKeySet and DataKeys.
326      */
327     protected void deleteDataKeySet() {
328         /* Mark this dataKeySet as deleted */
329         setDeleted(true);
330     }
331 
332     /**
333      * DataKeySet List.
334      */
335     public static class PrometheusDataKeySetList
336             extends PrometheusDataList<PrometheusDataKeySet> {
337         /**
338          * Report fields.
339          */
340         private static final MetisFieldSet<PrometheusDataKeySetList> FIELD_DEFS = MetisFieldSet.newFieldSet(PrometheusDataKeySetList.class);
341 
342         /**
343          * Construct an empty CORE list.
344          *
345          * @param pData the DataSet for the list
346          */
347         protected PrometheusDataKeySetList(final PrometheusDataSet pData) {
348             this(pData, PrometheusListStyle.CORE);
349         }
350 
351         /**
352          * Construct an empty generic ControlKey list.
353          *
354          * @param pData  the DataSet for the list
355          * @param pStyle the style of the list
356          */
357         protected PrometheusDataKeySetList(final PrometheusDataSet pData,
358                                            final PrometheusListStyle pStyle) {
359             super(PrometheusDataKeySet.class, pData, PrometheusCryptographyDataType.DATAKEYSET, pStyle);
360         }
361 
362         /**
363          * Constructor for a cloned List.
364          *
365          * @param pSource the source List
366          */
367         private PrometheusDataKeySetList(final PrometheusDataKeySetList pSource) {
368             super(pSource);
369         }
370 
371         @Override
372         public MetisFieldSet<PrometheusDataKeySetList> getDataFieldSet() {
373             return FIELD_DEFS;
374         }
375 
376         @Override
377         public String listName() {
378             return LIST_NAME;
379         }
380 
381         @Override
382         public MetisFieldSet<PrometheusDataKeySet> getItemFields() {
383             return PrometheusDataKeySet.FIELD_DEFS;
384         }
385 
386         @Override
387         public boolean includeDataXML() {
388             return false;
389         }
390 
391         @Override
392         protected PrometheusDataKeySetList getEmptyList(final PrometheusListStyle pStyle) {
393             final PrometheusDataKeySetList myList = new PrometheusDataKeySetList(this);
394             myList.setStyle(pStyle);
395             return myList;
396         }
397 
398         @Override
399         public PrometheusDataKeySetList deriveList(final PrometheusListStyle pStyle) throws OceanusException {
400             return (PrometheusDataKeySetList) super.deriveList(pStyle);
401         }
402 
403         @Override
404         public PrometheusDataKeySetList deriveDifferences(final PrometheusDataSet pDataSet,
405                                                           final PrometheusDataList<?> pOld) {
406             return (PrometheusDataKeySetList) super.deriveDifferences(pDataSet, pOld);
407         }
408 
409         @Override
410         public PrometheusDataKeySet addCopyItem(final PrometheusDataItem pItem) {
411             /* Can only clone a DataKeySet */
412             if (!(pItem instanceof PrometheusDataKeySet)) {
413                 return null;
414             }
415 
416             /* Clone the data key set */
417             final PrometheusDataKeySet mySet = new PrometheusDataKeySet(this, (PrometheusDataKeySet) pItem);
418             add(mySet);
419             return mySet;
420         }
421 
422         @Override
423         public PrometheusDataKeySet addNewItem() {
424             throw new UnsupportedOperationException();
425         }
426 
427         @Override
428         public PrometheusDataKeySet addValuesItem(final PrometheusDataValues pValues) throws OceanusException {
429             /* Create the dataKeySet */
430             final PrometheusDataKeySet mySet = new PrometheusDataKeySet(this, pValues);
431 
432             /* Check that this keyId has not been previously added */
433             if (!isIdUnique(mySet.getIndexedId())) {
434                 mySet.addError(ERROR_DUPLICATE, MetisDataResource.DATA_ID);
435                 throw new PrometheusDataException(mySet, ERROR_VALIDATION);
436             }
437 
438             /* Add to the list */
439             add(mySet);
440 
441             /* Return it */
442             return mySet;
443         }
444 
445         /**
446          * Clone KeySet from a DataBase.
447          *
448          * @param pControlKeySet the ControlKeySet to clone
449          * @param pKeySet        the DataKeySet to clone
450          * @return the new DataKeySet
451          * @throws OceanusException on error
452          */
453         protected PrometheusDataKeySet cloneDataKeySet(final PrometheusControlKeySet pControlKeySet,
454                                                        final PrometheusDataKeySet pKeySet) throws OceanusException {
455             /* Build data values */
456             final PrometheusDataValues myValues = new PrometheusDataValues(OBJECT_NAME);
457             myValues.addValue(MetisDataResource.DATA_ID, pKeySet.getIndexedId());
458             myValues.addValue(PrometheusCryptographyDataType.CONTROLKEYSET, pControlKeySet);
459             myValues.addValue(PrometheusDataResource.KEYSET_KEYSETDEF, pKeySet.getSecuredKeySetDef());
460             myValues.addValue(PrometheusDataResource.KEYSET_KEYSET, pKeySet.getKeySet());
461 
462             /* Clone the dataKeySet */
463             return addValuesItem(myValues);
464         }
465 
466         @Override
467         public void postProcessOnLoad() throws OceanusException {
468             /* Just sort the list */
469             reSort();
470         }
471 
472         @Override
473         protected PrometheusDataMapItem allocateDataMap() {
474             /* Unused */
475             throw new UnsupportedOperationException();
476         }
477     }
478 }