1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
31
32 public final class GordianMacSpec
33 implements GordianKeySpec {
34
35
36
37 static final String SEP = "-";
38
39
40
41
42 private final GordianMacType theMacType;
43
44
45
46
47 private final GordianLength theKeyLength;
48
49
50
51
52 private final Object theSubSpec;
53
54
55
56
57 private final boolean isValid;
58
59
60
61
62 private String theName;
63
64
65
66
67
68
69
70
71 public GordianMacSpec(final GordianMacType pMacType,
72 final GordianLength pKeyLength,
73 final GordianDigestSpec pDigestSpec) {
74
75 theMacType = pMacType;
76 theKeyLength = pKeyLength;
77 theSubSpec = pDigestSpec;
78 isValid = checkValidity();
79 }
80
81
82
83
84
85
86
87 public GordianMacSpec(final GordianMacType pMacType,
88 final GordianSymKeySpec pKeySpec) {
89
90 theMacType = pMacType;
91
92
93 theKeyLength = GordianMacType.POLY1305 == pMacType
94 ? GordianLength.LEN_256
95 : pKeySpec.getKeyLength();
96 theSubSpec = pKeySpec;
97 isValid = checkValidity();
98 }
99
100
101
102
103
104
105
106
107 public GordianMacSpec(final GordianMacType pMacType,
108 final GordianLength pKeyLength,
109 final GordianLength pLength) {
110
111 theMacType = pMacType;
112 theKeyLength = pKeyLength;
113 theSubSpec = pLength;
114 isValid = checkValidity();
115 }
116
117
118
119
120
121
122
123 public GordianMacSpec(final GordianMacType pMacType,
124 final GordianSipHashSpec pSpec) {
125
126 theMacType = pMacType;
127 theKeyLength = GordianLength.LEN_128;
128 theSubSpec = pSpec;
129 isValid = checkValidity();
130 }
131
132
133
134
135
136
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
148
149
150
151 public GordianMacType getMacType() {
152 return theMacType;
153 }
154
155 @Override
156 public GordianLength getKeyLength() {
157 return theKeyLength;
158 }
159
160
161
162
163
164
165 public Object getSubSpec() {
166 return theSubSpec;
167 }
168
169
170
171
172
173
174 public boolean isValid() {
175 return isValid;
176 }
177
178
179
180
181
182
183 public GordianDigestSpec getDigestSpec() {
184 return theSubSpec instanceof GordianDigestSpec mySpec
185 ? mySpec
186 : null;
187 }
188
189
190
191
192
193
194 private GordianDigestState getDigestState() {
195 return theSubSpec instanceof GordianDigestSpec mySpec
196 ? mySpec.getDigestState()
197 : null;
198 }
199
200
201
202
203
204
205 private GordianLength getDigestLength() {
206 return theSubSpec instanceof GordianDigestSpec mySpec
207 ? mySpec.getDigestLength()
208 : null;
209 }
210
211
212
213
214
215
216 public GordianSymKeySpec getSymKeySpec() {
217 return theSubSpec instanceof GordianSymKeySpec mySpec
218 ? mySpec
219 : null;
220 }
221
222
223
224
225
226
227 private GordianSymKeyType getSymKeyType() {
228 return theSubSpec instanceof GordianSymKeySpec mySpec
229 ? mySpec.getSymKeyType()
230 : null;
231 }
232
233
234
235
236
237
238 private GordianLength getSymKeyBlockLength() {
239 return theSubSpec instanceof GordianSymKeySpec mySpec
240 ? mySpec.getBlockLength()
241 : null;
242 }
243
244
245
246
247
248
249 private int getSymKeyBlockByteLength() {
250 return theSubSpec instanceof GordianSymKeySpec mySpec
251 ? Objects.requireNonNull(mySpec.getBlockLength()).getByteLength()
252 : 0;
253 }
254
255
256
257
258
259
260 private GordianLength getSymKeyHalfBlockLength() {
261 return theSubSpec instanceof GordianSymKeySpec mySpec
262 ? mySpec.getHalfBlockLength()
263 : null;
264 }
265
266
267
268
269
270
271 public GordianSipHashSpec getSipHashSpec() {
272 return theSubSpec instanceof GordianSipHashSpec mySpec
273 ? mySpec
274 : null;
275 }
276
277
278
279
280
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
315
316
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
349
350
351
352 private boolean checkValidity() {
353
354 if (theMacType == null || theKeyLength == null) {
355 return false;
356 }
357
358
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
398
399
400
401
402 private boolean checkDigestValidity(final GordianDigestType pDigestType) {
403
404 if (!(theSubSpec instanceof GordianDigestSpec)
405 || !((GordianDigestSpec) theSubSpec).isValid()) {
406 return false;
407 }
408
409
410 final GordianDigestType myType = ((GordianDigestSpec) theSubSpec).getDigestType();
411 return pDigestType == null
412 ? myType.supportsLargeData()
413 : myType == pDigestType;
414 }
415
416
417
418
419
420
421
422 private boolean checkSymKeyValidity(final GordianSymKeyType pSymKeyType) {
423
424 if (!(theSubSpec instanceof GordianSymKeySpec)
425 || !((GordianSymKeySpec) theSubSpec).isValid()) {
426 return false;
427 }
428
429
430 return pSymKeyType == null
431 || ((GordianSymKeySpec) theSubSpec).getSymKeyType() == pSymKeyType;
432 }
433
434
435
436
437
438
439 private boolean checkPoly1305Validity() {
440
441 if (theSubSpec != null
442 && !checkSymKeyValidity(null)) {
443 return false;
444 }
445
446
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
455
456
457
458 private boolean checkBlake2Validity() {
459
460 if (!checkDigestValidity(GordianDigestType.BLAKE2)) {
461 return false;
462 }
463
464
465 return checkBlake2KeyLength(theKeyLength, (GordianDigestSpec) theSubSpec);
466 }
467
468
469
470
471
472
473
474
475 private static boolean checkBlake2KeyLength(final GordianLength pKeyLen,
476 final GordianDigestSpec pSpec) {
477
478 return pKeyLen.getLength() <= pSpec.getDigestState().getLength().getLength();
479 }
480
481
482
483
484
485
486 private boolean checkKMACValidity() {
487
488 if (!checkDigestValidity(GordianDigestType.SHAKE)) {
489 return false;
490 }
491
492
493 return checkKMACKeyLength(theKeyLength, (GordianDigestSpec) theSubSpec);
494 }
495
496
497
498
499
500
501
502
503 private static boolean checkKMACKeyLength(final GordianLength pKeyLen,
504 final GordianDigestSpec pSpec) {
505
506 return pKeyLen.getLength() >= pSpec.getDigestState().getLength().getLength();
507 }
508
509
510
511
512
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
529
530
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
548 if (theName == null) {
549
550 if (!isValid) {
551
552 theName = "InvalidMacSpec: " + theMacType + ":" + theSubSpec + ":" + theKeyLength;
553 return theName;
554 }
555
556
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
614 return theName;
615 }
616
617 @Override
618 public boolean equals(final Object pThat) {
619
620 if (this == pThat) {
621 return true;
622 }
623 if (pThat == null) {
624 return false;
625 }
626
627
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 }