1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.github.tonywasher.joceanus.gordianknot.api.sign;
18
19 import io.github.tonywasher.joceanus.gordianknot.api.base.GordianLength;
20 import io.github.tonywasher.joceanus.gordianknot.api.digest.GordianDigestSpec;
21 import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianKeyPairType;
22
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.Objects;
26
27
28
29
30 public final class GordianSignatureSpec {
31
32
33
34 private static final String SEP = "-";
35
36
37
38
39 private final GordianKeyPairType theKeyPairType;
40
41
42
43
44 private final GordianSignatureType theSignatureType;
45
46
47
48
49 private final Object theSignatureSpec;
50
51
52
53
54 private final boolean isValid;
55
56
57
58
59 private String theName;
60
61
62
63
64
65
66
67 public GordianSignatureSpec(final GordianKeyPairType pKeyPairType,
68 final Object pSignatureSpec) {
69
70 this(pKeyPairType, GordianSignatureType.NATIVE, pSignatureSpec);
71 }
72
73
74
75
76
77
78
79 public GordianSignatureSpec(final GordianKeyPairType pKeyPairType,
80 final GordianSignatureType pSignatureType) {
81
82 this(pKeyPairType, pSignatureType, null);
83 }
84
85
86
87
88
89
90
91
92 public GordianSignatureSpec(final GordianKeyPairType pKeyPairType,
93 final GordianSignatureType pSignatureType,
94 final Object pSignatureSpec) {
95
96 theKeyPairType = pKeyPairType;
97 theSignatureType = pSignatureType;
98 theSignatureSpec = pSignatureSpec;
99 isValid = checkValidity();
100 }
101
102
103
104
105
106
107 public GordianKeyPairType getKeyPairType() {
108 return theKeyPairType;
109 }
110
111
112
113
114
115
116 public GordianSignatureType getSignatureType() {
117 return theSignatureType;
118 }
119
120
121
122
123
124
125 public Object getSignatureSpec() {
126 return theSignatureSpec;
127 }
128
129
130
131
132
133
134 public GordianDigestSpec getDigestSpec() {
135 if (!(theSignatureSpec instanceof GordianDigestSpec)) {
136 throw new IllegalArgumentException();
137 }
138 return (GordianDigestSpec) theSignatureSpec;
139 }
140
141
142
143
144
145
146 @SuppressWarnings("unchecked")
147 public Iterator<GordianSignatureSpec> signatureSpecIterator() {
148 if (!(theSignatureSpec instanceof List)) {
149 throw new IllegalArgumentException();
150 }
151 return ((List<GordianSignatureSpec>) theSignatureSpec).iterator();
152 }
153
154
155
156
157
158
159 public boolean isValid() {
160 return isValid;
161 }
162
163
164
165
166
167
168 public boolean supportsContext() {
169 switch (theKeyPairType) {
170 case MLDSA:
171 case SLHDSA:
172 return true;
173 default:
174 return false;
175 }
176 }
177
178
179
180
181
182
183 private boolean checkValidity() {
184 if (theKeyPairType == null || theSignatureType == null) {
185 return false;
186 }
187 switch (theKeyPairType) {
188 case RSA:
189 case DSA:
190 case EC:
191 case DSTU4145:
192 case GOST2012:
193 if (!(theSignatureSpec instanceof GordianDigestSpec)) {
194 return false;
195 }
196 final GordianDigestSpec mySpec = getDigestSpec();
197 return mySpec.isValid() && mySpec.getDigestType().supportsLargeData();
198 case EDDSA:
199 case SLHDSA:
200 case MLDSA:
201 case FALCON:
202 case MAYO:
203 case SNOVA:
204 case XMSS:
205 case LMS:
206 return theSignatureSpec == null;
207 case PICNIC:
208 return theSignatureSpec == null || checkPICNICDigest();
209 case SM2:
210 return checkSM2Digest();
211 case COMPOSITE:
212 return theSignatureSpec instanceof List && checkComposite();
213 default:
214 return false;
215 }
216 }
217
218
219
220
221
222
223 private boolean checkComposite() {
224 final Iterator<GordianSignatureSpec> myIterator = signatureSpecIterator();
225 while (myIterator.hasNext()) {
226
227 final GordianSignatureSpec mySpec = myIterator.next();
228 if (mySpec == null || !mySpec.isValid()) {
229 return false;
230 }
231 }
232 return true;
233 }
234
235
236
237
238
239
240 private boolean checkPICNICDigest() {
241
242 final GordianDigestSpec myDigest = getDigestSpec();
243 if (!GordianLength.LEN_512.equals(myDigest.getDigestLength())) {
244 return false;
245 }
246
247
248 switch (myDigest.getDigestType()) {
249 case SHA2:
250 case SHA3:
251 case SHAKE:
252 return true;
253 default:
254 return false;
255 }
256 }
257
258
259
260
261
262
263 private boolean checkSM2Digest() {
264
265 final GordianDigestSpec myDigest = getDigestSpec();
266 switch (myDigest.getDigestType()) {
267 case SM3:
268 return true;
269 case SHA2:
270 return GordianLength.LEN_256.equals(myDigest.getDigestLength())
271 && !myDigest.isSha2Hybrid();
272 default:
273 return false;
274 }
275 }
276
277 @Override
278 public String toString() {
279
280 if (theName == null) {
281
282 theName = theKeyPairType.toString();
283 if (theSignatureType != GordianSignatureType.NATIVE) {
284 theName += SEP + theSignatureType.toString();
285 }
286 if (theSignatureSpec != null) {
287 if (theKeyPairType == GordianKeyPairType.COMPOSITE) {
288 final Iterator<GordianSignatureSpec> myIterator = signatureSpecIterator();
289 final StringBuilder myBuilder = new StringBuilder(theName);
290 while (myIterator.hasNext()) {
291 myBuilder.append(SEP).append(myIterator.next().toString());
292 }
293 theName = myBuilder.toString();
294 } else {
295 theName += SEP + theSignatureSpec.toString();
296 }
297 }
298 }
299
300
301 return theName;
302 }
303
304 @Override
305 public boolean equals(final Object pThat) {
306
307 if (this == pThat) {
308 return true;
309 }
310 if (pThat == null) {
311 return false;
312 }
313
314
315 return pThat instanceof GordianSignatureSpec myThat
316 && theKeyPairType == myThat.getKeyPairType()
317 && theSignatureType == myThat.getSignatureType()
318 && Objects.equals(theSignatureSpec, myThat.theSignatureSpec);
319 }
320
321 @Override
322 public int hashCode() {
323 return Objects.hash(theKeyPairType, theSignatureType, theSignatureSpec);
324 }
325 }