1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
27
28 public class GordianDigestSpec
29 implements GordianIdSpec {
30
31
32
33 private static final String SEP = "-";
34
35
36
37
38 private final GordianDigestType theDigestType;
39
40
41
42
43 private final GordianDigestSubSpec theSubSpec;
44
45
46
47
48 private final GordianLength theLength;
49
50
51
52
53 private final Boolean isXofMode;
54
55
56
57
58 private final boolean isValid;
59
60
61
62
63 private String theName;
64
65
66
67
68
69
70 public GordianDigestSpec(final GordianDigestType pDigestType) {
71 this(pDigestType, pDigestType.getDefaultLength());
72 }
73
74
75
76
77
78
79
80 public GordianDigestSpec(final GordianDigestType pDigestType,
81 final GordianLength pLength) {
82
83 this(pDigestType, pLength, Boolean.FALSE);
84 }
85
86
87
88
89
90
91
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
101
102
103
104
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
114
115
116
117
118
119
120 public GordianDigestSpec(final GordianDigestType pDigestType,
121 final GordianDigestSubSpec pState,
122 final GordianLength pLength,
123 final Boolean pXofMode) {
124
125 theDigestType = pDigestType;
126 theSubSpec = pState;
127 theLength = pLength;
128 isXofMode = pXofMode;
129 isValid = checkValidity();
130 }
131
132
133
134
135
136
137 public GordianDigestType getDigestType() {
138 return theDigestType;
139 }
140
141
142
143
144
145
146 public GordianDigestSubSpec getSubSpec() {
147 return theSubSpec;
148 }
149
150
151
152
153
154
155 public GordianDigestState getDigestState() {
156 return theSubSpec instanceof GordianDigestState myState
157 ? myState
158 : null;
159 }
160
161
162
163
164
165
166 public GordianLength getDigestLength() {
167 return theLength;
168 }
169
170
171
172
173
174
175 public Boolean isXofMode() {
176 return isXofMode;
177 }
178
179
180
181
182
183
184 public Boolean isXof() {
185 return isXofMode || theDigestType.isXof();
186 }
187
188
189
190
191
192
193 public boolean isValid() {
194 return isValid;
195 }
196
197
198
199
200
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
211
212
213
214 private boolean checkValidity() {
215
216 if (theDigestType == null || theLength == null || isXofMode == null) {
217 return false;
218 }
219
220
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
247 if (theName == null) {
248
249 if (isValid) {
250
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
292 theName = "InvalidDigestSpec: " + theDigestType + ":" + theSubSpec + ":" + theLength + ":" + isXofMode;
293 }
294 }
295
296
297 return theName;
298 }
299
300 @Override
301 public boolean equals(final Object pThat) {
302
303 if (this == pThat) {
304 return true;
305 }
306 if (pThat == null) {
307 return false;
308 }
309
310
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 }