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.encrypt;
18  
19  import io.github.tonywasher.joceanus.gordianknot.api.digest.GordianDigestSpec;
20  import io.github.tonywasher.joceanus.gordianknot.api.digest.GordianDigestType;
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   * Asymmetric Encryption Specification.
29   */
30  public final class GordianEncryptorSpec {
31      /**
32       * The EC-ElGamal name.
33       */
34      private static final String ECELGAMAL = "ElGamal";
35  
36      /**
37       * The Separator.
38       */
39      static final String SEP = "-";
40  
41      /**
42       * KeyPairType.
43       */
44      private final GordianKeyPairType theKeyPairType;
45  
46      /**
47       * EncryptorType.
48       */
49      private final Object theEncryptorType;
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 pEncryptorType the encryptor type
66       */
67      public GordianEncryptorSpec(final GordianKeyPairType pKeyPairType,
68                                  final Object pEncryptorType) {
69          theKeyPairType = pKeyPairType;
70          theEncryptorType = pEncryptorType;
71          isValid = checkValidity();
72      }
73  
74      /**
75       * Obtain the keyPairType.
76       *
77       * @return the keyPairType.
78       */
79      public GordianKeyPairType getKeyPairType() {
80          return theKeyPairType;
81      }
82  
83      /**
84       * Obtain the encryptorType.
85       *
86       * @return the encryptorType.
87       */
88      public Object getEncryptorType() {
89          return theEncryptorType;
90      }
91  
92      /**
93       * Obtain the digestSpec.
94       *
95       * @return the digestSpec.
96       */
97      public GordianDigestSpec getDigestSpec() {
98          if (theEncryptorType instanceof GordianDigestSpec mySpec) {
99              return mySpec;
100         }
101         throw new IllegalArgumentException();
102     }
103 
104     /**
105      * Obtain the SM2 encryption Spec.
106      *
107      * @return the encryptionSpec.
108      */
109     public GordianSM2EncryptionSpec getSM2EncryptionSpec() {
110         if (theEncryptorType instanceof GordianSM2EncryptionSpec mySpec) {
111             return mySpec;
112         }
113         throw new IllegalArgumentException();
114     }
115 
116     /**
117      * Obtain the composite encryptorSpec iterator.
118      *
119      * @return the encryptorSpec iterator.
120      */
121     @SuppressWarnings("unchecked")
122     public Iterator<GordianEncryptorSpec> encryptorSpecIterator() {
123         if (theEncryptorType instanceof List) {
124             return ((List<GordianEncryptorSpec>) theEncryptorType).iterator();
125         }
126         throw new IllegalArgumentException();
127     }
128 
129     /**
130      * Is the encryptorSpec valid?
131      *
132      * @return true/false.
133      */
134     public boolean isValid() {
135         return isValid;
136     }
137 
138     /**
139      * Check spec validity.
140      *
141      * @return valid true/false
142      */
143     private boolean checkValidity() {
144         if (theKeyPairType == null) {
145             return false;
146         }
147         switch (theKeyPairType) {
148             case RSA:
149             case ELGAMAL:
150                 return theEncryptorType instanceof GordianDigestSpec s
151                         && s.isValid();
152             case SM2:
153                 return theEncryptorType == null
154                         || (theEncryptorType instanceof GordianSM2EncryptionSpec s
155                         && s.isValid());
156             case EC:
157             case GOST2012:
158                 return theEncryptorType == null;
159             case COMPOSITE:
160                 return theEncryptorType instanceof List && checkComposite();
161             default:
162                 return false;
163         }
164     }
165 
166     /**
167      * Is the Spec supported?
168      *
169      * @return true/false
170      */
171     public boolean isSupported() {
172         switch (theKeyPairType) {
173             case RSA:
174             case ELGAMAL:
175                 final GordianDigestSpec mySpec = getDigestSpec();
176                 return GordianDigestType.SHA2.equals(mySpec.getDigestType()) && !mySpec.isSha2Hybrid();
177             case EC:
178             case GOST2012:
179             case SM2:
180             case COMPOSITE:
181                 return true;
182             default:
183                 return false;
184         }
185     }
186 
187     /**
188      * Check composite spec validity.
189      *
190      * @return valid true/false
191      */
192     private boolean checkComposite() {
193         final Iterator<GordianEncryptorSpec> myIterator = encryptorSpecIterator();
194         while (myIterator.hasNext()) {
195             /* Check that each spec is valid */
196             final GordianEncryptorSpec mySpec = myIterator.next();
197             if (mySpec == null || !mySpec.isValid()) {
198                 return false;
199             }
200         }
201         return true;
202     }
203 
204     @Override
205     public String toString() {
206         /* If we have not yet loaded the name */
207         if (theName == null) {
208             /* If the encryptorSpec is valid */
209             if (isValid) {
210                 /* Load the name */
211                 theName = theKeyPairType.toString();
212                 switch (theKeyPairType) {
213                     case RSA:
214                     case ELGAMAL:
215                         theName += SEP + theEncryptorType;
216                         break;
217                     case EC:
218                     case GOST2012:
219                         theName += SEP + ECELGAMAL;
220                         break;
221                     case SM2:
222                         theName += SEP + (theEncryptorType == null ? ECELGAMAL : theEncryptorType);
223                         break;
224                     case COMPOSITE:
225                         final Iterator<GordianEncryptorSpec> myIterator = encryptorSpecIterator();
226                         final StringBuilder myBuilder = new StringBuilder(theName);
227                         while (myIterator.hasNext()) {
228                             myBuilder.append(SEP).append(myIterator.next().toString());
229                         }
230                         theName = myBuilder.toString();
231                         break;
232                     default:
233                         break;
234                 }
235             } else {
236                 /* Report invalid spec */
237                 theName = "InvalidEncryptorSpec: " + theKeyPairType + ":" + theEncryptorType;
238             }
239         }
240 
241         /* return the name */
242         return theName;
243     }
244 
245     @Override
246     public boolean equals(final Object pThat) {
247         /* Handle the trivial cases */
248         if (this == pThat) {
249             return true;
250         }
251         if (pThat == null) {
252             return false;
253         }
254 
255         /* Match fields */
256         return pThat instanceof GordianEncryptorSpec myThat
257                 && theKeyPairType == myThat.getKeyPairType()
258                 && Objects.equals(theEncryptorType, myThat.theEncryptorType);
259     }
260 
261     @Override
262     public int hashCode() {
263         return Objects.hash(theKeyPairType, theEncryptorType);
264     }
265 }