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