import djb.Curve25519;
import java.security.SecureRandom;
import org.apache.sshd.common.digest.BuiltinDigests;
import org.apache.sshd.common.digest.Digest;
import org.apache.sshd.common.kex.AbstractDH;


public class Curve25519SHA256AbstractDH extends AbstractDH
 {

 private final byte q_s[]=new byte[32]; //server's ephemeral public key octet string
 private final byte q_c[]=new byte[32]; // //client's ephemeral public key octet string
 private final byte privatekeyforkeyagreement[]=new byte[32];
 private final byte k_as_byte_array[]=new byte[32];
 
 public Curve25519SHA256AbstractDH(SecureRandom securerandom) throws Exception
  {
  super();
  //generate public key and private key for key agreement
  securerandom.nextBytes(privatekeyforkeyagreement);
  Curve25519.keygen(q_s, null, privatekeyforkeyagreement);
  }
 
 @Override
 public void setF(byte[] bytes) 
  {
  System.arraycopy(bytes, 0, q_c, 0, bytes.length);
  }

 @Override
 public byte[] getE() throws Exception 
  {
  return q_s;
  }

 @Override
 protected byte[] calculateK() throws Exception 
  {
  //create shared secret
  Curve25519.curve(k_as_byte_array, privatekeyforkeyagreement, q_c);
  //The whole 32 bytes need to be converted into a big integer following the network byte order
  return stripLeadingZeroes(k_as_byte_array);
  }

 @Override
 public Digest getHash() throws Exception 
  {
  return BuiltinDigests.sha256.create();
  }
 
 }

import java.security.SecureRandom;
import org.apache.sshd.common.kex.DHFactory;


public class Curve25519SHA256DHFactory implements DHFactory
 {

 private final SecureRandom securerandom;
 
 public Curve25519SHA256DHFactory(SecureRandom securerandom)
  {
  this.securerandom=securerandom;
  }
 
 @Override
 public boolean isGroupExchange() 
  {
  return false;
  }

 @Override
 public Curve25519SHA256AbstractDH create(Object... os) throws Exception      
  {
  return new Curve25519SHA256AbstractDH(securerandom);
  }

 @Override
 public String getName() 
  {
  return "[email protected]";
  }

 @Override
 public boolean isSupported() 
  {
  return true;
  }
    
 }
import java.security.KeyPair;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.util.Arrays;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.SshException;
import org.apache.sshd.common.digest.BuiltinDigests;
import org.apache.sshd.common.digest.Digest;
import org.apache.sshd.common.kex.KexProposalOption;
import org.apache.sshd.common.kex.KeyExchange;
import org.apache.sshd.common.session.Session;
import org.apache.sshd.common.signature.Signature;
import org.apache.sshd.common.util.NumberUtils;
import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.buffer.BufferUtils;
import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
import org.apache.sshd.server.session.ServerSession;




public class Curve25519SHA256KeyExchange implements KeyExchange
 {

 ServerSession session;
 private byte[] v_s;
 private byte[] v_c;
 private byte[] i_s;
 private byte[] i_c;
 

 byte privatekeykeyagreement[]=new byte[32];
 private final byte q_s[]=new byte[32]; //server's ephemeral public key octet string
 private byte q_c[]; //client's ephemeral public key octet string
 private byte k[]=new byte[32]; //shared secret
 private byte h[];  //hash
 
 KeyPair keypair;
 SecureRandom securerandom;
 Digest hash;
 
 
 public Curve25519SHA256KeyExchange(SecureRandom securerandom) throws Exception
  {
  this.securerandom=securerandom;
  hash=BuiltinDigests.sha256.create();
  }
 
 /*
 session - the session using this algorithm
 v_s - the server identification string
 v_c - the client identification string
 i_s - the server key init packet
 i_c - the client key init packet
 */
 
 @Override
 public void init(Session session, byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception 
  {
      

  this.session=(ServerSession)session;
  this.v_s = v_s;
  this.v_c = v_c;
  this.i_s = Arrays.copyOf(i_s, i_s.length);
  this.i_c = Arrays.copyOf(i_c, i_c.length);
  
  //generate public key and private key for key agreement
  securerandom.nextBytes(privatekeykeyagreement);
  djb.Curve25519.keygen(q_s, null, privatekeykeyagreement);
  
  //init digest
  hash.init();
  

  }

 /*
 cmd - the command
 buffer - the packet contents positioned after the command
 */
 @Override
 public boolean next(int messagenumber, Buffer buffer) throws Exception 
  {
      
  //RFC 5656
      
  // SSH_MSG_KEX_ECDH_INIT                30
  // SSH_MSG_KEX_ECDH_REPLY               31
      
  /*
      The client sends:

      byte     SSH_MSG_KEX_ECDH_INIT
      string   Q_C, client's ephemeral public key octet string

   The server responds with:

      byte     SSH_MSG_KEX_ECDH_REPLY
      string   K_S, server's public host key
      string   Q_S, server's ephemeral public key octet string
      string   the signature on the exchange hash
      
       The exchange hash H is computed as the hash of the concatenation of
   the following.

      string   V_C, client's identification string (CR and LF excluded)
      string   V_S, server's identification string (CR and LF excluded)
      string   I_C, payload of the client's SSH_MSG_KEXINIT
      string   I_S, payload of the server's SSH_MSG_KEXINIT
      string   K_S, server's public host key
      string   Q_C, client's ephemeral public key octet string
      string   Q_S, server's ephemeral public key octet string
      mpint    K,   shared secret
      
      
      */
      

  
  if (messagenumber != SshConstants.SSH_MSG_KEXDH_INIT) 
   {
   throw new SshException(SshConstants.SSH2_DISCONNECT_KEY_EXCHANGE_FAILED,"Protocol error: expected packet SSH_MSG_KEXDH_INIT, got " + KeyExchange.getSimpleKexOpcodeName(messagenumber));
   }


  keypair=session.getHostKey();

  q_c = buffer.getMPIntAsBytes();
  djb.Curve25519.curve(k, privatekeykeyagreement, q_c);
  //The shared secret, k, is defined in SSH specifications to be a big integer
  //The whole 32 bytes need to be converted into a big integer.
  k=stripLeadingZeroes(k);

  String serverkeysoption = session.getNegotiatedKexParameter(KexProposalOption.SERVERKEYS);

  Signature sig = ValidateUtils.checkNotNull(NamedFactory.create(session.getSignatureFactories(), serverkeysoption),"Unknown negotiated server keys: %s",serverkeysoption);
  sig.initSigner(keypair.getPrivate());
  

  buffer = new ByteArrayBuffer();
  buffer.putRawPublicKey(keypair.getPublic());
  byte[] k_s = buffer.getCompactData();


  buffer.clear();
  buffer.putBytes(v_c);
  buffer.putBytes(v_s);
  buffer.putBytes(i_c);
  buffer.putBytes(i_s);
  buffer.putBytes(k_s);
  buffer.putBytes(q_c);
  buffer.putBytes(q_s);
  buffer.putMPInt(k);
  
  hash.update(buffer.array(), 0, buffer.available());
  h = hash.digest();
  
  sig.update(h);

  buffer.clear();
  buffer.putString(serverkeysoption);
  byte[] sigBytes = sig.sign();
  buffer.putBytes(sigBytes);

  byte[] sigH = buffer.getCompactData();
  

  buffer = session.prepareBuffer(SshConstants.SSH_MSG_KEXDH_REPLY, BufferUtils.clear(buffer));
  
  
  buffer.putBytes(k_s);
  buffer.putBytes(q_s);
  buffer.putBytes(sigH);
  
  session.writePacket(buffer);

  return true;


  }

 //The message digest used by this key exchange algorithm.
 @Override
 public Digest getHash() 
  {
  return hash;
  }

 //Retrieves the computed h parameter
 @Override
 public byte[] getH() 
  {
  return h;
  }
 
 //retrieves shared secret
 @Override
 public byte[] getK() 
  {
  return k;
  }

 //Retrieves the server's key
 @Override
 public PublicKey getServerKey() 
  {
  return keypair.getPublic();
  }

 //get name
 @Override
 public String getName() 
  {
  return "[email protected]";
  }
 
 
 public static byte[] stripLeadingZeroes(byte[] x) 
  {
  int length = NumberUtils.length(x);
  for (int i = 0; i < x.length; i++) 
   {
   if (x[i] == 0) 
    {
    continue;
    }

   if (i == 0) 
    {   // 1st byte is non-zero so nothing to do
    return x;
    }

   byte[] ret = new byte[length - i];
   System.arraycopy(x, i, ret, 0, ret.length);
   return ret;
   }

  // all zeroes
  throw new IllegalArgumentException("No non-zero values in generated secret");
  }
    

 }

import java.security.SecureRandom;
import org.apache.sshd.common.kex.KeyExchange;
import org.apache.sshd.common.kex.KeyExchangeFactory;


public class Curve25519SHA256KeyExchangeFactory implements KeyExchangeFactory
 {

 Curve25519SHA256KeyExchange curve25519_Sha256_KeyExchange;
 SecureRandom securerandom;
 
 public Curve25519SHA256KeyExchangeFactory(SecureRandom securerandom)
  {
  this.securerandom=securerandom;
  }
 
 @Override
 public KeyExchange create() 
  {
  try
   {
   curve25519_Sha256_KeyExchange=new Curve25519SHA256KeyExchange(securerandom);
   return curve25519_Sha256_KeyExchange;
   } 
  catch (Exception e)
   {
   return null;
   }
  }

 @Override
 public KeyExchange get() 
  {
  return curve25519_Sha256_KeyExchange;
  }

 @Override
 public String getName() 
  {
  return "[email protected]";
  }
    
 }

Reply via email to