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.base.GordianLength;
22 import io.github.tonywasher.joceanus.gordianknot.api.cipher.GordianCipherParams;
23 import io.github.tonywasher.joceanus.gordianknot.api.cipher.GordianStreamCipher;
24 import io.github.tonywasher.joceanus.gordianknot.api.cipher.GordianSymCipher;
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.api.cipher.spec.GordianSymKeyType;
31 import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianBaseData;
32 import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianBaseFactory;
33 import io.github.tonywasher.joceanus.gordianknot.impl.core.cipher.GordianCoreCipher;
34 import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianCryptoException;
35 import io.github.tonywasher.joceanus.gordianknot.impl.core.spec.cipher.GordianCoreSymCipherSpec;
36 import io.github.tonywasher.joceanus.gordianknot.impl.jca.base.JcaKey;
37
38 import javax.crypto.BadPaddingException;
39 import javax.crypto.Cipher;
40 import javax.crypto.IllegalBlockSizeException;
41 import javax.crypto.SecretKey;
42 import javax.crypto.ShortBufferException;
43 import javax.crypto.spec.IvParameterSpec;
44 import javax.crypto.spec.RC2ParameterSpec;
45 import javax.crypto.spec.RC5ParameterSpec;
46 import java.security.InvalidAlgorithmParameterException;
47 import java.security.InvalidKeyException;
48 import java.security.spec.AlgorithmParameterSpec;
49
50
51
52
53
54
55 public abstract class JcaCipher<T extends GordianKeySpec>
56 extends GordianCoreCipher<T> {
57
58
59
60 private final Cipher theCipher;
61
62
63
64
65 private boolean isEncrypting;
66
67
68
69
70
71
72
73
74 JcaCipher(final GordianBaseFactory pFactory,
75 final GordianCipherSpec<T> pCipherSpec,
76 final Cipher pCipher) {
77 super(pFactory, pCipherSpec);
78 theCipher = pCipher;
79 }
80
81 @Override
82 public JcaKey<T> getKey() {
83 return (JcaKey<T>) super.getKey();
84 }
85
86 @Override
87 public void init(final boolean pEncrypt,
88 final GordianCipherParams pParams) throws GordianException {
89
90 processParameters(pParams);
91 final JcaKey<T> myJcaKey = JcaKey.accessKey(getKey());
92
93
94 final int myMode = pEncrypt
95 ? Cipher.ENCRYPT_MODE
96 : Cipher.DECRYPT_MODE;
97 final SecretKey myKey = myJcaKey.getKey();
98 final byte[] myIV = getInitVector();
99
100
101 try {
102
103 final T myKeyType = myJcaKey.getKeyType();
104 final boolean isRC5 = myKeyType instanceof GordianSymKeySpec mySymKeySpec
105 && GordianSymKeyType.RC5.equals(mySymKeySpec.getSymKeyType());
106
107
108 if (myIV != null || isRC5) {
109 final AlgorithmParameterSpec myParms = generateParameters(myJcaKey, myIV);
110 theCipher.init(myMode, myKey, myParms);
111 } else {
112 theCipher.init(myMode, myKey);
113 }
114 isEncrypting = pEncrypt;
115 } catch (InvalidKeyException
116 | InvalidAlgorithmParameterException e) {
117 throw new GordianCryptoException("Failed to initialise cipher", e);
118 }
119 }
120
121
122
123
124
125
126
127
128 static AlgorithmParameterSpec generateParameters(final JcaKey<?> pKey,
129 final byte[] pIV) {
130 final Object myKeyType = pKey.getKeyType();
131 if (myKeyType instanceof GordianSymKeySpec mySpec) {
132 final GordianSymKeyType myType = mySpec.getSymKeyType();
133 final GordianLength myLen = mySpec.getBlockLength();
134 if (GordianSymKeyType.RC2.equals(myType)) {
135 return new RC2ParameterSpec(pKey.getKeyBytes().length * Byte.SIZE, pIV);
136 }
137 if (GordianSymKeyType.RC5.equals(myType)) {
138 return pIV == null
139 ? new RC5ParameterSpec(1, GordianBaseData.RC5_ROUNDS, myLen.getLength() >> 1)
140 : new RC5ParameterSpec(1, GordianBaseData.RC5_ROUNDS, myLen.getLength() >> 1, pIV);
141 }
142 }
143 return new IvParameterSpec(pIV);
144 }
145
146 @Override
147 public int getOutputLength(final int pLength) {
148 return theCipher.getOutputSize(pLength);
149 }
150
151 @Override
152 public int doUpdate(final byte[] pBytes,
153 final int pOffset,
154 final int pLength,
155 final byte[] pOutput,
156 final int pOutOffset) throws GordianException {
157
158 try {
159
160 return theCipher.update(pBytes, pOffset, pLength, pOutput, pOutOffset);
161
162
163 } catch (ShortBufferException e) {
164 throw new GordianCryptoException("Failed to process bytes", e);
165 }
166 }
167
168 @Override
169 public int doFinish(final byte[] pOutput,
170 final int pOutOffset) throws GordianException {
171
172 try {
173
174 return theCipher.doFinal(pOutput, pOutOffset);
175
176
177 } catch (ShortBufferException
178 | IllegalBlockSizeException
179 | BadPaddingException e) {
180 throw new GordianCryptoException("Failed to finish operation", e);
181 }
182 }
183
184 @Override
185 public int getBlockSize() {
186 final GordianCipherSpec<T> mySpec = getCipherSpec();
187 return (mySpec instanceof GordianCoreSymCipherSpec mySymCipherSpec
188 && mySymCipherSpec.getCoreCipherMode().hasPadding())
189 ? theCipher.getBlockSize() : 0;
190 }
191
192
193
194
195 public static class JcaSymCipher
196 extends JcaCipher<GordianSymKeySpec>
197 implements GordianSymCipher {
198
199
200
201
202
203
204
205 JcaSymCipher(final GordianBaseFactory pFactory,
206 final GordianSymCipherSpec pCipherSpec,
207 final Cipher pCipher) {
208 super(pFactory, pCipherSpec, pCipher);
209 }
210 }
211
212
213
214
215 public static class JcaStreamCipher
216 extends JcaCipher<GordianStreamKeySpec>
217 implements GordianStreamCipher {
218
219
220
221
222
223
224
225 JcaStreamCipher(final GordianBaseFactory pFactory,
226 final GordianStreamCipherSpec pCipherSpec,
227 final Cipher pCipher) {
228 super(pFactory, pCipherSpec, pCipher);
229 }
230 }
231
232 @Override
233 public boolean equals(final Object pThat) {
234
235 if (this == pThat) {
236 return true;
237 }
238 if (pThat == null) {
239 return false;
240 }
241
242
243 if (!(pThat instanceof JcaCipher<?> myThat)) {
244 return false;
245 }
246
247
248 return isEncrypting == myThat.isEncrypting
249 && super.equals(myThat);
250 }
251
252 @Override
253 public int hashCode() {
254 return super.hashCode()
255 + (isEncrypting ? 1 : 0);
256 }
257 }