mirror of
https://github.com/libssh2/libssh2.git
synced 2025-11-20 02:42:09 +03:00
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
This commit is contained in:
committed by
Viktor Szakats
parent
bcd492163b
commit
aba34f5f56
649
src/openssl.c
649
src/openssl.c
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user