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.impl.jca;
18  
19  import io.github.tonywasher.joceanus.gordianknot.api.agree.GordianAgreementKDF;
20  import io.github.tonywasher.joceanus.gordianknot.api.agree.GordianAgreementSpec;
21  import io.github.tonywasher.joceanus.gordianknot.api.agree.GordianAgreementType;
22  import io.github.tonywasher.joceanus.gordianknot.api.base.GordianException;
23  import io.github.tonywasher.joceanus.gordianknot.api.base.GordianLength;
24  import io.github.tonywasher.joceanus.gordianknot.api.cipher.GordianSymKeyType;
25  import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianKeyPair;
26  import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianKeyPairSpec;
27  import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianKeyPairType;
28  import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianNTRUPrimeSpec;
29  import io.github.tonywasher.joceanus.gordianknot.impl.core.agree.GordianCoreAgreementEngine;
30  import io.github.tonywasher.joceanus.gordianknot.impl.core.agree.GordianCoreAgreementFactory;
31  import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianBaseData;
32  import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianCryptoException;
33  import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianDataException;
34  import io.github.tonywasher.joceanus.gordianknot.impl.core.keypair.GordianPrivateKey;
35  import io.github.tonywasher.joceanus.gordianknot.impl.core.keypair.GordianPublicKey;
36  import io.github.tonywasher.joceanus.gordianknot.impl.jca.JcaKeyPair.JcaPrivateKey;
37  import io.github.tonywasher.joceanus.gordianknot.impl.jca.JcaKeyPair.JcaPublicKey;
38  import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
39  import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
40  import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
41  import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
42  import org.bouncycastle.jcajce.SecretKeyWithEncapsulation;
43  import org.bouncycastle.jcajce.spec.DHUParameterSpec;
44  import org.bouncycastle.jcajce.spec.KEMExtractSpec;
45  import org.bouncycastle.jcajce.spec.KEMGenerateSpec;
46  import org.bouncycastle.jcajce.spec.MQVParameterSpec;
47  import org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec;
48  
49  import javax.crypto.KeyAgreement;
50  import javax.crypto.KeyGenerator;
51  import java.security.InvalidAlgorithmParameterException;
52  import java.security.InvalidKeyException;
53  import java.security.NoSuchAlgorithmException;
54  import java.security.Provider;
55  import java.security.PublicKey;
56  
57  /**
58   * Agreement classes.
59   */
60  public final class JcaAgreement {
61      /**
62       * Failed agreement message.
63       */
64      private static final String ERR_AGREEMENT = "Failed Agreement";
65  
66      /**
67       * Private constructor.
68       */
69      private JcaAgreement() {
70      }
71  
72      /**
73       * Jca PostQuantum Agreement.
74       */
75      public static class JcaPostQuantumEngine
76              extends JcaAgreementBase {
77          /**
78           * Key Agreement.
79           */
80          private final KeyGenerator theGenerator;
81  
82          /**
83           * Constructor.
84           *
85           * @param pFactory   the security factory
86           * @param pSpec      the agreementSpec
87           * @param pGenerator the generator
88           */
89          JcaPostQuantumEngine(final GordianCoreAgreementFactory pFactory,
90                               final GordianAgreementSpec pSpec,
91                               final KeyGenerator pGenerator) throws GordianException {
92              /* Initialize underlying class */
93              super(pFactory, pSpec);
94  
95              /* Store the generator */
96              theGenerator = pGenerator;
97          }
98  
99          @Override
100         public void buildClientHello() throws GordianException {
101             /* Protect against exceptions */
102             try {
103                 /* Create encapsulation */
104                 final JcaPublicKey myPublic = (JcaPublicKey) getPublicKey(getServerKeyPair());
105                 final KEMGenerateSpec mySpec = new KEMGenerateSpec.Builder(myPublic.getPublicKey(),
106                         GordianSymKeyType.AES.toString(), GordianLength.LEN_256.getLength()).withKdfAlgorithm(derivationAlgorithmId()).build();
107                 theGenerator.init(mySpec, getRandom());
108                 final SecretKeyWithEncapsulation mySecret = (SecretKeyWithEncapsulation) theGenerator.generateKey();
109 
110                 /* Store the encapsulation */
111                 setEncapsulated(mySecret.getEncapsulation());
112 
113                 /* Store secret */
114                 storeSecret(mySecret.getEncoded());
115 
116             } catch (InvalidAlgorithmParameterException e) {
117                 throw new GordianCryptoException(ERR_AGREEMENT, e);
118             }
119         }
120 
121         @Override
122         public void processClientHello() throws GordianException {
123             /* Protect against exceptions */
124             try {
125                 /* Create extractor */
126                 final JcaPrivateKey myPrivate = (JcaPrivateKey) getPrivateKey(getServerKeyPair());
127                 final KEMExtractSpec mySpec = new KEMExtractSpec.Builder(myPrivate.getPrivateKey(), getEncapsulated(),
128                         GordianSymKeyType.AES.toString(), GordianLength.LEN_256.getLength()).withKdfAlgorithm(derivationAlgorithmId()).build();
129                 theGenerator.init(mySpec);
130 
131                 /* Store secret */
132                 final SecretKeyWithEncapsulation mySecret = (SecretKeyWithEncapsulation) theGenerator.generateKey();
133                 storeSecret(mySecret.getEncoded());
134 
135             } catch (InvalidAlgorithmParameterException e) {
136                 throw new GordianCryptoException(ERR_AGREEMENT, e);
137             }
138         }
139     }
140 
141     /**
142      * Jca NewHope Agreement.
143      */
144     public static class JcaNewHopeEngine
145             extends JcaAgreementBase {
146         /**
147          * Key Agreement.
148          */
149         private final KeyAgreement theAgreement;
150 
151         /**
152          * Constructor.
153          *
154          * @param pFactory   the security factory
155          * @param pSpec      the agreementSpec
156          * @param pAgreement the agreement
157          */
158         JcaNewHopeEngine(final GordianCoreAgreementFactory pFactory,
159                          final GordianAgreementSpec pSpec,
160                          final KeyAgreement pAgreement) throws GordianException {
161             /* Initialize underlying class */
162             super(pFactory, pSpec);
163 
164             /* Store the agreement */
165             theAgreement = pAgreement;
166         }
167 
168         @Override
169         public void buildClientHello() throws GordianException {
170             /* Protect against exceptions */
171             try {
172                 /* Derive the secret */
173                 theAgreement.init(null, getRandom());
174                 final JcaPublicKey myTarget = (JcaPublicKey) getPublicKey(getServerKeyPair());
175                 final PublicKey myKey = (PublicKey) theAgreement.doPhase(myTarget.getPublicKey(), true);
176 
177                 /* Store the ephemeral */
178                 final GordianKeyPairSpec mySpec = getSpec().getKeyPairSpec();
179                 final JcaPublicKey myPublic = new JcaPublicKey(mySpec, myKey);
180                 final JcaKeyPair myEphemeral = new JcaKeyPair(myPublic);
181                 setClientEphemeralAsEncapsulated(myEphemeral);
182 
183                 /* Store secret */
184                 storeSecret(theAgreement.generateSecret());
185 
186             } catch (InvalidKeyException e) {
187                 throw new GordianCryptoException(ERR_AGREEMENT, e);
188             }
189         }
190 
191         @Override
192         public void processClientHello() throws GordianException {
193             /* Protect against exceptions */
194             try {
195                 /* Derive the secret */
196                 final JcaPrivateKey myPrivate = (JcaPrivateKey) getPrivateKey(getServerKeyPair());
197                 final JcaPublicKey myPublic = (JcaPublicKey) getPublicKey(getClientEphemeral());
198                 theAgreement.init(myPrivate.getPrivateKey());
199                 theAgreement.doPhase(myPublic.getPublicKey(), true);
200 
201                 /* Store secret */
202                 storeSecret(theAgreement.generateSecret());
203 
204             } catch (InvalidKeyException e) {
205                 throw new GordianCryptoException(ERR_AGREEMENT, e);
206             }
207         }
208     }
209 
210     /**
211      * Jca Anonymous Agreement.
212      */
213     public static class JcaAnonEngine
214             extends JcaAgreementBase {
215         /**
216          * Key Agreement.
217          */
218         private KeyAgreement theAgreement;
219 
220         /**
221          * Constructor.
222          *
223          * @param pFactory   the security factory
224          * @param pSpec      the agreementSpec
225          * @param pAgreement the agreement
226          */
227         JcaAnonEngine(final GordianCoreAgreementFactory pFactory,
228                       final GordianAgreementSpec pSpec,
229                       final KeyAgreement pAgreement) throws GordianException {
230             /* Initialize underlying class */
231             super(pFactory, pSpec);
232 
233             /* Store the agreement */
234             theAgreement = pAgreement;
235         }
236 
237         @Override
238         public void buildClientHello() throws GordianException {
239             /* Protect against exceptions */
240             try {
241                 /* Access keys */
242                 final JcaPublicKey myPublic = (JcaPublicKey) getPublicKey(getServerKeyPair());
243                 final JcaPrivateKey myPrivate = (JcaPrivateKey) getPrivateKey(getClientEphemeral());
244                 theAgreement = adjustAgreement(theAgreement, getServerKeyPair());
245 
246                 /* Derive the secret */
247                 initAgreement(theAgreement, myPrivate);
248                 theAgreement.doPhase(myPublic.getPublicKey(), true);
249                 storeSecret(theAgreement.generateSecret());
250 
251             } catch (InvalidKeyException e) {
252                 throw new GordianCryptoException(ERR_AGREEMENT, e);
253             }
254         }
255 
256         @Override
257         public void processClientHello() throws GordianException {
258             /* Protect against exceptions */
259             try {
260                 /* Access keys */
261                 final JcaPublicKey myPublic = (JcaPublicKey) getPublicKey(getClientEphemeral());
262                 final JcaPrivateKey myPrivate = (JcaPrivateKey) getPrivateKey(getServerKeyPair());
263                 theAgreement = adjustAgreement(theAgreement, getServerKeyPair());
264 
265                 /* Derive the secret */
266                 initAgreement(theAgreement, myPrivate);
267                 theAgreement.doPhase(myPublic.getPublicKey(), true);
268                 storeSecret(theAgreement.generateSecret());
269 
270             } catch (InvalidKeyException e) {
271                 throw new GordianCryptoException(ERR_AGREEMENT, e);
272             }
273         }
274     }
275 
276     /**
277      * Jca Basic Agreement.
278      */
279     public static class JcaBasicEngine
280             extends JcaAgreementBase {
281         /**
282          * Key Agreement.
283          */
284         private KeyAgreement theAgreement;
285 
286         /**
287          * Constructor.
288          *
289          * @param pFactory   the security factory
290          * @param pSpec      the agreementSpec
291          * @param pAgreement the agreement
292          */
293         JcaBasicEngine(final GordianCoreAgreementFactory pFactory,
294                        final GordianAgreementSpec pSpec,
295                        final KeyAgreement pAgreement) throws GordianException {
296             /* Initialize underlying class */
297             super(pFactory, pSpec);
298 
299             /* Store the agreement */
300             theAgreement = pAgreement;
301         }
302 
303         @Override
304         public void processClientHello() throws GordianException {
305             /* Protect against exceptions */
306             try {
307                 /* Access keys */
308                 final JcaPublicKey myPublic = (JcaPublicKey) getPublicKey(getClientKeyPair());
309                 final JcaPrivateKey myPrivate = (JcaPrivateKey) getPrivateKey(getServerKeyPair());
310                 theAgreement = adjustAgreement(theAgreement, getServerKeyPair());
311 
312                 /* Derive the secret */
313                 initAgreement(theAgreement, myPrivate);
314                 theAgreement.doPhase(myPublic.getPublicKey(), true);
315                 storeSecret(theAgreement.generateSecret());
316 
317             } catch (InvalidKeyException e) {
318                 throw new GordianCryptoException(ERR_AGREEMENT, e);
319             }
320         }
321 
322         @Override
323         public void processServerHello() throws GordianException {
324             /* Protect against exceptions */
325             try {
326                 /* Access keys */
327                 final JcaPublicKey myPublic = (JcaPublicKey) getPublicKey(getServerKeyPair());
328                 final JcaPrivateKey myPrivate = (JcaPrivateKey) getPrivateKey(getClientKeyPair());
329                 theAgreement = adjustAgreement(theAgreement, getServerKeyPair());
330 
331                 /* Derive the secret */
332                 initAgreement(theAgreement, myPrivate);
333                 theAgreement.doPhase(myPublic.getPublicKey(), true);
334                 storeSecret(theAgreement.generateSecret());
335 
336             } catch (InvalidKeyException e) {
337                 throw new GordianCryptoException(ERR_AGREEMENT, e);
338             }
339         }
340     }
341 
342     /**
343      * Jca Unified Agreement.
344      */
345     public static class JcaUnifiedEngine
346             extends JcaAgreementBase {
347         /**
348          * Key Agreement.
349          */
350         private KeyAgreement theAgreement;
351 
352         /**
353          * Constructor.
354          *
355          * @param pFactory   the security factory
356          * @param pSpec      the agreementSpec
357          * @param pAgreement the agreement
358          */
359         JcaUnifiedEngine(final GordianCoreAgreementFactory pFactory,
360                          final GordianAgreementSpec pSpec,
361                          final KeyAgreement pAgreement) throws GordianException {
362             /* Initialize underlying class */
363             super(pFactory, pSpec);
364 
365             /* Store the agreement */
366             theAgreement = pAgreement;
367         }
368 
369         @Override
370         public void processClientHello() throws GordianException {
371             /* Protect against exceptions */
372             try {
373                 /* Access keys */
374                 final JcaPublicKey myClientPublic = (JcaPublicKey) getPublicKey(getClientKeyPair());
375                 final JcaPublicKey myClientEphPublic = (JcaPublicKey) getPublicKey(getClientEphemeral());
376                 final JcaPrivateKey myPrivate = (JcaPrivateKey) getPrivateKey(getServerKeyPair());
377                 final JcaPublicKey myEphPublic = (JcaPublicKey) getPublicKey(getServerEphemeral());
378                 final JcaPrivateKey myEphPrivate = (JcaPrivateKey) getPrivateKey(getServerEphemeral());
379                 theAgreement = adjustAgreement(theAgreement, getServerKeyPair());
380 
381                 /* Derive the secret */
382                 final DHUParameterSpec myParams = new DHUParameterSpec(myEphPublic.getPublicKey(),
383                         myEphPrivate.getPrivateKey(), myClientEphPublic.getPublicKey(), getAdditional());
384                 theAgreement.init(myPrivate.getPrivateKey(), myParams, getRandom());
385                 theAgreement.doPhase(myClientPublic.getPublicKey(), true);
386                 storeSecret(theAgreement.generateSecret());
387 
388             } catch (InvalidKeyException
389                      | InvalidAlgorithmParameterException e) {
390                 throw new GordianCryptoException(ERR_AGREEMENT, e);
391             }
392         }
393 
394         @Override
395         public void processServerHello() throws GordianException {
396             /* Protect against exceptions */
397             try {
398                 /* Access keys */
399                 final JcaPublicKey myServerPublic = (JcaPublicKey) getPublicKey(getServerKeyPair());
400                 final JcaPublicKey myServerEphPublic = (JcaPublicKey) getPublicKey(getServerEphemeral());
401                 final JcaPrivateKey myPrivate = (JcaPrivateKey) getPrivateKey(getClientKeyPair());
402                 final JcaPublicKey myEphPublic = (JcaPublicKey) getPublicKey(getClientEphemeral());
403                 final JcaPrivateKey myEphPrivate = (JcaPrivateKey) getPrivateKey(getClientEphemeral());
404                 theAgreement = adjustAgreement(theAgreement, getServerKeyPair());
405 
406                 /* Derive the secret */
407                 final DHUParameterSpec myParams = new DHUParameterSpec(myEphPublic.getPublicKey(),
408                         myEphPrivate.getPrivateKey(), myServerEphPublic.getPublicKey(), getAdditional());
409                 theAgreement.init(myPrivate.getPrivateKey(), myParams);
410                 theAgreement.doPhase(myServerPublic.getPublicKey(), true);
411                 storeSecret(theAgreement.generateSecret());
412 
413             } catch (InvalidKeyException
414                      | InvalidAlgorithmParameterException e) {
415                 throw new GordianCryptoException(ERR_AGREEMENT, e);
416             }
417         }
418     }
419 
420     /**
421      * Jca MQV Agreement.
422      */
423     public static class JcaMQVEngine
424             extends JcaAgreementBase {
425         /**
426          * Key Agreement.
427          */
428         private final KeyAgreement theAgreement;
429 
430         /**
431          * Constructor.
432          *
433          * @param pFactory   the security factory
434          * @param pSpec      the agreementSpec
435          * @param pAgreement the agreement
436          */
437         JcaMQVEngine(final GordianCoreAgreementFactory pFactory,
438                      final GordianAgreementSpec pSpec,
439                      final KeyAgreement pAgreement) throws GordianException {
440             /* Initialize underlying class */
441             super(pFactory, pSpec);
442 
443             /* Store the agreement */
444             theAgreement = pAgreement;
445         }
446 
447         @Override
448         public void processClientHello() throws GordianException {
449             /* Protect against exceptions */
450             try {
451                 /* Access keys */
452                 final JcaPublicKey myClientPublic = (JcaPublicKey) getPublicKey(getClientKeyPair());
453                 final JcaPublicKey myClientEphPublic = (JcaPublicKey) getPublicKey(getClientEphemeral());
454                 final JcaPrivateKey myPrivate = (JcaPrivateKey) getPrivateKey(getServerKeyPair());
455                 final JcaPublicKey myEphPublic = (JcaPublicKey) getPublicKey(getServerEphemeral());
456                 final JcaPrivateKey myEphPrivate = (JcaPrivateKey) getPrivateKey(getServerEphemeral());
457 
458                 /* Derive the secret */
459                 final MQVParameterSpec myParams = new MQVParameterSpec(myEphPublic.getPublicKey(),
460                         myEphPrivate.getPrivateKey(), myClientEphPublic.getPublicKey(), getAdditional());
461                 theAgreement.init(myPrivate.getPrivateKey(), myParams, getRandom());
462                 theAgreement.doPhase(myClientPublic.getPublicKey(), true);
463                 storeSecret(theAgreement.generateSecret());
464 
465             } catch (InvalidKeyException
466                      | InvalidAlgorithmParameterException e) {
467                 throw new GordianCryptoException(ERR_AGREEMENT, e);
468             }
469         }
470 
471         @Override
472         public void processServerHello() throws GordianException {
473             /* Protect against exceptions */
474             try {
475                 /* Access keys */
476                 final JcaPublicKey myServerPublic = (JcaPublicKey) getPublicKey(getServerKeyPair());
477                 final JcaPublicKey myServerEphPublic = (JcaPublicKey) getPublicKey(getServerEphemeral());
478                 final JcaPrivateKey myPrivate = (JcaPrivateKey) getPrivateKey(getClientKeyPair());
479                 final JcaPublicKey myEphPublic = (JcaPublicKey) getPublicKey(getClientEphemeral());
480                 final JcaPrivateKey myEphPrivate = (JcaPrivateKey) getPrivateKey(getClientEphemeral());
481 
482                 /* Derive the secret */
483                 final MQVParameterSpec myParams = new MQVParameterSpec(myEphPublic.getPublicKey(),
484                         myEphPrivate.getPrivateKey(), myServerEphPublic.getPublicKey(), getAdditional());
485                 theAgreement.init(myPrivate.getPrivateKey(), myParams);
486                 theAgreement.doPhase(myServerPublic.getPublicKey(), true);
487                 storeSecret(theAgreement.generateSecret());
488 
489             } catch (InvalidKeyException
490                      | InvalidAlgorithmParameterException e) {
491                 throw new GordianCryptoException(ERR_AGREEMENT, e);
492             }
493         }
494     }
495 
496     /**
497      * Base Agreement Engine class.
498      */
499     public abstract static class JcaAgreementBase
500             extends GordianCoreAgreementEngine {
501         /**
502          * Empty byteArray.
503          */
504         private static final byte[] EMPTY = new byte[0];
505 
506         /**
507          * Constructor.
508          *
509          * @param pFactory the security factory
510          * @param pSpec    the agreementSpec
511          * @throws GordianException on error
512          */
513         JcaAgreementBase(final GordianCoreAgreementFactory pFactory,
514                          final GordianAgreementSpec pSpec) throws GordianException {
515             /* Invoke underlying constructor */
516             super(pFactory, pSpec);
517         }
518 
519         @Override
520         protected GordianPublicKey getPublicKey(final GordianKeyPair pKeyPair) throws GordianException {
521             /* Validate the keyPair */
522             if (!(pKeyPair instanceof JcaKeyPair)) {
523                 /* Reject keyPair */
524                 throw new GordianDataException("Invalid KeyPair");
525             }
526 
527             /* Access private key */
528             return super.getPublicKey(pKeyPair);
529         }
530 
531         @Override
532         protected GordianPrivateKey getPrivateKey(final GordianKeyPair pKeyPair) throws GordianException {
533             /* Validate the keyPair */
534             if (!(pKeyPair instanceof JcaKeyPair)) {
535                 /* Reject keyPair */
536                 throw new GordianDataException("Invalid KeyPair");
537             }
538 
539             /* Access private key */
540             return super.getPrivateKey(pKeyPair);
541         }
542 
543         /**
544          * Initialise agreement.
545          *
546          * @param pAgreement the agreement
547          * @param pPrivate   the private key
548          */
549         void initAgreement(final KeyAgreement pAgreement,
550                            final JcaPrivateKey pPrivate) throws GordianException {
551             /* Protect against exceptions */
552             try {
553                 if (getSpec().getKDFType() == GordianAgreementKDF.NONE) {
554                     pAgreement.init(pPrivate.getPrivateKey(), getRandom());
555                 } else {
556                     pAgreement.init(pPrivate.getPrivateKey(), new UserKeyingMaterialSpec(getAdditional()), getRandom());
557                 }
558             } catch (InvalidKeyException
559                      | InvalidAlgorithmParameterException e) {
560                 throw new GordianCryptoException(ERR_AGREEMENT, e);
561             }
562         }
563 
564         /**
565          * Obtain the additionalInfo.
566          *
567          * @return the additionalData
568          */
569         byte[] getAdditional() {
570             final byte[] myAdditional = this.getBuilder().getState().getAdditionalData();
571             return myAdditional == null ? EMPTY : myAdditional;
572         }
573 
574         /**
575          * adjust agreement if necessary.
576          *
577          * @param pCurrent the current agreement
578          * @param pKeyPair the keyPair
579          * @return the adjusted agreement
580          * @throws GordianException on error
581          */
582         KeyAgreement adjustAgreement(final KeyAgreement pCurrent,
583                                      final GordianKeyPair pKeyPair) throws GordianException {
584             /* If we need to change agreement based on keySpec */
585             if (getSpec().getKeyPairSpec().getKeyPairType().equals(GordianKeyPairType.XDH)) {
586                 final String myBase = pKeyPair.getKeyPairSpec().toString();
587                 final String myXtra = GordianAgreementType.UNIFIED.equals(getSpec().getAgreementType())
588                         ? "U" : "";
589                 final String myName = getFullAgreementName(myBase + myXtra, getSpec());
590                 return getJavaKeyAgreement(myName, false);
591             }
592 
593             /* Just return the current agreement */
594             return pCurrent;
595         }
596 
597         /**
598          * Obtain the required derivation id.
599          *
600          * @return the derivation id
601          */
602         AlgorithmIdentifier derivationAlgorithmId() {
603             final GordianAgreementSpec mySpec = getSpec();
604             switch (mySpec.getKDFType()) {
605                 case SHA256KDF:
606                     return new AlgorithmIdentifier(X9ObjectIdentifiers.id_kdf_kdf2, new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256));
607                 case SHA512KDF:
608                     return new AlgorithmIdentifier(X9ObjectIdentifiers.id_kdf_kdf2, new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512));
609                 case SHA256CKDF:
610                     return new AlgorithmIdentifier(X9ObjectIdentifiers.id_kdf_kdf3, new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256));
611                 case SHA512CKDF:
612                     return new AlgorithmIdentifier(X9ObjectIdentifiers.id_kdf_kdf3, new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512));
613                 case SHA256HKDF:
614                     return new AlgorithmIdentifier(PKCSObjectIdentifiers.id_alg_hkdf_with_sha256, null);
615                 case SHA512HKDF:
616                     return new AlgorithmIdentifier(PKCSObjectIdentifiers.id_alg_hkdf_with_sha512, null);
617                 case KMAC128:
618                     return new AlgorithmIdentifier(NISTObjectIdentifiers.id_Kmac128, null);
619                 case KMAC256:
620                     return new AlgorithmIdentifier(NISTObjectIdentifiers.id_Kmac256, null);
621                 case SHAKE256:
622                     return new AlgorithmIdentifier(NISTObjectIdentifiers.id_shake256, null);
623                 case NONE:
624                 default:
625                     return null;
626             }
627         }
628     }
629 
630     /**
631      * Create the BouncyCastle KeyGenerator via JCA.
632      *
633      * @param pSpec the KeySpec
634      * @return the KeyFactory
635      * @throws GordianException on error
636      */
637     static KeyGenerator getJavaKeyGenerator(final GordianKeyPairSpec pSpec) throws GordianException {
638         /* Protect against exceptions */
639         try {
640             /* Determine the algorithm name */
641             String myName = pSpec.getKeyPairType().toString();
642             switch (pSpec.getKeyPairType()) {
643                 case NTRUPRIME:
644                     final GordianNTRUPrimeSpec myNTRUSpec = pSpec.getNTRUPrimeKeySpec();
645                     myName = myNTRUSpec.getType() + "PRIME";
646                     break;
647                 case MLKEM:
648                     myName = "ML-KEM";
649                     break;
650                 default:
651                     break;
652             }
653 
654             /* Determine source of keyGenerator */
655             final Provider myProvider = pSpec.getKeyPairType().isStandardJca() ? JcaProvider.BCPROV : JcaProvider.BCPQPROV;
656 
657             /* Return a KeyAgreement for the algorithm */
658             return KeyGenerator.getInstance(myName, myProvider);
659 
660             /* Catch exceptions */
661         } catch (NoSuchAlgorithmException e) {
662             /* Throw the exception */
663             throw new GordianCryptoException("Failed to create KeyGenerator", e);
664         }
665     }
666 
667     /**
668      * Create the BouncyCastle KeyFactory via JCA.
669      *
670      * @param pAlgorithm  the Algorithm
671      * @param postQuantum is this a postQuantum algorithm?
672      * @return the KeyFactory
673      * @throws GordianException on error
674      */
675     static KeyAgreement getJavaKeyAgreement(final String pAlgorithm,
676                                             final boolean postQuantum) throws GordianException {
677         /* Protect against exceptions */
678         try {
679             /* Return a KeyAgreement for the algorithm */
680             return KeyAgreement.getInstance(pAlgorithm, postQuantum
681                     ? JcaProvider.BCPQPROV
682                     : JcaProvider.BCPROV);
683 
684             /* Catch exceptions */
685         } catch (NoSuchAlgorithmException e) {
686             /* Throw the exception */
687             throw new GordianCryptoException("Failed to create KeyAgreement", e);
688         }
689     }
690 
691     /**
692      * Obtain the agreement name.
693      *
694      * @param pBase          the base agreement
695      * @param pAgreementSpec the agreementSpec
696      * @return the full agreement name
697      * @throws GordianException on error
698      */
699     static String getFullAgreementName(final String pBase,
700                                        final GordianAgreementSpec pAgreementSpec) throws GordianException {
701         switch (pAgreementSpec.getKDFType()) {
702             case NONE:
703                 return pBase;
704             case SHA256KDF:
705                 return pBase + "withSHA256KDF";
706             case SHA512KDF:
707                 return pBase + "withSHA512KDF";
708             case SHA256CKDF:
709                 return pBase + "withSHA256CKDF";
710             case SHA512CKDF:
711                 return pBase + "withSHA512CKDF";
712             case SHA256HKDF:
713                 return pBase + "withSHA256HKDF";
714             case SHA512HKDF:
715                 return pBase + "withSHA512HKDF";
716             default:
717                 throw new GordianDataException(GordianBaseData.getInvalidText(pAgreementSpec));
718         }
719     }
720 }