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<1..2^16-1>; 68 } CertificateURL; 69 70 struct { 71 opaque url<1..2^16-1>; 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