1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.github.tonywasher.joceanus.gordianknot.api.keypair;
18
19 import io.github.tonywasher.joceanus.gordianknot.api.base.GordianLength;
20 import org.bouncycastle.pqc.crypto.lms.LMOtsParameters;
21 import org.bouncycastle.pqc.crypto.lms.LMSParameters;
22 import org.bouncycastle.pqc.crypto.lms.LMSigParameters;
23
24 import java.util.ArrayList;
25 import java.util.List;
26 import java.util.Objects;
27
28
29
30
31 public class GordianLMSKeySpec {
32
33
34
35 private static final String SEP = "-";
36
37
38
39
40 private static final String INVALID_LENGTH = "Invalid Length: ";
41
42
43
44
45 private final GordianLMSHash theHash;
46
47
48
49
50 private final GordianLMSWidth theWidth;
51
52
53
54
55 private final GordianLMSHeight theHeight;
56
57
58
59
60 private final GordianLength theLength;
61
62
63
64
65 private final LMSParameters theParams;
66
67
68
69
70 private final boolean isValid;
71
72
73
74
75 private String theName;
76
77
78
79
80
81
82
83
84
85 public GordianLMSKeySpec(final GordianLMSHash pHashType,
86 final GordianLMSHeight pHeight,
87 final GordianLMSWidth pWidth,
88 final GordianLength pLength) {
89
90 theHash = pHashType;
91 theWidth = pWidth;
92 theHeight = pHeight;
93 theLength = pLength;
94
95
96 isValid = checkValidity();
97
98
99 final LMSigParameters mySig = isValid ? theHeight.getSigParameter(theHash, theLength) : null;
100 final LMOtsParameters myOts = isValid ? theWidth.getOtsParameter(theHash, theLength) : null;
101 theParams = isValid ? new LMSParameters(mySig, myOts) : null;
102 }
103
104
105
106
107
108
109 public GordianLMSHash getHash() {
110 return theHash;
111 }
112
113
114
115
116
117
118 public GordianLMSHeight getHeight() {
119 return theHeight;
120 }
121
122
123
124
125
126
127 public GordianLMSWidth getWidth() {
128 return theWidth;
129 }
130
131
132
133
134
135
136 public GordianLength getLength() {
137 return theLength;
138 }
139
140
141
142
143
144
145 public LMSParameters getParameters() {
146 return theParams;
147 }
148
149
150
151
152
153
154 public boolean isHigh() {
155 return isValid && theHeight.isHigh();
156 }
157
158
159
160
161
162
163 public boolean isValid() {
164 return isValid;
165 }
166
167
168
169
170
171
172 private boolean checkValidity() {
173 if (theWidth == null || theHeight == null || theHash == null || theLength == null) {
174 return false;
175 }
176 switch (theLength) {
177 case LEN_192:
178 case LEN_256:
179 return true;
180 default:
181 return false;
182 }
183 }
184
185 @Override
186 public String toString() {
187
188 if (theName == null) {
189
190 if (isValid) {
191
192 theName = theHash.toString() + SEP + theWidth.toString() + SEP + theHeight.toString() + SEP + theLength.toString();
193 } else {
194
195 theName = "InvalidLMSKeySpec: " + theHash + SEP + theWidth + SEP + theHeight + SEP + theLength;
196 }
197 }
198
199
200 return theName;
201 }
202
203 @Override
204 public boolean equals(final Object pThat) {
205
206 if (this == pThat) {
207 return true;
208 }
209 if (pThat == null) {
210 return false;
211 }
212
213
214 return pThat instanceof GordianLMSKeySpec myThat
215 && theHash == myThat.theHash
216 && theLength == myThat.theLength
217 && theWidth == myThat.theWidth
218 && theHeight == myThat.theHeight;
219 }
220
221 @Override
222 public int hashCode() {
223 return Objects.hash(theHash, theHeight, theWidth, theLength);
224 }
225
226
227
228
229
230
231 public static List<GordianLMSKeySpec> listPossibleKeySpecs() {
232
233 final List<GordianLMSKeySpec> mySpecs = new ArrayList<>();
234
235
236 for (final GordianLMSHeight myHeight : GordianLMSHeight.values()) {
237 for (final GordianLMSWidth myWidth : GordianLMSWidth.values()) {
238 mySpecs.add(new GordianLMSKeySpec(GordianLMSHash.SHA256, myHeight, myWidth, GordianLength.LEN_256));
239 mySpecs.add(new GordianLMSKeySpec(GordianLMSHash.SHA256, myHeight, myWidth, GordianLength.LEN_192));
240 mySpecs.add(new GordianLMSKeySpec(GordianLMSHash.SHAKE256, myHeight, myWidth, GordianLength.LEN_256));
241 mySpecs.add(new GordianLMSKeySpec(GordianLMSHash.SHAKE256, myHeight, myWidth, GordianLength.LEN_192));
242 }
243 }
244
245
246 return mySpecs;
247 }
248
249
250
251
252
253
254
255
256 public static GordianLMSKeySpec determineKeySpec(final LMSigParameters pSigParams,
257 final LMOtsParameters pOtsParams) {
258 final List<GordianLMSKeySpec> mySpecs = listPossibleKeySpecs();
259 for (GordianLMSKeySpec mySpec : mySpecs) {
260 if (pSigParams.equals(mySpec.getParameters().getLMSigParam())
261 && pOtsParams.equals(mySpec.getParameters().getLMOTSParam())) {
262 return mySpec;
263 }
264 }
265 throw new IllegalArgumentException("Unsupported LMSSpec");
266 }
267
268
269
270
271 public static class GordianHSSKeySpec {
272
273
274
275 public static final int MAX_DEPTH = 8;
276
277
278
279
280 private final GordianLMSKeySpec theKeySpec;
281
282
283
284
285 private final int theDepth;
286
287
288
289
290 private final boolean isValid;
291
292
293
294
295 private String theName;
296
297
298
299
300
301
302
303 GordianHSSKeySpec(final GordianLMSKeySpec pKeySpec,
304 final int pDepth) {
305
306 theKeySpec = pKeySpec;
307 theDepth = pDepth;
308 isValid = checkValidity();
309 }
310
311
312
313
314
315
316 public GordianLMSKeySpec getKeySpec() {
317
318 return isValid
319 ? theKeySpec
320 : null;
321 }
322
323
324
325
326
327
328 public int getTreeDepth() {
329
330 return isValid
331 ? theDepth
332 : -1;
333 }
334
335
336
337
338
339
340 public boolean isValid() {
341 return isValid;
342 }
343
344
345
346
347
348
349 private boolean checkValidity() {
350
351 if (theDepth < 1 || theDepth > MAX_DEPTH) {
352 return false;
353 }
354
355
356 return theKeySpec != null && theKeySpec.isValid;
357 }
358
359 @Override
360 public String toString() {
361
362 if (theName == null) {
363
364 if (isValid) {
365
366 theName = "HSS-" + theDepth + "-" + theKeySpec;
367
368 } else {
369
370 theName = "InvalidHSSKeySpec: " + theDepth + ":" + theKeySpec;
371 }
372 }
373
374
375 return theName;
376 }
377
378 @Override
379 public boolean equals(final Object pThat) {
380
381 if (this == pThat) {
382 return true;
383 }
384 if (pThat == null) {
385 return false;
386 }
387
388
389 if (pThat.getClass() != this.getClass()) {
390 return false;
391 }
392
393
394 final GordianHSSKeySpec myThat = (GordianHSSKeySpec) pThat;
395
396
397 return theDepth == myThat.theDepth
398 && Objects.equals(theKeySpec, myThat.theKeySpec);
399 }
400
401 @Override
402 public int hashCode() {
403 return theKeySpec.hashCode() + theDepth;
404 }
405 }
406
407
408
409
410 public enum GordianLMSHash {
411
412
413
414 SHA256,
415
416
417
418
419 SHAKE256;
420 }
421
422
423
424
425 public enum GordianLMSHeight {
426
427
428
429 H5,
430
431
432
433
434 H10,
435
436
437
438
439 H15,
440
441
442
443
444 H20,
445
446
447
448
449 H25;
450
451
452
453
454
455
456
457
458 private LMSigParameters getSigParameter(final GordianLMSHash pHash,
459 final GordianLength pLength) {
460 switch (this) {
461 case H5:
462 return getH5Parameter(pHash, pLength);
463 case H10:
464 return getH10Parameter(pHash, pLength);
465 case H15:
466 return getH15Parameter(pHash, pLength);
467 case H20:
468 return getH20Parameter(pHash, pLength);
469 case H25:
470 return getH25Parameter(pHash, pLength);
471 default:
472 throw new IllegalStateException();
473 }
474 }
475
476
477
478
479
480
481
482
483 private LMSigParameters getH5Parameter(final GordianLMSHash pHash,
484 final GordianLength pLength) {
485 switch (pLength) {
486 case LEN_192:
487 return pHash == GordianLMSHash.SHA256 ? LMSigParameters.lms_sha256_n24_h5 : LMSigParameters.lms_shake256_n24_h5;
488 case LEN_256:
489 return pHash == GordianLMSHash.SHA256 ? LMSigParameters.lms_sha256_n32_h5 : LMSigParameters.lms_shake256_n32_h5;
490 default:
491 throw new IllegalArgumentException(INVALID_LENGTH + pLength);
492 }
493 }
494
495
496
497
498
499
500
501
502 private LMSigParameters getH10Parameter(final GordianLMSHash pHash,
503 final GordianLength pLength) {
504 switch (pLength) {
505 case LEN_192:
506 return pHash == GordianLMSHash.SHA256 ? LMSigParameters.lms_sha256_n24_h10 : LMSigParameters.lms_shake256_n24_h10;
507 case LEN_256:
508 return pHash == GordianLMSHash.SHA256 ? LMSigParameters.lms_sha256_n32_h10 : LMSigParameters.lms_shake256_n32_h10;
509 default:
510 throw new IllegalArgumentException(INVALID_LENGTH + pLength);
511 }
512 }
513
514
515
516
517
518
519
520
521 private LMSigParameters getH15Parameter(final GordianLMSHash pHash,
522 final GordianLength pLength) {
523 switch (pLength) {
524 case LEN_192:
525 return pHash == GordianLMSHash.SHA256 ? LMSigParameters.lms_sha256_n24_h15 : LMSigParameters.lms_shake256_n24_h15;
526 case LEN_256:
527 return pHash == GordianLMSHash.SHA256 ? LMSigParameters.lms_sha256_n32_h15 : LMSigParameters.lms_shake256_n32_h15;
528 default:
529 throw new IllegalArgumentException(INVALID_LENGTH + pLength);
530 }
531 }
532
533
534
535
536
537
538
539
540 private LMSigParameters getH20Parameter(final GordianLMSHash pHash,
541 final GordianLength pLength) {
542 switch (pLength) {
543 case LEN_192:
544 return pHash == GordianLMSHash.SHA256 ? LMSigParameters.lms_sha256_n24_h20 : LMSigParameters.lms_shake256_n24_h20;
545 case LEN_256:
546 return pHash == GordianLMSHash.SHA256 ? LMSigParameters.lms_sha256_n32_h20 : LMSigParameters.lms_shake256_n32_h20;
547 default:
548 throw new IllegalArgumentException(INVALID_LENGTH + pLength);
549 }
550 }
551
552
553
554
555
556
557
558
559 private LMSigParameters getH25Parameter(final GordianLMSHash pHash,
560 final GordianLength pLength) {
561 switch (pLength) {
562 case LEN_192:
563 return pHash == GordianLMSHash.SHA256 ? LMSigParameters.lms_sha256_n24_h25 : LMSigParameters.lms_shake256_n24_h25;
564 case LEN_256:
565 return pHash == GordianLMSHash.SHA256 ? LMSigParameters.lms_sha256_n32_h25 : LMSigParameters.lms_shake256_n32_h25;
566 default:
567 throw new IllegalArgumentException(INVALID_LENGTH + pLength);
568 }
569 }
570
571
572
573
574
575
576 public boolean isHigh() {
577 switch (this) {
578 case H15:
579 case H20:
580 case H25:
581 return true;
582 default:
583 return false;
584 }
585 }
586 }
587
588
589
590
591 public enum GordianLMSWidth {
592
593
594
595 W1,
596
597
598
599
600 W2,
601
602
603
604
605 W4,
606
607
608
609
610 W8;
611
612
613
614
615
616
617
618
619 private LMOtsParameters getOtsParameter(final GordianLMSHash pHash,
620 final GordianLength pLength) {
621 switch (this) {
622 case W1:
623 return getW1Parameter(pHash, pLength);
624 case W2:
625 return getW2Parameter(pHash, pLength);
626 case W4:
627 return getW4Parameter(pHash, pLength);
628 case W8:
629 return getW8Parameter(pHash, pLength);
630 default:
631 throw new IllegalStateException();
632 }
633 }
634
635
636
637
638
639
640
641
642 private LMOtsParameters getW1Parameter(final GordianLMSHash pHash,
643 final GordianLength pLength) {
644 switch (pLength) {
645 case LEN_192:
646 return pHash == GordianLMSHash.SHA256 ? LMOtsParameters.sha256_n24_w1 : LMOtsParameters.shake256_n24_w1;
647 case LEN_256:
648 return pHash == GordianLMSHash.SHA256 ? LMOtsParameters.sha256_n32_w1 : LMOtsParameters.shake256_n32_w1;
649 default:
650 throw new IllegalArgumentException(INVALID_LENGTH + pLength);
651 }
652 }
653
654
655
656
657
658
659
660
661 private LMOtsParameters getW2Parameter(final GordianLMSHash pHash,
662 final GordianLength pLength) {
663 switch (pLength) {
664 case LEN_192:
665 return pHash == GordianLMSHash.SHA256 ? LMOtsParameters.sha256_n24_w2 : LMOtsParameters.shake256_n24_w2;
666 case LEN_256:
667 return pHash == GordianLMSHash.SHA256 ? LMOtsParameters.sha256_n32_w2 : LMOtsParameters.shake256_n32_w2;
668 default:
669 throw new IllegalArgumentException(INVALID_LENGTH + pLength);
670 }
671 }
672
673
674
675
676
677
678
679
680 private LMOtsParameters getW4Parameter(final GordianLMSHash pHash,
681 final GordianLength pLength) {
682 switch (pLength) {
683 case LEN_192:
684 return pHash == GordianLMSHash.SHA256 ? LMOtsParameters.sha256_n24_w4 : LMOtsParameters.shake256_n24_w4;
685 case LEN_256:
686 return pHash == GordianLMSHash.SHA256 ? LMOtsParameters.sha256_n32_w4 : LMOtsParameters.shake256_n32_w4;
687 default:
688 throw new IllegalArgumentException(INVALID_LENGTH + pLength);
689 }
690 }
691
692
693
694
695
696
697
698
699 private LMOtsParameters getW8Parameter(final GordianLMSHash pHash,
700 final GordianLength pLength) {
701 switch (pLength) {
702 case LEN_192:
703 return pHash == GordianLMSHash.SHA256 ? LMOtsParameters.sha256_n24_w8 : LMOtsParameters.shake256_n24_w8;
704 case LEN_256:
705 return pHash == GordianLMSHash.SHA256 ? LMOtsParameters.sha256_n32_w8 : LMOtsParameters.shake256_n32_w8;
706 default:
707 throw new IllegalArgumentException(INVALID_LENGTH + pLength);
708 }
709 }
710 }
711 }