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]";
}
}