summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCasey Marshall <csm@gnu.org>2006-07-09 21:27:32 +0000
committerCasey Marshall <csm@gnu.org>2006-07-09 21:27:32 +0000
commit2509f39f9dfbc80d489e0c1e52391907a630b54b (patch)
tree648885828e0c8a4957f56e5a0473bc4014757894
parent2b5879e9a2e0ac0ebc96dbb747b3d1398676133e (diff)
downloadclasspath-2509f39f9dfbc80d489e0c1e52391907a630b54b.tar.gz
2006-07-09 Casey Marshall <csm@gnu.org>
* gnu/javax/net/ssl/AbstractSessionContext.java (newInstance): return `AbstractSessionContext.' (getSession): check if the session is null. * gnu/javax/net/ssl/Session.java (packetBufferSize): removed. (<init>): initialize `applicationBufferSize.' (getPacketBufferSize): return application buffer size, plus 2048. * gnu/javax/net/ssl/provider/AbstractHandshake.java (PAD1, PAD2): new constants. (handleInput): implement; call `implHandleOutput,' and hash messages as they are consumed. (implHandleInput): new abstract method. (handleOutput): fix hashing of produced bytes. (status, handleV2Hello): new abstract methods. (pollHandshake): don't hash the input here; add logging. (hasMessage): add logging. (reallocateBuffer): shift the existing contents down in the buffer, if it is, on the whole, large enough for new input. (genV2CertificateVerify): renamed... (genV3CertificateVerify): to this, which is correct. (generateKeys): fix PRF setup; generate an IV for 1.1; add logging. (generateFinished): add logging; update with correct padding. (generateMasterSecret): add logging; fix PRF initialization. * gnu/javax/net/ssl/provider/CipherSuite.java (mac): use mac algorithm name "HMac-SHA1". * gnu/javax/net/ssl/provider/ClientHello.java: make extendable. * gnu/javax/net/ssl/provider/ClientHelloBuilder.java: new file. * gnu/javax/net/ssl/provider/ClientHelloV2.java (<init>): order the input buffer BIG_ENDIAN. (cipherSpecs): made public; use qualified return type. * gnu/javax/net/ssl/provider/Debug.java: new file. * gnu/javax/net/ssl/provider/Extension.java (<init>): order the input buffer BIG_ENDIAN. (length): return the total length, including the length field. (toString): add prefix to value. * gnu/javax/net/ssl/provider/ExtensionList.java (<init>): order the input buffer BIG_ENDIAN. * gnu/javax/net/ssl/provider/InputSecurityParameters.java (logger): new constant. (suite): new field. (<init>): also take a `CipherSuite' argument. (decrypt): use `update,' not `doFinal' for decryption; add debug logging; fix mac computation; fix copying fragment to output. (cipherSuite): return `suite' field. * gnu/javax/net/ssl/provider/Jessie.java (<init>): add "TLSv1.1-RSA" signature. * gnu/javax/net/ssl/provider/OutputSecurityParameters.java (logger): new constant. (suite): new field. (<init>): take additional `CipherSuite' argument. (encrypt): add debug logging; fix mac computation; various little fixes. (suite): new method. * gnu/javax/net/ssl/provider/ProtocolVersion.java (forName): also recognize "TLSv1.1". * gnu/javax/net/ssl/provider/Random.java (copy): fix copying the internal buffer. * gnu/javax/net/ssl/provider/Record.java (<init>): order the input buffer BIG_ENDIAN. (toString): include length in output. * gnu/javax/net/ssl/provider/SSLContextImpl.java (serverContext, clientContext): declare both as `AbstractSessionContext.' * gnu/javax/net/ssl/provider/SSLEngineImpl.java (logger): make an instance of `SystemLogger.' (mode): declare as a Mode. (Mode): new enum. (<init>): add logging; initialize `enabledProtocols' and `enabledSuites.' (beginHandshake): debug logging; handle Mode enum. (closeOutbound): prepare `lastAlert' to carry the close alert. (isInboundDone, isOutboundDone): implement. (setUseClientMode): use Mode enum. (unwrap): fix V2 hello handling; optimize calls when the cipher suite is TLS_NULL_WITH_NULL_NULL; add debug logging; handle closue alerts properly; fix record length reporting. (wrap): set `outClosed' if we are sending a closure alert here; delay changing output security params until we emit the change notification; optimize initial handshake; fix input buffer consumption; handle end of handshake. * gnu/javax/net/ssl/provider/SSLRSASignatureImpl.java: new file. * gnu/javax/net/ssl/provider/ServerDHParams.java (buffer): set position to 0 in the buffer we return. * gnu/javax/net/ssl/provider/ServerHandshake.java (version, suite): removed. (chooseSuite): make non-static; only choose a cipher suite that we have a compatible certificate for. (chooseCompression): use properties to enable/disable zlib. (doHash): say no if we are handling a V2 hello. (handleInput): rename to... (implHandleInput): this; only handle a single handshake message in this method (handleInput from the superclass will call us repeatedly to drain the input buffer); various other fixes. (implHandleOutput): debug logging; temporarily disable packing more than one handshake per record; various little fixes. (status, handleV2Hello): new methods. (genDiffieHellman): use static parameters from the DiffieHellman class. (signParams): use correct signature algorithm. * gnu/javax/net/ssl/provider/ServerHello.java (totalLength): removed. (disableExtensions): new field. (length): don't query extensions if `disableExtensions' is true. * gnu/javax/net/ssl/provider/ServerHelloBuilder.java (setDisableExtensions): new method. * gnu/javax/net/ssl/provider/ServerNameList.java: various parsing fixes. * gnu/javax/net/ssl/provider/SessionImpl.java (<init>): new constructor. (setApplicationBufferSize): new method. (setPacketBufferSize): new method. * gnu/javax/net/ssl/provider/SignatureAlgorithm.java (getAlgorithm): new method. * gnu/javax/net/ssl/provider/Util.java: make public; mark security-sensitive methods deprecated. * gnu/javax/net/ssl/provider/X509KeyManagerFactory.java (chooseAliases): handle DSA; handle unrecognized signature algorithms.
-rw-r--r--ChangeLog-ssl-nio122
-rw-r--r--gnu/javax/net/ssl/AbstractSessionContext.java5
-rw-r--r--gnu/javax/net/ssl/Session.java5
-rw-r--r--gnu/javax/net/ssl/provider/AbstractHandshake.java232
-rw-r--r--gnu/javax/net/ssl/provider/CipherSuite.java2
-rw-r--r--gnu/javax/net/ssl/provider/ClientHello.java56
-rw-r--r--gnu/javax/net/ssl/provider/ClientHelloBuilder.java132
-rw-r--r--gnu/javax/net/ssl/provider/ClientHelloV2.java5
-rw-r--r--gnu/javax/net/ssl/provider/Debug.java66
-rw-r--r--gnu/javax/net/ssl/provider/Extension.java10
-rw-r--r--gnu/javax/net/ssl/provider/ExtensionList.java3
-rw-r--r--gnu/javax/net/ssl/provider/InputSecurityParameters.java75
-rw-r--r--gnu/javax/net/ssl/provider/Jessie.java4
-rw-r--r--gnu/javax/net/ssl/provider/OutputSecurityParameters.java74
-rw-r--r--gnu/javax/net/ssl/provider/ProtocolVersion.java3
-rw-r--r--gnu/javax/net/ssl/provider/Random.java2
-rw-r--r--gnu/javax/net/ssl/provider/Record.java8
-rw-r--r--gnu/javax/net/ssl/provider/SSLContextImpl.java4
-rw-r--r--gnu/javax/net/ssl/provider/SSLEngineImpl.java350
-rw-r--r--gnu/javax/net/ssl/provider/SSLRSASignatureImpl.java233
-rw-r--r--gnu/javax/net/ssl/provider/ServerDHParams.java2
-rw-r--r--gnu/javax/net/ssl/provider/ServerHandshake.java428
-rw-r--r--gnu/javax/net/ssl/provider/ServerHello.java9
-rw-r--r--gnu/javax/net/ssl/provider/ServerHelloBuilder.java5
-rw-r--r--gnu/javax/net/ssl/provider/ServerNameList.java9
-rw-r--r--gnu/javax/net/ssl/provider/SessionImpl.java16
-rw-r--r--gnu/javax/net/ssl/provider/SignatureAlgorithm.java20
-rw-r--r--gnu/javax/net/ssl/provider/Util.java69
-rw-r--r--gnu/javax/net/ssl/provider/X509KeyManagerFactory.java11
29 files changed, 1482 insertions, 478 deletions
diff --git a/ChangeLog-ssl-nio b/ChangeLog-ssl-nio
index e89bdc39c..a3d6771f9 100644
--- a/ChangeLog-ssl-nio
+++ b/ChangeLog-ssl-nio
@@ -1,8 +1,128 @@
+2006-07-09 Casey Marshall <csm@gnu.org>
+
+ * gnu/javax/net/ssl/AbstractSessionContext.java (newInstance):
+ return `AbstractSessionContext.'
+ (getSession): check if the session is null.
+ * gnu/javax/net/ssl/Session.java (packetBufferSize): removed.
+ (<init>): initialize `applicationBufferSize.'
+ (getPacketBufferSize): return application buffer size, plus 2048.
+ * gnu/javax/net/ssl/provider/AbstractHandshake.java (PAD1, PAD2):
+ new constants.
+ (handleInput): implement; call `implHandleOutput,' and hash
+ messages as they are consumed.
+ (implHandleInput): new abstract method.
+ (handleOutput): fix hashing of produced bytes.
+ (status, handleV2Hello): new abstract methods.
+ (pollHandshake): don't hash the input here; add logging.
+ (hasMessage): add logging.
+ (reallocateBuffer): shift the existing contents down in the
+ buffer, if it is, on the whole, large enough for new input.
+ (genV2CertificateVerify): renamed...
+ (genV3CertificateVerify): to this, which is correct.
+ (generateKeys): fix PRF setup; generate an IV for 1.1; add
+ logging.
+ (generateFinished): add logging; update with correct padding.
+ (generateMasterSecret): add logging; fix PRF initialization.
+ * gnu/javax/net/ssl/provider/CipherSuite.java (mac): use mac
+ algorithm name "HMac-SHA1".
+ * gnu/javax/net/ssl/provider/ClientHello.java: make extendable.
+ * gnu/javax/net/ssl/provider/ClientHelloBuilder.java: new file.
+ * gnu/javax/net/ssl/provider/ClientHelloV2.java (<init>): order
+ the input buffer BIG_ENDIAN.
+ (cipherSpecs): made public; use qualified return type.
+ * gnu/javax/net/ssl/provider/Debug.java: new file.
+ * gnu/javax/net/ssl/provider/Extension.java (<init>): order the
+ input buffer BIG_ENDIAN.
+ (length): return the total length, including the length field.
+ (toString): add prefix to value.
+ * gnu/javax/net/ssl/provider/ExtensionList.java (<init>): order
+ the input buffer BIG_ENDIAN.
+ * gnu/javax/net/ssl/provider/InputSecurityParameters.java
+ (logger): new constant.
+ (suite): new field.
+ (<init>): also take a `CipherSuite' argument.
+ (decrypt): use `update,' not `doFinal' for decryption; add debug
+ logging; fix mac computation; fix copying fragment to output.
+ (cipherSuite): return `suite' field.
+ * gnu/javax/net/ssl/provider/Jessie.java (<init>): add
+ "TLSv1.1-RSA" signature.
+ * gnu/javax/net/ssl/provider/OutputSecurityParameters.java
+ (logger): new constant.
+ (suite): new field.
+ (<init>): take additional `CipherSuite' argument.
+ (encrypt): add debug logging; fix mac computation; various little
+ fixes.
+ (suite): new method.
+ * gnu/javax/net/ssl/provider/ProtocolVersion.java (forName): also
+ recognize "TLSv1.1".
+ * gnu/javax/net/ssl/provider/Random.java (copy): fix copying the
+ internal buffer.
+ * gnu/javax/net/ssl/provider/Record.java (<init>): order the input
+ buffer BIG_ENDIAN.
+ (toString): include length in output.
+ * gnu/javax/net/ssl/provider/SSLContextImpl.java (serverContext,
+ clientContext): declare both as `AbstractSessionContext.'
+ * gnu/javax/net/ssl/provider/SSLEngineImpl.java (logger): make an
+ instance of `SystemLogger.'
+ (mode): declare as a Mode.
+ (Mode): new enum.
+ (<init>): add logging; initialize `enabledProtocols' and
+ `enabledSuites.'
+ (beginHandshake): debug logging; handle Mode enum.
+ (closeOutbound): prepare `lastAlert' to carry the close alert.
+ (isInboundDone, isOutboundDone): implement.
+ (setUseClientMode): use Mode enum.
+ (unwrap): fix V2 hello handling; optimize calls when the cipher
+ suite is TLS_NULL_WITH_NULL_NULL; add debug logging; handle closue
+ alerts properly; fix record length reporting.
+ (wrap): set `outClosed' if we are sending a closure alert here;
+ delay changing output security params until we emit the change
+ notification; optimize initial handshake; fix input buffer
+ consumption; handle end of handshake.
+ * gnu/javax/net/ssl/provider/SSLRSASignatureImpl.java: new file.
+ * gnu/javax/net/ssl/provider/ServerDHParams.java (buffer): set
+ position to 0 in the buffer we return.
+ * gnu/javax/net/ssl/provider/ServerHandshake.java (version,
+ suite): removed.
+ (chooseSuite): make non-static; only choose a cipher suite that we
+ have a compatible certificate for.
+ (chooseCompression): use properties to enable/disable zlib.
+ (doHash): say no if we are handling a V2 hello.
+ (handleInput): rename to...
+ (implHandleInput): this; only handle a single handshake message in
+ this method (handleInput from the superclass will call us
+ repeatedly to drain the input buffer); various other fixes.
+ (implHandleOutput): debug logging; temporarily disable packing
+ more than one handshake per record; various little fixes.
+ (status, handleV2Hello): new methods.
+ (genDiffieHellman): use static parameters from the DiffieHellman
+ class.
+ (signParams): use correct signature algorithm.
+ * gnu/javax/net/ssl/provider/ServerHello.java (totalLength):
+ removed.
+ (disableExtensions): new field.
+ (length): don't query extensions if `disableExtensions' is true.
+ * gnu/javax/net/ssl/provider/ServerHelloBuilder.java
+ (setDisableExtensions): new method.
+ * gnu/javax/net/ssl/provider/ServerNameList.java: various parsing
+ fixes.
+ * gnu/javax/net/ssl/provider/SessionImpl.java (<init>): new
+ constructor.
+ (setApplicationBufferSize): new method.
+ (setPacketBufferSize): new method.
+ * gnu/javax/net/ssl/provider/SignatureAlgorithm.java
+ (getAlgorithm): new method.
+ * gnu/javax/net/ssl/provider/Util.java: make public; mark
+ security-sensitive methods deprecated.
+ * gnu/javax/net/ssl/provider/X509KeyManagerFactory.java
+ (chooseAliases): handle DSA; handle unrecognized signature
+ algorithms.
+
2006-06-28 Casey Marshall <csm@gnu.org>
* jessie-tests/testCertificate.java: update for Builder
interface and API changes.
- * jessie-tests/testServerHello.java: likewise.
+ * jesasie-tests/testServerHello.java: likewise.
* jessie-tests/testServerKeyExchange.java: likewise.
2006-06-28 Casey Marshall <csm@gnu.org>
diff --git a/gnu/javax/net/ssl/AbstractSessionContext.java b/gnu/javax/net/ssl/AbstractSessionContext.java
index 590d49a7f..916fec089 100644
--- a/gnu/javax/net/ssl/AbstractSessionContext.java
+++ b/gnu/javax/net/ssl/AbstractSessionContext.java
@@ -105,7 +105,7 @@ public abstract class AbstractSessionContext implements SSLSessionContext
* @return The new session context.
* @throws SSLException If an error occurs in creating the instance.
*/
- public static SSLSessionContext newInstance () throws SSLException
+ public static AbstractSessionContext newInstance () throws SSLException
{
try
{
@@ -173,7 +173,8 @@ public abstract class AbstractSessionContext implements SSLSessionContext
public final SSLSession getSession (byte[] sessionId)
{
Session s = implGet (sessionId);
- if (System.currentTimeMillis () - s.getLastAccessedTime () > timeout)
+ if (s != null
+ && System.currentTimeMillis () - s.getLastAccessedTime () > timeout)
{
remove (sessionId);
return null;
diff --git a/gnu/javax/net/ssl/Session.java b/gnu/javax/net/ssl/Session.java
index e5e2700f6..e2b21aa1e 100644
--- a/gnu/javax/net/ssl/Session.java
+++ b/gnu/javax/net/ssl/Session.java
@@ -68,8 +68,6 @@ public abstract class Session implements SSLSession, Serializable
protected long lastAccessedTime;
protected int applicationBufferSize;
- // Default to 2^14 + 5 -- the maximum size for a record.
- protected int packetBufferSize = 16389;
protected ID sessionId;
protected Certificate[] localCerts;
protected Certificate[] peerCerts;
@@ -87,6 +85,7 @@ public abstract class Session implements SSLSession, Serializable
{
creationTime = System.currentTimeMillis();
values = new HashMap<String, Object>();
+ applicationBufferSize = (1 << 14);
}
public void access()
@@ -143,7 +142,7 @@ public abstract class Session implements SSLSession, Serializable
public int getPacketBufferSize()
{
- return packetBufferSize;
+ return applicationBufferSize + 2048;
}
public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException
diff --git a/gnu/javax/net/ssl/provider/AbstractHandshake.java b/gnu/javax/net/ssl/provider/AbstractHandshake.java
index 336d26160..6166bbde2 100644
--- a/gnu/javax/net/ssl/provider/AbstractHandshake.java
+++ b/gnu/javax/net/ssl/provider/AbstractHandshake.java
@@ -51,6 +51,7 @@ import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.logging.Logger;
@@ -104,18 +105,36 @@ public abstract class AbstractHandshake
};
/**
- * SSL 3.0
+ * SSL 3.0; the string "CLNT"
*/
private static final byte[] SENDER_CLIENT
= new byte[] { 0x43, 0x4C, 0x4E, 0x54 };
/**
- * SSL 3.0
+ * SSL 3.0; the string "SRVR"
*/
private static final byte[] SENDER_SERVER
= new byte[] { 0x53, 0x52, 0x56, 0x52 };
/**
+ * SSL 3.0; the value 0x36 40 (for SHA-1 hashes) or 48 (for MD5 hashes)
+ * times.
+ */
+ private static final byte[] PAD1 = new byte[48];
+
+ /**
+ * SSL 3.0; the value 0x5c 40 (for SHA-1 hashes) or 48 (for MD5 hashes)
+ * times.
+ */
+ private static final byte[] PAD2 = new byte[48];
+
+ static
+ {
+ Arrays.fill(PAD1, SSLHMac.PAD1);
+ Arrays.fill(PAD2, SSLHMac.PAD2);
+ }
+
+ /**
* The currently-read handshake messages. There may be zero, or
* multiple, handshake messages in this buffer.
*/
@@ -149,10 +168,55 @@ public abstract class AbstractHandshake
* output or temporary storage.
* @return An {@link SSLEngineResult} describing the result.
*/
- public abstract SSLEngineResult.HandshakeStatus handleInput (ByteBuffer fragment)
- throws SSLException;
+ public final HandshakeStatus handleInput (ByteBuffer fragment)
+ throws SSLException
+ {
+ HandshakeStatus status = status();
+ if (status != HandshakeStatus.NEED_UNWRAP)
+ return status;
+
+ // Try to read another...
+ if (!pollHandshake(fragment))
+ return HandshakeStatus.NEED_UNWRAP;
+
+ while (hasMessage() && status == HandshakeStatus.NEED_UNWRAP)
+ {
+ int pos = handshakeOffset;
+ status = implHandleInput();
+ int len = handshakeOffset - pos;
+ if (len == 0)
+ {
+ // Don't bother; the impl is just telling us to go around
+ // again.
+ continue;
+ }
+ if (doHash())
+ {
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_HANDSHAKE, "hashing output\n{0}",
+ Util.hexDump((ByteBuffer) handshakeBuffer
+ .duplicate().position(pos)
+ .limit(pos+len), " >> "));
+ sha.update((ByteBuffer) handshakeBuffer.duplicate()
+ .position(pos).limit(pos+len));
+ md5.update((ByteBuffer) handshakeBuffer.duplicate()
+ .position(pos).limit(pos+len));
+ }
+ }
+ return status;
+ }
/**
+ * Called to process more handshake data. This method will be called
+ * repeatedly while there is remaining handshake data, and while the
+ * status is
+ * @return
+ * @throws SSLException
+ */
+ protected abstract HandshakeStatus implHandleInput()
+ throws SSLException;
+
+ /**
* Produce more handshake output. This is called in response to a
* call to {@link javax.net.ssl.SSLEngine#wrap}, when the handshake
* is still in progress.
@@ -166,20 +230,27 @@ public abstract class AbstractHandshake
public final SSLEngineResult.HandshakeStatus handleOutput (ByteBuffer fragment)
throws SSLException
{
+ int orig = fragment.position();
SSLEngineResult.HandshakeStatus status = implHandleOutput(fragment);
if (doHash())
{
- sha.update((ByteBuffer) fragment.duplicate().flip());
- md5.update((ByteBuffer) fragment.duplicate().flip());
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_HANDSHAKE, "hashing output:\n{0}",
+ Util.hexDump((ByteBuffer) fragment.duplicate().flip().position(orig), " >> "));
+ sha.update((ByteBuffer) fragment.duplicate().flip().position(orig));
+ md5.update((ByteBuffer) fragment.duplicate().flip().position(orig));
}
return status;
}
/**
- * Called to implement the underlying
- * @param record
- * @return
- * @throws SSLException
+ * Called to implement the underlying output handling. The callee should
+ * attempt to fill the given buffer as much as it can; this can include
+ * multiple, and even partial, handshake messages.
+ *
+ * @param fragment The buffer the callee should write handshake messages to.
+ * @return The new status of the handshake.
+ * @throws SSLException If an error occurs processing the output message.
*/
protected abstract SSLEngineResult.HandshakeStatus implHandleOutput (ByteBuffer fragment)
throws SSLException;
@@ -208,6 +279,23 @@ public abstract class AbstractHandshake
abstract OutputSecurityParameters getOutputParams() throws SSLException;
/**
+ * Used by the skeletal code to query the current status of the handshake.
+ * This <em>should</em> be the same value as returned by the previous call
+ * to {@link #implHandleOutput(ByteBuffer)} or {@link
+ * #implHandleInput(ByteBuffer)}.
+ *
+ * @return The current handshake status.
+ */
+ abstract SSLEngineResult.HandshakeStatus status();
+
+ /**
+ * Handle an SSLv2 client hello. This is only used by SSL servers.
+ *
+ * @param hello The hello message.
+ */
+ abstract void handleV2Hello(ByteBuffer hello) throws SSLException;
+
+ /**
* Attempt to read the next handshake message from the given
* record. If only a partial handshake message is available, then
* this method saves the incoming bytes and returns false. If a
@@ -221,29 +309,28 @@ public abstract class AbstractHandshake
*/
protected boolean pollHandshake (final ByteBuffer fragment)
{
- Record record = new Record(fragment);
// Allocate space for the new fragment.
- if (handshakeBuffer == null || handshakeBuffer.remaining () < record.length ())
+ if (handshakeBuffer == null
+ || handshakeBuffer.remaining() < fragment.remaining())
{
// We need space for anything still unread in the handshake
// buffer...
int len = ((handshakeBuffer == null) ? 0
- : handshakeBuffer.position () - handshakeOffset);
+ : handshakeBuffer.position() - handshakeOffset);
// Plus room for the incoming record.
len += fragment.remaining();
- reallocateBuffer (len);
+ reallocateBuffer(len);
}
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_HANDSHAKE, "inserting {0} into {1}",
+ fragment, handshakeBuffer);
+
// Put the fragment into the buffer.
- if (doHash())
- {
- sha.update(fragment);
- md5.update(fragment);
- }
- handshakeBuffer.put (fragment);
+ handshakeBuffer.put(fragment);
- return hasMessage ();
+ return hasMessage();
}
protected boolean doHash()
@@ -255,15 +342,23 @@ public abstract class AbstractHandshake
* Tell if the handshake buffer currently has a full handshake
* message.
*/
- protected boolean hasMessage ()
+ protected boolean hasMessage()
{
if (handshakeBuffer == null)
return false;
- ByteBuffer tmp = handshakeBuffer.duplicate ();
- tmp.flip ();
- tmp.position (handshakeOffset);
- Handshake handshake = new Handshake (tmp);
- return (handshake.length () >= tmp.remaining ());
+ ByteBuffer tmp = handshakeBuffer.duplicate();
+ tmp.flip();
+ tmp.position(handshakeOffset);
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_HANDSHAKE, "current buffer: {0}; test buffer {1}",
+ handshakeBuffer, tmp);
+ if (tmp.remaining() < 4)
+ return false;
+ Handshake handshake = new Handshake(tmp.slice());
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_HANDSHAKE, "handshake len:{0} remaining:{1}",
+ handshake.length(), tmp.remaining());
+ return (handshake.length() <= tmp.remaining() - 4);
}
/**
@@ -273,9 +368,23 @@ public abstract class AbstractHandshake
*/
private void reallocateBuffer (final int totalLen)
{
- int len = handshakeBuffer == null ? 0 : handshakeBuffer.capacity ();
+ int len = handshakeBuffer == null ? -1
+ : handshakeBuffer.capacity() - (handshakeBuffer.limit() - handshakeOffset);
if (len >= totalLen)
- return; // Big enough; no need to reallocate.
+ {
+ // Big enough; no need to reallocate; but maybe shift the contents
+ // down.
+ if (handshakeOffset > 0)
+ {
+ ByteBuffer tmp = handshakeBuffer.duplicate();
+ tmp.flip();
+ tmp.position(handshakeOffset);
+ handshakeBuffer.position(0);
+ handshakeBuffer.put(tmp);
+ handshakeOffset = 0;
+ }
+ return;
+ }
// Start at 1K (probably the system's page size). Double the size
// from there.
@@ -288,8 +397,8 @@ public abstract class AbstractHandshake
if (handshakeBuffer != null)
{
handshakeBuffer.flip ();
- handshakeBuffer.position (handshakeOffset);
- newBuf.put (handshakeBuffer);
+ handshakeBuffer.position(handshakeOffset);
+ newBuf.put(handshakeBuffer);
}
handshakeBuffer = newBuf;
@@ -319,7 +428,7 @@ Certificate.signature.sha_hash
* @param session The current session being negotiated.
* @return The computed to-be-signed value.
*/
- protected byte[] genV2CertificateVerify(MessageDigest md5,
+ protected byte[] genV3CertificateVerify(MessageDigest md5,
MessageDigest sha,
SessionImpl session)
{
@@ -386,9 +495,9 @@ Certificate.signature.sha_hash
{
byte[] seed = new byte[clientRandom.length()
+ serverRandom.length()];
- clientRandom.buffer().get(seed, 0, clientRandom.length());
- serverRandom.buffer().get(seed, clientRandom.length(),
- serverRandom.length());
+ serverRandom.buffer().get(seed, 0, serverRandom.length());
+ clientRandom.buffer().get(seed, serverRandom.length(),
+ clientRandom.length());
prf = new SSLRandom();
HashMap<String,byte[]> attr = new HashMap<String,byte[]>(2);
attr.put(SSLRandom.SECRET, session.privateData.masterSecret);
@@ -401,11 +510,11 @@ Certificate.signature.sha_hash
+ clientRandom.length()
+ serverRandom.length()];
System.arraycopy(KEY_EXPANSION, 0, seed, 0, KEY_EXPANSION.length);
- clientRandom.buffer().get(seed, KEY_EXPANSION.length,
- clientRandom.length());
- serverRandom.buffer().get(seed, (KEY_EXPANSION.length
- + clientRandom.length()),
+ serverRandom.buffer().get(seed, KEY_EXPANSION.length,
serverRandom.length());
+ clientRandom.buffer().get(seed, (KEY_EXPANSION.length
+ + serverRandom.length()),
+ clientRandom.length());
prf = new TLSRandom();
HashMap<String,byte[]> attr = new HashMap<String,byte[]>(2);
@@ -420,11 +529,8 @@ Certificate.signature.sha_hash
prf.nextBytes(keys[1], 0, keys[1].length);
prf.nextBytes(keys[2], 0, keys[2].length);
prf.nextBytes(keys[3], 0, keys[3].length);
- if (session.version.compareTo(ProtocolVersion.TLS_1_1) < 0)
- {
- prf.nextBytes(keys[4], 0, keys[4].length);
- prf.nextBytes(keys[5], 0, keys[5].length);
- }
+ prf.nextBytes(keys[4], 0, keys[4].length);
+ prf.nextBytes(keys[5], 0, keys[5].length);
}
catch (LimitReachedException lre)
{
@@ -432,6 +538,16 @@ Certificate.signature.sha_hash
throw new Error(lre);
}
+ if (Debug.DEBUG_KEY_EXCHANGE)
+ logger.logv(Component.SSL_KEY_EXCHANGE,
+ "keys generated;\n [0]: {0}\n [1]: {1}\n [2]: {2}\n" +
+ " [3]: {3}\n [4]: {4}\n [5]: {5}",
+ Util.toHexString(keys[0], ':'),
+ Util.toHexString(keys[1], ':'),
+ Util.toHexString(keys[2], ':'),
+ Util.toHexString(keys[3], ':'),
+ Util.toHexString(keys[4], ':'),
+ Util.toHexString(keys[5], ':'));
return keys;
}
@@ -459,6 +575,10 @@ Certificate.signature.sha_hash
TLSRandom prf = new TLSRandom();
byte[] md5val = md5.digest();
byte[] shaval = sha.digest();
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_HANDSHAKE, "finished md5:{0} sha:{1}",
+ Util.toHexString(md5val, ':'),
+ Util.toHexString(shaval, ':'));
byte[] seed = new byte[CLIENT_FINISHED.length
+ md5val.length
+ shaval.length];
@@ -503,23 +623,23 @@ Certificate.signature.sha_hash
md5.update(isClient ? SENDER_CLIENT : SENDER_SERVER);
md5.update(session.privateData.masterSecret);
- md5.update(SSLHMac.PAD1);
+ md5.update(PAD1);
byte[] tmp = md5.digest();
md5.reset();
md5.update(session.privateData.masterSecret);
- md5.update(SSLHMac.PAD2);
+ md5.update(PAD2);
md5.update(tmp);
finishedBuffer.put(md5.digest());
sha.update(isClient ? SENDER_CLIENT : SENDER_SERVER);
sha.update(session.privateData.masterSecret);
- sha.update(SSLHMac.PAD1);
+ sha.update(PAD1, 0, 40);
tmp = sha.digest();
sha.reset();
sha.update(session.privateData.masterSecret);
- sha.update(SSLHMac.PAD2);
+ sha.update(PAD2, 0, 40);
sha.update(tmp);
finishedBuffer.put(sha.digest()).position(0);
}
@@ -574,6 +694,10 @@ Certificate.signature.sha_hash
SessionImpl session)
throws SSLException
{
+ if (Debug.DEBUG_KEY_EXCHANGE)
+ logger.logv(Component.SSL_KEY_EXCHANGE, "preMasterSecret:\n{0}",
+ new ByteArray(preMasterSecret));
+
if (session.version == ProtocolVersion.SSL_3)
{
try
@@ -623,12 +747,12 @@ Certificate.signature.sha_hash
byte[] seed = new byte[clientRandom.length()
+ serverRandom.length()
+ MASTER_SECRET.length];
- clientRandom.buffer().get(seed, 0, clientRandom.length());
- serverRandom.buffer().get(seed, clientRandom.length(),
+ System.arraycopy(MASTER_SECRET, 0, seed, 0, MASTER_SECRET.length);
+ clientRandom.buffer().get(seed, MASTER_SECRET.length,
+ clientRandom.length());
+ serverRandom.buffer().get(seed,
+ MASTER_SECRET.length + clientRandom.length(),
serverRandom.length());
- System.arraycopy(MASTER_SECRET, 0, seed,
- clientRandom.length() + serverRandom.length(),
- MASTER_SECRET.length);
TLSRandom prf = new TLSRandom();
HashMap<String,byte[]> attr = new HashMap<String,byte[]>(2);
attr.put(TLSRandom.SECRET, preMasterSecret);
@@ -639,7 +763,7 @@ Certificate.signature.sha_hash
prf.nextBytes(session.privateData.masterSecret, 0, 48);
}
- if (Configuration.DEBUG)
+ if (Debug.DEBUG_KEY_EXCHANGE)
logger.log(Component.SSL_KEY_EXCHANGE, "master_secret: {0}",
new ByteArray(session.privateData.masterSecret));
diff --git a/gnu/javax/net/ssl/provider/CipherSuite.java b/gnu/javax/net/ssl/provider/CipherSuite.java
index d3e89bd50..f3fbdcbc2 100644
--- a/gnu/javax/net/ssl/provider/CipherSuite.java
+++ b/gnu/javax/net/ssl/provider/CipherSuite.java
@@ -560,7 +560,7 @@ public final class CipherSuite implements Constructed
if (macAlgorithm == MacAlgorithm.MD5)
macAlg = "HMac-MD5";
if (macAlgorithm == MacAlgorithm.SHA)
- macAlg = "HMac-SHA-1";
+ macAlg = "HMac-SHA1";
}
GetSecurityPropertyAction gspa =
diff --git a/gnu/javax/net/ssl/provider/ClientHello.java b/gnu/javax/net/ssl/provider/ClientHello.java
index 2b05f8ea5..9592bb8ff 100644
--- a/gnu/javax/net/ssl/provider/ClientHello.java
+++ b/gnu/javax/net/ssl/provider/ClientHello.java
@@ -48,6 +48,7 @@ import java.io.StringReader;
import java.io.StringWriter;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Iterator;
@@ -71,7 +72,7 @@ struct
} ClientHello;
</pre>
*/
-public final class ClientHello implements Handshake.Body
+public class ClientHello implements Handshake.Body
{
// Fields.
@@ -79,22 +80,20 @@ public final class ClientHello implements Handshake.Body
// To help track offsets into the message:
// The location of the 'random' field.
- private static final int RANDOM_OFFSET = 2;
+ protected static final int RANDOM_OFFSET = 2;
// The location of the sesion_id length.
- private static final int SESSID_OFFSET = 32 + RANDOM_OFFSET;
+ protected static final int SESSID_OFFSET = 32 + RANDOM_OFFSET;
// The location of the session_id bytes (if any).
- private static final int SESSID_OFFSET2 = SESSID_OFFSET + 1;
+ protected static final int SESSID_OFFSET2 = SESSID_OFFSET + 1;
- private final ByteBuffer buffer;
- private int totalLength;
+ protected ByteBuffer buffer;
// Constructor.
// -------------------------------------------------------------------------
public ClientHello (final ByteBuffer buffer)
{
- this.buffer = buffer;
- totalLength = buffer.limit ();
+ this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN);
}
// Instance methods.
@@ -102,7 +101,12 @@ public final class ClientHello implements Handshake.Body
public int length ()
{
- return totalLength;
+ int len = SESSID_OFFSET2 + buffer.get(SESSID_OFFSET);
+ len += (buffer.getShort(len) & 0xFFFF) + 2;
+ len += (buffer.get(len) & 0xFF) + 1;
+ if (hasExtensions())
+ len += (buffer.get(len) & 0xFFFF) + 2;
+ return len;
}
/**
@@ -177,45 +181,27 @@ public final class ClientHello implements Handshake.Body
.limit (offset + len + 2)).slice ();
return new ExtensionList (ebuf);
}
-
- public void setVersion (final ProtocolVersion version)
- {
- buffer.putShort (0, (short) version.rawValue ());
- }
-
- public void setSessionId (final byte[] buffer)
- {
- setSessionId (buffer, 0, buffer.length);
- }
-
- public void setSessionId (final byte[] buffer, final int offset, final int length)
- {
- int len = Math.min (32, length);
- this.buffer.put (SESSID_OFFSET, (byte) len);
- this.buffer.position (SESSID_OFFSET2);
- this.buffer.put (buffer, offset, len);
- }
-
- public void setExtensionsLength (final int length)
+
+ public int extensionsLength()
{
- int offset = getExtensionsOffset();
- this.totalLength = offset + length + 4;
- buffer.putShort(offset, (short) length);
+ if (hasExtensions())
+ return 0;
+ return buffer.getShort(getExtensionsOffset()) & 0xFFFF;
}
- private int getCipherSuitesOffset ()
+ protected int getCipherSuitesOffset ()
{
return (SESSID_OFFSET2 + (buffer.get (SESSID_OFFSET) & 0xFF));
}
- private int getCompressionMethodsOffset ()
+ protected int getCompressionMethodsOffset ()
{
int csOffset = getCipherSuitesOffset ();
int csLen = buffer.getShort (csOffset) & 0xFFFF;
return csOffset + csLen + 2;
}
- private int getExtensionsOffset ()
+ protected int getExtensionsOffset ()
{
int cmOffset = getCompressionMethodsOffset ();
return (buffer.get (cmOffset) & 0xFF) + cmOffset + 1;
diff --git a/gnu/javax/net/ssl/provider/ClientHelloBuilder.java b/gnu/javax/net/ssl/provider/ClientHelloBuilder.java
new file mode 100644
index 000000000..e82ef48b1
--- /dev/null
+++ b/gnu/javax/net/ssl/provider/ClientHelloBuilder.java
@@ -0,0 +1,132 @@
+/* ClientHelloBuilder.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 java.nio.ByteBuffer;
+import java.util.List;
+
+/**
+ * Builder for {@link ClientHello} objects.
+ *
+ * @author Casey Marshall (csm@gnu.org)
+ */
+public class ClientHelloBuilder extends ClientHello implements Builder
+{
+ public ClientHelloBuilder()
+ {
+ super(ByteBuffer.allocate(256));
+ }
+
+ /* (non-Javadoc)
+ * @see gnu.javax.net.ssl.provider.Builder#buffer()
+ */
+ public ByteBuffer buffer()
+ {
+ return (ByteBuffer) buffer.duplicate().position(0).limit(length());
+ }
+
+ public void setVersion(final ProtocolVersion version)
+ {
+ ensureCapacity(2);
+ buffer.putShort(0, (short) version.rawValue ());
+ }
+
+ public void setSessionId (final byte[] buffer)
+ {
+ setSessionId(buffer, 0, buffer.length);
+ }
+
+ public void setSessionId (final byte[] buffer, final int offset, final int length)
+ {
+ ensureCapacity(SESSID_OFFSET2 + length);
+ int len = Math.min (32, length);
+ this.buffer.put (SESSID_OFFSET, (byte) len);
+ this.buffer.position (SESSID_OFFSET2);
+ this.buffer.put (buffer, offset, len);
+ }
+
+ public void setCipherSuites(List<CipherSuite> suites)
+ {
+ int off = getCipherSuitesOffset();
+ ensureCapacity(off + (2 * suites.size()) + 2);
+ buffer.putShort(off, (short) (suites.size() * 2));
+ int i = 2;
+ for (CipherSuite suite : suites)
+ {
+ ((ByteBuffer) buffer.duplicate().position(off+i)).put(suite.id());
+ i += 2;
+ }
+ }
+
+ public void setCompressionMethods(List<CompressionMethod> methods)
+ {
+ int off = getCompressionMethodsOffset();
+ ensureCapacity(off + methods.size() + 1);
+ buffer.put(off, (byte) methods.size());
+ for (CompressionMethod method : methods)
+ buffer.put(++off, (byte) method.getValue());
+ }
+
+ public void setExtensionsLength (final int length)
+ {
+ if (length < 0 || length > 16384)
+ throw new IllegalArgumentException("length must be nonnegative and not exceed 16384");
+ int needed = getExtensionsOffset() + 2 + length;
+ if (buffer.capacity() < needed)
+ ensureCapacity(needed);
+ buffer.putShort(getExtensionsOffset(), (short) length);
+ }
+
+ public void setExtensions(ByteBuffer extensions)
+ {
+ extensions = (ByteBuffer)
+ extensions.duplicate().limit(extensions.position() + extensionsLength());
+ ((ByteBuffer) buffer.duplicate().position(getExtensionsOffset() + 2)).put(extensions);
+ }
+
+ public void ensureCapacity(final int length)
+ {
+ if (buffer.capacity() >= length)
+ return;
+ ByteBuffer newBuf = ByteBuffer.allocate(length);
+ newBuf.put((ByteBuffer) buffer.position(0));
+ newBuf.position(0);
+ this.buffer = newBuf;
+ }
+}
diff --git a/gnu/javax/net/ssl/provider/ClientHelloV2.java b/gnu/javax/net/ssl/provider/ClientHelloV2.java
index 11c46bf40..a514d9ad3 100644
--- a/gnu/javax/net/ssl/provider/ClientHelloV2.java
+++ b/gnu/javax/net/ssl/provider/ClientHelloV2.java
@@ -41,6 +41,7 @@ package gnu.javax.net.ssl.provider;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
@@ -71,7 +72,7 @@ class ClientHelloV2 implements Constructed
ClientHelloV2 (final ByteBuffer buffer)
{
- this.buffer = buffer;
+ this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN);
}
public int length ()
@@ -99,7 +100,7 @@ class ClientHelloV2 implements Constructed
return buffer.getShort (7) & 0xFFFF;
}
- List cipherSpecs ()
+ public List<CipherSuite> cipherSpecs ()
{
int n = cipherSpecsLength ();
List<CipherSuite> l = new ArrayList<CipherSuite>(n / 3);
diff --git a/gnu/javax/net/ssl/provider/Debug.java b/gnu/javax/net/ssl/provider/Debug.java
new file mode 100644
index 000000000..8b56f2046
--- /dev/null
+++ b/gnu/javax/net/ssl/provider/Debug.java
@@ -0,0 +1,66 @@
+/* Debug.java -- Jessie debug constants.
+ 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;
+
+/**
+ * Debug constants for Jessie.
+ *
+ * @author Casey Marshall (csm@gnu.org)
+ */
+public final class Debug
+{
+ /**
+ * Set to true to dump out traces of SSL connections to the system
+ * logger.
+ */
+ public static final boolean DEBUG = true;
+
+ /**
+ * Set to true to dump out info about the SSL key exchange. Since this
+ * MAY contain sensitive data, it is a separate value.
+ */
+ public static final boolean DEBUG_KEY_EXCHANGE = true;
+
+ /**
+ * Set to true to turn on dumping of decrypted packets. Since this will
+ * log potentially-sensitive information (i.e., decrypted messages), only
+ * enable this in debug scenarios.
+ */
+ public static final boolean DEBUG_DECRYPTION = true;
+}
diff --git a/gnu/javax/net/ssl/provider/Extension.java b/gnu/javax/net/ssl/provider/Extension.java
index cd0285ed3..5442daa02 100644
--- a/gnu/javax/net/ssl/provider/Extension.java
+++ b/gnu/javax/net/ssl/provider/Extension.java
@@ -42,6 +42,7 @@ import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
/**
* An SSL hello extension.
@@ -67,7 +68,7 @@ public final class Extension implements Constructed
Extension(final ByteBuffer buffer)
{
- this.buffer = buffer;
+ this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN);
}
// Instance methods.
@@ -75,7 +76,7 @@ public final class Extension implements Constructed
public int length ()
{
- return (buffer.getShort (2) & 0xFFFF);
+ return (buffer.getShort (2) & 0xFFFF) + 2;
}
public Type type()
@@ -171,9 +172,10 @@ public final class Extension implements Constructed
if (prefix != null) out.print (prefix);
out.println(" type = " + type () + ";");
if (prefix != null) out.print (prefix);
+ String subprefix = " ";
+ if (prefix != null) subprefix = prefix + subprefix;
out.println(" value =");
-// out.println(Util.hexDump(value (), (prefix != null) ? prefix + " " : " "));
- out.println(value());
+ out.println(value().toString(subprefix));
if (prefix != null) out.print (prefix);
out.print("} Extension;");
return str.toString();
diff --git a/gnu/javax/net/ssl/provider/ExtensionList.java b/gnu/javax/net/ssl/provider/ExtensionList.java
index 686eae000..07c689ae8 100644
--- a/gnu/javax/net/ssl/provider/ExtensionList.java
+++ b/gnu/javax/net/ssl/provider/ExtensionList.java
@@ -3,6 +3,7 @@ package gnu.javax.net.ssl.provider;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.ListIterator;
@@ -23,7 +24,7 @@ public class ExtensionList implements Iterable<Extension>
public ExtensionList (ByteBuffer buffer)
{
- this.buffer = buffer;
+ this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN);
modCount = 0;
}
diff --git a/gnu/javax/net/ssl/provider/InputSecurityParameters.java b/gnu/javax/net/ssl/provider/InputSecurityParameters.java
index 94f58760d..e1f56f052 100644
--- a/gnu/javax/net/ssl/provider/InputSecurityParameters.java
+++ b/gnu/javax/net/ssl/provider/InputSecurityParameters.java
@@ -38,12 +38,16 @@ exception statement from your version. */
package gnu.javax.net.ssl.provider;
+import gnu.classpath.ByteArray;
+import gnu.classpath.debug.Component;
+import gnu.classpath.debug.SystemLogger;
import gnu.java.io.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;
@@ -57,20 +61,24 @@ 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 SessionImpl session,
+ final CipherSuite suite)
{
this.cipher = cipher;
this.mac = mac;
this.inflater = inflater;
this.session = session;
+ this.suite = suite;
sequence = 0;
}
@@ -127,29 +135,28 @@ public class InputSecurityParameters
if (cipher != null)
{
ByteBuffer input = record.fragment();
- fragment = ByteBuffer.allocate (input.remaining());
- try
- {
- cipher.doFinal(input, fragment);
- }
- catch (BadPaddingException bpe)
- {
- // Should not happen, because we're doing the padding ourselves
- // (the cipher itself should use NoPadding.
- badPadding = true;
- }
+ 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;
- if (!session.suite.isStreamCipher ())
+ if (!suite.isStreamCipher ())
{
padlen = fragment.get(record.length() - 1) & 0xFF;
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_RECORD_LAYER, "padlen:{0}", padlen);
if (record.version() == ProtocolVersion.SSL_3)
{
@@ -167,8 +174,22 @@ public class InputSecurityParameters
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 - padlen - 1;
}
+
+ 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)
@@ -188,14 +209,18 @@ public class InputSecurityParameters
mac.update((byte) version.major());
mac.update((byte) version.minor());
}
- mac.update((byte) ((record.length() - maclen) >>> 8));
- mac.update((byte) (record.length() - maclen));
+ mac.update((byte) ((fragmentLength - ivlen) >>> 8));
+ mac.update((byte) (fragmentLength - ivlen));
ByteBuffer content =
- (ByteBuffer) fragment.duplicate().limit(record.length() - maclen - padlen - 1);
+ (ByteBuffer) fragment.duplicate().position(ivlen).limit(fragmentLength);
mac.update(content);
byte[] mac1 = mac.doFinal ();
byte[] mac2 = new byte[maclen];
- ((ByteBuffer) fragment.duplicate().position(record.length() - maclen - padlen - 1)).get(mac2);
+ 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;
}
@@ -210,11 +235,11 @@ public class InputSecurityParameters
if (inflater != null)
{
ByteBufferOutputStream out = new ByteBufferOutputStream(record.length());
- byte[] inbuffer = new byte[4096];
- byte[] outbuffer = new byte[4096];
+ byte[] inbuffer = new byte[1024];
+ byte[] outbuffer = new byte[1024];
boolean done = false;
if (record.version().compareTo(ProtocolVersion.TLS_1_1) >= 0
- && !session.suite.isStreamCipher())
+ && !suite.isStreamCipher())
fragment.position (cipher.getBlockSize());
else
fragment.position(0);
@@ -237,7 +262,7 @@ public class InputSecurityParameters
ByteBuffer outbuf = out.buffer();
if (outputStream != null)
{
- byte[] buf = new byte[4096];
+ byte[] buf = new byte[1024];
while (outbuf.hasRemaining())
{
int l = Math.min(outbuf.remaining(), buf.length);
@@ -265,13 +290,13 @@ public class InputSecurityParameters
else
{
ByteBuffer outbuf = (ByteBuffer)
- fragment.duplicate().limit(record.length() - maclen - padlen - 1);
+ fragment.duplicate().position(0).limit(record.length() - maclen - padlen - 1);
if (record.version().compareTo(ProtocolVersion.TLS_1_1) >= 0
- && !session.suite.isStreamCipher())
+ && !suite.isStreamCipher())
outbuf.position(cipher.getBlockSize());
if (outputStream != null)
{
- byte[] buf = new byte[4096];
+ byte[] buf = new byte[1024];
while (outbuf.hasRemaining())
{
int l = Math.min(outbuf.remaining(), buf.length);
@@ -303,6 +328,6 @@ public class InputSecurityParameters
CipherSuite cipherSuite ()
{
- return session.suite;
+ return suite;
}
}
diff --git a/gnu/javax/net/ssl/provider/Jessie.java b/gnu/javax/net/ssl/provider/Jessie.java
index fa7a44d92..5886b6c36 100644
--- a/gnu/javax/net/ssl/provider/Jessie.java
+++ b/gnu/javax/net/ssl/provider/Jessie.java
@@ -88,6 +88,10 @@ public class Jessie extends Provider
put("Mac.SSLv3HMac-MD5", SSLv3HMacMD5Impl.class.getName());
put("Mac.SSLv3HMac-SHA", SSLv3HMacSHAImpl.class.getName());
+ put("Signature.TLSv1.1-RSA", SSLRSASignatureImpl.class.getName());
+ put("Alg.Alias.Signature.TLSv1-RSA", "TLSv1.1-RSA");
+ put("Alg.Alias.Signature.SSLv3-RSA", "TLSv1.1-RSA");
+
return null;
}
});
diff --git a/gnu/javax/net/ssl/provider/OutputSecurityParameters.java b/gnu/javax/net/ssl/provider/OutputSecurityParameters.java
index d0fd13316..f940ec727 100644
--- a/gnu/javax/net/ssl/provider/OutputSecurityParameters.java
+++ b/gnu/javax/net/ssl/provider/OutputSecurityParameters.java
@@ -38,10 +38,14 @@ exception statement from your version. */
package gnu.javax.net.ssl.provider;
+import gnu.classpath.ByteArray;
+import gnu.classpath.debug.Component;
+import gnu.classpath.debug.SystemLogger;
import gnu.java.io.ByteBufferOutputStream;
import java.nio.ByteBuffer;
+import java.util.logging.Level;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
@@ -53,19 +57,23 @@ 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)
+ final Deflater deflater, SessionImpl session,
+ CipherSuite suite)
{
this.cipher = cipher;
this.mac = mac;
this.deflater = deflater;
this.session = session;
+ this.suite = suite;
sequence = 0;
}
@@ -84,6 +92,11 @@ public class OutputSecurityParameters
|| 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 ();
@@ -91,7 +104,7 @@ public class OutputSecurityParameters
int ivlen = 0;
byte[] iv = null;
if (session.version.compareTo(ProtocolVersion.TLS_1_1) >= 0
- && !session.suite.isStreamCipher())
+ && !suite.isStreamCipher())
{
ivlen = cipher.getBlockSize();
iv = new byte[ivlen];
@@ -99,13 +112,13 @@ public class OutputSecurityParameters
}
int padaddlen = 0;
- if (!session.suite.isStreamCipher()
+ 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.
@@ -113,17 +126,22 @@ public class OutputSecurityParameters
{
ByteBufferOutputStream deflated = new ByteBufferOutputStream();
- byte[] inbuf = new byte[4096];
- byte[] outbuf = new byte[4096];
+ 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; i++)
+ 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);
+ 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);
@@ -140,6 +158,7 @@ public class OutputSecurityParameters
}
fragments = new ByteBuffer[] { deflated.buffer() };
fragmentLength = ((int) deflater.getBytesWritten()) + maclen + ivlen;
+ deflater.reset();
offset = 0;
length = 1;
}
@@ -154,20 +173,24 @@ public class OutputSecurityParameters
}
fragmentLength += maclen + ivlen;
}
-
- // XXX Compute padding...
+
+ // Compute padding...
int padlen = 0;
byte[] pad = null;
- if (!session.suite.isStreamCipher())
+ if (!suite.isStreamCipher())
{
int bs = cipher.getBlockSize();
- padlen = bs - ((fragmentLength + bs - 1) % bs);
+ 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 += session.random().nextInt(256 / bs) * bs;
+ padlen += padaddlen;
while (padlen > 255)
padlen -= bs;
pad = new byte[padlen];
@@ -182,6 +205,7 @@ public class OutputSecurityParameters
session.random().nextBytes(pad);
pad[padlen - 1] = (byte) (padlen - 1);
}
+ fragmentLength += pad.length;
}
// If there is a MAC, compute it.
@@ -202,25 +226,17 @@ public class OutputSecurityParameters
mac.update((byte) session.version.major ());
mac.update((byte) session.version.minor ());
}
- mac.update((byte) (fragmentLength >>> 8));
- mac.update((byte) fragmentLength);
- if (iv != null)
- {
- mac.update(iv);
- }
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].slice();
+ ByteBuffer fragment = fragments[i].duplicate();
int l = Math.min(fragment.remaining(), toWrite - written);
fragment.limit(fragment.position() + l);
mac.update(fragment);
}
- if (pad != null)
- {
- mac.update(pad);
- }
macValue = mac.doFinal();
}
@@ -266,19 +282,25 @@ public class OutputSecurityParameters
int toWrite = fragmentLength - maclen;
for (int i = offset; i < offset + length && consumed < toWrite; i++)
{
- ByteBuffer fragment = fragments[i].slice();
+ ByteBuffer fragment = fragments[i];
int l = Math.min(fragment.remaining(), toWrite - consumed);
fragment.limit(fragment.position() + l);
outfragment.put(fragment);
- fragments[i].position(fragments[i].position() + l);
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;
+ }
} \ No newline at end of file
diff --git a/gnu/javax/net/ssl/provider/ProtocolVersion.java b/gnu/javax/net/ssl/provider/ProtocolVersion.java
index e96367acc..ca62054a8 100644
--- a/gnu/javax/net/ssl/provider/ProtocolVersion.java
+++ b/gnu/javax/net/ssl/provider/ProtocolVersion.java
@@ -81,7 +81,8 @@ public final class ProtocolVersion
return SSL_3;
if (name.equalsIgnoreCase ("TLSv1"))
return TLS_1;
- // TLSv1.1 not really supported yet.
+ if (name.equalsIgnoreCase("TLSv1.1"))
+ return TLS_1_1;
throw new IllegalArgumentException ("unknown protocol name: " + name);
}
diff --git a/gnu/javax/net/ssl/provider/Random.java b/gnu/javax/net/ssl/provider/Random.java
index 516ebbd29..e68159309 100644
--- a/gnu/javax/net/ssl/provider/Random.java
+++ b/gnu/javax/net/ssl/provider/Random.java
@@ -79,7 +79,7 @@ public class Random implements Builder, Constructed
public Random copy()
{
ByteBuffer buffer = ByteBuffer.allocate(32);
- buffer.put(this.buffer);
+ buffer.put((ByteBuffer) this.buffer.duplicate().position(0));
return new Random(buffer);
}
diff --git a/gnu/javax/net/ssl/provider/Record.java b/gnu/javax/net/ssl/provider/Record.java
index a6307d20e..6f5a23ef4 100644
--- a/gnu/javax/net/ssl/provider/Record.java
+++ b/gnu/javax/net/ssl/provider/Record.java
@@ -41,6 +41,7 @@ package gnu.javax.net.ssl.provider;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
/**
* A SSL/TLS record structure. An SSL record is defined to be:
@@ -61,7 +62,7 @@ public class Record
public Record (final ByteBuffer buffer)
{
- this.buffer = buffer;
+ this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN);
}
// XXX remove
@@ -114,6 +115,8 @@ public class Record
*/
public int length ()
{
+ // XXX this is different behavior than we usually want: we return the
+ // length field, not the total length. We should consider changing this.
return buffer.getShort (3) & 0xFFFF;
}
@@ -183,6 +186,9 @@ public class Record
out.print (" version: ");
out.print (version ());
out.println (";");
+ out.print(" length: ");
+ out.print(length());
+ out.println(";");
out.println (" fragment {");
out.print (Util.hexDump (fragment (), " "));
out.println (" };");
diff --git a/gnu/javax/net/ssl/provider/SSLContextImpl.java b/gnu/javax/net/ssl/provider/SSLContextImpl.java
index be75cb028..22fcbd7d8 100644
--- a/gnu/javax/net/ssl/provider/SSLContextImpl.java
+++ b/gnu/javax/net/ssl/provider/SSLContextImpl.java
@@ -74,8 +74,8 @@ import javax.net.ssl.X509TrustManager;
public final class SSLContextImpl
extends SSLContextSpi
{
- private SSLSessionContext serverContext;
- private SSLSessionContext clientContext;
+ AbstractSessionContext serverContext;
+ AbstractSessionContext clientContext;
X509ExtendedKeyManager keyManager;
X509TrustManager trustManager;
diff --git a/gnu/javax/net/ssl/provider/SSLEngineImpl.java b/gnu/javax/net/ssl/provider/SSLEngineImpl.java
index 8fc9520cd..b7f19946a 100644
--- a/gnu/javax/net/ssl/provider/SSLEngineImpl.java
+++ b/gnu/javax/net/ssl/provider/SSLEngineImpl.java
@@ -49,8 +49,10 @@ import gnu.javax.net.ssl.provider.Alert.Level;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import java.util.zip.DataFormatException;
@@ -71,7 +73,7 @@ public final class SSLEngineImpl extends SSLEngine
{
final SSLContextImpl contextImpl;
private SSLRecordHandler[] handlers;
- private static final Logger logger = SystemLogger.SYSTEM;
+ private static final SystemLogger logger = SystemLogger.SYSTEM;
private SessionImpl session;
private InputSecurityParameters insec;
private OutputSecurityParameters outsec;
@@ -101,12 +103,15 @@ public final class SSLEngineImpl extends SSLEngine
*/
private final ByteBuffer alertBuffer;
- private int mode;
- private static final int MODE_NONE = 0, MODE_SERVER = 1, MODE_CLIENT = 2;
+ private Mode mode;
+ private enum Mode { SERVER, CLIENT };
+
SSLEngineImpl (SSLContextImpl contextImpl, String host, int port)
{
super(host, port);
+ logger.logv(java.util.logging.Level.INFO, "creating SSLEngine {0} {1}:{2}",
+ this, host, port);
this.contextImpl = contextImpl;
handlers = new SSLRecordHandler[256];
session = new SessionImpl();
@@ -117,9 +122,15 @@ public final class SSLEngineImpl extends SSLEngine
session.setId(new Session.ID(sid));
session.setRandom(contextImpl.random);
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_RECORD_LAYER, "generated session ID {0} with random {1}",
+ session.id(), contextImpl.random);
+
// Begin with no encryption.
- insec = new InputSecurityParameters (null, null, null, session);
- outsec = new OutputSecurityParameters (null, null, null, session);
+ insec = new InputSecurityParameters (null, null, null, session,
+ CipherSuite.TLS_NULL_WITH_NULL_NULL);
+ outsec = new OutputSecurityParameters (null, null, null, session,
+ CipherSuite.TLS_NULL_WITH_NULL_NULL);
inClosed = false;
outClosed = false;
needClientAuth = false;
@@ -127,10 +138,49 @@ public final class SSLEngineImpl extends SSLEngine
createSessions = true;
initialHandshakeDone = false;
alertBuffer = ByteBuffer.wrap (new byte[2]);
- mode = MODE_NONE;
+ mode = null;
lastAlert = null;
handshakeStatus = SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING;
changeCipherSpec = false;
+
+ // Set up default protocols and suites.
+ enabledProtocols = new String[] {
+ ProtocolVersion.TLS_1_1.toString(),
+ ProtocolVersion.TLS_1.toString(),
+ ProtocolVersion.SSL_3.toString()
+ };
+ enabledSuites = new String[] {
+ CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA.toString(),
+ CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA.toString(),
+ CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA.toString(),
+ CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA.toString(),
+ CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA.toString(),
+ CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA.toString(),
+ CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA.toString(),
+ CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA.toString(),
+ CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA.toString(),
+ CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA.toString(),
+ CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA.toString(),
+ CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA.toString(),
+ CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA.toString(),
+ CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA.toString(),
+ CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA.toString(),
+ CipherSuite.TLS_RSA_WITH_RC4_128_MD5.toString(),
+ CipherSuite.TLS_RSA_WITH_RC4_128_SHA.toString(),
+ CipherSuite.TLS_DHE_DSS_WITH_DES_CBC_SHA.toString(),
+ CipherSuite.TLS_DHE_RSA_WITH_DES_CBC_SHA.toString(),
+ CipherSuite.TLS_DH_DSS_WITH_DES_CBC_SHA.toString(),
+ CipherSuite.TLS_DH_RSA_WITH_DES_CBC_SHA.toString(),
+ CipherSuite.TLS_RSA_WITH_DES_CBC_SHA.toString(),
+ CipherSuite.TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA.toString(),
+ CipherSuite.TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA.toString(),
+ CipherSuite.TLS_RSA_EXPORT_WITH_DES40_CBC_SHA.toString(),
+ CipherSuite.TLS_RSA_EXPORT_WITH_RC4_40_MD5.toString(),
+ CipherSuite.TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA.toString(),
+ CipherSuite.TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA.toString(),
+ CipherSuite.TLS_RSA_WITH_NULL_MD5.toString(),
+ CipherSuite.TLS_RSA_WITH_NULL_SHA.toString()
+ };
}
// XXX implement?
@@ -152,9 +202,15 @@ public final class SSLEngineImpl extends SSLEngine
@Override
public void beginHandshake () throws SSLException
{
+ if (Debug.DEBUG)
+ logger.log(Component.SSL_HANDSHAKE, "{0} handshake begins", mode);
+
+ if (mode == null)
+ throw new IllegalStateException("setUseClientMode was never used");
+
switch (mode)
{
- case MODE_SERVER:
+ case SERVER:
if (getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
throw new SSLException("handshake already in progress");
try
@@ -167,12 +223,9 @@ public final class SSLEngineImpl extends SSLEngine
}
break;
- case MODE_CLIENT:
+ case CLIENT:
throw new UnsupportedOperationException("client handshake not yet implemented");
//break;
-
- case MODE_NONE:
- throw new IllegalStateException ("setUseClientMode was never called");
}
}
@@ -185,7 +238,7 @@ public final class SSLEngineImpl extends SSLEngine
@Override
public void closeOutbound()
{
- outClosed = true;
+ lastAlert = new Alert(Alert.Level.WARNING, Alert.Description.CLOSE_NOTIFY);
}
@Override
@@ -233,7 +286,7 @@ public final class SSLEngineImpl extends SSLEngine
@Override
public boolean getUseClientMode ()
{
- return (mode == MODE_CLIENT);
+ return (mode == Mode.CLIENT);
}
@Override
@@ -245,13 +298,13 @@ public final class SSLEngineImpl extends SSLEngine
@Override
public boolean isInboundDone()
{
- return false; // XXX
+ return inClosed;
}
@Override
public boolean isOutboundDone()
{
- return false; // XXX
+ return outClosed;
}
@Override
@@ -303,9 +356,9 @@ public final class SSLEngineImpl extends SSLEngine
public void setUseClientMode (final boolean clientMode)
{
if (clientMode)
- mode = MODE_CLIENT;
+ mode = Mode.CLIENT;
else
- mode = MODE_SERVER;
+ mode = Mode.SERVER;
}
public @Override void setWantClientAuth(final boolean wantClientAuth)
@@ -318,49 +371,51 @@ public final class SSLEngineImpl extends SSLEngine
final int offset, final int length)
throws SSLException
{
- if (mode == MODE_NONE)
+ if (mode == null)
throw new IllegalStateException ("setUseClientMode was never called");
- Record record = new Record (source.slice ());
- ContentType type = record.contentType ();
+ if (inClosed)
+ return new SSLEngineResult(SSLEngineResult.Status.CLOSED,
+ handshakeStatus, 0, 0);
+
+ if (source.remaining() < 5)
+ {
+ return new SSLEngineResult(SSLEngineResult.Status.BUFFER_UNDERFLOW,
+ handshakeStatus, 0, 0);
+ }
+
+ Record record = null;
+ boolean helloV2 = false;
// XXX: messages may be chunked across multiple records; does this
// include the SSLv2 message? I don't think it does, but we should
// make sure.
- if (!getUseClientMode () && type == ContentType.CLIENT_HELLO_V2)
+ if (!getUseClientMode() && (source.get(source.position()) & 0x80) == 0x80)
{
- if (!insec.cipherSuite ().equals (CipherSuite.TLS_NULL_WITH_NULL_NULL))
- throw new SSLException ("received SSLv2 client hello in encrypted session; this is invalid.");
- logger.log (Component.SSL_RECORD_LAYER, "converting SSLv2 client hello to version 3 hello");
- ClientHelloV2 v2 = new ClientHelloV2 (source.slice ());
- List suites = v2.cipherSpecs ();
-
- // For the length of the "fake" v3 hello we need:
- // 1 for the content type
- // 2 for the protocol version
- // 2 for the record length
- // 2 for the client version
- // 32 for the random value
- // 33 for the session ID
- // 2 for the cipher suites length
- // 2*n for the n cipher suites
- // 2 for the singleton compression method list, with length
- int len = 76 + 2 * suites.size ();
- ByteBuffer buf = ByteBuffer.allocate (len);
- Record rec = new Record (buf);
- rec.setContentType (ContentType.HANDSHAKE);
- rec.setVersion (v2.version ());
- rec.setLength (len - 5);
-
- Handshake handshake = new Handshake (rec.fragment ());
- handshake.setType (Handshake.Type.CLIENT_HELLO);
- handshake.setLength (len - 9);
-
- ClientHello hello = (ClientHello) handshake.body ();
- hello.setVersion (v2.version ());
-
- Random random = hello.random ();
- byte[] challenge = v2.challenge ();
+ if (handshake == null)
+ beginHandshake();
+ int hellolen = source.getShort(source.position()) & 0x7FFF;
+ this.handshake.handleV2Hello(source.slice());
+ if (!insec.cipherSuite().equals (CipherSuite.TLS_NULL_WITH_NULL_NULL))
+ throw new SSLException ("received SSLv2 client hello in encrypted "
+ + "session; this is invalid.");
+ if (Debug.DEBUG)
+ logger.log (Component.SSL_RECORD_LAYER,
+ "converting SSLv2 client hello to version 3 hello");
+
+ source.getShort(); // skip length
+ ClientHelloV2 v2 = new ClientHelloV2(source.slice());
+
+ if (Debug.DEBUG)
+ logger.log(Component.SSL_RECORD_LAYER, "v2 hello: {0}", v2);
+
+ List<CipherSuite> suites = v2.cipherSpecs();
+
+ ClientHelloBuilder hello = new ClientHelloBuilder();
+ hello.setVersion(v2.version ());
+
+ Random random = hello.random();
+ byte[] challenge = v2.challenge();
if (challenge.length < 32)
{
byte[] b = new byte[32];
@@ -368,28 +423,39 @@ public final class SSLEngineImpl extends SSLEngine
challenge.length);
challenge = b;
}
- random.setGmtUnixTime ((challenge[0] & 0xFF) << 24
- | (challenge[1] & 0xFF) << 16
- | (challenge[2] & 0xFF) << 8
- | (challenge[3] & 0xFF));
- random.setRandomBytes (challenge, 4);
-
- byte[] sessionId = v2.sessionId ();
- hello.setSessionId (sessionId, 0, sessionId.length);
-
- CipherSuiteList mySuites = hello.cipherSuites ();
- mySuites.setSize (2 * suites.size ());
- for (int i = 0; i < suites.size (); i++)
- mySuites.put (i, (CipherSuite) suites.get (i));
-
- CompressionMethodList comps = hello.compressionMethods ();
- comps.setSize (1);
- comps.put (0, CompressionMethod.NULL);
-
- record = rec;
+ random.setGmtUnixTime((challenge[0] & 0xFF) << 24
+ | (challenge[1] & 0xFF) << 16
+ | (challenge[2] & 0xFF) << 8
+ | (challenge[3] & 0xFF));
+ random.setRandomBytes(challenge, 4);
+
+ byte[] sessionId = v2.sessionId();
+ hello.setSessionId(sessionId, 0, sessionId.length);
+ hello.setCipherSuites(suites);
+ ArrayList<CompressionMethod> comps = new ArrayList<CompressionMethod>(1);
+ comps.add(CompressionMethod.NULL);
+ hello.setCompressionMethods(comps);
+
+ record = new Record(ByteBuffer.allocate(hello.length() + 9));
+ record.setContentType(ContentType.HANDSHAKE);
+ record.setVersion(v2.version());
+ record.setLength(hello.length() + 4);
+
+ Handshake handshake = new Handshake(record.fragment());
+ handshake.setLength(hello.length());
+ handshake.setType(Handshake.Type.CLIENT_HELLO);
+
+ handshake.bodyBuffer().put(hello.buffer());
+ source.position(source.position() + hellolen);
+ helloV2 = true;
}
-
- logger.log (Component.SSL_RECORD_LAYER, "read record {0}", record);
+ else
+ record = new Record(source);
+
+ ContentType type = record.contentType ();
+
+ if (Debug.DEBUG)
+ logger.log(Component.SSL_RECORD_LAYER, "input record:\n{0}", record);
if (record.length() > session.getPacketBufferSize() - 5)
{
@@ -398,12 +464,8 @@ public final class SSLEngineImpl extends SSLEngine
throw new AlertException(lastAlert);
}
- ByteBufferOutputStream sysMsg = null;
-
- // We will get a message, when decompressed, that does not exceed
- // 2^14 bytes.
- if (record.contentType() != ContentType.APPLICATION_DATA)
- sysMsg = new ByteBufferOutputStream();
+ ByteBufferOutputStream sysMsg = null;
+ ByteBuffer msg = null;
int produced = 0;
try
@@ -413,12 +475,25 @@ public final class SSLEngineImpl extends SSLEngine
if (record.contentType() == ContentType.APPLICATION_DATA)
produced = insec.decrypt(record, sinks, offset, length);
else
- insec.decrypt(record, sysMsg);
+ {
+ if (insec.cipherSuite() == CipherSuite.TLS_NULL_WITH_NULL_NULL)
+ msg = record.fragment();
+ else
+ {
+ sysMsg = new ByteBufferOutputStream();
+ insec.decrypt(record, sysMsg);
+ }
+ }
+
+ // Advance the input buffer past the record we just read.
+ if (!helloV2)
+ source.position(source.position() + record.length() + 5);
}
catch (BufferOverflowException boe)
{
// We throw this if the output buffers are not large enough; signal
// the caller about this.
+ logger.log(Component.SSL_RECORD_LAYER, "buffer overflow when decrypting", boe);
return new SSLEngineResult(SSLEngineResult.Status.BUFFER_OVERFLOW,
handshakeStatus, 0, 0);
}
@@ -452,9 +527,12 @@ public final class SSLEngineImpl extends SSLEngine
// If we need to handle the output here, do it. Otherwise, the output
// has been stored in the supplied output buffers.
- ByteBuffer msg = null;
if (sysMsg != null)
- msg = sysMsg.buffer();
+ {
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_RECORD_LAYER, "sysmessage {0}", sysMsg);
+ msg = sysMsg.buffer();
+ }
if (type == ContentType.CHANGE_CIPHER_SPEC)
{
@@ -464,7 +542,7 @@ public final class SSLEngineImpl extends SSLEngine
{
result = new SSLEngineResult (SSLEngineResult.Status.OK,
handshakeStatus,
- record.length(), 0);
+ record.length() + 5, 0);
}
else
{
@@ -474,11 +552,11 @@ public final class SSLEngineImpl extends SSLEngine
InputSecurityParameters params = handshake.getInputParams();
logger.log (Component.SSL_RECORD_LAYER,
"switching to input security parameters {0}",
- params.cipherSuite ());
+ params.cipherSuite());
insec = params;
result = new SSLEngineResult (SSLEngineResult.Status.OK,
- SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING,
- record.length(), 0); // XXXX
+ handshakeStatus,
+ record.length() + 5, 0); // XXXX
}
}
else if (type == ContentType.ALERT)
@@ -508,28 +586,35 @@ public final class SSLEngineImpl extends SSLEngine
{
if (alerts[i].level () == Alert.Level.FATAL)
throw new AlertException (alerts[i]);
- logger.log (java.util.logging.Level.WARNING, "received alert: {0}", alerts[i]);
- if (alerts[i].description () == Alert.Description.CLOSE_NOTIFY)
+ logger.log (java.util.logging.Level.WARNING,
+ "received alert: {0}", alerts[i]);
+ if (alerts[i].description() == Alert.Description.CLOSE_NOTIFY)
inClosed = true;
}
- if (msg.hasRemaining ())
- alertBuffer.position (0).limit (2);
+ if (msg.hasRemaining())
+ alertBuffer.position(0).limit(2);
- result = new SSLEngineResult (inClosed ? SSLEngineResult.Status.CLOSED
- : SSLEngineResult.Status.OK,
- SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING,
- record.length (), 0);
+ result = new SSLEngineResult (SSLEngineResult.Status.OK,
+ handshakeStatus,
+ record.length() + 5, 0);
}
else if (type == ContentType.HANDSHAKE)
{
if (handshake == null)
beginHandshake();
handshakeStatus = handshake.handleInput(msg);
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_HANDSHAKE, "handshake status {0}", handshakeStatus);
result = new SSLEngineResult(SSLEngineResult.Status.OK,
handshakeStatus,
record.length() + 5,
0);
+ if (handshakeStatus == HandshakeStatus.FINISHED)
+ {
+ handshake = null;
+ handshakeStatus = HandshakeStatus.NOT_HANDSHAKING;
+ }
}
else if (type == ContentType.APPLICATION_DATA)
{
@@ -554,6 +639,9 @@ public final class SSLEngineImpl extends SSLEngine
throw new SSLException ("unknown content type: " + type);
}
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_RECORD_LAYER, "return result: {0}", result);
+
return result;
}
@@ -561,9 +649,13 @@ public final class SSLEngineImpl extends SSLEngine
ByteBuffer sink)
throws SSLException
{
- if (mode == MODE_NONE)
+ if (mode == null)
throw new IllegalStateException ("setUseClientMode was never called");
+ if (outClosed)
+ return new SSLEngineResult(SSLEngineResult.Status.CLOSED,
+ handshakeStatus, 0, 0);
+
ContentType type = null;
ByteBuffer sysMessage = null;
@@ -574,21 +666,54 @@ public final class SSLEngineImpl extends SSLEngine
Alert alert = new Alert(sysMessage);
alert.setDescription(lastAlert.description());
alert.setLevel(lastAlert.level());
+ if (lastAlert.description() == Alert.Description.CLOSE_NOTIFY)
+ outClosed = true;
}
else if (changeCipherSpec)
{
type = ContentType.CHANGE_CIPHER_SPEC;
sysMessage = ByteBuffer.allocate(1);
sysMessage.put(0, (byte) 1);
- outsec = handshake.getOutputParams();
- changeCipherSpec = false;
}
else if (handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP)
{
+ // If we are not encrypting, optimize the handshake to fill
+ // the buffer directly.
+ if (outsec.suite() == CipherSuite.TLS_NULL_WITH_NULL_NULL)
+ {
+ int orig = sink.position();
+ sink.order(ByteOrder.BIG_ENDIAN);
+ sink.put((byte) ContentType.HANDSHAKE.getValue());
+ sink.putShort((short) session.version.rawValue());
+ sink.putShort((short) 0);
+ handshakeStatus = handshake.handleOutput(sink);
+ int produced = sink.position() - orig;
+ sink.putShort(orig + 3, (short) (produced - 5));
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_RECORD_LAYER, "emitting record:\n{0}",
+ new Record((ByteBuffer) sink.duplicate().position(orig)));
+ SSLEngineResult result = new SSLEngineResult(SSLEngineResult.Status.OK,
+ handshakeStatus, 0, produced);
+
+ // Note, this will only happen if we transition from
+ // TLS_NULL_WITH_NULL_NULL *to* TLS_NULL_WITH_NULL_NULL, which
+ // doesn't make a lot of sense, but we support it anyway.
+ if (handshakeStatus == HandshakeStatus.FINISHED)
+ {
+ handshake = null; // finished with it.
+ handshakeStatus = HandshakeStatus.NOT_HANDSHAKING;
+ }
+ return result;
+ }
+
// Rough guideline; XXX.
sysMessage = ByteBuffer.allocate(sink.remaining() - 2048);
type = ContentType.HANDSHAKE;
handshakeStatus = handshake.handleOutput(sysMessage);
+ sysMessage.flip();
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_HANDSHAKE, "handshake status {0}",
+ handshakeStatus);
}
int produced = 0;
@@ -596,20 +721,27 @@ public final class SSLEngineImpl extends SSLEngine
try
{
+ int orig = sink.position();
int[] inout = null;
if (sysMessage != null)
{
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_RECORD_LAYER, "encrypt system message {0} to {1}", sysMessage, sink);
inout = outsec.encrypt(new ByteBuffer[] { sysMessage }, 0, 1,
- type, sink);
+ type, sink);
produced = inout[1];
}
else
{
inout = outsec.encrypt(sources, offset, length,
- ContentType.APPLICATION_DATA, sink);
+ ContentType.APPLICATION_DATA, sink);
consumed = inout[0];
produced = inout[1];
}
+
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_RECORD_LAYER, "emitting record:\n{0}",
+ new Record((ByteBuffer) sink.duplicate().position(orig).limit(produced)));
}
catch (ShortBufferException sbe)
{
@@ -638,8 +770,22 @@ public final class SSLEngineImpl extends SSLEngine
lastAlert = null;
throw ae;
}
- return new SSLEngineResult(SSLEngineResult.Status.OK,
- handshakeStatus, consumed, produced);
+
+ if (changeCipherSpec)
+ {
+ outsec = handshake.getOutputParams();
+ changeCipherSpec = false;
+ }
+ SSLEngineResult result
+ = new SSLEngineResult(outClosed ? SSLEngineResult.Status.CLOSED
+ : SSLEngineResult.Status.OK,
+ handshakeStatus, consumed, produced);
+ if (handshakeStatus == HandshakeStatus.FINISHED)
+ {
+ handshake = null; // done with it.
+ handshakeStatus = HandshakeStatus.NOT_HANDSHAKING;
+ }
+ return result;
}
// Package-private methods.
diff --git a/gnu/javax/net/ssl/provider/SSLRSASignatureImpl.java b/gnu/javax/net/ssl/provider/SSLRSASignatureImpl.java
new file mode 100644
index 000000000..415efc6f5
--- /dev/null
+++ b/gnu/javax/net/ssl/provider/SSLRSASignatureImpl.java
@@ -0,0 +1,233 @@
+/* SSLRSASignatureImpl.java -- SSL/TLS RSA implementation.
+ 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.sig.rsa.RSA;
+
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.SignatureSpi;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.Arrays;
+
+/**
+ * An implementation of of the RSA signature algorithm; this is an RSA
+ * encrypted MD5 hash followed by a SHA-1 hash.
+ *
+ * @author Casey Marshall (csm@gnu.org)
+ */
+public class SSLRSASignatureImpl extends SignatureSpi
+{
+ private static final SystemLogger logger = SystemLogger.SYSTEM;
+ private RSAPublicKey pubkey;
+ private RSAPrivateKey privkey;
+ private final MessageDigest md5, sha;
+ private boolean initSign = false;
+ private boolean initVerify = false;
+
+ public SSLRSASignatureImpl() throws NoSuchAlgorithmException
+ {
+ md5 = MessageDigest.getInstance("MD5");
+ sha = MessageDigest.getInstance("SHA-1");
+ }
+
+ /* (non-Javadoc)
+ * @see java.security.SignatureSpi#engineInitVerify(java.security.PublicKey)
+ */
+ @Override protected void engineInitVerify(PublicKey publicKey)
+ throws InvalidKeyException
+ {
+ try
+ {
+ pubkey = (RSAPublicKey) publicKey;
+ initVerify = true;
+ initSign = false;
+ privkey = null;
+ }
+ catch (ClassCastException cce)
+ {
+ throw new InvalidKeyException(cce);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.security.SignatureSpi#engineInitSign(java.security.PrivateKey)
+ */
+ @Override protected void engineInitSign(PrivateKey privateKey)
+ throws InvalidKeyException
+ {
+ try
+ {
+ privkey = (RSAPrivateKey) privateKey;
+ initSign = true;
+ initVerify = false;
+ pubkey = null;
+ }
+ catch (ClassCastException cce)
+ {
+ throw new InvalidKeyException(cce);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.security.SignatureSpi#engineUpdate(byte)
+ */
+ @Override protected void engineUpdate(byte b) throws SignatureException
+ {
+ if (!initSign && !initVerify)
+ throw new IllegalStateException("not initialized");
+ if (Debug.DEBUG)
+ logger.log(Component.SSL_HANDSHAKE, "SSL/RSA update 0x{0}",
+ Util.formatInt(b & 0xFF, 16, 2));
+ md5.update(b);
+ sha.update(b);
+ }
+
+ /* (non-Javadoc)
+ * @see java.security.SignatureSpi#engineUpdate(byte[], int, int)
+ */
+ @Override protected void engineUpdate(byte[] b, int off, int len)
+ throws SignatureException
+ {
+ if (!initSign && !initVerify)
+ throw new IllegalStateException("not initialized");
+ if (Debug.DEBUG)
+ logger.log(Component.SSL_HANDSHAKE, "SSL/RSA update\n{0}",
+ Util.hexDump(b, off, len, ">> "));
+ md5.update(b, off, len);
+ sha.update(b, off, len);
+ }
+
+ /* (non-Javadoc)
+ * @see java.security.SignatureSpi#engineSign()
+ */
+ @Override protected byte[] engineSign() throws SignatureException
+ {
+ // FIXME we need to add RSA blinding to this, somehow.
+
+ if (!initSign)
+ throw new SignatureException("not initialized for signing");
+ // Pad the hash results with RSA block type 1.
+ final int k = (privkey.getModulus().bitLength() + 7) >>> 3;
+ final byte[] d = Util.concat(md5.digest(), sha.digest());
+ if (k - 11 < d.length)
+ throw new SignatureException("message too long");
+ final byte[] eb = new byte[k];
+ eb[0] = 0x00;
+ eb[1] = 0x01;
+ for (int i = 2; i < k - d.length - 1; i++)
+ eb[i] = (byte) 0xFF;
+ System.arraycopy(d, 0, eb, k - d.length, d.length);
+ BigInteger EB = new BigInteger(eb);
+
+ // Private-key encrypt the padded hashes.
+ BigInteger EM = RSA.sign(privkey, EB);
+ return Util.trim(EM);
+ }
+
+ /* (non-Javadoc)
+ * @see java.security.SignatureSpi#engineVerify(byte[])
+ */
+ @Override protected boolean engineVerify(byte[] sigBytes)
+ throws SignatureException
+ {
+ if (!initVerify)
+ throw new SignatureException("not initialized for verifying");
+
+ // Public-key decrypt the signature representative.
+ BigInteger EM = new BigInteger(1, (byte[]) sigBytes);
+ BigInteger EB = RSA.verify(pubkey, EM);
+
+ // Unpad the decrypted message.
+ int i = 0;
+ final byte[] eb = EB.toByteArray();
+ if (eb[0] == 0x00)
+ {
+ for (i = 0; i < eb.length && eb[i] == 0x00; i++);
+ }
+ else if (eb[0] == 0x01)
+ {
+ for (i = 1; i < eb.length && eb[i] != 0x00; i++)
+ {
+ if (eb[i] != (byte) 0xFF)
+ {
+ throw new SignatureException("bad padding");
+ }
+ }
+ i++;
+ }
+ else
+ {
+ throw new SignatureException("decryption failed");
+ }
+ byte[] d1 = Util.trim(eb, i, eb.length - i);
+ byte[] d2 = Util.concat(md5.digest(), sha.digest());
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_HANDSHAKE, "SSL/RSA d1:{0} d2:{1}",
+ Util.toHexString(d1, ':'), Util.toHexString(d2, ':'));
+ return Arrays.equals(d1, d2);
+ }
+
+ /* (non-Javadoc)
+ * @see java.security.SignatureSpi#engineSetParameter(java.lang.String, java.lang.Object)
+ */
+ @Override protected void engineSetParameter(String param, Object value)
+ throws InvalidParameterException
+ {
+ throw new InvalidParameterException("parameters not supported");
+ }
+
+ /* (non-Javadoc)
+ * @see java.security.SignatureSpi#engineGetParameter(java.lang.String)
+ */
+ @Override protected Object engineGetParameter(String param)
+ throws InvalidParameterException
+ {
+ throw new InvalidParameterException("parameters not supported");
+ }
+}
diff --git a/gnu/javax/net/ssl/provider/ServerDHParams.java b/gnu/javax/net/ssl/provider/ServerDHParams.java
index d28571f95..c3e06a389 100644
--- a/gnu/javax/net/ssl/provider/ServerDHParams.java
+++ b/gnu/javax/net/ssl/provider/ServerDHParams.java
@@ -118,7 +118,7 @@ public class ServerDHParams implements Builder, ServerKeyExchangeParams
public ByteBuffer buffer()
{
- return (ByteBuffer) buffer.duplicate().limit(length());
+ return (ByteBuffer) buffer.duplicate().position(0).limit(length());
}
/**
diff --git a/gnu/javax/net/ssl/provider/ServerHandshake.java b/gnu/javax/net/ssl/provider/ServerHandshake.java
index 0ff324f44..047b6f306 100644
--- a/gnu/javax/net/ssl/provider/ServerHandshake.java
+++ b/gnu/javax/net/ssl/provider/ServerHandshake.java
@@ -46,13 +46,18 @@ import static gnu.javax.net.ssl.provider.Handshake.Type.*;
import gnu.classpath.Configuration;
import gnu.classpath.debug.Component;
+import gnu.java.security.action.GetSecurityPropertyAction;
import gnu.javax.crypto.key.dh.GnuDHPublicKey;
+import gnu.javax.net.ssl.AbstractSessionContext;
+import gnu.javax.net.ssl.Session;
+import gnu.javax.net.ssl.Session.ID;
import gnu.javax.net.ssl.provider.CertificateRequest.ClientCertificateType;
import gnu.javax.net.ssl.provider.ServerNameList.ServerName;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
+import java.security.AccessController;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
@@ -66,12 +71,9 @@ import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.logging.Level;
-import java.util.logging.Logger;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
@@ -83,19 +85,16 @@ import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.interfaces.DHPrivateKey;
import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
-import javax.net.ssl.SSLEngine;
-import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.X509ExtendedKeyManager;
-import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
-import javax.net.ssl.SSLEngineResult.Status;
import javax.security.auth.x500.X500Principal;
class ServerHandshake extends AbstractHandshake
@@ -144,8 +143,6 @@ class ServerHandshake extends AbstractHandshake
private final SSLEngineImpl engine;
/* Handshake result fields. */
- private ProtocolVersion version;
- private CipherSuite suite;
private CompressionMethod compression;
private Random clientRandom;
private Random serverRandom;
@@ -156,6 +153,14 @@ class ServerHandshake extends AbstractHandshake
private String keyAlias = null;
private X509Certificate clientCert = null;
private X509Certificate localCert = null;
+ private boolean helloV2 = false;
+
+ /**
+ * We remember this to ensure that we use a different one if the client's
+ * session ID happens to be the same as our random one (this is possible,
+ * if the random we're initialized with is predictable).
+ */
+ private Session.ID clientSessionID;
private InputSecurityParameters inParams;
private OutputSecurityParameters outParams;
@@ -206,16 +211,27 @@ class ServerHandshake extends AbstractHandshake
* Choose the first cipher suite in the client's requested list that
* we have enabled.
*/
- private static CipherSuite chooseSuite (final CipherSuiteList clientSuites,
- final String[] enabledSuites,
- final ProtocolVersion version)
+ private CipherSuite chooseSuite (final CipherSuiteList clientSuites,
+ final String[] enabledSuites,
+ final ProtocolVersion version)
throws SSLException
{
- HashSet<CipherSuite> suites = new HashSet<CipherSuite> (enabledSuites.length);
+ // Figure out which SignatureAlgorithms we can support.
+ HashSet<SignatureAlgorithm> sigs = new HashSet<SignatureAlgorithm>(2);
+ X509ExtendedKeyManager km = engine.contextImpl.keyManager;
+ if (km.getServerAliases(SignatureAlgorithm.DSA.name(), null).length != 0)
+ sigs.add(SignatureAlgorithm.DSA);
+ if (km.getServerAliases(SignatureAlgorithm.RSA.name(), null).length != 0)
+ sigs.add(SignatureAlgorithm.RSA);
+
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_HANDSHAKE, "we have certs for signature algorithms {0}", sigs);
+
+ HashSet<CipherSuite> suites = new HashSet<CipherSuite>(enabledSuites.length);
for (String s : enabledSuites)
{
CipherSuite suite = CipherSuite.forName(s);
- if (suite != null)
+ if (suite != null && sigs.contains(suite.signatureAlgorithm()))
suites.add (suite);
}
for (CipherSuite suite : clientSuites)
@@ -238,11 +254,17 @@ class ServerHandshake extends AbstractHandshake
private static CompressionMethod chooseCompression (final CompressionMethodList comps)
throws SSLException
{
+ GetSecurityPropertyAction gspa
+ = new GetSecurityPropertyAction("jessie.enable.compression");
+ String enable = AccessController.doPrivileged(gspa);
// Scan for ZLIB first.
- for (CompressionMethod cm : comps)
+ if (Boolean.valueOf(enable))
{
- if (cm.equals (CompressionMethod.ZLIB))
- return CompressionMethod.ZLIB;
+ for (CompressionMethod cm : comps)
+ {
+ if (cm.equals (CompressionMethod.ZLIB))
+ return CompressionMethod.ZLIB;
+ }
}
for (CompressionMethod cm : comps)
{
@@ -255,67 +277,66 @@ class ServerHandshake extends AbstractHandshake
protected @Override boolean doHash()
{
- return state != WRITE_HELLO_REQUEST;
+ boolean b = helloV2;
+ helloV2 = false;
+ return (state != WRITE_HELLO_REQUEST) && !b;
}
- public @Override HandshakeStatus handleInput (ByteBuffer fragment)
+ public @Override HandshakeStatus implHandleInput()
throws SSLException
- {
+ {
if (state == DONE)
return HandshakeStatus.FINISHED;
- if (state.isWriteState() || outBuffer.hasRemaining())
+ if (state.isWriteState()
+ || (outBuffer != null && outBuffer.hasRemaining()))
return HandshakeStatus.NEED_WRAP;
- // If we don't have a message already waiting...
- if (!hasMessage())
- {
- // Try to read another...
- if (!pollHandshake (fragment))
- return HandshakeStatus.NEED_UNWRAP;
-
- // Otherwise, we've got something to process.
- }
-
- while (hasMessage() && state.isReadState())
- {
- // Copy the current buffer, and prepare it for reading.
- ByteBuffer buffer = handshakeBuffer.duplicate ();
- buffer.flip();
- buffer.position(handshakeOffset);
- Handshake handshake = new Handshake(buffer.slice());
+ // Copy the current buffer, and prepare it for reading.
+ ByteBuffer buffer = handshakeBuffer.duplicate ();
+ buffer.flip();
+ buffer.position(handshakeOffset);
+ Handshake handshake = new Handshake(buffer.slice(),
+ engine.session().suite,
+ engine.session().version);
- if (Configuration.DEBUG)
- logger.logv(Component.SSL_HANDSHAKE, "processing in state {0}: {1}",
- state, handshake);
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_HANDSHAKE, "processing in state {0}:\n{1}",
+ state, handshake);
- switch (state)
- {
- // Client Hello.
- //
- // This message is sent by the client to initiate a new handshake.
- // On a new connection, it is the first handshake message sent.
- //
- // The state of the handshake, after this message is processed,
- // will have a protocol version, cipher suite, compression method,
- // session ID, and various extensions (that the server also
- // supports).
- case READ_CLIENT_HELLO:
- if (handshake.type () != CLIENT_HELLO)
- throw new SSLException ("expecting client hello");
- // XXX throw better exception.
+ switch (state)
+ {
+ // Client Hello.
+ //
+ // This message is sent by the client to initiate a new handshake.
+ // On a new connection, it is the first handshake message sent.
+ //
+ // The state of the handshake, after this message is processed,
+ // will have a protocol version, cipher suite, compression method,
+ // session ID, and various extensions (that the server also
+ // supports).
+ case READ_CLIENT_HELLO:
+ if (handshake.type () != CLIENT_HELLO)
+ throw new SSLException ("expecting client hello");
+ // XXX throw better exception.
{
ClientHello hello = (ClientHello) handshake.body ();
engine.session().version
= chooseProtocol (hello.version (),
engine.getEnabledProtocols ());
- engine.session().suite
- = chooseSuite (hello.cipherSuites (),
- engine.getEnabledCipherSuites (), version);
+ engine.session().suite =
+ chooseSuite (hello.cipherSuites (),
+ engine.getEnabledCipherSuites (),
+ engine.session().version);
compression = chooseCompression (hello.compressionMethods ());
- clientRandom = hello.random ().copy ();
- byte[] sessionId = hello.sessionId ();
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_HANDSHAKE,
+ "chose version:{0} suite:{1} compression:{2}",
+ engine.session().version, engine.session().suite,
+ compression);
+ clientRandom = hello.random().copy();
+ byte[] sessionId = hello.sessionId();
if (hello.hasExtensions())
{
ExtensionList exts = hello.extensions();
@@ -334,7 +355,7 @@ class ServerHandshake extends AbstractHandshake
case MAX_FRAGMENT_LENGTH:
MaxFragmentLength len = (MaxFragmentLength) e.value();
engine.session().maxLength = len;
- engine.session().setPacketBufferSize(len.maxLength() + 2048);
+ engine.session().setApplicationBufferSize(len.maxLength());
break;
case SERVER_NAME:
@@ -351,36 +372,50 @@ class ServerHandshake extends AbstractHandshake
}
}
}
- SSLSessionContext sessions =
+ AbstractSessionContext sessions = (AbstractSessionContext)
engine.contextImpl.engineGetServerSessionContext();
SSLSession s = sessions.getSession(sessionId);
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_HANDSHAKE, "looked up saved session {0}", s);
if (s != null && s.isValid() && (s instanceof SessionImpl))
{
engine.setSession((SessionImpl) s);
continuedSession = true;
- version = ((SessionImpl) s).version;
+ }
+ else
+ {
+ // We *may* wind up with a badly seeded PRNG, and emit the
+ // same session ID over and over (this did happen to me,
+ // so we add this sanity check just in case).
+ if (engine.session().id().equals(new Session.ID(sessionId)))
+ {
+ byte[] newId = new byte[32];
+ engine.session().random().nextBytes(newId);
+ engine.session().setId(new Session.ID(newId));
+ }
+ sessions.put(engine.session());
}
state = WRITE_SERVER_HELLO;
}
break;
- // Certificate.
- //
- // This message is sent by the client if the server had previously
- // requested that the client authenticate itself with a certificate,
- // and if the client has an appropriate certificate available.
- //
- // Processing this message will save the client's certificate,
- // rejecting it if the certificate is not trusted, in preparation
- // for the certificate verify message that will follow.
- case READ_CERTIFICATE:
+ // Certificate.
+ //
+ // This message is sent by the client if the server had previously
+ // requested that the client authenticate itself with a certificate,
+ // and if the client has an appropriate certificate available.
+ //
+ // Processing this message will save the client's certificate,
+ // rejecting it if the certificate is not trusted, in preparation
+ // for the certificate verify message that will follow.
+ case READ_CERTIFICATE:
{
if (handshake.type() != CERTIFICATE)
{
if (engine.getNeedClientAuth()) // XXX throw better exception.
throw new SSLException("client auth required");
state = READ_CLIENT_KEY_EXCHANGE;
- continue;
+ return HandshakeStatus.NEED_UNWRAP;
}
Certificate cert = (Certificate) handshake.body();
@@ -415,15 +450,15 @@ class ServerHandshake extends AbstractHandshake
}
break;
- // Client Key Exchange.
- //
- // The client's key exchange. This message is sent either following
- // the certificate message, or if no certificate is available or
- // requested, following the server's hello done message.
- //
- // After receipt of this message, the session keys for this
- // session will have been created.
- case READ_CLIENT_KEY_EXCHANGE:
+ // Client Key Exchange.
+ //
+ // The client's key exchange. This message is sent either following
+ // the certificate message, or if no certificate is available or
+ // requested, following the server's hello done message.
+ //
+ // After receipt of this message, the session keys for this
+ // session will have been created.
+ case READ_CLIENT_KEY_EXCHANGE:
{
if (handshake.type() != CLIENT_KEY_EXCHANGE)
throw new SSLException("expecting client key exchange");
@@ -489,30 +524,34 @@ class ServerHandshake extends AbstractHandshake
engine.session());
try
{
- CipherSuite suite = engine.session().suite;
- Cipher inCipher = suite.cipher();
- Mac inMac = suite.mac(engine.session().version);
+ CipherSuite s = engine.session().suite;
+ Cipher inCipher = s.cipher();
+ Mac inMac = s.mac(engine.session().version);
Inflater inflater = (compression == CompressionMethod.ZLIB
? new Inflater() : null);
inCipher.init(Cipher.DECRYPT_MODE,
- new SecretKeySpec(keys[2], suite.cipherAlgorithm().toString()),
+ new SecretKeySpec(keys[2],
+ s.cipherAlgorithm().toString()),
new IvParameterSpec(keys[4]));
- inMac.init(new SecretKeySpec(keys[0], suite.macAlgorithm().toString()));
+ inMac.init(new SecretKeySpec(keys[0],
+ inMac.getAlgorithm()));
inParams = new InputSecurityParameters(inCipher, inMac,
inflater,
- engine.session());
+ engine.session(), s);
- Cipher outCipher = suite.cipher();
- Mac outMac = suite.mac(engine.session().version);
+ Cipher outCipher = s.cipher();
+ Mac outMac = s.mac(engine.session().version);
Deflater deflater = (compression == CompressionMethod.ZLIB
? new Deflater() : null);
outCipher.init(Cipher.ENCRYPT_MODE,
- new SecretKeySpec(keys[3], suite.cipherAlgorithm().toString()),
+ new SecretKeySpec(keys[3],
+ s.cipherAlgorithm().toString()),
new IvParameterSpec(keys[5]));
- inMac.init(new SecretKeySpec(keys[1], suite.macAlgorithm().toString()));
+ outMac.init(new SecretKeySpec(keys[1],
+ outMac.getAlgorithm()));
outParams = new OutputSecurityParameters(outCipher, outMac,
deflater,
- engine.session());
+ engine.session(), s);
}
catch (InvalidAlgorithmParameterException iape)
{
@@ -535,21 +574,26 @@ class ServerHandshake extends AbstractHandshake
state = READ_CERTIFICATE_VERIFY;
else
{
- engine.changeCipherSpec();
- state = WRITE_FINISHED;
+ if (continuedSession)
+ {
+ engine.changeCipherSpec();
+ state = WRITE_FINISHED;
+ }
+ else
+ state = READ_FINISHED;
}
}
break;
- // Certificate Verify.
- //
- // This message is sent following the client key exchange message,
- // but only when the client included its certificate in a previous
- // message.
- //
- // After receipt of this message, the client's certificate (and,
- // to a degree, the client's identity) will have been verified.
- case READ_CERTIFICATE_VERIFY:
+ // Certificate Verify.
+ //
+ // This message is sent following the client key exchange message,
+ // but only when the client included its certificate in a previous
+ // message.
+ //
+ // After receipt of this message, the client's certificate (and,
+ // to a degree, the client's identity) will have been verified.
+ case READ_CERTIFICATE_VERIFY:
{
if (handshake.type() != CERTIFICATE_VERIFY)
throw new SSLException("expecting certificate verify message");
@@ -565,22 +609,29 @@ class ServerHandshake extends AbstractHandshake
if (engine.getNeedClientAuth())
throw new SSLException("client auth failed", se);
}
+ if (continuedSession)
+ {
+ engine.changeCipherSpec();
+ state = WRITE_FINISHED;
+ }
+ else
+ state = READ_FINISHED;
}
break;
- // Finished.
- //
- // This message is sent immediately following the change cipher
- // spec message (which is sent outside of the handshake layer).
- // After receipt of this message, the session keys for the client
- // side will have been verified (this is the first message the
- // client sends encrypted and authenticated with the newly
- // negotiated keys).
- //
- // In the case of a continued session, the client sends its
- // finished message first. Otherwise, the server will send its
- // finished message first.
- case READ_FINISHED:
+ // Finished.
+ //
+ // This message is sent immediately following the change cipher
+ // spec message (which is sent outside of the handshake layer).
+ // After receipt of this message, the session keys for the client
+ // side will have been verified (this is the first message the
+ // client sends encrypted and authenticated with the newly
+ // negotiated keys).
+ //
+ // In the case of a continued session, the client sends its
+ // finished message first. Otherwise, the server will send its
+ // finished message first.
+ case READ_FINISHED:
{
if (handshake.type() != FINISHED)
{
@@ -604,13 +655,14 @@ class ServerHandshake extends AbstractHandshake
}
Finished serverFinished =
new Finished(generateFinished(md5copy, shacopy,
- true, engine.session()), version);
+ true, engine.session()),
+ engine.session().version);
- if (Configuration.DEBUG)
+ if (Debug.DEBUG)
logger.log(Component.SSL_HANDSHAKE, "server finished: {0}",
serverFinished);
- if (version == ProtocolVersion.SSL_3)
+ if (engine.session().version == ProtocolVersion.SSL_3)
{
if (!Arrays.equals(clientFinished.md5Hash(),
serverFinished.md5Hash())
@@ -640,11 +692,10 @@ class ServerHandshake extends AbstractHandshake
}
}
break;
- }
-
- handshakeOffset += handshake.length();
}
+ handshakeOffset += handshake.length() + 4;
+
if (state.isReadState())
return HandshakeStatus.NEED_UNWRAP;
if (state.isWriteState())
@@ -656,6 +707,11 @@ class ServerHandshake extends AbstractHandshake
public @Override HandshakeStatus implHandleOutput (ByteBuffer fragment)
throws SSLException
{
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_HANDSHAKE,
+ "handle output state: {0}; output fragment: {1}",
+ state, fragment);
+
// Drain the output buffer, if it needs it.
if (outBuffer != null && outBuffer.hasRemaining())
{
@@ -671,7 +727,7 @@ class ServerHandshake extends AbstractHandshake
else
return HandshakeStatus.NEED_UNWRAP;
}
-
+
// XXX what we need to do here is generate a "stream" of handshake
// messages, and insert them into fragment amounts that we have available.
// A handshake message can span multiple records, and we can put
@@ -732,6 +788,7 @@ class ServerHandshake extends AbstractHandshake
// when we run out of space in the output buffer, and split the
// overflow message. This sounds like the best, but also probably
// the hardest to code.
+output_loop:
while (fragment.remaining() >= 4 && state.isWriteState())
{
switch (state)
@@ -746,11 +803,11 @@ class ServerHandshake extends AbstractHandshake
handshake.setType(Handshake.Type.HELLO_REQUEST);
handshake.setLength(0);
fragment.position(fragment.position() + 4);
- if (Configuration.DEBUG)
+ if (Debug.DEBUG)
logger.log(Component.SSL_HANDSHAKE, "{0}", handshake);
state = READ_CLIENT_HELLO;
}
- break;
+ break output_loop; // XXX temporary
// Server Hello.
//
@@ -761,9 +818,7 @@ class ServerHandshake extends AbstractHandshake
case WRITE_SERVER_HELLO:
{
ServerHelloBuilder hello = new ServerHelloBuilder();
- hello.setVersion (version);
- hello.setCipherSuite (suite);
- hello.setCompressionMethod(compression);
+ hello.setVersion (engine.session().version);
Random r = hello.random();
r.setGmtUnixTime ((int) (System.currentTimeMillis() / 1000));
byte[] nonce = new byte[28];
@@ -771,12 +826,16 @@ class ServerHandshake extends AbstractHandshake
r.setRandomBytes(nonce);
serverRandom = r.copy ();
hello.setSessionId(engine.session().getId());
+ hello.setCipherSuite (engine.session().suite);
+ hello.setCompressionMethod(compression);
if (clientHadExtensions)
{
// XXX figure this out.
}
+ else // Don't send any extensions.
+ hello.setDisableExtensions(true);
- if (Configuration.DEBUG)
+ if (Debug.DEBUG)
logger.log(Component.SSL_HANDSHAKE, "{0}", hello);
int typeLen = ((Handshake.Type.SERVER_HELLO.getValue() << 24)
@@ -796,7 +855,7 @@ class ServerHandshake extends AbstractHandshake
else
state = WRITE_CERTIFICATE;
}
- break;
+ break output_loop; // XXX temporary
// Certificate.
//
@@ -805,13 +864,13 @@ class ServerHandshake extends AbstractHandshake
// itself (usually, servers must authenticate).
case WRITE_CERTIFICATE:
{
- if (suite.keyExchangeAlgorithm() == null)
+ if (engine.session().suite.keyExchangeAlgorithm() == null)
{
state = WRITE_SERVER_KEY_EXCHANGE;
break;
}
String sigAlg = null;
- switch (suite.keyExchangeAlgorithm())
+ switch (engine.session().suite.keyExchangeAlgorithm())
{
case NONE:
break;
@@ -821,11 +880,11 @@ class ServerHandshake extends AbstractHandshake
break;
case DIFFIE_HELLMAN:
- if (suite.isEphemeralDH())
+ if (engine.session().suite.isEphemeralDH())
sigAlg = "DHE_";
else
sigAlg = "DH_";
- switch (suite.signatureAlgorithm())
+ switch (engine.session().suite.signatureAlgorithm())
{
case RSA:
sigAlg += "RSA";
@@ -837,7 +896,7 @@ class ServerHandshake extends AbstractHandshake
}
case SRP:
- switch (suite.signatureAlgorithm())
+ switch (engine.session().suite.signatureAlgorithm())
{
case RSA:
sigAlg = "SRP_RSA";
@@ -865,8 +924,11 @@ class ServerHandshake extends AbstractHandshake
engine.session().setLocalCertificates(chain);
localCert = chain[0];
- if (Configuration.DEBUG)
- logger.log(Component.SSL_HANDSHAKE, "{0}", cert);
+ if (Debug.DEBUG)
+ {
+ logger.logv(Component.SSL_HANDSHAKE, "my cert:\n{0}", localCert);
+ logger.logv(Component.SSL_HANDSHAKE, "{0}", cert);
+ }
int typeLen = ((CERTIFICATE.getValue() << 24)
| (cert.length() & 0xFFFFFF));
@@ -879,7 +941,7 @@ class ServerHandshake extends AbstractHandshake
state = WRITE_SERVER_KEY_EXCHANGE;
}
- break;
+ break output_loop; // XXX temporary
// Server key exchange.
//
@@ -890,7 +952,7 @@ class ServerHandshake extends AbstractHandshake
// implicit in the server's certificate).
case WRITE_SERVER_KEY_EXCHANGE:
{
- KeyExchangeAlgorithm kex = suite.keyExchangeAlgorithm();
+ KeyExchangeAlgorithm kex = engine.session().suite.keyExchangeAlgorithm();
if (kex == KeyExchangeAlgorithm.DIFFIE_HELLMAN)
{
@@ -902,7 +964,7 @@ class ServerHandshake extends AbstractHandshake
if (kex != KeyExchangeAlgorithm.NONE
&& kex != KeyExchangeAlgorithm.RSA
- && suite.isEphemeralDH())
+ && engine.session().suite.isEphemeralDH())
{
// This key exchange requires server params; construct them.
ByteBuffer paramBuffer = null;
@@ -915,22 +977,25 @@ class ServerHandshake extends AbstractHandshake
pubKey.getParams().getG(),
pubKey.getY());
paramBuffer = params.buffer();
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_HANDSHAKE, "server DH params:\n{0}",
+ Util.hexDump(paramBuffer));
}
// XXX handle SRP
- ByteBuffer sigBuffer = signParams(paramBuffer);
- ServerKeyExchangeBuilder ske = new ServerKeyExchangeBuilder(suite);
+ ByteBuffer sigBuffer = signParams(paramBuffer.duplicate());
+ ServerKeyExchangeBuilder ske
+ = new ServerKeyExchangeBuilder(engine.session().suite);
ske.setParams(paramBuffer);
ske.setSignature(sigBuffer);
- if (Configuration.DEBUG)
+ if (Debug.DEBUG)
logger.log(Component.SSL_HANDSHAKE, "{0}", ske);
- fragment.putInt((SERVER_KEY_EXCHANGE.getValue() << 24)
- | (paramBuffer.remaining() & 0xFFFFFF));
-
outBuffer = ske.buffer();
int l = Math.min(fragment.remaining(), outBuffer.remaining());
+ fragment.putInt((SERVER_KEY_EXCHANGE.getValue() << 24)
+ | (ske.length() & 0xFFFFFF));
fragment.put((ByteBuffer) outBuffer.duplicate().limit(outBuffer.position() + l));
outBuffer.position(outBuffer.position() + l);
}
@@ -940,7 +1005,7 @@ class ServerHandshake extends AbstractHandshake
else
state = WRITE_SERVER_HELLO_DONE;
}
- break;
+ break output_loop; // XXX temporary
// Certificate Request.
//
@@ -970,7 +1035,7 @@ class ServerHandshake extends AbstractHandshake
issuers.add(cert.getIssuerX500Principal());
req.setAuthorities(issuers);
- if (Configuration.DEBUG)
+ if (Debug.DEBUG)
logger.log(Component.SSL_HANDSHAKE, "{0}", req);
fragment.putInt((CERTIFICATE_REQUEST.getValue() << 24)
@@ -983,7 +1048,7 @@ class ServerHandshake extends AbstractHandshake
state = WRITE_SERVER_HELLO_DONE;
}
- break;
+ break output_loop; // XXX temporary
// Server Hello Done.
//
@@ -997,9 +1062,11 @@ class ServerHandshake extends AbstractHandshake
// ServerHelloDone is zero-length; just put in the type
// field.
fragment.putInt(SERVER_HELLO_DONE.getValue() << 24);
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_HANDSHAKE, "writing ServerHelloDone");
state = READ_CERTIFICATE;
}
- break;
+ break output_loop; // XXX temporary
// Finished.
//
@@ -1048,9 +1115,22 @@ class ServerHandshake extends AbstractHandshake
break;
}
}
- return (state.isWriteState() || outBuffer.hasRemaining()
- ? HandshakeStatus.NEED_WRAP
- : HandshakeStatus.NEED_UNWRAP);
+ if (state.isWriteState() || outBuffer.hasRemaining())
+ return HandshakeStatus.NEED_WRAP;
+ if (state.isReadState())
+ return HandshakeStatus.NEED_UNWRAP;
+
+ return HandshakeStatus.FINISHED;
+ }
+
+ @Override HandshakeStatus status()
+ {
+ if (state.isReadState())
+ return HandshakeStatus.NEED_UNWRAP;
+ if (state.isWriteState())
+ return HandshakeStatus.NEED_WRAP;
+
+ return HandshakeStatus.FINISHED;
}
@Override InputSecurityParameters getInputParams() throws SSLException
@@ -1067,6 +1147,14 @@ class ServerHandshake extends AbstractHandshake
return outParams;
}
+ @Override void handleV2Hello(ByteBuffer hello)
+ {
+ int len = hello.getShort(0) & 0x7FFF;
+ md5.update((ByteBuffer) hello.duplicate().position(2).limit(len+2));
+ sha.update((ByteBuffer) hello.duplicate().position(2).limit(len+2));
+ helloV2 = true;
+ }
+
/**
* Generate, or fetch from our certificate, the Diffie-Hellman exchange
* parameters.
@@ -1077,11 +1165,18 @@ class ServerHandshake extends AbstractHandshake
{
try
{
- if (suite.isEphemeralDH())
+ // XXX generating a DH key, especially given a large prime, can
+ // take a while. We should perhaps look into making this a delegated
+ // task, so we don't block other connections when generating keys.
+ //
+ // Also, I should investigate whether or not these DH parameters are
+ // "secure" or not; I mean, I'm not really worried, because SSH uses
+ // similar static parameters, but this *could* be a potential hole.
+ if (engine.session().suite.isEphemeralDH())
{
KeyPairGenerator dhGen = KeyPairGenerator.getInstance("DH");
- // XXX figure this out, key size and shit.
- dhGen.initialize(2048, engine.session().random());
+ DHParameterSpec dhparams = DiffieHellman.getParams().getParams();
+ dhGen.initialize(dhparams, engine.session().random());
dhPair = dhGen.generateKeyPair();
}
else
@@ -1092,11 +1187,15 @@ class ServerHandshake extends AbstractHandshake
dhPair = new KeyPair(cert.getPublicKey(), key);
}
- if (Configuration.DEBUG)
+ if (Debug.DEBUG_KEY_EXCHANGE)
logger.logv(Component.SSL_KEY_EXCHANGE,
"Diffie-Hellman public:{0} private:{1}",
dhPair.getPublic(), dhPair.getPrivate());
}
+ catch (InvalidAlgorithmParameterException iape)
+ {
+ throw new SSLException(iape);
+ }
catch (NoSuchAlgorithmException nsae)
{
throw new SSLException(nsae);
@@ -1107,15 +1206,18 @@ class ServerHandshake extends AbstractHandshake
{
try
{
+ SignatureAlgorithm alg = engine.session().suite.signatureAlgorithm();
java.security.Signature sig
- = java.security.Signature.getInstance(suite.signatureAlgorithm().name());
+ = java.security.Signature.getInstance(alg.algorithm());
PrivateKey key = engine.contextImpl.keyManager.getPrivateKey(keyAlias);
+ if (Debug.DEBUG_KEY_EXCHANGE)
+ logger.logv(Component.SSL_HANDSHAKE, "server key: {0}", key);
sig.initSign(key);
sig.update(clientRandom.buffer());
sig.update(serverRandom.buffer());
sig.update(serverParams);
byte[] sigVal = sig.sign();
- Signature signature = new Signature(sigVal, suite.signatureAlgorithm());
+ Signature signature = new Signature(sigVal, engine.session().suite.signatureAlgorithm());
return signature.buffer();
}
catch (NoSuchAlgorithmException nsae)
@@ -1148,7 +1250,7 @@ class ServerHandshake extends AbstractHandshake
}
byte[] toSign = null;
if (engine.session().version == ProtocolVersion.SSL_3)
- toSign = genV2CertificateVerify(md5copy, shacopy, engine.session());
+ toSign = genV3CertificateVerify(md5copy, shacopy, engine.session());
else
{
if (engine.session().suite.signatureAlgorithm() == SignatureAlgorithm.RSA)
diff --git a/gnu/javax/net/ssl/provider/ServerHello.java b/gnu/javax/net/ssl/provider/ServerHello.java
index f58a65f46..2bbce37fb 100644
--- a/gnu/javax/net/ssl/provider/ServerHello.java
+++ b/gnu/javax/net/ssl/provider/ServerHello.java
@@ -73,16 +73,15 @@ public class ServerHello implements Handshake.Body
protected static final int SESSID_OFFSET2 = SESSID_OFFSET + 1;
protected ByteBuffer buffer;
-
- /** The total length of the message, including the extensions. */
- private int totalLength;
-
+ protected boolean disableExtensions;
+
// Constructor.
// -------------------------------------------------------------------------
public ServerHello (final ByteBuffer buffer)
{
this.buffer = buffer;
+ disableExtensions = false;
}
public int length ()
@@ -90,7 +89,7 @@ public class ServerHello implements Handshake.Body
int sessionLen = buffer.get(SESSID_OFFSET) & 0xFF;
int len = SESSID_OFFSET2 + sessionLen + 3;
int elen = 0;
- if (len + 1 < buffer.limit()
+ if (!disableExtensions && len + 1 < buffer.limit()
&& (elen = buffer.getShort(len)) != 0)
len += 2 + elen;
return len;
diff --git a/gnu/javax/net/ssl/provider/ServerHelloBuilder.java b/gnu/javax/net/ssl/provider/ServerHelloBuilder.java
index a52e7a835..09ad1d9e8 100644
--- a/gnu/javax/net/ssl/provider/ServerHelloBuilder.java
+++ b/gnu/javax/net/ssl/provider/ServerHelloBuilder.java
@@ -96,6 +96,11 @@ public class ServerHelloBuilder extends ServerHello implements Builder
// For extensions, we do reallocate the buffer.
+ public void setDisableExtensions(boolean disable)
+ {
+ disableExtensions = disable;
+ }
+
public void setExtensionsLength (final int length)
{
if (length < 0 || length > 16384)
diff --git a/gnu/javax/net/ssl/provider/ServerNameList.java b/gnu/javax/net/ssl/provider/ServerNameList.java
index 218e04a34..f119be637 100644
--- a/gnu/javax/net/ssl/provider/ServerNameList.java
+++ b/gnu/javax/net/ssl/provider/ServerNameList.java
@@ -6,6 +6,7 @@ import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.util.NoSuchElementException;
@@ -42,12 +43,12 @@ public class ServerNameList extends Value implements Iterable<ServerNameList.Ser
public ServerNameList (final ByteBuffer buffer)
{
- this.buffer = buffer;
+ this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN);
}
public int length()
{
- return buffer.getShort(0) & 0xFFFF;
+ return (buffer.getShort(0) & 0xFFFF) + 2;
}
public int size()
@@ -70,7 +71,7 @@ public class ServerNameList extends Value implements Iterable<ServerNameList.Ser
throw new IndexOutOfBoundsException("0; " + index);
int n = 0;
int i;
- int l = 0;
+ int l = buffer.getShort(3);
for (i = 2; i < len && n < index; )
{
l = buffer.getShort(i+1);
@@ -156,7 +157,7 @@ public class ServerNameList extends Value implements Iterable<ServerNameList.Ser
public ServerName(final ByteBuffer buffer)
{
- this.buffer = buffer;
+ this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN);
}
public int length()
diff --git a/gnu/javax/net/ssl/provider/SessionImpl.java b/gnu/javax/net/ssl/provider/SessionImpl.java
index 2e470977b..86dcb4915 100644
--- a/gnu/javax/net/ssl/provider/SessionImpl.java
+++ b/gnu/javax/net/ssl/provider/SessionImpl.java
@@ -69,6 +69,12 @@ public class SessionImpl extends Session
transient PrivateData privateData;
+ public SessionImpl()
+ {
+ super();
+ privateData = new PrivateData();
+ }
+
SecureRandom random ()
{
return random;
@@ -148,15 +154,15 @@ public class SessionImpl extends Session
{
this.sealedPrivateData = so;
}
-
- void setRandom(SecureRandom random)
+
+ void setApplicationBufferSize(int size)
{
- this.random = random;
+ applicationBufferSize = size;
}
- void setPacketBufferSize(int size)
+ void setRandom(SecureRandom random)
{
- this.packetBufferSize = size;
+ this.random = random;
}
void setTruncatedMac(boolean truncatedMac)
diff --git a/gnu/javax/net/ssl/provider/SignatureAlgorithm.java b/gnu/javax/net/ssl/provider/SignatureAlgorithm.java
index e5c691b2e..a789576db 100644
--- a/gnu/javax/net/ssl/provider/SignatureAlgorithm.java
+++ b/gnu/javax/net/ssl/provider/SignatureAlgorithm.java
@@ -40,5 +40,23 @@ package gnu.javax.net.ssl.provider;
public enum SignatureAlgorithm
{
- ANONYMOUS, RSA, DSA
+ ANONYMOUS, RSA, DSA;
+
+ /**
+ * Returns the algorithm name for this signature algorithm, which can
+ * be used with the JCA API to get a {@link java.security.Signature} for
+ * that algorithm.
+ *
+ * @return The algorithm name.
+ */
+ public String algorithm()
+ {
+ switch (this)
+ {
+ case ANONYMOUS: return null;
+ case RSA: return "TLSv1.1-RSA";
+ case DSA: return "DSS";
+ }
+ return null;
+ }
}
diff --git a/gnu/javax/net/ssl/provider/Util.java b/gnu/javax/net/ssl/provider/Util.java
index f541c6c5c..0dae5a712 100644
--- a/gnu/javax/net/ssl/provider/Util.java
+++ b/gnu/javax/net/ssl/provider/Util.java
@@ -57,7 +57,7 @@ import java.security.Security;
*
* @author Casey Marshall (rsdio@metastatic.org)
*/
-final class Util
+public final class Util
{
// Constants.
@@ -77,7 +77,7 @@ final class Util
* @param hex The hexadecimal string.
* @return The converted bytes.
*/
- static byte[] toByteArray(String hex)
+ public static byte[] toByteArray(String hex)
{
hex = hex.toLowerCase();
byte[] buf = new byte[hex.length() / 2];
@@ -99,7 +99,7 @@ final class Util
* @param len The number of bytes to format.
* @return A hexadecimal representation of the specified bytes.
*/
- static String toHexString(byte[] buf, int off, int len)
+ public static String toHexString(byte[] buf, int off, int len)
{
StringBuffer str = new StringBuffer();
for (int i = 0; i < len; i++)
@@ -113,7 +113,7 @@ final class Util
/**
* See {@link #toHexString(byte[],int,int)}.
*/
- static String toHexString(byte[] buf)
+ public static String toHexString(byte[] buf)
{
return Util.toHexString(buf, 0, buf.length);
}
@@ -128,7 +128,7 @@ final class Util
* @param sep The character to insert between octets.
* @return A hexadecimal representation of the specified bytes.
*/
- static String toHexString(byte[] buf, int off, int len, char sep)
+ public static String toHexString(byte[] buf, int off, int len, char sep)
{
StringBuffer str = new StringBuffer();
for (int i = 0; i < len; i++)
@@ -144,7 +144,7 @@ final class Util
/**
* See {@link #toHexString(byte[],int,int,char)}.
*/
- static String toHexString(byte[] buf, char sep)
+ public static String toHexString(byte[] buf, char sep)
{
return Util.toHexString(buf, 0, buf.length, sep);
}
@@ -164,7 +164,7 @@ final class Util
* @param prefix A string to prepend to every line.
* @return The formatted string.
*/
- static String hexDump(byte[] buf, int off, int len, String prefix)
+ public static String hexDump(byte[] buf, int off, int len, String prefix)
{
String nl = getProperty("line.separator");
StringBuffer str = new StringBuffer();
@@ -192,48 +192,49 @@ final class Util
return str.toString();
}
- static String hexDump (ByteBuffer buf)
+ public static String hexDump (ByteBuffer buf)
{
return hexDump (buf, null);
}
- static String hexDump (ByteBuffer buf, String prefix)
+ public static String hexDump (ByteBuffer buf, String prefix)
{
+ buf = buf.duplicate();
StringWriter str = new StringWriter ();
PrintWriter out = new PrintWriter (str);
int i = 0;
- int len = buf.limit ();
+ int len = buf.remaining();
byte[] line = new byte[16];
while (i < len)
{
if (prefix != null)
- out.print (prefix);
- out.print (Util.formatInt (i, 16, 8));
- out.print (" ");
- int l = Math.min (16, len - i);
- buf.get (line, 0, l);
- String s = Util.toHexString (line, 0, l, ' ');
- out.print (s);
- for (int j = s.length (); j < 49; j++)
- out.print (' ');
+ out.print(prefix);
+ out.print(Util.formatInt (i, 16, 8));
+ out.print(" ");
+ int l = Math.min(16, len - i);
+ buf.get(line, 0, l);
+ String s = Util.toHexString(line, 0, l, ' ');
+ out.print(s);
+ for (int j = s.length(); j < 49; j++)
+ out.print(' ');
for (int j = 0; j < l; j++)
{
int c = line[j] & 0xFF;
if (c < 0x20 || c > 0x7E)
- out.print ('.');
+ out.print('.');
else
- out.print ((char) c);
+ out.print((char) c);
}
- out.println ();
+ out.println();
i += 16;
}
- return str.toString ();
+ return str.toString();
}
/**
* See {@link #hexDump(byte[],int,int,String)}.
*/
- static String hexDump(byte[] buf, int off, int len)
+ public static String hexDump(byte[] buf, int off, int len)
{
return hexDump(buf, off, len, "");
}
@@ -241,7 +242,7 @@ final class Util
/**
* See {@link #hexDump(byte[],int,int,String)}.
*/
- static String hexDump(byte[] buf, String prefix)
+ public static String hexDump(byte[] buf, String prefix)
{
return hexDump(buf, 0, buf.length, prefix);
}
@@ -249,7 +250,7 @@ final class Util
/**
* See {@link #hexDump(byte[],int,int,String)}.
*/
- static String hexDump(byte[] buf)
+ public static String hexDump(byte[] buf)
{
return hexDump(buf, 0, buf.length);
}
@@ -263,7 +264,7 @@ final class Util
* zero-padded to this length, but may be longer.
* @return The formatted integer.
*/
- static String formatInt(int i, int radix, int len)
+ public static String formatInt(int i, int radix, int len)
{
String s = Integer.toString(i, radix);
StringBuffer buf = new StringBuffer();
@@ -280,7 +281,7 @@ final class Util
* @param b2 The second byte array.
* @return The concatenation of b1 and b2.
*/
- static byte[] concat(byte[] b1, byte[] b2)
+ public static byte[] concat(byte[] b1, byte[] b2)
{
byte[] b3 = new byte[b1.length+b2.length];
System.arraycopy(b1, 0, b3, 0, b1.length);
@@ -291,7 +292,7 @@ final class Util
/**
* See {@link #trim(byte[],int,int)}.
*/
- static byte[] trim(byte[] buffer, int len)
+ public static byte[] trim(byte[] buffer, int len)
{
return trim(buffer, 0, len);
}
@@ -309,7 +310,7 @@ final class Util
* length.
* @return The trimmed byte array.
*/
- static byte[] trim(byte[] buffer, int off, int len)
+ public static byte[] trim(byte[] buffer, int off, int len)
{
if (off < 0 || len < 0 || off > buffer.length)
throw new IndexOutOfBoundsException("max=" + buffer.length +
@@ -329,7 +330,7 @@ final class Util
* @return The byte representation of the big integer, with any leading
* zero removed.
*/
- static byte[] trim(BigInteger bi)
+ public static byte[] trim(BigInteger bi)
{
byte[] buf = bi.toByteArray();
if (buf[0] == 0x00 && !bi.equals(BigInteger.ZERO))
@@ -348,7 +349,7 @@ final class Util
*
* @return The current time, in seconds.
*/
- static int unixTime()
+ public static int unixTime()
{
return (int) (System.currentTimeMillis() / 1000L);
}
@@ -428,7 +429,7 @@ final class Util
* @throws SecurityException If the Jessie code still does not have
* permission to read the property.
*/
- static String getProperty(final String name)
+ @Deprecated static String getProperty(final String name)
{
return (String) AccessController.doPrivileged(
new PrivilegedAction()
@@ -450,7 +451,7 @@ final class Util
* @throws SecurityException If the Jessie code still does not have
* permission to read the property.
*/
- static String getSecurityProperty(final String name)
+ @Deprecated static String getSecurityProperty(final String name)
{
return (String) AccessController.doPrivileged(
new PrivilegedAction()
diff --git a/gnu/javax/net/ssl/provider/X509KeyManagerFactory.java b/gnu/javax/net/ssl/provider/X509KeyManagerFactory.java
index 2e5e3ddd9..b82f5b71e 100644
--- a/gnu/javax/net/ssl/provider/X509KeyManagerFactory.java
+++ b/gnu/javax/net/ssl/provider/X509KeyManagerFactory.java
@@ -339,15 +339,16 @@ public class X509KeyManagerFactory extends KeyManagerFactorySpi
!(pubKey instanceof RSAPublicKey))
continue;
}
- if (keyType.equalsIgnoreCase("DHE_DSS")
+ else if (keyType.equalsIgnoreCase("DHE_DSS")
|| keyType.equalsIgnoreCase("dss_sign")
- || keyType.equalsIgnoreCase("SRP_DSS"))
+ || keyType.equalsIgnoreCase("SRP_DSS")
+ || keyType.equalsIgnoreCase("DSA"))
{
if (!(privKey instanceof DSAPrivateKey) ||
!(pubKey instanceof DSAPublicKey))
continue;
}
- if (keyType.equalsIgnoreCase("DH_RSA")
+ else if (keyType.equalsIgnoreCase("DH_RSA")
|| keyType.equalsIgnoreCase("rsa_fixed_dh"))
{
if (!(privKey instanceof DHPrivateKey) ||
@@ -356,7 +357,7 @@ public class X509KeyManagerFactory extends KeyManagerFactorySpi
if (!chain[0].getSigAlgName().equalsIgnoreCase("RSA"))
continue;
}
- if (keyType.equalsIgnoreCase("DH_DSS")
+ else if (keyType.equalsIgnoreCase("DH_DSS")
|| keyType.equalsIgnoreCase("dss_fixed_dh"))
{
if (!(privKey instanceof DHPrivateKey) ||
@@ -365,6 +366,8 @@ public class X509KeyManagerFactory extends KeyManagerFactorySpi
if (!chain[0].getSigAlgName().equalsIgnoreCase("DSA"))
continue;
}
+ else // Unknown key type; ignore it.
+ continue;
if (issuers == null || issuers.length == 0)
{
l.add(alias);