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;
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
48
49
50
51 public class JcaAEADCipher<T extends GordianKeySpec>
52 extends GordianCoreCipher<T>
53 implements GordianAEADCipher {
54
55
56
57 private final Cipher theCipher;
58
59
60
61
62 private boolean isEncrypting;
63
64
65
66
67
68
69
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
88 processParameters(pParams);
89 final JcaKey<T> myJcaKey = JcaKey.accessKey(getKey());
90
91
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
99 try {
100
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
124 try {
125
126 return theCipher.update(pBytes, pOffset, pLength, pOutput, pOutOffset);
127
128
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
139 try {
140
141 theCipher.updateAAD(pBytes, pOffset, pLength);
142
143
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
153 try {
154
155 return theCipher.doFinal(pOutput, pOutOffset);
156
157
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
172
173 public static class JcaSymAEADCipher
174 extends JcaAEADCipher<GordianSymKeySpec>
175 implements GordianSymAEADCipher {
176
177
178
179
180
181
182
183 JcaSymAEADCipher(final GordianBaseFactory pFactory,
184 final GordianSymCipherSpec pCipherSpec,
185 final Cipher pCipher) {
186 super(pFactory, pCipherSpec, pCipher);
187 }
188 }
189
190
191
192
193 public static class JcaStreamAEADCipher
194 extends JcaAEADCipher<GordianStreamKeySpec>
195 implements GordianStreamAEADCipher {
196
197
198
199
200
201
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
213 if (this == pThat) {
214 return true;
215 }
216 if (pThat == null) {
217 return false;
218 }
219
220
221 if (!(pThat instanceof JcaAEADCipher)) {
222 return false;
223 }
224 final JcaAEADCipher<?> myThat = (JcaAEADCipher<?>) pThat;
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 }