1 /* 2 * Copyright (c) 1996, 2017, 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 boolean allowBER = true; 48 49 // used by sun/security/util/DerInputBuffer/DerInputBufferEqualsHashCode.java 50 DerInputBuffer(byte[] buf) { 51 this(buf, true); 52 } 53 54 DerInputBuffer(byte[] buf, boolean allowBER) { 55 super(buf); 56 this.allowBER = allowBER; 57 } 58 59 DerInputBuffer(byte[] buf, int offset, int len, boolean allowBER) { 60 super(buf, offset, len); 61 this.allowBER = allowBER; 62 } 63 64 DerInputBuffer dup() { 65 try { 66 DerInputBuffer retval = (DerInputBuffer)clone(); 67 retval.mark(Integer.MAX_VALUE); 68 return retval; 69 } catch (CloneNotSupportedException e) { 70 throw new IllegalArgumentException(e.toString()); 71 } 72 } 73 74 byte[] toByteArray() { 75 int len = available(); 76 if (len <= 0) 77 return null; 78 byte[] retval = new byte[len]; 79 80 System.arraycopy(buf, pos, retval, 0, len); 81 return retval; 82 } 83 84 int peek() throws IOException { 85 if (pos >= count) 86 throw new IOException("out of data"); 87 else 88 return buf[pos]; 89 } 90 91 /** 92 * Compares this DerInputBuffer for equality with the specified 93 * object. 94 */ 95 public boolean equals(Object other) { 96 if (other instanceof DerInputBuffer) 97 return equals((DerInputBuffer)other); 98 else 99 return false; 100 } 101 102 boolean equals(DerInputBuffer other) { 103 if (this == other) 104 return true; 105 106 int max = this.available(); 107 if (other.available() != max) 108 return false; 109 for (int i = 0; i < max; i++) { 110 if (this.buf[this.pos + i] != other.buf[other.pos + i]) { 111 return false; 112 } 113 } 114 return true; 115 } 116 117 /** 118 * Returns a hashcode for this DerInputBuffer. 119 * 120 * @return a hashcode for this DerInputBuffer. 121 */ 122 public int hashCode() { 123 int retval = 0; 124 125 int len = available(); 126 int p = pos; 127 128 for (int i = 0; i < len; i++) 129 retval += buf[p + i] * i; 130 return retval; 131 } 132 133 void truncate(int len) throws IOException { 134 if (len > available()) 135 throw new IOException("insufficient data"); 136 count = pos + len; 137 } 138 139 /** 140 * Returns the integer which takes up the specified number 141 * of bytes in this buffer as a BigInteger. 142 * @param len the number of bytes to use. 143 * @param makePositive whether to always return a positive value, 144 * irrespective of actual encoding 145 * @return the integer as a BigInteger. 146 */ 147 BigInteger getBigInteger(int len, boolean makePositive) throws IOException { 148 if (len > available()) 149 throw new IOException("short read of integer"); 150 151 if (len == 0) { 152 throw new IOException("Invalid encoding: zero length Int value"); 153 } 154 155 byte[] bytes = new byte[len]; 156 157 System.arraycopy(buf, pos, bytes, 0, len); 158 skip(len); 159 160 // BER allows leading 0s but DER does not 161 if (!allowBER && (len >= 2 && (bytes[0] == 0) && (bytes[1] >= 0))) { 162 throw new IOException("Invalid encoding: redundant leading 0s"); 163 } 164 165 if (makePositive) { 166 return new BigInteger(1, bytes); 167 } else { 168 return new BigInteger(bytes); 169 } 170 } 171 172 /** 173 * Returns the integer which takes up the specified number 174 * of bytes in this buffer. 175 * @throws IOException if the result is not within the valid 176 * range for integer, i.e. between Integer.MIN_VALUE and 177 * Integer.MAX_VALUE. 178 * @param len the number of bytes to use. 179 * @return the integer. 180 */ 181 public int getInteger(int len) throws IOException { 182 183 BigInteger result = getBigInteger(len, false); 184 if (result.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0) { 185 throw new IOException("Integer below minimum valid value"); 186 } 187 if (result.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) { 188 throw new IOException("Integer exceeds maximum valid value"); 189 } 190 return result.intValue(); 191 } 192 193 /** 194 * Returns the bit string which takes up the specified 195 * number of bytes in this buffer. 196 */ 197 public byte[] getBitString(int len) throws IOException { 198 if (len > available()) 199 throw new IOException("short read of bit string"); 200 201 if (len == 0) { 202 throw new IOException("Invalid encoding: zero length bit string"); 203 } 204 205 int numOfPadBits = buf[pos]; 206 if ((numOfPadBits < 0) || (numOfPadBits > 7)) { 207 throw new IOException("Invalid number of padding bits"); 208 } 209 // minus the first byte which indicates the number of padding bits 210 byte[] retval = new byte[len - 1]; 211 System.arraycopy(buf, pos + 1, retval, 0, len - 1); 212 if (numOfPadBits != 0) { 213 // get rid of the padding bits 214 retval[len - 2] &= (0xff << numOfPadBits); 215 } 216 skip(len); 217 return retval; 218 } 219 220 /** 221 * Returns the bit string which takes up the rest of this buffer. 222 */ 223 byte[] getBitString() throws IOException { 224 return getBitString(available()); 225 } 226 227 /** 228 * Returns the bit string which takes up the rest of this buffer. 229 * The bit string need not be byte-aligned. 230 */ 231 BitArray getUnalignedBitString() throws IOException { 232 if (pos >= count) 233 return null; 234 /* 235 * Just copy the data into an aligned, padded octet buffer, 236 * and consume the rest of the buffer. 237 */ 238 int len = available(); 239 int unusedBits = buf[pos] & 0xff; 240 if (unusedBits > 7 ) { 241 throw new IOException("Invalid value for unused bits: " + unusedBits); 242 } 243 byte[] bits = new byte[len - 1]; 244 // number of valid bits 245 int length = (bits.length == 0) ? 0 : bits.length * 8 - unusedBits; 246 247 System.arraycopy(buf, pos + 1, bits, 0, len - 1); 248 249 BitArray bitArray = new BitArray(length, bits); 250 pos = count; 251 return bitArray; 252 } 253 254 /** 255 * Returns the UTC Time value that takes up the specified number 256 * of bytes in this buffer. 257 * @param len the number of bytes to use 258 */ 259 public Date getUTCTime(int len) throws IOException { 260 if (len > available()) 261 throw new IOException("short read of DER UTC Time"); 262 263 if (len < 11 || len > 17) 264 throw new IOException("DER UTC Time length error"); 265 266 return getTime(len, false); 267 } 268 269 /** 270 * Returns the Generalized Time value that takes up the specified 271 * number of bytes in this buffer. 272 * @param len the number of bytes to use 273 */ 274 public Date getGeneralizedTime(int len) throws IOException { 275 if (len > available()) 276 throw new IOException("short read of DER Generalized Time"); 277 278 if (len < 13 || len > 23) 279 throw new IOException("DER Generalized Time length error"); 280 281 return getTime(len, true); 282 283 } 284 285 /** 286 * Private helper routine to extract time from the der value. 287 * @param len the number of bytes to use 288 * @param generalized true if Generalized Time is to be read, false 289 * if UTC Time is to be read. 290 */ 291 private Date getTime(int len, boolean generalized) throws IOException { 292 293 /* 294 * UTC time encoded as ASCII chars: 295 * YYMMDDhhmmZ 296 * YYMMDDhhmmssZ 297 * YYMMDDhhmm+hhmm 298 * YYMMDDhhmm-hhmm 299 * YYMMDDhhmmss+hhmm 300 * YYMMDDhhmmss-hhmm 301 * UTC Time is broken in storing only two digits of year. 302 * If YY < 50, we assume 20YY; 303 * if YY >= 50, we assume 19YY, as per RFC 5280. 304 * 305 * Generalized time has a four-digit year and allows any 306 * precision specified in ISO 8601. However, for our purposes, 307 * we will only allow the same format as UTC time, except that 308 * fractional seconds (millisecond precision) are supported. 309 */ 310 311 int year, month, day, hour, minute, second, millis; 312 String type = null; 313 314 if (generalized) { 315 type = "Generalized"; 316 year = 1000 * Character.digit((char)buf[pos++], 10); 317 year += 100 * Character.digit((char)buf[pos++], 10); 318 year += 10 * Character.digit((char)buf[pos++], 10); 319 year += Character.digit((char)buf[pos++], 10); 320 len -= 2; // For the two extra YY 321 } else { 322 type = "UTC"; 323 year = 10 * Character.digit((char)buf[pos++], 10); 324 year += Character.digit((char)buf[pos++], 10); 325 326 if (year < 50) // origin 2000 327 year += 2000; 328 else 329 year += 1900; // origin 1900 330 } 331 332 month = 10 * Character.digit((char)buf[pos++], 10); 333 month += Character.digit((char)buf[pos++], 10); 334 335 day = 10 * Character.digit((char)buf[pos++], 10); 336 day += Character.digit((char)buf[pos++], 10); 337 338 hour = 10 * Character.digit((char)buf[pos++], 10); 339 hour += Character.digit((char)buf[pos++], 10); 340 341 minute = 10 * Character.digit((char)buf[pos++], 10); 342 minute += Character.digit((char)buf[pos++], 10); 343 344 len -= 10; // YYMMDDhhmm 345 346 /* 347 * We allow for non-encoded seconds, even though the 348 * IETF-PKIX specification says that the seconds should 349 * always be encoded even if it is zero. 350 */ 351 352 millis = 0; 353 if (len > 2 && len < 12) { 354 second = 10 * Character.digit((char)buf[pos++], 10); 355 second += Character.digit((char)buf[pos++], 10); 356 len -= 2; 357 // handle fractional seconds (if present) 358 if (buf[pos] == '.' || buf[pos] == ',') { 359 len --; 360 pos++; 361 // handle upto milisecond precision only 362 int precision = 0; 363 int peek = pos; 364 while (buf[peek] != 'Z' && 365 buf[peek] != '+' && 366 buf[peek] != '-') { 367 peek++; 368 precision++; 369 } 370 switch (precision) { 371 case 3: 372 millis += 100 * Character.digit((char)buf[pos++], 10); 373 millis += 10 * Character.digit((char)buf[pos++], 10); 374 millis += Character.digit((char)buf[pos++], 10); 375 break; 376 case 2: 377 millis += 100 * Character.digit((char)buf[pos++], 10); 378 millis += 10 * Character.digit((char)buf[pos++], 10); 379 break; 380 case 1: 381 millis += 100 * Character.digit((char)buf[pos++], 10); 382 break; 383 default: 384 throw new IOException("Parse " + type + 385 " time, unsupported precision for seconds value"); 386 } 387 len -= precision; 388 } 389 } else 390 second = 0; 391 392 if (month == 0 || day == 0 393 || month > 12 || day > 31 394 || hour >= 24 || minute >= 60 || second >= 60) 395 throw new IOException("Parse " + type + " time, invalid format"); 396 397 /* 398 * Generalized time can theoretically allow any precision, 399 * but we're not supporting that. 400 */ 401 CalendarSystem gcal = CalendarSystem.getGregorianCalendar(); 402 CalendarDate date = gcal.newCalendarDate(null); // no time zone 403 date.setDate(year, month, day); 404 date.setTimeOfDay(hour, minute, second, millis); 405 long time = gcal.getTime(date); 406 407 /* 408 * Finally, "Z" or "+hhmm" or "-hhmm" ... offsets change hhmm 409 */ 410 if (! (len == 1 || len == 5)) 411 throw new IOException("Parse " + type + " time, invalid offset"); 412 413 int hr, min; 414 415 switch (buf[pos++]) { 416 case '+': 417 hr = 10 * Character.digit((char)buf[pos++], 10); 418 hr += Character.digit((char)buf[pos++], 10); 419 min = 10 * Character.digit((char)buf[pos++], 10); 420 min += Character.digit((char)buf[pos++], 10); 421 422 if (hr >= 24 || min >= 60) 423 throw new IOException("Parse " + type + " time, +hhmm"); 424 425 time -= ((hr * 60) + min) * 60 * 1000; 426 break; 427 428 case '-': 429 hr = 10 * Character.digit((char)buf[pos++], 10); 430 hr += Character.digit((char)buf[pos++], 10); 431 min = 10 * Character.digit((char)buf[pos++], 10); 432 min += Character.digit((char)buf[pos++], 10); 433 434 if (hr >= 24 || min >= 60) 435 throw new IOException("Parse " + type + " time, -hhmm"); 436 437 time += ((hr * 60) + min) * 60 * 1000; 438 break; 439 440 case 'Z': 441 break; 442 443 default: 444 throw new IOException("Parse " + type + " time, garbage offset"); 445 } 446 return new Date(time); 447 } 448 }