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.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   * Signature Specification.
29   */
30  public final class GordianSignatureSpec {
31      /**
32       * The Separator.
33       */
34      private static final String SEP = "-";
35  
36      /**
37       * KeyPairType.
38       */
39      private final GordianKeyPairType theKeyPairType;
40  
41      /**
42       * SignatureType.
43       */
44      private final GordianSignatureType theSignatureType;
45  
46      /**
47       * SignatureSpec.
48       */
49      private final Object theSignatureSpec;
50  
51      /**
52       * The Validity.
53       */
54      private final boolean isValid;
55  
56      /**
57       * The String name.
58       */
59      private String theName;
60  
61      /**
62       * Constructor.
63       *
64       * @param pKeyPairType   the keyPairType
65       * @param pSignatureSpec the signatureSpec
66       */
67      public GordianSignatureSpec(final GordianKeyPairType pKeyPairType,
68                                  final Object pSignatureSpec) {
69          /* Store parameters */
70          this(pKeyPairType, GordianSignatureType.NATIVE, pSignatureSpec);
71      }
72  
73      /**
74       * Constructor.
75       *
76       * @param pKeyPairType   the keyPairType
77       * @param pSignatureType the signatureType
78       */
79      public GordianSignatureSpec(final GordianKeyPairType pKeyPairType,
80                                  final GordianSignatureType pSignatureType) {
81          /* Store parameters */
82          this(pKeyPairType, pSignatureType, null);
83      }
84  
85      /**
86       * Constructor.
87       *
88       * @param pKeyPairType   the keyPairType
89       * @param pSignatureType the signatureType
90       * @param pSignatureSpec the signatureSpec
91       */
92      public GordianSignatureSpec(final GordianKeyPairType pKeyPairType,
93                                  final GordianSignatureType pSignatureType,
94                                  final Object pSignatureSpec) {
95          /* Store parameters */
96          theKeyPairType = pKeyPairType;
97          theSignatureType = pSignatureType;
98          theSignatureSpec = pSignatureSpec;
99          isValid = checkValidity();
100     }
101 
102     /**
103      * Obtain the keyPairType.
104      *
105      * @return the keyPairType.
106      */
107     public GordianKeyPairType getKeyPairType() {
108         return theKeyPairType;
109     }
110 
111     /**
112      * Obtain the SignatureType.
113      *
114      * @return the signatureType.
115      */
116     public GordianSignatureType getSignatureType() {
117         return theSignatureType;
118     }
119 
120     /**
121      * Obtain the signatureSpec.
122      *
123      * @return the signatureSpec.
124      */
125     public Object getSignatureSpec() {
126         return theSignatureSpec;
127     }
128 
129     /**
130      * Obtain the DigestSpec.
131      *
132      * @return the digestSpec.
133      */
134     public GordianDigestSpec getDigestSpec() {
135         if (!(theSignatureSpec instanceof GordianDigestSpec)) {
136             throw new IllegalArgumentException();
137         }
138         return (GordianDigestSpec) theSignatureSpec;
139     }
140 
141     /**
142      * Obtain the composite signatureSpec iterator.
143      *
144      * @return the signatureeSpec iterator.
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      * Is the signatureSpec valid?
156      *
157      * @return true/false.
158      */
159     public boolean isValid() {
160         return isValid;
161     }
162 
163     /**
164      * Does this signatureSpec support context?
165      *
166      * @return true/false
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      * Check spec validity.
180      *
181      * @return valid true/false
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      * Check composite spec validity.
220      *
221      * @return valid true/false
222      */
223     private boolean checkComposite() {
224         final Iterator<GordianSignatureSpec> myIterator = signatureSpecIterator();
225         while (myIterator.hasNext()) {
226             /* Check that each spec is valid */
227             final GordianSignatureSpec mySpec = myIterator.next();
228             if (mySpec == null || !mySpec.isValid()) {
229                 return false;
230             }
231         }
232         return true;
233     }
234 
235     /**
236      * Check picnic spec validity.
237      *
238      * @return valid true/false
239      */
240     private boolean checkPICNICDigest() {
241         /* Check that signature length is 512 */
242         final GordianDigestSpec myDigest = getDigestSpec();
243         if (!GordianLength.LEN_512.equals(myDigest.getDigestLength())) {
244             return false;
245         }
246 
247         /* Switch on DigestType */
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      * Check sm2 spec validity.
260      *
261      * @return valid true/false
262      */
263     private boolean checkSM2Digest() {
264         /* Switch on DigestType */
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         /* If we have not yet loaded the name */
280         if (theName == null) {
281             /* Load the name */
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         /* return the name */
301         return theName;
302     }
303 
304     @Override
305     public boolean equals(final Object pThat) {
306         /* Handle the trivial cases */
307         if (this == pThat) {
308             return true;
309         }
310         if (pThat == null) {
311             return false;
312         }
313 
314         /* Check KeyPairType, signatureType and signatureSpec */
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 }