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
26package sun.security.util;
27
28import java.io.ByteArrayInputStream;
29import java.io.IOException;
30import java.io.OutputStream;
31import java.math.BigInteger;
32import java.util.Date;
33import sun.util.calendar.CalendarDate;
34import 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 */
45class 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}
449