1/*
2 * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
3 * Please refer to the LICENSE.txt for licensing details.
4 */
5package ch.ethz.ssh2.crypto.cipher;
6
7import java.io.IOException;
8import java.io.InputStream;
9
10/**
11 * CipherInputStream.
12 *
13 * @author Christian Plattner
14 * @version $Id: CipherInputStream.java 11 2011-05-27 14:14:06Z dkocher@sudo.ch $
15 */
16public class CipherInputStream
17{
18	BlockCipher currentCipher;
19	InputStream bi;
20	byte[] buffer;
21	byte[] enc;
22	int blockSize;
23	int pos;
24
25	/*
26	 * We cannot use java.io.BufferedInputStream, since that is not available in
27	 * J2ME. Everything could be improved alot here.
28	 */
29
30	private static final int BUFF_SIZE = 8192;
31	byte[] input_buffer = new byte[BUFF_SIZE];
32	int input_buffer_pos = 0;
33	int input_buffer_size = 0;
34
35	public CipherInputStream(BlockCipher tc, InputStream bi)
36	{
37		this.bi = bi;
38		changeCipher(tc);
39	}
40
41	private int fill_buffer() throws IOException
42	{
43		input_buffer_pos = 0;
44		input_buffer_size = 0;
45		input_buffer_size = bi.read(input_buffer, 0, BUFF_SIZE);
46		return input_buffer_size;
47	}
48
49	private int internal_read(byte[] b, int off, int len) throws IOException
50	{
51		if (input_buffer_size < 0)
52		{
53			return -1;
54		}
55
56		if (input_buffer_pos >= input_buffer_size)
57		{
58			if (fill_buffer() <= 0)
59			{
60				return -1;
61			}
62		}
63
64		int avail = input_buffer_size - input_buffer_pos;
65		int thiscopy = (len > avail) ? avail : len;
66
67		System.arraycopy(input_buffer, input_buffer_pos, b, off, thiscopy);
68		input_buffer_pos += thiscopy;
69
70		return thiscopy;
71	}
72
73	public void changeCipher(BlockCipher bc)
74	{
75		this.currentCipher = bc;
76		blockSize = bc.getBlockSize();
77		buffer = new byte[blockSize];
78		enc = new byte[blockSize];
79		pos = blockSize;
80	}
81
82	private void getBlock() throws IOException
83	{
84		int n = 0;
85		while (n < blockSize)
86		{
87			int len = internal_read(enc, n, blockSize - n);
88			if (len < 0)
89			{
90				throw new IOException("Cannot read full block, EOF reached.");
91			}
92			n += len;
93		}
94
95		try
96		{
97			currentCipher.transformBlock(enc, 0, buffer, 0);
98		}
99		catch (Exception e)
100		{
101			throw new IOException("Error while decrypting block.");
102		}
103		pos = 0;
104	}
105
106	public int read(byte[] dst) throws IOException
107	{
108		return read(dst, 0, dst.length);
109	}
110
111	public int read(byte[] dst, int off, int len) throws IOException
112	{
113		int count = 0;
114
115		while (len > 0)
116		{
117			if (pos >= blockSize)
118			{
119				getBlock();
120			}
121
122			int avail = blockSize - pos;
123			int copy = Math.min(avail, len);
124			System.arraycopy(buffer, pos, dst, off, copy);
125			pos += copy;
126			off += copy;
127			len -= copy;
128			count += copy;
129		}
130		return count;
131	}
132
133	public int read() throws IOException
134	{
135		if (pos >= blockSize)
136		{
137			getBlock();
138		}
139		return buffer[pos++] & 0xff;
140	}
141
142	public int readPlain(byte[] b, int off, int len) throws IOException
143	{
144		if (pos != blockSize)
145		{
146			throw new IOException("Cannot read plain since crypto buffer is not aligned.");
147		}
148		int n = 0;
149		while (n < len)
150		{
151			int cnt = internal_read(b, off + n, len - n);
152			if (cnt < 0)
153			{
154				throw new IOException("Cannot fill buffer, EOF reached.");
155			}
156			n += cnt;
157		}
158		return n;
159	}
160}
161