gcj hack
[org.ibex.crypto.git] / src / org / ibex / crypto / DER.java
1 /*
2  * org.ibex.der.* - By Brian Alliet
3  * Copyright (C) 2004 Brian Alliet
4  * 
5  * Based on Bouncy Castle by The Legion Of The Bouncy Castle
6  * Copyright (c) 2000 The Legion Of The Bouncy Castle 
7  * (http://www.bouncycastle.org)
8  * 
9  * Permission is hereby granted, free of charge, to any person obtaining a 
10  * copy of this software and associated documentation files (the "Software"),
11  * to deal in the Software without restriction, including without limitation 
12  * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
13  * and/or sell copies of the Software, and to permit persons to whom the 
14  * Software is furnished to do so, subject to the following conditions:
15  * 
16  * The above copyright notice and this permission notice shall be included in
17  * all copies or substantial portions of the Software.
18  * 
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22  * AUTHORS OR COPYRIGHT HOLDER.S BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
25  * DEALINGS IN THE SOFTWARE.
26  */
27 package org.ibex.crypto;
28 import java.io.IOException;
29 import java.io.*;
30 import java.text.ParseException;
31 import java.text.SimpleDateFormat;
32 import java.util.*;
33 import java.math.BigInteger;
34
35 public class DER {
36     public static class Null {
37         final static Null instance = new Null();
38         private Null() { /* noop */ }
39         public boolean equals(Object o) { return o == this; }
40     }
41
42     public static class TaggedObject {
43         public final Object object;
44         public final int tag;
45         public TaggedObject(int tag, Object object) { this.tag = tag; this.object = object; }
46     }
47
48     public static class UnknownObject {
49         public final byte[] data;
50         public final int tag;
51         public UnknownObject(int tag,byte[] data) { this.tag = tag; this.data = data; }
52     }
53
54     public static class BitString {
55         public final int paddingBits;
56         public final byte[] data;
57     
58         public BitString(int paddingBits, byte[] data) {
59             this.paddingBits = paddingBits;
60             this.data = data;
61         }
62     }
63
64     public static class Exception extends java.io.IOException {
65         public Exception(String s) { super(s); }
66     }
67
68     public static class InputStream extends FilterInputStream {
69         private static final int MAX_OBJECT_SIZE = 4*1024*1024;
70     
71         private int limit;
72         public int bytesLeft() { return limit < 0 ? Integer.MAX_VALUE : limit-pos; }
73         private int pos;
74         public int getPos() { return pos; }
75     
76         // hack around gcj bug
77         public DER.InputStream New(java.io.InputStream is) { return new DER.InputStream(is); }
78         public InputStream(java.io.InputStream is) { this(is,-1); } 
79         public InputStream(java.io.InputStream is, int limit) {
80             super(is);
81             this.limit = limit;
82         }
83     
84         public int read() throws IOException {
85             if(limit >= 0 && pos >= limit) return -1;
86             int n = super.read();
87             if(n != -1) {
88                 pos++;
89             }
90             return n;
91         }
92     
93         public int read(byte[] buf, int start, int len) throws IOException {
94             if(limit >= 0) {
95                 if(pos >= limit) return -1;
96                 len = Math.min(len,limit-pos);
97             }
98             int n = super.read(buf,start,len);
99             if(n != -1) {
100                 pos += n;
101             }
102             return n;
103         }
104     
105         protected void readFully(byte[] buf) throws IOException {
106             int pos = 0;
107             int left = buf.length;
108             while(left > 0) {
109                 int n = read(buf,pos,left);
110                 if(n == -1) throw new EOFException();
111                 pos += n;
112                 left -=n;
113             }
114         }
115     
116         protected int readByte() throws IOException {
117             int n = read();
118             if(n == -1) throw new EOFException();
119             return n;
120         }
121     
122         // From bouncycastle
123         private int readLength() throws IOException {
124             int length = read();
125             if (length < 0) throw new IOException("EOF found when length expected");
126             if (length == 0x80) return -1;      // indefinite-length encoding
127             if (length > 127) {
128                 int size = length & 0x7f;
129                 length = 0;
130                 for (int i = 0; i < size; i++) {
131                     int next = read();
132                     if (next < 0) throw new IOException("EOF found reading length");    
133                     length = (length << 8) + next;
134                 }
135             }
136             return length;
137         }
138         
139         public InputStream getSequenceStream() throws IOException {
140             int tag = readByte();
141             int length = readLength();
142             if(length < 0) throw new Exception("Indefinite length objects not supported");
143             if(tag != (CONSTRUCTED|0x10)) throw new Exception("Constructed Sequence expected");
144             return new InputStream(this,length);
145         }
146     
147         private static final int CONSTRUCTED = 0x20;    
148         public Object readObject() throws IOException {
149             int tag = readByte();        
150             int length = readLength();
151             if(length < 0) throw new Exception("Indefinite length objects not supported");
152             if(length > MAX_OBJECT_SIZE) throw new Exception("Object too large");
153         
154             switch(tag) {
155                 case 0x01: return buildBoolean(length); // Boolean
156                 case 0x02: return buildInteger(length); // Integer
157                 case 0x03: return buildBitString(length); // Bit String
158                 case 0x04: return buildOctetString(length); // Octet String
159                 case 0x05: return Null.instance; // NULL
160                 case 0x06: return buildObjectIdentifier(length); // Object Identifier
161                 
162                 case 0x13: // PrintableString
163                     // It is incorrect to treat this as an IA5String but the T.61 standard is way too old and backwards
164                     // to be worth supporting
165                 case 0x14: // T.61 String 
166                 case 0x16: // IA5String
167                     return buildIA5String(length);
168                 case 0x17: return buildTime(length,false);// UTC Time
169                 case 0x18: return buildTime(length,true); // Generalizd Time
170                 
171                 case CONSTRUCTED | 0x10: // Constructed Sequence
172                 case CONSTRUCTED | 0x11: // Constructed Set
173                     { 
174                         return buildSequence(length);
175                     }
176                 default: {
177                     if((tag & 0x80) != 0) {
178                         if ((tag & 0x1f) == 0x1f) throw new Exception("Unsupported high tag ecountered");
179                         // tagged object - bottom 5 bits are tag
180                         if(length == 0)
181                             return new TaggedObject(tag&0x1,((tag & CONSTRUCTED) == 0) ? (Object)Null.instance : (Object)new Vector());
182                         if((tag & CONSTRUCTED) == 0)
183                             return new TaggedObject(tag&0x1f,buildOctetString(length));
184                  
185                         InputStream dis = new InputStream(this,length);
186                         Object o = dis.readObject();
187                         if(dis.bytesLeft() == 0) return new TaggedObject(tag&0x1f,o);
188                         Vector v = new Vector();
189                         v.add(o);
190                         return buildSequence(dis,v);
191                     } else {
192                         return new UnknownObject(tag,readBytes(length));
193                     }
194                 }     
195             }
196         }
197
198         protected Vector buildSequence(int length) throws IOException { return buildSequence(new InputStream(this,length),new Vector()); }
199         protected Vector buildSequence(InputStream dis,Vector v) throws IOException {
200             try {
201                 for(;;) v.add(dis.readObject());
202             } catch(EOFException e) { 
203                 return v; 
204             }
205         }    
206     
207         protected byte[] readBytes(int length) throws IOException {
208             byte[] buf = new byte[length];
209             readFully(buf);
210             return buf;
211         }
212     
213         protected BigInteger buildInteger(int length) throws IOException { return new BigInteger(readBytes(length)); }
214     
215         // From bouncycastle
216         protected String buildObjectIdentifier(int length) throws IOException {
217             byte[] bytes = readBytes(length);
218             StringBuffer    objId = new StringBuffer();
219             int             value = 0;
220             boolean         first = true;
221
222             for (int i = 0; i != bytes.length; i++)
223                 {
224                     int b = bytes[i] & 0xff;
225
226                     value = value * 128 + (b & 0x7f);
227                     if ((b & 0x80) == 0)             // end of number reached
228                         {
229                             if (first)
230                                 {
231                                     switch (value / 40)
232                                         {
233                                             case 0:
234                                                 objId.append('0');
235                                                 break;
236                                             case 1:
237                                                 objId.append('1');
238                                                 value -= 40;
239                                                 break;
240                                             default:
241                                                 objId.append('2');
242                                                 value -= 80;
243                                         }
244                                     first = false;
245                                 }
246
247                             objId.append('.');
248                             objId.append(Integer.toString(value));
249                             value = 0;
250                         }
251                 }
252             return objId.toString();
253         }
254     
255         protected String buildIA5String(int length) throws IOException {
256             byte[] buf = readBytes(length);
257             char[] buf2 = new char[buf.length];
258             for(int i=0;i<buf.length;i++) buf2[i] = (char)(buf[i]&0xff);
259             return new String(buf2);
260         }
261     
262         private static final SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
263         protected Date buildTime(int length, boolean generalized) throws IOException {
264             String s = buildIA5String(length);
265             if(!generalized && s.length() > 0) {
266                 if(s.charAt(0) < '5') s = "20" + s;
267                 else s = "19" + s;
268             }
269             switch(s.length()) {
270                 case 13: s = s.substring(0,12) + "00GMT+00:00"; break; //  YYYYMMDDhhmmZ
271                 case 15: s = s.substring(0,14) + "GMT+00:00";   break; // YYYYMMDDhhmmssZ
272                 case 17: s = s.substring(0,12) + "00GMT" + s.substring(12,15) + ":" + s.substring(15,17); break;  // YYYYMMDDhhmm+hh'mm'
273                 case 19: s = s.substring(0,14) + "GMT" + s.substring(14, 17) + ":" + s.substring(17, 19); // YYYYMMDDhhmmss+hh'mm'
274                 default: throw new Exception("Unknown time format " + s);
275             }
276             try {
277                 return dateF.parse(s);
278             } catch(ParseException e) {
279                 throw new Exception("Coudln't parse time: " + e.getMessage());
280             }
281         }
282     
283         protected BitString buildBitString(int length) throws IOException {
284             if(length < 1) throw new Exception("bit string too short");
285             int padding = read();
286             if(padding == -1) throw new IOException("unexpected eof");
287             return new BitString(padding,readBytes(length-1));
288         }
289     
290         protected byte[] buildOctetString(int length) throws IOException { return readBytes(length); }
291     
292         protected Boolean buildBoolean(int length) throws IOException {
293             byte[] bytes = readBytes(length);
294             return bytes[0] != 0 ? Boolean.TRUE : Boolean.FALSE;
295         }
296     
297         /*
298           public static void main(String[] args) throws Exception {
299           InputStream is = new InputStream(new FileInputStream(args[0]));
300           try {
301           for(;;) dump(is.readObject(),"");
302           } catch(EOFException e) {
303           System.err.println("EOF");
304           }
305           }
306           public static void dump(Object o, String indent) {
307           if(o instanceof Vector) {
308           Vector v = (Vector) o;
309           System.out.println(indent + "Sequence/Set");
310           for(int i=0;i<v.size();i++) {
311           dump(v.elementAt(i),indent + i + ":  ");
312           }
313           } else if(o instanceof TaggedObject){
314           dump(((TaggedObject)o).object,indent + "Tagged object: ");
315           } else if(o instanceof byte[]) {
316           System.err.println(indent + "<Byte Array>");
317           } else {
318           System.err.println(indent + o.toString());
319           }
320           }*/
321     }
322 }