• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
DerInputBuffer(byte[] buf)47     DerInputBuffer(byte[] buf) { super(buf); }
48 
DerInputBuffer(byte[] buf, int offset, int len)49     DerInputBuffer(byte[] buf, int offset, int len) {
50         super(buf, offset, len);
51     }
52 
dup()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 
toByteArray()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     // BEGIN Android-added: Added getPos & getSlice, needed for APK parsing
getPos()75     int getPos() {
76         return pos;
77     }
78 
getSlice(int startPos, int size)79     byte[] getSlice(int startPos, int size) {
80         byte[] result = new byte[size];
81         System.arraycopy(buf, startPos, result, 0, size);
82         return result;
83     }
84     // END Android-added: Added getPos & getSlice, needed for APK parsing
85 
peek()86     int peek() throws IOException {
87         if (pos >= count)
88             throw new IOException("out of data");
89         else
90             return buf[pos];
91     }
92 
93     /**
94      * Compares this DerInputBuffer for equality with the specified
95      * object.
96      */
equals(Object other)97     public boolean equals(Object other) {
98         if (other instanceof DerInputBuffer)
99             return equals((DerInputBuffer)other);
100         else
101             return false;
102     }
103 
equals(DerInputBuffer other)104     boolean equals(DerInputBuffer other) {
105         if (this == other)
106             return true;
107 
108         int max = this.available();
109         if (other.available() != max)
110             return false;
111         for (int i = 0; i < max; i++) {
112             if (this.buf[this.pos + i] != other.buf[other.pos + i]) {
113                 return false;
114             }
115         }
116         return true;
117     }
118 
119     /**
120      * Returns a hashcode for this DerInputBuffer.
121      *
122      * @return a hashcode for this DerInputBuffer.
123      */
hashCode()124     public int hashCode() {
125         int retval = 0;
126 
127         int len = available();
128         int p = pos;
129 
130         for (int i = 0; i < len; i++)
131             retval += buf[p + i] * i;
132         return retval;
133     }
134 
truncate(int len)135     void truncate(int len) throws IOException {
136         if (len > available())
137             throw new IOException("insufficient data");
138         count = pos + len;
139     }
140 
141     /**
142      * Returns the integer which takes up the specified number
143      * of bytes in this buffer as a BigInteger.
144      * @param len the number of bytes to use.
145      * @param makePositive whether to always return a positive value,
146      *   irrespective of actual encoding
147      * @return the integer as a BigInteger.
148      */
getBigInteger(int len, boolean makePositive)149     BigInteger getBigInteger(int len, boolean makePositive) throws IOException {
150         if (len > available())
151             throw new IOException("short read of integer");
152 
153         if (len == 0) {
154             throw new IOException("Invalid encoding: zero length Int value");
155         }
156 
157         byte[] bytes = new byte[len];
158 
159         System.arraycopy(buf, pos, bytes, 0, len);
160         skip(len);
161 
162         // check to make sure no extra leading 0s for DER
163         if (len >= 2 && (bytes[0] == 0) && (bytes[1] >= 0)) {
164             throw new IOException("Invalid encoding: redundant leading 0s");
165         }
166 
167         if (makePositive) {
168             return new BigInteger(1, bytes);
169         } else {
170             return new BigInteger(bytes);
171         }
172     }
173 
174     /**
175      * Returns the integer which takes up the specified number
176      * of bytes in this buffer.
177      * @throws IOException if the result is not within the valid
178      * range for integer, i.e. between Integer.MIN_VALUE and
179      * Integer.MAX_VALUE.
180      * @param len the number of bytes to use.
181      * @return the integer.
182      */
getInteger(int len)183     public int getInteger(int len) throws IOException {
184 
185         BigInteger result = getBigInteger(len, false);
186         if (result.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0) {
187             throw new IOException("Integer below minimum valid value");
188         }
189         if (result.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) {
190             throw new IOException("Integer exceeds maximum valid value");
191         }
192         return result.intValue();
193     }
194 
195     /**
196      * Returns the bit string which takes up the specified
197      * number of bytes in this buffer.
198      */
getBitString(int len)199     public byte[] getBitString(int len) throws IOException {
200         if (len > available())
201             throw new IOException("short read of bit string");
202 
203         if (len == 0) {
204             throw new IOException("Invalid encoding: zero length bit string");
205         }
206 
207         int numOfPadBits = buf[pos];
208         if ((numOfPadBits < 0) || (numOfPadBits > 7)) {
209             throw new IOException("Invalid number of padding bits");
210         }
211         // minus the first byte which indicates the number of padding bits
212         byte[] retval = new byte[len - 1];
213         System.arraycopy(buf, pos + 1, retval, 0, len - 1);
214         if (numOfPadBits != 0) {
215             // get rid of the padding bits
216             retval[len - 2] &= (0xff << numOfPadBits);
217         }
218         skip(len);
219         return retval;
220     }
221 
222     /**
223      * Returns the bit string which takes up the rest of this buffer.
224      */
getBitString()225     byte[] getBitString() throws IOException {
226         return getBitString(available());
227     }
228 
229     /**
230      * Returns the bit string which takes up the rest of this buffer.
231      * The bit string need not be byte-aligned.
232      */
getUnalignedBitString()233     BitArray getUnalignedBitString() throws IOException {
234         if (pos >= count)
235             return null;
236         /*
237          * Just copy the data into an aligned, padded octet buffer,
238          * and consume the rest of the buffer.
239          */
240         int len = available();
241         int unusedBits = buf[pos] & 0xff;
242         if (unusedBits > 7 ) {
243             throw new IOException("Invalid value for unused bits: " + unusedBits);
244         }
245         byte[] bits = new byte[len - 1];
246         // number of valid bits
247         int length = (bits.length == 0) ? 0 : bits.length * 8 - unusedBits;
248 
249         System.arraycopy(buf, pos + 1, bits, 0, len - 1);
250 
251         BitArray bitArray = new BitArray(length, bits);
252         pos = count;
253         return bitArray;
254     }
255 
256     /**
257      * Returns the UTC Time value that takes up the specified number
258      * of bytes in this buffer.
259      * @param len the number of bytes to use
260      */
getUTCTime(int len)261     public Date getUTCTime(int len) throws IOException {
262         if (len > available())
263             throw new IOException("short read of DER UTC Time");
264 
265         if (len < 11 || len > 17)
266             throw new IOException("DER UTC Time length error");
267 
268         return getTime(len, false);
269     }
270 
271     /**
272      * Returns the Generalized Time value that takes up the specified
273      * number of bytes in this buffer.
274      * @param len the number of bytes to use
275      */
getGeneralizedTime(int len)276     public Date getGeneralizedTime(int len) throws IOException {
277         if (len > available())
278             throw new IOException("short read of DER Generalized Time");
279 
280         if (len < 13 || len > 23)
281             throw new IOException("DER Generalized Time length error");
282 
283         return getTime(len, true);
284 
285     }
286 
287     /**
288      * Private helper routine to extract time from the der value.
289      * @param len the number of bytes to use
290      * @param generalized true if Generalized Time is to be read, false
291      * if UTC Time is to be read.
292      */
getTime(int len, boolean generalized)293     private Date getTime(int len, boolean generalized) throws IOException {
294 
295         /*
296          * UTC time encoded as ASCII chars:
297          *       YYMMDDhhmmZ
298          *       YYMMDDhhmmssZ
299          *       YYMMDDhhmm+hhmm
300          *       YYMMDDhhmm-hhmm
301          *       YYMMDDhhmmss+hhmm
302          *       YYMMDDhhmmss-hhmm
303          * UTC Time is broken in storing only two digits of year.
304          * If YY < 50, we assume 20YY;
305          * if YY >= 50, we assume 19YY, as per RFC 3280.
306          *
307          * Generalized time has a four-digit year and allows any
308          * precision specified in ISO 8601. However, for our purposes,
309          * we will only allow the same format as UTC time, except that
310          * fractional seconds (millisecond precision) are supported.
311          */
312 
313         int year, month, day, hour, minute, second, millis;
314         String type = null;
315 
316         if (generalized) {
317             type = "Generalized";
318             year = 1000 * Character.digit((char)buf[pos++], 10);
319             year += 100 * Character.digit((char)buf[pos++], 10);
320             year += 10 * Character.digit((char)buf[pos++], 10);
321             year += Character.digit((char)buf[pos++], 10);
322             len -= 2; // For the two extra YY
323         } else {
324             type = "UTC";
325             year = 10 * Character.digit((char)buf[pos++], 10);
326             year += Character.digit((char)buf[pos++], 10);
327 
328             if (year < 50)              // origin 2000
329                 year += 2000;
330             else
331                 year += 1900;   // origin 1900
332         }
333 
334         month = 10 * Character.digit((char)buf[pos++], 10);
335         month += Character.digit((char)buf[pos++], 10);
336 
337         day = 10 * Character.digit((char)buf[pos++], 10);
338         day += Character.digit((char)buf[pos++], 10);
339 
340         hour = 10 * Character.digit((char)buf[pos++], 10);
341         hour += Character.digit((char)buf[pos++], 10);
342 
343         minute = 10 * Character.digit((char)buf[pos++], 10);
344         minute += Character.digit((char)buf[pos++], 10);
345 
346         len -= 10; // YYMMDDhhmm
347 
348         /*
349          * We allow for non-encoded seconds, even though the
350          * IETF-PKIX specification says that the seconds should
351          * always be encoded even if it is zero.
352          */
353 
354         millis = 0;
355         if (len > 2 && len < 12) {
356             second = 10 * Character.digit((char)buf[pos++], 10);
357             second += Character.digit((char)buf[pos++], 10);
358             len -= 2;
359             // handle fractional seconds (if present)
360             if (buf[pos] == '.' || buf[pos] == ',') {
361                 len --;
362                 pos++;
363                 // handle upto milisecond precision only
364                 int precision = 0;
365                 int peek = pos;
366                 while (buf[peek] != 'Z' &&
367                        buf[peek] != '+' &&
368                        buf[peek] != '-') {
369                     peek++;
370                     precision++;
371                 }
372                 switch (precision) {
373                 case 3:
374                     millis += 100 * Character.digit((char)buf[pos++], 10);
375                     millis += 10 * Character.digit((char)buf[pos++], 10);
376                     millis += Character.digit((char)buf[pos++], 10);
377                     break;
378                 case 2:
379                     millis += 100 * Character.digit((char)buf[pos++], 10);
380                     millis += 10 * Character.digit((char)buf[pos++], 10);
381                     break;
382                 case 1:
383                     millis += 100 * Character.digit((char)buf[pos++], 10);
384                     break;
385                 default:
386                         throw new IOException("Parse " + type +
387                             " time, unsupported precision for seconds value");
388                 }
389                 len -= precision;
390             }
391         } else
392             second = 0;
393 
394         if (month == 0 || day == 0
395             || month > 12 || day > 31
396             || hour >= 24 || minute >= 60 || second >= 60)
397             throw new IOException("Parse " + type + " time, invalid format");
398 
399         /*
400          * Generalized time can theoretically allow any precision,
401          * but we're not supporting that.
402          */
403         CalendarSystem gcal = CalendarSystem.getGregorianCalendar();
404         CalendarDate date = gcal.newCalendarDate(null); // no time zone
405         date.setDate(year, month, day);
406         date.setTimeOfDay(hour, minute, second, millis);
407         long time = gcal.getTime(date);
408 
409         /*
410          * Finally, "Z" or "+hhmm" or "-hhmm" ... offsets change hhmm
411          */
412         if (! (len == 1 || len == 5))
413             throw new IOException("Parse " + type + " time, invalid offset");
414 
415         int hr, min;
416 
417         switch (buf[pos++]) {
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 '-':
431             hr = 10 * Character.digit((char)buf[pos++], 10);
432             hr += Character.digit((char)buf[pos++], 10);
433             min = 10 * Character.digit((char)buf[pos++], 10);
434             min += Character.digit((char)buf[pos++], 10);
435 
436             if (hr >= 24 || min >= 60)
437                 throw new IOException("Parse " + type + " time, -hhmm");
438 
439             time += ((hr * 60) + min) * 60 * 1000;
440             break;
441 
442         case 'Z':
443             break;
444 
445         default:
446             throw new IOException("Parse " + type + " time, garbage offset");
447         }
448         return new Date(time);
449     }
450 }
451