finished last of the compile errors
[org.ibex.crypto.git] / src / org / ibex / net / SSL.java
1 /*
2  * org.ibex.net.SSL - By Brian Alliet
3  * Copyright (C) 2004 Brian Alliet
4  * 
5  * Based on TinySSL by Adam Megacz
6  * Copyright (C) 2003 Adam Megacz <adam@xwt.org> all rights reserved.
7  * 
8  * You may modify, copy, and redistribute this code under the terms of
9  * the GNU Lesser General Public License version 2.1, with the exception
10  * of the portion of clause 6a after the semicolon (aka the "obnoxious
11  * relink clause")
12  */
13
14 package org.ibex.net;
15
16 import org.ibex.crypto.*;
17 import java.security.SecureRandom;
18
19 import java.net.Socket;
20 import java.net.SocketException;
21
22 import java.io.*;
23 import java.util.Enumeration;
24 import java.util.Hashtable;
25 import java.util.Random;
26 import java.util.Vector;
27
28 // FEATURE: Server socket
29
30 public class SSL extends Socket {
31     private String hostname;
32     
33     private int negotiated;
34     
35     private boolean tls = true;
36     private boolean sha;
37     
38     private final DataInputStream rawIS;
39     private final DataOutputStream rawOS;
40     
41     private final InputStream sslIS;
42     private final OutputStream sslOS;
43     
44     private byte[] sessionID;
45
46     private Digest clientWriteMACDigest;        
47     private Digest serverWriteMACDigest;        
48     private byte[] masterSecret;
49     
50     private RC4 writeRC4;
51     private RC4 readRC4;
52     
53     private long serverSequenceNumber;
54     private long clientSequenceNumber;
55     
56     private int warnings;
57     private boolean closed;
58     
59     // These are only used during negotiation
60     private byte[] serverRandom;
61     private byte[] clientRandom;
62     private byte[] preMasterSecret;
63     
64     // Buffers
65     private byte[] mac;
66     
67     private byte[] pending = new byte[16384];
68     private int pendingStart;
69     private int pendingLength;
70
71     private byte[] sendRecordBuf = new byte[16384];
72     
73     private int handshakeDataStart;
74     private int handshakeDataLength;
75     private byte[] readRecordBuf = new byte[16384+20];   // 20 == sizeof(sha1 hash)
76     private byte[] readRecordScratch = new byte[16384+20];
77     
78     private ByteArrayOutputStream handshakesBuffer;
79     
80     // End Buffers
81     
82     // Static variables
83     private final static byte[] pad1 = new byte[48];
84     private final static byte[] pad2 = new byte[48];
85     private final static byte[] pad1_sha = new byte[40];
86     private final static byte[] pad2_sha = new byte[40];
87     
88     static {
89         for(int i=0; i<pad1.length; i++) pad1[i] = (byte)0x36;
90         for(int i=0; i<pad2.length; i++) pad2[i] = (byte)0x5C;
91         for(int i=0; i<pad1_sha.length; i++) pad1_sha[i] = (byte)0x36;
92         for(int i=0; i<pad2_sha.length; i++) pad2_sha[i] = (byte)0x5C;
93     }
94     
95     private final static Hashtable caKeys = new Hashtable();
96     private static VerifyCallback verifyCallback;
97     
98     //
99     // Constructors
100     //
101     public SSL(String host) throws IOException { this(host,443); }
102     public SSL(String host, int port) throws IOException { this(host,port,true); }
103     public SSL(String host, int port, boolean negotiate) throws IOException { this(host,port,negotiate,null); }
104     public SSL(String host, int port, State state) throws IOException { this(host,port,true,state); }
105     public SSL(String host, int port, boolean negotiate, State state) throws IOException {
106         super(host,port);
107         hostname = host;
108         rawIS = new DataInputStream(new BufferedInputStream(super.getInputStream()));
109         rawOS = new DataOutputStream(new BufferedOutputStream(super.getOutputStream()));
110         sslIS = new SSLInputStream();
111         sslOS = new SSLOutputStream();
112         if(negotiate) negotiate(state);
113     }
114
115     public synchronized void setTLS(boolean b) { if(negotiated!=0) throw new IllegalStateException("already negotiated"); tls = b; }
116     
117     public void negotiate() throws IOException { negotiate(null); }
118     public synchronized void negotiate(State state) throws IOException {
119         if(negotiated != 0) throw new IllegalStateException("already negotiated");
120         
121         handshakesBuffer = new ByteArrayOutputStream();
122         
123         try {
124             sendClientHello(state != null ? state.sessionID : null);
125             flush();
126             debug("sent ClientHello (" + (tls?"TLSv1.0":"SSLv3.0")+")");
127             
128             receiveServerHello();
129             debug("got ServerHello (" + (tls?"TLSv1.0":"SSLv3.0")+")");
130             
131             boolean resume = 
132                 state != null && sessionID.length == state.sessionID.length && 
133                 eq(state.sessionID,0,sessionID,0,sessionID.length);
134             
135             if(resume) 
136                 negotiateResume(state);
137             else
138                 negotiateNew();
139             
140             // we're done with these now
141             clientRandom = serverRandom = preMasterSecret = null;
142             handshakesBuffer = null;
143             
144             log("Negotiation with " + hostname + " complete (" + (tls?"TLSv1.0":"SSLv3.0")+")");
145         } finally {
146             if((negotiated & 3) != 3) {
147                 negotiated = 0;
148                 try { super.close(); } catch(IOException e) { /* ignore */ }
149                 closed = true;
150             }
151         }
152     }
153     
154     private void negotiateResume(State state) throws IOException {
155         masterSecret = state.masterSecret;
156         
157         initCrypto();
158         log("initializec crypto");
159         
160         receiveChangeCipherSpec();
161         debug("Received ChangeCipherSpec");
162         negotiated |= 2;
163         receieveFinished();
164         debug("Received Finished");
165         
166         sendChangeCipherSpec();
167         debug("Sent ChangeCipherSpec");
168         negotiated |= 1;
169         sendFinished();
170         debug("Sent Finished");
171     }
172     
173     private void negotiateNew() throws IOException {
174         X509.Certificate[] certs = receiveServerCertificates();
175         debug("got Certificate");
176         
177         boolean gotCertificateRequest = false;
178         OUTER: for(;;) {
179             byte[] buf = readHandshake();
180             switch(buf[0]) {
181             case 14: // ServerHelloDone
182                 if(buf.length != 4) throw new Exn("ServerHelloDone contained trailing garbage");
183                 debug("got ServerHelloDone");
184                 break OUTER;
185             case 13: // CertificateRequest
186                 debug("Got a CertificateRequest message but we don't suport client certificates");
187                 gotCertificateRequest = true;
188                 break;
189             default:
190                 throw new Exn("unknown handshake type " + buf[0]);
191             }
192         }
193         
194         if(gotCertificateRequest)
195             sendHandshake((byte)11,new byte[3]); // send empty cert list
196         
197         try {
198             if(!hostname.equalsIgnoreCase(certs[0].getCN()))
199                 throw new Exn("Certificate is for " + certs[0].getCN() + " not " + hostname);
200             verifyCerts(certs);
201         } catch(Exn e) {
202             if(verifyCallback == null) throw e;
203             synchronized(SSL.class) {
204                 if(!verifyCallback.checkCerts(certs,hostname,e)) throw e;
205             }
206         }
207         
208         computeMasterSecret();
209         
210         sendClientKeyExchange(certs[0]);
211         debug("sent ClientKeyExchange");
212         
213         initCrypto();
214         
215         sendChangeCipherSpec();
216         debug("sent ChangeCipherSpec");
217         negotiated |= 1;
218         sendFinished();
219         debug("sent Finished");
220         flush();
221         
222         receiveChangeCipherSpec();
223         debug("got ChangeCipherSpec");
224         negotiated |= 2;
225         receieveFinished();
226         debug("got Finished");
227     }
228     
229     public State getSessionState() {
230         if((negotiated&3)!=3 || !closed || warnings != 0) return null;
231         return new State(sessionID,masterSecret);
232     }
233     public boolean isActive() { return !closed; }
234     public boolean isNegotiated() { return (negotiated&3) == 3; }
235     
236     private void sendClientHello(byte[] sessionID) throws IOException {
237         if(sessionID != null && sessionID.length > 256) throw new IllegalArgumentException("sessionID");
238         // 2 = version, 32 = randomvalue, 1 = sessionID size, 2 = cipher list size, 4 = the two ciphers,
239         // 2 = compression length/no compression
240         int p = 0;
241         byte[] buf = new byte[2+32+1+(sessionID == null ? 0 : sessionID.length)+2+2+4];
242         buf[p++] = 0x03; // major version
243         buf[p++] = tls ? (byte)0x01 : (byte)0x00;
244         
245         clientRandom = new byte[32];
246         int now = (int)(System.currentTimeMillis() / 1000L);
247         new Random().nextBytes(clientRandom);
248         clientRandom[0] = (byte)(now>>>24);
249         clientRandom[1] = (byte)(now>>>16);
250         clientRandom[2] = (byte)(now>>>8);
251         clientRandom[3] = (byte)(now>>>0);
252         System.arraycopy(clientRandom,0,buf,p,32);
253         p += 32;
254         
255         buf[p++] = sessionID != null ? (byte)sessionID.length : 0;
256         if(sessionID != null && sessionID.length != 0) System.arraycopy(sessionID,0,buf,p,sessionID.length);
257         p += sessionID != null ? sessionID.length : 0;
258         buf[p++] = 0x00; // 4 bytes of ciphers
259         buf[p++] = 0x04;
260         buf[p++] = 0x00; // SSL_RSA_WITH_RC4_128_SHA
261         buf[p++] = 0x05;
262         buf[p++] = 0x00; // SSL_RSA_WITH_RC4_128_MD5
263         buf[p++] = 0x04; 
264         
265         buf[p++] = 0x01;
266         buf[p++] = 0x00;
267                 
268         sendHandshake((byte)1,buf);
269         flush();
270     }
271     
272     private void receiveServerHello() throws IOException {
273         // ServerHello
274         byte[] buf = readHandshake();
275         if(buf[0] != 2) throw new Exn("expected a ServerHello message");
276         
277         if(buf.length < 6 + 32 + 1) throw new Exn("ServerHello too small");
278         if(buf.length < 6 + 32 + 1 + buf[6+32] + 3) throw new Exn("ServerHello too small " + buf.length+" "+buf[6+32]); 
279         
280         if(buf[4] != 0x03 || !(buf[5]==0x00 || buf[5]==0x01)) throw new Exn("server wants to use version " + buf[4] + "." + buf[5]);
281         tls = buf[5] == 0x01;
282         int p = 6;
283         serverRandom = new byte[32];
284         System.arraycopy(buf,p,serverRandom,0,32);
285         p += 32;
286         sessionID = new byte[buf[p++]&0xff];
287         if(sessionID.length != 0) System.arraycopy(buf,p,sessionID,0,sessionID.length);
288         p += sessionID.length;
289         int cipher = ((buf[p]&0xff)<<8) | (buf[p+1]&0xff);
290         p += 2;
291         switch(cipher) {
292             case 0x0004: sha = false; debug("Using SSL_RSA_WITH_RC4_128_MD5"); break;
293             case 0x0005: sha = true;  debug("Using SSL_RSA_WITH_RC4_128_SHA"); break;
294             default: throw new Exn("Unsupported cipher " + cipher);
295         }
296         mac = new byte[sha ? 20 : 16];
297         if(buf[p++] != 0x0) throw new Exn("unsupported compression " + buf[p-1]);
298     }
299     
300     private X509.Certificate[] receiveServerCertificates() throws IOException {
301         byte[] buf = readHandshake();
302         if(buf[0] != 11) throw new Exn("expected a Certificate message");
303         if((((buf[4]&0xff)<<16)|((buf[5]&0xff)<<8)|((buf[6]&0xff)<<0)) != buf.length-7) throw new Exn("size mismatch in Certificate message");
304         int p = 7;
305         int count = 0;
306         
307         for(int i=p;i<buf.length-3;i+=((buf[p+0]&0xff)<<16)|((buf[p+1]&0xff)<<8)|((buf[p+2]&0xff)<<0)) count++;
308         if(count == 0) throw new Exn("server didn't provide any certificates");
309         X509.Certificate[] certs = new X509.Certificate[count];
310         count = 0;
311         while(p < buf.length) {
312             int len = ((buf[p+0]&0xff)<<16)|((buf[p+1]&0xff)<<8)|((buf[p+2]&0xff)<<0);
313             p += 3;
314             if(p + len > buf.length) throw new Exn("Certificate message cut short");
315             certs[count++] = new X509.Certificate(new ByteArrayInputStream(buf,p,len));
316             p += len;
317         }
318         return certs;
319     }
320     
321     private void sendClientKeyExchange(X509.Certificate serverCert) throws IOException {
322         byte[] encryptedPreMasterSecret;
323         RSA.PublicKey pks = serverCert.getRSAPublicKey();
324         PKCS1 pkcs1 = new PKCS1(new RSA(pks.modulus,pks.exponent,false),random);
325         encryptedPreMasterSecret = pkcs1.encode(preMasterSecret);
326         byte[] buf;
327         if(tls) {
328             buf = new byte[encryptedPreMasterSecret.length+2];
329             buf[0] = (byte) (encryptedPreMasterSecret.length>>>8);
330             buf[1] = (byte) (encryptedPreMasterSecret.length>>>0);
331             System.arraycopy(encryptedPreMasterSecret,0,buf,2,encryptedPreMasterSecret.length);
332         } else {
333             // ugh... netscape didn't send the length bytes and now every SSLv3 implementation
334             // must implement this bug
335             buf = encryptedPreMasterSecret;
336         }
337         sendHandshake((byte)16,buf);
338     }
339     
340     private void sendChangeCipherSpec() throws IOException {
341         sendRecord((byte)20,new byte[] { 0x01 });
342     }
343     
344     private void computeMasterSecret() {
345         preMasterSecret = new byte[48];
346         preMasterSecret[0] = 0x03; // version_high
347         preMasterSecret[1] = tls ? (byte) 0x01 : (byte) 0x00; // version_low
348         randomBytes(preMasterSecret,2,46);
349         
350         if(tls) {
351             masterSecret = tlsPRF(48,preMasterSecret,getBytes("master secret"),concat(clientRandom,serverRandom));
352         } else {
353             masterSecret = concat(new byte[][] {
354                     md5(new byte[][] { preMasterSecret,
355                             sha1(new byte[][] { new byte[] { 0x41 }, preMasterSecret, clientRandom, serverRandom })}),
356                             md5(new byte[][] { preMasterSecret,
357                                     sha1(new byte[][] { new byte[] { 0x42, 0x42 }, preMasterSecret, clientRandom, serverRandom })}),
358                                     md5(new byte[][] { preMasterSecret,
359                                             sha1(new byte[][] { new byte[] { 0x43, 0x43, 0x43 }, preMasterSecret, clientRandom, serverRandom })})
360             } );    
361         }
362     }
363     
364     public void initCrypto() {
365         byte[] keyMaterial;
366         
367         if(tls) {
368             keyMaterial = tlsPRF(
369                     (mac.length + 16 + 0)*2, // MAC len + key len + iv len
370                     masterSecret,
371                     getBytes("key expansion"),
372                     concat(serverRandom,clientRandom)
373             );
374         } else {
375             keyMaterial = new byte[] { };
376             for(int i=0; keyMaterial.length < 72; i++) {
377                 byte[] crap = new byte[i + 1];
378                 for(int j=0; j<crap.length; j++) crap[j] = (byte)(((byte)0x41) + ((byte)i));
379                 keyMaterial = concat(new byte[][] { keyMaterial,
380                         md5(new byte[][] { masterSecret,
381                                 sha1(new byte[][] { crap, masterSecret, serverRandom, clientRandom }) }) });
382             }            
383         }
384
385         byte[] clientWriteMACSecret = new byte[mac.length];
386         byte[] serverWriteMACSecret = new byte[mac.length];
387         byte[] clientWriteKey = new byte[16];
388         byte[] serverWriteKey = new byte[16];
389         
390         int p = 0;
391         System.arraycopy(keyMaterial, p, clientWriteMACSecret, 0, mac.length); p += mac.length;
392         System.arraycopy(keyMaterial, p, serverWriteMACSecret, 0, mac.length); p += mac.length;
393         System.arraycopy(keyMaterial, p, clientWriteKey, 0, 16); p += 16; 
394         System.arraycopy(keyMaterial, p, serverWriteKey, 0, 16); p += 16;
395         
396         Digest inner;
397         
398         writeRC4 = new RC4(clientWriteKey);
399         inner = sha ? (Digest)new SHA1() : (Digest)new MD5();
400         clientWriteMACDigest = tls ? (Digest) new HMAC(inner,clientWriteMACSecret) : (Digest)new SSLv3HMAC(inner,clientWriteMACSecret);
401         
402         readRC4 = new RC4(serverWriteKey);
403         inner = sha ? (Digest)new SHA1() : (Digest)new MD5();
404         serverWriteMACDigest = tls ? (Digest)new HMAC(inner,serverWriteMACSecret) : (Digest)new SSLv3HMAC(inner,serverWriteMACSecret);
405     }
406     
407     private void sendFinished() throws IOException {
408         byte[] handshakes = handshakesBuffer.toByteArray();
409         if(tls) {
410             sendHandshake((byte)20, tlsPRF(
411                     12,
412                     masterSecret,
413                     getBytes("client finished"),
414                     concat(md5(handshakes),sha1(handshakes))));
415             
416         } else {
417             sendHandshake((byte)20, concat(new byte[][] { 
418                     md5(new byte[][] { masterSecret, pad2, 
419                                        md5(new byte[][] { handshakes, new byte[] { (byte)0x43, (byte)0x4C, (byte)0x4E, (byte)0x54 },
420                                                           masterSecret, pad1 }) }),
421                     sha1(new byte[][] { masterSecret, pad2_sha,
422                                        sha1(new byte[][] { handshakes, new byte[] { (byte)0x43, (byte)0x4C, (byte)0x4E, (byte)0x54 },
423                                                           masterSecret, pad1_sha } ) })
424                 }));
425         }
426     }
427         
428     private void receiveChangeCipherSpec() throws IOException {    
429         int size = readRecord((byte)20);
430         if(size == -1) throw new Exn("got eof when expecting a ChangeCipherSpec message");
431         if(size != 1 || readRecordBuf[0] != 0x01) throw new Exn("Invalid ChangeCipherSpec message");
432     }
433     
434     private void receieveFinished() throws IOException {
435         byte[] handshakes = handshakesBuffer.toByteArray();
436         byte[] buf = readHandshake();
437         if(buf[0] != 20) throw new Exn("expected a Finished message");
438         byte[] expected;
439         
440         if(tls) {
441             if(buf.length != 4 + 12) throw new Exn("Finished message too short");
442             expected = tlsPRF(
443                     12,masterSecret,
444                     getBytes("server finished"),
445                     concat(md5(handshakes),sha1(handshakes)));
446         } else {
447             if(buf.length != 4 + 16 +20) throw new Exn("Finished message too short");
448             expected = concat(new byte[][] {
449                     md5(new byte[][] { masterSecret, pad2,
450                             md5(new byte[][] { handshakes, new byte[] { (byte)0x53, (byte)0x52, (byte)0x56, (byte)0x52 },
451                                     masterSecret, pad1 }) }),
452                                     sha1(new byte[][] { masterSecret, pad2_sha,
453                                             sha1(new byte[][] { handshakes, new byte[] { (byte)0x53, (byte)0x52, (byte)0x56, (byte)0x52 },
454                                                     masterSecret, pad1_sha } ) } ) } );
455         }
456         if(!eq(expected,0,buf,4,expected.length)) throw new Exn("server finished message mismatch");
457     }
458     
459     private void flush() throws IOException { rawOS.flush(); }
460
461     private void sendHandshake(byte type, byte[] payload) throws IOException {
462         if(payload.length > (1<<24)) throw new IllegalArgumentException("payload.length");
463         byte[] buf = new byte[4+payload.length];
464         buf[0] = type;
465         buf[1] = (byte)(payload.length>>>16);
466         buf[2] = (byte)(payload.length>>>8);
467         buf[3] = (byte)(payload.length>>>0);
468         System.arraycopy(payload,0,buf,4,payload.length);
469         handshakesBuffer.write(buf);
470         sendRecord((byte)22,buf);
471     }
472     
473     private void sendRecord(byte proto, byte[] buf) throws IOException { sendRecord(proto,buf,0,buf.length); }
474     private void sendRecord(byte proto, byte[] payload, int off, int totalLen) throws IOException {
475         int macLength = (negotiated & 1) != 0 ? mac.length : 0;
476         while(totalLen > 0) {
477             int len = min(totalLen,16384-macLength);
478             rawOS.writeByte(proto);
479             rawOS.writeShort(tls ? 0x0301 : 0x0300);
480             if((negotiated & 1) != 0) {
481                 computeMAC(proto,payload,off,len,clientWriteMACDigest,clientSequenceNumber);
482                 // FEATURE: Encode in place
483                 writeRC4.process(payload,off,sendRecordBuf,0,len);
484                 writeRC4.process(mac,0,sendRecordBuf,len,macLength);
485                 rawOS.writeShort(len + macLength);
486                 rawOS.write(sendRecordBuf,0, len +macLength);
487                 clientSequenceNumber++;
488             } else {
489                 rawOS.writeShort(len);
490                 rawOS.write(payload,off,len);
491             }
492             totalLen -= len;
493             off += len;
494         }
495     }
496     
497     private byte[] readHandshake() throws IOException {
498         if(handshakeDataLength == 0) {
499             handshakeDataStart = 0;
500             handshakeDataLength = readRecord((byte)22);
501             if(handshakeDataLength == -1) throw new Exn("got eof when expecting a handshake packet");
502         }
503         byte[] buf = readRecordBuf;
504         int len = ((buf[handshakeDataStart+1]&0xff)<<16)|((buf[handshakeDataStart+2]&0xff)<<8)|((buf[handshakeDataStart+3]&0xff)<<0);
505         // Handshake messages can theoretically span multiple records, but in practice this does not occur
506         if(len > handshakeDataLength) {
507             sendAlert(true,10); // 10 == unexpected message
508             throw new Exn("handshake message size too large " + len + " vs " + (handshakeDataLength-handshakeDataStart));
509         }
510         byte[] ret = new byte[4+len];
511         System.arraycopy(buf,handshakeDataStart,ret,0,ret.length);
512         handshakeDataLength -= ret.length;
513         handshakeDataStart += ret.length;
514         handshakesBuffer.write(ret);
515         return ret;
516     }
517     
518     private int readRecord(byte reqProto) throws IOException {
519         int macLength = (negotiated & 2) != 0 ? mac.length : 0;
520         for(;;) {
521             byte proto;
522             int version, len;
523             
524             try {
525                 proto = rawIS.readByte();
526             } catch(EOFException e) {
527                 // this may or may not be an error. it is up to the application protocol
528                 closed = true;
529                 super.close();
530                 throw new PrematureCloseExn();
531             }
532             try {
533                 version = rawIS.readShort();
534                 if(version != 0x0300 && version != 0x0301) throw new Exn("invalid version ");
535                 len = rawIS.readShort();
536                 if(len <= 0 || len > 16384+((negotiated&2)!=0 ? macLength : 0)) throw new Exn("invalid length " + len);
537                 rawIS.readFully((negotiated&2)!=0 ? readRecordScratch : readRecordBuf,0,len);
538             } catch(EOFException e) {
539                 // an EOF here is always an error (we don't pass the EOF back on to the app
540                 // because it isn't a "legitimate" eof)
541                 throw new Exn("Hit EOF too early");
542             }
543             
544             if((negotiated & 2) != 0) {
545                 if(len < macLength) throw new Exn("packet size < macLength");
546                 // FEATURE: Decode in place
547                 readRC4.process(readRecordScratch,0,readRecordBuf,0,len);
548                 computeMAC(proto,readRecordBuf,0,len-macLength,serverWriteMACDigest,serverSequenceNumber);
549                 for(int i=0;i<macLength;i++)
550                     if(mac[i] != readRecordBuf[len-macLength+i])
551                         throw new Exn("mac mismatch");
552                 len -= macLength;
553                 serverSequenceNumber++;
554             }
555             
556             if(proto == reqProto) return len;
557             
558             switch(proto) {
559                 case 21: { // ALERT
560                     if(len != 2) throw new Exn("invalid lengh for alert");
561                     int level = readRecordBuf[0];
562                     int desc = readRecordBuf[1];
563                     if(level == 1) {
564                         if(desc == 0) { // CloseNotify
565                             debug("Server requested connection closure");
566                             try {
567                                 sendCloseNotify();
568                             } catch(SocketException e) { /* incomplete close, thats ok */ }
569                             closed = true;
570                             super.close();
571                             return -1;
572                         } else {
573                             warnings++;
574                             log("SSL ALERT WARNING: desc: " + desc);
575                         }
576                     } else if(level == 2) {
577                         throw new Exn("SSL ALERT FATAL: desc: " +desc);
578                     } else {
579                         throw new Exn("invalid alert level");
580                     }
581                     break;
582                 }
583                 case 22: { // Handshake
584                     int type = readRecordBuf[0];
585                     int hslen = ((readRecordBuf[1]&0xff)<<16)|((readRecordBuf[2]&0xff)<<8)|((readRecordBuf[3]&0xff)<<0);
586                     if(hslen > len - 4) throw new Exn("Multiple sequential handshake messages received after negotiation");
587                     if(type == 0) { // HellloRequest
588                         if(tls) sendAlert(false,100); // politely refuse, 100 == NoRegnegotiation
589                     } else {
590                         throw new Exn("Unexpected Handshake type: " + type);
591                     }
592                 }
593                 default: throw new Exn("Unexpected protocol: " + proto);
594             }
595         }
596     }
597     
598     private static void longToBytes(long l, byte[] buf, int off) {
599         for(int i=0;i<8;i++) buf[off+i] = (byte)(l>>>(8*(7-i)));
600     }
601     private void computeMAC(byte proto, byte[] payload, int off, int len, Digest digest, long sequenceNumber) {
602         if(tls) {
603             longToBytes(sequenceNumber,mac,0);
604             mac[8] = proto;
605             mac[9] = 0x03; // version
606             mac[10] = 0x01;
607             mac[11] = (byte)(len>>>8);
608             mac[12] = (byte)(len>>>0);
609             
610             digest.update(mac,0,13);
611             digest.update(payload,off,len);
612             digest.doFinal(mac,0);
613         } else {
614             longToBytes(sequenceNumber, mac, 0);
615             mac[8] = proto;
616             mac[9] = (byte)(len>>>8);
617             mac[10] = (byte)(len>>>0);
618             
619             digest.update(mac, 0, 11);
620             digest.update(payload, off, len);
621             digest.doFinal(mac, 0);
622         }
623     }
624     
625     private void sendCloseNotify() throws IOException { sendRecord((byte)21, new byte[] { 0x01, 0x00 }); }
626     private void sendAlert(boolean fatal, int message) throws IOException {
627         byte[] buf = new byte[] { fatal ? (byte)2 :(byte)1, (byte)message };
628         sendRecord((byte)21,buf);
629         flush();
630     }
631     
632     //
633     // Hash functions
634     //
635     
636     // Shared digest objects
637     private MD5 masterMD5 = new MD5();
638     private SHA1 masterSHA1 = new SHA1();
639     
640     private byte[] md5(byte[] in) { return md5( new byte[][] { in }); }
641     private byte[] md5(byte[][] inputs) {
642         masterMD5.reset();
643         for(int i=0; i<inputs.length; i++) masterMD5.update(inputs[i], 0, inputs[i].length);
644         byte[] ret = new byte[masterMD5.getDigestSize()];
645         masterMD5.doFinal(ret, 0);
646         return ret;
647     }
648     
649     private byte[] sha1(byte[] in)  { return sha1(new byte[][] { in }); }
650     private byte[] sha1(byte[][] inputs) {
651         masterSHA1.reset();
652         for(int i=0; i<inputs.length; i++) masterSHA1.update(inputs[i], 0, inputs[i].length);
653         byte[] ret = new byte[masterSHA1.getDigestSize()];
654         masterSHA1.doFinal(ret, 0);
655         return ret;
656     }
657     
658     /*  RFC-2246
659      PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed);
660      L_S = length in bytes of secret;
661      L_S1 = L_S2 = ceil(L_S / 2);
662      
663      The secret is partitioned into two halves (with the possibility of
664      one shared byte) as described above, S1 taking the first L_S1 bytes
665      and S2 the last L_S2 bytes.
666      
667      P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
668      HMAC_hash(secret, A(2) + seed) +
669      HMAC_hash(secret, A(3) + seed) + ...
670      
671      A(0) = seed
672      A(i) = HMAC_hash(secret, A(i-1))
673      */           
674     private byte[] tlsPRF(int size,byte[] secret, byte[] label, byte[] seed) {
675         if(size > 112) throw new IllegalArgumentException("size > 112");
676         seed = concat(label,seed);
677         
678         int half_length = (secret.length + 1) / 2;
679         byte[] s1 = new byte[half_length];
680         System.arraycopy(secret,0,s1,0,half_length);
681         byte[] s2 = new byte[half_length];
682         System.arraycopy(secret,secret.length - half_length, s2, 0, half_length);
683
684         Digest hmac_md5 = new HMAC(new MD5(),s1);
685         Digest hmac_sha = new HMAC(new SHA1(),s2);
686         
687         byte[] md5out = new byte[112];
688         byte[] shaout = new byte[120];
689         byte[] digest = new byte[20];
690         int n;
691         
692         n = 0;
693         hmac_md5.update(seed,0,seed.length);
694         hmac_md5.doFinal(digest,0);
695         
696         // digest == md5_a_1
697         while(n < size) {
698             hmac_md5.update(digest,0,16);
699             hmac_md5.update(seed,0,seed.length);
700             hmac_md5.doFinal(md5out,n);
701             hmac_md5.update(digest,0,16);
702             hmac_md5.doFinal(digest,0);
703             n += 16;
704         }
705         
706         n = 0;
707         hmac_sha.update(seed,0,seed.length);
708         hmac_sha.doFinal(digest,0);
709         
710         while(n < size) {
711             hmac_sha.update(digest,0,20);
712             hmac_sha.update(seed,0,seed.length);
713             hmac_sha.doFinal(shaout,n);
714             hmac_sha.update(digest,0,20);
715             hmac_sha.doFinal(digest,0);
716             n += 20;
717          }
718             
719         byte[] ret = new byte[size];
720         for(int i=0;i<size;i++) ret[i] = (byte)(md5out[i] ^ shaout[i]);
721         return ret;
722     }
723
724     public static class SSLv3HMAC extends Digest {
725         private final Digest h;
726         private final byte[] digest;
727         private final byte[] key;
728         private final int padSize;
729         
730         public int getDigestSize() { return h.getDigestSize(); }
731         
732         public SSLv3HMAC(Digest h, byte[] key) {
733             this.h = h;
734             this.key = key;
735             switch(h.getDigestSize()) {
736                 case 16: padSize = 48; break;
737                 case 20: padSize = 40; break;
738                 default: throw new IllegalArgumentException("unsupported digest size");
739             }
740             digest = new byte[h.getDigestSize()];
741             reset();
742         }
743         public void reset() {
744             h.reset();
745             h.update(key,0,key.length);
746             h.update(pad1,0,padSize);
747         }
748         public void update(byte[] b, int off, int len) { h.update(b,off,len); }
749         public void doFinal(byte[] out, int off){
750             h.doFinal(digest,0);
751             h.update(key,0,key.length);
752             h.update(pad2,0,padSize);
753             h.update(digest,0,digest.length);
754             h.doFinal(out,off);
755             reset();
756         }
757         protected void processWord(byte[] in, int inOff) {}
758         protected void processLength(long bitLength) {}
759         protected void processBlock() {}
760     }
761     
762     //
763     // Static Methods
764     //
765     
766     private static SecureRandom random = new SecureRandom();
767     public static synchronized void randomBytes(byte[] buf, int off, int len) {
768         byte[] bytes =  new byte[len];
769         random.nextBytes(bytes);
770         System.arraycopy(bytes,0,buf,off,len);
771     }
772     
773     public static byte[] concat(byte[] a, byte[] b) { return concat(new byte[][] { a, b }); }
774     public static byte[] concat(byte[] a, byte[] b, byte[] c) { return concat(new byte[][] { a, b, c }); }
775     public static byte[] concat(byte[][] inputs) {
776         int total = 0;
777         for(int i=0; i<inputs.length; i++) total += inputs[i].length;
778         byte[] ret = new byte[total];
779         for(int i=0,pos=0; i<inputs.length;pos+=inputs[i].length,i++)
780             System.arraycopy(inputs[i], 0, ret, pos, inputs[i].length);
781         return ret;
782     }
783     
784     public static byte[] getBytes(String s) {
785         try {
786             return s.getBytes("US-ASCII");
787         } catch (UnsupportedEncodingException e) {
788             return null; // will never happen
789         }
790     }
791     
792     public static boolean eq(byte[] a, int aoff, byte[] b, int boff, int len){
793         for(int i=0;i<len;i++) if(a[aoff+i] != b[boff+i]) return false;
794         return true;
795     }
796     
797     //
798     // InputStream/OutputStream/Socket interfaces
799     //
800     public OutputStream getOutputStream() { return sslOS; }
801     public InputStream getInputStream() { return sslIS; }
802     public synchronized void close() throws IOException {
803         if(!closed) {
804             if(negotiated != 0) {
805                 sendCloseNotify();
806                 flush();
807                 // don't bother sending a close_notify back to the server 
808                 // this is an incomplete close which is allowed by the spec
809             }
810             super.close();
811             closed = true;
812         }
813     }
814     
815     private int read(byte[] buf, int off, int len) throws IOException {
816         if(pendingLength == 0) {
817             if(closed) return -1;
818             int readLen = readRecord((byte)23);
819             if(readLen == -1) return -1; // EOF
820             len = min(len,readLen);
821             System.arraycopy(readRecordBuf,0,buf,off,len);
822             if(readLen > len) System.arraycopy(readRecordBuf,len,pending,0,readLen-len);
823             pendingStart = 0;
824             pendingLength = readLen - len;
825             return len;
826         } else {
827             len = min(len,pendingLength);
828             System.arraycopy(pending,pendingStart,buf,off,len);
829             pendingLength -= len;
830             pendingStart += len;
831             return len;
832         }
833     }
834     
835     private void write(byte[] buf, int off, int len) throws IOException {
836         if(closed) throw new SocketException("Socket closed");
837         sendRecord((byte)23,buf,off,len);
838         flush();
839     }
840     
841     private class SSLInputStream extends InputStream {
842         public int available() throws IOException {
843             synchronized(SSL.this) {
844                 return negotiated != 0 ? pendingLength : rawIS.available();
845             }
846         }
847         public int read() throws IOException {
848             synchronized(SSL.this) {
849                 if(negotiated==0) return rawIS.read();
850                 if(pendingLength > 0) {
851                     pendingLength--;
852                     return pending[pendingStart++];
853                 } else {
854                     byte[] buf = new byte[1];
855                     int n = read(buf);
856                     return n == -1 ? -1 : buf[0]&0xff;
857                 }
858             }
859         }
860         public int read(byte[] buf, int off, int len) throws IOException {
861             synchronized(SSL.this) {
862                 return negotiated!=0 ? SSL.this.read(buf,off,len) : rawIS.read(buf,off,len);
863             }
864         }
865         public long skip(long n) throws IOException {
866             synchronized(SSL.this) {
867                 if(negotiated==0) return rawIS.skip(n);
868                 if(pendingLength > 0) {
869                     n = min((int)n,pendingLength);
870                     pendingLength -= n;
871                     pendingStart += n;
872                     return n;
873                 }
874                 return super.skip(n);
875             }
876         }
877     }
878     
879     private class SSLOutputStream extends OutputStream {
880         public void flush() throws IOException { rawOS.flush(); }
881         public void write(int b) throws IOException { write(new byte[] { (byte)b }); }
882         public void write(byte[] buf, int off, int len) throws IOException {
883             synchronized(SSL.this) {
884                 if(negotiated!=0)
885                     SSL.this.write(buf,off,len);
886                 else
887                     rawOS.write(buf,off,len);
888             }
889         }
890     }
891     
892     public static class Exn extends IOException { public Exn(String s) { super(s); } }
893     public static class PrematureCloseExn extends Exn {
894         public PrematureCloseExn() { super("Connection was closed by the remote WITHOUT a close_noify"); }
895     }
896     
897     public static boolean debugOn = false;
898     private static void debug(Object o) { if(debugOn) System.err.println("[BriSSL-Debug] " + o.toString()); }
899     private static void log(Object o) { System.err.println("[BriSSL] " + o.toString()); }
900             
901     private static void verifyCerts(X509.Certificate[] certs) throws DER.Exception, Exn {
902         try {
903             verifyCerts_(certs);
904         } catch(RuntimeException e) {
905             e.printStackTrace();
906             throw new Exn("Error while verifying certificates: " + e);
907         }
908     }
909     
910     private static void verifyCerts_(X509.Certificate[] certs) throws DER.Exception, Exn {
911         boolean ignoreLast = false;
912         for(int i=0;i<certs.length;i++) {
913             debug("Cert " + i + ": " + certs[i].subject + " ok");
914             if(!certs[i].isValid())
915                 throw new Exn("Certificate " + i + " in certificate chain is not valid (" + certs[i].startDate + " - " + certs[i].endDate + ")");
916             if(i != 0) {
917                 X509.Certificate.BC bc = certs[i].basicContraints;
918                 if(bc == null) {
919                     if(i == certs.length - 1) {
920                         ignoreLast = true;
921                         break;
922                     }
923                     throw new Exn("CA-cert lacks Basic Constraints");
924                 } else {
925                     if(!bc.isCA) throw new Exn("non-CA certificate used for signing");
926                     if(bc.pathLenConstraint != null && bc.pathLenConstraint.longValue() < i-1) throw new Exn("CA cert can't be used this deep");
927                 }
928             }
929             if(i != certs.length - 1) {
930                 if(!certs[i].issuer.equals(certs[i+1].subject))
931                     throw new Exn("Issuer for certificate " + i + " does not match next in chain");
932                 if(!certs[i].isSignedBy(certs[i+1]))
933                     throw new Exn("Certificate " + i + " in chain is not signed by the next certificate");
934             }
935         }
936         
937         X509.Certificate cert = certs[ignoreLast ? certs.length - 2 : certs.length-1];
938         
939         RSA.PublicKey pks = (RSA.PublicKey) caKeys.get(cert.issuer);
940         if(pks == null) throw new Exn("Certificate is signed by an unknown CA (" + cert.issuer + ")");
941         if(!cert.isSignedWith(pks)) throw new Exn("Certificate is not signed by its CA");
942         log("" + cert.subject + " is signed by " + cert.issuer);
943     }
944     
945     public static void addCACert(byte[] b) throws IOException { addCACert(new ByteArrayInputStream(b)); }
946     public static void addCACert(InputStream is) throws IOException { addCACert(new X509.Certificate(is)); }
947     public static void addCACert(X509.Certificate cert) throws DER.Exception { addCAKey(cert.subject,cert.getRSAPublicKey()); }
948     public static void addCAKey(X509.Name subject, RSA.PublicKey pks)  {
949         synchronized(caKeys) {
950             if(caKeys.get(subject) != null)
951                 throw new IllegalArgumentException(subject.toString() + " already exists!");
952             caKeys.put(subject,pks);
953         }
954     }
955     
956     static {
957         try {
958             // This will force a <clinit> which'll load the certs
959             Class.forName("org.ibex.net.ssl.RootCerts");
960             log("Loaded root keys from org.ibex.net.ssl.RootCerts");
961         } catch(ClassNotFoundException e) {
962             InputStream is = SSL.class.getClassLoader().getResourceAsStream("org.ibex/net/ssl/rootcerts.dat");
963             if(is != null) {
964                 try {
965                     addCompactCAKeys(is);
966                     log("Loaded root certs from rootcerts.dat");
967                 } catch(IOException e2) {
968                     log("Error loading certs from rootcerts.dat: " + e2.getMessage()); 
969                 }
970             }
971         }
972     }
973         
974     public static int addCompactCAKeys(InputStream is) throws IOException {
975         synchronized(caKeys) {
976             try {
977                 Vector seq = (Vector) new DER.InputStream(is).readObject();
978                 for(Enumeration e = seq.elements(); e.hasMoreElements();) {
979                     Vector seq2 = (Vector) e.nextElement();
980                     X509.Name subject = new X509.Name(seq2.elementAt(0));
981                     RSA.PublicKey pks = new RSA.PublicKey(seq2.elementAt(1));
982                     addCAKey(subject,pks);
983                 }
984                 return seq.size();
985             } catch(RuntimeException e) {
986                 e.printStackTrace();
987                 throw new IOException("error while reading stream: " + e);
988             }
989         }
990     }
991     
992     public static synchronized void setVerifyCallback(VerifyCallback cb) { verifyCallback = cb; }
993     
994     // State Info
995     public static class State {
996         byte[] sessionID;
997         byte[] masterSecret;
998         State(byte[] sessionID, byte[] masterSecret) {
999             this.sessionID = sessionID;
1000             this.masterSecret = masterSecret;
1001         }
1002     }
1003     
1004     public interface VerifyCallback {
1005         public boolean checkCerts(X509.Certificate[] certs, String hostname, Exn exn);
1006     }
1007     
1008     // Helper methods
1009     private static final int min(int a, int b) { return a < b ? a : b; }
1010 }