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.zip.DataFormatException;
  51: import java.util.zip.Inflater;
  52: 
  53: import javax.crypto.Cipher;
  54: import javax.crypto.IllegalBlockSizeException;
  55: import javax.crypto.Mac;
  56: import javax.crypto.ShortBufferException;
  57: 
  58: import javax.net.ssl.SSLException;
  59: 
  60: public class InputSecurityParameters
  61: {
  62:   private static final SystemLogger logger = SystemLogger.SYSTEM;
  63:   private final Cipher cipher;
  64:   private final Mac mac;
  65:   private final Inflater inflater;
  66:   private SessionImpl session;
  67:   private final CipherSuite suite;
  68:   private long sequence;
  69: 
  70:   public InputSecurityParameters (final Cipher cipher, final Mac mac,
  71:                                   final Inflater inflater,
  72:                                   final SessionImpl session,
  73:                                   final CipherSuite suite)
  74:   {
  75:     this.cipher = cipher;
  76:     this.mac = mac;
  77:     this.inflater = inflater;
  78:     this.session = session;
  79:     this.suite = suite;
  80:     sequence = 0;
  81:   }
  82: 
  83:   /**
  84:    * Decrypt a record, storing the decrypted fragment into the given array
  85:    * of byte buffers.
  86:    *
  87:    * @param record The input record.
  88:    * @param output The output buffers.
  89:    * @param offset The offset of the first buffer to use.
  90:    * @param length The number of buffers to use.
  91:    * @return The number of bytes put in the output buffers.
  92:    * @throws DataFormatException If decompression fails.
  93:    * @throws IllegalBlockSizeException If the current cipher is a block cipher,
  94:    *  and the input fragment is not a multiple of the block size.
  95:    * @throws MacException If verifying the MAC fails.
  96:    * @throws SSLException ???
  97:    * @throws ShortBufferException
  98:    */
  99:   public int decrypt(Record record, ByteBuffer[] output, int offset, int length)
 100:     throws DataFormatException, IllegalBlockSizeException,
 101:            MacException, SSLException, ShortBufferException
 102:   {
 103:     return decrypt(record, output, offset, length, null);
 104:   }
 105: 
 106:   /**
 107:    * Decrypt a record, storing the decrypted fragment into the given growable
 108:    * buffer.
 109:    *
 110:    * @param record The input record.
 111:    * @param outputStream The output buffer.
 112:    * @return The number of bytes put into the output buffer.
 113:    * @throws DataFormatException
 114:    * @throws IllegalBlockSizeException
 115:    * @throws MacException
 116:    * @throws SSLException
 117:    * @throws ShortBufferException
 118:    */
 119:   public int decrypt(Record record, ByteBufferOutputStream outputStream)
 120:     throws DataFormatException, IllegalBlockSizeException,
 121:            MacException, SSLException, ShortBufferException
 122:   {
 123:     return decrypt(record, null, 0, 0, outputStream);
 124:   }
 125: 
 126:   private int decrypt(Record record, ByteBuffer[] output, int offset, int length,
 127:                       ByteBufferOutputStream outputStream)
 128:     throws DataFormatException, IllegalBlockSizeException,
 129:            MacException, SSLException, ShortBufferException
 130:   {
 131:     boolean badPadding = false;
 132:     ByteBuffer fragment;
 133:     if (cipher != null)
 134:       {
 135:         ByteBuffer input = record.fragment();
 136:         fragment = ByteBuffer.allocate(input.remaining());
 137:         cipher.update(input, fragment);
 138:       }
 139:     else
 140:       fragment = record.fragment();
 141: 
 142:     if (Debug.DEBUG_DECRYPTION)
 143:       logger.logv(Component.SSL_RECORD_LAYER, "decrypted fragment:\n{0}",
 144:                   Util.hexDump((ByteBuffer) fragment.duplicate().position(0), " >> "));
 145: 
 146:     int fragmentLength = record.length();
 147:     int maclen = 0;
 148:     if (mac != null)
 149:       maclen = mac.getMacLength();
 150:     fragmentLength -= maclen;
 151: 
 152:     int padlen = 0;
 153:     int padRemoveLen = 0;
 154:     if (!suite.isStreamCipher ())
 155:       {
 156:         padlen = fragment.get(record.length() - 1) & 0xFF;
 157:         padRemoveLen = padlen + 1;
 158:         if (Debug.DEBUG)
 159:           logger.logv(Component.SSL_RECORD_LAYER, "padlen:{0}", padlen);
 160: 
 161:         if (record.version() == ProtocolVersion.SSL_3)
 162:           {
 163:             // In SSLv3, the padding length must not be larger than
 164:             // the cipher's block size.
 165:             if (padlen > cipher.getBlockSize ())
 166:               badPadding = true;
 167:           }
 168:         else if (record.version().compareTo(ProtocolVersion.TLS_1) >= 0)
 169:           {
 170:             // In TLSv1 and later, the padding must be `padlen' copies of the
 171:             // value `padlen'.
 172:             byte[] pad = new byte[padlen];
 173:             ((ByteBuffer) fragment.duplicate().position(record.length() - padlen - 1)).get(pad);
 174:             for (int i = 0; i < pad.length; i++)
 175:               if ((pad[i] & 0xFF) != padlen)
 176:                 badPadding = true;
 177:             if (Debug.DEBUG)
 178:               logger.logv(Component.SSL_RECORD_LAYER, "TLSv1.x padding\n{0}",
 179:                           new ByteArray(pad));
 180:           }
 181: 
 182:         if (Debug.DEBUG)
 183:           logger.logv(Component.SSL_RECORD_LAYER, "padding bad? {0}",
 184:                       badPadding);
 185:         if (!badPadding)
 186:           fragmentLength = fragmentLength - padRemoveLen;
 187:       }
 188: 
 189:     int ivlen = 0;
 190:     if (session.version.compareTo(ProtocolVersion.TLS_1_1) >= 0
 191:         && !suite.isStreamCipher())
 192:       ivlen = cipher.getBlockSize();
 193: 
 194:     // Compute and check the MAC.
 195:     if (mac != null)
 196:       {
 197:         mac.update((byte) (sequence >>> 56));
 198:         mac.update((byte) (sequence >>> 48));
 199:         mac.update((byte) (sequence >>> 40));
 200:         mac.update((byte) (sequence >>> 32));
 201:         mac.update((byte) (sequence >>> 24));
 202:         mac.update((byte) (sequence >>> 16));
 203:         mac.update((byte) (sequence >>>  8));
 204:         mac.update((byte)  sequence);
 205:         mac.update((byte) record.getContentType().getValue());
 206:         ProtocolVersion version = record.version();
 207:         if (version != ProtocolVersion.SSL_3)
 208:           {
 209:             mac.update((byte) version.major());
 210:             mac.update((byte) version.minor());
 211:           }
 212:         mac.update((byte) ((fragmentLength - ivlen) >>> 8));
 213:         mac.update((byte)  (fragmentLength - ivlen));
 214:         ByteBuffer content =
 215:           (ByteBuffer) fragment.duplicate().position(ivlen).limit(fragmentLength);
 216:         mac.update(content);
 217:         byte[] mac1 = mac.doFinal ();
 218:         byte[] mac2 = new byte[maclen];
 219:         mac.reset();
 220:         ((ByteBuffer) fragment.duplicate().position(fragmentLength)).get(mac2);
 221:         if (Debug.DEBUG)
 222:           logger.logv(Component.SSL_RECORD_LAYER, "mac1:{0} mac2:{1}",
 223:                       Util.toHexString(mac1, ':'), Util.toHexString(mac2, ':'));
 224:         if (!Arrays.equals (mac1, mac2))
 225:           badPadding = true;
 226:       }
 227: 
 228:     // We always say "bad MAC" and not "bad padding," because saying
 229:     // the latter will leak information to an attacker.
 230:     if (badPadding)
 231:       throw new MacException ();
 232: 
 233:     // Inflate the compressed bytes.
 234:     int produced = 0;
 235:     if (inflater != null)
 236:       {
 237:         ByteBufferOutputStream out = new ByteBufferOutputStream(fragmentLength);
 238:         byte[] inbuffer = new byte[1024];
 239:         byte[] outbuffer = new byte[1024];
 240:         boolean done = false;
 241:         if (record.version().compareTo(ProtocolVersion.TLS_1_1) >= 0
 242:             && !suite.isStreamCipher())
 243:           fragment.position (cipher.getBlockSize());
 244:         else
 245:           fragment.position(0);
 246:         fragment.limit(fragmentLength);
 247: 
 248:         while (!done)
 249:           {
 250:             int l;
 251:             if (inflater.needsInput())
 252:               {
 253:                 l = Math.min(inbuffer.length, fragment.remaining());
 254:                 fragment.get(inbuffer, 0, l);
 255:                 inflater.setInput(inbuffer);
 256:               }
 257: 
 258:             l = inflater.inflate(outbuffer);
 259:             out.write(outbuffer, 0, l);
 260:             done = !fragment.hasRemaining() && inflater.finished();
 261:           }
 262: 
 263:         ByteBuffer outbuf = out.buffer();
 264:         if (outputStream != null)
 265:           {
 266:             byte[] buf = new byte[1024];
 267:             while (outbuf.hasRemaining())
 268:               {
 269:                 int l = Math.min(outbuf.remaining(), buf.length);
 270:                 outbuf.get(buf, 0, l);
 271:                 outputStream.write(buf, 0, l);
 272:                 produced += l;
 273:               }
 274:           }
 275:         else
 276:           {
 277:             int i = offset;
 278:             while (outbuf.hasRemaining() && i < offset + length)
 279:               {
 280:                 int l = Math.min(output[i].remaining(), outbuf.remaining());
 281:                 ByteBuffer b = (ByteBuffer)
 282:                   outbuf.duplicate().limit(outbuf.position() + l);
 283:                 output[i++].put(b);
 284:                 outbuf.position(outbuf.position() + l);
 285:                 produced += l;
 286:               }
 287:             if (outbuf.hasRemaining())
 288:               throw new BufferOverflowException();
 289:           }
 290:       }
 291:     else
 292:       {
 293:         ByteBuffer outbuf = (ByteBuffer)
 294:           fragment.duplicate().position(0).limit(record.length() - maclen - padRemoveLen);
 295:         if (record.version().compareTo(ProtocolVersion.TLS_1_1) >= 0
 296:             && !suite.isStreamCipher())
 297:           outbuf.position(cipher.getBlockSize());
 298:         if (outputStream != null)
 299:           {
 300:             byte[] buf = new byte[1024];
 301:             while (outbuf.hasRemaining())
 302:               {
 303:                 int l = Math.min(outbuf.remaining(), buf.length);
 304:                 outbuf.get(buf, 0, l);
 305:                 outputStream.write(buf, 0, l);
 306:                 produced += l;
 307:               }
 308:           }
 309:         else
 310:           {
 311:             int i = offset;
 312:             while (outbuf.hasRemaining() && i < offset + length)
 313:               {
 314:                 int l = Math.min(output[i].remaining(), outbuf.remaining());
 315:                 ByteBuffer b = (ByteBuffer) outbuf.duplicate().limit(outbuf.position() + l);
 316:                 output[i++].put(b);
 317:                 outbuf.position(outbuf.position() + l);
 318:                 produced += l;
 319:               }
 320:             if (outbuf.hasRemaining())
 321:               throw new BufferOverflowException();
 322:           }
 323:       }
 324: 
 325:     sequence++;
 326: 
 327:     return produced;
 328:   }
 329: 
 330:   CipherSuite cipherSuite ()
 331:   {
 332:     return suite;
 333:   }
 334: }