Buenas, Blau otra vez andaba haciendo un chat con servidor centralizado, ésta es la implementación tradicional y me apeteció escribir algo diferente, además de implementar el establecimiento de claves para un posterior cifrado con AES mediante Diffie-Hellman y así solucionar la dependencia de un canal seguro para intercambiar las claves. Quería practicar además con las arquitecturas P2P y elegí una topología parcialmente centralizada (híbrida) con supernodos que implementen un sencillo servidor de nombres para obtener la ip del usario con el que se quiere establecer comunicación, algo así como una arquitectura simplista del modelo que emplea Skype [Enlace externo eliminado para invitados].
Imagen



El código está un poco trastocado, lo que quería probar que era la topología y Diffie-Hellman funcionan correctamente por eso no lo voy a tocar más, probando el cliente me surgieron problemas ya que corre en puerto específico y hubo que cambiar cosas, solo está implementada la comunicación unidireccional del nodo cliente cliente al nodo cliente servidor además de problemas en el envío del texto cifrado en AES (codificación UTF-8) el cifrado y el descifrado funcionan bien . Dejo el código y algunas pruebas:

[Package Network]
[ServerSuperNode.java]

Código: Seleccionar todo

package Network;
import java.net.ServerSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.net.Socket;
import java.io.PrintWriter;
import java.util.Scanner;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import Configuration.ConstantsNetwork;
/**
 * Server for a SuperNodes which compounds the system. (Partially centralized)
 * @author (Overxfl0w13) 
 * @version (a version number or a date)
 */
public class ServerSuperNode
{
    private ServerSocket serverSocket;
    private ConcurrentHashMap<String,InetAddress> userIndex;
    private String[] superNodeList; // It contains neighbours supernodes ip to communicate with them if there is no users with name username in it's user index
    private final int maxSuperNodes = 50; // It's possible to use a LPI to avoid limit neighbours supernodes
    /**
     * Constructor for objects of class ServerSuperNode
    */
    public ServerSuperNode()
    {
        try{
            this.serverSocket  = new ServerSocket(ConstantsNetwork.SNport);
            this.userIndex     = new ConcurrentHashMap<String,InetAddress>();
            this.superNodeList = new String[50];
        }catch(UnknownHostException e){ e.printStackTrace(); }
        catch(IOException e)          { e.printStackTrace(); }
    }

    /**
     * Throws the server
    */
    public void runServer(){
        try{
            for(;;){
                Socket sClient      = this.serverSocket.accept();
                ThreadSuperNode tsn = new ThreadSuperNode(sClient,this.userIndex,superNodeList);
                tsn.start();
            }
        }catch(UnknownHostException e){ e.printStackTrace(); }
        catch(IOException e)          { e.printStackTrace(); }
    }
        
    public static void main(String args[]){
        ServerSuperNode ssn = new ServerSuperNode();
        ssn.runServer();
    }      
}
[Client.java]

Código: Seleccionar todo

package Network;
import Criptography.DiffieHellman;
import Criptography.AES;
import Utils.CoreUtils;
import Configuration.ConstantsNetwork;
import java.net.ServerSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.net.Socket;
import java.io.PrintWriter;
import java.util.Scanner;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.math.BigInteger;
public class Client
{
    private final String myName;
    private Socket mySocket;
    private PrintWriter pwOutput;
    private Scanner     scInput;
    
    public Client(){
        this.myName   = ConstantsNetwork.user;
        this.mySocket = null;
        this.pwOutput = null;
        this.scInput  = null;
        this.registerUser();
    }
    
    private final void registerUser(){
        try{
            this.mySocket = new Socket(ConstantsNetwork.ipSN,ConstantsNetwork.SNport);
            this.pwOutput = new PrintWriter(this.mySocket.getOutputStream());
            this.scInput  = new Scanner(this.mySocket.getInputStream());
            this.pwOutput.print("reg " + ConstantsNetwork.user + " " + InetAddress.getByName("localhost").toString().split("/")[1] + "\n"); 
            this.pwOutput.flush();
            System.out.println(scInput.next()); 
            this.mySocket.close();
            this.pwOutput.close();
            this.scInput.close();
        }catch(UnknownHostException e){ e.printStackTrace(); }
        catch(IOException e)          { e.printStackTrace(); }
    }
           
    public String getOtherIp(String nameOther){
        String res = "";
        try{
            this.mySocket = new Socket(ConstantsNetwork.ipSN,ConstantsNetwork.SNport);
            this.pwOutput = new PrintWriter(this.mySocket.getOutputStream());
            this.scInput  = new Scanner(this.mySocket.getInputStream());
            this.pwOutput.print("who" + " " + nameOther + "\n"); this.pwOutput.flush();
            res = scInput.nextLine(); 
            this.mySocket.close();
            this.pwOutput.close();
            this.scInput.close();
        }catch(UnknownHostException e){ e.printStackTrace(); }
        catch(IOException e)          { e.printStackTrace(); }
        finally{ return res; }
    }

    public void connectToUser(String ipUser){
        this.mySocket = null;
        try{
            this.mySocket = new Socket(InetAddress.getByName(ipUser),ConstantsNetwork.Otport);
            this.pwOutput = new PrintWriter(this.mySocket.getOutputStream());
            this.scInput  = new Scanner(this.mySocket.getInputStream());
        }catch(UnknownHostException e){ e.printStackTrace(); }
        catch(IOException e)      { e.printStackTrace(); }
    }
      
    public void sendToUser(String data){
        this.pwOutput.print(data+"\n"); this.pwOutput.flush();           
    }
    
    public String recvFromUser(){
        return this.scInput.nextLine();
    }
    
    public static void main(String args[]){ 
        Scanner tcInput = new Scanner(System.in);
        
        // Get user given information //
        System.out.println("Insert your username : ");
        ConstantsNetwork.user   = tcInput.nextLine();
        System.out.println("Insert your application port : ");
        ConstantsNetwork.Clport  = tcInput.nextInt();
        System.out.println("Insert the other application port : ");
        ConstantsNetwork.Otport = tcInput.nextInt();
        
        // Register me in user index //
        Client myClient = new Client();        
        String otherName = "",otherIp = "";   
        String text = "";
        
        // Generate my secret_number //
        DiffieHellman clientDH = new DiffieHellman();
        
        // One-time client is registered, throw another thread to act like a server waitting other connections //
        ThreadServerClient tsc = new ThreadServerClient(clientDH,myClient);
        tsc.start();
        
        // Search another user ip if the user is not connected with the other //
          //do{
                System.out.println("Name of the other user or wait to connection > ");
                tcInput.nextLine();
                otherName = tcInput.nextLine();
                if(!ConstantsNetwork.conn){
                otherIp = myClient.getOtherIp(otherName).replace("/","");
            //}while((otherIp = myClient.getOtherIp(otherName).replace("/",""))=="");      
            // Connect with that user //
            myClient.connectToUser(otherIp);  
           
            // Send my secret_number to the other user //
            myClient.sendToUser(String.valueOf(clientDH.get_data_to_send()));
            //System.out.println("Mi data: " + clientDH.get_data_to_send());
            String other_secret = myClient.recvFromUser();
            //System.out.println("Other secret : " + other_secret);
            // Recv the other user secret_number and set my secret_key with it //
            clientDH.set_secret_key(new BigInteger(other_secret));
            //System.out.println("My number (a|b) (Sender): " + clientDH.get_secret_number());
            System.out.println("Acorded Key   (Sender): " + clientDH.get_str_secret_key());
            // With all keys stablished it's possible to initialise a conversation //
            byte[] cryptedOne = {};
            while(true){
                text = tcInput.nextLine();
                //text = CoreUtils.fill_text_16(text);
                try{
                    cryptedOne = AES.encrypt(text,clientDH.get_str_secret_key());
                    myClient.sendToUser(new String(cryptedOne)); // El crypter AES usa UTF-8 mirar eso.
                    System.out.println("Transmited : " + cryptedOne);
                }catch(Exception e) { e.printStackTrace(); }
            }
        }
        else{
            System.out.println("You has been connected by other"); 
            byte[] cryptedOne = {};
            while(true){
               text = tcInput.nextLine();
               //text = CoreUtils.fill_text_16(text);
               try{
                   cryptedOne = AES.encrypt(text,(clientDH.get_str_secret_key()));
                   myClient.sendToUser(new String(cryptedOne,"UTF-8")+"\n");
                   System.out.println("Transmited : " + cryptedOne);
                   //System.out.println("1 sends to 2 : " + cryptedOne);
               }catch(Exception e) { e.printStackTrace(); }
            }
        }
}   
}
[ThreadServerClient.java]

Código: Seleccionar todo

package Network;
import java.net.*;
import java.io.*;
import java.util.Scanner;
import Criptography.DiffieHellman;
import Criptography.AES;
import java.nio.charset.Charset;
import Configuration.ConstantsNetwork;
import java.math.BigInteger;
/**
 * Write a description of class ThreadServerClient here.
 * 
 * @author (your name) 
 * @version (a version number or a date)
 */
public class ThreadServerClient extends Thread
{
   private ServerSocket sSocket;
   private DiffieHellman dh;
   private Client myClient;
   private Socket cSocket;
   
   public ThreadServerClient(DiffieHellman dh,Client myClient){
       try{
           this.sSocket  = new ServerSocket(ConstantsNetwork.Clport);
           this.cSocket  = null;
           this.dh       = dh;
           this.myClient = myClient;
       }catch(IOException e){ e.printStackTrace(); }
   }
   
   public Socket getcSocket(){ return this.cSocket; }
   public void run(){
       Socket sClient = null;
       try{
           sClient               = this.sSocket.accept();
           this.cSocket          = sClient;
           ConstantsNetwork.conn = new Boolean(true);
           Scanner scInput       = new Scanner(sClient.getInputStream()); 
           Scanner tcInput       = new Scanner(System.in);
           PrintWriter pwInput   = new PrintWriter(sClient.getOutputStream());
           // Get other secret_number //
           String other_secret = scInput.nextLine();
           //long other_secret = Long.parseLong(myClient.recvFromUser());
           this.dh.set_secret_key(new BigInteger(other_secret));
           String decryptedOne = "";
           //System  .out.println("My number (a|b) (Receiver): " + this.dh.get_secret_number());
           //System.out.println("The other number            : " + other_secret);              
           System.out.println("Acorded Key  (Receiver): " + this.dh.get_str_secret_key());
           // Send my data //
           pwInput.print(String.valueOf(this.dh.get_data_to_send()) + "\n"); pwInput.flush();
           for(;;){
                try{  
                    String received = new String(scInput.nextLine().getBytes(),"UTF-8");
                    System.out.println("String received : " + received);
                    decryptedOne = AES.decrypt(received.getBytes(),(this.dh.get_str_secret_key()));
                    if(decryptedOne!=null) System.out.println("Message: " + decryptedOne);
                }catch(Exception e) { e.printStackTrace(); }
           }
        }catch(IOException e){ e.printStackTrace(); }
       finally{ try{sClient.close();} catch(IOException e){ e.printStackTrace(); } }
   }
}
[ThreadSuperNode.java]

Código: Seleccionar todo

package Network;
import java.net.ServerSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.net.Socket;
import java.io.PrintWriter;
import java.util.Scanner;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import Configuration.ConstantsNetwork;
/** Commands **/
/*  Register username -> reg [username] [ip]
 *  Query    username -> who [username]
 */
/**
 * Server for a SuperNodes which compounds the system. (Partially centralized)
 * 
 * @author (Overxfl0w13) 
 * @version (a version number or a date)
 */
public class ThreadSuperNode extends Thread
{
    private Socket cSocket;
    private ConcurrentHashMap<String,InetAddress> userIndex;
    private Scanner scInput;
    private PrintWriter pwOutput;
    private String[] superNodeList;
    /**
     * Constructor for objects of class ServerSuperNode
    */
    public ThreadSuperNode(Socket cSocket,ConcurrentHashMap<String,InetAddress> userIndex,String[] superNodeList){
        this.cSocket   = cSocket;
        this.userIndex = userIndex;
        this.superNodeList = superNodeList;
        try{
            this.scInput  = new Scanner(this.cSocket.getInputStream());
            this.pwOutput = new PrintWriter(this.cSocket.getOutputStream(),true);
        }catch(IOException e){ e.printStackTrace(); }
    }
    
    public void run(){
        String userCommand = this.scInput.nextLine();
        System.out.println(userCommand);
        if(userCommand.indexOf("reg")==0) this.registerUsername(userCommand);
        else if(userCommand.indexOf("who")==0) this.getUserIp(userCommand);
        else this.communicateError("Command is not correct\n");
        try{
            this.cSocket.close();
            this.pwOutput.close();
            this.scInput.close();
        }catch(IOException e){ e.printStackTrace(); }
    }
    
    // splitted[3] = myPort
    public void registerUsername(String userCommand){
        String splitted[] = userCommand.split(" ");
        // Check if there are enough items in splitted //
        boolean exists = (userIndex.get(splitted[1])!=null) ? true : false;       
        if(!exists){           
            try{ userIndex.put(splitted[1],InetAddress.getByName(splitted[2])); }
            catch(UnknownHostException e){ e.printStackTrace(); }
            this.pwOutput.print("Username " + splitted[1] + " registered with ip: " + splitted[2] + "\n"); this.pwOutput.flush();            
        }else{
            this.pwOutput.print("Username " + splitted[1] + " is already registered" + "\n"); this.pwOutput.flush();   
        }  
    }
    
    public void getUserIp(String userCommand){
        String splitted[] = userCommand.split(" ");
        // Check if there are enough items in splitted //
        boolean exists = (userIndex.get(splitted[1])!=null) ? true : false;
        if(exists){
            InetAddress ia = userIndex.get(splitted[1]); 
            this.pwOutput.print(ia.toString()); 
            this.pwOutput.flush();
        }else{
            /** Communicate with neighbours supernodes (it's essential that exists the supernode if it is indicated in superNodeList array) **/
            String ia;
            // Si no está en este supernodo, se conecta con el vecino para pedirle a el y así iterativo hasta el último que no tenga vecinos //
            if((ia = this.getFromOtherNodes(userCommand))!="") this.pwOutput.print(ia);
            else this.pwOutput.print("Username " + splitted[1] + " is not registered in our supernodes \n"); 
            this.pwOutput.flush();  
        }
    }
    
    public void communicateError(String error){
        this.pwOutput.print(error); this.pwOutput.flush();
    }
    
    private String getFromOtherNodes(String command){
        Socket cSocket       = null;
        PrintWriter pwSocket = null;
        Scanner scSocket     = null;
        String res           = ""  ;
        try{
            for(String ip : superNodeList){
                if(ip!=null){
                    cSocket = new Socket(ip,ConstantsNetwork.SNport);
                    pwSocket = new PrintWriter(cSocket.getOutputStream());
                    scSocket = new Scanner(cSocket.getInputStream());
                    pwSocket.print(command); pwSocket.flush();
                    res = scSocket.next();
                    if(res.indexOf("Username")!=0) return res;
                }
            }
        }catch(UnknownHostException e){ e.printStackTrace(); }
        catch(IOException e){ e.printStackTrace(); }
        finally{
            try{
                pwSocket.close();
                scSocket.close();
                cSocket.close();
            }catch(IOException e){ e.printStackTrace(); }
            finally{ return res; }
        }
    }
}
[Package Cryptography]

[DiffieHellman.java]

Código: Seleccionar todo

package Cryptography;
import Configuration.ConstantsCrypto;
import Utils.CryptographyUtils;
import java.util.Random;
import java.math.BigInteger;
import java.math.BigDecimal;
/** @http://www.javiercampos.es/blog/2011/07/22/el-algoritmo-de-diffie-hellman/ **/

public class DiffieHellman{
    
    private       BigInteger secret_number;
    private       BigInteger secret_key;
    private       String str_secret_key;
    
    public DiffieHellman(){
        this.secret_number = set_secret_number();
        this.secret_key    = new BigInteger("-1");        
    }
    
    public BigInteger get_secret_number(){ return this.secret_number; }
    
    private BigInteger set_secret_number(){ 
        return (new BigDecimal(String.valueOf(Math.random())).multiply(new BigDecimal(Configuration.ConstantsCrypto.diffie_p)).add(new BigDecimal("1"))).toBigInteger();
    }
    public void set_secret_number(BigInteger sn){ this.secret_number = sn; }
     
    
    public BigInteger get_data_to_send() { 
        return Configuration.ConstantsCrypto.diffie_g.modPow(this.secret_number,Configuration.ConstantsCrypto.diffie_p);
    }
    public BigInteger get_secret_key(){ return this.secret_key; }
    public String get_str_secret_key(){ return this.str_secret_key; }
    public void set_secret_key(BigInteger the_other_data){ 
        this.secret_key = the_other_data.modPow(this.secret_number,Configuration.ConstantsCrypto.diffie_p);
        this.str_secret_key = String.valueOf(secret_key);
        while(this.str_secret_key.length()!=16) this.str_secret_key += "\00";
    } 
    
   /**public static void main(String args[]){
        DiffieHellman client1 = new DiffieHellman(); // Genera un número aleatorio < p (primo alto) para el cliente 1 (a)
        DiffieHellman client2 = new DiffieHellman(); // Genera un número aleatorio < p (primo alto) para el cliente 2 (b)
        System.out.println("Numero secreto calculado por el cliente 1 : " + client1.get_secret_number());
        System.out.println("Numero secreto calculado por el cliente 2 : " + client2.get_secret_number());
        System.out.println("El cliente 1 envía al cliente 2 ((g^a)%p) resultado del método get_data_to_send() : " + client1.get_data_to_send());
        System.out.println("El cliente 2 envía al cliente 1 ((g^b)%p) resultado del método get_data_to_send() : " + client2.get_data_to_send());
        // Aqui el cliente 2 ya tiene ((g^a)%p) y genera la clave con (((g^a)%p)^b)%p -> método set_secret_key() //
        client2.set_secret_key(client1.get_data_to_send());
        // Aqui el cliente 1 ya tiene ((g^b)%p) y genera la clave con (((g^b)%p)^a)%p -> método set_secret_key() //
        client1.set_secret_key(client2.get_data_to_send());
        System.out.println("Key calculada por el cliente 2: " + client2.get_secret_key());  
        System.out.println("Key calculada por el cliente 1: " + client1.get_secret_key());       
    } **/
}   
[AES.java]

Código: Seleccionar todo

package Cryptography;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.Cipher;
import Configuration.ConstantsCrypto;
import java.io.*;
/**
 * Write a description of class AES here.
 * 
 * @author (your name) 
 * @version (a version number or a date)
 */
public final class AES
{
    /**
     * Thanks to bricef https://gist.github.com/bricef/2436364
     * @param  plainText   Text plain to cipher
     * @param encriptionKey Key obtained by Diffie-Hellman algorithm
     * @return     byte[] array with @plainText ciphered with @encriptionKey
     */
    public final static byte[] encrypt(String plainText,String encryptionKey) throws Exception
    {
        Cipher cipher       = Cipher.getInstance("AES/CBC/NoPadding","SunJCE");
        SecretKeySpec key   = new SecretKeySpec(encryptionKey.getBytes("UTF-8"),"AES");
        cipher.init(Cipher.ENCRYPT_MODE,key,new IvParameterSpec(ConstantsCrypto.iv.getBytes("UTF-8")));
        return cipher.doFinal(plainText.getBytes("UTF-8"));
    }
    
        /**
     * Thanks to bricef https://gist.github.com/bricef/2436364
     * @param  plainText   Text plain to decrypt
     * @param encriptionKey Key obtained by Diffie-Hellman algorithm
     * @return     byte[] array with @plainText ciphered with @encriptionKey
     */
    public final static String decrypt(byte[] cipherText,String encryptionKey) throws Exception
    {
        Cipher cipher       = Cipher.getInstance("AES/CBC/NoPadding","SunJCE");
        SecretKeySpec key   = new SecretKeySpec(encryptionKey.getBytes("UTF-8"),"AES");
        cipher.init(Cipher.DECRYPT_MODE,key,new IvParameterSpec(ConstantsCrypto.iv.getBytes("UTF-8")));
        return new String(cipher.doFinal(cipherText),"UTF-8");
    }
    
   /** public static void main(String args[]){
        /** TEST 
        try{
            String txt = "holaholaholahola";
            byte enc[] = {};
            System.out.println("holaholaholahola encrypted : " + (enc = AES.encrypt(txt,"1337133713371337")));
            System.out.println(enc + " decrypted : " + AES.decrypt(enc,"1337133713371337"));
        }catch(Exception e){ e.printStackTrace(); }
    }**/
}
[Package Configuration]

[ConstantsCrypto.java]

Código: Seleccionar todo

package Configuration;
import Utils.CryptographyUtils;
import java.math.BigInteger;
/**
 * Constants for cryptographic algorithms 
 * 
 * @author (Overxfl0w) 
 * @version (Test)
 */
public class ConstantsCrypto
{
    public static final BigInteger diffie_p = new BigInteger(String.valueOf(CryptographyUtils.get_prime((long)Math.pow(10,10),(int)(Math.random()))));
    public static final BigInteger diffie_g = new BigInteger(String.valueOf(CryptographyUtils.get_prime((long)Math.pow(5,5),(int)(Math.random()))));
    public static final String iv     = CryptographyUtils.get_iv(16);
    public static final String alphch = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
}
[ConstantsNetwork.java]

Código: Seleccionar todo

package Configuration;


/**
 * Write a description of class ConstantsNetwork here.
 * 
 * @author (Overxfl0w13) 
 * @version (Test)
 */
public class ConstantsNetwork
{
    public final static int    SNport = 1337;
    public static int          Clport = 1338;
    public static int          Otport = 1339; // For test tasks
    public static String user   = "Anonymous";
    public static String ipSN   = "192.168.1.130"; // Initial SuperNode ip
    public static Boolean conn  = new Boolean(false);
}
[Package Utils]
[CryptographyUtils.java]

Código: Seleccionar todo

package Utils;
import Configuration.ConstantsCrypto;
/**
 * Write a description of class CryptographyUtils here.
 * 
 * @author (Overxfl0w13) 
 * @version (Test)
 */
public class CryptographyUtils
{
    /**
     * Get a pseudorandom prime number starting at @baseNumber
     * @maxPrims = Max prime selection starting at 1
     */
    public final static long get_prime(long baseNumber,int maxPrims){
        int primsGet   = (int)(Math.random()*maxPrims)+1;
        int countPrims = 0;
        baseNumber     = (baseNumber%2==0) ? baseNumber+=1 : baseNumber;
        long lastPrim  = -1;
        for(long i=baseNumber;countPrims<primsGet;i+=2)
        {
            if (CryptographyUtils.is_prime(i)){
                countPrims++;
                lastPrim = i;
            }
        }
        
        return lastPrim;
    }
    
    /**
     * Check if given @pprime is prime
     */
    public final static boolean is_prime(long pprime){
        for(int j=2;j<(long)Math.sqrt(pprime);j++) if(pprime%j==0) return false;
        return true;
    }
    
    /**
     * @length Length of iv results
     * return iv
     */
    public final static String get_iv(int length){
        String res = "";
        for(int i=0;i<length;i++) res += ConstantsCrypto.alphch.charAt((int)(Math.random()*ConstantsCrypto.alphch.length()));
        return res;
        
    }
    
    /**
     * @baseKey fill with 0 baseKey length to 16B
     * return iv
     */
    public final static String fill_dh_key(String baseKey){
        for(int i=baseKey.length();i<16;i++) baseKey += "0";
        return baseKey;
    }
    
        
    /**public static void main(String args[]){
        System.out.println(CryptographyUtils.get_iv(128));
    }**/
    /**public static void main(String args[]){
        System.out.println(CryptographyUtils.get_prime((long)Math.pow(13,10),(int)(Math.random()*100)+20));
    }**/
}
[CoreUtils.java]

Código: Seleccionar todo

package Utils;

public class CoreUtils
{
    public final static String fill_text_16(String text){
        int nextMultiple = next_multiple(text.length());
        for(int i=text.length();i<nextMultiple-2;i++) text += "\00";
        return text;
    }
    
    private final static int next_multiple(int length){
        length         = (length%2==0) ? length : length+1;
        boolean finded = false;
        int multiple   = length;
        for(;multiple<length*2 && !finded;multiple+=2){ if(multiple%16==0) finded = true; }
        return multiple;
    }
}
Imagen


Un saludo :)
Te quedo de putisima madre tio, podrias haber colocado el codigo con resaltado de sintaxys para que otros programadores puedan leerlo mejor xD, pero eso es lo de menos, este chat tendria futuro como un buen proyecto, salu2
Abolición para el torneo del toro de la vega. Death to the murderers of bulls.
Responder

Volver a “Fuentes”