📄 protocoldecoderphe.java
字号:
/*
* Created on 17-Jan-2006
* Created by Paul Gardner
* Copyright (C) 2006 Aelitis, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* AELITIS, SAS au capital de 46,603.30 euros
* 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
*
*/
package com.aelitis.azureus.core.networkmanager.impl;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.util.*;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.DHPublicKeySpec;
import javax.crypto.spec.SecretKeySpec;
import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.config.ParameterListener;
import org.gudy.azureus2.core3.logging.LogAlert;
import org.gudy.azureus2.core3.logging.LogEvent;
import org.gudy.azureus2.core3.logging.LogIDs;
import org.gudy.azureus2.core3.logging.Logger;
import org.gudy.azureus2.core3.util.AEMonitor;
import org.gudy.azureus2.core3.util.ByteFormatter;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.HashWrapper;
import org.gudy.azureus2.core3.util.SHA1Hasher;
import org.gudy.azureus2.core3.util.SystemTime;
import com.aelitis.azureus.core.networkmanager.NetworkManager;
import com.aelitis.azureus.core.util.bloom.BloomFilter;
import com.aelitis.azureus.core.util.bloom.BloomFilterFactory;
public class
ProtocolDecoderPHE
extends ProtocolDecoder
{
private static final LogIDs LOGID = LogIDs.NWMAN;
private static final byte CRYPTO_PLAIN = 0x01;
private static final byte CRYPTO_RC4 = 0x02;
private static final byte CRYPTO_XOR = 0x04;
private static final byte CRYPTO_AES = 0x08;
//private static final String DH_P = "92d862b3a95bff4e6cbdce3a266ff4b46e6e1ecad76c0a877d92a3dae4999e6414efde56fc14d1cca6d5408a8ef9ea248389168876b6e8f4503845dfe373549f";
//private static final String DH_G = "4383b53ee650fd73e41e8c9e8527997ab8cb41e1cbd73ac7685493e1e5d091e3e3789dea03ab9d5b2c368faa617bb30e427cbaeb23c268edb38eb8c747756080";
// private static final String DH_P = "f3f90c790c63b119f9c1be43fdb12dc6ed6f26325999c01ba6ed373e75d6b2dee8d1c0475652a987c8df57b23d395bdb142be316d780b9361f85629535030873";
private static final String DH_P = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A63A36210000000000090563";
private static final String DH_G = "02";
private static final int DH_L = 160;
private static final int DH_SIZE_BYTES = DH_P.length()/2;
public static final int MIN_INCOMING_INITIAL_PACKET_SIZE = DH_SIZE_BYTES;
private static final BigInteger DH_P_BI = new BigInteger( DH_P, 16 );
private static final BigInteger DH_G_BI = new BigInteger( DH_G, 16 );
private static KeyPairGenerator dh_key_generator;
private static long last_dh_incoming_key_generate;
private static final int BLOOM_RECREATE = 30*1000;
private static final int BLOOM_INCREASE = 1000;
private static BloomFilter generate_bloom = BloomFilterFactory.createAddRemove4Bit(BLOOM_INCREASE);
private static long generate_bloom_create_time = SystemTime.getCurrentTime();
private static boolean crypto_ok;
//private static boolean aes_ok;
/*
private static final String AES_STREAM_ALG = "AES";
private static final String AES_STREAM_CIPHER = "AES/CFB8/NoPadding";
private static final int AES_STREAM_KEY_SIZE = 128;
private static final int AES_STREAM_KEY_SIZE_BYTES = AES_STREAM_KEY_SIZE/8;
*/
//private static final byte[] AES_STREAM_IV =
// { (byte)0x15, (byte)0xE0, (byte)0x6B, (byte)0x7E, (byte)0x98, (byte)0x59, (byte)0xE4, (byte)0xA7,
// (byte)0x34, (byte)0x66, (byte)0xAD, (byte)0x48, (byte)0x35, (byte)0xE2, (byte)0xD0, (byte)0x24 };
private static final String RC4_STREAM_ALG = "RC4";
private static final String RC4_STREAM_CIPHER = "RC4";
private static final int RC4_STREAM_KEY_SIZE = 128;
private static final int RC4_STREAM_KEY_SIZE_BYTES = RC4_STREAM_KEY_SIZE/8;
private static final int PADDING_MAX = 512;
private static final int PADDING_MAX_NORMAL = PADDING_MAX;
private static final int PADDING_MAX_LIMITED = 128;
public static int
getMaxIncomingInitialPacketSize(
boolean min_overheads )
{
return( MIN_INCOMING_INITIAL_PACKET_SIZE + (min_overheads?PADDING_MAX_LIMITED:PADDING_MAX_NORMAL)/2 );
}
private static final Random random = new SecureRandom();
private static Map global_shared_secrets = new HashMap();
static{
try{
DHParameterSpec dh_param_spec = new DHParameterSpec( DH_P_BI, DH_G_BI, DH_L );
dh_key_generator = KeyPairGenerator.getInstance("DH");
dh_key_generator.initialize(dh_param_spec);
dh_key_generator.generateKeyPair();
byte[] rc4_test_secret = new byte[RC4_STREAM_KEY_SIZE_BYTES];
SecretKeySpec rc4_test_secret_key_spec = new SecretKeySpec(rc4_test_secret, 0, RC4_STREAM_KEY_SIZE_BYTES, RC4_STREAM_ALG );
TransportCipher rc4_cipher = new TransportCipher( RC4_STREAM_CIPHER, Cipher.ENCRYPT_MODE, rc4_test_secret_key_spec );
rc4_cipher = new TransportCipher( RC4_STREAM_CIPHER, Cipher.DECRYPT_MODE, rc4_test_secret_key_spec );
/*
try{
byte[] aes_test_secret = new byte[AES_STREAM_KEY_SIZE_BYTES];
SecretKeySpec aes_test_secret_key_spec = new SecretKeySpec(aes_test_secret, 0, AES_STREAM_KEY_SIZE_BYTES, AES_STREAM_ALG );
AlgorithmParameterSpec spec = new IvParameterSpec( aes_test_secret );
TCPTransportCipher aes_cipher = new TCPTransportCipher( AES_STREAM_CIPHER, Cipher.ENCRYPT_MODE, aes_test_secret_key_spec, spec );
aes_cipher = new TCPTransportCipher( AES_STREAM_CIPHER, Cipher.DECRYPT_MODE, aes_test_secret_key_spec, spec );
aes_ok = true;
}catch( Throwable e ){
Logger.log( new LogEvent(LOGID, "AES Unavailable", e ));
}
*/
crypto_ok = true;
if (Logger.isEnabled()){
Logger.log( new LogEvent(LOGID, "PHE crypto initialised" ));
}
}catch( NoClassDefFoundError e ){
// running without PHE classes, not such a severe error
Logger.log( new LogEvent(LOGID, "PHE crypto disabled as classes unavailable" ));
crypto_ok = false;
}catch( Throwable e ){
Logger.log( new LogEvent(LOGID, "PHE crypto initialisation failed", e ));
crypto_ok = false;
}
}
public static boolean
isCryptoOK()
{
return( crypto_ok );
}
public static void
addSecretsSupport(
byte[][] secrets )
{
for (int i=0;i<secrets.length;i++){
SHA1Hasher hasher = new SHA1Hasher();
hasher.update( REQ2_IV );
hasher.update( secrets[i] );
byte[] encoded = hasher.getDigest();
synchronized( global_shared_secrets ){
global_shared_secrets.put( new HashWrapper( encoded ), secrets[i] );
}
}
}
public static void
removeSecretsSupport(
byte[][] secrets )
{
for (int i=0;i<secrets.length;i++){
SHA1Hasher hasher = new SHA1Hasher();
hasher.update( REQ2_IV );
hasher.update( secrets[i] );
byte[] encoded = hasher.getDigest();
synchronized( global_shared_secrets ){
global_shared_secrets.remove( new HashWrapper( encoded ));
}
}
}
// private static final byte SUPPORTED_PROTOCOLS = (byte)((aes_ok?CRYPTO_AES:0) | CRYPTO_RC4 | CRYPTO_XOR | CRYPTO_PLAIN );
private static final byte SUPPORTED_PROTOCOLS = (byte)(CRYPTO_RC4 | CRYPTO_PLAIN );
private static byte MIN_CRYPTO;
static{
COConfigurationManager.addAndFireParameterListeners(
new String[]{ "network.transport.encrypted.min_level" },
new ParameterListener()
{
public void
parameterChanged(
String ignore )
{
if ( NetworkManager.REQUIRE_CRYPTO_HANDSHAKE && !isCryptoOK() ){
Logger.log( new LogAlert(true,LogAlert.AT_ERROR,"Connection encryption unavailable, please update your Java version" ));
}
String min = COConfigurationManager.getStringParameter( "network.transport.encrypted.min_level");
if ( min.equals( "XOR" )){
MIN_CRYPTO = CRYPTO_XOR | CRYPTO_RC4 | CRYPTO_AES;
}else if ( min.equals( "RC4" )){
MIN_CRYPTO = CRYPTO_RC4 | CRYPTO_AES;
}else if ( min.equals( "AES" )){
MIN_CRYPTO = CRYPTO_AES;
}else{
MIN_CRYPTO = CRYPTO_PLAIN | CRYPTO_XOR | CRYPTO_RC4 | CRYPTO_AES;
}
MIN_CRYPTO = (byte)(MIN_CRYPTO & SUPPORTED_PROTOCOLS);
}
});
}
private static final int PS_OUTBOUND_1 = 0;
private static final int PS_OUTBOUND_2 = 1;
private static final int PS_OUTBOUND_3 = 2;
private static final int PS_OUTBOUND_4 = 3;
private static final int PS_INBOUND_1 = 10;
private static final int PS_INBOUND_2 = 11;
private static final int PS_INBOUND_3 = 12;
private static final int PS_INBOUND_4 = 13;
public static final byte[] KEYA_IV = "keyA".getBytes();
public static final byte[] KEYB_IV = "keyB".getBytes();
public static final byte[] REQ1_IV = "req1".getBytes();
public static final byte[] REQ2_IV = "req2".getBytes();
public static final byte[] REQ3_IV = "req3".getBytes();
public static final byte[] VC = { 0,0,0,0,0,0,0,0};
private TransportHelper transport;
private ByteBuffer write_buffer;
private ByteBuffer read_buffer;
private ProtocolDecoderAdapter adapter;
private KeyAgreement key_agreement;
private byte[] dh_public_key_bytes;
private byte[] shared_secret;
private byte[] secret_bytes;
private ByteBuffer initial_data_out;
private int initial_data_out_len;
private int initial_data_in_len;
private TransportCipher write_cipher;
private TransportCipher read_cipher;
private byte[] padding_skip_marker;
private byte my_supported_protocols;
private byte selected_protocol;
private boolean outbound;
private int protocol_state;
private int protocol_substate;
private boolean handshake_complete;
private int bytes_read;
private int bytes_written;
private long last_read_time = SystemTime.getCurrentTime();
private TransportHelperFilter filter;
private boolean processing_complete;
private AEMonitor process_mon = new AEMonitor( "ProtocolDecoderPHE:process" );
public
ProtocolDecoderPHE(
TransportHelper _transport,
byte[][] _shared_secrets,
ByteBuffer _header,
ByteBuffer _initial_data,
ProtocolDecoderAdapter _adapter )
throws IOException
{
super( false );
if ( !isCryptoOK()){
throw( new IOException( "PHE crypto broken" ));
}
transport = _transport;
initial_data_out = _initial_data;
adapter = _adapter;
if ( _shared_secrets == null || _shared_secrets.length == 0 ){
shared_secret = new byte[0];
}else{
if ( _shared_secrets.length == 1 ){
shared_secret = _shared_secrets[0];
}else{
shared_secret = _shared_secrets[random.nextInt( _shared_secrets.length )];
}
// System.out.println( "outbound - using crypto secret " + ByteFormatter.encodeString( shared_secret ));
}
outbound = _header == null;
if ( outbound ){
initial_data_out_len = initial_data_out==null?0:initial_data_out.remaining();
}
my_supported_protocols = SUPPORTED_PROTOCOLS;
if ( outbound ){
//if ( !NetworkManager.REQUIRE_CRYPTO_HANDSHAKE ){
// throw( new IOException( "Crypto encoder selected for outbound but crypto not required" ));
//}
// outbound connection, we require a certain minimal level of support
my_supported_protocols = MIN_CRYPTO;
}else{
// incoming. If we require crypto then we use minimum otherwise available
if ( NetworkManager.REQUIRE_CRYPTO_HANDSHAKE ){
my_supported_protocols = MIN_CRYPTO;
}
}
initCrypto();
try{
process_mon.enter();
transport.registerForReadSelects(
new TransportHelper.selectListener()
{
public boolean
selectSuccess(
TransportHelper helper,
Object attachment )
{
return( ProtocolDecoderPHE.this.selectSuccess( helper, attachment, false ));
}
public void
selectFailure(
TransportHelper helper,
Object attachment,
Throwable msg)
{
ProtocolDecoderPHE.this.selectFailure( helper, attachment, msg );
}
},
null );
transport.registerForWriteSelects(
new TransportHelper.selectListener()
{
public boolean
selectSuccess(
TransportHelper helper,
Object attachment )
{
return( ProtocolDecoderPHE.this.selectSuccess( helper, attachment, true ));
}
public void
selectFailure(
TransportHelper helper,
Object attachment,
Throwable msg)
{
ProtocolDecoderPHE.this.selectFailure( helper, attachment, msg );
}
},
null );
transport.pauseWriteSelects();
if ( outbound ){
protocol_state = PS_OUTBOUND_1;
transport.pauseReadSelects();
}else{
protocol_state = PS_INBOUND_1;
read_buffer = ByteBuffer.allocate( dh_public_key_bytes.length );
read_buffer.put( _header );
bytes_read += _header.limit();
}
}finally{
process_mon.exit();
}
process();
}
protected void
initCrypto()
throws IOException
{
try{
KeyPair key_pair = generateDHKeyPair( transport, outbound );
key_agreement = KeyAgreement.getInstance("DH");
key_agreement.init(key_pair.getPrivate());
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -