package Client;

import java.io.*;
import java.util.*;
import java.net.*;
import java.io.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.math.*;
import org.bouncycastle.openssl.*; 
import org.bouncycastle.util.encoders.*;

/*
* >--TCP--<
* Author: Someone
*/
public class TCP implements Runnable{
  private Socket $sock = null;
  private InputStream $in = null;
  private OutputStream $out = null;
  private Cipher $c2s = Cipher.getInstance("RSA/NONE/OAEPWithSHA256AndMGF1Padding");
  private Cipher $s2c = Cipher.getInstance("RSA/NONE/OAEPWithSHA256AndMGF1Padding");
  private PublicKey $puk = null;
  public PrivateKey $prk = null;
  public String $challenge = null;


public TCP(String $server, int $port)throws Exception{
  System.out.println("CLIENT TCP: "+$server+":"+$port);
  $sock = new Socket($server, $port);
  $in = $sock.getInputStream();
  $out = $sock.getOutputStream();

  PEMReader in = new PEMReader(new FileReader(Main.$cfg.getString("server.key"))); 
  $puk = (PublicKey) in.readObject();

  $c2s.init(Cipher.ENCRYPT_MODE, $puk);
  $s2c.init(Cipher.DECRYPT_MODE, $prk);
  new Thread(this).start();
}

public void run(){
  try{
    while($sock != null){
      receive();
    }
  }catch(Exception $e){}
  System.out.println("TCP:run() ended");
}

public void send(String $msg){
  try{
    byte[] $encrypted = $c2s.doFinal($msg.getBytes());
    byte[] $len = Helper.int2ba($encrypted.length);
    $out.write($len);
    $out.write($encrypted);
    $out.flush();
  }catch(Exception $e){$e.printStackTrace();}
}

public void receive()throws Exception{
  int $len = Helper.ba2int(Helper.readBA($in, 4));
  byte[]$raw = Helper.readBA($in, $len);
  String $msg = new String($s2c.doFinal($raw));
  if($msg.startsWith("!ok")){
    String[] $eing = $msg.split("\\s+");
    if(!$challenge.equals($eing[1])){
      System.out.println("ERROR: Challenge mismatch!");
      shutdown();
    }
    send($eing[2]);
    byte[] $skey = Base64.decode($eing[3].getBytes());
    byte[] $iv = Base64.decode($eing[4].getBytes());
    $c2s = Cipher.getInstance("AES/CTR/NoPadding");
    $s2c = Cipher.getInstance("AES/CTR/NoPadding");
    $c2s.init(Cipher.ENCRYPT_MODE, new SecretKeySpec($skey, "AES"), new IvParameterSpec($iv));
    $s2c.init(Cipher.DECRYPT_MODE, new SecretKeySpec($skey, "AES"), new IvParameterSpec($iv));

  }else{
    System.out.println($msg);
  }
}

public void shutdown(){
  try{
    send("CLIENT shutting down...");
    $out.close();
    $in.close();
    $sock.close();
    $sock = null;
  }catch(Exception $e){$e.printStackTrace();}
}

}