This implements the SSL hello extension values from RFC 3546. These extensions allow clients to request extended functionality from a server.

It's unlikely that we will support all of these extensions in Jessie, at least not in this Summer of Code release, but we can still recognize and parse them. Lots of these new classes turned out to be "pseudo-list" types, and also implement Iterable for their underlying list type. There are a lot of these kinds of lists; unifying them may be a good idea, eventually.

2006-06-10  Casey Marshall  <[EMAIL PROTECTED]>

        * gnu/javax/net/ssl/provider/ServerHello.java (extensions): return
        an ExtensionList.
        (setExtensionsLength): set the length in the buffer.
        (toString): print out individual extensions.
        * gnu/javax/net/ssl/provider/Extension.java (valueBytes): new
        method.
        (valueBuffer): new method.
        (value): return an Extenion.Value.
        (toString): print out extension value.
        (Value): new abstract inner class.
        * gnu/javax/net/ssl/provider/ClientHello.java (extensions): return
        an ExtensionList.
        (setExtensionListLength): set the length in the buffer.
        (toString): print out extensions.
        * gnu/javax/net/ssl/provider/ServerHandshake.java
        (chooseSuite, chooseCompression): use generics and foreach loops.
        * gnu/javax/net/ssl/provider/ExtensionList.java: new class.
        * gnu/javax/net/ssl/provider/MaxFragmentLength.java: new class.
        * gnu/javax/net/ssl/provider/CertificateURL.java: new class.
        * gnu/javax/net/ssl/provider/UnresolvedExtensionValue.java: new
        class.
        * gnu/javax/net/ssl/provider/TruncatedHMAC.java: new class.
        * gnu/javax/net/ssl/provider/ServerNameList.java: new class.
        * gnu/javax/net/ssl/provider/TrustedAuthorities.java: new class.
        * gnu/javax/net/ssl/provider/CertificateStatusType.java: new
        class.
        * gnu/javax/net/ssl/provider/CertificateStatusRequest.java: new
        class.

Committed.

### Eclipse Workspace Patch 1.0
#P classpath-ssl-nio
Index: gnu/javax/net/ssl/provider/ServerHello.java
===================================================================
RCS file: 
/cvsroot/classpath/classpath/gnu/javax/net/ssl/provider/ServerHello.java,v
retrieving revision 1.1.4.1.2.1
diff -u -r1.1.4.1.2.1 ServerHello.java
--- gnu/javax/net/ssl/provider/ServerHello.java 3 Jun 2006 07:49:53 -0000       
1.1.4.1.2.1
+++ gnu/javax/net/ssl/provider/ServerHello.java 11 Jun 2006 06:50:20 -0000
@@ -67,6 +67,7 @@
   SessionID session_id;
   CipherSuite cipher_suite;
   CompressionMethod compression_method;
+  Extensions server_hello_extension_list&lt;0..2^16-1&gt;
 } ServerHello;
 </pre>
  *
@@ -171,11 +172,19 @@
     return CompressionMethod.getInstance (buffer.get (offset) & 0xFF);
   }
 
-  public ByteBuffer extensions ()
+  public ExtensionList extensions ()
   {
     int offset = SESSID_OFFSET + (buffer.get (SESSID_OFFSET) & 0xFF) + 4;
-    return ((ByteBuffer) buffer.duplicate ().position (offset)
-            .limit (totalLength)).slice ();
+    if (offset == totalLength)
+      return null;
+    if (buffer.limit() <= offset)
+      return null;
+    int len = buffer.getShort(offset) & 0xFFFF;
+    if (len == 0)
+      len = buffer.limit() - offset - 2;
+    ByteBuffer ebuf = ((ByteBuffer) buffer.duplicate ().position (offset)
+                       .limit (offset + len + 2)).slice ();
+    return new ExtensionList (ebuf);
   }
 
   public void setVersion (final ProtocolVersion version)
@@ -213,7 +222,9 @@
   public void setExtensionsLength (final int length)
   {
     totalLength = (SESSID_OFFSET + (buffer.get (SESSID_OFFSET) & 0xFF)
-                   + 4 + length);
+                   + 4 + length + 4);
+    buffer.putShort (SESSID_OFFSET + (buffer.get (SESSID_OFFSET) & 0xFF) + 4,
+                     (short) length);
   }
 
   public String toString ()
@@ -250,13 +261,11 @@
     out.print ("compressionMethod: ");
     out.print (compressionMethod ());
     out.println (";");
-    ByteBuffer extbuf = extensions ();
-    if (extbuf.limit () > 0)
-      {
-        out.print (subprefix);
-        out.println ("extensions:");
-        out.print (Util.hexDump (extbuf, subprefix));
-      }
+    ExtensionList exts = extensions ();
+    out.print (subprefix);
+    out.println ("extensions:");
+    out.println (exts != null ? exts.toString (subprefix+"  ")
+                                : subprefix + "  (nil)");
     if (prefix != null)
       out.print (prefix);
     out.print ("} ServerHello;");
Index: gnu/javax/net/ssl/provider/Extension.java
===================================================================
RCS file: 
/cvsroot/classpath/classpath/gnu/javax/net/ssl/provider/Extension.java,v
retrieving revision 1.1.4.1.2.2
diff -u -r1.1.4.1.2.2 Extension.java
--- gnu/javax/net/ssl/provider/Extension.java   6 Jun 2006 01:01:19 -0000       
1.1.4.1.2.2
+++ gnu/javax/net/ssl/provider/Extension.java   11 Jun 2006 06:50:19 -0000
@@ -83,7 +83,7 @@
     return Type.forValue (buffer.getShort (0) & 0xFFFF);
   }
 
-  public byte[] value()
+  public byte[] valueBytes()
   {
     int len = buffer.getShort (2) & 0xFFFF;
     byte[] value = new byte[len];
@@ -91,6 +91,48 @@
     return value;
   }
   
+  public ByteBuffer valueBuffer()
+  {
+    int len = buffer.getShort(2) & 0xFFFF;
+    return ((ByteBuffer) buffer.duplicate().position(4).limit(len+4)).slice();
+  }
+  
+  public Value value()
+  {
+    switch (type ())
+      {
+        case SERVER_NAME:
+          return new ServerNameList(valueBuffer());
+          
+        case MAX_FRAGMENT_LENGTH:
+          switch (valueBuffer().get() & 0xFF)
+            {
+              case 1: return MaxFragmentLength.LEN_2_9;
+              case 2: return MaxFragmentLength.LEN_2_10;
+              case 3: return MaxFragmentLength.LEN_2_11;
+              case 4: return MaxFragmentLength.LEN_2_12;
+              default:
+                throw new IllegalArgumentException("invalid max_fragment_len");
+            }
+          
+        case TRUNCATED_HMAC:
+          return new TruncatedHMAC();
+
+        case CLIENT_CERTIFICATE_URL:
+          return new CertificateURL(valueBuffer());
+          
+        case TRUSTED_CA_KEYS:
+          return new TrustedAuthorities(valueBuffer());
+          
+        case STATUS_REQUEST:
+          return new CertificateStatusRequest(valueBuffer());
+          
+        case SRP:
+        case CERT_TYPE:
+      }
+    return new UnresolvedExtensionValue(valueBuffer());
+  }
+  
   public void setLength (final int newLength)
   {
     if (newLength < 0 || newLength > 65535)
@@ -130,13 +172,14 @@
     out.println("  type = " + type () + ";");
     if (prefix != null) out.print (prefix);
     out.println("  value =");
-    out.println(Util.hexDump(value (), (prefix != null) ? prefix + "    " : "  
  "));
+//    out.println(Util.hexDump(value (), (prefix != null) ? prefix + "    " : 
"    "));
+    out.println(value());
     if (prefix != null) out.print (prefix);
     out.print("} Extension;");
     return str.toString();
   }
 
-  // Inner class.
+  // Inner classes.
   // -------------------------------------------------------------------------
 
   public static enum Type
@@ -178,4 +221,8 @@
       return value;
     }
   }
+  
+  public static abstract class Value implements Constructed
+  {
+  }
 }
Index: gnu/javax/net/ssl/provider/ClientHello.java
===================================================================
RCS file: 
/cvsroot/classpath/classpath/gnu/javax/net/ssl/provider/ClientHello.java,v
retrieving revision 1.1.4.1.2.1
diff -u -r1.1.4.1.2.1 ClientHello.java
--- gnu/javax/net/ssl/provider/ClientHello.java 3 Jun 2006 07:49:53 -0000       
1.1.4.1.2.1
+++ gnu/javax/net/ssl/provider/ClientHello.java 11 Jun 2006 06:50:19 -0000
@@ -67,6 +67,7 @@
   SessionID         session_id;                    // 1 + 0..32
   CipherSuite       cipher_suites&lt;2..2^16-1&gt;
   CompressionMethod compression_methods&lt;1..2^8-1&gt;
+  Extension         client_hello_extension_list&lt;0..2^16-1&gt;
 } ClientHello;
 </pre>
  */
@@ -156,11 +157,17 @@
     return new CompressionMethodList (listBuf);
   }
 
-  public ByteBuffer extensions()
+  public ExtensionList extensions()
   {
     int offset = getExtensionsOffset ();
-    return ((ByteBuffer) buffer.duplicate ().position (offset)
-            .limit (totalLength)).slice ();
+    if (offset >= buffer.limit())
+      return null;
+    int len = buffer.getShort(offset) & 0xFFFF;
+    if (len == 0)
+      len = buffer.limit() - offset - 2;
+    ByteBuffer ebuf = ((ByteBuffer) buffer.duplicate ().position (offset)
+                       .limit (offset + len + 2)).slice ();
+    return new ExtensionList (ebuf);
   }
 
   public void setVersion (final ProtocolVersion version)
@@ -183,7 +190,9 @@
 
   public void setExtensionsLength (final int length)
   {
-    this.totalLength = getExtensionsOffset () + length;
+    int offset = getExtensionsOffset();
+    this.totalLength = offset + length + 4;
+    buffer.putShort(offset, (short) length);
   }
 
   private int getCipherSuitesOffset ()
@@ -238,13 +247,10 @@
     out.print (subprefix);
     out.println ("compression_methods:");
     out.println (compressionMethods ().toString (subprefix));
-    ByteBuffer extbuf = extensions ();
-    if (extbuf.limit () > 0)
-      {
-        out.print (subprefix);
-        out.println ("extensions:");
-        out.print (Util.hexDump (extbuf, subprefix));
-      }
+    out.print (subprefix);
+    out.print ("extensions: ");
+    ExtensionList el = extensions();
+    out.println (el != null ? el.toString(subprefix+"  ") : "(nil)");
     if (prefix != null)
       out.print (prefix);
     out.print ("} ClientHello;");
Index: gnu/javax/net/ssl/provider/ServerHandshake.java
===================================================================
RCS file: 
/cvsroot/classpath/classpath/gnu/javax/net/ssl/provider/Attic/ServerHandshake.java,v
retrieving revision 1.1.2.1
diff -u -r1.1.2.1 ServerHandshake.java
--- gnu/javax/net/ssl/provider/ServerHandshake.java     3 Jun 2006 07:49:53 
-0000       1.1.2.1
+++ gnu/javax/net/ssl/provider/ServerHandshake.java     11 Jun 2006 06:50:20 
-0000
@@ -127,19 +127,18 @@
                                           final ProtocolVersion version)
     throws SSLException
   {
-    HashSet suites = new HashSet (enabledSuites.length);
-    for (int i = 0; i < enabledSuites.length; i++)
+    HashSet<CipherSuite> suites = new HashSet<CipherSuite> 
(enabledSuites.length);
+    for (String s : enabledSuites)
       {
-        CipherSuite suite = CipherSuite.forName (enabledSuites[i]);
+        CipherSuite suite = CipherSuite.forName (s);
         if (suite != null)
           {
             suite = suite.resolve (version);
             suites.add (suite);
           }
       }
-    for (int i = 0; i < clientSuites.size (); i++)
+    for (CipherSuite suite : clientSuites)
       {
-        CipherSuite suite = clientSuites.get (i);
         if (suites.contains (suite))
           return suite.resolve (version);
       }
@@ -159,14 +158,14 @@
     throws SSLException
   {
     // Scan for ZLIB first.
-    for (int i = 0; i < comps.size (); i++)
+    for (CompressionMethod cm : comps)
       {
-        if (comps.get (i).equals (CompressionMethod.ZLIB))
+        if (cm.equals (CompressionMethod.ZLIB))
           return CompressionMethod.ZLIB;
       }
-    for (int i = 0; i < comps.size (); i++)
+    for (CompressionMethod cm : comps)
       {
-        if (comps.get (i).equals (CompressionMethod.NULL))
+        if (cm.equals (CompressionMethod.NULL))
           return CompressionMethod.NULL;
       }
 
Index: gnu/javax/net/ssl/provider/ExtensionList.java
===================================================================
RCS file: 
/cvsroot/classpath/classpath/gnu/javax/net/ssl/provider/Attic/ExtensionList.java,v
retrieving revision 1.1.2.1
diff -u -r1.1.2.1 ExtensionList.java
--- gnu/javax/net/ssl/provider/ExtensionList.java       6 Jun 2006 01:01:19 
-0000       1.1.2.1
+++ gnu/javax/net/ssl/provider/ExtensionList.java       11 Jun 2006 06:50:19 
-0000
@@ -113,7 +113,7 @@
                                          + "list length");
     buffer.putShort(i, (short) e.type().getValue());
     buffer.putShort(i+2, (short) e.length());
-    ((ByteBuffer) buffer.duplicate().position(i+4)).put (e.value());
+    ((ByteBuffer) buffer.duplicate().position(i+4)).put (e.valueBuffer());
     modCount++;
   }
   
Index: gnu/javax/net/ssl/provider/MaxFragmentLength.java
===================================================================
RCS file: gnu/javax/net/ssl/provider/MaxFragmentLength.java
diff -N gnu/javax/net/ssl/provider/MaxFragmentLength.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ gnu/javax/net/ssl/provider/MaxFragmentLength.java   1 Jan 1970 00:00:00 
-0000
@@ -0,0 +1,52 @@
+package gnu.javax.net.ssl.provider;
+
+import gnu.javax.net.ssl.provider.Extension.Value;
+
+/**
+ * Extension value 
+ * @author csm
+ */
+public class MaxFragmentLength extends Value
+{
+  public static final MaxFragmentLength LEN_2_9 = new MaxFragmentLength(1, 1 
<< 9);
+  public static final MaxFragmentLength LEN_2_10 = new MaxFragmentLength(2, 1 
<< 10);
+  public static final MaxFragmentLength LEN_2_11 = new MaxFragmentLength(3, 1 
<< 11);
+  public static final MaxFragmentLength LEN_2_12 = new MaxFragmentLength(4, 1 
<< 12);
+  
+  private final int value;
+  private final int length;
+  
+  private MaxFragmentLength(int value, int length)
+  {
+    this.value = value;
+    this.length = length;
+  }
+  
+  public int length()
+  {
+    return 1;
+  }
+
+  public int getValue ()
+  {
+    return value;
+  }
+  
+  public int maxLength()
+  {
+    return length;
+  }
+  
+  public String toString()
+  {
+    return toString(null);
+  }
+  
+  public String toString(String prefix)
+  {
+    String s = "max_fragment_length = ";
+    if (prefix != null)
+      s = prefix + s;
+    return s + maxLength() + ";";
+  }
+}
Index: gnu/javax/net/ssl/provider/CertificateURL.java
===================================================================
RCS file: gnu/javax/net/ssl/provider/CertificateURL.java
diff -N gnu/javax/net/ssl/provider/CertificateURL.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ gnu/javax/net/ssl/provider/CertificateURL.java      1 Jan 1970 00:00:00 
-0000
@@ -0,0 +1,300 @@
+package gnu.javax.net.ssl.provider;
+
+import gnu.javax.net.ssl.provider.Extension.Value;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.NoSuchElementException;
+
+/**
+ * The CertificateURL extension value.
+ * 
+ * <pre>
+enum {
+  individual_certs(0), pkipath(1), (255)
+} CertChainType;
+
+enum {
+  false(0), true(1)
+} Boolean;
+
+struct {
+  CertChainType type;
+  URLAndOptionalHash url_and_hash_list&lt;1..2^16-1&gt;;
+} CertificateURL;
+
+struct {
+  opaque url&lt;1..2^16-1&gt;;
+  Boolean hash_present;
+  select (hash_present) {
+    case false: struct {};
+    case true: SHA1Hash;
+  } hash;
+} URLAndOptionalHash;
+
+opaque SHA1Hash[20];</pre>
+ *
+ * @author csm
+ *
+ */
+public class CertificateURL extends Value implements 
Iterable<CertificateURL.URLAndOptionalHash>
+{
+  private final ByteBuffer buffer;
+  
+  public CertificateURL(final ByteBuffer buffer)
+  {
+    this.buffer = buffer;
+  }
+  
+  public int length()
+  {
+    return 3 + (buffer.getShort(1) & 0xFFFF);
+  }
+
+  public CertChainType type()
+  {
+    switch (buffer.get(0))
+      {
+        case 0: return CertChainType.INDIVIDUAL_CERTS;
+        case 1: return CertChainType.PKIPATH;
+      }
+    throw new IllegalArgumentException("unknown certificate URL type");
+  }
+  
+  public int size()
+  {
+    int len = buffer.getShort(1) & 0xFFFF;
+    int n = 0;
+    for (int i = 3; i < len; )
+      {
+        URLAndOptionalHash u
+          = new URLAndOptionalHash((ByteBuffer) 
buffer.duplicate().position(i));
+        int l = u.length();
+        i += l;
+        n++;
+      }
+    return n;
+  }
+  
+  public URLAndOptionalHash get(int index)
+  {
+    int len = buffer.getShort(1) & 0xFFFF;
+    int n = 0;
+    int l = 0;
+    int i;
+    for (i = 3; i < len && n < index; )
+      {
+        URLAndOptionalHash u
+          = new URLAndOptionalHash((ByteBuffer) 
buffer.duplicate().position(i));
+        l = u.length();
+        i += l;
+        n++;
+      }
+    if (n < index)
+      throw new IndexOutOfBoundsException();
+    return new URLAndOptionalHash(((ByteBuffer) 
buffer.duplicate().position(i).limit(i+l)).slice());
+  }
+  
+  public void set(int index, URLAndOptionalHash url)
+  {
+    int len = buffer.getShort(1) & 0xFFFF;
+    int n = 0;
+    int i;
+    for (i = 3; i < len && n < index-1; )
+      {
+        URLAndOptionalHash u
+          = new URLAndOptionalHash((ByteBuffer) 
buffer.duplicate().position(i));
+        int l = u.length();
+        i += l;
+        n++;
+      }
+    if (n < index - 1)
+      throw new IndexOutOfBoundsException();
+    int l = url.urlLength();
+    buffer.putShort(i, (short) l);
+    ((ByteBuffer) buffer.duplicate().position(i+2)).put(url.urlBuffer());
+    buffer.put(i+l+2, (byte) (url.hashPresent() ? 1 : 0));
+    if (url.hashPresent())
+      ((ByteBuffer) buffer.duplicate().position(i+l+3)).put (url.sha1Hash());
+  }
+  
+  public void setLength(final int length)
+  {
+    if (length < 0 || length > 65535)
+      throw new IllegalArgumentException("length must be between 0 and 65535");
+    buffer.putShort(1, (short) length);
+  }
+  
+  public String toString()
+  {
+    return toString(null);
+  }
+  
+  public String toString(String prefix)
+  {
+    StringWriter str = new StringWriter();
+    PrintWriter out = new PrintWriter(str);
+    if (prefix != null) out.print(prefix);
+    out.println ("struct {");
+    if (prefix != null) out.print(prefix);
+    out.print("  type = ");
+    out.print(type());
+    out.println(";");
+    if (prefix != null) out.print(prefix);
+    out.println("  url_and_hash_list = {");
+    String subprefix = "  ";
+    if (prefix != null) subprefix = prefix + subprefix;
+    for (URLAndOptionalHash url : this)
+      {
+        out.println(url.toString(subprefix));
+      }
+    if (prefix != null) out.print(prefix);
+    out.println("  };");
+    if (prefix != null) out.print(prefix);
+    out.print("} CertificateURL;");
+    return str.toString();
+  }
+
+  public java.util.Iterator<URLAndOptionalHash> iterator()
+  {
+    return new Iterator();
+  }
+  
+  public class Iterator implements java.util.Iterator<URLAndOptionalHash>
+  {
+    private int index;
+    
+    public Iterator()
+    {
+      index = 0;
+    }
+    
+    public URLAndOptionalHash next() throws NoSuchElementException
+    {
+      try
+        {
+          return get(index++);
+        }
+      catch (IndexOutOfBoundsException ioobe)
+        {
+          throw new NoSuchElementException();
+        }
+    }
+    
+    public boolean hasNext()
+    {
+      return index < size();
+    }
+    
+    public void remove()
+    {
+      throw new UnsupportedOperationException();
+    }
+  }
+  
+  public static enum CertChainType
+  {
+    INDIVIDUAL_CERTS (0), PKIPATH (1);
+    
+    private final int value;
+    
+    private CertChainType (final int value)
+    {
+      this.value = value;
+    }
+    
+    public int getValue()
+    {
+      return value;
+    }
+  }
+  
+  public static class URLAndOptionalHash implements Constructed
+  {
+    private final ByteBuffer buffer;
+    
+    public URLAndOptionalHash (final ByteBuffer buffer)
+    {
+      this.buffer = buffer;
+    }
+    
+    public int length()
+    {
+      return ((buffer.getShort(0) & 0xFFFF)
+              + (hashPresent() ? 23 : 3));
+    }
+    
+    public String url()
+    {
+      Charset cs = Charset.forName("ASCII");
+      return cs.decode(urlBuffer()).toString();
+    }
+    
+    public int urlLength()
+    {
+      return buffer.getShort(0) & 0xFFFF;
+    }
+    
+    public ByteBuffer urlBuffer()
+    {
+      int len = urlLength();
+      return ((ByteBuffer) 
buffer.duplicate().position(2).limit(2+len)).slice();
+    }
+    
+    public boolean hashPresent()
+    {
+      int i = (buffer.getShort(0) & 0xFFFF) + 2;
+      byte b = buffer.get(i);
+      if (b == 0)
+        return false;
+      if (b == 1)
+        return true;
+      throw new IllegalArgumentException("expecting 0 or 1: " + (b & 0xFF));
+    }
+    
+    public byte[] sha1Hash()
+    {
+      int i = (buffer.getShort(0) & 0xFFFF) + 2;
+      byte b = buffer.get(i);
+      if (b == 0)
+        return null;
+      byte[] buf = new byte[20];
+      ((ByteBuffer) buffer.duplicate().position(i+1)).get(buf);
+      return buf;
+    }
+    
+    public String toString()
+    {
+      return toString(null);
+    }
+    
+    public String toString(final String prefix)
+    {
+      StringWriter str = new StringWriter();
+      PrintWriter out = new PrintWriter(str);
+      if (prefix != null) out.print(prefix);
+      out.println("struct {");
+      if (prefix != null) out.print(prefix);
+      out.print("  url = ");
+      out.print(url());
+      out.println(";");
+      boolean has_hash = hashPresent();
+      if (prefix != null) out.print(prefix);
+      out.print("  hash_present = ");
+      out.print(has_hash);
+      out.println(";");
+      if (has_hash)
+        {
+          if (prefix != null) out.print(prefix);
+          out.print("  sha1Hash = ");
+          out.print(Util.toHexString(sha1Hash(), ':'));
+          out.println(";");
+        }
+      if (prefix != null) out.print(prefix);
+      out.print("} URLAndOptionalHash;");
+      return str.toString();
+    }
+  }
+}
Index: gnu/javax/net/ssl/provider/UnresolvedExtensionValue.java
===================================================================
RCS file: gnu/javax/net/ssl/provider/UnresolvedExtensionValue.java
diff -N gnu/javax/net/ssl/provider/UnresolvedExtensionValue.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ gnu/javax/net/ssl/provider/UnresolvedExtensionValue.java    1 Jan 1970 
00:00:00 -0000
@@ -0,0 +1,40 @@
+package gnu.javax.net.ssl.provider;
+
+import gnu.javax.net.ssl.provider.Extension.Value;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.ByteBuffer;
+
+public class UnresolvedExtensionValue extends Value
+{
+  private final ByteBuffer buffer;
+  
+  public UnresolvedExtensionValue (final ByteBuffer buffer)
+  {
+    this.buffer = buffer;
+  }
+  
+  public int length()
+  {
+    return buffer.limit();
+  }
+  
+  public ByteBuffer value()
+  {
+    return buffer.slice();
+  }
+  
+  public String toString()
+  {
+    return toString(null);
+  }
+  
+  public String toString(final String prefix)
+  {
+    String s = Util.hexDump(buffer);
+    if (prefix != null)
+      s = prefix + s;
+    return s;
+  }
+}
Index: gnu/javax/net/ssl/provider/TruncatedHMAC.java
===================================================================
RCS file: gnu/javax/net/ssl/provider/TruncatedHMAC.java
diff -N gnu/javax/net/ssl/provider/TruncatedHMAC.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ gnu/javax/net/ssl/provider/TruncatedHMAC.java       1 Jan 1970 00:00:00 
-0000
@@ -0,0 +1,31 @@
+package gnu.javax.net.ssl.provider;
+
+import gnu.javax.net.ssl.provider.Extension.Value;
+
+/**
+ * The value type for the [EMAIL PROTECTED] Extension.Type#TRUNCATED_HMAC} 
extension.
+ * This extension has an empty value; this class is thusly empty.
+ * 
+ * @author csm
+ */
+public class TruncatedHMAC extends Value
+{
+
+  public int length()
+  {
+    return 0;
+  }
+  
+  public String toString()
+  {
+    return toString(null);
+  }
+
+  public String toString(String prefix)
+  {
+    String s = "TruncatedHMAC;";
+    if (prefix != null)
+      s = prefix + s;
+    return s;
+  }
+}
Index: gnu/javax/net/ssl/provider/ServerNameList.java
===================================================================
RCS file: gnu/javax/net/ssl/provider/ServerNameList.java
diff -N gnu/javax/net/ssl/provider/ServerNameList.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ gnu/javax/net/ssl/provider/ServerNameList.java      1 Jan 1970 00:00:00 
-0000
@@ -0,0 +1,225 @@
+package gnu.javax.net.ssl.provider;
+
+import gnu.javax.net.ssl.provider.Extension.Value;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.NoSuchElementException;
+
+/**
+ * The ServerName extension.
+ * 
+ * <pre>
+ struct {
+   NameType name_type;
+   select (name_type) {
+     case host_name: HostName;
+   } name;
+} ServerName;
+
+enum {
+  host_name(0), (255)
+} NameType;
+
+opaque HostName<1..2^16-1>;
+
+struct {
+  ServerName server_name_list<1..2^16-1>
+} ServerNameList;</pre>
+ *
+ * <p><b>Implementation note: this class does not currently contain a
+ * <code>set</code> method. If you are modifying this list, then use the
+ * [EMAIL PROTECTED] #get(int)} method, and modify the returned [EMAIL 
PROTECTED] ServerName}.
+ *
+ * @author csm
+ */
+public class ServerNameList extends Value implements 
Iterable<ServerNameList.ServerName>
+{
+  private final ByteBuffer buffer;
+  
+  public ServerNameList (final ByteBuffer buffer)
+  {
+    this.buffer = buffer;
+  }
+
+  public int length()
+  {
+    return buffer.getShort(0) & 0xFFFF;
+  }
+  
+  public int size()
+  {
+    int n = 0;
+    final int len = length();
+    for (int i = 2; i < len; )
+      {
+        int l = buffer.getShort(i+1);
+        i += l + 3;
+        n++;
+      }
+    return n;
+  }
+  
+  public ServerName get (int index)
+  {
+    final int len = length();
+    if (len == 0)
+      throw new IndexOutOfBoundsException("0; " + index);
+    int n = 0;
+    int i;
+    int l = 0;
+    for (i = 2; i < len && n < index; )
+      {
+        l = buffer.getShort(i+1);
+        i += l + 3;
+        n++;
+      }
+    if (n < index)
+      throw new IndexOutOfBoundsException(n + "; " + index);
+    ByteBuffer buf = ((ByteBuffer) 
buffer.duplicate().position(i).limit(i+l+3)).slice();
+    return new ServerName (buf);
+  }
+  
+  public void setLength(final int newLength)
+  {
+    if (newLength < 0 || newLength > 65535)
+      throw new IllegalArgumentException("length must be between 0 and 65535");
+    buffer.putShort(0, (short) newLength);
+  }
+  
+  public String toString()
+  {
+    return toString(null);
+  }
+
+  public String toString(String prefix)
+  {
+    StringWriter str = new StringWriter();
+    PrintWriter out = new PrintWriter(str);
+    if (prefix != null) out.print(prefix);
+    out.println ("ServerNameList {");
+    String subprefix = "  ";
+    if (prefix != null)
+      subprefix = prefix + subprefix;
+    for (ServerName name : this)
+      {
+        out.println (name.toString(subprefix));
+      }
+    if (prefix != null) out.print(prefix);
+    out.print ("};");
+    return str.toString();
+  }
+  
+  public java.util.Iterator<ServerName> iterator()
+  {
+    return new Iterator();
+  }
+
+  public class Iterator implements java.util.Iterator<ServerName>
+  {
+    private int index;
+    
+    public Iterator()
+    {
+      index = 0;
+    }
+    
+    public boolean hasNext()
+    {
+      return index < size();
+    }
+    
+    public ServerName next() throws NoSuchElementException
+    {
+      try
+        {
+          return get (index++);
+        }
+      catch (IndexOutOfBoundsException ioobe)
+        {
+          throw new NoSuchElementException();
+        }
+    }
+    
+    public void remove()
+    {
+      throw new UnsupportedOperationException();
+    }
+  }
+
+  public static class ServerName implements Constructed
+  {
+    private final ByteBuffer buffer;
+    
+    public ServerName(final ByteBuffer buffer)
+    {
+      this.buffer = buffer;
+    }
+    
+    public int length()
+    {
+      return buffer.getShort(1) & 0xFFFF;
+    }
+
+    public NameType type()
+    {
+      int v = (buffer.get(0) & 0xFF);
+      if (v == 0)
+        {
+          return NameType.HOST_NAME;
+        }
+      throw new IllegalArgumentException ("illegal name type: " + v);
+    }
+    
+    public String name()
+    {
+      int len = length();
+      Charset cs = Charset.forName ("UTF-8");
+      return cs.decode(((ByteBuffer) 
buffer.duplicate().position(3).limit(len))).toString();
+    }
+    
+    public String toString()
+    {
+      return toString (null);
+    }
+    
+    public String toString(String prefix)
+    {
+      StringWriter str = new StringWriter();
+      PrintWriter out = new PrintWriter(str);
+      if (prefix != null) out.print (prefix);
+      out.println ("struct {");
+      if (prefix != null) out.print (prefix);
+      out.print ("  name_type = ");
+      out.print (type());
+      out.println (";");
+      if (prefix != null) out.print (prefix);
+      out.print ("  server_name = ");
+      out.print (name());
+      out.println (";");
+      if (prefix != null) out.print (prefix);
+      out.print ("} ServerName;");
+      return str.toString();
+    }
+  }
+
+  public static enum NameType
+  {
+    HOST_NAME (0);
+    
+    private final int value;
+    
+    private NameType (int value)
+    {
+      this.value = value;
+    }
+    
+    public int getValue()
+    {
+      return value;
+    }
+  }
+}
Index: gnu/javax/net/ssl/provider/TrustedAuthorities.java
===================================================================
RCS file: gnu/javax/net/ssl/provider/TrustedAuthorities.java
diff -N gnu/javax/net/ssl/provider/TrustedAuthorities.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ gnu/javax/net/ssl/provider/TrustedAuthorities.java  1 Jan 1970 00:00:00 
-0000
@@ -0,0 +1,252 @@
+package gnu.javax.net.ssl.provider;
+
+import gnu.java.security.x509.X500DistinguishedName;
+import gnu.javax.net.ssl.provider.Extension.Value;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.ByteBuffer;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * The trusted authorities hello extension.
+ * 
+ * <pre>
+struct {
+  TrustedAuthority trusted_authorities_list&lt;0..2^16-1&gt;;
+} TrustedAuthorities;
+
+struct {
+  IdentifierType identifier_type;
+  select (identifier_type) {
+    case pre_agreed: struct {};
+    case key_sha1_hash: SHA1Hash;
+    case x509_name: DistinguishedName;
+    case cert_sha1_hash: SHA1Hash;
+  } identifier;
+} TrustedAuthority;
+
+enum {
+  pre_agreed(0), key_sha1_hash(1), x509_name(2),
+  cert_sha1_hash(3), (255)
+} IdentifierType;
+
+opaque DistinguishedName&lt;1..2^16-1&gt;;</pre>
+ * 
+ * @author csm
+ */
+public class TrustedAuthorities extends Value
+  implements Iterable<TrustedAuthorities.TrustedAuthority>
+{
+  private final ByteBuffer buffer;
+
+  public TrustedAuthorities(final ByteBuffer buffer)
+  {
+    this.buffer = buffer;
+  }
+  
+  public int length()
+  {
+    return 2 + (buffer.getShort(0) & 0xFFFF);
+  }
+  
+  public int size()
+  {
+    int len = buffer.getShort(0) & 0xFFFF;
+    int n = 0;
+    for (int i = 2; i < len; i++)
+      {
+        TrustedAuthority auth =
+          new TrustedAuthority((ByteBuffer) buffer.duplicate().position(i));
+        i += auth.length();
+        n++;
+      }
+    return n;
+  }
+
+  public TrustedAuthority get(final int index)
+  {
+    int len = buffer.getShort(0) & 0xFFFF;
+    int n = 0;
+    int i = 2;
+    while (i < len && n <= index)
+      {
+        TrustedAuthority auth =
+          new TrustedAuthority((ByteBuffer) buffer.duplicate().position(i));
+        if (n == index)
+          return auth;
+        i += auth.length();
+        n++;
+      }
+    throw new IndexOutOfBoundsException();
+  }
+  
+  public String toString()
+  {
+    return toString(null);
+  }
+  
+  public String toString(String prefix)
+  {
+    StringWriter str = new StringWriter();
+    PrintWriter out = new PrintWriter(str);
+    if (prefix != null) out.print(prefix);
+    out.println("struct {");
+    String subprefix = "  ";
+    if (prefix != null)
+      subprefix = prefix + subprefix;
+    for(TrustedAuthority ta : this)
+      out.println(ta);
+    if (prefix != null) out.print(prefix);
+    out.print("} TrustedAuthorities;");
+    return str.toString();
+  }
+  
+  public Iterator<TrustedAuthority> iterator()
+  {
+    return new AuthoritiesIterator();
+  }
+  
+  public class AuthoritiesIterator implements Iterator<TrustedAuthority>
+  {
+    private int index;
+    
+    public AuthoritiesIterator()
+    {
+      index = 0;
+    }
+    
+    public TrustedAuthority next() throws NoSuchElementException
+    {
+      try
+        {
+          return get(index++);
+        }
+      catch (IndexOutOfBoundsException ioobe)
+        {
+          throw new NoSuchElementException();
+        }
+    }
+    
+    public boolean hasNext()
+    {
+      return index < size();
+    }
+    
+    public void remove()
+    {
+      throw new UnsupportedOperationException();
+    }
+  }
+
+  public static class TrustedAuthority implements Constructed
+  {
+    private final ByteBuffer buffer;
+    
+    public TrustedAuthority(final ByteBuffer buffer)
+    {
+      this.buffer = buffer;
+    }
+    
+    public int length()
+    {
+      switch (type().getValue())
+      {
+        case 0: return 1;
+        case 1:
+        case 3: return 21;
+        case 2: return 3 + (buffer.getShort(1) & 0xFFFF);
+      }
+      throw new IllegalArgumentException("unknown authority type");
+    }
+    
+    public byte[] sha1Hash()
+    {
+      IdentifierType t = type();
+      if (t != IdentifierType.CERT_SHA1_HASH
+          && t != IdentifierType.KEY_SHA1_HASH)
+        throw new IllegalArgumentException(t + " does not have a hash value");
+      byte[] b = new byte[20];
+      ((ByteBuffer) buffer.duplicate().position(1)).get(b);
+      return b;
+    }
+    
+    public X500Principal name()
+    {
+      int len = buffer.getShort(1) & 0xFFFF;
+      byte[] b = new byte[len];
+      ((ByteBuffer) buffer.duplicate().position(3)).get(b);
+      return new X500Principal(b);
+    }
+    
+    public IdentifierType type()
+    {
+      switch (buffer.get(0))
+      {
+        case 0: return IdentifierType.PRE_AGREED;
+        case 1: return IdentifierType.KEY_SHA1_HASH;
+        case 2: return IdentifierType.X509_NAME;
+        case 3: return IdentifierType.CERT_SHA1_HASH;
+      }
+      
+      throw new IllegalArgumentException("invalid IdentifierType");
+    }
+    
+    public String toString()
+    {
+      return toString(null);
+    }
+    
+    public String toString(String prefix)
+    {
+      StringWriter str = new StringWriter();
+      PrintWriter out = new PrintWriter(str);
+      if (prefix != null) out.print(prefix);
+      out.println("struct {");
+      if (prefix != null) out.print(prefix);
+      out.print("  identifier_type = ");
+      out.print(type());
+      out.println(";");
+      switch (type().getValue())
+      {
+        case 0: break;
+        case 1:
+        case 3:
+          if (prefix != null) out.print(prefix);
+          out.print("  sha1_hash = ");
+          out.print(Util.toHexString(sha1Hash(), ':'));
+          out.println(";");
+          break;
+          
+        case 2:
+          if (prefix != null) out.print(prefix);
+          out.print("  name = ");
+          out.print(name());
+          out.println(";");
+      }
+      if (prefix != null) out.print(prefix);
+      out.print("} TrustedAuthority;");
+      return str.toString();
+    }
+  }
+  
+  public static enum IdentifierType
+  {
+    PRE_AGREED (0), KEY_SHA1_HASH (1), X509_NAME (2), CERT_SHA1_HASH (3);
+    
+    private final int value;
+    
+    private IdentifierType(final int value)
+    {
+      this.value = value;
+    }
+    
+    public int getValue()
+    {
+      return value;
+    }
+  }
+}
Index: gnu/javax/net/ssl/provider/CertificateStatusType.java
===================================================================
RCS file: gnu/javax/net/ssl/provider/CertificateStatusType.java
diff -N gnu/javax/net/ssl/provider/CertificateStatusType.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ gnu/javax/net/ssl/provider/CertificateStatusType.java       1 Jan 1970 
00:00:00 -0000
@@ -0,0 +1,13 @@
+package gnu.javax.net.ssl.provider;
+
+public enum CertificateStatusType
+{
+  OCSP (1);
+  
+  public final int value;
+  
+  private CertificateStatusType (final int value)
+  {
+    this.value = value;
+  }
+}
Index: gnu/javax/net/ssl/provider/CertificateStatusRequest.java
===================================================================
RCS file: gnu/javax/net/ssl/provider/CertificateStatusRequest.java
diff -N gnu/javax/net/ssl/provider/CertificateStatusRequest.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ gnu/javax/net/ssl/provider/CertificateStatusRequest.java    1 Jan 1970 
00:00:00 -0000
@@ -0,0 +1,204 @@
+package gnu.javax.net.ssl.provider;
+
+import gnu.javax.net.ssl.provider.Extension.Value;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.ByteBuffer;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * <pre>
+struct {
+  CertificateStatusType status_type;
+  select (status_type) {
+    case ocsp: OCSPStatusRequest;
+  } request;
+} CertificateStatusRequest;
+
+enum { ocsp(1), (255) } CertificateStatusType;
+
+struct {
+  ResponderID responder_id_list&lt;0..2^16-1&gt;;
+  Extensions  request_extensions;
+} OCSPStatusRequest;
+
+opaque ResponderID&lt;1..2^16-1&gt;;
+opaque Extensions&lt;0..2^16-1&gt;;</pre>
+ *
+ * @author csm
+ */
+public class CertificateStatusRequest extends Value implements Iterable<byte[]>
+{
+  private final ByteBuffer buffer;
+  
+  public CertificateStatusRequest(final ByteBuffer buffer)
+  {
+    this.buffer = buffer;
+  }
+
+  public int length()
+  {
+    int l = 3 + (buffer.getShort(1) & 0xFFFF);
+    return l + (buffer.getShort(l) & 0xFFFF) + 2;
+  }
+  
+  public CertificateStatusType statusType()
+  {
+    int x = buffer.get(0) & 0xFF;
+    if (x == 1)
+      return CertificateStatusType.OCSP;
+    throw new IllegalArgumentException ("invalid type: " + x);
+  }
+
+  public int size()
+  {
+    int len = buffer.getShort(1) & 0xFFFF;
+    int n = 0;
+    for (int i = 3; i < len; )
+      {
+        int l = buffer.getShort(i);
+        i += l + 2;
+        n++;
+      }
+    return n;
+  }
+  
+  public byte[] responderId(int index)
+  {
+    int len = buffer.getShort(1) & 0xFFFF;
+    int n = 0;
+    int i = 3;
+    while (i < len && n <= index)
+      {
+        int l = buffer.getShort(i) & 0xFFFF;
+        if (n == index)
+          {
+            byte[] b = new byte[l];
+            ((ByteBuffer) buffer.duplicate().position(i+2)).get(b);
+            return b;
+          }
+        i += l + 2;
+        n++;
+      }
+    throw new IndexOutOfBoundsException();
+  }
+  
+  public byte[] requestExtensions()
+  {
+    int l = 2 + (buffer.getShort(0) & 0xFFFF);
+    int ll = buffer.getShort(l) & 0xFFFF;
+    byte[] b = new byte[ll];
+    ((ByteBuffer) buffer.duplicate().position(ll+2)).get(b);
+    return b;
+  }
+  
+  public void setStatusType(CertificateStatusType type)
+  {
+    buffer.put(0, (byte) type.value);
+  }
+  
+  public void setRequestIdListLength(int newLength)
+  {
+    if (newLength < 0 || newLength > 0xFFFF)
+      throw new IllegalArgumentException("length out of range");
+    buffer.putShort(1, (short) newLength);
+  }
+  
+  public void putRequestId(int index, byte[] id)
+  {
+    if (id.length > 0xFFFF)
+      throw new IllegalArgumentException("request ID too large");
+    int len = buffer.getShort(1) & 0xFFFF;
+    int n = 0;
+    int i = 3;
+    while (i < len && n < index)
+      {
+        int l = buffer.getShort(i) & 0xFFFF;
+        i += l + 2;
+        n++;
+      }
+    if (n < index)
+      throw new IndexOutOfBoundsException();
+    buffer.putShort(i, (short) id.length);
+    ((ByteBuffer) buffer.duplicate().position(i)).put(id);
+  }
+  
+  public void setRequestExtensions(int index, byte[] ext)
+  {
+    if (ext.length > 0xFFFF)
+      throw new IllegalArgumentException("exceptions too large");
+    int off = 3 + (buffer.getShort(1) & 0xFFFF);
+    buffer.putShort(off, (short) ext.length);
+    ((ByteBuffer) buffer.duplicate().position(off+2)).put(ext);
+  }
+  
+  public Iterator<byte[]> iterator()
+  {
+    return new ResponderIdIterator();
+  }
+  
+  public String toString()
+  {
+    return toString(null);
+  }
+  
+  public String toString(String prefix)
+  {
+    StringWriter str = new StringWriter();
+    PrintWriter out = new PrintWriter(str);
+    if (prefix != null) out.print(prefix);
+    out.println("struct {");
+    if (prefix != null) out.print(prefix);
+    out.print("  status_type = ");
+    out.print(statusType());
+    out.println(";");
+    String subprefix = "    ";
+    if (prefix != null) subprefix = prefix + subprefix;
+    if (prefix != null) out.print(prefix);
+    out.println("  responder_id_list = {");
+    for (byte[] b : this)
+      out.print(Util.hexDump(b, subprefix));
+    if (prefix != null) out.print(prefix);
+    out.println("  };");
+    if (prefix != null) out.print(prefix);
+    out.println("  request_extensions =");
+    out.print(Util.hexDump(requestExtensions(), subprefix));
+    if (prefix != null) out.print(prefix);
+    out.print("} CertificateStatus;");
+    return str.toString();
+  }
+  
+  public class ResponderIdIterator implements Iterator<byte[]>
+  {
+    private int index;
+    
+    public ResponderIdIterator()
+    {
+      index = 0;
+    }
+    
+    public byte[] next() throws NoSuchElementException
+    {
+      try
+        {
+          return responderId(index++);
+        }
+      catch (IndexOutOfBoundsException ioobe)
+        {
+          throw new NoSuchElementException();
+        }
+    }
+    
+    public boolean hasNext()
+    {
+      return index < size();
+    }
+    
+    public void remove()
+    {
+      throw new UnsupportedOperationException();
+    }
+  }
+}

Attachment: PGP.sig
Description: This is a digitally signed message part

Reply via email to