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.base.GordianException;
20  import io.github.tonywasher.joceanus.gordianknot.api.base.GordianKeySpec;
21  import io.github.tonywasher.joceanus.gordianknot.api.cipher.GordianAEADCipher;
22  import io.github.tonywasher.joceanus.gordianknot.api.cipher.GordianCipherParameters;
23  import io.github.tonywasher.joceanus.gordianknot.api.cipher.GordianCipherSpec;
24  import io.github.tonywasher.joceanus.gordianknot.api.cipher.GordianStreamAEADCipher;
25  import io.github.tonywasher.joceanus.gordianknot.api.cipher.GordianStreamCipherSpec;
26  import io.github.tonywasher.joceanus.gordianknot.api.cipher.GordianStreamKeySpec;
27  import io.github.tonywasher.joceanus.gordianknot.api.cipher.GordianSymAEADCipher;
28  import io.github.tonywasher.joceanus.gordianknot.api.cipher.GordianSymCipherSpec;
29  import io.github.tonywasher.joceanus.gordianknot.api.cipher.GordianSymKeySpec;
30  import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianBaseFactory;
31  import io.github.tonywasher.joceanus.gordianknot.impl.core.cipher.GordianCoreCipher;
32  import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianCryptoException;
33  import org.bouncycastle.jcajce.spec.AEADParameterSpec;
34  
35  import javax.crypto.BadPaddingException;
36  import javax.crypto.Cipher;
37  import javax.crypto.IllegalBlockSizeException;
38  import javax.crypto.SecretKey;
39  import javax.crypto.ShortBufferException;
40  import javax.crypto.spec.IvParameterSpec;
41  import java.security.InvalidAlgorithmParameterException;
42  import java.security.InvalidKeyException;
43  import java.security.spec.AlgorithmParameterSpec;
44  import java.util.Arrays;
45  
46  /**
47   * Cipher for JCA BouncyCastle AEAD Ciphers.
48   *
49   * @param <T> the key Type
50   */
51  public class JcaAEADCipher<T extends GordianKeySpec>
52          extends GordianCoreCipher<T>
53          implements GordianAEADCipher {
54      /**
55       * Cipher.
56       */
57      private final Cipher theCipher;
58  
59      /**
60       * is the cipher encrypting?
61       */
62      private boolean isEncrypting;
63  
64      /**
65       * Constructor.
66       *
67       * @param pFactory    the Security Factory
68       * @param pCipherSpec the cipherSpec
69       * @param pCipher     the cipher
70       */
71      JcaAEADCipher(final GordianBaseFactory pFactory,
72                    final GordianCipherSpec<T> pCipherSpec,
73                    final Cipher pCipher) {
74          super(pFactory, pCipherSpec);
75          theCipher = pCipher;
76      }
77  
78      @Override
79      public JcaKey<T> getKey() {
80          return (JcaKey<T>) super.getKey();
81      }
82  
83  
84      @Override
85      public void init(final boolean pEncrypt,
86                       final GordianCipherParameters pParams) throws GordianException {
87          /* Process the parameters and access the key */
88          processParameters(pParams);
89          final JcaKey<T> myJcaKey = JcaKey.accessKey(getKey());
90  
91          /* Access details */
92          final int myMode = pEncrypt
93                  ? Cipher.ENCRYPT_MODE
94                  : Cipher.DECRYPT_MODE;
95          final SecretKey myKey = myJcaKey.getKey();
96          final byte[] myAEAD = getInitialAEAD();
97  
98          /* Protect against exceptions */
99          try {
100             /* Initialise as required */
101             final AlgorithmParameterSpec myParms = myAEAD == null
102                     ? new IvParameterSpec(getInitVector())
103                     : new AEADParameterSpec(getInitVector(), getAEADMacSize(), myAEAD);
104             theCipher.init(myMode, myKey, myParms);
105             isEncrypting = pEncrypt;
106         } catch (InvalidKeyException
107                  | InvalidAlgorithmParameterException e) {
108             throw new GordianCryptoException("Failed to initialise cipher", e);
109         }
110     }
111 
112     @Override
113     public int getOutputLength(final int pLength) {
114         return theCipher.getOutputSize(pLength);
115     }
116 
117     @Override
118     public int doUpdate(final byte[] pBytes,
119                         final int pOffset,
120                         final int pLength,
121                         final byte[] pOutput,
122                         final int pOutOffset) throws GordianException {
123         /* Protect against exceptions */
124         try {
125             /* Process the bytes */
126             return theCipher.update(pBytes, pOffset, pLength, pOutput, pOutOffset);
127 
128             /* Handle exceptions */
129         } catch (ShortBufferException e) {
130             throw new GordianCryptoException("Failed to process bytes", e);
131         }
132     }
133 
134     @Override
135     public void updateAAD(final byte[] pBytes,
136                           final int pOffset,
137                           final int pLength) throws GordianException {
138         /* Protect against exceptions */
139         try {
140             /* Process the bytes */
141             theCipher.updateAAD(pBytes, pOffset, pLength);
142 
143             /* Handle exceptions */
144         } catch (IllegalStateException e) {
145             throw new GordianCryptoException("Failed to process AAD bytes", e);
146         }
147     }
148 
149     @Override
150     public int doFinish(final byte[] pOutput,
151                         final int pOutOffset) throws GordianException {
152         /* Protect against exceptions */
153         try {
154             /* Finish the operation */
155             return theCipher.doFinal(pOutput, pOutOffset);
156 
157             /* Handle exceptions */
158         } catch (ShortBufferException
159                  | IllegalBlockSizeException
160                  | BadPaddingException e) {
161             throw new GordianCryptoException("Failed to finish operation", e);
162         }
163     }
164 
165     @Override
166     public int getBlockSize() {
167         return 0;
168     }
169 
170     /**
171      * JcaSymAADCipher.
172      */
173     public static class JcaSymAEADCipher
174             extends JcaAEADCipher<GordianSymKeySpec>
175             implements GordianSymAEADCipher {
176         /**
177          * Constructor.
178          *
179          * @param pFactory    the Security Factory
180          * @param pCipherSpec the cipherSpec
181          * @param pCipher     the cipher
182          */
183         JcaSymAEADCipher(final GordianBaseFactory pFactory,
184                          final GordianSymCipherSpec pCipherSpec,
185                          final Cipher pCipher) {
186             super(pFactory, pCipherSpec, pCipher);
187         }
188     }
189 
190     /**
191      * JcaStreamAEADCipher.
192      */
193     public static class JcaStreamAEADCipher
194             extends JcaAEADCipher<GordianStreamKeySpec>
195             implements GordianStreamAEADCipher {
196         /**
197          * Constructor.
198          *
199          * @param pFactory    the Security Factory
200          * @param pCipherSpec the cipherSpec
201          * @param pCipher     the cipher
202          */
203         JcaStreamAEADCipher(final GordianBaseFactory pFactory,
204                             final GordianStreamCipherSpec pCipherSpec,
205                             final Cipher pCipher) {
206             super(pFactory, pCipherSpec, pCipher);
207         }
208     }
209 
210     @Override
211     public boolean equals(final Object pThat) {
212         /* Handle trivial cases */
213         if (this == pThat) {
214             return true;
215         }
216         if (pThat == null) {
217             return false;
218         }
219 
220         /* Make sure that the classes are the same */
221         if (!(pThat instanceof JcaAEADCipher)) {
222             return false;
223         }
224         final JcaAEADCipher<?> myThat = (JcaAEADCipher<?>) pThat;
225 
226         /* Check that the fields are equal */
227         return isEncrypting == myThat.isEncrypting
228                 && Arrays.equals(getInitialAEAD(), myThat.getInitialAEAD())
229                 && super.equals(myThat);
230     }
231 
232     @Override
233     public int hashCode() {
234         return super.hashCode()
235                 + Arrays.hashCode(getInitialAEAD())
236                 + (isEncrypting ? 1 : 0);
237     }
238 }