1 /* 2 * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.util; 27 28 import java.io.ByteArrayInputStream; 29 import java.io.IOException; 30 import java.io.OutputStream; 31 import java.math.BigInteger; 32 import java.util.Date; 33 import sun.util.calendar.CalendarDate; 34 import sun.util.calendar.CalendarSystem; 35 36 /** 37 * DER input buffer ... this is the main abstraction in the DER library 38 * which actively works with the "untyped byte stream" abstraction. It 39 * does so with impunity, since it's not intended to be exposed to 40 * anyone who could violate the "typed value stream" DER model and hence 41 * corrupt the input stream of DER values. 42 * 43 * @author David Brownell 44 */ 45 class DerInputBuffer extends ByteArrayInputStream implements Cloneable { 46 47 DerInputBuffer(byte[] buf) { super(buf); } 48 49 DerInputBuffer(byte[] buf, int offset, int len) { 50 super(buf, offset, len); 51 } 52 53 DerInputBuffer dup() { 54 try { 55 DerInputBuffer retval = (DerInputBuffer)clone(); 56 57 retval.mark(Integer.MAX_VALUE); 58 return retval; 59 } catch (CloneNotSupportedException e) { 60 throw new IllegalArgumentException(e.toString()); 61 } 62 } 63 64 byte[] toByteArray() { 65 int len = available(); 66 if (len <= 0) 67 return null; 68 byte[] retval = new byte[len]; 69 70 System.arraycopy(buf, pos, retval, 0, len); 71 return retval; 72 } 73 74 int peek() throws IOException { 75 if (pos >= count) 76 throw new IOException("out of data"); 77 else 78 return buf[pos]; 79 } 80 81 /** 82 * Compares this DerInputBuffer for equality with the specified 83 * object. 84 */ 85 public boolean equals(Object other) { 86 if (other instanceof DerInputBuffer) 87 return equals((DerInputBuffer)other); 88 else 89 return false; 90 } 91 92 boolean equals(DerInputBuffer other) { 93 if (this == other) 94 return true; 95 96 int max = this.available(); 97 if (other.available() != max) 98 return false; 99 for (int i = 0; i < max; i++) { 100 if (this.buf[this.pos + i] != other.buf[other.pos + i]) { 101 return false; 102 } 103 } 104 return true; 105 } 106 107 /** 108 * Returns a hashcode for this DerInputBuffer. 109 * 110 * @return a hashcode for this DerInputBuffer. 111 */ 112 public int hashCode() { 113 int retval = 0; 114 115 int len = available(); 116 int p = pos; 117 118 for (int i = 0; i < len; i++) 119 retval += buf[p + i] * i; 120 return retval; 121 } 122 123 void truncate(int len) throws IOException { 124 if (len > available()) 125 throw new IOException("insufficient data"); 126 count = pos + len; 127 } 128 129 /** 130 * Returns the integer which takes up the specified number 131 * of bytes in this buffer as a BigInteger. 132 * @param len the number of bytes to use. 133 * @param makePositive whether to always return a positive value, 134 * irrespective of actual encoding 135 * @return the integer as a BigInteger. 136 */ 137 BigInteger getBigInteger(int len, boolean makePositive) throws IOException { 138 if (len > available()) 139 throw new IOException("short read of integer"); 140 141 if (len == 0) { 142 throw new IOException("Invalid encoding: zero length Int value"); 143 } 144 145 byte[] bytes = new byte[len]; 146 147 System.arraycopy(buf, pos, bytes, 0, len); 148 skip(len); 149 150 // check to make sure no extra leading 0s for DER 151 if (len >= 2 && (bytes[0] == 0) && (bytes[1] >= 0)) { 152 throw new IOException("Invalid encoding: redundant leading 0s"); 153 } 154 155 if (makePositive) { 156 return new BigInteger(1, bytes); 157 } else { 158 return new BigInteger(bytes); 159 } 160 } 161 162 /** 163 * Returns the integer which takes up the specified number 164 * of bytes in this buffer. 165 * @throws IOException if the result is not within the valid 166 * range for integer, i.e. between Integer.MIN_VALUE and 167 * Integer.MAX_VALUE. 168 * @param len the number of bytes to use. 169 * @return the integer. 170 */ 171 public int getInteger(int len) throws IOException { 172 173 BigInteger result = getBigInteger(len, false); 174 if (result.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0) { 175 throw new IOException("Integer below minimum valid value"); 176 } 177 if (result.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) { 178 throw new IOException("Integer exceeds maximum valid value"); 179 } 180 return result.intValue(); 181 } 182 183 /** 184 * Returns the bit string which takes up the specified 185 * number of bytes in this buffer. 186 */ 187 public byte[] getBitString(int len) throws IOException { 188 if (len > available()) 189 throw new IOException("short read of bit string"); 190 191 if (len == 0) { 192 throw new IOException("Invalid encoding: zero length bit string"); 193 } 194 195 int numOfPadBits = buf[pos]; 196 if ((numOfPadBits < 0) || (numOfPadBits > 7)) { 197 throw new IOException("Invalid number of padding bits"); 198 } 199 // minus the first byte which indicates the number of padding bits 200 byte[] retval = new byte[len - 1]; 201 System.arraycopy(buf, pos + 1, retval, 0, len - 1); 202 if (numOfPadBits != 0) { 203 // get rid of the padding bits 204 retval[len - 2] &= (0xff << numOfPadBits); 205 } 206 skip(len); 207 return retval; 208 } 209 210 /** 211 * Returns the bit string which takes up the rest of this buffer. 212 */ 213 byte[] getBitString() throws IOException { 214 return getBitString(available()); 215 } 216 217 /** 218 * Returns the bit string which takes up the rest of this buffer. 219 * The bit string need not be byte-aligned. 220 */ 221 BitArray getUnalignedBitString() throws IOException { 222 if (pos >= count) 223 return null; 224 /* 225 * Just copy the data into an aligned, padded octet buffer, 226 * and consume the rest of the buffer. 227 */ 228 int len = available(); 229 int unusedBits = buf[pos] & 0xff; 230 if (unusedBits > 7 ) { 231 throw new IOException("Invalid value for unused bits: " + unusedBits); 232 } 233 byte[] bits = new byte[len - 1]; 234 // number of valid bits 235 int length = (bits.length == 0) ? 0 : bits.length * 8 - unusedBits; 236 237 System.arraycopy(buf, pos + 1, bits, 0, len - 1); 238 239 BitArray bitArray = new BitArray(length, bits); 240 pos = count; 241 return bitArray; 242 } 243 244 /** 245 * Returns the UTC Time value that takes up the specified number 246 * of bytes in this buffer. 247 * @param len the number of bytes to use 248 */ 249 public Date getUTCTime(int len) throws IOException { 250 if (len > available()) 251 throw new IOException("short read of DER UTC Time"); 252 253 if (len < 11 || len > 17) 254 throw new IOException("DER UTC Time length error"); 255 256 return getTime(len, false); 257 } 258 259 /** 260 * Returns the Generalized Time value that takes up the specified 261 * number of bytes in this buffer. 262 * @param len the number of bytes to use 263 */ 264 public Date getGeneralizedTime(int len) throws IOException { 265 if (len > available()) 266 throw new IOException("short read of DER Generalized Time"); 267 268 if (len < 13 || len > 23) 269 throw new IOException("DER Generalized Time length error"); 270 271 return getTime(len, true); 272 273 } 274 275 /** 276 * Private helper routine to extract time from the der value. 277 * @param len the number of bytes to use 278 * @param generalized true if Generalized Time is to be read, false 279 * if UTC Time is to be read. 280 */ 281 private Date getTime(int len, boolean generalized) throws IOException { 282 283 /* 284 * UTC time encoded as ASCII chars: 285 * YYMMDDhhmmZ 286 * YYMMDDhhmmssZ 287 * YYMMDDhhmm+hhmm 288 * YYMMDDhhmm-hhmm 289 * YYMMDDhhmmss+hhmm 290 * YYMMDDhhmmss-hhmm 291 * UTC Time is broken in storing only two digits of year. 292 * If YY < 50, we assume 20YY; 293 * if YY >= 50, we assume 19YY, as per RFC 5280. 294 * 295 * Generalized time has a four-digit year and allows any 296 * precision specified in ISO 8601. However, for our purposes, 297 * we will only allow the same format as UTC time, except that 298 * fractional seconds (millisecond precision) are supported. 299 */ 300 301 int year, month, day, hour, minute, second, millis; 302 String type = null; 303 304 if (generalized) { 305 type = "Generalized"; 306 year = 1000 * Character.digit((char)buf[pos++], 10); 307 year += 100 * Character.digit((char)buf[pos++], 10); 308 year += 10 * Character.digit((char)buf[pos++], 10); 309 year += Character.digit((char)buf[pos++], 10); 310 len -= 2; // For the two extra YY 311 } else { 312 type = "UTC"; 313 year = 10 * Character.digit((char)buf[pos++], 10); 314 year += Character.digit((char)buf[pos++], 10); 315 316 if (year < 50) // origin 2000 317 year += 2000; 318 else 319 year += 1900; // origin 1900 320 } 321 322 month = 10 * Character.digit((char)buf[pos++], 10); 323 month += Character.digit((char)buf[pos++], 10); 324 325 day = 10 * Character.digit((char)buf[pos++], 10); 326 day += Character.digit((char)buf[pos++], 10); 327 328 hour = 10 * Character.digit((char)buf[pos++], 10); 329 hour += Character.digit((char)buf[pos++], 10); 330 331 minute = 10 * Character.digit((char)buf[pos++], 10); 332 minute += Character.digit((char)buf[pos++], 10); 333 334 len -= 10; // YYMMDDhhmm 335 336 /* 337 * We allow for non-encoded seconds, even though the 338 * IETF-PKIX specification says that the seconds should 339 * always be encoded even if it is zero. 340 */ 341 342 millis = 0; 343 if (len > 2 && len < 12) { 344 second = 10 * Character.digit((char)buf[pos++], 10); 345 second += Character.digit((char)buf[pos++], 10); 346 len -= 2; 347 // handle fractional seconds (if present) 348 if (buf[pos] == '.' || buf[pos] == ',') { 349 len --; 350 pos++; 351 // handle upto milisecond precision only 352 int precision = 0; 353 int peek = pos; 354 while (buf[peek] != 'Z' && 355 buf[peek] != '+' && 356 buf[peek] != '-') { 357 peek++; 358 precision++; 359 } 360 switch (precision) { 361 case 3: 362 millis += 100 * Character.digit((char)buf[pos++], 10); 363 millis += 10 * Character.digit((char)buf[pos++], 10); 364 millis += Character.digit((char)buf[pos++], 10); 365 break; 366 case 2: 367 millis += 100 * Character.digit((char)buf[pos++], 10); 368 millis += 10 * Character.digit((char)buf[pos++], 10); 369 break; 370 case 1: 371 millis += 100 * Character.digit((char)buf[pos++], 10); 372 break; 373 default: 374 throw new IOException("Parse " + type + 375 " time, unsupported precision for seconds value"); 376 } 377 len -= precision; 378 } 379 } else 380 second = 0; 381 382 if (month == 0 || day == 0 383 || month > 12 || day > 31 384 || hour >= 24 || minute >= 60 || second >= 60) 385 throw new IOException("Parse " + type + " time, invalid format"); 386 387 /* 388 * Generalized time can theoretically allow any precision, 389 * but we're not supporting that. 390 */ 391 CalendarSystem gcal = CalendarSystem.getGregorianCalendar(); 392 CalendarDate date = gcal.newCalendarDate(null); // no time zone 393 date.setDate(year, month, day); 394 date.setTimeOfDay(hour, minute, second, millis); 395 long time = gcal.getTime(date); 396 397 /* 398 * Finally, "Z" or "+hhmm" or "-hhmm" ... offsets change hhmm 399 */ 400 if (! (len == 1 || len == 5)) 401 throw new IOException("Parse " + type + " time, invalid offset"); 402 403 int hr, min; 404 405 switch (buf[pos++]) { 406 case '+': 407 hr = 10 * Character.digit((char)buf[pos++], 10); 408 hr += Character.digit((char)buf[pos++], 10); 409 min = 10 * Character.digit((char)buf[pos++], 10); 410 min += Character.digit((char)buf[pos++], 10); 411 412 if (hr >= 24 || min >= 60) 413 throw new IOException("Parse " + type + " time, +hhmm"); 414 415 time -= ((hr * 60) + min) * 60 * 1000; 416 break; 417 418 case '-': 419 hr = 10 * Character.digit((char)buf[pos++], 10); 420 hr += Character.digit((char)buf[pos++], 10); 421 min = 10 * Character.digit((char)buf[pos++], 10); 422 min += Character.digit((char)buf[pos++], 10); 423 424 if (hr >= 24 || min >= 60) 425 throw new IOException("Parse " + type + " time, -hhmm"); 426 427 time += ((hr * 60) + min) * 60 * 1000; 428 break; 429 430 case 'Z': 431 break; 432 433 default: 434 throw new IOException("Parse " + type + " time, garbage offset"); 435 } 436 return new Date(time); 437 } 438 }