finished last of the compile errors
[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         public InputStream(java.io.InputStream is) { this(is,-1); } 
77         public InputStream(java.io.InputStream is, int limit) {
78             super(is);
79             this.limit = limit;
80         }
81     
82         public int read() throws IOException {
83             if(limit >= 0 && pos >= limit) return -1;
84             int n = super.read();
85             if(n != -1) {
86                 pos++;
87             }
88             return n;
89         }
90     
91         public int read(byte[] buf, int start, int len) throws IOException {
92             if(limit >= 0) {
93                 if(pos >= limit) return -1;
94                 len = Math.min(len,limit-pos);
95             }
96             int n = super.read(buf,start,len);
97             if(n != -1) {
98                 pos += n;
99             }
100             return n;
101         }
102     
103         protected void readFully(byte[] buf) throws IOException {
104             int pos = 0;
105             int left = buf.length;
106             while(left > 0) {
107                 int n = read(buf,pos,left);
108                 if(n == -1) throw new EOFException();
109                 pos += n;
110                 left -=n;
111             }
112         }
113     
114         protected int readByte() throws IOException {
115             int n = read();
116             if(n == -1) throw new EOFException();
117             return n;
118         }
119     
120         // From bouncycastle
121         private int readLength() throws IOException {
122             int length = read();
123             if (length < 0) throw new IOException("EOF found when length expected");
124             if (length == 0x80) return -1;      // indefinite-length encoding
125             if (length > 127) {
126                 int size = length & 0x7f;
127                 length = 0;
128                 for (int i = 0; i < size; i++) {
129                     int next = read();
130                     if (next < 0) throw new IOException("EOF found reading length");    
131                     length = (length << 8) + next;
132                 }
133             }
134             return length;
135         }
136         
137         public InputStream getSequenceStream() throws IOException {
138             int tag = readByte();
139             int length = readLength();
140             if(length < 0) throw new Exception("Indefinite length objects not supported");
141             if(tag != (CONSTRUCTED|0x10)) throw new Exception("Constructed Sequence expected");
142             return new InputStream(this,length);
143         }
144     
145         private static final int CONSTRUCTED = 0x20;    
146         public Object readObject() throws IOException {
147             int tag = readByte();        
148             int length = readLength();
149             if(length < 0) throw new Exception("Indefinite length objects not supported");
150             if(length > MAX_OBJECT_SIZE) throw new Exception("Object too large");
151         
152             switch(tag) {
153                 case 0x01: return buildBoolean(length); // Boolean
154                 case 0x02: return buildInteger(length); // Integer
155                 case 0x03: return buildBitString(length); // Bit String
156                 case 0x04: return buildOctetString(length); // Octet String
157                 case 0x05: return Null.instance; // NULL
158                 case 0x06: return buildObjectIdentifier(length); // Object Identifier
159                 
160                 case 0x13: // PrintableString
161                     // It is incorrect to treat this as an IA5String but the T.61 standard is way too old and backwards
162                     // to be worth supporting
163                 case 0x14: // T.61 String 
164                 case 0x16: // IA5String
165                     return buildIA5String(length);
166                 case 0x17: return buildTime(length,false);// UTC Time
167                 case 0x18: return buildTime(length,true); // Generalizd Time
168                 
169                 case CONSTRUCTED | 0x10: // Constructed Sequence
170                 case CONSTRUCTED | 0x11: // Constructed Set
171                     { 
172                         return buildSequence(length);
173                     }
174                 default: {
175                     if((tag & 0x80) != 0) {
176                         if ((tag & 0x1f) == 0x1f) throw new Exception("Unsupported high tag ecountered");
177                         // tagged object - bottom 5 bits are tag
178                         if(length == 0)
179                             return new TaggedObject(tag&0x1,((tag & CONSTRUCTED) == 0) ? (Object)Null.instance : (Object)new Vector());
180                         if((tag & CONSTRUCTED) == 0)
181                             return new TaggedObject(tag&0x1f,buildOctetString(length));
182                  
183                         InputStream dis = new InputStream(this,length);
184                         Object o = dis.readObject();
185                         if(dis.bytesLeft() == 0) return new TaggedObject(tag&0x1f,o);
186                         Vector v = new Vector();
187                         v.add(o);
188                         return buildSequence(dis,v);
189                     } else {
190                         return new UnknownObject(tag,readBytes(length));
191                     }
192                 }     
193             }
194         }
195
196         protected Vector buildSequence(int length) throws IOException { return buildSequence(new InputStream(this,length),new Vector()); }
197         protected Vector buildSequence(InputStream dis,Vector v) throws IOException {
198             try {
199                 for(;;) v.add(dis.readObject());
200             } catch(EOFException e) { 
201                 return v; 
202             }
203         }    
204     
205         protected byte[] readBytes(int length) throws IOException {
206             byte[] buf = new byte[length];
207             readFully(buf);
208             return buf;
209         }
210     
211         protected BigInteger buildInteger(int length) throws IOException { return new BigInteger(readBytes(length)); }
212     
213         // From bouncycastle
214         protected String buildObjectIdentifier(int length) throws IOException {
215             byte[] bytes = readBytes(length);
216             StringBuffer    objId = new StringBuffer();
217             int             value = 0;
218             boolean         first = true;
219
220             for (int i = 0; i != bytes.length; i++)
221                 {
222                     int b = bytes[i] & 0xff;
223
224                     value = value * 128 + (b & 0x7f);
225                     if ((b & 0x80) == 0)             // end of number reached
226                         {
227                             if (first)
228                                 {
229                                     switch (value / 40)
230                                         {
231                                             case 0:
232                                                 objId.append('0');
233                                                 break;
234                                             case 1:
235                                                 objId.append('1');
236                                                 value -= 40;
237                                                 break;
238                                             default:
239                                                 objId.append('2');
240                                                 value -= 80;
241                                         }
242                                     first = false;
243                                 }
244
245                             objId.append('.');
246                             objId.append(Integer.toString(value));
247                             value = 0;
248                         }
249                 }
250             return objId.toString();
251         }
252     
253         protected String buildIA5String(int length) throws IOException {
254             byte[] buf = readBytes(length);
255             char[] buf2 = new char[buf.length];
256             for(int i=0;i<buf.length;i++) buf2[i] = (char)(buf[i]&0xff);
257             return new String(buf2);
258         }
259     
260         private static final SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
261         protected Date buildTime(int length, boolean generalized) throws IOException {
262             String s = buildIA5String(length);
263             if(!generalized && s.length() > 0) {
264                 if(s.charAt(0) < '5') s = "20" + s;
265                 else s = "19" + s;
266             }
267             switch(s.length()) {
268                 case 13: s = s.substring(0,12) + "00GMT+00:00"; break; //  YYYYMMDDhhmmZ
269                 case 15: s = s.substring(0,14) + "GMT+00:00";   break; // YYYYMMDDhhmmssZ
270                 case 17: s = s.substring(0,12) + "00GMT" + s.substring(12,15) + ":" + s.substring(15,17); break;  // YYYYMMDDhhmm+hh'mm'
271                 case 19: s = s.substring(0,14) + "GMT" + s.substring(14, 17) + ":" + s.substring(17, 19); // YYYYMMDDhhmmss+hh'mm'
272                 default: throw new Exception("Unknown time format " + s);
273             }
274             try {
275                 return dateF.parse(s);
276             } catch(ParseException e) {
277                 throw new Exception("Coudln't parse time: " + e.getMessage());
278             }
279         }
280     
281         protected BitString buildBitString(int length) throws IOException {
282             if(length < 1) throw new Exception("bit string too short");
283             int padding = read();
284             if(padding == -1) throw new IOException("unexpected eof");
285             return new BitString(padding,readBytes(length-1));
286         }
287     
288         protected byte[] buildOctetString(int length) throws IOException { return readBytes(length); }
289     
290         protected Boolean buildBoolean(int length) throws IOException {
291             byte[] bytes = readBytes(length);
292             return bytes[0] != 0 ? Boolean.TRUE : Boolean.FALSE;
293         }
294     
295         /*
296           public static void main(String[] args) throws Exception {
297           InputStream is = new InputStream(new FileInputStream(args[0]));
298           try {
299           for(;;) dump(is.readObject(),"");
300           } catch(EOFException e) {
301           System.err.println("EOF");
302           }
303           }
304           public static void dump(Object o, String indent) {
305           if(o instanceof Vector) {
306           Vector v = (Vector) o;
307           System.out.println(indent + "Sequence/Set");
308           for(int i=0;i<v.size();i++) {
309           dump(v.elementAt(i),indent + i + ":  ");
310           }
311           } else if(o instanceof TaggedObject){
312           dump(((TaggedObject)o).object,indent + "Tagged object: ");
313           } else if(o instanceof byte[]) {
314           System.err.println(indent + "<Byte Array>");
315           } else {
316           System.err.println(indent + o.toString());
317           }
318           }*/
319     }
320 }