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.base.GordianLength;
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.GordianStreamCipher;
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.GordianSymCipher;
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.api.cipher.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
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 javax.crypto.spec.RC2ParameterSpec;
43 import javax.crypto.spec.RC5ParameterSpec;
44 import java.security.InvalidAlgorithmParameterException;
45 import java.security.InvalidKeyException;
46 import java.security.spec.AlgorithmParameterSpec;
47
48
49
50
51
52
53 public abstract class JcaCipher<T extends GordianKeySpec>
54 extends GordianCoreCipher<T> {
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 JcaCipher(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 @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[] myIV = getInitVector();
97
98
99 try {
100
101 final T myKeyType = myJcaKey.getKeyType();
102 final boolean isRC5 = myKeyType instanceof GordianSymKeySpec
103 && GordianSymKeyType.RC5.equals(((GordianSymKeySpec) myKeyType).getSymKeyType());
104
105
106 if (myIV != null || isRC5) {
107 final AlgorithmParameterSpec myParms = generateParameters(myJcaKey, myIV);
108 theCipher.init(myMode, myKey, myParms);
109 } else {
110 theCipher.init(myMode, myKey);
111 }
112 isEncrypting = pEncrypt;
113 } catch (InvalidKeyException
114 | InvalidAlgorithmParameterException e) {
115 throw new GordianCryptoException("Failed to initialise cipher", e);
116 }
117 }
118
119
120
121
122
123
124
125
126 static AlgorithmParameterSpec generateParameters(final JcaKey<?> pKey,
127 final byte[] pIV) {
128 final Object myKeyType = pKey.getKeyType();
129 if (myKeyType instanceof GordianSymKeySpec) {
130 final GordianSymKeySpec mySpec = (GordianSymKeySpec) myKeyType;
131 final GordianSymKeyType myType = mySpec.getSymKeyType();
132 final GordianLength myLen = mySpec.getBlockLength();
133 if (GordianSymKeyType.RC2.equals(myType)) {
134 return new RC2ParameterSpec(pKey.getKeyBytes().length * Byte.SIZE, pIV);
135 }
136 if (GordianSymKeyType.RC5.equals(myType)) {
137 return pIV == null
138 ? new RC5ParameterSpec(1, GordianBaseData.RC5_ROUNDS, myLen.getLength() >> 1)
139 : new RC5ParameterSpec(1, GordianBaseData.RC5_ROUNDS, myLen.getLength() >> 1, pIV);
140 }
141 }
142 return new IvParameterSpec(pIV);
143 }
144
145 @Override
146 public int getOutputLength(final int pLength) {
147 return theCipher.getOutputSize(pLength);
148 }
149
150 @Override
151 public int doUpdate(final byte[] pBytes,
152 final int pOffset,
153 final int pLength,
154 final byte[] pOutput,
155 final int pOutOffset) throws GordianException {
156
157 try {
158
159 return theCipher.update(pBytes, pOffset, pLength, pOutput, pOutOffset);
160
161
162 } catch (ShortBufferException e) {
163 throw new GordianCryptoException("Failed to process bytes", e);
164 }
165 }
166
167 @Override
168 public int doFinish(final byte[] pOutput,
169 final int pOutOffset) throws GordianException {
170
171 try {
172
173 return theCipher.doFinal(pOutput, pOutOffset);
174
175
176 } catch (ShortBufferException
177 | IllegalBlockSizeException
178 | BadPaddingException e) {
179 throw new GordianCryptoException("Failed to finish operation", e);
180 }
181 }
182
183 @Override
184 public int getBlockSize() {
185 final GordianCipherSpec<T> mySpec = getCipherSpec();
186 return (mySpec instanceof GordianSymCipherSpec
187 && ((GordianSymCipherSpec) mySpec).getCipherMode().hasPadding())
188 ? theCipher.getBlockSize() : 0;
189 }
190
191
192
193
194 public static class JcaSymCipher
195 extends JcaCipher<GordianSymKeySpec>
196 implements GordianSymCipher {
197
198
199
200
201
202
203
204 JcaSymCipher(final GordianBaseFactory pFactory,
205 final GordianSymCipherSpec pCipherSpec,
206 final Cipher pCipher) {
207 super(pFactory, pCipherSpec, pCipher);
208 }
209 }
210
211
212
213
214 public static class JcaStreamCipher
215 extends JcaCipher<GordianStreamKeySpec>
216 implements GordianStreamCipher {
217
218
219
220
221
222
223
224 JcaStreamCipher(final GordianBaseFactory pFactory,
225 final GordianStreamCipherSpec pCipherSpec,
226 final Cipher pCipher) {
227 super(pFactory, pCipherSpec, pCipher);
228 }
229 }
230
231 @Override
232 public boolean equals(final Object pThat) {
233
234 if (this == pThat) {
235 return true;
236 }
237 if (pThat == null) {
238 return false;
239 }
240
241
242 if (!(pThat instanceof JcaCipher)) {
243 return false;
244 }
245 final JcaCipher<?> myThat = (JcaCipher<?>) pThat;
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 }