1
0
mirror of https://git.libssh.org/projects/libssh.git synced 2025-05-28 17:41:28 +03:00

[crypto] initial support for ecdh-sha2-nistp256

Works with openssl
Still requires work for libgcrypt and other modes
This commit is contained in:
Aris Adamantiadis 2011-06-13 13:46:34 +02:00
parent 3b72bf0880
commit c5a998f47a
18 changed files with 638 additions and 164 deletions

View File

@ -35,6 +35,10 @@
/* Define to 1 if you have the <openssl/des.h> header file. */
#cmakedefine HAVE_OPENSSL_DES_H 1
/* Define to 1 if you have the <openssl/ecdh.h> header file. */
#cmakedefine HAVE_OPENSSL_ECDH_H 1
/* Define to 1 if you have the <pthread.h> header file. */
#cmakedefine HAVE_PTHREAD_H 1

View File

@ -40,18 +40,31 @@
#undef cbc_decrypt
#endif
#ifdef HAVE_OPENSSL_ECDH_H
#include <openssl/ecdh.h>
#endif
enum ssh_key_exchange_e {
/* diffie-hellman-group1-sha1 */
SSH_KEX_DH_GROUP1_SHA1=1,
/* ecdh-sha2-nistp256 */
SSH_KEX_ECDH_SHA2_NISTP256
};
struct ssh_crypto_struct {
bignum e,f,x,k,y;
unsigned char session_id[SHA_DIGEST_LEN];
unsigned char encryptIV[SHA_DIGEST_LEN*2];
unsigned char decryptIV[SHA_DIGEST_LEN*2];
unsigned char decryptkey[SHA_DIGEST_LEN*2];
unsigned char encryptkey[SHA_DIGEST_LEN*2];
unsigned char encryptMAC[SHA_DIGEST_LEN];
unsigned char decryptMAC[SHA_DIGEST_LEN];
EC_KEY *ecdh_privkey;
ssh_string ecdh_client_pubkey;
ssh_string ecdh_server_pubkey;
ssh_string dh_server_signature; /* information used by dh_handshake. */
size_t digest_len; /* len of all the fields below */
unsigned char *session_id;
unsigned char *encryptIV;
unsigned char *decryptIV;
unsigned char *decryptkey;
unsigned char *encryptkey;
unsigned char *encryptMAC;
unsigned char *decryptMAC;
unsigned char hmacbuf[EVP_MAX_MD_SIZE];
struct crypto_struct *in_cipher, *out_cipher; /* the cipher structures/objects */
ssh_string server_pubkey;
@ -62,6 +75,8 @@ struct ssh_crypto_struct {
int delayed_compress_out;
void *compress_out_ctx; /* don't touch it */
void *compress_in_ctx; /* really, don't */
enum ssh_key_exchange_e kex_type;
enum ssh_mac_e mac_type; /* Mac operations to use for key gen */
};
struct crypto_struct {

View File

@ -41,6 +41,9 @@ int dh_import_f(ssh_session session,ssh_string f_string);
int dh_import_e(ssh_session session, ssh_string e_string);
void dh_import_pubkey(ssh_session session,ssh_string pubkey_string);
int dh_build_k(ssh_session session);
int ssh_client_dh_init(ssh_session session);
int ssh_client_dh_reply(ssh_session session, ssh_buffer packet);
int make_sessionid(ssh_session session);
/* add data for the final cookie */
int hashbufin_add_cookie(ssh_session session, unsigned char *cookie);

39
include/libssh/ecdh.h Normal file
View File

@ -0,0 +1,39 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2011 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#ifndef ECDH_H_
#define ECDH_H_
#include "config.h"
#ifdef HAVE_LIBCRYPTO
#ifdef HAVE_OPENSSL_ECDH_H
#define HAVE_ECDH
#endif /* HAVE_OPENSSL_ECDH_H */
#endif /* HAVE_LIBCRYPTO */
int ssh_client_ecdh_init(ssh_session session);
int ssh_client_ecdh_reply(ssh_session session, ssh_buffer packet);
#endif /* ECDH_H_ */

View File

@ -32,6 +32,7 @@
#include <openssl/md5.h>
#include <openssl/hmac.h>
typedef SHA_CTX* SHACTX;
typedef SHA256_CTX* SHA256CTX;
typedef MD5_CTX* MD5CTX;
typedef HMAC_CTX* HMACCTX;
@ -67,6 +68,10 @@ typedef BN_CTX* bignum_CTX;
#define bignum_bn2bin(num,ptr) BN_bn2bin(num,ptr)
#define bignum_cmp(num1,num2) BN_cmp(num1,num2)
SHA256CTX sha256_init(void);
void sha256_update(SHA256CTX c, const void *data, unsigned long len);
void sha256_final(unsigned char *md, SHA256CTX c);
struct crypto_struct *ssh_get_ciphertab(void);
#endif /* HAVE_LIBCRYPTO */

View File

@ -30,8 +30,13 @@
typedef gcry_md_hd_t SHACTX;
typedef gcry_md_hd_t MD5CTX;
typedef gcry_md_hd_t HMACCTX;
#define SHA_DIGEST_LEN 20
#define SHA_DIGEST_LENGTH 20
#define SHA_DIGEST_LEN SHA_DIGEST_LENGTH
#define MD5_DIGEST_LEN 16
#define SHA256_DIGEST_LENGTH 32
#define SHA384_DIGEST_LENGTH 48
#define SHA512_DIGEST_LENGTH 64
#define EVP_MAX_MD_SIZE 36
typedef gcry_mpi_t bignum;

View File

@ -103,7 +103,7 @@ struct ssh_session_struct {
enum ssh_auth_service_state_e auth_service_state;
enum ssh_auth_state_e auth_state;
enum ssh_channel_request_state_e global_req_state;
ssh_string dh_server_signature; /* information used by dh_handshake. */
KEX server_kex;
KEX client_kex;
ssh_buffer in_hashbuf;

View File

@ -13,6 +13,10 @@
#define SSH2_MSG_KEXDH_INIT 30
#define SSH2_MSG_KEXDH_REPLY 31
#define SSH2_MSG_KEX_ECDH_INIT 30
#define SSH2_MSG_KEX_ECDH_REPLY 31
#define SSH2_MSG_ECMQV_INIT 30
#define SSH2_MSG_ECMQV_REPLY 31
#define SSH2_MSG_KEX_DH_GEX_REQUEST_OLD 30
#define SSH2_MSG_KEX_DH_GEX_GROUP 31

View File

@ -25,7 +25,21 @@
#include "config.h"
#include "libssh/libcrypto.h"
#include "libssh/libgcrypt.h"
#include "libssh/crypto.h"
enum ssh_mac_e {
SSH_MAC_SHA1=1,
SSH_MAC_SHA256,
SSH_MAC_SHA384,
SSH_MAC_SHA512
};
enum ssh_hmac_e {
SSH_HMAC_SHA1 = 1,
SSH_HMAC_MD5
};
typedef struct ssh_mac_ctx_struct *ssh_mac_ctx;
MD5CTX md5_init(void);
void md5_update(MD5CTX c, const void *data, unsigned long len);
void md5_final(unsigned char *md,MD5CTX c);
@ -33,9 +47,13 @@ SHACTX sha1_init(void);
void sha1_update(SHACTX c, const void *data, unsigned long len);
void sha1_final(unsigned char *md,SHACTX c);
void sha1(unsigned char *digest,int len,unsigned char *hash);
#define HMAC_SHA1 1
#define HMAC_MD5 2
HMACCTX hmac_init(const void *key,int len,int type);
void sha256(unsigned char *digest, int len, unsigned char *hash);
ssh_mac_ctx ssh_mac_ctx_init(enum ssh_mac_e type);
void ssh_mac_update(ssh_mac_ctx ctx, const void *data, unsigned long len);
void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx);
HMACCTX hmac_init(const void *key,int len, enum ssh_hmac_e type);
void hmac_update(HMACCTX c, const void *data, unsigned long len);
void hmac_final(HMACCTX ctx,unsigned char *hashmacbuf,unsigned int *len);

View File

@ -85,6 +85,7 @@ set(libssh_SRCS
crc32.c
crypt.c
dh.c
ecdh.c
error.c
getpass.c
gzip.c

View File

@ -36,6 +36,7 @@
#include "libssh/socket.h"
#include "libssh/session.h"
#include "libssh/dh.h"
#include "libssh/ecdh.h"
#include "libssh/threads.h"
#include "libssh/misc.h"
@ -176,12 +177,8 @@ end:
return err;
}
SSH_PACKET_CALLBACK(ssh_packet_dh_reply){
ssh_string f = NULL;
ssh_string pubkey = NULL;
ssh_string signature = NULL;
int rc;
(void)type;
(void)user;
ssh_log(session,SSH_LOG_PROTOCOL,"Received SSH_KEXDH_REPLY");
@ -191,48 +188,23 @@ SSH_PACKET_CALLBACK(ssh_packet_dh_reply){
session->session_state,session->dh_handshake_state);
goto error;
}
pubkey = buffer_get_ssh_string(packet);
if (pubkey == NULL){
ssh_set_error(session,SSH_FATAL, "No public key in packet");
goto error;
switch(session->next_crypto->kex_type){
case SSH_KEX_DH_GROUP1_SHA1:
rc=ssh_client_dh_reply(session, packet);
break;
#ifdef HAVE_ECDH
case SSH_KEX_ECDH_SHA2_NISTP256:
rc = ssh_client_ecdh_reply(session, packet);
break;
#endif
default:
ssh_set_error(session,SSH_FATAL,"Wrong kex type in ssh_packet_dh_reply");
goto error;
}
dh_import_pubkey(session, pubkey);
f = buffer_get_ssh_string(packet);
if (f == NULL) {
ssh_set_error(session,SSH_FATAL, "No F number in packet");
goto error;
if(rc==SSH_OK) {
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
return SSH_PACKET_USED;
}
if (dh_import_f(session, f) < 0) {
ssh_set_error(session, SSH_FATAL, "Cannot import f number");
goto error;
}
ssh_string_burn(f);
ssh_string_free(f);
f=NULL;
signature = buffer_get_ssh_string(packet);
if (signature == NULL) {
ssh_set_error(session, SSH_FATAL, "No signature in packet");
goto error;
}
session->dh_server_signature = signature;
signature=NULL; /* ownership changed */
if (dh_build_k(session) < 0) {
ssh_set_error(session, SSH_FATAL, "Cannot build k number");
goto error;
}
/* Send the MSG_NEWKEYS */
if (buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
goto error;
}
packet_send(session);
ssh_log(session, SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
return SSH_PACKET_USED;
error:
session->session_state=SSH_SESSION_STATE_ERROR;
return SSH_PACKET_USED;
@ -274,12 +246,12 @@ SSH_PACKET_CALLBACK(ssh_packet_newkeys){
}
/* Verify the host's signature. FIXME do it sooner */
signature = session->dh_server_signature;
session->dh_server_signature = NULL;
signature = session->next_crypto->dh_server_signature;
session->next_crypto->dh_server_signature = NULL;
if (signature_verify(session, signature)) {
goto error;
}
ssh_log(session,SSH_LOG_PROTOCOL,"Signature verified and valid");
/* forget it for now ... */
ssh_string_burn(signature);
ssh_string_free(signature);
@ -325,7 +297,20 @@ static int dh_handshake(ssh_session session) {
switch (session->dh_handshake_state) {
case DH_STATE_INIT:
rc = ssh_client_dh_init(session);
switch(session->next_crypto->kex_type){
case SSH_KEX_DH_GROUP1_SHA1:
rc = ssh_client_dh_init(session);
break;
#ifdef HAVE_ECDH
case SSH_KEX_ECDH_SHA2_NISTP256:
rc = ssh_client_ecdh_init(session);
break;
#endif
default:
rc=SSH_ERROR;
goto error;
}
if (rc == SSH_ERROR) {
goto error;
}

View File

@ -136,7 +136,7 @@ unsigned char *packet_encrypt(ssh_session session, void *data, uint32_t len) {
#endif
if (session->version == 2) {
ctx = hmac_init(session->current_crypto->encryptMAC,20,HMAC_SHA1);
ctx = hmac_init(session->current_crypto->encryptMAC,20,SSH_HMAC_SHA1);
if (ctx == NULL) {
SAFE_FREE(out);
return NULL;
@ -190,7 +190,7 @@ int packet_hmac_verify(ssh_session session, ssh_buffer buffer,
unsigned int len;
uint32_t seq;
ctx = hmac_init(session->current_crypto->decryptMAC, 20, HMAC_SHA1);
ctx = hmac_init(session->current_crypto->decryptMAC, 20, SSH_HMAC_SHA1);
if (ctx == NULL) {
return -1;
}

255
src/dh.c
View File

@ -521,6 +521,56 @@ int ssh_client_dh_init(ssh_session session){
leave_function();
return SSH_ERROR;
}
int ssh_client_dh_reply(ssh_session session, ssh_buffer packet){
ssh_string f = NULL;
ssh_string pubkey = NULL;
ssh_string signature = NULL;
int rc;
pubkey = buffer_get_ssh_string(packet);
if (pubkey == NULL){
ssh_set_error(session,SSH_FATAL, "No public key in packet");
goto error;
}
dh_import_pubkey(session, pubkey);
f = buffer_get_ssh_string(packet);
if (f == NULL) {
ssh_set_error(session,SSH_FATAL, "No F number in packet");
goto error;
}
if (dh_import_f(session, f) < 0) {
ssh_set_error(session, SSH_FATAL, "Cannot import f number");
goto error;
}
ssh_string_burn(f);
ssh_string_free(f);
f=NULL;
signature = buffer_get_ssh_string(packet);
if (signature == NULL) {
ssh_set_error(session, SSH_FATAL, "No signature in packet");
goto error;
}
session->next_crypto->dh_server_signature = signature;
signature=NULL; /* ownership changed */
if (dh_build_k(session) < 0) {
ssh_set_error(session, SSH_FATAL, "Cannot build k number");
goto error;
}
/* Send the MSG_NEWKEYS */
if (buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
goto error;
}
rc=packet_send(session);
ssh_log(session, SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
return rc;
error:
return SSH_ERROR;
}
/*
static void sha_add(ssh_string str,SHACTX ctx){
sha1_update(ctx,str,string_len(str)+4);
@ -531,7 +581,6 @@ static void sha_add(ssh_string str,SHACTX ctx){
*/
int make_sessionid(ssh_session session) {
SHACTX ctx;
ssh_string num = NULL;
ssh_string str = NULL;
ssh_buffer server_hash = NULL;
@ -542,11 +591,6 @@ int make_sessionid(ssh_session session) {
enter_function();
ctx = sha1_init();
if (ctx == NULL) {
return rc;
}
buf = ssh_buffer_new();
if (buf == NULL) {
return rc;
@ -614,29 +658,33 @@ int make_sessionid(ssh_session session) {
if (buffer_add_data(buf, session->next_crypto->server_pubkey, len) < 0) {
goto error;
}
if(session->next_crypto->kex_type == SSH_KEX_DH_GROUP1_SHA1){
num = make_bignum_string(session->next_crypto->e);
if (num == NULL) {
goto error;
}
num = make_bignum_string(session->next_crypto->e);
if (num == NULL) {
goto error;
len = ssh_string_len(num) + 4;
if (buffer_add_data(buf, num, len) < 0) {
goto error;
}
ssh_string_free(num);
num = make_bignum_string(session->next_crypto->f);
if (num == NULL) {
goto error;
}
len = ssh_string_len(num) + 4;
if (buffer_add_data(buf, num, len) < 0) {
goto error;
}
ssh_string_free(num);
} else if (session->next_crypto->kex_type == SSH_KEX_ECDH_SHA2_NISTP256){
buffer_add_ssh_string(buf,session->next_crypto->ecdh_client_pubkey);
buffer_add_ssh_string(buf,session->next_crypto->ecdh_server_pubkey);
}
len = ssh_string_len(num) + 4;
if (buffer_add_data(buf, num, len) < 0) {
goto error;
}
ssh_string_free(num);
num = make_bignum_string(session->next_crypto->f);
if (num == NULL) {
goto error;
}
len = ssh_string_len(num) + 4;
if (buffer_add_data(buf, num, len) < 0) {
goto error;
}
ssh_string_free(num);
num = make_bignum_string(session->next_crypto->k);
if (num == NULL) {
goto error;
@ -651,8 +699,31 @@ int make_sessionid(ssh_session session) {
ssh_print_hexa("hash buffer", ssh_buffer_get_begin(buf), ssh_buffer_get_len(buf));
#endif
sha1_update(ctx, buffer_get_rest(buf), buffer_get_rest_len(buf));
sha1_final(session->next_crypto->session_id, ctx);
switch(session->next_crypto->kex_type){
case SSH_KEX_DH_GROUP1_SHA1:
session->next_crypto->digest_len = SHA_DIGEST_LENGTH;
session->next_crypto->mac_type = SSH_MAC_SHA1;
session->next_crypto->session_id = malloc(session->next_crypto->digest_len);
if(session->next_crypto->session_id == NULL){
ssh_set_error_oom(session);
goto error;
}
sha1(buffer_get_rest(buf), buffer_get_rest_len(buf),
session->next_crypto->session_id);
break;
case SSH_KEX_ECDH_SHA2_NISTP256:
session->next_crypto->digest_len = SHA256_DIGEST_LENGTH;
session->next_crypto->mac_type = SSH_MAC_SHA256;
session->next_crypto->session_id = malloc(session->next_crypto->digest_len);
if(session->next_crypto->session_id == NULL){
ssh_set_error_oom(session);
goto error;
}
sha256(buffer_get_rest(buf), buffer_get_rest_len(buf),
session->next_crypto->session_id);
break;
}
#ifdef DEBUG_CRYPTO
printf("Session hash: ");
@ -723,126 +794,134 @@ int hashbufin_add_cookie(ssh_session session, unsigned char *cookie) {
}
static int generate_one_key(ssh_string k,
unsigned char session_id[SHA_DIGEST_LEN],
unsigned char output[SHA_DIGEST_LEN],
char letter) {
SHACTX ctx = NULL;
struct ssh_crypto_struct *crypto, unsigned char *output, char letter) {
ssh_mac_ctx ctx;
ctx=ssh_mac_ctx_init(crypto->mac_type);
ctx = sha1_init();
if (ctx == NULL) {
return -1;
}
sha1_update(ctx, k, ssh_string_len(k) + 4);
sha1_update(ctx, session_id, SHA_DIGEST_LEN);
sha1_update(ctx, &letter, 1);
sha1_update(ctx, session_id, SHA_DIGEST_LEN);
sha1_final(output, ctx);
ssh_mac_update(ctx, k, ssh_string_len(k) + 4);
ssh_mac_update(ctx, crypto->session_id, crypto->digest_len);
ssh_mac_update(ctx, &letter, 1);
ssh_mac_update(ctx, crypto->session_id, crypto->digest_len);
ssh_mac_final(output, ctx);
return 0;
}
int generate_session_keys(ssh_session session) {
ssh_string k_string = NULL;
SHACTX ctx = NULL;
ssh_mac_ctx ctx = NULL;
struct ssh_crypto_struct *crypto = session->next_crypto;
int rc = -1;
enter_function();
k_string = make_bignum_string(session->next_crypto->k);
k_string = make_bignum_string(crypto->k);
if (k_string == NULL) {
ssh_set_error_oom(session);
goto error;
}
crypto->encryptIV = malloc(crypto->digest_len);
crypto->decryptIV = malloc(crypto->digest_len);
crypto->encryptkey = malloc(crypto->digest_len);
crypto->decryptkey = malloc(crypto->digest_len);
crypto->encryptMAC = malloc(crypto->digest_len);
crypto->decryptMAC = malloc(crypto->digest_len);
if(crypto->encryptIV == NULL || crypto->decryptIV == NULL ||
crypto->encryptkey == NULL || crypto->decryptkey == NULL ||
crypto->encryptMAC == NULL || crypto->decryptMAC == NULL){
ssh_set_error_oom(session);
goto error;
}
/* IV */
if (session->client) {
if (generate_one_key(k_string, session->next_crypto->session_id,
session->next_crypto->encryptIV, 'A') < 0) {
if (generate_one_key(k_string, crypto, crypto->encryptIV, 'A') < 0) {
goto error;
}
if (generate_one_key(k_string, session->next_crypto->session_id,
session->next_crypto->decryptIV, 'B') < 0) {
if (generate_one_key(k_string, crypto, crypto->decryptIV, 'B') < 0) {
goto error;
}
} else {
if (generate_one_key(k_string, session->next_crypto->session_id,
session->next_crypto->decryptIV, 'A') < 0) {
if (generate_one_key(k_string, crypto, crypto->decryptIV, 'A') < 0) {
goto error;
}
if (generate_one_key(k_string, session->next_crypto->session_id,
session->next_crypto->encryptIV, 'B') < 0) {
if (generate_one_key(k_string, crypto, crypto->encryptIV, 'B') < 0) {
goto error;
}
}
if (session->client) {
if (generate_one_key(k_string, session->next_crypto->session_id,
session->next_crypto->encryptkey, 'C') < 0) {
if (generate_one_key(k_string, crypto, crypto->encryptkey, 'C') < 0) {
goto error;
}
if (generate_one_key(k_string, session->next_crypto->session_id,
session->next_crypto->decryptkey, 'D') < 0) {
if (generate_one_key(k_string, crypto, crypto->decryptkey, 'D') < 0) {
goto error;
}
} else {
if (generate_one_key(k_string, session->next_crypto->session_id,
session->next_crypto->decryptkey, 'C') < 0) {
if (generate_one_key(k_string, crypto, crypto->decryptkey, 'C') < 0) {
goto error;
}
if (generate_one_key(k_string, session->next_crypto->session_id,
session->next_crypto->encryptkey, 'D') < 0) {
if (generate_one_key(k_string, crypto, crypto->encryptkey, 'D') < 0) {
goto error;
}
}
/* some ciphers need more than 20 bytes of input key */
/* XXX verify it's ok for server implementation */
if (session->next_crypto->out_cipher->keysize > SHA_DIGEST_LEN * 8) {
ctx = sha1_init();
/* some ciphers need more than DIGEST_LEN bytes of input key */
if (crypto->out_cipher->keysize > crypto->digest_len * 8) {
crypto->encryptkey = realloc(crypto->encryptkey, crypto->digest_len * 2);
if(crypto->encryptkey == NULL)
goto error;
ctx = ssh_mac_ctx_init(crypto->mac_type);
if (ctx == NULL) {
goto error;
}
sha1_update(ctx, k_string, ssh_string_len(k_string) + 4);
sha1_update(ctx, session->next_crypto->session_id, SHA_DIGEST_LEN);
sha1_update(ctx, session->next_crypto->encryptkey, SHA_DIGEST_LEN);
sha1_final(session->next_crypto->encryptkey + SHA_DIGEST_LEN, ctx);
ssh_mac_update(ctx, k_string, ssh_string_len(k_string) + 4);
ssh_mac_update(ctx, crypto->session_id,
crypto->digest_len);
ssh_mac_update(ctx, crypto->encryptkey, crypto->digest_len);
ssh_mac_final(crypto->encryptkey + crypto->digest_len, ctx);
}
if (session->next_crypto->in_cipher->keysize > SHA_DIGEST_LEN * 8) {
ctx = sha1_init();
sha1_update(ctx, k_string, ssh_string_len(k_string) + 4);
sha1_update(ctx, session->next_crypto->session_id, SHA_DIGEST_LEN);
sha1_update(ctx, session->next_crypto->decryptkey, SHA_DIGEST_LEN);
sha1_final(session->next_crypto->decryptkey + SHA_DIGEST_LEN, ctx);
if (crypto->in_cipher->keysize > crypto->digest_len * 8) {
crypto->decryptkey = realloc(crypto->decryptkey, crypto->digest_len *2);
if(crypto->decryptkey == NULL)
goto error;
ctx = ssh_mac_ctx_init(crypto->mac_type);
ssh_mac_update(ctx, k_string, ssh_string_len(k_string) + 4);
ssh_mac_update(ctx, crypto->session_id,
crypto->digest_len);
ssh_mac_update(ctx, crypto->decryptkey, crypto->digest_len);
ssh_mac_final(crypto->decryptkey + crypto->digest_len, ctx);
}
if(session->client) {
if (generate_one_key(k_string, session->next_crypto->session_id,
session->next_crypto->encryptMAC, 'E') < 0) {
if (generate_one_key(k_string, crypto, crypto->encryptMAC, 'E') < 0) {
goto error;
}
if (generate_one_key(k_string, session->next_crypto->session_id,
session->next_crypto->decryptMAC, 'F') < 0) {
if (generate_one_key(k_string, crypto, crypto->decryptMAC, 'F') < 0) {
goto error;
}
} else {
if (generate_one_key(k_string, session->next_crypto->session_id,
session->next_crypto->decryptMAC, 'E') < 0) {
if (generate_one_key(k_string, crypto, crypto->decryptMAC, 'E') < 0) {
goto error;
}
if (generate_one_key(k_string, session->next_crypto->session_id,
session->next_crypto->encryptMAC, 'F') < 0) {
if (generate_one_key(k_string, crypto, crypto->encryptMAC, 'F') < 0) {
goto error;
}
}
#ifdef DEBUG_CRYPTO
ssh_print_hexa("Encrypt IV", session->next_crypto->encryptIV, SHA_DIGEST_LEN);
ssh_print_hexa("Decrypt IV", session->next_crypto->decryptIV, SHA_DIGEST_LEN);
ssh_print_hexa("Encryption key", session->next_crypto->encryptkey,
session->next_crypto->out_cipher->keysize);
ssh_print_hexa("Decryption key", session->next_crypto->decryptkey,
session->next_crypto->in_cipher->keysize);
ssh_print_hexa("Encryption MAC", session->next_crypto->encryptMAC, SHA_DIGEST_LEN);
ssh_print_hexa("Decryption MAC", session->next_crypto->decryptMAC, 20);
ssh_print_hexa("Encrypt IV", crypto->encryptIV, SHA_DIGEST_LEN);
ssh_print_hexa("Decrypt IV", crypto->decryptIV, SHA_DIGEST_LEN);
ssh_print_hexa("Encryption key", crypto->encryptkey,
crypto->out_cipher->keysize);
ssh_print_hexa("Decryption key", crypto->decryptkey,
crypto->in_cipher->keysize);
ssh_print_hexa("Encryption MAC", crypto->encryptMAC, SHA_DIGEST_LEN);
ssh_print_hexa("Decryption MAC", crypto->decryptMAC, 20);
#endif
rc = 0;
@ -1090,7 +1169,7 @@ int signature_verify(ssh_session session, ssh_string signature) {
"Going to verify a %s type signature", pubkey->type_c);
err = sig_verify(session,pubkey,sign,
session->next_crypto->session_id,SHA_DIGEST_LEN);
session->next_crypto->session_id, session->next_crypto->digest_len);
signature_free(sign);
session->next_crypto->server_pubkey_type = pubkey->type_c;
publickey_free(pubkey);

167
src/ecdh.c Normal file
View File

@ -0,0 +1,167 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2011 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include "libssh/session.h"
#include "libssh/ecdh.h"
#include "libssh/buffer.h"
#include "libssh/ssh2.h"
#ifdef HAVE_ECDH
#include <openssl/ecdh.h>
#define NISTP256 NID_X9_62_prime256v1
#define NISTP384 NID_secp384r1
#define NISTP521 NID_secp521r1
/** @internal
* @brief Starts ecdh-sha2-nistp256 key exchange
*/
int ssh_client_ecdh_init(ssh_session session){
ssh_string e = NULL;
EC_KEY *key=NULL;
const EC_GROUP *group;
const EC_POINT *pubkey;
ssh_string client_pubkey;
int len;
int rc;
bignum_CTX ctx=BN_CTX_new();
enter_function();
if (buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_INIT) < 0) {
goto error;
}
key = EC_KEY_new_by_curve_name(NISTP256);
group = EC_KEY_get0_group(key);
EC_KEY_generate_key(key);
pubkey=EC_KEY_get0_public_key(key);
len = EC_POINT_point2oct(group,pubkey,POINT_CONVERSION_UNCOMPRESSED,
NULL,0,ctx);
client_pubkey=ssh_string_new(len);
EC_POINT_point2oct(group,pubkey,POINT_CONVERSION_UNCOMPRESSED,
ssh_string_data(client_pubkey),len,ctx);
buffer_add_ssh_string(session->out_buffer,client_pubkey);
BN_CTX_free(ctx);
session->next_crypto->ecdh_privkey = key;
session->next_crypto->ecdh_client_pubkey = client_pubkey;
rc = packet_send(session);
return rc;
error:
if(e != NULL){
ssh_string_burn(e);
ssh_string_free(e);
}
leave_function();
return SSH_ERROR;
}
static void ecdh_import_pubkey(ssh_session session, ssh_string pubkey_string) {
session->next_crypto->server_pubkey = pubkey_string;
}
static int ecdh_build_k(ssh_session session) {
const EC_GROUP *group = EC_KEY_get0_group(session->next_crypto->ecdh_privkey);
EC_POINT *pubkey=EC_POINT_new(group);
void *buffer;
int len = (EC_GROUP_get_degree(group) + 7) / 8;
#ifdef HAVE_LIBCRYPTO
bignum_CTX ctx = bignum_ctx_new();
if (ctx == NULL) {
return -1;
}
#endif
session->next_crypto->k = bignum_new();
if (session->next_crypto->k == NULL) {
#ifdef HAVE_LIBCRYPTO
bignum_ctx_free(ctx);
#endif
return -1;
}
EC_POINT_oct2point(group,pubkey,ssh_string_data(session->next_crypto->ecdh_server_pubkey),
ssh_string_len(session->next_crypto->ecdh_server_pubkey),ctx);
buffer = malloc(len);
ECDH_compute_key(buffer,len,pubkey,session->next_crypto->ecdh_privkey,NULL);
BN_bin2bn(buffer,len,session->next_crypto->k);
free(buffer);
#ifdef DEBUG_CRYPTO
ssh_print_hexa("Session server cookie", session->server_kex.cookie, 16);
ssh_print_hexa("Session client cookie", session->client_kex.cookie, 16);
ssh_print_bignum("Shared secret key", session->next_crypto->k);
#endif
#ifdef HAVE_LIBCRYPTO
bignum_ctx_free(ctx);
#endif
return 0;
}
/** @internal
* @brief parses a SSH_MSG_KEX_ECDH_REPLY packet and sends back
* a SSH_MSG_NEWKEYS
*/
int ssh_client_ecdh_reply(ssh_session session, ssh_buffer packet){
ssh_string q_s_string = NULL;
ssh_string pubkey = NULL;
ssh_string signature = NULL;
int rc;
pubkey = buffer_get_ssh_string(packet);
if (pubkey == NULL){
ssh_set_error(session,SSH_FATAL, "No public key in packet");
goto error;
}
ecdh_import_pubkey(session, pubkey);
q_s_string = buffer_get_ssh_string(packet);
if (q_s_string == NULL) {
ssh_set_error(session,SSH_FATAL, "No Q_S ECC point in packet");
goto error;
}
session->next_crypto->ecdh_server_pubkey = q_s_string;
signature = buffer_get_ssh_string(packet);
if (signature == NULL) {
ssh_set_error(session, SSH_FATAL, "No signature in packet");
goto error;
}
session->next_crypto->dh_server_signature = signature;
signature=NULL; /* ownership changed */
if (ecdh_build_k(session) < 0) {
ssh_set_error(session, SSH_FATAL, "Cannot build k number");
goto error;
}
/* Send the MSG_NEWKEYS */
if (buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
goto error;
}
rc=packet_send(session);
ssh_log(session, SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
return rc;
error:
return SSH_ERROR;
}
#endif /* HAVE_ECDH */

View File

@ -42,6 +42,7 @@
#include "libssh/dh.h"
#include "libssh/kex.h"
#include "libssh/string.h"
#include "libssh/ecdh.h"
#ifdef HAVE_LIBGCRYPT
#define BLOWFISH "blowfish-cbc,"
@ -72,8 +73,14 @@
#define ZLIB "none"
#endif
#ifdef HAVE_ECDH
#define KEY_EXCHANGE "ecdh-sha2-nistp256,diffie-hellman-group1-sha1"
#else
#define KEY_EXCHANGE "diffie-hellman-group1-sha1"
#endif
const char *default_methods[] = {
"diffie-hellman-group1-sha1",
KEY_EXCHANGE,
"ssh-rsa,ssh-dss",
AES BLOWFISH DES,
AES BLOWFISH DES,
@ -87,7 +94,7 @@ const char *default_methods[] = {
};
const char *supported_methods[] = {
"diffie-hellman-group1-sha1",
KEY_EXCHANGE,
"ssh-rsa,ssh-dss",
AES BLOWFISH DES,
AES BLOWFISH DES,
@ -370,7 +377,7 @@ int set_kex(ssh_session session){
ssh_get_random(client->cookie,16,0);
client->methods=malloc(10 * sizeof(char **));
if (client->methods == NULL) {
ssh_set_error(session, SSH_FATAL, "No space left");
ssh_set_error_oom(session);
leave_function();
return -1;
}
@ -394,6 +401,11 @@ int set_kex(ssh_session session){
}
}
}
if(strcmp(client->methods[SSH_KEX], "diffie-hellman-group1-sha1") == 0){
session->next_crypto->kex_type=SSH_KEX_DH_GROUP1_SHA1;
} else if(strcmp(client->methods[SSH_KEX], "ecdh-sha2-nistp256") == 0){
session->next_crypto->kex_type=SSH_KEX_ECDH_SHA2_NISTP256;
}
leave_function();
return 0;
}

View File

@ -346,7 +346,7 @@ static int match_hashed_host(ssh_session session, const char *host,
return 0;
}
mac = hmac_init(buffer_get_rest(salt), buffer_get_rest_len(salt), HMAC_SHA1);
mac = hmac_init(buffer_get_rest(salt), buffer_get_rest_len(salt), SSH_HMAC_SHA1);
if (mac == NULL) {
ssh_buffer_free(salt);
ssh_buffer_free(hash);

View File

@ -57,6 +57,14 @@
#include "libssh/crypto.h"
struct ssh_mac_ctx_struct {
enum ssh_mac_e mac_type;
union {
SHACTX sha1_ctx;
SHA256CTX sha256_ctx;
} ctx;
};
static int alloc_key(struct crypto_struct *cipher) {
cipher->key = malloc(cipher->keylen);
if (cipher->key == NULL) {
@ -89,6 +97,29 @@ void sha1(unsigned char *digest, int len, unsigned char *hash) {
SHA1(digest, len, hash);
}
SHA256CTX sha256_init(void){
SHA256CTX c = malloc(sizeof(*c));
if (c == NULL) {
return NULL;
}
SHA256_Init(c);
return c;
}
void sha256_update(SHA256CTX c, const void *data, unsigned long len){
SHA256_Update(c,data,len);
}
void sha256_final(unsigned char *md, SHA256CTX c) {
SHA256_Final(md, c);
SAFE_FREE(c);
}
void sha256(unsigned char *digest, int len, unsigned char *hash) {
SHA256(digest, len, hash);
}
MD5CTX md5_init(void) {
MD5CTX c = malloc(sizeof(*c));
if (c == NULL) {
@ -109,7 +140,56 @@ void md5_final(unsigned char *md, MD5CTX c) {
SAFE_FREE(c);
}
HMACCTX hmac_init(const void *key, int len, int type) {
ssh_mac_ctx ssh_mac_ctx_init(enum ssh_mac_e type){
ssh_mac_ctx ctx=malloc(sizeof(struct ssh_mac_ctx_struct));
ctx->mac_type=type;
switch(type){
case SSH_MAC_SHA1:
ctx->ctx.sha1_ctx = sha1_init();
return ctx;
case SSH_MAC_SHA256:
ctx->ctx.sha256_ctx = sha256_init();
return ctx;
case SSH_MAC_SHA384:
case SSH_MAC_SHA512:
default:
SAFE_FREE(ctx);
return NULL;
}
}
void ssh_mac_update(ssh_mac_ctx ctx, const void *data, unsigned long len) {
switch(ctx->mac_type){
case SSH_MAC_SHA1:
sha1_update(ctx->ctx.sha1_ctx, data, len);
break;
case SSH_MAC_SHA256:
sha256_update(ctx->ctx.sha256_ctx, data, len);
break;
case SSH_MAC_SHA384:
case SSH_MAC_SHA512:
default:
break;
}
}
void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx) {
switch(ctx->mac_type){
case SSH_MAC_SHA1:
sha1_final(md,ctx->ctx.sha1_ctx);
break;
case SSH_MAC_SHA256:
sha256_final(md,ctx->ctx.sha256_ctx);
break;
case SSH_MAC_SHA384:
case SSH_MAC_SHA512:
default:
break;
}
SAFE_FREE(ctx);
}
HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type) {
HMACCTX ctx = NULL;
ctx = malloc(sizeof(*ctx));
@ -122,10 +202,10 @@ HMACCTX hmac_init(const void *key, int len, int type) {
#endif
switch(type) {
case HMAC_SHA1:
case SSH_HMAC_SHA1:
HMAC_Init(ctx, key, len, EVP_sha1());
break;
case HMAC_MD5:
case SSH_HMAC_MD5:
HMAC_Init(ctx, key, len, EVP_md5());
break;
default:

View File

@ -31,6 +31,10 @@
#ifdef HAVE_LIBGCRYPT
#include <gcrypt.h>
struct ssh_mac_ctx_struct {
enum ssh_mac_e mac_type;
gcry_md_hd_t ctx;
};
static int alloc_key(struct crypto_struct *cipher) {
cipher->key = malloc(cipher->keylen);
@ -62,6 +66,10 @@ void sha1(unsigned char *digest, int len, unsigned char *hash) {
gcry_md_hash_buffer(GCRY_MD_SHA1, hash, digest, len);
}
void sha256(unsigned char *digest, int len, unsigned char *hash){
gcry_md_hash_buffer(GCRY_MD_SHA256, hash, digest, len);
}
MD5CTX md5_init(void) {
MD5CTX c = NULL;
gcry_md_open(&c, GCRY_MD_MD5, 0);
@ -79,14 +87,63 @@ void md5_final(unsigned char *md, MD5CTX c) {
gcry_md_close(c);
}
HMACCTX hmac_init(const void *key, int len, int type) {
ssh_mac_ctx ssh_mac_ctx_init(enum ssh_mac_e type){
ssh_mac_ctx ctx=malloc(sizeof(struct ssh_mac_ctx_struct));
ctx->mac_type=type;
switch(type){
case SSH_MAC_SHA1:
gcry_md_open(&ctx->ctx, GCRY_MD_SHA1, 0);
break;
case SSH_MAC_SHA256:
gcry_md_open(&ctx->ctx, GCRY_MD_SHA256, 0);
break;
case SSH_MAC_SHA384:
gcry_md_open(&ctx->ctx, GCRY_MD_SHA384, 0);
break;
case SSH_MAC_SHA512:
gcry_md_open(&ctx->ctx, GCRY_MD_SHA512, 0);
break;
default:
SAFE_FREE(ctx);
return NULL;
}
return ctx;
}
void ssh_mac_update(ssh_mac_ctx ctx, const void *data, unsigned long len) {
gcry_md_write(ctx->ctx,data,len);
}
void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx) {
size_t len;
switch(ctx->mac_type){
case SSH_MAC_SHA1:
len=SHA_DIGEST_LEN;
break;
case SSH_MAC_SHA256:
len=SHA256_DIGEST_LENGTH;
break;
case SSH_MAC_SHA384:
len=SHA384_DIGEST_LENGTH;
break;
case SSH_MAC_SHA512:
len=SHA512_DIGEST_LENGTH;
break;
}
gcry_md_final(ctx->ctx);
memcpy(md, gcry_md_read(ctx->ctx, 0), len);
gcry_md_close(ctx->ctx);
SAFE_FREE(ctx);
}
HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type) {
HMACCTX c = NULL;
switch(type) {
case HMAC_SHA1:
case SSH_HMAC_SHA1:
gcry_md_open(&c, GCRY_MD_SHA1, GCRY_MD_FLAG_HMAC);
break;
case HMAC_MD5:
case SSH_HMAC_MD5:
gcry_md_open(&c, GCRY_MD_MD5, GCRY_MD_FLAG_HMAC);
break;
default: