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.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
37 import javax.crypto.BadPaddingException;
38 import javax.crypto.Cipher;
39 import javax.crypto.IllegalBlockSizeException;
40 import javax.crypto.SecretKey;
41 import javax.crypto.ShortBufferException;
42 import javax.crypto.spec.IvParameterSpec;
43 import javax.crypto.spec.RC2ParameterSpec;
44 import javax.crypto.spec.RC5ParameterSpec;
45 import java.security.InvalidAlgorithmParameterException;
46 import java.security.InvalidKeyException;
47 import java.security.spec.AlgorithmParameterSpec;
48
49
50
51
52
53
54 public abstract class JcaCipher<T extends GordianKeySpec>
55 extends GordianCoreCipher<T> {
56
57
58
59 private final Cipher theCipher;
60
61
62
63
64 private boolean isEncrypting;
65
66
67
68
69
70
71
72
73 JcaCipher(final GordianBaseFactory pFactory,
74 final GordianCipherSpec<T> pCipherSpec,
75 final Cipher pCipher) {
76 super(pFactory, pCipherSpec);
77 theCipher = pCipher;
78 }
79
80 @Override
81 public JcaKey<T> getKey() {
82 return (JcaKey<T>) super.getKey();
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[] myIV = getInitVector();
98
99
100 try {
101
102 final T myKeyType = myJcaKey.getKeyType();
103 final boolean isRC5 = myKeyType instanceof GordianSymKeySpec mySymKeySpec
104 && GordianSymKeyType.RC5.equals(mySymKeySpec.getSymKeyType());
105
106
107 if (myIV != null || isRC5) {
108 final AlgorithmParameterSpec myParms = generateParameters(myJcaKey, myIV);
109 theCipher.init(myMode, myKey, myParms);
110 } else {
111 theCipher.init(myMode, myKey);
112 }
113 isEncrypting = pEncrypt;
114 } catch (InvalidKeyException
115 | InvalidAlgorithmParameterException e) {
116 throw new GordianCryptoException("Failed to initialise cipher", e);
117 }
118 }
119
120
121
122
123
124
125
126
127 static AlgorithmParameterSpec generateParameters(final JcaKey<?> pKey,
128 final byte[] pIV) {
129 final Object myKeyType = pKey.getKeyType();
130 if (myKeyType instanceof GordianSymKeySpec mySpec) {
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 GordianCoreSymCipherSpec mySymCipherSpec
187 && mySymCipherSpec.getCoreCipherMode().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<?> myThat)) {
243 return false;
244 }
245
246
247 return isEncrypting == myThat.isEncrypting
248 && super.equals(myThat);
249 }
250
251 @Override
252 public int hashCode() {
253 return super.hashCode()
254 + (isEncrypting ? 1 : 0);
255 }
256 }