diff options
Diffstat (limited to 'java/com/jcraft/jsch/DHGEX.java')
-rw-r--r-- | java/com/jcraft/jsch/DHGEX.java | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/java/com/jcraft/jsch/DHGEX.java b/java/com/jcraft/jsch/DHGEX.java new file mode 100644 index 00000000..23fa9ebf --- /dev/null +++ b/java/com/jcraft/jsch/DHGEX.java @@ -0,0 +1,340 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class DHGEX extends KeyExchange{ + + private static final int SSH_MSG_KEX_DH_GEX_GROUP= 31; + private static final int SSH_MSG_KEX_DH_GEX_INIT= 32; + private static final int SSH_MSG_KEX_DH_GEX_REPLY= 33; + private static final int SSH_MSG_KEX_DH_GEX_REQUEST= 34; + + static int min=1024; + +// static int min=512; + static int preferred=1024; + static int max=1024; + +// static int preferred=1024; +// static int max=2000; + + static final int RSA=0; + static final int DSS=1; + private int type=0; + + private int state; + +// com.jcraft.jsch.DH dh; + DH dh; + + byte[] V_S; + byte[] V_C; + byte[] I_S; + byte[] I_C; + + private Buffer buf; + private Packet packet; + + private byte[] p; + private byte[] g; + private byte[] e; + //private byte[] f; + + public void init(Session session, + byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C) throws Exception{ + this.session=session; + this.V_S=V_S; + this.V_C=V_C; + this.I_S=I_S; + this.I_C=I_C; + + try{ + Class c=Class.forName(session.getConfig("sha-1")); + sha=(HASH)(c.newInstance()); + sha.init(); + } + catch(Exception e){ + System.err.println(e); + } + + buf=new Buffer(); + packet=new Packet(buf); + + try{ + Class c=Class.forName(session.getConfig("dh")); + dh=(com.jcraft.jsch.DH)(c.newInstance()); + dh.init(); + } + catch(Exception e){ +// System.err.println(e); + throw e; + } + + packet.reset(); + buf.putByte((byte)SSH_MSG_KEX_DH_GEX_REQUEST); + buf.putInt(min); + buf.putInt(preferred); + buf.putInt(max); + session.write(packet); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "SSH_MSG_KEX_DH_GEX_REQUEST("+min+"<"+preferred+"<"+max+") sent"); + JSch.getLogger().log(Logger.INFO, + "expecting SSH_MSG_KEX_DH_GEX_GROUP"); + } + + state=SSH_MSG_KEX_DH_GEX_GROUP; + } + + public boolean next(Buffer _buf) throws Exception{ + int i,j; + switch(state){ + case SSH_MSG_KEX_DH_GEX_GROUP: + // byte SSH_MSG_KEX_DH_GEX_GROUP(31) + // mpint p, safe prime + // mpint g, generator for subgroup in GF (p) + _buf.getInt(); + _buf.getByte(); + j=_buf.getByte(); + if(j!=SSH_MSG_KEX_DH_GEX_GROUP){ + System.err.println("type: must be SSH_MSG_KEX_DH_GEX_GROUP "+j); + return false; + } + + p=_buf.getMPInt(); + g=_buf.getMPInt(); + /* +for(int iii=0; iii<p.length; iii++){ +System.err.println("0x"+Integer.toHexString(p[iii]&0xff)+","); +} +System.err.println(""); +for(int iii=0; iii<g.length; iii++){ +System.err.println("0x"+Integer.toHexString(g[iii]&0xff)+","); +} + */ + dh.setP(p); + dh.setG(g); + + // The client responds with: + // byte SSH_MSG_KEX_DH_GEX_INIT(32) + // mpint e <- g^x mod p + // x is a random number (1 < x < (p-1)/2) + + e=dh.getE(); + + packet.reset(); + buf.putByte((byte)SSH_MSG_KEX_DH_GEX_INIT); + buf.putMPInt(e); + session.write(packet); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "SSH_MSG_KEX_DH_GEX_INIT sent"); + JSch.getLogger().log(Logger.INFO, + "expecting SSH_MSG_KEX_DH_GEX_REPLY"); + } + + state=SSH_MSG_KEX_DH_GEX_REPLY; + return true; + //break; + + case SSH_MSG_KEX_DH_GEX_REPLY: + // The server responds with: + // byte SSH_MSG_KEX_DH_GEX_REPLY(33) + // string server public host key and certificates (K_S) + // mpint f + // string signature of H + j=_buf.getInt(); + j=_buf.getByte(); + j=_buf.getByte(); + if(j!=SSH_MSG_KEX_DH_GEX_REPLY){ + System.err.println("type: must be SSH_MSG_KEX_DH_GEX_REPLY "+j); + return false; + } + + K_S=_buf.getString(); + // K_S is server_key_blob, which includes .... + // string ssh-dss + // impint p of dsa + // impint q of dsa + // impint g of dsa + // impint pub_key of dsa + //System.err.print("K_S: "); dump(K_S, 0, K_S.length); + + byte[] f=_buf.getMPInt(); + byte[] sig_of_H=_buf.getString(); + + dh.setF(f); + K=dh.getK(); + + //The hash H is computed as the HASH hash of the concatenation of the + //following: + // string V_C, the client's version string (CR and NL excluded) + // string V_S, the server's version string (CR and NL excluded) + // string I_C, the payload of the client's SSH_MSG_KEXINIT + // string I_S, the payload of the server's SSH_MSG_KEXINIT + // string K_S, the host key + // uint32 min, minimal size in bits of an acceptable group + // uint32 n, preferred size in bits of the group the server should send + // uint32 max, maximal size in bits of an acceptable group + // mpint p, safe prime + // mpint g, generator for subgroup + // mpint e, exchange value sent by the client + // mpint f, exchange value sent by the server + // mpint K, the shared secret + // This value is called the exchange hash, and it is used to authenti- + // cate the key exchange. + + buf.reset(); + buf.putString(V_C); buf.putString(V_S); + buf.putString(I_C); buf.putString(I_S); + buf.putString(K_S); + buf.putInt(min); buf.putInt(preferred); buf.putInt(max); + buf.putMPInt(p); buf.putMPInt(g); buf.putMPInt(e); buf.putMPInt(f); + buf.putMPInt(K); + + byte[] foo=new byte[buf.getLength()]; + buf.getByte(foo); + sha.update(foo, 0, foo.length); + + H=sha.digest(); + + // System.err.print("H -> "); dump(H, 0, H.length); + + i=0; + j=0; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + String alg=Util.byte2str(K_S, i, j); + i+=j; + + boolean result=false; + if(alg.equals("ssh-rsa")){ + byte[] tmp; + byte[] ee; + byte[] n; + + type=RSA; + + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + ee=tmp; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + n=tmp; + +// SignatureRSA sig=new SignatureRSA(); +// sig.init(); + + SignatureRSA sig=null; + try{ + Class c=Class.forName(session.getConfig("signature.rsa")); + sig=(SignatureRSA)(c.newInstance()); + sig.init(); + } + catch(Exception e){ + System.err.println(e); + } + + sig.setPubKey(ee, n); + sig.update(H); + result=sig.verify(sig_of_H); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "ssh_rsa_verify: signature "+result); + } + + } + else if(alg.equals("ssh-dss")){ + byte[] q=null; + byte[] tmp; + + type=DSS; + + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + p=tmp; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + q=tmp; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + g=tmp; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + f=tmp; + +// SignatureDSA sig=new SignatureDSA(); +// sig.init(); + + SignatureDSA sig=null; + try{ + Class c=Class.forName(session.getConfig("signature.dss")); + sig=(SignatureDSA)(c.newInstance()); + sig.init(); + } + catch(Exception e){ + System.err.println(e); + } + + sig.setPubKey(f, p, q, g); + sig.update(H); + result=sig.verify(sig_of_H); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "ssh_dss_verify: signature "+result); + } + + } + else{ + System.err.println("unknown alg"); + } + state=STATE_END; + return result; + } + return false; + } + + public String getKeyType(){ + if(type==DSS) return "DSA"; + return "RSA"; + } + + public int getState(){return state; } +} |