summaryrefslogtreecommitdiff
path: root/gnu/javax/net/ssl/provider/InputSecurityParameters.java
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/javax/net/ssl/provider/InputSecurityParameters.java')
-rw-r--r--gnu/javax/net/ssl/provider/InputSecurityParameters.java336
1 files changed, 336 insertions, 0 deletions
diff --git a/gnu/javax/net/ssl/provider/InputSecurityParameters.java b/gnu/javax/net/ssl/provider/InputSecurityParameters.java
new file mode 100644
index 000000000..13a3ef814
--- /dev/null
+++ b/gnu/javax/net/ssl/provider/InputSecurityParameters.java
@@ -0,0 +1,336 @@
+/* SecurityParameters.java -- SSL security parameters.
+ 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.ByteArray;
+import gnu.java.security.util.ByteBufferOutputStream;
+
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+
+import java.util.Arrays;
+import java.util.logging.Level;
+import java.util.zip.DataFormatException;
+import java.util.zip.Inflater;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.Mac;
+import javax.crypto.ShortBufferException;
+
+import javax.net.ssl.SSLException;
+
+public class InputSecurityParameters
+{
+ private static final SystemLogger logger = SystemLogger.SYSTEM;
+ private final Cipher cipher;
+ private final Mac mac;
+ private final Inflater inflater;
+ private SessionImpl session;
+ private final CipherSuite suite;
+ private long sequence;
+
+ public InputSecurityParameters (final Cipher cipher, final Mac mac,
+ final Inflater inflater,
+ final SessionImpl session,
+ final CipherSuite suite)
+ {
+ this.cipher = cipher;
+ this.mac = mac;
+ this.inflater = inflater;
+ this.session = session;
+ this.suite = suite;
+ sequence = 0;
+ }
+
+ /**
+ * Decrypt a record, storing the decrypted fragment into the given array
+ * of byte buffers.
+ *
+ * @param record The input record.
+ * @param output The output buffers.
+ * @param offset The offset of the first buffer to use.
+ * @param length The number of buffers to use.
+ * @return The number of bytes put in the output buffers.
+ * @throws DataFormatException If decompression fails.
+ * @throws IllegalBlockSizeException If the current cipher is a block cipher,
+ * and the input fragment is not a multiple of the block size.
+ * @throws MacException If verifying the MAC fails.
+ * @throws SSLException ???
+ * @throws ShortBufferException
+ */
+ public int decrypt(Record record, ByteBuffer[] output, int offset, int length)
+ throws DataFormatException, IllegalBlockSizeException,
+ MacException, SSLException, ShortBufferException
+ {
+ return decrypt(record, output, offset, length, null);
+ }
+
+ /**
+ * Decrypt a record, storing the decrypted fragment into the given growable
+ * buffer.
+ *
+ * @param record The input record.
+ * @param outputStream The output buffer.
+ * @return The number of bytes put into the output buffer.
+ * @throws DataFormatException
+ * @throws IllegalBlockSizeException
+ * @throws MacException
+ * @throws SSLException
+ * @throws ShortBufferException
+ */
+ public int decrypt(Record record, ByteBufferOutputStream outputStream)
+ throws DataFormatException, IllegalBlockSizeException,
+ MacException, SSLException, ShortBufferException
+ {
+ return decrypt(record, null, 0, 0, outputStream);
+ }
+
+ private int decrypt(Record record, ByteBuffer[] output, int offset, int length,
+ ByteBufferOutputStream outputStream)
+ throws DataFormatException, IllegalBlockSizeException,
+ MacException, SSLException, ShortBufferException
+ {
+ boolean badPadding = false;
+ ByteBuffer fragment;
+ if (cipher != null)
+ {
+ ByteBuffer input = record.fragment();
+ fragment = ByteBuffer.allocate(input.remaining());
+ cipher.update(input, fragment);
+ }
+ else
+ fragment = record.fragment();
+
+ if (Debug.DEBUG_DECRYPTION)
+ logger.logv(Component.SSL_RECORD_LAYER, "decrypted fragment:\n{0}",
+ Util.hexDump((ByteBuffer) fragment.duplicate().position(0), " >> "));
+
+ int fragmentLength = record.length();
+ int maclen = 0;
+ if (mac != null)
+ maclen = mac.getMacLength();
+ fragmentLength -= maclen;
+
+ int padlen = 0;
+ int padRemoveLen = 0;
+ if (!suite.isStreamCipher ())
+ {
+ padlen = fragment.get(record.length() - 1) & 0xFF;
+ padRemoveLen = padlen + 1;
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_RECORD_LAYER, "padlen:{0}", padlen);
+
+ if (record.version() == ProtocolVersion.SSL_3)
+ {
+ // In SSLv3, the padding length must not be larger than
+ // the cipher's block size.
+ if (padlen > cipher.getBlockSize ())
+ badPadding = true;
+ }
+ else if (record.version().compareTo(ProtocolVersion.TLS_1) >= 0)
+ {
+ // In TLSv1 and later, the padding must be `padlen' copies of the
+ // value `padlen'.
+ byte[] pad = new byte[padlen];
+ ((ByteBuffer) fragment.duplicate().position(record.length() - padlen - 1)).get(pad);
+ for (int i = 0; i < pad.length; i++)
+ if ((pad[i] & 0xFF) != padlen)
+ badPadding = true;
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_RECORD_LAYER, "TLSv1.x padding\n{0}",
+ new ByteArray(pad));
+ }
+
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_RECORD_LAYER, "padding bad? {0}",
+ badPadding);
+ if (!badPadding)
+ fragmentLength = fragmentLength - padRemoveLen;
+ }
+
+ int ivlen = 0;
+ if (session.version.compareTo(ProtocolVersion.TLS_1_1) >= 0
+ && !suite.isStreamCipher())
+ ivlen = cipher.getBlockSize();
+
+ // Compute and check the MAC.
+ 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) record.getContentType().getValue());
+ ProtocolVersion version = record.version();
+ if (version != ProtocolVersion.SSL_3)
+ {
+ mac.update((byte) version.major());
+ mac.update((byte) version.minor());
+ }
+ mac.update((byte) ((fragmentLength - ivlen) >>> 8));
+ mac.update((byte) (fragmentLength - ivlen));
+ ByteBuffer content =
+ (ByteBuffer) fragment.duplicate().position(ivlen).limit(fragmentLength);
+ mac.update(content);
+ byte[] mac1 = mac.doFinal ();
+ byte[] mac2 = new byte[maclen];
+ mac.reset();
+ ((ByteBuffer) fragment.duplicate().position(fragmentLength)).get(mac2);
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_RECORD_LAYER, "mac1:{0} mac2:{1}",
+ Util.toHexString(mac1, ':'), Util.toHexString(mac2, ':'));
+ if (!Arrays.equals (mac1, mac2))
+ badPadding = true;
+ }
+
+ // We always say "bad MAC" and not "bad padding," because saying
+ // the latter will leak information to an attacker.
+ if (badPadding)
+ throw new MacException ();
+
+ // Inflate the compressed bytes.
+ int produced = 0;
+ if (inflater != null)
+ {
+ ByteBufferOutputStream out = new ByteBufferOutputStream(fragmentLength);
+ byte[] inbuffer = new byte[1024];
+ byte[] outbuffer = new byte[1024];
+ boolean done = false;
+ if (record.version().compareTo(ProtocolVersion.TLS_1_1) >= 0
+ && !suite.isStreamCipher())
+ fragment.position (cipher.getBlockSize());
+ else
+ fragment.position(0);
+ fragment.limit(fragmentLength);
+
+ while (!done)
+ {
+ int l;
+ if (inflater.needsInput())
+ {
+ l = Math.min(inbuffer.length, fragment.remaining());
+ fragment.get(inbuffer, 0, l);
+ inflater.setInput(inbuffer);
+ }
+
+ l = inflater.inflate(outbuffer);
+ out.write(outbuffer, 0, l);
+ done = !fragment.hasRemaining() && inflater.finished();
+ }
+
+ ByteBuffer outbuf = out.buffer();
+ if (outputStream != null)
+ {
+ byte[] buf = new byte[1024];
+ while (outbuf.hasRemaining())
+ {
+ int l = Math.min(outbuf.remaining(), buf.length);
+ outbuf.get(buf, 0, l);
+ outputStream.write(buf, 0, l);
+ produced += l;
+ }
+ }
+ else
+ {
+ int i = offset;
+ while (outbuf.hasRemaining() && i < offset + length)
+ {
+ int l = Math.min(output[i].remaining(), outbuf.remaining());
+ ByteBuffer b = (ByteBuffer)
+ outbuf.duplicate().limit(outbuf.position() + l);
+ output[i++].put(b);
+ outbuf.position(outbuf.position() + l);
+ produced += l;
+ }
+ if (outbuf.hasRemaining())
+ throw new BufferOverflowException();
+ }
+ }
+ else
+ {
+ ByteBuffer outbuf = (ByteBuffer)
+ fragment.duplicate().position(0).limit(record.length() - maclen - padRemoveLen);
+ if (record.version().compareTo(ProtocolVersion.TLS_1_1) >= 0
+ && !suite.isStreamCipher())
+ outbuf.position(cipher.getBlockSize());
+ if (outputStream != null)
+ {
+ byte[] buf = new byte[1024];
+ while (outbuf.hasRemaining())
+ {
+ int l = Math.min(outbuf.remaining(), buf.length);
+ outbuf.get(buf, 0, l);
+ outputStream.write(buf, 0, l);
+ produced += l;
+ }
+ }
+ else
+ {
+ int i = offset;
+ while (outbuf.hasRemaining() && i < offset + length)
+ {
+ int l = Math.min(output[i].remaining(), outbuf.remaining());
+ ByteBuffer b = (ByteBuffer) outbuf.duplicate().limit(outbuf.position() + l);
+ output[i++].put(b);
+ outbuf.position(outbuf.position() + l);
+ produced += l;
+ }
+ if (outbuf.hasRemaining())
+ throw new BufferOverflowException();
+ }
+ }
+
+ sequence++;
+
+ return produced;
+ }
+
+ CipherSuite cipherSuite ()
+ {
+ return suite;
+ }
+}