View Javadoc
1   /*
2    * GordianKnot: Security Suite
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.gordianknot.api.keypair;
18  
19  import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianLMSKeySpec.GordianHSSKeySpec;
20  import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianXMSSKeySpec.GordianXMSSDigestType;
21  
22  import java.util.ArrayList;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Objects;
26  
27  /**
28   * Asymmetric KeyPair Specification.
29   */
30  public class GordianKeyPairSpec {
31      /**
32       * The Separator.
33       */
34      private static final String SEP = "-";
35  
36      /**
37       * The keyPairType.
38       */
39      private final GordianKeyPairType theKeyPairType;
40  
41      /**
42       * The SubKeyType.
43       */
44      private final Object theSubKeyType;
45  
46      /**
47       * The Validity.
48       */
49      private final boolean isValid;
50  
51      /**
52       * The String name.
53       */
54      private String theName;
55  
56      /**
57       * Constructor.
58       *
59       * @param pKeyType    the keyType
60       * @param pSubKeyType the subKeyType
61       */
62      public GordianKeyPairSpec(final GordianKeyPairType pKeyType,
63                                final Object pSubKeyType) {
64          theKeyPairType = pKeyType;
65          theSubKeyType = pSubKeyType;
66          isValid = checkValidity();
67      }
68  
69      /**
70       * Obtain the keyPairType.
71       *
72       * @return the keyPairType.
73       */
74      public GordianKeyPairType getKeyPairType() {
75          return theKeyPairType;
76      }
77  
78      /**
79       * Obtain the subKeyType.
80       *
81       * @return the keyType.
82       */
83      public Object getSubKeyType() {
84          return theSubKeyType;
85      }
86  
87      /**
88       * Is the keySpec valid?
89       *
90       * @return true/false.
91       */
92      public boolean isValid() {
93          return isValid;
94      }
95  
96      /**
97       * Obtain the RSAmodulus.
98       *
99       * @return the modulus.
100      */
101     public GordianRSAModulus getRSAModulus() {
102         if (theSubKeyType instanceof GordianRSAModulus myMod) {
103             return myMod;
104         }
105         throw new IllegalArgumentException();
106     }
107 
108     /**
109      * Obtain the DSA keyType.
110      *
111      * @return the keyType.
112      */
113     public GordianDSAKeyType getDSAKeyType() {
114         if (theSubKeyType instanceof GordianDSAKeyType myType) {
115             return myType;
116         }
117         throw new IllegalArgumentException();
118     }
119 
120     /**
121      * Obtain the DH Group.
122      *
123      * @return the dhGroup.
124      */
125     public GordianDHGroup getDHGroup() {
126         if (theSubKeyType instanceof GordianDHGroup myGroup) {
127             return myGroup;
128         }
129         throw new IllegalArgumentException();
130     }
131 
132     /**
133      * Obtain the elliptic curve.
134      *
135      * @return the curve.
136      */
137     public GordianElliptic getElliptic() {
138         if (theSubKeyType instanceof GordianElliptic myElliptic) {
139             return myElliptic;
140         }
141         throw new IllegalArgumentException();
142     }
143 
144     /**
145      * Obtain the elliptic curve.
146      *
147      * @return the curve.
148      */
149     public GordianEdwardsElliptic getEdwardsElliptic() {
150         if (theSubKeyType instanceof GordianEdwardsElliptic myElliptic) {
151             return myElliptic;
152         }
153         throw new IllegalArgumentException();
154     }
155 
156     /**
157      * Obtain the lms keySpec.
158      *
159      * @return the keySpec.
160      */
161     public GordianLMSKeySpec getLMSKeySpec() {
162         if (theSubKeyType instanceof GordianLMSKeySpec mySpec) {
163             return mySpec;
164         }
165         throw new IllegalArgumentException();
166     }
167 
168     /**
169      * Obtain the hss keySpec.
170      *
171      * @return the keySpec.
172      */
173     public GordianHSSKeySpec getHSSKeySpec() {
174         if (theSubKeyType instanceof GordianHSSKeySpec mySpec) {
175             return mySpec;
176         }
177         throw new IllegalArgumentException();
178     }
179 
180     /**
181      * Obtain the XMSS keySpec.
182      *
183      * @return the keySpec.
184      */
185     public GordianXMSSKeySpec getXMSSKeySpec() {
186         if (theSubKeyType instanceof GordianXMSSKeySpec mySpec) {
187             return mySpec;
188         }
189         throw new IllegalArgumentException();
190     }
191 
192     /**
193      * Obtain the XMSS digestType.
194      *
195      * @return the digestType.
196      */
197     public GordianXMSSDigestType getXMSSDigestType() {
198         return getXMSSKeySpec().getDigestType();
199     }
200 
201     /**
202      * Obtain the SLHDSA keySpec.
203      *
204      * @return the keySpec.
205      */
206     public GordianSLHDSASpec getSLHDSAKeySpec() {
207         if (theSubKeyType instanceof GordianSLHDSASpec mySpec) {
208             return mySpec;
209         }
210         throw new IllegalArgumentException();
211     }
212 
213     /**
214      * Obtain the CMCE keySpec.
215      *
216      * @return the keySpec.
217      */
218     public GordianCMCESpec getCMCEKeySpec() {
219         if (theSubKeyType instanceof GordianCMCESpec mySpec) {
220             return mySpec;
221         }
222         throw new IllegalArgumentException();
223     }
224 
225     /**
226      * Obtain the FRODO keySpec.
227      *
228      * @return the keySpec.
229      */
230     public GordianFRODOSpec getFRODOKeySpec() {
231         if (theSubKeyType instanceof GordianFRODOSpec mySpec) {
232             return mySpec;
233         }
234         throw new IllegalArgumentException();
235     }
236 
237     /**
238      * Obtain the Saber keySpec.
239      *
240      * @return the keySpec.
241      */
242     public GordianSABERSpec getSABERKeySpec() {
243         if (theSubKeyType instanceof GordianSABERSpec mySpec) {
244             return mySpec;
245         }
246         throw new IllegalArgumentException();
247     }
248 
249     /**
250      * Obtain the MLKEM keySpec.
251      *
252      * @return the keySpec.
253      */
254     public GordianMLKEMSpec getMLKEMKeySpec() {
255         if (theSubKeyType instanceof GordianMLKEMSpec mySpec) {
256             return mySpec;
257         }
258         throw new IllegalArgumentException();
259     }
260 
261     /**
262      * Obtain the MLDSA keySpec.
263      *
264      * @return the keySpec.
265      */
266     public GordianMLDSASpec getMLDSAKeySpec() {
267         if (theSubKeyType instanceof GordianMLDSASpec mySpec) {
268             return mySpec;
269         }
270         throw new IllegalArgumentException();
271     }
272 
273     /**
274      * Obtain the HQC keySpec.
275      *
276      * @return the keySpec.
277      */
278     public GordianHQCSpec getHQCKeySpec() {
279         if (theSubKeyType instanceof GordianHQCSpec mySpec) {
280             return mySpec;
281         }
282         throw new IllegalArgumentException();
283     }
284 
285     /**
286      * Obtain the Bike keySpec.
287      *
288      * @return the keySpec.
289      */
290     public GordianBIKESpec getBIKEKeySpec() {
291         if (theSubKeyType instanceof GordianBIKESpec mySpec) {
292             return mySpec;
293         }
294         throw new IllegalArgumentException();
295     }
296 
297     /**
298      * Obtain the NTRU keySpec.
299      *
300      * @return the keySpec.
301      */
302     public GordianNTRUSpec getNTRUKeySpec() {
303         if (theSubKeyType instanceof GordianNTRUSpec mySpec) {
304             return mySpec;
305         }
306         throw new IllegalArgumentException();
307     }
308 
309     /**
310      * Obtain the NTRUPRIME keySpec.
311      *
312      * @return the keySpec.
313      */
314     public GordianNTRUPrimeSpec getNTRUPrimeKeySpec() {
315         if (theSubKeyType instanceof GordianNTRUPrimeSpec mySpec) {
316             return mySpec;
317         }
318         throw new IllegalArgumentException();
319     }
320 
321     /**
322      * Obtain the Falcon keySpec.
323      *
324      * @return the keySpec.
325      */
326     public GordianFalconSpec getFalconKeySpec() {
327         if (theSubKeyType instanceof GordianFalconSpec mySpec) {
328             return mySpec;
329         }
330         throw new IllegalArgumentException();
331     }
332 
333     /**
334      * Obtain the Mayo keySpec.
335      *
336      * @return the keySpec.
337      */
338     public GordianMayoSpec getMayoKeySpec() {
339         if (theSubKeyType instanceof GordianMayoSpec mySpec) {
340             return mySpec;
341         }
342         throw new IllegalArgumentException();
343     }
344 
345     /**
346      * Obtain the Snova keySpec.
347      *
348      * @return the keySpec.
349      */
350     public GordianSnovaSpec getSnovaKeySpec() {
351         if (theSubKeyType instanceof GordianSnovaSpec mySpec) {
352             return mySpec;
353         }
354         throw new IllegalArgumentException();
355     }
356 
357     /**
358      * Obtain the Picnic keySpec.
359      *
360      * @return the keySpec.
361      */
362     public GordianPicnicSpec getPicnicKeySpec() {
363         if (theSubKeyType instanceof GordianPicnicSpec mySpec) {
364             return mySpec;
365         }
366         throw new IllegalArgumentException();
367     }
368 
369     /**
370      * Obtain the composite keySpec iterator.
371      *
372      * @return the keySpec iterator.
373      */
374     @SuppressWarnings("unchecked")
375     public Iterator<GordianKeyPairSpec> keySpecIterator() {
376         if (theSubKeyType instanceof List) {
377             return ((List<GordianKeyPairSpec>) theSubKeyType).iterator();
378         }
379         throw new IllegalArgumentException();
380     }
381 
382     @Override
383     public String toString() {
384         /* If we have not yet loaded the name */
385         if (theName == null) {
386             /* If the keySpec is valid */
387             if (isValid) {
388                 /* Derive the name */
389                 deriveName();
390             } else {
391                 /* Report invalid spec */
392                 theName = "InvalidKeyPairSpec: " + theKeyPairType + ":" + theSubKeyType;
393             }
394         }
395 
396         /* return the name */
397         return theName;
398     }
399 
400     /**
401      * Derive name.
402      */
403     private void deriveName() {
404         /* Load the name */
405         theName = theKeyPairType.toString();
406         if (theSubKeyType != null) {
407             switch (theKeyPairType) {
408                 case XMSS:
409                     theName = theSubKeyType.toString();
410                     break;
411                 case EDDSA:
412                     theName = "Ed" + ((GordianEdwardsElliptic) theSubKeyType).getSuffix();
413                     break;
414                 case XDH:
415                     theName = "X" + ((GordianEdwardsElliptic) theSubKeyType).getSuffix();
416                     break;
417                 case COMPOSITE:
418                     final Iterator<GordianKeyPairSpec> myIterator = keySpecIterator();
419                     final StringBuilder myBuilder = new StringBuilder(theName);
420                     while (myIterator.hasNext()) {
421                         myBuilder.append(SEP).append(myIterator.next().toString());
422                     }
423                     theName = myBuilder.toString();
424                     break;
425                 default:
426                     theName += SEP + theSubKeyType;
427                     break;
428             }
429         }
430     }
431 
432     @Override
433     public boolean equals(final Object pThat) {
434         /* Handle the trivial cases */
435         if (this == pThat) {
436             return true;
437         }
438         if (pThat == null) {
439             return false;
440         }
441 
442         /* Check KeyPairType and subKeyType */
443         return pThat instanceof GordianKeyPairSpec myThat
444                 && theKeyPairType == myThat.getKeyPairType()
445                 && Objects.equals(theSubKeyType, myThat.theSubKeyType);
446     }
447 
448     @Override
449     public int hashCode() {
450         return Objects.hash(theKeyPairType, theSubKeyType);
451     }
452 
453     /**
454      * Check spec validity.
455      *
456      * @return valid true/false
457      */
458     private boolean checkValidity() {
459         /* Handle null keyPairType */
460         if (theKeyPairType == null) {
461             return false;
462         }
463 
464         /* Switch on keyPairType */
465         switch (theKeyPairType) {
466             case RSA:
467                 return theSubKeyType instanceof GordianRSAModulus;
468             case DSA:
469                 return theSubKeyType instanceof GordianDSAKeyType;
470             case DH:
471             case ELGAMAL:
472                 return theSubKeyType instanceof GordianDHGroup;
473             case EC:
474                 return theSubKeyType instanceof GordianDSAElliptic;
475             case SM2:
476                 return theSubKeyType instanceof GordianSM2Elliptic;
477             case GOST2012:
478                 return theSubKeyType instanceof GordianGOSTElliptic;
479             case DSTU4145:
480                 return theSubKeyType instanceof GordianDSTU4145Elliptic;
481             case XMSS:
482                 return theSubKeyType instanceof GordianXMSSKeySpec s && s.isValid();
483             case SLHDSA:
484                 return theSubKeyType instanceof GordianSLHDSASpec;
485             case CMCE:
486                 return theSubKeyType instanceof GordianCMCESpec;
487             case FRODO:
488                 return theSubKeyType instanceof GordianFRODOSpec;
489             case SABER:
490                 return theSubKeyType instanceof GordianSABERSpec;
491             case MLKEM:
492                 return theSubKeyType instanceof GordianMLKEMSpec;
493             case MLDSA:
494                 return theSubKeyType instanceof GordianMLDSASpec;
495             case HQC:
496                 return theSubKeyType instanceof GordianHQCSpec;
497             case BIKE:
498                 return theSubKeyType instanceof GordianBIKESpec;
499             case NTRU:
500                 return theSubKeyType instanceof GordianNTRUSpec;
501             case NTRUPRIME:
502                 return theSubKeyType instanceof GordianNTRUPrimeSpec;
503             case FALCON:
504                 return theSubKeyType instanceof GordianFalconSpec;
505             case MAYO:
506                 return theSubKeyType instanceof GordianMayoSpec;
507             case SNOVA:
508                 return theSubKeyType instanceof GordianSnovaSpec;
509             case PICNIC:
510                 return theSubKeyType instanceof GordianPicnicSpec;
511             case NEWHOPE:
512                 return theSubKeyType == null;
513             case LMS:
514                 return (theSubKeyType instanceof GordianLMSKeySpec ls && ls.isValid())
515                         || (theSubKeyType instanceof GordianHSSKeySpec hs && hs.isValid());
516             case EDDSA:
517             case XDH:
518                 return theSubKeyType instanceof GordianEdwardsElliptic;
519             case COMPOSITE:
520                 return theSubKeyType instanceof List && checkComposite();
521             default:
522                 return false;
523         }
524     }
525 
526     /**
527      * Check composite spec validity.
528      *
529      * @return valid true/false
530      */
531     private boolean checkComposite() {
532         Boolean stateAware = null;
533         final List<GordianKeyPairType> myExisting = new ArrayList<>();
534         final Iterator<GordianKeyPairSpec> myIterator = keySpecIterator();
535         while (myIterator.hasNext()) {
536             /* Check that we have not got a null */
537             final GordianKeyPairSpec mySpec = myIterator.next();
538             if (mySpec == null) {
539                 return false;
540             }
541 
542             /* Check that we have not got a duplicate or COMPOSITE */
543             final GordianKeyPairType myType = mySpec.getKeyPairType();
544             if (myExisting.contains(myType) || myType == GordianKeyPairType.COMPOSITE) {
545                 return false;
546             }
547 
548             /* Check that stateAwareness is identical */
549             if (stateAware == null) {
550                 stateAware = mySpec.isStateAware();
551             } else if (mySpec.isStateAware() != stateAware) {
552                 return false;
553             }
554 
555             /* Add to list */
556             myExisting.add(myType);
557         }
558 
559         /* Make sure there are at least two */
560         return myExisting.size() > 1;
561     }
562 
563     /**
564      * is the use subType for signatures?
565      *
566      * @return true/false
567      */
568     public boolean isStateAware() {
569         switch (theKeyPairType) {
570             case XMSS:
571             case LMS:
572                 return true;
573             case COMPOSITE:
574                 return keySpecIterator().next().isStateAware();
575             default:
576                 return false;
577         }
578     }
579 }