files/files-jsch/src/main/java/com/jcraft/jsch/DHECN.java

188 lines
6 KiB
Java
Raw Normal View History

2024-07-23 22:00:58 +02:00
/*
* Copyright (c) 2015-2018 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;
abstract class DHECN extends KeyExchange {
private static final int SSH_MSG_KEX_ECDH_INIT = 30;
private static final int SSH_MSG_KEX_ECDH_REPLY = 31;
private int state;
byte[] Q_C;
byte[] V_S;
byte[] V_C;
byte[] I_S;
byte[] I_C;
byte[] e;
private Buffer buf;
private Packet packet;
private ECDH ecdh;
protected String sha_name;
protected int key_size;
@Override
public void init(Session session, byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C)
throws Exception {
this.V_S = V_S;
this.V_C = V_C;
this.I_S = I_S;
this.I_C = I_C;
try {
Class<? extends HASH> c = Class.forName(session.getConfig(sha_name)).asSubclass(HASH.class);
sha = c.getDeclaredConstructor().newInstance();
sha.init();
} catch (Exception e) {
throw new JSchException(e.toString(), e);
}
buf = new Buffer();
packet = new Packet(buf);
packet.reset();
buf.putByte((byte) SSH_MSG_KEX_ECDH_INIT);
try {
Class<? extends ECDH> c =
Class.forName(session.getConfig("ecdh-sha2-nistp")).asSubclass(ECDH.class);
ecdh = c.getDeclaredConstructor().newInstance();
ecdh.init(key_size);
Q_C = ecdh.getQ();
buf.putString(Q_C);
} catch (Exception e) {
throw new JSchException(e.toString(), e);
}
if (V_S == null) { // This is a really ugly hack for Session.checkKexes ;-(
return;
}
session.write(packet);
if (session.getLogger().isEnabled(Logger.INFO)) {
session.getLogger().log(Logger.INFO, "SSH_MSG_KEX_ECDH_INIT sent");
session.getLogger().log(Logger.INFO, "expecting SSH_MSG_KEX_ECDH_REPLY");
}
state = SSH_MSG_KEX_ECDH_REPLY;
}
@Override
public boolean next(Buffer _buf) throws Exception {
int i, j;
switch (state) {
case SSH_MSG_KEX_ECDH_REPLY:
// 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
j = _buf.getInt();
j = _buf.getByte();
j = _buf.getByte();
if (j != SSH_MSG_KEX_ECDH_REPLY) {
if (session.getLogger().isEnabled(Logger.ERROR)) {
session.getLogger().log(Logger.ERROR, "type: must be SSH_MSG_KEX_ECDH_REPLY " + j);
}
return false;
}
K_S = _buf.getString();
byte[] Q_S = _buf.getString();
byte[][] r_s = KeyPairECDSA.fromPoint(Q_S);
// RFC 5656,
// 4. ECDH Key Exchange
// All elliptic curve public keys MUST be validated after they are
// received. An example of a validation algorithm can be found in
// Section 3.2.2 of [SEC1]. If a key fails validation,
// the key exchange MUST fail.
if (!ecdh.validate(r_s[0], r_s[1])) {
return false;
}
K = encodeAsMPInt(normalize(ecdh.getSecret(r_s[0], r_s[1])));
byte[] sig_of_H = _buf.getString();
// The hash H is computed as the HASH 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
// 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.putString(Q_C);
buf.putString(Q_S);
byte[] foo = new byte[buf.getLength()];
buf.getByte(foo);
sha.update(foo, 0, foo.length);
sha.update(K, 0, K.length);
H = sha.digest();
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 = verify(alg, K_S, i, sig_of_H);
state = STATE_END;
return result;
}
return false;
}
@Override
public int getState() {
return state;
}
}