001    /*
002     * CPXInputStream.java
003     *
004     *
005     *  The Salamander Project - 2D and 3D graphics libraries in Java
006     *  Copyright (C) 2004 Mark McKay
007     *
008     *  This library is free software; you can redistribute it and/or
009     *  modify it under the terms of the GNU Lesser General Public
010     *  License as published by the Free Software Foundation; either
011     *  version 2.1 of the License, or (at your option) any later version.
012     *
013     *  This library is distributed in the hope that it will be useful,
014     *  but WITHOUT ANY WARRANTY; without even the implied warranty of
015     *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016     *  Lesser General Public License for more details.
017     *
018     *  You should have received a copy of the GNU Lesser General Public
019     *  License along with this library; if not, write to the Free Software
020     *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021     *
022     *  Mark McKay can be contacted at mark@kitfox.com.  Salamander and other
023     *  projects can be found at http://www.kitfox.com
024     *
025     * Created on February 12, 2004, 10:34 AM
026     */
027    
028    package com.kitfox.svg.xml.cpx;
029    
030    import java.io.*;
031    import java.util.*;
032    import java.util.zip.*;
033    import java.security.*;
034    import javax.crypto.*;
035    
036    /**
037     * This class reads/decodes the CPX file format.  This format is a simple
038     * compression/encryption transformer for XML data.  This stream takes in
039     * encrypted XML and outputs decrypted.  It does this by checking for a magic
040     * number at the start of the stream.  If absent, it treats the stream as
041     * raw XML data and passes it through unaltered.  This is to aid development
042     * in debugging versions, where the XML files will not be in CPX format.
043     *
044     * See http://java.sun.com/developer/technicalArticles/Security/Crypto/
045     *
046     * @author Mark McKay
047     * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
048     */
049    public class CPXInputStream extends FilterInputStream implements CPXConsts {
050    
051    
052        SecureRandom sec = new SecureRandom();
053    
054        Inflater inflater = new Inflater();
055    
056        int xlateMode;
057    
058        //Keep header bytes in case this stream turns out to be plain text
059        byte[] head = new byte[4];
060        int headSize = 0;
061        int headPtr = 0;
062    
063        boolean reachedEOF = false;
064        byte[] inBuffer = new byte[2048];
065        byte[] decryptBuffer = new byte[2048];
066    
067        /** Creates a new instance of CPXInputStream */
068        public CPXInputStream(InputStream in) throws IOException {
069            super(in);
070    
071            //Determine processing type
072            for (int i = 0; i < 4; i++)
073            {
074                int val = in.read();
075                head[i] = (byte)val;
076                if (val == -1 || head[i] != MAGIC_NUMBER[i])
077                {
078                    headSize = i + 1;
079                    xlateMode = XL_PLAIN;
080                    return;
081                }
082            }
083    
084            xlateMode = XL_ZIP_CRYPT;
085        }
086    
087        /**
088         * We do not allow marking
089         */
090        public boolean markSupported() { return false; }
091    
092        /**
093         * Closes this input stream and releases any system resources
094         * associated with the stream.
095         * This
096         * method simply performs <code>in.close()</code>.
097         *
098         * @exception  IOException  if an I/O error occurs.
099         * @see        java.io.FilterInputStream#in
100         */
101        public void close() throws IOException {
102            reachedEOF = true;
103            in.close();
104        }
105    
106        /**
107         * Reads the next byte of data from this input stream. The value
108         * byte is returned as an <code>int</code> in the range
109         * <code>0</code> to <code>255</code>. If no byte is available
110         * because the end of the stream has been reached, the value
111         * <code>-1</code> is returned. This method blocks until input data
112         * is available, the end of the stream is detected, or an exception
113         * is thrown.
114         * <p>
115         * This method
116         * simply performs <code>in.read()</code> and returns the result.
117         *
118         * @return     the next byte of data, or <code>-1</code> if the end of the
119         *             stream is reached.
120         * @exception  IOException  if an I/O error occurs.
121         * @see        java.io.FilterInputStream#in
122         */
123        public int read() throws IOException
124        {
125            final byte[] b = new byte[1];
126            int retVal = read(b, 0, 1);
127            if (retVal == -1) return -1;
128            return b[0];
129        }
130    
131        /**
132         * Reads up to <code>byte.length</code> bytes of data from this
133         * input stream into an array of bytes. This method blocks until some
134         * input is available.
135         * <p>
136         * This method simply performs the call
137         * <code>read(b, 0, b.length)</code> and returns
138         * the  result. It is important that it does
139         * <i>not</i> do <code>in.read(b)</code> instead;
140         * certain subclasses of  <code>FilterInputStream</code>
141         * depend on the implementation strategy actually
142         * used.
143         *
144         * @param      b   the buffer into which the data is read.
145         * @return     the total number of bytes read into the buffer, or
146         *             <code>-1</code> if there is no more data because the end of
147         *             the stream has been reached.
148         * @exception  IOException  if an I/O error occurs.
149         * @see        java.io.FilterInputStream#read(byte[], int, int)
150         */
151        public int read(byte[] b) throws IOException
152        {
153            return read(b, 0, b.length);
154        }
155    
156        /**
157         * Reads up to <code>len</code> bytes of data from this input stream
158         * into an array of bytes. This method blocks until some input is
159         * available.
160         * <p>
161         * This method simply performs <code>in.read(b, off, len)</code>
162         * and returns the result.
163         *
164         * @param      b     the buffer into which the data is read.
165         * @param      off   the start offset of the data.
166         * @param      len   the maximum number of bytes read.
167         * @return     the total number of bytes read into the buffer, or
168         *             <code>-1</code> if there is no more data because the end of
169         *             the stream has been reached.
170         * @exception  IOException  if an I/O error occurs.
171         * @see        java.io.FilterInputStream#in
172         */
173        public int read(byte[] b, int off, int len) throws IOException
174        {
175            if (reachedEOF) return -1;
176    
177            if (xlateMode == XL_PLAIN)
178            {
179                int count = 0;
180                //Write header if appropriate
181                while (headPtr < headSize && len > 0)
182                {
183                    b[off++] = head[headPtr++];
184                    count++;
185                    len--;
186                }
187    
188                return (len == 0) ? count : count + in.read(b, off, len);
189            }
190    
191            //Decrypt and inflate
192            if (inflater.needsInput() && !decryptChunk())
193            {
194                reachedEOF = true;
195    
196                //Read remaining bytes
197                int numRead;
198                try {
199                    numRead = inflater.inflate(b, off, len);
200                }
201                catch (Exception e)
202                {
203                    e.printStackTrace();
204                    return -1;
205                }
206    
207                if (!inflater.finished())
208                {
209                    new Exception("Inflation incomplete").printStackTrace();
210                }
211    
212                return numRead == 0 ? -1 : numRead;
213            }
214    
215            try {
216                return inflater.inflate(b, off, len);
217            }
218            catch (DataFormatException e)
219            {
220                e.printStackTrace();
221                return -1;
222            }
223        }
224    
225    
226        /**
227         * Call when inflater indicates that it needs more bytes.
228         * @return - true if we decrypted more bytes to deflate, false if we
229         * encountered the end of stream
230         */
231        protected boolean decryptChunk() throws IOException
232        {
233            while (inflater.needsInput())
234            {
235                int numInBytes = in.read(inBuffer);
236                if (numInBytes == -1) return false;
237    //            int numDecryptBytes = cipher.update(inBuffer, 0, numInBytes, decryptBuffer);
238    //            inflater.setInput(decryptBuffer, 0, numDecryptBytes);
239    inflater.setInput(inBuffer, 0, numInBytes);
240            }
241    
242            return true;
243        }
244    
245        /**
246         * This method returns 1 if we've not reached EOF, 0 if we have.  Programs
247         * should not rely on this to determine the number of bytes that can be
248         * read without blocking.
249         */
250        public int available() { return reachedEOF ? 0 : 1; }
251    
252        /**
253         * Skips bytes by reading them into a cached buffer
254         */
255        public long skip(long n) throws IOException
256        {
257            int skipSize = (int)n;
258            if (skipSize > inBuffer.length) skipSize = inBuffer.length;
259            return read(inBuffer, 0, skipSize);
260        }
261    
262    }
263    
264    /*
265     import java.security.KeyPairGenerator;
266      import java.security.KeyPair;
267      import java.security.KeyPairGenerator;
268      import java.security.PrivateKey;
269      import java.security.PublicKey;
270      import java.security.SecureRandom;
271      import java.security.Cipher;
272    
273      ....
274    
275      java.security.Security.addProvider(new cryptix.provider.Cryptix());
276    
277      SecureRandom random = new SecureRandom(SecureRandom.getSeed(30));
278      KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA");
279      keygen.initialize(1024, random);
280      keypair = keygen.generateKeyPair();
281    
282      PublicKey  pubkey  = keypair.getPublic();
283      PrivateKey privkey = keypair.getPrivate();
284     */
285    
286    /*
287     *
288     *Generate key pairs
289    KeyPairGenerator keyGen =
290                 KeyPairGenerator.getInstance("DSA");
291    KeyGen.initialize(1024, new SecureRandom(userSeed));
292    KeyPair pair = KeyGen.generateKeyPair();
293     */