diff options
Diffstat (limited to 'libjava/classpath/gnu/javax/net/ssl/provider/OutputSecurityParameters.java')
-rw-r--r-- | libjava/classpath/gnu/javax/net/ssl/provider/OutputSecurityParameters.java | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/OutputSecurityParameters.java b/libjava/classpath/gnu/javax/net/ssl/provider/OutputSecurityParameters.java new file mode 100644 index 000000000..c6ed7d587 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/OutputSecurityParameters.java @@ -0,0 +1,294 @@ +/* OutputSecurityParameters.java -- + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.net.ssl.provider; + +import gnu.classpath.debug.Component; +import gnu.classpath.debug.SystemLogger; +import gnu.java.security.util.ByteBufferOutputStream; + +import java.nio.ByteBuffer; + +import java.util.zip.DataFormatException; +import java.util.zip.Deflater; + +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.Mac; +import javax.crypto.ShortBufferException; + +public class OutputSecurityParameters +{ + private static final SystemLogger logger = SystemLogger.SYSTEM; + private final Cipher cipher; + private final Mac mac; + private final Deflater deflater; + private final SessionImpl session; + private final CipherSuite suite; + private long sequence; + + public OutputSecurityParameters (final Cipher cipher, final Mac mac, + final Deflater deflater, SessionImpl session, + CipherSuite suite) + { + this.cipher = cipher; + this.mac = mac; + this.deflater = deflater; + this.session = session; + this.suite = suite; + sequence = 0; + } + + /** + * Encrypt a record, storing the result in the given output buffer. + * + * @return The number of bytes taken from the input, and the number stored + * into `output;' that is, the size of the encrypted fragment, plus the + * encoding for the record. + */ + public int[] encrypt (final ByteBuffer[] input, int offset, int length, + final ContentType contentType, final ByteBuffer output) + throws DataFormatException, IllegalBlockSizeException, ShortBufferException + { + if (offset < 0 || offset >= input.length + || length <= 0 || offset + length > input.length) + throw new IndexOutOfBoundsException(); + + if (Debug.DEBUG) + for (int i = offset; i < offset+length; i++) + logger.logv(Component.SSL_RECORD_LAYER, "encrypting record [{0}]: {1}", + i-offset, input[i]); + + int maclen = 0; + if (mac != null) + maclen = session.isTruncatedMac() ? 10 : mac.getMacLength (); + + int ivlen = 0; + byte[] iv = null; + if (session.version.compareTo(ProtocolVersion.TLS_1_1) >= 0 + && !suite.isStreamCipher()) + { + ivlen = cipher.getBlockSize(); + iv = new byte[ivlen]; + session.random().nextBytes(iv); + } + + int padaddlen = 0; + if (!suite.isStreamCipher() + && session.version.compareTo(ProtocolVersion.TLS_1) >= 0) + { + padaddlen = (session.random().nextInt(255 / cipher.getBlockSize()) + * cipher.getBlockSize()); + } + + int fragmentLength = 0; + ByteBuffer[] fragments = null; + // Compress the content, if needed. + if (deflater != null) + { + ByteBufferOutputStream deflated = new ByteBufferOutputStream(); + + byte[] inbuf = new byte[1024]; + byte[] outbuf = new byte[1024]; + int written = 0; + + // Here we use the guarantee that the deflater won't increase the + // output size by more than 1K -- we resign ourselves to only deflate + // as much data as we have space for *uncompressed*, + int limit = output.remaining() - (maclen + ivlen + padaddlen) - 1024; + + for (int i = offset; i < length && written < limit; i++) + { + ByteBuffer in = input[i]; + while (in.hasRemaining() && written < limit) + { + int l = Math.min(in.remaining(), inbuf.length); + l = Math.min(limit - written, l); + in.get(inbuf, 0, l); + deflater.setInput(inbuf, 0, l); + l = deflater.deflate(outbuf); + deflated.write(outbuf, 0, l); + written += l; + } + } + deflater.finish(); + while (!deflater.finished()) + { + int l = deflater.deflate(outbuf); + deflated.write(outbuf, 0, l); + written += l; + } + fragments = new ByteBuffer[] { deflated.buffer() }; + fragmentLength = ((int) deflater.getBytesWritten()) + maclen + ivlen; + deflater.reset(); + offset = 0; + length = 1; + } + else + { + int limit = output.remaining() - (maclen + ivlen + padaddlen); + fragments = input; + for (int i = offset; i < length && fragmentLength < limit; i++) + { + int l = Math.min(limit - fragmentLength, fragments[i].remaining()); + fragmentLength += l; + } + fragmentLength += maclen + ivlen; + } + + // Compute padding... + int padlen = 0; + byte[] pad = null; + if (!suite.isStreamCipher()) + { + int bs = cipher.getBlockSize(); + padlen = bs - (fragmentLength % bs); + if (Debug.DEBUG) + logger.logv(Component.SSL_RECORD_LAYER, + "framentLen:{0} padlen:{1} blocksize:{2}", + fragmentLength, padlen, bs); + if (session.version.compareTo(ProtocolVersion.TLS_1) >= 0) + { + // TLS 1.0 and later uses a random amount of padding, up to + // 255 bytes. Each byte of the pad is equal to the padding + // length, minus one. + padlen += padaddlen; + while (padlen > 255) + padlen -= bs; + pad = new byte[padlen]; + for (int i = 0; i < padlen; i++) + pad[i] = (byte) (padlen - 1); + } + else + { + // SSL 3 uses a pad only as large as the block size, but the + // pad may contain any values. + pad = new byte[padlen]; + session.random().nextBytes(pad); + pad[padlen - 1] = (byte) (padlen - 1); + } + fragmentLength += pad.length; + } + + // If there is a MAC, compute it. + byte[] macValue = null; + if (mac != null) + { + mac.update((byte) (sequence >>> 56)); + mac.update((byte) (sequence >>> 48)); + mac.update((byte) (sequence >>> 40)); + mac.update((byte) (sequence >>> 32)); + mac.update((byte) (sequence >>> 24)); + mac.update((byte) (sequence >>> 16)); + mac.update((byte) (sequence >>> 8)); + mac.update((byte) sequence); + mac.update((byte) contentType.getValue()); + if (session.version != ProtocolVersion.SSL_3) + { + mac.update((byte) session.version.major ()); + mac.update((byte) session.version.minor ()); + } + int toWrite = fragmentLength - maclen - ivlen - padlen; + mac.update((byte) (toWrite >>> 8)); + mac.update((byte) toWrite); + int written = 0; + for (int i = offset; i < length && written < toWrite; i++) + { + ByteBuffer fragment = fragments[i].duplicate(); + int l = Math.min(fragment.remaining(), toWrite - written); + fragment.limit(fragment.position() + l); + mac.update(fragment); + } + macValue = mac.doFinal(); + } + + Record outrecord = new Record(output); + outrecord.setContentType(contentType); + outrecord.setVersion(session.version); + outrecord.setLength(fragmentLength); + + int consumed = 0; + ByteBuffer outfragment = outrecord.fragment(); + + if (cipher != null) + { + if (iv != null) + cipher.update(ByteBuffer.wrap(iv), outfragment); + int toWrite = fragmentLength - maclen - ivlen - padlen; + for (int i = offset; i < offset + length && consumed < toWrite; i++) + { + ByteBuffer fragment = fragments[i].slice(); + int l = Math.min(fragment.remaining(), toWrite - consumed); + fragment.limit(fragment.position() + l); + cipher.update(fragment, outfragment); + fragments[i].position(fragments[i].position() + l); + consumed += l; + } + if (macValue != null) + cipher.update(ByteBuffer.wrap(macValue), outfragment); + if (pad != null) + cipher.update(ByteBuffer.wrap(pad), outfragment); + } + else + { + // iv and pad are only used if we have a block cipher. + int toWrite = fragmentLength - maclen; + for (int i = offset; i < offset + length && consumed < toWrite; i++) + { + ByteBuffer fragment = fragments[i]; + int l = Math.min(fragment.remaining(), toWrite - consumed); + fragment.limit(fragment.position() + l); + outfragment.put(fragment); + consumed += l; + } + if (macValue != null) + outfragment.put(macValue); + } + + // Advance the output buffer's position. + output.position(output.position() + outrecord.length() + 5); + sequence++; + + return new int[] { consumed, fragmentLength + 5 }; + } + + CipherSuite suite() + { + return suite; + } +} |