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.digest;
18  
19  import io.github.tonywasher.joceanus.gordianknot.api.base.GordianIdSpec;
20  import io.github.tonywasher.joceanus.gordianknot.api.base.GordianLength;
21  import io.github.tonywasher.joceanus.gordianknot.api.digest.GordianDigestSubSpec.GordianDigestState;
22  
23  import java.util.Objects;
24  
25  /**
26   * Digest Specification.
27   */
28  public class GordianDigestSpec
29          implements GordianIdSpec {
30      /**
31       * The Separator.
32       */
33      private static final String SEP = "-";
34  
35      /**
36       * The Digest Type.
37       */
38      private final GordianDigestType theDigestType;
39  
40      /**
41       * The Digest SubSpec.
42       */
43      private final GordianDigestSubSpec theSubSpec;
44  
45      /**
46       * The Digest Length.
47       */
48      private final GordianLength theLength;
49  
50      /**
51       * Is this a Xof mode?
52       */
53      private final Boolean isXofMode;
54  
55      /**
56       * The Validity.
57       */
58      private final boolean isValid;
59  
60      /**
61       * The String name.
62       */
63      private String theName;
64  
65      /**
66       * Constructor.
67       *
68       * @param pDigestType the digestType
69       */
70      public GordianDigestSpec(final GordianDigestType pDigestType) {
71          this(pDigestType, pDigestType.getDefaultLength());
72      }
73  
74      /**
75       * Constructor.
76       *
77       * @param pDigestType the digestType
78       * @param pLength     the length
79       */
80      public GordianDigestSpec(final GordianDigestType pDigestType,
81                               final GordianLength pLength) {
82          /* Store parameters */
83          this(pDigestType, pLength, Boolean.FALSE);
84      }
85  
86      /**
87       * Constructor.
88       *
89       * @param pDigestType the digestType
90       * @param pLength     the length
91       * @param pXofMode    is this an explicit Xof?
92       */
93      public GordianDigestSpec(final GordianDigestType pDigestType,
94                               final GordianLength pLength,
95                               final Boolean pXofMode) {
96          this(pDigestType, GordianDigestSubSpec.getDefaultSubSpecForTypeAndLength(pDigestType, pLength), pLength, pXofMode);
97      }
98  
99      /**
100      * Constructor.
101      *
102      * @param pDigestType the digestType
103      * @param pState      the digestState
104      * @param pLength     the length
105      */
106     public GordianDigestSpec(final GordianDigestType pDigestType,
107                              final GordianDigestSubSpec pState,
108                              final GordianLength pLength) {
109         this(pDigestType, pState, pLength, Boolean.FALSE);
110     }
111 
112     /**
113      * Constructor.
114      *
115      * @param pDigestType the digestType
116      * @param pState      the digestState
117      * @param pLength     the length
118      * @param pXofMode    is this an explicit Xof?
119      */
120     public GordianDigestSpec(final GordianDigestType pDigestType,
121                              final GordianDigestSubSpec pState,
122                              final GordianLength pLength,
123                              final Boolean pXofMode) {
124         /* Store parameters */
125         theDigestType = pDigestType;
126         theSubSpec = pState;
127         theLength = pLength;
128         isXofMode = pXofMode;
129         isValid = checkValidity();
130     }
131 
132     /**
133      * Obtain Digest Type.
134      *
135      * @return the DigestType
136      */
137     public GordianDigestType getDigestType() {
138         return theDigestType;
139     }
140 
141     /**
142      * Obtain DigestSubSpec.
143      *
144      * @return the SubSpec
145      */
146     public GordianDigestSubSpec getSubSpec() {
147         return theSubSpec;
148     }
149 
150     /**
151      * Obtain DigestState.
152      *
153      * @return the State
154      */
155     public GordianDigestState getDigestState() {
156         return theSubSpec instanceof GordianDigestState myState
157                 ? myState
158                 : null;
159     }
160 
161     /**
162      * Obtain Digest Length.
163      *
164      * @return the Length
165      */
166     public GordianLength getDigestLength() {
167         return theLength;
168     }
169 
170     /**
171      * Is the digestSpec a Xof mode?
172      *
173      * @return true/false.
174      */
175     public Boolean isXofMode() {
176         return isXofMode;
177     }
178 
179     /**
180      * Is the digestSpec a Xof?
181      *
182      * @return true/false.
183      */
184     public Boolean isXof() {
185         return isXofMode || theDigestType.isXof();
186     }
187 
188     /**
189      * Is the digestSpec valid?
190      *
191      * @return true/false.
192      */
193     public boolean isValid() {
194         return isValid;
195     }
196 
197     /**
198      * Is this a sha2 hybrid state.
199      *
200      * @return true/false
201      */
202     public boolean isSha2Hybrid() {
203         return GordianDigestType.SHA2.equals(theDigestType)
204                 && GordianDigestState.STATE512.equals(theSubSpec)
205                 && (GordianLength.LEN_224.equals(theLength)
206                 || GordianLength.LEN_256.equals(theLength));
207     }
208 
209     /**
210      * Check spec validity.
211      *
212      * @return valid true/false
213      */
214     private boolean checkValidity() {
215         /* Handle null type */
216         if (theDigestType == null || theLength == null || isXofMode == null) {
217             return false;
218         }
219 
220         /* Switch on keyType */
221         switch (theDigestType) {
222             case SHA2:
223             case SHAKE:
224             case KANGAROO:
225             case HARAKA:
226                 return theSubSpec instanceof GordianDigestState
227                         && !isXofMode
228                         && getDigestState().validForTypeAndLength(theDigestType, theLength);
229             case SKEIN:
230             case BLAKE2:
231                 return theSubSpec instanceof GordianDigestState
232                         && (isXofMode ? getDigestState().lengthForXofType(theDigestType) == theLength
233                         : getDigestState().validForTypeAndLength(theDigestType, theLength));
234             case ASCON:
235                 return theSubSpec == null
236                         && theDigestType.isLengthValid(theLength);
237             default:
238                 return theSubSpec == null
239                         && !isXofMode
240                         && theDigestType.isLengthValid(theLength);
241         }
242     }
243 
244     @Override
245     public String toString() {
246         /* If we have not yet loaded the name */
247         if (theName == null) {
248             /* If the keySpec is valid */
249             if (isValid) {
250                 /* Load the name */
251                 theName = theDigestType.toString();
252                 switch (theDigestType) {
253                     case SHA2:
254                         if (isSha2Hybrid()) {
255                             theName += SEP + theSubSpec;
256                         }
257                         theName += SEP + theLength;
258                         break;
259                     case SHAKE:
260                         theName += theSubSpec;
261                         break;
262                     case SKEIN:
263                         if (Boolean.TRUE.equals(isXofMode)) {
264                             theName += "X" + SEP + theSubSpec;
265                         } else {
266                             theName += SEP + theSubSpec + SEP + theLength;
267                         }
268                         break;
269                     case BLAKE2:
270                         theName += getDigestState().getBlake2Algorithm(isXofMode);
271                         if (!Boolean.TRUE.equals(isXofMode)) {
272                             theName += SEP + theLength;
273                         }
274                         break;
275                     case KANGAROO:
276                         theName = getDigestState().getKangarooAlgorithm();
277                         break;
278                     case HARAKA:
279                         theName += SEP + theSubSpec;
280                         break;
281                     case ASCON:
282                         theName += Boolean.TRUE.equals(isXofMode) ? "X" : "";
283                         break;
284                     default:
285                         if (theDigestType.getSupportedLengths().length > 1) {
286                             theName += SEP + theLength;
287                         }
288                         break;
289                 }
290             } else {
291                 /* Report invalid spec */
292                 theName = "InvalidDigestSpec: " + theDigestType + ":" + theSubSpec + ":" + theLength + ":" + isXofMode;
293             }
294         }
295 
296         /* return the name */
297         return theName;
298     }
299 
300     @Override
301     public boolean equals(final Object pThat) {
302         /* Handle the trivial cases */
303         if (this == pThat) {
304             return true;
305         }
306         if (pThat == null) {
307             return false;
308         }
309 
310         /* Check subFields */
311         return pThat instanceof GordianDigestSpec myThat
312                 && theDigestType == myThat.getDigestType()
313                 && theSubSpec == myThat.getSubSpec()
314                 && theLength == myThat.getDigestLength()
315                 && isXofMode == myThat.isXofMode();
316     }
317 
318     @Override
319     public int hashCode() {
320         return Objects.hash(theDigestType, theSubSpec, theLength, isXofMode);
321     }
322 }