Source for gnu.javax.net.ssl.provider.InputSecurityParameters

   1: /* SecurityParameters.java -- SSL security parameters.
   2:    Copyright (C) 2006  Free Software Foundation, Inc.
   3: 
   4: This file is a part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2 of the License, or (at
   9: your option) any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; if not, write to the Free Software
  18: Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
  19: USA
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version.  */
  37: 
  38: 
  39: package gnu.javax.net.ssl.provider;
  40: 
  41: import gnu.classpath.debug.Component;
  42: import gnu.classpath.debug.SystemLogger;
  43: import gnu.java.security.util.ByteArray;
  44: import gnu.java.security.util.ByteBufferOutputStream;
  45: 
  46: import java.nio.BufferOverflowException;
  47: import java.nio.ByteBuffer;
  48: 
  49: import java.util.Arrays;
  50: import java.util.logging.Level;
  51: import java.util.zip.DataFormatException;
  52: import java.util.zip.Inflater;
  53: 
  54: import javax.crypto.BadPaddingException;
  55: import javax.crypto.Cipher;
  56: import javax.crypto.IllegalBlockSizeException;
  57: import javax.crypto.Mac;
  58: import javax.crypto.ShortBufferException;
  59: 
  60: import javax.net.ssl.SSLException;
  61: 
  62: public class InputSecurityParameters
  63: {
  64:   private static final SystemLogger logger = SystemLogger.SYSTEM;
  65:   private final Cipher cipher;
  66:   private final Mac mac;
  67:   private final Inflater inflater;
  68:   private SessionImpl session;
  69:   private final CipherSuite suite;
  70:   private long sequence;
  71: 
  72:   public InputSecurityParameters (final Cipher cipher, final Mac mac,
  73:                                   final Inflater inflater,
  74:                                   final SessionImpl session,
  75:                                   final CipherSuite suite)
  76:   {
  77:     this.cipher = cipher;
  78:     this.mac = mac;
  79:     this.inflater = inflater;
  80:     this.session = session;
  81:     this.suite = suite;
  82:     sequence = 0;
  83:   }
  84: 
  85:   /**
  86:    * Decrypt a record, storing the decrypted fragment into the given array
  87:    * of byte buffers.
  88:    * 
  89:    * @param record The input record.
  90:    * @param output The output buffers.
  91:    * @param offset The offset of the first buffer to use.
  92:    * @param length The number of buffers to use.
  93:    * @return The number of bytes put in the output buffers.
  94:    * @throws DataFormatException If decompression fails.
  95:    * @throws IllegalBlockSizeException If the current cipher is a block cipher,
  96:    *  and the input fragment is not a multiple of the block size.
  97:    * @throws MacException If verifying the MAC fails.
  98:    * @throws SSLException ???
  99:    * @throws ShortBufferException 
 100:    */
 101:   public int decrypt(Record record, ByteBuffer[] output, int offset, int length)
 102:     throws DataFormatException, IllegalBlockSizeException,
 103:            MacException, SSLException, ShortBufferException
 104:   {
 105:     return decrypt(record, output, offset, length, null);
 106:   }
 107:   
 108:   /**
 109:    * Decrypt a record, storing the decrypted fragment into the given growable
 110:    * buffer.
 111:    * 
 112:    * @param record The input record.
 113:    * @param outputStream The output buffer.
 114:    * @return The number of bytes put into the output buffer.
 115:    * @throws DataFormatException
 116:    * @throws IllegalBlockSizeException
 117:    * @throws MacException
 118:    * @throws SSLException
 119:    * @throws ShortBufferException
 120:    */
 121:   public int decrypt(Record record, ByteBufferOutputStream outputStream)
 122:     throws DataFormatException, IllegalBlockSizeException,
 123:            MacException, SSLException, ShortBufferException
 124:   {
 125:     return decrypt(record, null, 0, 0, outputStream);
 126:   }
 127:   
 128:   private int decrypt(Record record, ByteBuffer[] output, int offset, int length,
 129:                       ByteBufferOutputStream outputStream)
 130:     throws DataFormatException, IllegalBlockSizeException,
 131:            MacException, SSLException, ShortBufferException
 132:   {
 133:     boolean badPadding = false;
 134:     ByteBuffer fragment;
 135:     if (cipher != null)
 136:       {
 137:         ByteBuffer input = record.fragment();
 138:         fragment = ByteBuffer.allocate(input.remaining());
 139:         cipher.update(input, fragment);
 140:       }
 141:     else
 142:       fragment = record.fragment();
 143: 
 144:     if (Debug.DEBUG_DECRYPTION)
 145:       logger.logv(Component.SSL_RECORD_LAYER, "decrypted fragment:\n{0}",
 146:                   Util.hexDump((ByteBuffer) fragment.duplicate().position(0), " >> "));
 147:     
 148:     int fragmentLength = record.length();
 149:     int maclen = 0;
 150:     if (mac != null)
 151:       maclen = mac.getMacLength();
 152:     fragmentLength -= maclen;
 153: 
 154:     int padlen = 0;
 155:     int padRemoveLen = 0;
 156:     if (!suite.isStreamCipher ())
 157:       {
 158:         padlen = fragment.get(record.length() - 1) & 0xFF;
 159:         padRemoveLen = padlen + 1;
 160:         if (Debug.DEBUG)
 161:           logger.logv(Component.SSL_RECORD_LAYER, "padlen:{0}", padlen);
 162: 
 163:         if (record.version() == ProtocolVersion.SSL_3)
 164:           {
 165:             // In SSLv3, the padding length must not be larger than
 166:             // the cipher's block size.
 167:             if (padlen > cipher.getBlockSize ())
 168:               badPadding = true;
 169:           }
 170:         else if (record.version().compareTo(ProtocolVersion.TLS_1) >= 0)
 171:           {
 172:             // In TLSv1 and later, the padding must be `padlen' copies of the
 173:             // value `padlen'.
 174:             byte[] pad = new byte[padlen];
 175:             ((ByteBuffer) fragment.duplicate().position(record.length() - padlen - 1)).get(pad);
 176:             for (int i = 0; i < pad.length; i++)
 177:               if ((pad[i] & 0xFF) != padlen)
 178:                 badPadding = true;
 179:             if (Debug.DEBUG)
 180:               logger.logv(Component.SSL_RECORD_LAYER, "TLSv1.x padding\n{0}",
 181:                           new ByteArray(pad));
 182:           }
 183:         
 184:         if (Debug.DEBUG)
 185:           logger.logv(Component.SSL_RECORD_LAYER, "padding bad? {0}",
 186:                       badPadding);
 187:         if (!badPadding)
 188:           fragmentLength = fragmentLength - padRemoveLen;
 189:       }
 190:     
 191:     int ivlen = 0;
 192:     if (session.version.compareTo(ProtocolVersion.TLS_1_1) >= 0
 193:         && !suite.isStreamCipher())
 194:       ivlen = cipher.getBlockSize();
 195: 
 196:     // Compute and check the MAC.
 197:     if (mac != null)
 198:       {
 199:         mac.update((byte) (sequence >>> 56));
 200:         mac.update((byte) (sequence >>> 48));
 201:         mac.update((byte) (sequence >>> 40));
 202:         mac.update((byte) (sequence >>> 32));
 203:         mac.update((byte) (sequence >>> 24));
 204:         mac.update((byte) (sequence >>> 16));
 205:         mac.update((byte) (sequence >>>  8));
 206:         mac.update((byte)  sequence);
 207:         mac.update((byte) record.getContentType().getValue());
 208:         ProtocolVersion version = record.version();
 209:         if (version != ProtocolVersion.SSL_3)
 210:           {
 211:             mac.update((byte) version.major());
 212:             mac.update((byte) version.minor());
 213:           }
 214:         mac.update((byte) ((fragmentLength - ivlen) >>> 8));
 215:         mac.update((byte)  (fragmentLength - ivlen));
 216:         ByteBuffer content =
 217:           (ByteBuffer) fragment.duplicate().position(ivlen).limit(fragmentLength);
 218:         mac.update(content);
 219:         byte[] mac1 = mac.doFinal ();
 220:         byte[] mac2 = new byte[maclen];
 221:         mac.reset();
 222:         ((ByteBuffer) fragment.duplicate().position(fragmentLength)).get(mac2);
 223:         if (Debug.DEBUG)
 224:           logger.logv(Component.SSL_RECORD_LAYER, "mac1:{0} mac2:{1}",
 225:                       Util.toHexString(mac1, ':'), Util.toHexString(mac2, ':'));
 226:         if (!Arrays.equals (mac1, mac2))
 227:           badPadding = true;
 228:       }
 229: 
 230:     // We always say "bad MAC" and not "bad padding," because saying
 231:     // the latter will leak information to an attacker.
 232:     if (badPadding)
 233:       throw new MacException ();
 234: 
 235:     // Inflate the compressed bytes.
 236:     int produced = 0;
 237:     if (inflater != null)
 238:       {
 239:         ByteBufferOutputStream out = new ByteBufferOutputStream(fragmentLength);
 240:         byte[] inbuffer = new byte[1024];
 241:         byte[] outbuffer = new byte[1024];
 242:         boolean done = false;
 243:         if (record.version().compareTo(ProtocolVersion.TLS_1_1) >= 0
 244:             && !suite.isStreamCipher())
 245:           fragment.position (cipher.getBlockSize());
 246:         else
 247:           fragment.position(0);
 248:         fragment.limit(fragmentLength);
 249:         
 250:         while (!done)
 251:           {
 252:             int l;
 253:             if (inflater.needsInput())
 254:               {
 255:                 l = Math.min(inbuffer.length, fragment.remaining());
 256:                 fragment.get(inbuffer, 0, l);
 257:                 inflater.setInput(inbuffer);
 258:               }
 259: 
 260:             l = inflater.inflate(outbuffer);
 261:             out.write(outbuffer, 0, l);
 262:             done = !fragment.hasRemaining() && inflater.finished();
 263:           }
 264:         
 265:         ByteBuffer outbuf = out.buffer();
 266:         if (outputStream != null)
 267:           {
 268:             byte[] buf = new byte[1024];
 269:             while (outbuf.hasRemaining())
 270:               {
 271:                 int l = Math.min(outbuf.remaining(), buf.length);
 272:                 outbuf.get(buf, 0, l);
 273:                 outputStream.write(buf, 0, l);
 274:                 produced += l;
 275:               }
 276:           }
 277:         else
 278:           {
 279:             int i = offset;
 280:             while (outbuf.hasRemaining() && i < offset + length)
 281:               {
 282:                 int l = Math.min(output[i].remaining(), outbuf.remaining());
 283:                 ByteBuffer b = (ByteBuffer)
 284:                   outbuf.duplicate().limit(outbuf.position() + l);
 285:                 output[i++].put(b);
 286:                 outbuf.position(outbuf.position() + l);
 287:                 produced += l;
 288:               }
 289:             if (outbuf.hasRemaining())
 290:               throw new BufferOverflowException();
 291:           }
 292:       }
 293:     else
 294:       {
 295:         ByteBuffer outbuf = (ByteBuffer)
 296:           fragment.duplicate().position(0).limit(record.length() - maclen - padRemoveLen);
 297:         if (record.version().compareTo(ProtocolVersion.TLS_1_1) >= 0
 298:             && !suite.isStreamCipher())
 299:           outbuf.position(cipher.getBlockSize());
 300:         if (outputStream != null)
 301:           {
 302:             byte[] buf = new byte[1024];
 303:             while (outbuf.hasRemaining())
 304:               {
 305:                 int l = Math.min(outbuf.remaining(), buf.length);
 306:                 outbuf.get(buf, 0, l);
 307:                 outputStream.write(buf, 0, l);
 308:                 produced += l;
 309:               }
 310:           }
 311:         else
 312:           {
 313:             int i = offset;
 314:             while (outbuf.hasRemaining() && i < offset + length)
 315:               {
 316:                 int l = Math.min(output[i].remaining(), outbuf.remaining());
 317:                 ByteBuffer b = (ByteBuffer) outbuf.duplicate().limit(outbuf.position() + l);
 318:                 output[i++].put(b);
 319:                 outbuf.position(outbuf.position() + l);
 320:                 produced += l;
 321:               }
 322:             if (outbuf.hasRemaining())
 323:               throw new BufferOverflowException();
 324:           }
 325:       }
 326: 
 327:     sequence++;
 328:     
 329:     return produced;
 330:   }
 331:   
 332:   CipherSuite cipherSuite ()
 333:   {
 334:     return suite;
 335:   }
 336: }