• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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 
getPos()74     int getPos() {
75         return pos;
76     }
77 
getSlice(int startPos, int size)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 
peek()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      */
equals(Object other)95     public boolean equals(Object other) {
96         if (other instanceof DerInputBuffer)
97             return equals((DerInputBuffer)other);
98         else
99             return false;
100     }
101 
equals(DerInputBuffer other)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      */
hashCode()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 
truncate(int len)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      */
getBigInteger(int len, boolean makePositive)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      */
getInteger(int len)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      */
getBitString(int len)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      */
getBitString()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      */
getUnalignedBitString()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      */
getUTCTime(int len)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      */
getGeneralizedTime(int len)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      */
getTime(int len, boolean generalized)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