001 /*
002 * Copyright (c) 2000 - 2006 The Legion Of The Bouncy Castle (http://www.bouncycastle.org)
003 *
004 * Permission is hereby granted, free of charge, to any person obtaining a copy of this
005 * software and associated documentation files (the "Software"), to deal in the Software
006 * without restriction, including without limitation the rights to use, copy, modify,
007 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
008 * permit persons to whom the Software is furnished to do so, subject to the following
009 * conditions:
010 *
011 * The above copyright notice and this permission notice shall be included in all copies
012 * or substantial portions of the Software.
013 *
014 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
015 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
016 * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
017 * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
018 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
019 * DEALINGS IN THE SOFTWARE.
020 *
021 */
022
023 package org.apache.directory.shared.asn1.der;
024
025
026 import java.io.ByteArrayInputStream;
027 import java.io.ByteArrayOutputStream;
028 import java.io.EOFException;
029 import java.io.FilterInputStream;
030 import java.io.IOException;
031 import java.io.InputStream;
032 import java.nio.ByteBuffer;
033 import java.util.Vector;
034
035
036 /**
037 * General purpose ASN.1 decoder.
038 */
039 public class ASN1InputStream extends FilterInputStream
040 {
041 private boolean EOF_FOUND = false;
042
043 private DERObject END_OF_STREAM = new DERObject( 0, null )
044 {
045 public void encode( ASN1OutputStream out ) throws IOException
046 {
047 throw new IOException( "End of stream." );
048 }
049
050
051 /**
052 * Compute the instance hash code
053 * @return the instance's hashcode
054 */
055 public int hashCode()
056 {
057 return 0;
058 }
059
060
061 public boolean equals( Object o )
062 {
063 return o == this;
064 }
065 };
066
067
068 public ASN1InputStream(ByteBuffer in)
069 {
070 super( newInputStream( in ) );
071 }
072
073
074 public ASN1InputStream(byte[] input)
075 {
076 super( new ByteArrayInputStream( input ) );
077 }
078
079
080 private static InputStream newInputStream( final ByteBuffer buf )
081 {
082 return new InputStream()
083 {
084 public synchronized int read() throws IOException
085 {
086 if ( !buf.hasRemaining() )
087 {
088 return -1;
089 }
090
091 int result = buf.get() & 0x000000FF;
092
093 return result;
094 }
095
096
097 public synchronized int read( byte[] bytes, int off, int len ) throws IOException
098 {
099 // Read only what's left
100 len = Math.min( len, buf.remaining() );
101 buf.get( bytes, off, len );
102 return len;
103 }
104 };
105 }
106
107
108 protected int readLength() throws IOException
109 {
110 int length = read();
111 if ( length < 0 )
112 {
113 throw new IOException( "EOF found when length expected." );
114 }
115
116 // Indefinite-length encoding.
117 if ( length == 0x80 )
118 {
119 return -1;
120 }
121
122 if ( length > 127 )
123 {
124 int size = length & 0x7f;
125
126 if ( size > 4 )
127 {
128 throw new IOException( "DER length more than 4 bytes." );
129 }
130
131 length = 0;
132 for ( int i = 0; i < size; i++ )
133 {
134 int next = read();
135
136 if ( next < 0 )
137 {
138 throw new IOException( "EOF found reading length." );
139 }
140
141 length = ( length << 8 ) + next;
142 }
143
144 if ( length < 0 )
145 {
146 throw new IOException( "Corrupted steam - negative length found." );
147 }
148 }
149
150 return length;
151 }
152
153
154 protected void readFully( byte[] bytes ) throws IOException
155 {
156 int left = bytes.length;
157 int len;
158
159 if ( left == 0 )
160 {
161 return;
162 }
163
164 while ( ( len = read( bytes, bytes.length - left, left ) ) > 0 )
165 {
166 if ( ( left -= len ) == 0 )
167 {
168 return;
169 }
170 }
171
172 if ( left != 0 )
173 {
174 throw new EOFException( "EOF encountered in middle of object." );
175 }
176 }
177
178
179 /**
180 * Build an object given its tag and a byte stream.
181 */
182 protected DEREncodable buildObject( int tag, byte[] bytes ) throws IOException
183 {
184 if ( ( tag & DERObject.APPLICATION ) != 0 )
185 {
186 return new DERApplicationSpecific( tag, bytes );
187 }
188
189 switch ( tag )
190 {
191 case DERObject.NULL:
192 return new DERNull();
193 case DERObject.SEQUENCE | DERObject.CONSTRUCTED:
194 ASN1InputStream ais = new ASN1InputStream( bytes );
195 DEREncodable obj = null;
196 DERSequence sequence = new DERSequence();
197
198 try
199 {
200 obj = ais.readObject();
201
202 while ( obj != null )
203 {
204 sequence.add( obj );
205 obj = ais.readObject();
206 }
207 }
208 finally
209 {
210 ais.close();
211 }
212
213 return sequence;
214 case DERObject.SET | DERObject.CONSTRUCTED:
215 ais = new ASN1InputStream( bytes );
216 DERSet set = new DERSet();
217
218 try
219 {
220 obj = ais.readObject();
221
222 while ( obj != null )
223 {
224 set.add( obj );
225 obj = ais.readObject();
226 }
227 }
228 finally
229 {
230 ais.close();
231 }
232
233 return set;
234 case DERObject.BOOLEAN:
235 return new DERBoolean( bytes );
236 case DERObject.INTEGER:
237 return new DERInteger( bytes );
238 case DERObject.ENUMERATED:
239 return new DEREnumerated( bytes );
240 case DERObject.OBJECT_IDENTIFIER:
241 return new DERObjectIdentifier( bytes );
242 case DERObject.BIT_STRING:
243 return new DERBitString( bytes );
244 case DERObject.NUMERIC_STRING:
245 return new DERNumericString( bytes );
246 case DERObject.UTF8_STRING:
247 return new DERUTF8String( bytes );
248 case DERObject.PRINTABLE_STRING:
249 return new DERPrintableString( bytes );
250 case DERObject.IA5_STRING:
251 return new DERIA5String( bytes );
252 case DERObject.T61_STRING:
253 return new DERTeletexString( bytes );
254 case DERObject.VISIBLE_STRING:
255 return new DERVisibleString( bytes );
256 case DERObject.GENERAL_STRING:
257 return new DERGeneralString( bytes );
258 case DERObject.UNIVERSAL_STRING:
259 return new DERUniversalString( bytes );
260 case DERObject.BMP_STRING:
261 return new DERBMPString( bytes );
262 case DERObject.OCTET_STRING:
263 return new DEROctetString( bytes );
264 case DERObject.UTC_TIME:
265 return new DERUTCTime( bytes );
266 case DERObject.GENERALIZED_TIME:
267 return new DERGeneralizedTime( bytes );
268 default:
269 // Tag number is bottom 5 bits.
270 if ( ( tag & DERObject.TAGGED ) != 0 )
271 {
272 int tagNo = tag & 0x1f;
273
274 if ( tagNo == 0x1f )
275 {
276 int idx = 0;
277
278 tagNo = 0;
279
280 while ( ( bytes[idx] & 0x80 ) != 0 )
281 {
282 tagNo |= ( bytes[idx++] & 0x7f );
283 tagNo <<= 7;
284 }
285
286 tagNo |= ( bytes[idx] & 0x7f );
287
288 byte[] tmp = bytes;
289
290 bytes = new byte[tmp.length - ( idx + 1 )];
291 System.arraycopy( tmp, idx + 1, bytes, 0, bytes.length );
292 }
293
294 // Empty tag.
295 if ( bytes.length == 0 )
296 {
297 if ( ( tag & DERObject.CONSTRUCTED ) == 0 )
298 {
299 return new DERTaggedObject( tagNo, new DERNull() );
300 }
301
302 return new DERTaggedObject( false, tagNo, new DERSequence() );
303 }
304
305 // Simple type - implicit, return an octet string.
306 if ( ( tag & DERObject.CONSTRUCTED ) == 0 )
307 {
308 return new DERTaggedObject( false, tagNo, new DEROctetString( bytes ) );
309 }
310
311 ais = new ASN1InputStream( bytes );
312
313 try
314 {
315 DEREncodable encodable = ais.readObject();
316
317 // Explicitly tagged - if it isn't we'd have to tell from
318 // the context.
319 if ( ais.available() == 0 )
320 {
321 return new DERTaggedObject( true, tagNo, encodable, bytes );
322 }
323
324 // Another implicit object, create a sequence.
325 DERSequence derSequence = new DERSequence();
326
327 while ( encodable != null )
328 {
329 derSequence.add( encodable );
330 encodable = ais.readObject();
331 }
332
333 return new DERTaggedObject( false, tagNo, derSequence );
334 }
335 finally
336 {
337 ais.close();
338 }
339 }
340
341 return new DERUnknownTag( tag, bytes );
342 }
343 }
344
345
346 /**
347 * Read a string of bytes representing an indefinite length object.
348 */
349 private byte[] readIndefiniteLengthFully() throws IOException
350 {
351 ByteArrayOutputStream baos = new ByteArrayOutputStream();
352 int b, b1;
353
354 b1 = read();
355
356 while ( ( b = read() ) >= 0 )
357 {
358 if ( b1 == 0 && b == 0 )
359 {
360 break;
361 }
362
363 baos.write( b1 );
364 b1 = b;
365 }
366
367 return baos.toByteArray();
368 }
369
370
371 private BERConstructedOctetString buildConstructedOctetString() throws IOException
372 {
373 Vector<DEREncodable> octets = new Vector<DEREncodable>();
374
375 for ( ;; )
376 {
377 DEREncodable encodable = readObject();
378
379 if ( encodable == END_OF_STREAM )
380 {
381 break;
382 }
383
384 octets.addElement( encodable );
385 }
386
387 return new BERConstructedOctetString( octets );
388 }
389
390
391 public DEREncodable readObject() throws IOException
392 {
393 int tag = read();
394 if ( tag == -1 )
395 {
396 if ( EOF_FOUND )
397 {
398 throw new EOFException( "Attempt to read past end of file." );
399 }
400
401 EOF_FOUND = true;
402
403 return null;
404 }
405
406 int length = readLength();
407
408 // Indefinite length method.
409 if ( length < 0 )
410 {
411 switch ( tag )
412 {
413 case DERObject.NULL:
414 return new BERNull();
415 case DERObject.SEQUENCE | DERObject.CONSTRUCTED:
416 BERSequence sequence = new BERSequence();
417
418 for ( ;; )
419 {
420 DEREncodable obj = readObject();
421
422 if ( obj == END_OF_STREAM )
423 {
424 break;
425 }
426
427 sequence.add( obj );
428 }
429 return sequence;
430 case DERObject.SET | DERObject.CONSTRUCTED:
431 BERSet set = new BERSet();
432
433 for ( ;; )
434 {
435 DEREncodable obj = readObject();
436
437 if ( obj == END_OF_STREAM )
438 {
439 break;
440 }
441
442 set.add( obj );
443 }
444 return set;
445 case DERObject.OCTET_STRING | DERObject.CONSTRUCTED:
446 return buildConstructedOctetString();
447 default:
448 // Tag number is bottom 5 bits.
449 if ( ( tag & DERObject.TAGGED ) != 0 )
450 {
451 int tagNo = tag & 0x1f;
452
453 if ( tagNo == 0x1f )
454 {
455 int b = read();
456
457 tagNo = 0;
458
459 while ( ( b >= 0 ) && ( ( b & 0x80 ) != 0 ) )
460 {
461 tagNo |= ( b & 0x7f );
462 tagNo <<= 7;
463 b = read();
464 }
465
466 tagNo |= ( b & 0x7f );
467 }
468
469 // Simple type - implicit, return an octet string.
470 if ( ( tag & DERObject.CONSTRUCTED ) == 0 )
471 {
472 byte[] bytes = readIndefiniteLengthFully();
473
474 return new BERTaggedObject( false, tagNo, new DEROctetString( bytes ) );
475 }
476
477 // Either constructed or explicitly tagged
478 DEREncodable dObj = readObject();
479
480 // Empty tag!
481 if ( dObj == END_OF_STREAM )
482 {
483 return new DERTaggedObject( tagNo );
484 }
485
486 DEREncodable next = readObject();
487
488 // Explicitly tagged.
489 if ( next == END_OF_STREAM )
490 {
491 return new BERTaggedObject( tagNo, dObj );
492 }
493
494 // Another implicit object, create a sequence.
495 BERSequence berSequence = new BERSequence();
496
497 berSequence.add( dObj );
498
499 do
500 {
501 berSequence.add( next );
502 next = readObject();
503 }
504 while ( next != END_OF_STREAM );
505
506 return new BERTaggedObject( false, tagNo, berSequence );
507 }
508
509 throw new IOException( "Unknown BER object encountered." );
510 }
511 }
512
513 // End of contents marker.
514 if ( tag == 0 && length == 0 )
515 {
516 return END_OF_STREAM;
517 }
518
519 byte[] bytes = new byte[length];
520
521 readFully( bytes );
522
523 return buildObject( tag, bytes );
524 }
525 }