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