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