Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (c) 1996, 2006, 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 getPos() {
     75         return pos;
     76     }
     77 
     78     byte[] getSlice(int startPos, int size) {
     79         byte[] result = new byte[size];
     80         System.arraycopy(buf, startPos, result, 0, size);
     81         return result;
     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         if (makePositive) {
    161             return new BigInteger(1, bytes);
    162         } else {
    163             return new BigInteger(bytes);
    164         }
    165     }
    166 
    167     /**
    168      * Returns the integer which takes up the specified number
    169      * of bytes in this buffer.
    170      * @throws IOException if the result is not within the valid
    171      * range for integer, i.e. between Integer.MIN_VALUE and
    172      * Integer.MAX_VALUE.
    173      * @param len the number of bytes to use.
    174      * @return the integer.
    175      */
    176     public int getInteger(int len) throws IOException {
    177 
    178         BigInteger result = getBigInteger(len, false);
    179         if (result.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0) {
    180             throw new IOException("Integer below minimum valid value");
    181         }
    182         if (result.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) {
    183             throw new IOException("Integer exceeds maximum valid value");
    184         }
    185         return result.intValue();
    186     }
    187 
    188     /**
    189      * Returns the bit string which takes up the specified
    190      * number of bytes in this buffer.
    191      */
    192     public byte[] getBitString(int len) throws IOException {
    193         if (len > available())
    194             throw new IOException("short read of bit string");
    195 
    196         if (len == 0) {
    197             throw new IOException("Invalid encoding: zero length bit string");
    198         }
    199 
    200         int numOfPadBits = buf[pos];
    201         if ((numOfPadBits < 0) || (numOfPadBits > 7)) {
    202             throw new IOException("Invalid number of padding bits");
    203         }
    204         // minus the first byte which indicates the number of padding bits
    205         byte[] retval = new byte[len - 1];
    206         System.arraycopy(buf, pos + 1, retval, 0, len - 1);
    207         if (numOfPadBits != 0) {
    208             // get rid of the padding bits
    209             retval[len - 2] &= (0xff << numOfPadBits);
    210         }
    211         skip(len);
    212         return retval;
    213     }
    214 
    215     /**
    216      * Returns the bit string which takes up the rest of this buffer.
    217      */
    218     byte[] getBitString() throws IOException {
    219         return getBitString(available());
    220     }
    221 
    222     /**
    223      * Returns the bit string which takes up the rest of this buffer.
    224      * The bit string need not be byte-aligned.
    225      */
    226     BitArray getUnalignedBitString() throws IOException {
    227         if (pos >= count)
    228             return null;
    229         /*
    230          * Just copy the data into an aligned, padded octet buffer,
    231          * and consume the rest of the buffer.
    232          */
    233         int len = available();
    234         int unusedBits = buf[pos] & 0xff;
    235         if (unusedBits > 7 ) {
    236             throw new IOException("Invalid value for unused bits: " + unusedBits);
    237         }
    238         byte[] bits = new byte[len - 1];
    239         // number of valid bits
    240         int length = (bits.length == 0) ? 0 : bits.length * 8 - unusedBits;
    241 
    242         System.arraycopy(buf, pos + 1, bits, 0, len - 1);
    243 
    244         BitArray bitArray = new BitArray(length, bits);
    245         pos = count;
    246         return bitArray;
    247     }
    248 
    249     /**
    250      * Returns the UTC Time value that takes up the specified number
    251      * of bytes in this buffer.
    252      * @param len the number of bytes to use
    253      */
    254     public Date getUTCTime(int len) throws IOException {
    255         if (len > available())
    256             throw new IOException("short read of DER UTC Time");
    257 
    258         if (len < 11 || len > 17)
    259             throw new IOException("DER UTC Time length error");
    260 
    261         return getTime(len, false);
    262     }
    263 
    264     /**
    265      * Returns the Generalized Time value that takes up the specified
    266      * number of bytes in this buffer.
    267      * @param len the number of bytes to use
    268      */
    269     public Date getGeneralizedTime(int len) throws IOException {
    270         if (len > available())
    271             throw new IOException("short read of DER Generalized Time");
    272 
    273         if (len < 13 || len > 23)
    274             throw new IOException("DER Generalized Time length error");
    275 
    276         return getTime(len, true);
    277 
    278     }
    279 
    280     /**
    281      * Private helper routine to extract time from the der value.
    282      * @param len the number of bytes to use
    283      * @param generalized true if Generalized Time is to be read, false
    284      * if UTC Time is to be read.
    285      */
    286     private Date getTime(int len, boolean generalized) throws IOException {
    287 
    288         /*
    289          * UTC time encoded as ASCII chars:
    290          *       YYMMDDhhmmZ
    291          *       YYMMDDhhmmssZ
    292          *       YYMMDDhhmm+hhmm
    293          *       YYMMDDhhmm-hhmm
    294          *       YYMMDDhhmmss+hhmm
    295          *       YYMMDDhhmmss-hhmm
    296          * UTC Time is broken in storing only two digits of year.
    297          * If YY < 50, we assume 20YY;
    298          * if YY >= 50, we assume 19YY, as per RFC 3280.
    299          *
    300          * Generalized time has a four-digit year and allows any
    301          * precision specified in ISO 8601. However, for our purposes,
    302          * we will only allow the same format as UTC time, except that
    303          * fractional seconds (millisecond precision) are supported.
    304          */
    305 
    306         int year, month, day, hour, minute, second, millis;
    307         String type = null;
    308 
    309         if (generalized) {
    310             type = "Generalized";
    311             year = 1000 * Character.digit((char)buf[pos++], 10);
    312             year += 100 * Character.digit((char)buf[pos++], 10);
    313             year += 10 * Character.digit((char)buf[pos++], 10);
    314             year += Character.digit((char)buf[pos++], 10);
    315             len -= 2; // For the two extra YY
    316         } else {
    317             type = "UTC";
    318             year = 10 * Character.digit((char)buf[pos++], 10);
    319             year += Character.digit((char)buf[pos++], 10);
    320 
    321             if (year < 50)              // origin 2000
    322                 year += 2000;
    323             else
    324                 year += 1900;   // origin 1900
    325         }
    326 
    327         month = 10 * Character.digit((char)buf[pos++], 10);
    328         month += Character.digit((char)buf[pos++], 10);
    329 
    330         day = 10 * Character.digit((char)buf[pos++], 10);
    331         day += Character.digit((char)buf[pos++], 10);
    332 
    333         hour = 10 * Character.digit((char)buf[pos++], 10);
    334         hour += Character.digit((char)buf[pos++], 10);
    335 
    336         minute = 10 * Character.digit((char)buf[pos++], 10);
    337         minute += Character.digit((char)buf[pos++], 10);
    338 
    339         len -= 10; // YYMMDDhhmm
    340 
    341         /*
    342          * We allow for non-encoded seconds, even though the
    343          * IETF-PKIX specification says that the seconds should
    344          * always be encoded even if it is zero.
    345          */
    346 
    347         millis = 0;
    348         if (len > 2 && len < 12) {
    349             second = 10 * Character.digit((char)buf[pos++], 10);
    350             second += Character.digit((char)buf[pos++], 10);
    351             len -= 2;
    352             // handle fractional seconds (if present)
    353             if (buf[pos] == '.' || buf[pos] == ',') {
    354                 len --;
    355                 pos++;
    356                 // handle upto milisecond precision only
    357                 int precision = 0;
    358                 int peek = pos;
    359                 while (buf[peek] != 'Z' &&
    360                        buf[peek] != '+' &&
    361                        buf[peek] != '-') {
    362                     peek++;
    363                     precision++;
    364                 }
    365                 switch (precision) {
    366                 case 3:
    367                     millis += 100 * Character.digit((char)buf[pos++], 10);
    368                     millis += 10 * Character.digit((char)buf[pos++], 10);
    369                     millis += Character.digit((char)buf[pos++], 10);
    370                     break;
    371                 case 2:
    372                     millis += 100 * Character.digit((char)buf[pos++], 10);
    373                     millis += 10 * Character.digit((char)buf[pos++], 10);
    374                     break;
    375                 case 1:
    376                     millis += 100 * Character.digit((char)buf[pos++], 10);
    377                     break;
    378                 default:
    379                         throw new IOException("Parse " + type +
    380                             " time, unsupported precision for seconds value");
    381                 }
    382                 len -= precision;
    383             }
    384         } else
    385             second = 0;
    386 
    387         if (month == 0 || day == 0
    388             || month > 12 || day > 31
    389             || hour >= 24 || minute >= 60 || second >= 60)
    390             throw new IOException("Parse " + type + " time, invalid format");
    391 
    392         /*
    393          * Generalized time can theoretically allow any precision,
    394          * but we're not supporting that.
    395          */
    396         CalendarSystem gcal = CalendarSystem.getGregorianCalendar();
    397         CalendarDate date = gcal.newCalendarDate(null); // no time zone
    398         date.setDate(year, month, day);
    399         date.setTimeOfDay(hour, minute, second, millis);
    400         long time = gcal.getTime(date);
    401 
    402         /*
    403          * Finally, "Z" or "+hhmm" or "-hhmm" ... offsets change hhmm
    404          */
    405         if (! (len == 1 || len == 5))
    406             throw new IOException("Parse " + type + " time, invalid offset");
    407 
    408         int hr, min;
    409 
    410         switch (buf[pos++]) {
    411         case '+':
    412             hr = 10 * Character.digit((char)buf[pos++], 10);
    413             hr += Character.digit((char)buf[pos++], 10);
    414             min = 10 * Character.digit((char)buf[pos++], 10);
    415             min += Character.digit((char)buf[pos++], 10);
    416 
    417             if (hr >= 24 || min >= 60)
    418                 throw new IOException("Parse " + type + " time, +hhmm");
    419 
    420             time -= ((hr * 60) + min) * 60 * 1000;
    421             break;
    422 
    423         case '-':
    424             hr = 10 * Character.digit((char)buf[pos++], 10);
    425             hr += Character.digit((char)buf[pos++], 10);
    426             min = 10 * Character.digit((char)buf[pos++], 10);
    427             min += Character.digit((char)buf[pos++], 10);
    428 
    429             if (hr >= 24 || min >= 60)
    430                 throw new IOException("Parse " + type + " time, -hhmm");
    431 
    432             time += ((hr * 60) + min) * 60 * 1000;
    433             break;
    434 
    435         case 'Z':
    436             break;
    437 
    438         default:
    439             throw new IOException("Parse " + type + " time, garbage offset");
    440         }
    441         return new Date(time);
    442     }
    443 }
    444