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 }