1 /* CertificateURL.java --
2    Copyright (C) 2006  Free Software Foundation, Inc.
3 
4 This file is a part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or (at
9 your option) any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19 USA
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version.  */
37 
38 
39 package gnu.javax.net.ssl.provider;
40 
41 import gnu.javax.net.ssl.provider.Extension.Value;
42 
43 import java.io.PrintWriter;
44 import java.io.StringWriter;
45 import java.nio.ByteBuffer;
46 import java.nio.ByteOrder;
47 import java.nio.CharBuffer;
48 import java.nio.charset.Charset;
49 import java.nio.charset.CharsetEncoder;
50 import java.util.List;
51 import java.util.NoSuchElementException;
52 
53 /**
54  * The CertificateURL extension value.
55  *
56  * <pre>
57 enum {
58   individual_certs(0), pkipath(1), (255)
59 } CertChainType;
60 
61 enum {
62   false(0), true(1)
63 } Boolean;
64 
65 struct {
66   CertChainType type;
67   URLAndOptionalHash url_and_hash_list&lt;1..2^16-1&gt;;
68 } CertificateURL;
69 
70 struct {
71   opaque url&lt;1..2^16-1&gt;;
72   Boolean hash_present;
73   select (hash_present) {
74     case false: struct {};
75     case true: SHA1Hash;
76   } hash;
77 } URLAndOptionalHash;
78 
79 opaque SHA1Hash[20];</pre>
80  *
81  * @author csm
82  *
83  */
84 public class CertificateURL extends Value implements Iterable<CertificateURL.URLAndOptionalHash>
85 {
86   private ByteBuffer buffer;
87 
CertificateURL(final ByteBuffer buffer)88   public CertificateURL(final ByteBuffer buffer)
89   {
90     this.buffer = buffer;
91   }
92 
CertificateURL(CertChainType type, List<URLAndOptionalHash> urls)93   public CertificateURL(CertChainType type, List<URLAndOptionalHash> urls)
94   {
95     int length = 3;
96     for (URLAndOptionalHash url : urls)
97       length += url.length();
98     buffer = ByteBuffer.allocate(length);
99     buffer.put((byte) type.getValue());
100     buffer.putShort((short) (length - 1));
101     for (URLAndOptionalHash url : urls)
102       buffer.put(url.buffer());
103     buffer.rewind();
104   }
105 
length()106   public int length()
107   {
108     return 3 + (buffer.getShort(1) & 0xFFFF);
109   }
110 
buffer()111   public ByteBuffer buffer()
112   {
113     return (ByteBuffer) buffer.duplicate().limit(length());
114   }
115 
type()116   public CertChainType type()
117   {
118     switch (buffer.get(0))
119       {
120         case 0: return CertChainType.INDIVIDUAL_CERTS;
121         case 1: return CertChainType.PKIPATH;
122       }
123     throw new IllegalArgumentException("unknown certificate URL type");
124   }
125 
size()126   public int size()
127   {
128     int len = buffer.getShort(1) & 0xFFFF;
129     int n = 0;
130     for (int i = 3; i < len; )
131       {
132         URLAndOptionalHash u
133           = new URLAndOptionalHash((ByteBuffer) buffer.duplicate().position(i));
134         int l = u.length();
135         i += l;
136         n++;
137       }
138     return n;
139   }
140 
get(int index)141   public URLAndOptionalHash get(int index)
142   {
143     int len = buffer.getShort(1) & 0xFFFF;
144     int n = 0;
145     int l = 0;
146     int i;
147     for (i = 3; i < len && n < index; )
148       {
149         URLAndOptionalHash u
150           = new URLAndOptionalHash((ByteBuffer) buffer.duplicate().position(i));
151         l = u.length();
152         i += l;
153         n++;
154       }
155     if (n < index)
156       throw new IndexOutOfBoundsException();
157     return new URLAndOptionalHash(((ByteBuffer) buffer.duplicate().position(i).limit(i+l)).slice());
158   }
159 
set(int index, URLAndOptionalHash url)160   public void set(int index, URLAndOptionalHash url)
161   {
162     int len = buffer.getShort(1) & 0xFFFF;
163     int n = 0;
164     int i;
165     for (i = 3; i < len && n < index-1; )
166       {
167         URLAndOptionalHash u
168           = new URLAndOptionalHash((ByteBuffer) buffer.duplicate().position(i));
169         int l = u.length();
170         i += l;
171         n++;
172       }
173     if (n < index - 1)
174       throw new IndexOutOfBoundsException();
175     int l = url.urlLength();
176     buffer.putShort(i, (short) l);
177     ((ByteBuffer) buffer.duplicate().position(i+2)).put(url.urlBuffer());
178     buffer.put(i+l+2, (byte) (url.hashPresent() ? 1 : 0));
179     if (url.hashPresent())
180       ((ByteBuffer) buffer.duplicate().position(i+l+3)).put (url.sha1Hash());
181   }
182 
setLength(final int length)183   public void setLength(final int length)
184   {
185     if (length < 0 || length > 65535)
186       throw new IllegalArgumentException("length must be between 0 and 65535");
187     buffer.putShort(1, (short) length);
188   }
189 
toString()190   public String toString()
191   {
192     return toString(null);
193   }
194 
toString(String prefix)195   public String toString(String prefix)
196   {
197     StringWriter str = new StringWriter();
198     PrintWriter out = new PrintWriter(str);
199     if (prefix != null) out.print(prefix);
200     out.println ("struct {");
201     if (prefix != null) out.print(prefix);
202     out.print("  type = ");
203     out.print(type());
204     out.println(";");
205     if (prefix != null) out.print(prefix);
206     out.println("  url_and_hash_list = {");
207     String subprefix = "  ";
208     if (prefix != null) subprefix = prefix + subprefix;
209     for (URLAndOptionalHash url : this)
210       {
211         out.println(url.toString(subprefix));
212       }
213     if (prefix != null) out.print(prefix);
214     out.println("  };");
215     if (prefix != null) out.print(prefix);
216     out.print("} CertificateURL;");
217     return str.toString();
218   }
219 
iterator()220   public java.util.Iterator<URLAndOptionalHash> iterator()
221   {
222     return new Iterator();
223   }
224 
225   public class Iterator implements java.util.Iterator<URLAndOptionalHash>
226   {
227     private int index;
228 
Iterator()229     public Iterator()
230     {
231       index = 0;
232     }
233 
next()234     public URLAndOptionalHash next() throws NoSuchElementException
235     {
236       try
237         {
238           return get(index++);
239         }
240       catch (IndexOutOfBoundsException ioobe)
241         {
242           throw new NoSuchElementException();
243         }
244     }
245 
hasNext()246     public boolean hasNext()
247     {
248       return index < size();
249     }
250 
remove()251     public void remove()
252     {
253       throw new UnsupportedOperationException();
254     }
255   }
256 
257   public static enum CertChainType
258   {
259     INDIVIDUAL_CERTS (0), PKIPATH (1);
260 
261     private final int value;
262 
CertChainType(final int value)263     private CertChainType (final int value)
264     {
265       this.value = value;
266     }
267 
getValue()268     public int getValue()
269     {
270       return value;
271     }
272   }
273 
274   public static class URLAndOptionalHash implements Builder, Constructed
275   {
276     private ByteBuffer buffer;
277 
URLAndOptionalHash(final ByteBuffer buffer)278     public URLAndOptionalHash (final ByteBuffer buffer)
279     {
280       this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN);
281     }
282 
URLAndOptionalHash(String url)283     public URLAndOptionalHash(String url)
284     {
285       this(url, null);
286     }
287 
URLAndOptionalHash(String url, byte[] hash)288     public URLAndOptionalHash(String url, byte[] hash)
289     {
290       if (hash != null && hash.length < 20)
291         throw new IllegalArgumentException();
292       int length = 3 + url.length();
293       if (hash != null)
294         length += 20;
295       buffer = ByteBuffer.allocate(length);
296       buffer.putShort((short) url.length());
297       Charset cs = Charset.forName("US-ASCII");
298       CharsetEncoder ascii = cs.newEncoder();
299       ascii.encode(CharBuffer.wrap(url), buffer, true);
300       buffer.put((byte) (hash != null ? 1 : 0));
301       if (hash != null)
302         buffer.put(hash, 0, 20);
303       buffer.rewind();
304     }
305 
length()306     public int length()
307     {
308       return ((buffer.getShort(0) & 0xFFFF)
309               + (hashPresent() ? 23 : 3));
310     }
311 
buffer()312     public ByteBuffer buffer()
313     {
314       return (ByteBuffer) buffer.duplicate().limit(length());
315     }
316 
url()317     public String url()
318     {
319       Charset cs = Charset.forName("ASCII");
320       return cs.decode(urlBuffer()).toString();
321     }
322 
urlLength()323     public int urlLength()
324     {
325       return buffer.getShort(0) & 0xFFFF;
326     }
327 
urlBuffer()328     public ByteBuffer urlBuffer()
329     {
330       int len = urlLength();
331       return ((ByteBuffer) buffer.duplicate().position(2).limit(2+len)).slice();
332     }
333 
hashPresent()334     public boolean hashPresent()
335     {
336       int i = (buffer.getShort(0) & 0xFFFF) + 2;
337       byte b = buffer.get(i);
338       if (b == 0)
339         return false;
340       if (b == 1)
341         return true;
342       throw new IllegalArgumentException("expecting 0 or 1: " + (b & 0xFF));
343     }
344 
sha1Hash()345     public byte[] sha1Hash()
346     {
347       int i = (buffer.getShort(0) & 0xFFFF) + 2;
348       byte b = buffer.get(i);
349       if (b == 0)
350         return null;
351       byte[] buf = new byte[20];
352       ((ByteBuffer) buffer.duplicate().position(i+1)).get(buf);
353       return buf;
354     }
355 
toString()356     public String toString()
357     {
358       return toString(null);
359     }
360 
toString(final String prefix)361     public String toString(final String prefix)
362     {
363       StringWriter str = new StringWriter();
364       PrintWriter out = new PrintWriter(str);
365       if (prefix != null) out.print(prefix);
366       out.println("struct {");
367       if (prefix != null) out.print(prefix);
368       out.print("  url = ");
369       out.print(url());
370       out.println(";");
371       boolean has_hash = hashPresent();
372       if (prefix != null) out.print(prefix);
373       out.print("  hash_present = ");
374       out.print(has_hash);
375       out.println(";");
376       if (has_hash)
377         {
378           if (prefix != null) out.print(prefix);
379           out.print("  sha1Hash = ");
380           out.print(Util.toHexString(sha1Hash(), ':'));
381           out.println(";");
382         }
383       if (prefix != null) out.print(prefix);
384       out.print("} URLAndOptionalHash;");
385       return str.toString();
386     }
387   }
388 }
389