aboutsummaryrefslogtreecommitdiffstats
path: root/lib/src/main/java/com/trilead/ssh2/crypto/cipher/CipherOutputStream.java
blob: 9db88c262905f7e98fc5040063e047f204873ca0 (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
package com.trilead.ssh2.crypto.cipher;

import java.io.IOException;
import java.io.OutputStream;

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

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

	final int BUFF_SIZE = 2048;
	byte[] out_buffer = new byte[BUFF_SIZE];
	int out_buffer_pos = 0;

	public CipherOutputStream(BlockCipher tc, OutputStream bo)
	{
		this.bo = bo;
		changeCipher(tc);
	}

	private void internal_write(byte[] src, int off, int len) throws IOException
	{
		while (len > 0)
		{
			int space = BUFF_SIZE - out_buffer_pos;
			int copy = (len > space) ? space : len;

			System.arraycopy(src, off, out_buffer, out_buffer_pos, copy);

			off += copy;
			out_buffer_pos += copy;
			len -= copy;

			if (out_buffer_pos >= BUFF_SIZE)
			{
				bo.write(out_buffer, 0, BUFF_SIZE);
				out_buffer_pos = 0;
			}
		}
	}

	private void internal_write(int b) throws IOException
	{
		out_buffer[out_buffer_pos++] = (byte) b;
		if (out_buffer_pos >= BUFF_SIZE)
		{
			bo.write(out_buffer, 0, BUFF_SIZE);
			out_buffer_pos = 0;
		}
	}

	public void flush() throws IOException
	{
		if (pos != 0)
			throw new IOException("FATAL: cannot flush since crypto buffer is not aligned.");

		if (out_buffer_pos > 0)
		{
			bo.write(out_buffer, 0, out_buffer_pos);
			out_buffer_pos = 0;
		}
		bo.flush();
	}

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

	private void writeBlock() throws IOException
	{
		try
		{
			currentCipher.transformBlock(buffer, 0, enc, 0);
		}
		catch (Exception e)
		{
			throw (IOException) new IOException("Error while decrypting block.").initCause(e);
		}

		internal_write(enc, 0, blockSize);
		pos = 0;
	}

	public void write(byte[] src, int off, int len) throws IOException
	{
		while (len > 0)
		{
			int avail = blockSize - pos;
			int copy = Math.min(avail, len);

			System.arraycopy(src, off, buffer, pos, copy);
			pos += copy;
			off += copy;
			len -= copy;

			if (pos >= blockSize)
				writeBlock();
		}
	}

	public void write(int b) throws IOException
	{
		buffer[pos++] = (byte) b;
		if (pos >= blockSize)
			writeBlock();
	}

	public void writePlain(int b) throws IOException
	{
		if (pos != 0)
			throw new IOException("Cannot write plain since crypto buffer is not aligned.");
		internal_write(b);
	}

	public void writePlain(byte[] b, int off, int len) throws IOException
	{
		if (pos != 0)
			throw new IOException("Cannot write plain since crypto buffer is not aligned.");
		internal_write(b, off, len);
	}
}