1
1

Add support for ECDSA keys and host keys (#41)

This commit lands full ECDSA key support when using the OpenSSL
backend. Which includes:

New KEX methods:
ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, ecdsa-sha2-nistp521

Can now read OpenSSL formatted ECDSA key files.

Now supports known host keys of type ecdsa-sha2-nistp256.

New curve types:
NID_X9_62_prime256v1, NID_secp384r1, NID_secp521r1

Default host key preferred ordering is now nistp256, nistp384,
nistp521, rsa, dss.

Ref: https://github.com/libssh2/libssh2/issues/41

Closes https://github.com/libssh2/libssh2/pull/206
Этот коммит содержится в:
Will Cosgrove 2017-08-31 14:57:40 -07:00 коммит произвёл Viktor Szakats
родитель bcd492163b
Коммит aba34f5f56
13 изменённых файлов: 2087 добавлений и 110 удалений

Просмотреть файл

@ -404,11 +404,12 @@ typedef struct _LIBSSH2_POLLFD {
#define LIBSSH2_HOSTKEY_HASH_MD5 1
#define LIBSSH2_HOSTKEY_HASH_SHA1 2
#define LIBSSH2_HOSTKEY_HASH_SHA256 3
/* Hostkey Types */
#define LIBSSH2_HOSTKEY_TYPE_UNKNOWN 0
#define LIBSSH2_HOSTKEY_TYPE_RSA 1
#define LIBSSH2_HOSTKEY_TYPE_DSS 2
#define LIBSSH2_HOSTKEY_TYPE_UNKNOWN 0
#define LIBSSH2_HOSTKEY_TYPE_RSA 1
#define LIBSSH2_HOSTKEY_TYPE_DSS 2
#define LIBSSH2_HOSTKEY_TYPE_ECDSA 3
/* Disconnect Codes (defined by SSH protocol) */
#define SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1
@ -967,6 +968,7 @@ libssh2_knownhost_init(LIBSSH2_SESSION *session);
#define LIBSSH2_KNOWNHOST_KEY_RSA1 (1<<18)
#define LIBSSH2_KNOWNHOST_KEY_SSHRSA (2<<18)
#define LIBSSH2_KNOWNHOST_KEY_SSHDSS (3<<18)
#define LIBSSH2_KNOWNHOST_KEY_ECDSA (4<<18)
#define LIBSSH2_KNOWNHOST_KEY_UNKNOWN (7<<18)
LIBSSH2_API int

Просмотреть файл

@ -122,6 +122,53 @@ int _libssh2_dsa_new_private_frommemory(libssh2_dsa_ctx ** dsa,
unsigned const char *passphrase);
#endif
#if LIBSSH2_ECDSA
int
_libssh2_ecdsa_curve_name_with_octal_new(libssh2_ecdsa_ctx ** ecdsactx,
const unsigned char *k,
size_t k_len, libssh2_curve_type type);
int
_libssh2_ecdsa_new_private(libssh2_ecdsa_ctx ** ec_ctx,
LIBSSH2_SESSION * session,
const char *filename, unsigned const char *passphrase);
int _libssh2_ecdsa_new_openssh_private(libssh2_ecdsa_ctx ** dsa,
LIBSSH2_SESSION * session,
const char *filename,
unsigned const char *passphrase);
int
_libssh2_ecdsa_verify(libssh2_ecdsa_ctx * ctx,
const unsigned char *r, size_t r_len,
const unsigned char *s, size_t s_len,
const unsigned char *m, size_t m_len);
int
_libssh2_ecdsa_create_key(_libssh2_ec_key **out_private_key,
unsigned char **out_public_key_octal,
size_t *out_public_key_octal_len, libssh2_curve_type curve_type);
int
_libssh2_ecdh_gen_k(_libssh2_bn **k, _libssh2_ec_key *private_key,
const unsigned char *server_public_key, size_t server_public_key_len);
int
_libssh2_ecdsa_sign(LIBSSH2_SESSION *session, libssh2_ecdsa_ctx *ec_ctx,
const unsigned char *hash, unsigned long hash_len,
unsigned char **signature, size_t *signature_len);
int _libssh2_ecdsa_new_private_frommemory(libssh2_ecdsa_ctx ** ec_ctx,
LIBSSH2_SESSION * session,
const char *filedata, size_t filedata_len,
unsigned const char *passphrase);
libssh2_curve_type
_libssh2_ecdsa_key_get_curve_type(_libssh2_ec_key *key);
int
_libssh2_ecdsa_curve_type_from_name(const char *name, libssh2_curve_type *out_type);
#endif /* LIBSSH2_ECDSA */
int _libssh2_cipher_init(_libssh2_cipher_ctx * h,
_libssh2_cipher_type(algo),
unsigned char *iv,

Просмотреть файл

@ -483,7 +483,296 @@ static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ssh_dss = {
};
#endif /* LIBSSH2_DSA */
#if LIBSSH2_ECDSA
/* ***********
* ecdsa-sha2-nistp256/384/521 *
*********** */
static int
hostkey_method_ssh_ecdsa_dtor(LIBSSH2_SESSION * session,
void **abstract);
/*
* hostkey_method_ssh_ecdsa_init
*
* Initialize the server hostkey working area with e/n pair
*/
static int
hostkey_method_ssh_ecdsa_init(LIBSSH2_SESSION * session,
const unsigned char *hostkey_data,
size_t hostkey_data_len,
void **abstract)
{
libssh2_ecdsa_ctx *ecdsactx = NULL;
const unsigned char *s, *k;
size_t len, key_len, n_len;
libssh2_curve_type type;
if (abstract != NULL && *abstract) {
hostkey_method_ssh_ecdsa_dtor(session, abstract);
*abstract = NULL;
}
if ( hostkey_data_len < 23 )
return -1;
s = hostkey_data;
len = _libssh2_ntohu32(s);
s += 4;
if (len != 19 )
return -1;
if (strncmp((char*) s, "ecdsa-sha2-nistp256", 19) == 0 ) {
type = LIBSSH2_EC_CURVE_NISTP256;
} else if(strncmp((char*) s, "ecdsa-sha2-nistp384", 19) == 0 ) {
type = LIBSSH2_EC_CURVE_NISTP384;
} else if(strncmp((char*) s, "ecdsa-sha2-nistp521", 19) == 0 ) {
type = LIBSSH2_EC_CURVE_NISTP521;
} else {
return -1;
}
s += 19;
/* Domain length */
n_len = _libssh2_ntohu32(s);
s += 4;
if (n_len != 8)
return -1;
if ( type == LIBSSH2_EC_CURVE_NISTP256 && strncmp((char*)s, "nistp256", 8) != 0) {
return -1;
} else if ( type == LIBSSH2_EC_CURVE_NISTP384 && strncmp((char*)s, "nistp384", 8) != 0) {
return -1;
} else if ( type == LIBSSH2_EC_CURVE_NISTP521 && strncmp((char*)s, "nistp521", 8) != 0) {
return -1;
}
s += 8;
/* public key */
key_len = _libssh2_ntohu32(s);
s += 4;
k = s;
if (_libssh2_ecdsa_curve_name_with_octal_new(&ecdsactx, k, key_len, type) )
return -1;
if ( abstract != NULL )
*abstract = ecdsactx;
return 0;
}
/*
* hostkey_method_ssh_ecdsa_initPEM
*
* Load a Private Key from a PEM file
*/
static int
hostkey_method_ssh_ecdsa_initPEM(LIBSSH2_SESSION * session,
const char *privkeyfile,
unsigned const char *passphrase,
void **abstract)
{
libssh2_ecdsa_ctx *ec_ctx = NULL;
int ret;
if (abstract != NULL && *abstract) {
hostkey_method_ssh_ecdsa_dtor(session, abstract);
*abstract = NULL;
}
ret = _libssh2_ecdsa_new_private(&ec_ctx, session, privkeyfile, passphrase);
if ( abstract != NULL )
*abstract = ec_ctx;
return ret;
}
/*
* hostkey_method_ssh_ecdsa_initPEMFromMemory
*
* Load a Private Key from memory
*/
static int
hostkey_method_ssh_ecdsa_initPEMFromMemory(LIBSSH2_SESSION * session,
const char *privkeyfiledata,
size_t privkeyfiledata_len,
unsigned const char *passphrase,
void **abstract)
{
libssh2_ecdsa_ctx *ec_ctx = NULL;
int ret;
if (abstract != NULL && *abstract) {
hostkey_method_ssh_ecdsa_dtor(session, abstract);
*abstract = NULL;
}
ret = _libssh2_ecdsa_new_private_frommemory(&ec_ctx, session,
privkeyfiledata,
privkeyfiledata_len, passphrase);
if (ret) {
return -1;
}
if (abstract != NULL)
*abstract = ec_ctx;
return 0;
}
/*
* hostkey_method_ecdsa_sig_verify
*
* Verify signature created by remote
*/
static int
hostkey_method_ssh_ecdsa_sig_verify(LIBSSH2_SESSION * session,
const unsigned char *sig,
size_t sig_len,
const unsigned char *m,
size_t m_len, void **abstract)
{
const unsigned char *r, *s, *p;
size_t r_len, s_len;
libssh2_ecdsa_ctx *ctx = (libssh2_ecdsa_ctx *) (*abstract);
(void) session;
if ( sig_len < 35 )
return -1;
/* Skip past keyname_len(4) + keyname(19){"ecdsa-sha2-nistp256"} + signature_len(4) */
p = sig;
p += 27;
r_len = _libssh2_ntohu32(p);
p += 4;
r = p;
p += r_len;
s_len = _libssh2_ntohu32(p);
p += 4;
s = p;
return _libssh2_ecdsa_verify(ctx, r, r_len, s, s_len, m, m_len);
}
#define LIBSSH2_HOSTKEY_METHOD_EC_SIGNV_HASH(digest_type) \
{ \
unsigned char hash[SHA##digest_type##_DIGEST_LENGTH]; \
libssh2_sha##digest_type##_ctx ctx; \
int i; \
libssh2_sha##digest_type##_init(&ctx); \
for(i = 0; i < veccount; i++) { \
libssh2_sha##digest_type##_update(ctx, datavec[i].iov_base, datavec[i].iov_len); \
} \
libssh2_sha##digest_type##_final(ctx, hash); \
ret = _libssh2_ecdsa_sign(session, ec_ctx, hash, SHA##digest_type##_DIGEST_LENGTH, \
signature, signature_len); \
}
/*
* hostkey_method_ecdsa_signv
*
* Construct a signature from an array of vectors
*/
static int
hostkey_method_ssh_ecdsa_signv(LIBSSH2_SESSION * session,
unsigned char **signature,
size_t *signature_len,
int veccount,
const struct iovec datavec[],
void **abstract)
{
libssh2_ecdsa_ctx *ec_ctx = (libssh2_ecdsa_ctx *) (*abstract);
libssh2_curve_type type = _libssh2_ecdsa_key_get_curve_type(ec_ctx);
int ret = 0;
if ( type == LIBSSH2_EC_CURVE_NISTP256 ) {
LIBSSH2_HOSTKEY_METHOD_EC_SIGNV_HASH(256);
}else if ( type == LIBSSH2_EC_CURVE_NISTP384 ) {
LIBSSH2_HOSTKEY_METHOD_EC_SIGNV_HASH(384);
}else if ( type == LIBSSH2_EC_CURVE_NISTP521 ){
LIBSSH2_HOSTKEY_METHOD_EC_SIGNV_HASH(512);
}else{
return -1;
}
return ret;
}
/*
* hostkey_method_ssh_ecdsa_dtor
*
* Shutdown the hostkey by freeing EC_KEY context
*/
static int
hostkey_method_ssh_ecdsa_dtor(LIBSSH2_SESSION * session, void **abstract)
{
libssh2_ecdsa_ctx *keyctx = (libssh2_ecdsa_ctx *) (*abstract);
(void) session;
if (keyctx != NULL)
_libssh2_ecdsa_free(keyctx);
*abstract = NULL;
return 0;
}
static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ecdsa_ssh_nistp256 = {
"ecdsa-sha2-nistp256",
SHA256_DIGEST_LENGTH,
hostkey_method_ssh_ecdsa_init,
hostkey_method_ssh_ecdsa_initPEM,
hostkey_method_ssh_ecdsa_initPEMFromMemory,
hostkey_method_ssh_ecdsa_sig_verify,
hostkey_method_ssh_ecdsa_signv,
NULL, /* encrypt */
hostkey_method_ssh_ecdsa_dtor,
};
static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ecdsa_ssh_nistp384 = {
"ecdsa-sha2-nistp384",
SHA384_DIGEST_LENGTH,
hostkey_method_ssh_ecdsa_init,
hostkey_method_ssh_ecdsa_initPEM,
hostkey_method_ssh_ecdsa_initPEMFromMemory,
hostkey_method_ssh_ecdsa_sig_verify,
hostkey_method_ssh_ecdsa_signv,
NULL, /* encrypt */
hostkey_method_ssh_ecdsa_dtor,
};
static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ecdsa_ssh_nistp521 = {
"ecdsa-sha2-nistp521",
SHA512_DIGEST_LENGTH,
hostkey_method_ssh_ecdsa_init,
hostkey_method_ssh_ecdsa_initPEM,
hostkey_method_ssh_ecdsa_initPEMFromMemory,
hostkey_method_ssh_ecdsa_sig_verify,
hostkey_method_ssh_ecdsa_signv,
NULL, /* encrypt */
hostkey_method_ssh_ecdsa_dtor,
};
#endif /* LIBSSH2_ECDSA */
static const LIBSSH2_HOSTKEY_METHOD *hostkey_methods[] = {
#if LIBSSH2_ECDSA
&hostkey_method_ecdsa_ssh_nistp256,
&hostkey_method_ecdsa_ssh_nistp384,
&hostkey_method_ecdsa_ssh_nistp521,
#endif
#if LIBSSH2_RSA
&hostkey_method_ssh_rsa,
#endif /* LIBSSH2_RSA */
@ -541,6 +830,15 @@ static int hostkey_type(const unsigned char *hostkey, size_t len)
const unsigned char dss[] = {
0, 0, 0, 0x07, 's', 's', 'h', '-', 'd', 's', 's'
};
const unsigned char ecdsa_256[] = {
0, 0, 0, 0x13, 'e', 'c', 'd', 's', 'a', '-', 's', 'h', 'a', '2', '-', 'n', 'i', 's', 't', 'p', '2', '5', '6'
};
const unsigned char ecdsa_384[] = {
0, 0, 0, 0x13, 'e', 'c', 'd', 's', 'a', '-', 's', 'h', 'a', '2', '-', 'n', 'i', 's', 't', 'p', '3', '8', '4'
};
const unsigned char ecdsa_521[] = {
0, 0, 0, 0x13, 'e', 'c', 'd', 's', 'a', '-', 's', 'h', 'a', '2', '-', 'n', 'i', 's', 't', 'p', '5', '2', '1'
};
if (len < 11)
return LIBSSH2_HOSTKEY_TYPE_UNKNOWN;
@ -551,6 +849,21 @@ static int hostkey_type(const unsigned char *hostkey, size_t len)
if (!memcmp(dss, hostkey, 11))
return LIBSSH2_HOSTKEY_TYPE_DSS;
if ( len < 15 )
return LIBSSH2_HOSTKEY_TYPE_UNKNOWN;
if ( len < 23 )
return LIBSSH2_HOSTKEY_TYPE_UNKNOWN;
if (!memcmp(ecdsa_256, hostkey, 23))
return LIBSSH2_HOSTKEY_TYPE_ECDSA;
if (!memcmp(ecdsa_384, hostkey, 23))
return LIBSSH2_HOSTKEY_TYPE_ECDSA;
if (!memcmp(ecdsa_521, hostkey, 23))
return LIBSSH2_HOSTKEY_TYPE_ECDSA;
return LIBSSH2_HOSTKEY_TYPE_UNKNOWN;
}
@ -575,4 +888,3 @@ libssh2_session_hostkey(LIBSSH2_SESSION *session, size_t *len, int *type)
*len = 0;
return NULL;
}

868
src/kex.c
Просмотреть файл

@ -68,34 +68,47 @@
libssh2_sha1_final(hash, (value) + len); \
len += SHA_DIGEST_LENGTH; \
} \
}
} \
/* Helper macro called from kex_method_diffie_hellman_group1_sha256_key_exchange */
#define LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA256_HASH(value, reqlen, version) \
{ \
libssh2_sha256_ctx hash; \
unsigned long len = 0; \
if (!(value)) { \
value = LIBSSH2_ALLOC(session, reqlen + SHA256_DIGEST_LENGTH); \
} \
if (value) \
while (len < (unsigned long)reqlen) { \
libssh2_sha256_init(&hash); \
libssh2_sha256_update(hash, exchange_state->k_value, \
exchange_state->k_value_len); \
libssh2_sha256_update(hash, exchange_state->h_sig_comp, \
SHA256_DIGEST_LENGTH); \
if (len > 0) { \
libssh2_sha256_update(hash, value, len); \
} else { \
libssh2_sha256_update(hash, (version), 1); \
libssh2_sha256_update(hash, session->session_id, \
session->session_id_len); \
} \
libssh2_sha256_final(hash, (value) + len); \
len += SHA256_DIGEST_LENGTH; \
} \
#define LIBSSH2_KEX_METHOD_EC_SHA_VALUE_HASH(value, reqlen, version) \
{ \
if (type == LIBSSH2_EC_CURVE_NISTP256) { \
LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(256, value, reqlen, version); \
} \
else if (type == LIBSSH2_EC_CURVE_NISTP384 ) { \
LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(384, value, reqlen, version); \
} \
else if (type == LIBSSH2_EC_CURVE_NISTP521 ) { \
LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(512, value, reqlen, version); \
} \
} \
#define LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(digest_type, value, reqlen, version) \
{ \
libssh2_sha##digest_type##_ctx hash; \
unsigned long len = 0; \
if (!(value)) { \
value = LIBSSH2_ALLOC(session, reqlen + SHA##digest_type##_DIGEST_LENGTH); \
} \
if (value) \
while (len < (unsigned long)reqlen) { \
libssh2_sha##digest_type##_init(&hash); \
libssh2_sha##digest_type##_update(hash, exchange_state->k_value, \
exchange_state->k_value_len); \
libssh2_sha##digest_type##_update(hash, exchange_state->h_sig_comp, \
SHA##digest_type##_DIGEST_LENGTH); \
if (len > 0) { \
libssh2_sha##digest_type##_update(hash, value, len); \
} else { \
libssh2_sha##digest_type##_update(hash, (version), 1); \
libssh2_sha##digest_type##_update(hash, session->session_id, \
session->session_id_len); \
} \
libssh2_sha##digest_type##_final(hash, (value) + len); \
len += SHA##digest_type##_DIGEST_LENGTH; \
} \
}
@ -305,10 +318,10 @@ static int diffie_hellman_sha1(LIBSSH2_SESSION *session,
"Server's SHA1 Fingerprint: %s", fingerprint);
}
#endif /* LIBSSH2DEBUG */
{
libssh2_sha256_ctx fingerprint_ctx;
if (libssh2_sha256_init(&fingerprint_ctx)) {
libssh2_sha256_update(fingerprint_ctx, session->server_hostkey,
session->server_hostkey_len);
@ -959,10 +972,10 @@ static int diffie_hellman_sha256(LIBSSH2_SESSION *session,
"Server's SHA1 Fingerprint: %s", fingerprint);
}
#endif /* LIBSSH2DEBUG */
{
libssh2_sha256_ctx fingerprint_ctx;
if (libssh2_sha256_init(&fingerprint_ctx)) {
libssh2_sha256_update(fingerprint_ctx, session->server_hostkey,
session->server_hostkey_len);
@ -1199,18 +1212,18 @@ static int diffie_hellman_sha256(LIBSSH2_SESSION *session,
unsigned char *iv = NULL, *secret = NULL;
int free_iv = 0, free_secret = 0;
LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA256_HASH(iv,
session->local.crypt->
iv_len,
(const unsigned char *)"A");
LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(256, iv,
session->local.crypt->
iv_len,
(const unsigned char *)"A");
if (!iv) {
ret = -1;
goto clean_exit;
}
LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA256_HASH(secret,
session->local.crypt->
secret_len,
(const unsigned char *)"C");
LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(256, secret,
session->local.crypt->
secret_len,
(const unsigned char *)"C");
if (!secret) {
LIBSSH2_FREE(session, iv);
ret = LIBSSH2_ERROR_KEX_FAILURE;
@ -1248,18 +1261,18 @@ static int diffie_hellman_sha256(LIBSSH2_SESSION *session,
unsigned char *iv = NULL, *secret = NULL;
int free_iv = 0, free_secret = 0;
LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA256_HASH(iv,
session->remote.crypt->
iv_len,
(const unsigned char *)"B");
LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(256, iv,
session->remote.crypt->
iv_len,
(const unsigned char *)"B");
if (!iv) {
ret = LIBSSH2_ERROR_KEX_FAILURE;
goto clean_exit;
}
LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA256_HASH(secret,
session->remote.crypt->
secret_len,
(const unsigned char *)"D");
LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(256, secret,
session->remote.crypt->
secret_len,
(const unsigned char *)"D");
if (!secret) {
LIBSSH2_FREE(session, iv);
ret = LIBSSH2_ERROR_KEX_FAILURE;
@ -1295,10 +1308,10 @@ static int diffie_hellman_sha256(LIBSSH2_SESSION *session,
unsigned char *key = NULL;
int free_key = 0;
LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA256_HASH(key,
session->local.mac->
key_len,
(const unsigned char *)"E");
LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(256, key,
session->local.mac->
key_len,
(const unsigned char *)"E");
if (!key) {
ret = LIBSSH2_ERROR_KEX_FAILURE;
goto clean_exit;
@ -1322,10 +1335,10 @@ static int diffie_hellman_sha256(LIBSSH2_SESSION *session,
unsigned char *key = NULL;
int free_key = 0;
LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA256_HASH(key,
session->remote.mac->
key_len,
(const unsigned char *)"F");
LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(256, key,
session->remote.mac->
key_len,
(const unsigned char *)"F");
if (!key) {
ret = LIBSSH2_ERROR_KEX_FAILURE;
goto clean_exit;
@ -1745,6 +1758,725 @@ kex_method_diffie_hellman_group_exchange_sha256_key_exchange
}
#if LIBSSH2_ECDSA
/* kex_session_ecdh_curve_type
* returns the EC curve type by name used in key exchange
*/
static int
kex_session_ecdh_curve_type(const char *name, libssh2_curve_type *out_type)
{
int ret = 0;
libssh2_curve_type type;
if ( name == NULL )
return -1;
if ( strcmp(name, "ecdh-sha2-nistp256") == 0)
type = LIBSSH2_EC_CURVE_NISTP256;
else if ( strcmp(name, "ecdh-sha2-nistp384") == 0)
type = LIBSSH2_EC_CURVE_NISTP384;
else if ( strcmp(name, "ecdh-sha2-nistp521") == 0)
type = LIBSSH2_EC_CURVE_NISTP521;
else {
ret = -1;
}
if (ret == 0 && out_type) {
*out_type = type;
}
return ret;
}
/* LIBSSH2_KEX_METHOD_EC_SHA_HASH_CREATE_VERIFY
*
* Macro that create and verifies EC SHA hash with a given digest bytes
*
* Payload format:
*
* 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
*
*/
#define LIBSSH2_KEX_METHOD_EC_SHA_HASH_CREATE_VERIFY(digest_type) \
{ \
\
libssh2_sha##digest_type##_ctx ctx; \
exchange_state->exchange_hash = (void*)&ctx; \
\
libssh2_sha##digest_type##_init(&ctx); \
if (session->local.banner) { \
_libssh2_htonu32(exchange_state->h_sig_comp, \
strlen((char *) session->local.banner) - 2); \
libssh2_sha##digest_type##_update(ctx, \
exchange_state->h_sig_comp, 4); \
libssh2_sha##digest_type##_update(ctx, \
(char *) session->local.banner, \
strlen((char *) session->local.banner) - 2); \
} else { \
_libssh2_htonu32(exchange_state->h_sig_comp, \
sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1); \
libssh2_sha##digest_type##_update(ctx, \
exchange_state->h_sig_comp, 4); \
libssh2_sha##digest_type##_update(ctx, \
LIBSSH2_SSH_DEFAULT_BANNER, \
sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1); \
} \
\
_libssh2_htonu32(exchange_state->h_sig_comp, \
strlen((char *) session->remote.banner)); \
libssh2_sha##digest_type##_update(ctx, \
exchange_state->h_sig_comp, 4); \
libssh2_sha##digest_type##_update(ctx, \
session->remote.banner, \
strlen((char *) session->remote.banner)); \
\
_libssh2_htonu32(exchange_state->h_sig_comp, \
session->local.kexinit_len); \
libssh2_sha##digest_type##_update(ctx, \
exchange_state->h_sig_comp, 4); \
libssh2_sha##digest_type##_update(ctx, \
session->local.kexinit, \
session->local.kexinit_len); \
\
_libssh2_htonu32(exchange_state->h_sig_comp, \
session->remote.kexinit_len); \
libssh2_sha##digest_type##_update(ctx, \
exchange_state->h_sig_comp, 4); \
libssh2_sha##digest_type##_update(ctx, \
session->remote.kexinit, \
session->remote.kexinit_len); \
\
_libssh2_htonu32(exchange_state->h_sig_comp, \
session->server_hostkey_len); \
libssh2_sha##digest_type##_update(ctx, \
exchange_state->h_sig_comp, 4); \
libssh2_sha##digest_type##_update(ctx, \
session->server_hostkey, \
session->server_hostkey_len); \
\
_libssh2_htonu32(exchange_state->h_sig_comp, \
public_key_len); \
libssh2_sha##digest_type##_update(ctx, \
exchange_state->h_sig_comp, 4); \
libssh2_sha##digest_type##_update(ctx, \
public_key, \
public_key_len); \
\
_libssh2_htonu32(exchange_state->h_sig_comp, \
server_public_key_len); \
libssh2_sha##digest_type##_update(ctx, \
exchange_state->h_sig_comp, 4); \
libssh2_sha##digest_type##_update(ctx, \
server_public_key, \
server_public_key_len); \
\
libssh2_sha##digest_type##_update(ctx, \
exchange_state->k_value, \
exchange_state->k_value_len); \
\
libssh2_sha##digest_type##_final(ctx, exchange_state->h_sig_comp); \
\
if (session->hostkey-> \
sig_verify(session, exchange_state->h_sig, \
exchange_state->h_sig_len, exchange_state->h_sig_comp, \
SHA##digest_type##_DIGEST_LENGTH, &session->server_hostkey_abstract)) { \
rc = -1; \
} \
} \
/* ecdh_sha2_nistp
* Elliptic Curve Diffie Hellman Key Exchange
*/
static int ecdh_sha2_nistp(LIBSSH2_SESSION *session, libssh2_curve_type type,
unsigned char *data, size_t data_len, unsigned char *public_key,
size_t public_key_len, _libssh2_ec_key *private_key,
kmdhgGPshakex_state_t *exchange_state)
{
int ret = 0;
int rc;
if (exchange_state->state == libssh2_NB_state_idle) {
/* Setup initial values */
exchange_state->k = _libssh2_bn_init();
exchange_state->state = libssh2_NB_state_created;
}
if ( exchange_state->state == libssh2_NB_state_created )
{
/* parse INIT reply data */
/* host key K_S */
unsigned char *s = data + 1; /* Advance past packet type */
unsigned char *server_public_key;
size_t server_public_key_len;
size_t host_sig_len;
session->server_hostkey_len = _libssh2_ntohu32((const unsigned char*)s);
s += 4;
session->server_hostkey = LIBSSH2_ALLOC(session, session->server_hostkey_len);
if (!session->server_hostkey) {
ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
"Unable to allocate memory for a copy "
"of the host key");
goto clean_exit;
}
memcpy(session->server_hostkey, s, session->server_hostkey_len);
s += session->server_hostkey_len;
#if LIBSSH2_MD5
{
libssh2_md5_ctx fingerprint_ctx;
if (libssh2_md5_init(&fingerprint_ctx)) {
libssh2_md5_update(fingerprint_ctx, session->server_hostkey,
session->server_hostkey_len);
libssh2_md5_final(fingerprint_ctx, session->server_hostkey_md5);
session->server_hostkey_md5_valid = TRUE;
}
else {
session->server_hostkey_md5_valid = FALSE;
}
}
#ifdef LIBSSH2DEBUG
{
char fingerprint[50], *fprint = fingerprint;
int i;
for(i = 0; i < 16; i++, fprint += 3) {
snprintf(fprint, 4, "%02x:", session->server_hostkey_md5[i]);
}
*(--fprint) = '\0';
_libssh2_debug(session, LIBSSH2_TRACE_KEX,
"Server's MD5 Fingerprint: %s", fingerprint);
}
#endif /* LIBSSH2DEBUG */
#endif /* ! LIBSSH2_MD5 */
{
libssh2_sha1_ctx fingerprint_ctx;
if (libssh2_sha1_init(&fingerprint_ctx)) {
libssh2_sha1_update(fingerprint_ctx, session->server_hostkey,
session->server_hostkey_len);
libssh2_sha1_final(fingerprint_ctx, session->server_hostkey_sha1);
session->server_hostkey_sha1_valid = TRUE;
}
else {
session->server_hostkey_sha1_valid = FALSE;
}
}
#ifdef LIBSSH2DEBUG
{
char fingerprint[64], *fprint = fingerprint;
int i;
for(i = 0; i < 20; i++, fprint += 3) {
snprintf(fprint, 4, "%02x:", session->server_hostkey_sha1[i]);
}
*(--fprint) = '\0';
_libssh2_debug(session, LIBSSH2_TRACE_KEX,
"Server's SHA1 Fingerprint: %s", fingerprint);
}
#endif /* LIBSSH2DEBUG */
/* SHA256 */
{
libssh2_sha256_ctx fingerprint_ctx;
if (libssh2_sha256_init(&fingerprint_ctx)) {
libssh2_sha256_update(fingerprint_ctx, session->server_hostkey,
session->server_hostkey_len);
libssh2_sha256_final(fingerprint_ctx, session->server_hostkey_sha256);
session->server_hostkey_sha256_valid = TRUE;
}
else {
session->server_hostkey_sha256_valid = FALSE;
}
}
#ifdef LIBSSH2DEBUG
{
char *base64Fingerprint = NULL;
_libssh2_base64_encode(session, (const char*)session->server_hostkey_sha256, SHA256_DIGEST_LENGTH, &base64Fingerprint);
if ( base64Fingerprint != NULL ) {
_libssh2_debug(session, LIBSSH2_TRACE_KEX,
"Server's SHA256 Fingerprint: %s", base64Fingerprint);
LIBSSH2_FREE(session, base64Fingerprint);
}
}
#endif /* LIBSSH2DEBUG */
if (session->hostkey->init(session, session->server_hostkey,
session->server_hostkey_len,
&session->server_hostkey_abstract)) {
ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT,
"Unable to initialize hostkey importer");
goto clean_exit;
}
/* server public key Q_S */
server_public_key_len = _libssh2_ntohu32((const unsigned char*)s);
s += 4;
server_public_key = s;
s += server_public_key_len;
/* server signature */
host_sig_len = _libssh2_ntohu32((const unsigned char*)s);
s += 4;
exchange_state->h_sig = s;
exchange_state->h_sig_len = host_sig_len;
s += host_sig_len;
/* Compute the shared secret K */
rc = _libssh2_ecdh_gen_k(&exchange_state->k, private_key, server_public_key, server_public_key_len);
if ( rc != 0 ) {
ret = _libssh2_error(session, LIBSSH2_ERROR_KEX_FAILURE,
"Unable to create ECDH shared secret");
goto clean_exit;
}
exchange_state->k_value_len = _libssh2_bn_bytes(exchange_state->k) + 5;
if (_libssh2_bn_bits(exchange_state->k) % 8) {
/* don't need leading 00 */
exchange_state->k_value_len--;
}
exchange_state->k_value =
LIBSSH2_ALLOC(session, exchange_state->k_value_len);
if (!exchange_state->k_value) {
ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
"Unable to allocate buffer for K");
goto clean_exit;
}
_libssh2_htonu32(exchange_state->k_value,
exchange_state->k_value_len - 4);
if (_libssh2_bn_bits(exchange_state->k) % 8) {
_libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 4);
} else {
exchange_state->k_value[4] = 0;
_libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 5);
}
/* verify hash */
switch ( type ) {
case LIBSSH2_EC_CURVE_NISTP256:
LIBSSH2_KEX_METHOD_EC_SHA_HASH_CREATE_VERIFY(256);
break;
case LIBSSH2_EC_CURVE_NISTP384:
LIBSSH2_KEX_METHOD_EC_SHA_HASH_CREATE_VERIFY(384);
break;
case LIBSSH2_EC_CURVE_NISTP521:
LIBSSH2_KEX_METHOD_EC_SHA_HASH_CREATE_VERIFY(512);
break;
}
if ( rc != 0 ) {
ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_SIGN,
"Unable to verify hostkey signature");
goto clean_exit;
}
exchange_state->c = SSH_MSG_NEWKEYS;
exchange_state->state = libssh2_NB_state_sent;
}
if (exchange_state->state == libssh2_NB_state_sent) {
rc = _libssh2_transport_send(session, &exchange_state->c, 1, NULL, 0);
if (rc == LIBSSH2_ERROR_EAGAIN) {
return rc;
} else if (rc) {
ret = _libssh2_error(session, rc, "Unable to send NEWKEYS message");
goto clean_exit;
}
exchange_state->state = libssh2_NB_state_sent2;
}
if (exchange_state->state == libssh2_NB_state_sent2) {
rc = _libssh2_packet_require(session, SSH_MSG_NEWKEYS,
&exchange_state->tmp,
&exchange_state->tmp_len, 0, NULL, 0,
&exchange_state->req_state);
if (rc == LIBSSH2_ERROR_EAGAIN) {
return rc;
} else if (rc) {
ret = _libssh2_error(session, rc, "Timed out waiting for NEWKEYS");
goto clean_exit;
}
/* The first key exchange has been performed,
switch to active crypt/comp/mac mode */
session->state |= LIBSSH2_STATE_NEWKEYS;
_libssh2_debug(session, LIBSSH2_TRACE_KEX, "Received NEWKEYS message");
/* This will actually end up being just packet_type(1)
for this packet type anyway */
LIBSSH2_FREE(session, exchange_state->tmp);
if (!session->session_id) {
size_t digest_length = 0;
if ( type == LIBSSH2_EC_CURVE_NISTP256 )
digest_length = SHA256_DIGEST_LENGTH;
else if ( type == LIBSSH2_EC_CURVE_NISTP384 )
digest_length = SHA384_DIGEST_LENGTH;
else if ( type == LIBSSH2_EC_CURVE_NISTP521 )
digest_length = SHA512_DIGEST_LENGTH;
else{
ret = _libssh2_error(session, LIBSSH2_ERROR_KEX_FAILURE,
"Unknown SHA digest for EC curve");
goto clean_exit;
}
session->session_id = LIBSSH2_ALLOC(session, digest_length);
if (!session->session_id) {
ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
"Unable to allocate buffer for SHA digest");
goto clean_exit;
}
memcpy(session->session_id, exchange_state->h_sig_comp,
digest_length);
session->session_id_len = digest_length;
_libssh2_debug(session, LIBSSH2_TRACE_KEX, "session_id calculated");
}
/* Cleanup any existing cipher */
if (session->local.crypt->dtor) {
session->local.crypt->dtor(session,
&session->local.crypt_abstract);
}
/* Calculate IV/Secret/Key for each direction */
if (session->local.crypt->init) {
unsigned char *iv = NULL, *secret = NULL;
int free_iv = 0, free_secret = 0;
LIBSSH2_KEX_METHOD_EC_SHA_VALUE_HASH(iv,
session->local.crypt->
iv_len, "A");
if (!iv) {
ret = -1;
goto clean_exit;
}
LIBSSH2_KEX_METHOD_EC_SHA_VALUE_HASH(secret,
session->local.crypt->
secret_len, "C");
if (!secret) {
LIBSSH2_FREE(session, iv);
ret = LIBSSH2_ERROR_KEX_FAILURE;
goto clean_exit;
}
if (session->local.crypt->
init(session, session->local.crypt, iv, &free_iv, secret,
&free_secret, 1, &session->local.crypt_abstract)) {
LIBSSH2_FREE(session, iv);
LIBSSH2_FREE(session, secret);
ret = LIBSSH2_ERROR_KEX_FAILURE;
goto clean_exit;
}
if (free_iv) {
memset(iv, 0, session->local.crypt->iv_len);
LIBSSH2_FREE(session, iv);
}
if (free_secret) {
memset(secret, 0, session->local.crypt->secret_len);
LIBSSH2_FREE(session, secret);
}
}
_libssh2_debug(session, LIBSSH2_TRACE_KEX,
"Client to Server IV and Key calculated");
if (session->remote.crypt->dtor) {
/* Cleanup any existing cipher */
session->remote.crypt->dtor(session,
&session->remote.crypt_abstract);
}
if (session->remote.crypt->init) {
unsigned char *iv = NULL, *secret = NULL;
int free_iv = 0, free_secret = 0;
LIBSSH2_KEX_METHOD_EC_SHA_VALUE_HASH(iv,
session->remote.crypt->
iv_len, "B");
if (!iv) {
ret = LIBSSH2_ERROR_KEX_FAILURE;
goto clean_exit;
}
LIBSSH2_KEX_METHOD_EC_SHA_VALUE_HASH(secret,
session->remote.crypt->
secret_len, "D");
if (!secret) {
LIBSSH2_FREE(session, iv);
ret = LIBSSH2_ERROR_KEX_FAILURE;
goto clean_exit;
}
if (session->remote.crypt->
init(session, session->remote.crypt, iv, &free_iv, secret,
&free_secret, 0, &session->remote.crypt_abstract)) {
LIBSSH2_FREE(session, iv);
LIBSSH2_FREE(session, secret);
ret = LIBSSH2_ERROR_KEX_FAILURE;
goto clean_exit;
}
if (free_iv) {
memset(iv, 0, session->remote.crypt->iv_len);
LIBSSH2_FREE(session, iv);
}
if (free_secret) {
memset(secret, 0, session->remote.crypt->secret_len);
LIBSSH2_FREE(session, secret);
}
}
_libssh2_debug(session, LIBSSH2_TRACE_KEX,
"Server to Client IV and Key calculated");
if (session->local.mac->dtor) {
session->local.mac->dtor(session, &session->local.mac_abstract);
}
if (session->local.mac->init) {
unsigned char *key = NULL;
int free_key = 0;
LIBSSH2_KEX_METHOD_EC_SHA_VALUE_HASH(key,
session->local.mac->
key_len, "E");
if (!key) {
ret = LIBSSH2_ERROR_KEX_FAILURE;
goto clean_exit;
}
session->local.mac->init(session, key, &free_key,
&session->local.mac_abstract);
if (free_key) {
memset(key, 0, session->local.mac->key_len);
LIBSSH2_FREE(session, key);
}
}
_libssh2_debug(session, LIBSSH2_TRACE_KEX,
"Client to Server HMAC Key calculated");
if (session->remote.mac->dtor) {
session->remote.mac->dtor(session, &session->remote.mac_abstract);
}
if (session->remote.mac->init) {
unsigned char *key = NULL;
int free_key = 0;
LIBSSH2_KEX_METHOD_EC_SHA_VALUE_HASH(key,
session->remote.mac->
key_len, "F");
if (!key) {
ret = LIBSSH2_ERROR_KEX_FAILURE;
goto clean_exit;
}
session->remote.mac->init(session, key, &free_key,
&session->remote.mac_abstract);
if (free_key) {
memset(key, 0, session->remote.mac->key_len);
LIBSSH2_FREE(session, key);
}
}
_libssh2_debug(session, LIBSSH2_TRACE_KEX,
"Server to Client HMAC Key calculated");
/* Initialize compression for each direction */
/* Cleanup any existing compression */
if (session->local.comp && session->local.comp->dtor) {
session->local.comp->dtor(session, 1,
&session->local.comp_abstract);
}
if (session->local.comp && session->local.comp->init) {
if (session->local.comp->init(session, 1,
&session->local.comp_abstract)) {
ret = LIBSSH2_ERROR_KEX_FAILURE;
goto clean_exit;
}
}
_libssh2_debug(session, LIBSSH2_TRACE_KEX,
"Client to Server compression initialized");
if (session->remote.comp && session->remote.comp->dtor) {
session->remote.comp->dtor(session, 0,
&session->remote.comp_abstract);
}
if (session->remote.comp && session->remote.comp->init) {
if (session->remote.comp->init(session, 0,
&session->remote.comp_abstract)) {
ret = LIBSSH2_ERROR_KEX_FAILURE;
goto clean_exit;
}
}
_libssh2_debug(session, LIBSSH2_TRACE_KEX,
"Server to Client compression initialized");
}
clean_exit:
_libssh2_bn_free(exchange_state->k);
exchange_state->k = NULL;
if (exchange_state->k_value) {
LIBSSH2_FREE(session, exchange_state->k_value);
exchange_state->k_value = NULL;
}
exchange_state->state = libssh2_NB_state_idle;
return ret;
}
/* kex_method_ecdh_key_exchange
*
* Elliptic Curve Diffie Hellman Key Exchange
* supports SHA256/384/512 hashes based on negotated ecdh method
*
*/
static int
kex_method_ecdh_key_exchange
(LIBSSH2_SESSION * session, key_exchange_state_low_t * key_state)
{
int ret = 0;
int rc = 0;
unsigned char *s;
libssh2_curve_type type;
if (key_state->state == libssh2_NB_state_idle) {
key_state->public_key_oct = NULL;
key_state->state = libssh2_NB_state_created;
}
if ( key_state->state == libssh2_NB_state_created )
{
rc = kex_session_ecdh_curve_type(session->kex->name, &type);
if ( rc != 0 ) {
ret = _libssh2_error(session, -1,
"Unknown KEX nistp curve type");
goto ecdh_clean_exit;
}
rc = _libssh2_ecdsa_create_key(&key_state->private_key, &key_state->public_key_oct,
&key_state->public_key_oct_len, type);
if ( rc != 0 )
{
ret = _libssh2_error(session, rc,
"Unable to create private key");
goto ecdh_clean_exit;
}
key_state->request[0] = SSH2_MSG_KEX_ECDH_INIT;
s = key_state->request + 1;
_libssh2_store_str(&s, (const char*)key_state->public_key_oct, key_state->public_key_oct_len);
key_state->request_len = key_state->public_key_oct_len + 5;
_libssh2_debug(session, LIBSSH2_TRACE_KEX,
"Initiating ECDH SHA2 NISTP256");
key_state->state = libssh2_NB_state_sent;
}
if ( key_state->state == libssh2_NB_state_sent ) {
rc = _libssh2_transport_send(session, key_state->request,
key_state->request_len, NULL, 0);
if (rc == LIBSSH2_ERROR_EAGAIN) {
return rc;
} else if (rc) {
ret = _libssh2_error(session, rc,
"Unable to send ECDH_INIT");
goto ecdh_clean_exit;
}
key_state->state = libssh2_NB_state_sent1;
}
if ( key_state->state == libssh2_NB_state_sent1 ) {
rc = _libssh2_packet_require(session, SSH2_MSG_KEX_ECDH_REPLY,
&key_state->data, &key_state->data_len,
0, NULL, 0, &key_state->req_state);
if (rc == LIBSSH2_ERROR_EAGAIN) {
return rc;
} else if (rc) {
ret = _libssh2_error(session, rc,
"Timeout waiting for ECDH_REPLY reply");
goto ecdh_clean_exit;
}
key_state->state = libssh2_NB_state_sent2;
}
if ( key_state->state == libssh2_NB_state_sent2 ) {
(void)kex_session_ecdh_curve_type(session->kex->name, &type);
ret = ecdh_sha2_nistp(session, type, key_state->data, key_state->data_len, (unsigned char*)key_state->public_key_oct,
key_state->public_key_oct_len, key_state->private_key, &key_state->exchange_state);
if (ret == LIBSSH2_ERROR_EAGAIN) {
return ret;
}
LIBSSH2_FREE(session, key_state->data);
}
ecdh_clean_exit:
if (key_state->public_key_oct) {
free(key_state->public_key_oct);
key_state->public_key_oct = NULL;
}
if (key_state->private_key) {
_libssh2_ecdsa_free(key_state->private_key);
key_state->private_key = NULL;
}
key_state->state = libssh2_NB_state_idle;
return ret;
}
#endif /*LIBSSH2_ECDSA*/
#define LIBSSH2_KEX_METHOD_FLAG_REQ_ENC_HOSTKEY 0x0001
#define LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY 0x0002
@ -1774,7 +2506,35 @@ kex_method_diffie_helman_group_exchange_sha256 = {
LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
};
#if LIBSSH2_ECDSA
static const LIBSSH2_KEX_METHOD
kex_method_ecdh_sha2_nistp256 = {
"ecdh-sha2-nistp256",
kex_method_ecdh_key_exchange,
LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
};
static const LIBSSH2_KEX_METHOD
kex_method_ecdh_sha2_nistp384 = {
"ecdh-sha2-nistp384",
kex_method_ecdh_key_exchange,
LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
};
static const LIBSSH2_KEX_METHOD
kex_method_ecdh_sha2_nistp521 = {
"ecdh-sha2-nistp521",
kex_method_ecdh_key_exchange,
LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
};
#endif
static const LIBSSH2_KEX_METHOD *libssh2_kex_methods[] = {
#if LIBSSH2_ECDSA
&kex_method_ecdh_sha2_nistp256,
&kex_method_ecdh_sha2_nistp384,
&kex_method_ecdh_sha2_nistp521,
#endif
&kex_method_diffie_helman_group_exchange_sha256,
&kex_method_diffie_helman_group_exchange_sha1,
&kex_method_diffie_helman_group14_sha1,

Просмотреть файл

@ -54,10 +54,15 @@
#define LIBSSH2_RSA 1
#define LIBSSH2_DSA 1
#define LIBSSH2_ECDSA 0
#define MD5_DIGEST_LENGTH 16
#define SHA_DIGEST_LENGTH 20
#define SHA256_DIGEST_LENGTH 32
#define SHA384_DIGEST_LENGTH 48
#define SHA512_DIGEST_LENGTH 64
#define EC_MAX_POINT_LEN ((528 * 2 / 8) + 1)
#define _libssh2_random(buf, len) \
(gcry_randomize ((buf), (len), GCRY_STRONG_RANDOM), 1)
@ -87,6 +92,28 @@
#define libssh2_sha256(message, len, out) \
gcry_md_hash_buffer (GCRY_MD_SHA256, out, message, len)
#define libssh2_sha384_ctx gcry_md_hd_t
#define libssh2_sha384_init(ctx) \
(GPG_ERR_NO_ERROR == gcry_md_open (ctx, GCRY_MD_SHA384, 0))
#define libssh2_sha384_update(ctx, data, len) \
gcry_md_write (ctx, (unsigned char *) data, len)
#define libssh2_sha384_final(ctx, out) \
memcpy (out, gcry_md_read (ctx, 0), SHA384_DIGEST_LENGTH), gcry_md_close (ctx)
#define libssh2_sha384(message, len, out) \
gcry_md_hash_buffer (GCRY_MD_SHA384, out, message, len)
#define libssh2_sha512_ctx gcry_md_hd_t
#define libssh2_sha512_init(ctx) \
(GPG_ERR_NO_ERROR == gcry_md_open (ctx, GCRY_MD_SHA512, 0))
#define libssh2_sha512_update(ctx, data, len) \
gcry_md_write (ctx, (unsigned char *) data, len)
#define libssh2_sha512_final(ctx, out) \
memcpy (out, gcry_md_read (ctx, 0), SHA512_DIGEST_LENGTH), gcry_md_close (ctx)
#define libssh2_sha512(message, len, out) \
gcry_md_hash_buffer (GCRY_MD_SHA512, out, message, len)
#define libssh2_md5_ctx gcry_md_hd_t
/* returns 0 in case of failure */
@ -135,6 +162,11 @@
#define _libssh2_dsa_free(dsactx) gcry_sexp_release (dsactx)
#if LIBSSH2_ECDSA
#else
#define _libssh2_ec_key void
#endif
#define _libssh2_cipher_type(name) int name
#define _libssh2_cipher_ctx gcry_cipher_hd_t

Просмотреть файл

@ -154,7 +154,7 @@ static inline int writev(int sock, struct iovec *iov, int nvecs)
* padding length, payload, padding, and MAC.)."
*/
#define MAX_SSH_PACKET_LEN 35000
#define MAX_SHA_DIGEST_LEN SHA256_DIGEST_LENGTH
#define MAX_SHA_DIGEST_LEN SHA512_DIGEST_LENGTH
#define LIBSSH2_ALLOC(session, count) \
session->alloc((count), &(session)->abstract)
@ -271,10 +271,13 @@ typedef struct key_exchange_state_low_t
kmdhgGPshakex_state_t exchange_state;
_libssh2_bn *p; /* SSH2 defined value (p_value) */
_libssh2_bn *g; /* SSH2 defined value (2) */
unsigned char request[13];
unsigned char request[256]; /* Must fit EC_MAX_POINT_LEN + data */
unsigned char *data;
size_t request_len;
size_t data_len;
_libssh2_ec_key *private_key; /* SSH2 ecdh private key */
unsigned char *public_key_oct; /* SSH2 ecdh public key octal value */
size_t public_key_oct_len; /* SSH2 ecdh public key octal value length */
} key_exchange_state_low_t;
typedef struct key_exchange_state_t
@ -987,6 +990,10 @@ _libssh2_debug(LIBSSH2_SESSION * session, int context, const char *format, ...)
#define SSH_MSG_KEX_DH_GEX_INIT 32
#define SSH_MSG_KEX_DH_GEX_REPLY 33
/* ecdh */
#define SSH2_MSG_KEX_ECDH_INIT 30
#define SSH2_MSG_KEX_ECDH_REPLY 31
/* User Authentication */
#define SSH_MSG_USERAUTH_REQUEST 50
#define SSH_MSG_USERAUTH_FAILURE 51

Просмотреть файл

@ -27,12 +27,21 @@
#define LIBSSH2_RSA 1
#define LIBSSH2_DSA 0
#define LIBSSH2_ECDSA 0
#define MD5_DIGEST_LENGTH 16
#define SHA_DIGEST_LENGTH 20
#define SHA256_DIGEST_LENGTH 32
#define SHA384_DIGEST_LENGTH 48
#define SHA512_DIGEST_LENGTH 64
#define EC_MAX_POINT_LEN ((528 * 2 / 8) + 1)
#if LIBSSH2_ECDSA
#else
#define _libssh2_ec_key void
#endif
/*******************************************************************/
/*
* mbedTLS backend: Global context handles
@ -80,6 +89,8 @@ mbedtls_ctr_drbg_context _libssh2_mbedtls_ctr_drbg;
_libssh2_mbedtls_hash_init(pctx, MBEDTLS_MD_RIPEMD160, key, keylen)
#define libssh2_hmac_sha256_init(pctx, key, keylen) \
_libssh2_mbedtls_hash_init(pctx, MBEDTLS_MD_SHA256, key, keylen)
#define libssh2_hmac_sha384_init(pctx, key, keylen) \
_libssh2_mbedtls_hash_init(pctx, MBEDTLS_MD_SHA384, key, keylen)
#define libssh2_hmac_sha512_init(pctx, key, keylen) \
_libssh2_mbedtls_hash_init(pctx, MBEDTLS_MD_SHA512, key, keylen)
@ -117,6 +128,23 @@ mbedtls_ctr_drbg_context _libssh2_mbedtls_ctr_drbg;
_libssh2_mbedtls_hash(data, datalen, MBEDTLS_MD_SHA256, hash)
/*******************************************************************/
/*
* mbedTLS backend: SHA384 functions
*/
#define libssh2_sha384_ctx mbedtls_md_context_t
#define libssh2_sha384_init(pctx) \
_libssh2_mbedtls_hash_init(pctx, MBEDTLS_MD_SHA384, NULL, 0)
#define libssh2_sha384_update(ctx, data, datalen) \
mbedtls_md_update(&ctx, (unsigned char *) data, datalen)
#define libssh2_sha384_final(ctx, hash) \
_libssh2_mbedtls_hash_final(&ctx, hash)
#define libssh2_sha384(data, datalen, hash) \
_libssh2_mbedtls_hash(data, datalen, MBEDTLS_MD_SHA384, hash)
/*******************************************************************/
/*
* mbedTLS backend: SHA512 functions

Просмотреть файл

@ -49,6 +49,24 @@
#define EVP_MAX_BLOCK_LENGTH 32
#endif
static unsigned char *
write_bn(unsigned char *buf, const BIGNUM *bn, int bn_bytes)
{
unsigned char *p = buf;
/* Left space for bn size which will be written below. */
p += 4;
*p = 0;
BN_bn2bin(bn, p + 1);
if (!(*(p + 1) & 0x80)) {
memmove(p, p + 1, --bn_bytes);
}
_libssh2_htonu32(p - 4, bn_bytes); /* Post write bn size. */
return p + bn_bytes;
}
int
_libssh2_rsa_new(libssh2_rsa_ctx ** rsa,
const unsigned char *edata,
@ -231,6 +249,144 @@ _libssh2_dsa_sha1_verify(libssh2_dsa_ctx * dsactx,
}
#endif /* LIBSSH_DSA */
#if LIBSSH2_ECDSA
/* _libssh2_ecdsa_key_get_curve_type
*
* returns key curve type that maps to libssh2_curve_type
*
*/
libssh2_curve_type
_libssh2_ecdsa_key_get_curve_type(_libssh2_ec_key *key)
{
const EC_GROUP *group = EC_KEY_get0_group(key);
return EC_GROUP_get_curve_name(group);
}
/* _libssh2_ecdsa_curve_type_from_name
*
* returns 0 for success, key curve type that maps to libssh2_curve_type
*
*/
int
_libssh2_ecdsa_curve_type_from_name(const char *name, libssh2_curve_type *out_type)
{
int ret = 0;
libssh2_curve_type type;
if ( name == NULL || strlen(name) != 19 )
return -1;
if ( strcmp(name, "ecdsa-sha2-nistp256") == 0)
type = LIBSSH2_EC_CURVE_NISTP256;
else if ( strcmp(name, "ecdsa-sha2-nistp384") == 0)
type = LIBSSH2_EC_CURVE_NISTP384;
else if ( strcmp(name, "ecdsa-sha2-nistp521") == 0)
type = LIBSSH2_EC_CURVE_NISTP521;
else {
ret = -1;
}
if (ret == 0 && out_type) {
*out_type = type;
}
return ret;
}
/* _libssh2_ecdsa_curve_name_with_octal_new
*
* Creates a new public key given an octal string, length and type
*
*/
int
_libssh2_ecdsa_curve_name_with_octal_new(libssh2_ecdsa_ctx ** ec_ctx,
const unsigned char *k,
size_t k_len, libssh2_curve_type curve)
{
int ret = 0;
const EC_GROUP *ec_group = NULL;
EC_KEY *ec_key = EC_KEY_new_by_curve_name(curve);
EC_POINT *point = NULL;
if ( ec_key ) {
ec_group = EC_KEY_get0_group(ec_key);
point = EC_POINT_new(ec_group);
ret = EC_POINT_oct2point(ec_group, point, k, k_len, NULL);
ret = EC_KEY_set_public_key(ec_key, point);
if (point != NULL)
EC_POINT_free(point);
if ( ec_ctx != NULL )
*ec_ctx = ec_key;
}
return (ret == 1) ? 0 : -1;
}
#define LIBSSH2_ECDSA_VERIFY(digest_type) \
{ \
unsigned char hash[SHA##digest_type##_DIGEST_LENGTH]; \
libssh2_sha##digest_type(m, m_len, hash); \
ret = ECDSA_do_verify(hash, SHA##digest_type##_DIGEST_LENGTH, \
ecdsa_sig, ec_key); \
\
}
int
_libssh2_ecdsa_verify(libssh2_ecdsa_ctx * ctx,
const unsigned char *r, size_t r_len,
const unsigned char *s, size_t s_len,
const unsigned char *m, size_t m_len)
{
int ret = 0;
EC_KEY *ec_key = (EC_KEY*)ctx;
libssh2_curve_type type = _libssh2_ecdsa_key_get_curve_type(ec_key);
#if HAVE_OPAQUE_STRUCTS
ECDSA_SIG *ecdsa_sig = ECDSA_SIG_new();
BIGNUM *pr = BN_new();
BIGNUM *ps = BN_new();
BN_bin2bn(r, r_len, pr);
BN_bin2bn(s, s_len, ps);
ECDSA_SIG_set0(ecdsa_sig, pr, ps);
#else
ECDSA_SIG ecdsa_sig_;
ECDSA_SIG *ecdsa_sig = &ecdsa_sig_;
ecdsa_sig_.r = BN_new();
BN_bin2bn(r, r_len, ecdsa_sig_.r);
ecdsa_sig_.s = BN_new();
BN_bin2bn(s, s_len, ecdsa_sig_.s);
#endif
if ( type == LIBSSH2_EC_CURVE_NISTP256 ) {
LIBSSH2_ECDSA_VERIFY(256);
}else if ( type == LIBSSH2_EC_CURVE_NISTP384 ) {
LIBSSH2_ECDSA_VERIFY(384);
}else if ( type == LIBSSH2_EC_CURVE_NISTP521 ) {
LIBSSH2_ECDSA_VERIFY(512);
}
#if HAVE_OPAQUE_STRUCTS
if ( ecdsa_sig )
ECDSA_SIG_free(ecdsa_sig);
#else
BN_clear_free(ecdsa_sig_.s);
BN_clear_free(ecdsa_sig_.r);
#endif
return (ret == 1) ? 0 : -1;
}
#endif /* LIBSSH2_ECDSA */
int
_libssh2_cipher_init(_libssh2_cipher_ctx * h,
_libssh2_cipher_type(algo),
@ -599,6 +755,41 @@ _libssh2_dsa_new_private(libssh2_dsa_ctx ** dsa,
}
#endif /* LIBSSH_DSA */
#if LIBSSH2_ECDSA
int
_libssh2_ecdsa_new_private_frommemory(libssh2_ecdsa_ctx ** ec_ctx,
LIBSSH2_SESSION * session,
const char *filedata, size_t filedata_len,
unsigned const char *passphrase)
{
pem_read_bio_func read_ec =
(pem_read_bio_func) &PEM_read_bio_ECPrivateKey;
(void) session;
_libssh2_init_if_needed();
return read_private_key_from_memory((void **) ec_ctx, read_ec,
filedata, filedata_len, passphrase);
}
int
_libssh2_ecdsa_new_private(libssh2_ecdsa_ctx ** ec_ctx,
LIBSSH2_SESSION * session,
const char *filename, unsigned const char *passphrase)
{
pem_read_bio_func read_ec = (pem_read_bio_func) &PEM_read_bio_ECPrivateKey;
(void) session;
_libssh2_init_if_needed ();
return read_private_key_from_file((void **) ec_ctx, read_ec,
filename, passphrase);
}
#endif /* LIBSSH2_ECDSA */
int
_libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session,
libssh2_rsa_ctx * rsactx,
@ -675,6 +866,69 @@ _libssh2_dsa_sha1_sign(libssh2_dsa_ctx * dsactx,
}
#endif /* LIBSSH_DSA */
#if LIBSSH2_ECDSA
int
_libssh2_ecdsa_sign(LIBSSH2_SESSION * session, libssh2_ecdsa_ctx * ec_ctx,
const unsigned char *hash, unsigned long hash_len,
unsigned char **signature, size_t *signature_len)
{
int r_len, s_len;
int rc = 0;
size_t out_buffer_len = 0;
unsigned char *sp;
const BIGNUM *pr = NULL, *ps = NULL;
unsigned char *temp_buffer = NULL;
unsigned char *out_buffer = NULL;
ECDSA_SIG *sig = ECDSA_do_sign(hash, hash_len, ec_ctx);
if ( sig == NULL )
return -1;
#if HAVE_OPAQUE_STRUCTS
ECDSA_SIG_get0(sig, &pr, &ps);
#else
pr = sig->r;
ps = sig->s;
#endif
r_len = BN_num_bytes(pr) + 1;
s_len = BN_num_bytes(ps) + 1;
temp_buffer = malloc(r_len + s_len + 8);
if ( temp_buffer == NULL ) {
rc = -1;
goto clean_exit;
}
sp = temp_buffer;
sp = write_bn(sp, pr, r_len);
sp = write_bn(sp, ps, s_len);
out_buffer_len = (size_t)(sp - temp_buffer);
out_buffer = LIBSSH2_CALLOC(session, out_buffer_len);
if ( out_buffer == NULL ) {
rc = -1;
goto clean_exit;
}
memcpy(out_buffer, temp_buffer, out_buffer_len);
*signature = out_buffer;
*signature_len = out_buffer_len;
clean_exit:
if ( temp_buffer != NULL )
free(temp_buffer);
if ( sig )
ECDSA_SIG_free(sig);
return rc;
}
#endif /* LIBSSH2_ECDSA */
int
_libssh2_sha1_init(libssh2_sha1_ctx *ctx)
{
@ -779,6 +1033,110 @@ _libssh2_sha256(const unsigned char *message, unsigned long len,
return 1; /* error */
}
int
_libssh2_sha384_init(libssh2_sha384_ctx *ctx)
{
#ifdef HAVE_OPAQUE_STRUCTS
*ctx = EVP_MD_CTX_new();
if (*ctx == NULL)
return 0;
if (EVP_DigestInit(*ctx, EVP_get_digestbyname("sha384")))
return 1;
EVP_MD_CTX_free(*ctx);
*ctx = NULL;
return 0;
#else
EVP_MD_CTX_init(ctx);
return EVP_DigestInit(ctx, EVP_get_digestbyname("sha384"));
#endif
}
int
_libssh2_sha384(const unsigned char *message, unsigned long len,
unsigned char *out)
{
#ifdef HAVE_OPAQUE_STRUCTS
EVP_MD_CTX * ctx = EVP_MD_CTX_new();
if (ctx == NULL)
return 1; /* error */
if(EVP_DigestInit(ctx, EVP_get_digestbyname("sha384"))) {
EVP_DigestUpdate(ctx, message, len);
EVP_DigestFinal(ctx, out, NULL);
EVP_MD_CTX_free(ctx);
return 0; /* success */
}
EVP_MD_CTX_free(ctx);
#else
EVP_MD_CTX ctx;
EVP_MD_CTX_init(&ctx);
if(EVP_DigestInit(&ctx, EVP_get_digestbyname("sha384"))) {
EVP_DigestUpdate(&ctx, message, len);
EVP_DigestFinal(&ctx, out, NULL);
return 0; /* success */
}
#endif
return 1; /* error */
}
int
_libssh2_sha512_init(libssh2_sha512_ctx *ctx)
{
#ifdef HAVE_OPAQUE_STRUCTS
*ctx = EVP_MD_CTX_new();
if (*ctx == NULL)
return 0;
if (EVP_DigestInit(*ctx, EVP_get_digestbyname("sha512")))
return 1;
EVP_MD_CTX_free(*ctx);
*ctx = NULL;
return 0;
#else
EVP_MD_CTX_init(ctx);
return EVP_DigestInit(ctx, EVP_get_digestbyname("sha512"));
#endif
}
int
_libssh2_sha512(const unsigned char *message, unsigned long len,
unsigned char *out)
{
#ifdef HAVE_OPAQUE_STRUCTS
EVP_MD_CTX * ctx = EVP_MD_CTX_new();
if (ctx == NULL)
return 1; /* error */
if(EVP_DigestInit(ctx, EVP_get_digestbyname("sha512"))) {
EVP_DigestUpdate(ctx, message, len);
EVP_DigestFinal(ctx, out, NULL);
EVP_MD_CTX_free(ctx);
return 0; /* success */
}
EVP_MD_CTX_free(ctx);
#else
EVP_MD_CTX ctx;
EVP_MD_CTX_init(&ctx);
if(EVP_DigestInit(&ctx, EVP_get_digestbyname("sha512"))) {
EVP_DigestUpdate(&ctx, message, len);
EVP_DigestFinal(&ctx, out, NULL);
return 0; /* success */
}
#endif
return 1; /* error */
}
int
_libssh2_md5_init(libssh2_md5_ctx *ctx)
{
@ -801,24 +1159,6 @@ _libssh2_md5_init(libssh2_md5_ctx *ctx)
#endif
}
static unsigned char *
write_bn(unsigned char *buf, const BIGNUM *bn, int bn_bytes)
{
unsigned char *p = buf;
/* Left space for bn size which will be written below. */
p += 4;
*p = 0;
BN_bn2bin(bn, p + 1);
if (!(*(p + 1) & 0x80)) {
memmove(p, p + 1, --bn_bytes);
}
_libssh2_htonu32(p - 4, bn_bytes); /* Post write bn size. */
return p + bn_bytes;
}
static unsigned char *
gen_publickey_from_rsa(LIBSSH2_SESSION *session, RSA *rsa,
size_t *key_len)
@ -1029,6 +1369,272 @@ gen_publickey_from_dsa_evp(LIBSSH2_SESSION *session,
}
#endif /* LIBSSH_DSA */
#if LIBSSH2_ECDSA
static int
gen_publickey_from_ec_evp(LIBSSH2_SESSION *session,
unsigned char **method,
size_t *method_len,
unsigned char **pubkeydata,
size_t *pubkeydata_len,
EVP_PKEY *pk)
{
int rc = 0;
EC_KEY *ec = NULL;
unsigned char *p;
unsigned char* method_buf = NULL;
unsigned char *key;
size_t key_len = 0;
unsigned char *octal_value = NULL;
size_t octal_len;
const EC_POINT *public_key;
const EC_GROUP *group;
BN_CTX *bn_ctx;
libssh2_curve_type type;
_libssh2_debug(session,
LIBSSH2_TRACE_AUTH,
"Computing public key from EC private key envelop");
bn_ctx = BN_CTX_new();
if ( bn_ctx == NULL )
return -1;
ec = EVP_PKEY_get1_EC_KEY(pk);
if ( ec == NULL ) {
rc = -1;
goto clean_exit;
}
public_key = EC_KEY_get0_public_key(ec);
group = EC_KEY_get0_group(ec);
type = _libssh2_ecdsa_key_get_curve_type(ec);
method_buf = LIBSSH2_ALLOC(session, 19);
if (method_buf == NULL) {
return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
"out of memory");
}
if ( type == LIBSSH2_EC_CURVE_NISTP256 )
memcpy(method_buf, "ecdsa-sha2-nistp256", 19);
else if ( type == LIBSSH2_EC_CURVE_NISTP384 )
memcpy(method_buf, "ecdsa-sha2-nistp384", 19);
else if ( type == LIBSSH2_EC_CURVE_NISTP521 )
memcpy(method_buf, "ecdsa-sha2-nistp521", 19);
else {
_libssh2_debug(session,
LIBSSH2_TRACE_ERROR,
"Unsupported EC private key type");
rc = -1;
goto clean_exit;
}
/* get length */
octal_len = EC_POINT_point2oct(group, public_key, POINT_CONVERSION_UNCOMPRESSED, NULL, 0, bn_ctx);
if (octal_len > EC_MAX_POINT_LEN) {
rc = -1;
goto clean_exit;
}
octal_value = malloc(octal_len);
if ( octal_value == NULL ) {
rc = -1;
goto clean_exit;
}
/* convert to octal */
if (EC_POINT_point2oct(group, public_key, POINT_CONVERSION_UNCOMPRESSED,
octal_value, octal_len, bn_ctx) != octal_len) {
rc = -1;
goto clean_exit;
}
/* Key form is: type_len(4) + type(19) + domain_len(4) + domain(8) + pub_key_len(4) + pub_key(~65). */
key_len = 4 + 19 + 4 + 8 + 4 + octal_len;
key = LIBSSH2_ALLOC(session, key_len);
if (key == NULL) {
rc = -1;
goto clean_exit;
}
/* Process key encoding. */
p = key;
/* Key type */
_libssh2_store_str(&p, (const char*)method_buf, 19);
/* Name domain */
_libssh2_store_str(&p, (const char*)method_buf + 11, 8);
/* Public key */
_libssh2_store_str(&p, (const char*)octal_value, octal_len);
*method = method_buf;
*method_len = 19;
*pubkeydata = key;
*pubkeydata_len = key_len;
clean_exit:
if ( ec != NULL)
EC_KEY_free(ec);
if (bn_ctx != NULL) {
BN_CTX_free(bn_ctx);
}
if ( octal_value != NULL )
free(octal_value);
if ( rc == 0 )
return 0;
if (method_buf != NULL )
LIBSSH2_FREE(session, method_buf);
return -1;
}
/*
* _libssh2_ecdsa_create_key
*
* Creates a local private key based on input curve
* and returns octal value and octal length
*
*/
int
_libssh2_ecdsa_create_key(_libssh2_ec_key **out_private_key,
unsigned char **out_public_key_octal,
size_t *out_public_key_octal_len, libssh2_curve_type curve_type)
{
int ret = 1;
size_t octal_len = 0;
unsigned char octal_value[EC_MAX_POINT_LEN];
const EC_POINT *public_key = NULL;
EC_KEY *private_key = NULL;
const EC_GROUP *group = NULL;
/* create key */
BN_CTX *bn_ctx = BN_CTX_new();
if (!bn_ctx)
return -1;
private_key = EC_KEY_new_by_curve_name(curve_type);
group = EC_KEY_get0_group(private_key);
EC_KEY_generate_key(private_key);
public_key = EC_KEY_get0_public_key(private_key);
/* get length */
octal_len = EC_POINT_point2oct(group, public_key, POINT_CONVERSION_UNCOMPRESSED, NULL, 0, bn_ctx);
if (octal_len > EC_MAX_POINT_LEN) {
ret = -1;
goto clean_exit;
}
/* convert to octal */
if (EC_POINT_point2oct(group, public_key, POINT_CONVERSION_UNCOMPRESSED,
octal_value, octal_len, bn_ctx) != octal_len){
ret = -1;
goto clean_exit;
}
if (out_private_key != NULL)
*out_private_key = private_key;
if (out_public_key_octal) {
*out_public_key_octal = malloc(octal_len);
if (out_public_key_octal == NULL) {
ret = -1;
goto clean_exit;
}
memcpy(*out_public_key_octal, octal_value, octal_len);
}
if (out_public_key_octal_len != NULL)
*out_public_key_octal_len = octal_len;
clean_exit:
if (bn_ctx)
BN_CTX_free(bn_ctx);
return (ret == 1) ? 0 : -1;
}
/* _libssh2_ecdh_gen_k
*
* Computes the shared secret K given a local private key,
* remote public key and length
*/
int
_libssh2_ecdh_gen_k(_libssh2_bn **k, _libssh2_ec_key *private_key,
const unsigned char *server_public_key, size_t server_public_key_len)
{
int ret = 0;
int rc;
size_t secret_len;
unsigned char *secret;
const EC_GROUP *private_key_group;
EC_POINT *server_public_key_point;
BN_CTX *bn_ctx = BN_CTX_new();
if ( !bn_ctx )
return -1;
if ( k == NULL )
return -1;
private_key_group = EC_KEY_get0_group(private_key);
server_public_key_point = EC_POINT_new(private_key_group);
if ( server_public_key_point == NULL )
return -1;
rc = EC_POINT_oct2point(private_key_group, server_public_key_point, server_public_key, server_public_key_len, bn_ctx);
if ( rc != 1 ) {
ret = -1;
goto clean_exit;
}
secret_len = (EC_GROUP_get_degree(private_key_group) + 7) / 8;
secret = malloc(secret_len);
if (!secret) {
ret = -1;
goto clean_exit;
}
secret_len = ECDH_compute_key(secret, secret_len, server_public_key_point, private_key, NULL);
if( secret_len <= 0 || secret_len > EC_MAX_POINT_LEN ) {
ret = -1;
goto clean_exit;
}
BN_bin2bn(secret, secret_len, *k);
clean_exit:
if ( server_public_key_point != NULL )
EC_POINT_free(server_public_key_point);
if ( bn_ctx != NULL )
BN_CTX_free(bn_ctx);
if ( secret != NULL )
free(secret);
return ret;
}
#endif /* LIBSSH2_ECDSA */
int
_libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session,
unsigned char **method,
@ -1088,6 +1694,13 @@ _libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session,
break;
#endif /* LIBSSH_DSA */
#if LIBSSH2_ECDSA
case EVP_PKEY_EC :
st = gen_publickey_from_ec_evp(
session, method, method_len, pubkeydata, pubkeydata_len, pk);
break;
#endif
default :
st = _libssh2_error(session,
LIBSSH2_ERROR_FILE,

Просмотреть файл

@ -70,6 +70,12 @@
# define LIBSSH2_DSA 1
#endif
#ifdef OPENSSL_NO_ECDSA
# define LIBSSH2_ECDSA 0
#else
# define LIBSSH2_ECDSA 1
#endif
#ifdef OPENSSL_NO_MD5
# define LIBSSH2_MD5 0
#else
@ -117,6 +123,8 @@
# define LIBSSH2_3DES 1
#endif
#define EC_MAX_POINT_LEN ((528 * 2 / 8) + 1)
#define _libssh2_random(buf, len) RAND_bytes ((buf), (len))
#define libssh2_prepare_iovec(vec, len) /* Empty. */
@ -167,6 +175,52 @@ int _libssh2_sha256(const unsigned char *message, unsigned long len,
unsigned char *out);
#define libssh2_sha256(x,y,z) _libssh2_sha256(x,y,z)
#ifdef HAVE_OPAQUE_STRUCTS
#define libssh2_sha384_ctx EVP_MD_CTX *
#else
#define libssh2_sha384_ctx EVP_MD_CTX
#endif
/* returns 0 in case of failure */
int _libssh2_sha384_init(libssh2_sha384_ctx *ctx);
#define libssh2_sha384_init(x) _libssh2_sha384_init(x)
#ifdef HAVE_OPAQUE_STRUCTS
#define libssh2_sha384_update(ctx, data, len) EVP_DigestUpdate(ctx, data, len)
#define libssh2_sha384_final(ctx, out) do { \
EVP_DigestFinal(ctx, out, NULL); \
EVP_MD_CTX_free(ctx); \
} while(0)
#else
#define libssh2_sha384_update(ctx, data, len) EVP_DigestUpdate(&(ctx), data, len)
#define libssh2_sha384_final(ctx, out) EVP_DigestFinal(&(ctx), out, NULL)
#endif
int _libssh2_sha384(const unsigned char *message, unsigned long len,
unsigned char *out);
#define libssh2_sha384(x,y,z) _libssh2_sha384(x,y,z)
#ifdef HAVE_OPAQUE_STRUCTS
#define libssh2_sha512_ctx EVP_MD_CTX *
#else
#define libssh2_sha512_ctx EVP_MD_CTX
#endif
/* returns 0 in case of failure */
int _libssh2_sha512_init(libssh2_sha512_ctx *ctx);
#define libssh2_sha512_init(x) _libssh2_sha512_init(x)
#ifdef HAVE_OPAQUE_STRUCTS
#define libssh2_sha512_update(ctx, data, len) EVP_DigestUpdate(ctx, data, len)
#define libssh2_sha512_final(ctx, out) do { \
EVP_DigestFinal(ctx, out, NULL); \
EVP_MD_CTX_free(ctx); \
} while(0)
#else
#define libssh2_sha512_update(ctx, data, len) EVP_DigestUpdate(&(ctx), data, len)
#define libssh2_sha512_final(ctx, out) EVP_DigestFinal(&(ctx), out, NULL)
#endif
int _libssh2_sha512(const unsigned char *message, unsigned long len,
unsigned char *out);
#define libssh2_sha512(x,y,z) _libssh2_sha512(x,y,z)
#ifdef HAVE_OPAQUE_STRUCTS
#define libssh2_md5_ctx EVP_MD_CTX *
#else
@ -246,9 +300,23 @@ int _libssh2_md5_init(libssh2_md5_ctx *ctx);
#define libssh2_dsa_ctx DSA
#define _libssh2_dsa_free(dsactx) DSA_free(dsactx)
#if LIBSSH2_ECDSA
#define libssh2_ecdsa_ctx EC_KEY
#define _libssh2_ecdsa_free(ecdsactx) EC_KEY_free(ecdsactx)
#define _libssh2_ec_key EC_KEY
typedef enum {
LIBSSH2_EC_CURVE_NISTP256 = NID_X9_62_prime256v1,
LIBSSH2_EC_CURVE_NISTP384 = NID_secp384r1,
LIBSSH2_EC_CURVE_NISTP521 = NID_secp521r1,
}
libssh2_curve_type;
#else
#define _libssh2_ec_key void
#endif
#define _libssh2_cipher_type(name) const EVP_CIPHER *(*name)(void)
#ifdef HAVE_OPAQUE_STRUCTS
#define _libssh2_cipher_ctx EVP_CIPHER_CTX *
@ -311,4 +379,3 @@ extern void _libssh2_dh_dtor(_libssh2_dh_ctx *dhctx);
const EVP_CIPHER *_libssh2_EVP_aes_128_ctr(void);
const EVP_CIPHER *_libssh2_EVP_aes_192_ctr(void);
const EVP_CIPHER *_libssh2_EVP_aes_256_ctr(void);

Просмотреть файл

@ -50,6 +50,10 @@ COPY ssh_host_rsa_key /tmp/etc/ssh/ssh_host_rsa_key
RUN mv /tmp/etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_rsa_key
RUN chmod 600 /etc/ssh/ssh_host_rsa_key
COPY ssh_host_ecdsa_key /tmp/etc/ssh/ssh_host_ecdsa_key
RUN mv /tmp/etc/ssh/ssh_host_ecdsa_key /etc/ssh/ssh_host_ecdsa_key
RUN chmod 600 /etc/ssh/ssh_host_ecdsa_key
RUN adduser --disabled-password --gecos 'Test user for libssh2 integration tests' libssh2
RUN echo 'libssh2:my test password' | chpasswd

5
tests/openssh_server/ssh_host_ecdsa_key Обычный файл
Просмотреть файл

@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIKdqGrp+52U1ehslMI4fX0cmvgHFmKSkMzQGmj6B07ecoAoGCCqGSM49
AwEHoUQDQgAEL7+zLJ4okP10LZkf1DuIkZF5HhgzetQIyxLKeTJeiN19IKUYIxjs
m9aW3fQRKNi/GhN9JEbHpa9qpgr+8+hhDg==
-----END EC PRIVATE KEY-----

Просмотреть файл

@ -4,7 +4,7 @@
#include <stdio.h>
const char *EXPECTED_HOSTKEY =
const char *EXPECTED_RSA_HOSTKEY =
"AAAAB3NzaC1yc2EAAAABIwAAAQEArrr/JuJmaZligyfS8vcNur+mWR2ddDQtVdhHzdKU"
"UoR6/Om6cvxpe61H1YZO1xCpLUBXmkki4HoNtYOpPB2W4V+8U4BDeVBD5crypEOE1+7B"
"Am99fnEDxYIOZq2/jTP0yQmzCpWYS3COyFmkOL7sfX1wQMeW5zQT2WKcxC6FSWbhDqrB"
@ -12,6 +12,10 @@ const char *EXPECTED_HOSTKEY =
"i6ELfP3r+q6wdu0P4jWaoo3De1aYxnToV/ldXykpipON4NPamsb6Ph2qlJQKypq7J4iQ"
"gkIIbCU1A31+4ExvcIVoxLQw/aTSbw==";
const char *EXPECTED_ECDSA_HOSTKEY =
"AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBC+/syyeKJD9dC2ZH"
"9Q7iJGReR4YM3rUCMsSynkyXojdfSClGCMY7JvWlt30ESjYvxoTfSRGx6WvaqYK/vPoYQ4=";
int test(LIBSSH2_SESSION *session)
{
int rc;
@ -26,14 +30,19 @@ int test(LIBSSH2_SESSION *session)
return 1;
}
if (type != LIBSSH2_HOSTKEY_TYPE_RSA) {
/* Hostkey configured in docker container is RSA */
fprintf(stderr, "Wrong type of hostkey\n");
if (type == LIBSSH2_HOSTKEY_TYPE_ECDSA) {
rc = libssh2_base64_decode(session, &expected_hostkey, &expected_len,
EXPECTED_ECDSA_HOSTKEY, strlen(EXPECTED_ECDSA_HOSTKEY));
}
else if (type == LIBSSH2_HOSTKEY_TYPE_RSA) {
rc = libssh2_base64_decode(session, &expected_hostkey, &expected_len,
EXPECTED_RSA_HOSTKEY, strlen(EXPECTED_RSA_HOSTKEY));
}
else {
fprintf(stderr, "Unexpected type of hostkey: %i\n", type);
return 1;
}
rc = libssh2_base64_decode(session, &expected_hostkey, &expected_len,
EXPECTED_HOSTKEY, strlen(EXPECTED_HOSTKEY));
if (rc != 0) {
print_last_session_error("libssh2_base64_decode");
return 1;

Просмотреть файл

@ -5,7 +5,7 @@
#include <stdio.h>
const char *EXPECTED_HOSTKEY =
const char *EXPECTED_RSA_HOSTKEY =
"AAAAB3NzaC1yc2EAAAABIwAAAQEArrr/JuJmaZligyfS8vcNur+mWR2ddDQtVdhHzdKU"
"UoR6/Om6cvxpe61H1YZO1xCpLUBXmkki4HoNtYOpPB2W4V+8U4BDeVBD5crypEOE1+7B"
"Am99fnEDxYIOZq2/jTP0yQmzCpWYS3COyFmkOL7sfX1wQMeW5zQT2WKcxC6FSWbhDqrB"
@ -13,13 +13,27 @@ const char *EXPECTED_HOSTKEY =
"i6ELfP3r+q6wdu0P4jWaoo3De1aYxnToV/ldXykpipON4NPamsb6Ph2qlJQKypq7J4iQ"
"gkIIbCU1A31+4ExvcIVoxLQw/aTSbw==";
const char *EXPECTED_MD5_HASH_DIGEST = "0C0ED1A5BB10275F76924CE187CE5C5E";
const char *EXPECTED_ECDSA_HOSTKEY =
"AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBC+/syyeKJD9dC2ZH"
"9Q7iJGReR4YM3rUCMsSynkyXojdfSClGCMY7JvWlt30ESjYvxoTfSRGx6WvaqYK/vPoYQ4=";
const char *EXPECTED_SHA1_HASH_DIGEST =
const char *EXPECTED_RSA_MD5_HASH_DIGEST = "0C0ED1A5BB10275F76924CE187CE5C5E";
const char *EXPECTED_RSA_SHA1_HASH_DIGEST =
"F3CD59E2913F4422B80F7B0A82B2B89EAE449387";
const char *EXPECTED_RSA_SHA256_HASH_DIGEST = "92E3DA49DF3C7F99A828F505ED8239397A5D1F62914459760F878F7510F563A3";
const char *EXPECTED_ECDSA_MD5_HASH_DIGEST = "0402E4D897580BBC911379CBD88BCD3D";
const char *EXPECTED_ECDSA_SHA1_HASH_DIGEST =
"12FDAD1E3B31B10BABB00F2A8D1B9A62C326BD2F";
const char *EXPECTED_ECDSA_SHA256_HASH_DIGEST = "56FCD975B166C3F0342D0036E44C311A86C0EAE40713B53FC776369BAE7F5264";
const int MD5_HASH_SIZE = 16;
const int SHA1_HASH_SIZE = 20;
const int SHA256_HASH_SIZE = 32;
static void calculate_digest(const char *hash, size_t hash_len, char *buffer,
size_t buffer_len)
@ -39,34 +53,111 @@ int test(LIBSSH2_SESSION *session)
const char *md5_hash;
const char *sha1_hash;
const char *sha256_hash;
int type;
size_t len;
md5_hash = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
if (md5_hash == NULL) {
print_last_session_error(
"libssh2_hostkey_hash(LIBSSH2_HOSTKEY_HASH_MD5)");
const char *hostkey = libssh2_session_hostkey(session, &len, &type);
if (hostkey == NULL) {
print_last_session_error("libssh2_session_hostkey");
return 1;
}
calculate_digest(md5_hash, MD5_HASH_SIZE, buf, BUFSIZ);
if (type == LIBSSH2_HOSTKEY_TYPE_ECDSA) {
if (strcmp(buf, EXPECTED_MD5_HASH_DIGEST) != 0) {
fprintf(stderr, "MD5 hash not as expected - digest %s != %s\n", buf,
EXPECTED_MD5_HASH_DIGEST);
return 1;
}
md5_hash = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
if (md5_hash == NULL) {
print_last_session_error(
"libssh2_hostkey_hash(LIBSSH2_HOSTKEY_HASH_MD5)");
return 1;
}
sha1_hash = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
if (sha1_hash == NULL) {
print_last_session_error(
"libssh2_hostkey_hash(LIBSSH2_HOSTKEY_HASH_SHA1)");
return 1;
}
calculate_digest(md5_hash, MD5_HASH_SIZE, buf, BUFSIZ);
calculate_digest(sha1_hash, SHA1_HASH_SIZE, buf, BUFSIZ);
if (strcmp(buf, EXPECTED_ECDSA_MD5_HASH_DIGEST) != 0) {
fprintf(stderr, "ECDSA MD5 hash not as expected - digest %s != %s\n", buf,
EXPECTED_ECDSA_MD5_HASH_DIGEST);
return 1;
}
if (strcmp(buf, EXPECTED_SHA1_HASH_DIGEST) != 0) {
fprintf(stderr, "SHA1 hash not as expected - digest %s != %s\n", buf,
EXPECTED_SHA1_HASH_DIGEST);
sha1_hash = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
if (sha1_hash == NULL) {
print_last_session_error(
"libssh2_hostkey_hash(LIBSSH2_HOSTKEY_HASH_SHA1)");
return 1;
}
calculate_digest(sha1_hash, SHA1_HASH_SIZE, buf, BUFSIZ);
if (strcmp(buf, EXPECTED_ECDSA_SHA1_HASH_DIGEST) != 0) {
fprintf(stderr, "ECDSA SHA1 hash not as expected - digest %s != %s\n", buf,
EXPECTED_ECDSA_SHA1_HASH_DIGEST);
return 1;
}
sha256_hash = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA256);
if (sha256_hash == NULL) {
print_last_session_error(
"libssh2_hostkey_hash(LIBSSH2_HOSTKEY_HASH_SHA256)");
return 1;
}
calculate_digest(sha256_hash, SHA256_HASH_SIZE, buf, BUFSIZ);
if (strcmp(buf, EXPECTED_ECDSA_SHA256_HASH_DIGEST) != 0) {
fprintf(stderr, "ECDSA SHA256 hash not as expected - digest %s != %s\n", buf,
EXPECTED_ECDSA_SHA256_HASH_DIGEST);
return 1;
}
} else if ( type == LIBSSH2_HOSTKEY_TYPE_RSA ) {
md5_hash = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
if (md5_hash == NULL) {
print_last_session_error(
"libssh2_hostkey_hash(LIBSSH2_HOSTKEY_HASH_MD5)");
return 1;
}
calculate_digest(md5_hash, MD5_HASH_SIZE, buf, BUFSIZ);
if (strcmp(buf, EXPECTED_RSA_MD5_HASH_DIGEST) != 0) {
fprintf(stderr, "MD5 hash not as expected - digest %s != %s\n", buf,
EXPECTED_RSA_MD5_HASH_DIGEST);
return 1;
}
sha1_hash = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
if (sha1_hash == NULL) {
print_last_session_error(
"libssh2_hostkey_hash(LIBSSH2_HOSTKEY_HASH_SHA1)");
return 1;
}
calculate_digest(sha1_hash, SHA1_HASH_SIZE, buf, BUFSIZ);
if (strcmp(buf, EXPECTED_RSA_SHA1_HASH_DIGEST) != 0) {
fprintf(stderr, "SHA1 hash not as expected - digest %s != %s\n", buf,
EXPECTED_RSA_SHA1_HASH_DIGEST);
return 1;
}
sha256_hash = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA256);
if (sha256_hash == NULL) {
print_last_session_error(
"libssh2_hostkey_hash(LIBSSH2_HOSTKEY_HASH_SHA256)");
return 1;
}
calculate_digest(sha256_hash, SHA256_HASH_SIZE, buf, BUFSIZ);
if (strcmp(buf, EXPECTED_RSA_SHA256_HASH_DIGEST) != 0) {
fprintf(stderr, "SHA256 hash not as expected - digest %s != %s\n", buf,
EXPECTED_RSA_SHA256_HASH_DIGEST);
return 1;
}
} else {
fprintf(stderr, "Unexpected type of hostkey: %i\n", type);
return 1;
}