ProtocolDecoderPHEpublic class ProtocolDecoderPHE extends ProtocolDecoder
Fields Summary |
---|
private static final org.gudy.azureus2.core3.logging.LogIDs | LOGID | private static final byte | CRYPTO_PLAIN | private static final byte | CRYPTO_RC4 | private static final byte | CRYPTO_XOR | private static final byte | CRYPTO_AES | private static final String | DH_P | private static final String | DH_G | private static final int | DH_L | private static final int | DH_SIZE_BYTES | public static final int | MIN_INCOMING_INITIAL_PACKET_SIZE | private static final BigInteger | DH_P_BI | private static final BigInteger | DH_G_BI | private static KeyPairGenerator | dh_key_generator | private static long | last_dh_incoming_key_generate | private static final int | BLOOM_RECREATE | private static final int | BLOOM_INCREASE | private static com.aelitis.azureus.core.util.bloom.BloomFilter | generate_bloom | private static long | generate_bloom_create_time | private static boolean | crypto_ok | private static final String | RC4_STREAM_ALG | private static final String | RC4_STREAM_CIPHER | private static final int | RC4_STREAM_KEY_SIZE | private static final int | RC4_STREAM_KEY_SIZE_BYTES | private static final int | PADDING_MAX | private static final int | PADDING_MAX_NORMAL | private static final int | PADDING_MAX_LIMITED | private static final Random | random | private static Map | global_shared_secrets | private static final byte | SUPPORTED_PROTOCOLS | private static byte | MIN_CRYPTO | private static final int | PS_OUTBOUND_1 | private static final int | PS_OUTBOUND_2 | private static final int | PS_OUTBOUND_3 | private static final int | PS_OUTBOUND_4 | private static final int | PS_INBOUND_1 | private static final int | PS_INBOUND_2 | private static final int | PS_INBOUND_3 | private static final int | PS_INBOUND_4 | public static final byte[] | KEYA_IV | public static final byte[] | KEYB_IV | public static final byte[] | REQ1_IV | public static final byte[] | REQ2_IV | public static final byte[] | REQ3_IV | public static final byte[] | VC | 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 ByteBuffer | initial_data_in | 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 | private TransportHelperFilter | filter | private boolean | delay_outbound_4 | private boolean | processing_complete | private org.gudy.azureus2.core3.util.AEMonitor | process_mon |
Constructors Summary |
---|
public ProtocolDecoderPHE(TransportHelper _transport, byte[] _shared_secrets, ByteBuffer _header, ByteBuffer _initial_data, ProtocolDecoderAdapter _adapter)
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;
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();
|
Methods Summary |
---|
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] );
}
}
| protected byte[] | bigIntegerToBytes(java.math.BigInteger bi, int num_bytes)
String str = bi.toString(16);
while( str.length() < num_bytes*2 ){
str = "0" + str;
}
return( ByteFormatter.decodeString(str));
| protected java.math.BigInteger | bytesToBigInteger(byte[] bytes, int offset, int len)
return( new BigInteger( ByteFormatter.encodeString( bytes, offset, len ), 16 ));
| protected void | complete()
// System.out.println( (outbound?"out: ":"in :") + " complete, r " + bytes_read + ", w " + bytes_written + ", initial data = " + initial_data_in.length + "/" + initial_data_out.length );
processing_complete = true;
adapter.decodeComplete( this, initial_data_out );
| protected void | completeDH(byte[] buffer)
try{
BigInteger other_dh_y = bytesToBigInteger( buffer, 0, DH_SIZE_BYTES );
KeyFactory dh_key_factory = KeyFactory.getInstance("DH");
PublicKey other_public_key = dh_key_factory.generatePublic( new DHPublicKeySpec( other_dh_y, DH_P_BI, DH_G_BI ));
key_agreement.doPhase( other_public_key, true );
secret_bytes = key_agreement.generateSecret();
adapter.gotSecret( secret_bytes );
// System.out.println( "secret = " + ByteFormatter.encodeString( secret_bytes ));
}catch( Throwable e ){
throw( new IOException( Debug.getNestedExceptionMessage(e)));
}
| protected void | failed(java.lang.Throwable cause)
// System.out.println( (outbound?"out: ":"in :") + " failed, " + cause.getMessage());
processing_complete = true;
transport.cancelReadSelects();
transport.cancelWriteSelects();
adapter.decodeFailed( this, cause );
| protected static java.security.KeyPair | generateDHKeyPair(TransportHelper transport, boolean outbound)
synchronized( dh_key_generator ){
if ( !outbound ){
byte[] address = transport.getAddress().getAddress().getAddress();
int hit_count = generate_bloom.add( address );
long now = SystemTime.getCurrentTime();
// allow up to 10% bloom filter utilisation
if ( generate_bloom.getSize() / generate_bloom.getEntryCount() < 10 ){
generate_bloom = BloomFilterFactory.createAddRemove4Bit(generate_bloom.getSize() + BLOOM_INCREASE );
generate_bloom_create_time = now;
Logger.log( new LogEvent(LOGID, "PHE bloom: size increased to " + generate_bloom.getSize()));
}else if ( now < generate_bloom_create_time || now - generate_bloom_create_time > BLOOM_RECREATE ){
generate_bloom = BloomFilterFactory.createAddRemove4Bit(generate_bloom.getSize());
generate_bloom_create_time = now;
}
if ( hit_count >= 15 ){
Logger.log( new LogEvent(LOGID, "PHE bloom: too many recent connection attempts from " + transport.getAddress()));
throw( new IOException( "Too many recent connection attempts (phe)"));
}
long since_last = now - last_dh_incoming_key_generate;
long delay = 100 - since_last;
// limit key gen operations to 10 a second
if ( delay > 0 && delay < 100 ){
try{
Thread.sleep( delay );
}catch( Throwable e ){
}
}
last_dh_incoming_key_generate = now;
}
KeyPair res = dh_key_generator.generateKeyPair();
return( res );
}
| public TransportHelperFilter | getFilter()
return( filter );
| public long | getLastReadTime()
long now = SystemTime.getCurrentTime();
if ( last_read_time > now ){
last_read_time = now;
}
return( last_read_time );
| public static int | getMaxIncomingInitialPacketSize(boolean min_overheads)
return( MIN_INCOMING_INITIAL_PACKET_SIZE + (min_overheads?PADDING_MAX_LIMITED:PADDING_MAX_NORMAL)/2 );
| protected int | getPaddingMax()
if ( transport.minimiseOverheads()){
return( PADDING_MAX_LIMITED );
}else{
return( PADDING_MAX_NORMAL );
}
| protected static synchronized byte[] | getRandomPadding(int max_len)
byte[] bytes = new byte[ random.nextInt(max_len)];
random.nextBytes(bytes);
return( bytes );
| public java.lang.String | getString()
return( "state=" + protocol_state + ",sub=" + protocol_substate + ",in=" + bytes_read + ",out=" + bytes_written);
| protected static synchronized byte[] | getZeroPadding(int max_len)
byte[] bytes = new byte[ random.nextInt(max_len)];
return( bytes );
| protected void | handshakeComplete()
if ( selected_protocol == CRYPTO_PLAIN ){
filter = new TransportHelperFilterTransparent( transport, true );
}else if ( selected_protocol == CRYPTO_XOR ){
filter = new TransportHelperFilterStreamXOR( transport, secret_bytes );
}else if ( selected_protocol == CRYPTO_RC4 ){
filter = new TransportHelperFilterStreamCipher(
transport,
read_cipher,
write_cipher );
/*
}else if ( selected_protocol == CRYPTO_AES ){
try{
SecretKeySpec secret_key_spec = new SecretKeySpec( secret_bytes, 32, AES_STREAM_KEY_SIZE_BYTES, AES_STREAM_ALG );
AlgorithmParameterSpec spec = new IvParameterSpec( secret_bytes, 48, AES_STREAM_KEY_SIZE_BYTES );
write_cipher = new TCPTransportCipher( AES_STREAM_CIPHER, Cipher.ENCRYPT_MODE, secret_key_spec, spec );
read_cipher = new TCPTransportCipher( AES_STREAM_CIPHER, Cipher.DECRYPT_MODE, secret_key_spec, spec );
filter = new TCPTransportHelperFilterStreamCipher(
helper,
read_cipher,
write_cipher );
}catch( Throwable e ){
throw( new IOException( "AES crypto init failed: " + Debug.getNestedExceptionMessage(e)));
}
*/
}else{
throw( new IOException( "Invalid selected protocol '" + selected_protocol + "'" ));
}
if ( initial_data_in != null ){
filter = new TransportHelperFilterInserter( filter, initial_data_in );
}
handshake_complete = true;
| protected void | initCrypto()
try{
KeyPair key_pair = generateDHKeyPair( transport, outbound );
key_agreement = KeyAgreement.getInstance("DH");
key_agreement.init(key_pair.getPrivate());
DHPublicKey dh_public_key = (DHPublicKey)key_pair.getPublic();
BigInteger dh_y = dh_public_key.getY();
dh_public_key_bytes = bigIntegerToBytes( dh_y, DH_SIZE_BYTES );
}catch( Throwable e ){
throw( new IOException( Debug.getNestedExceptionMessage(e)));
}
| public boolean | isComplete(long now)
return( processing_complete );
| public static boolean | isCryptoOK()
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;
}
return( crypto_ok );
| protected void | process()
try{
process_mon.enter();
if ( handshake_complete ){
Debug.out( "Handshake process already completed" );
return;
}
boolean loop = true;
while( loop ){
// System.out.println( this + ":" + (outbound?"out: ":"in : ") + protocol_state + "/" + protocol_substate + ": r " + bytes_read + " - " + read_buffer + ", w " + bytes_written + " - " + write_buffer );
if ( protocol_state == PS_OUTBOUND_1 ){
if ( write_buffer == null ){
// A sends B Ya + Pa
byte[] padding_a = getRandomPadding(getPaddingMax()/2); // note that /2 also used in calculating max initial packet size above
write_buffer = ByteBuffer.allocate( dh_public_key_bytes.length + padding_a.length );
write_buffer.put( dh_public_key_bytes );
write_buffer.put( padding_a );
write_buffer.flip();
}
write( write_buffer );
if ( !write_buffer.hasRemaining()){
write_buffer = null;
protocol_state = PS_INBOUND_2;
}
}else if ( protocol_state == PS_INBOUND_1 ){
// B receives Ya
read( read_buffer );
if ( !read_buffer.hasRemaining()){
read_buffer.flip();
byte[] other_dh_public_key_bytes = new byte[read_buffer.remaining()];
read_buffer.get( other_dh_public_key_bytes );
completeDH( other_dh_public_key_bytes );
read_buffer = null;
protocol_state = PS_OUTBOUND_2;
}
}else if ( protocol_state == PS_OUTBOUND_2 ){
// B->A: Yb PadB
if ( write_buffer == null ){
byte[] padding_b = getRandomPadding( getPaddingMax()/2 );
write_buffer = ByteBuffer.allocate( dh_public_key_bytes.length + padding_b.length );
write_buffer.put( dh_public_key_bytes );
write_buffer.put( padding_b );
write_buffer.flip();
}
write( write_buffer );
if ( !write_buffer.hasRemaining()){
write_buffer = null;
protocol_state = PS_INBOUND_3;
}
}else if ( protocol_state == PS_INBOUND_2 ){
// A receives: Yb
if ( read_buffer == null ){
read_buffer = ByteBuffer.allocate( dh_public_key_bytes.length );
}
read( read_buffer );
if ( !read_buffer.hasRemaining()){
read_buffer.flip();
byte[] other_dh_public_key_bytes = new byte[read_buffer.remaining()];
read_buffer.get( other_dh_public_key_bytes );
completeDH( other_dh_public_key_bytes );
// A initiates SKEY so we can now set up crypto
setupCrypto();
read_buffer = null;
protocol_state = PS_OUTBOUND_3;
}
}else if ( protocol_state == PS_OUTBOUND_3 ){
// A->B: HASH('req1', S), HASH('req2', SKEY)^HASH('req3', S), ENCRYPT(VC, crypto_provide, len(PadC), PadC, len(IA)), ENCRYPT(IA)
if ( write_buffer == null ){
int initial_data_out_len = initial_data_out==null?0:initial_data_out.remaining();
// padding_a here is half of the padding from before
int pad_max = getPaddingMax();
byte[] padding_a = getRandomPadding(pad_max/2);
byte[] padding_c = getZeroPadding(pad_max);
write_buffer = ByteBuffer.allocate( padding_a.length + 20 + 20 + ( VC.length + 4 + 2 + padding_c.length + 2 ) + initial_data_out_len );
write_buffer.put( padding_a );
// HASH('req1', S)
SHA1Hasher hasher = new SHA1Hasher();
hasher.update( REQ1_IV );
hasher.update( secret_bytes );
byte[] sha1 = hasher.getDigest();
write_buffer.put( sha1 );
// HASH('req2', SKEY)^HASH('req3', S)
hasher = new SHA1Hasher();
hasher.update( REQ2_IV );
hasher.update( shared_secret );
byte[] sha1_1 = hasher.getDigest();
hasher = new SHA1Hasher();
hasher.update( REQ3_IV );
hasher.update( secret_bytes );
byte[] sha1_2 = hasher.getDigest();
for (int i=0;i<sha1_1.length;i++){
sha1_1[i] ^= sha1_2[i];
}
write_buffer.put( sha1_1 );
// ENCRYPT(VC, crypto_provide, len(PadC), PadC, len(IA)
write_buffer.put( write_cipher.update( VC ));
write_buffer.put( write_cipher.update( new byte[]{ 0, 0, 0, my_supported_protocols }));
write_buffer.put( write_cipher.update( new byte[]{ (byte)(padding_c.length>>8),(byte)padding_c.length }));
write_buffer.put( write_cipher.update( padding_c ));
write_buffer.put( write_cipher.update( new byte[]{ (byte)(initial_data_out_len>>8),(byte)initial_data_out_len }));
if ( initial_data_out_len > 0 ){
int save_pos = initial_data_out.position();
write_cipher.update( initial_data_out, write_buffer );
// reset in case buffer needs to be used again by caller
initial_data_out.position( save_pos );
initial_data_out = null;
}
write_buffer.flip();
}
write( write_buffer );
if ( !write_buffer.hasRemaining()){
write_buffer = null;
protocol_state = PS_INBOUND_4;
}
}else if ( protocol_state == PS_INBOUND_3 ){
// B receives: HASH('req1', S), HASH('req2', SKEY)^HASH('req3', S), ENCRYPT(VC, crypto_provide, len(PadC), PadC, len(IA)), ENCRYPT(IA)
if ( read_buffer == null ){
read_buffer = ByteBuffer.allocate( 20 + PADDING_MAX );
read_buffer.limit( 20 );
SHA1Hasher hasher = new SHA1Hasher();
hasher.update( REQ1_IV );
hasher.update( secret_bytes );
padding_skip_marker = hasher.getDigest();
protocol_substate = 1;
}
while( true ){
read( read_buffer );
if ( read_buffer.hasRemaining()){
break;
}
if ( protocol_substate == 1 ){
//skip up to HASH('req1', S)
int limit = read_buffer.limit();
read_buffer.position( limit - 20 );
boolean match = true;
for (int i=0;i<20;i++){
if ( read_buffer.get() != padding_skip_marker[i] ){
match = false;
break;
}
}
if ( match ){
read_buffer = ByteBuffer.allocate( 20 + VC.length + 4 + 2 );
protocol_substate = 2;
break;
}else{
if ( limit == read_buffer.capacity()){
throw( new IOException( "PHE skip to SHA1 marker failed" ));
}
read_buffer.limit( limit + 1 );
read_buffer.position( limit );
}
}else if ( protocol_substate == 2 ){
// find SKEY using HASH('req2', SKEY)^HASH('req3', S) , ENCRYPT(VC, crypto_provide, len(PadC),
read_buffer.flip();
final byte[] decode = new byte[20];
read_buffer.get( decode );
SHA1Hasher hasher = new SHA1Hasher();
hasher.update( REQ3_IV );
hasher.update( secret_bytes );
byte[] sha1 = hasher.getDigest();
for (int i=0;i<decode.length;i++){
decode[i] ^= sha1[i];
}
synchronized( global_shared_secrets ){
shared_secret = (byte[])global_shared_secrets.get( new HashWrapper( decode ));
}
if ( shared_secret == null ){
throw( new IOException( "No matching shared secret" ));
}
// System.out.println( "inbound - using crypto secret " + ByteFormatter.encodeString( shared_secret ));
setupCrypto();
byte[] crypted = new byte[VC.length + 4 + 2];
read_buffer.get( crypted );
byte[] plain = read_cipher.update( crypted );
byte other_supported_protocols = plain[VC.length+3];
int common_protocols = my_supported_protocols & other_supported_protocols;
if (( common_protocols & CRYPTO_PLAIN )!= 0 ){
selected_protocol = CRYPTO_PLAIN;
}else if (( common_protocols & CRYPTO_XOR )!= 0 ){
selected_protocol = CRYPTO_XOR;
}else if (( common_protocols & CRYPTO_RC4 )!= 0 ){
selected_protocol = CRYPTO_RC4;
}else if (( common_protocols & CRYPTO_AES )!= 0 ){
selected_protocol = CRYPTO_AES;
}else{
throw( new IOException(
"No crypto protocol in common: mine = " +
Integer.toHexString((byte)my_supported_protocols) + ", theirs = " +
Integer.toHexString((byte)other_supported_protocols)));
}
int padding = (( plain[VC.length+4] & 0xff ) << 8 ) + ( plain[VC.length+5] & 0xff );
if ( padding > PADDING_MAX ){
throw( new IOException( "Invalid padding '" + padding + "'" ));
}
read_buffer = ByteBuffer.allocate( padding + 2 );
// skip the padding
protocol_substate = 3;
}else if ( protocol_substate == 3 ){
// ENCRYPT( len(IA)), { ENCRYPT(IA) }
read_buffer.flip();
byte[] data = new byte[read_buffer.remaining()];
read_buffer.get( data );
data = read_cipher.update( data );
int ia_len = 0xffff & ((( data[data.length-2] & 0xff ) << 8 ) + ( data[data.length-1] & 0xff ));
if ( ia_len > 65535 ){
throw( new IOException( "Invalid IA length '" + ia_len + "'" ));
}
if ( ia_len > 0 ){
read_buffer = ByteBuffer.allocate( ia_len );
// skip the padding
protocol_substate = 4;
}else{
read_buffer = null;
protocol_state = PS_OUTBOUND_4;
break;
}
}else if ( protocol_substate == 4 ){
// ENCRYPT(IA)
read_buffer.flip();
byte[] data = new byte[read_buffer.remaining()];
read_buffer.get( data );
data = read_cipher.update( data );
// hack alert - we can delay the writing of the outbound_4 packet if this is an incoming packet with
// a piggybacked bt handshake as we know that we'll be sending our own handshake back out pretty soon
// and it'll take the delayed data with it. To be more generic we'd need to add a callback to the pattern
// matcher to allow it to decide whether delaying was sensible / or stick a timer on the delayed data
delay_outbound_4 = new String( data ).indexOf( "BitTorrent" ) != -1;
// System.out.println( "Initial Data In: " + new String( data ) + "->delay=" +delay_outbound_4 );
initial_data_in = ByteBuffer.wrap( data );
read_buffer = null;
protocol_state = PS_OUTBOUND_4;
break;
}
}
}else if ( protocol_state == PS_OUTBOUND_4 ){
// B->A: ENCRYPT(VC, crypto_select, len(padD), padD, // len(IB)), ENCRYPT(IB)
if ( write_buffer == null ){
int pad_max = getPaddingMax();
byte[] padding_b = getRandomPadding( pad_max/2 ); // half padding b sent here
byte[] padding_d = getZeroPadding( pad_max );
write_buffer = ByteBuffer.allocate( padding_b.length + VC.length + 4 + 2 + padding_d.length ); // + 2 + initial_data_out.length );
write_buffer.put( padding_b );
write_buffer.put( write_cipher.update( VC ));
write_buffer.put( write_cipher.update( new byte[]{ 0, 0, 0, selected_protocol }));
write_buffer.put( write_cipher.update( new byte[]{ (byte)(padding_d.length>>8),(byte)padding_d.length }));
write_buffer.put( write_cipher.update( padding_d ));
//write_buffer.put( write_cipher.update( new byte[]{ (byte)(initial_data_out.length>>8),(byte)initial_data_out.length }));
//write_buffer.put( write_cipher.update( initial_data_out ));
write_buffer.flip();
}
if ( delay_outbound_4 ){
if ( transport.delayWrite( write_buffer )){
write_buffer = null;
handshakeComplete();
}else{
delay_outbound_4 = false;
}
}
if ( !delay_outbound_4 ){
write( write_buffer );
if ( !write_buffer.hasRemaining()){
write_buffer = null;
handshakeComplete();
}
}
}else if ( protocol_state == PS_INBOUND_4 ){
// B->A: ENCRYPT(VC, crypto_select, len(padD), padD // , len(IB)), ENCRYPT(IB)
if ( read_buffer == null ){
read_buffer = ByteBuffer.allocate( VC.length + PADDING_MAX );
read_buffer.limit( VC.length );
padding_skip_marker = new byte[VC.length];
padding_skip_marker = read_cipher.update( padding_skip_marker );
protocol_substate = 1;
}
while( true ){
read( read_buffer );
if ( read_buffer.hasRemaining()){
break;
}
if ( protocol_substate == 1 ){
//skip up to marker
int limit = read_buffer.limit();
read_buffer.position( limit - VC.length );
boolean match = true;
for (int i=0;i<VC.length;i++){
if ( read_buffer.get() != padding_skip_marker[i] ){
match = false;
break;
}
}
if ( match ){
read_buffer = ByteBuffer.allocate( 4 + 2 );
protocol_substate = 2;
break;
}else{
if ( limit == read_buffer.capacity()){
throw( new IOException( "PHE skip to SHA1 marker failed" ));
}
read_buffer.limit( limit + 1 );
read_buffer.position( limit );
}
}else if ( protocol_substate == 2 ){
// ENCRYPT( crypto_select, len(padD))
read_buffer.flip();
byte[] crypted = new byte[4 + 2];
read_buffer.get( crypted );
byte[] plain = read_cipher.update( crypted );
selected_protocol = plain[3];
if (( selected_protocol & my_supported_protocols ) == 0 ){
throw( new IOException(
"Selected protocol has nothing in common: mine = " +
Integer.toHexString((byte)my_supported_protocols) + ", theirs = " +
Integer.toHexString((byte)selected_protocol)));
}
int pad_len = 0xffff&((( plain[4] & 0xff ) << 8 ) + ( plain[5] & 0xff ));
if ( pad_len > 65535 ){
throw( new IOException( "Invalid pad length '" + pad_len + "'" ));
}
read_buffer = ByteBuffer.allocate( pad_len ); // + 2 );
protocol_substate = 3;
}else if ( protocol_substate == 3 ){
read_buffer.flip();
byte[] data = new byte[read_buffer.remaining()];
read_buffer.get( data );
data = read_cipher.update( data );
handshakeComplete();
read_buffer = null;
break;
/*
int ib_len = 0xffff & ((( data[data.length-2] & 0xff ) << 8 ) + ( data[data.length-1] & 0xff ));
if ( ib_len > 65535 ){
throw( new IOException( "Invalid IB length '" + ib_len + "'" ));
}
read_buffer = ByteBuffer.allocate( ib_len );
protocol_substate = 4;
}else{
read_buffer.flip();
byte[] data = new byte[read_buffer.remaining()];
read_buffer.get( data );
initial_data_in = read_cipher.update( data );
handshakeComplete();
read_buffer = null;
break;
*/
}
}
}
if ( handshake_complete ){
transport.cancelReadSelects();
transport.cancelWriteSelects();
loop = false;
complete();
}else{
if ( read_buffer == null ){
transport.pauseReadSelects();
}else{
transport.resumeReadSelects();
loop = false;
}
if ( write_buffer == null ){
transport.pauseWriteSelects();
}else{
transport.resumeWriteSelects();
loop = false;
}
}
}
}catch( Throwable e ){
failed( e );
if ( e instanceof IOException ){
throw((IOException)e);
}else{
throw( new IOException( Debug.getNestedExceptionMessage(e)));
}
}finally{
process_mon.exit();
}
| protected void | read(java.nio.ByteBuffer buffer)
int len = transport.read( buffer );
//System.out.println( "read:" + this + "/" + protocol_state + "/" + protocol_substate + " -> " + len +"[" + buffer +"]");
if ( len < 0 ){
throw( new IOException( "end of stream on socket read - phe: " + getString()));
}
bytes_read += len;
| 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 ));
}
}
| public void | selectFailure(TransportHelper transport, java.lang.Object attachment, java.lang.Throwable msg)
failed( msg );
| public boolean | selectSuccess(TransportHelper transport, java.lang.Object attachment, boolean write_operation)
try{
int old_bytes_read = bytes_read;
int old_bytes_written = bytes_written;
process();
if ( write_operation ){
return( bytes_written != old_bytes_written );
}else{
boolean progress = bytes_read != old_bytes_read;
if ( progress ){
last_read_time = SystemTime.getCurrentTime();
}
return( progress );
}
}catch( Throwable e ){
failed( e );
return( false );
}
| protected void | setupCrypto()
try{
//"HASH('keyA', S, SKEY)" if you're A
//"HASH('keyB', S, SKEY)" if you're B
SHA1Hasher hasher = new SHA1Hasher();
hasher.update( KEYA_IV );
hasher.update( secret_bytes );
hasher.update( shared_secret );
byte[] a_key = hasher.getDigest();
hasher = new SHA1Hasher();
hasher.update( KEYB_IV );
hasher.update( secret_bytes );
hasher.update( shared_secret );
byte[] b_key = hasher.getDigest();
SecretKeySpec secret_key_spec_a = new SecretKeySpec( a_key, RC4_STREAM_ALG );
SecretKeySpec secret_key_spec_b = new SecretKeySpec( b_key, RC4_STREAM_ALG );
write_cipher = new TransportCipher( RC4_STREAM_CIPHER, Cipher.ENCRYPT_MODE, outbound?secret_key_spec_a:secret_key_spec_b );
read_cipher = new TransportCipher( RC4_STREAM_CIPHER, Cipher.DECRYPT_MODE, outbound?secret_key_spec_b:secret_key_spec_a );
}catch( Throwable e ){
e.printStackTrace();
throw( new IOException( Debug.getNestedExceptionMessage(e)));
}
| protected void | write(java.nio.ByteBuffer buffer)
//System.out.println( "write pre:" + this + "/" + protocol_state + "/" + protocol_substate + " - " + buffer );
int len = transport.write( buffer, false );
//System.out.println( "write:" + this + "/" + protocol_state + "/" + protocol_substate + " -> " + len +"[" + buffer +"]");
if ( len < 0 ){
throw( new IOException( "bytes written < 0 " ));
}
bytes_written += len;
|
|