aboutsummaryrefslogtreecommitdiffstats
path: root/lib/src/main/java/com/trilead/ssh2/crypto/cipher/CipherInputStream.java
blob: c9055ab5548b369d7996005fd6263fd3a9e422b7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package com.trilead.ssh2.crypto.cipher;

import java.io.IOException;
import java.io.InputStream;

/**
 * CipherInputStream.
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: CipherInputStream.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
 */
public class CipherInputStream
{
	BlockCipher currentCipher;
	InputStream bi;
	byte[] buffer;
	byte[] enc;
	int blockSize;
	int pos;

	/*
	 * We cannot use java.io.BufferedInputStream, since that is not available in
	 * J2ME. Everything could be improved alot here.
	 */

	final int BUFF_SIZE = 2048;
	byte[] input_buffer = new byte[BUFF_SIZE];
	int input_buffer_pos = 0;
	int input_buffer_size = 0;

	public CipherInputStream(BlockCipher tc, InputStream bi)
	{
		this.bi = bi;
		changeCipher(tc);
	}

	private int fill_buffer() throws IOException
	{
		input_buffer_pos = 0;
		input_buffer_size = bi.read(input_buffer, 0, BUFF_SIZE);
		return input_buffer_size;
	}

	private int internal_read(byte[] b, int off, int len) throws IOException
	{
		if (input_buffer_size < 0)
			return -1;

		if (input_buffer_pos >= input_buffer_size)
		{
			if (fill_buffer() <= 0)
				return -1;
		}
		
		int avail = input_buffer_size - input_buffer_pos;
		int thiscopy = (len > avail) ? avail : len;

		System.arraycopy(input_buffer, input_buffer_pos, b, off, thiscopy);
		input_buffer_pos += thiscopy;

		return thiscopy;
	}

	public void changeCipher(BlockCipher bc)
	{
		this.currentCipher = bc;
		blockSize = bc.getBlockSize();
		buffer = new byte[blockSize];
		enc = new byte[blockSize];
		pos = blockSize;
	}

	private void getBlock() throws IOException
	{
		int n = 0;
		while (n < blockSize)
		{
			int len = internal_read(enc, n, blockSize - n);
			if (len < 0)
				throw new IOException("Cannot read full block, EOF reached.");
			n += len;
		}

		try
		{
			currentCipher.transformBlock(enc, 0, buffer, 0);
		}
		catch (Exception e)
		{
			throw new IOException("Error while decrypting block.");
		}
		pos = 0;
	}

	public int read(byte[] dst) throws IOException
	{
		return read(dst, 0, dst.length);
	}

	public int read(byte[] dst, int off, int len) throws IOException
	{
		int count = 0;

		while (len > 0)
		{
			if (pos >= blockSize)
				getBlock();

			int avail = blockSize - pos;
			int copy = Math.min(avail, len);
			System.arraycopy(buffer, pos, dst, off, copy);
			pos += copy;
			off += copy;
			len -= copy;
			count += copy;
		}
		return count;
	}

	public int read() throws IOException
	{
		if (pos >= blockSize)
		{
			getBlock();
		}
		return buffer[pos++] & 0xff;
	}

	public int readPlain(byte[] b, int off, int len) throws IOException
	{
		if (pos != blockSize)
			throw new IOException("Cannot read plain since crypto buffer is not aligned.");
		int n = 0;
		while (n < len)
		{
			int cnt = internal_read(b, off + n, len - n);
			if (cnt < 0)
				throw new IOException("Cannot fill buffer, EOF reached.");
			n += cnt;
		}
		return n;
	}
}