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