From 2d5539a40aebddc8b35c830dd4077a2b16bd5d17 Mon Sep 17 00:00:00 2001 From: adam Date: Tue, 13 Jul 2004 07:07:55 +0000 Subject: [PATCH] added SSL darcs-hash:20040713070755-5007d-2d080b9e0d1702968919b344bc432561648e8769.gz --- src/org/ibex/net/SSL.java | 1010 +++++++++++++++++++++++++ src/org/ibex/net/ssl/GenCompactCAList.java | 98 +++ src/org/ibex/net/ssl/RootCerts.java | 29 + src/org/ibex/net/ssl/SwingVerifyCallback.java | 99 +++ src/org/ibex/net/ssl/Test.java | 32 + src/org/ibex/net/ssl/rootcerts.dat | Bin 0 -> 32407 bytes 6 files changed, 1268 insertions(+) create mode 100644 src/org/ibex/net/SSL.java create mode 100644 src/org/ibex/net/ssl/GenCompactCAList.java create mode 100644 src/org/ibex/net/ssl/RootCerts.java create mode 100644 src/org/ibex/net/ssl/SwingVerifyCallback.java create mode 100644 src/org/ibex/net/ssl/Test.java create mode 100644 src/org/ibex/net/ssl/rootcerts.dat diff --git a/src/org/ibex/net/SSL.java b/src/org/ibex/net/SSL.java new file mode 100644 index 0000000..04519cd --- /dev/null +++ b/src/org/ibex/net/SSL.java @@ -0,0 +1,1010 @@ +/* + * org.ibex.net.SSL - By Brian Alliet + * Copyright (C) 2004 Brian Alliet + * + * Based on TinySSL by Adam Megacz + * Copyright (C) 2003 Adam Megacz all rights reserved. + * + * You may modify, copy, and redistribute this code under the terms of + * the GNU Lesser General Public License version 2.1, with the exception + * of the portion of clause 6a after the semicolon (aka the "obnoxious + * relink clause") + */ + +package org.ibex.net; + +import org.ibex.crypto.*; +import java.security.SecureRandom; + +import java.net.Socket; +import java.net.SocketException; + +import java.io.*; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Random; +import java.util.Vector; + +// FEATURE: Server socket + +public class SSL extends Socket { + private String hostname; + + private int negotiated; + + private boolean tls = true; + private boolean sha; + + private final DataInputStream rawIS; + private final DataOutputStream rawOS; + + private final InputStream sslIS; + private final OutputStream sslOS; + + private byte[] sessionID; + + private Digest clientWriteMACDigest; + private Digest serverWriteMACDigest; + private byte[] masterSecret; + + private RC4 writeRC4; + private RC4 readRC4; + + private long serverSequenceNumber; + private long clientSequenceNumber; + + private int warnings; + private boolean closed; + + // These are only used during negotiation + private byte[] serverRandom; + private byte[] clientRandom; + private byte[] preMasterSecret; + + // Buffers + private byte[] mac; + + private byte[] pending = new byte[16384]; + private int pendingStart; + private int pendingLength; + + private byte[] sendRecordBuf = new byte[16384]; + + private int handshakeDataStart; + private int handshakeDataLength; + private byte[] readRecordBuf = new byte[16384+20]; // 20 == sizeof(sha1 hash) + private byte[] readRecordScratch = new byte[16384+20]; + + private ByteArrayOutputStream handshakesBuffer; + + // End Buffers + + // Static variables + private final static byte[] pad1 = new byte[48]; + private final static byte[] pad2 = new byte[48]; + private final static byte[] pad1_sha = new byte[40]; + private final static byte[] pad2_sha = new byte[40]; + + static { + for(int i=0; i 256) throw new IllegalArgumentException("sessionID"); + // 2 = version, 32 = randomvalue, 1 = sessionID size, 2 = cipher list size, 4 = the two ciphers, + // 2 = compression length/no compression + int p = 0; + byte[] buf = new byte[2+32+1+(sessionID == null ? 0 : sessionID.length)+2+2+4]; + buf[p++] = 0x03; // major version + buf[p++] = tls ? (byte)0x01 : (byte)0x00; + + clientRandom = new byte[32]; + int now = (int)(System.currentTimeMillis() / 1000L); + new Random().nextBytes(clientRandom); + clientRandom[0] = (byte)(now>>>24); + clientRandom[1] = (byte)(now>>>16); + clientRandom[2] = (byte)(now>>>8); + clientRandom[3] = (byte)(now>>>0); + System.arraycopy(clientRandom,0,buf,p,32); + p += 32; + + buf[p++] = sessionID != null ? (byte)sessionID.length : 0; + if(sessionID != null && sessionID.length != 0) System.arraycopy(sessionID,0,buf,p,sessionID.length); + p += sessionID != null ? sessionID.length : 0; + buf[p++] = 0x00; // 4 bytes of ciphers + buf[p++] = 0x04; + buf[p++] = 0x00; // SSL_RSA_WITH_RC4_128_SHA + buf[p++] = 0x05; + buf[p++] = 0x00; // SSL_RSA_WITH_RC4_128_MD5 + buf[p++] = 0x04; + + buf[p++] = 0x01; + buf[p++] = 0x00; + + sendHandshake((byte)1,buf); + flush(); + } + + private void receiveServerHello() throws IOException { + // ServerHello + byte[] buf = readHandshake(); + if(buf[0] != 2) throw new Exn("expected a ServerHello message"); + + if(buf.length < 6 + 32 + 1) throw new Exn("ServerHello too small"); + if(buf.length < 6 + 32 + 1 + buf[6+32] + 3) throw new Exn("ServerHello too small " + buf.length+" "+buf[6+32]); + + if(buf[4] != 0x03 || !(buf[5]==0x00 || buf[5]==0x01)) throw new Exn("server wants to use version " + buf[4] + "." + buf[5]); + tls = buf[5] == 0x01; + int p = 6; + serverRandom = new byte[32]; + System.arraycopy(buf,p,serverRandom,0,32); + p += 32; + sessionID = new byte[buf[p++]&0xff]; + if(sessionID.length != 0) System.arraycopy(buf,p,sessionID,0,sessionID.length); + p += sessionID.length; + int cipher = ((buf[p]&0xff)<<8) | (buf[p+1]&0xff); + p += 2; + switch(cipher) { + case 0x0004: sha = false; debug("Using SSL_RSA_WITH_RC4_128_MD5"); break; + case 0x0005: sha = true; debug("Using SSL_RSA_WITH_RC4_128_SHA"); break; + default: throw new Exn("Unsupported cipher " + cipher); + } + mac = new byte[sha ? 20 : 16]; + if(buf[p++] != 0x0) throw new Exn("unsupported compression " + buf[p-1]); + } + + private X509.Certificate[] receiveServerCertificates() throws IOException { + byte[] buf = readHandshake(); + if(buf[0] != 11) throw new Exn("expected a Certificate message"); + if((((buf[4]&0xff)<<16)|((buf[5]&0xff)<<8)|((buf[6]&0xff)<<0)) != buf.length-7) throw new Exn("size mismatch in Certificate message"); + int p = 7; + int count = 0; + + for(int i=p;i buf.length) throw new Exn("Certificate message cut short"); + certs[count++] = new X509.Certificate(new ByteArrayInputStream(buf,p,len)); + p += len; + } + return certs; + } + + private void sendClientKeyExchange(X509.Certificate serverCert) throws IOException { + byte[] encryptedPreMasterSecret; + RSA.PublicKey pks = serverCert.getRSAPublicKey(); + PKCS1 pkcs1 = new PKCS1(new RSA(pks.modulus,pks.exponent,false),random); + encryptedPreMasterSecret = pkcs1.encode(preMasterSecret); + byte[] buf; + if(tls) { + buf = new byte[encryptedPreMasterSecret.length+2]; + buf[0] = (byte) (encryptedPreMasterSecret.length>>>8); + buf[1] = (byte) (encryptedPreMasterSecret.length>>>0); + System.arraycopy(encryptedPreMasterSecret,0,buf,2,encryptedPreMasterSecret.length); + } else { + // ugh... netscape didn't send the length bytes and now every SSLv3 implementation + // must implement this bug + buf = encryptedPreMasterSecret; + } + sendHandshake((byte)16,buf); + } + + private void sendChangeCipherSpec() throws IOException { + sendRecord((byte)20,new byte[] { 0x01 }); + } + + private void computeMasterSecret() { + preMasterSecret = new byte[48]; + preMasterSecret[0] = 0x03; // version_high + preMasterSecret[1] = tls ? (byte) 0x01 : (byte) 0x00; // version_low + randomBytes(preMasterSecret,2,46); + + if(tls) { + masterSecret = tlsPRF(48,preMasterSecret,getBytes("master secret"),concat(clientRandom,serverRandom)); + } else { + masterSecret = concat(new byte[][] { + md5(new byte[][] { preMasterSecret, + sha1(new byte[][] { new byte[] { 0x41 }, preMasterSecret, clientRandom, serverRandom })}), + md5(new byte[][] { preMasterSecret, + sha1(new byte[][] { new byte[] { 0x42, 0x42 }, preMasterSecret, clientRandom, serverRandom })}), + md5(new byte[][] { preMasterSecret, + sha1(new byte[][] { new byte[] { 0x43, 0x43, 0x43 }, preMasterSecret, clientRandom, serverRandom })}) + } ); + } + } + + public void initCrypto() { + byte[] keyMaterial; + + if(tls) { + keyMaterial = tlsPRF( + (mac.length + 16 + 0)*2, // MAC len + key len + iv len + masterSecret, + getBytes("key expansion"), + concat(serverRandom,clientRandom) + ); + } else { + keyMaterial = new byte[] { }; + for(int i=0; keyMaterial.length < 72; i++) { + byte[] crap = new byte[i + 1]; + for(int j=0; j (1<<24)) throw new IllegalArgumentException("payload.length"); + byte[] buf = new byte[4+payload.length]; + buf[0] = type; + buf[1] = (byte)(payload.length>>>16); + buf[2] = (byte)(payload.length>>>8); + buf[3] = (byte)(payload.length>>>0); + System.arraycopy(payload,0,buf,4,payload.length); + handshakesBuffer.write(buf); + sendRecord((byte)22,buf); + } + + private void sendRecord(byte proto, byte[] buf) throws IOException { sendRecord(proto,buf,0,buf.length); } + private void sendRecord(byte proto, byte[] payload, int off, int totalLen) throws IOException { + int macLength = (negotiated & 1) != 0 ? mac.length : 0; + while(totalLen > 0) { + int len = min(totalLen,16384-macLength); + rawOS.writeByte(proto); + rawOS.writeShort(tls ? 0x0301 : 0x0300); + if((negotiated & 1) != 0) { + computeMAC(proto,payload,off,len,clientWriteMACDigest,clientSequenceNumber); + // FEATURE: Encode in place + writeRC4.process(payload,off,sendRecordBuf,0,len); + writeRC4.process(mac,0,sendRecordBuf,len,macLength); + rawOS.writeShort(len + macLength); + rawOS.write(sendRecordBuf,0, len +macLength); + clientSequenceNumber++; + } else { + rawOS.writeShort(len); + rawOS.write(payload,off,len); + } + totalLen -= len; + off += len; + } + } + + private byte[] readHandshake() throws IOException { + if(handshakeDataLength == 0) { + handshakeDataStart = 0; + handshakeDataLength = readRecord((byte)22); + if(handshakeDataLength == -1) throw new Exn("got eof when expecting a handshake packet"); + } + byte[] buf = readRecordBuf; + int len = ((buf[handshakeDataStart+1]&0xff)<<16)|((buf[handshakeDataStart+2]&0xff)<<8)|((buf[handshakeDataStart+3]&0xff)<<0); + // Handshake messages can theoretically span multiple records, but in practice this does not occur + if(len > handshakeDataLength) { + sendAlert(true,10); // 10 == unexpected message + throw new Exn("handshake message size too large " + len + " vs " + (handshakeDataLength-handshakeDataStart)); + } + byte[] ret = new byte[4+len]; + System.arraycopy(buf,handshakeDataStart,ret,0,ret.length); + handshakeDataLength -= ret.length; + handshakeDataStart += ret.length; + handshakesBuffer.write(ret); + return ret; + } + + private int readRecord(byte reqProto) throws IOException { + int macLength = (negotiated & 2) != 0 ? mac.length : 0; + for(;;) { + byte proto; + int version, len; + + try { + proto = rawIS.readByte(); + } catch(EOFException e) { + // this may or may not be an error. it is up to the application protocol + closed = true; + super.close(); + throw new PrematureCloseExn(); + } + try { + version = rawIS.readShort(); + if(version != 0x0300 && version != 0x0301) throw new Exn("invalid version "); + len = rawIS.readShort(); + if(len <= 0 || len > 16384+((negotiated&2)!=0 ? macLength : 0)) throw new Exn("invalid length " + len); + rawIS.readFully((negotiated&2)!=0 ? readRecordScratch : readRecordBuf,0,len); + } catch(EOFException e) { + // an EOF here is always an error (we don't pass the EOF back on to the app + // because it isn't a "legitimate" eof) + throw new Exn("Hit EOF too early"); + } + + if((negotiated & 2) != 0) { + if(len < macLength) throw new Exn("packet size < macLength"); + // FEATURE: Decode in place + readRC4.process(readRecordScratch,0,readRecordBuf,0,len); + computeMAC(proto,readRecordBuf,0,len-macLength,serverWriteMACDigest,serverSequenceNumber); + for(int i=0;i len - 4) throw new Exn("Multiple sequential handshake messages received after negotiation"); + if(type == 0) { // HellloRequest + if(tls) sendAlert(false,100); // politely refuse, 100 == NoRegnegotiation + } else { + throw new Exn("Unexpected Handshake type: " + type); + } + } + default: throw new Exn("Unexpected protocol: " + proto); + } + } + } + + private static void longToBytes(long l, byte[] buf, int off) { + for(int i=0;i<8;i++) buf[off+i] = (byte)(l>>>(8*(7-i))); + } + private void computeMAC(byte proto, byte[] payload, int off, int len, Digest digest, long sequenceNumber) { + if(tls) { + longToBytes(sequenceNumber,mac,0); + mac[8] = proto; + mac[9] = 0x03; // version + mac[10] = 0x01; + mac[11] = (byte)(len>>>8); + mac[12] = (byte)(len>>>0); + + digest.update(mac,0,13); + digest.update(payload,off,len); + digest.doFinal(mac,0); + } else { + longToBytes(sequenceNumber, mac, 0); + mac[8] = proto; + mac[9] = (byte)(len>>>8); + mac[10] = (byte)(len>>>0); + + digest.update(mac, 0, 11); + digest.update(payload, off, len); + digest.doFinal(mac, 0); + } + } + + private void sendCloseNotify() throws IOException { sendRecord((byte)21, new byte[] { 0x01, 0x00 }); } + private void sendAlert(boolean fatal, int message) throws IOException { + byte[] buf = new byte[] { fatal ? (byte)2 :(byte)1, (byte)message }; + sendRecord((byte)21,buf); + flush(); + } + + // + // Hash functions + // + + // Shared digest objects + private MD5 masterMD5 = new MD5(); + private SHA1 masterSHA1 = new SHA1(); + + private byte[] md5(byte[] in) { return md5( new byte[][] { in }); } + private byte[] md5(byte[][] inputs) { + masterMD5.reset(); + for(int i=0; i 112) throw new IllegalArgumentException("size > 112"); + seed = concat(label,seed); + + int half_length = (secret.length + 1) / 2; + byte[] s1 = new byte[half_length]; + System.arraycopy(secret,0,s1,0,half_length); + byte[] s2 = new byte[half_length]; + System.arraycopy(secret,secret.length - half_length, s2, 0, half_length); + + Digest hmac_md5 = new HMAC(new MD5(),s1); + Digest hmac_sha = new HMAC(new SHA1(),s2); + + byte[] md5out = new byte[112]; + byte[] shaout = new byte[120]; + byte[] digest = new byte[20]; + int n; + + n = 0; + hmac_md5.update(seed,0,seed.length); + hmac_md5.doFinal(digest,0); + + // digest == md5_a_1 + while(n < size) { + hmac_md5.update(digest,0,16); + hmac_md5.update(seed,0,seed.length); + hmac_md5.doFinal(md5out,n); + hmac_md5.update(digest,0,16); + hmac_md5.doFinal(digest,0); + n += 16; + } + + n = 0; + hmac_sha.update(seed,0,seed.length); + hmac_sha.doFinal(digest,0); + + while(n < size) { + hmac_sha.update(digest,0,20); + hmac_sha.update(seed,0,seed.length); + hmac_sha.doFinal(shaout,n); + hmac_sha.update(digest,0,20); + hmac_sha.doFinal(digest,0); + n += 20; + } + + byte[] ret = new byte[size]; + for(int i=0;i len) System.arraycopy(readRecordBuf,len,pending,0,readLen-len); + pendingStart = 0; + pendingLength = readLen - len; + return len; + } else { + len = min(len,pendingLength); + System.arraycopy(pending,pendingStart,buf,off,len); + pendingLength -= len; + pendingStart += len; + return len; + } + } + + private void write(byte[] buf, int off, int len) throws IOException { + if(closed) throw new SocketException("Socket closed"); + sendRecord((byte)23,buf,off,len); + flush(); + } + + private class SSLInputStream extends InputStream { + public int available() throws IOException { + synchronized(SSL.this) { + return negotiated != 0 ? pendingLength : rawIS.available(); + } + } + public int read() throws IOException { + synchronized(SSL.this) { + if(negotiated==0) return rawIS.read(); + if(pendingLength > 0) { + pendingLength--; + return pending[pendingStart++]; + } else { + byte[] buf = new byte[1]; + int n = read(buf); + return n == -1 ? -1 : buf[0]&0xff; + } + } + } + public int read(byte[] buf, int off, int len) throws IOException { + synchronized(SSL.this) { + return negotiated!=0 ? SSL.this.read(buf,off,len) : rawIS.read(buf,off,len); + } + } + public long skip(long n) throws IOException { + synchronized(SSL.this) { + if(negotiated==0) return rawIS.skip(n); + if(pendingLength > 0) { + n = min((int)n,pendingLength); + pendingLength -= n; + pendingStart += n; + return n; + } + return super.skip(n); + } + } + } + + private class SSLOutputStream extends OutputStream { + public void flush() throws IOException { rawOS.flush(); } + public void write(int b) throws IOException { write(new byte[] { (byte)b }); } + public void write(byte[] buf, int off, int len) throws IOException { + synchronized(SSL.this) { + if(negotiated!=0) + SSL.this.write(buf,off,len); + else + rawOS.write(buf,off,len); + } + } + } + + public static class Exn extends IOException { public Exn(String s) { super(s); } } + public static class PrematureCloseExn extends Exn { + public PrematureCloseExn() { super("Connection was closed by the remote WITHOUT a close_noify"); } + } + + public static boolean debugOn = false; + private static void debug(Object o) { if(debugOn) System.err.println("[BriSSL-Debug] " + o.toString()); } + private static void log(Object o) { System.err.println("[BriSSL] " + o.toString()); } + + private static void verifyCerts(X509.Certificate[] certs) throws DER.Exception, Exn { + try { + verifyCerts_(certs); + } catch(RuntimeException e) { + e.printStackTrace(); + throw new Exn("Error while verifying certificates: " + e); + } + } + + private static void verifyCerts_(X509.Certificate[] certs) throws DER.Exception, Exn { + boolean ignoreLast = false; + for(int i=0;i which'll load the certs + Class.forName("org.ibex.net.ssl.RootCerts"); + log("Loaded root keys from org.ibex.net.ssl.RootCerts"); + } catch(ClassNotFoundException e) { + InputStream is = SSL.class.getClassLoader().getResourceAsStream("org.ibex/net/ssl/rootcerts.dat"); + if(is != null) { + try { + addCompactCAKeys(is); + log("Loaded root certs from rootcerts.dat"); + } catch(IOException e2) { + log("Error loading certs from rootcerts.dat: " + e2.getMessage()); + } + } + } + } + + public static int addCompactCAKeys(InputStream is) throws IOException { + synchronized(caKeys) { + try { + Vector seq = (Vector) new DER.InputStream(is).readObject(); + for(Enumeration e = seq.elements(); e.hasMoreElements();) { + Vector seq2 = (Vector) e.nextElement(); + X509.Name subject = new X509.Name(seq2.elementAt(0)); + RSA.PublicKey pks = new RSA.PublicKey(seq2.elementAt(1)); + addCAKey(subject,pks); + } + return seq.size(); + } catch(RuntimeException e) { + e.printStackTrace(); + throw new IOException("error while reading stream: " + e); + } + } + } + + public static synchronized void setVerifyCallback(VerifyCallback cb) { verifyCallback = cb; } + + // State Info + public static class State { + byte[] sessionID; + byte[] masterSecret; + State(byte[] sessionID, byte[] masterSecret) { + this.sessionID = sessionID; + this.masterSecret = masterSecret; + } + } + + public interface VerifyCallback { + public boolean checkCerts(X509.Certificate[] certs, String hostname, Exn exn); + } + + // Helper methods + private static final int min(int a, int b) { return a < b ? a : b; } +} diff --git a/src/org/ibex/net/ssl/GenCompactCAList.java b/src/org/ibex/net/ssl/GenCompactCAList.java new file mode 100644 index 0000000..e15a194 --- /dev/null +++ b/src/org/ibex/net/ssl/GenCompactCAList.java @@ -0,0 +1,98 @@ +package org.ibex.net.ssl; + +import java.io.*; +//import org.bouncycastle.asn1.*; +//import org.bouncycastle.asn1.x509.*; + +public class GenCompactCAList { + /* + public static void main(String[] args) throws Exception { + if(args.length < 2) throw new Exception("Usage: GenCAList format file(s)"); + String format = args[0]; + DER.EncodableVector vec = new DEREncodableVector(); + for(int i=1;i>>(7*(7-j)))&0x7f); + if(c=='\n') sb.append("\\n"); + else if(c=='\r') sb.append("\\r"); + else if(c=='\\') sb.append("\\\\"); + else if(c=='"') sb.append("\\\""); + else if(c >= 32 && c <= 126) sb.append(c); + else sb.append("\\" + toOctal3(c)); + } + } + System.out.println("package org.ibex.net.ssl;"); + System.out.println("public final class RootCerts {"); + System.out.println(" private final static String DATA = \"" + sb.toString() + "\";"); + System.out.print( + " static {\n" + + " try {\n" + + " org.ibex.net.SSL.addCompactCAKeys(new java.io.ByteArrayInputStream(unpack(DATA)));\n" + + " } catch(Exception e) {\n" + + " System.err.println(\"Error loading root CA keys: \" + e.getMessage());\n" + + " }\n" + + " }\n"); + System.out.println(" public static void load() { }"); // force clinit + System.out.print( + " private static byte[] unpack(String s) {\n" + + " int len = s.length();\n" + + " if(len % 8 != 0) throw new IllegalArgumentException(\"not a multiple of 8\");\n" + + " byte[] ret = new byte[(len / 8) * 7];\n" + + " for(int i=0; i=0; j--) {\n" + + " ret[base + j] = (byte)(l & 0xff);\n" + + " l >>>= 8;\n" + + " }\n" + + " }\n" + + " return ret;\n" + + " }"); + System.out.println("}"); + } else { + throw new Error("unknown format"); + } + } + + private final static String toOctal3(int n) { + char[] buf = new char[3]; + for(int i=2;i>=0;i--) { + buf[i] = (char) ('0' + (n & 7)); + n >>= 3; + } + return new String(buf); + } + */ +} diff --git a/src/org/ibex/net/ssl/RootCerts.java b/src/org/ibex/net/ssl/RootCerts.java new file mode 100644 index 0000000..18438a0 --- /dev/null +++ b/src/org/ibex/net/ssl/RootCerts.java @@ -0,0 +1,29 @@ +package org.ibex.net.ssl; +public final class RootCerts { + private final static String DATA = "\030 Oi\031B\004\001M\014\020\030ID\0260\004A@5(\020\014\023\001\025*3\010,`\011\003\000jP &\002\"\020f\021\031@\"\006\001U @8L\024W0\\m\006K9Nt7[F\0219@*\006\001U @PL\034A!\020%d*\r\036M\026\010\011\024r\014\\1\014L\002p0\r*\004\001Db\004\n\011\002.\"Piti\001$o7]\004\004\032\004b$\030\010@`I*\014HC=aP\010$\002\026\nX,Fk%\\@2\032,w\033%Nt9\035.7!9Fo6L\020 \010(\005\002\000@ \013\016L#`4\nHEq?\037L\014\027H{ajbF\036G^\013I0h_F\037\011@A&:@*zgGl\017=5x=q(\030:BLZ\016T[\032}4\"K{\003\031^\004\nh~yVzLF(,r\036\011\002 hJ\031\035:rYad\025\ngPV/\000s*_}Z}VBYv9\026\034G\013\007:')i^nsg-b\003TN\011!32\022*e6F1\033g\004UF;3K_K\026[\010>\005f\011W\014)&\nZ\002S\017\"e,m\037\014ogh\000B-Jt\024\026:2T>%%'o~x o\027W\011#i;H/\037y1P\011c\"gT\0241!\001\001\001\000`\020\000\004a\002\000_f\006yD\0260\004A@5(\020\014\023\001\024hS\010P`\022\003\000jP (&\013 Y\014E#Ijs:\010\010\024\021DL0\022\001@5(\020\026\023\016P,F\"Qdu9]\004\004+ahe9\033L\026a\001(T(\010\011f+Qno9\032f\022\021@@\006\001U @\030L2A2\031\nG\023Uft\020\021/\007#\025dn0[\004\004\032\004@R7[nC\004\010\002\n\001 @\020\010\002ow\r\014|o\020\000\010-\034x\011e_4?<6\003y[/hG6g7S1\011N/$\024S\017Y\034~zJ^$}P\030kG\017gy-U\002k-\027\024e\013(\005A\022zmKR_mc\025?\033N\005Qqz\"AV\\\nl?A\036?t\014Sg&+\021\020*:8ex=q\035M\020\007@u1C*/\005\021yt\036Bj(7\032yR:^\005YiN\031$qc3(\030\001,8N:8\003a@*G&Pi\023\006\022\nZ2`O\021jg$|\0231_g'|\026K>wW!5m4'\023\017-w\033(\025{I4q\004\rv7K\026HYP%\006\1778\000^\ra$N-V@prmE\025\004.\021:*>mvM6\023],fDhK>$W]!@\004_S\006ri5\026\0041\r^N3GUNUOW\032)kQVpQ-K6iPMq-Jp{}{}\025jO*4-\011` \030\004\000\001\030 @\027)AJ1\005L\001\0200\r*\004\003\004`%\032\024b\024\030\004@`\032T\010\n\011Bh\026#\021(r:\\nB\002\005\0041\016L\00300\r*\004\005DbD\013\021HT9\035.7!\001(T(\010\011f+Qno9\032f\022\011@>\006\001U @\030L0A2\031\nG\023Uft\020\020mF\013Mf \030H\0104\011\001$o7]\006\010\020\004\024\002A\000 \020\004Z-T\020R,\016\023/PA\003C\033l'\0038\023\021s8\023.\037wV'\004!&:{j\006-ZtPAm6)\013\016\\x=Hh\006bo\000Z(\ry\002eJ\"\nw\006\021\001^uSm\037xZV`E9n\031\032:\025\027\004\03704<`\004o)vF\035S\1771N+GJ{^lVbm5:\027%R:\036CF7IH~pA$S\026\0136\0320\004#n\023}\031\020W\014~\004]b-L\\R]\021yE;\021y`\001D.=1g_RK_5\026rb\0252H\037#+\031S^oK\036\026upa[\026\011\002-_,j54$\0310\005B.!\036]\004\022\033\024h+]^\013\032\036[Bv\034fTDL:\030tW[TIU\0173\033i\016\013I]\0101L:HpKw/DM\031\\1aWC\005xcG4*h\016.,|3d\\7Zp$\030l,5lI~cx\0119\025\0318??&@@0\010\000\0020A\000.C\003\020b\013\030\002 `\032T\010\006\011@J4)D(0\011\001@5(\020\024\023\005P,F\"Qdu9]\004\004\n\010b\035\030\006``\032T\010\013\011E\010\026#\021(r:\\nB\002Q(P\020\023LW#]^r5L$\003\000x\014\003*A\0001\030]\002d2\025\016'+Mh (\035,&c%F !P$\005\023=^t\030 @\020P\n\004\001\000@\035\021QB\037\003D\005\030\022\006`y\033GFo`\033RwZ4thj/b\035B\006\0042\005\r\024^f*\020\n^Vl>J+;Sr\033;h9eh7\026\177I}y.adf\014x9xq;\024h\017`CE\0223\002\020a\1776&9\036Xp\014\\\016\027|uc\002aAz\016=\007L!v\031\027@~_,'>M\010\036\023}GtZ\022g7\0346'\021{\003*QI\034`; \001ScyE\011nvG8\011g]fD2Bax\010|_cCgn\007\034\177J\031\024\\\026[\004\027fH0\006[\010Kzllj{#\177n\037>(&jtwVw\020\000F\036O\007k\0253y;+QH\010Y\027.8dR?\010lo\011\004\020@R-D=wFB\007vjR\022\r\001@9O@>,\017sgI\010y7 \016\\Q\013k2e\r\024\005k\001UZ.\027'h\020#(uL Y4Q\034X\020cS}\003\003z\002\001@ \000\011B\004\001;L\014s\010,`\011\003\000jP \030&\002)Q&\021!@$\006\001U @PL\026A2\031\nG\023Uft\020\020(#\010t`\033\003\000jP ,&\024 Y\014E#Ijs:\010\nE\"@@N2]\016v{IV1\021L\004\0200\r*\004\001Dc$\013\021HT9\035.7!\001\"u0[\r\0263%Jd\020\020h\022\002I^o:\014\020 \010(\005\002\000@ \016 z5~n\002+(=\022>G_\004+z|\020i[1\017\017\027E_R0Z{\001Aa+\010\026\177x\035G\017.\025u\010[]Tx6#R\011\006\005THT\003BR\010B<8EI\177\021\004S+L98/\0043n\026UMN\001,|+T7N`xR}Ad\023kF\177\rs\032\017\005\\Q}\025:Sk\035Br\004U?>l=Rc8\025(\0375GD3 7\\\004WqyRJ?Cd-G>Z\010\010fE\031O\006TD\"fB%qz:m)?\034z\001uP/x\022n\007`Lq-^\005$_\016[\035+Q0\006\0351>?`\177E\013\037d\022'YC_\011E OW\037hK\"\037@`t:o\011;\037>M\001mR\001B,!oC\021`UF_\000S8e\033\002-j]\020\017G\032t8 \031Ll[:IMT\027D$k![i7\014St4\016{\"6v*\000\177L\013mKK\0271i\001\000`\020\000\004a\002\000GF\010\014$LJ\013J~E%\033(\032lDv?\021\n\037J\rN\177E\011r r%ya\010x\034r\017>]\0322\027\001'3**8$)\031.]-MJ<5\031\036AP\"n\023/Z\000)d*#zD_\004\024\000_\033\025\014\034x/r7-\031s^H\177Rd\037\\\022V\r\037 0\0017DbE\021L\003\002:&\017\\[\03393u\025vcgym\033R\021\007i\0315u\037D\ru$r/zb\034HSg!\017s\002<\022\002\035SU/rTw\000/]\016]\025a^\032=\023\014;\032mZ jI\003Wz$N^\r\010\024P+!wi8-v3BD\027\022@c\\3u\022\002\035:n\"G;\010ZNv=;gT\023CIlZ#]N/.@M!daU,\004\023WDz,\001UA\035\026\0079CZqjC=/L&Q,Hg\013O\024\021D;WN;\023oP\010FN4\002k\020I\026\003v~|A~h@\001\007\000Xl\036;j=4\013\036\n\035a\032-\020\011K6xJ`]6h\010\006\001\000\000&\010\017<`a\030Bf\000H\030\006U\002\001B0\022%\n1\011\014\002\0000\r*\004\005\004a\024\023\005Xt4[-w\023\024b\023\030\004 `\032T\010\013\011BH7K\011Jr*\034NW\033Pb)\030\011``\032T\010\003\011H\010&\0131hi6[n&)\001\006y1\031.%#Ijs:\010\011V{\011Rl2H\n&{=h0@b (\014\004\001#6l'\00553y5+\010;l\004A:&#`Xgl\"\017^Kw4R]O-\n\001jZ\0274\"\030@@0\010\000\0020A\000-#\002hb\013\030\002 `\032T\010\006\011@I\024)D$0\010\001@5(\020\024\023\004PL\026cQRm7\\LS\010L`\021\003\000jP ,&\n!^,&+I(r:\\nC\011\010` \003\000jP \014&\031!\030-G#%Zo9\031$\004\033eDe9\025\016'+Mh )\033mw!B\004\001\005\000P \010\004\001#\002.d*\\`zWt\011N)UUsT\024x\\\036DV\0011X8k8q,S\03227t\025o6`\011\002[;2A;\034bQ\030_k$#4\022uN\0352\014w\000A\025@*'\\!oS\016#x{Uf\02155\002\035KOEPD!\022\002=\026\003\005$W\027L\017uZL\005~-\033P:d\005T,F\023\006\037VJcp$\001Gjvd\006=O^#_3B\005\011zC+qW2\025\023\\*55.Ayu^\032xN\003\027-G6{ZO#$o\011UAF\007\017>J\037\")o\03038\016S\\CoD@D\\#\003m T\004\032\007s\013i+KGC\006W>\000e\013 `\rCE\031>rTJ\031Iq6\n\007pO(=\006\034-MH`.2V2W(yMa|%1m\021O \025cy{77869\025\001\000`\020\000\004a\002\000RF\010\rlb\013\030\002 `\032T\010\006\011@H$)D\"0\007A@5(\020\016\023\004\020N'+Mfe6\034f\021\031@\"\006\001U @PL\024B2[\n6K\035\\ '\025F\023A@l\006\001U @XL^B2[\n6K\035\\ 'XM&+\rh (\035,&c%fh4[Lr\002\rJr:\032,fK\rBt2H\010\027+QPo9\032.GIDJ0\021A@5(\020\006\023\016\020LVbMRg7\010\011v\023)Jc:\010\n\007+\011Xi9Z\r\026s\034@C L$3\001\004\014\011\025!I\0107\\\032\001\004@\"a#]Jb6X.7#\025d@1\031-G\033%Nn\027\030LS\004\006\022\002@` \014!8?6_{P$\006ws7\027\020:Yp\026bZh\011OF\022T\007\033~2l\035r/_~t&?h=f\021OH6ApIqm_\013evlY|JE~m\027E@\002}0--BH\013U\"D\004\003\000@\000\023\004\010\002B\030 63\010,`\011\003\000jP \030&\002!\021&\021\011@\036\006\001U @8L\020B9\035.7\033\025Xs\030Df\001\010\030\006U\002\002B0R\011Jl)Z,vq\001\034V\030M\006\003\020\030\006U\002\002b2Z\011Jl)Z,vq\001&e1].&)\001&e9\035LW\021\001\006e9\035\r\0263%Fa:\031$\004\013Uhh7\\M\027#db!\030\007``\032T\010\003\011F\010&+1&i3[D\005\033\025Fu9\031$\005\033\025dv2\\D\004\032\004b#\030\010 `I*\014HC=aP\010$\002\026\n\035lV\0235Bs:\031.$\003\011Jl9Z,vq9De\030 1\020\024\006\002\000k\000\"'DKp\004!\037y\0349\n\007|>\021n\003\001-\022\013\037\rH\011\001#4!9`j?\026\023\"\026 0\n?}:MF\022,dvm\026f=\034]kZ@\033mfto\0141\n8\014e8\002SY\037fD@E_i( :\032\026XuY@C\020$\014p\n\011qY\003\026{\031\031n\016\006e9R\005\000IBCPp|#FYSu\023A0G+\032vh\014\030J_5\\w2S>hF\001t\004El*Qy?_t$+t+}|\005.ik-\000=\005L\011W\0314!IJV\022\020\\\001\022g\021%_C\003P\000\r/\017qW=\024 \031\017\010\035\007\0309D@@0\010\000\0020@tF\0041D\0260\004A@5(\020\014\023\001\025*3\011\020`\"\003\000jP (&\033\"\032,vKQBl\020\024m\026;9Bt:\\LR\002Qdu9]\004\004\033<\\1\010L\001p0\r*\004\005Da\004\"M(C H\010S\011B\003\007\001 0\020\005\001Y\001TsfAq\023;~C\n\031Mt\014_y/5\010-\027!h`3\004s\0000rRQ\011\032-s\nVrI9\000\001\007vNJb\"\rdIL?3\003$#s\026lT';!NedJ:\0207`nk\007\177{C=|.l\034oM\017@Li^\037\034GPx>\000\0316\016%\010'8Z*TSy\022MvhT;p\011\030\000e\000U\016p\004\00412\003j\"hMhv\033-/+!5b\002\000@f\010\020\006t0@j&\020Y@\022\006\001U @0L\004u9L!S\000,\014\003*A\001\001\030\021*t0Z\006\0219@*\006\001U @8L\034S0[\016B\0021Bk2H\0106KQr1\022\014\004 0\r*\004\005\004c4#%Ni:\030-B\002MRg7\030.G+IJ *\034NW\033P@C7KF\021\011@\036\006\001U @XL\020D)U\0104\011\00101\030EF\001 \030\006U\002\000b0j\021&T\020\024Mv{Q\006A\020\026\006\023\011\004`\037\003\002%(2\"\rw\006@!\020\010X$c0P\014FK\035fi3]\016'+Mh.1[mS\004\010\002\n\001 @\020\010\003%F\023-\\z)w\003D45*\006zO\no$Da\033EZ|teg)?0(1422\177q\033TUa|PF\0363gI\017n[o,K4\np31Iw$DFunB\0034n\002GZ25\\+\010\031N}\026 wp,l\\l Y\024\017#\005\026\030\022Cr\025+ lja\002\017\031Ah\035]\036)JA\"hh\000e[\014\002}#l\024V\004&5\006J8#K\010,WBN\0265ET\013?-\004sr\023\004&)\017[\026\011\024VI\033\036k\\\011l,\037S\022zI\010}Q\033\036\005.\"KH^+yd?Bg2F\020CL\r\026Hi[\025fQca3FiKg'\033\037]\024e\001\0020+\010\014p\021l~Gg9\013Y2\002k4M*HCg\011XD IY8n^ \030\004\000\001\030 :#\002\030b\013\030\002 `\032T\010\006\011@JU\031DH0\021\001@5(\020\024\023\rQ\r\026;%ha6\010\n6K\035\\a:\035.&)\001(r:\\nB\002\r^.\030D&\000x\030\006U\002\002b0B\021&T!P$\004)Ha\001C@P\030\010\002\177\023GEr.yL&\030uD\017tpZ\177\177\003#e(-q=y\022)DHEX\0077`pkp\032o\"oW\037H.S\030G81`joY [a\034\"vNu \003ag\007\026hA(DFB~sz;BRG\n\017yms\027a(8}ir32\031d`^k8mk*3\025*IOi\024\000r-g\026`\005M\003b_K-2r+<_#OrThw\003cq\004\014_|N~By6v\005cs\021/\\\002.\000D\017{%N\004\023{\021.$JFI\027Jb4w\034^p\001%=\031BuW(\n\013'2\024\005\\(d20`\011[?\027\001FM\026\0342A\024&hy\007d\nB^lW\010FBvUVnAnO\033CYJHm\002%\026#\014E9}Qnh,2\014DeB;0\020\014\002\000\000L\020 \np`L\030Bf\000H\030\006U\002\001B0\023\rB1\011\014\002\0000\r*\004\005\004a\024)5\006e9\035\r\0263db\022\030\004\000`\032T\010\013\011B)\024!\001\006e7\035\014W\021D*0\011A@5(\020\006\023\006\021%T\033\025dt4YO\022\002\r\0020A\000! \024\010\002\001\000.S:3F\000U:\022%\025+X7G\033%&\002aH\022Qw\014\024F[\"\016Gl\n\001!;}_(M\010\026jX\007%.P\021(c(\010\005|m8\004\007\020(f\rI\\\027\000$A\023y4\016\0270hE\\9.V\017,1\rMi]Ef\"7#X\0224!e\034egJOf\"\003>lUc!)HEnl\r9~\031\025iE\022Lqa\n[ \005*&\016$ey\002dEb3em\036[z\034\0260ta>56Z'\033\177!37\002mR\001K&\036F_dB~Mi@\035\\|3~O\034~o'G@\034\r$_j3pR6&T*Y\033_\007\"\033nG\006\nx\007M\033\023\001LKg=0L-\0212U\006\020X>:\026\035\023e?wCUT@MQ\023e1b=K=\032#AAB\014\017M\013T\177d)\\kXNc\\@'B2y<\025G\rjh\025h4\rW[*\032\013nB,\004\003\000@\000\023\004\010\002\\\030\023\006\020Y@\022\006\001U @0L\004c0L\"#\000@\014\003*A\001!\030%\n-!Y.'#%Ly\030DF\001\000\030\006U\002\002b0J%\010 !Y-g#\025d1\nL\00200\r*\004\001DaD)5\006e9\035\r\0263d@R L\020 \010(\005\002\000@ \reBn%}{AKVbFr\"\003e*\037\r\020\004h}\006\007 \030\020\016/Mxs5^\005\032\022RuFZ&2\000\rE~/\006|.\027t{$\006PQW}ds\001~01W\006r}4OVl\n*\0002\001^U)J@\0332^>+Ts\016n\031[4 r=CEPg\014]sa\032dv9#8O\024R`K=\0249\003KL\022\036\034\0160m\0318i\025\020&h:r!0;oK \003vRb\013a/8\021\035\004\027wc\nc\014\014 nvV\030\016\007C-?F\021\007?OWxVso!\002A,yO\025\031}pr7W!%\034\034~UYu/\0179^+\034&;M3'&\024\023ra\017\033:UD;3yVJGi7G[*?D\035o0\002\nc\010Q?BDaj\031{\001\000`\020\000\004a\002\000Pf\010\rPb\024\030\004@`\032T\010\n\011BhVsQdu9]\005fs\025h1 \014\007`0\r*\004\005E\006w;]n.2[NG\023Uft\027\033LW!=\016C!P+t\032A& 4[L6{I`.\020\030O\022\003IJf\027\010\005\006c%Zi:\034d\006c%Bb\027\n&\022)@F\006\001U @XL8(1J$\003\021@`0\020\021-g#Ijs:\013Mf+P@L4[-\027#\025H1\031L\006\0200\r*\004\001De$+9hr:\\nBs9Jt\020\020mFK\025\\t\020\020lW\023QRf4Xl\027#%^n\020\020.W#!^r4]\017\023\004\006\022\002@` \011\033Ri6r1)=5\005P\17715=O:E/39\022S\017.B\023IVXyTiU\"jB5\025\\K!ro\\:b\035\\\036\\\\\022\024\"?\nPK\004WJ%9\177\016\001blr{\\2\025\0224D$\013`\014__j)i[_f\007[[GY~)\020(\036X`5\030kvqL5R`?\010\010\031\\Ui(\002 \001~tJ(3PA&g\010\005}0wv\0369C=\003Y.Go\016V\022;\035a7*SPY~\037L-j|F*n\0035\000tj,XV#F\000E\026h\034\020Iol9\004oC_F9@*'\031\036^{b` \010\014a\002\000T\006\010\016\014b\013\030\002 `\032T\010\006\011@JU\031D(0\011\001@5(\020\024\023\005Q-g#Ijs:\013Mf+Pb;\030\016 `\032T\010\013\011LNw;\\\\e7\035\016'+Mh.7\031.Bz\r S\020\032-f\033=dp\027\010\014'I\001de3\013D\002C1Rm4]\0162\0031Ra1\013E\023\011\024`#\003\000jP ,&\034\024\030e\022\001Dr9\034H\010VsQdu9]\005fs\025h &\032-VKQJd\030NF\003@\030\006U\002\000b3\n\025\\t9\035.7!9\\e:\010\n6+\rjr2H\n6+Ile9\010\0106+Ihi3\032,6\013QRo7\010\010\027+QPo9\032.GIB\003\007\001 0\020\0064Q\003\032\025\0038OL\037/\033d&\037}ky\007IO3fF\023\024\030}l\0225@^),\014pCa=L#&+\022x.Wz-Ej5\177_\004\014\005X\010\006\001\000\000&\010\016p`N\030Bf\000H\030\006U\002\001B0\022U&1\010\014\001`0\r*\004\005\004`t+Eji3\030/\003\0114`+\003\000jP ,&$\"\\.VK\031Bx\020\024lV\033Ude\020\020lW\023QRf4Xl\027#\024@A:]\r\006{IRt\0106d\"FD`\024\036\1776@FxN\010b\020pz\027f:>\005p.\000\037\0028ilq\"RP#\022Yq5Tkp-O\026g@/h \024;}]\000B=N\010\000(O~>~\000fu\007:-J|\021|\001a\031\nUr17D\037\0275-x&A\0149L&V&\nbD<;WJzns\r\n{bv+u\006KFP\007NQ\016=|)}p1w\020\020\014\002\000\000L\020\036AA41\005L\001\0200\r*\004\003\004`%*Lb\034\030\006@`\032T\010\n\011DhW\013URf0^\004\005\033\025Fu9\031$\004K9F.\030K&\002X\030\006U\002\000b2\"\025bu4YL\027A\001&e1].&)\001\016l7XL\026a\001JB:\\m\026s\025fs\020\020h\022iDa\001D@P\030\010\002ug\013d\000&-DhU\036\0228%\016W?'hMq}\017\036\002s TL\011\\vB\027\023\036\025\\=GP&J\014V]qN\032:\027v~)Si4sL(kNOn\024s\rW\034|\035~\000lfj>M1/\001w|f5&\035\nbG\001\003zN\"Dt\022\036\033\021%RR\010pmp8Y\006=dEej\013\034\0064g\035\037hrn\\\024f!\016\017Ccs\001\\vN\031p\\.#\006a\001\000`\020\000\004a\001pL\n3\010,`\011\003\000jP \030&\002*Tf\021a@4\006\001U @PL&E8]-\0263\005p )Y,7+IJ $[L2qDL0\022\001@5(\020\006\023\016Q.\027+%La<\010\n6+\rjr2H\014T\023Ufi7\031.7\031\001\006A\026L&\010\014$\005\001@@\031bxfx\027[]{i\035%>Z\006Eis h\031\030z\010[\025NS\010h#<\026C.\026\n\003\0201Ut-\024BQ\"P=12`l27xJ\006\023\023s}(g\025\"&gi\035AJPpQ\007&\022\034\r\rHh\001\\ZP6(.phC\025|TIE*k\n\017\034\023hoS7p.\032uCWg/EpX\0350\014E$',_\"\035sS:V_\025\016\014 \025@${\177#t`\030\010\006\001\000\000&\010\016p`N\030Bf\000H\030\006U\002\001B0\022U&1\013L\002P0\r*\004\005\004ad+Eji3\030/\002\002MJc:\\LS\011\030`$\003\000jP ,&\035\"\\.VK\031Bx\020\024lV\033Ude\020\031('+MRn2\\n2\002\r\002-\031\014\020\030H\n\003\001\0009\007\023LL\021;Q\007&#-\035]VGY\024W-{\034y\020\016iN-8\021FY\022U#\0075Q\"M\034iI:\027\027s~ #k\016R\027/o\0215Ug.g@yq\021oitZi0r\0230oj\n8g5\032Wz;\021Q\007\\l.Rr@4\034|.)\014\027D=\024t\030\035AX%\033\016,\000\026$-\006\002\017\006hAW8\027hHy\004Z2 H\020\027;q\004\035<+\024EL\016E@\016=\001SK%\025dFP{(Z|(@Eh\027\0335L\010n&\031\023h&\022\017i\034C2V`\024]P~_=*09SUr\005\034(NtRY8zI\031)Np*`CF$z\020S[pqCA\036\016,K7\010\020\001PgEz\033\\J3\036*:\010\rvm-\022\037PoVjUb97\016Y\027\025:\035\017ydu\031n \037cU\033HQh\010\032 A\r\011'\011`c\034Xw?@63\031\036Gl9\024\0307}\\\0273\"dRq\031E\002\037yy65\022=5MZOm;\021/lLt\027wB\003DwP\007#\003\nAo\007K\035S>aiM\010\010\006\001\000\000&\010\020\004\0060:L!3\000$\014\003*A\000a\030\011*S\030F\006\0010\030\006U\002\002B0z\035(E\020\020mw\023A^r0]\r\026{8b'\030\011 `\032T\010\013\011GHu\"\024@C\003Hz\1772LT:j\030Ok\026_gd,6\026aPk\r{n,E9 \026!-7NOvqp.C\021B$c|\"\037n$wU}yM\030C.\026~c5\023SH6<\027H\001,h]+-Ay0v\037dK\021D\035@\010Bu .:68fu\"C1$+H_*+U<@>\r\007b:NA{as!\004g.]p\020\014\002\000\000L\020\036AA41\005L\001\0200\r*\004\003\004`$R@b\037\030\007 `\032T\010\n\011EH7K\011Jr*\034NW\033P@J0\\\014\026q0@I7\030ec\011(`(\003\000jP \014&!!^,&+I(r:\\nB\002)\002P SD\005\033\025Fu9\031$\005\033\025dv2\\D\004\032\004a\001D@P\030\010\002X&Qh7\006{+\000,\011>;3\010\026%g~~ZH\rRqU\02552\017WA\006e~\004B\\W3790wF6\031]\037/\000\017\017y\021\\6\0309y3sd#b\032\011Q\037gOK\006&JCa?\004x(\007\020[\n|\022w\007b(l\031XZ\177\032_C\016\037\007L\\slt/j\"uE=*TkQNo;/H[KF\030]Ps\022\035_\020\0228\0344\001\"\013i\027\031\002mI\001\000`\020\000\004a\002\000`\006\007\001D\0260\004A@5(\020\014\023\001\025*3\010``\026\003\000jP (&\017#U\010R\002\r^r8\033n&\013QRo7\014$s\001\024\014\003*A\0011\030y\016T\"H\0107K\011Jr*\034NW\033P@S7[\016W#%^n9K\004\004K9F.\030GF\001`\030\006U\002\000b1*\035(E\020\020o\026\023\025dT9\035.7!\001$o7]\004\003)B\004\001\005\000P \010\004\001<\011\033GxSqu\027\000{\003kYe4\002z\036\010J\022 7\016=R*'UavGYkrI[>w\026}\nm&*rp\034J\034wk|$\024\"@;J\177d0\013;8'rsH\0235#CW\025\016.*_@\031fZS:q\027,r \032\n1\rA{~\\Ei$w\017Na\001\006.{a@]vk0pz\025J\036\036&A)Uy:\035L{\034\177\026G\014z\013v5YZR,\177b\037\014\013|\026T\025\032G(\rPFyl*\010%\002i\016C(x3e\\nwx~\007W\\\026(\035-K\003m\032%7!<1f\006\014<$\033$-Bsr9\025-[~4\016?=sN{\006|9\010gW*>\014e}\003\037s\016yQ\026\036q\003\031[mdpg\001E\034\005@\034m\013U95$-\010\011T\023\033\025u+_\nL\017nG\0261{8\037MK(\037y|#-z\016i^,~\031?D\020\020\014\002\000\000L\020\035\031A\n1\005L\001\0200\r*\004\003\004`%*Lb\030\030\005@`\032T\010\n\011Chu\"\024@C7\\N\006{IBt4[mc\010p`\032\003\000jP \014&\023#U\010R\002\rrb2\\JG\023Uft\020\024Mv{Pa\001D@P\030\010\002qf'n[9Cqb|WQ\026}\030=\rY292<\n\n\035G]\0221S)5ugEo\020X\017f]oOiJ&\n\021r\032:Q<\006\nY\nm!#\007W\026.M$\011-s/.YAP0\024\0268u\032MlZ=\002,k~QNwl|\030)C|rb9\\w=N{ 1JY$I\036\\ZA=\016N\022V%\177a45'M\nmzjg0 \"?\r2c\027L:9juXw:q&|~m;\024\0208\002\027m\034H\r\001R-=\177/O\\v7SB\017$l`\000k>\0367$v9\025b]\rC[>\024l\025Jg\014D\027)?D]=(\000-{B:,o0J8fq^&Tj\014p\037#R(t}pl\005Yfs[95kp~|f;\011x\031\034K\1778\024\036h\032ek\004\003\031K\002fz\023\r\000)I\037M\025\006w\001\000`\020\000\004a\002\000_&\006iD\0260\004A@5(\020\014\023\001\020HS\010d`\027\003\000jP (&\020#[\rv\023\005XS4Ymb\0039l-9X&\021Y@2\006\001U @XL$P9\032-V\013Ir ![\014\027\033L@3\020\020h\023\011\030`$\003\000jP \014&\035#[\rv\023\005XS4Ymb\002Adi6X.'I\001\006l0\\n2\001L@C L\020 \010(\005\002\000@ \011\ny,ek0\030\000$r~Gb\027\0177QD\003y,%S\036\030r\010/n\025QzoU3\001jPlejj'\014:rU\0368G\002Q\010\005\014\014I3({WqNx5-t'W\033r[H/\0256#>oaWP\032\0246tY\024uJ]p*=KD\0020lH::\nHc\025L\020So%;9\002\032\005$k/\007nHH#(8~\013#f6\001\034\177us^L[npR1Xi\024.\0147\034\017\0242\031}Cv \177\"\002)I'\023h={\035,o^@j\007deO$\\O\022\026`\011]\004k?\010o>1^\030\037\005~\016x-H\026+yUH7E\021*!p9\022;afD\"xPWmk\1773p~c0-\021\026;LDDP\n@(jWW=,@3]dm;\0118ZqP\023Jw\030;)@uW\001\000`\020\000\004a\002\000Yf\0059D\0260\004A@5(\020\014\023\001\020HS\010d`\027\003\000jP (&\020#[\rv\023\005XS4Ymb\0039l-9X&\021\001@\034\006\001U @XL\016R7[nB\002\r\0021\rL\003\0200\r*\004\001Db$;1^b0[\n6K\035\\ )\033mw!\001\006A\030 @\020P\n\004\001\000@\033 w\0323\rg(|4|)}{xbp2+/TH\017|%+\005f*\021\002/>\006\036GDg33cMn<6H\025{QXL:5/\024`\014Z_'\016-\0112u1b1`\007PO!#\0064+\r'~\033]$1 \025\016;j\016v\020]w~:*/\024E{`6u&S\0049=D\026G?Ur)'oy\034C\036\002\001@ \000\011B\004\001\\L\020\032AD\0260\004A@5(\020\014\023\001\020h\023\010,`\011\003\000jP &\002'SF\021\001@\034\006\001U @8L\016T7\\MvsQ^1\014\014\002`0\r*\004\005\004atk\005Rl\"[LvK9J $[L2qDR0\023A@5(\020\026\023\020\020lW\023QRf4Xl\027#%^n\020\020.W#!^r4]\017\022\002\021Rv4\\m\026{8b\023\030\004 `\032T\010\003\011BMV\013%Xe7\031m\026s\024b \030\007@`I*\014HC=aP\010$\002\026\010Xl\024\0035Bi6\031-f;%\\e\027\030mviB\004\001\005\000P \010\004\001)S\rn\034b]e\022&\\>-+!^:1-\013\013pMc!v)W\037w\005Oakz5\1775Z\023=}a\r(\r\0246Q,dP\034%\031)nL'H&\014\031O\033v('h&`h\006aB[\r,YcBE\000k%\013q{\rY~\025X\027\177Lc 2b#nseLTV?blJ\014*9fhPtX\020\002\006E;i\024\011=U*K4V6h5k\036EW\036/0\016\021\023G\036aQ Vo\014AE$'~%*\\M28S]g[(\013>\020;eMS\021+\027\010{\001z\0342\016H]\030sN*R\013S\027<]x8@\027\026\030$#Z12]T\002`\031.FB1^k/a8\031pmC\032\005S\032z\031:>\001V\025v6Ua(G_\016q\0076O1o\020\020\014\002\000\000L\020 \n,a\001^\014!3\000$\014\003*A\000a\030\011\010E\030D\006\000p\030\006U\002\002\0020:!Bm1\035.&9D 0\007\001@5(\020\016\023\003R\014\026k\011jr3L'#\001`\014\003*A\001!\031E(C\020\025\016'+MhC2[NF+H@f7\\D\005\033\025Fu9\032.GI\001Rn\020\021\014\027#\004@N2]\016v{IVs\020\021mV\022 b\"\030\010\000`\032T\010\013\011F*D\031\001(r:\\nD\033\025\\t2\\D\004\0331Bs9H\006\002\002\r\0021\024L\004p0$U\006$!^ph\004\022\001\013\006L6+Ihi3\032,6\013QJ@:\034NW\033QFe7\035\014W\0219He\030 1\020\024\006\002\000o_-\000%\013U\n!z#w],($@x\n\017V\"\027!>\035\000R\177&\000\021tRRf\\C$I\00698\025VW$Q\0266w)IOi#O%\003]n\002\001@ \000\011B\004\001%L\020\033aD\0260\004A@5(\020\014\023\001\021\010S\010@`\016\003\000jP &\007$\030-V\023Udg\030D\006\000p\030\006U\002\001b0:!Bm1\035.&9Dt0\034\001@5(\020\024\023\030U\0102\002Qdu9]\0106+9he9\010\014f{H@S2XnW\023%hy\020\032-b\002\021Bt0H\011f+Qno9\032n2\002\035Zb$\014$#\001\000\014\003*A\0011\030e(C\020\025\016'+MhC2[NF+H@C6\030.7\031\000h !P&\022I@N\006\004JPdD\033n\r\000B \0210iFe9\035\r\0263%Fa:\031(\007#Ijs:\030lVsQJr\027\031\014S\004\006\022\002@` \013y=GV\033\036v nM+u2\033\034b*w\r4d\005\027\")b\007k+\030TU-%K#z\010s\034\022.sPfU\014z5\016A#TLe77\022{-;l]U`IiCbmAu\024ie\023!d\014&0T\036ERw<\rma^@7\025x\027KJF`U.NahJ\022S-?\000+w\004\022\023bRE}av\026\031Vuq2\"\027vLz3+C(tRL(|~&bT\004\003\000@\000\023\004\010\002Z\030 93\010,`\011\003\000jP \030&\002-\020&\021)@&\006\001U @@L\030W2\\nF+I\\ !X.\006)D$0\010\001@5(\020\016\023\004Pl\027\003\024@T7]mc\010h`\030\003\000jP (&\021*\032\014\027;QJ ![mg\033UXt4[Ls\011 `&\003\000jP ,&\037!Y.'#%Li1X.FK=\\ )Y.'3%Fe9H\010FKYRs4[mc\011\004`\037\003\000jP \014&\030*\032\014\027;QJ (\031.'\033=\\a6\010\010&\013MRc\020\020h\023\011 `&\003\002%(2\"\rw\006@!\020\010X2p2\\N6{9Bl\026XL\027\033%F@:\032\014\027;QJ.1[mS\004\006\022\002@` \013er&S6p\n\004|\010+f$%\006Z2j|o!>At?9nu971TLn,WP\036\032LQ}#34Br\0270\\D\007KZXE\014M\027`F{fy-\004\030Mm\r\022\001h^\023\027p\r'\005:+\001t\004\034\035\023b\021vP?\0303)\007\014GSD\004(b?T\026\007\007.{]\013|@\nD.*`Y89\000m<<9q'^\024^BHOXm6\030hod9\031\004\004\003\000@\000\023\004\010\002`\030 :\023\010,`\011\003\000jP \030&\002-\020&\021)@&\006\001U @@L\030W2\\nF+I\\ !X.\006)D$0\010\001@5(\020\016\023\004Pl\027\003\024@T7]mc\010h`\030\003\000jP (&\021*\032\014\027;QJ ![mg\033UXt4[Ls\011 `&\003\000jP ,&\037!Y.'#%Li1X.FK=\\ )Y.'3%Fe9H\010FKYRs4[mc\011\020`\"\003\000jP \014&\033*\032\014\027;QJ (\031.'\033=\\a6\010\010g\023\025Jm0Z-B\002\r\0021\025L\005\0200$U\006$!^ph\004\022\001\013\007\016\006+Ifo7\030-Bk\031de2[,\026K1\000t4\030.w#\024\\c7[&\010\014$\005\001@@\032FN_)0J\031\0137\017%\017X\006\0246nSJ#0B\027ORh6\036{\0117pR+TPt\035\020EF\031\036?\025=R}wpf\016\035C:T=mg\035\0262\006X!#9\011,>9Q~B4\036q\017P\007\n0\002G\007<=_\031$\031=-u0\013g7/2\024QW-gSp\031%%U/p0dR\177O&Dm+*v8\022\002\r)F\036cZQf\022\037kg7e-1\003KH\010\006\001\000\000&\010\020\005<0@sf\020Y@\022\006\001U @0L\004Z L\"S\000L\014\003*A\001\001\0301.e9]\014W\0238@C0\\\014S\010H`\020\003\000jP \034&\011!X.\006)\001(o;[F\021Q@0\006\001U @PL\"T4\030.w#\024@C7[N7+1hi7\031f\022A@L\006\001U @XL>C2\\NFK\031Rc0]\r\026{8@S2\\NfK\rJs\020\021\r\0273%fi7[F\022\031@B\006\001U @\030L4T4\030.w#\024@P2\\N6{9Bl\020\024\016&+5Ru6H\0104\011DT0\024\001A\022T\031\021\006{C \020H\004,\0338\031.'\033=\\a6\013.\007\023\025Zi:[(\007#!Bw:\031%f\033=Z0@b (\014\004\001I36?\000:\023\0379F\013^\n\017<&E6\001;}q\034,Q\033\020\"\026c0wm\177\004\017Q\024{K\033!&_f\001G\006f[\\g$E9\035P\031\rrHET\0336l|~$&g+\010\0117.doH5|7HcgWG1\030\"S@q\007<,Bh\020nVj+=bU2&\037x|\000p>\031Yq\001\024\021:};@\016J\030\006f\023D\"S])7rL7&d\025K5\010:@@0\010\000\0020A\000+S\004\007\0341\005L\001\0200\r*\004\003\004`%R\004b\025\030\004``\032T\010\010\011C\nv+Mhe9\033D\004\033\005`e\030DF\001\000\030\006U\002\001b0J\rBp2H\nF{]\\1\016L\00300\r*\004\005\004bE#!Bw:\031$\004\033=\\s:[\016FK9N 1Xf\022A@L\006\001U @XL>C2\\NFK\031Rc0]\r\026{8@S2\\NfK\rJs\020\021\r\0273%fi7[F\022\011@>\006\001U @\030L0T4\030.w#\024@P9\031-VKUZ )Y.'3\025d !P&\022A@L\006\004JPdD\033n\r\000B \0210e`r2[-\027+4Zs2\\Nf+I\000t4\030.w#\024\\c7[&\010\014$\005\001@@\032#1YU\013kpK9vj\002A1#g\016r$\010Uk4\035q`n*\026#{c\004ATtRz\nm 4Bt\rI\001BG\036R'KKJ@\011bq\034\014^>BU\027sdD76B\024S8\021\037\027K6=;6u\003\r\007*\036E*a6PB/rQt#T;(-T@tJ!C`{W\023i\004\030>P34`n\nF .JK\000bmS3\031w\011T\014\"\016\031_u{Fd\004\\\030_uEV)[\013i\034Ao('\002\011\"\0119?O\0206p\005\004\024g.L!wX?)\005ZTOW85G\007\001dlVv\013Frh{oMG,\025UL\"\0115Pph\010\006\001\000\000&\010\020\00440@bf\020Y@\022\006\001U @0L\004Z L\"S\000L\014\003*A\001\001\0301.e9]\014W\0238@C0\\\014S\010P`\022\003\000jP \034&\013\"\035.&\023\005\\v4[\rF)D\0360\006A@5(\020\024\023\003\025\r\006\013]he\030G&\001X\030\006U\002\002b1\"QPa;]\014R\002\rJr:\032,fK\rBt4[mc\010|`\035\003\000jP \014&\026*\032\014\027;QJ *\032-V+Mha6\\\r\026s\034@C L\020\030H\n\003\001\0005E5CaBEC\024}##m#\034vl\034bp`\035~p\027u\002>NiIp\037\013\024pX\034s*\030\030\027\177Z>.tNPR Ty[#A\014<\034{\011\024\r\026[tckgY!G'A\003ir%md\037o\002GN0k\020 \017l|Q_\0078Qy\013,8d@\013$A\003\030}W}_U\\]\n|\"\006p\020\014\002\000\000L\020 C\034`W\030Cf\000h\030\006U\002\002B02QPa;]\014S\011\004`\037\003\000jP ,&\030*\032\014\027;QJ *[M\0273\025ds0[\004\004\032\004@R7[nC\011\004`\037\003\000jP \014&\030*\032\014\027;QJ *[M\0273\025ds0[\004\004\032\004@R7[nC\004\010\020\n\001 A\000\010\003E\011\002[87}6M\011\035?w<>6:7Xx:\"\010z6a\025\005S/\035K%5'\032\013\000]0[K{\ny#h@.\027WSro6d!gM{#om)p**\030q\032\034-4[,!Vb\037_(_kSm\020TY\0074S\025K\035'-T\036CTztp}p\025E&qYNI\026>K<]wm\017{EjV V&^YM4f(|Z*\030+\003\014g\034kA\013<[\014Jc\022\033\017w\037eVUR\\d\026%\003H\025(\034N\011om|hnD\020)P\010yt\032\026/uN,\032?dG\177\035sH\003NdU>vH\014L\0177n\00602em|JJ\025=\005TNxUh!\014J\011\031DVVPymDEv)\020\0031=:\017|C=S\037\024'U(hB\030n\007\002g<\004+gUQ\016\027\022C8;i\013\"zX\0344KEh\026U7\013_ WaHQ=g\006HXu\010o~\036M|&\010{Db\006YM\004xz}tfA\023q\"hNVBgH*it^\034f\033\003ri_\024Wt\001*%&T0YDan\016\002a4R\036\037T\016qs4TTr\025!)\011bCEZYT'2k\\\017_m\014~\011!\\\017pMT\030\026F\177T;x8F9J&xd9jE\037xFys\032\031B4Cl\0201W%&|D[$9LJ\022\017Jv\025ojD@B \r^PgDk}j\007?>)\031\n\0042{R\177o\020FI\034&$\0141\010QA\014L^\014\005\031\"t\"\016bQT|b\r^q\005=fP[XTLr|.>vSP\036I\037dWjH\026o\031\023owpH]\027\031&+\"\021\003s>t\030}Qr2~U\010W\016\n|BT[S}v\0035\014f|%\rNW,\003t7mg4I\033N?gE>b2\010\016aRH\020\004\024?G>$Q\014\014Q1\010c\000?\ra*lr_\023rA\031\030mx\010\022.%\004%{\011\035Z7)<\036\0013\007bgFwpw\025>\017Zg~j\011#;)\024Bzq\"g(CuK\022HM/\003\034j:\021\011]\000\027'\007\n{&m\005\022\026f*\\\022>r\034\rA?\013TQCeOs\024!>\027\007\\\035\003R\037JIril2eY#;F7wH\024\000z%$G#'ba(0@]m%s\013lpFbC\"A\013!OF\006^cj\024\016vk-\014J a:Y\002}r-b;\032q\014\035\001\npZ\034&'a*2}[r2fioY\020p\"~)XXN?$\017zdPV23oG\005\003Zq. n:r\016\024\022L02\0245\016\020i\033+M{|^$fWEz\17774|2MbZCrR\026\\+vKqZ=\024\001K\002>DV\0219hH\rrE(ek76?=W0\023rSCmV\017\n\r\177a~\r\004\nQ\016;Pi'%L\\0\\\007V\003Z\021E$\n\\\034\021\025'\177\017P\023\027\0240\010o:8\026%\016}`}8$::\014\025g\003_ f\006sco\0040y7u\003-]KE\030\033q]\004i=0`A]4Li#\030V]8d&\021\"92'<\026~}c\035}Mf&nViX\031*`KRwD&5}tPv\025\"~\000\004(\020EL'Fl\020Ty?\036C'\017\r\017\016\020j|t@4Kn\013G2DF\022?=6FQl8Yn${\036C\0357\022ST#\023p\"L|$\016\024Y\021\027eDWQo\035bg-\000|\020\010n\002\\Wrs\013bZ\034\010qS&[N\023&?]`\035=\026AD\006=D\017BO\030 \0229}5{1%\"L\022f\037\033J\\\032SI\005DvZc(|H8]_4>%_JW*Tt^[E\021\002+\025d-a\022#\025\030l\rq\\G4\0000Zm@Dsz9ZB;\030\017SoJ\035D)\n45B)d.\006t\022)0\036H)\035rpK3X[\014\014p0\027\026\004~fa$\023\000Gz''s|Aw0\177%+[\035\022Q@2*D986IbVo\034\\/x'\005\003Cps\005(I\001$.ACmCI}\026-\013+\005_\001\022X\0009}\023;\030\030~\005\\\005\020Q}.<\\\036'L)h9Z;@\002?@\027*E\027:2X\177O\r-aarTZ4k\001\020p\011\002%\036K\005\013m/]P\022\rC`o\022Du+cL\021.(\001\025\003F\r\nn\026+#y8=\010\020\007\033\007\016%M1\037)f8\026[\027.3)\020\014gQ\004\025\030e\004rGeByA\023M*L%n\001\"\006\016\027,\n\00068K!}Tq1\006I\007UL(w\007HVWo?q\016\177\013#^[Ut6\007,$J\\w\nGa]/\002L\011NN\005M\017}js|D\003h\1778\011\010r .\031#Y5'\035n\017\nhbevh\r\013\021W\rmX\000cFYfz\002%1QKa}J\023e#8\013Snp:\\2h\016ghX\037Uf2n\017.\022Sg\027N3\r8|D\017>gB6qR\0014\r\031'&N?G~Bi!qL*e/1tD_L+\006\0168]K7<4A|m\006a\037mQ?k\010-\r[`%\024vv;v\032\014.\026o79 \0045\"\036@M\031!\007H5L\002uiJp)\032nNmQl(J;\006\013\n'b$E\026-q`DgW \030\004\000\001\030 @\033yB\003.\030Bf\000H\030\006U\002\001B0\022\r\0021\005L\001\0200\r*\004\004\004`$z8b\020\030\003@`\032T\010\007\011AjF{I^n:\033f\021Q@0\006\001U @PL\"T9\030,F+I\nn3Z-f)\001\022n1KF\022I@N\006\001U @XL@C2\\NFK\031Rc0]\r\026{8@A:]\r\006{IRt:>GL?D\024\007\026-q5a\nHZe)\177-m\177F\037\023/\011;V&#e\177%*8]U\0078/=\017}xWP3.Q)I;%+dN2$sj\004pM\020\n\010b\020LY`\021+M\002yd\037~1et\007#=2&!v\007Qpv\004\016w\"z]O)*\024W8\035i|t\027\004\024\036\020P$X_\006\013|f(\036<:W})Q>%I[I\033\021\026=;/\006amoxDjPw\020&Q\r\037MGXLA*\021\0047n2p\022\003\026qx\007(\007\000\006\0273G\ns\034|e\007qR\011U|$*u|[^\034\037j%&ya\007;\0002k\032\033\0024Vd\010I)<\004\003\000@\000\023\004\007x07\014!3\000$\014\003*A\000a\030\011*S\030I&\002\030\030\006U\002\002B1bU\\i:\031,B\002Mha:\031.2\002A^s:\030-B\002MJr;\032,6)D20\013A@5(\020\026\023\010\035nw99js8\034ef\033=Z/!T\n3\010t`\033\003\000jP \014&\024*Tj\005\031\001 r7Y\016V\033QRo7\010\0104\011\000b0@b (\014\004\001Fk.={\022LqJi\035aFaXP0a4\005\000\023abN\006b(,:dO3H\002\004}$]4\036[\036\010D\003-iFFjj\030_\"Mm(iZg(_Xz*\027Jh\037\037`mb\022>A=t\014\013\0111J^\177&Qa\024\013t\004\034=kt\177v;GozUT<7*\"j3MTk\037+%M%KT$DeR\"48P7m8\030*Pw~8y\0325b\013s\000p$# Rlr63|gu+p6?Z\027lG\031SG4\011/pv'c4\022\177Md|\rM\007;w^mbs'n\002\001@ \000\011B\004\001%\014\020\033YDH0\021\001@5(\020\016\023\rUL\026c%\006e9\035\004\0053\005Xi2\030.FK=\\ '\031.G;=dk\030Ef\001(\030\006U\002\002B0rYBl4PlW\023PX $[L2qDj0\031A@5(\020\026\023\026\025L\026c%\006e9\035\004\004\0331Bs9H\0062\002A^l4Xo\022\002YBl4Y\014\027#%^n\020\020.W#!^r4]\017\023\011\004`\037\003\000jP \014&\0304\035\016G\001h^/;]nrsYBl4XlW\023P\\c7[%s\011\000`\036\003\002%(2\"\rw\006@!\020\010X\"i7\031Mt\003YBl4XlW\023P\\c7[&\010\014$\005\001@@\0349BF,\034t56\0204\005TWa\\NY\035/\037\036S?\03612K,-&gfT% \\\004&Ai\030Q`Su;M|{=-;_\033\034\"IL?9BI<\026KR\025J\007\026Nle\011$Nha.VQ(\037jB 44c,?l\006M<x\031\004G5\027\033+\r\033vmCm\"a7)Ox>#E7rM\033D\014#T\011 /N9\021uWg:@@0\010\000\0020A\000;3\004\007\0241\005L\001\0200\r*\004\003\004`%*Lb\027\030\005 `\032T\010\n\011CJf+IRS4Ymba\001\022n1KF\021y@:\006\001U @XL,V2\\M\025\033%Nn\020\025\016'+Mh '\031.G;=dk\030NF\003@\030\006U\002\002b3\011!F)\020\014'\023Id@V2\\M\025\033%Nn\026\010\011\026s\014\\ \026H\010f{H@a:]\r\006{IRz2Y\004\007+MJ 7[MGIE\n0!A@5(\020\006\023\036\025LW\023%&i3[D\004\0331Bs9H\006\022\002Ajb6\032,2\002Adi6X.'I\001\006e9\035\r\0263%Fa:\032-vq\001\002u:\032\rw\023%hy\020\013$\0049La\002\000B@(\020\004\002\000na\032KMSs'l<`GDs<=n\033\00216eu]\022\024,l\006\034LY\006k\000`F\n)Q\031G~\010\031T>n\rr\033jS8*N*\011}|#45l\005xao\002\022JWcP\013\001u\r\016\0378{\014\01736)H\014\016=_r\0100Voy\011DHApN\000]FV\030Hm]3/\000\020!ZFU454#0&Y+\020IJ@54\177J\n8\025LU4\014\035Jx_\011oG@`\177pOw)-\031Lm?PyI\007U:\177\017JM>I2xmO\rl}\000&\000\r.\004z\017\022|gK2T\014-N9]d]3nYB4_Qs;\032s`2?\021z+MY&{-*r\0035$1Gvt)],9\\$\\QJ#`\026g6\037\035m\032pzY\017\001\nA\011*|\014OQbV\021\014\016wm*Shc\rS\"Tm\010T<\033{\025W\037K\035Z\023h\010\006\001\000\000&\010\020\004l0@if\0219@*\006\001U @PL\034V2\\M\025\033%Nn\026\010\011\026s\014\\1\017L\003P0\r*\004\005Dbe3\025di)Z,vq\001(r:\\nB\0029Jt;[n&YDv0\034A@5(\020\026\023\031\025\014W\0235f 7YD\007+MJ 0]\004\006CQhp9NEr{]nw\027\035LW\023%fi3[Ef\033=Z/)\024\010\022\001!F)\030\014\006\022q@X\006\001U @\030LJC6\030.7\031\000b (\035,&c%F (\034M\026k\005dy\020\023h5\032@@R2\\n\006{9He9\014\020\030H\n\003\001\000.=Ushtw/sKsRKyM2=n\033+=U\026c\0262%,Q:/-KX\021\017(M\neip\023gY4Do\036B2\177u4Ql^fr2+\000'\037A\0006R[\003\003\0268-u]%(P\022\022Xbsi\031!-MjB!\004b\037P^\003\0075\036bpm\010Z\006Wx\016b 1^\1774mz\014\003.\011<9#Hf\021a\n\036z\032o\023l &<_\024m,c:\000P\020\014\002\000\000L\020\036iA>1\005L\001\0200\r*\004\003\004`%*Lb\027\030\005 `\032T\010\n\011CJf+IRS4Ymba\001\022n1KF\0239@j\006\001U @XL\\C6\030.7\031\000d (\035,&c%F (\034M\026k\005dy\020\020lW\023QRf4Xl\027#%^n\020\020.W#!^r4]\017\023\004\006\022\002@` \0132j\027#\006ZD8\034\001WO\034a~B\010Lf\006&\0114.jT%\027bip\013Oj\007A\020(\037\"xT\032|PX\r7sdYz\003\003g*wX6oXt_ry!$_A_)<#jox3\007p\005'roBG\026)!\"]\rQM$wd\014D\007;\n`\0225g/OL\004\033\007suM\025|\177RmB\036U!\002\023\021\003\030q\017\006qu\006$D8\0222'\003}\\W6vj\034I(i\034n4\003M435\026MjGk\016C3J!\002MAP1:\02420.mi+\014c|Z\031`B5\006oy\0113E|D,\nB|\010}y\006^b*\011B\017b\022W\003t$\004\003\000@\000\023\004\010\0026\030 4s\010\\`\025\003\000jP (&\016+\031.&JMRg7\013\004\004K9F.\030Gf\001h\030\006U\002\002b12YJr4Tm\026;8@T9\035.7!\001\034e:\035mw\023,b;\030\016 `\032T\010\013\011LJF+IZs\020\033lb\003Ufe\020\030.B\003!ht8\034g\"y=nw;KNf+IRs4Ymbs\r^m\027TJ\004\011\000Pc\024L\006\003\0118`,\003\000jP \014&%![\014\027\033L@2\020\024\016V\0231Rc\020\024\016&K5Bri\037x-++\nw?9V\006r\035:\0036(b\021;\011~'\177\030\030\010\006\001\000\000&\010\0174`_\030Bf\000H\030\006U\002\001B0\022U&1\013L\002P0\r*\004\005\004ae3\025di)Z,vq0@I7\030ec\011\\`5\003\000jP ,&.![\014\027\033L@3\020\024\016V\0231Rc\020\024\016&K5Br\010\014\005t&=Nd#Ow\000K\001y\027n*T\021\000ZW0\006\037<\037si\035f4\000U\010o]52\013\"1Q8w\021\037\031t&1NW\016Hso'P^\007.|\025\020c#D\006|`\037xV\010kw\001$m$da\\{\014hAK)i0^9v|}M?DD)O8v\n{!z\023EHL2KP\026B\026N\002\001@ \000\011B\004\001(\014\020\034\011D\0260\004A@5(\020\014\023\001\025*3\010\\`\025\003\000jP (&\016+\031.&JMRg7\013\004\004K9F.\030O\006\003P\030\006U\002\002b3\032\rXa9\\d\003\031\001 u1\033\r\026\031\001 r4[,\027\023d@C2\\NFK\031Rc0]\r\026{8@A:]\r\006{IRta]uJJ'E\007 iW<\007u\013S5\007\021JchU:y}E}rT@'/U\027\033U\006\006}l\002G\031\014y]b^6d4X?\011Xt9\011'W\014p\020\014\002\000\000L\020 \016la\001e\014!3\000$\014\003*A\000a\030\011*S\030Ef\001(\030\006U\002\002B0rYJr4Tm\026;8X $[L2qD>0\016A@5(\020\026\023\013\025LW\023%&i3[D\005#Ijs:\010\011f+Qno9\032f\023Q@p\006\001U @XLb(1J$\003\011dr9\020\025LW\023%&i3[EB\002%\\c\027\010\005R\002\031^r\020\030.W#!^r4^LV!\001js2H\rvs1r1\"L\01000\r*\004\001DgE3\025di)Z,vq\001\006l0\\n2\001L@P:XMFK\014@P9\032-V\013Ir !Y.'#%Li1X.FK=\\ ].FC=di:\036$\002i\001\0163\030 @\020P\n\004\001\000@\031;Tq%|<\007c!s<679o?\014K.(\022\030\023~\0032\037kPHB^Q>\"Ja`EH\004on\024aH9.b\006\004\"\006>jeFu\033j\021 c_\"m4Ta,i\014r23!h^\024K7\001\000\010Xh^Y\032#&y\003\013Y\025P,.yP,3QXgWWp!\022!9l\\Ng\031\033\\\032tL*\0348/\004\004i<1^I\"\035\006d\025.\021\r:/n\031\030\"TQ\0212e,\017\0240Dz\033ME\017Z*@\017owK\025.>\nH\014`A>+\023_jHr}!\r\027x\004\\/v\010\024+(yNn5]\r7q\0034le#\006L,U1\0367!&\\\016:S_\003\010,\013Sr+pgC=B;3U\177;+ez iT#\030S\023e=\030z9s\010oUBY3`w\031&$:\006kahI&[cK\034x\032$v\025W\"M\013J^.\002\001@ \000\011B\004\001\033\014\020\0329D.0\nA@5(\020\024\023\007\025LW\023%&i3[EB\002%\\c\027\014#s\000t\014\003*A\0011\030Y,e9\032*6K\035\\ *\034NW\033P@N2]\016v{IV1\035L\007\0200\r*\004\005Df%#\025dm9H\rv1\001js2H\014\027!\001Pt:\034\0163Q<^w;]eg3\025di9Z,vq9Fo6Kj%\002\004@(1J&\003\001D\\0\026\001@5(\020\006\023\022PmF\013Mf \031H\n\007+\011Xi1H\n\007\023%Za9\036$\004z\r&P\020\024LW\033A^n2\031.#\004\006\022\002@` \017\017\020\020\016Ann^\032#K8mi^\013M/R<11kc*o \002*\004;\014NE-9;\022w\032[kMM]=A@&Y}VX\002LZb8scx}eZ\031\024soX\"y{vl\030]7\034\036@>5aw\0350\007c([ZxF\020y|\021\030\\Gk!OYgF}V3C\004\"-\023\024es\007o'A ^?\002v`B\r\177BE3?GK<\013ZZ\\\005B[JT\004\003\000@\000\023\004\010\002P\030 8\023\010,`\011\003\000jP \030&\002*Tf\0219@*\006\001U @PL\034V2\\M\025\033%Nn\026\010\011\026s\014\\1\036\014\007 0\r*\004\005Df4\0331Bs9H\006B\002Ajb6\032,2\002Adi6X.'I\001\006e9\035\r\0263%Fa:\032-vq\001\002u:\032\rw\023%hy\020\013$\0049Hb:\030\016\000`\032T\010\013\011L%\006\031$@1\034N'\002\002YJr4Tm\026;8X $[L2q\000Z #\033n\"\003\005jt4\033n&KiJd\020\035.6)\001^n6\036&\021y@:\006\001U @XL,V2\\M\025\033%Nn\020\025\016'+Mh '\031.G;=dk\030 1\020\024\006\002\000]<\034L\177g\011.BU\027\020:_s\017b_m\001\017c\010\027X\021\033N\031AgU\025\030E+FC\030\034vJ\0137uuU\\Yd$LR\005D<~\000@\033xp,1J\0012w3:E-^X\"vb8a\006x\024&rRC\017(1 ;EZ*UAP-\rnY?JWbl%4\003C6Z&h1a}t\006\037l\000zHz22q^8P,bcQ\030\035!Z81cf\000\024G` \030\004\000\001\030 @\035YB\003J\030Bf\000H\030\006U\002\001B0\022U&1\013L\002P0\r*\004\005\004ae3\025di)Z,vq0@I7\030ec\010|`\035\003\000jP ,&\026+\031.&JMRg7\010\nG\023Uft\020\023LW#]^r5L'#\001`\014\003*A\0011\031DPc\024H\006\023Idr +\031.&JMRg7\013\004\004K9F.\020\013$\0043=d 0].FC=di=\031,B\003Ufe\020\033mfcdbE\030\020``\032T\010\003\011O\nf+IRS4Ymb\002\rXa9\\d\003!\001 u1\033\r\026\031\001 r4[,\027\023d@C2\\NFK\031Rc0]\r\026{8@A:]\r\006{IRtNd\013\rR\036e#^.\001\037\004dv44! \014jkQ5O\003-Uo'\027\177\001\014\010<81M\014e\006P}\0239~F\014\033\032[\177IK2~x\014\004\003\000@\000\023\004\007R0/L!3\000$\014\003*A\000a\030\011*S\030H\006\001p\030\006U\002\002B1:I&A\020\021\014\027#\004@S2XnW\023%hy\026\010\011\026s\014\\1\027\014\005@0\r*\004\005DdU\033\025Fu9\031$\005\033\025dv2\\D\004\033\025dt4YM\026\033\005hi7[D\004\013Uhh7\\M\027#da\001B@O`\004K\034z`kP3rjU\011AUuB(\005l\014VkQba_\035k\032^\014E \017K\004 \0249\033|<\021bE A\r\020Z\0147*z6\022\010\nZv4 3 -\011\027I%I,b\022YY\010\003c~x\ne[\004\003Y.\001\033El>M]iN\032B\022VL\022\027\021m\034R\032\011KI!\nf8\036\nu)\025!1f:4`V\0319f8m8\031i\016.t[Vd\007O0\020\014\002\000\000L\020 \0114a\001O\014\"s\000T\014\003*A\001!\0309,e9\032*6K\035\\,\020\022-f\0318b\037\030\007 `\032T\010\013\011EJf+IRS4Ymb\002Qdu9]\004\004s\025hw7\\M3\011l`9\003\000jP ,&2*\031.&kL@o3\010\016W\033\024@a:\010\r\007#Q`s\035\013ew;]n.;\031.&KMRg7\013L6{4^R(\020$\002C\014R0\030\014$S\001\014\014\003*A\0001\030q&e1].&)\001&e9\035LW\021\001\036C)T\004\005\023\025fp7[LF+Ha\001D@P\030\010\002pQLY\020Pw:f\n4<\027v\033X:Sz\177\024\027D0gnO}\027cNj6\017&Qu\030y\003PK\"/9lkn!Q\030:\022\014!\025w\011\0247W_4L;\177\\\177r+-&\033dW3\034,\030\036\006jQ\\B\000\011\035D\"S\r$cI6=O0\003\037D=\023/P!\036\024qk3!K'E4rH*i\025\"q\026\036:h\023W\034e;\001iV=B^lk\\\0101\026\037\001\000`\020\000\004a\002\000M\006\010\r\024b\027\030\005 `\032T\010\n\011CJf+IRS4Ymba\001\022n1KF\021y@:\006\001U @XL,V2\\M\025\033%Nn\020\025\016'+Mh '\031.G;=dk\030Nf\003H\030\006U\002\002b3\022QJr6\\d\006{\030@u9Y$\006\013P@h:\035\016\007\031h^/;]nrsYJr4\\m\026;8\\c7[%w\023AB \024\030e\023\001@b,\030\n@`\032T\010\003\011Hjf+IRS4Ymb\002QRm2H\n7#\005Zp4[Lr\002\005jt4\033n&KQr !P&\010\014$\005\001@@\032!LuOB\000\010+\026\026:h\011\021\021\r\030W`JZm{![9Y|Np.rCt@K\007a\030(rNfzT\\\030yBcuS4\031M^C8\0365AnP\nOg\001q\035P$w\020ItFQ?Q\030!#S6\n_2y\023\001Q\001\030\0374\032r(B\003\024^AiyfY('oKU\013~.!\014F]);g\013PDCs\r~y\016M:Y\011'&mL|\0050Y\0335Lw\025l\"p\\ Ll#([|r\016Jn{\177#4;91w\001\000`\020\000\004a\002\000\\&\006\011D\0260\004A@5(\020\014\023\001\025*3\0104`\013\003\000jP (&\004+\022*4\011D^0\026A@5(\020\026\023\023\025M\027\033\004@I7\035\014W\0239Bt4[mf\0130@S2\\NfK\rJ \\n6{\rRa:\032-vqD$0\010\001@5(\020\006\023\004Qj\002\002I^o:\010\006#\004\010\002\n\001 @\020\010\002R\0018-5,\"\003a+5\011L\027Hd\001|_ffuH2^o2\006\177\rTV($4Lb\027\006+FV:(Bu;\0218 \0038:n{Fn;{jx\026NA\023$XA\032\177\"]E\011.,\016Rh\\HiqPL\016'9iVU`\nmD Qa}[$/i\rxF9Soe#L=(\026\025+\007-]\031s-\0214P\020kuZNph\\\031\011=sE|,\002NK\016_Z\003\007%\014\017^0|5\034|?Ku\031QHJ\013wd\n7Qk=YV\017MlD\006vaI\03468E`\011^eg\017EDr\177AQKZ#x- \025Ggm\017p!5\024`%d9\0116v?.=\034b26XaHTtc\016z\"u-\005\030\"\036\r\006$7aJW]\n}s41Uzlv(O%+;\004kC#K\022RX2Bm\014@ny\003;XM\030`\014\030ddjg` \030\004\000\001\030 @\027\011AB1\005L\001\0200\r*\004\003\004`%*Lb\r\030\002``\032T\010\n\011A\ndJM\0021\027L\005P0\r*\004\005Dde3%fa\020\022-g#\025dn0]\r\026{9Bl\020\024lW\023YRc2H\010\027\033M^c4X.FK=\\1\011\014\002\0000\r*\004\001Da\024:@@R7[nB\001La\002\000B@(\020\004\002\000];\010;ASU#3wnpH|4\020Q\007U\024tU\030tsC\033Ff\0321PU~Br$iO#$E\014HL>&\022\027{q}F8 \004Ea8\ra>x)L+sD\026\027\027RK3~J.\024Zn&c\007\010uIT\010Lu2\021]#Z3IR+\\_\034*C6T^\034G#/Z\0308\023\002U%7#at \020\034\005:~[\0022^Fldn~NX;,z7},F$\013\031\017\001b5;HI4\003^h\004P\022L\025\\fIAy9\032q4@7H\006\007R\001[4y\034kw\005<}uo0\nD\030'c\177 \034H:=C5)GaZGLBp>.#\\BFbU\026\011\00331l+,\027g\003Qg'lI>}\007l\013C\032^\007\003\037GPJCf!\037KSD-cTEH7\013zm\006-ma\006\025r~\011\032\036)B\024~.,:'J%bIC(\010\006\001\000\000&\010\017<`a\030Bf\000H\030\006U\002\001B0\022U&1\006L\00100\r*\004\005\004`E2%&A\030Kf\002h\030\006U\002\002b22YRs0H\011\026sQJr7\030.FK=\\a6\010\n6+Ili1Y$\004\013Mfo1Z,\027#%^n\030DF\001\000\030\006U\002\000b0J\035 )\033mw!\000h0@b (\014\004\0018u|\021z\007\033cc\000\017kg\024z\\\004\007}\011|v+4b\031z]eV\013\003v\027\016\021\033 7\037!W.&\033\037vMG[1\rH\0302\000a\034\016A+pp\003 \014F|\177[I[\0221ftjyOi4-F]\002Fy\031g\033|\"O\017\000/`\n\022\035D us%'*\\\032$\021o=eS6nSA|_U\005eDZnajBu\r\032@GR+\033G7:0\023]\033 @@0\010\000\0020@{f\006\011D\0260\004A@5(\020\014\023\001\025*3\0104`\013\003\000jP (&\004+\022*4\011D^0\026A@5(\020\026\023\023\025M\027\033\004@I7\035\014W\0239Bt4[mf\0130@S2\\NfK\rJ \\n6{\rRa:\032-vqD$0\010\001@5(\020\006\023\004Qj\002\002I^o:\010\006S\004\006\022\002@` \nh\001]rP\037\006\021\010q\0030\033cp:\022\017\027\027/@D\010H5]<\025,>\032+\023_XG\022Zg\024v\025bO}P\000y\016XSap3p\026\023pX#:^JL\n^m\003QPo\0117x\006\\\007J])eC\022B\"~\035D(fN\036\004v]3Gxk\037-\003GP.\017U\032YFL\033Ldc,7b&Y\014\004\003\000@\000\023\004\010\003\035\030 1C\010,`\011\003\000jP \030&\002*Tf\020i@\026\006\001U @@L\010U:\030-\003\010\\`\025\003\000jP \034&\016)X-G!\001\030a5Y$\004\033%hy\030F\006\0010\030\006U\002\002B0zaFe9\035\004\004*h@bc[G\0054G\032'S`1\013wSu\016\031;D,\017w\r(\002w8\03540\0165\005\023A\003.' 6\n\011\032\016\177R\014(E\0270\025e*5mM\031E-r\036\016\031>VT/yE\013*T'\026iAm'\033C\022+h\024\035\0308j\011Z\010xDuh6\002\000{6\002??GQ}\177 \000E6h\0075\0301%\nij{2K\004\001\013f\016x|*\003F\r-\r\"W\032_)k\000\025L~`*I>6c:\017z\003zC~:9\006\004\005@|t\010.LEP%/qWx9gS\025\000\030\005\013|\014Y@sWVm9X-g0!nnG\034O2\022C17W\026T\035rV9\\p,\034oc2\023N\030C3\005\034tvy_k\r:>\035\032%kU-+:)\024%\r}$|w\034 \030\004\000\001\030 @\024Y@v1\020L\003p0\r*\004\005\004c\005C\rJr:\010\011\026sQJr7\030.FK=\\a6\010\011\026s\014\\1\013\014\002@0\r*\004\005DaUC\rJr:\010\n&{=h !P&\010\020\004\024\002A\000 \020\005\"haEG#CtAvp\031\020\rXh[\02450X6^\027l+Iz\010+h\010o\036\"N+\025;[\003! \031wB\025\030h\022\017\035FB:NR\017L,=eU\025&EBTN]\\yR;!\001;w\017\025`VWD\004\022;-$jc]A($\031\011\177?8\027Z2E\025*(\020\022r4L>O?\036P\005\000hF^r\023\002 S '\013+\000djz\r1LTL\010SB\037.t\0142'\026cooEFK0YpZ?UH\030>Q\"^(8\021K\006E\020\0043Vrj\024(ZLun jDW;/I~\006\034 IWlH=hj~Y\002J\035R\032\nD1J}\030YA\024=\r\025[&=d\036\004}l\005\005Aj ox&\022-[\"\033a\007#wN`2W\"M6`Of:\020BO92`f)/\033U\014S&E>G\001HUoEL9*RCEQ\022bKh@@0\010\000\0020@sF\004\001DB0\017A@5(\020\024\023\014\026\0146+Ih $[NF+I\\a:\032-vs\005X $[L2qD60\014A@5(\020\026\023\011\026\0146+Ih )\033mw!\001\006A\020\014&\003\021Pa\001D@P\030\010\003->\033b\"\032M\013nG\026\nYvP\000Le\024\021\024HW\023= !U3\024#.xn\\1yl3\016mSt\033xYA,I\021\\'T+SE\021^\032\010\004\027V[%EDLTXN\0339\005B\025\036)r#[L\"-@o|;\011N,k3vg$DX`Ob@R\016\001\007\014TJ\016\031 \017HV\006\034+\010jU#q\030\026w2D\016)\034WI\016K\031a\001\027\016\023+he{k\001\000`\020\000\004a\002\000SF\003qDB0\017A@5(\020\024\023\014\026\0146+Ih $[NF+I\\a:\032-vs\005X $[L2qD20\013A@5(\020\026\023\010\026\0146+Ih )\033mw!\001\006A\020\035F\023\004\010\002\n\001 @\020\010\003\003\022U|Vc)X\004H\007a>Dq>\004efR\027k\r\\{3=kW#\021R\0146ElFxh\0318\032\025)\030\014so9Qcpt\031LgzI\024@(\023\027%\007\n\032\014 \036L\005:_\031\033z\037zHD\0023vsj7\022g'Q\035d5\007#9=\001x0pp\016;06Dw=<#\017B)bqO>\025\007/yExU\0179wh\010\006\001\000\000&\010\020\005T0-\014!3\000$\014\003*A\000a\030\011.W\030DF\001\000\030\006U\002\002B0K\011JT)\025*5#\025H1\rL\003\0200\r*\004\001Db&\023\025(R*TjF+\020@R7[nB\002\r\002s\030FF\001@\030\006U\002\000b1\013\011JT)\025*5#\025H )\033mw!\001\006A\030 @\020P\n\004\001\000@\032K#Mt\023\005\016\nP\rz\022Vpe\023m%y5k%\r\016Q[\004SDpk!\006\001\005pH\177m\032\017Ry\003\r0Q\020~G7\022X}?\0229V}\037j\037\r/_AMufxuyVz%\026='K-`t\014t\030<\035t<\177\037hM9v\023#Xh;>JTyr\004_\005O\037\011\n&`2\034e,q.\037{\010\023\017U11|c\002@s%5g\003xUe?Q%vw\007\"_b?\005@dgVTd\023\034@r-4k|\014,$m>c~\031\007 c8fqpE3M\007_%<9\rr\033->i\002fgM-\014\034X]_\011\032\177|tv\000d\022s{]M\177~.0:;1T<;W4aQ\034j:1~V!z\027\013eZ}zAe|k_o\0116IoC\021Az\031\001\\\\q\0017R,CN\031n1=0; j--) { + ret[base + j] = (byte)(l & 0xff); + l >>>= 8; + } + } + return ret; + }} diff --git a/src/org/ibex/net/ssl/SwingVerifyCallback.java b/src/org/ibex/net/ssl/SwingVerifyCallback.java new file mode 100644 index 0000000..832ef2f --- /dev/null +++ b/src/org/ibex/net/ssl/SwingVerifyCallback.java @@ -0,0 +1,99 @@ +package org.ibex.net.ssl; + +import javax.swing.*; + +import java.awt.*; + +import org.ibex.net.SSL; +import org.ibex.crypto.*; + +public class SwingVerifyCallback extends JDialog implements SSL.VerifyCallback { + private Component owner; + + public SwingVerifyCallback(Component owner) { + this.owner = owner; + } + /* + super(owner,"Certificate Verification",true); + setModal(true); + + JTextPane tp = new JTextPane(); + doc = tp.getStyledDocument(); + JScrollPane sp = new JScrollPane(); + sp.setPreferredSize(new Dimension(400,300)); + sp.setViewportView(tp); + sp.setAutoscrolls(false); + + this.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); + JComponent bottom = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + JButton accept = new JButton("Accept"); + JButton reject = new JButton("Reject"); + accept.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { + accepted = true; + hide(); + }}); + reject.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { + accepted = false; + hide(); + }}); + bottom.add(accept); + bottom.add(reject); + getContentPane().add(BorderLayout.CENTER,sp); + getContentPane().add(BorderLayout.SOUTH,bottom); + pack(); + }*/ + + public static String prettyFingerprint(byte[] fp) { + StringBuffer sb = new StringBuffer(fp.length*3); + for(int i=0;i0) sb.append(":"); + sb.append("0123456789abcdef".charAt((fp[i] & 0xf0) >>> 4)); + sb.append("0123456789abcdef".charAt((fp[i] & 0x0f) >>> 0)); + } + return sb.toString(); + } + + public synchronized boolean checkCerts(X509.Certificate[] certs, String hostname, SSL.Exn exn) { + final boolean[] ret = new boolean[1]; + JTextArea ta = new JTextArea(); + ta.append("Subject: " + certs[0].subject + "\n"); + ta.append("Issuer: " + certs[0].issuer + "\n"); + ta.append("Start Date: " + certs[0].startDate + "\n"); + ta.append("End Date: " + certs[0].endDate + "\n"); + ta.append("MD5: " + prettyFingerprint(certs[0].getMD5Fingerprint()) + "\n"); + ta.append("SHA1: " + prettyFingerprint(certs[0].getSHA1Fingerprint()) + "\n"); + ta.setEditable(false); + ta.setOpaque(false); + JScrollPane sp = new JScrollPane(ta); + sp.setPreferredSize(new Dimension(300,150)); + final Object[] messages = new Object[] { + "The SSL Certificate the server presented could not be verified.", + exn.getMessage(), + sp, + }; + Runnable r = new Runnable() { public void run() { + int n = JOptionPane.showOptionDialog( + owner, + messages, + "Confirm Server Certificate", + 0, + JOptionPane.WARNING_MESSAGE, + null, + new Object[] { "Accept", "Reject" }, + "Accept"); + ret[0] = n == 0; + + } }; + if(SwingUtilities.isEventDispatchThread()) { + r.run(); + } else { + try { + SwingUtilities.invokeAndWait(r); + } catch(Exception e) { + e.printStackTrace(); + } + } + return ret[0]; + } + +} diff --git a/src/org/ibex/net/ssl/Test.java b/src/org/ibex/net/ssl/Test.java new file mode 100644 index 0000000..aa43ccb --- /dev/null +++ b/src/org/ibex/net/ssl/Test.java @@ -0,0 +1,32 @@ +package org.ibex.net.ssl; + +import org.ibex.net.SSL; +import java.io.*; + +public class Test { + public static void main(String[] args) throws Exception { + SSL.debugOn = true; + if(args.length < 2) { System.err.println("Usage: SSL host port"); } + String host = args[0]; + int port = Integer.parseInt(args[1]); + SSL ssl = new SSL(host,port); + //ssl.setTLS(false); + ssl.getOutputStream().write(SSL.getBytes("GET / HTTP/1.0\r\nHost: " + host + "\r\n\r\n")); + cat(ssl.getInputStream()); + ssl.close(); + + // try to resume + ssl = new SSL(host,port,ssl.getSessionState()); + ssl.getOutputStream().write(SSL.getBytes("GET / HTTP/1.0\r\nHost: " + host + "\r\n\r\n")); + cat(ssl.getInputStream()); + ssl.close(); + } + private static void cat(InputStream is) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String line; + int count = 100; + try { + while((line = br.readLine()) != null && --count >= 0) System.out.println(line); + } catch(SSL.PrematureCloseExn e) { /* ignore */ } + } +} diff --git a/src/org/ibex/net/ssl/rootcerts.dat b/src/org/ibex/net/ssl/rootcerts.dat new file mode 100644 index 0000000000000000000000000000000000000000..c97bfab3c729ecf2c5c7f310da9536a4f2073631 GIT binary patch literal 32407 zcmd3PbwHF`w>RA(3=PsPF?1^3-2&3x-AI>6r+|W_NOw0#cQ=C4DFT8Ze1m{;-t(S& z-}>&o|H-rWJTvRJ)?U4~KtHcopdWZL(DwxsA`l)1QUwYI0HLCE_XivRLRg3i07Sc9 zfdwF_={cC1S(`Z8STkV*F|QvY08j)31sFtxy9TOPww9UcxTCI3rnJJ^j6?l6+F}jdrJ|1s4{GUHb;3NQ;b`9Qt8DW= z_g}0$_!jDqk}h4ron5Y@L7KROo6-tx`eUv7eRLe%XIF7@JPKUy?fBPR2%$rQWd{Tk zI_DOQBCd``_SSlq#LCL@ z#Ii0n;DUooWV~GDz)7#9!Nl*ABTAKQBswUY<5aWNL7_x%~W8vTQMQd5&G&YrArJU4$;c8&23D zh}_LwGfJGOeCZN7vV3m2fa#^dv}H4!`YFLF3*paQg?m;4I`$fNa2(}sMp^1_<2b&7 zF(S9c*v}4g+RtI+igakvKCiBr?AR+900>w2JX#O}qxh~@Yi9nG46XVdOQfok`tbnG zlxQ`=BcrM@K^x4n8jth+O)aGfRt>5<7B~To{t*^D1mfSY0OJ?5_mL+765Sw=^J`hi zQqRGGm*ZHI@8iI-TF^r}~HXM&84Hox$ za2YGihX&|uNKd~$ng>P8A-;{1sbiP)s;!uVpD5p8GCVkki3CC|zN;{(xGH^J#9QoA zrTU>VKV#F3`!NcO8#AA+UrdsoRX7kX(vB}jTn+b>VqVJlS+-;3tD^3+78ug?0*9q+ zhI7UFQcNS_?`+0qi%WE0tQ&xHbmZuB+iYy6QGH+~i6oC5Nyv;?AJx06XHG6Vb0ud< zM1(mOTW2L_&ZLZTK1<2K>31RvXxd(pVX=$AJ7_!YdYDlNnMij!MA3OtL(WucpcUh7 za6r+c#6Z+6a|gU5(C|M1JTZ{)26*gWz{@-7TbddC3HMDrV5ENtMl10&Z%QP-7o=nT ztl5}o_Gv^L5?Yi(HlwjJzslAek)-Ng8kzMGS6_Oq&7s$sRxqVrD#7W>PLA&>$p`X& zG$g(`N_+P%@I+Ybvnd=p;ByprLVdzcsg@tl^IcKs|1qN?M}%MmL&a-8wD}A6mIZ+~c+@bEV94i8_tNci$l(Im zcAUtzQJfinUCTs8P2F=SwA^hb=!7`grz8<+!@582aCZTk{0DF+1Crd}j`s_91t&dA zGh;I&!#_d)nK1cs9$q6jD^08kO{X}3bxxnx&9!N2;JM@r)5}$j-dn1YG@#mgv_K*X$D%LHAGI`2E8N)zm*| zw^GYXYN7IeoOD%`*3iB^fBFUYt&J^(*Ap{IZ&cFh{X#gjWSXSqJYkV!bnPc^tQ0TS z-##{FJdm>W1aKBMU{ZXU9L&#XVVY!2`*?++ei0Bm6YMFi4A@Q^p+`H#;pSeXnE5uw zzncYv-odjR-KxrVfH7~x4sV^G1>40o(gw-GuIWT+CHMv1LdZtSF77+)!(D)NuaEaD zM$1~G>CHRn34y+m_pk>UV=5rUHH-)VLIEoydou$)Yhn>sTYDpr<`A;6vemP81DRfH z1BUx1;BPl78yT2d+gRF|m>D@RF#_pt_(J*T7aG|+g7{;g=V(MM;N)m(V{hi@267J| ze0{-s4kcm&dw*ckRKipV?_7jr9tI6wVaSma#GJK}feiAJ7HfA6#?Az-ZL~!Eltuq*2gA)aw)n7r8}+BD>|jn{iO41`#uq|P#igxUn2 zN3y`4+2{pl*w1}6bu}mHq21%9+jvbF1BCA>uDAJ28(H|zM$Dyt-*qU?}w9l#FD z=D&UrJ%``NNf>KU({cuaylQY$BBBRN;DcIfoIRE;akMGTXfbEf@x39f^;}tkHwn=T zuNxiVbM5u=N=UfBJf?$Gg&%F5Z?Ei9=Ez^8%qSDJbLd42*=txg{5Fh+=fJPgEkTD` zy&vUm>I21zh>yxMe*Ct)En=Cq!Y9cp)p!4@mMASsF~82|kwk9;BN^+*mkAyok`qyvFxF;e+hmXu8(s#8@p?V7G&8RmZ%K#< zQ$!xH5g2cVDthP3NdHQFv^6fX5?4!0!SEj$GY4w?izNT{iT^Ii*YV3QpEwh$o;3f}W3>%R`{YWu zahjFojd-8Zl24iuk7$w&m(sO%OW>ic=BOD4^)vJ@D?(<`w>h^--n0lj0$211aTz0s z?5e8`sWgXQci2GVe>c+Ks~=9k8i|-AKbkAwPQ7)UPZ1oNoIvp2q=pY^;@g=bfUymK z!q)a6vIV^trZ0JUrkBOtW%6{V_KT^opg3T(r7f#STlcam2l}F70ahJiTpj0?!Ev_1 z$0(%dT4gZqUjw(c$03Wf1D@zyVeXnWoN=++I}N!g%6#pKqN{zp+C!$qM@0ULlswWAFa4iM|+=%WhiSzCx$o0wS}-Po;Li9-b3)2e1R*1ru9 zv9Ou5nFHw2ovwfcAn1c$V)XMROxOP1y|@z1K#%|T>K|gtDZdRFeu}A6;l7phNx`lV zY}Gm7H#I*JmIgZ+#RB~{*!B0@+rt3qFl9ckR56aEDX*<+c_xCV?Fdwci&n~s@7<2R zZ#>@7DpTxZG23KuIkBI5`+b)AMM%tpn9M$AhAA|s$k}o{=^Tm3Qb-?4P` z@Noxoa}P+*BQ6#+Vd95QlQgBdJv_1XdvT1cgSQhJTk}3&W!Uq$!J^}%(PWp^-uRIj z$BTRzpxu5pk?=dWAZu8qT2xPQGzK!*N@)Hd|F78D&uR|Z)jT1oD{x@iLyf-C#)S2( zOv`%##vdcuS09YJfC5>5ebA2r{R#$sqg#ihgMp~3dC!l3 z;jEfLU?LT|RAZRZQ(px#<)Tk-QELBkBk3}@He@Jj78Rb?c?mnQs*jICbu=rb)u8MLx$;+v% zvdbTHxoG9WoUCm&7@*0*7}+Aci9O5Uf9Wk5fD(>XI@u3rh>rA;TB$PLV-!$y1mku%Xiqcsw?w^s_z5A6AQ(l-WFYDE=D?<=Pqhw-t&3=p}!xP=2re zL80tsO(|fco2^=%>@V;+vO7IZS!8W9{=)1~kd|eNr)N^4WfA$&A;vR;1UF&TI?2n0 z`PnAUve=exO@Niv6A^MEbaK;?yqB3!LQy>UDwP9iVM9WB4@tSdVyQEv)T4E-N5>b~ zJ}})AdnWvv22A7lb$D`I;$tT80v5tZB_Etkf^};?ndFZcUtA$br5L0IZLg5)*%0Eb zCi};pOQ$A@CkVrUz&6(3-ETmee3_G^-R$7olD6o4@A`TYv2c* zr+6gsGD42HlJ&XBPSIAEP0k;xSmCT|bYtg67?7nTO0^!s!ggol;URyD$=}oO13tQ~5 z3nZKv4lcKr)n;Kpy^`OsJ&j;XelF3f_fWlP76vE&dEwyB`=${bRQ_mZ+RW7-moG4n z)V_UGnRO}zJECi4>#q=^RM8h^0H=5zs(H{l494bq$+j5BZ|Lfu-p}>omikek=soH| z5!-d5^C$KA!e%CBj-YViT7Bv{UZ3n+N^KbKR4yU_PFP79lmQZ90{RAD2U>3}ecunU z5|S@m22uAf`ZJzsj|+}&IO-Y$UcnH&S3ozjVN$7;um^+l8q3xf<{(dsobeZrOtOjx z)C=)2NXUk8wAMfMzFclRt=Aj)D%O&e^W|&whN~x7pI(nJm7ST2q?d%uZboJkgfv($ zMD89q1d}1EB0a#-nZ44zg5C}S91`U2)B%0p-y_S(fe9Iicuf`@07}JC&-5+@3=2R} z0{Qa9(s~v~#6qBO@n0nWDH9VG5aap_AOXlAM1H!MAXq@puLa_sO+W`3_`k{;k9Ja4 zt!F=O?J%8?w^90FBLRS~DW($-$1u;t*3mc{V{l@erq&;cWxS9X&ktUO>q=UW;d5%- zrl`PdO~HLUDjYuLNNmLc5os2y8QOvS=BNzi0?V?t6gtL#B#=%w?um%X+nIbgk~Q58 z9>>EZc?Hv9NcvKrrp#%@65#WjgUG6vJzwCPIQ<7}ywT}}E?^%qcfJPr*tn3)Qt3P1%*Idrd zGOJQ!BXCvk9Ao5mFxiXig#tk9!`PCQA9?PH%orkSO5|k;4N`Upqg&Q2xc!GklKEC7 zn`0xfWA<49INJ}rWwDyCU?OP)8??{JbI3!gAgf#3bs#lIY>K=Dw~WbZ4E$Bksp*$F zUsmkpf-eseRE5xziUyviy|aBUhQT+uXmpTk_f3w{GOVGqa(_{R>PnpU`GyQ+e^;4e zCpHs+r&4CFyW^xCm7I1K>CJkyrq4j!oBt(|Wc~}0Wd3JFa^C46ti>rIxkaCd@>|Dp z^43RH2hF4baN%6Is)={Acu+W8F_NyCzHP@Y?(!A3SIPOEUA@-+a)PhUDG_JqttQ_m zsVJIkQbWefua-b3qhg*}sN1U^e&@Q#Gu=Zadf?A^@#Z^r!h+wYHIe5nQa-{&yL2A| zendZuQ8uGk)i;}8-2+o>WI-%+f(U$PC{Owu|0CCCS5ul8Mr`h3G#OFQKyDMvm( z5s)voEFxuPn6R++uXC$$HWHtOr!h1jtvwfW4h#zt?t`DZGw{!V()aX_s86p5)LKx)!{Imy{K){V!K>D>0ls^0wk=3Q-*!ie9O3II750Ex z7N)^s)6@vq-Zr_(#8vs=qeX8;MA;x9E@nRx+as_b$J*9FMM<2Qa@HfGS#xPUb?+oI z-mJ%Frrg*;Hw~uguU(bv55eD!w@X(dW!Uw9cFC+Jt*FGgjA;d?A`wj9_ZdsPCTVrn zeJL~Wm1EmkzJ-PrnBL=;gHKEhGzgz~TNLDPF{f;*y&?7g1z*MA_|BJ9Q=IQ1)lFyG3!~CzeiDZo zIRZMuz@g}Hbg@_aB~wThSV$y+;bR{jEMa4B?h>10Sp4YeM_og*2wFYP`aTCu3)YRi zm1o4y-urdci7R5>cutrgwf&_KXJ5lXa?eaJ2cTE6m3~ux};$8hwb zMrq}il?FpQU8A7RnSlJKy5>yQ&*e%(GA=Qsxx>!y@D&33R@^JTiC8=StXWzcIWqAB zpIl2QA_j+xiwgrtYVTGU#e{?ebcE!Uh|R1GZ0v0ri1pow?Tw5Xh^Z~jtjru8h%L?Z z^ciUGq9a5A0kr`QF*6VdB)<0r#M0NBjSQJsfJ`?>f%g8Ykfj;uJMTpce~6xMouydE zimJ~aq$bl%z4a${zA31c+e^MW9)8hNXfPndreGd0Uf5&mW>d3`HWyszJ*Q^3D%G(ZSchXEv1}=i`GRcT8ht`Y~NL&VOC$bZ)KsW@mzR5W*;zN zv&wo;q26eGd`9&--ei(5#z*^*_4%W~I~XN^zIA^ABOmbb4UFu63!{>f^nVaVZXo9k zicI%W{HxIG?}6FXsv~RnW$-{Z2zB(S_ZUkeJ$8l@urk4rlF_tXI6v2E4a|g9E!C)t zG{s?UYyl31LnV(w)Yr1{GWWzuO)V_SQnLNYODQ3Q9yHbDWGd36A(d3U8Hg3nDo;+> z`A^<*$+eIIK2=U80K~KnHTG^TF!n`bj9F`EA~}vQKftZ$=?DJdUy(_WIO;G1SvmhX znPlSP;`(P~5)@9|R1*GxA2IdyH`Dx5-i0z!@6m(GwlaxfF|iD3A4!cgBTC9d?D{xo zn^e2SuPo3_j;JzCJNael=X`x5VsCx9D;NVA%7Xq6m;u+;Z``16{63n|Di5J>a+ z+5F^S-i~qUZ{TRkLlBRHUEZh+d~WxeGB?Z*#0ZMlOk|d~dE8xRjP&WEu|}fR`Go}T zkCxoV7nvrEooNkrxVkm;n{+)<&c+^bE5)vpMoL<|ldsE$XdurRv*gRh#Rs98RYHH1 z8A_vkeg*3^=e<-5r=lAOq&f2t&oT@8#KuP$q~-38*|-lDfk~>24HiWi+khfr8r^&< zHuff7uoXpS$%b;M9fw5USjcvAq6K#O?o_A(eS7X%M^F&)yUc%2NSVZeqBlo`?{AOD zjS|oSsT&DvV;jc5r*rPBoWIdH|15X^8;$cbj_Kx3(q_V5Dxk87^7xE3Qio zaM8c4?#Y*l7d$+Hb$O0-mwZgt#nJKBFypW?Qgo_fI8chSC=X(r4Wd$(m%`Q{vex9Z zzrDP18s&k$?f(d}JV36W$O4%EgzP`7zW)-;e@pTF#B_k#pH&(E#aGl~j%cQNparn_nDvFu+E4C}>?p|JVL#AH5D;rdb{Dcd85(9aMhqo*~L+l~3)@`)LHgt1YN z)*>vC&%U0Nck^}E7~}ZXCOJ(LLuokj`IRL%Sm-fpiqYxrrw3O*Y9yY{zg_g>vhXd% z!qux)u38hi#?*HKDECK1Z?nA!09X+_Co^L`S0)A^-3^))w@--W?TxI=oUHCyse9SM zTN^c)4if>>4~N(ql)Fegfs8VO+>y3rK2zStEXHpW8j=Oy$VvBbt#2*sEBp5BcEg2X zIqV62L*8H-uDGe>#c_s+7yN_c$a0BiWH4M?fR{Ch1JMtib!Iryh0k@O4o%7jaFT?G zoY5^H4&(hfh1yQ^S$lh2wRA?O>yodU9_VV`oJQYypzMD~A3gBV4SnQy^xc||8%Or< zS5RBEN}rm*>F>QwrYC~4ipDT~g+O>Y3uOZc&?jjh@f=Kp{#XNkAj`V55*iO>Sn$O{ zNe8;T&%~!$5F+Ao0UUm384N@2;`{(dC2!*og%Xge`JJu#8)ANzm2XLf1pF@LF8{h5EkF;-Vf-u>*R5kmK^T>?s_;s*DNHQA8{vo?S@(4iP}EI%la5X4 zTG&;(K^ezfxyTaE%{X_M?5^SPD)ZUqd8h;av-S37AIpLFN0X0QzMUoI8Dx}-L7#p8ZK8q%Jp3CV{w|G!jJOTh)h>&%?{zegvE^9tkX{{;&Zv)fn2H`$uzd)G zBT{tUY~gb(~RED zhjKVMLSBZpIN|^wI$lcm3LcqIUbM_VMYUeO6eIQ*SHxlk zPNdu4qAk?8cMf>JkTuImX*{*3Z1+l+WvOJ-87pHInxC#qq((;Ze3qr>8Y$@rbVd%y z>7oNuCmn7H4jO@}_xn!n25%Mv4~1C2g0>F7)Z|QKD?#Y$0Ws*5g1efdS69MYwA6G1#!YK|L=#okej!!^&8f~V@MS7= z(YgwsG;(4&vC_vEnw9~M7xeFjJIw2fY36Dj+J0pw+<2Pg!keKYL3Z!ljbpXgK?=c zFm?|LJg4csUVSdW8;d8J89nMk~`{+^9)PgRpARB{@aM{} zUI=0(8%w9_9S*mBKEL~_{$}H?LjGB=zB$A3sBfzd;NcZCcrV52aU#0}#=MmdRV z361N5>5fY5t}(=O_p;B0sBjMjNqHtB;KOV4i2%U5*VG}Y2Wn=zrSW$S;Qj^yc>&pDJ%6Gg0eCr8;Sx8sLV@p^Y@sU<60__ta^HEKi>jYY@Hc~^!Y zQFLibs~MPH zR&d4ec=}So;(-e@N;nW#_1wv{5771=H$ncxe?X=Qfdn^bVE&Cv|E&11{fZYG(5?AG zJ>3(*cL?jbG$4*#g)_wQBRnJ&-D^C=%8TPtZ0ZxfKQcFZRzncy(A4|_fz)SD?IH}i z-Zm|f3u-nq_*w31Z*dDt@&v~-se;cw!#QNS=RfQj;@)%(wm~uPyXtD~76zk=7!mR& zBP2IzJxtnJKOE_F$DS!nQBLsg@YHP%%)?}R$UlqLu%k>uO;47ddoV;luyEmps=aIk ztJ()!F{-+u&D4)$&=E9BA2<{nlW%ot%5}cz&=_?3S=4*ozC%>fv3E1TW@Gl3Q^b1B(2k zEV%V}AOV2CgY4}gs#%k(n|tMH=UFUVJ%%beW4IdIDj3mKClSs@@4o)BaTS_Ly|a`N z0ST1F=gWXM4p1fJpurE`^g`-QPRsT_#*E9H$Po3LHkBUXz=Sa^Zagh1KcG2|*MP^; zb3q`b7?%^t>uOw0SUMZejS%c7Zy}7PlB~3X@iE})$k67z^<@0OZGbxW$O5$`+)fi9 z03O`vwx4YX*3R?}dUx?LA^=fd&)(76$o|^@yi+WY0IYi-xVPb#AdS+UG8agqyK{8Y zL}wR;SrekPu(E`#ve;n}xCWTE=%E@a60q;Xv`NeQSIh^~8G4Ktr zXX}!Pk7Yb4GphArUe1ax#A5UDX+lULR)r9w#efyocQK5A5-B=xRTyZ<+(H%B zc^K*w6GX169`0zG;V)R8`dD5d$cEVZkMDBb?okmCb! zZ-O3ld3!S}J$pCe-wo_{&LzlqzK{H$_Wfc&BQcxU2=ydfCOQ|H7;>X6i6V*`Q+y7q zn#GQnkXt*9qWC~VWqMNMG!b*d(3G{k6@v?Dl%K3rWHJ)CVDgNudes}EHG>hP!iTb_ zfxc%&`)N6fdctmaP9h)mR7I==!yeoB^g>mC{hHSag8ieU+fKBe~9hAqB6$J^YDDE^x#>U)|)p*Cq%_0$3_r025YcrY4o%YVxyf5Sk|bq)``xSDAwK{9OWWe1Ed)&0nI zB!B8I^9szu)2&W+P;#Y?{bBV7cFg$KhJsr<9d*b2&`rzS>Wu;KaBCK2l;j6tM@ALK z_K|%p*Ve82v^@Dd`*-C0sVJ^&XG!rSp;Lg%W0)6fZFM83TcEDYg`KiUg_5s_R><71S+*+q}umro#V3!DBJ0XTpeE_b>x z9wv`TurI&Y!SL;3Q}HnR`A4e>HeiIdki((C=7jmDvKQQ{T%0yAydh?qv@##wDc4xO z=ubK0?Opxx;uxJb#x-Gb-qAW%SZ^e}OSH@hC1?XTbi&GV!WAn%W3wB9bNi4SAm^YQ z3kc-KZy;o?d+-U6j)*uqA705iYS4qjvDQ{BnpD96%dM!iNPrTq8~4$OXrB0_ zA?=hu>lGbst0k_GBy8aNnCA(J>HgNCVw!hs)X*K&|3)k=qi1IMZ#P+4fnM_0vGniN zKQvjrcS|dk%kKIllI&H;p*ZT{1GZ(6@-d~yC8oeOgb2C;hxWeiY!sHsr2$q5ZhY8)65MVUVOBgM; zLUu}h`RwonG9)&_0~rIeI?NxF)VoLJS#r+@p4#19tW*DnaFOLQJC{xhDL*s%0>0l0NSi`sx*YOltzcEFnE#vW*d7-M* zuG^jtKX56aZ^ONV0&26mJq>UGSaCfoeJ6X9yC1F@KuzWCj+AR++wZ$5it_kq`1@49>UUULrKb*J3z z2nqksI;a7tHfdzfV0hQIx%7Oksd?gn(MlF&1Ua8xS}%{=i>9hHE40@eKb5|gfKt;;&!6!I;Qy%8%b#AiTcQVx(^3$BPPXr)9}vZh zK%|c5$Qa?1{R0T`m_~8H9#4Ygbwi(Ggm}6^Lid9}pqZMZNQ*{L=fvZWrfScb%*n$= z5}z=6nHn=&lUwaDv-b$}9xw@aXX^Fhzhx)-KMUtir|6%S&RNclU1l&EJAZKG<}mR2 z+0H_)toNae6Ck^#Bj2fiY~0uzO}dl!P+{tExk8!0tX7{y$$_-Wss(*lAR>?;M%Hh< z7h?CDP1F3xPZ%}ON{7iusxLKC17k-j{8Wo%>T{*kuo^B;pQfJDgqL^>xc0~uyfi`j z^7{NjEk*5_9PXW zIl+#aIzOB`XsLeh6Vh|wWV>R1q!!g{ivnx> zVj(?Sqq}}CSOEO>pTx>GF4jzVK%AT8ADXhMo(pKwPzaRUbOMcoS(`9X11WDpOrqa~ zwCe_f>xOtU10#puqaeRCgtu>z2c-aPuE*U4^&EcA_Wpd?J=271d%J^P|9S_%1y2M5*r#~m8@Fi<@_+$#N*g)FBs0qz^$`0-;zu@4HOZDEINMELVGnsPz2vA*WMd&{qEyF-uqgEl zBi_UaA+@TA3|-<{2%qC?;*2zt#oi`&{J?dAz9aw3=-#$4Ljv&s7F|($ zBcp3a?OJ{x0cq~reu7^B8{cmFGsfd)Q{yFZhMIPpV$(~#c>5(Hn(3b)! zoTweS1?dc^(&9a&_Pua}9|mn@F4PVt7~-{ISPK_;u0?LhpxP1fbLN0$2D_e|#RX{> zZnbDTZ_i+4YFWE`B4tadc*Ee*c&8>)PMFtolEaz5>MEVhd=>q}ZEjVggW$FX>Ibe3 z^d0(N#`t$0|8Ft=RVx=@x=s_{lVSW{5Q8Rwuj}c5M!3g#<`h|+TI3prUyF0SPDFkKOW*?JleJ)nTA`@RRZDGF&!g@(zKr+N5l zfnBg{HpJ;pM`!_k2mfV+Z$|_W02sH}{MHe~1_u9@nz-#lxErw%__Lb0*+K8{a|q|p z0FSb>o4=fD(@dT970{35+>?M(nH=6Hz>mig+s6@S<5oO+WDXO_!?>mTsW>P+75iIi zg^?44%zhty28qIS%CO0B9dZl|hQoMlUm`8o>u0h^4c{>pFAjyRXVE0B;nEoCKb_@q z2RT%UDn0Ff0o~}2R^z~z2OEAQD6`K0hJfP^U?re$$NxUSzb|~b<ZZ6TRDiDl%q%?40h|_s` zICM6%v@~Ku1tQ;AH<%kd?oYein!8^S(skMIwiE`+Sl{BOY-RRgkL zp1SIZ1SxH?dMLc!Em>p0LAm^>lS?&L(eu$$T^0+)L0Pxe`y`EFlRJcfmi1`yWguBLQg~PgbwaHS@S0nZ*4Im0Oytl6>Brl{F=k{p#06% zCijMV{%Q-*4-Qm2hXV&&c>!(J?p>Jkg8QnzYv2t>>GCKEVTv9t=DLTPoC@!7{n^ap zCpir}6@^O4SP%xrRta;hor^V_ry|7TteCD$CQOnPhWt3jJ|3GD z%#PZniBE9io()bPKOPHSgx1gbWb7C-l-*FjWO`-iq$;4!w#}kptZ+D*iO?E0=6K=n#L_q?al$QD7Dl= zv=2Q(w)^50+64q=3=Dn~nlGFDns70+*O5uP6!5PIQ@#Kr?U-Q7S*ap*+>QO@Lg@kd zfnOw)Vl>LjY>W_WC=BQoNC=F^ke7K_zFo)6!_eA zEzj|H2S+7phrWJz{vyhE+Dx4ILFVaxi~@`Lc zxasr^KYFCo)?$Nkc^4}SQAvToi6|X6`i`9`v|#?wWz!YIQx)cOn^?M8ml={)>{WI; zS+6MXg1AS_CQY33&NW2>wNoS?;H7oDGZi{mQ`9(?`g&N@UT&W|r{T(cFejoFDSEWa z82&CBtPkotg+7C$IDRi}SlwKe*GWBu=lk}#r>^PE2bXvcRL}-D%w!XcIH;x)7&jt(3dZeo;q2@76)lN+g@|QY*)Fck^ zf*Fe>3uaMG_p4AUy4VK#^F5Yk<29MOsuUO%-9)Jd;Q_Cs^yxgt76@t*EPdBGIOW5O zBi2uykpx0&gHd==^9(%H(J%PiXW1S`(Cta=9I+JkDCLHwK`A{UCs6D`KhsysY}ljQ z=JzBfvwakQ=71eK(n?^KQ_Y!d^3C?4y9N^fkUnZ4ZySWHSLMkETAF}zA`aT+JbS^8 zT?@R(7y_-PkweYH{Ji(0$2D03j~+!^ql@>xn&t@a-US0zRzb;ny`Bx&Y5DH4`Ybx1?|@~2_T_%s7sQGT{-~B*d{XpS1@s5x1z-+GDdjMH&@~u=MER-DcvJP?pjh$v zz+XUYWh6TGcAb4vU2gBq&Pi^G`bdthG}`uZwrLkaNZVxj#VfbHzP#Rc{Q324(QHo( ztl%mQ>Zv&@UhVA0Duo3ip&=g1Ztidjy;g}MUi=>6tzv?_U*;sjo9&+894v9QYWNDnq@Me$UW( z?Cd^0{CPYMZ56Z96=uH1Co<_hG0Wozfzn3sy-%KM>&7PM>1(wpzz}HZzD0g5C3Nx* zizF*J@356&F9ukXy0r8F{ACcl?X+cXwLBqKLd7?eA8gztQMAlmU>-8C58!JV9j0-- zOp>7FU@qjl0#n}bzbQ}P_?|sqW-q%h3s3hW`iIQ7`keG9`5qaN{5kJcs8+t8;SL5n z<$ZBt|N6Bi`ZT)zr4t}R+9(-|%Y2n}!#zRWo2>-who;$u5ast{YU|73887nlino5& z${ANh&H5Eb_TX)Ouf$$0OIKq1_zXtoQ120nn4d3wd3d40Gr4tS`A}a%jVClRXzM@; zYVb9d=~Z3rMF^_cHwa;S{+eDMdZjn>ysBa99;SG7;db?wU|o2FHvBQAnT)72!iSl* z45@o4Ma;uIPhP@qt+|yyqgi=wk*j^C!L$LFkP!y?O7u(3^C)G?U1$Bv)XijT?H-A) zXRJyz$}>BkMVEbeoMx8J-6n#+)TM6Y4oWLJtZ4=pQOI$*^>@~sJ@D8$NU_U;emh1p zChJXfL{IJ7uM+P;7|qzPisSIIY_7o!c5QfCZa3rVX<(++M@gk>ljq?`&on(;s3F*d z-J!;)g}N<(*jmgrG;DRwvpg7*&NxrAPGfjeWms%PE#PLD#7!5D&dF6-%ZAL1o5V*$ zjY$U%%?8?(03tL3q?1~&h^n&6GTZhb7!~m6{j+v#DRRf*^q6fvr~5K^8rXlQF>0_C z9@4t=Z<~R>MfV&rP`vryTrqJkv0$%fXk`B{mMebGn>qe`&27Qr`j(J;-Yft#g?fMe z4<(BsiZgz>TJfcFpPA!k&&?yZ$Q29}bsMZ25%J1+x#N28&L=XiiB7R(eLra5VVeir zHf~%sJsLKyUCjtir9;q%0wkmg)0qsf7e5gA^t@vfwo+tKa`bUOnR-uD({gBp)fn34 zqh8440{UZ2X02?s7zgs#$56|xKIu3i(&av(wQn0f^PNR#`;IgkCZXycxjMt~)G?}= zyjNDN_pkGEWO$Iif`R_D2}h^D7nRpZXDgX8rl*>G27O4Y7&+Mb3XM&EPk;^W-C9>E zGeQOl{+>}w6!W|%tULjj{?4b+HHS6%3p4oqFQi4y^oyL{ipq<&!Oq|b<)W>ug;B!s zj5jRwy#QL@@$9Dk_VyA81ONfZLj(;p5i2=@u9|WnmbU?Uyr41Zn=H>=+Xo`x!F4ab zlY_0pHM-X`4R_u&BmhH23DlP_Z*ODhWN_UbckOB3bsu+5*6x=narTb7AX&0g1KWnF zAzbZbkzWXQsZ*6l!BLGTY7kbr3iDeyc82FGyxhpyRmhoi!rpOL)>idjKd|bMIKpm( zK}7FV+271vre=IA6zOzyH8cPGGH=}V>+9GLNwfN*DaX#_%J2v@+ZZB(h*!wYkVNl{ zLNc#P-| zR5_2e3q&*O(W46K&`!Ceb(K^!7fH)KkFNMcsdX%bL0P-VYei>#tn_^Gv#MHIhTHLS z#jN=^^3`(c%T$+y6_hS$$o@@*lY|SjmIQhSwyUQOI)~zHtHM&lUK3!10anu#)4!S? ze_k(gn?ZsF;H!e}e7nx(5nunq@Mp8*U7hJ}h#Yi}=-sZ{20%6-%S|GX?rz~%vx&S7 z=!Yo3@dlUl2t3Rhs%`ss{v4DQRcKTk znG0gS5eF5=aA*6s58pg&S>Us~Q-l9s(PRc4z5fw32f6KfSM$5oV^pT{ryWtw_GV60 z#MF(%P@q-jw_maIK2y>f0}gTSGzz@fr4WQCw?NP?_gqk3Y4>^Tnc$hv$7nnRO)hqmuY4+=_PUWyVTjNU z$EA-qG`ju%i2SEo^E(I?i@ulswA$Md0L8!5TO=v(LesSh0p=qc=o} zMa`Q*P6os;8hxI=fkJwd+e*HPxXR?(ctMZEVV>&hvnlFywwrkl@aS%p9?jf8<}J~W zl+@x`h&PD@)VA_YzN1XpY3FgDk!i3>*}w$PmBQ<1DDA@BnjXe+WJs%Av45I(c>V$n49>^%n0K75>_5B5l} zW-jpn*3mLDKIg05pNv`bR(DFFDK$zc6WYy8K1M^PJ`nR?@UzSklZHetu74ah5ungD!yo~? zzhL|4Imhy=C2%2Nytd-(-Sk%|*Vl`W=PdzPGwuuI3dSv6lgBP>J*{%l0n z_}z(nDhuw+ zW`NGVVB7!>#h$5RI+4K+Uyx5isSH!d*NgG7ptlg_Vf@PJn=c=AuiCtw9!dAlUIr(T zY}H*pQJxxAcb((49(oG^!>gf6|J-Df(EIj7`Rj!qr_^m=vQFl}Opz-tAKB)!Ihw{o zm2Q0HFoTngoUJYn^k;xr zp3n*McH*=cH5ppbwkSjU-ud#LXb3O50~mj3D9m_pP)fE?@j*eFtG4>nE!$|mSC%K2 z>|}74PS@m6CMRJcN_WL^ephNcOFC?R39HZF`Ryd-ITB2bR2TJUW3m8+ebMUJXuHYjQ01J#XMaHfQdg6z7KDFQd zTZ!L(XT(JIOxLyWa-;hWgUVfWf^Sngdc{bssN?@!s>8kd)R@Re`J%clLR}I=O>`tV zwn-owvL4eX_SyD&Kb$4slXA(6D=AB|33)$CVcZC^cDpTHaxth>r{?Y)uwQLQ|Aun? zM=AavQ?Bnqz)2kG*6|<@UM(l`qLRRUc|Di7nS9X(`fnMUbQHV$3F**P>1x2u==zh0 z3QH1-+@KBtT&*6j>fq=$+5!M{d1jmNM_unBvIN`6+&E5xy|*kby|s-*{1pAI{s0?qx%CYqs~Onn{1aX^r+k zoB)8OH0-yFKM~a>4g^hURSmp++t0jE{exenOrAiss6X6Ari)nS9h39EX-sGoA)K#j zS)o~`;&2~92iaqyq)X`yEQ5$nxQG*`lhN<1&I^?UF_{tr1Ki;!&oncL7?G_M0Acz?0EmQ$1kC$^ANEZ;PA@m|jRj$qme$LFjcjlSr&Yi#KH z#g#i47aJ`&{ZstKLPH)86rfcVLlE)N;qS1su*4&im&3kTq|F)Db-pX}MrBFGUa-Y% z1gVZ0@0v`M&I@E~QgWzRtA>ZX&B@qS)UFXRexPC3ex!Ippp#GX?kK6Ln#P^*Riunb z*zxm&z`sqdU;h~X6>{w|Hb~u}3dg)scIiE~#N@nQv4gAxJP1JW2J-eTF>(V-pN@Ht zJxAtz-r6}j@B68-)kzq6jDDj9FodOShwIheU6pnZmqDRTb*U|n_xRE6QJ_1CMEeDd zx18DdGrNM9N|Hc5T3N6q(R#n6FHEwKDNmAhB7}>TiZxe&o$QNKBKRBR`e#S{kCAJy z=^J7bWBY;pp=t+}r*sC1;3!fUNX|)Kd>94_nsqAO&s+y=lM&rGcyYiy)`SeQ-0`(U zBII}4jUZ8064-W452~%GWxYDap&#SeL~f#a$GqusUOP!Yj)TkUnfkXKb!TF)t|SO( zgYVQ}%JV?_ru34n?9KO+X{f{0jcrdc{nwN0Uzxcd&!B5B9%}*3OA2l^aM2#xq$oY%Xxe#Wv*0bLYqU;eOfIaFz`V!x|Kuu_Vdyg}NS!nZR z1hRe-U;=gC#qD$66K)jaBVG57ltwnQaLJDb`=xBxkJ@HhsXp_R{tdGEvqH!}MmDdv7LW(6MKJAZT)hK$+kHFY{Z_JUCBl|2l`M(#K5Qk`^MzSeOmeq}QLEPw<2y3hT_t9M!4H z0;Y_V@|x1hpsU=AdTk}wB%CJ=Rnw#B>dw)PpBsj&Jaws^d)TXwP81Fv3Ksl;!FKj_ z{I4gQzcO<_$fmF-i`c&1Ab%TPPC=yG-&;-xT1aJKR-SRQ4l|e@2weC`R9;iK?Te;A zUO)pjd}SFZJY&j$l^`R$L-V4XovcT&X7r){dUx$%`=DqX&0?c^P}kcG8CD#JG1-Ex+YXy*xAuu2(h6Gg+T0u#i*-8$x7ZY( z#BwHVo+iI6|5iEt-suP*NT#VJ`D;PXX@$>s3GqiR({Dls{yXImhUJeD)9=$%6ewnx z7Z#{jz>Mh$C@hNN5a?g!^S07Hw-_X$(bWLIFc<#t!XRm|fX;FWJyxNoT|m-#gmRZM zvkil;V<;|sFTlVl2-n&f=;A4s?4>e)GfX#8p_=9e8zu0oWyTPeYt-|6v&e2ZVd#A+82@Xj8|B?iLWY_z(?17fJd?$~*&4rH_x|c@{GscgIUENmp4m2I&>L9v zm*FwU*oW^k8&0khR`UC+&D1;*i)IdKHkW66Tc=%R*~{T)Uk2gd6~IkudMBZMJmdW= zEw&m@lu_fpO!F1@N&NbrE-YJ!JZ()w0A$g%cQgOm!s1)C!!Ng* zKW?x=l5TEyEbV`JdM56*(Wtsf#?6At93A^@S3GbETCtnWxG%&YLF>@mE*BPE#OH|` zz{uX?*i0z7L?Isw`TLE0+v^MqVb!ms%7gha)Wwb6qu= zJy*+<)U$!tu18YJHNj){;&Nh4@=!OU)Jw*_oAzpv$#&@i;FvCYZoBZb(V$#?Rlur_ z=xcmAg~R6hYTU-Px>(-3LZX#PylDW@jFY4w)ZvVHo^}u>LxM=fuNY&~cdM)tFrmv%=Du~C6DE5Iv2*vlt~-KE&&n8tGdsHJQh8%9zsY_;F|!1FY*)`B z%=o1TCmECdyp-9(hWlv#1KsgZ*4ZN`hR=+?a&GGW+HSNt96I_Ql`0<$WwWp-4=0|r z$hI(;eu&sMV;k(aRR$2&2;2nk4=;7>96rrpxLJR2ULd1zJ*fq?7D`n?ag|Ci;(_W~ zbw^uR&oL#>I^$M3rvAYv_zWyp1ua0*kq(D%-^%Rfy$c#6q<*Dczym9@BAk}2DdBC$4Cb*Kd45Wt{{ZMe&ga)_(p9`G z8Mg1e@aQNnA!u{e;#Az108@_bi39nEWK2o68H!h|!r-x&xi{hC{g<#KV2xAUwQPyS zDN=1cgYn4StTs-lYNTM)D~AhDM;=qk(9gmx<*CV^4JbgRjE!>+8#3_^Htp~vo-|iN6ayE#ia0?6(d42a{)xLn z#Z`LGeKyV5>4Wx9(uC-T#+|e~S{y_=jp?}LU9ULmDC6PuMYY`~Si+ST_KUgYNA%Jd zq8At_P8*bSH~M*k8pD(aLnoAbbfCv`7ND0f%K~Q$(%M;!itSboSK<2=^xrgL`afCd zUrGL#u(qnzEqEGgdmcmF)HSsTn`l?Pzk9+0va(^=-1Lb{FszIanO;GYO=96k|qDS5%YZ?aUlkDC(ZXdF}37N0eB!n zx+mcl7ATAmTFYLton^H-9;Gzu`YaVnmy~k=J6F(j)gfrnM!_Y?REj8k?)j$&M2rPm zEltm{TKDzkR(5kA7CYg}1#fd6<5bb=j0l;>1VwC*heOmvfaRku$G64GsC@`RM)+)a zTikdo_1|B<&3L-8o`C}*|M0x{U;8Bdh)vS}=1u|08h%TMmeJBa4OBV@B>L@}Z%+IF zFrocVtq=;e$*0R2_6pJNdFv6r@aAC;%8{Vz3X9{g&4mQ>m?N61Swnf=)KnaXqoEbS zo~j7kJ<2pPFI+<~^)Q#w~<{?}s z%+}SQ6wE&OG~oH0$xjPO~^qggcQ>nFH)r@b>}{?c2R&`@RaVf$-e_dyd6-F){xrApAYR{VlKZopCAAd{`!+sYt?ClL{6D`1P!%V^gU-D38zP- z-F)We<#R_QUAy$A0aQqal!E*kvw<|oG_e*X9?=a-VD4~8ul^pVW-g*RS9l%C`z#%6 zvuda6nqKIo*z#OH%bN32(hT<-oK1&VvvgQN%CU;@7Y(8ktc1kHt}J-xdGXrvF=O@8 z3v9F#8B!QmUjwXCDnyUw+T)v%1nMiD;KT51-PFYKlx!H0tFFTuvboQ^KqHrH%6IPL z2tHKxi=jo29CotF+f$MdM;2#uG_mg58`-BWhyXVl)6G^S8=5VYoe%;+!eSp@@)U*N zb_9M9rpLb*uMc4}h30hIBkH4YWKFhhgmjds4Vg-mzni-P`oR)^2F)K)K|i7S)uG38 zN?D`z0%63th&_1@cBBIai#4lEX86K%nzYVSncKC{s*e74%ZefNowmelOpmKl5wBeo zrg=g1KBI$eVLVx3?NhKI?VVJG^`=CP2yMZr5}V)Xx^M>#qBI0 zpDUrv%a}8(^whv*6Eam;ws8*N5=^S2@LN6Iz-nNzKQp55bxvPBi9e6X^ZMDhMpI1w zP6Z!7mI4uMSB?9jb)Q0gNu_A%y`tAVyul)g4f`+aj+%Qv;Ux$fKgwcMo9zrjMccaH z$oK?(dJW!!JD8w>tB&21`f6WfPY@NaB)m#;XUPI34Z(dyXo!qs6S55jG7d*N-HM{0 zx&EPi>SbNbaBC>?GgK<{DQU&+NKbw9`Bi^YDPDAMnAmHgaYP*3mlqZ1#9 zXJM_asiUR+HJ<-R7R}kS)3`#n)3WDtK;qxMJX=A-Wo|w|d_f(|`Y_$uC#l2rhO0`v z9#rORt+Ry}%;>!pPRa%jO(h?h%Y>lUyYvr_W0T&iuZZ#a?L&M}+hcuq6qQBQIiVK= zg~`3WGLTEVs&3;+Hz;Z97yKmbK*--7ThmwBU4m9aL(YRA$~o zb|;MQWJ#@&=kn6V+WV6Pu>1NW`KZ;h78%qFmHWEV=&IU^`oq8kRLxOqMQl9