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