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.mac;
18  
19  import io.github.tonywasher.joceanus.gordianknot.api.base.GordianKeySpec;
20  import io.github.tonywasher.joceanus.gordianknot.api.base.GordianLength;
21  import io.github.tonywasher.joceanus.gordianknot.api.cipher.GordianSymKeySpec;
22  import io.github.tonywasher.joceanus.gordianknot.api.cipher.GordianSymKeyType;
23  import io.github.tonywasher.joceanus.gordianknot.api.digest.GordianDigestSpec;
24  import io.github.tonywasher.joceanus.gordianknot.api.digest.GordianDigestSubSpec.GordianDigestState;
25  import io.github.tonywasher.joceanus.gordianknot.api.digest.GordianDigestType;
26  
27  import java.util.Objects;
28  
29  /**
30   * Mac Specification.
31   */
32  public final class GordianMacSpec
33          implements GordianKeySpec {
34      /**
35       * The Separator.
36       */
37      static final String SEP = "-";
38  
39      /**
40       * The Mac Type.
41       */
42      private final GordianMacType theMacType;
43  
44      /**
45       * The KeyLength.
46       */
47      private final GordianLength theKeyLength;
48  
49      /**
50       * The SubSpec.
51       */
52      private final Object theSubSpec;
53  
54      /**
55       * The Validity.
56       */
57      private final boolean isValid;
58  
59      /**
60       * The String name.
61       */
62      private String theName;
63  
64      /**
65       * hMac/skeinMac Constructor.
66       *
67       * @param pMacType    the macType
68       * @param pKeyLength  the keyLength
69       * @param pDigestSpec the digestSpec
70       */
71      public GordianMacSpec(final GordianMacType pMacType,
72                            final GordianLength pKeyLength,
73                            final GordianDigestSpec pDigestSpec) {
74          /* Store parameters */
75          theMacType = pMacType;
76          theKeyLength = pKeyLength;
77          theSubSpec = pDigestSpec;
78          isValid = checkValidity();
79      }
80  
81      /**
82       * gMac/Poly1305 Constructor.
83       *
84       * @param pMacType the macType
85       * @param pKeySpec the keySpec
86       */
87      public GordianMacSpec(final GordianMacType pMacType,
88                            final GordianSymKeySpec pKeySpec) {
89          /* Store macType */
90          theMacType = pMacType;
91  
92          /* Special handling for Poly1305 */
93          theKeyLength = GordianMacType.POLY1305 == pMacType
94                  ? GordianLength.LEN_256
95                  : pKeySpec.getKeyLength();
96          theSubSpec = pKeySpec;
97          isValid = checkValidity();
98      }
99  
100     /**
101      * zucMac Constructor.
102      *
103      * @param pMacType   the macType
104      * @param pKeyLength the keyLength
105      * @param pLength    the length
106      */
107     public GordianMacSpec(final GordianMacType pMacType,
108                           final GordianLength pKeyLength,
109                           final GordianLength pLength) {
110         /* Store parameters */
111         theMacType = pMacType;
112         theKeyLength = pKeyLength;
113         theSubSpec = pLength;
114         isValid = checkValidity();
115     }
116 
117     /**
118      * sipHash Constructor.
119      *
120      * @param pMacType the macType
121      * @param pSpec    the SipHashSpec
122      */
123     public GordianMacSpec(final GordianMacType pMacType,
124                           final GordianSipHashSpec pSpec) {
125         /* Store parameters */
126         theMacType = pMacType;
127         theKeyLength = GordianLength.LEN_128;
128         theSubSpec = pSpec;
129         isValid = checkValidity();
130     }
131 
132     /**
133      * vmpcMac/raw poly1305Mac Constructor.
134      *
135      * @param pKeyLength the keyLength
136      * @param pMacType   the macType
137      */
138     public GordianMacSpec(final GordianMacType pMacType,
139                           final GordianLength pKeyLength) {
140         theMacType = pMacType;
141         theKeyLength = pKeyLength;
142         theSubSpec = null;
143         isValid = checkValidity();
144     }
145 
146     /**
147      * Obtain Mac Type.
148      *
149      * @return the MacType
150      */
151     public GordianMacType getMacType() {
152         return theMacType;
153     }
154 
155     @Override
156     public GordianLength getKeyLength() {
157         return theKeyLength;
158     }
159 
160     /**
161      * Obtain SubSpec.
162      *
163      * @return the SubSpec
164      */
165     public Object getSubSpec() {
166         return theSubSpec;
167     }
168 
169     /**
170      * Is the macSpec valid?
171      *
172      * @return true/false.
173      */
174     public boolean isValid() {
175         return isValid;
176     }
177 
178     /**
179      * Obtain DigestSpec.
180      *
181      * @return the DigestSpec
182      */
183     public GordianDigestSpec getDigestSpec() {
184         return theSubSpec instanceof GordianDigestSpec mySpec
185                 ? mySpec
186                 : null;
187     }
188 
189     /**
190      * Obtain DigestState.
191      *
192      * @return the State
193      */
194     private GordianDigestState getDigestState() {
195         return theSubSpec instanceof GordianDigestSpec mySpec
196                 ? mySpec.getDigestState()
197                 : null;
198     }
199 
200     /**
201      * Obtain DigestLength.
202      *
203      * @return the Length
204      */
205     private GordianLength getDigestLength() {
206         return theSubSpec instanceof GordianDigestSpec mySpec
207                 ? mySpec.getDigestLength()
208                 : null;
209     }
210 
211     /**
212      * Obtain SymKeySpec.
213      *
214      * @return the SymKeySpec
215      */
216     public GordianSymKeySpec getSymKeySpec() {
217         return theSubSpec instanceof GordianSymKeySpec mySpec
218                 ? mySpec
219                 : null;
220     }
221 
222     /**
223      * Obtain SymKeyType.
224      *
225      * @return the Type
226      */
227     private GordianSymKeyType getSymKeyType() {
228         return theSubSpec instanceof GordianSymKeySpec mySpec
229                 ? mySpec.getSymKeyType()
230                 : null;
231     }
232 
233     /**
234      * Obtain SymKeyBlockLength.
235      *
236      * @return the BlockLength
237      */
238     private GordianLength getSymKeyBlockLength() {
239         return theSubSpec instanceof GordianSymKeySpec mySpec
240                 ? mySpec.getBlockLength()
241                 : null;
242     }
243 
244     /**
245      * Obtain SymKeyBlockLength.
246      *
247      * @return the BlockLength
248      */
249     private int getSymKeyBlockByteLength() {
250         return theSubSpec instanceof GordianSymKeySpec mySpec
251                 ? Objects.requireNonNull(mySpec.getBlockLength()).getByteLength()
252                 : 0;
253     }
254 
255     /**
256      * Obtain SymKeyHalfBlockLength.
257      *
258      * @return the HalfBlockLength
259      */
260     private GordianLength getSymKeyHalfBlockLength() {
261         return theSubSpec instanceof GordianSymKeySpec mySpec
262                 ? mySpec.getHalfBlockLength()
263                 : null;
264     }
265 
266     /**
267      * Obtain SipHashSpec.
268      *
269      * @return the Spec
270      */
271     public GordianSipHashSpec getSipHashSpec() {
272         return theSubSpec instanceof GordianSipHashSpec mySpec
273                 ? mySpec
274                 : null;
275     }
276 
277     /**
278      * Obtain MacLength.
279      *
280      * @return the Length
281      */
282     public GordianLength getMacLength() {
283         switch (theMacType) {
284             case HMAC:
285             case BLAKE2:
286             case BLAKE3:
287             case SKEIN:
288             case KUPYNA:
289             case KMAC:
290                 return getDigestLength();
291             case GMAC:
292             case POLY1305:
293                 return GordianLength.LEN_128;
294             case CMAC:
295             case KALYNA:
296                 return getSymKeyBlockLength();
297             case CBCMAC:
298             case CFBMAC:
299                 return getSymKeyHalfBlockLength();
300             case ZUC:
301                 return (GordianLength) theSubSpec;
302             case VMPC:
303                 return GordianLength.LEN_160;
304             case GOST:
305                 return GordianLength.LEN_32;
306             case SIPHASH:
307                 return ((GordianSipHashSpec) theSubSpec).getOutLength();
308             default:
309                 return GordianLength.LEN_64;
310         }
311     }
312 
313     /**
314      * Obtain the IV length.
315      *
316      * @return the IV Length
317      */
318     public int getIVLen() {
319         switch (theMacType) {
320             case VMPC:
321             case SKEIN:
322                 return GordianLength.LEN_128.getByteLength();
323             case POLY1305:
324                 return theSubSpec == null
325                         ? 0
326                         : GordianLength.LEN_128.getByteLength();
327             case BLAKE2:
328                 return Objects.requireNonNull(getDigestState()).isBlake2bState()
329                         ? GordianLength.LEN_128.getByteLength()
330                         : GordianLength.LEN_64.getByteLength();
331             case GMAC:
332                 return GordianLength.LEN_96.getByteLength();
333             case CBCMAC:
334             case CFBMAC:
335                 return getSymKeyBlockByteLength();
336             case GOST:
337                 return GordianLength.LEN_64.getByteLength();
338             case ZUC:
339                 return GordianLength.LEN_128 == theKeyLength
340                         ? GordianLength.LEN_128.getByteLength()
341                         : GordianLength.LEN_200.getByteLength();
342             default:
343                 return 0;
344         }
345     }
346 
347     /**
348      * Check spec validity.
349      *
350      * @return valid true/false
351      */
352     private boolean checkValidity() {
353         /* Make sure that macType and keyLength are non-null */
354         if (theMacType == null || theKeyLength == null) {
355             return false;
356         }
357 
358         /* Switch on MacType */
359         switch (theMacType) {
360             case HMAC:
361                 return checkDigestValidity(null);
362             case KUPYNA:
363                 return checkDigestValidity(GordianDigestType.KUPYNA);
364             case SKEIN:
365                 return checkDigestValidity(GordianDigestType.SKEIN);
366             case BLAKE2:
367                 return checkBlake2Validity();
368             case BLAKE3:
369                 return checkDigestValidity(GordianDigestType.BLAKE3);
370             case KALYNA:
371                 return checkSymKeyValidity(GordianSymKeyType.KALYNA);
372             case KMAC:
373                 return checkKMACValidity();
374             case CMAC:
375             case GMAC:
376             case CBCMAC:
377             case CFBMAC:
378                 return checkSymKeyValidity(null);
379             case POLY1305:
380                 return checkPoly1305Validity();
381             case ZUC:
382                 return checkZucValidity();
383             case SIPHASH:
384                 return theSubSpec instanceof GordianSipHashSpec
385                         && theKeyLength == GordianLength.LEN_128;
386             case GOST:
387                 return theSubSpec == null
388                         && theKeyLength == GordianLength.LEN_256;
389             case VMPC:
390                 return theSubSpec == null;
391             default:
392                 return false;
393         }
394     }
395 
396     /**
397      * Check digest validity.
398      *
399      * @param pDigestType required digestType (or null)
400      * @return valid true/false
401      */
402     private boolean checkDigestValidity(final GordianDigestType pDigestType) {
403         /* Check that the digestSpec is valid */
404         if (!(theSubSpec instanceof GordianDigestSpec)
405                 || !((GordianDigestSpec) theSubSpec).isValid()) {
406             return false;
407         }
408 
409         /* Check for digestType restrictions */
410         final GordianDigestType myType = ((GordianDigestSpec) theSubSpec).getDigestType();
411         return pDigestType == null
412                 ? myType.supportsLargeData()
413                 : myType == pDigestType;
414     }
415 
416     /**
417      * Check symKey validity.
418      *
419      * @param pSymKeyType required symKeyType (or null)
420      * @return valid true/false
421      */
422     private boolean checkSymKeyValidity(final GordianSymKeyType pSymKeyType) {
423         /* Check that the symKeySpec is valid */
424         if (!(theSubSpec instanceof GordianSymKeySpec)
425                 || !((GordianSymKeySpec) theSubSpec).isValid()) {
426             return false;
427         }
428 
429         /* Check for symKeyType restrictions */
430         return pSymKeyType == null
431                 || ((GordianSymKeySpec) theSubSpec).getSymKeyType() == pSymKeyType;
432     }
433 
434     /**
435      * Check poly1305 validity.
436      *
437      * @return valid true/false
438      */
439     private boolean checkPoly1305Validity() {
440         /* Check that the subSpec is reasonable */
441         if (theSubSpec != null
442                 && !checkSymKeyValidity(null)) {
443             return false;
444         }
445 
446         /* Restrict keyLengths */
447         final GordianSymKeySpec mySpec = (GordianSymKeySpec) theSubSpec;
448         return theKeyLength == GordianLength.LEN_256
449                 && (mySpec == null
450                 || mySpec.getKeyLength() == GordianLength.LEN_128);
451     }
452 
453     /**
454      * Check blake validity.
455      *
456      * @return valid true/false
457      */
458     private boolean checkBlake2Validity() {
459         /* Check that the spec is reasonable */
460         if (!checkDigestValidity(GordianDigestType.BLAKE2)) {
461             return false;
462         }
463 
464         /* Check keyLength */
465         return checkBlake2KeyLength(theKeyLength, (GordianDigestSpec) theSubSpec);
466     }
467 
468     /**
469      * Check blake2 keyLength validity.
470      *
471      * @param pKeyLen the keyLength
472      * @param pSpec   the digestSpec
473      * @return valid true/false
474      */
475     private static boolean checkBlake2KeyLength(final GordianLength pKeyLen,
476                                                 final GordianDigestSpec pSpec) {
477         /* Key length must be less or equal to the stateLength */
478         return pKeyLen.getLength() <= pSpec.getDigestState().getLength().getLength();
479     }
480 
481     /**
482      * Check blake validity.
483      *
484      * @return valid true/false
485      */
486     private boolean checkKMACValidity() {
487         /* Check that the spec is reasonable */
488         if (!checkDigestValidity(GordianDigestType.SHAKE)) {
489             return false;
490         }
491 
492         /* Check keyLength */
493         return checkKMACKeyLength(theKeyLength, (GordianDigestSpec) theSubSpec);
494     }
495 
496     /**
497      * Check KMAC keyLength validity.
498      *
499      * @param pKeyLen the keyLength
500      * @param pSpec   the digestSpec
501      * @return valid true/false
502      */
503     private static boolean checkKMACKeyLength(final GordianLength pKeyLen,
504                                               final GordianDigestSpec pSpec) {
505         /* Key length must be greater or equal to the stateLength */
506         return pKeyLen.getLength() >= pSpec.getDigestState().getLength().getLength();
507     }
508 
509     /**
510      * Check zuc validity.
511      *
512      * @return valid true/false
513      */
514     private boolean checkZucValidity() {
515         switch (theKeyLength) {
516             case LEN_128:
517                 return GordianLength.LEN_32 == theSubSpec;
518             case LEN_256:
519                 return GordianLength.LEN_32 == theSubSpec
520                         || GordianLength.LEN_64 == theSubSpec
521                         || GordianLength.LEN_128 == theSubSpec;
522             default:
523                 return false;
524         }
525     }
526 
527     /**
528      * Is this a Xof Mac?
529      *
530      * @return true/false
531      */
532     public boolean isXof() {
533         switch (theMacType) {
534             case KMAC:
535             case BLAKE3:
536                 return true;
537             case BLAKE2:
538             case SKEIN:
539                 return Objects.requireNonNull(getDigestSpec()).isXof();
540             default:
541                 return false;
542         }
543     }
544 
545     @Override
546     public String toString() {
547         /* If we have not yet loaded the name */
548         if (theName == null) {
549             /* If the macSpec is invalid */
550             if (!isValid) {
551                 /* Report invalid spec */
552                 theName = "InvalidMacSpec: " + theMacType + ":" + theSubSpec + ":" + theKeyLength;
553                 return theName;
554             }
555 
556             /* Load the name */
557             theName = theMacType.toString();
558             switch (theMacType) {
559                 case SIPHASH:
560                     theName = theSubSpec.toString();
561                     break;
562                 case POLY1305:
563                     theName += theSubSpec == null ? "" : SEP + getSymKeyType();
564                     break;
565                 case GMAC:
566                 case CMAC:
567                 case CFBMAC:
568                 case CBCMAC:
569                     theName += SEP + theSubSpec.toString();
570                     break;
571                 case KALYNA:
572                     theName += getSymKeyBlockLength() + SEP + theKeyLength;
573                     break;
574                 case KUPYNA:
575                     theName += SEP + getDigestLength() + SEP + theKeyLength;
576                     break;
577                 case KMAC:
578                     theName += getDigestState() + SEP + theKeyLength;
579                     break;
580                 case SKEIN:
581                     final boolean isSkeinXof = Objects.requireNonNull(getDigestSpec()).isXofMode();
582                     theName = GordianDigestType.SKEIN
583                             + (isSkeinXof ? "X" : "")
584                             + "Mac"
585                             + SEP + getDigestState()
586                             + (isSkeinXof ? "" : SEP + getDigestLength())
587                             + SEP + theKeyLength;
588                     break;
589                 case HMAC:
590                 case ZUC:
591                     theName += theSubSpec.toString() + SEP + theKeyLength;
592                     break;
593                 case BLAKE2:
594                     final boolean isBlakeXof = Objects.requireNonNull(getDigestSpec()).isXofMode();
595                     theName = GordianDigestType.BLAKE2
596                             + Objects.requireNonNull(getDigestState())
597                             .getBlake2Algorithm(isBlakeXof)
598                             + "Mac" + (isBlakeXof ? "" : SEP + getDigestLength())
599                             + SEP + theKeyLength;
600                     break;
601                 case BLAKE3:
602                     theName += SEP + getDigestLength();
603                     break;
604                 case VMPC:
605                     theName += theKeyLength;
606                     break;
607                 case GOST:
608                 default:
609                     break;
610             }
611         }
612 
613         /* return the name */
614         return theName;
615     }
616 
617     @Override
618     public boolean equals(final Object pThat) {
619         /* Handle the trivial cases */
620         if (this == pThat) {
621             return true;
622         }
623         if (pThat == null) {
624             return false;
625         }
626 
627         /* Check MacType, keyLength and subSpec */
628         return pThat instanceof GordianMacSpec myThat
629                 && theMacType == myThat.getMacType()
630                 && theKeyLength == myThat.getKeyLength()
631                 && Objects.equals(theSubSpec, myThat.getSubSpec());
632     }
633 
634     @Override
635     public int hashCode() {
636         return Objects.hash(theMacType, theKeyLength, theSubSpec);
637     }
638 }