blob: bcc84c17aa7b92bc2971d10a8d64622126103b30 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26
27package sun.security.ssl;
28
29import java.io.ByteArrayInputStream;
30import java.io.IOException;
31
32import java.security.*;
33import javax.crypto.*;
34import javax.crypto.spec.SecretKeySpec;
35import javax.crypto.spec.IvParameterSpec;
36
37import java.nio.*;
38
39import sun.security.ssl.CipherSuite.*;
40import static sun.security.ssl.CipherSuite.*;
41
42import sun.misc.HexDumpEncoder;
43
44
45/**
46 * This class handles bulk data enciphering/deciphering for each SSLv3
47 * message. This provides data confidentiality. Stream ciphers (such
48 * as RC4) don't need to do padding; block ciphers (e.g. DES) need it.
49 *
50 * Individual instances are obtained by calling the static method
51 * newCipherBox(), which should only be invoked by BulkCipher.newCipher().
52 *
53 * NOTE that any ciphering involved in key exchange (e.g. with RSA) is
54 * handled separately.
55 *
56 * @author David Brownell
57 * @author Andreas Sterbenz
58 */
59final class CipherBox {
60
61 // A CipherBox that implements the identity operation
62 final static CipherBox NULL = new CipherBox();
63
64 /* Class and subclass dynamic debugging support */
65 private static final Debug debug = Debug.getInstance("ssl");
66
67 // the protocol version this cipher conforms to
68 private final ProtocolVersion protocolVersion;
69
70 // cipher object
71 private final Cipher cipher;
72
73 /**
74 * Cipher blocksize, 0 for stream ciphers
75 */
76 private int blockSize;
77
78 /**
79 * NULL cipherbox. Identity operation, no encryption.
80 */
81 private CipherBox() {
82 this.protocolVersion = ProtocolVersion.DEFAULT;
83 this.cipher = null;
84 }
85
86 /**
87 * Construct a new CipherBox using the cipher transformation.
88 *
89 * @exception NoSuchAlgorithmException if no appropriate JCE Cipher
90 * implementation could be found.
91 */
92 private CipherBox(ProtocolVersion protocolVersion, BulkCipher bulkCipher,
93 SecretKey key, IvParameterSpec iv, boolean encrypt)
94 throws NoSuchAlgorithmException {
95 try {
96 this.protocolVersion = protocolVersion;
97 this.cipher = JsseJce.getCipher(bulkCipher.transformation);
98 int mode = encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE;
99 cipher.init(mode, key, iv);
100 // do not call getBlockSize until after init()
101 // otherwise we would disrupt JCE delayed provider selection
102 blockSize = cipher.getBlockSize();
103 // some providers implement getBlockSize() incorrectly
104 if (blockSize == 1) {
105 blockSize = 0;
106 }
107 } catch (NoSuchAlgorithmException e) {
108 throw e;
109 } catch (Exception e) {
110 throw new NoSuchAlgorithmException
111 ("Could not create cipher " + bulkCipher, e);
112 } catch (ExceptionInInitializerError e) {
113 throw new NoSuchAlgorithmException
114 ("Could not create cipher " + bulkCipher, e);
115 }
116 }
117
118 /*
119 * Factory method to obtain a new CipherBox object.
120 */
121 static CipherBox newCipherBox(ProtocolVersion version, BulkCipher cipher,
122 SecretKey key, IvParameterSpec iv, boolean encrypt)
123 throws NoSuchAlgorithmException {
124 if (cipher.allowed == false) {
125 throw new NoSuchAlgorithmException("Unsupported cipher " + cipher);
126 }
127 if (cipher == B_NULL) {
128 return NULL;
129 } else {
130 return new CipherBox(version, cipher, key, iv, encrypt);
131 }
132 }
133
134 /*
135 * Encrypts a block of data, returning the size of the
136 * resulting block.
137 */
138 int encrypt(byte[] buf, int offset, int len) {
139 if (cipher == null) {
140 return len;
141 }
142 try {
143 if (blockSize != 0) {
144 len = addPadding(buf, offset, len, blockSize);
145 }
146 if (debug != null && Debug.isOn("plaintext")) {
147 try {
148 HexDumpEncoder hd = new HexDumpEncoder();
149
150 System.out.println(
151 "Padded plaintext before ENCRYPTION: len = "
152 + len);
153 hd.encodeBuffer(
154 new ByteArrayInputStream(buf, offset, len),
155 System.out);
156 } catch (IOException e) { }
157 }
158 int newLen = cipher.update(buf, offset, len, buf, offset);
159 if (newLen != len) {
160 // catch BouncyCastle buffering error
161 throw new RuntimeException("Cipher buffering error " +
162 "in JCE provider " + cipher.getProvider().getName());
163 }
164 return newLen;
165 } catch (ShortBufferException e) {
166 throw new ArrayIndexOutOfBoundsException(e.toString());
167 }
168 }
169
170 /*
171 * Encrypts a ByteBuffer block of data, returning the size of the
172 * resulting block.
173 *
174 * The byte buffers position and limit initially define the amount
175 * to encrypt. On return, the position and limit are
176 * set to last position padded/encrypted. The limit may have changed
177 * because of the added padding bytes.
178 */
179 int encrypt(ByteBuffer bb) {
180
181 int len = bb.remaining();
182
183 if (cipher == null) {
184 bb.position(bb.limit());
185 return len;
186 }
187
188 try {
189 int pos = bb.position();
190
191 if (blockSize != 0) {
192 // addPadding adjusts pos/limit
193 len = addPadding(bb, blockSize);
194 bb.position(pos);
195 }
196 if (debug != null && Debug.isOn("plaintext")) {
197 try {
198 HexDumpEncoder hd = new HexDumpEncoder();
199
200 System.out.println(
201 "Padded plaintext before ENCRYPTION: len = "
202 + len);
203 hd.encodeBuffer(bb, System.out);
204
205 } catch (IOException e) { }
206 /*
207 * reset back to beginning
208 */
209 bb.position(pos);
210 }
211
212 /*
213 * Encrypt "in-place". This does not add its own padding.
214 */
215 ByteBuffer dup = bb.duplicate();
216 int newLen = cipher.update(dup, bb);
217
218 if (bb.position() != dup.position()) {
219 throw new RuntimeException("bytebuffer padding error");
220 }
221
222 if (newLen != len) {
223 // catch BouncyCastle buffering error
224 throw new RuntimeException("Cipher buffering error " +
225 "in JCE provider " + cipher.getProvider().getName());
226 }
227 return newLen;
228 } catch (ShortBufferException e) {
229 RuntimeException exc = new RuntimeException(e.toString());
230 exc.initCause(e);
231 throw exc;
232 }
233 }
234
235
236 /*
237 * Decrypts a block of data, returning the size of the
238 * resulting block if padding was required.
239 */
240 int decrypt(byte[] buf, int offset, int len) throws BadPaddingException {
241 if (cipher == null) {
242 return len;
243 }
244 try {
245 int newLen = cipher.update(buf, offset, len, buf, offset);
246 if (newLen != len) {
247 // catch BouncyCastle buffering error
248 throw new RuntimeException("Cipher buffering error " +
249 "in JCE provider " + cipher.getProvider().getName());
250 }
251 if (debug != null && Debug.isOn("plaintext")) {
252 try {
253 HexDumpEncoder hd = new HexDumpEncoder();
254
255 System.out.println(
256 "Padded plaintext after DECRYPTION: len = "
257 + newLen);
258 hd.encodeBuffer(
259 new ByteArrayInputStream(buf, offset, newLen),
260 System.out);
261 } catch (IOException e) { }
262 }
263 if (blockSize != 0) {
264 newLen = removePadding(buf, offset, newLen,
265 blockSize, protocolVersion);
266 }
267 return newLen;
268 } catch (ShortBufferException e) {
269 throw new ArrayIndexOutOfBoundsException(e.toString());
270 }
271 }
272
273
274 /*
275 * Decrypts a block of data, returning the size of the
276 * resulting block if padding was required. position and limit
277 * point to the end of the decrypted/depadded data. The initial
278 * limit and new limit may be different, given we may
279 * have stripped off some padding bytes.
280 */
281 int decrypt(ByteBuffer bb) throws BadPaddingException {
282
283 int len = bb.remaining();
284
285 if (cipher == null) {
286 bb.position(bb.limit());
287 return len;
288 }
289
290 try {
291 /*
292 * Decrypt "in-place".
293 */
294 int pos = bb.position();
295
296 ByteBuffer dup = bb.duplicate();
297 int newLen = cipher.update(dup, bb);
298 if (newLen != len) {
299 // catch BouncyCastle buffering error
300 throw new RuntimeException("Cipher buffering error " +
301 "in JCE provider " + cipher.getProvider().getName());
302 }
303
304 if (debug != null && Debug.isOn("plaintext")) {
305 bb.position(pos);
306 try {
307 HexDumpEncoder hd = new HexDumpEncoder();
308
309 System.out.println(
310 "Padded plaintext after DECRYPTION: len = "
311 + newLen);
312
313 hd.encodeBuffer(bb, System.out);
314 } catch (IOException e) { }
315 }
316
317 /*
318 * Remove the block padding.
319 */
320 if (blockSize != 0) {
321 bb.position(pos);
322 newLen = removePadding(bb, blockSize, protocolVersion);
323 }
324 return newLen;
325 } catch (ShortBufferException e) {
326 RuntimeException exc = new RuntimeException(e.toString());
327 exc.initCause(e);
328 throw exc;
329 }
330 }
331
332 private static int addPadding(byte[] buf, int offset, int len,
333 int blockSize) {
334 int newlen = len + 1;
335 byte pad;
336 int i;
337
338 if ((newlen % blockSize) != 0) {
339 newlen += blockSize - 1;
340 newlen -= newlen % blockSize;
341 }
342 pad = (byte) (newlen - len);
343
344 if (buf.length < (newlen + offset)) {
345 throw new IllegalArgumentException("no space to pad buffer");
346 }
347
348 /*
349 * TLS version of the padding works for both SSLv3 and TLSv1
350 */
351 for (i = 0, offset += len; i < pad; i++) {
352 buf [offset++] = (byte) (pad - 1);
353 }
354 return newlen;
355 }
356
357 /*
358 * Apply the padding to the buffer.
359 *
360 * Limit is advanced to the new buffer length.
361 * Position is equal to limit.
362 */
363 private static int addPadding(ByteBuffer bb, int blockSize) {
364
365 int len = bb.remaining();
366 int offset = bb.position();
367
368 int newlen = len + 1;
369 byte pad;
370 int i;
371
372 if ((newlen % blockSize) != 0) {
373 newlen += blockSize - 1;
374 newlen -= newlen % blockSize;
375 }
376 pad = (byte) (newlen - len);
377
378 /*
379 * Update the limit to what will be padded.
380 */
381 bb.limit(newlen + offset);
382
383 /*
384 * TLS version of the padding works for both SSLv3 and TLSv1
385 */
386 for (i = 0, offset += len; i < pad; i++) {
387 bb.put(offset++, (byte) (pad - 1));
388 }
389
390 bb.position(offset);
391 bb.limit(offset);
392
393 return newlen;
394 }
395
396
397 /*
398 * Typical TLS padding format for a 64 bit block cipher is as follows:
399 * xx xx xx xx xx xx xx 00
400 * xx xx xx xx xx xx 01 01
401 * ...
402 * xx 06 06 06 06 06 06 06
403 * 07 07 07 07 07 07 07 07
404 * TLS also allows any amount of padding from 1 and 256 bytes as long
405 * as it makes the data a multiple of the block size
406 */
407 private static int removePadding(byte[] buf, int offset, int len,
408 int blockSize, ProtocolVersion protocolVersion)
409 throws BadPaddingException {
410 // last byte is length byte (i.e. actual padding length - 1)
411 int padOffset = offset + len - 1;
412 int pad = buf[padOffset] & 0x0ff;
413
414 int newlen = len - (pad + 1);
415 if (newlen < 0) {
416 throw new BadPaddingException("Padding length invalid: " + pad);
417 }
418
419 if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
420 for (int i = 1; i <= pad; i++) {
421 int val = buf[padOffset - i] & 0xff;
422 if (val != pad) {
423 throw new BadPaddingException
424 ("Invalid TLS padding: " + val);
425 }
426 }
427 } else { // SSLv3
428 // SSLv3 requires 0 <= length byte < block size
429 // some implementations do 1 <= length byte <= block size,
430 // so accept that as well
431 // v3 does not require any particular value for the other bytes
432 if (pad > blockSize) {
433 throw new BadPaddingException("Invalid SSLv3 padding: " + pad);
434 }
435 }
436 return newlen;
437 }
438
439 /*
440 * Position/limit is equal the removed padding.
441 */
442 private static int removePadding(ByteBuffer bb,
443 int blockSize, ProtocolVersion protocolVersion)
444 throws BadPaddingException {
445
446 int len = bb.remaining();
447 int offset = bb.position();
448
449 // last byte is length byte (i.e. actual padding length - 1)
450 int padOffset = offset + len - 1;
451 int pad = bb.get(padOffset) & 0x0ff;
452
453 int newlen = len - (pad + 1);
454 if (newlen < 0) {
455 throw new BadPaddingException("Padding length invalid: " + pad);
456 }
457
458 /*
459 * We could zero the padding area, but not much useful
460 * information there.
461 */
462 if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
463 bb.put(padOffset, (byte)0); // zero the padding.
464 for (int i = 1; i <= pad; i++) {
465 int val = bb.get(padOffset - i) & 0xff;
466 if (val != pad) {
467 throw new BadPaddingException
468 ("Invalid TLS padding: " + val);
469 }
470 }
471 } else { // SSLv3
472 // SSLv3 requires 0 <= length byte < block size
473 // some implementations do 1 <= length byte <= block size,
474 // so accept that as well
475 // v3 does not require any particular value for the other bytes
476 if (pad > blockSize) {
477 throw new BadPaddingException("Invalid SSLv3 padding: " + pad);
478 }
479 }
480
481 /*
482 * Reset buffer limit to remove padding.
483 */
484 bb.position(offset + newlen);
485 bb.limit(offset + newlen);
486
487 return newlen;
488 }
489}