001: /* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
002: /*
003: Copyright (c) 2002-2008 ymnk, JCraft,Inc. All rights reserved.
004:
005: Redistribution and use in source and binary forms, with or without
006: modification, are permitted provided that the following conditions are met:
007:
008: 1. Redistributions of source code must retain the above copyright notice,
009: this list of conditions and the following disclaimer.
010:
011: 2. Redistributions in binary form must reproduce the above copyright
012: notice, this list of conditions and the following disclaimer in
013: the documentation and/or other materials provided with the distribution.
014:
015: 3. The names of the authors may not be used to endorse or promote products
016: derived from this software without specific prior written permission.
017:
018: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
019: INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
020: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
021: INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
022: INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
023: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
024: OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
025: LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
026: NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
027: EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028: */
029:
030: package com.jcraft.jsch;
031:
032: public class DHGEX extends KeyExchange {
033:
034: private static final int SSH_MSG_KEX_DH_GEX_GROUP = 31;
035: private static final int SSH_MSG_KEX_DH_GEX_INIT = 32;
036: private static final int SSH_MSG_KEX_DH_GEX_REPLY = 33;
037: private static final int SSH_MSG_KEX_DH_GEX_REQUEST = 34;
038:
039: static int min = 1024;
040:
041: // static int min=512;
042: static int preferred = 1024;
043: static int max = 1024;
044:
045: // static int preferred=1024;
046: // static int max=2000;
047:
048: static final int RSA = 0;
049: static final int DSS = 1;
050: private int type = 0;
051:
052: private int state;
053:
054: // com.jcraft.jsch.DH dh;
055: DH dh;
056:
057: byte[] V_S;
058: byte[] V_C;
059: byte[] I_S;
060: byte[] I_C;
061:
062: private Buffer buf;
063: private Packet packet;
064:
065: private byte[] p;
066: private byte[] g;
067: private byte[] e;
068:
069: //private byte[] f;
070:
071: public void init(Session session, byte[] V_S, byte[] V_C,
072: byte[] I_S, byte[] I_C) throws Exception {
073: this .session = session;
074: this .V_S = V_S;
075: this .V_C = V_C;
076: this .I_S = I_S;
077: this .I_C = I_C;
078:
079: try {
080: Class c = Class.forName(session.getConfig("sha-1"));
081: sha = (HASH) (c.newInstance());
082: sha.init();
083: } catch (Exception e) {
084: System.err.println(e);
085: }
086:
087: buf = new Buffer();
088: packet = new Packet(buf);
089:
090: try {
091: Class c = Class.forName(session.getConfig("dh"));
092: dh = (com.jcraft.jsch.DH) (c.newInstance());
093: dh.init();
094: } catch (Exception e) {
095: // System.err.println(e);
096: throw e;
097: }
098:
099: packet.reset();
100: buf.putByte((byte) SSH_MSG_KEX_DH_GEX_REQUEST);
101: buf.putInt(min);
102: buf.putInt(preferred);
103: buf.putInt(max);
104: session.write(packet);
105:
106: if (JSch.getLogger().isEnabled(Logger.INFO)) {
107: JSch.getLogger().log(
108: Logger.INFO,
109: "SSH_MSG_KEX_DH_GEX_REQUEST(" + min + "<"
110: + preferred + "<" + max + ") sent");
111: JSch.getLogger().log(Logger.INFO,
112: "expecting SSH_MSG_KEX_DH_GEX_GROUP");
113: }
114:
115: state = SSH_MSG_KEX_DH_GEX_GROUP;
116: }
117:
118: public boolean next(Buffer _buf) throws Exception {
119: int i, j;
120: switch (state) {
121: case SSH_MSG_KEX_DH_GEX_GROUP:
122: // byte SSH_MSG_KEX_DH_GEX_GROUP(31)
123: // mpint p, safe prime
124: // mpint g, generator for subgroup in GF (p)
125: _buf.getInt();
126: _buf.getByte();
127: j = _buf.getByte();
128: if (j != SSH_MSG_KEX_DH_GEX_GROUP) {
129: System.err
130: .println("type: must be SSH_MSG_KEX_DH_GEX_GROUP "
131: + j);
132: return false;
133: }
134:
135: p = _buf.getMPInt();
136: g = _buf.getMPInt();
137: /*
138: for(int iii=0; iii<p.length; iii++){
139: System.err.println("0x"+Integer.toHexString(p[iii]&0xff)+",");
140: }
141: System.err.println("");
142: for(int iii=0; iii<g.length; iii++){
143: System.err.println("0x"+Integer.toHexString(g[iii]&0xff)+",");
144: }
145: */
146: dh.setP(p);
147: dh.setG(g);
148:
149: // The client responds with:
150: // byte SSH_MSG_KEX_DH_GEX_INIT(32)
151: // mpint e <- g^x mod p
152: // x is a random number (1 < x < (p-1)/2)
153:
154: e = dh.getE();
155:
156: packet.reset();
157: buf.putByte((byte) SSH_MSG_KEX_DH_GEX_INIT);
158: buf.putMPInt(e);
159: session.write(packet);
160:
161: if (JSch.getLogger().isEnabled(Logger.INFO)) {
162: JSch.getLogger().log(Logger.INFO,
163: "SSH_MSG_KEX_DH_GEX_INIT sent");
164: JSch.getLogger().log(Logger.INFO,
165: "expecting SSH_MSG_KEX_DH_GEX_REPLY");
166: }
167:
168: state = SSH_MSG_KEX_DH_GEX_REPLY;
169: return true;
170: //break;
171:
172: case SSH_MSG_KEX_DH_GEX_REPLY:
173: // The server responds with:
174: // byte SSH_MSG_KEX_DH_GEX_REPLY(33)
175: // string server public host key and certificates (K_S)
176: // mpint f
177: // string signature of H
178: j = _buf.getInt();
179: j = _buf.getByte();
180: j = _buf.getByte();
181: if (j != SSH_MSG_KEX_DH_GEX_REPLY) {
182: System.err
183: .println("type: must be SSH_MSG_KEX_DH_GEX_REPLY "
184: + j);
185: return false;
186: }
187:
188: K_S = _buf.getString();
189: // K_S is server_key_blob, which includes ....
190: // string ssh-dss
191: // impint p of dsa
192: // impint q of dsa
193: // impint g of dsa
194: // impint pub_key of dsa
195: //System.err.print("K_S: "); dump(K_S, 0, K_S.length);
196:
197: byte[] f = _buf.getMPInt();
198: byte[] sig_of_H = _buf.getString();
199:
200: dh.setF(f);
201: K = dh.getK();
202:
203: //The hash H is computed as the HASH hash of the concatenation of the
204: //following:
205: // string V_C, the client's version string (CR and NL excluded)
206: // string V_S, the server's version string (CR and NL excluded)
207: // string I_C, the payload of the client's SSH_MSG_KEXINIT
208: // string I_S, the payload of the server's SSH_MSG_KEXINIT
209: // string K_S, the host key
210: // uint32 min, minimal size in bits of an acceptable group
211: // uint32 n, preferred size in bits of the group the server should send
212: // uint32 max, maximal size in bits of an acceptable group
213: // mpint p, safe prime
214: // mpint g, generator for subgroup
215: // mpint e, exchange value sent by the client
216: // mpint f, exchange value sent by the server
217: // mpint K, the shared secret
218: // This value is called the exchange hash, and it is used to authenti-
219: // cate the key exchange.
220:
221: buf.reset();
222: buf.putString(V_C);
223: buf.putString(V_S);
224: buf.putString(I_C);
225: buf.putString(I_S);
226: buf.putString(K_S);
227: buf.putInt(min);
228: buf.putInt(preferred);
229: buf.putInt(max);
230: buf.putMPInt(p);
231: buf.putMPInt(g);
232: buf.putMPInt(e);
233: buf.putMPInt(f);
234: buf.putMPInt(K);
235:
236: byte[] foo = new byte[buf.getLength()];
237: buf.getByte(foo);
238: sha.update(foo, 0, foo.length);
239:
240: H = sha.digest();
241:
242: // System.err.print("H -> "); dump(H, 0, H.length);
243:
244: i = 0;
245: j = 0;
246: j = ((K_S[i++] << 24) & 0xff000000)
247: | ((K_S[i++] << 16) & 0x00ff0000)
248: | ((K_S[i++] << 8) & 0x0000ff00)
249: | ((K_S[i++]) & 0x000000ff);
250: String alg = new String(K_S, i, j);
251: i += j;
252:
253: boolean result = false;
254: if (alg.equals("ssh-rsa")) {
255: byte[] tmp;
256: byte[] ee;
257: byte[] n;
258:
259: type = RSA;
260:
261: j = ((K_S[i++] << 24) & 0xff000000)
262: | ((K_S[i++] << 16) & 0x00ff0000)
263: | ((K_S[i++] << 8) & 0x0000ff00)
264: | ((K_S[i++]) & 0x000000ff);
265: tmp = new byte[j];
266: System.arraycopy(K_S, i, tmp, 0, j);
267: i += j;
268: ee = tmp;
269: j = ((K_S[i++] << 24) & 0xff000000)
270: | ((K_S[i++] << 16) & 0x00ff0000)
271: | ((K_S[i++] << 8) & 0x0000ff00)
272: | ((K_S[i++]) & 0x000000ff);
273: tmp = new byte[j];
274: System.arraycopy(K_S, i, tmp, 0, j);
275: i += j;
276: n = tmp;
277:
278: // SignatureRSA sig=new SignatureRSA();
279: // sig.init();
280:
281: SignatureRSA sig = null;
282: try {
283: Class c = Class.forName(session
284: .getConfig("signature.rsa"));
285: sig = (SignatureRSA) (c.newInstance());
286: sig.init();
287: } catch (Exception e) {
288: System.err.println(e);
289: }
290:
291: sig.setPubKey(ee, n);
292: sig.update(H);
293: result = sig.verify(sig_of_H);
294:
295: if (JSch.getLogger().isEnabled(Logger.INFO)) {
296: JSch.getLogger().log(Logger.INFO,
297: "ssh_rsa_verify: signature " + result);
298: }
299:
300: } else if (alg.equals("ssh-dss")) {
301: byte[] q = null;
302: byte[] tmp;
303:
304: type = DSS;
305:
306: j = ((K_S[i++] << 24) & 0xff000000)
307: | ((K_S[i++] << 16) & 0x00ff0000)
308: | ((K_S[i++] << 8) & 0x0000ff00)
309: | ((K_S[i++]) & 0x000000ff);
310: tmp = new byte[j];
311: System.arraycopy(K_S, i, tmp, 0, j);
312: i += j;
313: p = tmp;
314: j = ((K_S[i++] << 24) & 0xff000000)
315: | ((K_S[i++] << 16) & 0x00ff0000)
316: | ((K_S[i++] << 8) & 0x0000ff00)
317: | ((K_S[i++]) & 0x000000ff);
318: tmp = new byte[j];
319: System.arraycopy(K_S, i, tmp, 0, j);
320: i += j;
321: q = tmp;
322: j = ((K_S[i++] << 24) & 0xff000000)
323: | ((K_S[i++] << 16) & 0x00ff0000)
324: | ((K_S[i++] << 8) & 0x0000ff00)
325: | ((K_S[i++]) & 0x000000ff);
326: tmp = new byte[j];
327: System.arraycopy(K_S, i, tmp, 0, j);
328: i += j;
329: g = tmp;
330: j = ((K_S[i++] << 24) & 0xff000000)
331: | ((K_S[i++] << 16) & 0x00ff0000)
332: | ((K_S[i++] << 8) & 0x0000ff00)
333: | ((K_S[i++]) & 0x000000ff);
334: tmp = new byte[j];
335: System.arraycopy(K_S, i, tmp, 0, j);
336: i += j;
337: f = tmp;
338:
339: // SignatureDSA sig=new SignatureDSA();
340: // sig.init();
341:
342: SignatureDSA sig = null;
343: try {
344: Class c = Class.forName(session
345: .getConfig("signature.dss"));
346: sig = (SignatureDSA) (c.newInstance());
347: sig.init();
348: } catch (Exception e) {
349: System.err.println(e);
350: }
351:
352: sig.setPubKey(f, p, q, g);
353: sig.update(H);
354: result = sig.verify(sig_of_H);
355:
356: if (JSch.getLogger().isEnabled(Logger.INFO)) {
357: JSch.getLogger().log(Logger.INFO,
358: "ssh_dss_verify: signature " + result);
359: }
360:
361: } else {
362: System.err.println("unknown alg");
363: }
364: state = STATE_END;
365: return result;
366: }
367: return false;
368: }
369:
370: public String getKeyType() {
371: if (type == DSS)
372: return "DSA";
373: return "RSA";
374: }
375:
376: public int getState() {
377: return state;
378: }
379: }
|