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

   1: /* OutputSecurityParameters.java --
   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.ByteBufferOutputStream;
  44: 
  45: import java.nio.ByteBuffer;
  46: 
  47: import java.util.zip.DataFormatException;
  48: import java.util.zip.Deflater;
  49: 
  50: import javax.crypto.Cipher;
  51: import javax.crypto.IllegalBlockSizeException;
  52: import javax.crypto.Mac;
  53: import javax.crypto.ShortBufferException;
  54: 
  55: public class OutputSecurityParameters
  56: {
  57:   private static final SystemLogger logger = SystemLogger.SYSTEM;
  58:   private final Cipher cipher;
  59:   private final Mac mac;
  60:   private final Deflater deflater;
  61:   private final SessionImpl session;
  62:   private final CipherSuite suite;
  63:   private long sequence;
  64: 
  65:   static final boolean enableCBCProtection;
  66: 
  67:   static
  68:   {
  69:     String enabled = Util.getProperty("jsse.enableCBCProtection");
  70:     if (enabled == null)
  71:       enableCBCProtection = true;
  72:     else
  73:       enableCBCProtection = Boolean.valueOf(enabled);
  74:   }
  75: 
  76:   public OutputSecurityParameters (final Cipher cipher, final Mac mac,
  77:                                    final Deflater deflater, SessionImpl session,
  78:                                    CipherSuite suite)
  79:   {
  80:     this.cipher = cipher;
  81:     this.mac = mac;
  82:     this.deflater = deflater;
  83:     this.session = session;
  84:     this.suite = suite;
  85:     sequence = 0;
  86:   }
  87: 
  88:   /**
  89:    * Encrypt a record, storing the result in the given output buffer.
  90:    *
  91:    * @return The number of bytes taken from the input, and the number stored
  92:    * into `output;' that is, the size of the encrypted fragment, plus the
  93:    * encoding for the record.
  94:    */
  95:   public int[] encrypt (final ByteBuffer[] input, int offset, int length,
  96:                         final ContentType contentType, final ByteBuffer output)
  97:     throws DataFormatException, IllegalBlockSizeException, ShortBufferException
  98:   {
  99:     if (offset < 0 || offset >= input.length
 100:         || length <= 0 || offset + length > input.length)
 101:       throw new IndexOutOfBoundsException();
 102: 
 103:     if (Debug.DEBUG)
 104:       for (int i = offset; i < offset+length; i++)
 105:         logger.logv(Component.SSL_RECORD_LAYER, "encrypting record [{0}]: {1}",
 106:                     i-offset, input[i]);
 107: 
 108:     int maclen = 0;
 109:     if (mac != null)
 110:       maclen = session.isTruncatedMac() ? 10 : mac.getMacLength ();
 111: 
 112:     int ivlen = 0;
 113:     byte[] iv = null;
 114:     if (session.version.compareTo(ProtocolVersion.TLS_1_1) >= 0
 115:         && !suite.isStreamCipher())
 116:       {
 117:         ivlen = cipher.getBlockSize();
 118:         iv = new byte[ivlen];
 119:         session.random().nextBytes(iv);
 120:       }
 121: 
 122:     int padaddlen = 0;
 123:     if (!suite.isStreamCipher()
 124:         && session.version.compareTo(ProtocolVersion.TLS_1) >= 0)
 125:       {
 126:         padaddlen = (session.random().nextInt(255 / cipher.getBlockSize())
 127:                      * cipher.getBlockSize());
 128:       }
 129: 
 130:     int fragmentLength = 0;
 131:     ByteBuffer[] fragments = null;
 132:     // Compress the content, if needed.
 133:     if (deflater != null)
 134:       {
 135:         ByteBufferOutputStream deflated = new ByteBufferOutputStream();
 136: 
 137:         byte[] inbuf = new byte[1024];
 138:         byte[] outbuf = new byte[1024];
 139:         int written = 0;
 140: 
 141:         // Here we use the guarantee that the deflater won't increase the
 142:         // output size by more than 1K -- we resign ourselves to only deflate
 143:         // as much data as we have space for *uncompressed*,
 144:         int limit = output.remaining() - (maclen + ivlen + padaddlen) - 1024;
 145: 
 146:         for (int i = offset; i < length && written < limit; i++)
 147:           {
 148:             ByteBuffer in = input[i];
 149:             while (in.hasRemaining() && written < limit)
 150:               {
 151:                 int l = Math.min(in.remaining(), inbuf.length);
 152:                 l = Math.min(limit - written, l);
 153:                 in.get(inbuf, 0, l);
 154:                 deflater.setInput(inbuf, 0, l);
 155:                 l = deflater.deflate(outbuf);
 156:                 deflated.write(outbuf, 0, l);
 157:                 written += l;
 158:               }
 159:           }
 160:         deflater.finish();
 161:         while (!deflater.finished())
 162:           {
 163:             int l = deflater.deflate(outbuf);
 164:             deflated.write(outbuf, 0, l);
 165:             written += l;
 166:           }
 167:         fragments = new ByteBuffer[] { deflated.buffer() };
 168:         fragmentLength = ((int) deflater.getBytesWritten()) + maclen + ivlen;
 169:         deflater.reset();
 170:         offset = 0;
 171:         length = 1;
 172:       }
 173:     else
 174:       {
 175:         int limit = output.remaining() - (maclen + ivlen + padaddlen);
 176:         fragments = input;
 177:         for (int i = offset; i < length && fragmentLength < limit; i++)
 178:           {
 179:             int l = Math.min(limit - fragmentLength, fragments[i].remaining());
 180:             fragmentLength += l;
 181:           }
 182:         fragmentLength += maclen + ivlen;
 183:       }
 184: 
 185:     // Compute padding...
 186:     int padlen = 0;
 187:     byte[] pad = null;
 188:     if (!suite.isStreamCipher())
 189:       {
 190:         int bs = cipher.getBlockSize();
 191:         padlen = bs - (fragmentLength % bs);
 192:         if (Debug.DEBUG)
 193:           logger.logv(Component.SSL_RECORD_LAYER,
 194:                       "framentLen:{0} padlen:{1} blocksize:{2}",
 195:                       fragmentLength, padlen, bs);
 196:         if (session.version.compareTo(ProtocolVersion.TLS_1) >= 0)
 197:           {
 198:             // TLS 1.0 and later uses a random amount of padding, up to
 199:             // 255 bytes. Each byte of the pad is equal to the padding
 200:             // length, minus one.
 201:             padlen += padaddlen;
 202:             while (padlen > 255)
 203:               padlen -= bs;
 204:             pad = new byte[padlen];
 205:             for (int i = 0; i < padlen; i++)
 206:               pad[i] = (byte) (padlen - 1);
 207:           }
 208:         else
 209:           {
 210:             // SSL 3 uses a pad only as large as the block size, but the
 211:             // pad may contain any values.
 212:             pad = new byte[padlen];
 213:             session.random().nextBytes(pad);
 214:             pad[padlen - 1] = (byte) (padlen - 1);
 215:           }
 216:         fragmentLength += pad.length;
 217:       }
 218: 
 219:     // If there is a MAC, compute it.
 220:     byte[] macValue = null;
 221:     if (mac != null)
 222:       {
 223:         mac.update((byte) (sequence >>> 56));
 224:         mac.update((byte) (sequence >>> 48));
 225:         mac.update((byte) (sequence >>> 40));
 226:         mac.update((byte) (sequence >>> 32));
 227:         mac.update((byte) (sequence >>> 24));
 228:         mac.update((byte) (sequence >>> 16));
 229:         mac.update((byte) (sequence >>>  8));
 230:         mac.update((byte)  sequence);
 231:         mac.update((byte) contentType.getValue());
 232:         if (session.version != ProtocolVersion.SSL_3)
 233:           {
 234:             mac.update((byte) session.version.major ());
 235:             mac.update((byte) session.version.minor ());
 236:           }
 237:         int toWrite = fragmentLength - maclen - ivlen - padlen;
 238:         mac.update((byte) (toWrite >>> 8));
 239:         mac.update((byte)  toWrite);
 240:         int written = 0;
 241:         for (int i = offset; i < length && written < toWrite; i++)
 242:           {
 243:             ByteBuffer fragment = fragments[i].duplicate();
 244:             int l = Math.min(fragment.remaining(), toWrite - written);
 245:             fragment.limit(fragment.position() + l);
 246:             mac.update(fragment);
 247:           }
 248:         macValue = mac.doFinal();
 249:       }
 250: 
 251:     Record outrecord = new Record(output);
 252:     outrecord.setContentType(contentType);
 253:     outrecord.setVersion(session.version);
 254:     outrecord.setLength(fragmentLength);
 255: 
 256:     int consumed = 0;
 257:     ByteBuffer outfragment = outrecord.fragment();
 258: 
 259:     if (cipher != null)
 260:       {
 261:         if (iv != null)
 262:           cipher.update(ByteBuffer.wrap(iv), outfragment);
 263:         int toWrite = fragmentLength - maclen - ivlen - padlen;
 264:         for (int i = offset; i < offset + length && consumed < toWrite; i++)
 265:           {
 266:             ByteBuffer fragment = fragments[i].slice();
 267:             int l = Math.min(fragment.remaining(), toWrite - consumed);
 268:             fragment.limit(fragment.position() + l);
 269:             cipher.update(fragment, outfragment);
 270:             fragments[i].position(fragments[i].position() + l);
 271:             consumed += l;
 272:           }
 273:         if (macValue != null)
 274:           cipher.update(ByteBuffer.wrap(macValue), outfragment);
 275:         if (pad != null)
 276:           cipher.update(ByteBuffer.wrap(pad), outfragment);
 277:       }
 278:     else
 279:       {
 280:         // iv and pad are only used if we have a block cipher.
 281:         int toWrite = fragmentLength - maclen;
 282:         for (int i = offset; i < offset + length && consumed < toWrite; i++)
 283:           {
 284:             ByteBuffer fragment = fragments[i];
 285:             int l = Math.min(fragment.remaining(), toWrite - consumed);
 286:             fragment.limit(fragment.position() + l);
 287:             outfragment.put(fragment);
 288:             consumed += l;
 289:           }
 290:         if (macValue != null)
 291:           outfragment.put(macValue);
 292:       }
 293: 
 294:     // Advance the output buffer's position.
 295:     output.position(output.position() + outrecord.length() + 5);
 296:     sequence++;
 297: 
 298:     return new int[] { consumed, fragmentLength + 5 };
 299:   }
 300: 
 301:   CipherSuite suite()
 302:   {
 303:     return suite;
 304:   }
 305: 
 306:   boolean needToSplitPayload()
 307:   {
 308:     return (session.version.compareTo(ProtocolVersion.TLS_1_1) < 0 &&
 309:             suite.isCBCMode() && enableCBCProtection);
 310:   }
 311: 
 312: }