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 }