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