1 /*
2  * Created on 19-Jan-2006
3  * Created by Paul Gardner
4  * Copyright (C) Azureus Software, Inc, All Rights Reserved.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17  *
18  */
19 
20 package com.aelitis.azureus.core.networkmanager.impl;
21 
22 import java.io.IOException;
23 import java.nio.ByteBuffer;
24 import java.security.spec.AlgorithmParameterSpec;
25 
26 import javax.crypto.Cipher;
27 import javax.crypto.spec.SecretKeySpec;
28 
29 import org.gudy.azureus2.core3.util.Debug;
30 import org.gudy.bouncycastle.crypto.CipherParameters;
31 import org.gudy.bouncycastle.crypto.engines.RC4Engine;
32 import org.gudy.bouncycastle.crypto.params.KeyParameter;
33 
34 public class
35 TransportCipher
36 {
37 	private static boolean	internal_rc4	= true;	// force internal as we want 160 bit and JCE no supports it
38 
39 	private Cipher		cipher;
40 	private RC4Engine	rc4_engine;
41 
42 	public
TransportCipher( String algorithm, int mode, SecretKeySpec key_spec, AlgorithmParameterSpec params )43 	TransportCipher(
44 		String					algorithm,
45 		int						mode,
46 		SecretKeySpec			key_spec,
47 		AlgorithmParameterSpec	params )
48 
49 		throws Exception
50 	{
51     	cipher = Cipher.getInstance( algorithm );
52 
53     	cipher.init( mode, key_spec, params );
54 	}
55 
TransportCipher( String algorithm, int mode, SecretKeySpec key_spec )56 	TransportCipher(
57 		String					algorithm,
58 		int						mode,
59 		SecretKeySpec			key_spec )
60 
61 		throws Exception
62 	{
63 	    if ( algorithm.equals( "RC4" )){
64 
65 	    	if ( !internal_rc4 ){
66 
67 	    		try{
68 	    	    	cipher = Cipher.getInstance( algorithm );
69 
70 	    	    	cipher.init( mode, key_spec );
71 
72 	    		}catch( Throwable e ){
73 
74 	    			internal_rc4	= true;
75 	    		}
76 	    	}
77 
78 	    	if ( internal_rc4 ){
79 
80 	    		rc4_engine	= new RC4Engine();
81 
82 	    		CipherParameters	params = new KeyParameter(key_spec.getEncoded());
83 
84 	    		rc4_engine.init( mode == Cipher.ENCRYPT_MODE, params );
85 	    	}
86 
87 	    	//System.out.println( "RC4 key: " + ByteFormatter.encodeString( key_spec.getEncoded()));
88 
89     			// skip first 1024 bytes of stream to protected against a Fluhrer, Mantin and Shamir attack
90 
91 	    	byte[]	temp = new byte[1024];
92 
93 	    	temp = update( temp );
94 
95 	    	//System.out.println( "RC4: first discard = " + ByteFormatter.encodeString( temp, 0, 4 ));
96 	    }else{
97 
98 	    	cipher = Cipher.getInstance( algorithm );
99 
100 	    	cipher.init( mode, key_spec );
101 	    }
102 	}
103 
104 	protected byte[]
update( byte[] data )105 	update(
106 		byte[]	data )
107 	{
108 		return( update( data, 0, data.length ));
109 	}
110 
111 	protected byte[]
update( byte[] data, int offset, int length )112    	update(
113    		byte[]	data,
114    		int		offset,
115    		int		length )
116    	{
117 		byte[]	result;
118 
119 		if ( length == 0 ){
120 
121 				// watch out, cipher.update returns NULL with 0 length input
122 
123 			result = new byte[0];
124 
125 		}else if ( cipher != null ){
126 
127 			result = cipher.update( data, offset, length );
128 
129 		}else{
130 
131 			result = new byte[length];
132 
133 			rc4_engine.processBytes( data, offset, length, result, 0 );
134 		}
135 
136 		return( result );
137    	}
138 
139 	protected void
update( ByteBuffer source_buffer, ByteBuffer target_buffer )140 	update(
141 		ByteBuffer	source_buffer,
142 		ByteBuffer	target_buffer )
143 
144 		throws IOException
145 	{
146 		try{
147 			// TODO: 1.5 supports update( ByteBuffer, ByteBuffer )
148 
149 			byte[]	source_bytes;
150 			int		offset;
151 			int		length	= source_buffer.remaining();
152 
153 			if ( source_buffer.hasArray()){
154 
155 				source_bytes 	= source_buffer.array();
156 
157 				offset			= source_buffer.arrayOffset() + source_buffer.position();
158 
159 			}else{
160 
161 				source_bytes 	= new byte[length];
162 
163 				offset			= 0;
164 
165 				source_buffer.get( source_bytes );
166 			}
167 
168 			byte[]	target_bytes = update( source_bytes, offset, length );
169 
170 			source_buffer.position( source_buffer.limit());
171 
172 			target_buffer.put( target_bytes );
173 
174 		}catch( Throwable e ){
175 
176 			throw( new IOException( Debug.getNestedExceptionMessage( e )));
177 		}
178 	}
179 
180 	public String
getName()181 	getName()
182 	{
183 		if ( cipher != null ){
184 
185 			String	s = cipher.getAlgorithm();
186 
187 			int	pos = s.indexOf("/");
188 
189 			if ( pos != -1 ){
190 
191 				s = s.substring(0,pos);
192 			}
193 
194 			if ( s.equals( "RC4" )){
195 
196 				s = "RC4-160";
197 
198 			}else{
199 
200 				s += "-" + cipher.getBlockSize()*8;
201 			}
202 
203 			return( s );
204 		}else{
205 
206 			return( "RC4-160" );
207 		}
208 	}
209 }
210