diff options
-rw-r--r-- | gnu/javax/net/ssl/provider/CertificateStatusRequest.java | 204 | ||||
-rw-r--r-- | gnu/javax/net/ssl/provider/CertificateStatusType.java | 13 | ||||
-rw-r--r-- | gnu/javax/net/ssl/provider/CertificateURL.java | 300 | ||||
-rw-r--r-- | gnu/javax/net/ssl/provider/ClientHello.java | 28 | ||||
-rw-r--r-- | gnu/javax/net/ssl/provider/Extension.java | 53 | ||||
-rw-r--r-- | gnu/javax/net/ssl/provider/ExtensionList.java | 2 | ||||
-rw-r--r-- | gnu/javax/net/ssl/provider/MaxFragmentLength.java | 52 | ||||
-rw-r--r-- | gnu/javax/net/ssl/provider/ServerHandshake.java | 17 | ||||
-rw-r--r-- | gnu/javax/net/ssl/provider/ServerHello.java | 31 | ||||
-rw-r--r-- | gnu/javax/net/ssl/provider/ServerNameList.java | 225 | ||||
-rw-r--r-- | gnu/javax/net/ssl/provider/TruncatedHMAC.java | 31 | ||||
-rw-r--r-- | gnu/javax/net/ssl/provider/TrustedAuthorities.java | 252 | ||||
-rw-r--r-- | gnu/javax/net/ssl/provider/UnresolvedExtensionValue.java | 40 |
13 files changed, 1213 insertions, 35 deletions
diff --git a/gnu/javax/net/ssl/provider/CertificateStatusRequest.java b/gnu/javax/net/ssl/provider/CertificateStatusRequest.java new file mode 100644 index 000000000..8bce9d549 --- /dev/null +++ b/gnu/javax/net/ssl/provider/CertificateStatusRequest.java @@ -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<0..2^16-1>; + Extensions request_extensions; +} OCSPStatusRequest; + +opaque ResponderID<1..2^16-1>; +opaque Extensions<0..2^16-1>;</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(); + } + } +} diff --git a/gnu/javax/net/ssl/provider/CertificateStatusType.java b/gnu/javax/net/ssl/provider/CertificateStatusType.java new file mode 100644 index 000000000..7cddf168f --- /dev/null +++ b/gnu/javax/net/ssl/provider/CertificateStatusType.java @@ -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; + } +} diff --git a/gnu/javax/net/ssl/provider/CertificateURL.java b/gnu/javax/net/ssl/provider/CertificateURL.java new file mode 100644 index 000000000..107b164e2 --- /dev/null +++ b/gnu/javax/net/ssl/provider/CertificateURL.java @@ -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<1..2^16-1>; +} CertificateURL; + +struct { + opaque url<1..2^16-1>; + 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(); + } + } +} diff --git a/gnu/javax/net/ssl/provider/ClientHello.java b/gnu/javax/net/ssl/provider/ClientHello.java index 14351c1fd..ab3309520 100644 --- a/gnu/javax/net/ssl/provider/ClientHello.java +++ b/gnu/javax/net/ssl/provider/ClientHello.java @@ -67,6 +67,7 @@ struct SessionID session_id; // 1 + 0..32 CipherSuite cipher_suites<2..2^16-1> CompressionMethod compression_methods<1..2^8-1> + Extension client_hello_extension_list<0..2^16-1> } ClientHello; </pre> */ @@ -156,11 +157,17 @@ public final class ClientHello implements Handshake.Body 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 final class ClientHello implements Handshake.Body 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 @@ public final class ClientHello implements Handshake.Body 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;"); diff --git a/gnu/javax/net/ssl/provider/Extension.java b/gnu/javax/net/ssl/provider/Extension.java index 9e2b69e3b..cd0285ed3 100644 --- a/gnu/javax/net/ssl/provider/Extension.java +++ b/gnu/javax/net/ssl/provider/Extension.java @@ -83,7 +83,7 @@ public final class Extension implements Constructed 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 @@ public final class Extension implements Constructed 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 @@ public final class Extension implements Constructed 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 @@ public final class Extension implements Constructed return value; } } + + public static abstract class Value implements Constructed + { + } } diff --git a/gnu/javax/net/ssl/provider/ExtensionList.java b/gnu/javax/net/ssl/provider/ExtensionList.java index ddb104491..686eae000 100644 --- a/gnu/javax/net/ssl/provider/ExtensionList.java +++ b/gnu/javax/net/ssl/provider/ExtensionList.java @@ -113,7 +113,7 @@ public class ExtensionList implements Iterable<Extension> + "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++; } diff --git a/gnu/javax/net/ssl/provider/MaxFragmentLength.java b/gnu/javax/net/ssl/provider/MaxFragmentLength.java new file mode 100644 index 000000000..c680b0774 --- /dev/null +++ b/gnu/javax/net/ssl/provider/MaxFragmentLength.java @@ -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() + ";"; + } +} diff --git a/gnu/javax/net/ssl/provider/ServerHandshake.java b/gnu/javax/net/ssl/provider/ServerHandshake.java index 878f89bdc..7830b2113 100644 --- a/gnu/javax/net/ssl/provider/ServerHandshake.java +++ b/gnu/javax/net/ssl/provider/ServerHandshake.java @@ -127,19 +127,18 @@ class ServerHandshake extends AbstractHandshake 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 @@ class ServerHandshake extends AbstractHandshake 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; } diff --git a/gnu/javax/net/ssl/provider/ServerHello.java b/gnu/javax/net/ssl/provider/ServerHello.java index da490f174..cd69f14b1 100644 --- a/gnu/javax/net/ssl/provider/ServerHello.java +++ b/gnu/javax/net/ssl/provider/ServerHello.java @@ -67,6 +67,7 @@ struct SessionID session_id; CipherSuite cipher_suite; CompressionMethod compression_method; + Extensions server_hello_extension_list<0..2^16-1> } ServerHello; </pre> * @@ -171,11 +172,19 @@ public class ServerHello implements Handshake.Body 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 class ServerHello implements Handshake.Body 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 @@ public class ServerHello implements Handshake.Body 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;"); diff --git a/gnu/javax/net/ssl/provider/ServerNameList.java b/gnu/javax/net/ssl/provider/ServerNameList.java new file mode 100644 index 000000000..218e04a34 --- /dev/null +++ b/gnu/javax/net/ssl/provider/ServerNameList.java @@ -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 + * {@link #get(int)} method, and modify the returned {@link 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; + } + } +} diff --git a/gnu/javax/net/ssl/provider/TruncatedHMAC.java b/gnu/javax/net/ssl/provider/TruncatedHMAC.java new file mode 100644 index 000000000..147b8ed1d --- /dev/null +++ b/gnu/javax/net/ssl/provider/TruncatedHMAC.java @@ -0,0 +1,31 @@ +package gnu.javax.net.ssl.provider; + +import gnu.javax.net.ssl.provider.Extension.Value; + +/** + * The value type for the {@link 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; + } +} diff --git a/gnu/javax/net/ssl/provider/TrustedAuthorities.java b/gnu/javax/net/ssl/provider/TrustedAuthorities.java new file mode 100644 index 000000000..89cf868b8 --- /dev/null +++ b/gnu/javax/net/ssl/provider/TrustedAuthorities.java @@ -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<0..2^16-1>; +} 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<1..2^16-1>;</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; + } + } +} diff --git a/gnu/javax/net/ssl/provider/UnresolvedExtensionValue.java b/gnu/javax/net/ssl/provider/UnresolvedExtensionValue.java new file mode 100644 index 000000000..720249e85 --- /dev/null +++ b/gnu/javax/net/ssl/provider/UnresolvedExtensionValue.java @@ -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; + } +} |