1    package org.bouncycastle.asn1;
2    
3    import java.math.BigInteger;
4    import java.io.FilterInputStream;
5    
6    import java.io.InputStream;
7    import java.io.ByteArrayInputStream;
8    import java.io.IOException;
9    import java.io.EOFException;
10   
11   
12   public class DERInputStream
13       extends FilterInputStream implements DERTags
14   {
15       public DERInputStream(
16           InputStream is)
17       {
18           super(is);
19       }
20   
21       protected int readLength()
22           throws IOException
23       {
24           int length = read();
25           if (length < 0)
26           {
27               throw new IOException("EOF found when length expected");
28           }
29   
30           if (length == 0x80)
31           {
32               return -1;      // indefinite-length encoding
33           }
34   
35           if (length > 127)
36           {
37               int size = length & 0x7f;
38   
39               length = 0;
40               for (int i = 0; i < size; i++)
41               {
42                   int next = read();
43   
44                   if (next < 0)
45                   {
46                       throw new IOException("EOF found reading length");
47                   }
48   
49                   length = (length << 8) + next;
50               }
51           }
52   
53           return length;
54       }
55   
56       protected void readFully(
57           byte[]  bytes)
58           throws IOException
59       {
60           int     left = bytes.length;
61   
62           if (left == 0)
63           {
64               return;
65           }
66   
67           while ((left -= read(bytes, bytes.length - left, left)) != 0)
68           {
69               ;
70           }
71       }
72   
73           /**
74            * build an object given its tag and a byte stream to construct it
75            * from.
76            */
77       protected DERObject buildObject(
78                   int         tag,
79                   byte[]  bytes)
80                   throws IOException
81           {
82                   switch (tag)
83           {
84           case NULL:
85               return null;   
86           case SEQUENCE | CONSTRUCTED:
87               ByteArrayInputStream    bIn = new ByteArrayInputStream(bytes);
88               BERInputStream          dIn = new BERInputStream(bIn);
89               DERConstructedSequence  seq = new DERConstructedSequence();
90   
91               try
92               {
93                   for (;;)
94                   {
95                       DERObject   obj = dIn.readObject();
96   
97                       seq.addObject(obj);
98                   }
99               }
100              catch (EOFException ex)
101              {
102                  return seq;
103              }
104          case SET | CONSTRUCTED:
105              bIn = new ByteArrayInputStream(bytes);
106              dIn = new BERInputStream(bIn);
107  
108              DEREncodableVector    v = new DEREncodableVector();
109  
110              try
111              {
112                  for (;;)
113                  {
114                      DERObject   obj = dIn.readObject();
115  
116                      v.add(obj);
117                  }
118              }
119              catch (EOFException ex)
120              {
121                  return new DERConstructedSet(v);
122              }
123          case BOOLEAN:
124              return new DERBoolean(bytes);
125          case INTEGER:
126              return new DERInteger(bytes);
127          case ENUMERATED:
128              return new DEREnumerated(bytes);
129          case OBJECT_IDENTIFIER:
130              return new DERObjectIdentifier(bytes);
131          case BIT_STRING:
132              int     padBits = bytes[0];
133              byte[]  data = new byte[bytes.length - 1];
134  
135              System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
136  
137              return new DERBitString(data, padBits);
138          case UTF8_STRING:
139              return new DERUTF8String(bytes);
140          case PRINTABLE_STRING:
141              return new DERPrintableString(bytes);
142          case IA5_STRING:
143              return new DERIA5String(bytes);
144          case T61_STRING:
145              return new DERT61String(bytes);
146          case VISIBLE_STRING:
147              return new DERVisibleString(bytes);
148          case UNIVERSAL_STRING:
149              return new DERUniversalString(bytes);
150          case BMP_STRING:
151              return new DERBMPString(bytes);
152          case OCTET_STRING:
153              return new DEROctetString(bytes);
154          case UTC_TIME:
155              return new DERUTCTime(bytes);
156          case GENERALIZED_TIME:
157              return new DERGeneralizedTime(bytes);
158          default:
159              //
160              // with tagged object tag number is bottom 5 bits
161              //
162              if ((tag & TAGGED) != 0)  
163              {
164                  if ((tag & 0x1f) == 0x1f)
165                  {
166                      throw new IOException("unsupported high tag encountered");
167                  }
168  
169                  if (bytes.length == 0)        // empty tag!
170                  {
171                      return new DERTaggedObject(false, tag & 0x1f, new DERConstructedSequence());
172                  }
173  
174                  //
175                  // simple type - implicit... return an octet string
176                  //
177                  if ((tag & CONSTRUCTED) == 0)
178                  {
179                      return new DERTaggedObject(false, tag & 0x1f, new DEROctetString(bytes));
180                  }
181  
182                  bIn = new ByteArrayInputStream(bytes);
183                  dIn = new BERInputStream(bIn);
184  
185                  DEREncodable dObj = dIn.readObject();
186  
187                  //
188                  // explicitly tagged (probably!) - if it isn't we'd have to
189                  // tell from the context
190                  //
191                  if (dIn.available() == 0)
192                  {
193                      return new DERTaggedObject(tag & 0x1f, dObj);
194                  }
195  
196                  //
197                  // another implicit object, we'll create a sequence...
198                  //
199                  seq = new DERConstructedSequence();
200  
201                  seq.addObject(dObj);
202  
203                  try
204                  {
205                      for (;;)
206                      {
207                          dObj = dIn.readObject();
208  
209                          seq.addObject(dObj);
210                      }
211                  }
212                  catch (EOFException ex)
213                  {
214                      // ignore --
215                  }
216  
217                  return new DERTaggedObject(false, tag & 0x1f, seq);
218              }
219  
220              return new DERUnknownTag(tag, bytes);
221          }
222          }
223  
224      public DERObject readObject()
225          throws IOException
226      {
227          int tag = read();
228          if (tag == -1)
229          {
230              throw new EOFException();
231          }
232  
233          int     length = readLength();
234          byte[]  bytes = new byte[length];
235  
236          readFully(bytes);
237  
238                  return buildObject(tag, bytes);
239          }
240  }
241