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.base.GordianLength;
20  import io.github.tonywasher.joceanus.gordianknot.api.digest.GordianDigestSpec;
21  import io.github.tonywasher.joceanus.gordianknot.api.digest.GordianDigestSpecBuilder;
22  
23  import java.util.ArrayList;
24  import java.util.List;
25  import java.util.Objects;
26  
27  /**
28   * XMSS KeySpec.
29   */
30  public class GordianXMSSKeySpec {
31      /**
32       * The Separator.
33       */
34      private static final String SEP = "-";
35  
36      /**
37       * The key type.
38       */
39      private final GordianXMSSKeyType theKeyType;
40  
41      /**
42       * The digest type.
43       */
44      private final GordianXMSSDigestType theDigestType;
45  
46      /**
47       * The height.
48       */
49      private final GordianXMSSHeight theHeight;
50  
51      /**
52       * The layers.
53       */
54      private final GordianXMSSMTLayers theLayers;
55  
56      /**
57       * The Validity.
58       */
59      private final boolean isValid;
60  
61      /**
62       * The String name.
63       */
64      private String theName;
65  
66      /**
67       * Constructor.
68       *
69       * @param pDigestType the digestType
70       * @param pHeight     the height
71       */
72      public GordianXMSSKeySpec(final GordianXMSSDigestType pDigestType,
73                                final GordianXMSSHeight pHeight) {
74          this(GordianXMSSKeyType.XMSS, pDigestType, pHeight, null);
75      }
76  
77      /**
78       * Constructor.
79       *
80       * @param pDigestType the digestType
81       * @param pHeight     the height
82       * @param pLayers     the layers
83       */
84      public GordianXMSSKeySpec(final GordianXMSSDigestType pDigestType,
85                                final GordianXMSSHeight pHeight,
86                                final GordianXMSSMTLayers pLayers) {
87          this(GordianXMSSKeyType.XMSSMT, pDigestType, pHeight, pLayers);
88      }
89  
90      /**
91       * Constructor.
92       *
93       * @param pKeyType    the keyType
94       * @param pDigestType the digestType
95       * @param pHeight     the height
96       * @param pLayers     the layers
97       */
98      public GordianXMSSKeySpec(final GordianXMSSKeyType pKeyType,
99                                final GordianXMSSDigestType pDigestType,
100                               final GordianXMSSHeight pHeight,
101                               final GordianXMSSMTLayers pLayers) {
102         theKeyType = pKeyType;
103         theDigestType = pDigestType;
104         theHeight = pHeight;
105         theLayers = pLayers;
106         isValid = checkValidity();
107     }
108 
109     /**
110      * Is this key MT?
111      *
112      * @return true/false
113      */
114     public boolean isMT() {
115         return theKeyType.isMT();
116     }
117 
118     /**
119      * Obtain the keyType.
120      *
121      * @return the keyType
122      */
123     public GordianXMSSKeyType getKeyType() {
124         return theKeyType;
125     }
126 
127     /**
128      * Obtain the digestType.
129      *
130      * @return the digestType
131      */
132     public GordianXMSSDigestType getDigestType() {
133         return theDigestType;
134     }
135 
136     /**
137      * Obtain the height.
138      *
139      * @return the height
140      */
141     public GordianXMSSHeight getHeight() {
142         return theHeight;
143     }
144 
145     /**
146      * Obtain the layers.
147      *
148      * @return the layers
149      */
150     public GordianXMSSMTLayers getLayers() {
151         return theLayers;
152     }
153 
154     /**
155      * Is the keySpec valid?
156      *
157      * @return true/false.
158      */
159     public boolean isValid() {
160         return isValid;
161     }
162 
163     /**
164      * Is the keySpec high (height > 15)?
165      *
166      * @return true/false.
167      */
168     public boolean isHigh() {
169         return isValid && theHeight.isHigh(theKeyType);
170     }
171 
172     /**
173      * Create XMSS keySpec.
174      *
175      * @param pDigestType the digestType
176      * @param pHeight     the height
177      * @return the keySpec
178      */
179     public static GordianXMSSKeySpec xmss(final GordianXMSSDigestType pDigestType,
180                                           final GordianXMSSHeight pHeight) {
181         return new GordianXMSSKeySpec(pDigestType, pHeight);
182     }
183 
184     /**
185      * Create XMSS keySpec.
186      *
187      * @param pDigestType the digestType
188      * @param pHeight     the height
189      * @param pLayers     the layers
190      * @return the keySpec
191      */
192     public static GordianXMSSKeySpec xmssmt(final GordianXMSSDigestType pDigestType,
193                                             final GordianXMSSHeight pHeight,
194                                             final GordianXMSSMTLayers pLayers) {
195         return new GordianXMSSKeySpec(pDigestType, pHeight, pLayers);
196     }
197 
198     /**
199      * Check spec validity.
200      *
201      * @return valid true/false
202      */
203     private boolean checkValidity() {
204         /* Check that required elements are present */
205         if (theKeyType == null || theDigestType == null || theHeight == null) {
206             return false;
207         }
208 
209         /* Check that the height is valid for the keyType */
210         if (!theHeight.validForKeyType(theKeyType)) {
211             return false;
212         }
213 
214         /* Check layers is valid for keyType/height */
215         return theKeyType == GordianXMSSKeyType.XMSS
216                 ? theLayers == null
217                 : theLayers != null && theHeight.hasValidLayers(theLayers);
218     }
219 
220     @Override
221     public String toString() {
222         /* If we have not yet loaded the name */
223         if (theName == null) {
224             /* If the keySpec is valid */
225             if (isValid) {
226                 /* Load the name */
227                 theName = theKeyType.toString()
228                         + SEP + theDigestType.toString()
229                         + SEP + theHeight.toString();
230                 if (isMT()) {
231                     theName += SEP + theLayers.toString();
232                 }
233 
234             } else {
235                 /* Report invalid spec */
236                 theName = "InvalidXMSSKeySpec: " + theKeyType + ":" + theDigestType
237                         + ":" + theHeight + ":" + theLayers;
238             }
239         }
240 
241         /* return the name */
242         return theName;
243     }
244 
245     @Override
246     public boolean equals(final Object pThat) {
247         /* Handle the trivial cases */
248         if (this == pThat) {
249             return true;
250         }
251         if (pThat == null) {
252             return false;
253         }
254 
255         /* Check KeyType, digestType, height and layers */
256         return pThat instanceof GordianXMSSKeySpec myThat
257                 && theKeyType == myThat.getKeyType()
258                 && theDigestType == myThat.getDigestType()
259                 && theHeight == myThat.getHeight()
260                 && theLayers == myThat.getLayers();
261     }
262 
263     @Override
264     public int hashCode() {
265         return Objects.hash(theKeyType, theDigestType, theHeight, theLayers);
266     }
267 
268     /**
269      * Obtain a list of all possible specs.
270      *
271      * @return the list
272      */
273     public static List<GordianXMSSKeySpec> listPossibleKeySpecs() {
274         /* Create the list */
275         final List<GordianXMSSKeySpec> mySpecs = new ArrayList<>();
276 
277         /* Add the specs */
278         for (final GordianXMSSDigestType myType : GordianXMSSDigestType.values()) {
279             mySpecs.addAll(listPossibleKeySpecs(myType));
280         }
281 
282         /* Return the list */
283         return mySpecs;
284     }
285 
286     /**
287      * Obtain a list of all possible specs.
288      *
289      * @param pDigestType the digestType
290      * @return the list
291      */
292     public static List<GordianXMSSKeySpec> listPossibleKeySpecs(final GordianXMSSDigestType pDigestType) {
293         /* Create the list */
294         final List<GordianXMSSKeySpec> mySpecs = new ArrayList<>();
295 
296         /* For all heights */
297         for (final GordianXMSSHeight myHeight : GordianXMSSHeight.values()) {
298             /* Add XMSS Spec if valid */
299             if (myHeight.validForKeyType(GordianXMSSKeyType.XMSS)) {
300                 mySpecs.add(xmss(pDigestType, myHeight));
301             }
302 
303             /* Add XMSSMT Specs if valid */
304             if (myHeight.validForKeyType(GordianXMSSKeyType.XMSSMT)) {
305                 /* For all heights */
306                 for (final GordianXMSSMTLayers myLayers : myHeight.getValidLayers()) {
307                     mySpecs.add(xmssmt(pDigestType, myHeight, myLayers));
308                 }
309             }
310         }
311 
312         /* Return the list */
313         return mySpecs;
314     }
315 
316     /**
317      * XMSS keyTypes.
318      */
319     public enum GordianXMSSKeyType {
320         /**
321          * XMSS.
322          */
323         XMSS,
324 
325         /**
326          * XMSSMT.
327          */
328         XMSSMT;
329 
330         /**
331          * Is this key MT?
332          *
333          * @return true/false
334          */
335         public boolean isMT() {
336             return this == XMSSMT;
337         }
338 
339         @Override
340         public String toString() {
341             return this == XMSSMT ? "XMSS^MT" : "XMSS";
342         }
343     }
344 
345     /**
346      * XMSS digestType.
347      */
348     public enum GordianXMSSDigestType {
349         /**
350          * SHA256.
351          */
352         SHA256,
353 
354         /**
355          * SHA512.
356          */
357         SHA512,
358 
359         /**
360          * SHAKE128.
361          */
362         SHAKE128,
363 
364         /**
365          * SHAKE256.
366          */
367         SHAKE256;
368 
369         /**
370          * Obtain the required digestSpec.
371          *
372          * @return the digestSpec
373          */
374         public GordianDigestSpec getDigestSpec() {
375             switch (this) {
376                 case SHA256:
377                     return GordianDigestSpecBuilder.sha2(GordianLength.LEN_256);
378                 case SHA512:
379                     return GordianDigestSpecBuilder.sha2(GordianLength.LEN_512);
380                 case SHAKE128:
381                     return GordianDigestSpecBuilder.shake128();
382                 case SHAKE256:
383                     return GordianDigestSpecBuilder.shake256();
384                 default:
385                     throw new IllegalStateException();
386             }
387         }
388     }
389 
390     /**
391      * XMSS Height.
392      */
393     public enum GordianXMSSHeight {
394         /**
395          * 10.
396          */
397         H10(10),
398 
399         /**
400          * 16.
401          */
402         H16(16),
403 
404         /**
405          * 20.
406          */
407         H20(20, new GordianXMSSMTLayers[]{GordianXMSSMTLayers.L2, GordianXMSSMTLayers.L4}),
408 
409         /**
410          * 40.
411          */
412         H40(40, new GordianXMSSMTLayers[]{GordianXMSSMTLayers.L2, GordianXMSSMTLayers.L4, GordianXMSSMTLayers.L8}),
413 
414         /**
415          * 12.
416          */
417         H60(12, new GordianXMSSMTLayers[]{GordianXMSSMTLayers.L3, GordianXMSSMTLayers.L6, GordianXMSSMTLayers.L12});
418 
419         /**
420          * The Height.
421          */
422         private final int theHeight;
423 
424         /**
425          * The Layers.
426          */
427         private final GordianXMSSMTLayers[] theLayers;
428 
429         /**
430          * Constructor.
431          *
432          * @param pHeight the height
433          */
434         GordianXMSSHeight(final int pHeight) {
435             this(pHeight, null);
436         }
437 
438         /**
439          * Constructor.
440          *
441          * @param pHeight the height
442          * @param pLayers the layers
443          */
444         GordianXMSSHeight(final int pHeight,
445                           final GordianXMSSMTLayers[] pLayers) {
446             theHeight = pHeight;
447             theLayers = pLayers;
448         }
449 
450         /**
451          * Obtain the height.
452          *
453          * @return the height
454          */
455         public int getHeight() {
456             return theHeight;
457         }
458 
459 
460         /**
461          * Is the height valid for the keyType.
462          *
463          * @param pKeyType the keyType
464          * @return true/false
465          */
466         boolean validForKeyType(final GordianXMSSKeyType pKeyType) {
467             switch (this) {
468                 case H10:
469                 case H16:
470                     return pKeyType == GordianXMSSKeyType.XMSS;
471                 case H40:
472                 case H60:
473                     return pKeyType == GordianXMSSKeyType.XMSSMT;
474                 default:
475                     return true;
476             }
477         }
478 
479         /**
480          * Is the layers valid for the height.
481          *
482          * @param pLayers the layers
483          * @return true/false
484          */
485         boolean hasValidLayers(final GordianXMSSMTLayers pLayers) {
486             if (theLayers != null) {
487                 for (GordianXMSSMTLayers myLayers : theLayers) {
488                     if (myLayers == pLayers) {
489                         return true;
490                     }
491                 }
492             }
493             return false;
494         }
495 
496         /**
497          * Obtain the valid XMSSMT layers.
498          *
499          * @return the height
500          */
501         GordianXMSSMTLayers[] getValidLayers() {
502             return theLayers;
503         }
504 
505         /**
506          * Is the parameter high?
507          *
508          * @param pKeyType the keyTypoe
509          * @return true/false.
510          */
511         public boolean isHigh(final GordianXMSSKeyType pKeyType) {
512             switch (this) {
513                 case H16:
514                 case H20:
515                     return pKeyType == GordianXMSSKeyType.XMSS;
516                 case H40:
517                 case H60:
518                     return true;
519                 default:
520                     return false;
521             }
522         }
523     }
524 
525     /**
526      * XMSSMT Layers.
527      */
528     public enum GordianXMSSMTLayers {
529         /**
530          * 2.
531          */
532         L2(2),
533 
534         /**
535          * 3.
536          */
537         L3(3),
538 
539         /**
540          * 4.
541          */
542         L4(4),
543 
544         /**
545          * 6.
546          */
547         L6(6),
548 
549         /**
550          * 40.
551          */
552         L8(8),
553 
554         /**
555          * 12.
556          */
557         L12(12);
558 
559         /**
560          * The layers.
561          */
562         private final int theLayers;
563 
564         /**
565          * Constructor.
566          *
567          * @param pLayers the layers
568          */
569         GordianXMSSMTLayers(final int pLayers) {
570             theLayers = pLayers;
571         }
572 
573         /**
574          * Obtain the layers.
575          *
576          * @return the layers
577          */
578         public int getLayers() {
579             return theLayers;
580         }
581     }
582 }