1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.crypto.cipher;
19
20 import java.io.ByteArrayOutputStream;
21 import java.nio.ByteBuffer;
22 import java.nio.ByteOrder;
23 import java.security.InvalidAlgorithmParameterException;
24 import java.security.spec.AlgorithmParameterSpec;
25
26 import javax.crypto.AEADBadTagException;
27 import javax.crypto.BadPaddingException;
28 import javax.crypto.IllegalBlockSizeException;
29 import javax.crypto.ShortBufferException;
30 import javax.crypto.spec.GCMParameterSpec;
31
32
33
34
35
36
37
38
39 final class OpenSslGaloisCounterMode extends AbstractOpenSslFeedbackCipher {
40
41 static final int DEFAULT_TAG_LEN = 16;
42
43 private ByteArrayOutputStream aadBuffer = new ByteArrayOutputStream();
44
45 private int tagBitLen = -1;
46
47
48 private ByteArrayOutputStream inBuffer;
49
50 public OpenSslGaloisCounterMode(final long context, final int algorithmMode, final int padding) {
51 super(context, algorithmMode, padding);
52 }
53
54 @Override
55 public void clean() {
56 super.clean();
57 aadBuffer = null;
58 }
59
60 @Override
61 public int doFinal(final byte[] input, final int inputOffset, final int inputLen, final byte[] output, final int outputOffset)
62 throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
63 checkState();
64
65 processAAD();
66
67 final int outputLength = output.length;
68 int len;
69 if (this.cipherMode == OpenSsl.DECRYPT_MODE) {
70
71
72 int inputOffsetFinal = inputOffset;
73 int inputLenFinal = inputLen;
74 final byte[] inputFinal;
75 if (inBuffer != null && inBuffer.size() > 0) {
76 inBuffer.write(input, inputOffset, inputLen);
77 inputFinal = inBuffer.toByteArray();
78 inputOffsetFinal = 0;
79 inputLenFinal = inputFinal.length;
80 inBuffer.reset();
81 } else {
82 inputFinal = input;
83 }
84
85 if (inputFinal.length < getTagLen()) {
86 throw new AEADBadTagException("Input too short - need tag");
87 }
88
89 final int inputDataLen = inputLenFinal - getTagLen();
90 len = OpenSslNative.updateByteArray(context, inputFinal, inputOffsetFinal,
91 inputDataLen, output, outputOffset, outputLength - outputOffset);
92
93
94 final ByteBuffer tag = ByteBuffer.allocate(getTagLen());
95 tag.put(input, input.length - getTagLen(), getTagLen());
96 tag.flip();
97 evpCipherCtxCtrl(context, OpenSslEvpCtrlValues.AEAD_SET_TAG.getValue(), getTagLen(), tag);
98 } else {
99 len = OpenSslNative.updateByteArray(context, input, inputOffset,
100 inputLen, output, outputOffset, outputLength - outputOffset);
101 }
102
103 len += OpenSslNative.doFinalByteArray(context, output, outputOffset + len,
104 outputLength - outputOffset - len);
105
106
107 if (this.cipherMode == OpenSsl.ENCRYPT_MODE) {
108 final ByteBuffer tag;
109 tag = ByteBuffer.allocate(getTagLen());
110 evpCipherCtxCtrl(context, OpenSslEvpCtrlValues.AEAD_GET_TAG.getValue(), getTagLen(), tag);
111 tag.get(output, outputLength - getTagLen(), getTagLen());
112 len += getTagLen();
113 }
114
115 return len;
116 }
117
118 @Override
119 public int doFinal(final ByteBuffer input, final ByteBuffer output)
120 throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
121 checkState();
122
123 processAAD();
124
125 int totalLen = 0;
126 int len;
127 if (this.cipherMode == OpenSsl.DECRYPT_MODE) {
128 final ByteBuffer tag = ByteBuffer.allocate(getTagLen());
129
130
131
132 if (inBuffer != null && inBuffer.size() > 0) {
133 final byte[] inputBytes = new byte[input.remaining()];
134 input.get(inputBytes, 0, inputBytes.length);
135 inBuffer.write(inputBytes, 0, inputBytes.length);
136 final byte[] inputFinal = inBuffer.toByteArray();
137 inBuffer.reset();
138
139 if (inputFinal.length < getTagLen()) {
140 throw new AEADBadTagException("Input too short - need tag");
141 }
142
143 len = OpenSslNative.updateByteArrayByteBuffer(context, inputFinal, 0,
144 inputFinal.length - getTagLen(),
145 output, output.position(), output.remaining());
146
147
148 tag.put(inputFinal, inputFinal.length - getTagLen(), getTagLen());
149
150 } else {
151
152 if (input.remaining() < getTagLen()) {
153 throw new AEADBadTagException("Input too short - need tag");
154 }
155
156 len = OpenSslNative.update(context, input, input.position(),
157 input.remaining() - getTagLen(), output, output.position(),
158 output.remaining());
159
160 input.position(input.position() + len);
161
162
163 tag.put(input);
164 }
165 tag.flip();
166
167
168 evpCipherCtxCtrl(context, OpenSslEvpCtrlValues.AEAD_SET_TAG.getValue(),
169 getTagLen(), tag);
170 } else {
171 len = OpenSslNative.update(context, input, input.position(),
172 input.remaining(), output, output.position(),
173 output.remaining());
174 input.position(input.limit());
175 }
176
177 totalLen += len;
178 output.position(output.position() + len);
179
180 len = OpenSslNative.doFinal(context, output, output.position(),
181 output.remaining());
182 output.position(output.position() + len);
183 totalLen += len;
184
185
186 if (this.cipherMode == OpenSsl.ENCRYPT_MODE) {
187 final ByteBuffer tag;
188 tag = ByteBuffer.allocate(getTagLen());
189 evpCipherCtxCtrl(context, OpenSslEvpCtrlValues.AEAD_GET_TAG.getValue(), getTagLen(), tag);
190 output.put(tag);
191 totalLen += getTagLen();
192 }
193
194 return totalLen;
195 }
196
197
198
199
200
201
202
203
204
205
206
207
208
209 private int evpCipherCtxCtrl(final long context, final int type, final int arg, final ByteBuffer data) {
210 checkState();
211 try {
212 if (data != null) {
213 data.order(ByteOrder.nativeOrder());
214 return OpenSslNative.ctrl(context, type, arg, data.array());
215 }
216 return OpenSslNative.ctrl(context, type, arg, null);
217 } catch (final Exception e) {
218 System.out.println(e.getMessage());
219 return 0;
220 }
221 }
222
223 private int getTagLen() {
224 return tagBitLen < 0 ? DEFAULT_TAG_LEN : tagBitLen >> 3;
225 }
226
227 @Override
228 public void init(final int mode, final byte[] key, final AlgorithmParameterSpec params)
229 throws InvalidAlgorithmParameterException {
230
231 if (aadBuffer == null) {
232 aadBuffer = new ByteArrayOutputStream();
233 } else {
234 aadBuffer.reset();
235 }
236
237 this.cipherMode = mode;
238 final byte[] iv;
239 if (!(params instanceof GCMParameterSpec)) {
240
241 throw new InvalidAlgorithmParameterException("Illegal parameters");
242 }
243 final GCMParameterSpec gcmParam = (GCMParameterSpec) params;
244 iv = gcmParam.getIV();
245 this.tagBitLen = gcmParam.getTLen();
246
247 if (this.cipherMode == OpenSsl.DECRYPT_MODE) {
248 inBuffer = new ByteArrayOutputStream();
249 }
250
251 context = OpenSslNative.init(context, mode, algorithmMode, padding, key, iv);
252 }
253
254 private void processAAD() {
255 if (aadBuffer != null && aadBuffer.size() > 0) {
256 OpenSslNative.updateByteArray(context, aadBuffer.toByteArray(), 0, aadBuffer.size(), null, 0, 0);
257 aadBuffer = null;
258 }
259 }
260
261 @Override
262 public int update(final byte[] input, final int inputOffset, final int inputLen, final byte[] output, final int outputOffset)
263 throws ShortBufferException {
264 checkState();
265
266 processAAD();
267
268 if (this.cipherMode == OpenSsl.DECRYPT_MODE) {
269
270
271
272 inBuffer.write(input, inputOffset, inputLen);
273 return 0;
274 }
275 return OpenSslNative.updateByteArray(context, input, inputOffset,
276 inputLen, output, outputOffset, output.length - outputOffset);
277 }
278
279 @Override
280 public int update(final ByteBuffer input, final ByteBuffer output) throws ShortBufferException {
281 checkState();
282
283 processAAD();
284
285 final int len;
286 if (this.cipherMode == OpenSsl.DECRYPT_MODE) {
287
288
289
290 final int inputLen = input.remaining();
291 final byte[] inputBuf = new byte[inputLen];
292 input.get(inputBuf, 0, inputLen);
293 inBuffer.write(inputBuf, 0, inputLen);
294 return 0;
295 }
296 len = OpenSslNative.update(context, input, input.position(),
297 input.remaining(), output, output.position(),
298 output.remaining());
299 input.position(input.limit());
300 output.position(output.position() + len);
301
302 return len;
303 }
304
305 @Override
306 public void updateAAD(final byte[] aad) {
307
308 if (aadBuffer == null) {
309
310 throw new IllegalStateException("Update has been called; no more AAD data");
311 }
312 aadBuffer.write(aad, 0, aad.length);
313 }
314 }